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}