1use rhdl::prelude::*;
4
5use crate::{KeccakState, keccak_f1600, software_keccak_f1600};
6
7pub const RATE_BYTES: usize = 136;
9
10pub const RATE_LANES: usize = RATE_BYTES / 8;
12
13pub const LEGACY_DOMAIN_BYTE: u8 = 0x01;
17
18pub type Digest = [u8; 32];
20
21#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Digital)]
26pub struct RateBlock {
27 pub lanes: [b64; RATE_LANES],
29}
30
31#[kernel]
37pub fn xor_rate_block(mut state: KeccakState, block: RateBlock) -> KeccakState {
38 state[0] ^= block.lanes[0];
39 state[1] ^= block.lanes[1];
40 state[2] ^= block.lanes[2];
41 state[3] ^= block.lanes[3];
42 state[4] ^= block.lanes[4];
43 state[5] ^= block.lanes[5];
44 state[6] ^= block.lanes[6];
45 state[7] ^= block.lanes[7];
46 state[8] ^= block.lanes[8];
47 state[9] ^= block.lanes[9];
48 state[10] ^= block.lanes[10];
49 state[11] ^= block.lanes[11];
50 state[12] ^= block.lanes[12];
51 state[13] ^= block.lanes[13];
52 state[14] ^= block.lanes[14];
53 state[15] ^= block.lanes[15];
54 state[16] ^= block.lanes[16];
55 state
56}
57
58#[kernel]
63pub fn absorb_rate_block(state: KeccakState, block: RateBlock) -> KeccakState {
64 keccak_f1600(xor_rate_block(state, block))
65}
66
67#[must_use]
72pub fn digest_from_state(state: KeccakState) -> Digest {
73 let mut digest = [0_u8; 32];
74 for lane in 0..4 {
75 let bytes = lane_as_u64(state[lane]).to_le_bytes();
76 digest[lane * 8..lane * 8 + 8].copy_from_slice(&bytes);
77 }
78 digest
79}
80
81#[derive(Debug, Default, Copy, Clone)]
86pub struct LegacyKeccak256;
87
88impl LegacyKeccak256 {
89 #[must_use]
94 pub fn padded_blocks(message: &[u8]) -> Vec<RateBlock> {
95 let full_blocks = message.len() / RATE_BYTES;
96 let remainder = message.len() % RATE_BYTES;
97 let mut blocks = Vec::with_capacity(full_blocks + 1);
98
99 for chunk in message[..full_blocks * RATE_BYTES].chunks_exact(RATE_BYTES) {
100 blocks.push(Self::pack_rate_bytes(chunk));
101 }
102
103 let mut tail = [0_u8; RATE_BYTES];
104 tail[..remainder].copy_from_slice(&message[full_blocks * RATE_BYTES..]);
105 tail[remainder] ^= LEGACY_DOMAIN_BYTE;
106 tail[RATE_BYTES - 1] ^= 0x80;
107 blocks.push(Self::pack_rate_bytes(&tail));
108 blocks
109 }
110
111 #[must_use]
113 pub fn digest(message: &[u8]) -> Digest {
114 let mut state = [0_u64; 25];
115 for block in Self::padded_blocks(message) {
116 for (state_lane, block_lane) in state.iter_mut().zip(block.lanes) {
117 *state_lane ^= lane_as_u64(block_lane);
118 }
119 state = software_keccak_f1600(state);
120 }
121 let mut digest = [0_u8; 32];
122 for lane in 0..4 {
123 digest[lane * 8..lane * 8 + 8].copy_from_slice(&state[lane].to_le_bytes());
124 }
125 digest
126 }
127
128 fn pack_rate_bytes(bytes: &[u8]) -> RateBlock {
129 debug_assert_eq!(bytes.len(), RATE_BYTES);
130 let mut block = RateBlock::default();
131 for (lane, chunk) in bytes.chunks_exact(8).enumerate() {
132 let mut lane_bytes = [0_u8; 8];
133 lane_bytes.copy_from_slice(chunk);
134 block.lanes[lane] = b64(u64::from_le_bytes(lane_bytes).into());
135 }
136 block
137 }
138}
139
140#[allow(clippy::cast_possible_truncation)]
144const fn lane_as_u64(lane: b64) -> u64 {
145 lane.raw() as u64
146}