script/dom/subtlecrypto/
ed25519_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 aws_lc_rs::encoding::{AsBigEndian, AsDer};
6use aws_lc_rs::signature::{ED25519, Ed25519KeyPair, KeyPair, ParsedPublicKey, UnparsedPublicKey};
7use js::context::JSContext;
8use rand::TryRngCore;
9use rand::rngs::OsRng;
10
11use crate::dom::bindings::codegen::Bindings::CryptoKeyBinding::{
12    CryptoKeyMethods, CryptoKeyPair, KeyType, KeyUsage,
13};
14use crate::dom::bindings::codegen::Bindings::SubtleCryptoBinding::{JsonWebKey, KeyFormat};
15use crate::dom::bindings::error::Error;
16use crate::dom::bindings::root::DomRoot;
17use crate::dom::bindings::str::DOMString;
18use crate::dom::cryptokey::{CryptoKey, Handle};
19use crate::dom::globalscope::GlobalScope;
20use crate::dom::subtlecrypto::{
21    CryptoAlgorithm, ExportedKey, JsonWebKeyExt, JwkStringField, KeyAlgorithmAndDerivatives,
22    SubtleKeyAlgorithm,
23};
24
25const ED25519_SEED_LENGTH: usize = 32;
26
27/// <https://w3c.github.io/webcrypto/#ed25519-operations-sign>
28pub(crate) fn sign(key: &CryptoKey, message: &[u8]) -> Result<Vec<u8>, Error> {
29    // Step 1. If the [[type]] internal slot of key is not "private", then throw an
30    // InvalidAccessError.
31    if key.Type() != KeyType::Private {
32        return Err(Error::InvalidAccess(Some(
33            "[[type]] internal slot of key is not \"private\"".into(),
34        )));
35    }
36
37    // Step 2. Let result be the result of performing the Ed25519 signing process, as specified in
38    // [RFC8032], Section 5.1.6, with message as M, using the Ed25519 private key associated with
39    // key.
40    let key_pair =
41        Ed25519KeyPair::from_seed_unchecked(key.handle().as_bytes()).map_err(|error| {
42            Error::Operation(Some(format!(
43                "The key was rejected for the following reason: {error}"
44            )))
45        })?;
46    let result = key_pair.sign(message).as_ref().to_vec();
47
48    // Step 3. Return result.
49    Ok(result)
50}
51
52/// <https://w3c.github.io/webcrypto/#ed25519-operations-verify>
53pub(crate) fn verify(key: &CryptoKey, message: &[u8], signature: &[u8]) -> Result<bool, Error> {
54    // Step 1. If the [[type]] internal slot of key is not "public", then throw an
55    // InvalidAccessError.
56    if key.Type() != KeyType::Public {
57        return Err(Error::InvalidAccess(Some(
58            "[[type]] internal slot of key is not \"public\"".into(),
59        )));
60    }
61
62    // Step 2. If the key data of key represents an invalid point or a small-order element on the
63    // Elliptic Curve of Ed25519, return false.
64    // NOTE: Not all implementations perform this check. See WICG/webcrypto-secure-curves issue 27.
65
66    // Step 3. If the point R, encoded in the first half of signature, represents an invalid point
67    // or a small-order element on the Elliptic Curve of Ed25519, return false.
68    // NOTE: Not all implementations perform this check. See WICG/webcrypto-secure-curves issue 27.
69
70    // Step 4. Perform the Ed25519 verification steps, as specified in [RFC8032], Section
71    // 5.1.7, using the cofactorless (unbatched) equation, [S]B = R + [k]A', on the signature, with
72    // message as M, using the Ed25519 public key associated with key.
73    // Step 5. Let result be a boolean with the value true if the signature is valid and the value
74    // false otherwise.
75    let public_key = UnparsedPublicKey::new(&ED25519, key.handle().as_bytes());
76    let result = match public_key.verify(message, signature) {
77        Ok(()) => true,
78        Err(aws_lc_rs::error::Unspecified) => false,
79    };
80
81    // Step 6. Return result.
82    Ok(result)
83}
84
85/// <https://w3c.github.io/webcrypto/#ed25519-operations-generate-key>
86pub(crate) fn generate_key(
87    cx: &mut JSContext,
88    global: &GlobalScope,
89    extractable: bool,
90    usages: Vec<KeyUsage>,
91) -> Result<CryptoKeyPair, Error> {
92    // Step 1. If usages contains any entry which is not "sign" or "verify", then throw a
93    // SyntaxError.
94    if usages
95        .iter()
96        .any(|usage| !matches!(usage, KeyUsage::Sign | KeyUsage::Verify))
97    {
98        return Err(Error::Syntax(Some(
99            "Usages contains an entry which is not \"sign\" or \"verify\"".into(),
100        )));
101    }
102
103    // Step 2. Generate an Ed25519 key pair, as defined in [RFC8032], section 5.1.5.
104    let mut seed = vec![0u8; ED25519_SEED_LENGTH];
105    if OsRng.try_fill_bytes(&mut seed).is_err() {
106        return Err(Error::Operation(Some("Error getting random data".into())));
107    }
108    let key_pair = Ed25519KeyPair::from_seed_unchecked(&seed).map_err(|error| {
109        Error::Operation(Some(format!(
110            "The key was rejected for the following reason: {error}"
111        )))
112    })?;
113
114    // Step 3. Let algorithm be a new KeyAlgorithm object.
115    // Step 4. Set the name attribute of algorithm to "Ed25519".
116    let algorithm = SubtleKeyAlgorithm {
117        name: CryptoAlgorithm::Ed25519,
118    };
119
120    // Step 5. Let publicKey be a new CryptoKey representing the public key of the generated key pair.
121    // Step 6. Set the [[type]] internal slot of publicKey to "public"
122    // Step 7. Set the [[algorithm]] internal slot of publicKey to algorithm.
123    // Step 8. Set the [[extractable]] internal slot of publicKey to true.
124    // Step 9. Set the [[usages]] internal slot of publicKey to be the usage intersection of usages
125    // and [ "verify" ].
126    let public_key = CryptoKey::new(
127        cx,
128        global,
129        KeyType::Public,
130        true,
131        KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm.clone()),
132        usages
133            .iter()
134            .filter(|&usage| *usage == KeyUsage::Verify)
135            .cloned()
136            .collect(),
137        Handle::Ed25519PublicKey(key_pair.public_key().as_ref().to_vec()),
138    );
139
140    // Step 10. Let privateKey be a new CryptoKey representing the private key of the generated key pair.
141    // Step 11. Set the [[type]] internal slot of privateKey to "private"
142    // Step 12. Set the [[algorithm]] internal slot of privateKey to algorithm.
143    // Step 13. Set the [[extractable]] internal slot of privateKey to extractable.
144    // Step 14. Set the [[usages]] internal slot of privateKey to be the usage intersection of
145    // usages and [ "sign" ].
146    let private_key = CryptoKey::new(
147        cx,
148        global,
149        KeyType::Private,
150        extractable,
151        KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm),
152        usages
153            .iter()
154            .filter(|&usage| *usage == KeyUsage::Sign)
155            .cloned()
156            .collect(),
157        Handle::Ed25519PrivateKey(seed),
158    );
159
160    // Step 16. Let result be a new CryptoKeyPair dictionary.
161    // Step 17. Set the publicKey attribute of result to be publicKey.
162    // Step 18. Set the privateKey attribute of result to be privateKey.
163    let result = CryptoKeyPair {
164        publicKey: Some(public_key),
165        privateKey: Some(private_key),
166    };
167
168    // Step 19. Return result.
169    Ok(result)
170}
171
172/// <https://w3c.github.io/webcrypto/#ed25519-operations-import-key>
173pub(crate) fn import_key(
174    cx: &mut JSContext,
175    global: &GlobalScope,
176    format: KeyFormat,
177    key_data: &[u8],
178    extractable: bool,
179    usages: Vec<KeyUsage>,
180) -> Result<DomRoot<CryptoKey>, Error> {
181    // Step 1. Let keyData be the key data to be imported.
182    // NOTE: It is given as a method parameter.
183
184    // Step 2.
185    let key = match format {
186        // If format is "spki":
187        KeyFormat::Spki => {
188            // Step 2.1. If usages contains a value which is not "verify" then throw a SyntaxError.
189            if usages.iter().any(|usage| *usage != KeyUsage::Verify) {
190                return Err(Error::Syntax(Some(
191                    "Usages contains an entry which is not \"verify\"".into(),
192                )));
193            }
194
195            // Step 2.2. Let spki be the result of running the parse a subjectPublicKeyInfo
196            // algorithm over keyData.
197            // Step 2.3. If an error occurred while parsing, then throw a DataError.
198            // Step 2.4. If the algorithm object identifier field of the algorithm
199            // AlgorithmIdentifier field of spki is not equal to the id-Ed25519 object identifier
200            // defined in [RFC8410], then throw a DataError.
201            // Step 2.5. If the parameters field of the algorithm AlgorithmIdentifier field of spki
202            // is present, then throw a DataError.
203            // Step 2.6. Let publicKey be the Ed25519 public key identified by the subjectPublicKey
204            // field of spki.
205            let public_key = ParsedPublicKey::new(&ED25519, key_data).map_err(|error| {
206                Error::Data(Some(format!(
207                    "The key was rejected for the following reason: {error}"
208                )))
209            })?;
210
211            // Step 2.9. Let algorithm be a new KeyAlgorithm.
212            // Step 2.10. Set the name attribute of algorithm to "Ed25519".
213            let algorithm = SubtleKeyAlgorithm {
214                name: CryptoAlgorithm::Ed25519,
215            };
216
217            // Step 2.7. Let key be a new CryptoKey that represents publicKey.
218            // Step 2.8. Set the [[type]] internal slot of key to "public"
219            // Step 2.11. Set the [[algorithm]] internal slot of key to algorithm.
220            CryptoKey::new(
221                cx,
222                global,
223                KeyType::Public,
224                extractable,
225                KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm),
226                usages,
227                Handle::Ed25519PublicKey(public_key.as_ref().to_vec()),
228            )
229        },
230        // If format is "pkcs8":
231        KeyFormat::Pkcs8 => {
232            // Step 2.1. If usages contains a value which is not "sign" then throw a SyntaxError.
233            if usages.iter().any(|usage| *usage != KeyUsage::Sign) {
234                return Err(Error::Syntax(Some(
235                    "Usages contains an entry which is not \"sign\"".into(),
236                )));
237            }
238
239            // Step 2.2. Let privateKeyInfo be the result of running the parse a privateKeyInfo
240            // algorithm over keyData.
241            // Step 2.3. If an error occurs while parsing, then throw a DataError.
242            // Step 2.4. If the algorithm object identifier field of the privateKeyAlgorithm
243            // PrivateKeyAlgorithm field of privateKeyInfo is not equal to the id-Ed25519 object
244            // identifier defined in [RFC8410], then throw a DataError.
245            // Step 2.5. If the parameters field of the privateKeyAlgorithm
246            // PrivateKeyAlgorithmIdentifier field of privateKeyInfo is present, then throw a
247            // DataError.
248            let private_key_info = Ed25519KeyPair::from_pkcs8(key_data).map_err(|error| {
249                Error::Data(Some(format!(
250                    "The key was rejected for the following reason: {error}"
251                )))
252            })?;
253
254            // Step 2.6. Let curvePrivateKey be the result of performing the parse an ASN.1
255            // structure algorithm, with data as the privateKey field of privateKeyInfo, structure
256            // as the ASN.1 CurvePrivateKey structure specified in Section 7 of [RFC8410], and
257            // exactData set to true.
258            // Step 2.7. If an error occurred while parsing, then throw a DataError.
259            let curve_private_key = private_key_info
260                .seed()
261                .map_err(|_| {
262                    Error::Data(Some("Failed to get the seed from the private key".into()))
263                })?
264                .as_be_bytes()
265                .map_err(|_| {
266                    Error::Data(Some(
267                        "Failed to serialize the seed of the private key".into(),
268                    ))
269                })?
270                .as_ref()
271                .to_vec();
272
273            // Step 2.10. Let algorithm be a new KeyAlgorithm.
274            // Step 2.11. Set the name attribute of algorithm to "Ed25519".
275            let algorithm = SubtleKeyAlgorithm {
276                name: CryptoAlgorithm::Ed25519,
277            };
278
279            // Step 2.8. Let key be a new CryptoKey that represents the Ed25519 private key
280            // identified by curvePrivateKey.
281            // Step 2.9. Set the [[type]] internal slot of key to "private"
282            // Step 2.12. Set the [[algorithm]] internal slot of key to algorithm.
283            CryptoKey::new(
284                cx,
285                global,
286                KeyType::Private,
287                extractable,
288                KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm),
289                usages,
290                Handle::Ed25519PrivateKey(curve_private_key),
291            )
292        },
293        // If format is "jwk":
294        KeyFormat::Jwk => {
295            // Step 2.1. If keyData is a JsonWebKey dictionary: Let jwk equal keyData.
296            // Otherwise: Throw a DataError.
297            let jwk = JsonWebKey::parse(cx, key_data)?;
298
299            // Step 2.2 If the d field is present and usages contains a value which is not "sign",
300            // or, if the d field is not present and usages contains a value which is not "verify"
301            // then throw a SyntaxError.
302            if jwk.d.as_ref().is_some() && usages.iter().any(|usage| *usage != KeyUsage::Sign) {
303                return Err(Error::Syntax(Some(
304                    "The 'd' field is present, but there are usages different than 'sign'".into(),
305                )));
306            }
307            if jwk.d.as_ref().is_none() && usages.iter().any(|usage| *usage != KeyUsage::Verify) {
308                return Err(Error::Syntax(Some(
309                    "The 'd' field is not present, but there are usages different than 'verify'"
310                        .into(),
311                )));
312            }
313
314            // Step 2.3 If the kty field of jwk is not "OKP", then throw a DataError.
315            if jwk.kty.as_ref().is_none_or(|kty| kty != "OKP") {
316                return Err(Error::Data(Some(
317                    "The 'kty' field is different from 'OKP'".into(),
318                )));
319            }
320
321            // Step 2.4 If the crv field of jwk is not "Ed25519", then throw a DataError.
322            if jwk.crv.as_ref().is_none_or(|crv| crv != "Ed25519") {
323                return Err(Error::Data(Some(
324                    "The 'crv' field of the key is different from 'Ed25519'".into(),
325                )));
326            }
327
328            // Step 2.5 If the alg field of jwk is present and is not "Ed25519" or "EdDSA", then
329            // throw a DataError.
330            if jwk
331                .alg
332                .as_ref()
333                .is_some_and(|alg| !matches!(alg.str().as_ref(), "Ed25519" | "EdDSA"))
334            {
335                return Err(Error::Data(Some(
336                    "The 'alg' field is different from 'Ed25519' and 'EdDSA'".into(),
337                )));
338            }
339
340            // Step 2.6 If usages is non-empty and the use field of jwk is present and is not
341            // "sig", then throw a DataError.
342            if !usages.is_empty() && jwk.use_.as_ref().is_some_and(|use_| use_ != "sig") {
343                return Err(Error::Data(Some(
344                    "There are usages, but the 'use' field is different from 'sig'".into(),
345                )));
346            }
347
348            // Step 2.7 If the key_ops field of jwk is present, and is invalid according to the
349            // requirements of JSON Web Key [JWK], or it does not contain all of the specified
350            // usages values, then throw a DataError.
351            jwk.check_key_ops(&usages)?;
352
353            // Step 2.8 If the ext field of jwk is present and has the value false and extractable
354            // is true, then throw a DataError.
355            if jwk.ext.as_ref().is_some_and(|ext| !ext) && extractable {
356                return Err(Error::Data(Some(
357                    "The 'ext' field is false, but 'extractable' is true".into(),
358                )));
359            }
360
361            // Step 2.10. Let algorithm be a new instance of a KeyAlgorithm object.
362            // Step 2.11. Set the name attribute of algorithm to "Ed25519".
363            let algorithm = SubtleKeyAlgorithm {
364                name: CryptoAlgorithm::Ed25519,
365            };
366
367            // Step 2.9
368            // If the d field is present:
369            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 d = jwk.decode_required_string_field(JwkStringField::D)?;
373                let x = jwk.decode_required_string_field(JwkStringField::X)?;
374                let _ = Ed25519KeyPair::from_seed_and_public_key(&d, &x).map_err(|error| {
375                    Error::Data(Some(format!(
376                        "Key rejected for the following reason: {error}"
377                    )))
378                })?;
379
380                // Step 2.9.2. Let key be a new CryptoKey object that represents the Ed25519
381                // private key identified by interpreting jwk according to Section
382                // 2 of [RFC8037]
383                // Step 2.9.3. Set the [[type]] internal slot of Key to "private".
384                CryptoKey::new(
385                    cx,
386                    global,
387                    KeyType::Private,
388                    extractable,
389                    KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm),
390                    usages,
391                    Handle::Ed25519PrivateKey(d),
392                )
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
400                // Step 2.9.2. Let key be a new CryptoKey object that represents the Ed25519 public
401                // key identified by interpreting jwk according to Section 2 of [RFC8037].
402                // Step 2.9.3. Set the [[type]] internal slot of Key to "public".
403                CryptoKey::new(
404                    cx,
405                    global,
406                    KeyType::Public,
407                    extractable,
408                    KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm),
409                    usages,
410                    Handle::Ed25519PublicKey(x),
411                )
412            }
413
414            // Step 2.12. Set the [[algorithm]] internal slot of key to algorithm.
415            // NOTE: Done in Step 2.9
416        },
417        // If format is "raw":
418        KeyFormat::Raw | KeyFormat::Raw_public => {
419            // Step 2.1. If usages contains a value which is not "verify" then throw a SyntaxError.
420            if usages.iter().any(|usage| *usage != KeyUsage::Verify) {
421                return Err(Error::Syntax(Some(
422                    "Usages contains an entry which is not one of \"verify\"".into(),
423                )));
424            }
425
426            // Step 2.2. If the length in bits of keyData is not 256 then throw a DataError.
427            if key_data.len() * 8 != 256 {
428                return Err(Error::Data(Some("The key length is not 256 bits".into())));
429            }
430
431            // Step 2.3. Let algorithm be a new KeyAlgorithm object.
432            // Step 2.4. Set the name attribute of algorithm to "Ed25519".
433            let algorithm = SubtleKeyAlgorithm {
434                name: CryptoAlgorithm::Ed25519,
435            };
436
437            // Step 2.5. Let key be a new CryptoKey representing the key data provided in keyData.
438            // Step 2.6. Set the [[type]] internal slot of key to "public"
439            // Step 2.7. Set the [[algorithm]] internal slot of key to algorithm.
440            CryptoKey::new(
441                cx,
442                global,
443                KeyType::Public,
444                extractable,
445                KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm),
446                usages,
447                Handle::Ed25519PublicKey(key_data.to_vec()),
448            )
449        },
450        // Otherwise:
451        _ => {
452            // throw a NotSupportedError.
453            return Err(Error::NotSupported(Some(
454                "Unsupported import key format for ED25519 key".into(),
455            )));
456        },
457    };
458
459    // Step 3. Return key
460    Ok(key)
461}
462
463/// Steps 9-15 <https://wicg.github.io/webcrypto-modern-algos/#SubtleCrypto-method-getPublicKey>
464pub(crate) fn get_public_key(
465    cx: &mut JSContext,
466    global: &GlobalScope,
467    key: &CryptoKey,
468    algorithm: &KeyAlgorithmAndDerivatives,
469    usages: Vec<KeyUsage>,
470) -> Result<DomRoot<CryptoKey>, Error> {
471    // NOTE: Steps 1-8 and 16-18 are handled by `SubtleCrypto::GetPublicKey`.
472
473    // Step 9. If usages contains an entry which is not supported for a public key by the algorithm
474    // identified by algorithm, then throw a SyntaxError.
475    if usages.iter().any(|usage| *usage != KeyUsage::Verify) {
476        return Err(Error::Syntax(Some(
477            "Usages contains an entry which is not supported for a public key by the algorithm \
478             identified by algorithm"
479                .into(),
480        )));
481    }
482
483    // Step 10. Let publicKey be a new CryptoKey representing the public key corresponding to the
484    // private key represented by the [[handle]] internal slot of key.
485    let seed = key.handle().as_bytes();
486    let key_pair = Ed25519KeyPair::from_seed_unchecked(seed).map_err(|_| {
487        // Step 11. If an error occurred, then throw a OperationError.
488        Error::Operation(None)
489    })?;
490    let public_key_bytes = key_pair.public_key().as_ref().to_vec();
491
492    // Step 12. Set the [[type]] internal slot of publicKey to "public".
493    // Step 13. Set the [[algorithm]] internal slot of publicKey to algorithm.
494    // Step 14. Set the [[extractable]] internal slot of publicKey to true.
495    // Step 15. Set the [[usages]] internal slot of publicKey to usages.
496    Ok(CryptoKey::new(
497        cx,
498        global,
499        KeyType::Public,
500        true,
501        algorithm.clone(),
502        usages,
503        Handle::Ed25519PublicKey(public_key_bytes),
504    ))
505}
506
507/// <https://w3c.github.io/webcrypto/#ed25519-operations-export-key>
508pub(crate) fn export_key(format: KeyFormat, key: &CryptoKey) -> Result<ExportedKey, Error> {
509    // Step 1. Let key be the CryptoKey to be exported.
510    // NOTE: It is given as a method parameter.
511
512    // Step 2. If the underlying cryptographic key material represented by the [[handle]] internal
513    // slot of key cannot be accessed, then throw an OperationError.
514    // NOTE: key.handle() guarantees access.
515    let key_data = key.handle().as_bytes();
516
517    // Step 3.
518    let result = match format {
519        // If format is "spki":
520        KeyFormat::Spki => {
521            // Step 3.1. If the [[type]] internal slot of key is not "public", then throw an
522            // InvalidAccessError.
523            if key.Type() != KeyType::Public {
524                return Err(Error::InvalidAccess(Some(
525                    "[[type]] internal slot of key is not \"public\"".into(),
526                )));
527            }
528
529            // Step 3.2. Let data be an instance of the SubjectPublicKeyInfo ASN.1 structure
530            // defined in [RFC5280] with the following properties:
531            //     Set the algorithm field to an AlgorithmIdentifier ASN.1 type with the following
532            //     properties:
533            //         Set the algorithm object identifier to the id-Ed25519 OID defined in
534            //         [RFC8410].
535            //     Set the subjectPublicKey field to keyData.
536            let data = ParsedPublicKey::new(&ED25519, key_data).map_err(|error| {
537                Error::Operation(Some(format!(
538                    "The key was rejected for the following reason: {error}"
539                )))
540            })?;
541
542            // Step 3.3. Let result be the result of DER-encoding data.
543            ExportedKey::Bytes(
544                data.as_der()
545                    .map_err(|_| {
546                        Error::Operation(Some(
547                            "Failed to serialize the key into a DER format".into(),
548                        ))
549                    })?
550                    .as_ref()
551                    .to_vec(),
552            )
553        },
554        // If format is "pkcs8":
555        KeyFormat::Pkcs8 => {
556            // Step 3.1. If the [[type]] internal slot of key is not "private", then throw an
557            // InvalidAccessError.
558            if key.Type() != KeyType::Private {
559                return Err(Error::InvalidAccess(Some(
560                    "[[type]] internal slot of key is not \"private\"".into(),
561                )));
562            }
563
564            // Step 3.2. Let data be an instance of the PrivateKeyInfo ASN.1 structure defined in
565            // [RFC5208] with the following properties:
566            //     Set the version field to 0.
567            //     Set the privateKeyAlgorithm field to a PrivateKeyAlgorithmIdentifier ASN.1 type
568            //     with the following properties:
569            //         Set the algorithm object identifier to the id-Ed25519 OID defined in
570            //         [RFC8410].
571            //     Set the privateKey field to the result of DER-encoding a CurvePrivateKey ASN.1
572            //     type, as defined in Section 7 of [RFC8410], that represents the Ed25519 private
573            //     key represented by the [[handle]] internal slot of key
574            let data = Ed25519KeyPair::from_seed_unchecked(key_data)
575                .map_err(|error| {
576                    Error::Operation(Some(format!(
577                        "The key was rejected for the following reason: {error}"
578                    )))
579                })?
580                .to_pkcs8v1()
581                .map_err(|_| {
582                    Error::Operation(Some(
583                        "Failed to serialize the key into a PKCS#8 format".into(),
584                    ))
585                })?;
586
587            // Step 3.3. Let result be the result of DER-encoding data.
588            ExportedKey::Bytes(data.as_ref().to_vec())
589        },
590        // If format is "jwk":
591        KeyFormat::Jwk => {
592            // Step 3.1. Let jwk be a new JsonWebKey dictionary.
593            // Step 3.2. Set the kty attribute of jwk to "OKP".
594            // Step 3.3. Set the alg attribute of jwk to "Ed25519".
595            // Step 3.4. Set the crv attribute of jwk to "Ed25519".
596            let mut jwk = JsonWebKey {
597                kty: Some(DOMString::from("OKP")),
598                alg: Some(DOMString::from("Ed25519")),
599                crv: Some(DOMString::from("Ed25519")),
600                ..Default::default()
601            };
602
603            // Step 3.5. Set the x attribute of jwk according to the definition in Section 2 of [RFC8037].
604            // Step 3.6.
605            // If the [[type]] internal slot of key is "private"
606            //     Set the d attribute of jwk according to the definition in Section 2 of [RFC8037].
607            match key.Type() {
608                KeyType::Public => {
609                    jwk.encode_string_field(JwkStringField::X, key_data);
610                },
611                KeyType::Private => {
612                    let key_pair =
613                        Ed25519KeyPair::from_seed_unchecked(key_data).map_err(|error| {
614                            Error::Data(Some(format!(
615                                "The key was rejected for the following reason: {error}"
616                            )))
617                        })?;
618                    jwk.encode_string_field(JwkStringField::X, key_pair.public_key().as_ref());
619                    jwk.encode_string_field(JwkStringField::D, key_data);
620                },
621                KeyType::Secret => {
622                    return Err(Error::Data(Some("Cannot export a secret key".into())));
623                },
624            }
625
626            // Step 3.7. Set the key_ops attribute of jwk to the usages attribute of key.
627            jwk.set_key_ops(key.usages());
628
629            // Step 3.8. Set the ext attribute of jwk to the [[extractable]] internal slot of key.
630            jwk.ext = Some(key.Extractable());
631
632            // Step 3.9. Let result be jwk.
633            ExportedKey::Jwk(Box::new(jwk))
634        },
635        // If format is "raw":
636        KeyFormat::Raw | KeyFormat::Raw_public => {
637            // Step 3.1. If the [[type]] internal slot of key is not "public", then throw an
638            // InvalidAccessError.
639            if key.Type() != KeyType::Public {
640                return Err(Error::InvalidAccess(Some(
641                    "[[type]] internal slot of key is not \"public\"".into(),
642                )));
643            }
644
645            // Step 3.2. Let data be a byte sequence representing the Ed25519 public key
646            // represented by the [[handle]] internal slot of key.
647            // Step 3.3. Let result be data.
648            ExportedKey::Bytes(key_data.to_vec())
649        },
650        // Otherwise:
651        _ => {
652            // throw a NotSupportedError.
653            return Err(Error::NotSupported(Some(
654                "Unsupported export key format for ED25519 key".into(),
655            )));
656        },
657    };
658
659    // Step 4. Return result.
660    Ok(result)
661}