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