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