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