Skip to main content

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