mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
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>
This commit is contained in:
parent
5113147f80
commit
83f8e88818
13 changed files with 1168 additions and 13478 deletions
|
@ -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 {
|
||||
|
@ -1139,6 +1160,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,
|
||||
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue