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}