script/dom/subtlecrypto/
aes_gcm_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::crypto_common::Key;
6use aes::cipher::typenum::{U12, U13, U14, U15, U16, U32};
7use aes::{Aes128, Aes192, Aes256};
8use aes_gcm::aead::AeadMutInPlace;
9use aes_gcm::{AesGcm, KeyInit};
10use cipher::{ArrayLength, BlockCipher, BlockEncrypt, BlockSizeUser};
11
12use crate::dom::bindings::codegen::Bindings::CryptoKeyBinding::{KeyType, KeyUsage};
13use crate::dom::bindings::codegen::Bindings::SubtleCryptoBinding::KeyFormat;
14use crate::dom::bindings::error::Error;
15use crate::dom::bindings::root::DomRoot;
16use crate::dom::cryptokey::{CryptoKey, Handle};
17use crate::dom::globalscope::GlobalScope;
18use crate::dom::subtlecrypto::aes_common::AesAlgorithm;
19use crate::dom::subtlecrypto::{
20    ALG_AES_GCM, ExportedKey, KeyAlgorithmAndDerivatives, SubtleAesDerivedKeyParams,
21    SubtleAesGcmParams, SubtleAesKeyAlgorithm, SubtleAesKeyGenParams, aes_common,
22};
23use crate::script_runtime::CanGc;
24
25/// <https://w3c.github.io/webcrypto/#aes-gcm-operations-encrypt>
26pub(crate) fn encrypt(
27    normalized_algorithm: &SubtleAesGcmParams,
28    key: &CryptoKey,
29    plaintext: &[u8],
30) -> Result<Vec<u8>, Error> {
31    // Step 1. If plaintext has a length greater than 2^39 - 256 bytes, then throw an
32    // OperationError.
33    if plaintext.len() as u64 > (1 << 39) - 256 {
34        return Err(Error::Operation(Some("The plaintext is too long".into())));
35    }
36
37    // Step 2. If the iv member of normalizedAlgorithm has a length greater than 2^64 - 1 bytes,
38    // then throw an OperationError.
39    if normalized_algorithm.iv.len() > u64::MAX as usize {
40        return Err(Error::Operation(Some(
41            "The iv member of normalizedAlgorithm is too long".into(),
42        )));
43    }
44
45    // Step 3. If the additionalData member of normalizedAlgorithm is present and has a length
46    // greater than 2^64 - 1 bytes, then throw an OperationError.
47    if normalized_algorithm
48        .additional_data
49        .as_ref()
50        .is_some_and(|data| data.len() > u64::MAX as usize)
51    {
52        return Err(Error::Operation(Some(
53            "The additional authentication data is too long".into(),
54        )));
55    }
56
57    // Step 4.
58    // If the tagLength member of normalizedAlgorithm is not present:
59    //     Let tagLength be 128.
60    // If the tagLength member of normalizedAlgorithm is one of 32, 64, 96, 104, 112, 120 or 128:
61    //     Let tagLength be equal to the tagLength member of normalizedAlgorithm
62    // Otherwise:
63    //     throw an OperationError.
64    let tag_length = match normalized_algorithm.tag_length {
65        None => 128,
66        Some(tag_length) if matches!(tag_length, 32 | 64 | 96 | 104 | 112 | 120 | 128) => {
67            tag_length
68        },
69        _ => {
70            return Err(Error::Operation(Some(
71                "The tagLength member of normalizedAlgorithm is present, \
72                and not one of 32, 64, 96, 104, 112, 120 or 128"
73                    .to_string(),
74            )));
75        },
76    };
77
78    // Step 5. Let additionalData be the additionalData member of normalizedAlgorithm if present or
79    // an empty byte sequence otherwise.
80    let additional_data = normalized_algorithm
81        .additional_data
82        .as_deref()
83        .unwrap_or_default();
84
85    // Step 6. Let C and T be the outputs that result from performing the Authenticated Encryption
86    // Function described in Section 7.1 of [NIST-SP800-38D] using AES as the block cipher, the iv
87    // member of normalizedAlgorithm as the IV input parameter, additionalData as the A input
88    // parameter, tagLength as the t pre-requisite and plaintext as the input plaintext.
89    // Step 7. Let ciphertext be equal to C | T, where '|' denotes concatenation.
90    //
91    // NOTE: We currently support:
92    // - IV: 96-bit, 128-bit, 256-bit
93    // - Tag length: 32-bit, 64-bit, 96-bit, 104-bit, 112-bit, 120-bit, 128-bit
94    //
95    // NOTE: The crate `aes-gcm` does not directly support 32-bit and 64-bit tag length. Our
96    // workaround is to perform the Authenticated Encryption Function using 96-bit tag length, and
97    // then remove the last 64 bits for 32-bit tag length, and remove last 32 bits for 64-bit tag
98    // length, from the ciphertext.
99    let iv = normalized_algorithm.iv.as_slice();
100    let ciphertext = match key.handle() {
101        Handle::Aes128Key(key) => match (iv.len(), tag_length) {
102            // 96-bit nonce
103            (12, 32) => {
104                let mut ciphertext =
105                    gcm_encrypt::<Aes128, U12, U12>(key, plaintext, iv, additional_data)?;
106                ciphertext.truncate(ciphertext.len() - 8);
107                ciphertext
108            },
109            (12, 64) => {
110                let mut ciphertext =
111                    gcm_encrypt::<Aes128, U12, U12>(key, plaintext, iv, additional_data)?;
112                ciphertext.truncate(ciphertext.len() - 4);
113                ciphertext
114            },
115            (12, 96) => gcm_encrypt::<Aes128, U12, U12>(key, plaintext, iv, additional_data)?,
116            (12, 104) => gcm_encrypt::<Aes128, U12, U13>(key, plaintext, iv, additional_data)?,
117            (12, 112) => gcm_encrypt::<Aes128, U12, U14>(key, plaintext, iv, additional_data)?,
118            (12, 120) => gcm_encrypt::<Aes128, U12, U15>(key, plaintext, iv, additional_data)?,
119            (12, 128) => gcm_encrypt::<Aes128, U12, U16>(key, plaintext, iv, additional_data)?,
120
121            // 128-bit nonce
122            (16, 32) => {
123                let mut ciphertext =
124                    gcm_encrypt::<Aes128, U16, U12>(key, plaintext, iv, additional_data)?;
125                ciphertext.truncate(ciphertext.len() - 8);
126                ciphertext
127            },
128            (16, 64) => {
129                let mut ciphertext =
130                    gcm_encrypt::<Aes128, U16, U12>(key, plaintext, iv, additional_data)?;
131                ciphertext.truncate(ciphertext.len() - 4);
132                ciphertext
133            },
134            (16, 96) => gcm_encrypt::<Aes128, U16, U12>(key, plaintext, iv, additional_data)?,
135            (16, 104) => gcm_encrypt::<Aes128, U16, U13>(key, plaintext, iv, additional_data)?,
136            (16, 112) => gcm_encrypt::<Aes128, U16, U14>(key, plaintext, iv, additional_data)?,
137            (16, 120) => gcm_encrypt::<Aes128, U16, U15>(key, plaintext, iv, additional_data)?,
138            (16, 128) => gcm_encrypt::<Aes128, U16, U16>(key, plaintext, iv, additional_data)?,
139
140            // 256-bit nonce
141            (32, 32) => {
142                let mut ciphertext =
143                    gcm_encrypt::<Aes128, U32, U12>(key, plaintext, iv, additional_data)?;
144                ciphertext.truncate(ciphertext.len() - 8);
145                ciphertext
146            },
147            (32, 64) => {
148                let mut ciphertext =
149                    gcm_encrypt::<Aes128, U32, U12>(key, plaintext, iv, additional_data)?;
150                ciphertext.truncate(ciphertext.len() - 4);
151                ciphertext
152            },
153            (32, 96) => gcm_encrypt::<Aes128, U32, U12>(key, plaintext, iv, additional_data)?,
154            (32, 104) => gcm_encrypt::<Aes128, U32, U13>(key, plaintext, iv, additional_data)?,
155            (32, 112) => gcm_encrypt::<Aes128, U32, U14>(key, plaintext, iv, additional_data)?,
156            (32, 120) => gcm_encrypt::<Aes128, U32, U15>(key, plaintext, iv, additional_data)?,
157            (32, 128) => gcm_encrypt::<Aes128, U32, U16>(key, plaintext, iv, additional_data)?,
158            _ => {
159                return Err(Error::Operation(Some(format!(
160                    "Unsupported iv size ({}-bit) and/or tag length ({}-bit)",
161                    iv.len() * 8,
162                    tag_length
163                ))));
164            },
165        },
166        Handle::Aes192Key(key) => match (iv.len(), tag_length) {
167            // 96-bit nonce
168            (12, 32) => {
169                let mut ciphertext =
170                    gcm_encrypt::<Aes192, U12, U12>(key, plaintext, iv, additional_data)?;
171                ciphertext.truncate(ciphertext.len() - 8);
172                ciphertext
173            },
174            (12, 64) => {
175                let mut ciphertext =
176                    gcm_encrypt::<Aes192, U12, U12>(key, plaintext, iv, additional_data)?;
177                ciphertext.truncate(ciphertext.len() - 4);
178                ciphertext
179            },
180            (12, 96) => gcm_encrypt::<Aes192, U12, U12>(key, plaintext, iv, additional_data)?,
181            (12, 104) => gcm_encrypt::<Aes192, U12, U13>(key, plaintext, iv, additional_data)?,
182            (12, 112) => gcm_encrypt::<Aes192, U12, U14>(key, plaintext, iv, additional_data)?,
183            (12, 120) => gcm_encrypt::<Aes192, U12, U15>(key, plaintext, iv, additional_data)?,
184            (12, 128) => gcm_encrypt::<Aes192, U12, U16>(key, plaintext, iv, additional_data)?,
185
186            // 128-bit nonce
187            (16, 32) => {
188                let mut ciphertext =
189                    gcm_encrypt::<Aes192, U16, U12>(key, plaintext, iv, additional_data)?;
190                ciphertext.truncate(ciphertext.len() - 8);
191                ciphertext
192            },
193            (16, 64) => {
194                let mut ciphertext =
195                    gcm_encrypt::<Aes192, U16, U12>(key, plaintext, iv, additional_data)?;
196                ciphertext.truncate(ciphertext.len() - 4);
197                ciphertext
198            },
199            (16, 96) => gcm_encrypt::<Aes192, U16, U12>(key, plaintext, iv, additional_data)?,
200            (16, 104) => gcm_encrypt::<Aes192, U16, U13>(key, plaintext, iv, additional_data)?,
201            (16, 112) => gcm_encrypt::<Aes192, U16, U14>(key, plaintext, iv, additional_data)?,
202            (16, 120) => gcm_encrypt::<Aes192, U16, U15>(key, plaintext, iv, additional_data)?,
203            (16, 128) => gcm_encrypt::<Aes192, U16, U16>(key, plaintext, iv, additional_data)?,
204
205            // 256-bit nonce
206            (32, 32) => {
207                let mut ciphertext =
208                    gcm_encrypt::<Aes192, U32, U12>(key, plaintext, iv, additional_data)?;
209                ciphertext.truncate(ciphertext.len() - 8);
210                ciphertext
211            },
212            (32, 64) => {
213                let mut ciphertext =
214                    gcm_encrypt::<Aes192, U32, U12>(key, plaintext, iv, additional_data)?;
215                ciphertext.truncate(ciphertext.len() - 4);
216                ciphertext
217            },
218            (32, 96) => gcm_encrypt::<Aes192, U32, U12>(key, plaintext, iv, additional_data)?,
219            (32, 104) => gcm_encrypt::<Aes192, U32, U13>(key, plaintext, iv, additional_data)?,
220            (32, 112) => gcm_encrypt::<Aes192, U32, U14>(key, plaintext, iv, additional_data)?,
221            (32, 120) => gcm_encrypt::<Aes192, U32, U15>(key, plaintext, iv, additional_data)?,
222            (32, 128) => gcm_encrypt::<Aes192, U32, U16>(key, plaintext, iv, additional_data)?,
223            _ => {
224                return Err(Error::Operation(Some(format!(
225                    "Unsupported iv size ({}-bit) and/or tag length ({}-bit)",
226                    iv.len() * 8,
227                    tag_length
228                ))));
229            },
230        },
231        Handle::Aes256Key(key) => match (iv.len(), tag_length) {
232            // 96-bit nonce
233            (12, 32) => {
234                let mut ciphertext =
235                    gcm_encrypt::<Aes256, U12, U12>(key, plaintext, iv, additional_data)?;
236                ciphertext.truncate(ciphertext.len() - 8);
237                ciphertext
238            },
239            (12, 64) => {
240                let mut ciphertext =
241                    gcm_encrypt::<Aes256, U12, U12>(key, plaintext, iv, additional_data)?;
242                ciphertext.truncate(ciphertext.len() - 4);
243                ciphertext
244            },
245            (12, 96) => gcm_encrypt::<Aes256, U12, U12>(key, plaintext, iv, additional_data)?,
246            (12, 104) => gcm_encrypt::<Aes256, U12, U13>(key, plaintext, iv, additional_data)?,
247            (12, 112) => gcm_encrypt::<Aes256, U12, U14>(key, plaintext, iv, additional_data)?,
248            (12, 120) => gcm_encrypt::<Aes256, U12, U15>(key, plaintext, iv, additional_data)?,
249            (12, 128) => gcm_encrypt::<Aes256, U12, U16>(key, plaintext, iv, additional_data)?,
250
251            // 128-bit nonce
252            (16, 32) => {
253                let mut ciphertext =
254                    gcm_encrypt::<Aes256, U16, U12>(key, plaintext, iv, additional_data)?;
255                ciphertext.truncate(ciphertext.len() - 8);
256                ciphertext
257            },
258            (16, 64) => {
259                let mut ciphertext =
260                    gcm_encrypt::<Aes256, U16, U12>(key, plaintext, iv, additional_data)?;
261                ciphertext.truncate(ciphertext.len() - 4);
262                ciphertext
263            },
264            (16, 96) => gcm_encrypt::<Aes256, U16, U12>(key, plaintext, iv, additional_data)?,
265            (16, 104) => gcm_encrypt::<Aes256, U16, U13>(key, plaintext, iv, additional_data)?,
266            (16, 112) => gcm_encrypt::<Aes256, U16, U14>(key, plaintext, iv, additional_data)?,
267            (16, 120) => gcm_encrypt::<Aes256, U16, U15>(key, plaintext, iv, additional_data)?,
268            (16, 128) => gcm_encrypt::<Aes256, U16, U16>(key, plaintext, iv, additional_data)?,
269
270            // 256-bit nonce
271            (32, 32) => {
272                let mut ciphertext =
273                    gcm_encrypt::<Aes256, U32, U12>(key, plaintext, iv, additional_data)?;
274                ciphertext.truncate(ciphertext.len() - 8);
275                ciphertext
276            },
277            (32, 64) => {
278                let mut ciphertext =
279                    gcm_encrypt::<Aes256, U32, U12>(key, plaintext, iv, additional_data)?;
280                ciphertext.truncate(ciphertext.len() - 4);
281                ciphertext
282            },
283            (32, 96) => gcm_encrypt::<Aes256, U32, U12>(key, plaintext, iv, additional_data)?,
284            (32, 104) => gcm_encrypt::<Aes256, U32, U13>(key, plaintext, iv, additional_data)?,
285            (32, 112) => gcm_encrypt::<Aes256, U32, U14>(key, plaintext, iv, additional_data)?,
286            (32, 120) => gcm_encrypt::<Aes256, U32, U15>(key, plaintext, iv, additional_data)?,
287            (32, 128) => gcm_encrypt::<Aes256, U32, U16>(key, plaintext, iv, additional_data)?,
288            _ => {
289                return Err(Error::Operation(Some(format!(
290                    "Unsupported iv size ({}-bit) and/or tag length ({}-bit)",
291                    iv.len() * 8,
292                    tag_length
293                ))));
294            },
295        },
296        _ => {
297            return Err(Error::Operation(Some(
298                "The key handle is not representing an AES key".to_string(),
299            )));
300        },
301    };
302
303    // Step 8. Return ciphertext.
304    Ok(ciphertext)
305}
306
307/// Helper for Step 6 and 7 of <https://w3c.github.io/webcrypto/#aes-gcm-operations-encrypt>
308fn gcm_encrypt<Aes, NonceSize, TagSize>(
309    key: &Key<Aes>,
310    plaintext: &[u8],
311    iv: &[u8],
312    additional_data: &[u8],
313) -> Result<Vec<u8>, Error>
314where
315    Aes: BlockCipher + BlockSizeUser<BlockSize = U16> + BlockEncrypt + KeyInit,
316    NonceSize: ArrayLength<u8>,
317    TagSize: aes_gcm::TagSize,
318{
319    let mut ciphertext = plaintext.to_vec();
320
321    let mut cipher = AesGcm::<Aes, NonceSize, TagSize>::new(key);
322    cipher
323        .encrypt_in_place(iv.into(), additional_data, &mut ciphertext)
324        .map_err(|_| {
325            Error::Operation(Some(
326                "AES-GCM failed to perform the Authenticated Encryption Function".to_string(),
327            ))
328        })?;
329
330    Ok(ciphertext)
331}
332
333/// <https://w3c.github.io/webcrypto/#aes-gcm-operations-decrypt>
334pub(crate) fn decrypt(
335    normalized_algorithm: &SubtleAesGcmParams,
336    key: &CryptoKey,
337    ciphertext: &[u8],
338) -> Result<Vec<u8>, Error> {
339    // Step 1.
340    // If the tagLength member of normalizedAlgorithm is not present:
341    //     Let tagLength be 128.
342    // If the tagLength member of normalizedAlgorithm is one of 32, 64, 96, 104, 112, 120 or 128:
343    //     Let tagLength be equal to the tagLength member of normalizedAlgorithm
344    // Otherwise:
345    //     throw an OperationError.
346    let tag_length = match normalized_algorithm.tag_length {
347        None => 128,
348        Some(tag_length) if matches!(tag_length, 32 | 64 | 96 | 104 | 112 | 120 | 128) => {
349            tag_length
350        },
351        _ => {
352            return Err(Error::Operation(Some(
353                "The tagLength member of normalizedAlgorithm is present, \
354                and not one of 32, 64, 96, 104, 112, 120 or 128"
355                    .to_string(),
356            )));
357        },
358    };
359
360    // Step 2. If ciphertext has a length in bits less than tagLength, then throw an
361    // OperationError.
362    if ciphertext.len() * 8 < tag_length as usize {
363        return Err(Error::Operation(Some(
364            "The ciphertext is shorter than the tag".into(),
365        )));
366    }
367
368    // Step 2. If the iv member of normalizedAlgorithm has a length greater than 2^64 - 1 bytes,
369    // then throw an OperationError.
370    if normalized_algorithm.iv.len() > u64::MAX as usize {
371        return Err(Error::Operation(Some(
372            "The iv member of normalizedAlgorithm is too long".into(),
373        )));
374    }
375
376    // Step 3. If the additionalData member of normalizedAlgorithm is present and has a length
377    // greater than 2^64 - 1 bytes, then throw an OperationError.
378    if normalized_algorithm
379        .additional_data
380        .as_ref()
381        .is_some_and(|data| data.len() > u64::MAX as usize)
382    {
383        return Err(Error::Operation(Some(
384            "The additional authentication data is too long".into(),
385        )));
386    }
387
388    // Step 5. Let tag be the last tagLength bits of ciphertext.
389    // Step 6. Let actualCiphertext be the result of removing the last tagLength bits from
390    // ciphertext.
391    // NOTE: aes_gcm splits the ciphertext for us
392
393    // Step 7. Let additionalData be the additionalData member of normalizedAlgorithm if present or
394    // an empty byte sequence otherwise.
395    let additional_data = normalized_algorithm
396        .additional_data
397        .as_deref()
398        .unwrap_or_default();
399
400    // Step 8. Perform the Authenticated Decryption Function described in Section 7.2 of
401    // [NIST-SP800-38D] using AES as the block cipher, the iv member of normalizedAlgorithm as the
402    // IV input parameter, additionalData as the A input parameter, tagLength as the t
403    // pre-requisite, actualCiphertext as the input ciphertext, C and tag as the authentication
404    // tag, T.
405    //
406    // If the result of the algorithm is the indication of inauthenticity, "FAIL":
407    //     throw an OperationError
408    // Otherwise:
409    //     Let plaintext be the output P of the Authenticated Decryption Function.
410    //
411    // NOTE: We currently support:
412    // - IV: 96-bit, 128-bit, 256-bit
413    // - Tag length: 96-bit, 104-bit, 112-bit, 120-bit, 128-bit
414    //
415    // NOTE: We currently do not support 32-bit and 64-bit tag length in decryption since the crate
416    // `aes-gcm` does not support 32-bit and 64-bit tag length.
417    let iv = normalized_algorithm.iv.as_slice();
418    let plaintext = match key.handle() {
419        Handle::Aes128Key(key) => match (iv.len(), tag_length) {
420            // 96-bit nonce
421            (12, 96) => gcm_decrypt::<Aes128, U12, U12>(key, ciphertext, iv, additional_data)?,
422            (12, 104) => gcm_decrypt::<Aes128, U12, U13>(key, ciphertext, iv, additional_data)?,
423            (12, 112) => gcm_decrypt::<Aes128, U12, U14>(key, ciphertext, iv, additional_data)?,
424            (12, 120) => gcm_decrypt::<Aes128, U12, U15>(key, ciphertext, iv, additional_data)?,
425            (12, 128) => gcm_decrypt::<Aes128, U12, U16>(key, ciphertext, iv, additional_data)?,
426
427            // 128-bit nonce
428            (16, 96) => gcm_decrypt::<Aes128, U16, U12>(key, ciphertext, iv, additional_data)?,
429            (16, 104) => gcm_decrypt::<Aes128, U16, U13>(key, ciphertext, iv, additional_data)?,
430            (16, 112) => gcm_decrypt::<Aes128, U16, U14>(key, ciphertext, iv, additional_data)?,
431            (16, 120) => gcm_decrypt::<Aes128, U16, U15>(key, ciphertext, iv, additional_data)?,
432            (16, 128) => gcm_decrypt::<Aes128, U16, U16>(key, ciphertext, iv, additional_data)?,
433
434            // 256-bit nonce
435            (32, 96) => gcm_decrypt::<Aes128, U32, U12>(key, ciphertext, iv, additional_data)?,
436            (32, 104) => gcm_decrypt::<Aes128, U32, U13>(key, ciphertext, iv, additional_data)?,
437            (32, 112) => gcm_decrypt::<Aes128, U32, U14>(key, ciphertext, iv, additional_data)?,
438            (32, 120) => gcm_decrypt::<Aes128, U32, U15>(key, ciphertext, iv, additional_data)?,
439            (32, 128) => gcm_decrypt::<Aes128, U32, U16>(key, ciphertext, iv, additional_data)?,
440
441            _ => {
442                return Err(Error::Operation(Some(format!(
443                    "Unsupported iv size ({}-bit) and/or tag length ({}-bit)",
444                    iv.len() * 8,
445                    tag_length
446                ))));
447            },
448        },
449        Handle::Aes192Key(key) => match (iv.len(), tag_length) {
450            // 96-bit nonce
451            (12, 96) => gcm_decrypt::<Aes192, U12, U12>(key, ciphertext, iv, additional_data)?,
452            (12, 104) => gcm_decrypt::<Aes192, U12, U13>(key, ciphertext, iv, additional_data)?,
453            (12, 112) => gcm_decrypt::<Aes192, U12, U14>(key, ciphertext, iv, additional_data)?,
454            (12, 120) => gcm_decrypt::<Aes192, U12, U15>(key, ciphertext, iv, additional_data)?,
455            (12, 128) => gcm_decrypt::<Aes192, U12, U16>(key, ciphertext, iv, additional_data)?,
456
457            // 128-bit nonce
458            (16, 96) => gcm_decrypt::<Aes192, U16, U12>(key, ciphertext, iv, additional_data)?,
459            (16, 104) => gcm_decrypt::<Aes192, U16, U13>(key, ciphertext, iv, additional_data)?,
460            (16, 112) => gcm_decrypt::<Aes192, U16, U14>(key, ciphertext, iv, additional_data)?,
461            (16, 120) => gcm_decrypt::<Aes192, U16, U15>(key, ciphertext, iv, additional_data)?,
462            (16, 128) => gcm_decrypt::<Aes192, U16, U16>(key, ciphertext, iv, additional_data)?,
463
464            // 256-bit nonce
465            (32, 96) => gcm_decrypt::<Aes192, U32, U12>(key, ciphertext, iv, additional_data)?,
466            (32, 104) => gcm_decrypt::<Aes192, U32, U13>(key, ciphertext, iv, additional_data)?,
467            (32, 112) => gcm_decrypt::<Aes192, U32, U14>(key, ciphertext, iv, additional_data)?,
468            (32, 120) => gcm_decrypt::<Aes192, U32, U15>(key, ciphertext, iv, additional_data)?,
469            (32, 128) => gcm_decrypt::<Aes192, U32, U16>(key, ciphertext, iv, additional_data)?,
470
471            _ => {
472                return Err(Error::Operation(Some(format!(
473                    "Unsupported iv size ({}-bit) and/or tag length ({}-bit)",
474                    iv.len() * 8,
475                    tag_length
476                ))));
477            },
478        },
479        Handle::Aes256Key(key) => match (iv.len(), tag_length) {
480            // 96-bit nonce
481            (12, 96) => gcm_decrypt::<Aes256, U12, U12>(key, ciphertext, iv, additional_data)?,
482            (12, 104) => gcm_decrypt::<Aes256, U12, U13>(key, ciphertext, iv, additional_data)?,
483            (12, 112) => gcm_decrypt::<Aes256, U12, U14>(key, ciphertext, iv, additional_data)?,
484            (12, 120) => gcm_decrypt::<Aes256, U12, U15>(key, ciphertext, iv, additional_data)?,
485            (12, 128) => gcm_decrypt::<Aes256, U12, U16>(key, ciphertext, iv, additional_data)?,
486
487            // 128-bit nonce
488            (16, 96) => gcm_decrypt::<Aes256, U16, U12>(key, ciphertext, iv, additional_data)?,
489            (16, 104) => gcm_decrypt::<Aes256, U16, U13>(key, ciphertext, iv, additional_data)?,
490            (16, 112) => gcm_decrypt::<Aes256, U16, U14>(key, ciphertext, iv, additional_data)?,
491            (16, 120) => gcm_decrypt::<Aes256, U16, U15>(key, ciphertext, iv, additional_data)?,
492            (16, 128) => gcm_decrypt::<Aes256, U16, U16>(key, ciphertext, iv, additional_data)?,
493
494            // 256-bit nonce
495            (32, 96) => gcm_decrypt::<Aes256, U32, U12>(key, ciphertext, iv, additional_data)?,
496            (32, 104) => gcm_decrypt::<Aes256, U32, U13>(key, ciphertext, iv, additional_data)?,
497            (32, 112) => gcm_decrypt::<Aes256, U32, U14>(key, ciphertext, iv, additional_data)?,
498            (32, 120) => gcm_decrypt::<Aes256, U32, U15>(key, ciphertext, iv, additional_data)?,
499            (32, 128) => gcm_decrypt::<Aes256, U32, U16>(key, ciphertext, iv, additional_data)?,
500
501            _ => {
502                return Err(Error::Operation(Some(format!(
503                    "Unsupported iv size ({}-bit) and/or tag length ({}-bit)",
504                    iv.len() * 8,
505                    tag_length
506                ))));
507            },
508        },
509        _ => {
510            return Err(Error::Operation(Some(
511                "The key handle is not representing an AES key".to_string(),
512            )));
513        },
514    };
515
516    // Step 9. Return plaintext.
517    Ok(plaintext)
518}
519
520/// Helper for Step 8 of <https://w3c.github.io/webcrypto/#aes-gcm-operations-decrypt>
521fn gcm_decrypt<Aes, NonceSize, TagSize>(
522    key: &Key<Aes>,
523    ciphertext: &[u8],
524    iv: &[u8],
525    additional_data: &[u8],
526) -> Result<Vec<u8>, Error>
527where
528    Aes: BlockCipher + BlockSizeUser<BlockSize = U16> + BlockEncrypt + KeyInit,
529    NonceSize: ArrayLength<u8>,
530    TagSize: aes_gcm::TagSize,
531{
532    let mut plaintext = ciphertext.to_vec();
533
534    let mut cipher = AesGcm::<Aes, NonceSize, TagSize>::new(key);
535    cipher
536        .decrypt_in_place(iv.into(), additional_data, &mut plaintext)
537        .map_err(|_| {
538            Error::Operation(Some(
539                "AES-GCM failed to perform the Authenticated Decryption Function".to_string(),
540            ))
541        })?;
542
543    Ok(plaintext)
544}
545
546/// <https://w3c.github.io/webcrypto/#aes-gcm-operations-generate-key>
547pub(crate) fn generate_key(
548    global: &GlobalScope,
549    normalized_algorithm: &SubtleAesKeyGenParams,
550    extractable: bool,
551    usages: Vec<KeyUsage>,
552    can_gc: CanGc,
553) -> Result<DomRoot<CryptoKey>, Error> {
554    aes_common::generate_key(
555        AesAlgorithm::AesGcm,
556        global,
557        normalized_algorithm,
558        extractable,
559        usages,
560        can_gc,
561    )
562}
563
564/// <https://w3c.github.io/webcrypto/#aes-gcm-operations-import-key>
565pub(crate) fn import_key(
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    // Step 1. Let keyData be the key data to be imported.
574
575    // Step 2. If usages contains an entry which is not one of "encrypt", "decrypt", "wrapKey" or
576    // "unwrapKey", then throw a SyntaxError.
577    if usages.iter().any(|usage| {
578        !matches!(
579            usage,
580            KeyUsage::Encrypt | KeyUsage::Decrypt | KeyUsage::WrapKey | KeyUsage::UnwrapKey
581        )
582    }) {
583        return Err(Error::Syntax(Some(
584            "Usages contains an entry which is not one of \"encrypt\", \"decrypt\", \"wrapKey\" \
585            or \"unwrapKey\""
586                .to_string(),
587        )));
588    }
589
590    // Step 3.
591    let data = aes_common::import_key_from_key_data(
592        AesAlgorithm::AesGcm,
593        format,
594        key_data,
595        extractable,
596        &usages,
597    )?;
598
599    // Step 4. Let key be a new CryptoKey object representing an AES key with value data.
600    // Step 5. Let algorithm be a new AesKeyAlgorithm.
601    // Step 6. Set the name attribute of algorithm to "AES-GCM".
602    // Step 7. Set the length attribute of algorithm to the length, in bits, of data.
603    // Step 8. Set the [[algorithm]] internal slot of key to algorithm.
604    let handle = match data.len() {
605        16 => Handle::Aes128Key(Key::<Aes128>::clone_from_slice(&data)),
606        24 => Handle::Aes192Key(Key::<Aes192>::clone_from_slice(&data)),
607        32 => Handle::Aes256Key(Key::<Aes256>::clone_from_slice(&data)),
608        _ => {
609            return Err(Error::Data(Some(
610                "The length in bits of data is not 128, 192 or 256".to_string(),
611            )));
612        },
613    };
614    let algorithm = SubtleAesKeyAlgorithm {
615        name: ALG_AES_GCM.to_string(),
616        length: data.len() as u16 * 8,
617    };
618    let key = CryptoKey::new(
619        global,
620        KeyType::Secret,
621        extractable,
622        KeyAlgorithmAndDerivatives::AesKeyAlgorithm(algorithm),
623        usages,
624        handle,
625        can_gc,
626    );
627
628    // Step 9. Return key.
629    Ok(key)
630}
631
632/// <https://w3c.github.io/webcrypto/#aes-gcm-operations-export-key>
633pub(crate) fn export_key(format: KeyFormat, key: &CryptoKey) -> Result<ExportedKey, Error> {
634    aes_common::export_key(AesAlgorithm::AesGcm, format, key)
635}
636
637/// <https://w3c.github.io/webcrypto/#aes-gcm-operations-get-key-length>
638pub(crate) fn get_key_length(
639    normalized_derived_key_algorithm: &SubtleAesDerivedKeyParams,
640) -> Result<Option<u32>, Error> {
641    aes_common::get_key_length(normalized_derived_key_algorithm)
642}