diff options
author | Simon Wülker <simon.wuelker@arcor.de> | 2024-11-19 13:42:37 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-11-19 12:42:37 +0000 |
commit | 83f8e888189cc265e73d6a3849f7b8c71c080181 (patch) | |
tree | 80535c774a67ce136a03edff95aa4f98a91b294a /components/script/dom | |
parent | 5113147f801bc6ef1f1da7d181599f13b76c0a0d (diff) | |
download | servo-83f8e888189cc265e73d6a3849f7b8c71c080181.tar.gz servo-83f8e888189cc265e73d6a3849f7b8c71c080181.zip |
Implement AES-GCM support for subtlecrypto (#34269)
* Support normalizing AES-GCM for encryption
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
* Implement "encrypt" operation for AES-GCM
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
* Allow importing AES-GCM keys
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
* Implement AES-GCM decryption
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
* Allow normalizing AES-GCM for "generate key"
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
* Update WPT expectations
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
* fmt
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
* Fix clippy errors
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
* Remove silly checks
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
* Support AES-GCM 128-bit encryption with 128 bit IV
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
* Support AES-GCM with wrapKey/unwrapKey
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
* Update WPT expectations (again)
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
---------
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
Diffstat (limited to 'components/script/dom')
-rw-r--r-- | components/script/dom/subtlecrypto.rs | 358 | ||||
-rw-r--r-- | components/script/dom/webidls/SubtleCrypto.webidl | 7 |
2 files changed, 343 insertions, 22 deletions
diff --git a/components/script/dom/subtlecrypto.rs b/components/script/dom/subtlecrypto.rs index 9ff82f02945..e3638d34b04 100644 --- a/components/script/dom/subtlecrypto.rs +++ b/components/script/dom/subtlecrypto.rs @@ -10,8 +10,10 @@ use aes::cipher::block_padding::Pkcs7; use aes::cipher::generic_array::GenericArray; use aes::cipher::{BlockDecryptMut, BlockEncryptMut, KeyIvInit, StreamCipher}; use aes::{Aes128, Aes192, Aes256}; +use aes_gcm::{AeadInPlace, AesGcm, KeyInit}; use aes_kw::{KekAes128, KekAes192, KekAes256}; use base64::prelude::*; +use cipher::consts::{U12, U16, U32}; use dom_struct::dom_struct; use js::conversions::ConversionResult; use js::jsapi::{JSObject, JS_NewObject}; @@ -28,9 +30,10 @@ use crate::dom::bindings::codegen::Bindings::CryptoKeyBinding::{ CryptoKeyMethods, KeyType, KeyUsage, }; use crate::dom::bindings::codegen::Bindings::SubtleCryptoBinding::{ - AesCbcParams, AesCtrParams, AesDerivedKeyParams, AesKeyAlgorithm, AesKeyGenParams, Algorithm, - AlgorithmIdentifier, HkdfParams, HmacImportParams, HmacKeyAlgorithm, HmacKeyGenParams, - JsonWebKey, KeyAlgorithm, KeyFormat, Pbkdf2Params, SubtleCryptoMethods, + AesCbcParams, AesCtrParams, AesDerivedKeyParams, AesGcmParams, AesKeyAlgorithm, + AesKeyGenParams, Algorithm, AlgorithmIdentifier, HkdfParams, HmacImportParams, + HmacKeyAlgorithm, HmacKeyGenParams, JsonWebKey, KeyAlgorithm, KeyFormat, Pbkdf2Params, + SubtleCryptoMethods, }; use crate::dom::bindings::codegen::UnionTypes::{ ArrayBufferViewOrArrayBuffer, ArrayBufferViewOrArrayBufferOrJsonWebKey, @@ -108,6 +111,14 @@ type Aes128Ctr = ctr::Ctr64BE<Aes128>; type Aes192Ctr = ctr::Ctr64BE<Aes192>; type Aes256Ctr = ctr::Ctr64BE<Aes256>; +type Aes128Gcm96Iv = AesGcm<Aes128, U12>; +type Aes128Gcm128Iv = AesGcm<Aes128, U16>; +type Aes192Gcm96Iv = AesGcm<Aes192, U12>; +type Aes256Gcm96Iv = AesGcm<Aes256, U12>; +type Aes128Gcm256Iv = AesGcm<Aes128, U32>; +type Aes192Gcm256Iv = AesGcm<Aes192, U32>; +type Aes256Gcm256Iv = AesGcm<Aes256, U32>; + #[dom_struct] pub struct SubtleCrypto { reflector_: Reflector, @@ -838,7 +849,7 @@ impl SubtleCryptoMethods for SubtleCrypto { return; } let exported_key = match alg_name.as_str() { - ALG_AES_CBC | ALG_AES_CTR | ALG_AES_KW => subtle.export_key_aes(format, &key), + ALG_AES_CBC | ALG_AES_CTR | ALG_AES_KW | ALG_AES_GCM => subtle.export_key_aes(format, &key), _ => Err(Error::NotSupported), }; match exported_key { @@ -965,6 +976,11 @@ impl SubtleCryptoMethods for SubtleCrypto { ¶ms, &wrapping_key, &bytes, cx, array_buffer_ptr.handle_mut() ) }, + KeyWrapAlgorithm::AesGcm(params) => { + subtle.encrypt_aes_gcm( + ¶ms, &wrapping_key, &bytes, cx, array_buffer_ptr.handle_mut() + ) + }, }; match result { @@ -1047,6 +1063,11 @@ impl SubtleCryptoMethods for SubtleCrypto { ¶ms, &unwrapping_key, &wrapped_key_bytes, cx, array_buffer_ptr.handle_mut() ) }, + KeyWrapAlgorithm::AesGcm(params) => { + subtle.decrypt_aes_gcm( + ¶ms, &unwrapping_key, &wrapped_key_bytes, cx, array_buffer_ptr.handle_mut() + ) + }, }; let bytes = match result { @@ -1140,6 +1161,34 @@ impl From<RootedTraceableBox<AesCtrParams>> for SubtleAesCtrParams { } #[derive(Clone, Debug)] +pub struct SubtleAesGcmParams { + pub name: String, + pub iv: Vec<u8>, + pub additional_data: Option<Vec<u8>>, + pub tag_length: Option<u8>, +} + +impl From<RootedTraceableBox<AesGcmParams>> for SubtleAesGcmParams { + fn from(params: RootedTraceableBox<AesGcmParams>) -> Self { + let iv = match ¶ms.iv { + ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(), + ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(), + }; + let additional_data = params.additionalData.as_ref().map(|data| match data { + ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(), + ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(), + }); + + SubtleAesGcmParams { + name: params.parent.name.to_string(), + iv, + additional_data, + tag_length: params.tagLength, + } + } +} + +#[derive(Clone, Debug)] pub struct SubtleAesKeyGenParams { pub name: String, pub length: u16, @@ -1313,6 +1362,7 @@ enum ImportKeyAlgorithm { AesCbc, AesCtr, AesKw, + AesGcm, Hmac(SubtleHmacImportParams), Pbkdf2, Hkdf, @@ -1329,9 +1379,11 @@ enum DeriveBitsAlgorithm { /// A normalized algorithm returned by [`normalize_algorithm`] with operation `"encrypt"` or `"decrypt"` /// /// [`normalize_algorithm`]: https://w3c.github.io/webcrypto/#algorithm-normalization-normalize-an-algorithm +#[allow(clippy::enum_variant_names)] enum EncryptionAlgorithm { AesCbc(SubtleAesCbcParams), AesCtr(SubtleAesCtrParams), + AesGcm(SubtleAesGcmParams), } /// A normalized algorithm returned by [`normalize_algorithm`] with operation `"sign"` or `"verify"` @@ -1357,6 +1409,7 @@ enum KeyWrapAlgorithm { AesKw, AesCbc(SubtleAesCbcParams), AesCtr(SubtleAesCtrParams), + AesGcm(SubtleAesGcmParams), } macro_rules! value_from_js_object { @@ -1381,7 +1434,8 @@ fn normalize_algorithm_for_get_key_length( let name = algorithm.name.str(); let normalized_algorithm = if name.eq_ignore_ascii_case(ALG_AES_CBC) || - name.eq_ignore_ascii_case(ALG_AES_CTR) + name.eq_ignore_ascii_case(ALG_AES_CTR) || + name.eq_ignore_ascii_case(ALG_AES_GCM) { let params = value_from_js_object!(AesDerivedKeyParams, cx, value); GetKeyLengthAlgorithm::Aes(params.length) @@ -1454,6 +1508,7 @@ fn normalize_algorithm_for_import_key( ALG_AES_CBC => ImportKeyAlgorithm::AesCbc, ALG_AES_CTR => ImportKeyAlgorithm::AesCtr, ALG_AES_KW => ImportKeyAlgorithm::AesKw, + ALG_AES_GCM => ImportKeyAlgorithm::AesGcm, ALG_PBKDF2 => ImportKeyAlgorithm::Pbkdf2, ALG_HKDF => ImportKeyAlgorithm::Hkdf, _ => return Err(Error::NotSupported), @@ -1510,6 +1565,9 @@ fn normalize_algorithm_for_encrypt_or_decrypt( } else if name.eq_ignore_ascii_case(ALG_AES_CTR) { let params = value_from_js_object!(AesCtrParams, cx, value); EncryptionAlgorithm::AesCtr(params.into()) + } else if name.eq_ignore_ascii_case(ALG_AES_GCM) { + let params = value_from_js_object!(AesGcmParams, cx, value); + EncryptionAlgorithm::AesGcm(params.into()) } else { return Err(Error::NotSupported); }; @@ -1557,7 +1615,8 @@ fn normalize_algorithm_for_generate_key( let name = algorithm.name.str(); let normalized_algorithm = if name.eq_ignore_ascii_case(ALG_AES_CBC) || name.eq_ignore_ascii_case(ALG_AES_CTR) || - name.eq_ignore_ascii_case(ALG_AES_KW) + name.eq_ignore_ascii_case(ALG_AES_KW) || + name.eq_ignore_ascii_case(ALG_AES_GCM) { let params = value_from_js_object!(AesKeyGenParams, cx, value); KeyGenerationAlgorithm::Aes(params.into()) @@ -1603,6 +1662,13 @@ fn normalize_algorithm_for_key_wrap( rooted!(in(*cx) let value = ObjectValue(obj.get())); KeyWrapAlgorithm::AesCtr(value_from_js_object!(AesCtrParams, cx, value).into()) }, + ALG_AES_GCM => { + let AlgorithmIdentifier::Object(obj) = algorithm else { + return Err(Error::Syntax); + }; + rooted!(in(*cx) let value = ObjectValue(obj.get())); + KeyWrapAlgorithm::AesGcm(value_from_js_object!(AesGcmParams, cx, value).into()) + }, _ => return Err(Error::NotSupported), }; @@ -1730,6 +1796,250 @@ impl SubtleCrypto { Ok(ciphertext) } + /// <https://w3c.github.io/webcrypto/#aes-gcm-operations> + fn encrypt_aes_gcm( + &self, + params: &SubtleAesGcmParams, + key: &CryptoKey, + plaintext: &[u8], + cx: JSContext, + handle: MutableHandleObject, + ) -> Result<Vec<u8>, Error> { + // Step 1. If plaintext has a length greater than 2^39 - 256 bytes, then throw an OperationError. + if plaintext.len() as u64 > (2 << 39) - 256 { + return Err(Error::Operation); + } + + // Step 2. If the iv member of normalizedAlgorithm has a length greater than 2^64 - 1 bytes, + // then throw an OperationError. + // NOTE: servo does not currently support 128-bit platforms, so this can never happen + + // Step 3. If the additionalData member of normalizedAlgorithm is present and has a length greater than 2^64 - 1 + // bytes, then throw an OperationError. + if params + .additional_data + .as_ref() + .is_some_and(|data| data.len() > u64::MAX as usize) + { + return Err(Error::Operation); + } + + // Step 4. + let tag_length = match params.tag_length { + // If the tagLength member of normalizedAlgorithm is not present: + None => { + // Let tagLength be 128. + 128 + }, + // If the tagLength member of normalizedAlgorithm is one of 32, 64, 96, 104, 112, 120 or 128: + Some(length) if matches!(length, 32 | 64 | 96 | 104 | 112 | 120 | 128) => { + // Let tagLength be equal to the tagLength member of normalizedAlgorithm + length + }, + // Otherwise: + _ => { + // throw an OperationError. + return Err(Error::Operation); + }, + }; + + // Step 5. Let additionalData be the contents of the additionalData member of normalizedAlgorithm if present + // or the empty octet string otherwise. + let additional_data = params.additional_data.as_deref().unwrap_or_default(); + + // Step 6. Let C and T be the outputs that result from performing the Authenticated Encryption Function + // described in Section 7.1 of [NIST-SP800-38D] using AES as the block cipher, the contents of the iv member + // of normalizedAlgorithm as the IV input parameter, the contents of additionalData as the A input parameter, + // tagLength as the t pre-requisite and the contents of plaintext as the input plaintext. + let key_length = key.handle().as_bytes().len(); + let iv_length = params.iv.len(); + let mut ciphertext = plaintext.to_vec(); + let key_bytes = key.handle().as_bytes(); + let tag = match (key_length, iv_length) { + (16, 12) => { + let nonce = GenericArray::from_slice(¶ms.iv); + <Aes128Gcm96Iv>::new_from_slice(key_bytes) + .expect("key length did not match") + .encrypt_in_place_detached(nonce, additional_data, &mut ciphertext) + }, + (16, 16) => { + let nonce = GenericArray::from_slice(¶ms.iv); + <Aes128Gcm128Iv>::new_from_slice(key_bytes) + .expect("key length did not match") + .encrypt_in_place_detached(nonce, additional_data, &mut ciphertext) + }, + (20, 12) => { + let nonce = GenericArray::from_slice(¶ms.iv); + <Aes192Gcm96Iv>::new_from_slice(key_bytes) + .expect("key length did not match") + .encrypt_in_place_detached(nonce, additional_data, &mut ciphertext) + }, + (32, 12) => { + let nonce = GenericArray::from_slice(¶ms.iv); + <Aes256Gcm96Iv>::new_from_slice(key_bytes) + .expect("key length did not match") + .encrypt_in_place_detached(nonce, additional_data, &mut ciphertext) + }, + (16, 32) => { + let nonce = GenericArray::from_slice(¶ms.iv); + <Aes128Gcm256Iv>::new_from_slice(key_bytes) + .expect("key length did not match") + .encrypt_in_place_detached(nonce, additional_data, &mut ciphertext) + }, + (20, 32) => { + let nonce = GenericArray::from_slice(¶ms.iv); + <Aes192Gcm256Iv>::new_from_slice(key_bytes) + .expect("key length did not match") + .encrypt_in_place_detached(nonce, additional_data, &mut ciphertext) + }, + (32, 32) => { + let nonce = GenericArray::from_slice(¶ms.iv); + <Aes256Gcm256Iv>::new_from_slice(key_bytes) + .expect("key length did not match") + .encrypt_in_place_detached(nonce, additional_data, &mut ciphertext) + }, + _ => { + log::warn!( + "Missing AES-GCM encryption implementation with {key_length}-byte key and {iv_length}-byte IV" + ); + return Err(Error::NotSupported); + }, + }; + + // Step 7. Let ciphertext be equal to C | T, where '|' denotes concatenation. + ciphertext.extend_from_slice(&tag.unwrap()[..tag_length as usize / 8]); + + // Step 8. Return the result of creating an ArrayBuffer containing ciphertext. + create_buffer_source::<ArrayBufferU8>(cx, &ciphertext, handle) + .expect("failed to create buffer source for encrypted ciphertext"); + + Ok(ciphertext) + } + + /// <https://w3c.github.io/webcrypto/#aes-gcm-operations> + fn decrypt_aes_gcm( + &self, + params: &SubtleAesGcmParams, + key: &CryptoKey, + ciphertext: &[u8], + cx: JSContext, + handle: MutableHandleObject, + ) -> Result<Vec<u8>, Error> { + // Step 1. + // FIXME: aes_gcm uses a fixed tag length + let tag_length = match params.tag_length { + // If the tagLength member of normalizedAlgorithm is not present: + None => { + // Let tagLength be 128. + 128 + }, + // If the tagLength member of normalizedAlgorithm is one of 32, 64, 96, 104, 112, 120 or 128: + Some(length) if matches!(length, 32 | 64 | 96 | 104 | 112 | 120 | 128) => { + // Let tagLength be equal to the tagLength member of normalizedAlgorithm + length as usize + }, + // Otherwise: + _ => { + // throw an OperationError. + return Err(Error::Operation); + }, + }; + + // Step 2. If ciphertext has a length less than tagLength bits, then throw an OperationError. + if ciphertext.len() < tag_length / 8 { + return Err(Error::Operation); + } + + // Step 3. If the iv member of normalizedAlgorithm has a length greater than 2^64 - 1 bytes, + // then throw an OperationError. + // NOTE: servo does not currently support 128-bit platforms, so this can never happen + + // Step 4. If the additionalData member of normalizedAlgorithm is present and has a length greater than 2^64 - 1 + // bytes, then throw an OperationError. + // NOTE: servo does not currently support 128-bit platforms, so this can never happen + + // Step 5. Let tag be the last tagLength bits of ciphertext. + // Step 6. Let actualCiphertext be the result of removing the last tagLength bits from ciphertext. + // NOTE: aes_gcm splits the ciphertext for us + + // Step 7. Let additionalData be the contents of the additionalData member of normalizedAlgorithm if present or + // the empty octet string otherwise. + let additional_data = params.additional_data.as_deref().unwrap_or_default(); + + // Step 8. Perform the Authenticated Decryption Function described in Section 7.2 of [NIST-SP800-38D] using AES + // as the block cipher, the contents of the iv member of normalizedAlgorithm as the IV input parameter, the + // contents of additionalData as the A input parameter, tagLength as the t pre-requisite, the contents of + // actualCiphertext as the input ciphertext, C and the contents of tag as the authentication tag, T. + let mut plaintext = ciphertext.to_vec(); + let key_length = key.handle().as_bytes().len(); + let iv_length = params.iv.len(); + let key_bytes = key.handle().as_bytes(); + let result = match (key_length, iv_length) { + (16, 12) => { + let nonce = GenericArray::from_slice(¶ms.iv); + <Aes128Gcm96Iv>::new_from_slice(key_bytes) + .expect("key length did not match") + .decrypt_in_place(nonce, additional_data, &mut plaintext) + }, + (16, 16) => { + let nonce = GenericArray::from_slice(¶ms.iv); + <Aes128Gcm128Iv>::new_from_slice(key_bytes) + .expect("key length did not match") + .decrypt_in_place(nonce, additional_data, &mut plaintext) + }, + (20, 12) => { + let nonce = GenericArray::from_slice(¶ms.iv); + <Aes192Gcm96Iv>::new_from_slice(key_bytes) + .expect("key length did not match") + .decrypt_in_place(nonce, additional_data, &mut plaintext) + }, + (32, 12) => { + let nonce = GenericArray::from_slice(¶ms.iv); + <Aes256Gcm96Iv>::new_from_slice(key_bytes) + .expect("key length did not match") + .decrypt_in_place(nonce, additional_data, &mut plaintext) + }, + (16, 32) => { + let nonce = GenericArray::from_slice(¶ms.iv); + <Aes128Gcm256Iv>::new_from_slice(key_bytes) + .expect("key length did not match") + .decrypt_in_place(nonce, additional_data, &mut plaintext) + }, + (20, 32) => { + let nonce = GenericArray::from_slice(¶ms.iv); + <Aes192Gcm256Iv>::new_from_slice(key_bytes) + .expect("key length did not match") + .decrypt_in_place(nonce, additional_data, &mut plaintext) + }, + (32, 32) => { + let nonce = GenericArray::from_slice(¶ms.iv); + <Aes256Gcm256Iv>::new_from_slice(key_bytes) + .expect("key length did not match") + .decrypt_in_place(nonce, additional_data, &mut plaintext) + }, + _ => { + log::warn!( + "Missing AES-GCM decryption implementation with {key_length}-byte key and {iv_length}-byte IV" + ); + return Err(Error::NotSupported); + }, + }; + + // If the result of the algorithm is the indication of inauthenticity, "FAIL": + if result.is_err() { + // throw an OperationError + return Err(Error::Operation); + } + // Otherwise: + // Let plaintext be the output P of the Authenticated Decryption Function. + + // Step 9. Return the result of creating an ArrayBuffer containing plaintext. + create_buffer_source::<ArrayBufferU8>(cx, &plaintext, handle) + .expect("failed to create buffer source for decrypted plaintext"); + + Ok(plaintext) + } + /// <https://w3c.github.io/webcrypto/#aes-cbc-operations> /// <https://w3c.github.io/webcrypto/#aes-ctr-operations> /// <https://w3c.github.io/webcrypto/#aes-kw-operations> @@ -1750,7 +2060,7 @@ impl SubtleCrypto { }; match key_gen_params.name.as_str() { - ALG_AES_CBC | ALG_AES_CTR => { + ALG_AES_CBC | ALG_AES_CTR | ALG_AES_GCM => { if usages.iter().any(|usage| { !matches!( usage, @@ -1780,6 +2090,7 @@ impl SubtleCrypto { ALG_AES_CBC => DOMString::from(ALG_AES_CBC), ALG_AES_CTR => DOMString::from(ALG_AES_CTR), ALG_AES_KW => DOMString::from(ALG_AES_KW), + ALG_AES_GCM => DOMString::from(ALG_AES_GCM), _ => return Err(Error::NotSupported), }; @@ -2303,6 +2614,7 @@ fn data_to_jwk_params(alg: &str, size: &str, key: &[u8]) -> (DOMString, DOMStrin ALG_AES_CBC => DOMString::from(format!("A{}CBC", size)), ALG_AES_CTR => DOMString::from(format!("A{}CTR", size)), ALG_AES_KW => DOMString::from(format!("A{}KW", size)), + ALG_AES_GCM => DOMString::from(format!("A{}GCM", size)), _ => unreachable!(), }; let data = base64::engine::general_purpose::STANDARD_NO_PAD.encode(key); @@ -2529,6 +2841,9 @@ impl ImportKeyAlgorithm { Self::AesKw => { subtle.import_key_aes(format, secret, extractable, key_usages, ALG_AES_KW) }, + Self::AesGcm => { + subtle.import_key_aes(format, secret, extractable, key_usages, ALG_AES_GCM) + }, Self::Hmac(params) => { subtle.import_key_hmac(params, format, secret, extractable, key_usages) }, @@ -2551,8 +2866,9 @@ impl EncryptionAlgorithm { /// <https://w3c.github.io/webcrypto/#dom-algorithm-name> fn name(&self) -> &str { match self { - Self::AesCbc(key_gen_params) => &key_gen_params.name, - Self::AesCtr(key_gen_params) => &key_gen_params.name, + Self::AesCbc(params) => ¶ms.name, + Self::AesCtr(params) => ¶ms.name, + Self::AesGcm(params) => ¶ms.name, } } @@ -2566,12 +2882,9 @@ impl EncryptionAlgorithm { result: MutableHandleObject, ) -> Result<Vec<u8>, Error> { match self { - Self::AesCbc(key_gen_params) => { - subtle.encrypt_aes_cbc(key_gen_params, key, data, cx, result) - }, - Self::AesCtr(key_gen_params) => { - subtle.encrypt_decrypt_aes_ctr(key_gen_params, key, data, cx, result) - }, + Self::AesCbc(params) => subtle.encrypt_aes_cbc(params, key, data, cx, result), + Self::AesCtr(params) => subtle.encrypt_decrypt_aes_ctr(params, key, data, cx, result), + Self::AesGcm(params) => subtle.encrypt_aes_gcm(params, key, data, cx, result), } } @@ -2585,12 +2898,9 @@ impl EncryptionAlgorithm { result: MutableHandleObject, ) -> Result<Vec<u8>, Error> { match self { - Self::AesCbc(key_gen_params) => { - subtle.decrypt_aes_cbc(key_gen_params, key, data, cx, result) - }, - Self::AesCtr(key_gen_params) => { - subtle.encrypt_decrypt_aes_ctr(key_gen_params, key, data, cx, result) - }, + Self::AesCbc(params) => subtle.decrypt_aes_cbc(params, key, data, cx, result), + Self::AesCtr(params) => subtle.encrypt_decrypt_aes_ctr(params, key, data, cx, result), + Self::AesGcm(params) => subtle.decrypt_aes_gcm(params, key, data, cx, result), } } } @@ -2684,6 +2994,7 @@ impl KeyWrapAlgorithm { Self::AesKw => ALG_AES_KW, Self::AesCbc(key_gen_params) => &key_gen_params.name, Self::AesCtr(key_gen_params) => &key_gen_params.name, + Self::AesGcm(_) => ALG_AES_GCM, } } } @@ -2704,7 +3015,10 @@ fn parse_jwk(bytes: &[u8], alg: ImportKeyAlgorithm, extractable: bool) -> Result } match alg { - ImportKeyAlgorithm::AesCbc | ImportKeyAlgorithm::AesCtr | ImportKeyAlgorithm::AesKw => { + ImportKeyAlgorithm::AesCbc | + ImportKeyAlgorithm::AesCtr | + ImportKeyAlgorithm::AesKw | + ImportKeyAlgorithm::AesGcm => { if kty != "oct" { return Err(Error::Data); } diff --git a/components/script/dom/webidls/SubtleCrypto.webidl b/components/script/dom/webidls/SubtleCrypto.webidl index f966bf11b91..be01cebf589 100644 --- a/components/script/dom/webidls/SubtleCrypto.webidl +++ b/components/script/dom/webidls/SubtleCrypto.webidl @@ -92,6 +92,13 @@ dictionary AesCtrParams : Algorithm { required [EnforceRange] octet length; }; +// https://w3c.github.io/webcrypto/#aes-gcm-params +dictionary AesGcmParams : Algorithm { + required BufferSource iv; + BufferSource additionalData; + [EnforceRange] octet tagLength; +}; + // https://w3c.github.io/webcrypto/#dfn-HmacImportParams dictionary HmacImportParams : Algorithm { required HashAlgorithmIdentifier hash; |