use crate::{
PLEN,
xor_block,
};
const TURBO_SHAKE_ROUND_COUNT: usize = 12;
const KECCAK_F_ROUND_COUNT: usize = 24;
const RATE_128: usize = 168;
const RATE_256: usize = 136;
const SHAKE_DS: u8 = 0x1F;
#[inline]
pub fn turbo_shake128_x4(ds: u8, inputs: [&[u8]; 4], outputs: [&mut [u8]; 4]) {
assert!(
(0x01..=0x7F).contains(&ds),
"invalid TurboSHAKE domain separator"
);
keccak_xof_x4::<RATE_128>(ds, TURBO_SHAKE_ROUND_COUNT, inputs, outputs);
}
#[inline]
pub fn turbo_shake256_x4(ds: u8, inputs: [&[u8]; 4], outputs: [&mut [u8]; 4]) {
assert!(
(0x01..=0x7F).contains(&ds),
"invalid TurboSHAKE domain separator"
);
keccak_xof_x4::<RATE_256>(ds, TURBO_SHAKE_ROUND_COUNT, inputs, outputs);
}
#[inline]
pub fn shake128_x4(inputs: [&[u8]; 4], outputs: [&mut [u8]; 4]) {
keccak_xof_x4::<RATE_128>(SHAKE_DS, KECCAK_F_ROUND_COUNT, inputs, outputs);
}
#[inline]
pub fn shake256_x4(inputs: [&[u8]; 4], outputs: [&mut [u8]; 4]) {
keccak_xof_x4::<RATE_256>(SHAKE_DS, KECCAK_F_ROUND_COUNT, inputs, outputs);
}
#[inline]
fn squeeze_into(state: &[u64; PLEN], out: &mut [u8]) {
for (chunk, s) in out.chunks_mut(8).zip(state.iter()) {
chunk.copy_from_slice(&s.to_le_bytes()[..chunk.len()]);
}
}
fn keccak_xof_x4<const RATE: usize>(
ds: u8,
rounds: usize,
inputs: [&[u8]; 4],
mut outputs: [&mut [u8]; 4],
) {
debug_assert!(
(0x01..=0xFF).contains(&ds),
"domain separator byte must be non-zero"
);
let in_len = inputs[0].len();
assert!(
inputs.iter().all(|i| i.len() == in_len),
"turbo_shake_x4 requires all inputs to share one length"
);
let out_len = outputs[0].len();
assert!(
outputs.iter().all(|o| o.len() == out_len),
"turbo_shake_x4 requires all outputs to share one length"
);
let mut states = [[0u64; PLEN]; 4];
let mut off = 0;
while off + RATE <= in_len {
for (state, input) in states.iter_mut().zip(inputs.iter()) {
xor_block(state, &input[off..off + RATE]);
}
lib_q_keccak::p1600x4(&mut states, rounds);
off += RATE;
}
let rem = in_len - off;
for (state, input) in states.iter_mut().zip(inputs.iter()) {
let mut block = [0u8; RATE];
block[..rem].copy_from_slice(&input[off..]);
block[rem] = ds;
block[RATE - 1] |= 0x80;
xor_block(state, &block);
}
lib_q_keccak::p1600x4(&mut states, rounds);
let mut produced = 0;
loop {
let n = core::cmp::min(RATE, out_len - produced);
for (state, out) in states.iter().zip(outputs.iter_mut()) {
squeeze_into(state, &mut out[produced..produced + n]);
}
produced += n;
if produced >= out_len {
break;
}
lib_q_keccak::p1600x4(&mut states, rounds);
}
}