Skip to main content

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