const T: [(usize, usize, u32); 24] = [
(0, 2, 1),
(2, 1, 3),
(1, 2, 6),
(2, 3, 10),
(3, 3, 15),
(3, 0, 21),
(0, 1, 28),
(1, 3, 36),
(3, 1, 45),
(1, 4, 55),
(4, 4, 2),
(4, 0, 14),
(0, 3, 27),
(3, 4, 41),
(4, 3, 56),
(3, 2, 8),
(2, 2, 25),
(2, 0, 43),
(0, 4, 62),
(4, 2, 18),
(2, 4, 39),
(4, 1, 61),
(1, 1, 20),
(1, 0, 44),
];
const R: [u64; 24] = [
0x0000000000000001,
0x0000000000008082,
0x800000000000808a,
0x8000000080008000,
0x000000000000808b,
0x0000000080000001,
0x8000000080008081,
0x8000000000008009,
0x000000000000008a,
0x0000000000000088,
0x0000000080008009,
0x000000008000000a,
0x000000008000808b,
0x800000000000008b,
0x8000000000008089,
0x8000000000008003,
0x8000000000008002,
0x8000000000000080,
0x000000000000800a,
0x800000008000000a,
0x8000000080008081,
0x8000000000008080,
0x0000000080000001,
0x8000000080008008,
];
fn keccak_f1600_on_lanes(mut lanes: [[u64; 5]; 5]) -> [[u64; 5]; 5] {
for r in R {
{
let c = lanes.map(|lane| lane[0] ^ lane[1] ^ lane[2] ^ lane[3] ^ lane[4]);
let d = [(4, 1), (0, 2), (1, 3), (2, 4), (3, 0)]
.map(|(i1, i2)| c[i1] ^ c[i2].rotate_left(1));
for x in 0..5 {
lanes[x].iter_mut().for_each(|a| *a ^= d[x]);
}
}
{
let mut a = lanes[1][0];
for (x, y, r) in T {
(a, lanes[x][y]) = (lanes[x][y], a.rotate_left(r));
}
}
{
for j in 0..5 {
let t = [(1, 2), (2, 3), (3, 4), (4, 0), (0, 1)]
.map(|(i1, i2)| !lanes[i1][j] & lanes[i2][j]);
(0..5).for_each(|i| lanes[i][j] ^= t[i])
}
}
lanes[0][0] ^= r;
}
lanes
}
#[inline(always)]
fn into_lanes(state: [u8; 200]) -> [[u64; 5]; 5] {
let mut lanes = [[0u64; 5]; 5];
let mut i = 0;
for y in 0..5 {
for lane in &mut lanes {
let j = i + 8;
lane[y] = u64::from_le_bytes(state[i..j].try_into().unwrap());
i = j;
}
}
lanes
}
#[inline(always)]
fn from_lanes(lanes: [[u64; 5]; 5]) -> [u8; 200] {
let mut state = [0u8; 200];
let mut i = 0;
for y in 0..5 {
for lane in &lanes {
let j = i + 8;
state[i..j].copy_from_slice(&u64::to_le_bytes(lane[y]));
i = j;
}
}
state
}
#[inline(always)]
fn keccak_f1600(state: [u8; 200]) -> [u8; 200] {
from_lanes(keccak_f1600_on_lanes(into_lanes(state)))
}
pub fn keccak<const OUTPUT_BYTE_LEN: usize>(
rate_in_bytes: usize,
input_bytes: &[u8],
delimited_suffix: u8,
) -> Option<[u8; OUTPUT_BYTE_LEN]> {
if rate_in_bytes == 0 || rate_in_bytes > 200 {
return None;
}
let mut last_block_size = 0;
let mut lanes = [[0u64; 5]; 5];
input_bytes.chunks(rate_in_bytes).for_each(|block| {
(0..5)
.flat_map(move |y| (0..5).map(move |x| (x, y)))
.zip(block.chunks(8).map(|bytes| {
let mut padded_bytes = [0u8; 8];
padded_bytes[0..bytes.len()].copy_from_slice(bytes);
u64::from_le_bytes(padded_bytes)
}))
.for_each(|((x, y), a)| {
lanes[x][y] ^= a;
});
last_block_size = match block.len() {
n if n == rate_in_bytes => {
lanes = keccak_f1600_on_lanes(lanes);
0
}
n => n,
}
});
let mut state = from_lanes(lanes);
state[last_block_size] ^= delimited_suffix;
if (delimited_suffix & 0x80) != 0 && last_block_size == (rate_in_bytes - 1) {
state = keccak_f1600(state);
}
state[rate_in_bytes - 1] ^= 0x80;
let mut output_bytes = [0u8; OUTPUT_BYTE_LEN];
output_bytes.chunks_mut(rate_in_bytes).for_each(|block| {
state = keccak_f1600(state);
block.copy_from_slice(&state[0..block.len()]);
});
Some(output_bytes)
}