#![allow(unsafe_code)]
pub(crate) const LANES: usize = 4;
pub(crate) const MAX_RATE: usize = 168;
pub(crate) fn supported() -> bool {
std::is_x86_feature_detected!("avx2")
}
pub(crate) fn keccak_p_x4(state: &mut [u64; 100], rounds: usize) {
unsafe { avx2::keccak_p_x4(state, rounds) }
}
mod avx2 {
use crate::hash::keccak::RC;
use core::arch::x86_64::*;
#[inline(always)]
unsafe fn rotl<const N: i32, const M: i32>(x: __m256i) -> __m256i {
unsafe { _mm256_or_si256(_mm256_slli_epi64::<N>(x), _mm256_srli_epi64::<M>(x)) }
}
#[inline(always)]
unsafe fn xor(a: __m256i, b: __m256i) -> __m256i {
unsafe { _mm256_xor_si256(a, b) }
}
#[inline(always)]
unsafe fn andnot(a: __m256i, b: __m256i) -> __m256i {
unsafe { _mm256_andnot_si256(a, b) }
}
#[inline(always)]
unsafe fn round(v: &mut [__m256i; 25], rc: __m256i) {
unsafe {
let c0 = xor(xor(xor(xor(v[0], v[5]), v[10]), v[15]), v[20]);
let c1 = xor(xor(xor(xor(v[1], v[6]), v[11]), v[16]), v[21]);
let c2 = xor(xor(xor(xor(v[2], v[7]), v[12]), v[17]), v[22]);
let c3 = xor(xor(xor(xor(v[3], v[8]), v[13]), v[18]), v[23]);
let c4 = xor(xor(xor(xor(v[4], v[9]), v[14]), v[19]), v[24]);
let d0 = xor(c4, rotl::<1, 63>(c1));
let d1 = xor(c0, rotl::<1, 63>(c2));
let d2 = xor(c1, rotl::<1, 63>(c3));
let d3 = xor(c2, rotl::<1, 63>(c4));
let d4 = xor(c3, rotl::<1, 63>(c0));
let b0 = xor(v[0], d0);
let b1 = rotl::<44, 20>(xor(v[6], d1));
let b2 = rotl::<43, 21>(xor(v[12], d2));
let b3 = rotl::<21, 43>(xor(v[18], d3));
let b4 = rotl::<14, 50>(xor(v[24], d4));
let b5 = rotl::<28, 36>(xor(v[3], d3));
let b6 = rotl::<20, 44>(xor(v[9], d4));
let b7 = rotl::<3, 61>(xor(v[10], d0));
let b8 = rotl::<45, 19>(xor(v[16], d1));
let b9 = rotl::<61, 3>(xor(v[22], d2));
let b10 = rotl::<1, 63>(xor(v[1], d1));
let b11 = rotl::<6, 58>(xor(v[7], d2));
let b12 = rotl::<25, 39>(xor(v[13], d3));
let b13 = rotl::<8, 56>(xor(v[19], d4));
let b14 = rotl::<18, 46>(xor(v[20], d0));
let b15 = rotl::<27, 37>(xor(v[4], d4));
let b16 = rotl::<36, 28>(xor(v[5], d0));
let b17 = rotl::<10, 54>(xor(v[11], d1));
let b18 = rotl::<15, 49>(xor(v[17], d2));
let b19 = rotl::<56, 8>(xor(v[23], d3));
let b20 = rotl::<62, 2>(xor(v[2], d2));
let b21 = rotl::<55, 9>(xor(v[8], d3));
let b22 = rotl::<39, 25>(xor(v[14], d4));
let b23 = rotl::<41, 23>(xor(v[15], d0));
let b24 = rotl::<2, 62>(xor(v[21], d1));
v[0] = xor(xor(b0, andnot(b1, b2)), rc);
v[1] = xor(b1, andnot(b2, b3));
v[2] = xor(b2, andnot(b3, b4));
v[3] = xor(b3, andnot(b4, b0));
v[4] = xor(b4, andnot(b0, b1));
v[5] = xor(b5, andnot(b6, b7));
v[6] = xor(b6, andnot(b7, b8));
v[7] = xor(b7, andnot(b8, b9));
v[8] = xor(b8, andnot(b9, b5));
v[9] = xor(b9, andnot(b5, b6));
v[10] = xor(b10, andnot(b11, b12));
v[11] = xor(b11, andnot(b12, b13));
v[12] = xor(b12, andnot(b13, b14));
v[13] = xor(b13, andnot(b14, b10));
v[14] = xor(b14, andnot(b10, b11));
v[15] = xor(b15, andnot(b16, b17));
v[16] = xor(b16, andnot(b17, b18));
v[17] = xor(b17, andnot(b18, b19));
v[18] = xor(b18, andnot(b19, b15));
v[19] = xor(b19, andnot(b15, b16));
v[20] = xor(b20, andnot(b21, b22));
v[21] = xor(b21, andnot(b22, b23));
v[22] = xor(b22, andnot(b23, b24));
v[23] = xor(b23, andnot(b24, b20));
v[24] = xor(b24, andnot(b20, b21));
}
}
#[target_feature(enable = "avx2")]
pub(super) unsafe fn keccak_p_x4(state: &mut [u64; 100], rounds: usize) {
unsafe {
let p = state.as_mut_ptr();
let mut v = [_mm256_setzero_si256(); 25];
for (i, vi) in v.iter_mut().enumerate() {
*vi = _mm256_loadu_si256(p.add(4 * i) as *const __m256i);
}
for &rc in RC[24 - rounds..].iter() {
round(&mut v, _mm256_set1_epi64x(rc as i64));
}
for (i, vi) in v.iter().enumerate() {
_mm256_storeu_si256(p.add(4 * i) as *mut __m256i, *vi);
}
}
}
}
pub(crate) struct KeccakX4 {
state: [u64; 100],
rate: usize,
fresh: bool,
}
impl KeccakX4 {
pub(crate) fn new(rate: usize, msgs: [&[u8]; LANES], pad: u8) -> Self {
debug_assert!(rate <= MAX_RATE && rate.is_multiple_of(8));
let mut state = [0u64; 100];
let mut block = [0u8; MAX_RATE];
for (l, msg) in msgs.iter().enumerate() {
debug_assert!(msg.len() < rate);
block[..rate].fill(0);
block[..msg.len()].copy_from_slice(msg);
block[msg.len()] ^= pad;
block[rate - 1] ^= 0x80;
for (i, chunk) in block[..rate].chunks_exact(8).enumerate() {
state[4 * i + l] = u64::from_le_bytes(chunk.try_into().unwrap());
}
}
keccak_p_x4(&mut state, 24);
KeccakX4 {
state,
rate,
fresh: true,
}
}
pub(crate) fn squeeze_blocks(&mut self, out: &mut [[u8; MAX_RATE]; LANES]) {
if self.fresh {
self.fresh = false;
} else {
keccak_p_x4(&mut self.state, 24);
}
for (l, lane_out) in out.iter_mut().enumerate() {
for (i, chunk) in lane_out[..self.rate].chunks_exact_mut(8).enumerate() {
chunk.copy_from_slice(&self.state[4 * i + l].to_le_bytes());
}
}
}
pub(crate) fn zeroize(&mut self) {
super::zeroize::zero_words(&mut self.state);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::hash::keccak::keccak_p;
fn xorshift(s: &mut u64) -> u64 {
*s ^= *s << 13;
*s ^= *s >> 7;
*s ^= *s << 17;
*s
}
#[test]
fn keccak_p_x4_matches_scalar() {
if !supported() {
return;
}
let mut seed = 0x1234_5678_9abc_def0u64;
for trial in 0..50 {
let rounds = if trial % 2 == 0 { 24 } else { 12 };
let mut scalar = [[0u64; 25]; LANES];
let mut interleaved = [0u64; 100];
for (l, st) in scalar.iter_mut().enumerate() {
for (i, w) in st.iter_mut().enumerate() {
*w = xorshift(&mut seed);
interleaved[4 * i + l] = *w;
}
}
keccak_p_x4(&mut interleaved, rounds);
for (l, st) in scalar.iter_mut().enumerate() {
keccak_p(st, rounds);
for (i, w) in st.iter().enumerate() {
assert_eq!(
interleaved[4 * i + l],
*w,
"trial {trial} rounds {rounds}: lane {i} stream {l}"
);
}
}
}
}
#[test]
fn keccak_x4_sponge_matches_scalar_shake() {
if !supported() {
return;
}
for &(rate, pad) in &[(168usize, 0x1Fu8), (136, 0x1F)] {
let msgs_buf: [[u8; 34]; LANES] =
core::array::from_fn(|l| core::array::from_fn(|i| (l * 37 + i) as u8));
let msgs: [&[u8]; LANES] = core::array::from_fn(|l| &msgs_buf[l][..]);
let mut x4 = KeccakX4::new(rate, msgs, pad);
let mut blocks = [[0u8; MAX_RATE]; LANES];
let mut scalars: [crate::hash::keccak::Keccak; LANES] = core::array::from_fn(|l| {
let mut k = crate::hash::keccak::Keccak::new(rate);
k.update(&msgs_buf[l]);
k.finalize(pad);
k
});
for block_idx in 0..5 {
x4.squeeze_blocks(&mut blocks);
for (l, k) in scalars.iter_mut().enumerate() {
let mut expect = [0u8; MAX_RATE];
k.squeeze(&mut expect[..rate]);
assert_eq!(
&blocks[l][..rate],
&expect[..rate],
"rate {rate} stream {l} block {block_idx}"
);
}
}
}
}
}