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 pkcs8::PrivateKeyInfo;
6use pkcs8::der::asn1::{BitStringRef, OctetString, OctetStringRef};
7use pkcs8::der::{AnyRef, Decode, Encode};
8use pkcs8::rand_core::OsRng;
9use pkcs8::spki::{AlgorithmIdentifier, ObjectIdentifier, SubjectPublicKeyInfo};
10use x25519_dalek::{PublicKey, StaticSecret};
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 ALG_X25519, ExportedKey, JsonWebKeyExt, JwkStringField, KeyAlgorithmAndDerivatives,
23 SubtleEcdhKeyDeriveParams, SubtleKeyAlgorithm,
24};
25use crate::script_runtime::CanGc;
26
27/// `id-X25519` object identifier defined in [RFC8410]
28const X25519_OID_STRING: &str = "1.3.101.110";
29
30const PRIVATE_KEY_LENGTH: usize = 32;
31const PUBLIC_KEY_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 shared_key = private_key.diffie_hellman(public_key);
71 let secret = shared_key.as_bytes();
72
73 // Step 6. If secret is the all-zero value, then throw a OperationError. This check must be
74 // performed in constant-time, as per [RFC7748] Section 6.1.
75 let mut is_all_zero = true;
76 for byte in secret {
77 is_all_zero &= *byte == 0;
78 }
79 if is_all_zero {
80 return Err(Error::Operation(None));
81 }
82
83 // Step 7.
84 // If length is null:
85 // Return secret
86 // Otherwise:
87 // If the length of secret in bits is less than length:
88 // throw an OperationError.
89 // Otherwise:
90 // Return a byte sequence containing the first length bits of secret.
91 match length {
92 None => Ok(secret.to_vec()),
93 Some(length) => {
94 if secret.len() * 8 < length as usize {
95 Err(Error::Operation(None))
96 } else {
97 let mut secret = secret[..length.div_ceil(8) as usize].to_vec();
98 if length % 8 != 0 {
99 // Clean excess bits in last byte of secret.
100 let mask = u8::MAX << (8 - length % 8);
101 if let Some(last_byte) = secret.last_mut() {
102 *last_byte &= mask;
103 }
104 }
105 Ok(secret)
106 }
107 },
108 }
109}
110
111/// <https://w3c.github.io/webcrypto/#x25519-operations-generate-key>
112pub(crate) fn generate_key(
113 global: &GlobalScope,
114 extractable: bool,
115 usages: Vec<KeyUsage>,
116 can_gc: CanGc,
117) -> Result<CryptoKeyPair, Error> {
118 // Step 1. If usages contains an entry which is not "deriveKey" or "deriveBits" then throw a
119 // SyntaxError.
120 if usages
121 .iter()
122 .any(|usage| !matches!(usage, KeyUsage::DeriveKey | KeyUsage::DeriveBits))
123 {
124 return Err(Error::Syntax(None));
125 }
126
127 // Step 2. Generate an X25519 key pair, with the private key being 32 random bytes, and the
128 // public key being X25519(a, 9), as defined in [RFC7748], section 6.1.
129 let private_key = StaticSecret::random_from_rng(OsRng);
130 let public_key = PublicKey::from(&private_key);
131
132 // Step 3. Let algorithm be a new KeyAlgorithm object.
133 // Step 4. Set the name attribute of algorithm to "X25519".
134 let algorithm = SubtleKeyAlgorithm {
135 name: ALG_X25519.to_string(),
136 };
137
138 // Step 5. Let publicKey be a new CryptoKey representing the public key of the generated key pair.
139 // Step 6. Set the [[type]] internal slot of publicKey to "public"
140 // Step 7. Set the [[algorithm]] internal slot of publicKey to algorithm.
141 // Step 8. Set the [[extractable]] internal slot of publicKey to true.
142 // Step 9. Set the [[usages]] internal slot of publicKey to be the empty list.
143 let public_key = CryptoKey::new(
144 global,
145 KeyType::Public,
146 true,
147 KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm.clone()),
148 Vec::new(),
149 Handle::X25519PublicKey(public_key),
150 can_gc,
151 );
152
153 // Step 10. Let privateKey be a new CryptoKey representing the private key of the generated key pair.
154 // Step 11. Set the [[type]] internal slot of privateKey to "private"
155 // Step 12. Set the [[algorithm]] internal slot of privateKey to algorithm.
156 // Step 13. Set the [[extractable]] internal slot of privateKey to extractable.
157 // Step 14. Set the [[usages]] internal slot of privateKey to be the usage intersection of
158 // usages and [ "deriveKey", "deriveBits" ].
159 let private_key = CryptoKey::new(
160 global,
161 KeyType::Private,
162 extractable,
163 KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm),
164 usages
165 .iter()
166 .filter(|usage| matches!(usage, KeyUsage::DeriveKey | KeyUsage::DeriveBits))
167 .cloned()
168 .collect(),
169 Handle::X25519PrivateKey(private_key),
170 can_gc,
171 );
172
173 // Step 15. Let result be a new CryptoKeyPair dictionary.
174 // Step 16. Set the publicKey attribute of result to be publicKey.
175 // Step 17. Set the privateKey attribute of result to be privateKey.
176 let result = CryptoKeyPair {
177 publicKey: Some(public_key),
178 privateKey: Some(private_key),
179 };
180
181 // Step 18. Return result.
182 Ok(result)
183}
184
185/// <https://w3c.github.io/webcrypto/#x25519-operations-import-key>
186pub(crate) fn import_key(
187 global: &GlobalScope,
188 format: KeyFormat,
189 key_data: &[u8],
190 extractable: bool,
191 usages: Vec<KeyUsage>,
192 can_gc: CanGc,
193) -> Result<DomRoot<CryptoKey>, Error> {
194 // Step 1. Let keyData be the key data to be imported.
195
196 // Step 2.
197 let key = match format {
198 // If format is "spki":
199 KeyFormat::Spki => {
200 // Step 2.1. If usages is not empty then throw a SyntaxError.
201 if !usages.is_empty() {
202 return Err(Error::Syntax(None));
203 }
204
205 // Step 2.2. Let spki be the result of running the parse a subjectPublicKeyInfo
206 // algorithm over keyData.
207 // Step 2.3. If an error occurred while parsing, then throw a DataError.
208 let spki = SubjectPublicKeyInfo::<AnyRef, BitStringRef>::from_der(key_data)
209 .map_err(|_| Error::Data(None))?;
210
211 // Step 2.4. If the algorithm object identifier field of the algorithm
212 // AlgorithmIdentifier field of spki is not equal to the id-X25519 object identifier
213 // defined in [RFC8410], then throw a DataError.
214 if spki.algorithm.oid != ObjectIdentifier::new_unwrap(X25519_OID_STRING) {
215 return Err(Error::Data(None));
216 }
217
218 // Step 2.5. If the parameters field of the algorithm AlgorithmIdentifier field of spki
219 // is present, then throw a DataError.
220 if spki.algorithm.parameters.is_some() {
221 return Err(Error::Data(None));
222 }
223
224 // Step 2.6. Let publicKey be the X25519 public key identified by the subjectPublicKey
225 // field of spki.
226 let key_bytes: [u8; PUBLIC_KEY_LENGTH] = spki
227 .subject_public_key
228 .as_bytes()
229 .ok_or(Error::Data(None))?
230 .try_into()
231 .map_err(|_| Error::Data(None))?;
232 let public_key = PublicKey::from(key_bytes);
233
234 // Step 2.7. Let key be a new CryptoKey that represents publicKey.
235 // Step 2.8. Set the [[type]] internal slot of key to "public"
236 // Step 2.9. Let algorithm be a new KeyAlgorithm.
237 // Step 2.10. Set the name attribute of algorithm to "X25519".
238 // Step 2.11. Set the [[algorithm]] internal slot of key to algorithm.
239 let algorithm = SubtleKeyAlgorithm {
240 name: ALG_X25519.to_string(),
241 };
242 CryptoKey::new(
243 global,
244 KeyType::Public,
245 extractable,
246 KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm),
247 usages,
248 Handle::X25519PublicKey(public_key),
249 can_gc,
250 )
251 },
252 // If format is "pkcs8":
253 KeyFormat::Pkcs8 => {
254 // Step 2.1. If usages contains an entry which is not "deriveKey" or "deriveBits" then
255 // throw a SyntaxError.
256 if usages
257 .iter()
258 .any(|usage| !matches!(usage, KeyUsage::DeriveKey | KeyUsage::DeriveBits))
259 {
260 return Err(Error::Syntax(None));
261 }
262
263 // Step 2.2. Let privateKeyInfo be the result of running the parse a privateKeyInfo
264 // algorithm over keyData.
265 // Step 2.3. If an error occurs while parsing, then throw a DataError.
266 let private_key_info =
267 PrivateKeyInfo::from_der(key_data).map_err(|_| Error::Data(None))?;
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(None));
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(None));
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 = OctetStringRef::from_der(private_key_info.private_key)
289 .map_err(|_| Error::Data(None))?;
290 let key_bytes: [u8; PRIVATE_KEY_LENGTH] = curve_private_key
291 .as_bytes()
292 .try_into()
293 .map_err(|_| Error::Data(None))?;
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(None));
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(None));
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(None));
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(None));
365 }
366
367 // Step 2.9.
368 // If the d field is present:
369 let (handle, key_type) = if jwk.d.is_some() {
370 // Step 2.9.1. If jwk does not meet the requirements of the JWK private key format
371 // described in Section 2 of [RFC8037], then throw a DataError.
372 let x = jwk.decode_required_string_field(JwkStringField::X)?;
373 let d = jwk.decode_required_string_field(JwkStringField::D)?;
374 let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] =
375 x.try_into().map_err(|_| Error::Data(None))?;
376 let private_key_bytes: [u8; PRIVATE_KEY_LENGTH] =
377 d.try_into().map_err(|_| Error::Data(None))?;
378 let public_key = PublicKey::from(public_key_bytes);
379 let private_key = StaticSecret::from(private_key_bytes);
380 if PublicKey::from(&private_key) != public_key {
381 return Err(Error::Data(None));
382 }
383
384 // Step 2.9.1. Let key be a new CryptoKey object that represents the X25519 private
385 // key identified by interpreting jwk according to Section 2 of [RFC8037].
386 // NOTE: CryptoKey is created in Step 2.10 - 2.12.
387 let handle = Handle::X25519PrivateKey(private_key);
388
389 // Step 2.9.1. Set the [[type]] internal slot of Key to "private".
390 let key_type = KeyType::Private;
391
392 (handle, key_type)
393 }
394 // Otherwise:
395 else {
396 // Step 2.9.1. If jwk does not meet the requirements of the JWK public key format
397 // described in Section 2 of [RFC8037], then throw a DataError.
398 let x = jwk.decode_required_string_field(JwkStringField::X)?;
399 let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] =
400 x.try_into().map_err(|_| Error::Data(None))?;
401 let public_key = PublicKey::from(public_key_bytes);
402
403 // Step 2.9.1. Let key be a new CryptoKey object that represents the X25519 public
404 // key identified by interpreting jwk according to Section 2 of [RFC8037].
405 // NOTE: CryptoKey is created in Step 2.10 - 2.12.
406 let handle = Handle::X25519PublicKey(public_key);
407
408 // Step 2.9.1. Set the [[type]] internal slot of Key to "public".
409 let key_type = KeyType::Public;
410
411 (handle, key_type)
412 };
413
414 // Step 2.10. Let algorithm be a new instance of a KeyAlgorithm object.
415 // Step 2.11. Set the name attribute of algorithm to "X25519".
416 // Step 2.12. Set the [[algorithm]] internal slot of key to algorithm.
417 let algorithm = SubtleKeyAlgorithm {
418 name: ALG_X25519.to_string(),
419 };
420 CryptoKey::new(
421 global,
422 key_type,
423 extractable,
424 KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm),
425 usages,
426 handle,
427 can_gc,
428 )
429 },
430 // If format is "raw":
431 KeyFormat::Raw | KeyFormat::Raw_public => {
432 // Step 2.1. If usages is not empty then throw a SyntaxError.
433 if !usages.is_empty() {
434 return Err(Error::Syntax(None));
435 }
436
437 // Step 2.2. If the length in bits of keyData is not 256 then throw a DataError.
438 if key_data.len() != 32 {
439 return Err(Error::Data(None));
440 }
441
442 // Step 2.3. Let algorithm be a new KeyAlgorithm object.
443 // Step 2.4. Set the name attribute of algorithm to "X25519".
444 // Step 2.5. Let key be a new CryptoKey representing the key data provided in keyData.
445 // Step 2.6. Set the [[type]] internal slot of key to "public"
446 // Step 2.7. Set the [[algorithm]] internal slot of key to algorithm.
447 let key_bytes: [u8; PUBLIC_KEY_LENGTH] =
448 key_data.try_into().map_err(|_| Error::Data(None))?;
449 let public_key = PublicKey::from(key_bytes);
450 let algorithm = SubtleKeyAlgorithm {
451 name: ALG_X25519.to_string(),
452 };
453 CryptoKey::new(
454 global,
455 KeyType::Public,
456 extractable,
457 KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm),
458 usages,
459 Handle::X25519PublicKey(public_key),
460 can_gc,
461 )
462 },
463 // Otherwise:
464 _ => {
465 // throw a NotSupportedError.
466 return Err(Error::NotSupported(None));
467 },
468 };
469
470 // Step 3. Return key
471 Ok(key)
472}
473
474/// <https://w3c.github.io/webcrypto/#x25519-operations-export-key>
475pub(crate) fn export_key(format: KeyFormat, key: &CryptoKey) -> Result<ExportedKey, Error> {
476 // Step 1. Let key be the CryptoKey to be exported.
477
478 // Step 2. If the underlying cryptographic key material represented by the [[handle]] internal
479 // slot of key cannot be accessed, then throw an OperationError.
480 // NOTE: Done in Step 3.
481
482 // Step 3.
483 let result = match format {
484 // If format is "spki":
485 KeyFormat::Spki => {
486 // Step 3.1. If the [[type]] internal slot of key is not "public", then throw an
487 // InvalidAccessError.
488 if key.Type() != KeyType::Public {
489 return Err(Error::InvalidAccess(None));
490 }
491
492 // Step 3.2.
493 // Let data be an instance of the SubjectPublicKeyInfo ASN.1 structure defined in
494 // [RFC5280] with the following properties:
495 // * Set the algorithm field to an AlgorithmIdentifier ASN.1 type with the
496 // following properties:
497 // * Set the algorithm object identifier to the id-X25519 OID defined in
498 // [RFC8410].
499 // * Set the subjectPublicKey field to keyData.
500 let Handle::X25519PublicKey(public_key) = key.handle() else {
501 return Err(Error::Operation(None));
502 };
503 let data = SubjectPublicKeyInfo::<BitStringRef, _> {
504 algorithm: AlgorithmIdentifier {
505 oid: ObjectIdentifier::new_unwrap(X25519_OID_STRING),
506 parameters: None,
507 },
508 subject_public_key: BitStringRef::from_bytes(public_key.as_bytes())
509 .map_err(|_| Error::Data(None))?,
510 };
511
512 // Step 3.3. Let result be the result of DER-encoding data.
513 ExportedKey::Bytes(data.to_der().map_err(|_| Error::Operation(None))?)
514 },
515 // If format is "pkcs8":
516 KeyFormat::Pkcs8 => {
517 // Step 3.1. If the [[type]] internal slot of key is not "private", then throw an
518 // InvalidAccessError.
519 if key.Type() != KeyType::Private {
520 return Err(Error::InvalidAccess(None));
521 }
522
523 // Step 3.2.
524 // Let data be an instance of the PrivateKeyInfo ASN.1 structure defined in [RFC5208]
525 // with the following properties:
526 // * Set the version field to 0.
527 // * Set the privateKeyAlgorithm field to a PrivateKeyAlgorithmIdentifier ASN.1
528 // type with the following properties:
529 // * Set the algorithm object identifier to the id-X25519 OID defined in
530 // [RFC8410].
531 // * Set the privateKey field to the result of DER-encoding a CurvePrivateKey ASN.1
532 // type, as defined in Section 7 of [RFC8410], that represents the X25519 private
533 // key represented by the [[handle]] internal slot of key
534 let Handle::X25519PrivateKey(private_key) = key.handle() else {
535 return Err(Error::Operation(None));
536 };
537 let curve_private_key =
538 OctetString::new(private_key.as_bytes()).map_err(|_| Error::Data(None))?;
539 let data = PrivateKeyInfo {
540 algorithm: AlgorithmIdentifier {
541 oid: ObjectIdentifier::new_unwrap(X25519_OID_STRING),
542 parameters: None,
543 },
544 private_key: &curve_private_key.to_der().map_err(|_| Error::Data(None))?,
545 public_key: None,
546 };
547
548 // Step 3.3. Let result be the result of DER-encoding data.
549 ExportedKey::Bytes(data.to_der().map_err(|_| Error::Operation(None))?)
550 },
551 // If format is "jwk":
552 KeyFormat::Jwk => {
553 // Step 3.1. Let jwk be a new JsonWebKey dictionary.
554 // Step 3.2. Set the kty attribute of jwk to "OKP".
555 // Step 3.3. Set the crv attribute of jwk to "X25519".
556 let mut jwk = JsonWebKey {
557 kty: Some(DOMString::from("OKP")),
558 crv: Some(DOMString::from(ALG_X25519)),
559 ..Default::default()
560 };
561
562 // Step 3.4. Set the x attribute of jwk according to the definition in Section 2 of
563 // [RFC8037].
564 match key.handle() {
565 Handle::X25519PrivateKey(private_key) => {
566 let public_key = PublicKey::from(private_key);
567 jwk.encode_string_field(JwkStringField::X, public_key.as_bytes());
568 },
569 Handle::X25519PublicKey(public_key) => {
570 jwk.encode_string_field(JwkStringField::X, public_key.as_bytes());
571 },
572 _ => return Err(Error::Operation(None)),
573 }
574
575 // Step 3.5.
576 // If the [[type]] internal slot of key is "private"
577 // Set the d attribute of jwk according to the definition in Section 2 of
578 // [RFC8037].
579 if key.Type() == KeyType::Private {
580 if let Handle::X25519PrivateKey(private_key) = key.handle() {
581 jwk.encode_string_field(JwkStringField::D, private_key.as_bytes());
582 } else {
583 return Err(Error::Operation(None));
584 }
585 }
586
587 // Step 3.6. Set the key_ops attribute of jwk to the usages attribute of key.
588 jwk.set_key_ops(key.usages());
589
590 // Step 3.7. Set the ext attribute of jwk to the [[extractable]] internal slot of key.
591 jwk.ext = Some(key.Extractable());
592
593 // Step 3.8. Let result be jwk.
594 ExportedKey::Jwk(Box::new(jwk))
595 },
596 // If format is "raw":
597 KeyFormat::Raw | KeyFormat::Raw_public => {
598 // Step 3.1. If the [[type]] internal slot of key is not "public", then throw an
599 // InvalidAccessError.
600 if key.Type() != KeyType::Public {
601 return Err(Error::InvalidAccess(None));
602 }
603
604 // Step 3.2. Let data be a byte sequence representing the X25519 public key represented
605 // by the [[handle]] internal slot of key.
606 let Handle::X25519PublicKey(public_key) = key.handle() else {
607 return Err(Error::Operation(None));
608 };
609 let data = public_key.as_bytes();
610
611 // Step 3.3. Let result be data.
612 ExportedKey::Bytes(data.to_vec())
613 },
614 // Otherwise:
615 _ => {
616 // throw a NotSupportedError.
617 return Err(Error::NotSupported(None));
618 },
619 };
620
621 // Step 4. Return result.
622 Ok(result)
623}