#![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 TWEAK_LENGTH: usize = 16;
pub const TWEAK_BITS: usize = 126;
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!(Self::validate_tweak(tweak));
let block0 = Self::make_block(tweak, 0);
let block1 = Self::make_block(tweak, 1);
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 fn validate_tweak(tweak: &[u8]) -> bool {
tweak.len() == Self::TWEAK_LENGTH && (tweak[0] & 0xC0) == 0
}
fn make_block(tweak: &[u8], prefix: u8) -> [u8; BLOCK_LENGTH] {
debug_assert!(prefix <= 2);
let mut block = [0u8; BLOCK_LENGTH];
block.copy_from_slice(tweak);
block[0] = (block[0] & 0x3F) | (prefix << 6);
block
}
}
pub struct CencKdf256 {
ks: Aes256,
}
impl CencKdf256 {
pub const MASTER_KEY_LENGTH: usize = 32;
pub const KEY_LENGTH: usize = 32;
pub const TWEAK_LENGTH: usize = 16;
pub const TWEAK_BITS: usize = 126;
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!(Self::validate_tweak(tweak));
let block0 = Self::make_block(tweak, 0);
let block1 = Self::make_block(tweak, 1);
let block2 = Self::make_block(tweak, 2);
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
}
pub fn validate_tweak(tweak: &[u8]) -> bool {
tweak.len() == Self::TWEAK_LENGTH && (tweak[0] & 0xC0) == 0
}
fn make_block(tweak: &[u8], prefix: u8) -> [u8; BLOCK_LENGTH] {
debug_assert!(prefix <= 2);
let mut block = [0u8; BLOCK_LENGTH];
block.copy_from_slice(tweak);
block[0] = (block[0] & 0x3F) | (prefix << 6);
block
}
}
#[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 KDF_TWEAK_LENGTH: usize = CencKdf128::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::KDF_TWEAK_LENGTH {
return Err(Error::TweakTooShort);
}
let kdf_tweak = &tweak[..Self::KDF_TWEAK_LENGTH];
if !CencKdf128::validate_tweak(kdf_tweak) {
return Err(Error::InvalidTweak);
}
let derived_key = self.kdf.derive_key(kdf_tweak);
let hctr2 = Hctr2_128::new(&derived_key);
hctr2.encrypt(plaintext, &tweak[Self::KDF_TWEAK_LENGTH..], ciphertext)?;
Ok(())
}
pub fn decrypt(
&self,
ciphertext: &[u8],
tweak: &[u8],
plaintext: &mut [u8],
) -> Result<(), Error> {
if tweak.len() < Self::KDF_TWEAK_LENGTH {
return Err(Error::TweakTooShort);
}
let kdf_tweak = &tweak[..Self::KDF_TWEAK_LENGTH];
if !CencKdf128::validate_tweak(kdf_tweak) {
return Err(Error::InvalidTweak);
}
let derived_key = self.kdf.derive_key(kdf_tweak);
let hctr2 = Hctr2_128::new(&derived_key);
hctr2.decrypt(ciphertext, &tweak[Self::KDF_TWEAK_LENGTH..], 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::KDF_TWEAK_LENGTH {
return Err(Error::TweakTooShort);
}
if kdf_tweak.len() > Self::KDF_TWEAK_LENGTH {
return Err(Error::TweakTooLong);
}
if !CencKdf128::validate_tweak(kdf_tweak) {
return Err(Error::InvalidTweak);
}
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::KDF_TWEAK_LENGTH {
return Err(Error::TweakTooShort);
}
if kdf_tweak.len() > Self::KDF_TWEAK_LENGTH {
return Err(Error::TweakTooLong);
}
if !CencKdf128::validate_tweak(kdf_tweak) {
return Err(Error::InvalidTweak);
}
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 KDF_TWEAK_LENGTH: usize = CencKdf256::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::KDF_TWEAK_LENGTH {
return Err(Error::TweakTooShort);
}
let kdf_tweak = &tweak[..Self::KDF_TWEAK_LENGTH];
if !CencKdf256::validate_tweak(kdf_tweak) {
return Err(Error::InvalidTweak);
}
let derived_key = self.kdf.derive_key(kdf_tweak);
let hctr2 = Hctr2_256::new(&derived_key);
hctr2.encrypt(plaintext, &tweak[Self::KDF_TWEAK_LENGTH..], ciphertext)?;
Ok(())
}
pub fn decrypt(
&self,
ciphertext: &[u8],
tweak: &[u8],
plaintext: &mut [u8],
) -> Result<(), Error> {
if tweak.len() < Self::KDF_TWEAK_LENGTH {
return Err(Error::TweakTooShort);
}
let kdf_tweak = &tweak[..Self::KDF_TWEAK_LENGTH];
if !CencKdf256::validate_tweak(kdf_tweak) {
return Err(Error::InvalidTweak);
}
let derived_key = self.kdf.derive_key(kdf_tweak);
let hctr2 = Hctr2_256::new(&derived_key);
hctr2.decrypt(ciphertext, &tweak[Self::KDF_TWEAK_LENGTH..], 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::KDF_TWEAK_LENGTH {
return Err(Error::TweakTooShort);
}
if kdf_tweak.len() > Self::KDF_TWEAK_LENGTH {
return Err(Error::TweakTooLong);
}
if !CencKdf256::validate_tweak(kdf_tweak) {
return Err(Error::InvalidTweak);
}
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::KDF_TWEAK_LENGTH {
return Err(Error::TweakTooShort);
}
if kdf_tweak.len() > Self::KDF_TWEAK_LENGTH {
return Err(Error::TweakTooLong);
}
if !CencKdf256::validate_tweak(kdf_tweak) {
return Err(Error::InvalidTweak);
}
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 = [0x01u8; 20];
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 = [0x01u8; 20];
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];
let tweak = [0x03u8; 16];
cipher.encrypt(&plaintext, &tweak, &mut ciphertext).unwrap();
cipher.decrypt(&ciphertext, &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];
let tweak = [0x04u8; 16];
assert_eq!(
cipher.encrypt(&plaintext, &tweak, &mut ciphertext),
Err(Error::InputTooShort)
);
}
#[test]
fn test_hctr2_twkd_128_tweak_too_short() {
let key = [0u8; 16];
let cipher = Hctr2TwKD_128::new(&key);
let plaintext = [0x42u8; 16];
let mut ciphertext = [0u8; 16];
let short_tweak = [0u8; 8];
assert_eq!(
cipher.encrypt(&plaintext, &short_tweak, &mut ciphertext),
Err(Error::TweakTooShort)
);
}
#[test]
fn test_hctr2_twkd_128_invalid_kdf_tweak_bits() {
let key = [0u8; 16];
let cipher = Hctr2TwKD_128::new(&key);
let plaintext = [0x42u8; 16];
let mut ciphertext = [0u8; 16];
let mut bad_tweak = [0u8; 16];
bad_tweak[0] = 0xC0;
assert_eq!(
cipher.encrypt(&plaintext, &bad_tweak, &mut ciphertext),
Err(Error::InvalidTweak)
);
}
#[test]
fn test_hctr2_twkd_128_split_tweak_too_long() {
let key = [0u8; 16];
let cipher = Hctr2TwKD_128::new(&key);
let plaintext = b"split tweak length";
let mut ciphertext = vec![0u8; plaintext.len()];
let kdf_tweak = [0x01u8; 17];
assert_eq!(
cipher.encrypt_split(plaintext, &kdf_tweak, b"hctr2", &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];
let tweak1 = [0x05u8; 20];
let tweak2 = [0x06u8; 20];
cipher.encrypt(&plaintext, &tweak1, &mut ciphertext1).unwrap();
cipher.encrypt(&plaintext, &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 = [0x07u8; 16];
let hctr2_tweak = b"hctr2 part - can be any length";
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];
let tweak = [0x08u8; 16];
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_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 = [0x02u8; 20];
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 tweak1 = [0x01u8; 16];
let tweak2 = [0x02u8; 16];
let key1 = kdf.derive_key(&tweak1);
let key2 = kdf.derive_key(&tweak2);
assert_ne!(key1, key2);
}
#[test]
fn test_cenc_kdf_128_deterministic() {
let master_key = [0u8; 16];
let kdf = CencKdf128::new(&master_key);
let tweak = [0x03u8; 16];
let key1 = kdf.derive_key(&tweak);
let key2 = kdf.derive_key(&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 tweak = [0x04u8; 16];
let key = kdf.derive_key(&tweak);
assert_eq!(key.len(), 32);
let tweak2 = [0x05u8; 16];
let key2 = kdf.derive_key(&tweak2);
assert_ne!(key, key2);
}
}