use aes::Aes128;
use cipher::block_padding::NoPadding;
use cipher::{BlockDecryptMut, BlockEncryptMut, KeyIvInit, StreamCipher};
use crate::{CrafterError, Result};
const AES_BLOCK_LEN: usize = 16;
const AES128_KEY_LEN: usize = 16;
const CTR_IV_LEN: usize = 8;
const CTR_NONCE_LEN: usize = 4;
type AesCbcEnc = cbc::Encryptor<Aes128>;
type AesCbcDec = cbc::Decryptor<Aes128>;
type AesCtr = ctr::Ctr128BE<Aes128>;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CipherTransform {
AesCbc,
AesCtr,
Null,
}
impl CipherTransform {
pub const fn block_size(self) -> usize {
match self {
Self::AesCbc => AES_BLOCK_LEN, Self::AesCtr => 1, Self::Null => 1, }
}
pub const fn iv_len(self) -> usize {
match self {
Self::AesCbc => AES_BLOCK_LEN, Self::AesCtr => CTR_IV_LEN, Self::Null => 0, }
}
pub fn encrypt(self, key: &[u8], iv: &[u8], plaintext: &[u8]) -> Result<Vec<u8>> {
match self {
Self::AesCbc => aes_cbc_encrypt(key, iv, plaintext),
Self::AesCtr => aes_ctr_apply(key, iv, plaintext),
Self::Null => Ok(plaintext.to_vec()),
}
}
pub fn decrypt(self, key: &[u8], iv: &[u8], ciphertext: &[u8]) -> Result<Vec<u8>> {
match self {
Self::AesCbc => aes_cbc_decrypt(key, iv, ciphertext),
Self::AesCtr => aes_ctr_apply(key, iv, ciphertext),
Self::Null => Ok(ciphertext.to_vec()),
}
}
}
fn cbc_key_iv(key: &[u8], iv: &[u8]) -> Result<()> {
if key.len() != AES128_KEY_LEN {
return Err(CrafterError::invalid_field_value(
"ipsec.cipher.aes_cbc.key",
"AES-CBC requires a 16-octet AES-128 key",
));
}
if iv.len() != AES_BLOCK_LEN {
return Err(CrafterError::invalid_field_value(
"ipsec.cipher.aes_cbc.iv",
"AES-CBC requires a 16-octet IV",
));
}
Ok(())
}
fn aes_cbc_encrypt(key: &[u8], iv: &[u8], plaintext: &[u8]) -> Result<Vec<u8>> {
cbc_key_iv(key, iv)?;
if plaintext.len() % AES_BLOCK_LEN != 0 {
return Err(CrafterError::invalid_field_value(
"ipsec.cipher.aes_cbc.plaintext",
"AES-CBC plaintext must be a multiple of the 16-octet block size",
));
}
let mut buf = plaintext.to_vec();
let len = buf.len();
let ct = AesCbcEnc::new(key.into(), iv.into())
.encrypt_padded_mut::<NoPadding>(&mut buf, len)
.map_err(|_| {
CrafterError::invalid_field_value("ipsec.cipher.aes_cbc", "AES-CBC encryption failed")
})?;
Ok(ct.to_vec())
}
fn aes_cbc_decrypt(key: &[u8], iv: &[u8], ciphertext: &[u8]) -> Result<Vec<u8>> {
cbc_key_iv(key, iv)?;
if ciphertext.len() % AES_BLOCK_LEN != 0 {
return Err(CrafterError::invalid_field_value(
"ipsec.cipher.aes_cbc.ciphertext",
"AES-CBC ciphertext must be a multiple of the 16-octet block size",
));
}
let mut buf = ciphertext.to_vec();
let pt = AesCbcDec::new(key.into(), iv.into())
.decrypt_padded_mut::<NoPadding>(&mut buf)
.map_err(|_| {
CrafterError::invalid_field_value("ipsec.cipher.aes_cbc", "AES-CBC decryption failed")
})?;
Ok(pt.to_vec())
}
fn aes_ctr_apply(key: &[u8], iv: &[u8], data: &[u8]) -> Result<Vec<u8>> {
if key.len() != AES128_KEY_LEN + CTR_NONCE_LEN {
return Err(CrafterError::invalid_field_value(
"ipsec.cipher.aes_ctr.key",
"AES-CTR requires aes_key(16) || salt(4)",
));
}
if iv.len() != CTR_IV_LEN {
return Err(CrafterError::invalid_field_value(
"ipsec.cipher.aes_ctr.iv",
"AES-CTR requires an 8-octet IV",
));
}
let aes_key = &key[..AES128_KEY_LEN];
let salt = &key[AES128_KEY_LEN..];
let mut counter = [0u8; AES_BLOCK_LEN];
counter[..CTR_NONCE_LEN].copy_from_slice(salt);
counter[CTR_NONCE_LEN..CTR_NONCE_LEN + CTR_IV_LEN].copy_from_slice(iv);
counter[AES_BLOCK_LEN - 4..].copy_from_slice(&1u32.to_be_bytes());
let mut buf = data.to_vec();
let mut ctr = AesCtr::new(aes_key.into(), (&counter).into());
ctr.apply_keystream(&mut buf);
Ok(buf)
}
#[cfg(test)]
mod tests {
use super::*;
fn hex(s: &str) -> Vec<u8> {
let s: String = s.chars().filter(|c| !c.is_whitespace()).collect();
assert!(s.len() % 2 == 0, "hex string must have even length");
(0..s.len())
.step_by(2)
.map(|i| u8::from_str_radix(&s[i..i + 2], 16).expect("valid hex"))
.collect()
}
#[test]
fn aes_cbc_rfc3602_case2() {
let key = hex("c286696d887c9aa0611bbb3e2025a45a");
let iv = hex("562e17996d093d28ddb3ba695a2e6f58");
let plaintext = hex("000102030405060708090a0b0c0d0e0f");
let expected = hex("d296cd94c2cccf8a3a863028b5e1dc0a");
assert_eq!(CipherTransform::AesCbc.block_size(), 16);
assert_eq!(CipherTransform::AesCbc.iv_len(), 16);
let ct = CipherTransform::AesCbc
.encrypt(&key, &iv, &plaintext)
.unwrap();
assert_eq!(ct, expected);
let pt = CipherTransform::AesCbc.decrypt(&key, &iv, &ct).unwrap();
assert_eq!(pt, plaintext);
}
#[test]
fn aes_cbc_rfc3602_case4() {
let key = hex("56e47a38c5598974bc46903dba290349");
let iv = hex("8ce82eefbea0da3c44699ed7db51b7d9");
let plaintext = hex("a0a1a2a3a4a5a6a7a8a9aaabacadaeaf\
b0b1b2b3b4b5b6b7b8b9babbbcbdbebf");
let expected = hex("c30e32ffedc0774e6aff6af0869f71aa\
0f3af07a9a31a9c684db207eb0ef8e4e");
let ct = CipherTransform::AesCbc
.encrypt(&key, &iv, &plaintext)
.unwrap();
assert_eq!(ct, expected);
let pt = CipherTransform::AesCbc.decrypt(&key, &iv, &ct).unwrap();
assert_eq!(pt, plaintext);
}
#[test]
fn aes_cbc_rejects_unaligned_and_bad_key() {
let key = hex("c286696d887c9aa0611bbb3e2025a45a");
let iv = hex("562e17996d093d28ddb3ba695a2e6f58");
assert!(CipherTransform::AesCbc
.encrypt(&key, &iv, &[0u8; 15])
.is_err());
assert!(CipherTransform::AesCbc
.encrypt(&hex("0011223344"), &iv, &[0u8; 16])
.is_err());
assert!(CipherTransform::AesCbc
.encrypt(&key, &hex("00112233"), &[0u8; 16])
.is_err());
}
#[test]
fn aes_ctr_rfc3686_vector1() {
let aes_key = hex("ae6852f8121067cc4bf7a5765577f39e");
let nonce = hex("00000030");
let iv = hex("0000000000000000");
let plaintext = hex("53696e676c6520626c6f636b206d7367");
let expected = hex("e4095d4fb7a7b3792d6175a3261311b8");
assert_eq!(CipherTransform::AesCtr.block_size(), 1);
assert_eq!(CipherTransform::AesCtr.iv_len(), 8);
let mut key = aes_key.clone();
key.extend_from_slice(&nonce);
let ct = CipherTransform::AesCtr
.encrypt(&key, &iv, &plaintext)
.unwrap();
assert_eq!(ct, expected);
let pt = CipherTransform::AesCtr.decrypt(&key, &iv, &ct).unwrap();
assert_eq!(pt, plaintext);
}
#[test]
fn aes_ctr_rfc3686_vector2() {
let aes_key = hex("7e24067817fae0d743d6ce1f32539163");
let nonce = hex("006cb6db");
let iv = hex("c0543b59da48d90b");
let plaintext = hex("000102030405060708090a0b0c0d0e0f\
101112131415161718191a1b1c1d1e1f");
let expected = hex("5104a106168a72d9790d41ee8edad388\
eb2e1efc46da57c8fce630df9141be28");
let mut key = aes_key.clone();
key.extend_from_slice(&nonce);
let ct = CipherTransform::AesCtr
.encrypt(&key, &iv, &plaintext)
.unwrap();
assert_eq!(ct, expected);
let pt = CipherTransform::AesCtr.decrypt(&key, &iv, &ct).unwrap();
assert_eq!(pt, plaintext);
}
#[test]
fn aes_ctr_rfc3686_vector3() {
let aes_key = hex("7691be035e5020a8ac6e618529f9a0dc");
let nonce = hex("00e0017b");
let iv = hex("27777f3f4a1786f0");
let plaintext = hex("000102030405060708090a0b0c0d0e0f\
101112131415161718191a1b1c1d1e1f\
20212223");
let expected = hex("c1cf48a89f2ffdd9cf4652e9efdb72d7\
4540a42bde6d7836d59a5ceaaef31053\
25b2072f");
let mut key = aes_key.clone();
key.extend_from_slice(&nonce);
let ct = CipherTransform::AesCtr
.encrypt(&key, &iv, &plaintext)
.unwrap();
assert_eq!(ct, expected);
let pt = CipherTransform::AesCtr.decrypt(&key, &iv, &ct).unwrap();
assert_eq!(pt, plaintext);
}
#[test]
fn aes_ctr_key_iv_guards() {
let key = hex("ae6852f8121067cc4bf7a5765577f39e00000030"); assert!(CipherTransform::AesCtr
.encrypt(&key, &hex("0000"), &[0u8; 4])
.is_err());
assert!(CipherTransform::AesCtr
.encrypt(
&hex("ae6852f8121067cc4bf7a5765577f39e"),
&hex("0000000000000000"),
&[0u8; 4]
)
.is_err());
}
#[test]
fn null_is_identity() {
assert_eq!(CipherTransform::Null.block_size(), 1);
assert_eq!(CipherTransform::Null.iv_len(), 0);
let data = hex("deadbeef0011223344");
let ct = CipherTransform::Null.encrypt(&[], &[], &data).unwrap();
assert_eq!(ct, data);
let pt = CipherTransform::Null.decrypt(&[], &[], &ct).unwrap();
assert_eq!(pt, data);
assert_eq!(
CipherTransform::Null.encrypt(&[], &[], &[]).unwrap(),
Vec::<u8>::new()
);
}
}