script/dom/
subtlecrypto.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
5mod aes_operation;
6mod hkdf_operation;
7mod hmac_operation;
8mod pbkdf2_operation;
9mod sha_operation;
10
11use std::ptr;
12use std::rc::Rc;
13use std::str::FromStr;
14
15use dom_struct::dom_struct;
16use js::conversions::ConversionResult;
17use js::jsapi::{Heap, JSObject};
18use js::jsval::{ObjectValue, UndefinedValue};
19use js::rust::wrappers::JS_ParseJSON;
20use js::rust::{HandleValue, MutableHandleValue};
21use js::typedarray::ArrayBufferU8;
22use servo_rand::ServoRng;
23
24use crate::dom::bindings::buffer_source::create_buffer_source;
25use crate::dom::bindings::cell::DomRefCell;
26use crate::dom::bindings::codegen::Bindings::CryptoKeyBinding::{
27    CryptoKeyMethods, KeyType, KeyUsage,
28};
29use crate::dom::bindings::codegen::Bindings::SubtleCryptoBinding::{
30    AesCbcParams, AesCtrParams, AesDerivedKeyParams, AesGcmParams, AesKeyAlgorithm,
31    AesKeyGenParams, Algorithm, AlgorithmIdentifier, HkdfParams, HmacImportParams,
32    HmacKeyAlgorithm, HmacKeyGenParams, JsonWebKey, KeyAlgorithm, KeyFormat, Pbkdf2Params,
33    RsaOtherPrimesInfo, SubtleCryptoMethods,
34};
35use crate::dom::bindings::codegen::UnionTypes::{
36    ArrayBufferViewOrArrayBuffer, ArrayBufferViewOrArrayBufferOrJsonWebKey, ObjectOrString,
37};
38use crate::dom::bindings::conversions::SafeToJSValConvertible;
39use crate::dom::bindings::error::{Error, Fallible};
40use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
41use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
42use crate::dom::bindings::root::DomRoot;
43use crate::dom::bindings::str::{DOMString, serialize_jsval_to_json_utf8};
44use crate::dom::bindings::trace::RootedTraceableBox;
45use crate::dom::cryptokey::{CryptoKey, CryptoKeyOrCryptoKeyPair};
46use crate::dom::globalscope::GlobalScope;
47use crate::dom::promise::Promise;
48use crate::realms::InRealm;
49use crate::script_runtime::{CanGc, JSContext};
50
51// String constants for algorithms/curves
52const ALG_AES_CBC: &str = "AES-CBC";
53const ALG_AES_CTR: &str = "AES-CTR";
54const ALG_AES_GCM: &str = "AES-GCM";
55const ALG_AES_KW: &str = "AES-KW";
56const ALG_SHA1: &str = "SHA-1";
57const ALG_SHA256: &str = "SHA-256";
58const ALG_SHA384: &str = "SHA-384";
59const ALG_SHA512: &str = "SHA-512";
60const ALG_HMAC: &str = "HMAC";
61const ALG_HKDF: &str = "HKDF";
62const ALG_PBKDF2: &str = "PBKDF2";
63const ALG_RSASSA_PKCS1: &str = "RSASSA-PKCS1-v1_5";
64const ALG_RSA_OAEP: &str = "RSA-OAEP";
65const ALG_RSA_PSS: &str = "RSA-PSS";
66const ALG_ECDH: &str = "ECDH";
67const ALG_ECDSA: &str = "ECDSA";
68
69static SUPPORTED_ALGORITHMS: &[&str] = &[
70    ALG_AES_CBC,
71    ALG_AES_CTR,
72    ALG_AES_GCM,
73    ALG_AES_KW,
74    ALG_SHA1,
75    ALG_SHA256,
76    ALG_SHA384,
77    ALG_SHA512,
78    ALG_HMAC,
79    ALG_HKDF,
80    ALG_PBKDF2,
81    ALG_RSASSA_PKCS1,
82    ALG_RSA_OAEP,
83    ALG_RSA_PSS,
84    ALG_ECDH,
85    ALG_ECDSA,
86];
87
88const NAMED_CURVE_P256: &str = "P-256";
89const NAMED_CURVE_P384: &str = "P-384";
90const NAMED_CURVE_P521: &str = "P-521";
91#[allow(dead_code)]
92static SUPPORTED_CURVES: &[&str] = &[NAMED_CURVE_P256, NAMED_CURVE_P384, NAMED_CURVE_P521];
93
94/// <https://w3c.github.io/webcrypto/#supported-operation>
95#[allow(dead_code)]
96enum Operation {
97    Encrypt,
98    Decrypt,
99    Sign,
100    Verify,
101    Digest,
102    GenerateKey,
103    DeriveKey,
104    DeriveBits,
105    ImportKey,
106    ExportKey,
107    WrapKey,
108    UnwrapKey,
109    GetKeyLength,
110}
111
112#[dom_struct]
113pub(crate) struct SubtleCrypto {
114    reflector_: Reflector,
115    #[no_trace]
116    rng: DomRefCell<ServoRng>,
117}
118
119impl SubtleCrypto {
120    fn new_inherited() -> SubtleCrypto {
121        SubtleCrypto {
122            reflector_: Reflector::new(),
123            rng: DomRefCell::new(ServoRng::default()),
124        }
125    }
126
127    pub(crate) fn new(global: &GlobalScope, can_gc: CanGc) -> DomRoot<SubtleCrypto> {
128        reflect_dom_object(Box::new(SubtleCrypto::new_inherited()), global, can_gc)
129    }
130
131    /// Queue a global task on the crypto task source, given realm's global object, to resolve
132    /// promise with the result of creating an ArrayBuffer in realm, containing data. If it fails
133    /// to create buffer source, reject promise with a JSFailedError.
134    fn resolve_promise_with_data(&self, promise: Rc<Promise>, data: Vec<u8>) {
135        let trusted_promise = TrustedPromise::new(promise);
136        self.global().task_manager().crypto_task_source().queue(
137            task!(resolve_data: move || {
138                let promise = trusted_promise.root();
139
140                let cx = GlobalScope::get_cx();
141                rooted!(in(*cx) let mut array_buffer_ptr = ptr::null_mut::<JSObject>());
142                match create_buffer_source::<ArrayBufferU8>(cx, &data, array_buffer_ptr.handle_mut(), CanGc::note()) {
143                    Ok(_) => promise.resolve_native(&*array_buffer_ptr, CanGc::note()),
144                    Err(_) => promise.reject_error(Error::JSFailed, CanGc::note()),
145                }
146            }),
147        );
148    }
149
150    /// Queue a global task on the crypto task source, given realm's global object, to resolve
151    /// promise with the result of converting a JsonWebKey dictionary to an ECMAScript Object in
152    /// realm, as defined by [WebIDL].
153    fn resolve_promise_with_jwk(&self, promise: Rc<Promise>, jwk: Box<JsonWebKey>) {
154        // NOTE: Serialize the JsonWebKey dictionary by stringifying it, in order to pass it to
155        // other threads.
156        let cx = GlobalScope::get_cx();
157        let stringified_jwk = match jwk.stringify(cx) {
158            Ok(stringified_jwk) => stringified_jwk.to_string(),
159            Err(error) => {
160                self.reject_promise_with_error(promise, error);
161                return;
162            },
163        };
164
165        let trusted_subtle = Trusted::new(self);
166        let trusted_promise = TrustedPromise::new(promise);
167        self.global()
168            .task_manager()
169            .crypto_task_source()
170            .queue(task!(resolve_jwk: move || {
171                let subtle = trusted_subtle.root();
172                let promise = trusted_promise.root();
173
174                let cx = GlobalScope::get_cx();
175                match JsonWebKey::parse(cx, stringified_jwk.as_bytes()) {
176                    Ok(jwk) => {
177                        rooted!(in(*cx) let mut rval = UndefinedValue());
178                        jwk.safe_to_jsval(cx, rval.handle_mut());
179                        rooted!(in(*cx) let mut object = rval.to_object());
180                        promise.resolve_native(&*object, CanGc::note());
181                    },
182                    Err(error) => {
183                        subtle.reject_promise_with_error(promise, error);
184                        return;
185                    },
186                }
187            }));
188    }
189
190    /// Queue a global task on the crypto task source, given realm's global object, to resolve
191    /// promise with a CryptoKey.
192    fn resolve_promise_with_key(&self, promise: Rc<Promise>, key: DomRoot<CryptoKey>) {
193        let trusted_key = Trusted::new(&*key);
194        let trusted_promise = TrustedPromise::new(promise);
195        self.global()
196            .task_manager()
197            .crypto_task_source()
198            .queue(task!(resolve_key: move || {
199                let key = trusted_key.root();
200                let promise = trusted_promise.root();
201                promise.resolve_native(&key, CanGc::note());
202            }));
203    }
204
205    /// Queue a global task on the crypto task source, given realm's global object, to resolve
206    /// promise with a bool value.
207    fn resolve_promise_with_bool(&self, promise: Rc<Promise>, result: bool) {
208        let trusted_promise = TrustedPromise::new(promise);
209        self.global().task_manager().crypto_task_source().queue(
210            task!(generate_key_result: move || {
211                let promise = trusted_promise.root();
212                promise.resolve_native(&result, CanGc::note());
213            }),
214        );
215    }
216
217    /// Queue a global task on the crypto task source, given realm's global object, to reject
218    /// promise with an error.
219    fn reject_promise_with_error(&self, promise: Rc<Promise>, error: Error) {
220        let trusted_promise = TrustedPromise::new(promise);
221        self.global()
222            .task_manager()
223            .crypto_task_source()
224            .queue(task!(reject_error: move || {
225                let promise = trusted_promise.root();
226                promise.reject_error(error, CanGc::note());
227            }));
228    }
229}
230
231impl SubtleCryptoMethods<crate::DomTypeHolder> for SubtleCrypto {
232    /// <https://w3c.github.io/webcrypto/#SubtleCrypto-method-encrypt>
233    fn Encrypt(
234        &self,
235        cx: JSContext,
236        algorithm: AlgorithmIdentifier,
237        key: &CryptoKey,
238        data: ArrayBufferViewOrArrayBuffer,
239        comp: InRealm,
240        can_gc: CanGc,
241    ) -> Rc<Promise> {
242        // Step 1. Let algorithm and key be the algorithm and key parameters passed to the
243        // encrypt() method, respectively.
244        // NOTE: We did that in method parameter.
245
246        // Step 2. Let data be the result of getting a copy of the bytes held by the data parameter
247        // passed to the encrypt() method.
248        let data = match data {
249            ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
250            ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
251        };
252
253        // Step 3. Let normalizedAlgorithm be the result of normalizing an algorithm, with alg set
254        // to algorithm and op set to "encrypt".
255        // Step 4. If an error occurred, return a Promise rejected with normalizedAlgorithm.
256        let promise = Promise::new_in_current_realm(comp, can_gc);
257        let normalized_algorithm =
258            match normalize_algorithm(cx, &Operation::Encrypt, &algorithm, can_gc) {
259                Ok(normalized_algorithm) => normalized_algorithm,
260                Err(error) => {
261                    promise.reject_error(error, can_gc);
262                    return promise;
263                },
264            };
265
266        // Step 5. Let realm be the relevant realm of this.
267        // Step 6. Let promise be a new Promise.
268        // NOTE: We did that in preparation of Step 4.
269
270        // Step 7. Return promise and perform the remaining steps in parallel.
271        let this = Trusted::new(self);
272        let trusted_promise = TrustedPromise::new(promise.clone());
273        let trusted_key = Trusted::new(key);
274        self.global()
275            .task_manager()
276            .dom_manipulation_task_source()
277            .queue(task!(encrypt: move || {
278                let subtle = this.root();
279                let promise = trusted_promise.root();
280                let key = trusted_key.root();
281
282                // Step 8. If the following steps or referenced procedures say to throw an error,
283                // queue a global task on the crypto task source, given realm's global object, to
284                // reject promise with the returned error; and then terminate the algorithm.
285
286                // Step 9. If the name member of normalizedAlgorithm is not equal to the name
287                // attribute of the [[algorithm]] internal slot of key then throw an
288                // InvalidAccessError.
289                if normalized_algorithm.name() != key.algorithm().name() {
290                    subtle.reject_promise_with_error(promise, Error::InvalidAccess);
291                    return;
292                }
293
294                // Step 10. If the [[usages]] internal slot of key does not contain an entry that
295                // is "encrypt", then throw an InvalidAccessError.
296                if !key.usages().contains(&KeyUsage::Encrypt) {
297                    subtle.reject_promise_with_error(promise, Error::InvalidAccess);
298                    return;
299                }
300
301                // Step 11. Let ciphertext be the result of performing the encrypt operation
302                // specified by normalizedAlgorithm using algorithm and key and with data as
303                // plaintext.
304                let ciphertext = match normalized_algorithm.encrypt(&key, &data) {
305                    Ok(ciphertext) => ciphertext,
306                    Err(error) => {
307                        subtle.reject_promise_with_error(promise, error);
308                        return;
309                    },
310                };
311
312                // Step 12. Queue a global task on the crypto task source, given realm's global
313                // object, to perform the remaining steps.
314                // Step 13. Let result be the result of creating an ArrayBuffer in realm,
315                // containing ciphertext.
316                // Step 14. Resolve promise with result.
317                subtle.resolve_promise_with_data(promise, ciphertext);
318            }));
319        promise
320    }
321
322    /// <https://w3c.github.io/webcrypto/#SubtleCrypto-method-decrypt>
323    fn Decrypt(
324        &self,
325        cx: JSContext,
326        algorithm: AlgorithmIdentifier,
327        key: &CryptoKey,
328        data: ArrayBufferViewOrArrayBuffer,
329        comp: InRealm,
330        can_gc: CanGc,
331    ) -> Rc<Promise> {
332        // Step 1. Let algorithm and key be the algorithm and key parameters passed to the
333        // decrypt() method, respectively.
334        // NOTE: We did that in method parameter.
335
336        // Step 2. Let data be the result of getting a copy of the bytes held by the data parameter
337        // passed to the decrypt() method.
338        let data = match data {
339            ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
340            ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
341        };
342
343        // Step 3. Let normalizedAlgorithm be the result of normalizing an algorithm, with alg set
344        // to algorithm and op set to "decrypt".
345        // Step 4. If an error occurred, return a Promise rejected with normalizedAlgorithm.
346        let promise = Promise::new_in_current_realm(comp, can_gc);
347        let normalized_algorithm =
348            match normalize_algorithm(cx, &Operation::Decrypt, &algorithm, can_gc) {
349                Ok(normalized_algorithm) => normalized_algorithm,
350                Err(error) => {
351                    promise.reject_error(error, can_gc);
352                    return promise;
353                },
354            };
355
356        // Step 5. Let realm be the relevant realm of this.
357        // Step 6. Let promise be a new Promise.
358        // NOTE: We did that in preparation of Step 4.
359
360        // Step 7. Return promise and perform the remaining steps in parallel.
361        let this = Trusted::new(self);
362        let trusted_promise = TrustedPromise::new(promise.clone());
363        let trusted_key = Trusted::new(key);
364        self.global()
365            .task_manager()
366            .dom_manipulation_task_source()
367            .queue(task!(decrypt: move || {
368                let subtle = this.root();
369                let promise = trusted_promise.root();
370                let key = trusted_key.root();
371
372                // Step 8. If the following steps or referenced procedures say to throw an error,
373                // queue a global task on the crypto task source, given realm's global object, to
374                // reject promise with the returned error; and then terminate the algorithm.
375
376                // Step 9. If the name member of normalizedAlgorithm is not equal to the name
377                // attribute of the [[algorithm]] internal slot of key then throw an
378                // InvalidAccessError.
379                if normalized_algorithm.name() != key.algorithm().name() {
380                    subtle.reject_promise_with_error(promise, Error::InvalidAccess);
381                    return;
382                }
383
384                // Step 10. If the [[usages]] internal slot of key does not contain an entry that
385                // is "decrypt", then throw an InvalidAccessError.
386                if !key.usages().contains(&KeyUsage::Decrypt) {
387                    subtle.reject_promise_with_error(promise, Error::InvalidAccess);
388                    return;
389                }
390
391                // Step 11. Let plaintext be the result of performing the decrypt operation
392                // specified by normalizedAlgorithm using key and algorithm and with data as
393                // ciphertext.
394                let plaintext = match normalized_algorithm.decrypt(&key, &data) {
395                    Ok(plaintext) => plaintext,
396                    Err(error) => {
397                        subtle.reject_promise_with_error(promise, error);
398                        return;
399                    },
400                };
401
402                // Step 12. Queue a global task on the crypto task source, given realm's global
403                // object, to perform the remaining steps.
404                // Step 13. Let result be the result of creating an ArrayBuffer in realm,
405                // containing plaintext.
406                // Step 14. Resolve promise with result.
407                subtle.resolve_promise_with_data(promise, plaintext);
408            }));
409        promise
410    }
411
412    /// <https://w3c.github.io/webcrypto/#SubtleCrypto-method-sign>
413    fn Sign(
414        &self,
415        cx: JSContext,
416        algorithm: AlgorithmIdentifier,
417        key: &CryptoKey,
418        data: ArrayBufferViewOrArrayBuffer,
419        comp: InRealm,
420        can_gc: CanGc,
421    ) -> Rc<Promise> {
422        // Step 1. Let algorithm and key be the algorithm and key parameters passed to the sign()
423        // method, respectively.
424        // NOTE: We did that in method parameter.
425
426        // Step 2. Let data be the result of getting a copy of the bytes held by the data parameter
427        // passed to the sign() method.
428        let data = match &data {
429            ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
430            ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
431        };
432
433        // Step 3. Let normalizedAlgorithm be the result of normalizing an algorithm, with alg set
434        // to algorithm and op set to "sign".
435        // Step 4. If an error occurred, return a Promise rejected with normalizedAlgorithm.
436        let promise = Promise::new_in_current_realm(comp, can_gc);
437        let normalized_algorithm =
438            match normalize_algorithm(cx, &Operation::Sign, &algorithm, can_gc) {
439                Ok(normalized_algorithm) => normalized_algorithm,
440                Err(error) => {
441                    promise.reject_error(error, can_gc);
442                    return promise;
443                },
444            };
445
446        // Step 5. Let realm be the relevant realm of this.
447        // Step 6. Let promise be a new Promise.
448        // NOTE: We did that in preparation of Step 4.
449
450        // Step 7. Return promise and perform the remaining steps in parallel.
451        let this = Trusted::new(self);
452        let trusted_promise = TrustedPromise::new(promise.clone());
453        let trusted_key = Trusted::new(key);
454        self.global()
455            .task_manager()
456            .dom_manipulation_task_source()
457            .queue(task!(sign: move || {
458                let subtle = this.root();
459                let promise = trusted_promise.root();
460                let key = trusted_key.root();
461
462                // Step 8. If the following steps or referenced procedures say to throw an error,
463                // queue a global task on the crypto task source, given realm's global object, to
464                // reject promise with the returned error; and then terminate the algorithm.
465
466                // Step 9. If the name member of normalizedAlgorithm is not equal to the name
467                // attribute of the [[algorithm]] internal slot of key then throw an
468                // InvalidAccessError.
469                if normalized_algorithm.name() != key.algorithm().name() {
470                    subtle.reject_promise_with_error(promise, Error::InvalidAccess);
471                    return;
472                }
473
474                // Step 10. If the [[usages]] internal slot of key does not contain an entry that
475                // is "sign", then throw an InvalidAccessError.
476                if !key.usages().contains(&KeyUsage::Sign) {
477                    subtle.reject_promise_with_error(promise, Error::InvalidAccess);
478                    return;
479                }
480
481                // Step 11. Let signature be the result of performing the sign operation specified
482                // by normalizedAlgorithm using key and algorithm and with data as message.
483                let signature = match normalized_algorithm.sign(&key, &data, CanGc::note()) {
484                    Ok(signature) => signature,
485                    Err(error) => {
486                        subtle.reject_promise_with_error(promise, error);
487                        return;
488                    },
489                };
490
491                // Step 12. Queue a global task on the crypto task source, given realm's global
492                // object, to perform the remaining steps.
493                // Step 13. Let result be the result of creating an ArrayBuffer in realm,
494                // containing signature.
495                // Step 14. Resolve promise with result.
496                subtle.resolve_promise_with_data(promise, signature);
497            }));
498        promise
499    }
500
501    /// <https://w3c.github.io/webcrypto/#SubtleCrypto-method-verify>
502    fn Verify(
503        &self,
504        cx: JSContext,
505        algorithm: AlgorithmIdentifier,
506        key: &CryptoKey,
507        signature: ArrayBufferViewOrArrayBuffer,
508        data: ArrayBufferViewOrArrayBuffer,
509        comp: InRealm,
510        can_gc: CanGc,
511    ) -> Rc<Promise> {
512        // Step 1. Let algorithm and key be the algorithm and key parameters passed to the verify()
513        // method, respectively.
514        // NOTE: We did that in method parameter.
515
516        // Step 2. Let signature be the result of getting a copy of the bytes held by the signature
517        // parameter passed to the verify() method.
518        let signature = match &signature {
519            ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
520            ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
521        };
522
523        // Step 3. Let data be the result of getting a copy of the bytes held by the data parameter
524        // passed to the verify() method.
525        let data = match &data {
526            ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
527            ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
528        };
529
530        // Step 4. Let normalizedAlgorithm be the result of normalizing an algorithm, with alg set to
531        // algorithm and op set to "verify".
532        // Step 5. If an error occurred, return a Promise rejected with normalizedAlgorithm.
533        let promise = Promise::new_in_current_realm(comp, can_gc);
534        let normalized_algorithm =
535            match normalize_algorithm(cx, &Operation::Verify, &algorithm, can_gc) {
536                Ok(algorithm) => algorithm,
537                Err(error) => {
538                    promise.reject_error(error, can_gc);
539                    return promise;
540                },
541            };
542
543        // Step 6. Let realm be the relevant realm of this.
544        // Step 7. Let promise be a new Promise.
545        // NOTE: We did that in preparation of Step 5.
546
547        // Step 8. Return promise and perform the remaining steps in parallel.
548        let this = Trusted::new(self);
549        let trusted_promise = TrustedPromise::new(promise.clone());
550        let trusted_key = Trusted::new(key);
551        self.global()
552            .task_manager()
553            .dom_manipulation_task_source()
554            .queue(task!(sign: move || {
555                let subtle = this.root();
556                let promise = trusted_promise.root();
557                let key = trusted_key.root();
558
559                // Step 9. If the following steps or referenced procedures say to throw an error,
560                // queue a global task on the crypto task source, given realm's global object, to
561                // reject promise with the returned error; and then terminate the algorithm.
562
563                // Step 10. If the name member of normalizedAlgorithm is not equal to the name
564                // attribute of the [[algorithm]] internal slot of key then throw an
565                // InvalidAccessError.
566                if normalized_algorithm.name() != key.algorithm().name() {
567                    subtle.reject_promise_with_error(promise, Error::InvalidAccess);
568                    return;
569                }
570
571                // Step 11. If the [[usages]] internal slot of key does not contain an entry that
572                // is "verify", then throw an InvalidAccessError.
573                if !key.usages().contains(&KeyUsage::Verify) {
574                    subtle.reject_promise_with_error(promise, Error::InvalidAccess);
575                    return;
576                }
577
578                // Step 12. Let result be the result of performing the verify operation specified
579                // by normalizedAlgorithm using key, algorithm and signature and with data as
580                // message.
581                let result = match normalized_algorithm.verify(&key, &data, &signature, CanGc::note()) {
582                    Ok(result) => result,
583                    Err(error) => {
584                        subtle.reject_promise_with_error(promise, error);
585                        return;
586                    },
587                };
588
589                // Step 13. Queue a global task on the crypto task source, given realm's global
590                // object, to perform the remaining steps.
591                // Step 14. Resolve promise with result.
592                subtle.resolve_promise_with_bool(promise, result);
593            }));
594        promise
595    }
596
597    /// <https://w3c.github.io/webcrypto/#SubtleCrypto-method-digest>
598    fn Digest(
599        &self,
600        cx: JSContext,
601        algorithm: AlgorithmIdentifier,
602        data: ArrayBufferViewOrArrayBuffer,
603        comp: InRealm,
604        can_gc: CanGc,
605    ) -> Rc<Promise> {
606        // Step 1. Let algorithm be the algorithm parameter passed to the digest() method.
607        // NOTE: We did that in method parameter.
608
609        // Step 2. Let data be the result of getting a copy of the bytes held by the
610        // data parameter passed to the digest() method.
611        let data = match data {
612            ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
613            ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
614        };
615
616        // Step 3. Let normalizedAlgorithm be the result of normalizing an algorithm,
617        // with alg set to algorithm and op set to "digest".
618        // Step 4. If an error occurred, return a Promise rejected with normalizedAlgorithm.
619        let promise = Promise::new_in_current_realm(comp, can_gc);
620        let normalized_algorithm =
621            match normalize_algorithm(cx, &Operation::Digest, &algorithm, can_gc) {
622                Ok(normalized_algorithm) => normalized_algorithm,
623                Err(error) => {
624                    promise.reject_error(error, can_gc);
625                    return promise;
626                },
627            };
628
629        // Step 5. Let realm be the relevant realm of this.
630        // Step 6. Let promise be a new Promise.
631        // NOTE: We did that in preparation of Step 3.
632
633        // Step 7. Return promise and perform the remaining steps in parallel.
634        let this = Trusted::new(self);
635        let trusted_promise = TrustedPromise::new(promise.clone());
636        self.global()
637            .task_manager()
638            .dom_manipulation_task_source()
639            .queue(task!(generate_key: move || {
640                let subtle = this.root();
641                let promise = trusted_promise.root();
642
643                // Step 8. If the following steps or referenced procedures say to throw an error,
644                // queue a global task on the crypto task source, given realm's global object, to
645                // reject promise with the returned error; and then terminate the algorithm.
646
647                // Step 9. Let digest be the result of performing the digest operation specified by
648                // normalizedAlgorithm using algorithm, with data as message.
649                let digest = match normalized_algorithm.digest(&data) {
650                    Ok(digest) => digest,
651                    Err(error) => {
652                        subtle.reject_promise_with_error(promise, error);
653                        return;
654                    }
655                };
656
657                // Step 10. Queue a global task on the crypto task source, given realm's global
658                // object, to perform the remaining steps.
659                // Step 11. Let result be the result of creating an ArrayBuffer in realm,
660                // containing digest.
661                // Step 12. Resolve promise with result.
662                subtle.resolve_promise_with_data(promise, digest);
663            }));
664        promise
665    }
666
667    /// <https://w3c.github.io/webcrypto/#SubtleCrypto-method-generateKey>
668    fn GenerateKey(
669        &self,
670        cx: JSContext,
671        algorithm: AlgorithmIdentifier,
672        extractable: bool,
673        key_usages: Vec<KeyUsage>,
674        comp: InRealm,
675        can_gc: CanGc,
676    ) -> Rc<Promise> {
677        // Step 1. Let algorithm, extractable and usages be the algorithm, extractable and
678        // keyUsages parameters passed to the generateKey() method, respectively.
679
680        // Step 2. Let normalizedAlgorithm be the result of normalizing an algorithm, with alg set
681        // to algorithm and op set to "generateKey".
682        // Step 3. If an error occurred, return a Promise rejected with normalizedAlgorithm.
683        let promise = Promise::new_in_current_realm(comp, can_gc);
684        let normalized_algorithm =
685            match normalize_algorithm(cx, &Operation::GenerateKey, &algorithm, can_gc) {
686                Ok(normalized_algorithm) => normalized_algorithm,
687                Err(error) => {
688                    promise.reject_error(error, can_gc);
689                    return promise;
690                },
691            };
692
693        // Step 4. Let realm be the relevant realm of this.
694        // Step 5. Let promise be a new Promise.
695        // NOTE: We did that in preparation of Step 3.
696
697        // Step 6. Return promise and perform the remaining steps in parallel.
698        let trusted_subtle = Trusted::new(self);
699        let trusted_promise = TrustedPromise::new(promise.clone());
700        self.global()
701            .task_manager()
702            .dom_manipulation_task_source()
703            .queue(task!(generate_key: move || {
704                let subtle = trusted_subtle.root();
705                let promise = trusted_promise.root();
706
707                // Step 7. If the following steps or referenced procedures say to throw an error,
708                // queue a global task on the crypto task source, given realm's global object, to
709                // reject promise with the returned error; and then terminate the algorithm.
710
711                // Step 8. Let result be the result of performing the generate key operation
712                // specified by normalizedAlgorithm using algorithm, extractable and usages.
713                let result = match normalized_algorithm.generate_key(
714                    &subtle.global(),
715                    extractable,
716                    key_usages,
717                    &subtle.rng,
718                    CanGc::note(),
719                ) {
720                    Ok(result) => result,
721                    Err(error) => {
722                        subtle.reject_promise_with_error(promise, error);
723                        return;
724                    }
725                };
726
727                // Step 9.
728                // If result is a CryptoKey object:
729                //     If the [[type]] internal slot of result is "secret" or "private" and usages
730                //     is empty, then throw a SyntaxError.
731                // If result is a CryptoKeyPair object:
732                //     If the [[usages]] internal slot of the privateKey attribute of result is the
733                //     empty sequence, then throw a SyntaxError.
734                // TODO: Implement CryptoKeyPair case
735                match &result {
736                    CryptoKeyOrCryptoKeyPair::CryptoKey(crpyto_key) => {
737                        if matches!(crpyto_key.Type(), KeyType::Secret | KeyType::Private)
738                            && crpyto_key.usages().is_empty()
739                        {
740                            subtle.reject_promise_with_error(promise, Error::Syntax(None));
741                            return;
742                        }
743                    },
744                };
745
746                // Step 10. Queue a global task on the crypto task source, given realm's global
747                // object, to perform the remaining steps.
748                // Step 11. Let result be the result of converting result to an ECMAScript Object
749                // in realm, as defined by [WebIDL].
750                // Step 12. Resolve promise with result.
751                match result {
752                    CryptoKeyOrCryptoKeyPair::CryptoKey(key) => {
753                        subtle.resolve_promise_with_key(promise, key);
754                    },
755                }
756            }));
757
758        promise
759    }
760
761    /// <https://w3c.github.io/webcrypto/#SubtleCrypto-method-deriveKey>
762    fn DeriveKey(
763        &self,
764        cx: JSContext,
765        algorithm: AlgorithmIdentifier,
766        base_key: &CryptoKey,
767        derived_key_type: AlgorithmIdentifier,
768        extractable: bool,
769        usages: Vec<KeyUsage>,
770        comp: InRealm,
771        can_gc: CanGc,
772    ) -> Rc<Promise> {
773        // Step 1. Let algorithm, baseKey, derivedKeyType, extractable and usages be the algorithm,
774        // baseKey, derivedKeyType, extractable and keyUsages parameters passed to the deriveKey()
775        // method, respectively.
776        // NOTE: We did that in method parameter.
777
778        // Step 2. Let normalizedAlgorithm be the result of normalizing an algorithm, with alg set
779        // to algorithm and op set to "deriveBits".
780        // Step 3. If an error occurred, return a Promise rejected with normalizedAlgorithm.
781        let promise = Promise::new_in_current_realm(comp, can_gc);
782        let normalized_algorithm =
783            match normalize_algorithm(cx, &Operation::DeriveBits, &algorithm, can_gc) {
784                Ok(normalized_algorithm) => normalized_algorithm,
785                Err(error) => {
786                    promise.reject_error(error, can_gc);
787                    return promise;
788                },
789            };
790
791        // Step 4. Let normalizedDerivedKeyAlgorithmImport be the result of normalizing an
792        // algorithm, with alg set to derivedKeyType and op set to "importKey".
793        // Step 5. If an error occurred, return a Promise rejected with
794        // normalizedDerivedKeyAlgorithmImport.
795        let normalized_derived_key_algorithm_import =
796            match normalize_algorithm(cx, &Operation::ImportKey, &derived_key_type, can_gc) {
797                Ok(normalized_algorithm) => normalized_algorithm,
798                Err(error) => {
799                    promise.reject_error(error, can_gc);
800                    return promise;
801                },
802            };
803
804        // Step 6. Let normalizedDerivedKeyAlgorithmLength be the result of normalizing an
805        // algorithm, with alg set to derivedKeyType and op set to "get key length".
806        // Step 7. If an error occurred, return a Promise rejected with
807        // normalizedDerivedKeyAlgorithmLength.
808        let normalized_derived_key_algorithm_length =
809            match normalize_algorithm(cx, &Operation::GetKeyLength, &derived_key_type, can_gc) {
810                Ok(normalized_algorithm) => normalized_algorithm,
811                Err(error) => {
812                    promise.reject_error(error, can_gc);
813                    return promise;
814                },
815            };
816
817        // Step 8. Let realm be the relevant realm of this.
818        // Step 9. Let promise be a new Promise.
819        // NOTE: We did that in preparation of Step 3.
820
821        // Step 10. Return promise and perform the remaining steps in parallel.
822        let trusted_subtle = Trusted::new(self);
823        let trusted_base_key = Trusted::new(base_key);
824        let trusted_promise = TrustedPromise::new(promise.clone());
825        self.global().task_manager().dom_manipulation_task_source().queue(
826            task!(derive_key: move || {
827                let subtle = trusted_subtle.root();
828                let base_key = trusted_base_key.root();
829                let promise = trusted_promise.root();
830
831                // Step 11. If the following steps or referenced procedures say to throw an error,
832                // queue a global task on the crypto task source, given realm's global object, to
833                // reject promise with the returned error; and then terminate the algorithm.
834
835                // Step 12. If the name member of normalizedAlgorithm is not equal to the name
836                // attribute of the [[algorithm]] internal slot of baseKey then throw an
837                // InvalidAccessError.
838                if normalized_algorithm.name() != base_key.algorithm().name() {
839                    subtle.reject_promise_with_error(promise, Error::InvalidAccess);
840                    return;
841                }
842
843                // Step 13. If the [[usages]] internal slot of baseKey does not contain an entry
844                // that is "deriveKey", then throw an InvalidAccessError.
845                if !base_key.usages().contains(&KeyUsage::DeriveKey) {
846                    subtle.reject_promise_with_error(promise, Error::InvalidAccess);
847                    return;
848                }
849
850                // Step 14. Let length be the result of performing the get key length algorithm
851                // specified by normalizedDerivedKeyAlgorithmLength using derivedKeyType.
852                let length = match normalized_derived_key_algorithm_length.get_key_length() {
853                    Ok(length) => length,
854                    Err(error) => {
855                        subtle.reject_promise_with_error(promise, error);
856                        return;
857                    }
858                };
859
860                // Step 15. Let secret be the result of performing the derive bits operation
861                // specified by normalizedAlgorithm using key, algorithm and length.
862                let secret = match normalized_algorithm.derive_bits(&base_key, length) {
863                    Ok(secret) => secret,
864                    Err(error) => {
865                        subtle.reject_promise_with_error(promise, error);
866                        return;
867                    }
868                };
869
870                // Step 16. Let result be the result of performing the import key operation
871                // specified by normalizedDerivedKeyAlgorithmImport using "raw" as format, secret
872                // as keyData, derivedKeyType as algorithm and using extractable and usages.
873                let result = match normalized_derived_key_algorithm_import.import_key(
874                    &subtle.global(),
875                    KeyFormat::Raw,
876                    &secret,
877                    extractable,
878                    usages.clone(),
879                    CanGc::note(),
880                ) {
881                    Ok(algorithm) => algorithm,
882                    Err(error) => {
883                        subtle.reject_promise_with_error(promise, error);
884                        return;
885                    },
886                };
887
888                // Step 17. If the [[type]] internal slot of result is "secret" or "private" and
889                // usages is empty, then throw a SyntaxError.
890                if matches!(result.Type(), KeyType::Secret | KeyType::Private) && usages.is_empty() {
891                    subtle.reject_promise_with_error(promise, Error::Syntax(None));
892                    return;
893                }
894
895                // Step 18. Set the [[extractable]] internal slot of result to extractable.
896                // Step 19. Set the [[usages]] internal slot of result to the normalized value of
897                // usages.
898                // NOTE: Done by normalized_derived_key_algorithm_import.import_key in Step 16.
899
900                // Step 20. Queue a global task on the crypto task source, given realm's global
901                // object, to perform the remaining steps.
902                // Step 20. Let result be the result of converting result to an ECMAScript Object
903                // in realm, as defined by [WebIDL].
904                // Step 20. Resolve promise with result.
905                subtle.resolve_promise_with_key(promise, result);
906            }),
907        );
908        promise
909    }
910
911    /// <https://w3c.github.io/webcrypto/#dfn-SubtleCrypto-method-deriveBits>
912    fn DeriveBits(
913        &self,
914        cx: JSContext,
915        algorithm: AlgorithmIdentifier,
916        base_key: &CryptoKey,
917        length: Option<u32>,
918        comp: InRealm,
919        can_gc: CanGc,
920    ) -> Rc<Promise> {
921        // Step 1. Let algorithm, baseKey and length, be the algorithm, baseKey and length
922        // parameters passed to the deriveBits() method, respectively.
923        // NOTE: We did that in method parameter.
924
925        // Step 2. Let normalizedAlgorithm be the result of normalizing an algorithm, with alg set
926        // to algorithm and op set to "deriveBits".
927        // Step 3. If an error occurred, return a Promise rejected with normalizedAlgorithm.
928        let promise = Promise::new_in_current_realm(comp, can_gc);
929        let normalized_algorithm =
930            match normalize_algorithm(cx, &Operation::DeriveBits, &algorithm, can_gc) {
931                Ok(normalized_algorithm) => normalized_algorithm,
932                Err(error) => {
933                    promise.reject_error(error, can_gc);
934                    return promise;
935                },
936            };
937
938        // Step 4. Let realm be the relevant realm of this.
939        // Step 5. Let promise be a new Promise.
940        // NOTE: We did that in preparation of Step 3.
941
942        // Step 5. Return promise and perform the remaining steps in parallel.
943        let trsuted_subtle = Trusted::new(self);
944        let trusted_base_key = Trusted::new(base_key);
945        let trusted_promise = TrustedPromise::new(promise.clone());
946        self.global()
947            .task_manager()
948            .dom_manipulation_task_source()
949            .queue(task!(import_key: move || {
950                let subtle = trsuted_subtle.root();
951                let base_key = trusted_base_key.root();
952                let promise = trusted_promise.root();
953
954                // Step 7. If the following steps or referenced procedures say to throw an error,
955                // queue a global task on the crypto task source, given realm's global object, to
956                // reject promise with the returned error; and then terminate the algorithm.
957
958                // Step 8. If the name member of normalizedAlgorithm is not equal to the name
959                // attribute of the [[algorithm]] internal slot of baseKey then throw an
960                // InvalidAccessError.
961                if normalized_algorithm.name() != base_key.algorithm().name() {
962                    subtle.reject_promise_with_error(promise, Error::InvalidAccess);
963                    return;
964                }
965
966                // Step 9. If the [[usages]] internal slot of baseKey does not contain an entry
967                // that is "deriveBits", then throw an InvalidAccessError.
968                if !base_key.usages().contains(&KeyUsage::DeriveBits) {
969                    subtle.reject_promise_with_error(promise, Error::InvalidAccess);
970                    return;
971                }
972
973                // Step 10. Let bits be the result of performing the derive bits operation
974                // specified by normalizedAlgorithm using baseKey, algorithm and length.
975                let bits = match normalized_algorithm.derive_bits(&base_key, length) {
976                    Ok(bits) => bits,
977                    Err(error) => {
978                        subtle.reject_promise_with_error(promise, error);
979                        return;
980                    }
981                };
982
983                // Step 11. Queue a global task on the crypto task source, given realm's global
984                // object, to perform the remaining steps.
985                // Step 12. Let result be the result of creating an ArrayBuffer in realm,
986                // containing bits.
987                // Step 13. Resolve promise with result.
988                subtle.resolve_promise_with_data(promise, bits);
989            }));
990        promise
991    }
992
993    /// <https://w3c.github.io/webcrypto/#SubtleCrypto-method-importKey>
994    fn ImportKey(
995        &self,
996        cx: JSContext,
997        format: KeyFormat,
998        key_data: ArrayBufferViewOrArrayBufferOrJsonWebKey,
999        algorithm: AlgorithmIdentifier,
1000        extractable: bool,
1001        key_usages: Vec<KeyUsage>,
1002        comp: InRealm,
1003        can_gc: CanGc,
1004    ) -> Rc<Promise> {
1005        // Step 1. Let format, algorithm, extractable and usages, be the format, algorithm,
1006        // extractable and keyUsages parameters passed to the importKey() method, respectively.
1007
1008        // Step 5. Let realm be the relevant realm of this.
1009        // Step 6. Let promise be a new Promise.
1010        let promise = Promise::new_in_current_realm(comp, can_gc);
1011
1012        // Step 2.
1013        let key_data = match format {
1014            // If format is equal to the string "raw", "pkcs8", or "spki":
1015            KeyFormat::Raw | KeyFormat::Pkcs8 | KeyFormat::Spki => {
1016                match key_data {
1017                    // Step 2.1. If the keyData parameter passed to the importKey() method is a
1018                    // JsonWebKey dictionary, throw a TypeError.
1019                    ArrayBufferViewOrArrayBufferOrJsonWebKey::JsonWebKey(_) => {
1020                        promise.reject_error(
1021                            Error::Type("The keyData type does not match the format".to_string()),
1022                            can_gc,
1023                        );
1024                        return promise;
1025                    },
1026
1027                    // Step 2.2. Let keyData be the result of getting a copy of the bytes held by
1028                    // the keyData parameter passed to the importKey() method.
1029                    ArrayBufferViewOrArrayBufferOrJsonWebKey::ArrayBufferView(view) => {
1030                        view.to_vec()
1031                    },
1032                    ArrayBufferViewOrArrayBufferOrJsonWebKey::ArrayBuffer(buffer) => {
1033                        buffer.to_vec()
1034                    },
1035                }
1036            },
1037            // If format is equal to the string "jwk":
1038            KeyFormat::Jwk => {
1039                match key_data {
1040                    ArrayBufferViewOrArrayBufferOrJsonWebKey::ArrayBufferView(_) |
1041                    ArrayBufferViewOrArrayBufferOrJsonWebKey::ArrayBuffer(_) => {
1042                        // Step 2.1. If the keyData parameter passed to the importKey() method is
1043                        // not a JsonWebKey dictionary, throw a TypeError.
1044                        promise.reject_error(
1045                            Error::Type("The keyData type does not match the format".to_string()),
1046                            can_gc,
1047                        );
1048                        return promise;
1049                    },
1050
1051                    ArrayBufferViewOrArrayBufferOrJsonWebKey::JsonWebKey(jwk) => {
1052                        // Step 2.2. Let keyData be the keyData parameter passed to the importKey() method.
1053                        //
1054                        // NOTE: Serialize JsonWebKey throught stringifying it.
1055                        // JsonWebKey::stringify internally relies on ToJSON, so it will raise an
1056                        // exception when a JS error is thrown. When this happens, we report the
1057                        // error.
1058                        match jwk.stringify(cx) {
1059                            Ok(stringified) => stringified.as_bytes().to_vec(),
1060                            Err(error) => {
1061                                promise.reject_error(error, can_gc);
1062                                return promise;
1063                            },
1064                        }
1065                    },
1066                }
1067            },
1068        };
1069
1070        // Step 3. Let normalizedAlgorithm be the result of normalizing an algorithm, with alg set
1071        // to algorithm and op set to "importKey".
1072        // Step 4. If an error occurred, return a Promise rejected with normalizedAlgorithm.
1073        let normalized_algorithm =
1074            match normalize_algorithm(cx, &Operation::ImportKey, &algorithm, can_gc) {
1075                Ok(algorithm) => algorithm,
1076                Err(error) => {
1077                    promise.reject_error(error, can_gc);
1078                    return promise;
1079                },
1080            };
1081
1082        // Step 7. Return promise and perform the remaining steps in parallel.
1083        let this = Trusted::new(self);
1084        let trusted_promise = TrustedPromise::new(promise.clone());
1085        self.global()
1086            .task_manager()
1087            .dom_manipulation_task_source()
1088            .queue(task!(import_key: move || {
1089                let subtle = this.root();
1090                let promise = trusted_promise.root();
1091
1092                // Step 8. If the following steps or referenced procedures say to throw an error,
1093                // queue a global task on the crypto task source, given realm's global object, to
1094                // reject promise with the returned error; and then terminate the algorithm.
1095
1096                // Step 9. Let result be the CryptoKey object that results from performing the
1097                // import key operation specified by normalizedAlgorithm using keyData, algorithm,
1098                // format, extractable and usages.
1099                let result = match normalized_algorithm.import_key(
1100                    &subtle.global(),
1101                    format,
1102                    &key_data,
1103                    extractable,
1104                    key_usages.clone(),
1105                    CanGc::note()
1106                ) {
1107                    Ok(key) => key,
1108                    Err(error) => {
1109                        subtle.reject_promise_with_error(promise, error);
1110                        return;
1111                    },
1112                };
1113
1114                // Step 10. If the [[type]] internal slot of result is "secret" or "private" and
1115                // usages is empty, then throw a SyntaxError.
1116                if matches!(result.Type(), KeyType::Secret | KeyType::Private) && key_usages.is_empty() {
1117                    subtle.reject_promise_with_error(promise, Error::Syntax(None));
1118                    return;
1119                }
1120
1121                // Step 11. Set the [[extractable]] internal slot of result to extractable.
1122                result.set_extractable(extractable);
1123
1124                // Step 12. Set the [[usages]] internal slot of result to the normalized value of usages.
1125                result.set_usages(&key_usages);
1126
1127                // Step 13. Queue a global task on the crypto task source, given realm's global
1128                // object, to perform the remaining steps.
1129                // Step 14. Let result be the result of converting result to an ECMAScript Object
1130                // in realm, as defined by [WebIDL].
1131                // Step 15. Resolve promise with result.
1132                subtle.resolve_promise_with_key(promise, result);
1133            }));
1134
1135        promise
1136    }
1137
1138    /// <https://w3c.github.io/webcrypto/#SubtleCrypto-method-exportKey>
1139    fn ExportKey(
1140        &self,
1141        format: KeyFormat,
1142        key: &CryptoKey,
1143        comp: InRealm,
1144        can_gc: CanGc,
1145    ) -> Rc<Promise> {
1146        // Step 1. Let format and key be the format and key parameters passed to the exportKey()
1147        // method, respectively.
1148        // NOTE: We did that in method parameter.
1149
1150        // Step 2. Let realm be the relevant realm of this.
1151        // Step 3. Let promise be a new Promise.
1152        let promise = Promise::new_in_current_realm(comp, can_gc);
1153
1154        // Step 4. Return promise and perform the remaining steps in parallel.
1155        let trusted_subtle = Trusted::new(self);
1156        let trusted_promise = TrustedPromise::new(promise.clone());
1157        let trusted_key = Trusted::new(key);
1158        self.global()
1159            .task_manager()
1160            .dom_manipulation_task_source()
1161            .queue(task!(export_key: move || {
1162                let subtle = trusted_subtle.root();
1163                let promise = trusted_promise.root();
1164                let key = trusted_key.root();
1165
1166                // Step 5. If the following steps or referenced procedures say to throw an error,
1167                // queue a global task on the crypto task source, given realm's global object, to
1168                // reject promise with the returned error; and then terminate the algorithm.
1169
1170                // Step 6. If the name member of the [[algorithm]] internal slot of key does not
1171                // identify a registered algorithm that supports the export key operation, then
1172                // throw a NotSupportedError.
1173                if matches!(
1174                    key.algorithm().name(),
1175                    ALG_SHA1 | ALG_SHA256 | ALG_SHA384 | ALG_SHA512 | ALG_HKDF | ALG_PBKDF2
1176                ) {
1177                    subtle.reject_promise_with_error(promise, Error::NotSupported);
1178                    return;
1179                }
1180
1181                // Step 7. If the [[extractable]] internal slot of key is false, then throw an
1182                // InvalidAccessError.
1183                if !key.Extractable() {
1184                    subtle.reject_promise_with_error(promise, Error::InvalidAccess);
1185                    return;
1186                }
1187
1188                // Step 8. Let result be the result of performing the export key operation
1189                // specified by the [[algorithm]] internal slot of key using key and format.
1190                let result = match perform_export_key_operation(format, &key) {
1191                    Ok(exported_key) => exported_key,
1192                    Err(error) => {
1193                        subtle.reject_promise_with_error(promise, error);
1194                        return;
1195                    },
1196                };
1197
1198                // Step 9. Queue a global task on the crypto task source, given realm's global
1199                // object, to perform the remaining steps.
1200                // Step 10.
1201                // If format is equal to the strings "raw", "pkcs8", or "spki":
1202                //     Let result be the result of creating an ArrayBuffer in realm, containing
1203                //     result.
1204                // If format is equal to the string "jwk":
1205                //     Let result be the result of converting result to an ECMAScript Object in
1206                //     realm, as defined by [WebIDL].
1207                // Step 11. Resolve promise with result.
1208                match result {
1209                    ExportedKey::Raw(raw) => {
1210                        subtle.resolve_promise_with_data(promise, raw);
1211                    },
1212                    ExportedKey::Jwk(jwk) => {
1213                        subtle.resolve_promise_with_jwk(promise, jwk);
1214                    },
1215                }
1216            }));
1217        promise
1218    }
1219
1220    /// <https://w3c.github.io/webcrypto/#SubtleCrypto-method-wrapKey>
1221    fn WrapKey(
1222        &self,
1223        cx: JSContext,
1224        format: KeyFormat,
1225        key: &CryptoKey,
1226        wrapping_key: &CryptoKey,
1227        algorithm: AlgorithmIdentifier,
1228        comp: InRealm,
1229        can_gc: CanGc,
1230    ) -> Rc<Promise> {
1231        // Step 1. Let format, key, wrappingKey and algorithm be the format, key, wrappingKey and
1232        // wrapAlgorithm parameters passed to the wrapKey() method, respectively.
1233        // NOTE: We did that in method parameter.
1234
1235        // Step 2. Let normalizedAlgorithm be the result of normalizing an algorithm, with alg set
1236        // to algorithm and op set to "wrapKey".
1237        let mut normalized_algorithm_result =
1238            normalize_algorithm(cx, &Operation::WrapKey, &algorithm, can_gc);
1239
1240        // Step 3. If an error occurred, let normalizedAlgorithm be the result of normalizing an
1241        // algorithm, with alg set to algorithm and op set to "encrypt".
1242        if normalized_algorithm_result.is_err() {
1243            normalized_algorithm_result =
1244                normalize_algorithm(cx, &Operation::Encrypt, &algorithm, can_gc);
1245        }
1246
1247        // Step 4. If an error occurred, return a Promise rejected with normalizedAlgorithm.
1248        let promise = Promise::new_in_current_realm(comp, can_gc);
1249        let normalized_algorithm = match normalized_algorithm_result {
1250            Ok(normalized_algorithm) => normalized_algorithm,
1251            Err(error) => {
1252                promise.reject_error(error, can_gc);
1253                return promise;
1254            },
1255        };
1256
1257        // Step 5. Let realm be the relevant realm of this.
1258        // Step 6. Let promise be a new Promise.
1259        // NOTE: We did that in preparation of Step 4.
1260
1261        // Step 7. Return promise and perform the remaining steps in parallel.
1262        let trusted_subtle = Trusted::new(self);
1263        let trusted_key = Trusted::new(key);
1264        let trusted_wrapping_key = Trusted::new(wrapping_key);
1265        let trusted_promise = TrustedPromise::new(promise.clone());
1266        self.global()
1267            .task_manager()
1268            .dom_manipulation_task_source()
1269            .queue(task!(wrap_key: move || {
1270                let subtle = trusted_subtle.root();
1271                let key = trusted_key.root();
1272                let wrapping_key = trusted_wrapping_key.root();
1273                let promise = trusted_promise.root();
1274
1275                // Step 8. If the following steps or referenced procedures say to throw an error,
1276                // queue a global task on the crypto task source, given realm's global object, to
1277                // reject promise with the returned error; and then terminate the algorithm.
1278
1279                // Step 9. If the name member of normalizedAlgorithm is not equal to the name
1280                // attribute of the [[algorithm]] internal slot of wrappingKey then throw an
1281                // InvalidAccessError.
1282                if normalized_algorithm.name() != wrapping_key.algorithm().name() {
1283                    subtle.reject_promise_with_error(promise, Error::InvalidAccess);
1284                    return;
1285                }
1286
1287                // Step 10. If the [[usages]] internal slot of wrappingKey does not contain an
1288                // entry that is "wrapKey", then throw an InvalidAccessError.
1289                if !wrapping_key.usages().contains(&KeyUsage::WrapKey) {
1290                    subtle.reject_promise_with_error(promise, Error::InvalidAccess);
1291                    return;
1292                }
1293
1294                // Step 11. If the algorithm identified by the [[algorithm]] internal slot of key
1295                // does not support the export key operation, then throw a NotSupportedError.
1296
1297                if matches!(
1298                    key.algorithm().name(),
1299                    ALG_SHA1 | ALG_SHA256 | ALG_SHA384 | ALG_SHA512 | ALG_HKDF | ALG_PBKDF2
1300                ) {
1301                    subtle.reject_promise_with_error(promise, Error::NotSupported);
1302                    return;
1303                }
1304
1305
1306                // Step 12. If the [[extractable]] internal slot of key is false, then throw an
1307                // InvalidAccessError.
1308                if !key.Extractable() {
1309                    subtle.reject_promise_with_error(promise, Error::InvalidAccess);
1310                    return;
1311                }
1312
1313                // Step 13. Let exportedKey be the result of performing the export key operation
1314                // specified by the [[algorithm]] internal slot of key using key and format.
1315                let exported_key = match perform_export_key_operation(format, &key) {
1316                    Ok(exported_key) => exported_key,
1317                    Err(error) => {
1318                        subtle.reject_promise_with_error(promise, error);
1319                        return;
1320                    },
1321                };
1322
1323                // Step 14.
1324                // If format is equal to the strings "raw", "pkcs8", or "spki":
1325                //     Let bytes be exportedKey.
1326                // If format is equal to the string "jwk":
1327                //     Step 14.1. Let json be the result of representing exportedKey as a UTF-16
1328                //     string conforming to the JSON grammar; for example, by executing the
1329                //     JSON.stringify algorithm specified in [ECMA-262] in the context of a new
1330                //     global object.
1331                //     Step 14.2. Let bytes be the result of UTF-8 encoding json.
1332                let cx = GlobalScope::get_cx();
1333                let bytes = match exported_key {
1334                    ExportedKey::Raw(raw) => raw,
1335                    ExportedKey::Jwk(jwk) => match jwk.stringify(cx) {
1336                        Ok(stringified_jwk) => stringified_jwk.as_bytes().to_vec(),
1337                        Err(error) => {
1338                            subtle.reject_promise_with_error(promise, error);
1339                            return;
1340                        },
1341                    },
1342                };
1343
1344                // Step 15.
1345                // If normalizedAlgorithm supports the wrap key operation:
1346                //     Let result be the result of performing the wrap key operation specified by
1347                //     normalizedAlgorithm using algorithm, wrappingKey as key and bytes as
1348                //     plaintext.
1349                // Otherwise, if normalizedAlgorithm supports the encrypt operation:
1350                //     Let result be the result of performing the encrypt operation specified by
1351                //     normalizedAlgorithm using algorithm, wrappingKey as key and bytes as
1352                //     plaintext.
1353                // Otherwise:
1354                //     throw a NotSupportedError.
1355                let mut result = normalized_algorithm.wrap_key(&wrapping_key, &bytes);
1356                if result.is_err() {
1357                    result = normalized_algorithm.encrypt(&wrapping_key, &bytes);
1358                }
1359                let result = match result {
1360                    Ok(result) => result,
1361                    Err(error) => {
1362                        subtle.reject_promise_with_error(promise, error);
1363                        return;
1364                    },
1365                };
1366
1367                // Step 16. Queue a global task on the crypto task source, given realm's global
1368                // object, to perform the remaining steps.
1369                // Step 17. Let result be the result of creating an ArrayBuffer in realm,
1370                // containing result.
1371                // Step 18. Resolve promise with result.
1372                subtle.resolve_promise_with_data(promise, result);
1373            }));
1374        promise
1375    }
1376
1377    /// <https://w3c.github.io/webcrypto/#SubtleCrypto-method-unwrapKey>
1378    fn UnwrapKey(
1379        &self,
1380        cx: JSContext,
1381        format: KeyFormat,
1382        wrapped_key: ArrayBufferViewOrArrayBuffer,
1383        unwrapping_key: &CryptoKey,
1384        algorithm: AlgorithmIdentifier,
1385        unwrapped_key_algorithm: AlgorithmIdentifier,
1386        extractable: bool,
1387        usages: Vec<KeyUsage>,
1388        comp: InRealm,
1389        can_gc: CanGc,
1390    ) -> Rc<Promise> {
1391        // Step 1. Let format, unwrappingKey, algorithm, unwrappedKeyAlgorithm, extractable and
1392        // usages, be the format, unwrappingKey, unwrapAlgorithm, unwrappedKeyAlgorithm,
1393        // extractable and keyUsages parameters passed to the unwrapKey() method, respectively.
1394        // NOTE: We did that in method parameter.
1395
1396        // Step 2. Let wrappedKey be the result of getting a copy of the bytes held by the
1397        // wrappedKey parameter passed to the unwrapKey() method.
1398        let wrapped_key = match wrapped_key {
1399            ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
1400            ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
1401        };
1402
1403        // Step 3. Let normalizedAlgorithm be the result of normalizing an algorithm, with alg set
1404        // to algorithm and op set to "unwrapKey".
1405        let mut normalized_algorithm =
1406            normalize_algorithm(cx, &Operation::UnwrapKey, &algorithm, can_gc);
1407
1408        // Step 4. If an error occurred, let normalizedAlgorithm be the result of normalizing an
1409        // algorithm, with alg set to algorithm and op set to "decrypt".
1410        if normalized_algorithm.is_err() {
1411            normalized_algorithm = normalize_algorithm(cx, &Operation::Decrypt, &algorithm, can_gc);
1412        }
1413
1414        // Step 5. If an error occurred, return a Promise rejected with normalizedAlgorithm.
1415        let promise = Promise::new_in_current_realm(comp, can_gc);
1416        let normalized_algorithm = match normalized_algorithm {
1417            Ok(algorithm) => algorithm,
1418            Err(error) => {
1419                promise.reject_error(error, can_gc);
1420                return promise;
1421            },
1422        };
1423
1424        // Step 6. Let normalizedKeyAlgorithm be the result of normalizing an algorithm, with alg
1425        // set to unwrappedKeyAlgorithm and op set to "importKey".
1426        // Step 7. If an error occurred, return a Promise rejected with normalizedKeyAlgorithm.
1427        let normalized_key_algorithm = match normalize_algorithm(
1428            cx,
1429            &Operation::ImportKey,
1430            &unwrapped_key_algorithm,
1431            can_gc,
1432        ) {
1433            Ok(algorithm) => algorithm,
1434            Err(error) => {
1435                promise.reject_error(error, can_gc);
1436                return promise;
1437            },
1438        };
1439
1440        // Step 8. Let realm be the relevant realm of this.
1441        // Step 9. Let promise be a new Promise.
1442        // NOTE: We did that in preparation of Step 5.
1443
1444        // Step 10. Return promise and perform the remaining steps in parallel.
1445        let trusted_subtle = Trusted::new(self);
1446        let trusted_unwrapping_key = Trusted::new(unwrapping_key);
1447        let trusted_promise = TrustedPromise::new(promise.clone());
1448        self.global().task_manager().dom_manipulation_task_source().queue(
1449            task!(unwrap_key: move || {
1450                let subtle = trusted_subtle.root();
1451                let unwrapping_key = trusted_unwrapping_key.root();
1452                let promise = trusted_promise.root();
1453
1454                // Step 11. If the following steps or referenced procedures say to throw an error,
1455                // queue a global task on the crypto task source, given realm's global object, to
1456                // reject promise with the returned error; and then terminate the algorithm.
1457
1458                // Step 12. If the name member of normalizedAlgorithm is not equal to the name
1459                // attribute of the [[algorithm]] internal slot of unwrappingKey then throw an
1460                // InvalidAccessError.
1461                if normalized_algorithm.name() != unwrapping_key.algorithm().name() {
1462                    subtle.reject_promise_with_error(promise, Error::InvalidAccess);
1463                    return;
1464                }
1465
1466                // Step 13. If the [[usages]] internal slot of unwrappingKey does not contain an
1467                // entry that is "unwrapKey", then throw an InvalidAccessError.
1468                if !unwrapping_key.usages().contains(&KeyUsage::UnwrapKey) {
1469                    subtle.reject_promise_with_error(promise, Error::InvalidAccess);
1470                    return;
1471                }
1472
1473                // Step 14.
1474                // If normalizedAlgorithm supports an unwrap key operation:
1475                //     Let bytes be the result of performing the unwrap key operation specified by
1476                //     normalizedAlgorithm using algorithm, unwrappingKey as key and wrappedKey as
1477                //     ciphertext.
1478                // Otherwise, if normalizedAlgorithm supports a decrypt operation:
1479                //     Let bytes be the result of performing the decrypt operation specified by
1480                //     normalizedAlgorithm using algorithm, unwrappingKey as key and wrappedKey as
1481                //     ciphertext.
1482                // Otherwise:
1483                //     throw a NotSupportedError.
1484                let mut bytes = normalized_algorithm.unwrap_key(&unwrapping_key, &wrapped_key);
1485                if bytes.is_err() {
1486                    bytes = normalized_algorithm.decrypt(&unwrapping_key, &wrapped_key);
1487                }
1488                let bytes = match bytes {
1489                    Ok(bytes) => bytes,
1490                    Err(error) => {
1491                        subtle.reject_promise_with_error(promise, error);
1492                        return;
1493                    },
1494                };
1495
1496                // Step 15.
1497                // If format is equal to the strings "raw", "pkcs8", or "spki":
1498                //     Let key be bytes.
1499                // If format is equal to the string "jwk":
1500                //     Let key be the result of executing the parse a JWK algorithm, with bytes as
1501                //     the data to be parsed.
1502                //     NOTE: We only parse bytes by executing the parse a JWK algorithm, but keep
1503                //     it as raw bytes for later steps, instead of converting it to a JsonWebKey
1504                //     dictionary.
1505                let cx = GlobalScope::get_cx();
1506                if format == KeyFormat::Jwk {
1507                    if let Err(error) = JsonWebKey::parse(cx, &bytes) {
1508                        subtle.reject_promise_with_error(promise, error);
1509                        return;
1510                    }
1511                }
1512                let key = bytes;
1513
1514                // Step 16. Let result be the result of performing the import key operation
1515                // specified by normalizedKeyAlgorithm using unwrappedKeyAlgorithm as algorithm,
1516                // format, usages and extractable and with key as keyData.
1517                let result = match normalized_key_algorithm.import_key(
1518                    &subtle.global(),
1519                    format,
1520                    &key,
1521                    extractable,
1522                    usages.clone(),
1523                    CanGc::note(),
1524                ) {
1525                    Ok(result) => result,
1526                    Err(error) => {
1527                        subtle.reject_promise_with_error(promise, error);
1528                        return;
1529                    },
1530                };
1531
1532                // Step 17. If the [[type]] internal slot of result is "secret" or "private" and
1533                // usages is empty, then throw a SyntaxError.
1534                if matches!(result.Type(), KeyType::Secret | KeyType::Private) && usages.is_empty() {
1535                    subtle.reject_promise_with_error(promise, Error::Syntax(None));
1536                    return;
1537                }
1538
1539                // Step 18. Set the [[extractable]] internal slot of result to extractable.
1540                // Step 19. Set the [[usages]] internal slot of result to the normalized value of
1541                // usages.
1542                // NOTE: Done by normalized_algorithm.import_key in Step 16.
1543
1544                // Step 20. Queue a global task on the crypto task source, given realm's global
1545                // object, to perform the remaining steps.
1546                // Step 21. Let result be the result of converting result to an ECMAScript Object
1547                // in realm, as defined by [WebIDL].
1548                // Step 22. Resolve promise with result.
1549                subtle.resolve_promise_with_key(promise, result);
1550            }),
1551        );
1552        promise
1553    }
1554}
1555
1556// These "subtle" structs are proxies for the codegen'd dicts which don't hold a DOMString
1557// so they can be sent safely when running steps in parallel.
1558
1559/// <https://w3c.github.io/webcrypto/#dfn-Algorithm>
1560#[derive(Clone, Debug, MallocSizeOf)]
1561struct SubtleAlgorithm {
1562    /// <https://w3c.github.io/webcrypto/#dom-algorithm-name>
1563    name: String,
1564}
1565
1566impl From<Algorithm> for SubtleAlgorithm {
1567    fn from(params: Algorithm) -> Self {
1568        SubtleAlgorithm {
1569            name: params.name.to_string(),
1570        }
1571    }
1572}
1573
1574/// <https://w3c.github.io/webcrypto/#dfn-KeyAlgorithm>
1575#[derive(Clone, Debug, MallocSizeOf)]
1576pub(crate) struct SubtleKeyAlgorithm {
1577    /// <https://w3c.github.io/webcrypto/#dom-keyalgorithm-name>
1578    name: String,
1579}
1580
1581impl SubtleKeyAlgorithm {
1582    fn block_size_in_bits(&self) -> Result<u32, Error> {
1583        let size = match self.name.as_str() {
1584            ALG_SHA1 => 160,
1585            ALG_SHA256 => 256,
1586            ALG_SHA384 => 384,
1587            ALG_SHA512 => 512,
1588            _ => {
1589                return Err(Error::NotSupported);
1590            },
1591        };
1592
1593        Ok(size)
1594    }
1595}
1596
1597impl From<NormalizedAlgorithm> for SubtleKeyAlgorithm {
1598    fn from(value: NormalizedAlgorithm) -> Self {
1599        SubtleKeyAlgorithm {
1600            name: value.name().to_string(),
1601        }
1602    }
1603}
1604
1605impl SafeToJSValConvertible for SubtleKeyAlgorithm {
1606    fn safe_to_jsval(&self, cx: JSContext, rval: MutableHandleValue) {
1607        let dictionary = KeyAlgorithm {
1608            name: self.name.clone().into(),
1609        };
1610        dictionary.safe_to_jsval(cx, rval);
1611    }
1612}
1613
1614#[derive(Clone, Debug, MallocSizeOf)]
1615pub(crate) struct SubtleAesCbcParams {
1616    pub(crate) name: String,
1617    pub(crate) iv: Vec<u8>,
1618}
1619
1620impl From<RootedTraceableBox<AesCbcParams>> for SubtleAesCbcParams {
1621    fn from(params: RootedTraceableBox<AesCbcParams>) -> Self {
1622        let iv = match &params.iv {
1623            ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
1624            ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
1625        };
1626        SubtleAesCbcParams {
1627            name: params.parent.name.to_string(),
1628            iv,
1629        }
1630    }
1631}
1632
1633#[derive(Clone, Debug, MallocSizeOf)]
1634pub(crate) struct SubtleAesCtrParams {
1635    pub(crate) name: String,
1636    pub(crate) counter: Vec<u8>,
1637    pub(crate) length: u8,
1638}
1639
1640impl From<RootedTraceableBox<AesCtrParams>> for SubtleAesCtrParams {
1641    fn from(params: RootedTraceableBox<AesCtrParams>) -> Self {
1642        let counter = match &params.counter {
1643            ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
1644            ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
1645        };
1646        SubtleAesCtrParams {
1647            name: params.parent.name.to_string(),
1648            counter,
1649            length: params.length,
1650        }
1651    }
1652}
1653
1654#[derive(Clone, Debug, MallocSizeOf)]
1655pub(crate) struct SubtleAesGcmParams {
1656    pub(crate) name: String,
1657    pub(crate) iv: Vec<u8>,
1658    pub(crate) additional_data: Option<Vec<u8>>,
1659    pub(crate) tag_length: Option<u8>,
1660}
1661
1662impl From<RootedTraceableBox<AesGcmParams>> for SubtleAesGcmParams {
1663    fn from(params: RootedTraceableBox<AesGcmParams>) -> Self {
1664        let iv = match &params.iv {
1665            ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
1666            ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
1667        };
1668        let additional_data = params.additionalData.as_ref().map(|data| match data {
1669            ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
1670            ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
1671        });
1672
1673        SubtleAesGcmParams {
1674            name: params.parent.name.to_string(),
1675            iv,
1676            additional_data,
1677            tag_length: params.tagLength,
1678        }
1679    }
1680}
1681
1682/// <https://w3c.github.io/webcrypto/#dfn-AesKeyAlgorithm>
1683#[derive(Clone, Debug, MallocSizeOf)]
1684pub(crate) struct SubtleAesKeyAlgorithm {
1685    /// <https://w3c.github.io/webcrypto/#dom-keyalgorithm-name>
1686    name: String,
1687
1688    /// <https://w3c.github.io/webcrypto/#dfn-AesKeyAlgorithm-length>
1689    length: u16,
1690}
1691
1692impl SafeToJSValConvertible for SubtleAesKeyAlgorithm {
1693    fn safe_to_jsval(&self, cx: JSContext, rval: MutableHandleValue) {
1694        let parent = KeyAlgorithm {
1695            name: self.name.clone().into(),
1696        };
1697        let dictionary = AesKeyAlgorithm {
1698            parent,
1699            length: self.length,
1700        };
1701        dictionary.safe_to_jsval(cx, rval);
1702    }
1703}
1704
1705/// <https://w3c.github.io/webcrypto/#dfn-AesKeyGenParams>
1706#[derive(Clone, Debug, MallocSizeOf)]
1707struct SubtleAesKeyGenParams {
1708    /// <https://w3c.github.io/webcrypto/#dom-algorithm-name>
1709    name: String,
1710
1711    /// <https://w3c.github.io/webcrypto/#dfn-AesKeyGenParams-length>
1712    length: u16,
1713}
1714
1715impl From<AesKeyGenParams> for SubtleAesKeyGenParams {
1716    fn from(params: AesKeyGenParams) -> Self {
1717        SubtleAesKeyGenParams {
1718            name: params.parent.name.to_string(),
1719            length: params.length,
1720        }
1721    }
1722}
1723
1724/// <https://w3c.github.io/webcrypto/#dfn-AesDerivedKeyParams>
1725#[derive(Clone, Debug, MallocSizeOf)]
1726struct SubtleAesDerivedKeyParams {
1727    /// <https://w3c.github.io/webcrypto/#dom-algorithm-name>
1728    name: String,
1729
1730    /// <https://w3c.github.io/webcrypto/#dfn-AesDerivedKeyParams-length>
1731    length: u16,
1732}
1733
1734impl From<AesDerivedKeyParams> for SubtleAesDerivedKeyParams {
1735    fn from(params: AesDerivedKeyParams) -> Self {
1736        SubtleAesDerivedKeyParams {
1737            name: params.parent.name.to_string(),
1738            length: params.length,
1739        }
1740    }
1741}
1742
1743/// <https://w3c.github.io/webcrypto/#dfn-HmacImportParams>
1744#[derive(Clone, Debug, MallocSizeOf)]
1745struct SubtleHmacImportParams {
1746    /// <https://w3c.github.io/webcrypto/#dom-algorithm-name>
1747    name: String,
1748
1749    /// <https://w3c.github.io/webcrypto/#dfn-HmacImportParams-hash>
1750    hash: SubtleKeyAlgorithm,
1751
1752    /// <https://w3c.github.io/webcrypto/#dfn-HmacImportParams-length>
1753    length: Option<u32>,
1754}
1755
1756impl TryFrom<RootedTraceableBox<HmacImportParams>> for SubtleHmacImportParams {
1757    type Error = Error;
1758
1759    fn try_from(params: RootedTraceableBox<HmacImportParams>) -> Result<Self, Error> {
1760        let cx = GlobalScope::get_cx();
1761        let hash = normalize_algorithm(cx, &Operation::Digest, &params.hash, CanGc::note())?;
1762        Ok(SubtleHmacImportParams {
1763            name: params.parent.name.to_string(),
1764            hash: hash.into(),
1765            length: params.length,
1766        })
1767    }
1768}
1769
1770/// <https://w3c.github.io/webcrypto/#dfn-HmacKeyAlgorithm>
1771#[derive(Clone, Debug, MallocSizeOf)]
1772pub(crate) struct SubtleHmacKeyAlgorithm {
1773    /// <https://w3c.github.io/webcrypto/#dom-keyalgorithm-name>
1774    name: String,
1775
1776    /// <https://w3c.github.io/webcrypto/#dfn-HmacKeyAlgorithm-hash>
1777    hash: SubtleKeyAlgorithm,
1778
1779    /// <https://w3c.github.io/webcrypto/#dfn-HmacKeyGenParams-length>
1780    length: u32,
1781}
1782
1783impl SafeToJSValConvertible for SubtleHmacKeyAlgorithm {
1784    fn safe_to_jsval(&self, cx: JSContext, rval: MutableHandleValue) {
1785        let parent = KeyAlgorithm {
1786            name: self.name.clone().into(),
1787        };
1788        let hash = KeyAlgorithm {
1789            name: self.hash.name.clone().into(),
1790        };
1791        let dictionary = HmacKeyAlgorithm {
1792            parent,
1793            hash,
1794            length: self.length,
1795        };
1796        dictionary.safe_to_jsval(cx, rval);
1797    }
1798}
1799
1800/// <https://w3c.github.io/webcrypto/#dfn-HmacKeyGenParams>
1801#[derive(Clone, Debug, MallocSizeOf)]
1802struct SubtleHmacKeyGenParams {
1803    /// <https://w3c.github.io/webcrypto/#dom-algorithm-name>
1804    name: String,
1805
1806    /// <https://w3c.github.io/webcrypto/#dfn-HmacKeyGenParams-hash>
1807    hash: SubtleKeyAlgorithm,
1808
1809    /// <https://w3c.github.io/webcrypto/#dfn-HmacKeyGenParams-length>
1810    length: Option<u32>,
1811}
1812
1813impl TryFrom<RootedTraceableBox<HmacKeyGenParams>> for SubtleHmacKeyGenParams {
1814    type Error = Error;
1815
1816    fn try_from(params: RootedTraceableBox<HmacKeyGenParams>) -> Result<Self, Error> {
1817        let cx = GlobalScope::get_cx();
1818        let hash = normalize_algorithm(cx, &Operation::Digest, &params.hash, CanGc::note())?;
1819        Ok(SubtleHmacKeyGenParams {
1820            name: params.parent.name.to_string(),
1821            hash: hash.into(),
1822            length: params.length,
1823        })
1824    }
1825}
1826
1827/// <https://w3c.github.io/webcrypto/#dfn-HkdfParams>
1828#[derive(Clone, Debug, MallocSizeOf)]
1829pub(crate) struct SubtleHkdfParams {
1830    /// <https://w3c.github.io/webcrypto/#dom-algorithm-name>
1831    name: String,
1832
1833    /// <https://w3c.github.io/webcrypto/#dfn-HkdfParams-hash>
1834    hash: SubtleKeyAlgorithm,
1835
1836    /// <https://w3c.github.io/webcrypto/#dfn-HkdfParams-salt>
1837    salt: Vec<u8>,
1838
1839    /// <https://w3c.github.io/webcrypto/#dfn-HkdfParams-info>
1840    info: Vec<u8>,
1841}
1842
1843impl TryFrom<RootedTraceableBox<HkdfParams>> for SubtleHkdfParams {
1844    type Error = Error;
1845
1846    fn try_from(params: RootedTraceableBox<HkdfParams>) -> Result<Self, Error> {
1847        let cx = GlobalScope::get_cx();
1848        let hash = normalize_algorithm(cx, &Operation::Digest, &params.hash, CanGc::note())?;
1849        let salt = match &params.salt {
1850            ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
1851            ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
1852        };
1853        let info = match &params.info {
1854            ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
1855            ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
1856        };
1857        Ok(SubtleHkdfParams {
1858            name: params.parent.name.to_string(),
1859            hash: hash.into(),
1860            salt,
1861            info,
1862        })
1863    }
1864}
1865
1866/// <https://w3c.github.io/webcrypto/#dfn-Pbkdf2Params>
1867#[derive(Clone, Debug, MallocSizeOf)]
1868pub(crate) struct SubtlePbkdf2Params {
1869    /// <https://w3c.github.io/webcrypto/#dom-algorithm-name>
1870    name: String,
1871
1872    /// <https://w3c.github.io/webcrypto/#dfn-Pbkdf2Params-salt>
1873    salt: Vec<u8>,
1874
1875    /// <https://w3c.github.io/webcrypto/#dfn-Pbkdf2Params-iterations>
1876    iterations: u32,
1877
1878    /// <https://w3c.github.io/webcrypto/#dfn-Pbkdf2Params-hash>
1879    hash: SubtleKeyAlgorithm,
1880}
1881
1882impl TryFrom<RootedTraceableBox<Pbkdf2Params>> for SubtlePbkdf2Params {
1883    type Error = Error;
1884
1885    fn try_from(params: RootedTraceableBox<Pbkdf2Params>) -> Result<Self, Error> {
1886        let cx = GlobalScope::get_cx();
1887        let salt = match &params.salt {
1888            ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
1889            ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
1890        };
1891        let hash = normalize_algorithm(cx, &Operation::Digest, &params.hash, CanGc::note())?;
1892        Ok(SubtlePbkdf2Params {
1893            name: params.parent.name.to_string(),
1894            salt,
1895            iterations: params.iterations,
1896            hash: hash.into(),
1897        })
1898    }
1899}
1900
1901/// Helper to abstract the conversion process of a JS value into many different WebIDL dictionaries.
1902trait DictionaryFromJSVal: Sized {
1903    fn create(
1904        cx: JSContext,
1905        value: HandleValue,
1906        can_gc: CanGc,
1907    ) -> Result<ConversionResult<Self>, ()>;
1908}
1909
1910impl DictionaryFromJSVal for Algorithm {
1911    fn create(
1912        cx: JSContext,
1913        value: HandleValue,
1914        can_gc: CanGc,
1915    ) -> Result<ConversionResult<Self>, ()> {
1916        Self::new(cx, value, can_gc)
1917    }
1918}
1919
1920impl DictionaryFromJSVal for AesDerivedKeyParams {
1921    fn create(
1922        cx: JSContext,
1923        value: HandleValue,
1924        can_gc: CanGc,
1925    ) -> Result<ConversionResult<Self>, ()> {
1926        Self::new(cx, value, can_gc)
1927    }
1928}
1929
1930impl DictionaryFromJSVal for AesKeyGenParams {
1931    fn create(
1932        cx: JSContext,
1933        value: HandleValue,
1934        can_gc: CanGc,
1935    ) -> Result<ConversionResult<Self>, ()> {
1936        Self::new(cx, value, can_gc)
1937    }
1938}
1939
1940impl DictionaryFromJSVal for RootedTraceableBox<HmacImportParams> {
1941    fn create(
1942        cx: JSContext,
1943        value: HandleValue,
1944        can_gc: CanGc,
1945    ) -> Result<ConversionResult<Self>, ()> {
1946        HmacImportParams::new(cx, value, can_gc)
1947    }
1948}
1949
1950impl DictionaryFromJSVal for RootedTraceableBox<HmacKeyGenParams> {
1951    fn create(
1952        cx: JSContext,
1953        value: HandleValue,
1954        can_gc: CanGc,
1955    ) -> Result<ConversionResult<Self>, ()> {
1956        HmacKeyGenParams::new(cx, value, can_gc)
1957    }
1958}
1959
1960impl DictionaryFromJSVal for HmacKeyAlgorithm {
1961    fn create(
1962        cx: JSContext,
1963        value: HandleValue,
1964        can_gc: CanGc,
1965    ) -> Result<ConversionResult<Self>, ()> {
1966        Self::new(cx, value, can_gc)
1967    }
1968}
1969
1970impl DictionaryFromJSVal for RootedTraceableBox<AesCbcParams> {
1971    fn create(
1972        cx: JSContext,
1973        value: HandleValue,
1974        can_gc: CanGc,
1975    ) -> Result<ConversionResult<Self>, ()> {
1976        AesCbcParams::new(cx, value, can_gc)
1977    }
1978}
1979
1980impl DictionaryFromJSVal for RootedTraceableBox<AesCtrParams> {
1981    fn create(
1982        cx: JSContext,
1983        value: HandleValue,
1984        can_gc: CanGc,
1985    ) -> Result<ConversionResult<Self>, ()> {
1986        AesCtrParams::new(cx, value, can_gc)
1987    }
1988}
1989
1990impl DictionaryFromJSVal for RootedTraceableBox<AesGcmParams> {
1991    fn create(
1992        cx: JSContext,
1993        value: HandleValue,
1994        can_gc: CanGc,
1995    ) -> Result<ConversionResult<Self>, ()> {
1996        AesGcmParams::new(cx, value, can_gc)
1997    }
1998}
1999
2000impl DictionaryFromJSVal for RootedTraceableBox<Pbkdf2Params> {
2001    fn create(
2002        cx: JSContext,
2003        value: HandleValue,
2004        can_gc: CanGc,
2005    ) -> Result<ConversionResult<Self>, ()> {
2006        Pbkdf2Params::new(cx, value, can_gc)
2007    }
2008}
2009
2010impl DictionaryFromJSVal for RootedTraceableBox<HkdfParams> {
2011    fn create(
2012        cx: JSContext,
2013        value: HandleValue,
2014        can_gc: CanGc,
2015    ) -> Result<ConversionResult<Self>, ()> {
2016        HkdfParams::new(cx, value, can_gc)
2017    }
2018}
2019
2020fn extract_native_dict<T>(converted: Result<ConversionResult<T>, ()>) -> Fallible<T> {
2021    let params_result = converted.map_err(|_| Error::JSFailed)?;
2022    let ConversionResult::Success(params) = params_result else {
2023        return Err(Error::Syntax(None));
2024    };
2025    Ok(params)
2026}
2027
2028fn value_from_js_object<T: DictionaryFromJSVal>(
2029    cx: JSContext,
2030    value: HandleValue,
2031    can_gc: CanGc,
2032) -> Fallible<T> {
2033    extract_native_dict(T::create(cx, value, can_gc))
2034}
2035
2036trait DictionaryFromJSValType: crate::JSTraceable {}
2037impl<T: crate::JSTraceable + 'static> DictionaryFromJSValType for T where
2038    RootedTraceableBox<T>: DictionaryFromJSVal
2039{
2040}
2041
2042fn boxed_value_from_js_object<T: DictionaryFromJSValType>(
2043    cx: JSContext,
2044    value: HandleValue,
2045    can_gc: CanGc,
2046) -> Fallible<RootedTraceableBox<T>>
2047where
2048    RootedTraceableBox<T>: DictionaryFromJSVal,
2049{
2050    extract_native_dict(<RootedTraceableBox<T>>::create(cx, value, can_gc))
2051}
2052
2053pub(crate) enum ExportedKey {
2054    Raw(Vec<u8>),
2055    Jwk(Box<JsonWebKey>),
2056}
2057
2058/// Union type of KeyAlgorithm and IDL dictionary types derived from it. Note that we actually use
2059/// our "subtle" structs of the corresponding IDL dictionary types so that they can be easily
2060/// passed to another threads.
2061#[derive(Clone, Debug, MallocSizeOf)]
2062#[allow(clippy::enum_variant_names)]
2063pub(crate) enum KeyAlgorithmAndDerivatives {
2064    KeyAlgorithm(SubtleKeyAlgorithm),
2065    AesKeyAlgorithm(SubtleAesKeyAlgorithm),
2066    HmacKeyAlgorithm(SubtleHmacKeyAlgorithm),
2067}
2068
2069impl KeyAlgorithmAndDerivatives {
2070    fn name(&self) -> &str {
2071        match self {
2072            KeyAlgorithmAndDerivatives::KeyAlgorithm(algo) => &algo.name,
2073            KeyAlgorithmAndDerivatives::AesKeyAlgorithm(algo) => &algo.name,
2074            KeyAlgorithmAndDerivatives::HmacKeyAlgorithm(algo) => &algo.name,
2075        }
2076    }
2077}
2078
2079impl From<NormalizedAlgorithm> for KeyAlgorithmAndDerivatives {
2080    fn from(value: NormalizedAlgorithm) -> Self {
2081        KeyAlgorithmAndDerivatives::KeyAlgorithm(SubtleKeyAlgorithm {
2082            name: value.name().to_string(),
2083        })
2084    }
2085}
2086
2087impl SafeToJSValConvertible for KeyAlgorithmAndDerivatives {
2088    fn safe_to_jsval(&self, cx: JSContext, rval: MutableHandleValue) {
2089        match self {
2090            KeyAlgorithmAndDerivatives::KeyAlgorithm(algo) => algo.safe_to_jsval(cx, rval),
2091            KeyAlgorithmAndDerivatives::AesKeyAlgorithm(algo) => algo.safe_to_jsval(cx, rval),
2092            KeyAlgorithmAndDerivatives::HmacKeyAlgorithm(algo) => algo.safe_to_jsval(cx, rval),
2093        }
2094    }
2095}
2096
2097#[expect(unused)]
2098trait RsaOtherPrimesInfoExt {
2099    fn from_value(value: &serde_json::Value) -> Result<RsaOtherPrimesInfo, Error>;
2100}
2101
2102impl RsaOtherPrimesInfoExt for RsaOtherPrimesInfo {
2103    fn from_value(value: &serde_json::Value) -> Result<RsaOtherPrimesInfo, Error> {
2104        let serde_json::Value::Object(object) = value else {
2105            return Err(Error::Data);
2106        };
2107
2108        let mut rsa_other_primes_info: RsaOtherPrimesInfo = Default::default();
2109        for (key, value) in object {
2110            match key.as_str() {
2111                "r" => {
2112                    rsa_other_primes_info.r =
2113                        Some(DOMString::from(value.as_str().ok_or(Error::Data)?))
2114                },
2115                "d" => {
2116                    rsa_other_primes_info.d =
2117                        Some(DOMString::from(value.as_str().ok_or(Error::Data)?))
2118                },
2119                "t" => {
2120                    rsa_other_primes_info.t =
2121                        Some(DOMString::from(value.as_str().ok_or(Error::Data)?))
2122                },
2123                _ => {
2124                    // Additional members can be present in the JWK; if not understood by
2125                    // implementations encountering them, they MUST be ignored.
2126                },
2127            }
2128        }
2129
2130        Ok(rsa_other_primes_info)
2131    }
2132}
2133
2134trait JsonWebKeyExt {
2135    fn parse(cx: JSContext, data: &[u8]) -> Result<JsonWebKey, Error>;
2136    fn stringify(&self, cx: JSContext) -> Result<DOMString, Error>;
2137    fn get_usages_from_key_ops(&self) -> Result<Vec<KeyUsage>, Error>;
2138    #[expect(unused)]
2139    fn get_rsa_other_primes_info_from_oth(&self) -> Result<&[RsaOtherPrimesInfo], Error>;
2140    fn check_key_ops(&self, specified_usages: &[KeyUsage]) -> Result<(), Error>;
2141}
2142
2143impl JsonWebKeyExt for JsonWebKey {
2144    /// <https://w3c.github.io/webcrypto/#concept-parse-a-jwk>
2145    #[allow(unsafe_code)]
2146    fn parse(cx: JSContext, data: &[u8]) -> Result<JsonWebKey, Error> {
2147        // Step 1. Let data be the sequence of bytes to be parsed.
2148        // (It is given as a method paramter.)
2149
2150        // Step 2. Let json be the Unicode string that results from interpreting data according to UTF-8.
2151        let json = String::from_utf8_lossy(data);
2152
2153        // Step 3. Convert json to UTF-16.
2154        let json: Vec<_> = json.encode_utf16().collect();
2155
2156        // Step 4. Let result be the object literal that results from executing the JSON.parse
2157        // internal function in the context of a new global object, with text argument set to a
2158        // JavaScript String containing json.
2159        rooted!(in(*cx) let mut result = UndefinedValue());
2160        unsafe {
2161            if !JS_ParseJSON(*cx, json.as_ptr(), json.len() as u32, result.handle_mut()) {
2162                return Err(Error::JSFailed);
2163            }
2164        }
2165
2166        // Step 5. Let key be the result of converting result to the IDL dictionary type of JsonWebKey.
2167        let key = match JsonWebKey::new(cx, result.handle(), CanGc::note()) {
2168            Ok(ConversionResult::Success(key)) => key,
2169            Ok(ConversionResult::Failure(error)) => {
2170                return Err(Error::Type(error.to_string()));
2171            },
2172            Err(()) => {
2173                return Err(Error::JSFailed);
2174            },
2175        };
2176
2177        // Step 6. If the kty field of key is not defined, then throw a DataError.
2178        if key.kty.is_none() {
2179            return Err(Error::Data);
2180        }
2181
2182        // Step 7. Result key.
2183        Ok(key)
2184    }
2185
2186    /// Convert a JsonWebKey value to DOMString. We first convert the JsonWebKey value to
2187    /// JavaScript value, and then serialize it by performing steps in
2188    /// <https://infra.spec.whatwg.org/#serialize-a-javascript-value-to-a-json-string>. This acts
2189    /// like the opposite of JsonWebKey::parse if you further convert the stringified result to
2190    /// bytes.
2191    fn stringify(&self, cx: JSContext) -> Result<DOMString, Error> {
2192        rooted!(in(*cx) let mut data = UndefinedValue());
2193        self.safe_to_jsval(cx, data.handle_mut());
2194        serialize_jsval_to_json_utf8(cx, data.handle())
2195    }
2196
2197    fn get_usages_from_key_ops(&self) -> Result<Vec<KeyUsage>, Error> {
2198        let mut usages = vec![];
2199        for op in self.key_ops.as_ref().ok_or(Error::Data)? {
2200            usages.push(KeyUsage::from_str(&op.str()).map_err(|_| Error::Data)?);
2201        }
2202        Ok(usages)
2203    }
2204
2205    fn get_rsa_other_primes_info_from_oth(&self) -> Result<&[RsaOtherPrimesInfo], Error> {
2206        self.oth.as_deref().ok_or(Error::Data)
2207    }
2208
2209    /// If the key_ops field of jwk is present, and is invalid according to the requirements of
2210    /// JSON Web Key [JWK] or does not contain all of the specified usages values, then throw a
2211    /// DataError.
2212    fn check_key_ops(&self, specified_usages: &[KeyUsage]) -> Result<(), Error> {
2213        // If the key_ops field of jwk is present,
2214        if let Some(ref key_ops) = self.key_ops {
2215            // and is invalid according to the requirements of JSON Web Key [JWK]:
2216            // 1. Duplicate key operation values MUST NOT be present in the array.
2217            if key_ops
2218                .iter()
2219                .collect::<std::collections::HashSet<_>>()
2220                .len() <
2221                key_ops.len()
2222            {
2223                return Err(Error::Data);
2224            }
2225            // 2. The "use" and "key_ops" JWK members SHOULD NOT be used together; however, if both
2226            //    are used, the information they convey MUST be consistent.
2227            if let Some(ref use_) = self.use_ {
2228                if key_ops.iter().any(|op| op != use_) {
2229                    return Err(Error::Data);
2230                }
2231            }
2232
2233            // or does not contain all of the specified usages values
2234            let key_ops_as_usages = self.get_usages_from_key_ops()?;
2235            if !specified_usages
2236                .iter()
2237                .all(|specified_usage| key_ops_as_usages.contains(specified_usage))
2238            {
2239                return Err(Error::Data);
2240            }
2241        }
2242
2243        Ok(())
2244    }
2245}
2246
2247/// The successful output of [`normalize_algorithm`], in form of an union type of (our "subtle"
2248/// binding of) IDL dictionary types.
2249///
2250/// <https://w3c.github.io/webcrypto/#algorithm-normalization-normalize-an-algorithm>
2251#[derive(Clone, Debug, MallocSizeOf)]
2252enum NormalizedAlgorithm {
2253    Algorithm(SubtleAlgorithm),
2254    AesCtrParams(SubtleAesCtrParams),
2255    AesKeyGenParams(SubtleAesKeyGenParams),
2256    AesDerivedKeyParams(SubtleAesDerivedKeyParams),
2257    AesCbcParams(SubtleAesCbcParams),
2258    AesGcmParams(SubtleAesGcmParams),
2259    HmacImportParams(SubtleHmacImportParams),
2260    HmacKeyGenParams(SubtleHmacKeyGenParams),
2261    HkdfParams(SubtleHkdfParams),
2262    Pbkdf2Params(SubtlePbkdf2Params),
2263}
2264
2265/// <https://w3c.github.io/webcrypto/#algorithm-normalization-normalize-an-algorithm>
2266fn normalize_algorithm(
2267    cx: JSContext,
2268    op: &Operation,
2269    alg: &AlgorithmIdentifier,
2270    can_gc: CanGc,
2271) -> Result<NormalizedAlgorithm, Error> {
2272    match alg {
2273        // If alg is an instance of a DOMString:
2274        ObjectOrString::String(name) => {
2275            // Return the result of running the normalize an algorithm algorithm, with the alg set
2276            // to a new Algorithm dictionary whose name attribute is alg, and with the op set to
2277            // op.
2278            let alg = Algorithm {
2279                name: name.to_owned(),
2280            };
2281            rooted!(in(*cx) let mut alg_value = UndefinedValue());
2282            alg.safe_to_jsval(cx, alg_value.handle_mut());
2283            let alg_obj = RootedTraceableBox::new(Heap::default());
2284            alg_obj.set(alg_value.to_object());
2285            normalize_algorithm(cx, op, &ObjectOrString::Object(alg_obj), can_gc)
2286        },
2287        // If alg is an object:
2288        ObjectOrString::Object(obj) => {
2289            // Step 1. Let registeredAlgorithms be the associative container stored at the op key
2290            // of supportedAlgorithms.
2291            // NOTE: The supportedAlgorithms and registeredAlgorithms are expressed as match arms
2292            // in Step 5.2 - Step 10.
2293
2294            // Stpe 2. Let initialAlg be the result of converting the ECMAScript object represented
2295            // by alg to the IDL dictionary type Algorithm, as defined by [WebIDL].
2296            // Step 3. If an error occurred, return the error and terminate this algorithm.
2297            rooted!(in(*cx) let value = ObjectValue(obj.get()));
2298            let initial_alg = value_from_js_object::<Algorithm>(cx, value.handle(), can_gc)?;
2299
2300            // Step 4. Let algName be the value of the name attribute of initialAlg.
2301            // Step 5.
2302            //     If registeredAlgorithms contains a key that is a case-insensitive string match
2303            //     for algName:
2304            //         Step 5.1. Set algName to the value of the matching key.
2305            //     Otherwise:
2306            //         Return a new NotSupportedError and terminate this algorithm.
2307            let Some(&alg_name) = SUPPORTED_ALGORITHMS.iter().find(|supported_algorithm| {
2308                supported_algorithm.eq_ignore_ascii_case(&initial_alg.name.str())
2309            }) else {
2310                return Err(Error::NotSupported);
2311            };
2312
2313            // Step 5.2. Let desiredType be the IDL dictionary type stored at algName in
2314            // registeredAlgorithms.
2315            // Step 6. Let normalizedAlgorithm be the result of converting the ECMAScript object
2316            // represented by alg to the IDL dictionary type desiredType, as defined by [WebIDL].
2317            // Step 7. Set the name attribute of normalizedAlgorithm to algName.
2318            // Step 8. If an error occurred, return the error and terminate this algorithm.
2319            // Step 9. Let dictionaries be a list consisting of the IDL dictionary type desiredType
2320            // and all of desiredType's inherited dictionaries, in order from least to most
2321            // derived.
2322            // Step 10. For each dictionary dictionary in dictionaries:
2323            //     Step 10.1. For each dictionary member member declared on dictionary, in order:
2324            //         Step 10.1.1. Let key be the identifier of member.
2325            //         Step 10.1.2. Let idlValue be the value of the dictionary member with key
2326            //         name of key on normalizedAlgorithm.
2327            //         Step 10.1.3.
2328            //             If member is of the type BufferSource and is present:
2329            //                 Set the dictionary member on normalizedAlgorithm with key name key
2330            //                 to the result of getting a copy of the bytes held by idlValue,
2331            //                 replacing the current value.
2332            //             If member is of the type HashAlgorithmIdentifier:
2333            //                 Set the dictionary member on normalizedAlgorithm with key name key
2334            //                 to the result of normalizing an algorithm, with the alg set to
2335            //                 idlValue and the op set to "digest".
2336            //             If member is of the type AlgorithmIdentifier:
2337            //                 Set the dictionary member on normalizedAlgorithm with key name key
2338            //                 to the result of normalizing an algorithm, with the alg set to
2339            //                 idlValue and the op set to the operation defined by the
2340            //                 specification that defines the algorithm identified by algName.
2341            //
2342            // NOTE: Instead of calculating the desiredType in Step 5.2 and filling in the IDL
2343            // dictionary in Step 7-10, we directly convert the JS object to our "subtle" binding
2344            // structs to complete Step 6, and put it in the NormalizedAlgorithm enum.
2345            //
2346            // NOTE: Step 10.1.3 is done by the `From` and `TryFrom` trait implementation of
2347            // "subtle" binding structs.
2348            let normalized_algorithm = match (alg_name, op) {
2349                // <https://w3c.github.io/webcrypto/#aes-ctr-registration>
2350                (ALG_AES_CTR, Operation::Encrypt) => {
2351                    let mut params =
2352                        boxed_value_from_js_object::<AesCtrParams>(cx, value.handle(), can_gc)?;
2353                    params.parent.name = DOMString::from(alg_name);
2354                    NormalizedAlgorithm::AesCtrParams(params.into())
2355                },
2356                (ALG_AES_CTR, Operation::Decrypt) => {
2357                    let mut params =
2358                        boxed_value_from_js_object::<AesCtrParams>(cx, value.handle(), can_gc)?;
2359                    params.parent.name = DOMString::from(alg_name);
2360                    NormalizedAlgorithm::AesCtrParams(params.into())
2361                },
2362                (ALG_AES_CTR, Operation::GenerateKey) => {
2363                    let mut params =
2364                        value_from_js_object::<AesKeyGenParams>(cx, value.handle(), can_gc)?;
2365                    params.parent.name = DOMString::from(alg_name);
2366                    NormalizedAlgorithm::AesKeyGenParams(params.into())
2367                },
2368                (ALG_AES_CTR, Operation::ImportKey) => {
2369                    let mut params = value_from_js_object::<Algorithm>(cx, value.handle(), can_gc)?;
2370                    params.name = DOMString::from(alg_name);
2371                    NormalizedAlgorithm::Algorithm(params.into())
2372                },
2373                (ALG_AES_CTR, Operation::ExportKey) => {
2374                    let mut params = value_from_js_object::<Algorithm>(cx, value.handle(), can_gc)?;
2375                    params.name = DOMString::from(alg_name);
2376                    NormalizedAlgorithm::Algorithm(params.into())
2377                },
2378                (ALG_AES_CTR, Operation::GetKeyLength) => {
2379                    let mut params =
2380                        value_from_js_object::<AesDerivedKeyParams>(cx, value.handle(), can_gc)?;
2381                    params.parent.name = DOMString::from(alg_name);
2382                    NormalizedAlgorithm::AesDerivedKeyParams(params.into())
2383                },
2384
2385                // <https://w3c.github.io/webcrypto/#aes-cbc-registration>
2386                (ALG_AES_CBC, Operation::Encrypt) => {
2387                    let mut params =
2388                        boxed_value_from_js_object::<AesCbcParams>(cx, value.handle(), can_gc)?;
2389                    params.parent.name = DOMString::from(alg_name);
2390                    NormalizedAlgorithm::AesCbcParams(params.into())
2391                },
2392                (ALG_AES_CBC, Operation::Decrypt) => {
2393                    let mut params =
2394                        boxed_value_from_js_object::<AesCbcParams>(cx, value.handle(), can_gc)?;
2395                    params.parent.name = DOMString::from(alg_name);
2396                    NormalizedAlgorithm::AesCbcParams(params.into())
2397                },
2398                (ALG_AES_CBC, Operation::GenerateKey) => {
2399                    let mut params =
2400                        value_from_js_object::<AesKeyGenParams>(cx, value.handle(), can_gc)?;
2401                    params.parent.name = DOMString::from(alg_name);
2402                    NormalizedAlgorithm::AesKeyGenParams(params.into())
2403                },
2404                (ALG_AES_CBC, Operation::ImportKey) => {
2405                    let mut params = value_from_js_object::<Algorithm>(cx, value.handle(), can_gc)?;
2406                    params.name = DOMString::from(alg_name);
2407                    NormalizedAlgorithm::Algorithm(params.into())
2408                },
2409                (ALG_AES_CBC, Operation::ExportKey) => {
2410                    let mut params = value_from_js_object::<Algorithm>(cx, value.handle(), can_gc)?;
2411                    params.name = DOMString::from(alg_name);
2412                    NormalizedAlgorithm::Algorithm(params.into())
2413                },
2414                (ALG_AES_CBC, Operation::GetKeyLength) => {
2415                    let mut params =
2416                        value_from_js_object::<AesDerivedKeyParams>(cx, value.handle(), can_gc)?;
2417                    params.parent.name = DOMString::from(alg_name);
2418                    NormalizedAlgorithm::AesDerivedKeyParams(params.into())
2419                },
2420
2421                // <https://w3c.github.io/webcrypto/#aes-gcm-registration>
2422                (ALG_AES_GCM, Operation::Encrypt) => {
2423                    let mut params =
2424                        boxed_value_from_js_object::<AesGcmParams>(cx, value.handle(), can_gc)?;
2425                    params.parent.name = DOMString::from(alg_name);
2426                    NormalizedAlgorithm::AesGcmParams(params.into())
2427                },
2428                (ALG_AES_GCM, Operation::Decrypt) => {
2429                    let mut params =
2430                        boxed_value_from_js_object::<AesGcmParams>(cx, value.handle(), can_gc)?;
2431                    params.parent.name = DOMString::from(alg_name);
2432                    NormalizedAlgorithm::AesGcmParams(params.into())
2433                },
2434                (ALG_AES_GCM, Operation::GenerateKey) => {
2435                    let mut params =
2436                        value_from_js_object::<AesKeyGenParams>(cx, value.handle(), can_gc)?;
2437                    params.parent.name = DOMString::from(alg_name);
2438                    NormalizedAlgorithm::AesKeyGenParams(params.into())
2439                },
2440                (ALG_AES_GCM, Operation::ImportKey) => {
2441                    let mut params = value_from_js_object::<Algorithm>(cx, value.handle(), can_gc)?;
2442                    params.name = DOMString::from(alg_name);
2443                    NormalizedAlgorithm::Algorithm(params.into())
2444                },
2445                (ALG_AES_GCM, Operation::ExportKey) => {
2446                    let mut params = value_from_js_object::<Algorithm>(cx, value.handle(), can_gc)?;
2447                    params.name = DOMString::from(alg_name);
2448                    NormalizedAlgorithm::Algorithm(params.into())
2449                },
2450                (ALG_AES_GCM, Operation::GetKeyLength) => {
2451                    let mut params =
2452                        value_from_js_object::<AesDerivedKeyParams>(cx, value.handle(), can_gc)?;
2453                    params.parent.name = DOMString::from(alg_name);
2454                    NormalizedAlgorithm::AesDerivedKeyParams(params.into())
2455                },
2456
2457                // <https://w3c.github.io/webcrypto/#aes-kw-registration>
2458                (ALG_AES_KW, Operation::WrapKey) => {
2459                    let mut params = value_from_js_object::<Algorithm>(cx, value.handle(), can_gc)?;
2460                    params.name = DOMString::from(alg_name);
2461                    NormalizedAlgorithm::Algorithm(params.into())
2462                },
2463                (ALG_AES_KW, Operation::UnwrapKey) => {
2464                    let mut params = value_from_js_object::<Algorithm>(cx, value.handle(), can_gc)?;
2465                    params.name = DOMString::from(alg_name);
2466                    NormalizedAlgorithm::Algorithm(params.into())
2467                },
2468                (ALG_AES_KW, Operation::GenerateKey) => {
2469                    let mut params =
2470                        value_from_js_object::<AesKeyGenParams>(cx, value.handle(), can_gc)?;
2471                    params.parent.name = DOMString::from(alg_name);
2472                    NormalizedAlgorithm::AesKeyGenParams(params.into())
2473                },
2474                (ALG_AES_KW, Operation::ImportKey) => {
2475                    let mut params = value_from_js_object::<Algorithm>(cx, value.handle(), can_gc)?;
2476                    params.name = DOMString::from(alg_name);
2477                    NormalizedAlgorithm::Algorithm(params.into())
2478                },
2479                (ALG_AES_KW, Operation::ExportKey) => {
2480                    let mut params = value_from_js_object::<Algorithm>(cx, value.handle(), can_gc)?;
2481                    params.name = DOMString::from(alg_name);
2482                    NormalizedAlgorithm::Algorithm(params.into())
2483                },
2484                // FIXME: Register "get key length" operation of AES-KW
2485
2486                // <https://w3c.github.io/webcrypto/#hmac-registration>
2487                (ALG_HMAC, Operation::Sign) => {
2488                    let mut params = value_from_js_object::<Algorithm>(cx, value.handle(), can_gc)?;
2489                    params.name = DOMString::from(alg_name);
2490                    NormalizedAlgorithm::Algorithm(params.into())
2491                },
2492                (ALG_HMAC, Operation::Verify) => {
2493                    let mut params = value_from_js_object::<Algorithm>(cx, value.handle(), can_gc)?;
2494                    params.name = DOMString::from(alg_name);
2495                    NormalizedAlgorithm::Algorithm(params.into())
2496                },
2497                (ALG_HMAC, Operation::GenerateKey) => {
2498                    let mut params =
2499                        boxed_value_from_js_object::<HmacKeyGenParams>(cx, value.handle(), can_gc)?;
2500                    params.parent.name = DOMString::from(alg_name);
2501                    NormalizedAlgorithm::HmacKeyGenParams(params.try_into()?)
2502                },
2503                (ALG_HMAC, Operation::ImportKey) => {
2504                    let mut params =
2505                        boxed_value_from_js_object::<HmacImportParams>(cx, value.handle(), can_gc)?;
2506                    params.parent.name = DOMString::from(alg_name);
2507                    NormalizedAlgorithm::HmacImportParams(params.try_into()?)
2508                },
2509                (ALG_HMAC, Operation::ExportKey) => {
2510                    let mut params = value_from_js_object::<Algorithm>(cx, value.handle(), can_gc)?;
2511                    params.name = DOMString::from(alg_name);
2512                    NormalizedAlgorithm::Algorithm(params.into())
2513                },
2514                (ALG_HMAC, Operation::GetKeyLength) => {
2515                    let mut params =
2516                        boxed_value_from_js_object::<HmacImportParams>(cx, value.handle(), can_gc)?;
2517                    params.parent.name = DOMString::from(alg_name);
2518                    NormalizedAlgorithm::HmacImportParams(params.try_into()?)
2519                },
2520
2521                // <https://w3c.github.io/webcrypto/#sha-registration>
2522                (ALG_SHA1, Operation::Digest) => {
2523                    let mut params = value_from_js_object::<Algorithm>(cx, value.handle(), can_gc)?;
2524                    params.name = DOMString::from(alg_name);
2525                    NormalizedAlgorithm::Algorithm(params.into())
2526                },
2527                (ALG_SHA256, Operation::Digest) => {
2528                    let mut params = value_from_js_object::<Algorithm>(cx, value.handle(), can_gc)?;
2529                    params.name = DOMString::from(alg_name);
2530                    NormalizedAlgorithm::Algorithm(params.into())
2531                },
2532                (ALG_SHA384, Operation::Digest) => {
2533                    let mut params = value_from_js_object::<Algorithm>(cx, value.handle(), can_gc)?;
2534                    params.name = DOMString::from(alg_name);
2535                    NormalizedAlgorithm::Algorithm(params.into())
2536                },
2537                (ALG_SHA512, Operation::Digest) => {
2538                    let mut params = value_from_js_object::<Algorithm>(cx, value.handle(), can_gc)?;
2539                    params.name = DOMString::from(alg_name);
2540                    NormalizedAlgorithm::Algorithm(params.into())
2541                },
2542
2543                // <https://w3c.github.io/webcrypto/#hkdf-registration>
2544                (ALG_HKDF, Operation::DeriveBits) => {
2545                    let mut params =
2546                        boxed_value_from_js_object::<HkdfParams>(cx, value.handle(), can_gc)?;
2547                    params.parent.name = DOMString::from(alg_name);
2548                    NormalizedAlgorithm::HkdfParams(params.try_into()?)
2549                },
2550                (ALG_HKDF, Operation::ImportKey) => {
2551                    let mut params = value_from_js_object::<Algorithm>(cx, value.handle(), can_gc)?;
2552                    params.name = DOMString::from(alg_name);
2553                    NormalizedAlgorithm::Algorithm(params.into())
2554                },
2555                (ALG_HKDF, Operation::GetKeyLength) => {
2556                    let mut params = value_from_js_object::<Algorithm>(cx, value.handle(), can_gc)?;
2557                    params.name = DOMString::from(alg_name);
2558                    NormalizedAlgorithm::Algorithm(params.into())
2559                },
2560
2561                // <https://w3c.github.io/webcrypto/#pbkdf2-registration>
2562                (ALG_PBKDF2, Operation::DeriveBits) => {
2563                    let mut params =
2564                        boxed_value_from_js_object::<Pbkdf2Params>(cx, value.handle(), can_gc)?;
2565                    params.parent.name = DOMString::from(alg_name);
2566                    NormalizedAlgorithm::Pbkdf2Params(params.try_into()?)
2567                },
2568                (ALG_PBKDF2, Operation::ImportKey) => {
2569                    let mut params = value_from_js_object::<Algorithm>(cx, value.handle(), can_gc)?;
2570                    params.name = DOMString::from(alg_name);
2571                    NormalizedAlgorithm::Algorithm(params.into())
2572                },
2573                (ALG_PBKDF2, Operation::GetKeyLength) => {
2574                    let mut params = value_from_js_object::<Algorithm>(cx, value.handle(), can_gc)?;
2575                    params.name = DOMString::from(alg_name);
2576                    NormalizedAlgorithm::Algorithm(params.into())
2577                },
2578
2579                _ => return Err(Error::NotSupported),
2580            };
2581
2582            // Step 11. Return normalizedAlgorithm.
2583            Ok(normalized_algorithm)
2584        },
2585    }
2586}
2587
2588impl NormalizedAlgorithm {
2589    /// <https://w3c.github.io/webcrypto/#dom-algorithm-name>
2590    fn name(&self) -> &str {
2591        match self {
2592            NormalizedAlgorithm::Algorithm(algo) => &algo.name,
2593            NormalizedAlgorithm::AesCtrParams(algo) => &algo.name,
2594            NormalizedAlgorithm::AesKeyGenParams(algo) => &algo.name,
2595            NormalizedAlgorithm::AesDerivedKeyParams(algo) => &algo.name,
2596            NormalizedAlgorithm::AesCbcParams(algo) => &algo.name,
2597            NormalizedAlgorithm::AesGcmParams(algo) => &algo.name,
2598            NormalizedAlgorithm::HmacImportParams(algo) => &algo.name,
2599            NormalizedAlgorithm::HmacKeyGenParams(algo) => &algo.name,
2600            NormalizedAlgorithm::HkdfParams(algo) => &algo.name,
2601            NormalizedAlgorithm::Pbkdf2Params(algo) => &algo.name,
2602        }
2603    }
2604
2605    fn encrypt(&self, key: &CryptoKey, plaintext: &[u8]) -> Result<Vec<u8>, Error> {
2606        match self {
2607            NormalizedAlgorithm::AesCtrParams(algo) => {
2608                aes_operation::encrypt_aes_ctr(algo, key, plaintext)
2609            },
2610            NormalizedAlgorithm::AesCbcParams(algo) => {
2611                aes_operation::encrypt_aes_cbc(algo, key, plaintext)
2612            },
2613            NormalizedAlgorithm::AesGcmParams(algo) => {
2614                aes_operation::encrypt_aes_gcm(algo, key, plaintext)
2615            },
2616            _ => Err(Error::NotSupported),
2617        }
2618    }
2619
2620    fn decrypt(&self, key: &CryptoKey, ciphertext: &[u8]) -> Result<Vec<u8>, Error> {
2621        match self {
2622            NormalizedAlgorithm::AesCtrParams(algo) => {
2623                aes_operation::decrypt_aes_ctr(algo, key, ciphertext)
2624            },
2625            NormalizedAlgorithm::AesCbcParams(algo) => {
2626                aes_operation::decrypt_aes_cbc(algo, key, ciphertext)
2627            },
2628            NormalizedAlgorithm::AesGcmParams(algo) => {
2629                aes_operation::decrypt_aes_gcm(algo, key, ciphertext)
2630            },
2631            _ => Err(Error::NotSupported),
2632        }
2633    }
2634
2635    fn sign(&self, key: &CryptoKey, message: &[u8], can_gc: CanGc) -> Result<Vec<u8>, Error> {
2636        match self {
2637            NormalizedAlgorithm::Algorithm(algo) => match algo.name.as_str() {
2638                ALG_HMAC => hmac_operation::sign(key, message, can_gc),
2639                _ => Err(Error::NotSupported),
2640            },
2641            _ => Err(Error::NotSupported),
2642        }
2643    }
2644
2645    fn verify(
2646        &self,
2647        key: &CryptoKey,
2648        message: &[u8],
2649        signature: &[u8],
2650        can_gc: CanGc,
2651    ) -> Result<bool, Error> {
2652        match self {
2653            NormalizedAlgorithm::Algorithm(algo) => match algo.name.as_str() {
2654                ALG_HMAC => hmac_operation::verify(key, message, signature, can_gc),
2655                _ => Err(Error::NotSupported),
2656            },
2657            _ => Err(Error::NotSupported),
2658        }
2659    }
2660
2661    fn digest(&self, message: &[u8]) -> Result<Vec<u8>, Error> {
2662        match self {
2663            NormalizedAlgorithm::Algorithm(algo) => match algo.name.as_str() {
2664                ALG_SHA1 | ALG_SHA256 | ALG_SHA384 | ALG_SHA512 => {
2665                    sha_operation::digest(algo, message)
2666                },
2667                _ => Err(Error::NotSupported),
2668            },
2669            _ => Err(Error::NotSupported),
2670        }
2671    }
2672
2673    fn generate_key(
2674        &self,
2675        global: &GlobalScope,
2676        extractable: bool,
2677        usages: Vec<KeyUsage>,
2678        rng: &DomRefCell<ServoRng>,
2679        can_gc: CanGc,
2680    ) -> Result<CryptoKeyOrCryptoKeyPair, Error> {
2681        match self {
2682            NormalizedAlgorithm::AesKeyGenParams(algo) => match algo.name.as_str() {
2683                ALG_AES_CTR => aes_operation::generate_key_aes_ctr(
2684                    global,
2685                    algo,
2686                    extractable,
2687                    usages,
2688                    rng,
2689                    can_gc,
2690                )
2691                .map(CryptoKeyOrCryptoKeyPair::CryptoKey),
2692                ALG_AES_CBC => aes_operation::generate_key_aes_cbc(
2693                    global,
2694                    algo,
2695                    extractable,
2696                    usages,
2697                    rng,
2698                    can_gc,
2699                )
2700                .map(CryptoKeyOrCryptoKeyPair::CryptoKey),
2701                ALG_AES_GCM => aes_operation::generate_key_aes_gcm(
2702                    global,
2703                    algo,
2704                    extractable,
2705                    usages,
2706                    rng,
2707                    can_gc,
2708                )
2709                .map(CryptoKeyOrCryptoKeyPair::CryptoKey),
2710                ALG_AES_KW => aes_operation::generate_key_aes_kw(
2711                    global,
2712                    algo,
2713                    extractable,
2714                    usages,
2715                    rng,
2716                    can_gc,
2717                )
2718                .map(CryptoKeyOrCryptoKeyPair::CryptoKey),
2719                _ => Err(Error::NotSupported),
2720            },
2721            NormalizedAlgorithm::HmacKeyGenParams(algo) => {
2722                hmac_operation::generate_key(global, algo, extractable, usages, rng, can_gc)
2723                    .map(CryptoKeyOrCryptoKeyPair::CryptoKey)
2724            },
2725            _ => Err(Error::NotSupported),
2726        }
2727    }
2728
2729    fn derive_bits(&self, key: &CryptoKey, length: Option<u32>) -> Result<Vec<u8>, Error> {
2730        match self {
2731            NormalizedAlgorithm::HkdfParams(algo) => hkdf_operation::derive_bits(algo, key, length),
2732            NormalizedAlgorithm::Pbkdf2Params(algo) => {
2733                pbkdf2_operation::derive_bits(algo, key, length)
2734            },
2735            _ => Err(Error::NotSupported),
2736        }
2737    }
2738
2739    fn import_key(
2740        &self,
2741        global: &GlobalScope,
2742        format: KeyFormat,
2743        key_data: &[u8],
2744        extractable: bool,
2745        usages: Vec<KeyUsage>,
2746        can_gc: CanGc,
2747    ) -> Result<DomRoot<CryptoKey>, Error> {
2748        match self {
2749            NormalizedAlgorithm::Algorithm(algo) => match algo.name.as_str() {
2750                ALG_AES_CTR => aes_operation::import_key_aes_ctr(
2751                    global,
2752                    format,
2753                    key_data,
2754                    extractable,
2755                    usages,
2756                    can_gc,
2757                ),
2758                ALG_AES_CBC => aes_operation::import_key_aes_cbc(
2759                    global,
2760                    format,
2761                    key_data,
2762                    extractable,
2763                    usages,
2764                    can_gc,
2765                ),
2766                ALG_AES_GCM => aes_operation::import_key_aes_gcm(
2767                    global,
2768                    format,
2769                    key_data,
2770                    extractable,
2771                    usages,
2772                    can_gc,
2773                ),
2774                ALG_AES_KW => aes_operation::import_key_aes_kw(
2775                    global,
2776                    format,
2777                    key_data,
2778                    extractable,
2779                    usages,
2780                    can_gc,
2781                ),
2782                ALG_HKDF => {
2783                    hkdf_operation::import(global, format, key_data, extractable, usages, can_gc)
2784                },
2785                ALG_PBKDF2 => {
2786                    pbkdf2_operation::import(global, format, key_data, extractable, usages, can_gc)
2787                },
2788                _ => Err(Error::NotSupported),
2789            },
2790            NormalizedAlgorithm::HmacImportParams(algo) => hmac_operation::import_key(
2791                global,
2792                algo,
2793                format,
2794                key_data,
2795                extractable,
2796                usages,
2797                can_gc,
2798            ),
2799            _ => Err(Error::NotSupported),
2800        }
2801    }
2802
2803    fn wrap_key(&self, key: &CryptoKey, plaintext: &[u8]) -> Result<Vec<u8>, Error> {
2804        match self {
2805            NormalizedAlgorithm::Algorithm(algo) => match algo.name.as_str() {
2806                ALG_AES_KW => aes_operation::wrap_key_aes_kw(key, plaintext),
2807                _ => Err(Error::NotSupported),
2808            },
2809            _ => Err(Error::NotSupported),
2810        }
2811    }
2812
2813    fn unwrap_key(&self, key: &CryptoKey, ciphertext: &[u8]) -> Result<Vec<u8>, Error> {
2814        match self {
2815            NormalizedAlgorithm::Algorithm(algo) => match algo.name.as_str() {
2816                ALG_AES_KW => aes_operation::unwrap_key_aes_kw(key, ciphertext),
2817                _ => Err(Error::NotSupported),
2818            },
2819            _ => Err(Error::NotSupported),
2820        }
2821    }
2822
2823    fn get_key_length(&self) -> Result<Option<u32>, Error> {
2824        match self {
2825            NormalizedAlgorithm::AesDerivedKeyParams(algo) => match algo.name.as_str() {
2826                ALG_AES_CTR => aes_operation::get_key_length_aes_ctr(algo),
2827                ALG_AES_CBC => aes_operation::get_key_length_aes_cbc(algo),
2828                ALG_AES_GCM => aes_operation::get_key_length_aes_gcm(algo),
2829                ALG_AES_KW => aes_operation::get_key_length_aes_kw(algo),
2830                _ => Err(Error::NotSupported),
2831            },
2832            NormalizedAlgorithm::HmacImportParams(algo) => hmac_operation::get_key_length(algo),
2833            NormalizedAlgorithm::HkdfParams(_algo) => hkdf_operation::get_key_length(),
2834            NormalizedAlgorithm::Pbkdf2Params(_algo) => pbkdf2_operation::get_key_length(),
2835            _ => Err(Error::NotSupported),
2836        }
2837    }
2838}
2839
2840/// Return the result of performing the export key operation specified by the [[algorithm]]
2841/// internal slot of key using key and format.
2842///
2843/// According to the WebCrypto API spec, the export key operation does not rely on the algorithm
2844/// normalization, We create this helper function to minic the functions of NormalizedAlgorithm
2845/// for export key operation.
2846fn perform_export_key_operation(format: KeyFormat, key: &CryptoKey) -> Result<ExportedKey, Error> {
2847    match key.algorithm().name() {
2848        ALG_AES_CTR => aes_operation::export_key_aes_ctr(format, key),
2849        ALG_AES_CBC => aes_operation::export_key_aes_cbc(format, key),
2850        ALG_AES_GCM => aes_operation::export_key_aes_gcm(format, key),
2851        ALG_AES_KW => aes_operation::export_key_aes_kw(format, key),
2852        ALG_HMAC => hmac_operation::export(format, key),
2853        _ => Err(Error::NotSupported),
2854    }
2855}