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