use aes::Aes128;
use aes::cipher::generic_array::GenericArray;
use aes::cipher::{BlockDecrypt, BlockEncrypt, KeyInit};
use zeroize::{Zeroize, ZeroizeOnDrop};
pub type Block = [u8; 16];
#[inline]
pub fn aes128_encrypt(key: &[u8; 16], block: &Block) -> Block {
let cipher = Aes128::new(GenericArray::from_slice(key));
let mut buf = *GenericArray::from_slice(block);
cipher.encrypt_block(&mut buf);
let mut out = [0u8; 16];
out.copy_from_slice(&buf);
out
}
#[inline]
pub fn aes128_decrypt(key: &[u8; 16], block: &Block) -> Block {
let cipher = Aes128::new(GenericArray::from_slice(key));
let mut buf = *GenericArray::from_slice(block);
cipher.decrypt_block(&mut buf);
let mut out = [0u8; 16];
out.copy_from_slice(&buf);
out
}
fn key_template(tag1: u8, tag2: u8, rnd_a: &[u8; 8]) -> Block {
let mut t = [0u8; 16];
t[0] = tag1;
t[1] = tag2;
t[2..8].copy_from_slice(&rnd_a[0..6]);
t
}
#[derive(Debug, Clone, PartialEq, Eq, Default, Zeroize, ZeroizeOnDrop)]
pub struct SessionKeys {
pub s_enc: [u8; 16],
pub s_mac1: [u8; 16],
pub s_mac2: [u8; 16],
}
impl SessionKeys {
pub fn derive(scbk: &[u8; 16], rnd_a: &[u8; 8]) -> Self {
Self {
s_enc: aes128_encrypt(scbk, &key_template(0x01, 0x82, rnd_a)),
s_mac1: aes128_encrypt(scbk, &key_template(0x01, 0x01, rnd_a)),
s_mac2: aes128_encrypt(scbk, &key_template(0x01, 0x02, rnd_a)),
}
}
}
pub fn client_cryptogram(s_enc: &[u8; 16], rnd_a: &[u8; 8], rnd_b: &[u8; 8]) -> Block {
let mut block = [0u8; 16];
block[..8].copy_from_slice(rnd_a);
block[8..].copy_from_slice(rnd_b);
aes128_encrypt(s_enc, &block)
}
pub fn server_cryptogram(s_enc: &[u8; 16], rnd_a: &[u8; 8], rnd_b: &[u8; 8]) -> Block {
let mut block = [0u8; 16];
block[..8].copy_from_slice(rnd_b);
block[8..].copy_from_slice(rnd_a);
aes128_encrypt(s_enc, &block)
}
pub fn initial_rmac(s_mac1: &[u8; 16], s_mac2: &[u8; 16], server_cryptogram: &Block) -> Block {
let first = aes128_encrypt(s_mac1, server_cryptogram);
aes128_encrypt(s_mac2, &first)
}
pub fn diversify_scbk_legacy(mk: &[u8; 16], cuid: &[u8; 8]) -> [u8; 16] {
let mut block = [0u8; 16];
block[..8].copy_from_slice(cuid);
for (i, &b) in cuid.iter().enumerate() {
block[8 + i] = !b;
}
aes128_encrypt(mk, &block)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn enc_then_dec_identity() {
let key = [0u8; 16];
let plain = [0xABu8; 16];
let ct = aes128_encrypt(&key, &plain);
let pt = aes128_decrypt(&key, &ct);
assert_eq!(pt, plain);
}
#[test]
fn fips_known_answer() {
let key = [0u8; 16];
let plain = [0u8; 16];
let ct = aes128_encrypt(&key, &plain);
let expected = [
0x66, 0xE9, 0x4B, 0xD4, 0xEF, 0x8A, 0x2C, 0x3B, 0x88, 0x4C, 0xFA, 0x59, 0xCA, 0x34,
0x2B, 0x2E,
];
assert_eq!(ct, expected);
}
#[test]
fn keys_differ() {
let scbk = [0xAA; 16];
let rnd_a = [0x55u8; 8];
let k = SessionKeys::derive(&scbk, &rnd_a);
assert_ne!(k.s_enc, k.s_mac1);
assert_ne!(k.s_mac1, k.s_mac2);
assert_ne!(k.s_enc, k.s_mac2);
}
#[test]
fn cryptograms_distinct() {
let s_enc = [0; 16];
let rnd_a = [1u8; 8];
let rnd_b = [2u8; 8];
let c = client_cryptogram(&s_enc, &rnd_a, &rnd_b);
let s = server_cryptogram(&s_enc, &rnd_a, &rnd_b);
assert_ne!(c, s);
}
}