bitwarden_crypto/keys/
symmetric_crypto_key.rsuse std::pin::Pin;
use aes::cipher::typenum::U32;
use base64::{engine::general_purpose::STANDARD, Engine};
use generic_array::GenericArray;
use rand::Rng;
use zeroize::{Zeroize, ZeroizeOnDrop};
use super::key_encryptable::CryptoKey;
use crate::CryptoError;
#[derive(ZeroizeOnDrop, Clone)]
#[cfg_attr(test, derive(Debug, PartialEq))]
pub struct Aes256CbcKey {
pub(crate) enc_key: Pin<Box<GenericArray<u8, U32>>>,
}
#[derive(ZeroizeOnDrop, Clone)]
#[cfg_attr(test, derive(Debug, PartialEq))]
pub struct Aes256CbcHmacKey {
pub(crate) enc_key: Pin<Box<GenericArray<u8, U32>>>,
pub(crate) mac_key: Pin<Box<GenericArray<u8, U32>>>,
}
#[derive(ZeroizeOnDrop, Clone)]
#[cfg_attr(test, derive(PartialEq))]
pub enum SymmetricCryptoKey {
Aes256CbcKey(Aes256CbcKey),
Aes256CbcHmacKey(Aes256CbcHmacKey),
}
impl SymmetricCryptoKey {
const KEY_LEN: usize = 32;
const MAC_LEN: usize = 32;
pub fn generate(mut rng: impl rand::RngCore) -> Self {
let mut enc_key = Box::pin(GenericArray::<u8, U32>::default());
let mut mac_key = Box::pin(GenericArray::<u8, U32>::default());
rng.fill(enc_key.as_mut_slice());
rng.fill(mac_key.as_mut_slice());
SymmetricCryptoKey::Aes256CbcHmacKey(Aes256CbcHmacKey { enc_key, mac_key })
}
fn total_len(&self) -> usize {
match self {
SymmetricCryptoKey::Aes256CbcKey(_) => 32,
SymmetricCryptoKey::Aes256CbcHmacKey(_) => 64,
}
}
pub fn to_base64(&self) -> String {
STANDARD.encode(self.to_vec())
}
pub fn to_vec(&self) -> Vec<u8> {
let mut buf = Vec::with_capacity(self.total_len());
match self {
SymmetricCryptoKey::Aes256CbcKey(key) => {
buf.extend_from_slice(&key.enc_key);
}
SymmetricCryptoKey::Aes256CbcHmacKey(key) => {
buf.extend_from_slice(&key.enc_key);
buf.extend_from_slice(&key.mac_key);
}
}
buf
}
}
impl TryFrom<String> for SymmetricCryptoKey {
type Error = CryptoError;
fn try_from(value: String) -> Result<Self, Self::Error> {
let b = STANDARD
.decode(value)
.map_err(|_| CryptoError::InvalidKey)?;
SymmetricCryptoKey::try_from(b)
}
}
impl TryFrom<Vec<u8>> for SymmetricCryptoKey {
type Error = CryptoError;
fn try_from(mut value: Vec<u8>) -> Result<Self, Self::Error> {
SymmetricCryptoKey::try_from(value.as_mut_slice())
}
}
impl TryFrom<&mut [u8]> for SymmetricCryptoKey {
type Error = CryptoError;
fn try_from(value: &mut [u8]) -> Result<Self, Self::Error> {
let result = if value.len() == Self::KEY_LEN + Self::MAC_LEN {
let mut enc_key = Box::pin(GenericArray::<u8, U32>::default());
let mut mac_key = Box::pin(GenericArray::<u8, U32>::default());
enc_key.copy_from_slice(&value[..Self::KEY_LEN]);
mac_key.copy_from_slice(&value[Self::KEY_LEN..]);
Ok(SymmetricCryptoKey::Aes256CbcHmacKey(Aes256CbcHmacKey {
enc_key,
mac_key,
}))
} else if value.len() == Self::KEY_LEN {
let mut enc_key = Box::pin(GenericArray::<u8, U32>::default());
enc_key.copy_from_slice(&value[..Self::KEY_LEN]);
Ok(SymmetricCryptoKey::Aes256CbcKey(Aes256CbcKey { enc_key }))
} else {
Err(CryptoError::InvalidKeyLen)
};
value.zeroize();
result
}
}
impl CryptoKey for SymmetricCryptoKey {}
impl std::fmt::Debug for SymmetricCryptoKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SymmetricCryptoKey").finish()
}
}
#[cfg(test)]
pub fn derive_symmetric_key(name: &str) -> Aes256CbcHmacKey {
use zeroize::Zeroizing;
use crate::{derive_shareable_key, generate_random_bytes};
let secret: Zeroizing<[u8; 16]> = generate_random_bytes();
derive_shareable_key(secret, name, None)
}
#[cfg(test)]
mod tests {
use super::{derive_symmetric_key, SymmetricCryptoKey};
#[test]
fn test_symmetric_crypto_key() {
let key = SymmetricCryptoKey::Aes256CbcHmacKey(derive_symmetric_key("test"));
let key2 = SymmetricCryptoKey::try_from(key.to_base64()).unwrap();
assert_eq!(key, key2);
let key = "UY4B5N4DA4UisCNClgZtRr6VLy9ZF5BXXC7cDZRqourKi4ghEMgISbCsubvgCkHf5DZctQjVot11/vVvN9NNHQ==".to_string();
let key2 = SymmetricCryptoKey::try_from(key.clone()).unwrap();
assert_eq!(key, key2.to_base64());
}
}