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