Skip to main content

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