Skip to main content

script_webcrypto/subtlecrypto/
ec_common.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::pkcs8::{DecodePrivateKey, DecodePublicKey, EncodePrivateKey, EncodePublicKey};
6use elliptic_curve::sec1::{ModulusSize, Sec1Point, ToSec1Point, ValidatePublicKey};
7use elliptic_curve::{Curve, FieldBytesSize, Generate, PublicKey, SecretKey};
8use js::context::JSContext;
9use p256::NistP256;
10use p384::NistP384;
11use p521::NistP521;
12
13use crate::cryptokey::{CryptoKey, Handle, KeyUsageSliceHelper};
14use crate::dom::bindings::codegen::GenericBindings::CryptoKeyBinding::{
15    CryptoKeyMethods, CryptoKeyPair, KeyType, KeyUsage,
16};
17use crate::dom::bindings::codegen::GenericBindings::SubtleCryptoBinding::{JsonWebKey, KeyFormat};
18use crate::dom::bindings::error::{Error, ErrorResult};
19use crate::dom::bindings::root::DomRoot;
20use crate::dom::bindings::str::DOMString;
21use crate::subtlecrypto::{
22    CryptoAlgorithm, EcKeyAlgorithm, EcKeyGenParams, EcKeyImportParams, ExportedKey, JsonWebKeyExt,
23    JwkStringField, KeyAlgorithmAndDerivatives, NAMED_CURVE_P256, NAMED_CURVE_P384,
24    NAMED_CURVE_P521, SUPPORTED_CURVES,
25};
26use crate::traits::Equivalence;
27
28#[derive(PartialEq)]
29pub(crate) enum EcAlgorithm {
30    Ecdsa,
31    Ecdh,
32}
33
34/// <https://w3c.github.io/webcrypto/#ecdsa-operations-generate-key>
35/// <https://w3c.github.io/webcrypto/#ecdh-operations-generate-key>
36pub(crate) fn generate_key<D: Equivalence>(
37    cx: &mut JSContext,
38    global: &D::GlobalScope,
39    ec_algorithm: EcAlgorithm,
40    normalized_algorithm: &EcKeyGenParams,
41    extractable: bool,
42    usages: Vec<KeyUsage>,
43) -> Result<CryptoKeyPair<D>, Error> {
44    match ec_algorithm {
45        EcAlgorithm::Ecdsa => {
46            // Step 1. If usages contains a value which is not one of "sign" or "verify", then throw
47            // a SyntaxError.
48            usages.ensure_only_contain_entries_from(&[KeyUsage::Sign, KeyUsage::Verify])?;
49        },
50        EcAlgorithm::Ecdh => {
51            // Step 1. If usages contains an entry which is not "deriveKey" or "deriveBits" then
52            // throw a SyntaxError.
53            usages
54                .ensure_only_contain_entries_from(&[KeyUsage::DeriveKey, KeyUsage::DeriveBits])?;
55        },
56    }
57
58    // Step 2.
59    // If the namedCurve member of normalizedAlgorithm is "P-256", "P-384" or "P-521":
60    //     Generate an Elliptic Curve key pair, as defined in [RFC6090] with domain parameters for
61    //     the curve identified by the namedCurve member of normalizedAlgorithm.
62    // If the namedCurve member of normalizedAlgorithm is a value specified in an applicable
63    // specification:
64    //     Perform the ECDSA generation steps specified in that specification, passing in
65    //     normalizedAlgorithm and resulting in an elliptic curve key pair.
66    // Otherwise:
67    //     throw a NotSupportedError
68    // Step 3. If performing the key generation operation results in an error, then throw an
69    // OperationError.
70    // NOTE: We currently do not support other applicable specifications.
71    let (private_key_handle, public_key_handle) = match normalized_algorithm.named_curve.as_str() {
72        NAMED_CURVE_P256 => {
73            let private_key = SecretKey::<NistP256>::try_generate().map_err(|_| {
74                Error::Operation(Some("Failed to generate P-256 private key".into()))
75            })?;
76            let public_key = private_key.public_key();
77            (
78                Handle::P256PrivateKey(private_key),
79                Handle::P256PublicKey(public_key),
80            )
81        },
82        NAMED_CURVE_P384 => {
83            let private_key = SecretKey::<NistP384>::try_generate().map_err(|_| {
84                Error::Operation(Some("Failed to generate P-384 private key".into()))
85            })?;
86            let public_key = private_key.public_key();
87            (
88                Handle::P384PrivateKey(private_key),
89                Handle::P384PublicKey(public_key),
90            )
91        },
92        NAMED_CURVE_P521 => {
93            let private_key = SecretKey::<NistP521>::try_generate().map_err(|_| {
94                Error::Operation(Some("Failed to generate P-521 private key".into()))
95            })?;
96            let public_key = private_key.public_key();
97            (
98                Handle::P521PrivateKey(private_key),
99                Handle::P521PublicKey(public_key),
100            )
101        },
102        named_curve => {
103            return Err(Error::NotSupported(Some(format!(
104                "Unsupported named curve: {}",
105                named_curve
106            ))));
107        },
108    };
109
110    // Step 4. Let algorithm be a new EcKeyAlgorithm object.
111    // Step 6. Set the namedCurve attribute of algorithm to equal the namedCurve member of
112    // normalizedAlgorithm.
113    let algorithm = EcKeyAlgorithm {
114        name: match ec_algorithm {
115            EcAlgorithm::Ecdsa => {
116                // Step 5. Set the name attribute of algorithm to "ECDSA".
117                CryptoAlgorithm::Ecdsa
118            },
119            EcAlgorithm::Ecdh => {
120                // Step 5. Set the name member of algorithm to "ECDH".
121                CryptoAlgorithm::Ecdh
122            },
123        },
124        named_curve: normalized_algorithm.named_curve.clone(),
125    };
126
127    // Step 7. Let publicKey be a new CryptoKey representing the public key of the generated key pair.
128    // Step 8. Set the [[type]] internal slot of publicKey to "public"
129    // Step 9. Set the [[algorithm]] internal slot of publicKey to algorithm.
130    // Step 10. Set the [[extractable]] internal slot of publicKey to true.
131    let public_key_usage = match ec_algorithm {
132        EcAlgorithm::Ecdsa => {
133            // Step 11. Set the [[usages]] internal slot of publicKey to be the usage intersection
134            // of usages and [ "verify" ].
135            usages.usage_intersection(&[KeyUsage::Verify])
136        },
137        EcAlgorithm::Ecdh => {
138            // Step 11. Set the [[usages]] internal slot of publicKey to be the empty list.
139            Vec::new()
140        },
141    };
142    let public_key = CryptoKey::new(
143        cx,
144        global,
145        KeyType::Public,
146        true,
147        KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algorithm.clone()),
148        public_key_usage,
149        public_key_handle,
150    );
151
152    // Step 12. Let privateKey be a new CryptoKey representing the private key of the generated key pair.
153    // Step 13. Set the [[type]] internal slot of privateKey to "private"
154    // Step 14. Set the [[algorithm]] internal slot of privateKey to algorithm.
155    // Step 15. Set the [[extractable]] internal slot of privateKey to extractable.
156    let private_key_usage = match ec_algorithm {
157        EcAlgorithm::Ecdsa => {
158            // Step 16. Set the [[usages]] internal slot of privateKey to be the usage intersection
159            // of usages and [ "sign" ].
160            usages.usage_intersection(&[KeyUsage::Sign])
161        },
162        EcAlgorithm::Ecdh => {
163            // Step 16. Set the [[usages]] internal slot of privateKey to be the usage intersection
164            // of usages and [ "deriveKey", "deriveBits" ].
165            usages.usage_intersection(&[KeyUsage::DeriveKey, KeyUsage::DeriveBits])
166        },
167    };
168    let private_key = CryptoKey::new(
169        cx,
170        global,
171        KeyType::Private,
172        extractable,
173        KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algorithm),
174        private_key_usage,
175        private_key_handle,
176    );
177
178    // Step 17. Let result be a new CryptoKeyPair dictionary.
179    // Step 18. Set the publicKey attribute of result to be publicKey.
180    // Step 19. Set the privateKey attribute of result to be privateKey.
181    let result = CryptoKeyPair {
182        publicKey: Some(public_key),
183        privateKey: Some(private_key),
184    };
185
186    // Step 20. Return result.
187    Ok(result)
188}
189
190/// <https://w3c.github.io/webcrypto/#ecdsa-operations-import-key>
191/// <https://w3c.github.io/webcrypto/#ecdh-operations-import-key>
192///
193/// This implementation is based on the specification of the importKey operation of ECDSA. When
194/// format is "jwk", Step 3.2 and Step 3.3 in the specification of the importKey operation of ECDH
195/// are combined into a single step, and Step 3.9.1 to Step 3.9.3 here are skipped for ECDH.
196#[allow(clippy::too_many_arguments)]
197pub(crate) fn import_key<D: Equivalence>(
198    cx: &mut JSContext,
199    global: &D::GlobalScope,
200    ec_algorithm: EcAlgorithm,
201    normalized_algorithm: &EcKeyImportParams,
202    format: KeyFormat,
203    key_data: &[u8],
204    extractable: bool,
205    usages: Vec<KeyUsage>,
206) -> Result<DomRoot<CryptoKey<D>>, Error> {
207    // Step 1. If the namedCurve member of normalizedAlgorithm is not one of "P-256", "P-384" or
208    // "P-521", and is not a value specified in an applicable specification that specifies the use
209    // of that value with ECDSA, then throw a NotSupportedError.
210    if !SUPPORTED_CURVES.contains(&normalized_algorithm.named_curve.as_str()) {
211        return Err(Error::NotSupported(Some("Unsupported namedCurve".into())));
212    }
213
214    // Step 2. Let keyData be the key data to be imported.
215
216    // Step 3.
217    let key = match format {
218        // If format is "spki":
219        KeyFormat::Spki => {
220            match ec_algorithm {
221                EcAlgorithm::Ecdsa => {
222                    // Step 3.1. If usages contains a value which is not "verify" then throw a
223                    // SyntaxError.
224                    usages.ensure_only_contain_entries_from(&[KeyUsage::Verify])?;
225                },
226                EcAlgorithm::Ecdh => {
227                    // Step 3.1. If usages is not empty then throw a SyntaxError.
228                    usages.ensure_only_contain_entries_from(&[])?;
229                },
230            }
231
232            // Step 3.2. Let spki be the result of running the parse a subjectPublicKeyInfo
233            // algorithm over keyData
234            // Step 3.3. If an error occurred while parsing, then throw a DataError.
235            // Step 3.4. If the algorithm object identifier field of the algorithm
236            // AlgorithmIdentifier field of spki is not equal to the id-ecPublicKey object
237            // identifier defined in [RFC5480], then throw a DataError.
238            // Step 3.5. If the parameters field of the algorithm AlgorithmIdentifier field of spki
239            // is absent, then throw a DataError.
240            // Step 3.6. Let params be the parameters field of the algorithm AlgorithmIdentifier
241            // field of spki.
242            // Step 3.7. If params is not an instance of the ECParameters ASN.1 type defined in
243            // [RFC5480] that specifies a namedCurve, then throw a DataError.
244            // Step 3.8. Let namedCurve be a string whose initial value is undefined.
245            // Step 3.9.
246            //     If params is equivalent to the secp256r1 object identifier defined in [RFC5480]:
247            //         Set namedCurve "P-256".
248            //     If params is equivalent to the secp384r1 object identifier defined in [RFC5480]:
249            //         Set namedCurve "P-384".
250            //     If params is equivalent to the secp521r1 object identifier defined in [RFC5480]:
251            //         Set namedCurve "P-521".
252            // Step 3.10.
253            //     If namedCurve is not undefined:
254            //         Step 3.10.1. Let publicKey be the Elliptic Curve public key identified by
255            //         performing the conversion steps defined in Section 2.3.4 of [SEC1] using the
256            //         subjectPublicKey field of spki. The uncompressed point format MUST be
257            //         supported.
258            //         Step 3.10.2. If the implementation does not support the compressed point
259            //         format and a compressed point is provided, throw a DataError.
260            //         Step 3.10.3. If a decode error occurs or an identity point is found, throw a
261            //         DataError.
262            //         Step 3.10.4. Let key be a new CryptoKey that represents publicKey.
263            //     Otherwise:
264            //         Step 3.10.1. Perform any key import steps defined by other applicable
265            //         specifications, passing format, spki and obtaining namedCurve and key.
266            //         Step 3.10.2. If an error occurred or there are no applicable specifications,
267            //         throw a DataError.
268            // Step 3.11. If namedCurve is defined, and not equal to the namedCurve member of
269            // normalizedAlgorithm, throw a DataError.
270            // Step 3.12. If the public key value is not a valid point on the Elliptic Curve
271            // identified by the namedCurve member of normalizedAlgorithm throw a DataError.
272            //
273            // NOTE: The new CryptoKey in Step 3.10.4 is created in Step 3.13 - 3.17.
274            let handle = match normalized_algorithm.named_curve.as_str() {
275                NAMED_CURVE_P256 => Handle::P256PublicKey(
276                    PublicKey::<NistP256>::from_public_key_der(key_data).map_err(|_| {
277                        Error::Data(Some(
278                            "Failed to parse the P-256 elliptic-curve public key in SPKI format"
279                                .into(),
280                        ))
281                    })?,
282                ),
283                NAMED_CURVE_P384 => Handle::P384PublicKey(
284                    PublicKey::<NistP384>::from_public_key_der(key_data).map_err(|_| {
285                        Error::Data(Some(
286                            "Failed to parse the P-384 elliptic-curve public key in SPKI format"
287                                .into(),
288                        ))
289                    })?,
290                ),
291                NAMED_CURVE_P521 => Handle::P521PublicKey(
292                    PublicKey::<NistP521>::from_public_key_der(key_data).map_err(|_| {
293                        Error::Data(Some(
294                            "Failed to parse the P-521 elliptic-curve public key in SPKI format"
295                                .into(),
296                        ))
297                    })?,
298                ),
299                _ => return Err(Error::Data(Some("Unsupported namedCurve".into()))),
300            };
301
302            // Step 3.13. Set the [[type]] internal slot of key to "public"
303            // Step 3.14. Let algorithm be a new EcKeyAlgorithm.
304            // Step 3.16. Set the namedCurve attribute of algorithm to namedCurve.
305            // Step 3.17. Set the [[algorithm]] internal slot of key to algorithm.
306            let algorithm = EcKeyAlgorithm {
307                name: match ec_algorithm {
308                    EcAlgorithm::Ecdsa => {
309                        // Step 3.15. Set the name attribute of algorithm to "ECDSA".
310                        CryptoAlgorithm::Ecdsa
311                    },
312                    EcAlgorithm::Ecdh => {
313                        // Step 3.15. Set the name attribute of algorithm to "ECDH".
314                        CryptoAlgorithm::Ecdh
315                    },
316                },
317                named_curve: normalized_algorithm.named_curve.clone(),
318            };
319            CryptoKey::new(
320                cx,
321                global,
322                KeyType::Public,
323                extractable,
324                KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algorithm),
325                usages.normalized_value(),
326                handle,
327            )
328        },
329        // If format is "pkcs8":
330        KeyFormat::Pkcs8 => {
331            match ec_algorithm {
332                EcAlgorithm::Ecdsa => {
333                    // Step 3.1. If usages contains a value which is not "sign" then throw a
334                    // SyntaxError.
335                    usages.ensure_only_contain_entries_from(&[KeyUsage::Sign])?;
336                },
337                EcAlgorithm::Ecdh => {
338                    // Step 3.1. If usages contains an entry which is not "deriveKey" or
339                    // "deriveBits" then throw a SyntaxError.
340                    usages.ensure_only_contain_entries_from(&[
341                        KeyUsage::DeriveKey,
342                        KeyUsage::DeriveBits,
343                    ])?;
344                },
345            }
346
347            // Step 3.2. Let privateKeyInfo be the result of running the parse a privateKeyInfo
348            // algorithm over keyData.
349            // Step 3.3. If an error occurs while parsing, throw a DataError.
350            // Step 3.4. If the algorithm object identifier field of the privateKeyAlgorithm
351            // PrivateKeyAlgorithm field of privateKeyInfo is not equal to the id-ecPublicKey
352            // object identifier defined in [RFC5480], throw a DataError.
353            // Step 3.5. If the parameters field of the privateKeyAlgorithm
354            // PrivateKeyAlgorithmIdentifier field of privateKeyInfo is not present, throw a
355            // DataError.
356            // Step 3.6. Let params be the parameters field of the privateKeyAlgorithm
357            // PrivateKeyAlgorithmIdentifier field of privateKeyInfo.
358            // Step 3.7. If params is not an instance of the ECParameters ASN.1 type defined in
359            // [RFC5480] that specifies a namedCurve, then throw a DataError.
360            // Step 3.8. Let namedCurve be a string whose initial value is undefined.
361            // Step 3.9.
362            //     If params is equivalent to the secp256r1 object identifier defined in [RFC5480]:
363            //         Set namedCurve to "P-256".
364            //     If params is equivalent to the secp384r1 object identifier defined in [RFC5480]:
365            //         Set namedCurve to "P-384".
366            //     If params is equivalent to the secp521r1 object identifier defined in [RFC5480]:
367            //         Set namedCurve to "P-521".
368            // Step 3.10.
369            //     If namedCurve is not undefined:
370            //         Step 3.10.1. Let ecPrivateKey be the result of performing the parse an ASN.1
371            //         structure algorithm, with data as the privateKey field of privateKeyInfo,
372            //         structure as the ASN.1 ECPrivateKey structure specified in Section 3 of
373            //         [RFC5915], and exactData set to true.
374            //         Step 3.10.2. If an error occurred while parsing, then throw a DataError.
375            //         Step 3.10.3. If the parameters field of ecPrivateKey is present, and is not
376            //         an instance of the namedCurve ASN.1 type defined in [RFC5480], or does not
377            //         contain the same object identifier as the parameters field of the
378            //         privateKeyAlgorithm PrivateKeyAlgorithmIdentifier field of privateKeyInfo,
379            //         then throw a DataError.
380            //         Step 3.10.4. Let key be a new CryptoKey that represents the Elliptic Curve
381            //         private key identified by performing the conversion steps defined in Section
382            //         3 of [RFC5915] using ecPrivateKey.
383            //     Otherwise:
384            //         Step 3.10.1. Perform any key import steps defined by other applicable
385            //         specifications, passing format, privateKeyInfo and obtaining namedCurve and
386            //         key.
387            //         Step 3.10.2. If an error occurred or there are no applicable specifications,
388            //         throw a DataError.
389            // Step 3.11. If namedCurve is defined, and not equal to the namedCurve member of
390            // normalizedAlgorithm, throw a DataError.
391            // Step 3.12. If the private key value is not a valid point on the Elliptic Curve
392            // identified by the namedCurve member of normalizedAlgorithm throw a DataError.
393            //
394            // NOTE: The new CryptoKey in Step 3.10.4 is created in Step 3.13 - 3.17.
395            let handle = match normalized_algorithm.named_curve.as_str() {
396                NAMED_CURVE_P256 => Handle::P256PrivateKey(
397                    SecretKey::<NistP256>::from_pkcs8_der(key_data).map_err(|_| {
398                        Error::Data(Some(
399                            "Failed to parse the P-256 elliptic-curve private key in PKCS#8 format"
400                                .into(),
401                        ))
402                    })?,
403                ),
404                NAMED_CURVE_P384 => Handle::P384PrivateKey(
405                    SecretKey::<NistP384>::from_pkcs8_der(key_data).map_err(|_| {
406                        Error::Data(Some(
407                            "Failed to parse the P-384 elliptic-curve private key in PKCS#8 format"
408                                .into(),
409                        ))
410                    })?,
411                ),
412                NAMED_CURVE_P521 => Handle::P521PrivateKey(
413                    SecretKey::<NistP521>::from_pkcs8_der(key_data).map_err(|_| {
414                        Error::Data(Some(
415                            "Failed to parse the P-521 elliptic-curve private key in PKCS#8 format"
416                                .into(),
417                        ))
418                    })?,
419                ),
420                _ => return Err(Error::Data(Some("Unsupported namedCurve".into()))),
421            };
422
423            // Step 3.13. Set the [[type]] internal slot of key to "private".
424            // Step 3.14. Let algorithm be a new EcKeyAlgorithm.
425            // Step 3.16. Set the namedCurve attribute of algorithm to namedCurve.
426            // Step 3.17. Set the [[algorithm]] internal slot of key to algorithm.
427            let algorithm = EcKeyAlgorithm {
428                name: match ec_algorithm {
429                    EcAlgorithm::Ecdsa => {
430                        // Step 3.15. Set the name attribute of algorithm to "ECDSA".
431                        CryptoAlgorithm::Ecdsa
432                    },
433                    EcAlgorithm::Ecdh => {
434                        // Step 3.15. Set the name attribute of algorithm to "ECDH".
435                        CryptoAlgorithm::Ecdh
436                    },
437                },
438                named_curve: normalized_algorithm.named_curve.clone(),
439            };
440            CryptoKey::new(
441                cx,
442                global,
443                KeyType::Private,
444                extractable,
445                KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algorithm),
446                usages.normalized_value(),
447                handle,
448            )
449        },
450        // If format is "jwk":
451        KeyFormat::Jwk => {
452            // Step 3.1.
453            // If keyData is a JsonWebKey dictionary:
454            //     Let jwk equal keyData.
455            // Otherwise:
456            //     Throw a DataError.
457            let jwk = JsonWebKey::parse(cx, key_data)?;
458
459            match ec_algorithm {
460                EcAlgorithm::Ecdsa => {
461                    // Step 3.2. If the d field is present and usages contains a value which is not
462                    // "sign", or, if the d field is not present and usages contains a value which
463                    // is not "verify" then throw a SyntaxError.
464                    match jwk.d.as_ref() {
465                        Some(_) => usages.ensure_only_contain_entries_from(&[KeyUsage::Sign])?,
466                        None => usages.ensure_only_contain_entries_from(&[KeyUsage::Verify])?,
467                    }
468                },
469                EcAlgorithm::Ecdh => {
470                    // Step 3.2. If the d field is present and if usages contains an entry which is
471                    // not "deriveKey" or "deriveBits" then throw a SyntaxError. If the d field is
472                    // not present and if usages is not empty then throw a SyntaxError.
473                    match jwk.d.as_ref() {
474                        Some(_) => usages.ensure_only_contain_entries_from(&[
475                            KeyUsage::DeriveKey,
476                            KeyUsage::DeriveBits,
477                        ])?,
478                        None => usages.ensure_only_contain_entries_from(&[])?,
479                    }
480                },
481            }
482
483            // Step 3.3. If the kty field of jwk is not "EC", then throw a DataError.
484            if jwk.kty.as_ref().is_none_or(|kty| kty != "EC") {
485                return Err(Error::Data(Some("JWK `kty` field is not \"EC\"".into())));
486            }
487
488            match ec_algorithm {
489                EcAlgorithm::Ecdsa => {
490                    // Step 3.4. If usages is non-empty and the use field of jwk is present and is
491                    // not "sig", then throw a DataError.
492                    if !usages.is_empty() && jwk.use_.as_ref().is_some_and(|use_| use_ != "sig") {
493                        return Err(Error::Data(Some(
494                            "Usages is not empty, JWK `use` field is present, \
495                                and it is not \"sign\""
496                                .into(),
497                        )));
498                    }
499                },
500                EcAlgorithm::Ecdh => {
501                    // Step 3.4. If usages is non-empty and the use field of jwk is present and is
502                    // not equal to "enc" then throw a DataError.
503                    if !usages.is_empty() && jwk.use_.as_ref().is_some_and(|use_| use_ != "enc") {
504                        return Err(Error::Data(Some(
505                            "Usages is not empty, JWK `use` field is present, \
506                                and it is not \"enc\""
507                                .into(),
508                        )));
509                    }
510                },
511            }
512
513            // Step 3.5. If the key_ops field of jwk is present, and is invalid according to the
514            // requirements of JSON Web Key [JWK], or it does not contain all of the specified
515            // usages values, then throw a DataError.
516            jwk.check_key_ops(&usages)?;
517
518            // Step 3.6. If the ext field of jwk is present and has the value false and extractable
519            // is true, then throw a DataError.
520            if jwk.ext.is_some_and(|ext| !ext) && extractable {
521                return Err(Error::Data(Some("JWK is not extractable".into())));
522            }
523
524            // Step 3.7. Let namedCurve be a string whose value is equal to the crv field of jwk.
525            // Step 3.8. If namedCurve is not equal to the namedCurve member of
526            // normalizedAlgorithm, throw a DataError.
527            let named_curve = jwk
528                .crv
529                .as_ref()
530                .filter(|crv| **crv == normalized_algorithm.named_curve)
531                .map(|crv| crv.to_string())
532                .ok_or(Error::Data(Some(
533                    "JWK named curve does not match algorithm named curve".into(),
534                )))?;
535
536            // Step 3.9.
537            // If namedCurve is "P-256", "P-384" or "P-521":
538            let (handle, key_type) = if matches!(
539                named_curve.as_str(),
540                NAMED_CURVE_P256 | NAMED_CURVE_P384 | NAMED_CURVE_P521
541            ) {
542                if ec_algorithm == EcAlgorithm::Ecdsa {
543                    // Step 3.9.1. Let algNamedCurve be a string whose initial value is undefined.
544                    // Step 3.9.2.
545                    // If the alg field is not present:
546                    //     Let algNamedCurve be undefined.
547                    // If the alg field is equal to the string "ES256":
548                    //     Let algNamedCurve be the string "P-256".
549                    // If the alg field is equal to the string "ES384":
550                    //     Let algNamedCurve be the string "P-384".
551                    // If the alg field is equal to the string "ES512":
552                    //     Let algNamedCurve be the string "P-521".
553                    // otherwise:
554                    //     throw a DataError.
555                    let alg = jwk.alg.as_ref().map(|alg| alg.to_string());
556                    let alg_named_curve = match alg.as_deref() {
557                        None => None,
558                        Some("ES256") => Some(NAMED_CURVE_P256),
559                        Some("ES384") => Some(NAMED_CURVE_P384),
560                        Some("ES521") => Some(NAMED_CURVE_P521),
561                        Some(alg) => {
562                            return Err(Error::Data(Some(format!(
563                                "Unsupported alg field in JsonWebKey: {}",
564                                alg
565                            ))));
566                        },
567                    };
568
569                    // Step 3.9.3. If algNamedCurve is defined, and is not equal to namedCurve,
570                    // throw a DataError.
571                    if alg_named_curve.is_some_and(|alg_named_curve| alg_named_curve != named_curve)
572                    {
573                        return Err(Error::Data(Some(
574                            "The algNamedCurve is defined, and is not equal to namedCurve".into(),
575                        )));
576                    }
577                }
578
579                // Step 3.9.4.
580                // If the d field is present:
581                if jwk.d.is_some() {
582                    // Step 3.9.4.1. If jwk does not meet the requirements of Section 6.2.2 of JSON
583                    // Web Algorithms [JWA], then throw a DataError.
584                    let x = jwk.decode_required_string_field(JwkStringField::X)?;
585                    let y = jwk.decode_required_string_field(JwkStringField::Y)?;
586                    let d = jwk.decode_required_string_field(JwkStringField::D)?;
587
588                    // Step 3.9.4.2. Let key be a new CryptoKey object that represents the Elliptic
589                    // Curve private key identified by interpreting jwk according to Section 6.2.2
590                    // of JSON Web Algorithms [JWA].
591                    // NOTE: CryptoKey is created in Step 3.11 - 3.14.
592                    let handle = match named_curve.as_str() {
593                        NAMED_CURVE_P256 => {
594                            let private_key =
595                                SecretKey::<NistP256>::from_slice(&d).map_err(|_| {
596                                    Error::Data(Some("Failed to parse P-256 private key".into()))
597                                })?;
598                            validate_public_key::<NistP256>(
599                                &private_key,
600                                &x_y_to_sec1_bytes(&x, &y),
601                            )?;
602                            Handle::P256PrivateKey(private_key)
603                        },
604                        NAMED_CURVE_P384 => {
605                            let private_key =
606                                SecretKey::<NistP384>::from_slice(&d).map_err(|_| {
607                                    Error::Data(Some("Failed to parse P-384 private key".into()))
608                                })?;
609                            validate_public_key::<NistP384>(
610                                &private_key,
611                                &x_y_to_sec1_bytes(&x, &y),
612                            )?;
613                            Handle::P384PrivateKey(private_key)
614                        },
615                        NAMED_CURVE_P521 => {
616                            let private_key =
617                                SecretKey::<NistP521>::from_slice(&d).map_err(|_| {
618                                    Error::Data(Some("Failed to parse P-521 private key".into()))
619                                })?;
620                            validate_public_key::<NistP521>(
621                                &private_key,
622                                &x_y_to_sec1_bytes(&x, &y),
623                            )?;
624                            Handle::P521PrivateKey(private_key)
625                        },
626                        _ => unreachable!(),
627                    };
628
629                    // Step 3.9.4.3. Set the [[type]] internal slot of Key to "private".
630                    // NOTE: CryptoKey is created in Step 3.11 - 3.14.
631                    let key_type = KeyType::Private;
632
633                    (handle, key_type)
634                }
635                // Otherwise:
636                else {
637                    // Step 3.9.4.1. If jwk does not meet the requirements of Section 6.2.1 of JSON
638                    // Web Algorithms [JWA], then throw a DataError.
639                    let x = jwk.decode_required_string_field(JwkStringField::X)?;
640                    let y = jwk.decode_required_string_field(JwkStringField::Y)?;
641
642                    // Step 3.9.4.2. Let key be a new CryptoKey object that represents the Elliptic
643                    // Curve public key identified by interpreting jwk according to Section 6.2.1 of
644                    // JSON Web Algorithms [JWA].
645                    // NOTE: CryptoKey is created in Step 3.11 - 3.14.
646                    let handle = match named_curve.as_str() {
647                        NAMED_CURVE_P256 => {
648                            let sec1_bytes = x_y_to_sec1_bytes(&x, &y);
649                            let public_key = PublicKey::<NistP256>::from_sec1_bytes(&sec1_bytes)
650                                .map_err(|_| {
651                                    Error::Data(Some("Failed to decode P-256 public key".into()))
652                                })?;
653                            Handle::P256PublicKey(public_key)
654                        },
655                        NAMED_CURVE_P384 => {
656                            let sec1_bytes = x_y_to_sec1_bytes(&x, &y);
657                            let public_key = PublicKey::<NistP384>::from_sec1_bytes(&sec1_bytes)
658                                .map_err(|_| {
659                                    Error::Data(Some("Failed to decode P-384 public key".into()))
660                                })?;
661                            Handle::P384PublicKey(public_key)
662                        },
663                        NAMED_CURVE_P521 => {
664                            let sec1_bytes = x_y_to_sec1_bytes(&x, &y);
665                            let public_key = PublicKey::<NistP521>::from_sec1_bytes(&sec1_bytes)
666                                .map_err(|_| {
667                                    Error::Data(Some("Failed to decode P-521 public key".into()))
668                                })?;
669                            Handle::P521PublicKey(public_key)
670                        },
671                        _ => unreachable!(),
672                    };
673
674                    // Step 3.9.4.4. Set the [[type]] internal slot of Key to "public".
675                    // NOTE: CryptoKey is created in Step 3.11 - 3.14.
676                    let key_type = KeyType::Public;
677
678                    (handle, key_type)
679                }
680            }
681            // Otherwise
682            else {
683                // Step 3.9.1. Perform any key import steps defined by other applicable
684                // specifications, passing format, jwk and obtaining key.
685                // Step 3.9.2. If an error occurred or there are no applicable specifications, throw
686                // a DataError.
687                // NOTE: We currently do not support applicable specifications.
688                return Err(Error::NotSupported(Some("Unsupported namedCurve".into())));
689            };
690
691            // Step 3.10. If the key value is not a valid point on the Elliptic Curve identified by
692            // the namedCurve member of normalizedAlgorithm throw a DataError.
693            // NOTE: Done in Step 3.9.
694
695            // Step 3.11. Let algorithm be a new instance of an EcKeyAlgorithm object.
696            // Step 3.13. Set the namedCurve attribute of algorithm to namedCurve.
697            // Step 3.14. Set the [[algorithm]] internal slot of key to algorithm.
698            let algorithm = EcKeyAlgorithm {
699                name: match ec_algorithm {
700                    EcAlgorithm::Ecdsa => {
701                        // Step 3.12. Set the name attribute of algorithm to "ECDSA".
702                        CryptoAlgorithm::Ecdsa
703                    },
704                    EcAlgorithm::Ecdh => {
705                        // Step 3.12. Set the name attribute of algorithm to "ECDH".
706                        CryptoAlgorithm::Ecdh
707                    },
708                },
709                named_curve,
710            };
711            CryptoKey::new(
712                cx,
713                global,
714                key_type,
715                extractable,
716                KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algorithm),
717                usages.normalized_value(),
718                handle,
719            )
720        },
721        // If format is "raw":
722        KeyFormat::Raw | KeyFormat::Raw_public => {
723            // Step 3.1. If the namedCurve member of normalizedAlgorithm is not a named curve, then
724            // throw a DataError.
725            if !SUPPORTED_CURVES
726                .iter()
727                .any(|&supported_curve| supported_curve == normalized_algorithm.named_curve)
728            {
729                return Err(Error::Data(Some("Unsupported namedCurve".into())));
730            }
731
732            match ec_algorithm {
733                EcAlgorithm::Ecdsa => {
734                    // Step 3.2. If usages contains a value which is not "verify" then throw a
735                    // SyntaxError.
736                    usages.ensure_only_contain_entries_from(&[KeyUsage::Verify])?;
737                },
738                EcAlgorithm::Ecdh => {
739                    // Step 3.2. If usages is not the empty list, then throw a SyntaxError.
740                    usages.ensure_only_contain_entries_from(&[])?;
741                },
742            }
743
744            // Step 3.3.
745            // If namedCurve is "P-256", "P-384" or "P-521":
746            let handle = if matches!(
747                normalized_algorithm.named_curve.as_str(),
748                NAMED_CURVE_P256 | NAMED_CURVE_P384 | NAMED_CURVE_P521
749            ) {
750                // Step 3.3.1. Let Q be the Elliptic Curve public key on the curve identified by the
751                // namedCurve member of normalizedAlgorithm identified by performing the conversion
752                // steps defined in Section 2.3.4 of [SEC1] to keyData. The uncompressed point
753                // format MUST be supported.
754                // Step 3.3.2. If the implementation does not support the compressed point format
755                // and a compressed point is provided, throw a DataError.
756                // Step 3.3.3. If a decode error occurs or an identity point is found, throw a
757                // DataError.
758                match normalized_algorithm.named_curve.as_str() {
759                    NAMED_CURVE_P256 => {
760                        let q = PublicKey::<NistP256>::from_sec1_bytes(key_data).map_err(|_| {
761                            Error::Data(Some("Failed to decode P-256 public key".into()))
762                        })?;
763                        Handle::P256PublicKey(q)
764                    },
765                    NAMED_CURVE_P384 => {
766                        let q = PublicKey::<NistP384>::from_sec1_bytes(key_data).map_err(|_| {
767                            Error::Data(Some("Failed to decode P-384 public key".into()))
768                        })?;
769                        Handle::P384PublicKey(q)
770                    },
771                    NAMED_CURVE_P521 => {
772                        let q = PublicKey::<NistP521>::from_sec1_bytes(key_data).map_err(|_| {
773                            Error::Data(Some("Failed to decode P-521 public key".into()))
774                        })?;
775                        Handle::P521PublicKey(q)
776                    },
777                    _ => unreachable!(),
778                }
779
780                // Step 3.3.4. Let key be a new CryptoKey that represents Q.
781                // NOTE: CryptoKey is created in Step 3.7 - 3.8.
782            }
783            // Otherwise:
784            else {
785                // Step 3.3.1. Perform any key import steps defined by other applicable
786                // specifications, passing format, keyData and obtaining key.
787                // Step 3.3.2. If an error occurred or there are no applicable specifications,
788                // throw a DataError.
789                // NOTE: We currently do not support applicable specifications.
790                return Err(Error::NotSupported(Some("Unsupported namedCurve".into())));
791            };
792
793            // Step 3.4. Let algorithm be a new EcKeyAlgorithm object.
794            // Step 3.6. Set the namedCurve attribute of algorithm to equal the namedCurve member
795            // of normalizedAlgorithm.
796            let algorithm = EcKeyAlgorithm {
797                name: match ec_algorithm {
798                    EcAlgorithm::Ecdsa => {
799                        // Step 3.5. Set the name attribute of algorithm to "ECDSA".
800                        CryptoAlgorithm::Ecdsa
801                    },
802                    EcAlgorithm::Ecdh => {
803                        // Step 3.5. Set the name attribute of algorithm to "ECDH".
804                        CryptoAlgorithm::Ecdh
805                    },
806                },
807                named_curve: normalized_algorithm.named_curve.clone(),
808            };
809
810            // Step 3.7. Set the [[type]] internal slot of key to "public"
811            // Step 3.8. Set the [[algorithm]] internal slot of key to algorithm.
812            CryptoKey::new(
813                cx,
814                global,
815                KeyType::Public,
816                extractable,
817                KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algorithm),
818                usages.normalized_value(),
819                handle,
820            )
821        },
822        // Otherwise:
823        _ => {
824            // throw a NotSupportedError.
825            return Err(Error::NotSupported(Some("Unsupported key format".into())));
826        },
827    };
828
829    // Step 3. Return key.
830    Ok(key)
831}
832
833/// <https://w3c.github.io/webcrypto/#ecdsa-operations-export-key>
834/// <https://w3c.github.io/webcrypto/#ecdh-operations-export-key>
835pub(crate) fn export_key<D: Equivalence>(
836    format: KeyFormat,
837    key: &CryptoKey<D>,
838) -> Result<ExportedKey, Error> {
839    // Step 1. Let key be the CryptoKey to be exported.
840
841    // Step 2. If the underlying cryptographic key material represented by the [[handle]] internal
842    // slot of key cannot be accessed, then throw an OperationError.
843    // NOTE: Done in Step 3.
844
845    // Step 3.
846    let result = match format {
847        // If format is "spki":
848        KeyFormat::Spki => {
849            // Step 3.1. If the [[type]] internal slot of key is not "public", then throw an
850            // InvalidAccessError.
851            key.ensure_type(KeyType::Public)?;
852
853            // Step 3.2.
854            // Let data be an instance of the SubjectPublicKeyInfo ASN.1 structure defined in
855            // [RFC5280] with the following properties:
856            //     * Set the algorithm field to an AlgorithmIdentifier ASN.1 type with the
857            //       following properties:
858            //         * Set the algorithm field to the OID id-ecPublicKey defined in [RFC5480].
859            //         * Set the parameters field to an instance of the ECParameters ASN.1 type
860            //           defined in [RFC5480] as follows:
861            //             If the namedCurve attribute of the [[algorithm]] internal slot of key is
862            //             "P-256", "P-384" or "P-521":
863            //                 Let keyData be the byte sequence that represents the Elliptic Curve
864            //                 public key represented by the [[handle]] internal slot of key
865            //                 according to the encoding rules specified in Section 2.2 of
866            //                 [RFC5480] and using the uncompressed form. and keyData.
867            //                     If the namedCurve attribute of the [[algorithm]] internal slot
868            //                     of key is "P-256":
869            //                         Set parameters to the namedCurve choice with value equal to
870            //                         the object identifier secp256r1 defined in [RFC5480]
871            //                     If the namedCurve attribute of the [[algorithm]] internal slot
872            //                     of key is "P-384":
873            //                         Set parameters to the namedCurve choice with value equal to
874            //                         the object identifier secp384r1 defined in [RFC5480]
875            //                     If the namedCurve attribute of the [[algorithm]] internal slot
876            //                     of key is "P-521":
877            //                         Set parameters to the namedCurve choice with value equal to
878            //                         the object identifier secp521r1 defined in [RFC5480]
879            //             Otherwise:
880            //                 1. Perform any key export steps defined by other applicable
881            //                    specifications, passing format and the namedCurve attribute of
882            //                    the [[algorithm]] internal slot of key and obtaining
883            //                    namedCurveOid and keyData.
884            //                 2. Set parameters to the namedCurve choice with value equal to the
885            //                    object identifier namedCurveOid.
886            //     * Set the subjectPublicKey field to keyData.
887            // NOTE: We currently do not support other applicable specifications.
888            let data = match key.handle() {
889                Handle::P256PublicKey(public_key) => public_key.to_public_key_der(),
890                Handle::P384PublicKey(public_key) => public_key.to_public_key_der(),
891                Handle::P521PublicKey(public_key) => public_key.to_public_key_der(),
892                _ => {
893                    return Err(Error::Operation(Some(
894                        "The key is not an elliptic curve public key".into(),
895                    )));
896                },
897            }
898            .map_err(|_| {
899                Error::Operation(Some("Failed to export elliptic curve public key".into()))
900            })?;
901
902            // Step 3.3. Let result be the result of DER-encoding data.
903            ExportedKey::new_bytes(data.to_vec())
904        },
905        // If format is "pkcs8":
906        KeyFormat::Pkcs8 => {
907            // Step 3.1. If the [[type]] internal slot of key is not "private", then throw an
908            // InvalidAccessError.
909            key.ensure_type(KeyType::Private)?;
910
911            // Step 3.2.
912            // Let data be an instance of the PrivateKeyInfo ASN.1 structure defined in [RFC5208]
913            // with the following properties:
914            //     * Set the version field to 0.
915            //     * Set the privateKeyAlgorithm field to a PrivateKeyAlgorithmIdentifier ASN.1
916            //       type with the following properties:
917            //         * Set the algorithm field to the OID id-ecPublicKey defined in [RFC5480].
918            //         * Set the parameters field to an instance of the ECParameters ASN.1 type
919            //           defined in [RFC5480] as follows:
920            //             If the namedCurve attribute of the [[algorithm]] internal slot of key is
921            //             "P-256", "P-384" or "P-521":
922            //                 Let keyData be the result of DER-encoding an instance of the
923            //                 ECPrivateKey structure defined in Section 3 of [RFC5915] for the
924            //                 Elliptic Curve private key represented by the [[handle]] internal
925            //                 slot of key and that conforms to the following:
926            //                     * The parameters field is present, and is equivalent to the
927            //                       parameters field of the privateKeyAlgorithm field of this
928            //                       PrivateKeyInfo ASN.1 structure.
929            //                     * The publicKey field is present and represents the Elliptic
930            //                       Curve public key associated with the Elliptic Curve private key
931            //                       represented by the [[handle]] internal slot of key.
932            //                     * If the namedCurve attribute of the [[algorithm]] internal slot
933            //                       of key is "P-256":
934            //                         Set parameters to the namedCurve choice with value equal to
935            //                         the object identifier secp256r1 defined in [RFC5480]
936            //                     * If the namedCurve attribute of the [[algorithm]] internal slot
937            //                       of key is "P-384":
938            //                         Set parameters to the namedCurve choice with value equal to
939            //                         the object identifier secp384r1 defined in [RFC5480]
940            //                     * If the namedCurve attribute of the [[algorithm]] internal slot
941            //                       of key is "P-521":
942            //                         Set parameters to the namedCurve choice with value equal to
943            //                         the object identifier secp521r1 defined in [RFC5480]
944            //             Otherwise:
945            //                 1. Perform any key export steps defined by other applicable
946            //                    specifications, passing format and the namedCurve attribute of
947            //                    the [[algorithm]] internal slot of key and obtaining
948            //                    namedCurveOid and keyData.
949            //                 2. Set parameters to the namedCurve choice with value equal to the
950            //                    object identifier namedCurveOid.
951            //     * Set the privateKey field to keyData.
952            // NOTE: We currently do not support other applicable specifications.
953            let data = match key.handle() {
954                Handle::P256PrivateKey(private_key) => private_key.to_pkcs8_der(),
955                Handle::P384PrivateKey(private_key) => private_key.to_pkcs8_der(),
956                Handle::P521PrivateKey(private_key) => private_key.to_pkcs8_der(),
957                _ => {
958                    return Err(Error::Operation(Some(
959                        "The key is not an elliptic curve public key".into(),
960                    )));
961                },
962            }
963            .map_err(|_| {
964                Error::Operation(Some("Failed to export elliptic curve private key".into()))
965            })?;
966
967            // Step 3.3. Let result be the result of DER-encoding data.
968            ExportedKey::new_bytes(data.as_bytes().to_vec())
969        },
970        // If format is "jwk":
971        KeyFormat::Jwk => {
972            // Step 3.1. Let jwk be a new JsonWebKey dictionary.
973            let mut jwk = JsonWebKey::default();
974
975            // Step 3.2. Set the kty attribute of jwk to "EC".
976            jwk.kty = Some(DOMString::from_static("EC"));
977
978            // Step 3.3.
979            let KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algorithm) = key.algorithm() else {
980                return Err(Error::Operation(Some(
981                    "The key is not an elliptic curve key".into(),
982                )));
983            };
984            // If the namedCurve attribute of the [[algorithm]] internal slot of key is "P-256",
985            // "P-384" or "P-521":
986            if matches!(
987                algorithm.named_curve.as_str(),
988                NAMED_CURVE_P256 | NAMED_CURVE_P384 | NAMED_CURVE_P521
989            ) {
990                // Step 3.3.1.
991                // If the namedCurve attribute of the [[algorithm]] internal slot of key is
992                // "P-256":
993                //     Set the crv attribute of jwk to "P-256"
994                // If the namedCurve attribute of the [[algorithm]] internal slot of key is
995                // "P-384":
996                //     Set the crv attribute of jwk to "P-384"
997                // If the namedCurve attribute of the [[algorithm]] internal slot of key is
998                // "P-521":
999                //     Set the crv attribute of jwk to "P-521"
1000                jwk.crv = Some(DOMString::from(algorithm.named_curve.as_str()));
1001
1002                // Step 3.3.2. Set the x attribute of jwk according to the definition in Section
1003                // 6.2.1.2 of JSON Web Algorithms [JWA].
1004                // Step 3.3.3. Set the y attribute of jwk according to the definition in Section
1005                // 6.2.1.3 of JSON Web Algorithms [JWA].
1006                let extraction_error = || {
1007                    Error::Operation(Some(
1008                        "Failed to extract encoded point from elliptic curve key".into(),
1009                    ))
1010                };
1011                let (x, y) = match key.handle() {
1012                    Handle::P256PublicKey(public_key) => {
1013                        let encoded_point = public_key.to_sec1_point(false);
1014                        (
1015                            encoded_point.x().ok_or(extraction_error())?.to_vec(),
1016                            encoded_point.y().ok_or(extraction_error())?.to_vec(),
1017                        )
1018                    },
1019                    Handle::P384PublicKey(public_key) => {
1020                        let encoded_point = public_key.to_sec1_point(false);
1021                        (
1022                            encoded_point.x().ok_or(extraction_error())?.to_vec(),
1023                            encoded_point.y().ok_or(extraction_error())?.to_vec(),
1024                        )
1025                    },
1026                    Handle::P521PublicKey(public_key) => {
1027                        let encoded_point = public_key.to_sec1_point(false);
1028                        (
1029                            encoded_point.x().ok_or(extraction_error())?.to_vec(),
1030                            encoded_point.y().ok_or(extraction_error())?.to_vec(),
1031                        )
1032                    },
1033                    Handle::P256PrivateKey(private_key) => {
1034                        let public_key = private_key.public_key();
1035                        let encoded_point = public_key.to_sec1_point(false);
1036                        (
1037                            encoded_point.x().ok_or(extraction_error())?.to_vec(),
1038                            encoded_point.y().ok_or(extraction_error())?.to_vec(),
1039                        )
1040                    },
1041                    Handle::P384PrivateKey(private_key) => {
1042                        let public_key = private_key.public_key();
1043                        let encoded_point = public_key.to_sec1_point(false);
1044                        (
1045                            encoded_point.x().ok_or(extraction_error())?.to_vec(),
1046                            encoded_point.y().ok_or(extraction_error())?.to_vec(),
1047                        )
1048                    },
1049                    Handle::P521PrivateKey(private_key) => {
1050                        let public_key = private_key.public_key();
1051                        let encoded_point = public_key.to_sec1_point(false);
1052                        (
1053                            encoded_point.x().ok_or(extraction_error())?.to_vec(),
1054                            encoded_point.y().ok_or(extraction_error())?.to_vec(),
1055                        )
1056                    },
1057                    _ => {
1058                        return Err(Error::Operation(Some(
1059                            "The key is not an elliptic curve key".into(),
1060                        )));
1061                    },
1062                };
1063                jwk.encode_string_field(JwkStringField::X, &x);
1064                jwk.encode_string_field(JwkStringField::Y, &y);
1065
1066                // Step 3.3.4.
1067                // If the [[type]] internal slot of key is "private"
1068                //     Set the d attribute of jwk according to the definition in Section 6.2.2.1 of
1069                //     JSON Web Algorithms [JWA].
1070                if key.Type() == KeyType::Private {
1071                    let d = match key.handle() {
1072                        Handle::P256PrivateKey(private_key) => {
1073                            private_key.to_bytes().as_slice().to_vec()
1074                        },
1075                        Handle::P384PrivateKey(private_key) => {
1076                            private_key.to_bytes().as_slice().to_vec()
1077                        },
1078                        Handle::P521PrivateKey(private_key) => {
1079                            private_key.to_bytes().as_slice().to_vec()
1080                        },
1081                        _ => {
1082                            return Err(Error::Operation(Some(
1083                                "The key is not an elliptic curve private key".into(),
1084                            )));
1085                        },
1086                    };
1087                    jwk.encode_string_field(JwkStringField::D, &d);
1088                }
1089            }
1090            // Otherwise:
1091            else {
1092                // Step 3.3.1. Perform any key export steps defined by other applicable
1093                // specifications, passing format and the namedCurve attribute of the [[algorithm]]
1094                // internal slot of key and obtaining namedCurve and a new value of jwk.
1095                // Step 3.3.2. Set the crv attribute of jwk to namedCurve.
1096                // NOTE: We currently do not support other applicable specifications.
1097                return Err(Error::NotSupported(Some("Unsupported named curve".into())));
1098            }
1099
1100            // Step 3.4. Set the key_ops attribute of jwk to the usages attribute of key.
1101            jwk.set_key_ops(key.usages());
1102
1103            // Step 3.4. Set the ext attribute of jwk to the [[extractable]] internal slot of key.
1104            jwk.ext = Some(key.Extractable());
1105
1106            // Step 3.4. Let result be jwk.
1107            ExportedKey::new_jwk(jwk)
1108        },
1109        // If format is "raw":
1110        KeyFormat::Raw | KeyFormat::Raw_public => {
1111            // Step 3.1. If the [[type]] internal slot of key is not "public", then throw an
1112            // InvalidAccessError.
1113            key.ensure_type(KeyType::Public)?;
1114
1115            // Step 3.2.
1116            // If the namedCurve attribute of the [[algorithm]] internal slot of key is "P-256",
1117            // "P-384" or "P-521":
1118            //     Let data be a byte sequence representing the Elliptic Curve point Q represented
1119            //     by the [[handle]] internal slot of key according to [SEC1] 2.3.3 using the
1120            //     uncompressed format.
1121            // Otherwise:
1122            //     Perform any key export steps defined by other applicable specifications, passing
1123            //     format and the namedCurve attribute of the [[algorithm]] internal slot of key
1124            //     and obtaining namedCurve and data.
1125            //     NOTE: We currently do not support other applicable specifications.
1126            let KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algorithm) = key.algorithm() else {
1127                return Err(Error::Operation(Some(
1128                    "The key is not an elliptic curve key".into(),
1129                )));
1130            };
1131            let data = if matches!(
1132                algorithm.named_curve.as_str(),
1133                NAMED_CURVE_P256 | NAMED_CURVE_P384 | NAMED_CURVE_P521
1134            ) {
1135                match key.handle() {
1136                    Handle::P256PublicKey(public_key) => public_key.to_sec1_bytes().to_vec(),
1137                    Handle::P384PublicKey(public_key) => public_key.to_sec1_bytes().to_vec(),
1138                    Handle::P521PublicKey(public_key) => public_key.to_sec1_bytes().to_vec(),
1139                    _ => {
1140                        return Err(Error::Operation(Some(
1141                            "The key is not an elliptic curve public key".into(),
1142                        )));
1143                    },
1144                }
1145            } else {
1146                return Err(Error::NotSupported(Some("Unsupported named curve".into())));
1147            };
1148
1149            // Step 3.3. Let result be data.
1150            ExportedKey::new_bytes(data)
1151        },
1152        // Otherwise:
1153        _ => {
1154            // throw a NotSupportedError.
1155            return Err(Error::NotSupported(Some("Unsupported key format".into())));
1156        },
1157    };
1158
1159    // Step 4. Return result.
1160    Ok(result)
1161}
1162
1163/// <https://wicg.github.io/webcrypto-modern-algos/#SubtleCrypto-method-getPublicKey>
1164/// Step 9 - 15, for elliptic curve cryptography
1165pub(crate) fn get_public_key<D: Equivalence>(
1166    cx: &mut JSContext,
1167    global: &D::GlobalScope,
1168    ec_algorithm: EcAlgorithm,
1169    key: &CryptoKey<D>,
1170    algorithm: &KeyAlgorithmAndDerivatives,
1171    usages: Vec<KeyUsage>,
1172) -> Result<DomRoot<CryptoKey<D>>, Error> {
1173    // Step 9. If usages contains an entry which is not supported for a public key by the algorithm
1174    // identified by algorithm, then throw a SyntaxError.
1175    //
1176    // NOTE: See "importKey" operation for supported usages
1177    match ec_algorithm {
1178        EcAlgorithm::Ecdsa => {
1179            usages.ensure_only_contain_entries_from(&[KeyUsage::Verify])?;
1180        },
1181        EcAlgorithm::Ecdh => {
1182            usages.ensure_only_contain_entries_from(&[])?;
1183        },
1184    }
1185
1186    // Step 10. Let publicKey be a new CryptoKey representing the public key corresponding to the
1187    // private key represented by the [[handle]] internal slot of key.
1188    // Step 11. If an error occurred, then throw a OperationError.
1189    // Step 12. Set the [[type]] internal slot of publicKey to "public".
1190    // Step 13. Set the [[algorithm]] internal slot of publicKey to algorithm.
1191    // Step 14. Set the [[extractable]] internal slot of publicKey to true.
1192    // Step 15. Set the [[usages]] internal slot of publicKey to usages.
1193    let public_key_handle = match key.handle() {
1194        Handle::P256PrivateKey(private_key) => Handle::P256PublicKey(private_key.public_key()),
1195        Handle::P384PrivateKey(private_key) => Handle::P384PublicKey(private_key.public_key()),
1196        Handle::P521PrivateKey(private_key) => Handle::P521PublicKey(private_key.public_key()),
1197        _ => {
1198            return Err(Error::Operation(Some(
1199                "[[handle]] internal slot of key is not an elliptic curve private key".to_string(),
1200            )));
1201        },
1202    };
1203    let public_key = CryptoKey::new(
1204        cx,
1205        global,
1206        KeyType::Public,
1207        true,
1208        algorithm.clone(),
1209        usages,
1210        public_key_handle,
1211    );
1212
1213    Ok(public_key)
1214}
1215
1216/// Concatenate big endian serialized coordinates of an elliptic curve point, to form an
1217/// uncompressed SEC1 encoded curve point, with prefix `0x04` indicating it is an uncompressed
1218/// point.
1219fn x_y_to_sec1_bytes(x: &[u8], y: &[u8]) -> Vec<u8> {
1220    let mut sec1_bytes = Vec::with_capacity(1 + x.len() + y.len());
1221    sec1_bytes.push(4u8);
1222    sec1_bytes.extend_from_slice(x);
1223    sec1_bytes.extend_from_slice(y);
1224    sec1_bytes
1225}
1226
1227/// Validate the public key in form of uncompressed SEC1 encoded curve point, against a private key.
1228fn validate_public_key<C>(private_key: &SecretKey<C>, sec1_bytes: &[u8]) -> ErrorResult
1229where
1230    C: Curve + ValidatePublicKey,
1231    FieldBytesSize<C>: ModulusSize,
1232{
1233    let sec1_point = Sec1Point::<C>::from_bytes(sec1_bytes)
1234        .map_err(|_| Error::Data(Some("Failed to encode curve point".into())))?;
1235    C::validate_public_key(private_key, &sec1_point)
1236        .map_err(|_| Error::Data(Some("The public key does not match the private key".into())))
1237}