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);
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);
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);
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);
67 };
68 let Handle::X25519PublicKey(public_key) = public_key.handle() else {
69 return Err(Error::Operation);
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);
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)
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)?;
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);
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);
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)?
231 .try_into()
232 .map_err(|_| Error::Data)?;
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 = PrivateKeyInfo::from_der(key_data).map_err(|_| Error::Data)?;
268
269 // Step 2.4. If the algorithm object identifier field of the privateKeyAlgorithm
270 // PrivateKeyAlgorithm field of privateKeyInfo is not equal to the id-X25519 object
271 // identifier defined in [RFC8410], then throw a DataError.
272 if private_key_info.algorithm.oid != ObjectIdentifier::new_unwrap(X25519_OID_STRING) {
273 return Err(Error::Data);
274 }
275
276 // Step 2.5. If the parameters field of the privateKeyAlgorithm
277 // PrivateKeyAlgorithmIdentifier field of privateKeyInfo is present, then throw a
278 // DataError.
279 if private_key_info.algorithm.parameters.is_some() {
280 return Err(Error::Data);
281 }
282
283 // Step 2.6. Let curvePrivateKey be the result of performing the parse an ASN.1
284 // structure algorithm, with data as the privateKey field of privateKeyInfo, structure
285 // as the ASN.1 CurvePrivateKey structure specified in Section 7 of [RFC8410], and
286 // exactData set to true.
287 // Step 2.7. If an error occurred while parsing, then throw a DataError.
288 let curve_private_key =
289 OctetStringRef::from_der(private_key_info.private_key).map_err(|_| Error::Data)?;
290 let key_bytes: [u8; PRIVATE_KEY_LENGTH] = curve_private_key
291 .as_bytes()
292 .try_into()
293 .map_err(|_| Error::Data)?;
294 let private_key = StaticSecret::from(key_bytes);
295
296 // Step 2.8. Let key be a new CryptoKey that represents the X25519 private key
297 // identified by curvePrivateKey.
298 // Step 2.9. Set the [[type]] internal slot of key to "private"
299 // Step 2.10. Let algorithm be a new KeyAlgorithm.
300 // Step 2.11. Set the name attribute of algorithm to "X25519".
301 // Step 2.12. Set the [[algorithm]] internal slot of key to algorithm.
302 let algorithm = SubtleKeyAlgorithm {
303 name: ALG_X25519.to_string(),
304 };
305 CryptoKey::new(
306 global,
307 KeyType::Private,
308 extractable,
309 KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm),
310 usages,
311 Handle::X25519PrivateKey(private_key),
312 can_gc,
313 )
314 },
315 // If format is "jwk":
316 KeyFormat::Jwk => {
317 // Step 2.1
318 // If keyData is a JsonWebKey dictionary:
319 // Let jwk equal keyData.
320 // Otherwise:
321 // Throw a DataError.
322 let jwk = JsonWebKey::parse(GlobalScope::get_cx(), key_data)?;
323
324 // Step 2.2. If the d field is present and if usages contains an entry which is not
325 // "deriveKey" or "deriveBits" then throw a SyntaxError.
326 if jwk.d.is_some() &&
327 usages
328 .iter()
329 .any(|usage| !matches!(usage, KeyUsage::DeriveKey | KeyUsage::DeriveBits))
330 {
331 return Err(Error::Syntax(None));
332 }
333
334 // Step 2.2. If the d field is not present and if usages is not empty then throw a
335 // SyntaxError.
336 if jwk.d.is_none() && !usages.is_empty() {
337 return Err(Error::Syntax(None));
338 }
339
340 // Step 2.2. If the kty field of jwk is not "OKP", then throw a DataError.
341 if jwk.kty.as_ref().is_none_or(|kty| kty != "OKP") {
342 return Err(Error::Data);
343 }
344
345 // Step 2.2. If the crv field of jwk is not "X25519", then throw a DataError.
346 if jwk.crv.as_ref().is_none_or(|crv| crv != ALG_X25519) {
347 return Err(Error::Data);
348 }
349
350 // Step 2.2. If usages is non-empty and the use field of jwk is present and is not
351 // equal to "enc" then throw a DataError.
352 if !usages.is_empty() && jwk.use_.as_ref().is_some_and(|use_| use_ != "enc") {
353 return Err(Error::Data);
354 }
355
356 // Step 2.2. If the key_ops field of jwk is present, and is invalid according to the
357 // requirements of JSON Web Key [JWK], or it does not contain all of the specified
358 // usages values, then throw a DataError.
359 jwk.check_key_ops(&usages)?;
360
361 // Step 2.2. If the ext field of jwk is present and has the value false and extractable
362 // is true, then throw a DataError.
363 if jwk.ext.is_some_and(|ext| !ext) && extractable {
364 return Err(Error::Data);
365 }
366
367 // Step 2.9.
368 let (handle, key_type) = match jwk.d {
369 Some(d) => {
370 // Step 2.9.1. If jwk does not meet the requirements of the JWK private key
371 // format described in Section 2 of [RFC8037], then throw a DataError.
372 let x = match jwk.x {
373 Some(x) => {
374 Base64UrlUnpadded::decode_vec(&x.str()).map_err(|_| Error::Data)?
375 },
376 None => return Err(Error::Data),
377 };
378 let d = Base64UrlUnpadded::decode_vec(&d.str()).map_err(|_| Error::Data)?;
379 let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] =
380 x.try_into().map_err(|_| Error::Data)?;
381 let private_key_bytes: [u8; PRIVATE_KEY_LENGTH] =
382 d.try_into().map_err(|_| Error::Data)?;
383 let public_key = PublicKey::from(public_key_bytes);
384 let private_key = StaticSecret::from(private_key_bytes);
385 if PublicKey::from(&private_key) != public_key {
386 return Err(Error::Data);
387 }
388
389 // Step 2.9.1. Let key be a new CryptoKey object that represents the X25519
390 // private key identified by interpreting jwk according to Section 2 of
391 // [RFC8037].
392 // NOTE: CryptoKey is created in Step 2.10 - 2.12.
393 let handle = Handle::X25519PrivateKey(private_key);
394
395 // Step 2.9.1. Set the [[type]] internal slot of Key to "private".
396 let key_type = KeyType::Private;
397
398 (handle, key_type)
399 },
400 // Otherwise:
401 None => {
402 // Step 2.9.1. If jwk does not meet the requirements of the JWK public key
403 // format described in Section 2 of [RFC8037], then throw a DataError.
404 let x = match jwk.x {
405 Some(x) => {
406 Base64UrlUnpadded::decode_vec(&x.str()).map_err(|_| Error::Data)?
407 },
408 None => return Err(Error::Data),
409 };
410 let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] =
411 x.try_into().map_err(|_| Error::Data)?;
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 => {
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);
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)?;
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: throw a NotSupportedError. (Unreachable)
477 };
478
479 // Step 3. Return key
480 Ok(key)
481}
482
483/// <https://w3c.github.io/webcrypto/#x25519-operations-export-key>
484pub(crate) fn export_key(format: KeyFormat, key: &CryptoKey) -> Result<ExportedKey, Error> {
485 // Step 1. Let key be the CryptoKey to be exported.
486
487 // Step 2. If the underlying cryptographic key material represented by the [[handle]] internal
488 // slot of key cannot be accessed, then throw an OperationError.
489 // NOTE: Done in Step 3.
490
491 // Step 3.
492 let result = match format {
493 // If format is "spki":
494 KeyFormat::Spki => {
495 // Step 3.1. If the [[type]] internal slot of key is not "public", then throw an
496 // InvalidAccessError.
497 if key.Type() != KeyType::Public {
498 return Err(Error::InvalidAccess);
499 }
500
501 // Step 3.2.
502 // Let data be an instance of the SubjectPublicKeyInfo ASN.1 structure defined in
503 // [RFC5280] with the following properties:
504 // * Set the algorithm field to an AlgorithmIdentifier ASN.1 type with the
505 // following properties:
506 // * Set the algorithm object identifier to the id-X25519 OID defined in
507 // [RFC8410].
508 // * Set the subjectPublicKey field to keyData.
509 let Handle::X25519PublicKey(public_key) = key.handle() else {
510 return Err(Error::Operation);
511 };
512 let data = SubjectPublicKeyInfo::<BitStringRef, _> {
513 algorithm: AlgorithmIdentifier {
514 oid: ObjectIdentifier::new_unwrap(X25519_OID_STRING),
515 parameters: None,
516 },
517 subject_public_key: BitStringRef::from_bytes(public_key.as_bytes())
518 .map_err(|_| Error::Data)?,
519 };
520
521 // Step 3.3. Let result be the result of DER-encoding data.
522 ExportedKey::Raw(data.to_der().map_err(|_| Error::Operation)?)
523 },
524 // If format is "pkcs8":
525 KeyFormat::Pkcs8 => {
526 // Step 3.1. If the [[type]] internal slot of key is not "private", then throw an
527 // InvalidAccessError.
528 if key.Type() != KeyType::Private {
529 return Err(Error::InvalidAccess);
530 }
531
532 // Step 3.2.
533 // Let data be an instance of the PrivateKeyInfo ASN.1 structure defined in [RFC5208]
534 // with the following properties:
535 // * Set the version field to 0.
536 // * Set the privateKeyAlgorithm field to a PrivateKeyAlgorithmIdentifier ASN.1
537 // type with the following properties:
538 // * Set the algorithm object identifier to the id-X25519 OID defined in
539 // [RFC8410].
540 // * Set the privateKey field to the result of DER-encoding a CurvePrivateKey ASN.1
541 // type, as defined in Section 7 of [RFC8410], that represents the X25519 private
542 // key represented by the [[handle]] internal slot of key
543 let Handle::X25519PrivateKey(private_key) = key.handle() else {
544 return Err(Error::Operation);
545 };
546 let curve_private_key =
547 OctetString::new(private_key.as_bytes()).map_err(|_| Error::Data)?;
548 let data = PrivateKeyInfo {
549 algorithm: AlgorithmIdentifier {
550 oid: ObjectIdentifier::new_unwrap(X25519_OID_STRING),
551 parameters: None,
552 },
553 private_key: &curve_private_key.to_der().map_err(|_| Error::Data)?,
554 public_key: None,
555 };
556
557 // Step 3.3. Let result be the result of DER-encoding data.
558 ExportedKey::Raw(data.to_der().map_err(|_| Error::Operation)?)
559 },
560 // If format is "jwk":
561 KeyFormat::Jwk => {
562 // Step 3.1. Let jwk be a new JsonWebKey dictionary.
563 // Step 3.2. Set the kty attribute of jwk to "OKP".
564 // Step 3.3. Set the crv attribute of jwk to "X25519".
565 let mut jwk = JsonWebKey {
566 kty: Some(DOMString::from("OKP")),
567 crv: Some(DOMString::from(ALG_X25519)),
568 ..Default::default()
569 };
570
571 // Step 3.4. Set the x attribute of jwk according to the definition in Section 2 of
572 // [RFC8037].
573 jwk.x = match key.handle() {
574 Handle::X25519PrivateKey(private_key) => {
575 let public_key = PublicKey::from(private_key);
576 Some(Base64UrlUnpadded::encode_string(public_key.as_bytes()).into())
577 },
578 Handle::X25519PublicKey(public_key) => {
579 Some(Base64UrlUnpadded::encode_string(public_key.as_bytes()).into())
580 },
581 _ => return Err(Error::Operation),
582 };
583
584 // Step 3.5.
585 // If the [[type]] internal slot of key is "private"
586 // Set the d attribute of jwk according to the definition in Section 2 of
587 // [RFC8037].
588 if key.Type() == KeyType::Private {
589 if let Handle::X25519PrivateKey(private_key) = key.handle() {
590 jwk.d = Some(Base64UrlUnpadded::encode_string(private_key.as_bytes()).into());
591 } else {
592 return Err(Error::Operation);
593 }
594 }
595
596 // Step 3.6. Set the key_ops attribute of jwk to the usages attribute of key.
597 jwk.key_ops = Some(
598 key.usages()
599 .iter()
600 .map(|usage| DOMString::from(usage.as_str()))
601 .collect::<Vec<DOMString>>(),
602 );
603
604 // Step 3.7. Set the ext attribute of jwk to the [[extractable]] internal slot of key.
605 jwk.ext = Some(key.Extractable());
606
607 // Step 3.8. Let result be jwk.
608 ExportedKey::Jwk(Box::new(jwk))
609 },
610 // If format is "raw":
611 KeyFormat::Raw => {
612 // Step 3.1. If the [[type]] internal slot of key is not "public", then throw an
613 // InvalidAccessError.
614 if key.Type() != KeyType::Public {
615 return Err(Error::InvalidAccess);
616 }
617
618 // Step 3.2. Let data be a byte sequence representing the X25519 public key represented
619 // by the [[handle]] internal slot of key.
620 let Handle::X25519PublicKey(public_key) = key.handle() else {
621 return Err(Error::Operation);
622 };
623 let data = public_key.as_bytes();
624
625 // Step 3.3. Let result be data.
626 ExportedKey::Raw(data.to_vec())
627 },
628 };
629
630 // Step 4. Return result.
631 Ok(result)
632}