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