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