Skip to main content

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