script_webcrypto/subtlecrypto/x25519_operation.rs
1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use js::context::JSContext;
6use pkcs8::der::asn1::OctetStringRef;
7use pkcs8::der::{Decode, Encode};
8use pkcs8::{AlgorithmIdentifierRef, ObjectIdentifier, PrivateKeyInfoRef, SubjectPublicKeyInfoRef};
9use script_bindings::codegen::GenericBindings::CryptoKeyBinding::{
10 CryptoKeyMethods, CryptoKeyPair, KeyType, KeyUsage,
11};
12use script_bindings::codegen::GenericBindings::SubtleCryptoBinding::{JsonWebKey, KeyFormat};
13use script_bindings::error::Error;
14use script_bindings::root::DomRoot;
15use script_bindings::str::DOMString;
16use x25519_dalek::{PublicKey, StaticSecret};
17use zeroize::Zeroizing;
18
19use crate::cryptokey::{CryptoKey, Handle, KeyUsageSliceHelper};
20use crate::subtlecrypto::{
21 CryptoAlgorithm, EcdhKeyDeriveParams, ExportedKey, JsonWebKeyExt, JwkStringField, KeyAlgorithm,
22 KeyAlgorithmAndDerivatives,
23};
24use crate::traits::Equivalence;
25
26/// `id-X25519` object identifier defined in [RFC8410]
27const X25519_OID_STRING: &str = "1.3.101.110";
28
29const PRIVATE_KEY_LENGTH: usize = 32;
30const PUBLIC_KEY_LENGTH: usize = 32;
31
32/// <https://w3c.github.io/webcrypto/#x25519-operations-derive-bits>
33pub(crate) fn derive_bits<D: Equivalence>(
34 normalized_algorithm: &EcdhKeyDeriveParams<D>,
35 key: &CryptoKey<D>,
36 length: Option<u32>,
37) -> Result<Vec<u8>, Error> {
38 // Step 1. Let publicKey be the public member of normalizedAlgorithm.
39 let public_key = normalized_algorithm.public.root();
40
41 // Step 2. If the [[type]] internal slot of publicKey is not "public", then throw an
42 // InvalidAccessError.
43 public_key.ensure_type(KeyType::Public)?;
44
45 // Step 3. If the name attribute of the [[algorithm]] internal slot of publicKey is not equal to
46 // the name member of normalizedAlgorithm, then throw an InvalidAccessError.
47 if public_key.algorithm().name() != normalized_algorithm.name {
48 return Err(Error::InvalidAccess(Some(
49 "The name attribute of the [[algorithm]] internal slot of publicKey does not match \
50 the name member of normalizedAlgorithm"
51 .into(),
52 )));
53 }
54
55 // Step 4. If length is not null and is greater than 256, then throw an OperationError.
56 if length.is_some_and(|length| length > 256) {
57 return Err(Error::Operation(Some(
58 "Required length is greater than 256".into(),
59 )));
60 }
61
62 // Step 5. If the [[type]] internal slot of key is not "private", then throw an
63 // InvalidAccessError.
64 key.ensure_type(KeyType::Private)?;
65
66 // Step 6. If the name attribute of the [[algorithm]] internal slot of publicKey is not equal to
67 // the name property of the [[algorithm]] internal slot of key, then throw an
68 // InvalidAccessError.
69 if public_key.algorithm().name() != key.algorithm().name() {
70 return Err(Error::InvalidAccess(Some(
71 "Public key [[algorithm]] internal slot name does not match that of private key".into(),
72 )));
73 }
74
75 // Step 7. Let secret be the result of performing the X25519 function specified in [RFC7748]
76 // Section 5 with key as the X25519 private key k and the X25519 public key represented by the
77 // [[handle]] internal slot of publicKey as the X25519 public key u.
78 let Handle::X25519PrivateKey(private_key) = key.handle() else {
79 return Err(Error::Operation(Some(
80 "Private key's handle is invalid".into(),
81 )));
82 };
83 let Handle::X25519PublicKey(public_key) = public_key.handle() else {
84 return Err(Error::Operation(Some(
85 "Public key's handle is invalid".into(),
86 )));
87 };
88 let secret = private_key.diffie_hellman(public_key);
89
90 // Step 8. If secret is the all-zero value, then throw a OperationError. This check must be
91 // performed in constant-time, as per [RFC7748] Section 6.1.
92 if !secret.was_contributory() {
93 return Err(Error::Operation(Some(
94 "Secret is the all-zero value".into(),
95 )));
96 }
97
98 // Step 9.
99 // If length is null:
100 // Return secret
101 // Otherwise:
102 // If the length of secret in bits is less than length:
103 // throw an OperationError.
104 // Otherwise:
105 // Return a byte sequence containing the first length bits of secret.
106 let secret_slice = secret.as_bytes();
107 match length {
108 None => Ok(secret_slice.to_vec()),
109 Some(length) => {
110 if secret_slice.len() * 8 < length as usize {
111 Err(Error::Operation(Some(
112 "Length of secret is less than requested length".into(),
113 )))
114 } else {
115 let mut secret = secret_slice[..length.div_ceil(8) as usize].to_vec();
116 if length % 8 != 0 {
117 // Clean excess bits in last byte of secret.
118 let mask = u8::MAX << (8 - length % 8);
119 if let Some(last_byte) = secret.last_mut() {
120 *last_byte &= mask;
121 }
122 }
123 Ok(secret)
124 }
125 },
126 }
127}
128
129/// <https://w3c.github.io/webcrypto/#x25519-operations-generate-key>
130pub(crate) fn generate_key<D: Equivalence>(
131 cx: &mut JSContext,
132 global: &D::GlobalScope,
133 extractable: bool,
134 usages: Vec<KeyUsage>,
135) -> Result<CryptoKeyPair<D>, Error> {
136 // Step 1. If usages contains an entry which is not "deriveKey" or "deriveBits" then throw a
137 // SyntaxError.
138 usages.ensure_only_contain_entries_from(&[KeyUsage::DeriveKey, KeyUsage::DeriveBits])?;
139
140 // Step 2. Generate an X25519 key pair, with the private key being 32 random bytes, and the
141 // public key being X25519(a, 9), as defined in [RFC7748], section 6.1.
142 let private_key = StaticSecret::random();
143 let public_key = PublicKey::from(&private_key);
144
145 // Step 3. Let algorithm be a new KeyAlgorithm object.
146 // Step 4. Set the name attribute of algorithm to "X25519".
147 let algorithm = KeyAlgorithm {
148 name: CryptoAlgorithm::X25519,
149 };
150
151 // Step 5. Let publicKey be a new CryptoKey representing the public key of the generated key pair.
152 // Step 6. Set the [[type]] internal slot of publicKey to "public"
153 // Step 7. Set the [[algorithm]] internal slot of publicKey to algorithm.
154 // Step 8. Set the [[extractable]] internal slot of publicKey to true.
155 // Step 9. Set the [[usages]] internal slot of publicKey to be the empty list.
156 let public_key = CryptoKey::new(
157 cx,
158 global,
159 KeyType::Public,
160 true,
161 KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm.clone()),
162 Vec::new(),
163 Handle::X25519PublicKey(public_key),
164 );
165
166 // Step 10. Let privateKey be a new CryptoKey representing the private key of the generated key pair.
167 // Step 11. Set the [[type]] internal slot of privateKey to "private"
168 // Step 12. Set the [[algorithm]] internal slot of privateKey to algorithm.
169 // Step 13. Set the [[extractable]] internal slot of privateKey to extractable.
170 // Step 14. Set the [[usages]] internal slot of privateKey to be the usage intersection of
171 // usages and [ "deriveKey", "deriveBits" ].
172 let private_key = CryptoKey::new(
173 cx,
174 global,
175 KeyType::Private,
176 extractable,
177 KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm),
178 usages.usage_intersection(&[KeyUsage::DeriveKey, KeyUsage::DeriveBits]),
179 Handle::X25519PrivateKey(private_key),
180 );
181
182 // Step 15. Let result be a new CryptoKeyPair dictionary.
183 // Step 16. Set the publicKey attribute of result to be publicKey.
184 // Step 17. Set the privateKey attribute of result to be privateKey.
185 let result = CryptoKeyPair {
186 publicKey: Some(public_key),
187 privateKey: Some(private_key),
188 };
189
190 // Step 18. Return result.
191 Ok(result)
192}
193
194/// <https://w3c.github.io/webcrypto/#x25519-operations-import-key>
195pub(crate) fn import_key<D: Equivalence>(
196 cx: &mut JSContext,
197 global: &D::GlobalScope,
198 format: KeyFormat,
199 key_data: &[u8],
200 extractable: bool,
201 usages: Vec<KeyUsage>,
202) -> Result<DomRoot<CryptoKey<D>>, Error> {
203 // Step 1. Let keyData be the key data to be imported.
204
205 // Step 2.
206 let key = match format {
207 // If format is "spki":
208 KeyFormat::Spki => {
209 // Step 2.1. If usages is not empty then throw a SyntaxError.
210 usages.ensure_only_contain_entries_from(&[])?;
211
212 // Step 2.2. Let spki be the result of running the parse a subjectPublicKeyInfo
213 // algorithm over keyData.
214 // Step 2.3. If an error occurred while parsing, then throw a DataError.
215 let spki = SubjectPublicKeyInfoRef::from_der(key_data).map_err(|_| {
216 Error::Data(Some(
217 "Failed to parse the X25519 public key in SPKI format".into(),
218 ))
219 })?;
220
221 // Step 2.4. If the algorithm object identifier field of the algorithm
222 // AlgorithmIdentifier field of spki is not equal to the id-X25519 object identifier
223 // defined in [RFC8410], then throw a DataError.
224 if spki.algorithm.oid != ObjectIdentifier::new_unwrap(X25519_OID_STRING) {
225 return Err(Error::Data(Some(
226 "Algorithm object identifiers do not match".into(),
227 )));
228 }
229
230 // Step 2.5. If the parameters field of the algorithm AlgorithmIdentifier field of spki
231 // is present, then throw a DataError.
232 if spki.algorithm.parameters.is_some() {
233 return Err(Error::Data(Some(
234 "Spki's algorithm contains parameters".into(),
235 )));
236 }
237
238 // Step 2.6. Let publicKey be the X25519 public key identified by the subjectPublicKey
239 // field of spki.
240 let key_bytes: [u8; PUBLIC_KEY_LENGTH] = spki
241 .subject_public_key
242 .as_bytes()
243 .ok_or(Error::Data(Some("Could not parse key as bytes".into())))?
244 .try_into()
245 .map_err(|_| {
246 Error::Data(Some("Could not convert 'Option' type to byte slice".into()))
247 })?;
248 let public_key = PublicKey::from(key_bytes);
249
250 // Step 2.7. Let key be a new CryptoKey that represents publicKey.
251 // Step 2.8. Set the [[type]] internal slot of key to "public"
252 // Step 2.9. Let algorithm be a new KeyAlgorithm.
253 // Step 2.10. Set the name attribute of algorithm to "X25519".
254 // Step 2.11. Set the [[algorithm]] internal slot of key to algorithm.
255 let algorithm = KeyAlgorithm {
256 name: CryptoAlgorithm::X25519,
257 };
258 CryptoKey::new(
259 cx,
260 global,
261 KeyType::Public,
262 extractable,
263 KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm),
264 usages.normalized_value(),
265 Handle::X25519PublicKey(public_key),
266 )
267 },
268 // If format is "pkcs8":
269 KeyFormat::Pkcs8 => {
270 // Step 2.1. If usages contains an entry which is not "deriveKey" or "deriveBits" then
271 // throw a SyntaxError.
272 usages
273 .ensure_only_contain_entries_from(&[KeyUsage::DeriveKey, KeyUsage::DeriveBits])?;
274
275 // Step 2.2. Let privateKeyInfo be the result of running the parse a privateKeyInfo
276 // algorithm over keyData.
277 // Step 2.3. If an error occurs while parsing, then throw a DataError.
278 let private_key_info = PrivateKeyInfoRef::from_der(key_data).map_err(|_| {
279 Error::Data(Some(
280 "Failed to parse the X25519 private key in PKCS#8 format".into(),
281 ))
282 })?;
283
284 // Step 2.4. If the algorithm object identifier field of the privateKeyAlgorithm
285 // PrivateKeyAlgorithm field of privateKeyInfo is not equal to the id-X25519 object
286 // identifier defined in [RFC8410], then throw a DataError.
287 if private_key_info.algorithm.oid != ObjectIdentifier::new_unwrap(X25519_OID_STRING) {
288 return Err(Error::Data(Some(
289 "Algorithm object identifiers do not match".into(),
290 )));
291 }
292
293 // Step 2.5. If the parameters field of the privateKeyAlgorithm
294 // PrivateKeyAlgorithmIdentifier field of privateKeyInfo is present, then throw a
295 // DataError.
296 if private_key_info.algorithm.parameters.is_some() {
297 return Err(Error::Data(Some(
298 "Private key's algorithm contains parameters".into(),
299 )));
300 }
301
302 // Step 2.6. Let curvePrivateKey be the result of performing the parse an ASN.1
303 // structure algorithm, with data as the privateKey field of privateKeyInfo, structure
304 // as the ASN.1 CurvePrivateKey structure specified in Section 7 of [RFC8410], and
305 // exactData set to true.
306 // Step 2.7. If an error occurred while parsing, then throw a DataError.
307 let curve_private_key = private_key_info
308 .private_key
309 .decode_into::<&OctetStringRef>()
310 .map_err(|_| {
311 Error::Data(Some(
312 "Failed to decode the privateKey field of PrivateKeyInfo ASN.1 structure"
313 .into(),
314 ))
315 })?;
316 let key_bytes: [u8; PRIVATE_KEY_LENGTH] =
317 curve_private_key.as_bytes().try_into().map_err(|_| {
318 Error::Data(Some(
319 "Failed to extract the raw bytes from the CurvePrivateKey ASN.1 structure"
320 .into(),
321 ))
322 })?;
323 let curve_private_key = StaticSecret::from(key_bytes);
324
325 // Step 2.8. Let key be a new CryptoKey that represents the X25519 private key
326 // identified by curvePrivateKey.
327 // Step 2.9. Set the [[type]] internal slot of key to "private"
328 // Step 2.10. Let algorithm be a new KeyAlgorithm.
329 // Step 2.11. Set the name attribute of algorithm to "X25519".
330 // Step 2.12. Set the [[algorithm]] internal slot of key to algorithm.
331 let algorithm = KeyAlgorithm {
332 name: CryptoAlgorithm::X25519,
333 };
334 CryptoKey::new(
335 cx,
336 global,
337 KeyType::Private,
338 extractable,
339 KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm),
340 usages.normalized_value(),
341 Handle::X25519PrivateKey(curve_private_key),
342 )
343 },
344 // If format is "jwk":
345 KeyFormat::Jwk => {
346 // Step 2.1
347 // If keyData is a JsonWebKey dictionary:
348 // Let jwk equal keyData.
349 // Otherwise:
350 // Throw a DataError.
351 let jwk = JsonWebKey::parse(cx, key_data)?;
352
353 // Step 2.2. If the d field is present and if usages contains an entry which is not
354 // "deriveKey" or "deriveBits" then throw a SyntaxError.
355 // Step 2.3. If the d field is not present and if usages is not empty then throw a
356 // SyntaxError.
357 match jwk.d.as_ref() {
358 Some(_) => usages.ensure_only_contain_entries_from(&[
359 KeyUsage::DeriveKey,
360 KeyUsage::DeriveBits,
361 ])?,
362 None => usages.ensure_only_contain_entries_from(&[])?,
363 }
364
365 // Step 2.4. If the kty field of jwk is not "OKP", then throw a DataError.
366 if jwk.kty.as_ref().is_none_or(|kty| kty != "OKP") {
367 return Err(Error::Data(Some(
368 "'kty' field of JSON Web Key is not 'OKP'".into(),
369 )));
370 }
371
372 // Step 2.5. If the crv field of jwk is not "X25519", then throw a DataError.
373 if jwk.crv.as_ref().is_none_or(|crv| crv != "X25519") {
374 return Err(Error::Data(Some(
375 "'crv' field of JSON Web Key is not 'X25519'".into(),
376 )));
377 }
378
379 // Step 2.6. If usages is non-empty and the use field of jwk is present and is not
380 // equal to "enc" then throw a DataError.
381 if !usages.is_empty() && jwk.use_.as_ref().is_some_and(|use_| use_ != "enc") {
382 return Err(Error::Data(Some(
383 "Key usages is not empty and 'use' field of JSON Web Key is not equal to 'enc'"
384 .into(),
385 )));
386 }
387
388 // Step 2.7. If the key_ops field of jwk is present, and is invalid according to the
389 // requirements of JSON Web Key [JWK], or it does not contain all of the specified
390 // usages values, then throw a DataError.
391 jwk.check_key_ops(&usages)?;
392
393 // Step 2.8. If the ext field of jwk is present and has the value false and extractable
394 // is true, then throw a DataError.
395 if jwk.ext.is_some_and(|ext| !ext) && extractable {
396 return Err(Error::Data(Some(
397 "'ext' field of JSON Web Key is false and 'extractable' is true".into(),
398 )));
399 }
400
401 // Step 2.9.
402 // If the d field is present:
403 let (handle, key_type) = if jwk.d.is_some() {
404 // Step 2.9.1. If jwk does not meet the requirements of the JWK private key format
405 // described in Section 2 of [RFC8037], then throw a DataError.
406 let x = jwk.decode_required_string_field(JwkStringField::X)?;
407 let d = jwk.decode_required_string_field(JwkStringField::D)?;
408 let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] =
409 x.as_slice().try_into().map_err(|_| {
410 Error::Data(Some("Could not convert public key to bytes".into()))
411 })?;
412 let private_key_bytes: [u8; PRIVATE_KEY_LENGTH] =
413 d.as_slice().try_into().map_err(|_| {
414 Error::Data(Some("Could not convert private key to bytes".into()))
415 })?;
416 let public_key = PublicKey::from(public_key_bytes);
417 let private_key = StaticSecret::from(private_key_bytes);
418 if PublicKey::from(&private_key) != public_key {
419 return Err(Error::Data(Some(
420 "Could not compute public key from given private key".into(),
421 )));
422 }
423
424 // Step 2.9.1. Let key be a new CryptoKey object that represents the X25519 private
425 // key identified by interpreting jwk according to Section 2 of [RFC8037].
426 // NOTE: CryptoKey is created in Step 2.10 - 2.12.
427 let handle = Handle::X25519PrivateKey(private_key);
428
429 // Step 2.9.1. Set the [[type]] internal slot of Key to "private".
430 let key_type = KeyType::Private;
431
432 (handle, key_type)
433 }
434 // Otherwise:
435 else {
436 // Step 2.9.1. If jwk does not meet the requirements of the JWK public key format
437 // described in Section 2 of [RFC8037], then throw a DataError.
438 let x = jwk.decode_required_string_field(JwkStringField::X)?;
439 let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] =
440 x.as_slice().try_into().map_err(|_| {
441 Error::Data(Some("Could not convert public key to bytes".into()))
442 })?;
443 let public_key = PublicKey::from(public_key_bytes);
444
445 // Step 2.9.1. Let key be a new CryptoKey object that represents the X25519 public
446 // key identified by interpreting jwk according to Section 2 of [RFC8037].
447 // NOTE: CryptoKey is created in Step 2.10 - 2.12.
448 let handle = Handle::X25519PublicKey(public_key);
449
450 // Step 2.9.1. Set the [[type]] internal slot of Key to "public".
451 let key_type = KeyType::Public;
452
453 (handle, key_type)
454 };
455
456 // Step 2.10. Let algorithm be a new instance of a KeyAlgorithm object.
457 // Step 2.11. Set the name attribute of algorithm to "X25519".
458 // Step 2.12. Set the [[algorithm]] internal slot of key to algorithm.
459 let algorithm = KeyAlgorithm {
460 name: CryptoAlgorithm::X25519,
461 };
462 CryptoKey::new(
463 cx,
464 global,
465 key_type,
466 extractable,
467 KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm),
468 usages.normalized_value(),
469 handle,
470 )
471 },
472 // If format is "raw":
473 KeyFormat::Raw | KeyFormat::Raw_public => {
474 // Step 2.1. If usages is not empty then throw a SyntaxError.
475 usages.ensure_only_contain_entries_from(&[])?;
476
477 // Step 2.2. If the length in bits of keyData is not 256 then throw a DataError.
478 if key_data.len() != 32 {
479 return Err(Error::Data(Some(
480 "Length of key data in bits is not 256".into(),
481 )));
482 }
483
484 // Step 2.3. Let algorithm be a new KeyAlgorithm object.
485 // Step 2.4. Set the name attribute of algorithm to "X25519".
486 // Step 2.5. Let key be a new CryptoKey representing the key data provided in keyData.
487 // Step 2.6. Set the [[type]] internal slot of key to "public"
488 // Step 2.7. Set the [[algorithm]] internal slot of key to algorithm.
489 let key_bytes: [u8; PUBLIC_KEY_LENGTH] = key_data
490 .try_into()
491 .map_err(|_| Error::Data(Some("Could not convert key to bytes".into())))?;
492 let public_key = PublicKey::from(key_bytes);
493 let algorithm = KeyAlgorithm {
494 name: CryptoAlgorithm::X25519,
495 };
496 CryptoKey::new(
497 cx,
498 global,
499 KeyType::Public,
500 extractable,
501 KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm),
502 usages.normalized_value(),
503 Handle::X25519PublicKey(public_key),
504 )
505 },
506 // Otherwise:
507 _ => {
508 // throw a NotSupportedError.
509 return Err(Error::NotSupported(Some("Key format not supported".into())));
510 },
511 };
512
513 // Step 3. Return key
514 Ok(key)
515}
516
517/// <https://w3c.github.io/webcrypto/#x25519-operations-export-key>
518pub(crate) fn export_key<D: Equivalence>(
519 format: KeyFormat,
520 key: &CryptoKey<D>,
521) -> Result<ExportedKey, Error> {
522 // Step 1. Let key be the CryptoKey to be exported.
523
524 // Step 2. If the underlying cryptographic key material represented by the [[handle]] internal
525 // slot of key cannot be accessed, then throw an OperationError.
526 // NOTE: Done in Step 3.
527
528 // Step 3.
529 let result = match format {
530 // If format is "spki":
531 KeyFormat::Spki => {
532 // Step 3.1. If the [[type]] internal slot of key is not "public", then throw an
533 // InvalidAccessError.
534 key.ensure_type(KeyType::Public)?;
535
536 // Step 3.2.
537 // Let data be an instance of the SubjectPublicKeyInfo ASN.1 structure defined in
538 // [RFC5280] with the following properties:
539 // * Set the algorithm field to an AlgorithmIdentifier ASN.1 type with the
540 // following properties:
541 // * Set the algorithm object identifier to the id-X25519 OID defined in
542 // [RFC8410].
543 // * Set the subjectPublicKey field to keyData.
544 let Handle::X25519PublicKey(public_key) = key.handle() else {
545 return Err(Error::Operation(Some(
546 "Key handle does not match with a X25519 public key".into(),
547 )));
548 };
549 let data = SubjectPublicKeyInfoRef {
550 algorithm: AlgorithmIdentifierRef {
551 oid: ObjectIdentifier::new_unwrap(X25519_OID_STRING),
552 parameters: None,
553 },
554 subject_public_key: public_key.as_bytes().try_into().map_err(|_| {
555 Error::Data(Some(
556 "Failed to construct the subjectPublicKey field of SubjectPublicKeyInfo \
557 ASN.1 structure"
558 .into(),
559 ))
560 })?,
561 };
562
563 // Step 3.3. Let result be the result of DER-encoding data.
564 ExportedKey::new_bytes(data.to_der().map_err(|_| {
565 Error::Operation(Some("Could not apply DER encoding on data".into()))
566 })?)
567 },
568 // If format is "pkcs8":
569 KeyFormat::Pkcs8 => {
570 // Step 3.1. If the [[type]] internal slot of key is not "private", then throw an
571 // InvalidAccessError.
572 key.ensure_type(KeyType::Private)?;
573
574 // Step 3.2.
575 // Let data be an instance of the PrivateKeyInfo ASN.1 structure defined in [RFC5208]
576 // with the following properties:
577 // * Set the version field to 0.
578 // * Set the privateKeyAlgorithm field to a PrivateKeyAlgorithmIdentifier ASN.1
579 // type with the following properties:
580 // * Set the algorithm object identifier to the id-X25519 OID defined in
581 // [RFC8410].
582 // * Set the privateKey field to the result of DER-encoding a CurvePrivateKey ASN.1
583 // type, as defined in Section 7 of [RFC8410], that represents the X25519 private
584 // key represented by the [[handle]] internal slot of key
585 let Handle::X25519PrivateKey(private_key) = key.handle() else {
586 return Err(Error::Operation(Some(
587 "Key handle does not match with a X25519 private key".into(),
588 )));
589 };
590 let curve_private_key = OctetStringRef::new(private_key.as_bytes().as_slice())
591 .map_err(|_| {
592 Error::Operation(Some(
593 "Failed to construct CurvePrivateKey ASN.1 structure".into(),
594 ))
595 })?;
596 let encoded_curve_private_key: Zeroizing<Vec<u8>> = curve_private_key
597 .to_der()
598 .map_err(|_| {
599 Error::Operation(Some(
600 "Failed to encode CurvePrivateKey ASN.1 structure in DER format".into(),
601 ))
602 })?
603 .into();
604 let private_key_field =
605 OctetStringRef::new(&encoded_curve_private_key).map_err(|_| {
606 Error::Operation(Some(
607 "Failed to construct privateKey field of PrivateKeyInfo ASN.1 structure"
608 .into(),
609 ))
610 })?;
611 let data = PrivateKeyInfoRef {
612 algorithm: AlgorithmIdentifierRef {
613 oid: ObjectIdentifier::new_unwrap(X25519_OID_STRING),
614 parameters: None,
615 },
616 private_key: private_key_field,
617 public_key: None,
618 };
619
620 // Step 3.3. Let result be the result of DER-encoding data.
621 ExportedKey::new_bytes(data.to_der().map_err(|_| {
622 Error::Operation(Some("Could not apply DER encoding on data".into()))
623 })?)
624 },
625 // If format is "jwk":
626 KeyFormat::Jwk => {
627 // Step 3.1. Let jwk be a new JsonWebKey dictionary.
628 let mut jwk = JsonWebKey::default();
629
630 // Step 3.2. Set the kty attribute of jwk to "OKP".
631 jwk.kty = Some(DOMString::from_static("OKP"));
632
633 // Step 3.3. Set the crv attribute of jwk to "X25519".
634 jwk.crv = Some(DOMString::from_static("X25519"));
635
636 // Step 3.4. Set the x attribute of jwk according to the definition in Section 2 of
637 // [RFC8037].
638 match key.handle() {
639 Handle::X25519PrivateKey(private_key) => {
640 let public_key = PublicKey::from(private_key);
641 jwk.encode_string_field(JwkStringField::X, public_key.as_bytes());
642 },
643 Handle::X25519PublicKey(public_key) => {
644 jwk.encode_string_field(JwkStringField::X, public_key.as_bytes());
645 },
646 _ => return Err(Error::Operation(Some(
647 "Key handle does not match with a X25519 private key or a X25519 public key"
648 .into(),
649 ))),
650 }
651
652 // Step 3.5.
653 // If the [[type]] internal slot of key is "private"
654 // Set the d attribute of jwk according to the definition in Section 2 of
655 // [RFC8037].
656 if key.Type() == KeyType::Private {
657 if let Handle::X25519PrivateKey(private_key) = key.handle() {
658 jwk.encode_string_field(JwkStringField::D, private_key.as_bytes());
659 } else {
660 return Err(Error::Operation(Some(
661 "Key handle does not match with a X25519 private key".into(),
662 )));
663 }
664 }
665
666 // Step 3.6. Set the key_ops attribute of jwk to the usages attribute of key.
667 jwk.set_key_ops(key.usages());
668
669 // Step 3.7. Set the ext attribute of jwk to the [[extractable]] internal slot of key.
670 jwk.ext = Some(key.Extractable());
671
672 // Step 3.8. Let result be jwk.
673 ExportedKey::new_jwk(jwk)
674 },
675 // If format is "raw":
676 KeyFormat::Raw | KeyFormat::Raw_public => {
677 // Step 3.1. If the [[type]] internal slot of key is not "public", then throw an
678 // InvalidAccessError.
679 key.ensure_type(KeyType::Public)?;
680
681 // Step 3.2. Let data be a byte sequence representing the X25519 public key represented
682 // by the [[handle]] internal slot of key.
683 let Handle::X25519PublicKey(public_key) = key.handle() else {
684 return Err(Error::Operation(Some(
685 "Key handle does not match with a X25519 public key".into(),
686 )));
687 };
688 let data = public_key.as_bytes();
689
690 // Step 3.3. Let result be data.
691 ExportedKey::new_bytes(data.to_vec())
692 },
693 // Otherwise:
694 _ => {
695 // throw a NotSupportedError.
696 return Err(Error::NotSupported(Some("Key format not supported".into())));
697 },
698 };
699
700 // Step 4. Return result.
701 Ok(result)
702}
703
704/// <https://wicg.github.io/webcrypto-modern-algos/#SubtleCrypto-method-getPublicKey>
705/// Step 9 - 15, for X25519
706pub(crate) fn get_public_key<D: Equivalence>(
707 cx: &mut JSContext,
708 global: &D::GlobalScope,
709 key: &CryptoKey<D>,
710 algorithm: &KeyAlgorithmAndDerivatives,
711 usages: Vec<KeyUsage>,
712) -> Result<DomRoot<CryptoKey<D>>, Error> {
713 // Step 9. If usages contains an entry which is not supported for a public key by the algorithm
714 // identified by algorithm, then throw a SyntaxError.
715 //
716 // NOTE: See "importKey" operation for supported usages
717 usages.ensure_only_contain_entries_from(&[])?;
718
719 // Step 10. Let publicKey be a new CryptoKey representing the public key corresponding to the
720 // private key represented by the [[handle]] internal slot of key.
721 // Step 11. If an error occurred, then throw a OperationError.
722 // Step 12. Set the [[type]] internal slot of publicKey to "public".
723 // Step 13. Set the [[algorithm]] internal slot of publicKey to algorithm.
724 // Step 14. Set the [[extractable]] internal slot of publicKey to true.
725 // Step 15. Set the [[usages]] internal slot of publicKey to usages.
726 let Handle::X25519PrivateKey(private_key) = key.handle() else {
727 return Err(Error::Operation(Some(
728 "Key handle does not match with a X25519 private key".into(),
729 )));
730 };
731 let public_key = CryptoKey::new(
732 cx,
733 global,
734 KeyType::Public,
735 true,
736 algorithm.clone(),
737 usages,
738 Handle::X25519PublicKey(private_key.into()),
739 );
740
741 Ok(public_key)
742}