use aes::Aes128;
use aes::cipher::{BlockCipherEncrypt, KeyInit, KeyIvInit};
const BATCH: usize = 64;
pub struct Cfb8Encryptor {
inner: cfb8::Encryptor<Aes128>,
}
impl std::fmt::Debug for Cfb8Encryptor {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("Cfb8Encryptor { .. }")
}
}
impl Cfb8Encryptor {
pub fn new(secret: &[u8; 16]) -> Self {
Cfb8Encryptor {
inner: cfb8::Encryptor::<Aes128>::new(&(*secret).into(), &(*secret).into()),
}
}
#[inline]
pub fn encrypt(&mut self, buf: &mut [u8]) {
self.inner.encrypt(buf);
}
}
pub struct Cfb8Decryptor {
cipher: Aes128,
shift: [u8; 16],
blocks: Box<[aes::Block; BATCH]>,
}
impl std::fmt::Debug for Cfb8Decryptor {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("Cfb8Decryptor { .. }")
}
}
impl Cfb8Decryptor {
pub fn new(secret: &[u8; 16]) -> Self {
Cfb8Decryptor {
cipher: Aes128::new(&(*secret).into()),
shift: *secret,
blocks: Box::new([aes::Block::default(); BATCH]),
}
}
pub fn decrypt(&mut self, buf: &mut [u8]) {
let mut window = [0u8; 16 + BATCH];
for chunk in buf.chunks_mut(BATCH) {
let n = chunk.len();
window[..16].copy_from_slice(&self.shift);
window[16..16 + n].copy_from_slice(chunk);
for (i, block) in self.blocks[..n].iter_mut().enumerate() {
block.copy_from_slice(&window[i..i + 16]);
}
self.cipher.encrypt_blocks(&mut self.blocks[..n]);
for (b, ks) in chunk.iter_mut().zip(self.blocks[..n].iter()) {
*b ^= ks[0];
}
self.shift.copy_from_slice(&window[n..n + 16]);
}
}
}
#[derive(Debug)]
pub struct CipherPair {
pub encryptor: Cfb8Encryptor,
pub decryptor: Cfb8Decryptor,
}
impl CipherPair {
pub fn new(secret: &[u8; 16]) -> Self {
CipherPair {
encryptor: Cfb8Encryptor::new(secret),
decryptor: Cfb8Decryptor::new(secret),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn hex(s: &str) -> Vec<u8> {
(0..s.len())
.step_by(2)
.map(|i| u8::from_str_radix(&s[i..i + 2], 16).unwrap())
.collect()
}
#[test]
fn nist_sp800_38a_cfb8() {
let key: [u8; 16] = hex("2b7e151628aed2a6abf7158809cf4f3c").try_into().unwrap();
let iv: [u8; 16] = hex("000102030405060708090a0b0c0d0e0f").try_into().unwrap();
let pt = hex("6bc1bee22e409f96e93d7e117393172aae2d");
let ct = hex("3b79424c9c0dd436bace9e0ed4586a4f32b9");
let mut enc = cfb8::Encryptor::<Aes128>::new(&key.into(), &iv.into());
let mut buf = pt.clone();
enc.encrypt(&mut buf);
assert_eq!(buf, ct);
let mut dec = Cfb8Decryptor {
cipher: Aes128::new(&key.into()),
shift: iv,
blocks: Box::new([aes::Block::default(); BATCH]),
};
let mut buf = ct.clone();
dec.decrypt(&mut buf);
assert_eq!(buf, pt);
}
#[test]
fn stream_in_pieces_matches_reference() {
let secret = [7u8; 16];
let data: Vec<u8> = (0..5000u32)
.map(|i| (i.wrapping_mul(2654435761) >> 13) as u8)
.collect();
let mut ct = data.clone();
let mut enc = Cfb8Encryptor::new(&secret);
let mut off = 0;
for step in [1, 15, 16, 17, 63, 64, 65, 200, 1000].iter().cycle() {
if off >= ct.len() {
break;
}
let end = (off + step).min(ct.len());
enc.encrypt(&mut ct[off..end]);
off = end;
}
let mut reference = ct.clone();
cfb8::Decryptor::<Aes128>::new(&secret.into(), &secret.into()).decrypt(&mut reference);
assert_eq!(reference, data);
let mut pt = ct.clone();
let mut dec = Cfb8Decryptor::new(&secret);
let mut off = 0;
for step in [3, 64, 1, 129, 7, 4096].iter().cycle() {
if off >= pt.len() {
break;
}
let end = (off + step).min(pt.len());
dec.decrypt(&mut pt[off..end]);
off = end;
}
assert_eq!(pt, data);
}
}