script/dom/subtlecrypto/
aes_operation.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 aes::cipher::block_padding::Pkcs7;
6use aes::cipher::generic_array::GenericArray;
7use aes::cipher::{BlockDecryptMut, BlockEncryptMut, KeyIvInit, StreamCipher};
8use aes::{Aes128, Aes192, Aes256};
9use aes_gcm::{AeadInPlace, AesGcm, KeyInit};
10use aes_kw::{KekAes128, KekAes192, KekAes256};
11use base64ct::{Base64UrlUnpadded, Encoding};
12use cipher::consts::{U12, U16, U32};
13use rand::TryRngCore;
14use rand::rngs::OsRng;
15
16use crate::dom::bindings::codegen::Bindings::CryptoKeyBinding::{
17    CryptoKeyMethods, KeyType, KeyUsage,
18};
19use crate::dom::bindings::codegen::Bindings::SubtleCryptoBinding::{JsonWebKey, KeyFormat};
20use crate::dom::bindings::error::Error;
21use crate::dom::bindings::root::DomRoot;
22use crate::dom::bindings::str::DOMString;
23use crate::dom::cryptokey::{CryptoKey, Handle};
24use crate::dom::globalscope::GlobalScope;
25use crate::dom::subtlecrypto::{
26    ALG_AES_CBC, ALG_AES_CTR, ALG_AES_GCM, ALG_AES_KW, ExportedKey, JsonWebKeyExt,
27    KeyAlgorithmAndDerivatives, SubtleAesCbcParams, SubtleAesCtrParams, SubtleAesDerivedKeyParams,
28    SubtleAesGcmParams, SubtleAesKeyAlgorithm, SubtleAesKeyGenParams,
29};
30use crate::script_runtime::CanGc;
31
32type Aes128CbcEnc = cbc::Encryptor<Aes128>;
33type Aes128CbcDec = cbc::Decryptor<Aes128>;
34type Aes192CbcEnc = cbc::Encryptor<Aes192>;
35type Aes192CbcDec = cbc::Decryptor<Aes192>;
36type Aes256CbcEnc = cbc::Encryptor<Aes256>;
37type Aes256CbcDec = cbc::Decryptor<Aes256>;
38type Aes128Ctr = ctr::Ctr64BE<Aes128>;
39type Aes192Ctr = ctr::Ctr64BE<Aes192>;
40type Aes256Ctr = ctr::Ctr64BE<Aes256>;
41
42type Aes128Gcm96Iv = AesGcm<Aes128, U12>;
43type Aes128Gcm128Iv = AesGcm<Aes128, U16>;
44type Aes192Gcm96Iv = AesGcm<Aes192, U12>;
45type Aes256Gcm96Iv = AesGcm<Aes256, U12>;
46type Aes128Gcm256Iv = AesGcm<Aes128, U32>;
47type Aes192Gcm256Iv = AesGcm<Aes192, U32>;
48type Aes256Gcm256Iv = AesGcm<Aes256, U32>;
49
50/// <https://w3c.github.io/webcrypto/#aes-ctr-operations-encrypt>
51pub(crate) fn encrypt_aes_ctr(
52    normalized_algorithm: &SubtleAesCtrParams,
53    key: &CryptoKey,
54    plaintext: &[u8],
55) -> Result<Vec<u8>, Error> {
56    // Step 1. If the counter member of normalizedAlgorithm does not have a length of 16 bytes,
57    // then throw an OperationError.
58    // Step 2. If the length member of normalizedAlgorithm is zero or is greater than 128, then
59    // throw an OperationError.
60    if normalized_algorithm.counter.len() != 16 ||
61        normalized_algorithm.length == 0 ||
62        normalized_algorithm.length > 128
63    {
64        return Err(Error::Operation);
65    }
66
67    // Step 3. Let ciphertext be the result of performing the CTR Encryption operation described in
68    // Section 6.5 of [NIST-SP800-38A] using AES as the block cipher, the counter member of
69    // normalizedAlgorithm as the initial value of the counter block, the length member of
70    // normalizedAlgorithm as the input parameter m to the standard counter block incrementing
71    // function defined in Appendix B.1 of [NIST-SP800-38A] and plaintext as the input plaintext.
72    let mut ciphertext = Vec::from(plaintext);
73    let counter = GenericArray::from_slice(&normalized_algorithm.counter);
74
75    match key.handle() {
76        Handle::Aes128(data) => {
77            let key_data = GenericArray::from_slice(data);
78            Aes128Ctr::new(key_data, counter).apply_keystream(&mut ciphertext)
79        },
80        Handle::Aes192(data) => {
81            let key_data = GenericArray::from_slice(data);
82            Aes192Ctr::new(key_data, counter).apply_keystream(&mut ciphertext)
83        },
84        Handle::Aes256(data) => {
85            let key_data = GenericArray::from_slice(data);
86            Aes256Ctr::new(key_data, counter).apply_keystream(&mut ciphertext)
87        },
88        _ => return Err(Error::Data),
89    };
90
91    // Step 3. Return ciphertext.
92    Ok(ciphertext)
93}
94
95/// <https://w3c.github.io/webcrypto/#aes-ctr-operations-decrypt>
96pub(crate) fn decrypt_aes_ctr(
97    normalized_algorithm: &SubtleAesCtrParams,
98    key: &CryptoKey,
99    ciphertext: &[u8],
100) -> Result<Vec<u8>, Error> {
101    // NOTE: Share implementation with `encrypt_aes_ctr`
102    encrypt_aes_ctr(normalized_algorithm, key, ciphertext)
103}
104
105/// <https://w3c.github.io/webcrypto/#aes-ctr-operations-generate-key>
106pub(crate) fn generate_key_aes_ctr(
107    global: &GlobalScope,
108    normalized_algorithm: &SubtleAesKeyGenParams,
109    extractable: bool,
110    usages: Vec<KeyUsage>,
111    can_gc: CanGc,
112) -> Result<DomRoot<CryptoKey>, Error> {
113    generate_key_aes(
114        global,
115        normalized_algorithm,
116        extractable,
117        usages,
118        ALG_AES_CTR,
119        &[
120            KeyUsage::Encrypt,
121            KeyUsage::Decrypt,
122            KeyUsage::WrapKey,
123            KeyUsage::UnwrapKey,
124        ],
125        can_gc,
126    )
127}
128
129/// <https://w3c.github.io/webcrypto/#aes-ctr-operations-import-key>
130pub(crate) fn import_key_aes_ctr(
131    global: &GlobalScope,
132    format: KeyFormat,
133    key_data: &[u8],
134    extractable: bool,
135    usages: Vec<KeyUsage>,
136    can_gc: CanGc,
137) -> Result<DomRoot<CryptoKey>, Error> {
138    import_key_aes(
139        global,
140        format,
141        key_data,
142        extractable,
143        usages,
144        ALG_AES_CTR,
145        can_gc,
146    )
147}
148
149/// <https://w3c.github.io/webcrypto/#aes-ctr-operations-export-key>
150pub(crate) fn export_key_aes_ctr(format: KeyFormat, key: &CryptoKey) -> Result<ExportedKey, Error> {
151    export_key_aes(format, key)
152}
153
154/// <https://w3c.github.io/webcrypto/#aes-ctr-operations-get-key-length>
155pub(crate) fn get_key_length_aes_ctr(
156    normalized_derived_key_algorithm: &SubtleAesDerivedKeyParams,
157) -> Result<Option<u32>, Error> {
158    get_key_length_aes(normalized_derived_key_algorithm)
159}
160
161/// <https://w3c.github.io/webcrypto/#aes-cbc-operations-encrypt>
162pub(crate) fn encrypt_aes_cbc(
163    normalized_algorithm: &SubtleAesCbcParams,
164    key: &CryptoKey,
165    plaintext: &[u8],
166) -> Result<Vec<u8>, Error> {
167    // Step 1. If the iv member of normalizedAlgorithm does not have a length of 16 bytes, then
168    // throw an OperationError.
169    if normalized_algorithm.iv.len() != 16 {
170        return Err(Error::Operation);
171    }
172
173    // Step 2. Let paddedPlaintext be the result of adding padding octets to plaintext according to
174    // the procedure defined in Section 10.3 of [RFC2315], step 2, with a value of k of 16.
175    // Step 3. Let ciphertext be the result of performing the CBC Encryption operation described in
176    // Section 6.2 of [NIST-SP800-38A] using AES as the block cipher, the iv member of
177    // normalizedAlgorithm as the IV input parameter and paddedPlaintext as the input plaintext.
178    let plaintext = Vec::from(plaintext);
179    let iv = GenericArray::from_slice(&normalized_algorithm.iv);
180    let ciphertext = match key.handle() {
181        Handle::Aes128(data) => {
182            let key_data = GenericArray::from_slice(data);
183            Aes128CbcEnc::new(key_data, iv).encrypt_padded_vec_mut::<Pkcs7>(&plaintext)
184        },
185        Handle::Aes192(data) => {
186            let key_data = GenericArray::from_slice(data);
187            Aes192CbcEnc::new(key_data, iv).encrypt_padded_vec_mut::<Pkcs7>(&plaintext)
188        },
189        Handle::Aes256(data) => {
190            let key_data = GenericArray::from_slice(data);
191            Aes256CbcEnc::new(key_data, iv).encrypt_padded_vec_mut::<Pkcs7>(&plaintext)
192        },
193        _ => return Err(Error::Data),
194    };
195
196    // Step 4. Return ciphertext.
197    Ok(ciphertext)
198}
199
200/// <https://w3c.github.io/webcrypto/#aes-cbc-operations-decrypt>
201pub(crate) fn decrypt_aes_cbc(
202    normalized_algorithm: &SubtleAesCbcParams,
203    key: &CryptoKey,
204    ciphertext: &[u8],
205) -> Result<Vec<u8>, Error> {
206    // Step 1. If the iv member of normalizedAlgorithm does not have a length of 16 bytes, then
207    // throw an OperationError.
208    if normalized_algorithm.iv.len() != 16 {
209        return Err(Error::Operation);
210    }
211
212    // Step 2. If the length of ciphertext is zero or is not a multiple of 16 bytes, then throw an
213    // OperationError.
214    if ciphertext.is_empty() || ciphertext.len() % 16 != 0 {
215        return Err(Error::Operation);
216    }
217
218    // Step 3. Let paddedPlaintext be the result of performing the CBC Decryption operation
219    // described in Section 6.2 of [NIST-SP800-38A] using AES as the block cipher, the iv member of
220    // normalizedAlgorithm as the IV input parameter and ciphertext as the input ciphertext.
221    // Step 4. Let p be the value of the last octet of paddedPlaintext.
222    // Step 5. If p is zero or greater than 16, or if any of the last p octets of paddedPlaintext
223    // have a value which is not p, then throw an OperationError.
224    // Step 6. Let plaintext be the result of removing p octets from the end of paddedPlaintext.
225    let mut ciphertext = Vec::from(ciphertext);
226    let iv = GenericArray::from_slice(&normalized_algorithm.iv);
227    let plaintext = match key.handle() {
228        Handle::Aes128(data) => {
229            let key_data = GenericArray::from_slice(data);
230            Aes128CbcDec::new(key_data, iv)
231                .decrypt_padded_mut::<Pkcs7>(ciphertext.as_mut_slice())
232                .map_err(|_| Error::Operation)?
233        },
234        Handle::Aes192(data) => {
235            let key_data = GenericArray::from_slice(data);
236            Aes192CbcDec::new(key_data, iv)
237                .decrypt_padded_mut::<Pkcs7>(ciphertext.as_mut_slice())
238                .map_err(|_| Error::Operation)?
239        },
240        Handle::Aes256(data) => {
241            let key_data = GenericArray::from_slice(data);
242            Aes256CbcDec::new(key_data, iv)
243                .decrypt_padded_mut::<Pkcs7>(ciphertext.as_mut_slice())
244                .map_err(|_| Error::Operation)?
245        },
246        _ => return Err(Error::Data),
247    };
248
249    // Step 7. Return plaintext.
250    Ok(plaintext.to_vec())
251}
252
253/// <https://w3c.github.io/webcrypto/#aes-cbc-operations-generate-key>
254pub(crate) fn generate_key_aes_cbc(
255    global: &GlobalScope,
256    normalized_algorithm: &SubtleAesKeyGenParams,
257    extractable: bool,
258    usages: Vec<KeyUsage>,
259    can_gc: CanGc,
260) -> Result<DomRoot<CryptoKey>, Error> {
261    generate_key_aes(
262        global,
263        normalized_algorithm,
264        extractable,
265        usages,
266        ALG_AES_CBC,
267        &[
268            KeyUsage::Encrypt,
269            KeyUsage::Decrypt,
270            KeyUsage::WrapKey,
271            KeyUsage::UnwrapKey,
272        ],
273        can_gc,
274    )
275}
276
277/// <https://w3c.github.io/webcrypto/#aes-cbc-operations-import-key>
278pub(crate) fn import_key_aes_cbc(
279    global: &GlobalScope,
280    format: KeyFormat,
281    key_data: &[u8],
282    extractable: bool,
283    usages: Vec<KeyUsage>,
284    can_gc: CanGc,
285) -> Result<DomRoot<CryptoKey>, Error> {
286    import_key_aes(
287        global,
288        format,
289        key_data,
290        extractable,
291        usages,
292        ALG_AES_CBC,
293        can_gc,
294    )
295}
296
297/// <https://w3c.github.io/webcrypto/#aes-cbc-operations-export-key>
298pub(crate) fn export_key_aes_cbc(format: KeyFormat, key: &CryptoKey) -> Result<ExportedKey, Error> {
299    export_key_aes(format, key)
300}
301
302/// <https://w3c.github.io/webcrypto/#aes-cbc-operations-get-key-length>
303pub(crate) fn get_key_length_aes_cbc(
304    normalized_derived_key_algorithm: &SubtleAesDerivedKeyParams,
305) -> Result<Option<u32>, Error> {
306    get_key_length_aes(normalized_derived_key_algorithm)
307}
308
309/// <https://w3c.github.io/webcrypto/#aes-gcm-operations-encrypt>
310pub(crate) fn encrypt_aes_gcm(
311    normalized_algorithm: &SubtleAesGcmParams,
312    key: &CryptoKey,
313    plaintext: &[u8],
314) -> Result<Vec<u8>, Error> {
315    // Step 1. If plaintext has a length greater than 2^39 - 256 bytes, then throw an OperationError.
316    if plaintext.len() as u64 > (2 << 39) - 256 {
317        return Err(Error::Operation);
318    }
319
320    // Step 2. If the iv member of normalizedAlgorithm has a length greater than 2^64 - 1 bytes,
321    // then throw an OperationError.
322    // NOTE: servo does not currently support 128-bit platforms, so this can never happen
323
324    // Step 3. If the additionalData member of normalizedAlgorithm is present and has a length
325    // greater than 2^64 - 1 bytes, then throw an OperationError.
326    if normalized_algorithm
327        .additional_data
328        .as_ref()
329        .is_some_and(|data| data.len() > u64::MAX as usize)
330    {
331        return Err(Error::Operation);
332    }
333
334    // Step 4.
335    // If the tagLength member of normalizedAlgorithm is not present:
336    //     Let tagLength be 128.
337    // If the tagLength member of normalizedAlgorithm is one of 32, 64, 96, 104, 112, 120 or 128:
338    //     Let tagLength be equal to the tagLength member of normalizedAlgorithm
339    // Otherwise:
340    //     throw an OperationError.
341    let tag_length = match normalized_algorithm.tag_length {
342        None => 128,
343        Some(length) if matches!(length, 32 | 64 | 96 | 104 | 112 | 120 | 128) => length,
344        _ => {
345            return Err(Error::Operation);
346        },
347    };
348
349    // Step 5. Let additionalData be the additionalData member of normalizedAlgorithm if present or
350    // an empty byte sequence otherwise.
351    let additional_data = normalized_algorithm
352        .additional_data
353        .as_deref()
354        .unwrap_or_default();
355
356    // Step 6. Let C and T be the outputs that result from performing the Authenticated Encryption
357    // Function described in Section 7.1 of [NIST-SP800-38D] using AES as the block cipher, the
358    // contents of the iv member of normalizedAlgorithm as the IV input parameter, the contents of
359    // additionalData as the A input parameter, tagLength as the t pre-requisite and the contents
360    // of plaintext as the input plaintext.
361    let key_length = key.handle().as_bytes().len();
362    let iv_length = normalized_algorithm.iv.len();
363    let mut ciphertext = plaintext.to_vec();
364    let key_bytes = key.handle().as_bytes();
365    let tag = match (key_length, iv_length) {
366        (16, 12) => {
367            let nonce = GenericArray::from_slice(&normalized_algorithm.iv);
368            <Aes128Gcm96Iv>::new_from_slice(key_bytes)
369                .expect("key length did not match")
370                .encrypt_in_place_detached(nonce, additional_data, &mut ciphertext)
371        },
372        (16, 16) => {
373            let nonce = GenericArray::from_slice(&normalized_algorithm.iv);
374            <Aes128Gcm128Iv>::new_from_slice(key_bytes)
375                .expect("key length did not match")
376                .encrypt_in_place_detached(nonce, additional_data, &mut ciphertext)
377        },
378        (24, 12) => {
379            let nonce = GenericArray::from_slice(&normalized_algorithm.iv);
380            <Aes192Gcm96Iv>::new_from_slice(key_bytes)
381                .expect("key length did not match")
382                .encrypt_in_place_detached(nonce, additional_data, &mut ciphertext)
383        },
384        (32, 12) => {
385            let nonce = GenericArray::from_slice(&normalized_algorithm.iv);
386            <Aes256Gcm96Iv>::new_from_slice(key_bytes)
387                .expect("key length did not match")
388                .encrypt_in_place_detached(nonce, additional_data, &mut ciphertext)
389        },
390        (16, 32) => {
391            let nonce = GenericArray::from_slice(&normalized_algorithm.iv);
392            <Aes128Gcm256Iv>::new_from_slice(key_bytes)
393                .expect("key length did not match")
394                .encrypt_in_place_detached(nonce, additional_data, &mut ciphertext)
395        },
396        (24, 32) => {
397            let nonce = GenericArray::from_slice(&normalized_algorithm.iv);
398            <Aes192Gcm256Iv>::new_from_slice(key_bytes)
399                .expect("key length did not match")
400                .encrypt_in_place_detached(nonce, additional_data, &mut ciphertext)
401        },
402        (32, 32) => {
403            let nonce = GenericArray::from_slice(&normalized_algorithm.iv);
404            <Aes256Gcm256Iv>::new_from_slice(key_bytes)
405                .expect("key length did not match")
406                .encrypt_in_place_detached(nonce, additional_data, &mut ciphertext)
407        },
408        _ => {
409            log::warn!(
410                "Missing AES-GCM encryption implementation with {key_length}-byte key and {iv_length}-byte IV"
411            );
412            return Err(Error::NotSupported);
413        },
414    };
415
416    // Step 7. Let ciphertext be equal to C | T, where '|' denotes concatenation.
417    ciphertext.extend_from_slice(&tag.unwrap()[..tag_length as usize / 8]);
418
419    // Step 8. Return ciphertext.
420    Ok(ciphertext)
421}
422
423/// <https://w3c.github.io/webcrypto/#aes-gcm-operations-decrypt>
424pub(crate) fn decrypt_aes_gcm(
425    normalized_algorithm: &SubtleAesGcmParams,
426    key: &CryptoKey,
427    ciphertext: &[u8],
428) -> Result<Vec<u8>, Error> {
429    // Step 1.
430    // If the tagLength member of normalizedAlgorithm is not present:
431    //     Let tagLength be 128.
432    // If the tagLength member of normalizedAlgorithm is one of 32, 64, 96, 104, 112, 120 or 128:
433    //     Let tagLength be equal to the tagLength member of normalizedAlgorithm
434    // Otherwise:
435    //     throw an OperationError.
436    let tag_length = match normalized_algorithm.tag_length {
437        None => 128,
438        Some(length) if matches!(length, 32 | 64 | 96 | 104 | 112 | 120 | 128) => length as usize,
439        _ => {
440            return Err(Error::Operation);
441        },
442    };
443
444    // Step 2. If ciphertext has a length in bits less than tagLength, then throw an
445    // OperationError.
446    if ciphertext.len() * 8 < tag_length {
447        return Err(Error::Operation);
448    }
449
450    // Step 3. If the iv member of normalizedAlgorithm has a length greater than 2^64 - 1 bytes,
451    // then throw an OperationError.
452    // NOTE: servo does not currently support 128-bit platforms, so this can never happen
453
454    // Step 4. If the additionalData member of normalizedAlgorithm is present and has a length
455    // greater than 2^64 - 1 bytes, then throw an OperationError.
456    // NOTE: servo does not currently support 128-bit platforms, so this can never happen
457
458    // Step 5. Let tag be the last tagLength bits of ciphertext.
459    // Step 6. Let actualCiphertext be the result of removing the last tagLength bits from
460    // ciphertext.
461    // NOTE: aes_gcm splits the ciphertext for us
462
463    // Step 7. Let additionalData be the additionalData member of normalizedAlgorithm if present or
464    // an empty byte sequence otherwise.
465    let additional_data = normalized_algorithm
466        .additional_data
467        .as_deref()
468        .unwrap_or_default();
469
470    // Step 8. Perform the Authenticated Decryption Function described in Section 7.2 of
471    // [NIST-SP800-38D] using AES as the block cipher, the iv member of normalizedAlgorithm as the
472    // IV input parameter, additionalData as the A input parameter, tagLength as the t
473    // pre-requisite, actualCiphertext as the input ciphertext, C and tag as the authentication
474    // tag, T.
475    // If the result of the algorithm is the indication of inauthenticity, "FAIL":
476    //     throw an OperationError
477    // Otherwise:
478    //     Let plaintext be the output P of the Authenticated Decryption Function.
479    let mut plaintext = ciphertext.to_vec();
480    let key_length = key.handle().as_bytes().len();
481    let iv_length = normalized_algorithm.iv.len();
482    let key_bytes = key.handle().as_bytes();
483    let result = match (key_length, iv_length) {
484        (16, 12) => {
485            let nonce = GenericArray::from_slice(&normalized_algorithm.iv);
486            <Aes128Gcm96Iv>::new_from_slice(key_bytes)
487                .expect("key length did not match")
488                .decrypt_in_place(nonce, additional_data, &mut plaintext)
489        },
490        (16, 16) => {
491            let nonce = GenericArray::from_slice(&normalized_algorithm.iv);
492            <Aes128Gcm128Iv>::new_from_slice(key_bytes)
493                .expect("key length did not match")
494                .decrypt_in_place(nonce, additional_data, &mut plaintext)
495        },
496        (24, 12) => {
497            let nonce = GenericArray::from_slice(&normalized_algorithm.iv);
498            <Aes192Gcm96Iv>::new_from_slice(key_bytes)
499                .expect("key length did not match")
500                .decrypt_in_place(nonce, additional_data, &mut plaintext)
501        },
502        (32, 12) => {
503            let nonce = GenericArray::from_slice(&normalized_algorithm.iv);
504            <Aes256Gcm96Iv>::new_from_slice(key_bytes)
505                .expect("key length did not match")
506                .decrypt_in_place(nonce, additional_data, &mut plaintext)
507        },
508        (16, 32) => {
509            let nonce = GenericArray::from_slice(&normalized_algorithm.iv);
510            <Aes128Gcm256Iv>::new_from_slice(key_bytes)
511                .expect("key length did not match")
512                .decrypt_in_place(nonce, additional_data, &mut plaintext)
513        },
514        (24, 32) => {
515            let nonce = GenericArray::from_slice(&normalized_algorithm.iv);
516            <Aes192Gcm256Iv>::new_from_slice(key_bytes)
517                .expect("key length did not match")
518                .decrypt_in_place(nonce, additional_data, &mut plaintext)
519        },
520        (32, 32) => {
521            let nonce = GenericArray::from_slice(&normalized_algorithm.iv);
522            <Aes256Gcm256Iv>::new_from_slice(key_bytes)
523                .expect("key length did not match")
524                .decrypt_in_place(nonce, additional_data, &mut plaintext)
525        },
526        _ => {
527            log::warn!(
528                "Missing AES-GCM decryption implementation with {key_length}-byte key and {iv_length}-byte IV"
529            );
530            return Err(Error::NotSupported);
531        },
532    };
533    if result.is_err() {
534        return Err(Error::Operation);
535    }
536
537    Ok(plaintext)
538}
539
540/// <https://w3c.github.io/webcrypto/#aes-gcm-operations-generate-key>
541pub(crate) fn generate_key_aes_gcm(
542    global: &GlobalScope,
543    normalized_algorithm: &SubtleAesKeyGenParams,
544    extractable: bool,
545    usages: Vec<KeyUsage>,
546    can_gc: CanGc,
547) -> Result<DomRoot<CryptoKey>, Error> {
548    generate_key_aes(
549        global,
550        normalized_algorithm,
551        extractable,
552        usages,
553        ALG_AES_GCM,
554        &[
555            KeyUsage::Encrypt,
556            KeyUsage::Decrypt,
557            KeyUsage::WrapKey,
558            KeyUsage::UnwrapKey,
559        ],
560        can_gc,
561    )
562}
563
564/// <https://w3c.github.io/webcrypto/#aes-gcm-operations-import-key>
565pub(crate) fn import_key_aes_gcm(
566    global: &GlobalScope,
567    format: KeyFormat,
568    key_data: &[u8],
569    extractable: bool,
570    usages: Vec<KeyUsage>,
571    can_gc: CanGc,
572) -> Result<DomRoot<CryptoKey>, Error> {
573    import_key_aes(
574        global,
575        format,
576        key_data,
577        extractable,
578        usages,
579        ALG_AES_GCM,
580        can_gc,
581    )
582}
583
584/// <https://w3c.github.io/webcrypto/#aes-gcm-operations-export-key>
585pub(crate) fn export_key_aes_gcm(format: KeyFormat, key: &CryptoKey) -> Result<ExportedKey, Error> {
586    export_key_aes(format, key)
587}
588
589/// <https://w3c.github.io/webcrypto/#aes-gcm-operations-get-key-length>
590pub(crate) fn get_key_length_aes_gcm(
591    normalized_derived_key_algorithm: &SubtleAesDerivedKeyParams,
592) -> Result<Option<u32>, Error> {
593    get_key_length_aes(normalized_derived_key_algorithm)
594}
595
596/// <https://w3c.github.io/webcrypto/#aes-kw-operations-wrap-key>
597pub(crate) fn wrap_key_aes_kw(key: &CryptoKey, plaintext: &[u8]) -> Result<Vec<u8>, Error> {
598    // Step 1. If plaintext is not a multiple of 64 bits in length, then throw an OperationError.
599    if plaintext.len() % 8 != 0 {
600        return Err(Error::Operation);
601    }
602
603    // Step 2. Let ciphertext be the result of performing the Key Wrap operation described in
604    // Section 2.2.1 of [RFC3394] with plaintext as the plaintext to be wrapped and using the
605    // default Initial Value defined in Section 2.2.3.1 of the same document.
606    let key_data = key.handle().as_bytes();
607    let ciphertext = match key_data.len() {
608        16 => {
609            let key_array = GenericArray::from_slice(key_data);
610            let kek = KekAes128::new(key_array);
611            match kek.wrap_vec(plaintext) {
612                Ok(key) => key,
613                Err(_) => return Err(Error::Operation),
614            }
615        },
616        24 => {
617            let key_array = GenericArray::from_slice(key_data);
618            let kek = KekAes192::new(key_array);
619            match kek.wrap_vec(plaintext) {
620                Ok(key) => key,
621                Err(_) => return Err(Error::Operation),
622            }
623        },
624        32 => {
625            let key_array = GenericArray::from_slice(key_data);
626            let kek = KekAes256::new(key_array);
627            match kek.wrap_vec(plaintext) {
628                Ok(key) => key,
629                Err(_) => return Err(Error::Operation),
630            }
631        },
632        _ => return Err(Error::Operation),
633    };
634
635    // Step 3. Return ciphertext.
636    Ok(ciphertext)
637}
638
639/// <https://w3c.github.io/webcrypto/#aes-kw-operations-unwrap-key>
640pub(crate) fn unwrap_key_aes_kw(key: &CryptoKey, ciphertext: &[u8]) -> Result<Vec<u8>, Error> {
641    // Step 1. Let plaintext be the result of performing the Key Unwrap operation described in
642    // Section 2.2.2 of [RFC3394] with ciphertext as the input ciphertext and using the default
643    // Initial Value defined in Section 2.2.3.1 of the same document.
644    // Step 2. If the Key Unwrap operation returns an error, then throw an OperationError.
645    let key_data = key.handle().as_bytes();
646    let plaintext = match key_data.len() {
647        16 => {
648            let key_array = GenericArray::from_slice(key_data);
649            let kek = KekAes128::new(key_array);
650            match kek.unwrap_vec(ciphertext) {
651                Ok(key) => key,
652                Err(_) => return Err(Error::Operation),
653            }
654        },
655        24 => {
656            let key_array = GenericArray::from_slice(key_data);
657            let kek = KekAes192::new(key_array);
658            match kek.unwrap_vec(ciphertext) {
659                Ok(key) => key,
660                Err(_) => return Err(Error::Operation),
661            }
662        },
663        32 => {
664            let key_array = GenericArray::from_slice(key_data);
665            let kek = KekAes256::new(key_array);
666            match kek.unwrap_vec(ciphertext) {
667                Ok(key) => key,
668                Err(_) => return Err(Error::Operation),
669            }
670        },
671        _ => return Err(Error::Operation),
672    };
673
674    // Step 3. Return plaintext.
675    Ok(plaintext)
676}
677
678/// <https://w3c.github.io/webcrypto/#aes-kw-operations-generate-key>
679pub(crate) fn generate_key_aes_kw(
680    global: &GlobalScope,
681    normalized_algorithm: &SubtleAesKeyGenParams,
682    extractable: bool,
683    usages: Vec<KeyUsage>,
684    can_gc: CanGc,
685) -> Result<DomRoot<CryptoKey>, Error> {
686    generate_key_aes(
687        global,
688        normalized_algorithm,
689        extractable,
690        usages,
691        ALG_AES_KW,
692        &[KeyUsage::WrapKey, KeyUsage::UnwrapKey],
693        can_gc,
694    )
695}
696
697/// <https://w3c.github.io/webcrypto/#aes-kw-operations-import-key>
698pub(crate) fn import_key_aes_kw(
699    global: &GlobalScope,
700    format: KeyFormat,
701    key_data: &[u8],
702    extractable: bool,
703    usages: Vec<KeyUsage>,
704    can_gc: CanGc,
705) -> Result<DomRoot<CryptoKey>, Error> {
706    import_key_aes(
707        global,
708        format,
709        key_data,
710        extractable,
711        usages,
712        ALG_AES_KW,
713        can_gc,
714    )
715}
716
717/// <https://w3c.github.io/webcrypto/#aes-kw-operations-export-key>
718pub(crate) fn export_key_aes_kw(format: KeyFormat, key: &CryptoKey) -> Result<ExportedKey, Error> {
719    export_key_aes(format, key)
720}
721
722/// <https://w3c.github.io/webcrypto/#aes-kw-operations-get-key-length>
723pub(crate) fn get_key_length_aes_kw(
724    normalized_derived_key_algorithm: &SubtleAesDerivedKeyParams,
725) -> Result<Option<u32>, Error> {
726    get_key_length_aes(normalized_derived_key_algorithm)
727}
728
729/// Helper function for
730/// <https://w3c.github.io/webcrypto/#aes-ctr-operations-generate-key>
731/// <https://w3c.github.io/webcrypto/#aes-cbc-operations-generate-key>
732/// <https://w3c.github.io/webcrypto/#aes-gcm-operations-generate-key>
733/// <https://w3c.github.io/webcrypto/#aes-kw-operations-generate-key>
734#[allow(clippy::too_many_arguments)]
735fn generate_key_aes(
736    global: &GlobalScope,
737    normalized_algorithm: &SubtleAesKeyGenParams,
738    extractable: bool,
739    usages: Vec<KeyUsage>,
740    alg_name: &str,
741    allowed_usages: &[KeyUsage],
742    can_gc: CanGc,
743) -> Result<DomRoot<CryptoKey>, Error> {
744    // Step 1. If usages contains any entry which is not one of allowed_usages, then throw a SyntaxError.
745    if usages.iter().any(|usage| !allowed_usages.contains(usage)) {
746        return Err(Error::Syntax(None));
747    }
748
749    // Step 2. If the length member of normalizedAlgorithm is not equal to one of 128, 192 or 256,
750    // then throw an OperationError.
751    if !matches!(normalized_algorithm.length, 128 | 192 | 256) {
752        return Err(Error::Operation);
753    }
754
755    // Step 3. Generate an AES key of length equal to the length member of normalizedAlgorithm.
756    // Step 4. If the key generation step fails, then throw an OperationError.
757    let mut rand = vec![0; normalized_algorithm.length as usize / 8];
758    if OsRng.try_fill_bytes(&mut rand).is_err() {
759        return Err(Error::JSFailed);
760    }
761
762    let handle = match normalized_algorithm.length {
763        128 => Handle::Aes128(rand),
764        192 => Handle::Aes192(rand),
765        256 => Handle::Aes256(rand),
766        _ => return Err(Error::Operation),
767    };
768
769    // Step 6. Let algorithm be a new AesKeyAlgorithm.
770    // Step 7. Set the name attribute of algorithm to alg_name.
771    // Step 8. Set the length attribute of algorithm to equal the length member of normalizedAlgorithm.
772    let algorithm = SubtleAesKeyAlgorithm {
773        name: alg_name.to_string(),
774        length: normalized_algorithm.length,
775    };
776
777    // Step 5. Let key be a new CryptoKey object representing the generated AES key.
778    // Step 9. Set the [[type]] internal slot of key to "secret".
779    // Step 10. Set the [[algorithm]] internal slot of key to algorithm.
780    // Step 11. Set the [[extractable]] internal slot of key to be extractable.
781    // Step 12. Set the [[usages]] internal slot of key to be usages.
782    let crypto_key = CryptoKey::new(
783        global,
784        KeyType::Secret,
785        extractable,
786        KeyAlgorithmAndDerivatives::AesKeyAlgorithm(algorithm),
787        usages,
788        handle,
789        can_gc,
790    );
791
792    // Step 13. Return key.
793    Ok(crypto_key)
794}
795
796/// Helper function for
797/// <https://w3c.github.io/webcrypto/#aes-ctr-operations-import-key>
798/// <https://w3c.github.io/webcrypto/#aes-cbc-operations-import-key>
799/// <https://w3c.github.io/webcrypto/#aes-gcm-operations-import-key>
800/// <https://w3c.github.io/webcrypto/#aes-kw-operations-import-key>
801fn import_key_aes(
802    global: &GlobalScope,
803    format: KeyFormat,
804    key_data: &[u8],
805    extractable: bool,
806    usages: Vec<KeyUsage>,
807    alg_name: &str,
808    can_gc: CanGc,
809) -> Result<DomRoot<CryptoKey>, Error> {
810    // Step 1. If usages contains an entry which is not one of "encrypt", "decrypt", "wrapKey" or
811    // "unwrapKey", then throw a SyntaxError.
812    if usages.iter().any(|usage| {
813        !matches!(
814            usage,
815            KeyUsage::Encrypt | KeyUsage::Decrypt | KeyUsage::WrapKey | KeyUsage::UnwrapKey
816        )
817    }) || usages.is_empty()
818    {
819        return Err(Error::Syntax(None));
820    }
821
822    // Step 2.
823    let data;
824    match format {
825        // If format is "raw":
826        KeyFormat::Raw => {
827            // Step 2.1. Let data be keyData.
828            data = key_data.to_vec();
829
830            // Step 2.2. If the length in bits of data is not 128, 192 or 256 then throw a DataError.
831            if !matches!(data.len() * 8, 128 | 192 | 256) {
832                return Err(Error::Data);
833            }
834        },
835        // If format is "jwk":
836        KeyFormat::Jwk => {
837            // Step 2.1. If keyData is a JsonWebKey dictionary: Let jwk equal keyData.
838            // Otherwise: Throw a DataError.
839            // NOTE: Deserialize keyData to JsonWebKey dictionary by calling JsonWebKey::parse
840            let jwk = JsonWebKey::parse(GlobalScope::get_cx(), key_data)?;
841
842            // Step 2.2. If the kty field of jwk is not "oct", then throw a DataError.
843            if jwk.kty.as_ref().is_none_or(|kty| kty != "oct") {
844                return Err(Error::Data);
845            }
846
847            // Step 2.3. If jwk does not meet the requirements of Section 6.4 of JSON Web
848            // Algorithms [JWA], then throw a DataError.
849            // NOTE: Done by Step 2.4 and 2.5.
850
851            // Step 2.4. Let data be the byte sequence obtained by decoding the k field of jwk.
852            data = Base64UrlUnpadded::decode_vec(&jwk.k.as_ref().ok_or(Error::Data)?.str())
853                .map_err(|_| Error::Data)?;
854
855            // NOTE: This function is shared by AES-CBC, AES-CTR, AES-GCM and AES-KW.
856            // Different static texts are used in different AES types, in the following step.
857            let alg_matching = match alg_name {
858                ALG_AES_CBC => ["A128CBC", "A192CBC", "A256CBC"],
859                ALG_AES_CTR => ["A128CTR", "A192CTR", "A256CTR"],
860                ALG_AES_GCM => ["A128GCM", "A192GCM", "A256GCM"],
861                ALG_AES_KW => ["A128KW", "A192KW", "A256KW"],
862                _ => unreachable!(),
863            };
864
865            // Step 2.5.
866            match data.len() * 8 {
867                // If the length in bits of data is 128:
868                128 => {
869                    // If the alg field of jwk is present, and is not "A128CBC", then throw a DataError.
870                    // If the alg field of jwk is present, and is not "A128CTR", then throw a DataError.
871                    // If the alg field of jwk is present, and is not "A128GCM", then throw a DataError.
872                    // If the alg field of jwk is present, and is not "A128KW", then throw a DataError.
873                    // NOTE: Only perform the step of the corresponding AES type.
874                    if jwk.alg.as_ref().is_some_and(|alg| alg != alg_matching[0]) {
875                        return Err(Error::Data);
876                    }
877                },
878                // If the length in bits of data is 192:
879                192 => {
880                    // If the alg field of jwk is present, and is not "A192CBC", then throw a DataError.
881                    // If the alg field of jwk is present, and is not "A192CTR", then throw a DataError.
882                    // If the alg field of jwk is present, and is not "A192GCM", then throw a DataError.
883                    // If the alg field of jwk is present, and is not "A192KW", then throw a DataError.
884                    // NOTE: Only perform the step of the corresponding AES type.
885                    if jwk.alg.as_ref().is_some_and(|alg| alg != alg_matching[1]) {
886                        return Err(Error::Data);
887                    }
888                },
889                // If the length in bits of data is 256:
890                256 => {
891                    // If the alg field of jwk is present, and is not "A256CBC", then throw a DataError.
892                    // If the alg field of jwk is present, and is not "A256CTR", then throw a DataError.
893                    // If the alg field of jwk is present, and is not "A256GCM", then throw a DataError.
894                    // If the alg field of jwk is present, and is not "A256KW", then throw a DataError.
895                    // NOTE: Only perform the step of the corresponding AES type.
896                    if jwk.alg.as_ref().is_some_and(|alg| alg != alg_matching[2]) {
897                        return Err(Error::Data);
898                    }
899                },
900                // Otherwise:
901                _ => {
902                    // throw a DataError.
903                    return Err(Error::Data);
904                },
905            }
906
907            // Step 2.6. If usages is non-empty and the use field of jwk is present and is not
908            // "enc", then throw a DataError.
909            if !usages.is_empty() && jwk.use_.as_ref().is_some_and(|use_| use_ != "enc") {
910                return Err(Error::Data);
911            }
912
913            // Step 2.7. If the key_ops field of jwk is present, and is invalid according to the
914            // requirements of JSON Web Key [JWK] or does not contain all of the specified usages
915            // values, then throw a DataError.
916            jwk.check_key_ops(&usages)?;
917
918            // Step 2.8. If the ext field of jwk is present and has the value false and extractable
919            // is true, then throw a DataError.
920            if jwk.ext.is_some_and(|ext| !ext) && extractable {
921                return Err(Error::Data);
922            }
923        },
924        // Otherwise:
925        _ => {
926            // throw a NotSupportedError
927            return Err(Error::NotSupported);
928        },
929    };
930
931    // Step 5. Let algorithm be a new AesKeyAlgorithm.
932    // Step 6. Set the name attribute of algorithm to alg_name.
933    // Step 7. Set the length attribute of algorithm to the length, in bits, of data.
934    let algorithm = SubtleAesKeyAlgorithm {
935        name: alg_name.to_string(),
936        length: (data.len() * 8) as u16,
937    };
938
939    // Step 3. Let key be a new CryptoKey object representing an AES key with value data.
940    // Step 4. Set the [[type]] internal slot of key to "secret".
941    // Step 8. Set the [[algorithm]] internal slot of key to algorithm.
942    let handle = match data.len() * 8 {
943        128 => Handle::Aes128(data.to_vec()),
944        192 => Handle::Aes192(data.to_vec()),
945        256 => Handle::Aes256(data.to_vec()),
946        _ => {
947            return Err(Error::Data);
948        },
949    };
950    let key = CryptoKey::new(
951        global,
952        KeyType::Secret,
953        extractable,
954        KeyAlgorithmAndDerivatives::AesKeyAlgorithm(algorithm),
955        usages,
956        handle,
957        can_gc,
958    );
959
960    // Step 9. Return key.
961    Ok(key)
962}
963
964/// Helper function for
965/// <https://w3c.github.io/webcrypto/#aes-ctr-operations-export-key>
966/// <https://w3c.github.io/webcrypto/#aes-cbc-operations-export-key>
967/// <https://w3c.github.io/webcrypto/#aes-gcm-operations-export-key>
968/// <https://w3c.github.io/webcrypto/#aes-kw-operations-export-key>
969fn export_key_aes(format: KeyFormat, key: &CryptoKey) -> Result<ExportedKey, Error> {
970    // Step 1. If the underlying cryptographic key material represented by the [[handle]]
971    // internal slot of key cannot be accessed, then throw an OperationError.
972    // NOTE: key.handle() guarantees access.
973
974    // Step 2.
975    let result;
976    match format {
977        // If format is "raw":
978        KeyFormat::Raw => match key.handle() {
979            // Step 2.1. Let data be a byte sequence containing the raw octets of the key
980            // represented by the [[handle]] internal slot of key.
981            // Step 2.2. Let result be data.
982            Handle::Aes128(key_data) => {
983                result = ExportedKey::Raw(key_data.clone());
984            },
985            Handle::Aes192(key_data) => {
986                result = ExportedKey::Raw(key_data.clone());
987            },
988            Handle::Aes256(key_data) => {
989                result = ExportedKey::Raw(key_data.clone());
990            },
991            _ => unreachable!(),
992        },
993        // If format is "jwk":
994        KeyFormat::Jwk => {
995            // Step 2.3. Set the k attribute of jwk to be a string containing the raw octets of
996            // the key represented by the [[handle]] internal slot of key, encoded according to
997            // Section 6.4 of JSON Web Algorithms [JWA].
998            let k = match key.handle() {
999                Handle::Aes128(key) => Base64UrlUnpadded::encode_string(key),
1000                Handle::Aes192(key) => Base64UrlUnpadded::encode_string(key),
1001                Handle::Aes256(key) => Base64UrlUnpadded::encode_string(key),
1002                _ => unreachable!(),
1003            };
1004
1005            // Step 2.4.
1006            // If the length attribute of key is 128: Set the alg attribute of jwk to the string "A128CTR".
1007            // If the length attribute of key is 192: Set the alg attribute of jwk to the string "A192CTR".
1008            // If the length attribute of key is 256: Set the alg attribute of jwk to the string "A256CTR".
1009            //
1010            // If the length attribute of key is 128: Set the alg attribute of jwk to the string "A128CTR".
1011            // If the length attribute of key is 192: Set the alg attribute of jwk to the string "A192CTR".
1012            // If the length attribute of key is 256: Set the alg attribute of jwk to the string "A256CTR".
1013            //
1014            // If the length attribute of key is 128: Set the alg attribute of jwk to the string "A128CTR".
1015            // If the length attribute of key is 192: Set the alg attribute of jwk to the string "A192CTR".
1016            // If the length attribute of key is 256: Set the alg attribute of jwk to the string "A256CTR".
1017            //
1018            // If the length attribute of key is 128: Set the alg attribute of jwk to the string "A128CTR".
1019            // If the length attribute of key is 192: Set the alg attribute of jwk to the string "A192CTR".
1020            // If the length attribute of key is 256: Set the alg attribute of jwk to the string "A256CTR".
1021            //
1022            // NOTE: Check key length via key.handle()
1023            let alg = match (key.handle(), key.algorithm().name()) {
1024                (Handle::Aes128(_), ALG_AES_CTR) => "A128CTR",
1025                (Handle::Aes192(_), ALG_AES_CTR) => "A192CTR",
1026                (Handle::Aes256(_), ALG_AES_CTR) => "A256CTR",
1027                (Handle::Aes128(_), ALG_AES_CBC) => "A128CBC",
1028                (Handle::Aes192(_), ALG_AES_CBC) => "A192CBC",
1029                (Handle::Aes256(_), ALG_AES_CBC) => "A256CBC",
1030                (Handle::Aes128(_), ALG_AES_GCM) => "A128GCM",
1031                (Handle::Aes192(_), ALG_AES_GCM) => "A192GCM",
1032                (Handle::Aes256(_), ALG_AES_GCM) => "A256GCM",
1033                (Handle::Aes128(_), ALG_AES_KW) => "A128KW",
1034                (Handle::Aes192(_), ALG_AES_KW) => "A192KW",
1035                (Handle::Aes256(_), ALG_AES_KW) => "A256KW",
1036                _ => unreachable!(),
1037            };
1038
1039            // Step 2.5. Set the key_ops attribute of jwk to equal the [[usages]] internal slot of key.
1040            let key_ops = key
1041                .usages()
1042                .iter()
1043                .map(|usage| DOMString::from(usage.as_str()))
1044                .collect::<Vec<DOMString>>();
1045
1046            // Step 2.1. Let jwk be a new JsonWebKey dictionary.
1047            // Step 2.2. Set the kty attribute of jwk to the string "oct".
1048            // Step 2.6. Set the ext attribute of jwk to equal the [[extractable]] internal slot of key.
1049            let jwk = JsonWebKey {
1050                kty: Some(DOMString::from("oct")),
1051                k: Some(DOMString::from(k)),
1052                alg: Some(DOMString::from(alg)),
1053                key_ops: Some(key_ops),
1054                ext: Some(key.Extractable()),
1055                ..Default::default()
1056            };
1057
1058            // Step 2.7. Let result be jwk.
1059            result = ExportedKey::Jwk(Box::new(jwk));
1060        },
1061        // Otherwise:
1062        _ => {
1063            // throw a NotSupportedError.
1064            return Err(Error::NotSupported);
1065        },
1066    };
1067
1068    // Step 3. Return result.
1069    Ok(result)
1070}
1071
1072/// Helper function for
1073/// <https://w3c.github.io/webcrypto/#aes-ctr-operations-get-key-length>
1074/// <https://w3c.github.io/webcrypto/#aes-cbc-operations-get-key-length>
1075/// <https://w3c.github.io/webcrypto/#aes-gcm-operations-get-key-length>
1076/// <https://w3c.github.io/webcrypto/#aes-kw-operations-get-key-length>
1077pub(crate) fn get_key_length_aes(
1078    normalized_derived_key_algorithm: &SubtleAesDerivedKeyParams,
1079) -> Result<Option<u32>, Error> {
1080    // Step 1. If the length member of normalizedDerivedKeyAlgorithm is not 128, 192 or 256, then
1081    // throw a OperationError.
1082    if !matches!(normalized_derived_key_algorithm.length, 128 | 192 | 256) {
1083        return Err(Error::Operation);
1084    }
1085
1086    // Step 2. Return the length member of normalizedDerivedKeyAlgorithm.
1087    Ok(Some(normalized_derived_key_algorithm.length as u32))
1088}