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