use std::sync::LazyLock;
use aes::{
Aes128,
cipher::{BlockCipherEncrypt, Key, KeyInit},
};
use bytemuck::Pod;
use crate::{block::Block, crypto::AES_PAR_BLOCKS, utils::xor_inplace};
#[derive(Clone)]
pub(crate) struct AesHash {
aes: Aes128,
}
impl AesHash {
pub(crate) fn new(key: &Key<Aes128>) -> Self {
Self {
aes: Aes128::new(key),
}
}
pub(crate) fn cr_hash_block(&self, x: Block) -> Block {
let mut x_enc = x.into();
self.aes.encrypt_block(&mut x_enc);
x ^ x_enc.into()
}
pub(crate) fn cr_hash_blocks<const N: usize>(&self, x: &[Block; N]) -> [Block; N]
where
[Block; N]: Pod,
[aes::Block; N]: Pod,
{
let mut blocks: [aes::Block; N] = bytemuck::cast(*x);
self.aes.encrypt_blocks(&mut blocks);
let mut blocks: [Block; N] = bytemuck::cast(blocks);
xor_inplace(&mut blocks, x);
blocks
}
pub(crate) fn cr_hash_blocks_b2b<const N: usize>(&self, inp: &[Block; N], out: &mut [Block])
where
[Block; N]: Pod,
[aes::Block; N]: Pod,
{
assert_eq!(N, out.len(), "inp.len() must be equal to out.len()");
let inp_aes: &[aes::Block; N] = bytemuck::cast_ref(inp);
let out_aes: &mut [aes::Block] = bytemuck::cast_slice_mut(out);
self.aes
.encrypt_blocks_b2b(inp_aes, out_aes)
.expect("buffer have equal size");
xor_inplace(out, inp);
}
pub(crate) fn cr_hash_slice_mut(&self, x: &mut [Block]) {
let mut tmp = [aes::Block::default(); AES_PAR_BLOCKS];
for chunk in x.chunks_mut(AES_PAR_BLOCKS) {
self.aes
.encrypt_blocks_b2b(bytemuck::cast_slice(chunk), &mut tmp[..chunk.len()])
.expect("in and out always have same length");
chunk
.iter_mut()
.zip(tmp)
.for_each(|(x, x_enc)| *x ^= x_enc.into());
}
}
pub(crate) fn tccr_hash_block(&self, tweak: Block, x: Block) -> Block {
let mut x_enc = x.into();
self.aes.encrypt_block(&mut x_enc);
let mut x_enc_xor_tweak_enc = (Block::from(x_enc) ^ tweak).into();
self.aes.encrypt_block(&mut x_enc_xor_tweak_enc);
Block::from(x_enc_xor_tweak_enc) ^ Block::from(x_enc)
}
pub(crate) fn tccr_hash_slice_mut(
&self,
x: &mut [Block],
mut tweak_fn: impl FnMut(usize) -> Block,
) {
let mut tmp = [aes::Block::default(); AES_PAR_BLOCKS];
for (chunk_idx, chunk) in x.chunks_mut(AES_PAR_BLOCKS).enumerate() {
self.aes
.encrypt_blocks_b2b(bytemuck::cast_slice(chunk), &mut tmp[..chunk.len()])
.expect("in and out always have same length");
chunk
.iter_mut()
.zip(&tmp)
.enumerate()
.for_each(|(idx, (dest, x_enc))| {
*dest = Block::from(*x_enc) ^ tweak_fn(chunk_idx * AES_PAR_BLOCKS + idx);
});
self.aes.encrypt_blocks(bytemuck::cast_slice_mut(chunk));
chunk
.iter_mut()
.zip(tmp)
.for_each(|(x, x_enc)| *x ^= x_enc.into());
}
}
}
pub(crate) static FIXED_KEY_HASH: LazyLock<AesHash> = LazyLock::new(|| {
let key = 193502124791825095790518994062991136444_u128
.to_le_bytes()
.into();
AesHash::new(&key)
});