Skip to main content

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