const ROUND_CONSTANTS: [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,
];
const ROTATIONS: [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, ];
fn keccak_f1600(state: &mut [u64; 25]) {
for round in 0..24 {
let mut c = [0u64; 5];
for x in 0..5 {
c[x] = state[x] ^ state[x + 5] ^ state[x + 10] ^ state[x + 15] ^ state[x + 20];
}
let mut d = [0u64; 5];
for x in 0..5 {
d[x] = c[(x + 4) % 5] ^ c[(x + 1) % 5].rotate_left(1);
}
for y in 0..5 {
for x in 0..5 {
state[x + 5 * y] ^= d[x];
}
}
let mut b = [0u64; 25];
for y in 0..5usize {
for x in 0..5usize {
let x_prime = y;
let y_prime = (2 * x + 3 * y) % 5;
b[x_prime + 5 * y_prime] = state[x + 5 * y].rotate_left(ROTATIONS[x + 5 * y]);
}
}
for y in 0..5 {
for x in 0..5 {
state[x + 5 * y] =
b[x + 5 * y] ^ ((!b[(x + 1) % 5 + 5 * y]) & b[(x + 2) % 5 + 5 * y]);
}
}
state[0] ^= ROUND_CONSTANTS[round];
}
}
const RATE_BYTES: usize = 136;
#[inline(always)]
fn absorb_block(state: &mut [u64; 25], block: &[u8; RATE_BYTES]) {
for i in 0..(RATE_BYTES / 8) {
let lane = u64::from_le_bytes(block[8 * i..8 * i + 8].try_into().unwrap());
state[i] ^= lane;
}
keccak_f1600(state);
}
pub fn keccak256(input: &[u8]) -> [u8; 32] {
let mut state = [0u64; 25];
let mut buf = [0u8; RATE_BYTES];
let mut offset = 0usize;
for chunk in input.chunks(RATE_BYTES) {
if chunk.len() == RATE_BYTES {
let block: &[u8; RATE_BYTES] = chunk.try_into().unwrap();
absorb_block(&mut state, block);
} else {
buf[offset..offset + chunk.len()].copy_from_slice(chunk);
offset += chunk.len();
}
}
buf[offset] = 0x01;
for b in buf[offset + 1..RATE_BYTES - 1].iter_mut() {
*b = 0;
}
buf[RATE_BYTES - 1] = 0x80;
absorb_block(&mut state, &buf);
let mut digest = [0u8; 32];
for i in 0..4 {
digest[8 * i..8 * i + 8].copy_from_slice(&state[i].to_le_bytes());
}
digest
}
#[cfg(test)]
mod tests {
use super::*;
fn hex(bytes: &[u8]) -> String {
bytes.iter().map(|b| format!("{:02x}", b)).collect()
}
#[test]
fn test_empty() {
let digest = keccak256(b"");
assert_eq!(
hex(&digest),
"c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
);
}
#[test]
fn test_abc() {
let digest = keccak256(b"abc");
assert_eq!(
hex(&digest),
"4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45",
);
}
#[test]
fn test_one_full_block() {
let input = [0u8; 136];
let digest = keccak256(&input);
assert_eq!(
hex(&digest),
"3a5912a7c5faa06ee4fe906253e339467a9ce87d533c65be3c15cb231cdb25f9",
);
}
#[test]
fn test_transfer_sig() {
let digest = keccak256(b"Transfer(address,address,uint256)");
assert_eq!(
hex(&digest),
"ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
);
}
}