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