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