#![allow(deprecated)]
#[allow(deprecated)]
use aes::cipher::{Array, BlockCipherDecrypt};
use aes::{Aes128, Aes256};
use polyval::{Polyval, universal_hash::UniversalHash};
use sha2::{Digest, Sha256};
use crate::common::{BLOCK_LENGTH, Direction, Error, absorb, elk, xor_blocks, xor_blocks_3};
use crate::hctr2::AesCipher;
#[allow(non_camel_case_types)]
#[deprecated(note = "Use common::Error instead")]
pub type Hctr3Error = Error;
pub struct Hctr3<Aes: AesCipher> {
ks_enc: Aes,
ks_dec: Aes::Dec,
ke_enc: Aes,
h: [u8; BLOCK_LENGTH],
l: [u8; BLOCK_LENGTH],
}
#[allow(non_camel_case_types)]
pub type Hctr3_128 = Hctr3<Aes128>;
#[allow(non_camel_case_types)]
pub type Hctr3_256 = Hctr3<Aes256>;
impl<Aes: AesCipher> Hctr3<Aes> {
pub const KEY_LENGTH: usize = Aes::KEY_LEN;
pub const BLOCK_LENGTH: usize = BLOCK_LENGTH;
pub const MIN_INPUT_LENGTH: usize = BLOCK_LENGTH;
pub fn new(key: &[u8]) -> Self {
debug_assert_eq!(key.len(), Aes::KEY_LEN);
let ks_enc = Aes::new(Array::from_slice(key));
let ks_dec = Aes::new_dec(key);
let ke_key: Vec<u8> = if Aes::KEY_LEN <= 16 {
let mut ke_block = Array::clone_from_slice(&[0u8; 16]);
ks_enc.encrypt_block(&mut ke_block);
ke_block[..Aes::KEY_LEN].to_vec()
} else {
let mut ke_block0 = Array::clone_from_slice(&[0u8; 16]);
let mut ke_block1 = Array::clone_from_slice(&[0x01u8; 16]);
ks_enc.encrypt_block(&mut ke_block0);
ks_enc.encrypt_block(&mut ke_block1);
let mut ke = vec![0u8; Aes::KEY_LEN];
ke[..16].copy_from_slice(ke_block0.as_slice());
ke[16..].copy_from_slice(&ke_block1.as_slice()[..(Aes::KEY_LEN - 16)]);
ke
};
let ke_enc = Aes::new(Array::from_slice(&ke_key));
let mut h_block = Array::clone_from_slice(&[0u8; 16]);
let mut l_block = Array::clone_from_slice(&{
let mut b = [0u8; 16];
b[15] = 1;
b
});
ke_enc.encrypt_block(&mut h_block);
ke_enc.encrypt_block(&mut l_block);
let h: [u8; 16] = h_block.as_slice().try_into().unwrap();
let l: [u8; 16] = l_block.as_slice().try_into().unwrap();
Self {
ks_enc,
ks_dec,
ke_enc,
h,
l,
}
}
pub fn encrypt(
&self,
plaintext: &[u8],
tweak: &[u8],
ciphertext: &mut [u8],
) -> Result<(), Error> {
self.hctr3(plaintext, tweak, ciphertext, Direction::Encrypt)
}
pub fn decrypt(
&self,
ciphertext: &[u8],
tweak: &[u8],
plaintext: &mut [u8],
) -> Result<(), Error> {
self.hctr3(ciphertext, tweak, plaintext, Direction::Decrypt)
}
fn hctr3(
&self,
src: &[u8],
tweak: &[u8],
dst: &mut [u8],
direction: Direction,
) -> Result<(), Error> {
debug_assert_eq!(dst.len(), src.len());
if src.len() < BLOCK_LENGTH {
return Err(Error::InputTooShort);
}
let m: [u8; BLOCK_LENGTH] = src[..BLOCK_LENGTH].try_into().unwrap();
let n = &src[BLOCK_LENGTH..];
let mut hasher = Sha256::new();
hasher.update(tweak);
let hash_out = hasher.finalize();
let t: [u8; BLOCK_LENGTH] = hash_out[..BLOCK_LENGTH].try_into().unwrap();
let tweak_len_bits = tweak.len() * 8;
let tweak_len_encoded: u128 = if n.len() % BLOCK_LENGTH == 0 {
(2 * tweak_len_bits + 2) as u128
} else {
(2 * tweak_len_bits + 3) as u128
};
let mut poly = Polyval::new(Array::from_slice(&self.h));
poly.update(&[Array::from(tweak_len_encoded.to_le_bytes())]);
poly.update(&[Array::from(t)]);
let poly_after_tweak = poly.clone();
let hh = absorb(&mut poly, n);
let mm = xor_blocks(&hh, &m);
let uu: [u8; BLOCK_LENGTH] = match direction {
Direction::Encrypt => {
let mut block = Array::clone_from_slice(&mm);
self.ks_enc.encrypt_block(&mut block);
block.as_slice().try_into().unwrap()
}
Direction::Decrypt => {
let mut block = Array::clone_from_slice(&mm);
self.ks_dec.decrypt_block(&mut block);
block.as_slice().try_into().unwrap()
}
};
let s = xor_blocks_3(&mm, &uu, &self.l);
let (u, v) = dst.split_at_mut(BLOCK_LENGTH);
elk(&self.ke_enc, v, n, &s);
let mut poly = poly_after_tweak;
let hh2 = absorb(&mut poly, v);
u.copy_from_slice(&xor_blocks(&uu, &hh2));
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::common::lfsr_next_128;
#[test]
fn test_hctr3_128_roundtrip() {
let key = [0u8; 16];
let cipher = Hctr3_128::new(&key);
let plaintext = b"Hello, HCTR3 World!";
let mut ciphertext = vec![0u8; plaintext.len()];
let mut decrypted = vec![0u8; plaintext.len()];
let tweak = b"test tweak";
cipher.encrypt(plaintext, tweak, &mut ciphertext).unwrap();
cipher.decrypt(&ciphertext, tweak, &mut decrypted).unwrap();
assert_eq!(plaintext.as_slice(), decrypted.as_slice());
}
#[test]
fn test_hctr3_128_roundtrip_nonzero_key() {
let key = [
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
0x0f, 0x10,
];
let cipher = Hctr3_128::new(&key);
let plaintext = b"Hello, HCTR3 World!";
let mut ciphertext = vec![0u8; plaintext.len()];
let mut decrypted = vec![0u8; plaintext.len()];
let tweak = b"test tweak";
cipher.encrypt(plaintext, tweak, &mut ciphertext).unwrap();
assert_ne!(plaintext.as_slice(), ciphertext.as_slice());
cipher.decrypt(&ciphertext, tweak, &mut decrypted).unwrap();
assert_eq!(plaintext.as_slice(), decrypted.as_slice());
}
#[test]
fn test_hctr3_128_minimum_length() {
let key = [0u8; 16];
let cipher = Hctr3_128::new(&key);
let plaintext = [0x42u8; 16];
let mut ciphertext = [0u8; 16];
let mut decrypted = [0u8; 16];
cipher.encrypt(&plaintext, b"", &mut ciphertext).unwrap();
cipher.decrypt(&ciphertext, b"", &mut decrypted).unwrap();
assert_eq!(plaintext, decrypted);
}
#[test]
fn test_hctr3_128_input_too_short() {
let key = [0u8; 16];
let cipher = Hctr3_128::new(&key);
let plaintext = [0x42u8; 15];
let mut ciphertext = [0u8; 15];
assert_eq!(
cipher.encrypt(&plaintext, b"", &mut ciphertext),
Err(Error::InputTooShort)
);
}
#[test]
fn test_hctr3_128_different_tweaks() {
let key = [0u8; 16];
let cipher = Hctr3_128::new(&key);
let plaintext = [0x42u8; 32];
let mut ciphertext1 = [0u8; 32];
let mut ciphertext2 = [0u8; 32];
cipher
.encrypt(&plaintext, b"tweak1", &mut ciphertext1)
.unwrap();
cipher
.encrypt(&plaintext, b"tweak2", &mut ciphertext2)
.unwrap();
assert_ne!(ciphertext1, ciphertext2);
}
#[test]
fn test_hctr3_128_large_message() {
let key = [0u8; 16];
let cipher = Hctr3_128::new(&key);
let plaintext = [0xABu8; 1024];
let mut ciphertext = [0u8; 1024];
let mut decrypted = [0u8; 1024];
cipher
.encrypt(&plaintext, b"large tweak", &mut ciphertext)
.unwrap();
cipher
.decrypt(&ciphertext, b"large tweak", &mut decrypted)
.unwrap();
assert_eq!(plaintext.as_slice(), decrypted.as_slice());
}
#[test]
fn test_hctr3_256_roundtrip() {
let key = [0u8; 32];
let cipher = Hctr3_256::new(&key);
let plaintext = b"Hello, HCTR3-256 World!";
let mut ciphertext = vec![0u8; plaintext.len()];
let mut decrypted = vec![0u8; plaintext.len()];
let tweak = b"test tweak 256";
cipher.encrypt(plaintext, tweak, &mut ciphertext).unwrap();
cipher.decrypt(&ciphertext, tweak, &mut decrypted).unwrap();
assert_eq!(plaintext.as_slice(), decrypted.as_slice());
}
#[test]
fn test_lfsr_128_produces_unique_states() {
let initial = [0x01u8; 16];
let mut state = initial;
let mut seen = std::collections::HashSet::new();
seen.insert(state);
for _ in 0..1000 {
state = lfsr_next_128(&state);
assert!(seen.insert(state), "LFSR produced duplicate state");
}
}
}