mod common;
use aescry::aes::{Aes, Aes128, Aes192, Aes256, Backend, Block, BlockCipher};
use aescry::Error;
use common::{array, hex, load_vectors};
fn check_vectors<C: BlockCipher>(file: &str, key_len: usize, new: impl Fn(&[u8]) -> C) {
let vectors = load_vectors(file);
for (i, v) in vectors.iter().enumerate() {
assert_eq!(v["key"].len(), key_len);
let cipher = new(&v["key"]);
let mut block: Block = array(&v["pt"]);
cipher.encrypt_block(&mut block);
assert_eq!(hex(&block), hex(&v["ct"]), "{} vector {} encrypt", file, i);
cipher.decrypt_block(&mut block);
assert_eq!(hex(&block), hex(&v["pt"]), "{} vector {} decrypt", file, i);
}
}
fn backends() -> Vec<Backend> {
[Backend::Software, Backend::AesNi].into_iter().filter(|b| b.is_available()).collect()
}
#[test]
fn rustcrypto_aes128() {
check_vectors("rustcrypto/aes128.txt", 16, |k| Aes128::new(&array(k)));
for backend in backends() {
check_vectors("rustcrypto/aes128.txt", 16, |k| Aes128::with_backend(&array(k), backend).unwrap());
check_vectors("rustcrypto/aes128.txt", 16, |k| Aes::with_backend(k, backend).unwrap());
}
}
#[test]
fn rustcrypto_aes192() {
check_vectors("rustcrypto/aes192.txt", 24, |k| Aes192::new(&array(k)));
for backend in backends() {
check_vectors("rustcrypto/aes192.txt", 24, |k| Aes192::with_backend(&array(k), backend).unwrap());
check_vectors("rustcrypto/aes192.txt", 24, |k| Aes::with_backend(k, backend).unwrap());
}
}
#[test]
fn rustcrypto_aes256() {
check_vectors("rustcrypto/aes256.txt", 32, |k| Aes256::new(&array(k)));
for backend in backends() {
check_vectors("rustcrypto/aes256.txt", 32, |k| Aes256::with_backend(&array(k), backend).unwrap());
check_vectors("rustcrypto/aes256.txt", 32, |k| Aes::with_backend(k, backend).unwrap());
}
}
#[test]
fn backend_selection() {
let best = Backend::detect();
assert!(best.is_available());
assert_eq!(Aes::new(&[0; 16]).unwrap().backend(), best);
assert_eq!(Aes256::new(&[0; 32]).backend(), best);
let soft = Aes::with_backend(&[0; 16], Backend::Software).unwrap();
assert_eq!(soft.backend(), Backend::Software);
assert!(!Backend::Software.is_constant_time());
assert!(Backend::AesNi.is_constant_time());
if !Backend::AesNi.is_available() {
assert!(matches!(Aes::with_backend(&[0; 16], Backend::AesNi), Err(Error::BackendUnavailable)));
}
}
#[test]
fn backends_agree() {
let backends = backends();
for key_len in [16, 24, 32] {
for _ in 0..200 {
let key = aescry::random::key(key_len).unwrap();
let block: Block = aescry::random::bytes().unwrap();
let results: Vec<(Block, Block)> = backends
.iter()
.map(|&b| {
let cipher = Aes::with_backend(&key, b).unwrap();
let (mut enc, mut dec) = (block, block);
cipher.encrypt_block(&mut enc);
cipher.decrypt_block(&mut dec);
(enc, dec)
})
.collect();
assert!(results.windows(2).all(|w| w[0] == w[1]), "backends disagree");
}
}
}
const FIPS197_PLAINTEXT: Block = [
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
];
fn fips197_roundtrip(key_len: usize, ciphertext: &str) {
let key: Vec<u8> = (0..key_len as u8).collect();
let cipher = Aes::new(&key).unwrap();
assert_eq!(cipher.key_size(), key_len);
let mut block = FIPS197_PLAINTEXT;
cipher.encrypt_block(&mut block);
assert_eq!(hex(&block), ciphertext);
cipher.decrypt_block(&mut block);
assert_eq!(block, FIPS197_PLAINTEXT);
}
#[test]
fn fips197_c1_aes128() {
fips197_roundtrip(16, "69c4e0d86a7b0430d8cdb78070b4c55a");
}
#[test]
fn fips197_c2_aes192() {
fips197_roundtrip(24, "dda97ca4864cdfe06eaf70a0ec0d7191");
}
#[test]
fn fips197_c3_aes256() {
fips197_roundtrip(32, "8ea2b7ca516745bfeafc49904b496089");
}
#[test]
fn invalid_key_lengths() {
for len in [0, 8, 15, 17, 20, 31, 33, 64] {
assert!(matches!(Aes::new(&vec![0u8; len]), Err(Error::InvalidKeyLength(n)) if n == len));
}
assert!(Aes128::from_slice(&[0u8; 32]).is_err());
assert!(Aes256::from_slice(&[0u8; 32]).is_ok());
}
#[test]
fn ecb_blocks_match_single_blocks() {
let cipher = Aes256::new(&[7u8; 32]);
let mut blocks = [[1u8; 16], [2u8; 16], [1u8; 16]];
let original = blocks;
cipher.encrypt_blocks(&mut blocks);
let mut single = original[1];
cipher.encrypt_block(&mut single);
assert_eq!(blocks[1], single);
assert_eq!(blocks[0], blocks[2]);
cipher.decrypt_blocks(&mut blocks);
assert_eq!(blocks, original);
}
#[test]
fn debug_does_not_print_key_material() {
assert_eq!(format!("{:?}", Aes128::new(&[0x41; 16])), "Aes128 { .. }");
assert_eq!(format!("{:?}", Aes::new(&[0x41; 24]).unwrap()), "Aes192 { .. }");
}
const MONTE_CARLO_ENCRYPT: [Block; 3] = [
[ 0xA0, 0x43, 0x77, 0xAB, 0xE2, 0x59, 0xB0, 0xD0,
0xB5, 0xBA, 0x2D, 0x40, 0xA5, 0x01, 0x97, 0x1B ],
[ 0x4E, 0x46, 0xF8, 0xC5, 0x09, 0x2B, 0x29, 0xE2,
0x9A, 0x97, 0x1A, 0x0C, 0xD1, 0xF6, 0x10, 0xFB ],
[ 0x1F, 0x67, 0x63, 0xDF, 0x80, 0x7A, 0x7E, 0x70,
0x96, 0x0D, 0x4C, 0xD3, 0x11, 0x8E, 0x60, 0x1A ],
];
const MONTE_CARLO_DECRYPT: [Block; 3] = [
[ 0xF5, 0xBF, 0x8B, 0x37, 0x13, 0x6F, 0x2E, 0x1F,
0x6B, 0xEC, 0x6F, 0x57, 0x20, 0x21, 0xE3, 0xBA ],
[ 0xF1, 0xA8, 0x1B, 0x68, 0xF6, 0xE5, 0xA6, 0x27,
0x1A, 0x8C, 0xB2, 0x4E, 0x7D, 0x94, 0x91, 0xEF ],
[ 0x4D, 0xE0, 0xC6, 0xDF, 0x7C, 0xB1, 0x69, 0x72,
0x84, 0x60, 0x4D, 0x60, 0x27, 0x1B, 0xC5, 0x9A ],
];
fn monte_carlo(expected: &[Block; 3], decrypt: bool) {
for backend in backends() {
monte_carlo_with(expected, decrypt, backend);
}
}
fn monte_carlo_with(expected: &[Block; 3], decrypt: bool, backend: Backend) {
for n in 0..3 {
let key_len = 16 + n * 8;
let mut buf = [0u8; 16];
let mut key = [0u8; 32];
for _ in 0..400 {
let cipher = Aes::with_backend(&key[..key_len], backend).unwrap();
let run = |buf: &mut Block| {
if decrypt { cipher.decrypt_block(buf) } else { cipher.encrypt_block(buf) }
};
for _ in 0..9999 {
run(&mut buf);
}
if n > 0 {
for j in 0..(n << 3) {
key[j] ^= buf[j + 16 - (n << 3)];
}
}
run(&mut buf);
for j in 0..16 {
key[j + (n << 3)] ^= buf[j];
}
}
assert_eq!(buf, expected[n], "{:?}, key size = {} bits", backend, key_len * 8);
}
}
#[test]
fn monte_carlo_encrypt() {
monte_carlo(&MONTE_CARLO_ENCRYPT, false);
}
#[test]
fn monte_carlo_decrypt() {
monte_carlo(&MONTE_CARLO_DECRYPT, true);
}