#![allow(clippy::needless_range_loop, dead_code)]
use crate::symmetric::KeccakState;
pub(crate) const SHAKE128_RATE: usize = 168;
pub(crate) const SHAKE256_RATE: usize = 136;
pub(crate) const SHA3_256_RATE: usize = 136;
pub(crate) const SHA3_512_RATE: usize = 72;
pub(crate) const NROUNDS: usize = 24;
fn rol(a: u64, offset: u64) -> u64 {
(a << offset) ^ (a >> (64 - offset))
}
pub(crate) fn load64(x: &[u8]) -> u64 {
let mut r = 0u64;
for i in 0..8 {
r |= (x[i] as u64) << (8 * i);
}
r
}
pub(crate) fn store64(x: &mut [u8], mut u: u64) {
for i in x.iter_mut().take(8) {
*i = u as u8;
u >>= 8;
}
}
const KECCAKF_ROUNDCONSTANTS: [u64; NROUNDS] = [
0x0000000000000001,
0x0000000000008082,
0x800000000000808a,
0x8000000080008000,
0x000000000000808b,
0x0000000080000001,
0x8000000080008081,
0x8000000000008009,
0x000000000000008a,
0x0000000000000088,
0x0000000080008009,
0x000000008000000a,
0x000000008000808b,
0x800000000000008b,
0x8000000000008089,
0x8000000000008003,
0x8000000000008002,
0x8000000000000080,
0x000000000000800a,
0x800000008000000a,
0x8000000080008081,
0x8000000000008080,
0x0000000080000001,
0x8000000080008008,
];
pub(crate) fn keccakf1600_statepermute(state: &mut [u64]) {
let mut aba = state[0];
let mut abe = state[1];
let mut abi = state[2];
let mut abo = state[3];
let mut abu = state[4];
let mut aga = state[5];
let mut age = state[6];
let mut agi = state[7];
let mut ago = state[8];
let mut agu = state[9];
let mut aka = state[10];
let mut ake = state[11];
let mut aki = state[12];
let mut ako = state[13];
let mut aku = state[14];
let mut ama = state[15];
let mut ame = state[16];
let mut ami = state[17];
let mut amo = state[18];
let mut amu = state[19];
let mut asa = state[20];
let mut ase = state[21];
let mut asi = state[22];
let mut aso = state[23];
let mut asu = state[24];
for round in (0..NROUNDS).step_by(2) {
let mut bca = aba ^ aga ^ aka ^ ama ^ asa;
let mut bce = abe ^ age ^ ake ^ ame ^ ase;
let mut bci = abi ^ agi ^ aki ^ ami ^ asi;
let mut bco = abo ^ ago ^ ako ^ amo ^ aso;
let mut bcu = abu ^ agu ^ aku ^ amu ^ asu;
let mut da = bcu ^ rol(bce, 1);
let mut de = bca ^ rol(bci, 1);
let mut di = bce ^ rol(bco, 1);
let mut d_o = bci ^ rol(bcu, 1);
let mut du = bco ^ rol(bca, 1);
aba ^= da;
bca = aba;
age ^= de;
bce = rol(age, 44);
aki ^= di;
bci = rol(aki, 43);
amo ^= d_o;
bco = rol(amo, 21);
asu ^= du;
bcu = rol(asu, 14);
let mut eba = bca ^ ((!bce) & bci);
eba ^= KECCAKF_ROUNDCONSTANTS[round];
let mut ebe = bce ^ ((!bci) & bco);
let mut ebi = bci ^ ((!bco) & bcu);
let mut ebo = bco ^ ((!bcu) & bca);
let mut ebu = bcu ^ ((!bca) & bce);
abo ^= d_o;
bca = rol(abo, 28);
agu ^= du;
bce = rol(agu, 20);
aka ^= da;
bci = rol(aka, 3);
ame ^= de;
bco = rol(ame, 45);
asi ^= di;
bcu = rol(asi, 61);
let mut ega = bca ^ ((!bce) & bci);
let mut ege = bce ^ ((!bci) & bco);
let mut egi = bci ^ ((!bco) & bcu);
let mut ego = bco ^ ((!bcu) & bca);
let mut egu = bcu ^ ((!bca) & bce);
abe ^= de;
bca = rol(abe, 1);
agi ^= di;
bce = rol(agi, 6);
ako ^= d_o;
bci = rol(ako, 25);
amu ^= du;
bco = rol(amu, 8);
asa ^= da;
bcu = rol(asa, 18);
let mut eka = bca ^ ((!bce) & bci);
let mut eke = bce ^ ((!bci) & bco);
let mut eki = bci ^ ((!bco) & bcu);
let mut eko = bco ^ ((!bcu) & bca);
let mut eku = bcu ^ ((!bca) & bce);
abu ^= du;
bca = rol(abu, 27);
aga ^= da;
bce = rol(aga, 36);
ake ^= de;
bci = rol(ake, 10);
ami ^= di;
bco = rol(ami, 15);
aso ^= d_o;
bcu = rol(aso, 56);
let mut ema = bca ^ ((!bce) & bci);
let mut eme = bce ^ ((!bci) & bco);
let mut emi = bci ^ ((!bco) & bcu);
let mut emo = bco ^ ((!bcu) & bca);
let mut emu = bcu ^ ((!bca) & bce);
abi ^= di;
bca = rol(abi, 62);
ago ^= d_o;
bce = rol(ago, 55);
aku ^= du;
bci = rol(aku, 39);
ama ^= da;
bco = rol(ama, 41);
ase ^= de;
bcu = rol(ase, 2);
let mut esa = bca ^ ((!bce) & bci);
let mut ese = bce ^ ((!bci) & bco);
let mut esi = bci ^ ((!bco) & bcu);
let mut eso = bco ^ ((!bcu) & bca);
let mut esu = bcu ^ ((!bca) & bce);
bca = eba ^ ega ^ eka ^ ema ^ esa;
bce = ebe ^ ege ^ eke ^ eme ^ ese;
bci = ebi ^ egi ^ eki ^ emi ^ esi;
bco = ebo ^ ego ^ eko ^ emo ^ eso;
bcu = ebu ^ egu ^ eku ^ emu ^ esu;
da = bcu ^ rol(bce, 1);
de = bca ^ rol(bci, 1);
di = bce ^ rol(bco, 1);
d_o = bci ^ rol(bcu, 1);
du = bco ^ rol(bca, 1);
eba ^= da;
bca = eba;
ege ^= de;
bce = rol(ege, 44);
eki ^= di;
bci = rol(eki, 43);
emo ^= d_o;
bco = rol(emo, 21);
esu ^= du;
bcu = rol(esu, 14);
aba = bca ^ ((!bce) & bci);
aba ^= KECCAKF_ROUNDCONSTANTS[round + 1];
abe = bce ^ ((!bci) & bco);
abi = bci ^ ((!bco) & bcu);
abo = bco ^ ((!bcu) & bca);
abu = bcu ^ ((!bca) & bce);
ebo ^= d_o;
bca = rol(ebo, 28);
egu ^= du;
bce = rol(egu, 20);
eka ^= da;
bci = rol(eka, 3);
eme ^= de;
bco = rol(eme, 45);
esi ^= di;
bcu = rol(esi, 61);
aga = bca ^ ((!bce) & bci);
age = bce ^ ((!bci) & bco);
agi = bci ^ ((!bco) & bcu);
ago = bco ^ ((!bcu) & bca);
agu = bcu ^ ((!bca) & bce);
ebe ^= de;
bca = rol(ebe, 1);
egi ^= di;
bce = rol(egi, 6);
eko ^= d_o;
bci = rol(eko, 25);
emu ^= du;
bco = rol(emu, 8);
esa ^= da;
bcu = rol(esa, 18);
aka = bca ^ ((!bce) & bci);
ake = bce ^ ((!bci) & bco);
aki = bci ^ ((!bco) & bcu);
ako = bco ^ ((!bcu) & bca);
aku = bcu ^ ((!bca) & bce);
ebu ^= du;
bca = rol(ebu, 27);
ega ^= da;
bce = rol(ega, 36);
eke ^= de;
bci = rol(eke, 10);
emi ^= di;
bco = rol(emi, 15);
eso ^= d_o;
bcu = rol(eso, 56);
ama = bca ^ ((!bce) & bci);
ame = bce ^ ((!bci) & bco);
ami = bci ^ ((!bco) & bcu);
amo = bco ^ ((!bcu) & bca);
amu = bcu ^ ((!bca) & bce);
ebi ^= di;
bca = rol(ebi, 62);
ego ^= d_o;
bce = rol(ego, 55);
eku ^= du;
bci = rol(eku, 39);
ema ^= da;
bco = rol(ema, 41);
ese ^= de;
bcu = rol(ese, 2);
asa = bca ^ ((!bce) & bci);
ase = bce ^ ((!bci) & bco);
asi = bci ^ ((!bco) & bcu);
aso = bco ^ ((!bcu) & bca);
asu = bcu ^ ((!bca) & bce);
}
state[0] = aba;
state[1] = abe;
state[2] = abi;
state[3] = abo;
state[4] = abu;
state[5] = aga;
state[6] = age;
state[7] = agi;
state[8] = ago;
state[9] = agu;
state[10] = aka;
state[11] = ake;
state[12] = aki;
state[13] = ako;
state[14] = aku;
state[15] = ama;
state[16] = ame;
state[17] = ami;
state[18] = amo;
state[19] = amu;
state[20] = asa;
state[21] = ase;
state[22] = asi;
state[23] = aso;
state[24] = asu;
}
pub(crate) fn keccak_squeezeblocks(
h: &mut [u8],
mut nblocks: usize,
s: &mut [u64],
r: usize,
) {
let mut idx = 0usize;
while nblocks > 0 {
keccakf1600_statepermute(s);
for i in 0..r / 8 {
store64(&mut h[idx + 8 * i..], s[i])
}
idx += r;
nblocks -= 1;
}
}
pub(crate) fn shake128_squeezeblocks(
out: &mut [u8],
nblocks: usize,
state: &mut KeccakState,
) {
keccak_squeezeblocks(out, nblocks, &mut state.s, SHAKE128_RATE);
}
pub(crate) fn shake256(
out: &mut [u8],
mut outlen: usize,
input: &[u8],
inlen: usize,
) {
let mut state = KeccakState::new();
let mut idx = 0;
shake256_absorb_once(&mut state, input, inlen);
let nblocks = outlen / SHAKE256_RATE;
shake256_squeezeblocks(&mut out[idx..], nblocks, &mut state);
outlen -= nblocks * SHAKE256_RATE;
idx += nblocks * SHAKE256_RATE;
shake256_squeeze(&mut out[idx..], outlen, &mut state);
}
pub(crate) fn sha3_256(h: &mut [u8], input: &[u8], inlen: usize) {
let mut s = [0u64; 25];
keccak_absorb_once(&mut s, SHA3_256_RATE, input, inlen, 0x06);
keccakf1600_statepermute(&mut s);
for i in 0..4 {
store64(&mut h[8 * i..], s[i]);
}
}
pub(crate) fn sha3_512(h: &mut [u8], input: &[u8], inlen: usize) {
let mut s = [0u64; 25];
keccak_absorb_once(&mut s, SHA3_512_RATE, input, inlen, 0x06);
keccakf1600_statepermute(&mut s);
for i in 0..8 {
store64(&mut h[8 * i..], s[i]);
}
}
fn keccak_finalize(s: &mut [u64], pos: usize, r: usize, p: u8) {
s[pos / 8] ^= (p as u64) << (8 * (pos % 8));
s[r / 8 - 1] ^= 1u64 << 63;
}
pub(crate) fn keccak_absorb_once(
s: &mut [u64],
r: usize,
input: &[u8],
mut inlen: usize,
p: u8,
) {
s.fill(0);
let mut idx = 0usize;
while inlen >= r {
for i in 0..(r / 8) {
s[i] ^= load64(&input[idx + 8 * i..]);
}
idx += r;
inlen -= r;
keccakf1600_statepermute(s);
}
for i in 0..inlen {
s[i / 8] ^= (input[idx + i] as u64) << (8 * (i % 8));
}
s[inlen / 8] ^= (p as u64) << (8 * (inlen % 8));
s[(r - 1) / 8] ^= 1u64 << 63;
}
pub(crate) fn keccak_squeeze(
out: &mut [u8],
mut outlen: usize,
s: &mut [u64],
mut pos: usize,
r: usize,
) -> usize {
let mut idx = 0;
while outlen > 0 {
if pos == r {
keccakf1600_statepermute(s);
pos = 0
}
let mut i = pos;
let mut w = i / 8;
while i < r && i < pos + outlen {
store64(&mut out[idx..], s[w]);
i += 8;
w += 1;
idx += 8;
}
outlen -= i - pos;
pos = i;
}
pos
}
fn shake128_init(state: &mut KeccakState) {
state.reset()
}
fn shake128_finalize(state: &mut KeccakState) {
keccak_finalize(&mut state.s, state.pos, SHAKE128_RATE, 0x1F);
state.pos = SHAKE128_RATE;
}
fn shake128_squeeze(
out: &mut [u8],
outlen: usize,
state: &mut KeccakState,
) {
state.pos = keccak_squeeze(
out,
outlen,
&mut state.s,
state.pos,
SHAKE128_RATE,
);
}
pub(crate) fn shake128_absorb_once(
state: &mut KeccakState,
input: &[u8],
inlen: usize,
) {
keccak_absorb_once(&mut state.s, SHAKE128_RATE, input, inlen, 0x1F);
state.pos = SHAKE128_RATE;
}
fn shake256_init(state: &mut KeccakState) {
state.reset();
}
fn shake256_finalize(state: &mut KeccakState) {
keccak_finalize(&mut state.s, state.pos, SHAKE256_RATE, 0x1F);
state.pos = SHAKE256_RATE;
}
fn shake256_squeeze(
out: &mut [u8],
outlen: usize,
state: &mut KeccakState,
) {
state.pos = keccak_squeeze(
out,
outlen,
&mut state.s,
state.pos,
SHAKE256_RATE,
);
}
fn shake256_absorb_once(
state: &mut KeccakState,
input: &[u8],
inlen: usize,
) {
keccak_absorb_once(&mut state.s, SHAKE256_RATE, input, inlen, 0x1F);
state.pos = SHAKE256_RATE;
}
fn shake256_squeezeblocks(
out: &mut [u8],
nblocks: usize,
state: &mut KeccakState,
) {
keccak_squeezeblocks(out, nblocks, &mut state.s, SHAKE256_RATE);
}
fn shake128(
out: &mut [u8],
mut outlen: usize,
input: &[u8],
inlen: usize,
) {
let mut state = KeccakState::new();
let mut idx = 0;
shake128_absorb_once(&mut state, input, inlen);
let nblocks = outlen / SHAKE128_RATE;
shake128_squeezeblocks(&mut out[idx..], nblocks, &mut state);
outlen -= nblocks * SHAKE128_RATE;
idx += nblocks * SHAKE128_RATE;
shake128_squeeze(&mut out[idx..], outlen, &mut state);
}
#[cfg(test)]
mod tests {
use super::*;
const SHAKE128_EMPTY_32: [u8; 32] = [
0x7F, 0x9C, 0x2B, 0xA4, 0xE8, 0x8F, 0x82, 0x7D, 0x61, 0x60,
0x45, 0x50, 0x76, 0x05, 0x85, 0x3E, 0xD7, 0x3B, 0x80, 0x93,
0xF6, 0xEF, 0xBC, 0x88, 0xEB, 0x1A, 0x6E, 0xAC, 0xFA, 0x66,
0xEF, 0x26,
];
const SHAKE256_EMPTY_32: [u8; 32] = [
0x46, 0xB9, 0xDD, 0x2B, 0x0B, 0xA8, 0x8D, 0x13, 0x23, 0x3B,
0x3F, 0xEB, 0x74, 0x3E, 0xEB, 0x24, 0x3F, 0xCD, 0x52, 0xEA,
0x62, 0xB8, 0x1B, 0x82, 0xB5, 0x0C, 0x27, 0x64, 0x6E, 0xD5,
0x76, 0x2F,
];
#[test]
fn shake128_free_function_matches_nist_cavs_empty_msg() {
let mut out = [0u8; 32];
shake128(&mut out, 32, &[], 0);
assert_eq!(out, SHAKE128_EMPTY_32);
}
#[test]
fn shake128_free_function_squeezes_long_output() {
let mut out_long = [0u8; 200];
shake128(&mut out_long, 200, &[], 0);
assert_eq!(&out_long[..32], &SHAKE128_EMPTY_32[..]);
assert!(out_long[168..].iter().any(|&b| b != 0));
}
#[test]
fn shake256_init_finalize_squeeze_sequence_matches_kat() {
let mut state = KeccakState::new();
shake256_init(&mut state);
shake256_absorb_once(&mut state, &[], 0);
let _ = shake256_finalize;
let mut state2 = KeccakState::new();
shake256_init(&mut state2);
shake256_finalize(&mut state2);
let mut out2 = [0u8; 32];
shake256_squeeze(&mut out2, 32, &mut state2);
assert_eq!(out2, SHAKE256_EMPTY_32);
let mut out_blocks = [0u8; 2 * SHAKE256_RATE];
let mut state3 = KeccakState::new();
shake256_init(&mut state3);
shake256_finalize(&mut state3);
shake256_squeezeblocks(&mut out_blocks, 2, &mut state3);
assert_eq!(&out_blocks[..32], &SHAKE256_EMPTY_32[..]);
}
#[test]
fn shake128_incremental_path_matches_shake128_oneshot() {
let input = b"kyberlib coverage gap kat";
let mut oneshot = [0u8; 80];
shake128(&mut oneshot, 80, input, input.len());
let mut state = KeccakState::new();
shake128_init(&mut state);
shake128_absorb_once(&mut state, input, input.len());
let mut pieces = [0u8; 80];
shake128_squeeze(&mut pieces[..40], 40, &mut state);
shake128_squeeze(&mut pieces[40..], 40, &mut state);
assert_eq!(pieces, oneshot);
let mut s = KeccakState::new();
shake128_init(&mut s);
for (i, b) in input.iter().enumerate() {
s.s[i / 8] ^= (*b as u64) << (8 * (i % 8));
}
s.pos = input.len();
shake128_finalize(&mut s);
let mut out = [0u8; 80];
shake128_squeeze(&mut out, 80, &mut s);
assert_eq!(out, oneshot);
}
}