script/dom/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 base64ct::{Base64UrlUnpadded, Encoding};
6use pkcs8::PrivateKeyInfo;
7use pkcs8::der::asn1::{BitStringRef, OctetString, OctetStringRef};
8use pkcs8::der::{AnyRef, Decode, Encode};
9use pkcs8::rand_core::OsRng;
10use pkcs8::spki::{AlgorithmIdentifier, ObjectIdentifier, SubjectPublicKeyInfo};
11use x25519_dalek::{PublicKey, StaticSecret};
12
13use crate::dom::bindings::codegen::Bindings::CryptoKeyBinding::{
14 CryptoKeyMethods, CryptoKeyPair, KeyType, KeyUsage,
15};
16use crate::dom::bindings::codegen::Bindings::SubtleCryptoBinding::{JsonWebKey, KeyFormat};
17use crate::dom::bindings::error::Error;
18use crate::dom::bindings::root::DomRoot;
19use crate::dom::bindings::str::DOMString;
20use crate::dom::cryptokey::{CryptoKey, Handle};
21use crate::dom::globalscope::GlobalScope;
22use crate::dom::subtlecrypto::{
23 ALG_X25519, ExportedKey, JsonWebKeyExt, KeyAlgorithmAndDerivatives, SubtleEcdhKeyDeriveParams,
24 SubtleKeyAlgorithm,
25};
26use crate::script_runtime::CanGc;
27
28/// `id-X25519` object identifier defined in [RFC8410]
29const X25519_OID_STRING: &str = "1.3.101.110";
30
31const PRIVATE_KEY_LENGTH: usize = 32;
32const PUBLIC_KEY_LENGTH: usize = 32;
33
34/// <https://w3c.github.io/webcrypto/#x25519-operations-derive-bits>
35pub(crate) fn derive_bits(
36 normalized_algorithm: &SubtleEcdhKeyDeriveParams,
37 key: &CryptoKey,
38 length: Option<u32>,
39) -> Result<Vec<u8>, Error> {
40 // Step 1. If the [[type]] internal slot of key is not "private", then throw an
41 // InvalidAccessError.
42 if key.Type() != KeyType::Private {
43 return Err(Error::InvalidAccess(None));
44 }
45
46 // Step 2. Let publicKey be the public member of normalizedAlgorithm.
47 let public_key = normalized_algorithm.public.root();
48
49 // Step 3. If the [[type]] internal slot of publicKey is not "public", then throw an
50 // InvalidAccessError.
51 if public_key.Type() != KeyType::Public {
52 return Err(Error::InvalidAccess(None));
53 }
54
55 // Step 4. If the name attribute of the [[algorithm]] internal slot of publicKey is not equal
56 // to the name property of the [[algorithm]] internal slot of key, then throw an
57 // InvalidAccessError.
58 if public_key.algorithm().name() != key.algorithm().name() {
59 return Err(Error::InvalidAccess(None));
60 }
61
62 // Step 5. Let secret be the result of performing the X25519 function specified in [RFC7748]
63 // Section 5 with key as the X25519 private key k and the X25519 public key represented by the
64 // [[handle]] internal slot of publicKey as the X25519 public key u.
65 let Handle::X25519PrivateKey(private_key) = key.handle() else {
66 return Err(Error::Operation(None));
67 };
68 let Handle::X25519PublicKey(public_key) = public_key.handle() else {
69 return Err(Error::Operation(None));
70 };
71 let shared_key = private_key.diffie_hellman(public_key);
72 let secret = shared_key.as_bytes();
73
74 // Step 6. If secret is the all-zero value, then throw a OperationError. This check must be
75 // performed in constant-time, as per [RFC7748] Section 6.1.
76 let mut is_all_zero = true;
77 for byte in secret {
78 is_all_zero &= *byte == 0;
79 }
80 if is_all_zero {
81 return Err(Error::Operation(None));
82 }
83
84 // Step 7.
85 // If length is null:
86 // Return secret
87 // Otherwise:
88 // If the length of secret in bits is less than length:
89 // throw an OperationError.
90 // Otherwise:
91 // Return a byte sequence containing the first length bits of secret.
92 match length {
93 None => Ok(secret.to_vec()),
94 Some(length) => {
95 if secret.len() * 8 < length as usize {
96 Err(Error::Operation(None))
97 } else {
98 let mut secret = secret[..length.div_ceil(8) as usize].to_vec();
99 if length % 8 != 0 {
100 // Clean excess bits in last byte of secret.
101 let mask = u8::MAX << (8 - length % 8);
102 if let Some(last_byte) = secret.last_mut() {
103 *last_byte &= mask;
104 }
105 }
106 Ok(secret)
107 }
108 },
109 }
110}
111
112/// <https://w3c.github.io/webcrypto/#x25519-operations-generate-key>
113pub(crate) fn generate_key(
114 global: &GlobalScope,
115 extractable: bool,
116 usages: Vec<KeyUsage>,
117 can_gc: CanGc,
118) -> Result<CryptoKeyPair, Error> {
119 // Step 1. If usages contains an entry which is not "deriveKey" or "deriveBits" then throw a
120 // SyntaxError.
121 if usages
122 .iter()
123 .any(|usage| !matches!(usage, KeyUsage::DeriveKey | KeyUsage::DeriveBits))
124 {
125 return Err(Error::Syntax(None));
126 }
127
128 // Step 2. Generate an X25519 key pair, with the private key being 32 random bytes, and the
129 // public key being X25519(a, 9), as defined in [RFC7748], section 6.1.
130 let private_key = StaticSecret::random_from_rng(OsRng);
131 let public_key = PublicKey::from(&private_key);
132
133 // Step 3. Let algorithm be a new KeyAlgorithm object.
134 // Step 4. Set the name attribute of algorithm to "X25519".
135 let algorithm = SubtleKeyAlgorithm {
136 name: ALG_X25519.to_string(),
137 };
138
139 // Step 5. Let publicKey be a new CryptoKey representing the public key of the generated key pair.
140 // Step 6. Set the [[type]] internal slot of publicKey to "public"
141 // Step 7. Set the [[algorithm]] internal slot of publicKey to algorithm.
142 // Step 8. Set the [[extractable]] internal slot of publicKey to true.
143 // Step 9. Set the [[usages]] internal slot of publicKey to be the empty list.
144 let public_key = CryptoKey::new(
145 global,
146 KeyType::Public,
147 true,
148 KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm.clone()),
149 Vec::new(),
150 Handle::X25519PublicKey(public_key),
151 can_gc,
152 );
153
154 // Step 10. Let privateKey be a new CryptoKey representing the private key of the generated key pair.
155 // Step 11. Set the [[type]] internal slot of privateKey to "private"
156 // Step 12. Set the [[algorithm]] internal slot of privateKey to algorithm.
157 // Step 13. Set the [[extractable]] internal slot of privateKey to extractable.
158 // Step 14. Set the [[usages]] internal slot of privateKey to be the usage intersection of
159 // usages and [ "deriveKey", "deriveBits" ].
160 let private_key = CryptoKey::new(
161 global,
162 KeyType::Private,
163 extractable,
164 KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm),
165 usages
166 .iter()
167 .filter(|usage| matches!(usage, KeyUsage::DeriveKey | KeyUsage::DeriveBits))
168 .cloned()
169 .collect(),
170 Handle::X25519PrivateKey(private_key),
171 can_gc,
172 );
173
174 // Step 15. Let result be a new CryptoKeyPair dictionary.
175 // Step 16. Set the publicKey attribute of result to be publicKey.
176 // Step 17. Set the privateKey attribute of result to be privateKey.
177 let result = CryptoKeyPair {
178 publicKey: Some(public_key),
179 privateKey: Some(private_key),
180 };
181
182 // Step 18. Return result.
183 Ok(result)
184}
185
186/// <https://w3c.github.io/webcrypto/#x25519-operations-import-key>
187pub(crate) fn import_key(
188 global: &GlobalScope,
189 format: KeyFormat,
190 key_data: &[u8],
191 extractable: bool,
192 usages: Vec<KeyUsage>,
193 can_gc: CanGc,
194) -> Result<DomRoot<CryptoKey>, Error> {
195 // Step 1. Let keyData be the key data to be imported.
196
197 // Step 2.
198 let key = match format {
199 // If format is "spki":
200 KeyFormat::Spki => {
201 // Step 2.1. If usages is not empty then throw a SyntaxError.
202 if !usages.is_empty() {
203 return Err(Error::Syntax(None));
204 }
205
206 // Step 2.2. Let spki be the result of running the parse a subjectPublicKeyInfo
207 // algorithm over keyData.
208 // Step 2.3. If an error occurred while parsing, then throw a DataError.
209 let spki = SubjectPublicKeyInfo::<AnyRef, BitStringRef>::from_der(key_data)
210 .map_err(|_| Error::Data(None))?;
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: ALG_X25519.to_string(),
242 };
243 CryptoKey::new(
244 global,
245 KeyType::Public,
246 extractable,
247 KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm),
248 usages,
249 Handle::X25519PublicKey(public_key),
250 can_gc,
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 =
268 PrivateKeyInfo::from_der(key_data).map_err(|_| Error::Data(None))?;
269
270 // Step 2.4. If the algorithm object identifier field of the privateKeyAlgorithm
271 // PrivateKeyAlgorithm field of privateKeyInfo is not equal to the id-X25519 object
272 // identifier defined in [RFC8410], then throw a DataError.
273 if private_key_info.algorithm.oid != ObjectIdentifier::new_unwrap(X25519_OID_STRING) {
274 return Err(Error::Data(None));
275 }
276
277 // Step 2.5. If the parameters field of the privateKeyAlgorithm
278 // PrivateKeyAlgorithmIdentifier field of privateKeyInfo is present, then throw a
279 // DataError.
280 if private_key_info.algorithm.parameters.is_some() {
281 return Err(Error::Data(None));
282 }
283
284 // Step 2.6. Let curvePrivateKey be the result of performing the parse an ASN.1
285 // structure algorithm, with data as the privateKey field of privateKeyInfo, structure
286 // as the ASN.1 CurvePrivateKey structure specified in Section 7 of [RFC8410], and
287 // exactData set to true.
288 // Step 2.7. If an error occurred while parsing, then throw a DataError.
289 let curve_private_key = OctetStringRef::from_der(private_key_info.private_key)
290 .map_err(|_| Error::Data(None))?;
291 let key_bytes: [u8; PRIVATE_KEY_LENGTH] = curve_private_key
292 .as_bytes()
293 .try_into()
294 .map_err(|_| Error::Data(None))?;
295 let private_key = StaticSecret::from(key_bytes);
296
297 // Step 2.8. Let key be a new CryptoKey that represents the X25519 private key
298 // identified by curvePrivateKey.
299 // Step 2.9. Set the [[type]] internal slot of key to "private"
300 // Step 2.10. Let algorithm be a new KeyAlgorithm.
301 // Step 2.11. Set the name attribute of algorithm to "X25519".
302 // Step 2.12. Set the [[algorithm]] internal slot of key to algorithm.
303 let algorithm = SubtleKeyAlgorithm {
304 name: ALG_X25519.to_string(),
305 };
306 CryptoKey::new(
307 global,
308 KeyType::Private,
309 extractable,
310 KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm),
311 usages,
312 Handle::X25519PrivateKey(private_key),
313 can_gc,
314 )
315 },
316 // If format is "jwk":
317 KeyFormat::Jwk => {
318 // Step 2.1
319 // If keyData is a JsonWebKey dictionary:
320 // Let jwk equal keyData.
321 // Otherwise:
322 // Throw a DataError.
323 let jwk = JsonWebKey::parse(GlobalScope::get_cx(), key_data)?;
324
325 // Step 2.2. If the d field is present and if usages contains an entry which is not
326 // "deriveKey" or "deriveBits" then throw a SyntaxError.
327 if jwk.d.is_some() &&
328 usages
329 .iter()
330 .any(|usage| !matches!(usage, KeyUsage::DeriveKey | KeyUsage::DeriveBits))
331 {
332 return Err(Error::Syntax(None));
333 }
334
335 // Step 2.2. If the d field is not present and if usages is not empty then throw a
336 // SyntaxError.
337 if jwk.d.is_none() && !usages.is_empty() {
338 return Err(Error::Syntax(None));
339 }
340
341 // Step 2.2. If the kty field of jwk is not "OKP", then throw a DataError.
342 if jwk.kty.as_ref().is_none_or(|kty| kty != "OKP") {
343 return Err(Error::Data(None));
344 }
345
346 // Step 2.2. If the crv field of jwk is not "X25519", then throw a DataError.
347 if jwk.crv.as_ref().is_none_or(|crv| crv != ALG_X25519) {
348 return Err(Error::Data(None));
349 }
350
351 // Step 2.2. If usages is non-empty and the use field of jwk is present and is not
352 // equal to "enc" then throw a DataError.
353 if !usages.is_empty() && jwk.use_.as_ref().is_some_and(|use_| use_ != "enc") {
354 return Err(Error::Data(None));
355 }
356
357 // Step 2.2. If the key_ops field of jwk is present, and is invalid according to the
358 // requirements of JSON Web Key [JWK], or it does not contain all of the specified
359 // usages values, then throw a DataError.
360 jwk.check_key_ops(&usages)?;
361
362 // Step 2.2. If the ext field of jwk is present and has the value false and extractable
363 // is true, then throw a DataError.
364 if jwk.ext.is_some_and(|ext| !ext) && extractable {
365 return Err(Error::Data(None));
366 }
367
368 // Step 2.9.
369 let (handle, key_type) = match jwk.d {
370 Some(d) => {
371 // Step 2.9.1. If jwk does not meet the requirements of the JWK private key
372 // format described in Section 2 of [RFC8037], then throw a DataError.
373 let x = match jwk.x {
374 Some(x) => Base64UrlUnpadded::decode_vec(&x.str())
375 .map_err(|_| Error::Data(None))?,
376 None => return Err(Error::Data(None)),
377 };
378 let d =
379 Base64UrlUnpadded::decode_vec(&d.str()).map_err(|_| Error::Data(None))?;
380 let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] =
381 x.try_into().map_err(|_| Error::Data(None))?;
382 let private_key_bytes: [u8; PRIVATE_KEY_LENGTH] =
383 d.try_into().map_err(|_| Error::Data(None))?;
384 let public_key = PublicKey::from(public_key_bytes);
385 let private_key = StaticSecret::from(private_key_bytes);
386 if PublicKey::from(&private_key) != public_key {
387 return Err(Error::Data(None));
388 }
389
390 // Step 2.9.1. Let key be a new CryptoKey object that represents the X25519
391 // private key identified by interpreting jwk according to Section 2 of
392 // [RFC8037].
393 // NOTE: CryptoKey is created in Step 2.10 - 2.12.
394 let handle = Handle::X25519PrivateKey(private_key);
395
396 // Step 2.9.1. Set the [[type]] internal slot of Key to "private".
397 let key_type = KeyType::Private;
398
399 (handle, key_type)
400 },
401 // Otherwise:
402 None => {
403 // Step 2.9.1. If jwk does not meet the requirements of the JWK public key
404 // format described in Section 2 of [RFC8037], then throw a DataError.
405 let x = match jwk.x {
406 Some(x) => Base64UrlUnpadded::decode_vec(&x.str())
407 .map_err(|_| Error::Data(None))?,
408 None => return Err(Error::Data(None)),
409 };
410 let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] =
411 x.try_into().map_err(|_| Error::Data(None))?;
412 let public_key = PublicKey::from(public_key_bytes);
413
414 // Step 2.9.1. Let key be a new CryptoKey object that represents the X25519
415 // public key identified by interpreting jwk according to Section 2 of
416 // [RFC8037].
417 // NOTE: CryptoKey is created in Step 2.10 - 2.12.
418 let handle = Handle::X25519PublicKey(public_key);
419
420 // Step 2.9.1. Set the [[type]] internal slot of Key to "public".
421 let key_type = KeyType::Public;
422
423 (handle, key_type)
424 },
425 };
426
427 // Step 2.10. Let algorithm be a new instance of a KeyAlgorithm object.
428 // Step 2.11. Set the name attribute of algorithm to "X25519".
429 // Step 2.12. Set the [[algorithm]] internal slot of key to algorithm.
430 let algorithm = SubtleKeyAlgorithm {
431 name: ALG_X25519.to_string(),
432 };
433 CryptoKey::new(
434 global,
435 key_type,
436 extractable,
437 KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm),
438 usages,
439 handle,
440 can_gc,
441 )
442 },
443 // If format is "raw":
444 KeyFormat::Raw | KeyFormat::Raw_public => {
445 // Step 2.1. If usages is not empty then throw a SyntaxError.
446 if !usages.is_empty() {
447 return Err(Error::Syntax(None));
448 }
449
450 // Step 2.2. If the length in bits of keyData is not 256 then throw a DataError.
451 if key_data.len() != 32 {
452 return Err(Error::Data(None));
453 }
454
455 // Step 2.3. Let algorithm be a new KeyAlgorithm object.
456 // Step 2.4. Set the name attribute of algorithm to "X25519".
457 // Step 2.5. Let key be a new CryptoKey representing the key data provided in keyData.
458 // Step 2.6. Set the [[type]] internal slot of key to "public"
459 // Step 2.7. Set the [[algorithm]] internal slot of key to algorithm.
460 let key_bytes: [u8; PUBLIC_KEY_LENGTH] =
461 key_data.try_into().map_err(|_| Error::Data(None))?;
462 let public_key = PublicKey::from(key_bytes);
463 let algorithm = SubtleKeyAlgorithm {
464 name: ALG_X25519.to_string(),
465 };
466 CryptoKey::new(
467 global,
468 KeyType::Public,
469 extractable,
470 KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm),
471 usages,
472 Handle::X25519PublicKey(public_key),
473 can_gc,
474 )
475 },
476 // Otherwise:
477 _ => {
478 // throw a NotSupportedError.
479 return Err(Error::NotSupported(None));
480 },
481 };
482
483 // Step 3. Return key
484 Ok(key)
485}
486
487/// <https://w3c.github.io/webcrypto/#x25519-operations-export-key>
488pub(crate) fn export_key(format: KeyFormat, key: &CryptoKey) -> Result<ExportedKey, Error> {
489 // Step 1. Let key be the CryptoKey to be exported.
490
491 // Step 2. If the underlying cryptographic key material represented by the [[handle]] internal
492 // slot of key cannot be accessed, then throw an OperationError.
493 // NOTE: Done in Step 3.
494
495 // Step 3.
496 let result = match format {
497 // If format is "spki":
498 KeyFormat::Spki => {
499 // Step 3.1. If the [[type]] internal slot of key is not "public", then throw an
500 // InvalidAccessError.
501 if key.Type() != KeyType::Public {
502 return Err(Error::InvalidAccess(None));
503 }
504
505 // Step 3.2.
506 // Let data be an instance of the SubjectPublicKeyInfo ASN.1 structure defined in
507 // [RFC5280] with the following properties:
508 // * Set the algorithm field to an AlgorithmIdentifier ASN.1 type with the
509 // following properties:
510 // * Set the algorithm object identifier to the id-X25519 OID defined in
511 // [RFC8410].
512 // * Set the subjectPublicKey field to keyData.
513 let Handle::X25519PublicKey(public_key) = key.handle() else {
514 return Err(Error::Operation(None));
515 };
516 let data = SubjectPublicKeyInfo::<BitStringRef, _> {
517 algorithm: AlgorithmIdentifier {
518 oid: ObjectIdentifier::new_unwrap(X25519_OID_STRING),
519 parameters: None,
520 },
521 subject_public_key: BitStringRef::from_bytes(public_key.as_bytes())
522 .map_err(|_| Error::Data(None))?,
523 };
524
525 // Step 3.3. Let result be the result of DER-encoding data.
526 ExportedKey::Bytes(data.to_der().map_err(|_| Error::Operation(None))?)
527 },
528 // If format is "pkcs8":
529 KeyFormat::Pkcs8 => {
530 // Step 3.1. If the [[type]] internal slot of key is not "private", then throw an
531 // InvalidAccessError.
532 if key.Type() != KeyType::Private {
533 return Err(Error::InvalidAccess(None));
534 }
535
536 // Step 3.2.
537 // Let data be an instance of the PrivateKeyInfo ASN.1 structure defined in [RFC5208]
538 // with the following properties:
539 // * Set the version field to 0.
540 // * Set the privateKeyAlgorithm field to a PrivateKeyAlgorithmIdentifier ASN.1
541 // type with the following properties:
542 // * Set the algorithm object identifier to the id-X25519 OID defined in
543 // [RFC8410].
544 // * Set the privateKey field to the result of DER-encoding a CurvePrivateKey ASN.1
545 // type, as defined in Section 7 of [RFC8410], that represents the X25519 private
546 // key represented by the [[handle]] internal slot of key
547 let Handle::X25519PrivateKey(private_key) = key.handle() else {
548 return Err(Error::Operation(None));
549 };
550 let curve_private_key =
551 OctetString::new(private_key.as_bytes()).map_err(|_| Error::Data(None))?;
552 let data = PrivateKeyInfo {
553 algorithm: AlgorithmIdentifier {
554 oid: ObjectIdentifier::new_unwrap(X25519_OID_STRING),
555 parameters: None,
556 },
557 private_key: &curve_private_key.to_der().map_err(|_| Error::Data(None))?,
558 public_key: None,
559 };
560
561 // Step 3.3. Let result be the result of DER-encoding data.
562 ExportedKey::Bytes(data.to_der().map_err(|_| Error::Operation(None))?)
563 },
564 // If format is "jwk":
565 KeyFormat::Jwk => {
566 // Step 3.1. Let jwk be a new JsonWebKey dictionary.
567 // Step 3.2. Set the kty attribute of jwk to "OKP".
568 // Step 3.3. Set the crv attribute of jwk to "X25519".
569 let mut jwk = JsonWebKey {
570 kty: Some(DOMString::from("OKP")),
571 crv: Some(DOMString::from(ALG_X25519)),
572 ..Default::default()
573 };
574
575 // Step 3.4. Set the x attribute of jwk according to the definition in Section 2 of
576 // [RFC8037].
577 jwk.x = match key.handle() {
578 Handle::X25519PrivateKey(private_key) => {
579 let public_key = PublicKey::from(private_key);
580 Some(Base64UrlUnpadded::encode_string(public_key.as_bytes()).into())
581 },
582 Handle::X25519PublicKey(public_key) => {
583 Some(Base64UrlUnpadded::encode_string(public_key.as_bytes()).into())
584 },
585 _ => return Err(Error::Operation(None)),
586 };
587
588 // Step 3.5.
589 // If the [[type]] internal slot of key is "private"
590 // Set the d attribute of jwk according to the definition in Section 2 of
591 // [RFC8037].
592 if key.Type() == KeyType::Private {
593 if let Handle::X25519PrivateKey(private_key) = key.handle() {
594 jwk.d = Some(Base64UrlUnpadded::encode_string(private_key.as_bytes()).into());
595 } else {
596 return Err(Error::Operation(None));
597 }
598 }
599
600 // Step 3.6. Set the key_ops attribute of jwk to the usages attribute of key.
601 jwk.key_ops = Some(
602 key.usages()
603 .iter()
604 .map(|usage| DOMString::from(usage.as_str()))
605 .collect::<Vec<DOMString>>(),
606 );
607
608 // Step 3.7. Set the ext attribute of jwk to the [[extractable]] internal slot of key.
609 jwk.ext = Some(key.Extractable());
610
611 // Step 3.8. Let result be jwk.
612 ExportedKey::Jwk(Box::new(jwk))
613 },
614 // If format is "raw":
615 KeyFormat::Raw | KeyFormat::Raw_public => {
616 // Step 3.1. If the [[type]] internal slot of key is not "public", then throw an
617 // InvalidAccessError.
618 if key.Type() != KeyType::Public {
619 return Err(Error::InvalidAccess(None));
620 }
621
622 // Step 3.2. Let data be a byte sequence representing the X25519 public key represented
623 // by the [[handle]] internal slot of key.
624 let Handle::X25519PublicKey(public_key) = key.handle() else {
625 return Err(Error::Operation(None));
626 };
627 let data = public_key.as_bytes();
628
629 // Step 3.3. Let result be data.
630 ExportedKey::Bytes(data.to_vec())
631 },
632 // Otherwise:
633 _ => {
634 // throw a NotSupportedError.
635 return Err(Error::NotSupported(None));
636 },
637 };
638
639 // Step 4. Return result.
640 Ok(result)
641}