use crate::utilities::{BLOCK_SIZE, aes256_encrypt, xor_arrays};
use zeroize::Zeroize;
pub const DEFAULT_DIGEST_SIZE: usize = 2usize * BLOCK_SIZE;
pub const DEFAULT_PERMUTE_ROUNDS: usize = 3usize;
#[cfg(feature = "tracing")]
macro_rules! trace {
($self:tt, $arg:tt) => {
log::trace!(
"SpongeHash256@{:p}: {} --> {:02x?} {:02x?} {:02x?}",
&$self,
$arg,
&$self.state0,
&$self.state1,
&$self.state2
);
};
}
#[cfg(not(feature = "tracing"))]
macro_rules! trace {
($self:tt, $arg:tt) => {};
}
struct NoneZeroArg<const N: usize>;
impl<const N: usize> NoneZeroArg<N> {
const OK: () = assert!(N > 0, "Const generic argument must be a non-zero value!");
}
pub struct SpongeHash256<const R: usize = DEFAULT_PERMUTE_ROUNDS> {
state0: [u8; BLOCK_SIZE],
state1: [u8; BLOCK_SIZE],
state2: [u8; BLOCK_SIZE],
offset: usize,
}
impl<const R: usize> SpongeHash256<R> {
const BIT_MASK_0: [u8; BLOCK_SIZE] = [0x5Cu8; BLOCK_SIZE];
const BIT_MASK_1: [u8; BLOCK_SIZE] = [0x36u8; BLOCK_SIZE];
pub fn new() -> Self {
let () = NoneZeroArg::<R>::OK;
Self {
state0: [0u8; BLOCK_SIZE],
state1: [0u8; BLOCK_SIZE],
state2: [0u8; BLOCK_SIZE],
offset: 0usize,
}
}
pub fn update(&mut self, message_chunk: &[u8]) {
trace!(self, "update::enter");
for byte in message_chunk {
self.state0[self.offset] ^= byte;
self.offset += 1usize;
if self.offset >= BLOCK_SIZE {
self.permute();
self.offset = 0usize;
}
}
trace!(self, "update::leave");
}
pub fn digest<const N: usize>(self) -> [u8; N] {
let mut digest = [0u8; N];
self.digest_to_slice(&mut digest);
digest
}
pub fn digest_to_slice<const N: usize>(mut self, digest_out: &mut [u8; N]) {
let () = NoneZeroArg::<N>::OK;
assert!(self.offset < BLOCK_SIZE);
trace!(self, "digest::enter");
let padding = (BLOCK_SIZE - self.offset) as u8;
while self.offset < BLOCK_SIZE {
self.state0[self.offset] ^= padding;
self.offset += 1usize;
}
let mut pos = 0usize;
while pos < N {
let copy_len = BLOCK_SIZE.min(N - pos);
self.permute();
digest_out[pos..(pos + copy_len)].copy_from_slice(&self.state0[..copy_len]);
pos += copy_len;
}
trace!(self, "digest::leave");
}
fn permute(&mut self) {
trace!(self, "permfn::enter");
let mut temp0 = [0u8; BLOCK_SIZE];
let mut temp1 = [0u8; BLOCK_SIZE];
let mut temp2 = [0u8; BLOCK_SIZE];
for _ in 0..R {
aes256_encrypt(&mut temp0, &self.state0, &self.state1, &self.state2);
aes256_encrypt(&mut temp1, &self.state1, &self.state2, &self.state0);
aes256_encrypt(&mut temp2, &self.state2, &self.state0, &self.state1);
xor_arrays(&mut self.state0, &temp0);
xor_arrays(&mut self.state1, &temp1);
xor_arrays(&mut self.state2, &temp2);
xor_arrays(&mut self.state1, &Self::BIT_MASK_0);
xor_arrays(&mut self.state2, &Self::BIT_MASK_1);
}
temp0.zeroize();
temp1.zeroize();
temp2.zeroize();
trace!(self, "permfn::leave");
}
}
impl Default for SpongeHash256 {
fn default() -> Self {
Self::new()
}
}
impl<const R: usize> Drop for SpongeHash256<R> {
fn drop(&mut self) {
self.state0.zeroize();
self.state1.zeroize();
self.state2.zeroize();
}
}
pub fn compute<const N: usize>(message: &[u8]) -> [u8; N] {
let mut state = SpongeHash256::default();
state.update(message);
state.digest()
}
pub fn compute_to_slice<const N: usize>(digest_out: &mut [u8; N], message: &[u8]) {
let mut state = SpongeHash256::default();
state.update(message);
state.digest_to_slice(digest_out);
}