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
5use std::num::NonZero;
6use std::ptr;
7use std::rc::Rc;
8use std::str::FromStr;
9
10use aes::cipher::block_padding::Pkcs7;
11use aes::cipher::generic_array::GenericArray;
12use aes::cipher::{BlockDecryptMut, BlockEncryptMut, KeyIvInit, StreamCipher};
13use aes::{Aes128, Aes192, Aes256};
14use aes_gcm::{AeadInPlace, AesGcm, KeyInit};
15use aes_kw::{KekAes128, KekAes192, KekAes256};
16use aws_lc_rs::{digest, hkdf, hmac, pbkdf2};
17use base64::prelude::*;
18use cipher::consts::{U12, U16, U32};
19use dom_struct::dom_struct;
20use js::conversions::ConversionResult;
21use js::jsapi::{JS_NewObject, JSObject};
22use js::jsval::{ObjectValue, UndefinedValue};
23use js::rust::wrappers::JS_ParseJSON;
24use js::rust::{HandleValue, MutableHandleObject};
25use js::typedarray::ArrayBufferU8;
26use servo_rand::{RngCore, ServoRng};
27
28use crate::dom::bindings::buffer_source::create_buffer_source;
29use crate::dom::bindings::cell::DomRefCell;
30use crate::dom::bindings::codegen::Bindings::CryptoKeyBinding::{
31    CryptoKeyMethods, KeyType, KeyUsage,
32};
33use crate::dom::bindings::codegen::Bindings::SubtleCryptoBinding::{
34    AesCbcParams, AesCtrParams, AesDerivedKeyParams, AesGcmParams, AesKeyAlgorithm,
35    AesKeyGenParams, Algorithm, AlgorithmIdentifier, HkdfParams, HmacImportParams,
36    HmacKeyAlgorithm, HmacKeyGenParams, JsonWebKey, KeyAlgorithm, KeyFormat, Pbkdf2Params,
37    RsaOtherPrimesInfo, SubtleCryptoMethods,
38};
39use crate::dom::bindings::codegen::UnionTypes::{
40    ArrayBufferViewOrArrayBuffer, ArrayBufferViewOrArrayBufferOrJsonWebKey,
41};
42use crate::dom::bindings::conversions::SafeToJSValConvertible;
43use crate::dom::bindings::error::{Error, Fallible};
44use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
45use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
46use crate::dom::bindings::root::DomRoot;
47use crate::dom::bindings::str::{DOMString, serialize_jsval_to_json_utf8};
48use crate::dom::bindings::trace::RootedTraceableBox;
49use crate::dom::cryptokey::{CryptoKey, CryptoKeyOrCryptoKeyPair, Handle};
50use crate::dom::globalscope::GlobalScope;
51use crate::dom::promise::Promise;
52use crate::realms::InRealm;
53use crate::script_runtime::{CanGc, JSContext};
54
55// String constants for algorithms/curves
56const ALG_AES_CBC: &str = "AES-CBC";
57const ALG_AES_CTR: &str = "AES-CTR";
58const ALG_AES_GCM: &str = "AES-GCM";
59const ALG_AES_KW: &str = "AES-KW";
60const ALG_SHA1: &str = "SHA-1";
61const ALG_SHA256: &str = "SHA-256";
62const ALG_SHA384: &str = "SHA-384";
63const ALG_SHA512: &str = "SHA-512";
64const ALG_HMAC: &str = "HMAC";
65const ALG_HKDF: &str = "HKDF";
66const ALG_PBKDF2: &str = "PBKDF2";
67const ALG_RSASSA_PKCS1: &str = "RSASSA-PKCS1-v1_5";
68const ALG_RSA_OAEP: &str = "RSA-OAEP";
69const ALG_RSA_PSS: &str = "RSA-PSS";
70const ALG_ECDH: &str = "ECDH";
71const ALG_ECDSA: &str = "ECDSA";
72
73#[allow(dead_code)]
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];
92
93const NAMED_CURVE_P256: &str = "P-256";
94const NAMED_CURVE_P384: &str = "P-384";
95const NAMED_CURVE_P521: &str = "P-521";
96#[allow(dead_code)]
97static SUPPORTED_CURVES: &[&str] = &[NAMED_CURVE_P256, NAMED_CURVE_P384, NAMED_CURVE_P521];
98
99type Aes128CbcEnc = cbc::Encryptor<Aes128>;
100type Aes128CbcDec = cbc::Decryptor<Aes128>;
101type Aes192CbcEnc = cbc::Encryptor<Aes192>;
102type Aes192CbcDec = cbc::Decryptor<Aes192>;
103type Aes256CbcEnc = cbc::Encryptor<Aes256>;
104type Aes256CbcDec = cbc::Decryptor<Aes256>;
105type Aes128Ctr = ctr::Ctr64BE<Aes128>;
106type Aes192Ctr = ctr::Ctr64BE<Aes192>;
107type Aes256Ctr = ctr::Ctr64BE<Aes256>;
108
109type Aes128Gcm96Iv = AesGcm<Aes128, U12>;
110type Aes128Gcm128Iv = AesGcm<Aes128, U16>;
111type Aes192Gcm96Iv = AesGcm<Aes192, U12>;
112type Aes256Gcm96Iv = AesGcm<Aes256, U12>;
113type Aes128Gcm256Iv = AesGcm<Aes128, U32>;
114type Aes192Gcm256Iv = AesGcm<Aes192, U32>;
115type Aes256Gcm256Iv = AesGcm<Aes256, U32>;
116
117#[dom_struct]
118pub(crate) struct SubtleCrypto {
119    reflector_: Reflector,
120    #[no_trace]
121    rng: DomRefCell<ServoRng>,
122}
123
124impl SubtleCrypto {
125    fn new_inherited() -> SubtleCrypto {
126        SubtleCrypto {
127            reflector_: Reflector::new(),
128            rng: DomRefCell::new(ServoRng::default()),
129        }
130    }
131
132    pub(crate) fn new(global: &GlobalScope, can_gc: CanGc) -> DomRoot<SubtleCrypto> {
133        reflect_dom_object(Box::new(SubtleCrypto::new_inherited()), global, can_gc)
134    }
135}
136
137impl SubtleCryptoMethods<crate::DomTypeHolder> for SubtleCrypto {
138    /// <https://w3c.github.io/webcrypto/#SubtleCrypto-method-encrypt>
139    fn Encrypt(
140        &self,
141        cx: JSContext,
142        algorithm: AlgorithmIdentifier,
143        key: &CryptoKey,
144        data: ArrayBufferViewOrArrayBuffer,
145        comp: InRealm,
146        can_gc: CanGc,
147    ) -> Rc<Promise> {
148        let promise = Promise::new_in_current_realm(comp, can_gc);
149        let normalized_algorithm =
150            match normalize_algorithm_for_encrypt_or_decrypt(cx, &algorithm, can_gc) {
151                Ok(algorithm) => algorithm,
152                Err(e) => {
153                    promise.reject_error(e, can_gc);
154                    return promise;
155                },
156            };
157        let data = match data {
158            ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
159            ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
160        };
161
162        let this = Trusted::new(self);
163        let trusted_promise = TrustedPromise::new(promise.clone());
164        let trusted_key = Trusted::new(key);
165        let key_alg = key.algorithm();
166        let valid_usage = key.usages().contains(&KeyUsage::Encrypt);
167        self.global()
168            .task_manager()
169            .dom_manipulation_task_source()
170            .queue(task!(encrypt: move || {
171                let subtle = this.root();
172                let promise = trusted_promise.root();
173                let key = trusted_key.root();
174
175                if !valid_usage || normalized_algorithm.name() != key_alg {
176                    promise.reject_error(Error::InvalidAccess, CanGc::note());
177                    return;
178                }
179
180                let cx = GlobalScope::get_cx();
181                rooted!(in(*cx) let mut array_buffer_ptr = ptr::null_mut::<JSObject>());
182
183                if let Err(e) = normalized_algorithm.encrypt(
184                    &subtle,
185                    &key,
186                    &data,
187                    cx,
188                    array_buffer_ptr.handle_mut(),
189                    CanGc::note(),
190                ) {
191                    promise.reject_error(e, CanGc::note());
192                    return;
193                }
194                promise.resolve_native(&*array_buffer_ptr.handle(), CanGc::note());
195            }));
196        promise
197    }
198
199    /// <https://w3c.github.io/webcrypto/#SubtleCrypto-method-decrypt>
200    fn Decrypt(
201        &self,
202        cx: JSContext,
203        algorithm: AlgorithmIdentifier,
204        key: &CryptoKey,
205        data: ArrayBufferViewOrArrayBuffer,
206        comp: InRealm,
207        can_gc: CanGc,
208    ) -> Rc<Promise> {
209        let promise = Promise::new_in_current_realm(comp, can_gc);
210        let normalized_algorithm =
211            match normalize_algorithm_for_encrypt_or_decrypt(cx, &algorithm, can_gc) {
212                Ok(algorithm) => algorithm,
213                Err(e) => {
214                    promise.reject_error(e, can_gc);
215                    return promise;
216                },
217            };
218        let data = match data {
219            ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
220            ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
221        };
222
223        let this = Trusted::new(self);
224        let trusted_promise = TrustedPromise::new(promise.clone());
225        let trusted_key = Trusted::new(key);
226        let key_alg = key.algorithm();
227        let valid_usage = key.usages().contains(&KeyUsage::Decrypt);
228        self.global()
229            .task_manager()
230            .dom_manipulation_task_source()
231            .queue(task!(decrypt: move || {
232                let subtle = this.root();
233                let promise = trusted_promise.root();
234                let key = trusted_key.root();
235                let cx = GlobalScope::get_cx();
236                rooted!(in(*cx) let mut array_buffer_ptr = ptr::null_mut::<JSObject>());
237
238                if !valid_usage || normalized_algorithm.name() != key_alg {
239                    promise.reject_error(Error::InvalidAccess, CanGc::note());
240                    return;
241                }
242
243                if let Err(e) = normalized_algorithm.decrypt(
244                    &subtle,
245                    &key,
246                    &data,
247                    cx,
248                    array_buffer_ptr.handle_mut(),
249                    CanGc::note(),
250                ) {
251                    promise.reject_error(e, CanGc::note());
252                    return;
253                }
254
255                promise.resolve_native(&*array_buffer_ptr.handle(), CanGc::note());
256            }));
257        promise
258    }
259
260    /// <https://w3c.github.io/webcrypto/#SubtleCrypto-method-sign>
261    fn Sign(
262        &self,
263        cx: JSContext,
264        algorithm: AlgorithmIdentifier,
265        key: &CryptoKey,
266        data: ArrayBufferViewOrArrayBuffer,
267        comp: InRealm,
268        can_gc: CanGc,
269    ) -> Rc<Promise> {
270        // Step 1. Let algorithm and key be the algorithm and key parameters passed to the sign() method, respectively.
271
272        // Step 2. Let data be the result of getting a copy of the bytes held by the data parameter passed to
273        // the sign() method.
274        let data = match &data {
275            ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
276            ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
277        };
278
279        // Step 3. Let normalizedAlgorithm be the result of normalizing an algorithm, with alg set to algorithm and
280        // op set to "sign".
281        let promise = Promise::new_in_current_realm(comp, can_gc);
282        let normalized_algorithm =
283            match normalize_algorithm_for_sign_or_verify(cx, &algorithm, can_gc) {
284                Ok(algorithm) => algorithm,
285                Err(e) => {
286                    // Step 4. If an error occurred, return a Promise rejected with normalizedAlgorithm.
287                    promise.reject_error(e, can_gc);
288                    return promise;
289                },
290            };
291
292        // Step 5. Let promise be a new Promise.
293        // NOTE: We did that in preparation of Step 4.
294
295        // Step 6. Return promise and perform the remaining steps in parallel.
296        let trusted_promise = TrustedPromise::new(promise.clone());
297        let trusted_key = Trusted::new(key);
298
299        self.global()
300            .task_manager()
301            .dom_manipulation_task_source()
302            .queue(task!(sign: move || {
303                // Step 7. If the following steps or referenced procedures say to throw an error, reject promise
304                // with the returned error and then terminate the algorithm.
305                let promise = trusted_promise.root();
306                let key = trusted_key.root();
307
308                // Step 8. If the name member of normalizedAlgorithm is not equal to the name attribute of the
309                // [[algorithm]] internal slot of key then throw an InvalidAccessError.
310                if normalized_algorithm.name() != key.algorithm() {
311                    promise.reject_error(Error::InvalidAccess, CanGc::note());
312                    return;
313                }
314
315                // Step 9. If the [[usages]] internal slot of key does not contain an entry that is "sign",
316                // then throw an InvalidAccessError.
317                if !key.usages().contains(&KeyUsage::Sign) {
318                    promise.reject_error(Error::InvalidAccess, CanGc::note());
319                    return;
320                }
321
322                // Step 10.  Let result be the result of performing the sign operation specified by normalizedAlgorithm
323                // using key and algorithm and with data as message.
324                let cx = GlobalScope::get_cx();
325                let result = match normalized_algorithm.sign(cx, &key, &data, CanGc::note()) {
326                    Ok(signature) => signature,
327                    Err(e) => {
328                        promise.reject_error(e, CanGc::note());
329                        return;
330                    }
331                };
332
333                rooted!(in(*cx) let mut array_buffer_ptr = ptr::null_mut::<JSObject>());
334                create_buffer_source::<ArrayBufferU8>(cx, &result, array_buffer_ptr.handle_mut(), CanGc::note())
335                    .expect("failed to create buffer source for exported key.");
336
337                // Step 9. Resolve promise with result.
338                promise.resolve_native(&*array_buffer_ptr, CanGc::note());
339            }));
340
341        promise
342    }
343
344    /// <https://w3c.github.io/webcrypto/#SubtleCrypto-method-verify>
345    fn Verify(
346        &self,
347        cx: JSContext,
348        algorithm: AlgorithmIdentifier,
349        key: &CryptoKey,
350        signature: ArrayBufferViewOrArrayBuffer,
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 verify() method,
356        // respectively.
357
358        // Step 2. Let signature be the result of getting a copy of the bytes held by the signature parameter passed
359        // to the verify() method.
360        let signature = match &signature {
361            ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
362            ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
363        };
364
365        // Step 3. Let data be the result of getting a copy of the bytes held by the data parameter passed to the
366        // verify() method.
367        let data = match &data {
368            ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
369            ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
370        };
371
372        // Step 4. Let normalizedAlgorithm be the result of normalizing an algorithm, with alg set to
373        // algorithm and op set to "verify".
374        let promise = Promise::new_in_current_realm(comp, can_gc);
375        let normalized_algorithm =
376            match normalize_algorithm_for_sign_or_verify(cx, &algorithm, can_gc) {
377                Ok(algorithm) => algorithm,
378                Err(e) => {
379                    // Step 5. If an error occurred, return a Promise rejected with normalizedAlgorithm.
380                    promise.reject_error(e, can_gc);
381                    return promise;
382                },
383            };
384
385        // Step 6. Let promise be a new Promise.
386        // NOTE: We did that in preparation of Step 6.
387
388        // Step 7. Return promise and perform the remaining steps in parallel.
389        let trusted_promise = TrustedPromise::new(promise.clone());
390        let trusted_key = Trusted::new(key);
391
392        self.global()
393            .task_manager()
394            .dom_manipulation_task_source()
395            .queue(task!(sign: move || {
396                // Step 8. If the following steps or referenced procedures say to throw an error, reject promise
397                // with the returned error and then terminate the algorithm.
398                let promise = trusted_promise.root();
399                let key = trusted_key.root();
400
401                // Step 9. If the name member of normalizedAlgorithm is not equal to the name attribute of the
402                // [[algorithm]] internal slot of key then throw an InvalidAccessError.
403                if normalized_algorithm.name() != key.algorithm() {
404                    promise.reject_error(Error::InvalidAccess, CanGc::note());
405                    return;
406                }
407
408                // Step 10. If the [[usages]] internal slot of key does not contain an entry that is "verify",
409                // then throw an InvalidAccessError.
410                if !key.usages().contains(&KeyUsage::Verify) {
411                    promise.reject_error(Error::InvalidAccess, CanGc::note());
412                    return;
413                }
414
415                // Step 1. Let result be the result of performing the verify operation specified by normalizedAlgorithm
416                // using key, algorithm and signature and with data as message.
417                let cx = GlobalScope::get_cx();
418                let result = match normalized_algorithm.verify(cx, &key, &data, &signature, CanGc::note()) {
419                    Ok(result) => result,
420                    Err(e) => {
421                        promise.reject_error(e, CanGc::note());
422                        return;
423                    }
424                };
425
426                // Step 9. Resolve promise with result.
427                promise.resolve_native(&result, CanGc::note());
428            }));
429
430        promise
431    }
432
433    /// <https://w3c.github.io/webcrypto/#SubtleCrypto-method-digest>
434    fn Digest(
435        &self,
436        cx: JSContext,
437        algorithm: AlgorithmIdentifier,
438        data: ArrayBufferViewOrArrayBuffer,
439        comp: InRealm,
440        can_gc: CanGc,
441    ) -> Rc<Promise> {
442        // Step 1. Let algorithm be the algorithm parameter passed to the digest() method.
443
444        // Step 2. Let data be the result of getting a copy of the bytes held by the
445        // data parameter passed to the digest() method.
446        let data = match data {
447            ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
448            ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
449        };
450
451        // Step 3. Let normalizedAlgorithm be the result of normalizing an algorithm,
452        // with alg set to algorithm and op set to "digest".
453        let promise = Promise::new_in_current_realm(comp, can_gc);
454        let normalized_algorithm = match normalize_algorithm_for_digest(cx, &algorithm, can_gc) {
455            Ok(normalized_algorithm) => normalized_algorithm,
456            Err(e) => {
457                // Step 4. If an error occurred, return a Promise rejected with normalizedAlgorithm.
458                promise.reject_error(e, can_gc);
459                return promise;
460            },
461        };
462
463        // Step 5. Let promise be a new Promise.
464        // NOTE: We did that in preparation of Step 4.
465
466        // Step 6. Return promise and perform the remaining steps in parallel.
467        let trusted_promise = TrustedPromise::new(promise.clone());
468
469        self.global().task_manager().dom_manipulation_task_source().queue(
470            task!(generate_key: move || {
471                // Step 7. If the following steps or referenced procedures say to throw an error, reject promise
472                // with the returned error and then terminate the algorithm.
473                let promise = trusted_promise.root();
474
475                // Step 8. Let result be the result of performing the digest operation specified by
476                // normalizedAlgorithm using algorithm, with data as message.
477                let digest = match normalized_algorithm.digest(&data) {
478                    Ok(digest) => digest,
479                    Err(e) => {
480                        promise.reject_error(e, CanGc::note());
481                        return;
482                    }
483                };
484
485                let cx = GlobalScope::get_cx();
486                rooted!(in(*cx) let mut array_buffer_ptr = ptr::null_mut::<JSObject>());
487                create_buffer_source::<ArrayBufferU8>(cx, digest.as_ref(), array_buffer_ptr.handle_mut(), CanGc::note())
488                    .expect("failed to create buffer source for exported key.");
489
490
491                // Step 9. Resolve promise with result.
492                promise.resolve_native(&*array_buffer_ptr, CanGc::note());
493            })
494        );
495
496        promise
497    }
498
499    /// <https://w3c.github.io/webcrypto/#SubtleCrypto-method-generateKey>
500    fn GenerateKey(
501        &self,
502        cx: JSContext,
503        algorithm: AlgorithmIdentifier,
504        extractable: bool,
505        key_usages: Vec<KeyUsage>,
506        comp: InRealm,
507        can_gc: CanGc,
508    ) -> Rc<Promise> {
509        let promise = Promise::new_in_current_realm(comp, can_gc);
510        let normalized_algorithm =
511            match normalize_algorithm_for_generate_key(cx, &algorithm, can_gc) {
512                Ok(algorithm) => algorithm,
513                Err(e) => {
514                    promise.reject_error(e, can_gc);
515                    return promise;
516                },
517            };
518
519        let this = Trusted::new(self);
520        let trusted_promise = TrustedPromise::new(promise.clone());
521        self.global()
522            .task_manager()
523            .dom_manipulation_task_source()
524            .queue(task!(generate_key: move || {
525                let subtle = this.root();
526                let promise = trusted_promise.root();
527
528                // Step 8. Let result be the result of performing the generate key operation
529                // specified by normalizedAlgorithm using algorithm, extractable and usages.
530                let key = match normalized_algorithm
531                    .generate_key(&subtle, key_usages, extractable, CanGc::note())
532                {
533                    Ok(key) => key,
534                    Err(error) => {
535                        promise.reject_error(error, CanGc::note());
536                        return;
537                    }
538                };
539
540                // Step 9.
541                // If result is a CryptoKey object:
542                //     If the [[type]] internal slot of result is "secret" or "private" and usages
543                //     is empty, then throw a SyntaxError.
544                // If result is a CryptoKeyPair object:
545                //     If the [[usages]] internal slot of the privateKey attribute of result is the
546                //     empty sequence, then throw a SyntaxError.
547                // TODO: Implement CryptoKeyPair case
548                match &key {
549                    CryptoKeyOrCryptoKeyPair::CryptoKey(crpyto_key) => {
550                        if matches!(crpyto_key.Type(), KeyType::Secret | KeyType::Private)
551                            && crpyto_key.usages().is_empty()
552                        {
553                            promise.reject_error(Error::Syntax(None), CanGc::note());
554                            return;
555                        }
556                    },
557                };
558
559                // TODO: Step 10. Queue a global task on the crypto task source, given realm's
560                // global object, to perform the remaining steps.
561
562                // Step 11. Let result be the result of converting result to an ECMAScript Object
563                // in realm, as defined by [WebIDL].
564                // Step 12. Resolve promise with result.
565                // TODO: Implement CryptoKeyPair case
566                match key {
567                    CryptoKeyOrCryptoKeyPair::CryptoKey(crypto_key) =>
568                        promise.resolve_native(&crypto_key, CanGc::note()),
569                };
570            }));
571
572        promise
573    }
574
575    /// <https://w3c.github.io/webcrypto/#SubtleCrypto-method-deriveKey>
576    fn DeriveKey(
577        &self,
578        cx: JSContext,
579        algorithm: AlgorithmIdentifier,
580        base_key: &CryptoKey,
581        derived_key_type: AlgorithmIdentifier,
582        extractable: bool,
583        key_usages: Vec<KeyUsage>,
584        comp: InRealm,
585        can_gc: CanGc,
586    ) -> Rc<Promise> {
587        // Step 1. Let algorithm, baseKey, derivedKeyType, extractable and usages be the algorithm, baseKey,
588        // derivedKeyType, extractable and keyUsages parameters passed to the deriveKey() method, respectively.
589
590        // Step 2. Let normalizedAlgorithm be the result of normalizing an algorithm, with alg set to algorithm
591        // and op set to "deriveBits".
592        let promise = Promise::new_in_current_realm(comp, can_gc);
593        let normalized_algorithm = match normalize_algorithm_for_derive_bits(cx, &algorithm, can_gc)
594        {
595            Ok(algorithm) => algorithm,
596            Err(e) => {
597                // Step 3. If an error occurred, return a Promise rejected with normalizedAlgorithm.
598                promise.reject_error(e, can_gc);
599                return promise;
600            },
601        };
602
603        // Step 4. Let normalizedDerivedKeyAlgorithmImport be the result of normalizing an algorithm,
604        // with alg set to derivedKeyType and op set to "importKey".
605        let normalized_derived_key_algorithm_import =
606            match normalize_algorithm_for_import_key(cx, &derived_key_type, can_gc) {
607                Ok(algorithm) => algorithm,
608                Err(e) => {
609                    // Step 5. If an error occurred, return a Promise rejected with normalizedDerivedKeyAlgorithmImport.
610                    promise.reject_error(e, can_gc);
611                    return promise;
612                },
613            };
614
615        // Step 6. Let normalizedDerivedKeyAlgorithmLength be the result of normalizing an algorithm, with alg set
616        // to derivedKeyType and op set to "get key length".
617        let normalized_derived_key_algorithm_length =
618            match normalize_algorithm_for_get_key_length(cx, &derived_key_type, can_gc) {
619                Ok(algorithm) => algorithm,
620                Err(e) => {
621                    // Step 7. If an error occurred, return a Promise rejected with normalizedDerivedKeyAlgorithmLength.
622                    promise.reject_error(e, can_gc);
623                    return promise;
624                },
625            };
626
627        // Step 8. Let promise be a new Promise.
628        // NOTE: We created the promise earlier, after Step 1.
629
630        // Step 9. Return promise and perform the remaining steps in parallel.
631        let trusted_promise = TrustedPromise::new(promise.clone());
632        let trusted_base_key = Trusted::new(base_key);
633        let this = Trusted::new(self);
634        self.global().task_manager().dom_manipulation_task_source().queue(
635            task!(derive_key: move || {
636                // Step 10. If the following steps or referenced procedures say to throw an error, reject promise
637                // with the returned error and then terminate the algorithm.
638
639                // TODO Step 11. If the name member of normalizedAlgorithm is not equal to the name attribute of the #
640                // [[algorithm]] internal slot of baseKey then throw an InvalidAccessError.
641                let promise = trusted_promise.root();
642                let base_key = trusted_base_key.root();
643                let subtle = this.root();
644
645                // Step 12. If the [[usages]] internal slot of baseKey does not contain an entry that is
646                // "deriveKey", then throw an InvalidAccessError.
647                if !base_key.usages().contains(&KeyUsage::DeriveKey) {
648                    promise.reject_error(Error::InvalidAccess, CanGc::note());
649                    return;
650                }
651
652                // Step 13. Let length be the result of performing the get key length algorithm specified by
653                // normalizedDerivedKeyAlgorithmLength using derivedKeyType.
654                let length = match normalized_derived_key_algorithm_length.get_key_length() {
655                    Ok(length) => length,
656                    Err(e) => {
657                        promise.reject_error(e, CanGc::note());
658                        return;
659                    }
660                };
661
662                // Step 14. Let secret be the result of performing the derive bits operation specified by
663                // normalizedAlgorithm using key, algorithm and length.
664                let secret = match normalized_algorithm.derive_bits(&base_key, Some(length)){
665                    Ok(secret) => secret,
666                    Err(e) => {
667                        promise.reject_error(e, CanGc::note());
668                        return;
669                    }
670                };
671
672                // Step 15.  Let result be the result of performing the import key operation specified by
673                // normalizedDerivedKeyAlgorithmImport using "raw" as format, secret as keyData, derivedKeyType as
674                // algorithm and using extractable and usages.
675                let result = normalized_derived_key_algorithm_import.import_key(
676                    &subtle,
677                    KeyFormat::Raw,
678                    &secret,
679                    extractable,
680                    key_usages,
681                    CanGc::note()
682                );
683                let result = match result  {
684                    Ok(key) => key,
685                    Err(e) => {
686                        promise.reject_error(e, CanGc::note());
687                        return;
688                    }
689                };
690
691                // Step 17. If the [[type]] internal slot of result is "secret" or "private" and usages
692                // is empty, then throw a SyntaxError.
693                if matches!(result.Type(), KeyType::Secret | KeyType::Private) && result.usages().is_empty() {
694                    promise.reject_error(Error::Syntax(None), CanGc::note());
695                    return;
696                }
697
698                // Step 17. Resolve promise with result.
699                promise.resolve_native(&*result, CanGc::note());
700            }),
701        );
702
703        promise
704    }
705
706    /// <https://w3c.github.io/webcrypto/#dfn-SubtleCrypto-method-deriveBits>
707    fn DeriveBits(
708        &self,
709        cx: JSContext,
710        algorithm: AlgorithmIdentifier,
711        base_key: &CryptoKey,
712        length: Option<u32>,
713        comp: InRealm,
714        can_gc: CanGc,
715    ) -> Rc<Promise> {
716        // Step 1.  Let algorithm, baseKey and length, be the algorithm, baseKey and
717        // length parameters passed to the deriveBits() method, respectively.
718
719        // Step 2. Let normalizedAlgorithm be the result of normalizing an algorithm,
720        // with alg set to algorithm and op set to "deriveBits".
721        let promise = Promise::new_in_current_realm(comp, can_gc);
722        let normalized_algorithm = match normalize_algorithm_for_derive_bits(cx, &algorithm, can_gc)
723        {
724            Ok(algorithm) => algorithm,
725            Err(e) => {
726                // Step 3. If an error occurred, return a Promise rejected with normalizedAlgorithm.
727                promise.reject_error(e, can_gc);
728                return promise;
729            },
730        };
731
732        // Step 4. Let promise be a new Promise object.
733        // NOTE: We did that in preparation of Step 3.
734
735        // Step 5. Return promise and perform the remaining steps in parallel.
736        let trusted_promise = TrustedPromise::new(promise.clone());
737        let trusted_base_key = Trusted::new(base_key);
738
739        self.global()
740            .task_manager()
741            .dom_manipulation_task_source()
742            .queue(task!(import_key: move || {
743                // Step 6. If the following steps or referenced procedures say to throw an error,
744                // reject promise with the returned error and then terminate the algorithm.
745
746                // TODO Step 7. If the name member of normalizedAlgorithm is not equal to the name attribute
747                // of the [[algorithm]] internal slot of baseKey then throw an InvalidAccessError.
748                let promise = trusted_promise.root();
749                let base_key = trusted_base_key.root();
750
751                // Step 8. If the [[usages]] internal slot of baseKey does not contain an entry that
752                // is "deriveBits", then throw an InvalidAccessError.
753                if !base_key.usages().contains(&KeyUsage::DeriveBits) {
754                    promise.reject_error(Error::InvalidAccess, CanGc::note());
755                    return;
756                }
757
758                // Step 9. Let result be the result of creating an ArrayBuffer containing the result of performing the
759                // derive bits operation specified by normalizedAlgorithm using baseKey, algorithm and length.
760                let cx = GlobalScope::get_cx();
761                rooted!(in(*cx) let mut array_buffer_ptr = ptr::null_mut::<JSObject>());
762                let result = match normalized_algorithm.derive_bits(&base_key, length) {
763                    Ok(derived_bits) => derived_bits,
764                    Err(e) => {
765                        promise.reject_error(e, CanGc::note());
766                        return;
767                    }
768                };
769
770                create_buffer_source::<ArrayBufferU8>(cx, &result, array_buffer_ptr.handle_mut(), CanGc::note())
771                    .expect("failed to create buffer source for derived bits.");
772
773                // Step 10. Resolve promise with result.
774                promise.resolve_native(&*array_buffer_ptr, CanGc::note());
775            }));
776
777        promise
778    }
779
780    /// <https://w3c.github.io/webcrypto/#SubtleCrypto-method-importKey>
781    fn ImportKey(
782        &self,
783        cx: JSContext,
784        format: KeyFormat,
785        key_data: ArrayBufferViewOrArrayBufferOrJsonWebKey,
786        algorithm: AlgorithmIdentifier,
787        extractable: bool,
788        key_usages: Vec<KeyUsage>,
789        comp: InRealm,
790        can_gc: CanGc,
791    ) -> Rc<Promise> {
792        // Step 1. Let format, algorithm, extractable and usages, be the format, algorithm,
793        // extractable and keyUsages parameters passed to the importKey() method, respectively.
794
795        // Step 5. Let realm be the relevant realm of this.
796        // Step 6. Let promise be a new Promise.
797        let promise = Promise::new_in_current_realm(comp, can_gc);
798
799        // Step 2.
800        let key_data = match format {
801            // If format is equal to the string "raw", "pkcs8", or "spki":
802            KeyFormat::Raw | KeyFormat::Pkcs8 | KeyFormat::Spki => {
803                match key_data {
804                    // Step 2.1. If the keyData parameter passed to the importKey() method is a
805                    // JsonWebKey dictionary, throw a TypeError.
806                    ArrayBufferViewOrArrayBufferOrJsonWebKey::JsonWebKey(_) => {
807                        promise.reject_error(
808                            Error::Type("The keyData type does not match the format".to_string()),
809                            can_gc,
810                        );
811                        return promise;
812                    },
813
814                    // Step 2.2. Let keyData be the result of getting a copy of the bytes held by
815                    // the keyData parameter passed to the importKey() method.
816                    ArrayBufferViewOrArrayBufferOrJsonWebKey::ArrayBufferView(view) => {
817                        view.to_vec()
818                    },
819                    ArrayBufferViewOrArrayBufferOrJsonWebKey::ArrayBuffer(buffer) => {
820                        buffer.to_vec()
821                    },
822                }
823            },
824            // If format is equal to the string "jwk":
825            KeyFormat::Jwk => {
826                match key_data {
827                    ArrayBufferViewOrArrayBufferOrJsonWebKey::ArrayBufferView(_) |
828                    ArrayBufferViewOrArrayBufferOrJsonWebKey::ArrayBuffer(_) => {
829                        // Step 2.1. If the keyData parameter passed to the importKey() method is
830                        // not a JsonWebKey dictionary, throw a TypeError.
831                        promise.reject_error(
832                            Error::Type("The keyData type does not match the format".to_string()),
833                            can_gc,
834                        );
835                        return promise;
836                    },
837
838                    ArrayBufferViewOrArrayBufferOrJsonWebKey::JsonWebKey(jwk) => {
839                        // Step 2.2. Let keyData be the keyData parameter passed to the importKey() method.
840                        //
841                        // NOTE: Serialize JsonWebKey throught stringifying it.
842                        // JsonWebKey::stringify internally relies on ToJSON, so it will raise an
843                        // exception when a JS error is thrown. When this happens, we report the
844                        // error.
845                        match jwk.stringify(cx) {
846                            Ok(stringified) => stringified.as_bytes().to_vec(),
847                            Err(error) => {
848                                promise.reject_error(error, can_gc);
849                                return promise;
850                            },
851                        }
852                    },
853                }
854            },
855        };
856
857        // Step 3. Let normalizedAlgorithm be the result of normalizing an algorithm, with alg set
858        // to algorithm and op set to "importKey".
859        let normalized_algorithm = match normalize_algorithm_for_import_key(cx, &algorithm, can_gc)
860        {
861            Ok(algorithm) => algorithm,
862            Err(error) => {
863                // Step 4. If an error occurred, return a Promise rejected with normalizedAlgorithm.
864                promise.reject_error(error, can_gc);
865                return promise;
866            },
867        };
868
869        // Step 7. Return promise and perform the remaining steps in parallel.
870        let this = Trusted::new(self);
871        let trusted_promise = TrustedPromise::new(promise.clone());
872        self.global()
873            .task_manager()
874            .dom_manipulation_task_source()
875            .queue(task!(import_key: move || {
876                let subtle = this.root();
877                let promise = trusted_promise.root();
878
879                // TODO: Step 8. If the following steps or referenced procedures say to throw an
880                // error, queue a global task on the crypto task source, given realm's global
881                // object, to reject promise with the returned error; and then terminate the
882                // algorithm.
883
884                // Step 9. Let result be the CryptoKey object that results from performing the
885                // import key operation specified by normalizedAlgorithm using keyData, algorithm,
886                // format, extractable and usages.
887                let result = match normalized_algorithm.import_key(
888                    &subtle,
889                    format,
890                    &key_data,
891                    extractable,
892                    key_usages.clone(),
893                    CanGc::note()
894                ) {
895                    Ok(key) => key,
896                    Err(error) => {
897                        promise.reject_error(error, CanGc::note());
898                        return;
899                    },
900                };
901
902                // Step 10. If the [[type]] internal slot of result is "secret" or "private" and
903                // usages is empty, then throw a SyntaxError.
904                if matches!(result.Type(), KeyType::Secret | KeyType::Private) && key_usages.is_empty() {
905                    promise.reject_error(Error::Syntax(None), CanGc::note());
906                    return;
907                }
908
909                // Step 11. Set the [[extractable]] internal slot of result to extractable.
910                result.set_extractable(extractable);
911
912                // Step 12. Set the [[usages]] internal slot of result to the normalized value of usages.
913                result.set_usages(&key_usages);
914
915                // TODO: Step 13. Queue a global task on the crypto task source, given realm's
916                // global object, to perform the remaining steps.
917
918                // Step 14. Let result be the result of converting result to an ECMAScript Object
919                // in realm, as defined by [WebIDL].
920                // Step 15. Resolve promise with result.
921                promise.resolve_native(&result, CanGc::note());
922            }));
923
924        promise
925    }
926
927    /// <https://w3c.github.io/webcrypto/#SubtleCrypto-method-exportKey>
928    fn ExportKey(
929        &self,
930        format: KeyFormat,
931        key: &CryptoKey,
932        comp: InRealm,
933        can_gc: CanGc,
934    ) -> Rc<Promise> {
935        let promise = Promise::new_in_current_realm(comp, can_gc);
936
937        let this = Trusted::new(self);
938        let trusted_key = Trusted::new(key);
939        let trusted_promise = TrustedPromise::new(promise.clone());
940        self.global().task_manager().dom_manipulation_task_source().queue(
941            task!(export_key: move || {
942                let subtle = this.root();
943                let promise = trusted_promise.root();
944                let key = trusted_key.root();
945                let alg_name = key.algorithm();
946                if matches!(
947                    alg_name.as_str(), ALG_SHA1 | ALG_SHA256 | ALG_SHA384 | ALG_SHA512 | ALG_HKDF | ALG_PBKDF2
948                ) {
949                    promise.reject_error(Error::NotSupported, CanGc::note());
950                    return;
951                }
952                if !key.Extractable() {
953                    promise.reject_error(Error::InvalidAccess, CanGc::note());
954                    return;
955                }
956                let exported_key = match alg_name.as_str() {
957                    ALG_AES_CBC | ALG_AES_CTR | ALG_AES_KW | ALG_AES_GCM => subtle.export_key_aes(format, &key),
958                    ALG_HMAC => subtle.export_key_hmac(format, &key),
959                    _ => Err(Error::NotSupported),
960                };
961                match exported_key {
962                    Ok(k) => {
963                        match k {
964                            ExportedKey::Raw(k) => {
965                                let cx = GlobalScope::get_cx();
966                                rooted!(in(*cx) let mut array_buffer_ptr = ptr::null_mut::<JSObject>());
967                                create_buffer_source::<ArrayBufferU8>(cx, &k, array_buffer_ptr.handle_mut(),
968                                    CanGc::note())
969                                    .expect("failed to create buffer source for exported key.");
970                                promise.resolve_native(&array_buffer_ptr.get(), CanGc::note())
971                            },
972                            ExportedKey::Jwk(k) => {
973                                promise.resolve_native(&k, CanGc::note())
974                            },
975                        }
976                    },
977                    Err(e) => promise.reject_error(e, CanGc::note()),
978                }
979            }),
980        );
981
982        promise
983    }
984
985    /// <https://w3c.github.io/webcrypto/#SubtleCrypto-method-wrapKey>
986    fn WrapKey(
987        &self,
988        cx: JSContext,
989        format: KeyFormat,
990        key: &CryptoKey,
991        wrapping_key: &CryptoKey,
992        wrap_algorithm: AlgorithmIdentifier,
993        comp: InRealm,
994        can_gc: CanGc,
995    ) -> Rc<Promise> {
996        let promise = Promise::new_in_current_realm(comp, can_gc);
997        let normalized_algorithm =
998            match normalize_algorithm_for_key_wrap(cx, &wrap_algorithm, can_gc) {
999                Ok(algorithm) => algorithm,
1000                Err(e) => {
1001                    promise.reject_error(e, can_gc);
1002                    return promise;
1003                },
1004            };
1005
1006        let this = Trusted::new(self);
1007        let trusted_key = Trusted::new(key);
1008        let trusted_wrapping_key = Trusted::new(wrapping_key);
1009        let trusted_promise = TrustedPromise::new(promise.clone());
1010        self.global().task_manager().dom_manipulation_task_source().queue(
1011            task!(wrap_key: move || {
1012                let subtle = this.root();
1013                let promise = trusted_promise.root();
1014                let key = trusted_key.root();
1015                let wrapping_key = trusted_wrapping_key.root();
1016                let alg_name = key.algorithm();
1017                let wrapping_alg_name = wrapping_key.algorithm();
1018                let valid_wrap_usage = wrapping_key.usages().contains(&KeyUsage::WrapKey);
1019                let names_match = normalized_algorithm.name() == wrapping_alg_name.as_str();
1020
1021                if !valid_wrap_usage || !names_match || !key.Extractable() {
1022                    promise.reject_error(Error::InvalidAccess, CanGc::note());
1023                    return;
1024                }
1025
1026                if matches!(
1027                    alg_name.as_str(), ALG_SHA1 | ALG_SHA256 | ALG_SHA384 | ALG_SHA512 | ALG_HKDF | ALG_PBKDF2
1028                ) {
1029                    promise.reject_error(Error::NotSupported, CanGc::note());
1030                    return;
1031                }
1032
1033                let exported_key = match subtle.export_key_aes(format, &key) {
1034                    Ok(k) => k,
1035                    Err(e) => {
1036                        promise.reject_error(e, CanGc::note());
1037                        return;
1038                    },
1039                };
1040
1041                let bytes = match exported_key {
1042                    ExportedKey::Raw(k) => k,
1043                    ExportedKey::Jwk(key) => {
1044                        // The spec states to convert this to an ECMAscript object and stringify it, but since we know
1045                        // that the output will be a string of JSON we can just construct it manually
1046                        // TODO: Support more than just a subset of the JWK dict, or find a way to
1047                        // stringify via SM internals
1048                        let Some(k) = key.k else {
1049                            promise.reject_error(Error::Syntax(None), CanGc::note());
1050                            return;
1051                        };
1052                        let Some(alg) = key.alg else {
1053                            promise.reject_error(Error::Syntax(None), CanGc::note());
1054                            return;
1055                        };
1056                        let Some(ext) = key.ext else {
1057                            promise.reject_error(Error::Syntax(None), CanGc::note());
1058                            return;
1059                        };
1060                        let Some(key_ops) = key.key_ops else {
1061                            promise.reject_error(Error::Syntax(None), CanGc::note());
1062                            return;
1063                        };
1064                        let key_ops_str = key_ops.iter().map(|op| op.to_string()).collect::<Vec<String>>();
1065                        format!("{{
1066                            \"kty\": \"oct\",
1067                            \"k\": \"{}\",
1068                            \"alg\": \"{}\",
1069                            \"ext\": {},
1070                            \"key_ops\": {:?}
1071                        }}", k, alg, ext, key_ops_str)
1072                        .into_bytes()
1073                    },
1074                };
1075
1076                let cx = GlobalScope::get_cx();
1077                rooted!(in(*cx) let mut array_buffer_ptr = ptr::null_mut::<JSObject>());
1078
1079                let result = match normalized_algorithm {
1080                    KeyWrapAlgorithm::AesKw => {
1081                        subtle.wrap_key_aes_kw(&wrapping_key, &bytes, cx, array_buffer_ptr.handle_mut(), CanGc::note())
1082                    },
1083                    KeyWrapAlgorithm::AesCbc(params) => {
1084                        subtle.encrypt_aes_cbc(&params, &wrapping_key, &bytes, cx, array_buffer_ptr.handle_mut(),
1085                            CanGc::note())
1086                    },
1087                    KeyWrapAlgorithm::AesCtr(params) => {
1088                        subtle.encrypt_decrypt_aes_ctr(
1089                            &params, &wrapping_key, &bytes, cx, array_buffer_ptr.handle_mut(), CanGc::note()
1090                        )
1091                    },
1092                    KeyWrapAlgorithm::AesGcm(params) => {
1093                        subtle.encrypt_aes_gcm(
1094                            &params, &wrapping_key, &bytes, cx, array_buffer_ptr.handle_mut(), CanGc::note()
1095                        )
1096                    },
1097                };
1098
1099                match result {
1100                    Ok(_) => promise.resolve_native(&*array_buffer_ptr, CanGc::note()),
1101                    Err(e) => promise.reject_error(e, CanGc::note()),
1102                }
1103            }),
1104        );
1105
1106        promise
1107    }
1108
1109    /// <https://w3c.github.io/webcrypto/#SubtleCrypto-method-unwrapKey>
1110    fn UnwrapKey(
1111        &self,
1112        cx: JSContext,
1113        format: KeyFormat,
1114        wrapped_key: ArrayBufferViewOrArrayBuffer,
1115        unwrapping_key: &CryptoKey,
1116        unwrap_algorithm: AlgorithmIdentifier,
1117        unwrapped_key_algorithm: AlgorithmIdentifier,
1118        extractable: bool,
1119        key_usages: Vec<KeyUsage>,
1120        comp: InRealm,
1121        can_gc: CanGc,
1122    ) -> Rc<Promise> {
1123        let promise = Promise::new_in_current_realm(comp, can_gc);
1124        let wrapped_key_bytes = match wrapped_key {
1125            ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
1126            ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
1127        };
1128        let normalized_algorithm =
1129            match normalize_algorithm_for_key_wrap(cx, &unwrap_algorithm, can_gc) {
1130                Ok(algorithm) => algorithm,
1131                Err(e) => {
1132                    promise.reject_error(e, can_gc);
1133                    return promise;
1134                },
1135            };
1136        let normalized_key_algorithm =
1137            match normalize_algorithm_for_import_key(cx, &unwrapped_key_algorithm, can_gc) {
1138                Ok(algorithm) => algorithm,
1139                Err(e) => {
1140                    promise.reject_error(e, can_gc);
1141                    return promise;
1142                },
1143            };
1144
1145        let this = Trusted::new(self);
1146        let trusted_key = Trusted::new(unwrapping_key);
1147        let trusted_promise = TrustedPromise::new(promise.clone());
1148        self.global().task_manager().dom_manipulation_task_source().queue(
1149            task!(unwrap_key: move || {
1150                let subtle = this.root();
1151                let promise = trusted_promise.root();
1152                let unwrapping_key = trusted_key.root();
1153                let alg_name = unwrapping_key.algorithm();
1154                let valid_usage = unwrapping_key.usages().contains(&KeyUsage::UnwrapKey);
1155
1156                if !valid_usage || normalized_algorithm.name() != alg_name.as_str() {
1157                    promise.reject_error(Error::InvalidAccess, CanGc::note());
1158                    return;
1159                }
1160
1161                let cx = GlobalScope::get_cx();
1162                rooted!(in(*cx) let mut array_buffer_ptr = ptr::null_mut::<JSObject>());
1163
1164                let result = match normalized_algorithm {
1165                    KeyWrapAlgorithm::AesKw => {
1166                        subtle.unwrap_key_aes_kw(&unwrapping_key, &wrapped_key_bytes, cx, array_buffer_ptr.handle_mut(),
1167                            CanGc::note())
1168                    },
1169                    KeyWrapAlgorithm::AesCbc(params) => {
1170                        subtle.decrypt_aes_cbc(
1171                            &params, &unwrapping_key, &wrapped_key_bytes, cx, array_buffer_ptr.handle_mut(),
1172                            CanGc::note()
1173                        )
1174                    },
1175                    KeyWrapAlgorithm::AesCtr(params) => {
1176                        subtle.encrypt_decrypt_aes_ctr(
1177                            &params, &unwrapping_key, &wrapped_key_bytes, cx, array_buffer_ptr.handle_mut(),
1178                            CanGc::note()
1179                        )
1180                    },
1181                    KeyWrapAlgorithm::AesGcm(params) => {
1182                        subtle.decrypt_aes_gcm(
1183                            &params, &unwrapping_key, &wrapped_key_bytes, cx, array_buffer_ptr.handle_mut(),
1184                            CanGc::note()
1185                        )
1186                    },
1187                };
1188
1189                let bytes = match result {
1190                    Ok(bytes) => bytes,
1191                    Err(e) => {
1192                        promise.reject_error(e, CanGc::note());
1193                        return;
1194                    },
1195                };
1196
1197                // Step 15.
1198                let import_key_bytes = match format {
1199                    // If format is equal to the strings "raw", "pkcs8", or "spki":
1200                    KeyFormat::Raw | KeyFormat::Pkcs8 | KeyFormat::Spki => {
1201                        // Let key be bytes.
1202                        bytes
1203                    },
1204                    // If format is equal to the string "jwk":
1205                    KeyFormat::Jwk => {
1206                        // Let key be the result of executing the parse a JWK algorithm, with bytes
1207                        // as the data to be parsed.
1208                        if let Err(error) = JsonWebKey::parse(cx, &bytes) {
1209                            promise.reject_error(error, CanGc::note());
1210                            return;
1211                        }
1212                        // NOTE: We can directly use bytes to perform the import key operation of
1213                        // normailized key algorithm, instead of re-serializing the resultant
1214                        // JsonWebKey dictionary.
1215                        bytes
1216                    },
1217                };
1218
1219                match normalized_key_algorithm.import_key(&subtle, format, &import_key_bytes,
1220                    extractable, key_usages, CanGc::note()) {
1221                    Ok(imported_key) => promise.resolve_native(&imported_key, CanGc::note()),
1222                    Err(e) => promise.reject_error(e, CanGc::note()),
1223                }
1224            }),
1225        );
1226
1227        promise
1228    }
1229}
1230
1231// These "subtle" structs are proxies for the codegen'd dicts which don't hold a DOMString
1232// so they can be sent safely when running steps in parallel.
1233
1234#[allow(dead_code)]
1235#[derive(Clone, Debug)]
1236pub(crate) struct SubtleAlgorithm {
1237    #[allow(dead_code)]
1238    pub(crate) name: String,
1239}
1240
1241impl From<DOMString> for SubtleAlgorithm {
1242    fn from(name: DOMString) -> Self {
1243        SubtleAlgorithm {
1244            name: name.to_string(),
1245        }
1246    }
1247}
1248
1249#[derive(Clone, Debug)]
1250pub(crate) struct SubtleAesCbcParams {
1251    #[allow(dead_code)]
1252    pub(crate) name: String,
1253    pub(crate) iv: Vec<u8>,
1254}
1255
1256impl From<RootedTraceableBox<AesCbcParams>> for SubtleAesCbcParams {
1257    fn from(params: RootedTraceableBox<AesCbcParams>) -> Self {
1258        let iv = match &params.iv {
1259            ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
1260            ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
1261        };
1262        SubtleAesCbcParams {
1263            name: params.parent.name.to_string(),
1264            iv,
1265        }
1266    }
1267}
1268
1269#[derive(Clone, Debug)]
1270pub(crate) struct SubtleAesCtrParams {
1271    pub(crate) name: String,
1272    pub(crate) counter: Vec<u8>,
1273    pub(crate) length: u8,
1274}
1275
1276impl From<RootedTraceableBox<AesCtrParams>> for SubtleAesCtrParams {
1277    fn from(params: RootedTraceableBox<AesCtrParams>) -> Self {
1278        let counter = match &params.counter {
1279            ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
1280            ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
1281        };
1282        SubtleAesCtrParams {
1283            name: params.parent.name.to_string(),
1284            counter,
1285            length: params.length,
1286        }
1287    }
1288}
1289
1290#[derive(Clone, Debug)]
1291pub(crate) struct SubtleAesGcmParams {
1292    pub(crate) name: String,
1293    pub(crate) iv: Vec<u8>,
1294    pub(crate) additional_data: Option<Vec<u8>>,
1295    pub(crate) tag_length: Option<u8>,
1296}
1297
1298impl From<RootedTraceableBox<AesGcmParams>> for SubtleAesGcmParams {
1299    fn from(params: RootedTraceableBox<AesGcmParams>) -> Self {
1300        let iv = match &params.iv {
1301            ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
1302            ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
1303        };
1304        let additional_data = params.additionalData.as_ref().map(|data| match data {
1305            ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
1306            ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
1307        });
1308
1309        SubtleAesGcmParams {
1310            name: params.parent.name.to_string(),
1311            iv,
1312            additional_data,
1313            tag_length: params.tagLength,
1314        }
1315    }
1316}
1317
1318#[derive(Clone, Debug)]
1319pub(crate) struct SubtleAesKeyGenParams {
1320    pub(crate) name: String,
1321    pub(crate) length: u16,
1322}
1323
1324impl From<AesKeyGenParams> for SubtleAesKeyGenParams {
1325    fn from(params: AesKeyGenParams) -> Self {
1326        SubtleAesKeyGenParams {
1327            name: params.parent.name.to_string().to_uppercase(),
1328            length: params.length,
1329        }
1330    }
1331}
1332
1333/// <https://w3c.github.io/webcrypto/#dfn-HmacImportParams>
1334#[derive(Clone)]
1335struct SubtleHmacImportParams {
1336    /// <https://w3c.github.io/webcrypto/#dfn-HmacKeyAlgorithm-hash>
1337    hash: DigestAlgorithm,
1338
1339    /// <https://w3c.github.io/webcrypto/#dfn-HmacKeyGenParams-length>
1340    length: Option<u32>,
1341}
1342
1343impl SubtleHmacImportParams {
1344    fn new(
1345        cx: JSContext,
1346        params: RootedTraceableBox<HmacImportParams>,
1347        can_gc: CanGc,
1348    ) -> Fallible<Self> {
1349        let hash = normalize_algorithm_for_digest(cx, &params.hash, can_gc)?;
1350        let params = Self {
1351            hash,
1352            length: params.length,
1353        };
1354        Ok(params)
1355    }
1356
1357    /// <https://w3c.github.io/webcrypto/#hmac-operations>
1358    fn get_key_length(&self) -> Result<u32, Error> {
1359        // Step 1.
1360        let length = match self.length {
1361            // If the length member of normalizedDerivedKeyAlgorithm is not present:
1362            None => {
1363                // Let length be the block size in bits of the hash function identified by the hash member of
1364                // normalizedDerivedKeyAlgorithm.
1365                match self.hash {
1366                    DigestAlgorithm::Sha1 => 160,
1367                    DigestAlgorithm::Sha256 => 256,
1368                    DigestAlgorithm::Sha384 => 384,
1369                    DigestAlgorithm::Sha512 => 512,
1370                }
1371            },
1372            // Otherwise, if the length member of normalizedDerivedKeyAlgorithm is non-zero:
1373            Some(length) if length != 0 => {
1374                // Let length be equal to the length member of normalizedDerivedKeyAlgorithm.
1375                length
1376            },
1377            // Otherwise:
1378            _ => {
1379                // throw a TypeError.
1380                return Err(Error::Type("[[length]] must not be zero".to_string()));
1381            },
1382        };
1383
1384        // Step 2. Return length.
1385        Ok(length)
1386    }
1387}
1388
1389struct SubtleHmacKeyGenParams {
1390    /// <https://w3c.github.io/webcrypto/#dfn-HmacKeyGenParams-hash>
1391    hash: DigestAlgorithm,
1392
1393    /// <https://w3c.github.io/webcrypto/#dfn-HmacKeyGenParams-length>
1394    length: Option<u32>,
1395}
1396
1397impl SubtleHmacKeyGenParams {
1398    fn new(
1399        cx: JSContext,
1400        params: RootedTraceableBox<HmacKeyGenParams>,
1401        can_gc: CanGc,
1402    ) -> Fallible<Self> {
1403        let hash = normalize_algorithm_for_digest(cx, &params.hash, can_gc)?;
1404        let params = Self {
1405            hash,
1406            length: params.length,
1407        };
1408        Ok(params)
1409    }
1410}
1411/// <https://w3c.github.io/webcrypto/#hkdf-params>
1412#[derive(Clone, Debug)]
1413pub(crate) struct SubtleHkdfParams {
1414    /// <https://w3c.github.io/webcrypto/#dfn-HkdfParams-hash>
1415    hash: DigestAlgorithm,
1416
1417    /// <https://w3c.github.io/webcrypto/#dfn-HkdfParams-salt>
1418    salt: Vec<u8>,
1419
1420    /// <https://w3c.github.io/webcrypto/#dfn-HkdfParams-info>
1421    info: Vec<u8>,
1422}
1423
1424impl SubtleHkdfParams {
1425    fn new(cx: JSContext, params: RootedTraceableBox<HkdfParams>, can_gc: CanGc) -> Fallible<Self> {
1426        let hash = normalize_algorithm_for_digest(cx, &params.hash, can_gc)?;
1427        let salt = match &params.salt {
1428            ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
1429            ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
1430        };
1431        let info = match &params.info {
1432            ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
1433            ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
1434        };
1435
1436        let params = Self { hash, salt, info };
1437
1438        Ok(params)
1439    }
1440}
1441
1442/// <https://w3c.github.io/webcrypto/#dfn-Pbkdf2Params>
1443#[derive(Clone, Debug)]
1444pub(crate) struct SubtlePbkdf2Params {
1445    /// <https://w3c.github.io/webcrypto/#dfn-Pbkdf2Params-salt>
1446    salt: Vec<u8>,
1447
1448    /// <https://w3c.github.io/webcrypto/#dfn-Pbkdf2Params-iterations>
1449    iterations: u32,
1450
1451    /// <https://w3c.github.io/webcrypto/#dfn-Pbkdf2Params-hash>
1452    hash: DigestAlgorithm,
1453}
1454
1455impl SubtlePbkdf2Params {
1456    fn new(
1457        cx: JSContext,
1458        params: RootedTraceableBox<Pbkdf2Params>,
1459        can_gc: CanGc,
1460    ) -> Fallible<Self> {
1461        let salt = match &params.salt {
1462            ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
1463            ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
1464        };
1465
1466        let params = Self {
1467            salt,
1468            iterations: params.iterations,
1469            hash: normalize_algorithm_for_digest(cx, &params.hash, can_gc)?,
1470        };
1471
1472        Ok(params)
1473    }
1474}
1475
1476enum GetKeyLengthAlgorithm {
1477    Aes(u16),
1478    Hmac(SubtleHmacImportParams),
1479}
1480
1481#[derive(Clone, Copy, Debug)]
1482enum DigestAlgorithm {
1483    /// <https://w3c.github.io/webcrypto/#sha>
1484    Sha1,
1485
1486    /// <https://w3c.github.io/webcrypto/#sha>
1487    Sha256,
1488
1489    /// <https://w3c.github.io/webcrypto/#sha>
1490    Sha384,
1491
1492    /// <https://w3c.github.io/webcrypto/#sha>
1493    Sha512,
1494}
1495
1496/// A normalized algorithm returned by [`normalize_algorithm`] with operation `"importKey"`
1497///
1498/// [`normalize_algorithm`]: https://w3c.github.io/webcrypto/#algorithm-normalization-normalize-an-algorithm
1499#[derive(Clone)]
1500enum ImportKeyAlgorithm {
1501    AesCbc,
1502    AesCtr,
1503    AesKw,
1504    AesGcm,
1505    Hmac(SubtleHmacImportParams),
1506    Pbkdf2,
1507    Hkdf,
1508}
1509
1510/// A normalized algorithm returned by [`normalize_algorithm`] with operation `"deriveBits"`
1511///
1512/// [`normalize_algorithm`]: https://w3c.github.io/webcrypto/#algorithm-normalization-normalize-an-algorithm
1513enum DeriveBitsAlgorithm {
1514    Pbkdf2(SubtlePbkdf2Params),
1515    Hkdf(SubtleHkdfParams),
1516}
1517
1518/// A normalized algorithm returned by [`normalize_algorithm`] with operation `"encrypt"` or `"decrypt"`
1519///
1520/// [`normalize_algorithm`]: https://w3c.github.io/webcrypto/#algorithm-normalization-normalize-an-algorithm
1521#[allow(clippy::enum_variant_names)]
1522enum EncryptionAlgorithm {
1523    AesCbc(SubtleAesCbcParams),
1524    AesCtr(SubtleAesCtrParams),
1525    AesGcm(SubtleAesGcmParams),
1526}
1527
1528/// A normalized algorithm returned by [`normalize_algorithm`] with operation `"sign"` or `"verify"`
1529///
1530/// [`normalize_algorithm`]: https://w3c.github.io/webcrypto/#algorithm-normalization-normalize-an-algorithm
1531enum SignatureAlgorithm {
1532    Hmac,
1533}
1534
1535/// A normalized algorithm returned by [`normalize_algorithm`] with operation `"generateKey"`
1536///
1537/// [`normalize_algorithm`]: https://w3c.github.io/webcrypto/#algorithm-normalization-normalize-an-algorithm
1538enum KeyGenerationAlgorithm {
1539    Aes(SubtleAesKeyGenParams),
1540    Hmac(SubtleHmacKeyGenParams),
1541}
1542
1543/// A normalized algorithm returned by [`normalize_algorithm`] with operation `"wrapKey"` or `"unwrapKey"`
1544///
1545/// [`normalize_algorithm`]: https://w3c.github.io/webcrypto/#algorithm-normalization-normalize-an-algorithm
1546#[allow(clippy::enum_variant_names)]
1547enum KeyWrapAlgorithm {
1548    AesKw,
1549    AesCbc(SubtleAesCbcParams),
1550    AesCtr(SubtleAesCtrParams),
1551    AesGcm(SubtleAesGcmParams),
1552}
1553
1554/// Helper to abstract the conversion process of a JS value into many different WebIDL dictionaries.
1555trait DictionaryFromJSVal: Sized {
1556    fn create(
1557        cx: JSContext,
1558        value: HandleValue,
1559        can_gc: CanGc,
1560    ) -> Result<ConversionResult<Self>, ()>;
1561}
1562
1563impl DictionaryFromJSVal for Algorithm {
1564    fn create(
1565        cx: JSContext,
1566        value: HandleValue,
1567        can_gc: CanGc,
1568    ) -> Result<ConversionResult<Self>, ()> {
1569        Self::new(cx, value, can_gc)
1570    }
1571}
1572
1573impl DictionaryFromJSVal for AesDerivedKeyParams {
1574    fn create(
1575        cx: JSContext,
1576        value: HandleValue,
1577        can_gc: CanGc,
1578    ) -> Result<ConversionResult<Self>, ()> {
1579        Self::new(cx, value, can_gc)
1580    }
1581}
1582
1583impl DictionaryFromJSVal for AesKeyGenParams {
1584    fn create(
1585        cx: JSContext,
1586        value: HandleValue,
1587        can_gc: CanGc,
1588    ) -> Result<ConversionResult<Self>, ()> {
1589        Self::new(cx, value, can_gc)
1590    }
1591}
1592
1593impl DictionaryFromJSVal for RootedTraceableBox<HmacImportParams> {
1594    fn create(
1595        cx: JSContext,
1596        value: HandleValue,
1597        can_gc: CanGc,
1598    ) -> Result<ConversionResult<Self>, ()> {
1599        HmacImportParams::new(cx, value, can_gc)
1600    }
1601}
1602
1603impl DictionaryFromJSVal for RootedTraceableBox<HmacKeyGenParams> {
1604    fn create(
1605        cx: JSContext,
1606        value: HandleValue,
1607        can_gc: CanGc,
1608    ) -> Result<ConversionResult<Self>, ()> {
1609        HmacKeyGenParams::new(cx, value, can_gc)
1610    }
1611}
1612
1613impl DictionaryFromJSVal for HmacKeyAlgorithm {
1614    fn create(
1615        cx: JSContext,
1616        value: HandleValue,
1617        can_gc: CanGc,
1618    ) -> Result<ConversionResult<Self>, ()> {
1619        Self::new(cx, value, can_gc)
1620    }
1621}
1622
1623impl DictionaryFromJSVal for RootedTraceableBox<AesCbcParams> {
1624    fn create(
1625        cx: JSContext,
1626        value: HandleValue,
1627        can_gc: CanGc,
1628    ) -> Result<ConversionResult<Self>, ()> {
1629        AesCbcParams::new(cx, value, can_gc)
1630    }
1631}
1632
1633impl DictionaryFromJSVal for RootedTraceableBox<AesCtrParams> {
1634    fn create(
1635        cx: JSContext,
1636        value: HandleValue,
1637        can_gc: CanGc,
1638    ) -> Result<ConversionResult<Self>, ()> {
1639        AesCtrParams::new(cx, value, can_gc)
1640    }
1641}
1642
1643impl DictionaryFromJSVal for RootedTraceableBox<AesGcmParams> {
1644    fn create(
1645        cx: JSContext,
1646        value: HandleValue,
1647        can_gc: CanGc,
1648    ) -> Result<ConversionResult<Self>, ()> {
1649        AesGcmParams::new(cx, value, can_gc)
1650    }
1651}
1652
1653impl DictionaryFromJSVal for RootedTraceableBox<Pbkdf2Params> {
1654    fn create(
1655        cx: JSContext,
1656        value: HandleValue,
1657        can_gc: CanGc,
1658    ) -> Result<ConversionResult<Self>, ()> {
1659        Pbkdf2Params::new(cx, value, can_gc)
1660    }
1661}
1662
1663impl DictionaryFromJSVal for RootedTraceableBox<HkdfParams> {
1664    fn create(
1665        cx: JSContext,
1666        value: HandleValue,
1667        can_gc: CanGc,
1668    ) -> Result<ConversionResult<Self>, ()> {
1669        HkdfParams::new(cx, value, can_gc)
1670    }
1671}
1672
1673fn extract_native_dict<T>(converted: Result<ConversionResult<T>, ()>) -> Fallible<T> {
1674    let params_result = converted.map_err(|_| Error::JSFailed)?;
1675    let ConversionResult::Success(params) = params_result else {
1676        return Err(Error::Syntax(None));
1677    };
1678    Ok(params)
1679}
1680
1681fn value_from_js_object<T: DictionaryFromJSVal>(
1682    cx: JSContext,
1683    value: HandleValue,
1684    can_gc: CanGc,
1685) -> Fallible<T> {
1686    extract_native_dict(T::create(cx, value, can_gc))
1687}
1688
1689trait DictionaryFromJSValType: crate::JSTraceable {}
1690impl<T: crate::JSTraceable + 'static> DictionaryFromJSValType for T where
1691    RootedTraceableBox<T>: DictionaryFromJSVal
1692{
1693}
1694
1695fn boxed_value_from_js_object<T: DictionaryFromJSValType>(
1696    cx: JSContext,
1697    value: HandleValue,
1698    can_gc: CanGc,
1699) -> Fallible<RootedTraceableBox<T>>
1700where
1701    RootedTraceableBox<T>: DictionaryFromJSVal,
1702{
1703    extract_native_dict(<RootedTraceableBox<T>>::create(cx, value, can_gc))
1704}
1705
1706/// <https://w3c.github.io/webcrypto/#algorithm-normalization-normalize-an-algorithm> with operation `"get key length"`
1707fn normalize_algorithm_for_get_key_length(
1708    cx: JSContext,
1709    algorithm: &AlgorithmIdentifier,
1710    can_gc: CanGc,
1711) -> Result<GetKeyLengthAlgorithm, Error> {
1712    match algorithm {
1713        AlgorithmIdentifier::Object(obj) => {
1714            rooted!(in(*cx) let value = ObjectValue(obj.get()));
1715            let algorithm = value_from_js_object::<Algorithm>(cx, value.handle(), can_gc)?;
1716
1717            let name = algorithm.name.str();
1718            let normalized_algorithm = if name.eq_ignore_ascii_case(ALG_AES_CBC) ||
1719                name.eq_ignore_ascii_case(ALG_AES_CTR) ||
1720                name.eq_ignore_ascii_case(ALG_AES_GCM)
1721            {
1722                let params =
1723                    value_from_js_object::<AesDerivedKeyParams>(cx, value.handle(), can_gc)?;
1724                GetKeyLengthAlgorithm::Aes(params.length)
1725            } else if name.eq_ignore_ascii_case(ALG_HMAC) {
1726                let params =
1727                    boxed_value_from_js_object::<HmacImportParams>(cx, value.handle(), can_gc)?;
1728                let subtle_params = SubtleHmacImportParams::new(cx, params, can_gc)?;
1729                return Ok(GetKeyLengthAlgorithm::Hmac(subtle_params));
1730            } else {
1731                return Err(Error::NotSupported);
1732            };
1733
1734            Ok(normalized_algorithm)
1735        },
1736        AlgorithmIdentifier::String(_) => {
1737            // All algorithms that support "get key length" require additional parameters
1738            Err(Error::NotSupported)
1739        },
1740    }
1741}
1742
1743/// <https://w3c.github.io/webcrypto/#algorithm-normalization-normalize-an-algorithm> with operation `"digest"`
1744fn normalize_algorithm_for_digest(
1745    cx: JSContext,
1746    algorithm: &AlgorithmIdentifier,
1747    can_gc: CanGc,
1748) -> Result<DigestAlgorithm, Error> {
1749    let name = match algorithm {
1750        AlgorithmIdentifier::Object(obj) => {
1751            rooted!(in(*cx) let value = ObjectValue(obj.get()));
1752            let algorithm = value_from_js_object::<Algorithm>(cx, value.handle(), can_gc)?;
1753
1754            algorithm.name.str().to_uppercase()
1755        },
1756        AlgorithmIdentifier::String(name) => name.str().to_uppercase(),
1757    };
1758
1759    let normalized_algorithm = match name.as_str() {
1760        ALG_SHA1 => DigestAlgorithm::Sha1,
1761        ALG_SHA256 => DigestAlgorithm::Sha256,
1762        ALG_SHA384 => DigestAlgorithm::Sha384,
1763        ALG_SHA512 => DigestAlgorithm::Sha512,
1764        _ => return Err(Error::NotSupported),
1765    };
1766
1767    Ok(normalized_algorithm)
1768}
1769
1770/// <https://w3c.github.io/webcrypto/#algorithm-normalization-normalize-an-algorithm> with operation `"importKey"`
1771fn normalize_algorithm_for_import_key(
1772    cx: JSContext,
1773    algorithm: &AlgorithmIdentifier,
1774    can_gc: CanGc,
1775) -> Result<ImportKeyAlgorithm, Error> {
1776    let name = match algorithm {
1777        AlgorithmIdentifier::Object(obj) => {
1778            rooted!(in(*cx) let value = ObjectValue(obj.get()));
1779            let algorithm = value_from_js_object::<Algorithm>(cx, value.handle(), can_gc)?;
1780
1781            let name = algorithm.name.str().to_uppercase();
1782            if name == ALG_HMAC {
1783                let params =
1784                    boxed_value_from_js_object::<HmacImportParams>(cx, value.handle(), can_gc)?;
1785                let subtle_params = SubtleHmacImportParams::new(cx, params, can_gc)?;
1786                return Ok(ImportKeyAlgorithm::Hmac(subtle_params));
1787            }
1788
1789            name
1790        },
1791        AlgorithmIdentifier::String(name) => name.str().to_uppercase(),
1792    };
1793
1794    let normalized_algorithm = match name.as_str() {
1795        ALG_AES_CBC => ImportKeyAlgorithm::AesCbc,
1796        ALG_AES_CTR => ImportKeyAlgorithm::AesCtr,
1797        ALG_AES_KW => ImportKeyAlgorithm::AesKw,
1798        ALG_AES_GCM => ImportKeyAlgorithm::AesGcm,
1799        ALG_PBKDF2 => ImportKeyAlgorithm::Pbkdf2,
1800        ALG_HKDF => ImportKeyAlgorithm::Hkdf,
1801        _ => return Err(Error::NotSupported),
1802    };
1803
1804    Ok(normalized_algorithm)
1805}
1806
1807/// <https://w3c.github.io/webcrypto/#algorithm-normalization-normalize-an-algorithm> with operation `"deriveBits"`
1808fn normalize_algorithm_for_derive_bits(
1809    cx: JSContext,
1810    algorithm: &AlgorithmIdentifier,
1811    can_gc: CanGc,
1812) -> Result<DeriveBitsAlgorithm, Error> {
1813    let AlgorithmIdentifier::Object(obj) = algorithm else {
1814        // All algorithms that support "deriveBits" require additional parameters
1815        return Err(Error::NotSupported);
1816    };
1817
1818    rooted!(in(*cx) let value = ObjectValue(obj.get()));
1819    let algorithm = value_from_js_object::<Algorithm>(cx, value.handle(), can_gc)?;
1820
1821    let normalized_algorithm = if algorithm.name.str().eq_ignore_ascii_case(ALG_PBKDF2) {
1822        let params = boxed_value_from_js_object::<Pbkdf2Params>(cx, value.handle(), can_gc)?;
1823        let subtle_params = SubtlePbkdf2Params::new(cx, params, can_gc)?;
1824        DeriveBitsAlgorithm::Pbkdf2(subtle_params)
1825    } else if algorithm.name.str().eq_ignore_ascii_case(ALG_HKDF) {
1826        let params = boxed_value_from_js_object::<HkdfParams>(cx, value.handle(), can_gc)?;
1827        let subtle_params = SubtleHkdfParams::new(cx, params, can_gc)?;
1828        DeriveBitsAlgorithm::Hkdf(subtle_params)
1829    } else {
1830        return Err(Error::NotSupported);
1831    };
1832
1833    Ok(normalized_algorithm)
1834}
1835
1836/// <https://w3c.github.io/webcrypto/#algorithm-normalization-normalize-an-algorithm> with operation `"deriveBits"`
1837fn normalize_algorithm_for_encrypt_or_decrypt(
1838    cx: JSContext,
1839    algorithm: &AlgorithmIdentifier,
1840    can_gc: CanGc,
1841) -> Result<EncryptionAlgorithm, Error> {
1842    let AlgorithmIdentifier::Object(obj) = algorithm else {
1843        // All algorithms that support "encrypt" or "decrypt" require additional parameters
1844        return Err(Error::NotSupported);
1845    };
1846
1847    rooted!(in(*cx) let value = ObjectValue(obj.get()));
1848    let algorithm = value_from_js_object::<Algorithm>(cx, value.handle(), can_gc)?;
1849
1850    let name = algorithm.name.str();
1851    let normalized_algorithm = if name.eq_ignore_ascii_case(ALG_AES_CBC) {
1852        let params = boxed_value_from_js_object::<AesCbcParams>(cx, value.handle(), can_gc)?;
1853        EncryptionAlgorithm::AesCbc(params.into())
1854    } else if name.eq_ignore_ascii_case(ALG_AES_CTR) {
1855        let params = boxed_value_from_js_object::<AesCtrParams>(cx, value.handle(), can_gc)?;
1856        EncryptionAlgorithm::AesCtr(params.into())
1857    } else if name.eq_ignore_ascii_case(ALG_AES_GCM) {
1858        let params = boxed_value_from_js_object::<AesGcmParams>(cx, value.handle(), can_gc)?;
1859        EncryptionAlgorithm::AesGcm(params.into())
1860    } else {
1861        return Err(Error::NotSupported);
1862    };
1863
1864    Ok(normalized_algorithm)
1865}
1866
1867/// <https://w3c.github.io/webcrypto/#algorithm-normalization-normalize-an-algorithm> with operation `"sign"`
1868/// or `"verify"`
1869fn normalize_algorithm_for_sign_or_verify(
1870    cx: JSContext,
1871    algorithm: &AlgorithmIdentifier,
1872    can_gc: CanGc,
1873) -> Result<SignatureAlgorithm, Error> {
1874    let name = match algorithm {
1875        AlgorithmIdentifier::Object(obj) => {
1876            rooted!(in(*cx) let value = ObjectValue(obj.get()));
1877            let algorithm = value_from_js_object::<Algorithm>(cx, value.handle(), can_gc)?;
1878
1879            algorithm.name.str().to_uppercase()
1880        },
1881        AlgorithmIdentifier::String(name) => name.str().to_uppercase(),
1882    };
1883
1884    let normalized_algorithm = match name.as_str() {
1885        ALG_HMAC => SignatureAlgorithm::Hmac,
1886        _ => return Err(Error::NotSupported),
1887    };
1888
1889    Ok(normalized_algorithm)
1890}
1891
1892/// <https://w3c.github.io/webcrypto/#algorithm-normalization-normalize-an-algorithm> with operation `"generateKey"`
1893fn normalize_algorithm_for_generate_key(
1894    cx: JSContext,
1895    algorithm: &AlgorithmIdentifier,
1896    can_gc: CanGc,
1897) -> Result<KeyGenerationAlgorithm, Error> {
1898    let AlgorithmIdentifier::Object(obj) = algorithm else {
1899        // All algorithms that support "generateKey" require additional parameters
1900        return Err(Error::NotSupported);
1901    };
1902
1903    rooted!(in(*cx) let value = ObjectValue(obj.get()));
1904    let algorithm = value_from_js_object::<Algorithm>(cx, value.handle(), can_gc)?;
1905
1906    let name = algorithm.name.str();
1907    let normalized_algorithm = if name.eq_ignore_ascii_case(ALG_AES_CBC) ||
1908        name.eq_ignore_ascii_case(ALG_AES_CTR) ||
1909        name.eq_ignore_ascii_case(ALG_AES_KW) ||
1910        name.eq_ignore_ascii_case(ALG_AES_GCM)
1911    {
1912        let params = value_from_js_object::<AesKeyGenParams>(cx, value.handle(), can_gc)?;
1913        KeyGenerationAlgorithm::Aes(params.into())
1914    } else if name.eq_ignore_ascii_case(ALG_HMAC) {
1915        let params = boxed_value_from_js_object::<HmacKeyGenParams>(cx, value.handle(), can_gc)?;
1916        let subtle_params = SubtleHmacKeyGenParams::new(cx, params, can_gc)?;
1917        KeyGenerationAlgorithm::Hmac(subtle_params)
1918    } else {
1919        return Err(Error::NotSupported);
1920    };
1921
1922    Ok(normalized_algorithm)
1923}
1924
1925/// <https://w3c.github.io/webcrypto/#algorithm-normalization-normalize-an-algorithm> with operation `"wrapKey"` or `"unwrapKey"`
1926fn normalize_algorithm_for_key_wrap(
1927    cx: JSContext,
1928    algorithm: &AlgorithmIdentifier,
1929    can_gc: CanGc,
1930) -> Result<KeyWrapAlgorithm, Error> {
1931    let name = match algorithm {
1932        AlgorithmIdentifier::Object(obj) => {
1933            rooted!(in(*cx) let value = ObjectValue(obj.get()));
1934            let algorithm = value_from_js_object::<Algorithm>(cx, value.handle(), can_gc)?;
1935
1936            algorithm.name.str().to_uppercase()
1937        },
1938        AlgorithmIdentifier::String(name) => name.str().to_uppercase(),
1939    };
1940
1941    let normalized_algorithm = match name.as_str() {
1942        ALG_AES_KW => KeyWrapAlgorithm::AesKw,
1943        ALG_AES_CBC => {
1944            let AlgorithmIdentifier::Object(obj) = algorithm else {
1945                return Err(Error::Syntax(None));
1946            };
1947            rooted!(in(*cx) let value = ObjectValue(obj.get()));
1948            KeyWrapAlgorithm::AesCbc(
1949                boxed_value_from_js_object::<AesCbcParams>(cx, value.handle(), can_gc)?.into(),
1950            )
1951        },
1952        ALG_AES_CTR => {
1953            let AlgorithmIdentifier::Object(obj) = algorithm else {
1954                return Err(Error::Syntax(None));
1955            };
1956            rooted!(in(*cx) let value = ObjectValue(obj.get()));
1957            KeyWrapAlgorithm::AesCtr(
1958                boxed_value_from_js_object::<AesCtrParams>(cx, value.handle(), can_gc)?.into(),
1959            )
1960        },
1961        ALG_AES_GCM => {
1962            let AlgorithmIdentifier::Object(obj) = algorithm else {
1963                return Err(Error::Syntax(None));
1964            };
1965            rooted!(in(*cx) let value = ObjectValue(obj.get()));
1966            KeyWrapAlgorithm::AesGcm(
1967                boxed_value_from_js_object::<AesGcmParams>(cx, value.handle(), can_gc)?.into(),
1968            )
1969        },
1970        _ => return Err(Error::NotSupported),
1971    };
1972
1973    Ok(normalized_algorithm)
1974}
1975
1976impl SubtleCrypto {
1977    /// <https://w3c.github.io/webcrypto/#aes-cbc-operations>
1978    fn encrypt_aes_cbc(
1979        &self,
1980        params: &SubtleAesCbcParams,
1981        key: &CryptoKey,
1982        data: &[u8],
1983        cx: JSContext,
1984        handle: MutableHandleObject,
1985        can_gc: CanGc,
1986    ) -> Result<Vec<u8>, Error> {
1987        if params.iv.len() != 16 {
1988            return Err(Error::Operation);
1989        }
1990
1991        let plaintext = Vec::from(data);
1992        let iv = GenericArray::from_slice(&params.iv);
1993
1994        let ct = match key.handle() {
1995            Handle::Aes128(data) => {
1996                let key_data = GenericArray::from_slice(data);
1997                Aes128CbcEnc::new(key_data, iv).encrypt_padded_vec_mut::<Pkcs7>(&plaintext)
1998            },
1999            Handle::Aes192(data) => {
2000                let key_data = GenericArray::from_slice(data);
2001                Aes192CbcEnc::new(key_data, iv).encrypt_padded_vec_mut::<Pkcs7>(&plaintext)
2002            },
2003            Handle::Aes256(data) => {
2004                let key_data = GenericArray::from_slice(data);
2005                Aes256CbcEnc::new(key_data, iv).encrypt_padded_vec_mut::<Pkcs7>(&plaintext)
2006            },
2007            _ => return Err(Error::Data),
2008        };
2009
2010        create_buffer_source::<ArrayBufferU8>(cx, &ct, handle, can_gc)
2011            .expect("failed to create buffer source for exported key.");
2012
2013        Ok(ct)
2014    }
2015
2016    /// <https://w3c.github.io/webcrypto/#aes-cbc-operations>
2017    fn decrypt_aes_cbc(
2018        &self,
2019        params: &SubtleAesCbcParams,
2020        key: &CryptoKey,
2021        data: &[u8],
2022        cx: JSContext,
2023        handle: MutableHandleObject,
2024        can_gc: CanGc,
2025    ) -> Result<Vec<u8>, Error> {
2026        if params.iv.len() != 16 {
2027            return Err(Error::Operation);
2028        }
2029
2030        let mut ciphertext = Vec::from(data);
2031        let iv = GenericArray::from_slice(&params.iv);
2032
2033        let plaintext = match key.handle() {
2034            Handle::Aes128(data) => {
2035                let key_data = GenericArray::from_slice(data);
2036                Aes128CbcDec::new(key_data, iv)
2037                    .decrypt_padded_mut::<Pkcs7>(ciphertext.as_mut_slice())
2038                    .map_err(|_| Error::Operation)?
2039            },
2040            Handle::Aes192(data) => {
2041                let key_data = GenericArray::from_slice(data);
2042                Aes192CbcDec::new(key_data, iv)
2043                    .decrypt_padded_mut::<Pkcs7>(ciphertext.as_mut_slice())
2044                    .map_err(|_| Error::Operation)?
2045            },
2046            Handle::Aes256(data) => {
2047                let key_data = GenericArray::from_slice(data);
2048                Aes256CbcDec::new(key_data, iv)
2049                    .decrypt_padded_mut::<Pkcs7>(ciphertext.as_mut_slice())
2050                    .map_err(|_| Error::Operation)?
2051            },
2052            _ => return Err(Error::Data),
2053        };
2054
2055        create_buffer_source::<ArrayBufferU8>(cx, plaintext, handle, can_gc)
2056            .expect("failed to create buffer source for exported key.");
2057
2058        Ok(plaintext.to_vec())
2059    }
2060
2061    /// <https://w3c.github.io/webcrypto/#aes-ctr-operations>
2062    fn encrypt_decrypt_aes_ctr(
2063        &self,
2064        params: &SubtleAesCtrParams,
2065        key: &CryptoKey,
2066        data: &[u8],
2067        cx: JSContext,
2068        handle: MutableHandleObject,
2069        can_gc: CanGc,
2070    ) -> Result<Vec<u8>, Error> {
2071        if params.counter.len() != 16 || params.length == 0 || params.length > 128 {
2072            return Err(Error::Operation);
2073        }
2074
2075        let mut ciphertext = Vec::from(data);
2076        let counter = GenericArray::from_slice(&params.counter);
2077
2078        match key.handle() {
2079            Handle::Aes128(data) => {
2080                let key_data = GenericArray::from_slice(data);
2081                Aes128Ctr::new(key_data, counter).apply_keystream(&mut ciphertext)
2082            },
2083            Handle::Aes192(data) => {
2084                let key_data = GenericArray::from_slice(data);
2085                Aes192Ctr::new(key_data, counter).apply_keystream(&mut ciphertext)
2086            },
2087            Handle::Aes256(data) => {
2088                let key_data = GenericArray::from_slice(data);
2089                Aes256Ctr::new(key_data, counter).apply_keystream(&mut ciphertext)
2090            },
2091            _ => return Err(Error::Data),
2092        };
2093
2094        create_buffer_source::<ArrayBufferU8>(cx, &ciphertext, handle, can_gc)
2095            .expect("failed to create buffer source for exported key.");
2096
2097        Ok(ciphertext)
2098    }
2099
2100    /// <https://w3c.github.io/webcrypto/#aes-gcm-operations>
2101    fn encrypt_aes_gcm(
2102        &self,
2103        params: &SubtleAesGcmParams,
2104        key: &CryptoKey,
2105        plaintext: &[u8],
2106        cx: JSContext,
2107        handle: MutableHandleObject,
2108        can_gc: CanGc,
2109    ) -> Result<Vec<u8>, Error> {
2110        // Step 1. If plaintext has a length greater than 2^39 - 256 bytes, then throw an OperationError.
2111        if plaintext.len() as u64 > (2 << 39) - 256 {
2112            return Err(Error::Operation);
2113        }
2114
2115        // Step 2. If the iv member of normalizedAlgorithm has a length greater than 2^64 - 1 bytes,
2116        // then throw an OperationError.
2117        // NOTE: servo does not currently support 128-bit platforms, so this can never happen
2118
2119        // Step 3. If the additionalData member of normalizedAlgorithm is present and has a length greater than 2^64 - 1
2120        // bytes, then throw an OperationError.
2121        if params
2122            .additional_data
2123            .as_ref()
2124            .is_some_and(|data| data.len() > u64::MAX as usize)
2125        {
2126            return Err(Error::Operation);
2127        }
2128
2129        // Step 4.
2130        let tag_length = match params.tag_length {
2131            // If the tagLength member of normalizedAlgorithm is not present:
2132            None => {
2133                // Let tagLength be 128.
2134                128
2135            },
2136            // If the tagLength member of normalizedAlgorithm is one of 32, 64, 96, 104, 112, 120 or 128:
2137            Some(length) if matches!(length, 32 | 64 | 96 | 104 | 112 | 120 | 128) => {
2138                // Let tagLength be equal to the tagLength member of normalizedAlgorithm
2139                length
2140            },
2141            // Otherwise:
2142            _ => {
2143                // throw an OperationError.
2144                return Err(Error::Operation);
2145            },
2146        };
2147
2148        // Step 5. Let additionalData be the contents of the additionalData member of normalizedAlgorithm if present
2149        // or the empty octet string otherwise.
2150        let additional_data = params.additional_data.as_deref().unwrap_or_default();
2151
2152        // Step 6. Let C and T be the outputs that result from performing the Authenticated Encryption Function
2153        // described in Section 7.1 of [NIST-SP800-38D] using AES as the block cipher, the contents of the iv member
2154        // of normalizedAlgorithm as the IV input parameter, the contents of additionalData as the A input parameter,
2155        // tagLength as the t pre-requisite and the contents of plaintext as the input plaintext.
2156        let key_length = key.handle().as_bytes().len();
2157        let iv_length = params.iv.len();
2158        let mut ciphertext = plaintext.to_vec();
2159        let key_bytes = key.handle().as_bytes();
2160        let tag = match (key_length, iv_length) {
2161            (16, 12) => {
2162                let nonce = GenericArray::from_slice(&params.iv);
2163                <Aes128Gcm96Iv>::new_from_slice(key_bytes)
2164                    .expect("key length did not match")
2165                    .encrypt_in_place_detached(nonce, additional_data, &mut ciphertext)
2166            },
2167            (16, 16) => {
2168                let nonce = GenericArray::from_slice(&params.iv);
2169                <Aes128Gcm128Iv>::new_from_slice(key_bytes)
2170                    .expect("key length did not match")
2171                    .encrypt_in_place_detached(nonce, additional_data, &mut ciphertext)
2172            },
2173            (24, 12) => {
2174                let nonce = GenericArray::from_slice(&params.iv);
2175                <Aes192Gcm96Iv>::new_from_slice(key_bytes)
2176                    .expect("key length did not match")
2177                    .encrypt_in_place_detached(nonce, additional_data, &mut ciphertext)
2178            },
2179            (32, 12) => {
2180                let nonce = GenericArray::from_slice(&params.iv);
2181                <Aes256Gcm96Iv>::new_from_slice(key_bytes)
2182                    .expect("key length did not match")
2183                    .encrypt_in_place_detached(nonce, additional_data, &mut ciphertext)
2184            },
2185            (16, 32) => {
2186                let nonce = GenericArray::from_slice(&params.iv);
2187                <Aes128Gcm256Iv>::new_from_slice(key_bytes)
2188                    .expect("key length did not match")
2189                    .encrypt_in_place_detached(nonce, additional_data, &mut ciphertext)
2190            },
2191            (24, 32) => {
2192                let nonce = GenericArray::from_slice(&params.iv);
2193                <Aes192Gcm256Iv>::new_from_slice(key_bytes)
2194                    .expect("key length did not match")
2195                    .encrypt_in_place_detached(nonce, additional_data, &mut ciphertext)
2196            },
2197            (32, 32) => {
2198                let nonce = GenericArray::from_slice(&params.iv);
2199                <Aes256Gcm256Iv>::new_from_slice(key_bytes)
2200                    .expect("key length did not match")
2201                    .encrypt_in_place_detached(nonce, additional_data, &mut ciphertext)
2202            },
2203            _ => {
2204                log::warn!(
2205                    "Missing AES-GCM encryption implementation with {key_length}-byte key and {iv_length}-byte IV"
2206                );
2207                return Err(Error::NotSupported);
2208            },
2209        };
2210
2211        // Step 7. Let ciphertext be equal to C | T, where '|' denotes concatenation.
2212        ciphertext.extend_from_slice(&tag.unwrap()[..tag_length as usize / 8]);
2213
2214        // Step 8. Return the result of creating an ArrayBuffer containing ciphertext.
2215        create_buffer_source::<ArrayBufferU8>(cx, &ciphertext, handle, can_gc)
2216            .expect("failed to create buffer source for encrypted ciphertext");
2217
2218        Ok(ciphertext)
2219    }
2220
2221    /// <https://w3c.github.io/webcrypto/#aes-gcm-operations>
2222    fn decrypt_aes_gcm(
2223        &self,
2224        params: &SubtleAesGcmParams,
2225        key: &CryptoKey,
2226        ciphertext: &[u8],
2227        cx: JSContext,
2228        handle: MutableHandleObject,
2229        can_gc: CanGc,
2230    ) -> Result<Vec<u8>, Error> {
2231        // Step 1.
2232        // FIXME: aes_gcm uses a fixed tag length
2233        let tag_length = match params.tag_length {
2234            // If the tagLength member of normalizedAlgorithm is not present:
2235            None => {
2236                // Let tagLength be 128.
2237                128
2238            },
2239            // If the tagLength member of normalizedAlgorithm is one of 32, 64, 96, 104, 112, 120 or 128:
2240            Some(length) if matches!(length, 32 | 64 | 96 | 104 | 112 | 120 | 128) => {
2241                // Let tagLength be equal to the tagLength member of normalizedAlgorithm
2242                length as usize
2243            },
2244            // Otherwise:
2245            _ => {
2246                // throw an OperationError.
2247                return Err(Error::Operation);
2248            },
2249        };
2250
2251        // Step 2. If ciphertext has a length less than tagLength bits, then throw an OperationError.
2252        if ciphertext.len() < tag_length / 8 {
2253            return Err(Error::Operation);
2254        }
2255
2256        // Step 3. If the iv member of normalizedAlgorithm has a length greater than 2^64 - 1 bytes,
2257        // then throw an OperationError.
2258        // NOTE: servo does not currently support 128-bit platforms, so this can never happen
2259
2260        // Step 4. If the additionalData member of normalizedAlgorithm is present and has a length greater than 2^64 - 1
2261        // bytes, then throw an OperationError.
2262        // NOTE: servo does not currently support 128-bit platforms, so this can never happen
2263
2264        // Step 5. Let tag be the last tagLength bits of ciphertext.
2265        // Step 6. Let actualCiphertext be the result of removing the last tagLength bits from ciphertext.
2266        // NOTE: aes_gcm splits the ciphertext for us
2267
2268        // Step 7. Let additionalData be the contents of the additionalData member of normalizedAlgorithm if present or
2269        // the empty octet string otherwise.
2270        let additional_data = params.additional_data.as_deref().unwrap_or_default();
2271
2272        // Step 8.  Perform the Authenticated Decryption Function described in Section 7.2 of [NIST-SP800-38D] using AES
2273        // as the block cipher, the contents of the iv member of normalizedAlgorithm as the IV input parameter, the
2274        // contents of additionalData as the A input parameter, tagLength as the t pre-requisite, the contents of
2275        // actualCiphertext as the input ciphertext, C and the contents of tag as the authentication tag, T.
2276        let mut plaintext = ciphertext.to_vec();
2277        let key_length = key.handle().as_bytes().len();
2278        let iv_length = params.iv.len();
2279        let key_bytes = key.handle().as_bytes();
2280        let result = match (key_length, iv_length) {
2281            (16, 12) => {
2282                let nonce = GenericArray::from_slice(&params.iv);
2283                <Aes128Gcm96Iv>::new_from_slice(key_bytes)
2284                    .expect("key length did not match")
2285                    .decrypt_in_place(nonce, additional_data, &mut plaintext)
2286            },
2287            (16, 16) => {
2288                let nonce = GenericArray::from_slice(&params.iv);
2289                <Aes128Gcm128Iv>::new_from_slice(key_bytes)
2290                    .expect("key length did not match")
2291                    .decrypt_in_place(nonce, additional_data, &mut plaintext)
2292            },
2293            (24, 12) => {
2294                let nonce = GenericArray::from_slice(&params.iv);
2295                <Aes192Gcm96Iv>::new_from_slice(key_bytes)
2296                    .expect("key length did not match")
2297                    .decrypt_in_place(nonce, additional_data, &mut plaintext)
2298            },
2299            (32, 12) => {
2300                let nonce = GenericArray::from_slice(&params.iv);
2301                <Aes256Gcm96Iv>::new_from_slice(key_bytes)
2302                    .expect("key length did not match")
2303                    .decrypt_in_place(nonce, additional_data, &mut plaintext)
2304            },
2305            (16, 32) => {
2306                let nonce = GenericArray::from_slice(&params.iv);
2307                <Aes128Gcm256Iv>::new_from_slice(key_bytes)
2308                    .expect("key length did not match")
2309                    .decrypt_in_place(nonce, additional_data, &mut plaintext)
2310            },
2311            (24, 32) => {
2312                let nonce = GenericArray::from_slice(&params.iv);
2313                <Aes192Gcm256Iv>::new_from_slice(key_bytes)
2314                    .expect("key length did not match")
2315                    .decrypt_in_place(nonce, additional_data, &mut plaintext)
2316            },
2317            (32, 32) => {
2318                let nonce = GenericArray::from_slice(&params.iv);
2319                <Aes256Gcm256Iv>::new_from_slice(key_bytes)
2320                    .expect("key length did not match")
2321                    .decrypt_in_place(nonce, additional_data, &mut plaintext)
2322            },
2323            _ => {
2324                log::warn!(
2325                    "Missing AES-GCM decryption implementation with {key_length}-byte key and {iv_length}-byte IV"
2326                );
2327                return Err(Error::NotSupported);
2328            },
2329        };
2330
2331        // If the result of the algorithm is the indication of inauthenticity, "FAIL":
2332        if result.is_err() {
2333            // throw an OperationError
2334            return Err(Error::Operation);
2335        }
2336        // Otherwise:
2337        // Let plaintext be the output P of the Authenticated Decryption Function.
2338
2339        // Step 9. Return the result of creating an ArrayBuffer containing plaintext.
2340        create_buffer_source::<ArrayBufferU8>(cx, &plaintext, handle, can_gc)
2341            .expect("failed to create buffer source for decrypted plaintext");
2342
2343        Ok(plaintext)
2344    }
2345
2346    /// <https://w3c.github.io/webcrypto/#aes-cbc-operations>
2347    /// <https://w3c.github.io/webcrypto/#aes-ctr-operations>
2348    /// <https://w3c.github.io/webcrypto/#aes-kw-operations>
2349    #[allow(unsafe_code)]
2350    fn generate_key_aes(
2351        &self,
2352        usages: Vec<KeyUsage>,
2353        key_gen_params: &SubtleAesKeyGenParams,
2354        extractable: bool,
2355        can_gc: CanGc,
2356    ) -> Result<DomRoot<CryptoKey>, Error> {
2357        let mut rand = vec![0; key_gen_params.length as usize / 8];
2358        self.rng.borrow_mut().fill_bytes(&mut rand);
2359        let handle = match key_gen_params.length {
2360            128 => Handle::Aes128(rand),
2361            192 => Handle::Aes192(rand),
2362            256 => Handle::Aes256(rand),
2363            _ => return Err(Error::Operation),
2364        };
2365
2366        match key_gen_params.name.as_str() {
2367            ALG_AES_CBC | ALG_AES_CTR | ALG_AES_GCM => {
2368                if usages.iter().any(|usage| {
2369                    !matches!(
2370                        usage,
2371                        KeyUsage::Encrypt |
2372                            KeyUsage::Decrypt |
2373                            KeyUsage::WrapKey |
2374                            KeyUsage::UnwrapKey
2375                    )
2376                }) || usages.is_empty()
2377                {
2378                    return Err(Error::Syntax(None));
2379                }
2380            },
2381            ALG_AES_KW => {
2382                if usages
2383                    .iter()
2384                    .any(|usage| !matches!(usage, KeyUsage::WrapKey | KeyUsage::UnwrapKey)) ||
2385                    usages.is_empty()
2386                {
2387                    return Err(Error::Syntax(None));
2388                }
2389            },
2390            _ => return Err(Error::NotSupported),
2391        }
2392
2393        let name = match key_gen_params.name.as_str() {
2394            ALG_AES_CBC => DOMString::from(ALG_AES_CBC),
2395            ALG_AES_CTR => DOMString::from(ALG_AES_CTR),
2396            ALG_AES_KW => DOMString::from(ALG_AES_KW),
2397            ALG_AES_GCM => DOMString::from(ALG_AES_GCM),
2398            _ => return Err(Error::NotSupported),
2399        };
2400
2401        let cx = GlobalScope::get_cx();
2402        rooted!(in(*cx) let mut algorithm_object = unsafe {JS_NewObject(*cx, ptr::null()) });
2403        assert!(!algorithm_object.is_null());
2404
2405        AesKeyAlgorithm::from_name_and_size(
2406            name.clone(),
2407            key_gen_params.length,
2408            algorithm_object.handle_mut(),
2409            cx,
2410        );
2411
2412        let crypto_key = CryptoKey::new(
2413            &self.global(),
2414            KeyType::Secret,
2415            extractable,
2416            name,
2417            algorithm_object.handle(),
2418            usages,
2419            handle,
2420            can_gc,
2421        );
2422
2423        Ok(crypto_key)
2424    }
2425
2426    /// <https://w3c.github.io/webcrypto/#hmac-operations>
2427    #[allow(unsafe_code)]
2428    fn generate_key_hmac(
2429        &self,
2430        usages: Vec<KeyUsage>,
2431        params: &SubtleHmacKeyGenParams,
2432        extractable: bool,
2433        can_gc: CanGc,
2434    ) -> Result<DomRoot<CryptoKey>, Error> {
2435        // Step 1. If usages contains any entry which is not "sign" or "verify", then throw a SyntaxError.
2436        if usages
2437            .iter()
2438            .any(|usage| !matches!(usage, KeyUsage::Sign | KeyUsage::Verify))
2439        {
2440            return Err(Error::Syntax(None));
2441        }
2442
2443        // Step 2.
2444        let length = match params.length {
2445            // If the length member of normalizedAlgorithm is not present:
2446            None => {
2447                // Let length be the block size in bits of the hash function identified by the
2448                // hash member of normalizedAlgorithm.
2449                params.hash.block_size_in_bits() as u32
2450            },
2451            // Otherwise, if the length member of normalizedAlgorithm is non-zero:
2452            Some(length) if length != 0 => {
2453                // Let length be equal to the length member of normalizedAlgorithm.
2454                length
2455            },
2456            // Otherwise:
2457            _ => {
2458                // throw an OperationError.
2459                return Err(Error::Operation);
2460            },
2461        };
2462
2463        // Step 3. Generate a key of length length bits.
2464        let mut key_data = vec![0; length as usize];
2465        self.rng.borrow_mut().fill_bytes(&mut key_data);
2466
2467        // Step 4. If the key generation step fails, then throw an OperationError.
2468        // NOTE: Our key generation is infallible.
2469
2470        // Step 5. Let key be a new CryptoKey object representing the generated key.
2471        // Step 6. Let algorithm be a new HmacKeyAlgorithm.
2472        // Step 7. Set the name attribute of algorithm to "HMAC".
2473        // Step 8. Let hash be a new KeyAlgorithm.
2474        // Step 9. Set the name attribute of hash to equal the name member of the hash member of normalizedAlgorithm.
2475        // Step 10. Set the hash attribute of algorithm to hash.
2476        // Step 11. Set the [[type]] internal slot of key to "secret".
2477        // Step 12. Set the [[algorithm]] internal slot of key to algorithm.
2478        // Step 13. Set the [[extractable]] internal slot of key to be extractable.
2479        // Step 14. Set the [[usages]] internal slot of key to be usages.
2480        let name = DOMString::from(ALG_HMAC);
2481        let cx = GlobalScope::get_cx();
2482        rooted!(in(*cx) let mut algorithm_object = unsafe {JS_NewObject(*cx, ptr::null()) });
2483        assert!(!algorithm_object.is_null());
2484        HmacKeyAlgorithm::from_length_and_hash(
2485            length,
2486            params.hash,
2487            algorithm_object.handle_mut(),
2488            cx,
2489        );
2490
2491        let key = CryptoKey::new(
2492            &self.global(),
2493            KeyType::Secret,
2494            extractable,
2495            name,
2496            algorithm_object.handle(),
2497            usages,
2498            Handle::Hmac(key_data),
2499            can_gc,
2500        );
2501
2502        // Step 15. Return key.
2503        Ok(key)
2504    }
2505
2506    /// <https://w3c.github.io/webcrypto/#aes-ctr-operations-import-key>
2507    /// <https://w3c.github.io/webcrypto/#aes-cbc-operations-import-key>
2508    /// <https://w3c.github.io/webcrypto/#aes-gcm-operations-import-key>
2509    /// <https://w3c.github.io/webcrypto/#aes-kw-operations-import-key>
2510    #[allow(unsafe_code)]
2511    fn import_key_aes(
2512        &self,
2513        format: KeyFormat,
2514        key_data: &[u8],
2515        extractable: bool,
2516        usages: Vec<KeyUsage>,
2517        alg_name: &str,
2518        can_gc: CanGc,
2519    ) -> Result<DomRoot<CryptoKey>, Error> {
2520        // Step 1. If usages contains an entry which is not one of "encrypt", "decrypt", "wrapKey"
2521        // or "unwrapKey", then throw a SyntaxError.
2522        if usages.iter().any(|usage| {
2523            !matches!(
2524                usage,
2525                KeyUsage::Encrypt | KeyUsage::Decrypt | KeyUsage::WrapKey | KeyUsage::UnwrapKey
2526            )
2527        }) || usages.is_empty()
2528        {
2529            return Err(Error::Syntax(None));
2530        }
2531
2532        // Step 2.
2533        let data;
2534        match format {
2535            // If format is "raw":
2536            KeyFormat::Raw => {
2537                // Step 2.1. Let data be keyData.
2538                data = key_data.to_vec();
2539
2540                // Step 2.2. If the length in bits of data is not 128, 192 or 256 then throw a DataError.
2541                if !matches!(data.len() * 8, 128 | 192 | 256) {
2542                    return Err(Error::Data);
2543                }
2544            },
2545            // If format is "jwk":
2546            KeyFormat::Jwk => {
2547                // Step 2.1. If keyData is a JsonWebKey dictionary: Let jwk equal keyData.
2548                // Otherwise: Throw a DataError.
2549                // NOTE: Deserialize keyData to JsonWebKey dictionary by calling JsonWebKey::parse
2550                let jwk = JsonWebKey::parse(GlobalScope::get_cx(), key_data)?;
2551
2552                // Step 2.2. If the kty field of jwk is not "oct", then throw a DataError.
2553                if jwk.kty.as_ref().is_none_or(|kty| kty != "oct") {
2554                    return Err(Error::Data);
2555                }
2556
2557                // Step 2.3. If jwk does not meet the requirements of Section 6.4 of JSON Web
2558                // Algorithms [JWA], then throw a DataError.
2559                // NOTE: Done by Step 2.4 and 2.5.
2560
2561                // Step 2.4. Let data be the byte sequence obtained by decoding the k field of jwk.
2562                data = base64::engine::general_purpose::STANDARD_NO_PAD
2563                    .decode(jwk.k.as_ref().ok_or(Error::Data)?.as_bytes())
2564                    .map_err(|_| Error::Data)?;
2565
2566                // NOTE: This function is shared by AES-CBC, AES-CTR, AES-GCM and AES-KW.
2567                // Different static texts are used in different AES types, in the following step.
2568                let alg_matching = match alg_name {
2569                    ALG_AES_CBC => ["A128CBC", "A192CBC", "A256CBC"],
2570                    ALG_AES_CTR => ["A128CTR", "A192CTR", "A256CTR"],
2571                    ALG_AES_GCM => ["A128GCM", "A192GCM", "A256GCM"],
2572                    ALG_AES_KW => ["A128KW", "A192KW", "A256KW"],
2573                    _ => unreachable!(),
2574                };
2575
2576                // Step 2.5.
2577                match data.len() * 8 {
2578                    // If the length in bits of data is 128:
2579                    128 => {
2580                        // If the alg field of jwk is present, and is not "A128CBC", then throw a DataError.
2581                        // If the alg field of jwk is present, and is not "A128CTR", then throw a DataError.
2582                        // If the alg field of jwk is present, and is not "A128GCM", then throw a DataError.
2583                        // If the alg field of jwk is present, and is not "A128KW", then throw a DataError.
2584                        // NOTE: Only perform the step of the corresponding AES type.
2585                        if jwk.alg.as_ref().is_some_and(|alg| alg != alg_matching[0]) {
2586                            return Err(Error::Data);
2587                        }
2588                    },
2589                    // If the length in bits of data is 192:
2590                    192 => {
2591                        // If the alg field of jwk is present, and is not "A192CBC", then throw a DataError.
2592                        // If the alg field of jwk is present, and is not "A192CTR", then throw a DataError.
2593                        // If the alg field of jwk is present, and is not "A192GCM", then throw a DataError.
2594                        // If the alg field of jwk is present, and is not "A192KW", then throw a DataError.
2595                        // NOTE: Only perform the step of the corresponding AES type.
2596                        if jwk.alg.as_ref().is_some_and(|alg| alg != alg_matching[1]) {
2597                            return Err(Error::Data);
2598                        }
2599                    },
2600                    // If the length in bits of data is 256:
2601                    256 => {
2602                        // If the alg field of jwk is present, and is not "A256CBC", then throw a DataError.
2603                        // If the alg field of jwk is present, and is not "A256CTR", then throw a DataError.
2604                        // If the alg field of jwk is present, and is not "A256GCM", then throw a DataError.
2605                        // If the alg field of jwk is present, and is not "A256KW", then throw a DataError.
2606                        // NOTE: Only perform the step of the corresponding AES type.
2607                        if jwk.alg.as_ref().is_some_and(|alg| alg != alg_matching[2]) {
2608                            return Err(Error::Data);
2609                        }
2610                    },
2611                    // Otherwise:
2612                    _ => {
2613                        // throw a DataError.
2614                        return Err(Error::Data);
2615                    },
2616                }
2617
2618                // Step 2.6. If usages is non-empty and the use field of jwk is present and is not
2619                // "enc", then throw a DataError.
2620                if !usages.is_empty() && jwk.use_.as_ref().is_some_and(|use_| use_ != "enc") {
2621                    return Err(Error::Data);
2622                }
2623
2624                // Step 2.7. If the key_ops field of jwk is present, and is invalid according to
2625                // the requirements of JSON Web Key [JWK] or does not contain all of the specified
2626                // usages values, then throw a DataError.
2627                jwk.check_key_ops(&usages)?;
2628
2629                // Step 2.8. If the ext field of jwk is present and has the value false and
2630                // extractable is true, then throw a DataError.
2631                if jwk.ext.is_some_and(|ext| !ext) && extractable {
2632                    return Err(Error::Data);
2633                }
2634            },
2635            // Otherwise:
2636            _ => {
2637                // throw a NotSupportedError
2638                return Err(Error::NotSupported);
2639            },
2640        };
2641
2642        // Step 5. Let algorithm be a new AesKeyAlgorithm.
2643        // Step 6. Set the name attribute of algorithm to "AES-CBC".
2644        // Step 7. Set the length attribute of algorithm to the length, in bits, of data.
2645        let name = DOMString::from(alg_name.to_string());
2646        let cx = GlobalScope::get_cx();
2647        rooted!(in(*cx) let mut algorithm_object = unsafe { JS_NewObject(*cx, ptr::null()) });
2648        assert!(!algorithm_object.is_null());
2649        AesKeyAlgorithm::from_name_and_size(
2650            name.clone(),
2651            (data.len() * 8) as u16,
2652            algorithm_object.handle_mut(),
2653            cx,
2654        );
2655
2656        // Step 3. Let key be a new CryptoKey object representing an AES key with value data.
2657        // Step 4. Set the [[type]] internal slot of key to "secret".
2658        // Step 8. Set the [[algorithm]] internal slot of key to algorithm.
2659        let handle = match data.len() * 8 {
2660            128 => Handle::Aes128(data.to_vec()),
2661            192 => Handle::Aes192(data.to_vec()),
2662            256 => Handle::Aes256(data.to_vec()),
2663            _ => {
2664                return Err(Error::Data);
2665            },
2666        };
2667        let key = CryptoKey::new(
2668            &self.global(),
2669            KeyType::Secret,
2670            extractable,
2671            name,
2672            algorithm_object.handle(),
2673            usages,
2674            handle,
2675            can_gc,
2676        );
2677
2678        // Return key.
2679        Ok(key)
2680    }
2681
2682    /// <https://w3c.github.io/webcrypto/#aes-ctr-operations-export-key>
2683    /// <https://w3c.github.io/webcrypto/#aes-cbc-operations-export-key>
2684    /// <https://w3c.github.io/webcrypto/#aes-gcm-operations-export-key>
2685    /// <https://w3c.github.io/webcrypto/#aes-kw-operations-export-key>
2686    fn export_key_aes(&self, format: KeyFormat, key: &CryptoKey) -> Result<ExportedKey, Error> {
2687        // Step 1. If the underlying cryptographic key material represented by the [[handle]]
2688        // internal slot of key cannot be accessed, then throw an OperationError.
2689        // NOTE: key.handle() guarantees access.
2690
2691        // Step 2.
2692        let result;
2693        match format {
2694            // If format is "raw":
2695            KeyFormat::Raw => match key.handle() {
2696                // Step 2.1. Let data be a byte sequence containing the raw octets of the key
2697                // represented by the [[handle]] internal slot of key.
2698                // Step 2.2. Let result be data.
2699                Handle::Aes128(key_data) => {
2700                    result = ExportedKey::Raw(key_data.clone());
2701                },
2702                Handle::Aes192(key_data) => {
2703                    result = ExportedKey::Raw(key_data.clone());
2704                },
2705                Handle::Aes256(key_data) => {
2706                    result = ExportedKey::Raw(key_data.clone());
2707                },
2708                _ => unreachable!(),
2709            },
2710            // If format is "jwk":
2711            KeyFormat::Jwk => {
2712                // Step 2.3. Set the k attribute of jwk to be a string containing the raw octets of
2713                // the key represented by the [[handle]] internal slot of key, encoded according to
2714                // Section 6.4 of JSON Web Algorithms [JWA].
2715                let k = match key.handle() {
2716                    Handle::Aes128(key) => {
2717                        base64::engine::general_purpose::STANDARD_NO_PAD.encode(key)
2718                    },
2719                    Handle::Aes192(key) => {
2720                        base64::engine::general_purpose::STANDARD_NO_PAD.encode(key)
2721                    },
2722                    Handle::Aes256(key) => {
2723                        base64::engine::general_purpose::STANDARD_NO_PAD.encode(key)
2724                    },
2725                    _ => unreachable!(),
2726                };
2727
2728                // Step 2.4.
2729                // If the length attribute of key is 128: Set the alg attribute of jwk to the string "A128CTR".
2730                // If the length attribute of key is 192: Set the alg attribute of jwk to the string "A192CTR".
2731                // If the length attribute of key is 256: Set the alg attribute of jwk to the string "A256CTR".
2732                //
2733                // If the length attribute of key is 128: Set the alg attribute of jwk to the string "A128CTR".
2734                // If the length attribute of key is 192: Set the alg attribute of jwk to the string "A192CTR".
2735                // If the length attribute of key is 256: Set the alg attribute of jwk to the string "A256CTR".
2736                //
2737                // If the length attribute of key is 128: Set the alg attribute of jwk to the string "A128CTR".
2738                // If the length attribute of key is 192: Set the alg attribute of jwk to the string "A192CTR".
2739                // If the length attribute of key is 256: Set the alg attribute of jwk to the string "A256CTR".
2740                //
2741                // If the length attribute of key is 128: Set the alg attribute of jwk to the string "A128CTR".
2742                // If the length attribute of key is 192: Set the alg attribute of jwk to the string "A192CTR".
2743                // If the length attribute of key is 256: Set the alg attribute of jwk to the string "A256CTR".
2744                //
2745                // NOTE: Check key length via key.handle()
2746                let alg = match (key.handle(), key.algorithm().as_str()) {
2747                    (Handle::Aes128(_), ALG_AES_CTR) => "A128CTR",
2748                    (Handle::Aes192(_), ALG_AES_CTR) => "A192CTR",
2749                    (Handle::Aes256(_), ALG_AES_CTR) => "A256CTR",
2750                    (Handle::Aes128(_), ALG_AES_CBC) => "A128CBC",
2751                    (Handle::Aes192(_), ALG_AES_CBC) => "A192CBC",
2752                    (Handle::Aes256(_), ALG_AES_CBC) => "A256CBC",
2753                    (Handle::Aes128(_), ALG_AES_GCM) => "A128GCM",
2754                    (Handle::Aes192(_), ALG_AES_GCM) => "A192GCM",
2755                    (Handle::Aes256(_), ALG_AES_GCM) => "A256GCM",
2756                    (Handle::Aes128(_), ALG_AES_KW) => "A128KW",
2757                    (Handle::Aes192(_), ALG_AES_KW) => "A192KW",
2758                    (Handle::Aes256(_), ALG_AES_KW) => "A256KW",
2759                    _ => unreachable!(),
2760                };
2761
2762                // Step 2.5. Set the key_ops attribute of jwk to equal the [[usages]] internal slot of key.
2763                let key_ops = key
2764                    .usages()
2765                    .iter()
2766                    .map(|usage| DOMString::from(usage.as_str()))
2767                    .collect::<Vec<DOMString>>();
2768
2769                // Step 2.1. Let jwk be a new JsonWebKey dictionary.
2770                // Step 2.2. Set the kty attribute of jwk to the string "oct".
2771                // Step 2.6. Set the ext attribute of jwk to equal the [[extractable]] internal slot of key.
2772                let jwk = JsonWebKey {
2773                    kty: Some(DOMString::from("oct")),
2774                    k: Some(DOMString::from(k)),
2775                    alg: Some(DOMString::from(alg)),
2776                    key_ops: Some(key_ops),
2777                    ext: Some(key.Extractable()),
2778                    ..Default::default()
2779                };
2780
2781                // Step 2.7. Let result be jwk.
2782                result = ExportedKey::Jwk(Box::new(jwk));
2783            },
2784            // Otherwise:
2785            _ => {
2786                // throw a NotSupportedError.
2787                return Err(Error::NotSupported);
2788            },
2789        };
2790
2791        // Step 3. Return result.
2792        Ok(result)
2793    }
2794
2795    /// <https://w3c.github.io/webcrypto/#hkdf-operations>
2796    #[allow(unsafe_code)]
2797    fn import_key_hkdf(
2798        &self,
2799        format: KeyFormat,
2800        data: &[u8],
2801        extractable: bool,
2802        usages: Vec<KeyUsage>,
2803        can_gc: CanGc,
2804    ) -> Result<DomRoot<CryptoKey>, Error> {
2805        // Step 1. Let keyData be the key data to be imported.
2806        // Step 2.  If format is "raw":
2807        if format == KeyFormat::Raw {
2808            // Step 1. If usages contains a value that is not "deriveKey" or "deriveBits", then throw a SyntaxError.
2809            if usages
2810                .iter()
2811                .any(|usage| !matches!(usage, KeyUsage::DeriveKey | KeyUsage::DeriveBits)) ||
2812                usages.is_empty()
2813            {
2814                return Err(Error::Syntax(None));
2815            }
2816
2817            // Step 2. If extractable is not false, then throw a SyntaxError.
2818            if extractable {
2819                return Err(Error::Syntax(None));
2820            }
2821
2822            // Step 3. Let key be a new CryptoKey representing the key data provided in keyData.
2823            // Step 4. Set the [[type]] internal slot of key to "secret".
2824            // Step 5.  Let algorithm be a new KeyAlgorithm object.
2825            // Step 6. Set the name attribute of algorithm to "HKDF".
2826            // Step 7. Set the [[algorithm]] internal slot of key to algorithm.
2827            let name = DOMString::from(ALG_HKDF);
2828            let cx = GlobalScope::get_cx();
2829            rooted!(in(*cx) let mut algorithm_object = unsafe {JS_NewObject(*cx, ptr::null()) });
2830            assert!(!algorithm_object.is_null());
2831            KeyAlgorithm::from_name(name.clone(), algorithm_object.handle_mut(), cx);
2832
2833            let key = CryptoKey::new(
2834                &self.global(),
2835                KeyType::Secret,
2836                extractable,
2837                name,
2838                algorithm_object.handle(),
2839                usages,
2840                Handle::Hkdf(data.to_vec()),
2841                can_gc,
2842            );
2843
2844            // Step 8. Return key.
2845            Ok(key)
2846        } else {
2847            // throw a NotSupportedError.
2848            Err(Error::NotSupported)
2849        }
2850    }
2851
2852    /// <https://w3c.github.io/webcrypto/#hmac-operations-import-key>
2853    #[allow(unsafe_code)]
2854    fn import_key_hmac(
2855        &self,
2856        normalized_algorithm: &SubtleHmacImportParams,
2857        format: KeyFormat,
2858        key_data: &[u8],
2859        extractable: bool,
2860        usages: Vec<KeyUsage>,
2861        can_gc: CanGc,
2862    ) -> Result<DomRoot<CryptoKey>, Error> {
2863        // Step 1. Let keyData be the key data to be imported.
2864        // Step 2. If usages contains an entry which is not "sign" or "verify", then throw a SyntaxError.
2865        // Note: This is not explicitly spec'ed, but also throw a SyntaxError if usages is empty
2866        if usages
2867            .iter()
2868            .any(|usage| !matches!(usage, KeyUsage::Sign | KeyUsage::Verify)) ||
2869            usages.is_empty()
2870        {
2871            return Err(Error::Syntax(None));
2872        }
2873
2874        // Step 3. Let hash be a new KeyAlgorithm.
2875        let hash;
2876
2877        // Step 4.
2878        let data;
2879        match format {
2880            // If format is "raw":
2881            KeyFormat::Raw => {
2882                // Step 4.1. Let data be keyData.
2883                data = key_data.to_vec();
2884
2885                // Step 4.2. Set hash to equal the hash member of normalizedAlgorithm.
2886                hash = normalized_algorithm.hash;
2887            },
2888            // If format is "jwk":
2889            KeyFormat::Jwk => {
2890                // Step 2.1. If keyData is a JsonWebKey dictionary: Let jwk equal keyData.
2891                // Otherwise: Throw a DataError.
2892                // NOTE: Deserialize keyData to JsonWebKey dictionary by running JsonWebKey::parse
2893                let jwk = JsonWebKey::parse(GlobalScope::get_cx(), key_data)?;
2894
2895                // Step 2.2. If the kty field of jwk is not "oct", then throw a DataError.
2896                if jwk.kty.as_ref().is_none_or(|kty| kty != "oct") {
2897                    return Err(Error::Data);
2898                }
2899
2900                // Step 2.3. If jwk does not meet the requirements of Section 6.4 of JSON Web
2901                // Algorithms [JWA], then throw a DataError.
2902                // NOTE: Done by Step 2.4 and 2.6.
2903
2904                // Step 2.4. Let data be the byte sequence obtained by decoding the k field of jwk.
2905                data = base64::engine::general_purpose::STANDARD_NO_PAD
2906                    .decode(jwk.k.as_ref().ok_or(Error::Data)?.as_bytes())
2907                    .map_err(|_| Error::Data)?;
2908
2909                // Step 2.5. Set the hash to equal the hash member of normalizedAlgorithm.
2910                hash = normalized_algorithm.hash;
2911
2912                // Step 2.6.
2913                match hash.name().to_string().as_str() {
2914                    // If the name attribute of hash is "SHA-1":
2915                    ALG_SHA1 => {
2916                        // If the alg field of jwk is present and is not "HS1", then throw a DataError.
2917                        if jwk.alg.as_ref().is_some_and(|alg| alg != "HS1") {
2918                            return Err(Error::Data);
2919                        }
2920                    },
2921                    // If the name attribute of hash is "SHA-256":
2922                    ALG_SHA256 => {
2923                        // If the alg field of jwk is present and is not "HS256", then throw a DataError.
2924                        if jwk.alg.as_ref().is_some_and(|alg| alg != "HS256") {
2925                            return Err(Error::Data);
2926                        }
2927                    },
2928                    // If the name attribute of hash is "SHA-384":
2929                    ALG_SHA384 => {
2930                        // If the alg field of jwk is present and is not "HS384", then throw a DataError.
2931                        if jwk.alg.as_ref().is_some_and(|alg| alg != "HS384") {
2932                            return Err(Error::Data);
2933                        }
2934                    },
2935                    // If the name attribute of hash is "SHA-512":
2936                    ALG_SHA512 => {
2937                        // If the alg field of jwk is present and is not "HS512", then throw a DataError.
2938                        if jwk.alg.as_ref().is_some_and(|alg| alg != "HS512") {
2939                            return Err(Error::Data);
2940                        }
2941                    },
2942                    // Otherwise,
2943                    _name => {
2944                        // if the name attribute of hash is defined in another applicable specification:
2945                        // Perform any key import steps defined by other applicable specifications,
2946                        // passing format, jwk and hash and obtaining hash
2947                        // NOTE: Currently not support applicable specification.
2948                        return Err(Error::NotSupported);
2949                    },
2950                }
2951
2952                // Step 2.7. If usages is non-empty and the use field of jwk is present and is not
2953                // "sig", then throw a DataError.
2954                if !usages.is_empty() && jwk.use_.as_ref().is_some_and(|use_| use_ != "sig") {
2955                    return Err(Error::Data);
2956                }
2957
2958                // Step 2.8. If the key_ops field of jwk is present, and is invalid according to
2959                // the requirements of JSON Web Key [JWK] or does not contain all of the specified
2960                // usages values, then throw a DataError.
2961                jwk.check_key_ops(&usages)?;
2962
2963                // Step 2.9. If the ext field of jwk is present and has the value false and
2964                // extractable is true, then throw a DataError.
2965                if jwk.ext.is_some_and(|ext| !ext) && extractable {
2966                    return Err(Error::Data);
2967                }
2968            },
2969            // Otherwise:
2970            _ => {
2971                // throw a NotSupportedError.
2972                return Err(Error::NotSupported);
2973            },
2974        }
2975
2976        // Step 5. Let length be equivalent to the length, in octets, of data, multiplied by 8.
2977        let mut length = data.len() as u32 * 8;
2978
2979        // Step 6. If length is zero then throw a DataError.
2980        if length == 0 {
2981            return Err(Error::Data);
2982        }
2983
2984        // Step 7. If the length member of normalizedAlgorithm is present:
2985        if let Some(given_length) = normalized_algorithm.length {
2986            //  If the length member of normalizedAlgorithm is greater than length:
2987            if given_length > length {
2988                // throw a DataError.
2989                return Err(Error::Data);
2990            }
2991            // Otherwise:
2992            else {
2993                // Set length equal to the length member of normalizedAlgorithm.
2994                length = given_length;
2995            }
2996        }
2997
2998        // Step 8. Let key be a new CryptoKey object representing an HMAC key with the first length bits of data.
2999        // Step 9. Set the [[type]] internal slot of key to "secret".
3000        // Step 10. Let algorithm be a new HmacKeyAlgorithm.
3001        // Step 11. Set the name attribute of algorithm to "HMAC".
3002        // Step 12. Set the length attribute of algorithm to length.
3003        // Step 13. Set the hash attribute of algorithm to hash.
3004        // Step 14. Set the [[algorithm]] internal slot of key to algorithm.
3005        let truncated_data = data[..length as usize / 8].to_vec();
3006        let name = DOMString::from(ALG_HMAC);
3007        let cx = GlobalScope::get_cx();
3008        rooted!(in(*cx) let mut algorithm_object = unsafe { JS_NewObject(*cx, ptr::null()) });
3009        assert!(!algorithm_object.is_null());
3010        HmacKeyAlgorithm::from_length_and_hash(length, hash, algorithm_object.handle_mut(), cx);
3011
3012        let key = CryptoKey::new(
3013            &self.global(),
3014            KeyType::Secret,
3015            extractable,
3016            name,
3017            algorithm_object.handle(),
3018            usages,
3019            Handle::Hmac(truncated_data),
3020            can_gc,
3021        );
3022
3023        // Step 15. Return key.
3024        Ok(key)
3025    }
3026
3027    /// <https://w3c.github.io/webcrypto/#hmac-operations-export-key>
3028    fn export_key_hmac(&self, format: KeyFormat, key: &CryptoKey) -> Result<ExportedKey, Error> {
3029        match format {
3030            KeyFormat::Raw => match key.handle() {
3031                Handle::Hmac(key_data) => Ok(ExportedKey::Raw(key_data.as_slice().to_vec())),
3032                _ => Err(Error::Operation),
3033            },
3034            // FIXME: Implement JWK export for HMAC keys
3035            _ => Err(Error::NotSupported),
3036        }
3037    }
3038
3039    /// <https://w3c.github.io/webcrypto/#aes-kw-operations>
3040    fn wrap_key_aes_kw(
3041        &self,
3042        wrapping_key: &CryptoKey,
3043        bytes: &[u8],
3044        cx: JSContext,
3045        handle: MutableHandleObject,
3046        can_gc: CanGc,
3047    ) -> Result<Vec<u8>, Error> {
3048        // Step 1. If plaintext is not a multiple of 64 bits in length, then throw an OperationError.
3049        if bytes.len() % 8 != 0 {
3050            return Err(Error::Operation);
3051        }
3052
3053        // Step 2. Let ciphertext be the result of performing the Key Wrap operation described in Section 2.2.1
3054        //         of [RFC3394] with plaintext as the plaintext to be wrapped and using the default Initial Value
3055        //         defined in Section 2.2.3.1 of the same document.
3056        let wrapped_key = match wrapping_key.handle() {
3057            Handle::Aes128(key_data) => {
3058                let key_array = GenericArray::from_slice(key_data.as_slice());
3059                let kek = KekAes128::new(key_array);
3060                match kek.wrap_vec(bytes) {
3061                    Ok(key) => key,
3062                    Err(_) => return Err(Error::Operation),
3063                }
3064            },
3065            Handle::Aes192(key_data) => {
3066                let key_array = GenericArray::from_slice(key_data.as_slice());
3067                let kek = KekAes192::new(key_array);
3068                match kek.wrap_vec(bytes) {
3069                    Ok(key) => key,
3070                    Err(_) => return Err(Error::Operation),
3071                }
3072            },
3073            Handle::Aes256(key_data) => {
3074                let key_array = GenericArray::from_slice(key_data.as_slice());
3075                let kek = KekAes256::new(key_array);
3076                match kek.wrap_vec(bytes) {
3077                    Ok(key) => key,
3078                    Err(_) => return Err(Error::Operation),
3079                }
3080            },
3081            _ => return Err(Error::Operation),
3082        };
3083
3084        create_buffer_source::<ArrayBufferU8>(cx, &wrapped_key, handle, can_gc)
3085            .expect("failed to create buffer source for wrapped key.");
3086
3087        // 3. Return ciphertext.
3088        Ok(wrapped_key)
3089    }
3090
3091    /// <https://w3c.github.io/webcrypto/#aes-kw-operations>
3092    fn unwrap_key_aes_kw(
3093        &self,
3094        wrapping_key: &CryptoKey,
3095        bytes: &[u8],
3096        cx: JSContext,
3097        handle: MutableHandleObject,
3098        can_gc: CanGc,
3099    ) -> Result<Vec<u8>, Error> {
3100        // Step 1. Let plaintext be the result of performing the Key Unwrap operation described in Section 2.2.2
3101        //         of [RFC3394] with ciphertext as the input ciphertext and using the default Initial Value defined
3102        //         in Section 2.2.3.1 of the same document.
3103        // Step 2. If the Key Unwrap operation returns an error, then throw an OperationError.
3104        let unwrapped_key = match wrapping_key.handle() {
3105            Handle::Aes128(key_data) => {
3106                let key_array = GenericArray::from_slice(key_data.as_slice());
3107                let kek = KekAes128::new(key_array);
3108                match kek.unwrap_vec(bytes) {
3109                    Ok(key) => key,
3110                    Err(_) => return Err(Error::Operation),
3111                }
3112            },
3113            Handle::Aes192(key_data) => {
3114                let key_array = GenericArray::from_slice(key_data.as_slice());
3115                let kek = KekAes192::new(key_array);
3116                match kek.unwrap_vec(bytes) {
3117                    Ok(key) => key,
3118                    Err(_) => return Err(Error::Operation),
3119                }
3120            },
3121            Handle::Aes256(key_data) => {
3122                let key_array = GenericArray::from_slice(key_data.as_slice());
3123                let kek = KekAes256::new(key_array);
3124                match kek.unwrap_vec(bytes) {
3125                    Ok(key) => key,
3126                    Err(_) => return Err(Error::Operation),
3127                }
3128            },
3129            _ => return Err(Error::Operation),
3130        };
3131
3132        create_buffer_source::<ArrayBufferU8>(cx, &unwrapped_key, handle, can_gc)
3133            .expect("failed to create buffer source for unwrapped key.");
3134
3135        // 3. Return plaintext.
3136        Ok(unwrapped_key)
3137    }
3138
3139    /// <https://w3c.github.io/webcrypto/#pbkdf2-operations>
3140    #[allow(unsafe_code)]
3141    fn import_key_pbkdf2(
3142        &self,
3143        format: KeyFormat,
3144        data: &[u8],
3145        extractable: bool,
3146        usages: Vec<KeyUsage>,
3147        can_gc: CanGc,
3148    ) -> Result<DomRoot<CryptoKey>, Error> {
3149        // Step 1. If format is not "raw", throw a NotSupportedError
3150        if format != KeyFormat::Raw {
3151            return Err(Error::NotSupported);
3152        }
3153
3154        // Step 2. If usages contains a value that is not "deriveKey" or "deriveBits", then throw a SyntaxError.
3155        if usages
3156            .iter()
3157            .any(|usage| !matches!(usage, KeyUsage::DeriveKey | KeyUsage::DeriveBits)) ||
3158            usages.is_empty()
3159        {
3160            return Err(Error::Syntax(None));
3161        }
3162
3163        // Step 3. If extractable is not false, then throw a SyntaxError.
3164        if extractable {
3165            return Err(Error::Syntax(None));
3166        }
3167
3168        // Step 4. Let key be a new CryptoKey representing keyData.
3169        // Step 5. Set the [[type]] internal slot of key to "secret".
3170        // Step 6. Let algorithm be a new KeyAlgorithm object.
3171        // Step 7. Set the name attribute of algorithm to "PBKDF2".
3172        // Step 8. Set the [[algorithm]] internal slot of key to algorithm.
3173        let name = DOMString::from(ALG_PBKDF2);
3174        let cx = GlobalScope::get_cx();
3175        rooted!(in(*cx) let mut algorithm_object = unsafe {JS_NewObject(*cx, ptr::null()) });
3176        assert!(!algorithm_object.is_null());
3177        KeyAlgorithm::from_name(name.clone(), algorithm_object.handle_mut(), cx);
3178
3179        let key = CryptoKey::new(
3180            &self.global(),
3181            KeyType::Secret,
3182            extractable,
3183            name,
3184            algorithm_object.handle(),
3185            usages,
3186            Handle::Pbkdf2(data.to_vec()),
3187            can_gc,
3188        );
3189
3190        // Step 9. Return key.
3191        Ok(key)
3192    }
3193}
3194
3195pub(crate) enum ExportedKey {
3196    Raw(Vec<u8>),
3197    Jwk(Box<JsonWebKey>),
3198}
3199
3200trait AlgorithmFromName {
3201    fn from_name(name: DOMString, out: MutableHandleObject, cx: JSContext);
3202}
3203
3204impl AlgorithmFromName for KeyAlgorithm {
3205    /// Fill the object referenced by `out` with an [KeyAlgorithm]
3206    /// of the specified name and size.
3207    #[allow(unsafe_code)]
3208    fn from_name(name: DOMString, out: MutableHandleObject, cx: JSContext) {
3209        let key_algorithm = Self { name };
3210
3211        unsafe {
3212            key_algorithm.to_jsobject(*cx, out);
3213        }
3214    }
3215}
3216
3217trait AlgorithmFromLengthAndHash {
3218    fn from_length_and_hash(
3219        length: u32,
3220        hash: DigestAlgorithm,
3221        out: MutableHandleObject,
3222        cx: JSContext,
3223    );
3224}
3225
3226impl AlgorithmFromLengthAndHash for HmacKeyAlgorithm {
3227    #[allow(unsafe_code)]
3228    fn from_length_and_hash(
3229        length: u32,
3230        hash: DigestAlgorithm,
3231        out: MutableHandleObject,
3232        cx: JSContext,
3233    ) {
3234        let hmac_key_algorithm = Self {
3235            parent: KeyAlgorithm {
3236                name: ALG_HMAC.into(),
3237            },
3238            length,
3239            hash: KeyAlgorithm { name: hash.name() },
3240        };
3241
3242        unsafe {
3243            hmac_key_algorithm.to_jsobject(*cx, out);
3244        }
3245    }
3246}
3247
3248trait AlgorithmFromNameAndSize {
3249    fn from_name_and_size(name: DOMString, size: u16, out: MutableHandleObject, cx: JSContext);
3250}
3251
3252impl AlgorithmFromNameAndSize for AesKeyAlgorithm {
3253    /// Fill the object referenced by `out` with an [AesKeyAlgorithm]
3254    /// of the specified name and size.
3255    #[allow(unsafe_code)]
3256    fn from_name_and_size(name: DOMString, size: u16, out: MutableHandleObject, cx: JSContext) {
3257        let key_algorithm = Self {
3258            parent: KeyAlgorithm { name },
3259            length: size,
3260        };
3261
3262        unsafe {
3263            key_algorithm.to_jsobject(*cx, out);
3264        }
3265    }
3266}
3267
3268impl SubtleHkdfParams {
3269    /// <https://w3c.github.io/webcrypto/#hkdf-operations>
3270    fn derive_bits(&self, key: &CryptoKey, length: Option<u32>) -> Result<Vec<u8>, Error> {
3271        // Step 1. If length is null or zero, or is not a multiple of 8, then throw an OperationError.
3272        let Some(length) = length else {
3273            return Err(Error::Operation);
3274        };
3275        if length == 0 || length % 8 != 0 {
3276            return Err(Error::Operation);
3277        };
3278
3279        // Step 3. Let keyDerivationKey be the secret represented by [[handle]] internal slot of key.
3280        let key_derivation_key = key.handle().as_bytes();
3281
3282        // Step 4. Let result be the result of performing the HKDF extract and then the HKDF expand step described
3283        // in Section 2 of [RFC5869] using:
3284        // * the hash member of normalizedAlgorithm as Hash,
3285        // * keyDerivationKey as the input keying material, IKM,
3286        // * the contents of the salt member of normalizedAlgorithm as salt,
3287        // * the contents of the info member of normalizedAlgorithm as info,
3288        // * length divided by 8 as the value of L,
3289        let mut result = vec![0; length as usize / 8];
3290        let algorithm = match self.hash {
3291            DigestAlgorithm::Sha1 => hkdf::HKDF_SHA1_FOR_LEGACY_USE_ONLY,
3292            DigestAlgorithm::Sha256 => hkdf::HKDF_SHA256,
3293            DigestAlgorithm::Sha384 => hkdf::HKDF_SHA384,
3294            DigestAlgorithm::Sha512 => hkdf::HKDF_SHA512,
3295        };
3296        let salt = hkdf::Salt::new(algorithm, &self.salt);
3297        let info = self.info.as_slice();
3298        let pseudo_random_key = salt.extract(key_derivation_key);
3299
3300        let Ok(output_key_material) =
3301            pseudo_random_key.expand(std::slice::from_ref(&info), algorithm)
3302        else {
3303            // Step 5. If the key derivation operation fails, then throw an OperationError.
3304            return Err(Error::Operation);
3305        };
3306
3307        if output_key_material.fill(&mut result).is_err() {
3308            return Err(Error::Operation);
3309        };
3310
3311        // Step 6. Return the result of creating an ArrayBuffer containing result.
3312        // NOTE: The ArrayBuffer is created by the caller
3313        Ok(result)
3314    }
3315}
3316
3317impl SubtlePbkdf2Params {
3318    /// <https://w3c.github.io/webcrypto/#pbkdf2-operations>
3319    fn derive_bits(&self, key: &CryptoKey, length: Option<u32>) -> Result<Vec<u8>, Error> {
3320        // Step 1. If length is null or zero, or is not a multiple of 8, then throw an OperationError.
3321        let Some(length) = length else {
3322            return Err(Error::Operation);
3323        };
3324        if length == 0 || length % 8 != 0 {
3325            return Err(Error::Operation);
3326        };
3327
3328        // Step 2. If the iterations member of normalizedAlgorithm is zero, then throw an OperationError.
3329        let Ok(iterations) = NonZero::<u32>::try_from(self.iterations) else {
3330            return Err(Error::Operation);
3331        };
3332
3333        // Step 3. Let prf be the MAC Generation function described in Section 4 of [FIPS-198-1]
3334        // using the hash function described by the hash member of normalizedAlgorithm.
3335        let prf = match self.hash {
3336            DigestAlgorithm::Sha1 => pbkdf2::PBKDF2_HMAC_SHA1,
3337            DigestAlgorithm::Sha256 => pbkdf2::PBKDF2_HMAC_SHA256,
3338            DigestAlgorithm::Sha384 => pbkdf2::PBKDF2_HMAC_SHA384,
3339            DigestAlgorithm::Sha512 => pbkdf2::PBKDF2_HMAC_SHA512,
3340        };
3341
3342        // Step 4. Let result be the result of performing the PBKDF2 operation defined in Section 5.2 of [RFC8018] using
3343        // prf as the pseudo-random function, PRF, the password represented by [[handle]] internal slot of key as
3344        // the password, P, the contents of the salt attribute of normalizedAlgorithm as the salt, S, the value of
3345        // the iterations attribute of normalizedAlgorithm as the iteration count, c, and length divided by 8 as the
3346        // intended key length, dkLen.
3347        let mut result = vec![0; length as usize / 8];
3348        pbkdf2::derive(
3349            prf,
3350            iterations,
3351            &self.salt,
3352            key.handle().as_bytes(),
3353            &mut result,
3354        );
3355
3356        // Step 5. If the key derivation operation fails, then throw an OperationError.
3357        // TODO: Investigate when key derivation can fail and how ring handles that case
3358        // (pbkdf2::derive does not return a Result type)
3359
3360        // Step 6. Return result
3361        Ok(result)
3362    }
3363}
3364
3365/// <https://w3c.github.io/webcrypto/#aes-ctr-operations>
3366fn get_key_length_for_aes(length: u16) -> Result<u32, Error> {
3367    // Step 1. If the length member of normalizedDerivedKeyAlgorithm is not 128, 192 or 256,
3368    // then throw an OperationError.
3369    if !matches!(length, 128 | 192 | 256) {
3370        return Err(Error::Operation);
3371    }
3372
3373    // Step 2. Return the length member of normalizedDerivedKeyAlgorithm.
3374    Ok(length as u32)
3375}
3376
3377impl GetKeyLengthAlgorithm {
3378    fn get_key_length(&self) -> Result<u32, Error> {
3379        match self {
3380            Self::Aes(length) => get_key_length_for_aes(*length),
3381            Self::Hmac(params) => params.get_key_length(),
3382        }
3383    }
3384}
3385
3386impl DigestAlgorithm {
3387    /// <https://w3c.github.io/webcrypto/#dom-algorithm-name>
3388    fn name(&self) -> DOMString {
3389        match self {
3390            Self::Sha1 => ALG_SHA1,
3391            Self::Sha256 => ALG_SHA256,
3392            Self::Sha384 => ALG_SHA384,
3393            Self::Sha512 => ALG_SHA512,
3394        }
3395        .into()
3396    }
3397
3398    fn digest(&self, data: &[u8]) -> Result<impl AsRef<[u8]>, Error> {
3399        let algorithm = match self {
3400            Self::Sha1 => &digest::SHA1_FOR_LEGACY_USE_ONLY,
3401            Self::Sha256 => &digest::SHA256,
3402            Self::Sha384 => &digest::SHA384,
3403            Self::Sha512 => &digest::SHA512,
3404        };
3405        Ok(digest::digest(algorithm, data))
3406    }
3407
3408    fn block_size_in_bits(&self) -> usize {
3409        match self {
3410            Self::Sha1 => 160,
3411            Self::Sha256 => 256,
3412            Self::Sha384 => 384,
3413            Self::Sha512 => 512,
3414        }
3415    }
3416}
3417
3418impl ImportKeyAlgorithm {
3419    fn import_key(
3420        &self,
3421        subtle: &SubtleCrypto,
3422        format: KeyFormat,
3423        key_data: &[u8],
3424        extractable: bool,
3425        key_usages: Vec<KeyUsage>,
3426        can_gc: CanGc,
3427    ) -> Result<DomRoot<CryptoKey>, Error> {
3428        match self {
3429            Self::AesCbc => subtle.import_key_aes(
3430                format,
3431                key_data,
3432                extractable,
3433                key_usages,
3434                ALG_AES_CBC,
3435                can_gc,
3436            ),
3437            Self::AesCtr => subtle.import_key_aes(
3438                format,
3439                key_data,
3440                extractable,
3441                key_usages,
3442                ALG_AES_CTR,
3443                can_gc,
3444            ),
3445            Self::AesKw => subtle.import_key_aes(
3446                format,
3447                key_data,
3448                extractable,
3449                key_usages,
3450                ALG_AES_KW,
3451                can_gc,
3452            ),
3453            Self::AesGcm => subtle.import_key_aes(
3454                format,
3455                key_data,
3456                extractable,
3457                key_usages,
3458                ALG_AES_GCM,
3459                can_gc,
3460            ),
3461            Self::Hmac(params) => {
3462                subtle.import_key_hmac(params, format, key_data, extractable, key_usages, can_gc)
3463            },
3464            Self::Pbkdf2 => {
3465                subtle.import_key_pbkdf2(format, key_data, extractable, key_usages, can_gc)
3466            },
3467            Self::Hkdf => subtle.import_key_hkdf(format, key_data, extractable, key_usages, can_gc),
3468        }
3469    }
3470}
3471
3472impl DeriveBitsAlgorithm {
3473    fn derive_bits(&self, key: &CryptoKey, length: Option<u32>) -> Result<Vec<u8>, Error> {
3474        match self {
3475            Self::Pbkdf2(pbkdf2_params) => pbkdf2_params.derive_bits(key, length),
3476            Self::Hkdf(hkdf_params) => hkdf_params.derive_bits(key, length),
3477        }
3478    }
3479}
3480
3481impl EncryptionAlgorithm {
3482    /// <https://w3c.github.io/webcrypto/#dom-algorithm-name>
3483    fn name(&self) -> &str {
3484        match self {
3485            Self::AesCbc(params) => &params.name,
3486            Self::AesCtr(params) => &params.name,
3487            Self::AesGcm(params) => &params.name,
3488        }
3489    }
3490
3491    // FIXME: This doesn't really need the "SubtleCrypto" argument
3492    fn encrypt(
3493        &self,
3494        subtle: &SubtleCrypto,
3495        key: &CryptoKey,
3496        data: &[u8],
3497        cx: JSContext,
3498        result: MutableHandleObject,
3499        can_gc: CanGc,
3500    ) -> Result<Vec<u8>, Error> {
3501        match self {
3502            Self::AesCbc(params) => subtle.encrypt_aes_cbc(params, key, data, cx, result, can_gc),
3503            Self::AesCtr(params) => {
3504                subtle.encrypt_decrypt_aes_ctr(params, key, data, cx, result, can_gc)
3505            },
3506            Self::AesGcm(params) => subtle.encrypt_aes_gcm(params, key, data, cx, result, can_gc),
3507        }
3508    }
3509
3510    // FIXME: This doesn't really need the "SubtleCrypto" argument
3511    fn decrypt(
3512        &self,
3513        subtle: &SubtleCrypto,
3514        key: &CryptoKey,
3515        data: &[u8],
3516        cx: JSContext,
3517        result: MutableHandleObject,
3518        can_gc: CanGc,
3519    ) -> Result<Vec<u8>, Error> {
3520        match self {
3521            Self::AesCbc(params) => subtle.decrypt_aes_cbc(params, key, data, cx, result, can_gc),
3522            Self::AesCtr(params) => {
3523                subtle.encrypt_decrypt_aes_ctr(params, key, data, cx, result, can_gc)
3524            },
3525            Self::AesGcm(params) => subtle.decrypt_aes_gcm(params, key, data, cx, result, can_gc),
3526        }
3527    }
3528}
3529
3530impl SignatureAlgorithm {
3531    fn name(&self) -> &str {
3532        match self {
3533            Self::Hmac => ALG_HMAC,
3534        }
3535    }
3536
3537    fn sign(
3538        &self,
3539        cx: JSContext,
3540        key: &CryptoKey,
3541        data: &[u8],
3542        can_gc: CanGc,
3543    ) -> Result<Vec<u8>, Error> {
3544        match self {
3545            Self::Hmac => sign_hmac(cx, key, data, can_gc).map(|s| s.as_ref().to_vec()),
3546        }
3547    }
3548
3549    fn verify(
3550        &self,
3551        cx: JSContext,
3552        key: &CryptoKey,
3553        data: &[u8],
3554        signature: &[u8],
3555        can_gc: CanGc,
3556    ) -> Result<bool, Error> {
3557        match self {
3558            Self::Hmac => verify_hmac(cx, key, data, signature, can_gc),
3559        }
3560    }
3561}
3562
3563impl KeyGenerationAlgorithm {
3564    // FIXME: This doesn't really need the "SubtleCrypto" argument
3565    fn generate_key(
3566        &self,
3567        subtle: &SubtleCrypto,
3568        usages: Vec<KeyUsage>,
3569        extractable: bool,
3570        can_gc: CanGc,
3571    ) -> Result<CryptoKeyOrCryptoKeyPair, Error> {
3572        let key_or_key_pair =
3573            match self {
3574                Self::Aes(params) => CryptoKeyOrCryptoKeyPair::CryptoKey(subtle.generate_key_aes(
3575                    usages,
3576                    params,
3577                    extractable,
3578                    can_gc,
3579                )?),
3580                Self::Hmac(params) => CryptoKeyOrCryptoKeyPair::CryptoKey(
3581                    subtle.generate_key_hmac(usages, params, extractable, can_gc)?,
3582                ),
3583            };
3584
3585        Ok(key_or_key_pair)
3586    }
3587}
3588
3589/// <https://w3c.github.io/webcrypto/#hmac-operations>
3590fn sign_hmac(
3591    cx: JSContext,
3592    key: &CryptoKey,
3593    data: &[u8],
3594    can_gc: CanGc,
3595) -> Result<impl AsRef<[u8]>, Error> {
3596    // Step 1. Let mac be the result of performing the MAC Generation operation described in Section 4 of [FIPS-198-1]
3597    // using the key represented by [[handle]] internal slot of key, the hash function identified by the hash attribute
3598    // of the [[algorithm]] internal slot of key and message as the input data text.
3599    rooted!(in(*cx) let mut algorithm_slot = ObjectValue(key.Algorithm(cx).as_ptr()));
3600    let params = value_from_js_object::<HmacKeyAlgorithm>(cx, algorithm_slot.handle(), can_gc)?;
3601
3602    let hash_algorithm = match params.hash.name.str() {
3603        ALG_SHA1 => hmac::HMAC_SHA1_FOR_LEGACY_USE_ONLY,
3604        ALG_SHA256 => hmac::HMAC_SHA256,
3605        ALG_SHA384 => hmac::HMAC_SHA384,
3606        ALG_SHA512 => hmac::HMAC_SHA512,
3607        _ => return Err(Error::NotSupported),
3608    };
3609
3610    let sign_key = hmac::Key::new(hash_algorithm, key.handle().as_bytes());
3611    let mac = hmac::sign(&sign_key, data);
3612
3613    // Step 2. Return the result of creating an ArrayBuffer containing mac.
3614    // NOTE: This is done by the caller
3615    Ok(mac)
3616}
3617
3618/// <https://w3c.github.io/webcrypto/#hmac-operations>
3619fn verify_hmac(
3620    cx: JSContext,
3621    key: &CryptoKey,
3622    data: &[u8],
3623    signature: &[u8],
3624    can_gc: CanGc,
3625) -> Result<bool, Error> {
3626    // Step 1. Let mac be the result of performing the MAC Generation operation described in Section 4 of [FIPS-198-1]
3627    // using the key represented by [[handle]] internal slot of key, the hash function identified by the hash attribute
3628    // of the [[algorithm]] internal slot of key and message as the input data text.
3629    let mac = sign_hmac(cx, key, data, can_gc)?;
3630
3631    // Step 2. Return true if mac is equal to signature and false otherwise.
3632    let is_valid = mac.as_ref() == signature;
3633    Ok(is_valid)
3634}
3635
3636impl KeyWrapAlgorithm {
3637    /// <https://w3c.github.io/webcrypto/#dom-algorithm-name>
3638    fn name(&self) -> &str {
3639        match self {
3640            Self::AesKw => ALG_AES_KW,
3641            Self::AesCbc(key_gen_params) => &key_gen_params.name,
3642            Self::AesCtr(key_gen_params) => &key_gen_params.name,
3643            Self::AesGcm(_) => ALG_AES_GCM,
3644        }
3645    }
3646}
3647
3648#[expect(unused)]
3649trait RsaOtherPrimesInfoExt {
3650    fn from_value(value: &serde_json::Value) -> Result<RsaOtherPrimesInfo, Error>;
3651}
3652
3653impl RsaOtherPrimesInfoExt for RsaOtherPrimesInfo {
3654    fn from_value(value: &serde_json::Value) -> Result<RsaOtherPrimesInfo, Error> {
3655        let serde_json::Value::Object(object) = value else {
3656            return Err(Error::Data);
3657        };
3658
3659        let mut rsa_other_primes_info: RsaOtherPrimesInfo = Default::default();
3660        for (key, value) in object {
3661            match key.as_str() {
3662                "r" => {
3663                    rsa_other_primes_info.r =
3664                        Some(DOMString::from(value.as_str().ok_or(Error::Data)?))
3665                },
3666                "d" => {
3667                    rsa_other_primes_info.d =
3668                        Some(DOMString::from(value.as_str().ok_or(Error::Data)?))
3669                },
3670                "t" => {
3671                    rsa_other_primes_info.t =
3672                        Some(DOMString::from(value.as_str().ok_or(Error::Data)?))
3673                },
3674                _ => {
3675                    // Additional members can be present in the JWK; if not understood by
3676                    // implementations encountering them, they MUST be ignored.
3677                },
3678            }
3679        }
3680
3681        Ok(rsa_other_primes_info)
3682    }
3683}
3684
3685trait JsonWebKeyExt {
3686    fn parse(cx: JSContext, data: &[u8]) -> Result<JsonWebKey, Error>;
3687    fn stringify(&self, cx: JSContext) -> Result<DOMString, Error>;
3688    fn get_usages_from_key_ops(&self) -> Result<Vec<KeyUsage>, Error>;
3689    #[expect(unused)]
3690    fn get_rsa_other_primes_info_from_oth(&self) -> Result<&[RsaOtherPrimesInfo], Error>;
3691    fn check_key_ops(&self, specified_usages: &[KeyUsage]) -> Result<(), Error>;
3692}
3693
3694impl JsonWebKeyExt for JsonWebKey {
3695    /// <https://w3c.github.io/webcrypto/#concept-parse-a-jwk>
3696    #[allow(unsafe_code)]
3697    fn parse(cx: JSContext, data: &[u8]) -> Result<JsonWebKey, Error> {
3698        // Step 1. Let data be the sequence of bytes to be parsed.
3699        // (It is given as a method paramter.)
3700
3701        // Step 2. Let json be the Unicode string that results from interpreting data according to UTF-8.
3702        let json = String::from_utf8_lossy(data);
3703
3704        // Step 3. Convert json to UTF-16.
3705        let json: Vec<_> = json.encode_utf16().collect();
3706
3707        // Step 4. Let result be the object literal that results from executing the JSON.parse
3708        // internal function in the context of a new global object, with text argument set to a
3709        // JavaScript String containing json.
3710        rooted!(in(*cx) let mut result = UndefinedValue());
3711        unsafe {
3712            if !JS_ParseJSON(*cx, json.as_ptr(), json.len() as u32, result.handle_mut()) {
3713                return Err(Error::JSFailed);
3714            }
3715        }
3716
3717        // Step 5. Let key be the result of converting result to the IDL dictionary type of JsonWebKey.
3718        let key = match JsonWebKey::new(cx, result.handle(), CanGc::note()) {
3719            Ok(ConversionResult::Success(key)) => key,
3720            Ok(ConversionResult::Failure(error)) => {
3721                return Err(Error::Type(error.to_string()));
3722            },
3723            Err(()) => {
3724                return Err(Error::JSFailed);
3725            },
3726        };
3727
3728        // Step 6. If the kty field of key is not defined, then throw a DataError.
3729        if key.kty.is_none() {
3730            return Err(Error::Data);
3731        }
3732
3733        // Step 7. Result key.
3734        Ok(key)
3735    }
3736
3737    /// Convert a JsonWebKey value to DOMString. We first convert the JsonWebKey value to
3738    /// JavaScript value, and then serialize it by performing steps in
3739    /// <https://infra.spec.whatwg.org/#serialize-a-javascript-value-to-a-json-string>. This acts
3740    /// like the opposite of JsonWebKey::parse if you further convert the stringified result to
3741    /// bytes.
3742    fn stringify(&self, cx: JSContext) -> Result<DOMString, Error> {
3743        rooted!(in(*cx) let mut data = UndefinedValue());
3744        self.safe_to_jsval(cx, data.handle_mut());
3745        serialize_jsval_to_json_utf8(cx, data.handle())
3746    }
3747
3748    fn get_usages_from_key_ops(&self) -> Result<Vec<KeyUsage>, Error> {
3749        let mut usages = vec![];
3750        for op in self.key_ops.as_ref().ok_or(Error::Data)? {
3751            usages.push(KeyUsage::from_str(op).map_err(|_| Error::Data)?);
3752        }
3753        Ok(usages)
3754    }
3755
3756    fn get_rsa_other_primes_info_from_oth(&self) -> Result<&[RsaOtherPrimesInfo], Error> {
3757        self.oth.as_deref().ok_or(Error::Data)
3758    }
3759
3760    /// If the key_ops field of jwk is present, and is invalid according to the requirements of
3761    /// JSON Web Key [JWK] or does not contain all of the specified usages values, then throw a
3762    /// DataError.
3763    fn check_key_ops(&self, specified_usages: &[KeyUsage]) -> Result<(), Error> {
3764        // If the key_ops field of jwk is present,
3765        if let Some(ref key_ops) = self.key_ops {
3766            // and is invalid according to the requirements of JSON Web Key [JWK]:
3767            // 1. Duplicate key operation values MUST NOT be present in the array.
3768            if key_ops
3769                .iter()
3770                .collect::<std::collections::HashSet<_>>()
3771                .len() <
3772                key_ops.len()
3773            {
3774                return Err(Error::Data);
3775            }
3776            // 2. The "use" and "key_ops" JWK members SHOULD NOT be used together; however, if both
3777            //    are used, the information they convey MUST be consistent.
3778            if let Some(ref use_) = self.use_ {
3779                if key_ops.iter().any(|op| op != use_) {
3780                    return Err(Error::Data);
3781                }
3782            }
3783
3784            // or does not contain all of the specified usages values
3785            let key_ops_as_usages = self.get_usages_from_key_ops()?;
3786            if !specified_usages
3787                .iter()
3788                .all(|specified_usage| key_ops_as_usages.contains(specified_usage))
3789            {
3790                return Err(Error::Data);
3791            }
3792        }
3793
3794        Ok(())
3795    }
3796}