use ::aes::cipher::generic_array::GenericArray;
use ::aes::cipher::{BlockDecrypt, BlockEncrypt, KeyInit};
use ::aes::Aes128;
pub const BLOCK_SIZE: usize = 16;
pub const IV0_AACS: [u8; 16] = [
0x0B, 0xA0, 0xF8, 0xDD, 0xFE, 0xA6, 0x1F, 0xB3, 0xD8, 0xDF, 0x9F, 0x56, 0x6A, 0x05, 0x0F, 0x78,
];
pub const H0_AACS: [u8; 16] = [
0x2D, 0xC2, 0xDF, 0x39, 0x42, 0x03, 0x21, 0xD0, 0xCE, 0xF1, 0xFE, 0x23, 0x74, 0x02, 0x9D, 0x95,
];
pub fn aes_128_ecb_encrypt(key: &[u8; 16], data: &[u8; 16]) -> [u8; 16] {
let cipher = Aes128::new(GenericArray::from_slice(key));
let mut out = *data;
cipher.encrypt_block(GenericArray::from_mut_slice(&mut out));
out
}
pub fn aes_128_ecb_decrypt(key: &[u8; 16], data: &[u8; 16]) -> [u8; 16] {
let cipher = Aes128::new(GenericArray::from_slice(key));
let mut out = *data;
cipher.decrypt_block(GenericArray::from_mut_slice(&mut out));
out
}
pub fn aes_128_cbc_encrypt(key: &[u8; 16], iv: &[u8; 16], data: &[u8]) -> Vec<u8> {
assert!(
data.len() % BLOCK_SIZE == 0,
"AES-128-CBC requires data length multiple of 16; got {}",
data.len()
);
let cipher = Aes128::new(GenericArray::from_slice(key));
let mut out = Vec::with_capacity(data.len());
let mut prev = *iv;
for chunk in data.chunks_exact(BLOCK_SIZE) {
let mut block = [0u8; BLOCK_SIZE];
for i in 0..BLOCK_SIZE {
block[i] = chunk[i] ^ prev[i];
}
cipher.encrypt_block(GenericArray::from_mut_slice(&mut block));
out.extend_from_slice(&block);
prev = block;
}
out
}
pub fn aes_128_cbc_decrypt(key: &[u8; 16], iv: &[u8; 16], data: &[u8]) -> Vec<u8> {
assert!(
data.len() % BLOCK_SIZE == 0,
"AES-128-CBC requires data length multiple of 16; got {}",
data.len()
);
let cipher = Aes128::new(GenericArray::from_slice(key));
let mut out = Vec::with_capacity(data.len());
let mut prev = *iv;
for chunk in data.chunks_exact(BLOCK_SIZE) {
let mut block = [0u8; BLOCK_SIZE];
block.copy_from_slice(chunk);
let ct = block;
cipher.decrypt_block(GenericArray::from_mut_slice(&mut block));
for i in 0..BLOCK_SIZE {
block[i] ^= prev[i];
}
out.extend_from_slice(&block);
prev = ct;
}
out
}
pub fn aes_g(x1: &[u8; 16], x2: &[u8; 16]) -> [u8; 16] {
let mut out = aes_128_ecb_decrypt(x1, x2);
for i in 0..16 {
out[i] ^= x2[i];
}
out
}
pub fn aes_h(data: &[u8]) -> [u8; 16] {
let bit_len: u64 = (data.len() as u64) * 8;
let mut padded = Vec::with_capacity(data.len() + 16 + 8);
padded.extend_from_slice(data);
padded.push(0x80);
while (padded.len() + 8) % 16 != 0 {
padded.push(0x00);
}
padded.extend_from_slice(&bit_len.to_be_bytes());
let mut h = H0_AACS;
for chunk in padded.chunks_exact(16) {
let mut x = [0u8; 16];
x.copy_from_slice(chunk);
h = aes_g(&x, &h);
}
h
}
pub fn aes_128_cmac(key: &[u8; 16], data: &[u8]) -> [u8; 16] {
let l = aes_128_ecb_encrypt(key, &[0u8; 16]);
let k1 = cmac_subkey(&l);
let k2 = cmac_subkey(&k1);
let n_blocks = data.len().div_ceil(BLOCK_SIZE);
let (n_blocks, complete_last) = if n_blocks == 0 {
(1, false)
} else {
(n_blocks, data.len() % BLOCK_SIZE == 0)
};
let mut last = [0u8; 16];
let last_start = (n_blocks - 1) * BLOCK_SIZE;
let tail = &data[last_start..];
if complete_last {
last.copy_from_slice(tail);
for i in 0..16 {
last[i] ^= k1[i];
}
} else {
last[..tail.len()].copy_from_slice(tail);
last[tail.len()] = 0x80;
for i in 0..16 {
last[i] ^= k2[i];
}
}
let mut x = [0u8; 16];
for blk in 0..n_blocks - 1 {
let chunk = &data[blk * BLOCK_SIZE..(blk + 1) * BLOCK_SIZE];
for i in 0..16 {
x[i] ^= chunk[i];
}
x = aes_128_ecb_encrypt(key, &x);
}
for i in 0..16 {
x[i] ^= last[i];
}
aes_128_ecb_encrypt(key, &x)
}
fn cmac_subkey(input: &[u8; 16]) -> [u8; 16] {
let mut out = [0u8; 16];
let mut carry = 0u8;
for i in (0..16).rev() {
out[i] = (input[i] << 1) | carry;
carry = input[i] >> 7;
}
if input[0] & 0x80 != 0 {
out[15] ^= 0x87;
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ecb_roundtrips() {
let key = [0x42u8; 16];
let pt = [0x11u8; 16];
let ct = aes_128_ecb_encrypt(&key, &pt);
let rt = aes_128_ecb_decrypt(&key, &ct);
assert_eq!(rt, pt);
assert_ne!(ct, pt);
}
#[test]
fn cbc_roundtrips_with_iv0() {
let key = [0x77u8; 16];
let pt: Vec<u8> = (0..64u8).collect();
let ct = aes_128_cbc_encrypt(&key, &IV0_AACS, &pt);
let rt = aes_128_cbc_decrypt(&key, &IV0_AACS, &ct);
assert_eq!(rt, pt);
assert_ne!(ct, pt);
}
#[test]
fn aes_g_is_inverse_xor_of_aes_128d() {
let x1 = [0x33u8; 16];
let x2 = [0xa5u8; 16];
let d = aes_128_ecb_decrypt(&x1, &x2);
let mut expected = [0u8; 16];
for i in 0..16 {
expected[i] = d[i] ^ x2[i];
}
assert_eq!(aes_g(&x1, &x2), expected);
}
#[test]
fn aes_h_is_deterministic_and_distinguishes_inputs() {
let a = aes_h(b"hello");
let b = aes_h(b"hello");
let c = aes_h(b"world");
assert_eq!(a, b);
assert_ne!(a, c);
}
#[test]
fn aes_h_empty_message_does_not_panic() {
let _ = aes_h(b"");
}
const CMAC_KEY: [u8; 16] = [
0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f,
0x3c,
];
const CMAC_MSG: [u8; 64] = [
0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17,
0x2a, 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf,
0x8e, 0x51, 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a,
0x0a, 0x52, 0xef, 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b,
0xe6, 0x6c, 0x37, 0x10,
];
#[test]
fn cmac_nist_empty_example() {
let mac = aes_128_cmac(&CMAC_KEY, &[]);
assert_eq!(
mac,
[
0xbb, 0x1d, 0x69, 0x29, 0xe9, 0x59, 0x37, 0x28, 0x7f, 0xa3, 0x7d, 0x12, 0x9b, 0x75,
0x67, 0x46
]
);
}
#[test]
fn cmac_nist_one_block_example() {
let mac = aes_128_cmac(&CMAC_KEY, &CMAC_MSG[..16]);
assert_eq!(
mac,
[
0x07, 0x0a, 0x16, 0xb4, 0x6b, 0x4d, 0x41, 0x44, 0xf7, 0x9b, 0xdd, 0x9d, 0xd0, 0x4a,
0x28, 0x7c
]
);
}
#[test]
fn cmac_nist_partial_block_example() {
let mac = aes_128_cmac(&CMAC_KEY, &CMAC_MSG[..40]);
assert_eq!(
mac,
[
0xdf, 0xa6, 0x67, 0x47, 0xde, 0x9a, 0xe6, 0x30, 0x30, 0xca, 0x32, 0x61, 0x14, 0x97,
0xc8, 0x27
]
);
}
#[test]
fn cmac_nist_full_message_example() {
let mac = aes_128_cmac(&CMAC_KEY, &CMAC_MSG);
assert_eq!(
mac,
[
0x51, 0xf0, 0xbe, 0xbf, 0x7e, 0x3b, 0x9d, 0x92, 0xfc, 0x49, 0x74, 0x17, 0x79, 0x36,
0x3c, 0xfe
]
);
}
}