#[cfg(feature = "zeroize")]
use zeroize::{Zeroize, ZeroizeOnDrop};
pub trait Unit: Clone + Sized {
const ZERO: Self;
}
macro_rules! impl_integer_unit {
($t:ty) => {
impl Unit for $t {
const ZERO: Self = 0;
}
};
}
impl_integer_unit!(u8);
impl_integer_unit!(u32);
impl_integer_unit!(u64);
impl_integer_unit!(u128);
impl_integer_unit!(usize);
pub trait DuplexSpongeInterface: Clone {
type U: Unit;
fn absorb(&mut self, input: &[Self::U]) -> &mut Self;
fn squeeze(&mut self, output: &mut [Self::U]) -> &mut Self;
fn ratchet(&mut self) -> &mut Self;
fn squeeze_array<const LEN: usize>(&mut self) -> [Self::U; LEN] {
let mut output = [Self::U::ZERO; LEN];
self.squeeze(&mut output);
output
}
fn squeeze_boxed(&mut self, len: usize) -> alloc::boxed::Box<[Self::U]> {
let mut output = alloc::vec![Self::U::ZERO; len];
self.squeeze(&mut output);
output.into_boxed_slice()
}
}
pub trait Permutation<const WIDTH: usize>: Clone {
type U: Unit;
fn permute(&self, state: &[Self::U; WIDTH]) -> [Self::U; WIDTH];
fn permute_mut(&self, state: &mut [Self::U; WIDTH]) {
let new_state = self.permute(state);
state.clone_from(&new_state);
}
}
#[derive(Clone, PartialEq, Eq)]
pub struct DuplexSponge<P, const WIDTH: usize, const RATE: usize>
where
P: Permutation<WIDTH>,
{
permutation: P,
permutation_state: [P::U; WIDTH],
absorb_pos: usize,
squeeze_pos: usize,
}
impl<P, const WIDTH: usize, const RATE: usize> DuplexSponge<P, WIDTH, RATE>
where
P: Permutation<WIDTH>,
{
fn with_permutation(permutation: P) -> Self {
assert!(WIDTH > RATE, "capacity segment must be non-empty");
assert!(RATE > 0, "rate segment must be non-empty");
Self {
permutation,
permutation_state: [P::U::ZERO; WIDTH],
absorb_pos: 0,
squeeze_pos: RATE,
}
}
}
impl<P, const WIDTH: usize, const RATE: usize> Default for DuplexSponge<P, WIDTH, RATE>
where
P: Permutation<WIDTH> + Default,
{
fn default() -> Self {
P::default().into()
}
}
impl<P, const WIDTH: usize, const RATE: usize> From<P> for DuplexSponge<P, WIDTH, RATE>
where
P: Permutation<WIDTH>,
{
fn from(value: P) -> Self {
Self::with_permutation(value)
}
}
#[cfg(feature = "zeroize")]
impl<P, const WIDTH: usize, const RATE: usize> Zeroize for DuplexSponge<P, WIDTH, RATE>
where
P: Permutation<WIDTH> + Clone,
{
fn zeroize(&mut self) {
self.absorb_pos.zeroize();
self.permutation_state.as_mut().fill(P::U::ZERO);
self.squeeze_pos.zeroize();
}
}
#[cfg(feature = "zeroize")]
impl<P, const WIDTH: usize, const RATE: usize> ZeroizeOnDrop for DuplexSponge<P, WIDTH, RATE> where
P: Permutation<WIDTH>
{
}
impl<P, const WIDTH: usize, const RATE: usize> DuplexSpongeInterface
for DuplexSponge<P, WIDTH, RATE>
where
P: Permutation<WIDTH>,
{
type U = P::U;
fn absorb(&mut self, mut input: &[Self::U]) -> &mut Self {
self.squeeze_pos = RATE;
while !input.is_empty() {
if self.absorb_pos == RATE {
self.permutation.permute_mut(&mut self.permutation_state);
self.absorb_pos = 0;
} else {
debug_assert!(self.absorb_pos < RATE);
let chunk_len = usize::min(input.len(), RATE - self.absorb_pos);
let (chunk, rest) = input.split_at(chunk_len);
self.permutation_state[self.absorb_pos..self.absorb_pos + chunk_len]
.clone_from_slice(chunk);
self.absorb_pos += chunk_len;
input = rest;
}
}
self
}
fn squeeze(&mut self, output: &mut [Self::U]) -> &mut Self {
if output.is_empty() {
return self;
}
self.absorb_pos = 0;
if self.squeeze_pos == RATE {
self.squeeze_pos = 0;
self.permutation.permute_mut(&mut self.permutation_state);
}
debug_assert!(self.squeeze_pos < RATE);
let chunk_len = usize::min(output.len(), RATE - self.squeeze_pos);
let (output, rest) = output.split_at_mut(chunk_len);
output.clone_from_slice(
&self.permutation_state[self.squeeze_pos..self.squeeze_pos + chunk_len],
);
self.squeeze_pos += chunk_len;
self.squeeze(rest)
}
fn ratchet(&mut self) -> &mut Self {
self.absorb_pos = RATE;
self.squeeze_pos = RATE;
self.permutation_state[0..RATE].fill_with(|| P::U::ZERO);
self.permutation.permute_mut(&mut self.permutation_state);
self
}
}