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