Skip to main content

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