#![allow(deprecated)]
#[allow(deprecated)]
use aes::cipher::{Array, BlockCipherEncrypt, KeyInit};
use aes::{Aes128, Aes256};
use crate::common::{BLOCK_LENGTH, Error, xor_blocks};
use crate::hctr2::{Hctr2_128, Hctr2_256};
#[allow(non_camel_case_types)]
#[deprecated(note = "Use common::Error instead")]
pub type Hctr2TwKDError = Error;
pub struct CencKdf128 {
ks: Aes128,
}
impl CencKdf128 {
pub const MASTER_KEY_LENGTH: usize = 16;
pub const KEY_LENGTH: usize = 16;
pub const MAX_TWEAK_LENGTH: usize = 14;
pub fn new(master_key: &[u8; 16]) -> Self {
Self {
ks: Aes128::new(Array::from_slice(master_key)),
}
}
pub fn derive_key(&self, tweak: &[u8]) -> [u8; 16] {
debug_assert!(tweak.len() <= Self::MAX_TWEAK_LENGTH);
let mut block0 = [0u8; BLOCK_LENGTH];
let mut block1 = [0u8; BLOCK_LENGTH];
block0[0] = 0x00;
block1[0] = 0x01;
block0[1..1 + tweak.len()].copy_from_slice(tweak);
block1[1..1 + tweak.len()].copy_from_slice(tweak);
let mut enc0 = Array::from(block0);
let mut enc1 = Array::from(block1);
self.ks.encrypt_block(&mut enc0);
self.ks.encrypt_block(&mut enc1);
xor_blocks(&enc0.into(), &enc1.into())
}
}
pub struct CencKdf256 {
ks: Aes256,
}
impl CencKdf256 {
pub const MASTER_KEY_LENGTH: usize = 32;
pub const KEY_LENGTH: usize = 32;
pub const MAX_TWEAK_LENGTH: usize = 14;
pub fn new(master_key: &[u8; 32]) -> Self {
Self {
ks: Aes256::new(Array::from_slice(master_key)),
}
}
pub fn derive_key(&self, tweak: &[u8]) -> [u8; 32] {
debug_assert!(tweak.len() <= Self::MAX_TWEAK_LENGTH);
let mut block0 = [0u8; BLOCK_LENGTH];
let mut block1 = [0u8; BLOCK_LENGTH];
let mut block2 = [0u8; BLOCK_LENGTH];
block0[0] = 0x00;
block1[0] = 0x01;
block2[0] = 0x02;
block0[1..1 + tweak.len()].copy_from_slice(tweak);
block1[1..1 + tweak.len()].copy_from_slice(tweak);
block2[1..1 + tweak.len()].copy_from_slice(tweak);
let mut enc0 = Array::from(block0);
let mut enc1 = Array::from(block1);
let mut enc2 = Array::from(block2);
self.ks.encrypt_block(&mut enc0);
self.ks.encrypt_block(&mut enc1);
self.ks.encrypt_block(&mut enc2);
let enc0_arr: [u8; 16] = enc0.into();
let enc1_arr: [u8; 16] = enc1.into();
let enc2_arr: [u8; 16] = enc2.into();
let mut derived = [0u8; 32];
derived[..16].copy_from_slice(&xor_blocks(&enc0_arr, &enc1_arr));
derived[16..].copy_from_slice(&xor_blocks(&enc0_arr, &enc2_arr));
derived
}
}
#[allow(non_camel_case_types)]
pub struct Hctr2TwKD_128 {
kdf: CencKdf128,
}
impl Hctr2TwKD_128 {
pub const KEY_LENGTH: usize = 16;
pub const BLOCK_LENGTH: usize = BLOCK_LENGTH;
pub const MAX_TWEAK_LENGTH: usize = CencKdf128::MAX_TWEAK_LENGTH;
pub const MIN_INPUT_LENGTH: usize = BLOCK_LENGTH;
pub fn new(key: &[u8; 16]) -> Self {
Self {
kdf: CencKdf128::new(key),
}
}
pub fn encrypt(
&self,
plaintext: &[u8],
tweak: &[u8],
ciphertext: &mut [u8],
) -> Result<(), Error> {
if tweak.len() > Self::MAX_TWEAK_LENGTH {
return Err(Error::TweakTooLong);
}
let derived_key = self.kdf.derive_key(tweak);
let hctr2 = Hctr2_128::new(&derived_key);
hctr2.encrypt(plaintext, &[], ciphertext)?;
Ok(())
}
pub fn decrypt(
&self,
ciphertext: &[u8],
tweak: &[u8],
plaintext: &mut [u8],
) -> Result<(), Error> {
if tweak.len() > Self::MAX_TWEAK_LENGTH {
return Err(Error::TweakTooLong);
}
let derived_key = self.kdf.derive_key(tweak);
let hctr2 = Hctr2_128::new(&derived_key);
hctr2.decrypt(ciphertext, &[], plaintext)?;
Ok(())
}
pub fn encrypt_split(
&self,
plaintext: &[u8],
kdf_tweak: &[u8],
hctr2_tweak: &[u8],
ciphertext: &mut [u8],
) -> Result<(), Error> {
if kdf_tweak.len() > Self::MAX_TWEAK_LENGTH {
return Err(Error::TweakTooLong);
}
let derived_key = self.kdf.derive_key(kdf_tweak);
let hctr2 = Hctr2_128::new(&derived_key);
hctr2.encrypt(plaintext, hctr2_tweak, ciphertext)?;
Ok(())
}
pub fn decrypt_split(
&self,
ciphertext: &[u8],
kdf_tweak: &[u8],
hctr2_tweak: &[u8],
plaintext: &mut [u8],
) -> Result<(), Error> {
if kdf_tweak.len() > Self::MAX_TWEAK_LENGTH {
return Err(Error::TweakTooLong);
}
let derived_key = self.kdf.derive_key(kdf_tweak);
let hctr2 = Hctr2_128::new(&derived_key);
hctr2.decrypt(ciphertext, hctr2_tweak, plaintext)?;
Ok(())
}
}
#[allow(non_camel_case_types)]
pub struct Hctr2TwKD_256 {
kdf: CencKdf256,
}
impl Hctr2TwKD_256 {
pub const KEY_LENGTH: usize = 32;
pub const BLOCK_LENGTH: usize = BLOCK_LENGTH;
pub const MAX_TWEAK_LENGTH: usize = CencKdf256::MAX_TWEAK_LENGTH;
pub const MIN_INPUT_LENGTH: usize = BLOCK_LENGTH;
pub fn new(key: &[u8; 32]) -> Self {
Self {
kdf: CencKdf256::new(key),
}
}
pub fn encrypt(
&self,
plaintext: &[u8],
tweak: &[u8],
ciphertext: &mut [u8],
) -> Result<(), Error> {
if tweak.len() > Self::MAX_TWEAK_LENGTH {
return Err(Error::TweakTooLong);
}
let derived_key = self.kdf.derive_key(tweak);
let hctr2 = Hctr2_256::new(&derived_key);
hctr2.encrypt(plaintext, &[], ciphertext)?;
Ok(())
}
pub fn decrypt(
&self,
ciphertext: &[u8],
tweak: &[u8],
plaintext: &mut [u8],
) -> Result<(), Error> {
if tweak.len() > Self::MAX_TWEAK_LENGTH {
return Err(Error::TweakTooLong);
}
let derived_key = self.kdf.derive_key(tweak);
let hctr2 = Hctr2_256::new(&derived_key);
hctr2.decrypt(ciphertext, &[], plaintext)?;
Ok(())
}
pub fn encrypt_split(
&self,
plaintext: &[u8],
kdf_tweak: &[u8],
hctr2_tweak: &[u8],
ciphertext: &mut [u8],
) -> Result<(), Error> {
if kdf_tweak.len() > Self::MAX_TWEAK_LENGTH {
return Err(Error::TweakTooLong);
}
let derived_key = self.kdf.derive_key(kdf_tweak);
let hctr2 = Hctr2_256::new(&derived_key);
hctr2.encrypt(plaintext, hctr2_tweak, ciphertext)?;
Ok(())
}
pub fn decrypt_split(
&self,
ciphertext: &[u8],
kdf_tweak: &[u8],
hctr2_tweak: &[u8],
plaintext: &mut [u8],
) -> Result<(), Error> {
if kdf_tweak.len() > Self::MAX_TWEAK_LENGTH {
return Err(Error::TweakTooLong);
}
let derived_key = self.kdf.derive_key(kdf_tweak);
let hctr2 = Hctr2_256::new(&derived_key);
hctr2.decrypt(ciphertext, hctr2_tweak, plaintext)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_hctr2_twkd_128_roundtrip() {
let key = [0u8; 16];
let cipher = Hctr2TwKD_128::new(&key);
let plaintext = b"Hello, HCTR2-TwKD!";
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_hctr2_twkd_128_roundtrip_nonzero_key() {
let key: [u8; 16] = core::array::from_fn(|i| (i + 1) as u8);
let cipher = Hctr2TwKD_128::new(&key);
let plaintext = b"Hello, HCTR2-TwKD!";
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_hctr2_twkd_128_minimum_length() {
let key = [0u8; 16];
let cipher = Hctr2TwKD_128::new(&key);
let plaintext = [0x42u8; 16];
let mut ciphertext = [0u8; 16];
let mut decrypted = [0u8; 16];
cipher
.encrypt(&plaintext, b"tweak", &mut ciphertext)
.unwrap();
cipher
.decrypt(&ciphertext, b"tweak", &mut decrypted)
.unwrap();
assert_eq!(plaintext, decrypted);
}
#[test]
fn test_hctr2_twkd_128_input_too_short() {
let key = [0u8; 16];
let cipher = Hctr2TwKD_128::new(&key);
let plaintext = [0x42u8; 15];
let mut ciphertext = [0u8; 15];
assert_eq!(
cipher.encrypt(&plaintext, b"tweak", &mut ciphertext),
Err(Error::InputTooShort)
);
}
#[test]
fn test_hctr2_twkd_128_tweak_too_long() {
let key = [0u8; 16];
let cipher = Hctr2TwKD_128::new(&key);
let plaintext = [0x42u8; 16];
let mut ciphertext = [0u8; 16];
let long_tweak = [0u8; 15];
assert_eq!(
cipher.encrypt(&plaintext, &long_tweak, &mut ciphertext),
Err(Error::TweakTooLong)
);
}
#[test]
fn test_hctr2_twkd_128_different_tweaks() {
let key = [0u8; 16];
let cipher = Hctr2TwKD_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_hctr2_twkd_128_split_tweak() {
let key = [0u8; 16];
let cipher = Hctr2TwKD_128::new(&key);
let plaintext = b"Test split tweak mode";
let mut ciphertext = vec![0u8; plaintext.len()];
let mut decrypted = vec![0u8; plaintext.len()];
let kdf_tweak = b"kdf part";
let hctr2_tweak = b"hctr2 part - can be longer than 14 bytes!";
cipher
.encrypt_split(plaintext, kdf_tweak, hctr2_tweak, &mut ciphertext)
.unwrap();
cipher
.decrypt_split(&ciphertext, kdf_tweak, hctr2_tweak, &mut decrypted)
.unwrap();
assert_eq!(plaintext.as_slice(), decrypted.as_slice());
}
#[test]
fn test_hctr2_twkd_128_large_message() {
let key = [0u8; 16];
let cipher = Hctr2TwKD_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_hctr2_twkd_256_roundtrip() {
let key = [0u8; 32];
let cipher = Hctr2TwKD_256::new(&key);
let plaintext = b"Hello, HCTR2-TwKD-256!";
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_cenc_kdf_128_different_tweaks() {
let master_key = [0u8; 16];
let kdf = CencKdf128::new(&master_key);
let key1 = kdf.derive_key(b"tweak1");
let key2 = kdf.derive_key(b"tweak2");
assert_ne!(key1, key2);
}
#[test]
fn test_cenc_kdf_128_deterministic() {
let master_key = [0u8; 16];
let kdf = CencKdf128::new(&master_key);
let key1 = kdf.derive_key(b"same tweak");
let key2 = kdf.derive_key(b"same tweak");
assert_eq!(key1, key2);
}
#[test]
fn test_cenc_kdf_256_key_derivation() {
let master_key = [0u8; 32];
let kdf = CencKdf256::new(&master_key);
let key = kdf.derive_key(b"test");
assert_eq!(key.len(), 32);
let key2 = kdf.derive_key(b"test2");
assert_ne!(key, key2);
}
}