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