#![cfg(target_endian="little")]
#![doc(cfg(target_endian="little"))]
use {
core::mem,
alloc::{
string::String,
vec::Vec,
},
crate::{Bytes, Result},
self::{
bits::Bits,
state::State,
},
};
#[cfg(feature="std")]
use {
std::io::Write,
crate::IoResult,
};
pub use self::{
hash::Hash,
keccak_x::{KeccakX, XType},
r#type::Type,
};
#[cfg(feature="std")]
pub use self::{
hash_reader::*,
hash_writer::*,
};
macro_rules! u8_bits { () => { 8 }}
mod bits;
mod hash;
mod keccak_x;
mod state;
mod tests;
mod r#type;
#[cfg(feature="std")]
mod hash_reader;
#[cfg(feature="std")]
mod hash_writer;
type OneLane = u64;
const SIZE_OF_ONE_LANE: usize = mem::size_of::<OneLane>();
type Lanes = [OneLane; NUMBER_OF_LANES];
const NUMBER_OF_LANES: usize = 25;
const LANES_OF_ZEROS: Lanes = [OneLane::MIN; NUMBER_OF_LANES];
type InnerType = char;
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Keccak<const TYPE: InnerType> {
hash: Hash,
bits: Bits,
buf: Vec<u8>,
buf_chunk: usize,
lanes: Lanes,
output_bytes: Option<usize>,
}
impl<const TYPE: InnerType> Keccak<TYPE> {
fn with(hash: Hash) -> Keccak<TYPE> {
let bits = Bits::B1600;
let buf_chunk = hash.rate() / u8_bits!();
let buf = Vec::with_capacity(buf_chunk);
let output_bytes = Some(hash.output_bytes());
Self {
hash,
bits,
buf,
buf_chunk,
lanes: LANES_OF_ZEROS,
output_bytes,
}
}
pub (crate) fn new(hash: Hash) -> Keccak<{Type::Function.id()}> {
Keccak::with(hash)
}
pub (crate) fn make_with_output_bytes(hash: Hash, output_bytes: usize) -> Result<Keccak<{Type::LimitedXOF.id()}>> {
if hash.is_extendable_output_function() {
if output_bytes >= Hash::MIN_CUSTOM_OUTPUT_BYTES {
let mut result = Keccak::with(hash);
result.output_bytes = Some(output_bytes);
Ok(result)
} else {
Err(err!("Custom output bytes must be equal to or larger than {min}", min=Hash::MIN_CUSTOM_OUTPUT_BYTES))
}
} else {
Err(err!("{hash} does not support custom output bits", hash=hash))
}
}
pub (crate) fn make_with_unlimited_output_bytes(hash: Hash) -> Result<Keccak<{Type::UnlimitedXOF.id()}>> {
if hash.is_extendable_output_function() {
let mut result = Keccak::with(hash);
result.output_bytes = None;
Ok(result)
} else {
Err(err!("{hash} does not support custom output bits", hash=hash))
}
}
pub const fn hash(&self) -> &Hash {
&self.hash
}
pub fn update<B>(&mut self, bytes: B) -> usize where B: AsRef<[u8]> {
macro_rules! proc { ($buf: expr) => {{
let buf = $buf.unwrap_or(&self.buf);
for (idx, c) in {
let chunks = buf.chunks_exact(SIZE_OF_ONE_LANE);
#[cfg(test)]
assert!(chunks.remainder().is_empty());
chunks.enumerate()
} {
self.lanes[idx] ^= u64::from_le_bytes([c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7]]);
}
keccak_p_1600_24(&mut self.lanes);
}}}
let mut bytes = bytes.as_ref();
let result = bytes.len();
if crate::io::fill_buffer(&mut self.buf, self.buf_chunk, &mut bytes) {
proc!(None);
self.buf.clear();
}
let chunks = bytes.chunks_exact(self.buf_chunk);
self.buf.extend(chunks.remainder());
chunks.for_each(|c| proc!(Some(c)));
result
}
pub fn update_bytes<'a, const N: usize, B, B0>(&mut self, bytes: B) -> Option<usize> where B: Into<Bytes<'a, N, B0>>, B0: AsRef<[u8]> + 'a {
let mut result = Some(usize::MIN);
for bytes in bytes.into().as_slice() {
let size = self.update(bytes);
if let Some(current) = result.as_mut() {
match current.checked_add(size) {
Some(new) => *current = new,
None => result = None,
};
}
}
result
}
fn finish_as_keccak_x<const T: keccak_x::InnerXType>(mut self) -> KeccakX<T> {
const ZEROS: &[u8] = &[u8::MIN; 128];
let suffix = self.hash.suffix();
let suffix_len = (u8_bits!() - suffix.leading_zeros()) as u8;
let bits = {
let j = (|| {
let j = isize::try_from(self.buf.len()).ok()?.checked_mul(u8_bits!())?.checked_add(suffix_len.into())?
.checked_neg()?.checked_sub(2)?.rem_euclid(isize::try_from(self.hash.rate()).ok()?);
usize::try_from(j).ok()
})().unwrap();
match j.checked_add(2).map(|n| n.checked_add(suffix_len.into())) {
Some(Some(bits)) => bits,
_ => panic!("{}", e!()),
}
};
let bytes = if bits % u8_bits!() == usize::MIN { bits / u8_bits!() } else { panic!("{}", err!("Invalid bits: {bits}")); };
self.buf.reserve(bytes);
self.buf.push(suffix | (1 << u32::from(suffix_len)));
if let Some(mut bytes) = bytes.checked_sub(1) {
while bytes > usize::MIN {
let count = bytes.min(ZEROS.len());
self.buf.extend(&ZEROS[..count]);
bytes -= count;
}
}
*self.buf.last_mut().unwrap() |= 0b_1000_0000;
self.update(&[]);
KeccakX::new(self.hash, self.lanes, self.buf_chunk, self.output_bytes)
}
}
impl Keccak<{Type::Function.id()}> {
pub fn finish(self) -> Vec<u8> {
self.finish_as_keccak_x::<{XType::Limited.id()}>().finish()
}
pub fn finish_as_hex(self) -> String {
crate::bytes_to_hex(self.finish())
}
}
impl Keccak<{Type::LimitedXOF.id()}> {
pub fn finish(self) -> KeccakX<{XType::Limited.id()}> {
self.finish_as_keccak_x()
}
}
impl Keccak<{Type::UnlimitedXOF.id()}> {
pub fn finish(self) -> KeccakX<{XType::Unlimited.id()}> {
self.finish_as_keccak_x()
}
}
impl From<Hash> for Keccak<{Type::Function.id()}> {
fn from(hash: Hash) -> Self {
Self::new(hash)
}
}
impl From<&Hash> for Keccak<{Type::Function.id()}> {
fn from(hash: &Hash) -> Self {
Self::new(hash.clone())
}
}
#[cfg(feature="std")]
#[doc(cfg(feature="std"))]
impl<const TYPE: InnerType> Write for Keccak<TYPE> {
#[inline(always)]
fn write(&mut self, buf: &[u8]) -> IoResult<usize> {
Ok(self.update(buf))
}
#[inline(always)]
fn flush(&mut self) -> IoResult<()> {
Ok(())
}
}
#[inline(always)]
fn keccak_p_1600_24(lanes: &mut Lanes) {
keccak_p(&Bits::B1600, 24, lanes);
}
#[inline(always)]
fn keccak_p(bits: &Bits, rounds: usize, lanes: &mut Lanes) {
let mut state = State::new(lanes);
let end = 12 + 2 * bits.l();
for round_index in end - rounds .. end {
state.theta();
state.rho_and_pi();
state.chi();
state.iota(round_index);
}
}
#[allow(unused)]
fn keccak_f(bits: &Bits, lanes: &mut Lanes) {
let rounds = 12 + 2 * bits.l();
keccak_p(bits, rounds, lanes);
}