use super::*;
use crate::utils::hex;
const ROUND_CONSTANTS: [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 RHO: [u32; 25] = [
0, 1, 62, 28, 27, 36, 44, 6, 55, 20, 3, 10, 43, 25, 39, 41, 45, 15, 21, 8, 18, 2, 61, 56, 14,
];
const PI: [usize; 25] = {
let mut moves = [0usize; 25];
let mut x = 0;
while x < 5 {
let mut y = 0;
while y < 5 {
moves[x + 5 * y] = y + 5 * ((2 * x + 3 * y) % 5);
y += 1;
}
x += 1;
}
moves
};
fn permute_portable(state: &mut [u64; 25]) {
for &round_constant in &ROUND_CONSTANTS {
let mut column = [0u64; 5];
for (x, parity) in column.iter_mut().enumerate() {
*parity = state[x] ^ state[x + 5] ^ state[x + 10] ^ state[x + 15] ^ state[x + 20];
}
let mut spread = [0u64; 5];
for (x, value) in spread.iter_mut().enumerate() {
*value = column[(x + 4) % 5] ^ column[(x + 1) % 5].rotate_left(1);
}
for (index, lane) in state.iter_mut().enumerate() {
*lane ^= spread[index % 5];
}
let mut moved = [0u64; 25];
for (index, &lane) in state.iter().enumerate() {
moved[PI[index]] = lane.rotate_left(RHO[index]);
}
for (y, row) in moved.chunks_exact(5).enumerate() {
let out = &mut state[y * 5..y * 5 + 5];
out[0] = row[0] ^ (!row[1] & row[2]);
out[1] = row[1] ^ (!row[2] & row[3]);
out[2] = row[2] ^ (!row[3] & row[4]);
out[3] = row[3] ^ (!row[4] & row[0]);
out[4] = row[4] ^ (!row[0] & row[1]);
}
state[0] ^= round_constant;
}
}
#[test]
fn the_published_vectors_hold_however_the_input_is_split() {
for (input, digest) in [
(&b""[..], "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"),
(&b"abc"[..], "4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45"),
(
&b"The quick brown fox jumps over the lazy dog"[..],
"4d741b6f1eb29cb2a9b9911c82f56fa8d73b04959d3d9d222895df6c0b28aa15",
),
] {
assert_eq!(hex::encode(&keccak256(input)), digest);
let mut sponge = Keccak256::default();
for byte in input {
sponge.update(&[*byte]);
}
assert_eq!(hex::encode(&sponge.finalize()), digest, "a byte at a time");
}
let whole = b"the quick brown fox jumps over the lazy dog, repeatedly, \
for long enough to cross a 136 byte block boundary and \
then some more so the tail is short";
assert!(whole.len() > RATE, "this needs to span two blocks");
for split in [0, 1, 7, 8, RATE - 1, RATE, RATE + 1, whole.len()] {
let pieced = Keccak256::new().chain(&whole[..split]).chain(&whole[split..]).finalize();
assert_eq!(pieced, keccak256(whole), "split at {split}");
}
assert_eq!(format!("{:?}", Keccak256::new()), "Keccak256", "the state is never shown");
}
#[test]
fn the_borrowed_permutation_is_the_one_this_build_would_have_written() {
let mut mine = [0u64; 25];
let mut theirs = [0u64; 25];
for (at, (a, b)) in mine.iter_mut().zip(theirs.iter_mut()).enumerate() {
let lane = 0x0123_4567_89ab_cdefu64.wrapping_mul(at as u64 + 1).rotate_left(at as u32);
*a = lane;
*b = lane;
}
for round in 0..64 {
permute_portable(&mut mine);
permute(&mut theirs);
assert_eq!(mine, theirs, "they parted at round {round}");
}
assert_ne!(mine, [0u64; 25], "and the state actually moved");
}