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