use super::XofReader;
pub(super) const RC: [u64; 24] = [
0x0000_0000_0000_0001,
0x0000_0000_0000_8082,
0x8000_0000_0000_808a,
0x8000_0000_8000_8000,
0x0000_0000_0000_808b,
0x0000_0000_8000_0001,
0x8000_0000_8000_8081,
0x8000_0000_0000_8009,
0x0000_0000_0000_008a,
0x0000_0000_0000_0088,
0x0000_0000_8000_8009,
0x0000_0000_8000_000a,
0x0000_0000_8000_808b,
0x8000_0000_0000_008b,
0x8000_0000_0000_8089,
0x8000_0000_0000_8003,
0x8000_0000_0000_8002,
0x8000_0000_0000_0080,
0x0000_0000_0000_800a,
0x8000_0000_8000_000a,
0x8000_0000_8000_8081,
0x8000_0000_0000_8080,
0x0000_0000_8000_0001,
0x8000_0000_8000_8008,
];
const COMPLEMENTED: [usize; 6] = [1, 2, 8, 12, 17, 20];
#[inline(always)]
fn round(a: &mut [u64; 25], rc: u64) {
let c0 = a[0] ^ a[5] ^ a[10] ^ a[15] ^ a[20];
let c1 = a[1] ^ a[6] ^ a[11] ^ a[16] ^ a[21];
let c2 = a[2] ^ a[7] ^ a[12] ^ a[17] ^ a[22];
let c3 = a[3] ^ a[8] ^ a[13] ^ a[18] ^ a[23];
let c4 = a[4] ^ a[9] ^ a[14] ^ a[19] ^ a[24];
let d0 = c4 ^ c1.rotate_left(1);
let d1 = c0 ^ c2.rotate_left(1);
let d2 = c1 ^ c3.rotate_left(1);
let d3 = c2 ^ c4.rotate_left(1);
let d4 = c3 ^ c0.rotate_left(1);
let b0 = a[0] ^ d0;
let b1 = (a[6] ^ d1).rotate_left(44);
let b2 = (a[12] ^ d2).rotate_left(43);
let b3 = (a[18] ^ d3).rotate_left(21);
let b4 = (a[24] ^ d4).rotate_left(14);
let b5 = (a[3] ^ d3).rotate_left(28);
let b6 = (a[9] ^ d4).rotate_left(20);
let b7 = (a[10] ^ d0).rotate_left(3);
let b8 = (a[16] ^ d1).rotate_left(45);
let b9 = (a[22] ^ d2).rotate_left(61);
let b10 = (a[1] ^ d1).rotate_left(1);
let b11 = (a[7] ^ d2).rotate_left(6);
let b12 = (a[13] ^ d3).rotate_left(25);
let b13 = (a[19] ^ d4).rotate_left(8);
let b14 = (a[20] ^ d0).rotate_left(18);
let b15 = (a[4] ^ d4).rotate_left(27);
let b16 = (a[5] ^ d0).rotate_left(36);
let b17 = (a[11] ^ d1).rotate_left(10);
let b18 = (a[17] ^ d2).rotate_left(15);
let b19 = (a[23] ^ d3).rotate_left(56);
let b20 = (a[2] ^ d2).rotate_left(62);
let b21 = (a[8] ^ d3).rotate_left(55);
let b22 = (a[14] ^ d4).rotate_left(39);
let b23 = (a[15] ^ d0).rotate_left(41);
let b24 = (a[21] ^ d1).rotate_left(2);
a[0] = (b0 ^ (b1 | b2)) ^ rc;
a[1] = b1 ^ (!b2 | b3);
a[2] = b2 ^ (b3 & b4);
a[3] = b3 ^ (b4 | b0);
a[4] = b4 ^ (b0 & b1);
a[5] = b5 ^ (b6 | b7);
a[6] = b6 ^ (b7 & b8);
a[7] = b7 ^ (b8 | !b9);
a[8] = b8 ^ (b9 | b5);
a[9] = b9 ^ (b5 & b6);
a[10] = b10 ^ (b11 | b12);
a[11] = b11 ^ (b12 & b13);
a[12] = b12 ^ (!b13 & b14);
a[13] = b13 ^ !(b14 | b10);
a[14] = b14 ^ (b10 & b11);
a[15] = b15 ^ (b16 & b17);
a[16] = b16 ^ (b17 | b18);
a[17] = b17 ^ (!b18 | b19);
a[18] = b18 ^ !(b19 & b15);
a[19] = b19 ^ (b15 | b16);
a[20] = b20 ^ (!b21 & b22);
a[21] = b21 ^ !(b22 | b23);
a[22] = b22 ^ (b23 & b24);
a[23] = b23 ^ (b24 | b20);
a[24] = b24 ^ (b20 & b21);
}
pub(super) fn keccak_p(a: &mut [u64; 25], rounds: usize) {
for &i in COMPLEMENTED.iter() {
a[i] = !a[i];
}
for &rc in RC[24 - rounds..].iter() {
round(a, rc);
}
for &i in COMPLEMENTED.iter() {
a[i] = !a[i];
}
}
const MAX_RATE: usize = 168;
#[derive(Clone)]
pub(super) struct Keccak {
state: [u64; 25],
buf: [u8; MAX_RATE],
buf_len: usize,
rate: usize,
rounds: usize,
squeeze_offset: usize,
}
impl Default for Keccak {
fn default() -> Self {
Keccak {
state: [0u64; 25],
buf: [0u8; MAX_RATE],
buf_len: 0,
rate: 0,
rounds: 0,
squeeze_offset: 0,
}
}
}
impl Keccak {
pub(super) fn new(rate: usize) -> Self {
Self::with_rounds(rate, 24)
}
pub(super) fn with_rounds(rate: usize, rounds: usize) -> Self {
Keccak {
state: [0u64; 25],
buf: [0u8; MAX_RATE],
buf_len: 0,
rate,
rounds,
squeeze_offset: 0,
}
}
fn absorb_buf(&mut self) {
for (i, chunk) in self.buf[..self.rate].chunks_exact(8).enumerate() {
self.state[i] ^= u64::from_le_bytes(chunk.try_into().unwrap());
}
keccak_p(&mut self.state, self.rounds);
}
pub(super) fn update(&mut self, mut data: &[u8]) {
if self.buf_len > 0 {
let take = (self.rate - self.buf_len).min(data.len());
self.buf[self.buf_len..self.buf_len + take].copy_from_slice(&data[..take]);
self.buf_len += take;
data = &data[take..];
if self.buf_len == self.rate {
self.absorb_buf();
self.buf_len = 0;
}
}
while data.len() >= self.rate {
self.buf[..self.rate].copy_from_slice(&data[..self.rate]);
self.absorb_buf();
data = &data[self.rate..];
}
if !data.is_empty() {
self.buf[..data.len()].copy_from_slice(data);
self.buf_len = data.len();
}
}
pub(super) fn finalize(&mut self, pad: u8) {
let len = self.buf_len;
for b in self.buf[len..self.rate].iter_mut() {
*b = 0;
}
self.buf[len] ^= pad;
self.buf[self.rate - 1] ^= 0x80;
self.absorb_buf();
self.buf_len = 0;
self.squeeze_offset = 0;
}
pub(super) fn zeroize(&mut self) {
super::zeroize::zero_words(&mut self.state);
super::zeroize::zero_bytes(&mut self.buf);
self.buf_len = 0;
self.squeeze_offset = 0;
}
pub(super) fn squeeze(&mut self, out: &mut [u8]) {
for b in out.iter_mut() {
if self.squeeze_offset == self.rate {
keccak_p(&mut self.state, self.rounds);
self.squeeze_offset = 0;
}
let p = self.squeeze_offset;
*b = (self.state[p / 8] >> (8 * (p % 8))) as u8;
self.squeeze_offset += 1;
}
}
}
#[derive(Clone)]
pub struct KeccakReader {
keccak: Keccak,
}
impl KeccakReader {
pub(super) fn new(mut keccak: Keccak, pad: u8) -> Self {
keccak.finalize(pad);
KeccakReader { keccak }
}
}
impl XofReader for KeccakReader {
fn read(&mut self, out: &mut [u8]) {
self.keccak.squeeze(out);
}
}
impl Drop for KeccakReader {
fn drop(&mut self) {
self.keccak.zeroize();
}
}