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