use crate::RATE;
use crate::gimli::{State, gimli};
pub const HASH_SIZE: usize = 32;
const DOMAIN_XOF: u8 = 0x1f;
const PADDING_MARKER: u8 = 0x80;
pub fn hash(input: &[u8]) -> [u8; HASH_SIZE] {
let mut state = State::new();
let mut iter = input.chunks_exact(RATE);
for chunk in &mut iter {
let state_bytes = state.as_bytes_mut();
for i in 0..RATE {
state_bytes[i] ^= chunk[i];
}
gimli(&mut state);
}
let remainder = iter.remainder();
let state_bytes = state.as_bytes_mut();
for i in 0..remainder.len() {
state_bytes[i] ^= remainder[i];
}
state_bytes[remainder.len()] ^= DOMAIN_XOF;
state_bytes[RATE - 1] ^= PADDING_MARKER;
gimli(&mut state);
let mut output = [0u8; HASH_SIZE];
output[..RATE].copy_from_slice(&state.as_bytes()[..RATE]);
gimli(&mut state);
output[RATE..].copy_from_slice(&state.as_bytes()[..RATE]);
output
}
#[derive(Clone)]
pub struct Hasher {
state: State,
buffer: [u8; RATE],
buffer_len: usize,
}
impl Hasher {
pub const fn new() -> Self {
Self {
state: State::new(),
buffer: [0u8; RATE],
buffer_len: 0,
}
}
pub fn update(&mut self, data: &[u8]) {
let mut pos = 0;
while pos < data.len() {
let available = (data.len() - pos).min(RATE - self.buffer_len);
self.buffer[self.buffer_len..self.buffer_len + available]
.copy_from_slice(&data[pos..pos + available]);
self.buffer_len += available;
pos += available;
if self.buffer_len == RATE {
let state_bytes = self.state.as_bytes_mut();
for (i, byte) in self.buffer.iter().enumerate().take(RATE) {
state_bytes[i] ^= byte;
}
gimli(&mut self.state);
self.buffer_len = 0;
}
}
}
pub fn finalize(mut self) -> [u8; HASH_SIZE] {
let state_bytes = self.state.as_bytes_mut();
for (i, byte) in self.buffer.iter().enumerate().take(self.buffer_len) {
state_bytes[i] ^= byte;
}
state_bytes[self.buffer_len] ^= DOMAIN_XOF;
state_bytes[RATE - 1] ^= PADDING_MARKER;
gimli(&mut self.state);
let mut output = [0u8; HASH_SIZE];
output[..RATE].copy_from_slice(&self.state.as_bytes()[..RATE]);
gimli(&mut self.state);
output[RATE..].copy_from_slice(&self.state.as_bytes()[..RATE]);
output
}
}
impl Default for Hasher {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests;