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