1use core::cmp::min;
2
3#[cfg(feature = "zeroize")]
4use zeroize::{Zeroize, ZeroizeOnDrop};
5
6pub const RHO: [u32; 24] = [
7 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44,
8];
9
10pub const PI: [usize; 24] = [
11 10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1,
12];
13
14pub const ROUND_CONSTANTS: [u64; 24] = [
15 0x0000000000000001,
16 0x0000000000008082,
17 0x800000000000808a,
18 0x8000000080008000,
19 0x000000000000808b,
20 0x0000000080000001,
21 0x8000000080008081,
22 0x8000000000008009,
23 0x000000000000008a,
24 0x0000000000000088,
25 0x0000000080008009,
26 0x000000008000000a,
27 0x000000008000808b,
28 0x800000000000008b,
29 0x8000000000008089,
30 0x8000000000008003,
31 0x8000000000008002,
32 0x8000000000000080,
33 0x000000000000800a,
34 0x800000008000000a,
35 0x8000000080008081,
36 0x8000000000008080,
37 0x0000000080000001,
38 0x8000000080008008,
39];
40
41#[derive(Debug, Clone, PartialEq, Eq)]
42#[cfg_attr(feature = "zeroize", derive(Zeroize, ZeroizeOnDrop))]
43enum SpongeMode {
44 Absorbing,
45 Squeezing,
46}
47
48#[derive(Clone)]
50#[cfg_attr(feature = "zeroize", derive(Zeroize, ZeroizeOnDrop))]
51#[repr(align(8))]
54pub struct KeccakSponge<const ROUNDS: usize> {
55 state: [u8; 200],
56 rate: usize,
57 padding: u8,
58 pos: usize,
60 mode: SpongeMode,
61}
62
63impl<const ROUNDS: usize> KeccakSponge<ROUNDS> {
64 #[inline]
65 pub fn new(rate: usize, delimiter: u8) -> Self {
66 debug_assert!(rate > 0 && rate < 200);
67 return KeccakSponge {
68 state: [0u8; 200],
69 rate,
70 padding: delimiter,
71 pos: 0,
72 mode: SpongeMode::Absorbing,
73 };
74 }
75
76 #[inline]
77 pub fn absorb(&mut self, data: &[u8]) {
78 assert_eq!(self.mode, SpongeMode::Absorbing, "absorb can't be called after squeezing");
79
80 let rate_remainder = min(self.rate - self.pos, data.len());
82 self.absorb_chunk(&data[..rate_remainder]);
83
84 for chunk in data[rate_remainder..].chunks(self.rate) {
86 self.absorb_chunk(chunk);
87 }
88 }
89
90 #[inline]
91 pub fn squeeze(&mut self, out: &mut [u8]) {
92 if self.mode == SpongeMode::Absorbing {
94 self.pad_and_permute();
95 self.mode = SpongeMode::Squeezing;
96 }
97
98 let rate_remainder = min(self.rate - self.pos, out.len());
100 self.squeeze_chunk(&mut out[..rate_remainder]);
101
102 for mut chunk in out[rate_remainder..].chunks_mut(self.rate) {
104 self.squeeze_chunk(&mut chunk);
105 }
106 }
107
108 #[inline]
109 fn absorb_chunk(&mut self, chunk: &[u8]) {
110 xor(&mut self.state[self.pos..self.pos + chunk.len()], &chunk);
111 self.pos += chunk.len();
112
113 if self.pos == self.rate {
115 self.permute_and_reset_pos();
116 }
117 }
118
119 #[inline]
120 fn squeeze_chunk(&mut self, out: &mut [u8]) {
121 if self.pos == self.rate {
122 self.permute_and_reset_pos();
123 }
124
125 out.copy_from_slice(&self.state[self.pos..self.pos + out.len()]);
126 self.pos += out.len();
127 }
128
129 #[inline]
130 fn pad_and_permute(&mut self) {
131 self.state[self.pos] ^= self.padding;
132 self.state[self.rate - 1] ^= 0x80;
133 self.permute_and_reset_pos();
134 }
135
136 #[inline]
137 fn permute_and_reset_pos(&mut self) {
138 let mut state: &mut [u64; 200 / 8] = unsafe { core::mem::transmute(&mut self.state) };
141 p1600::<ROUNDS>(&mut state);
142 self.pos = 0;
143 }
144}
145
146#[allow(unreachable_code)]
148pub fn p1600<const ROUNDS: usize>(state: &mut [u64; 25]) {
149 const {
150 assert!(ROUNDS <= 24, "A round_count greater than 24 is not supported.");
151 }
152
153 #[cfg(target_arch = "aarch64")]
155 unsafe {
156 super::keccak_arm64::p1600_armv8::<ROUNDS>(state);
157 return;
158 }
159
160 let round_consts: &[u64] = &ROUND_CONSTANTS[(24 - ROUNDS)..];
163
164 for &rc in round_consts {
167 let mut array = [0u64; 5];
168
169 for x in 0..5 {
171 for y in 0..5 {
172 array[x] ^= state[5 * y + x];
173 }
174 }
175
176 for x in 0..5 {
177 let t1 = array[(x + 4) % 5];
178 let t2 = array[(x + 1) % 5].rotate_left(1);
179 for y in 0..5 {
180 state[5 * y + x] ^= t1 ^ t2;
181 }
182 }
183
184 let mut last = state[1];
186 for x in 0..24 {
187 array[0] = state[PI[x]];
188 state[PI[x]] = last.rotate_left(RHO[x]);
189 last = array[0];
190 }
191
192 for y_step in 0..5 {
194 let y = 5 * y_step;
195
196 array.copy_from_slice(&state[y..][..5]);
197
198 for x in 0..5 {
199 let t1 = !array[(x + 1) % 5];
200 let t2 = array[(x + 2) % 5];
201 state[y + x] = array[x] ^ (t1 & t2);
202 }
203 }
204
205 state[0] ^= rc;
207 }
208}
209
210#[inline(always)]
212fn xor(dest: &mut [u8], source: &[u8]) {
213 dest.iter_mut()
214 .zip(source.iter())
215 .for_each(|(dest, source)| *dest ^= *source);
216}
217
218#[cfg(test)]
219mod tests {
220 use super::p1600;
221
222 fn keccak_f(state_first: [u64; 25], state_second: [u64; 25]) {
223 let mut state = [0u64; 25];
224
225 p1600::<24>(&mut state);
226 assert_eq!(state, state_first);
227
228 p1600::<24>(&mut state);
229 assert_eq!(state, state_second);
230 }
231
232 #[test]
233 fn keccak_f1600() {
234 let state_first = [
237 0xF1258F7940E1DDE7,
238 0x84D5CCF933C0478A,
239 0xD598261EA65AA9EE,
240 0xBD1547306F80494D,
241 0x8B284E056253D057,
242 0xFF97A42D7F8E6FD4,
243 0x90FEE5A0A44647C4,
244 0x8C5BDA0CD6192E76,
245 0xAD30A6F71B19059C,
246 0x30935AB7D08FFC64,
247 0xEB5AA93F2317D635,
248 0xA9A6E6260D712103,
249 0x81A57C16DBCF555F,
250 0x43B831CD0347C826,
251 0x01F22F1A11A5569F,
252 0x05E5635A21D9AE61,
253 0x64BEFEF28CC970F2,
254 0x613670957BC46611,
255 0xB87C5A554FD00ECB,
256 0x8C3EE88A1CCF32C8,
257 0x940C7922AE3A2614,
258 0x1841F924A2C509E4,
259 0x16F53526E70465C2,
260 0x75F644E97F30A13B,
261 0xEAF1FF7B5CECA249,
262 ];
263 let state_second = [
264 0x2D5C954DF96ECB3C,
265 0x6A332CD07057B56D,
266 0x093D8D1270D76B6C,
267 0x8A20D9B25569D094,
268 0x4F9C4F99E5E7F156,
269 0xF957B9A2DA65FB38,
270 0x85773DAE1275AF0D,
271 0xFAF4F247C3D810F7,
272 0x1F1B9EE6F79A8759,
273 0xE4FECC0FEE98B425,
274 0x68CE61B6B9CE68A1,
275 0xDEEA66C4BA8F974F,
276 0x33C43D836EAFB1F5,
277 0xE00654042719DBD9,
278 0x7CF8A9F009831265,
279 0xFD5449A6BF174743,
280 0x97DDAD33D8994B40,
281 0x48EAD5FC5D0BE774,
282 0xE3B8C8EE55B7B03C,
283 0x91A0226E649E42E9,
284 0x900E3129E7BADD7B,
285 0x202A9EC5FAA3CCE8,
286 0x5B3402464E1C3DB6,
287 0x609F4E62A44C1059,
288 0x20D06CD26A8FBF5C,
289 ];
290
291 keccak_f(state_first, state_second);
292 }
293}