script/dom/subtlecrypto/
ecdsa_operation.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 base64ct::{Base64UrlUnpadded, Encoding};
6use elliptic_curve::sec1::{FromEncodedPoint, ToEncodedPoint, ValidatePublicKey};
7use p256::NistP256;
8use p384::NistP384;
9use p521::NistP521;
10use pkcs8::der::Decode;
11use pkcs8::spki::EncodePublicKey;
12use pkcs8::{AssociatedOid, EncodePrivateKey, PrivateKeyInfo, SubjectPublicKeyInfo};
13use sec1::der::asn1::BitString;
14use sec1::{EcParameters, EcPrivateKey, EncodedPoint};
15
16use crate::dom::bindings::codegen::Bindings::CryptoKeyBinding::{
17    CryptoKeyMethods, KeyType, KeyUsage,
18};
19use crate::dom::bindings::codegen::Bindings::SubtleCryptoBinding::{JsonWebKey, KeyFormat};
20use crate::dom::bindings::error::Error;
21use crate::dom::bindings::root::DomRoot;
22use crate::dom::bindings::str::DOMString;
23use crate::dom::cryptokey::{CryptoKey, Handle};
24use crate::dom::globalscope::GlobalScope;
25use crate::dom::subtlecrypto::{
26    ALG_ECDSA, ExportedKey, JsonWebKeyExt, KeyAlgorithmAndDerivatives, NAMED_CURVE_P256,
27    NAMED_CURVE_P384, NAMED_CURVE_P521, SUPPORTED_CURVES, SubtleEcKeyAlgorithm,
28    SubtleEcKeyImportParams,
29};
30use crate::script_runtime::CanGc;
31
32/// <https://w3c.github.io/webcrypto/#ecdsa-operations-import-key>
33pub(crate) fn import_key(
34    global: &GlobalScope,
35    normalized_algorithm: &SubtleEcKeyImportParams,
36    format: KeyFormat,
37    key_data: &[u8],
38    extractable: bool,
39    usages: Vec<KeyUsage>,
40    can_gc: CanGc,
41) -> Result<DomRoot<CryptoKey>, Error> {
42    // Step 1. Let keyData be the key data to be imported.
43
44    // Step 2.
45    let key = match format {
46        // If format is "spki":
47        KeyFormat::Spki => {
48            // Step 2.1. If usages contains a value which is not "verify" then throw a SyntaxError.
49            if usages.iter().any(|usage| *usage != KeyUsage::Verify) {
50                return Err(Error::Syntax(None));
51            }
52
53            // Step 2.2. Let spki be the result of running the parse a subjectPublicKeyInfo
54            // algorithm over keyData
55            // Step 2.3. If an error occurred while parsing, then throw a DataError.
56            let spki = SubjectPublicKeyInfo::<_, BitString>::from_der(key_data)
57                .map_err(|_| Error::Data)?;
58
59            // Step 2.4. If the algorithm object identifier field of the algorithm
60            // AlgorithmIdentifier field of spki is not equal to the id-ecPublicKey object
61            // identifier defined in [RFC5480], then throw a DataError.
62            if spki.algorithm.oid != elliptic_curve::ALGORITHM_OID {
63                return Err(Error::Data);
64            }
65
66            // Step 2.5. If the parameters field of the algorithm AlgorithmIdentifier field of spki
67            // is absent, then throw a DataError.
68            // Step 2.6. Let params be the parameters field of the algorithm AlgorithmIdentifier
69            // field of spki.
70            // Step 2.7. If params is not an instance of the ECParameters ASN.1 type defined in
71            // [RFC5480] that specifies a namedCurve, then throw a DataError.
72            let Some(params): Option<EcParameters> = spki.algorithm.parameters else {
73                return Err(Error::Data);
74            };
75
76            // Step 2.8. Let namedCurve be a string whose initial value is undefined.
77            // Step 2.9.
78            // If params is equivalent to the secp256r1 object identifier defined in [RFC5480]:
79            //     Set namedCurve "P-256".
80            // If params is equivalent to the secp384r1 object identifier defined in [RFC5480]:
81            //     Set namedCurve "P-384".
82            // If params is equivalent to the secp521r1 object identifier defined in [RFC5480]:
83            //     Set namedCurve "P-521".
84            let named_curve = match params {
85                EcParameters::NamedCurve(NistP256::OID) => Some(NAMED_CURVE_P256),
86                EcParameters::NamedCurve(NistP384::OID) => Some(NAMED_CURVE_P384),
87                EcParameters::NamedCurve(NistP521::OID) => Some(NAMED_CURVE_P521),
88                _ => None,
89            };
90
91            // Step 2.10.
92            let handle = match named_curve {
93                // If namedCurve is not undefined:
94                Some(curve) => {
95                    // Step 2.10.1. Let publicKey be the Elliptic Curve public key identified by
96                    // performing the conversion steps defined in Section 2.3.4 of [SEC1] using the
97                    // subjectPublicKey field of spki.
98                    // Step 2.10.2. The uncompressed point format MUST be supported.
99                    // Step 2.10.3. If the implementation does not support the compressed point
100                    // format and a compressed point is provided, throw a DataError.
101                    // Step 2.10.4. If a decode error occurs or an identity point is found, throw a
102                    // DataError.
103                    let sec1_bytes = spki.subject_public_key.as_bytes().ok_or(Error::Data)?;
104                    match curve {
105                        NAMED_CURVE_P256 => {
106                            let public_key = p256::PublicKey::from_sec1_bytes(sec1_bytes)
107                                .map_err(|_| Error::Data)?;
108                            Handle::P256PublicKey(public_key)
109                        },
110                        NAMED_CURVE_P384 => {
111                            let public_key = p384::PublicKey::from_sec1_bytes(sec1_bytes)
112                                .map_err(|_| Error::Data)?;
113                            Handle::P384PublicKey(public_key)
114                        },
115                        NAMED_CURVE_P521 => {
116                            let public_key = p521::PublicKey::from_sec1_bytes(sec1_bytes)
117                                .map_err(|_| Error::Data)?;
118                            Handle::P521PublicKey(public_key)
119                        },
120                        _ => unreachable!(),
121                    }
122
123                    // Step 2.10.5. Let key be a new CryptoKey that represents publicKey.
124                    // NOTE: CryptoKey is created in Step 2.13 - 2.17.
125                },
126                // Otherwise:
127                None => {
128                    // Step 2.10.1. Perform any key import steps defined by other applicable
129                    // specifications, passing format, spki and obtaining namedCurve and key.
130                    // Step 2.10.2. If an error occurred or there are no applicable specifications,
131                    // throw a DataError.
132                    // NOTE: We currently do not support other applicable specifications.
133                    return Err(Error::Data);
134                },
135            };
136
137            // Step 2.11. If namedCurve is defined, and not equal to the namedCurve member of
138            // normalizedAlgorithm, throw a DataError.
139            if named_curve.is_some_and(|curve| curve != normalized_algorithm.named_curve) {
140                return Err(Error::Data);
141            }
142
143            // Step 2.12. If the key value is not a valid point on the Elliptic Curve identified by
144            // the namedCurve member of normalizedAlgorithm throw a DataError.
145            // NOTE: Done in Step 2.10.
146
147            // Step 2.13. Set the [[type]] internal slot of key to "public"
148            // Step 2.14. Let algorithm be a new EcKeyAlgorithm.
149            // Step 2.15. Set the name attribute of algorithm to "ECDSA".
150            // Step 2.16. Set the namedCurve attribute of algorithm to namedCurve.
151            // Step 2.17. Set the [[algorithm]] internal slot of key to algorithm.
152            let algorithm = SubtleEcKeyAlgorithm {
153                name: ALG_ECDSA.to_string(),
154                named_curve: named_curve
155                    .expect("named_curve must exist here")
156                    .to_string(),
157            };
158            CryptoKey::new(
159                global,
160                KeyType::Public,
161                extractable,
162                KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algorithm),
163                usages,
164                handle,
165                can_gc,
166            )
167        },
168        // If format is "pkcs8":
169        KeyFormat::Pkcs8 => {
170            // Step 2.1. If usages contains a value which is not "sign" then throw a SyntaxError.
171            if usages.iter().any(|usage| *usage != KeyUsage::Sign) {
172                return Err(Error::Syntax(None));
173            }
174
175            // Step 2.2. Let privateKeyInfo be the result of running the parse a privateKeyInfo
176            // algorithm over keyData.
177            // Step 2.3. If an error occurs while parsing, throw a DataError.
178            let private_key_info = PrivateKeyInfo::from_der(key_data).map_err(|_| Error::Data)?;
179
180            // Step 2.4. If the algorithm object identifier field of the privateKeyAlgorithm
181            // PrivateKeyAlgorithm field of privateKeyInfo is not equal to the id-ecPublicKey
182            // object identifier defined in [RFC5480], throw a DataError.
183            if private_key_info.algorithm.oid != elliptic_curve::ALGORITHM_OID {
184                return Err(Error::Data);
185            }
186
187            // Step 2.5. If the parameters field of the privateKeyAlgorithm
188            // PrivateKeyAlgorithmIdentifier field of privateKeyInfo is not present, then throw a
189            // DataError.
190            // Step 2.6. Let params be the parameters field of the privateKeyAlgorithm
191            // PrivateKeyAlgorithmIdentifier field of privateKeyInfo.
192            // Step 2.7. If params is not an instance of the ECParameters ASN.1 type defined in
193            // [RFC5480] that specifies a namedCurve, then throw a DataError.
194            let params: EcParameters = if let Some(params) = private_key_info.algorithm.parameters {
195                params.decode_as().map_err(|_| Error::Data)?
196            } else {
197                return Err(Error::Data);
198            };
199
200            // Step 2.8. Let namedCurve be a string whose initial value is undefined.
201            // Step 2.9.
202            // If params is equivalent to the secp256r1 object identifier defined in [RFC5480]:
203            //     Set namedCurve to "P-256".
204            // If params is equivalent to the secp384r1 object identifier defined in [RFC5480]:
205            //     Set namedCurve to "P-384".
206            // If params is equivalent to the secp521r1 object identifier defined in [RFC5480]:
207            //     Set namedCurve to "P-521".
208            let named_curve = match params {
209                EcParameters::NamedCurve(NistP256::OID) => Some(NAMED_CURVE_P256),
210                EcParameters::NamedCurve(NistP384::OID) => Some(NAMED_CURVE_P384),
211                EcParameters::NamedCurve(NistP521::OID) => Some(NAMED_CURVE_P521),
212                _ => None,
213            };
214
215            // Step 2.10.
216            let handle = match named_curve {
217                // If namedCurve is not undefined:
218                Some(curve) => {
219                    // Step 2.10.1. Let ecPrivateKey be the result of performing the parse an ASN.1
220                    // structure algorithm, with data as the privateKey field of privateKeyInfo,
221                    // structure as the ASN.1 ECPrivateKey structure specified in Section 3 of
222                    // [RFC5915], and exactData set to true.
223                    // Step 2.10.2. If an error occurred while parsing, then throw a DataError.
224                    let ec_private_key = EcPrivateKey::try_from(private_key_info.private_key)
225                        .map_err(|_| Error::Data)?;
226
227                    // Step 2.10.3. If the parameters field of ecPrivateKey is present, and is not
228                    // an instance of the namedCurve ASN.1 type defined in [RFC5480], or does not
229                    // contain the same object identifier as the parameters field of the
230                    // privateKeyAlgorithm PrivateKeyAlgorithmIdentifier field of privateKeyInfo,
231                    // then throw a DataError.
232                    if ec_private_key
233                        .parameters
234                        .is_some_and(|parameters| parameters != params)
235                    {
236                        return Err(Error::Data);
237                    }
238
239                    // Step 2.10.4. Let key be a new CryptoKey that represents the Elliptic Curve
240                    // private key identified by performing the conversion steps defined in Section
241                    // 3 of [RFC5915] using ecPrivateKey.
242                    // NOTE: CryptoKey is created in Step 2.13 - 2.17.
243                    match curve {
244                        NAMED_CURVE_P256 => {
245                            let private_key = p256::SecretKey::try_from(ec_private_key)
246                                .map_err(|_| Error::Data)?;
247                            Handle::P256PrivateKey(private_key)
248                        },
249                        NAMED_CURVE_P384 => {
250                            let private_key = p384::SecretKey::try_from(ec_private_key)
251                                .map_err(|_| Error::Data)?;
252                            Handle::P384PrivateKey(private_key)
253                        },
254                        NAMED_CURVE_P521 => {
255                            let private_key = p521::SecretKey::try_from(ec_private_key)
256                                .map_err(|_| Error::Data)?;
257                            Handle::P521PrivateKey(private_key)
258                        },
259                        _ => unreachable!(),
260                    }
261                },
262                // Otherwise:
263                None => {
264                    // Step 2.10.1. Perform any key import steps defined by other applicable
265                    // specifications, passing format, privateKeyInfo and obtaining namedCurve and
266                    // key.
267                    // Step 2.10.2. If an error occurred or there are no applicable specifications,
268                    // throw a DataError.
269                    // NOTE: We currently do not support other applicable specifications.
270                    return Err(Error::Data);
271                },
272            };
273
274            // Step 2.11. If namedCurve is defined, and not equal to the namedCurve member of
275            // normalizedAlgorithm, throw a DataError.
276            if named_curve.is_some_and(|curve| curve != normalized_algorithm.named_curve) {
277                return Err(Error::Data);
278            }
279
280            // Step 2.12. If the private key value is not a valid point on the Elliptic Curve
281            // identified by the namedCurve member of normalizedAlgorithm throw a DataError.
282            // NOTE: Done in Step 2.10.
283
284            // Step 2.13. Set the [[type]] internal slot of key to "private"
285            // Step 2.14. Let algorithm be a new EcKeyAlgorithm.
286            // Step 2.15. Set the name attribute of algorithm to "ECDSA".
287            // Step 2.16. Set the namedCurve attribute of algorithm to namedCurve.
288            // Step 2.17. Set the [[algorithm]] internal slot of key to algorithm.
289            let algorithm = SubtleEcKeyAlgorithm {
290                name: ALG_ECDSA.to_string(),
291                named_curve: named_curve
292                    .expect("named_curve must exist here")
293                    .to_string(),
294            };
295            CryptoKey::new(
296                global,
297                KeyType::Private,
298                extractable,
299                KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algorithm),
300                usages,
301                handle,
302                can_gc,
303            )
304        },
305        // If format is "jwk":
306        KeyFormat::Jwk => {
307            // Step 2.1.
308            // If keyData is a JsonWebKey dictionary:
309            //     Let jwk equal keyData.
310            // Otherwise:
311            //     Throw a DataError.
312            let jwk = JsonWebKey::parse(GlobalScope::get_cx(), key_data)?;
313
314            // Step 2.2. If the d field is present and usages contains a value which is not "sign",
315            // or, if the d field is not present and usages contains a value which is not "verify"
316            // then throw a SyntaxError.
317            if (jwk.d.is_some() && usages.iter().any(|usage| *usage != KeyUsage::Sign)) ||
318                (jwk.d.is_none() && usages.iter().any(|usage| *usage != KeyUsage::Verify))
319            {
320                return Err(Error::Syntax(None));
321            }
322
323            // Step 2.3. If the kty field of jwk is not "EC", then throw a DataError.
324            if jwk.kty.as_ref().is_none_or(|kty| kty != "EC") {
325                return Err(Error::Data);
326            }
327
328            // Step 2.4. If usages is non-empty and the use field of jwk is present and is not
329            // "sig", then throw a DataError.
330            if !usages.is_empty() && jwk.use_.as_ref().is_some_and(|use_| use_ != "sig") {
331                return Err(Error::Data);
332            }
333
334            // Step 2.5. If the key_ops field of jwk is present, and is invalid according to the
335            // requirements of JSON Web Key [JWK], or it does not contain all of the specified
336            // usages values, then throw a DataError.
337            jwk.check_key_ops(&usages)?;
338
339            // Step 2.6. If the ext field of jwk is present and has the value false and extractable
340            // is true, then throw a DataError.
341            if jwk.ext.is_some_and(|ext| !ext) && extractable {
342                return Err(Error::Data);
343            }
344
345            // Step 2.7. Let namedCurve be a string whose value is equal to the crv field of jwk.
346            // Step 2.8. If namedCurve is not equal to the namedCurve member of
347            // normalizedAlgorithm, throw a DataError.
348            let named_curve = jwk
349                .crv
350                .filter(|crv| *crv == normalized_algorithm.named_curve)
351                .map(|crv| crv.to_string())
352                .ok_or(Error::Data)?;
353
354            // Step 2.9.
355            let (handle, key_type) =
356                // If namedCurve is "P-256", "P-384" or "P-521":
357                if matches!(
358                    named_curve.as_str(),
359                    NAMED_CURVE_P256 | NAMED_CURVE_P384 | NAMED_CURVE_P521
360                ) {
361                    // Step 9.1. Let algNamedCurve be a string whose initial value is undefined.
362                    // Step 9.2.
363                    // If the alg field is not present:
364                    //     Let algNamedCurve be undefined.
365                    // If the alg field is equal to the string "ES256":
366                    //     Let algNamedCurve be the string "P-256".
367                    // If the alg field is equal to the string "ES384":
368                    //     Let algNamedCurve be the string "P-384".
369                    // If the alg field is equal to the string "ES512":
370                    //     Let algNamedCurve be the string "P-521".
371                    // otherwise:
372                    //     throw a DataError.
373                    let alg = jwk.alg.map(|alg| alg.to_string());
374                    let alg_named_curve = match alg.as_deref() {
375                        None => None,
376                        Some("ES256") => Some(NAMED_CURVE_P256),
377                        Some("ES384") => Some(NAMED_CURVE_P384),
378                        Some("ES521") => Some(NAMED_CURVE_P521),
379                        _ => return Err(Error::Data),
380                    };
381
382                    // Step 9.3. If algNamedCurve is defined, and is not equal to namedCurve, throw
383                    // a DataError.
384                    if alg_named_curve.is_some_and(|alg_named_curve| alg_named_curve != named_curve) {
385                        return Err(Error::Data);
386                    }
387
388                    match jwk.d {
389                        // If the d field is present:
390                        Some(d) => {
391                            // Step 2.9.1. If jwk does not meet the requirements of Section 6.2.2 of
392                            // JSON Web Algorithms [JWA], then throw a DataError.
393                            let x = match jwk.x {
394                                Some(x) => Base64UrlUnpadded::decode_vec(&x.str())
395                                    .map_err(|_| Error::Data)?,
396                                None => return Err(Error::Data),
397                            };
398                            let y = match jwk.y {
399                                Some(y) => Base64UrlUnpadded::decode_vec(&y.str())
400                                    .map_err(|_| Error::Data)?,
401                                None => return Err(Error::Data),
402                            };
403                            let d =
404                                Base64UrlUnpadded::decode_vec(&d.str()).map_err(|_| Error::Data)?;
405
406                            // Step 2.9.2. Let key be a new CryptoKey object that represents the
407                            // Elliptic Curve private key identified by interpreting jwk according to
408                            // Section 6.2.2 of JSON Web Algorithms [JWA].
409                            // NOTE: CryptoKey is created in Step 2.12 - 2.15.
410                            let handle = match named_curve.as_str() {
411                                NAMED_CURVE_P256 => {
412                                    let private_key =
413                                        p256::SecretKey::from_slice(&d).map_err(|_| Error::Data)?;
414                                    let mut sec1_bytes = vec![4u8];
415                                    sec1_bytes.extend_from_slice(&x);
416                                    sec1_bytes.extend_from_slice(&y);
417                                    let encoded_point = EncodedPoint::from_bytes(&sec1_bytes)
418                                        .map_err(|_| Error::Data)?;
419                                    NistP256::validate_public_key(&private_key, &encoded_point)
420                                        .map_err(|_| Error::Data)?;
421                                    Handle::P256PrivateKey(private_key)
422                                },
423                                NAMED_CURVE_P384 => {
424                                    let private_key =
425                                        p384::SecretKey::from_slice(&d).map_err(|_| Error::Data)?;
426                                    let mut sec1_bytes = vec![4u8];
427                                    sec1_bytes.extend_from_slice(&x);
428                                    sec1_bytes.extend_from_slice(&y);
429                                    let encoded_point = EncodedPoint::from_bytes(&sec1_bytes)
430                                        .map_err(|_| Error::Data)?;
431                                    NistP384::validate_public_key(&private_key, &encoded_point)
432                                        .map_err(|_| Error::Data)?;
433                                    Handle::P384PrivateKey(private_key)
434                                },
435                                NAMED_CURVE_P521 => {
436                                    let private_key =
437                                        p521::SecretKey::from_slice(&d).map_err(|_| Error::Data)?;
438                                    let mut sec1_bytes = vec![4u8];
439                                    sec1_bytes.extend_from_slice(&x);
440                                    sec1_bytes.extend_from_slice(&y);
441                                    let encoded_point = EncodedPoint::from_bytes(&sec1_bytes)
442                                        .map_err(|_| Error::Data)?;
443                                    NistP521::validate_public_key(&private_key, &encoded_point)
444                                        .map_err(|_| Error::Data)?;
445                                    Handle::P521PrivateKey(private_key)
446                                },
447                                _ => unreachable!(),
448                            };
449
450                            // Step 2.9.3. Set the [[type]] internal slot of Key to "private".
451                            let key_type = KeyType::Private;
452
453                            (handle, key_type)
454                        },
455                        // Otherwise:
456                        None => {
457                            // Step 2.9.1. If jwk does not meet the requirements of Section 6.2.1 of
458                            // JSON Web Algorithms [JWA], then throw a DataError.
459                            let x = match jwk.x {
460                                Some(x) => Base64UrlUnpadded::decode_vec(&x.str())
461                                    .map_err(|_| Error::Data)?,
462                                None => return Err(Error::Data),
463                            };
464                            let y = match jwk.y {
465                                Some(y) => Base64UrlUnpadded::decode_vec(&y.str())
466                                    .map_err(|_| Error::Data)?,
467                                None => return Err(Error::Data),
468                            };
469
470                            // Step 2.9.2. Let key be a new CryptoKey object that represents the
471                            // Elliptic Curve public key identified by interpreting jwk according to
472                            // Section 6.2.1 of JSON Web Algorithms [JWA].
473                            // NOTE: CryptoKey is created in Step 2.12 - 2.15.
474                            let handle = match named_curve.as_str() {
475                                NAMED_CURVE_P256 => {
476                                    let mut sec1_bytes = vec![4u8];
477                                    sec1_bytes.extend_from_slice(&x);
478                                    sec1_bytes.extend_from_slice(&y);
479                                    let encoded_point = EncodedPoint::from_bytes(&sec1_bytes)
480                                        .map_err(|_| Error::Data)?;
481                                    let public_key =
482                                        p256::PublicKey::from_encoded_point(&encoded_point)
483                                            .into_option()
484                                            .ok_or(Error::Data)?;
485                                    Handle::P256PublicKey(public_key)
486                                },
487                                NAMED_CURVE_P384 => {
488                                    let mut sec1_bytes = vec![4u8];
489                                    sec1_bytes.extend_from_slice(&x);
490                                    sec1_bytes.extend_from_slice(&y);
491                                    let encoded_point = EncodedPoint::from_bytes(&sec1_bytes)
492                                        .map_err(|_| Error::Data)?;
493                                    let public_key =
494                                        p384::PublicKey::from_encoded_point(&encoded_point)
495                                            .into_option()
496                                            .ok_or(Error::Data)?;
497                                    Handle::P384PublicKey(public_key)
498                                },
499                                NAMED_CURVE_P521 => {
500                                    let mut sec1_bytes = vec![4u8];
501                                    sec1_bytes.extend_from_slice(&x);
502                                    sec1_bytes.extend_from_slice(&y);
503                                    let encoded_point = EncodedPoint::from_bytes(&sec1_bytes)
504                                        .map_err(|_| Error::Data)?;
505                                    let public_key =
506                                        p521::PublicKey::from_encoded_point(&encoded_point)
507                                            .into_option()
508                                            .ok_or(Error::Data)?;
509                                    Handle::P521PublicKey(public_key)
510                                },
511                                _ => unreachable!(),
512                            };
513
514                            // Step 2.9.3. Set the [[type]] internal slot of Key to "public".
515                            let key_type = KeyType::Public;
516
517                            (handle, key_type)
518                        },
519                    }
520                }
521                // Otherwise
522                else {
523                    // Step 2.9.1. Perform any key import steps defined by other applicable
524                    // specifications, passing format, jwk and obtaining key.
525                    // Step 2.9.2. If an error occurred or there are no applicable specifications,
526                    // throw a DataError.
527                    // NOTE: We currently do not support other applicable specifications.
528                    return Err(Error::Data);
529                };
530
531            // Step 2.10. If the key value is not a valid point on the Elliptic Curve identified by
532            // the namedCurve member of normalizedAlgorithm throw a DataError.
533            // NOTE: Done in Step 2.9.
534
535            // Step 2.11. Let algorithm be a new instance of an EcKeyAlgorithm object.
536            // Step 2.12. Set the name attribute of algorithm to "ECDSA".
537            // Step 2.13. Set the namedCurve attribute of algorithm to namedCurve.
538            // Step 2.14. Set the [[algorithm]] internal slot of key to algorithm.
539            let algorithm = SubtleEcKeyAlgorithm {
540                name: ALG_ECDSA.to_string(),
541                named_curve,
542            };
543            CryptoKey::new(
544                global,
545                key_type,
546                extractable,
547                KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algorithm),
548                usages,
549                handle,
550                can_gc,
551            )
552        },
553        // If format is "raw":
554        KeyFormat::Raw => {
555            // Step 2.1. If the namedCurve member of normalizedAlgorithm is not a named curve, then
556            // throw a DataError.
557            if !SUPPORTED_CURVES
558                .iter()
559                .any(|&supported_curve| supported_curve == normalized_algorithm.named_curve)
560            {
561                return Err(Error::Data);
562            }
563
564            // Step 2.2. If usages contains a value which is not "verify" then throw a SyntaxError.
565            if usages.iter().any(|usage| *usage != KeyUsage::Verify) {
566                return Err(Error::Syntax(None));
567            }
568
569            // Step 2.3.
570            // If namedCurve is "P-256", "P-384" or "P-521":
571            let handle = if matches!(
572                normalized_algorithm.named_curve.as_str(),
573                NAMED_CURVE_P256 | NAMED_CURVE_P384 | NAMED_CURVE_P521
574            ) {
575                // Step 2.3.1. Let Q be the elliptic curve point on the curve identified by the
576                // namedCurve member of normalizedAlgorithm identified by performing the conversion
577                // steps defined in Section 2.3.4 of [SEC1] on keyData.
578                // Step 2.3.2. The uncompressed point format MUST be supported.
579                // Step 2.3.3. If the implementation does not support the compressed point format
580                // and a compressed point is provided, throw a DataError.
581                // Step 2.3.4. If a decode error occurs or an identity point is found, throw a
582                // DataError.
583                match normalized_algorithm.named_curve.as_str() {
584                    NAMED_CURVE_P256 => {
585                        let q =
586                            p256::PublicKey::from_sec1_bytes(key_data).map_err(|_| Error::Data)?;
587                        Handle::P256PublicKey(q)
588                    },
589                    NAMED_CURVE_P384 => {
590                        let q =
591                            p384::PublicKey::from_sec1_bytes(key_data).map_err(|_| Error::Data)?;
592                        Handle::P384PublicKey(q)
593                    },
594                    NAMED_CURVE_P521 => {
595                        let q =
596                            p521::PublicKey::from_sec1_bytes(key_data).map_err(|_| Error::Data)?;
597                        Handle::P521PublicKey(q)
598                    },
599                    _ => unreachable!(),
600                }
601
602                // Step 2.3.5. Let key be a new CryptoKey that represents Q.
603                // NOTE: CryptoKey is created in Step 2.7 - 2.8.
604            }
605            // Otherwise:
606            else {
607                // Step. 2.3.1. Perform any key import steps defined by other applicable
608                // specifications, passing format, keyData and obtaining key.
609                // Step. 2.3.2. If an error occured or there are no applicable specifications,
610                // throw a DataError.
611                // NOTE: We currently do not support other applicable specifications.
612                return Err(Error::Data);
613            };
614
615            // Step 2.4. Let algorithm be a new EcKeyAlgorithm object.
616            // Step 2.5. Set the name attribute of algorithm to "ECDSA".
617            // Step 2.6. Set the namedCurve attribute of algorithm to equal the namedCurve member
618            // of normalizedAlgorithm.
619            let algorithm = SubtleEcKeyAlgorithm {
620                name: ALG_ECDSA.to_string(),
621                named_curve: normalized_algorithm.named_curve.clone(),
622            };
623
624            // Step 2.7. Set the [[type]] internal slot of key to "public"
625            // Step 2.8. Set the [[algorithm]] internal slot of key to algorithm.
626            CryptoKey::new(
627                global,
628                KeyType::Public,
629                extractable,
630                KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algorithm),
631                usages,
632                handle,
633                can_gc,
634            )
635        },
636    };
637
638    // Step 3. Return key.
639    Ok(key)
640}
641
642/// <https://w3c.github.io/webcrypto/#ecdsa-operations-export-key>
643pub(crate) fn export_key(format: KeyFormat, key: &CryptoKey) -> Result<ExportedKey, Error> {
644    // Step 1. Let key be the CryptoKey to be exported.
645
646    // Step 2. If the underlying cryptographic key material represented by the [[handle]] internal
647    // slot of key cannot be accessed, then throw an OperationError.
648    // NOTE: Done in Step 3.
649
650    // Step 3.
651    let result = match format {
652        // If format is "spki":
653        KeyFormat::Spki => {
654            // Step 3.1. If the [[type]] internal slot of key is not "public", then throw an
655            // InvalidAccessError.
656            if key.Type() != KeyType::Public {
657                return Err(Error::InvalidAccess);
658            }
659
660            // Step 3.2.
661            // Let data be an instance of the SubjectPublicKeyInfo ASN.1 structure defined in
662            // [RFC5280] with the following properties:
663            //     * Set the algorithm field to an AlgorithmIdentifier ASN.1 type with the
664            //       following properties:
665            //         * Set the algorithm field to the OID id-ecPublicKey defined in [RFC5480].
666            //         * Set the parameters field to an instance of the ECParameters ASN.1 type
667            //           defined in [RFC5480] as follows:
668            //             If the namedCurve attribute of the [[algorithm]] internal slot of key is
669            //             "P-256", "P-384" or "P-521":
670            //                 Let keyData be the byte sequence that represents the Elliptic Curve
671            //                 public key represented by the [[handle]] internal slot of key
672            //                 according to the encoding rules specified in Section 2.2 of
673            //                 [RFC5480] and using the uncompressed form. and keyData.
674            //                     If the namedCurve attribute of the [[algorithm]] internal slot
675            //                     of key is "P-256":
676            //                         Set parameters to the namedCurve choice with value equal to
677            //                         the object identifier secp256r1 defined in [RFC5480]
678            //                     If the namedCurve attribute of the [[algorithm]] internal slot
679            //                     of key is "P-384":
680            //                         Set parameters to the namedCurve choice with value equal to
681            //                         the object identifier secp384r1 defined in [RFC5480]
682            //                     If the namedCurve attribute of the [[algorithm]] internal slot
683            //                     of key is "P-521":
684            //                         Set parameters to the namedCurve choice with value equal to
685            //                         the object identifier secp521r1 defined in [RFC5480]
686            //             Otherwise:
687            //                 1. Perform any key export steps defined by other applicable
688            //                    specifications, passing format and the namedCurve attribute of
689            //                    the [[algorithm]] internal slot of key and obtaining
690            //                    namedCurveOid and keyData.
691            //                 2. Set parameters to the namedCurve choice with value equal to the
692            //                    object identifier namedCurveOid.
693            //     * Set the subjectPublicKey field to keyData.
694            // NOTE: We currently do not support other applicable specifications.
695            let data = match key.handle() {
696                Handle::P256PublicKey(public_key) => public_key.to_public_key_der(),
697                Handle::P384PublicKey(public_key) => public_key.to_public_key_der(),
698                Handle::P521PublicKey(public_key) => public_key.to_public_key_der(),
699                _ => return Err(Error::Operation),
700            }
701            .map_err(|_| Error::Operation)?;
702
703            // Step 3.3. Let result be the result of DER-encoding data.
704            ExportedKey::Raw(data.to_vec())
705        },
706        // If format is "pkcs8":
707        KeyFormat::Pkcs8 => {
708            // Step 3.1. If the [[type]] internal slot of key is not "private", then throw an
709            // InvalidAccessError.
710            if key.Type() != KeyType::Private {
711                return Err(Error::InvalidAccess);
712            }
713
714            // Step 3.2.
715            // Let data be an instance of the PrivateKeyInfo ASN.1 structure defined in [RFC5208]
716            // with the following properties:
717            //     * Set the version field to 0.
718            //     * Set the privateKeyAlgorithm field to a PrivateKeyAlgorithmIdentifier ASN.1
719            //       type with the following properties:
720            //         * Set the algorithm field to the OID id-ecPublicKey defined in [RFC5480].
721            //         * Set the parameters field to an instance of the ECParameters ASN.1 type
722            //           defined in [RFC5480] as follows:
723            //             If the namedCurve attribute of the [[algorithm]] internal slot of key is
724            //             "P-256", "P-384" or "P-521":
725            //                 Let keyData be the result of DER-encoding an instance of the
726            //                 ECPrivateKey structure defined in Section 3 of [RFC5915] for the
727            //                 Elliptic Curve private key represented by the [[handle]] internal
728            //                 slot of key and that conforms to the following:
729            //                     * The parameters field is present, and is equivalent to the
730            //                       parameters field of the privateKeyAlgorithm field of this
731            //                       PrivateKeyInfo ASN.1 structure.
732            //                     * The publicKey field is present and represents the Elliptic
733            //                       Curve public key associated with the Elliptic Curve private key
734            //                       represented by the [[handle]] internal slot of key.
735            //                     * If the namedCurve attribute of the [[algorithm]] internal slot
736            //                       of key is "P-256":
737            //                         Set parameters to the namedCurve choice with value equal to
738            //                         the object identifier secp256r1 defined in [RFC5480]
739            //                     * If the namedCurve attribute of the [[algorithm]] internal slot
740            //                       of key is "P-384":
741            //                         Set parameters to the namedCurve choice with value equal to
742            //                         the object identifier secp384r1 defined in [RFC5480]
743            //                     * If the namedCurve attribute of the [[algorithm]] internal slot
744            //                       of key is "P-521":
745            //                         Set parameters to the namedCurve choice with value equal to
746            //                         the object identifier secp521r1 defined in [RFC5480]
747            //             Otherwise:
748            //                 1. Perform any key export steps defined by other applicable
749            //                    specifications, passing format and the namedCurve attribute of
750            //                    the [[algorithm]] internal slot of key and obtaining
751            //                    namedCurveOid and keyData.
752            //                 2. Set parameters to the namedCurve choice with value equal to the
753            //                    object identifier namedCurveOid.
754            //     * Set the privateKey field to keyData.
755            // NOTE: We currently do not support other applicable specifications.
756            let data = match key.handle() {
757                Handle::P256PrivateKey(private_key) => private_key.to_pkcs8_der(),
758                Handle::P384PrivateKey(private_key) => private_key.to_pkcs8_der(),
759                Handle::P521PrivateKey(private_key) => private_key.to_pkcs8_der(),
760                _ => return Err(Error::Operation),
761            }
762            .map_err(|_| Error::Operation)?;
763
764            // Step 3.3. Let result be the result of DER-encoding data.
765            ExportedKey::Raw(data.as_bytes().to_vec())
766        },
767        // If format is "jwk":
768        KeyFormat::Jwk => {
769            // Step 3.1. Let jwk be a new JsonWebKey dictionary.
770            // Step 3.2. Set the kty attribute of jwk to "EC".
771            let mut jwk = JsonWebKey {
772                kty: Some(DOMString::from("EC")),
773                ..Default::default()
774            };
775
776            // Step 3.3.
777            let KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algorithm) = key.algorithm() else {
778                return Err(Error::Operation);
779            };
780            // If the namedCurve attribute of the [[algorithm]] internal slot of key is "P-256",
781            // "P-384" or "P-521":
782            if matches!(
783                algorithm.named_curve.as_str(),
784                NAMED_CURVE_P256 | NAMED_CURVE_P384 | NAMED_CURVE_P521
785            ) {
786                // Step 3.3.1.
787                // If the namedCurve attribute of the [[algorithm]] internal slot of key is
788                // "P-256":
789                //     Set the crv attribute of jwk to "P-256"
790                // If the namedCurve attribute of the [[algorithm]] internal slot of key is
791                // "P-384":
792                //     Set the crv attribute of jwk to "P-384"
793                // If the namedCurve attribute of the [[algorithm]] internal slot of key is
794                // "P-521":
795                //     Set the crv attribute of jwk to "P-521"
796                jwk.crv = Some(DOMString::from(algorithm.named_curve.as_str()));
797
798                // Step 3.3.2. Set the x attribute of jwk according to the definition in Section
799                // 6.2.1.2 of JSON Web Algorithms [JWA].
800                // Step 3.3.3. Set the y attribute of jwk according to the definition in Section
801                // 6.2.1.3 of JSON Web Algorithms [JWA].
802                let (x, y) = match key.handle() {
803                    Handle::P256PublicKey(public_key) => {
804                        let encoded_point = public_key.to_encoded_point(false);
805                        (
806                            encoded_point.x().ok_or(Error::Operation)?.to_vec(),
807                            encoded_point.y().ok_or(Error::Operation)?.to_vec(),
808                        )
809                    },
810                    Handle::P384PublicKey(public_key) => {
811                        let encoded_point = public_key.to_encoded_point(false);
812                        (
813                            encoded_point.x().ok_or(Error::Operation)?.to_vec(),
814                            encoded_point.y().ok_or(Error::Operation)?.to_vec(),
815                        )
816                    },
817                    Handle::P521PublicKey(public_key) => {
818                        let encoded_point = public_key.to_encoded_point(false);
819                        (
820                            encoded_point.x().ok_or(Error::Operation)?.to_vec(),
821                            encoded_point.y().ok_or(Error::Operation)?.to_vec(),
822                        )
823                    },
824                    Handle::P256PrivateKey(private_key) => {
825                        let public_key = private_key.public_key();
826                        let encoded_point = public_key.to_encoded_point(false);
827                        (
828                            encoded_point.x().ok_or(Error::Operation)?.to_vec(),
829                            encoded_point.y().ok_or(Error::Operation)?.to_vec(),
830                        )
831                    },
832                    Handle::P384PrivateKey(private_key) => {
833                        let public_key = private_key.public_key();
834                        let encoded_point = public_key.to_encoded_point(false);
835                        (
836                            encoded_point.x().ok_or(Error::Operation)?.to_vec(),
837                            encoded_point.y().ok_or(Error::Operation)?.to_vec(),
838                        )
839                    },
840                    Handle::P521PrivateKey(private_key) => {
841                        let public_key = private_key.public_key();
842                        let encoded_point = public_key.to_encoded_point(false);
843                        (
844                            encoded_point.x().ok_or(Error::Operation)?.to_vec(),
845                            encoded_point.y().ok_or(Error::Operation)?.to_vec(),
846                        )
847                    },
848                    _ => return Err(Error::Operation),
849                };
850                jwk.x = Some(Base64UrlUnpadded::encode_string(&x).into());
851                jwk.y = Some(Base64UrlUnpadded::encode_string(&y).into());
852
853                // Step 3.3.4.
854                // If the [[type]] internal slot of key is "private"
855                //     Set the d attribute of jwk according to the definition in Section 6.2.2.1 of
856                //     JSON Web Algorithms [JWA].
857                if key.Type() == KeyType::Private {
858                    let d = match key.handle() {
859                        Handle::P256PrivateKey(private_key) => private_key.to_bytes().to_vec(),
860                        Handle::P384PrivateKey(private_key) => private_key.to_bytes().to_vec(),
861                        Handle::P521PrivateKey(private_key) => private_key.to_bytes().to_vec(),
862                        _ => return Err(Error::NotSupported),
863                    };
864                    jwk.d = Some(Base64UrlUnpadded::encode_string(&d).into());
865                }
866            }
867            // Otherwise:
868            else {
869                // Step 3.3.1. Perform any key export steps defined by other applicable
870                // specifications, passing format and the namedCurve attribute of the [[algorithm]]
871                // internal slot of key and obtaining namedCurve and a new value of jwk.
872                // Step 3.3.2. Set the crv attribute of jwk to namedCurve.
873                // NOTE: We currently do not support other applicable specifications.
874                unreachable!()
875            }
876
877            // Step 3.4. Set the key_ops attribute of jwk to the usages attribute of key.
878            jwk.key_ops = Some(
879                key.usages()
880                    .iter()
881                    .map(|usage| DOMString::from(usage.as_str()))
882                    .collect::<Vec<DOMString>>(),
883            );
884
885            // Step 3.4. Set the ext attribute of jwk to the [[extractable]] internal slot of key.
886            jwk.ext = Some(key.Extractable());
887
888            // Step 3.4. Let result be jwk.
889            ExportedKey::Jwk(Box::new(jwk))
890        },
891        // If format is "raw":
892        KeyFormat::Raw => {
893            // Step 3.1. If the [[type]] internal slot of key is not "public", then throw an
894            // InvalidAccessError.
895            if key.Type() != KeyType::Public {
896                return Err(Error::InvalidAccess);
897            }
898
899            // Step 3.2.
900            // If the namedCurve attribute of the [[algorithm]] internal slot of key is "P-256",
901            // "P-384" or "P-521":
902            //     Let data be a byte sequence representing the Elliptic Curve point Q represented
903            //     by the [[handle]] internal slot of key according to [SEC1] 2.3.3 using the
904            //     uncompressed format.
905            // Otherwise:
906            //     Perform any key export steps defined by other applicable specifications, passing
907            //     format and the namedCurve attribute of the [[algorithm]] internal slot of key
908            //     and obtaining namedCurve and data.
909            //     NOTE: We currently do not support other applicable specifications.
910            let named_curve =
911                if let KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algorithm) = key.algorithm() {
912                    algorithm.named_curve.as_str()
913                } else {
914                    return Err(Error::Operation);
915                };
916            let data = if matches!(
917                named_curve,
918                NAMED_CURVE_P256 | NAMED_CURVE_P384 | NAMED_CURVE_P521
919            ) {
920                match key.handle() {
921                    Handle::P256PublicKey(public_key) => public_key.to_sec1_bytes().to_vec(),
922                    Handle::P384PublicKey(public_key) => public_key.to_sec1_bytes().to_vec(),
923                    Handle::P521PublicKey(public_key) => public_key.to_sec1_bytes().to_vec(),
924                    _ => return Err(Error::Operation),
925                }
926            } else {
927                return Err(Error::NotSupported);
928            };
929
930            // Step 3.3. Let result be data.
931            ExportedKey::Raw(data)
932        },
933        // Otherwise: throw a NotSupportedError. (Unreachable)
934    };
935
936    // Step 4. Return result.
937    Ok(result)
938}