From a76b21bfa2a69e61790087ee8a5c74ce6c2eeee4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garci=CC=81a?= Date: Tue, 6 Feb 2024 19:06:25 +0100 Subject: [PATCH] Add support for missing AES decrypt types --- crates/bitwarden-crypto/src/aes.rs | 40 ++++++++++++++++++- .../src/enc_string/symmetric.rs | 15 ++++++- 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/crates/bitwarden-crypto/src/aes.rs b/crates/bitwarden-crypto/src/aes.rs index 195b818b7..bf433114d 100644 --- a/crates/bitwarden-crypto/src/aes.rs +++ b/crates/bitwarden-crypto/src/aes.rs @@ -6,7 +6,9 @@ //! [KeyEncryptable][crate::KeyEncryptable] & [KeyDecryptable][crate::KeyDecryptable] instead. use aes::cipher::{ - block_padding::Pkcs7, typenum::U32, BlockDecryptMut, BlockEncryptMut, KeyIvInit, + block_padding::Pkcs7, + typenum::{U16, U32}, + BlockDecryptMut, BlockEncryptMut, KeyIvInit, }; use generic_array::GenericArray; use hmac::Mac; @@ -109,6 +111,42 @@ fn encrypt_aes256_internal( (iv, data) } +/// Decrypt using AES-128 in CBC mode. +/// +/// Behaves similar to [decrypt_aes128_hmac], but does not validate the MAC. +fn decrypt_aes128(iv: &[u8; 16], data: Vec, key: &GenericArray) -> Result> { + // Decrypt data + let iv = GenericArray::from_slice(iv); + let mut data = data; + let decrypted_key_slice = cbc::Decryptor::::new(key, iv) + .decrypt_padded_mut::(&mut data) + .map_err(|_| CryptoError::KeyDecrypt)?; + + // Data is decrypted in place and returns a subslice of the original Vec, to avoid cloning it, + // we truncate to the subslice length + let decrypted_len = decrypted_key_slice.len(); + data.truncate(decrypted_len); + + Ok(data) +} + +/// Decrypt using AES-128 in CBC mode with MAC. +/// +/// Behaves similar to [decrypt_aes128], but also validates the MAC. +pub fn decrypt_aes128_hmac( + iv: &[u8; 16], + mac: &[u8; 32], + data: Vec, + mac_key: &GenericArray, + key: &GenericArray, +) -> Result> { + let res = generate_mac(mac_key, iv, &data)?; + if res.ct_ne(mac).into() { + return Err(CryptoError::InvalidMac); + } + decrypt_aes128(iv, data, key) +} + /// Generate a MAC using HMAC-SHA256. fn generate_mac(mac_key: &[u8], iv: &[u8], data: &[u8]) -> Result<[u8; 32]> { let mut hmac = PbkdfSha256Hmac::new_from_slice(mac_key).expect("HMAC can take key of any size"); diff --git a/crates/bitwarden-crypto/src/enc_string/symmetric.rs b/crates/bitwarden-crypto/src/enc_string/symmetric.rs index a7768de23..5d9d6ef97 100644 --- a/crates/bitwarden-crypto/src/enc_string/symmetric.rs +++ b/crates/bitwarden-crypto/src/enc_string/symmetric.rs @@ -236,13 +236,26 @@ impl KeyEncryptable for &[u8] { impl KeyDecryptable> for EncString { fn decrypt_with_key(&self, key: &SymmetricCryptoKey) -> Result> { match self { + EncString::AesCbc256_B64 { iv, data } => { + let dec = crate::aes::decrypt_aes256(iv, data.clone(), &key.key)?; + Ok(dec) + } + EncString::AesCbc128_HmacSha256_B64 { iv, mac, data } => { + // TODO: SymmetricCryptoKey is designed to handle 32 byte keys only, but this + // variant uses a 16 byte key This means the key+mac are going to be + // parsed as a single 32 byte key, at the moment we split it manually + // When refactoring the key handling, this should be fixed. + let enc_key = key.key[0..16].into(); + let mac_key = key.key[16..32].into(); + let dec = crate::aes::decrypt_aes128_hmac(iv, mac, data.clone(), mac_key, enc_key)?; + Ok(dec) + } EncString::AesCbc256_HmacSha256_B64 { iv, mac, data } => { let mac_key = key.mac_key.as_ref().ok_or(CryptoError::InvalidMac)?; let dec = crate::aes::decrypt_aes256_hmac(iv, mac, data.clone(), mac_key, &key.key)?; Ok(dec) } - _ => Err(CryptoError::InvalidKey), } } }