///| Keccak-256, the hash Ethereum uses for function selectors, custom error
/// selectors, and event topics.
///
/// Ethereum uses the original Keccak padding byte `0x01`, not the SHA-3
/// padding byte `0x06`, so this is Keccak-f[1600] with a 1088-bit rate. The
/// round constants, rotation offsets, and lane permutation are the ones
/// published with the Keccak specification, and the digest is checked against
/// published known-answer values, including the boundary lengths where
/// padding shares a byte.
///|
/// Keccak round constants for the iota step.
let round_constants : Array[UInt64] = [
0x0000000000000001UL, 0x0000000000008082UL, 0x800000000000808aUL, 0x8000000080008000UL,
0x000000000000808bUL, 0x0000000080000001UL, 0x8000000080008081UL, 0x8000000000008009UL,
0x000000000000008aUL, 0x0000000000000088UL, 0x0000000080008009UL, 0x000000008000000aUL,
0x000000008000808bUL, 0x800000000000008bUL, 0x8000000000008089UL, 0x8000000000008003UL,
0x8000000000008002UL, 0x8000000000000080UL, 0x000000000000800aUL, 0x800000008000000aUL,
0x8000000080008081UL, 0x8000000000008080UL, 0x0000000080000001UL, 0x8000000080008008UL,
]
///|
/// Rotation amount for each step of the combined rho and pi step.
let rotation_amounts : Array[Int] = [
1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39,
61, 20, 44,
]
///|
/// Lane visited by each step of the combined rho and pi step.
let lane_permutation : Array[Int] = [
10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22, 9,
6, 1,
]
///|
/// Number of bytes absorbed per permutation for a 1088-bit rate.
let rate_bytes = 136
///|
/// Rotates a lane left by `n` bits, for `0 <= n < 64`.
fn rotate_left(value : UInt64, n : Int) -> UInt64 {
if n == 0 {
value
} else {
(value << n) | (value >> (64 - n))
}
}
///|
/// Applies Keccak-f[1600] to the 25-lane state in place.
fn keccak_f1600(state : Array[UInt64]) -> Unit {
let column : Array[UInt64] = Array::make(5, 0UL)
for round in 0..<24 {
// Theta
for i in 0..<5 {
column[i] = state[i] ^
state[i + 5] ^
state[i + 10] ^
state[i + 15] ^
state[i + 20]
}
for i in 0..<5 {
let t = column[(i + 4) % 5] ^ rotate_left(column[(i + 1) % 5], 1)
for j in 0..<5 {
state[j * 5 + i] = state[j * 5 + i] ^ t
}
}
// Rho and pi
let mut carried = state[1]
for i in 0..<24 {
let lane = lane_permutation[i]
let saved = state[lane]
state[lane] = rotate_left(carried, rotation_amounts[i])
carried = saved
}
// Chi
for j in 0..<5 {
for i in 0..<5 {
column[i] = state[j * 5 + i]
}
for i in 0..<5 {
state[j * 5 + i] = state[j * 5 + i] ^
(column[(i + 1) % 5].lnot() & column[(i + 2) % 5])
}
}
// Iota
state[0] = state[0] ^ round_constants[round]
}
}
///|
/// XORs `length` bytes starting at `offset` into the state as little-endian
/// lanes.
fn absorb(
state : Array[UInt64],
data : Bytes,
offset : Int,
length : Int,
) -> Unit {
for i in 0.. Bytes {
let state : Array[UInt64] = Array::make(25, 0UL)
let length = data.length()
let mut offset = 0
while length - offset >= rate_bytes {
absorb(state, data, offset, rate_bytes)
keccak_f1600(state)
offset += rate_bytes
}
// The final block carries the Keccak pad: `0x01`, zeros, then `0x80` in the
// last byte of the block. When one byte is left, both bits share it.
let last_block : Array[Byte] = Array::make(rate_bytes, 0)
let remaining = length - offset
for i in 0..> (8 * b)) & 0xFFUL).to_byte()
}
}
Bytes::from_array(digest)
}
///|
/// Lowercase hex of `data`, without a `0x` prefix.
pub fn hex_encode(data : Bytes) -> String {
let buf = StringBuilder()
for i in 0..