1use std::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))]
43pub(crate) enum SpongeMode {
44 Absorbing,
45 Squeezing,
46}
47
48#[derive(Clone)]
49#[cfg_attr(feature = "zeroize", derive(Zeroize, ZeroizeOnDrop))]
50pub(crate) struct Keccak<const ROUNDS: usize> {
51 state: [u8; 200],
52 rate: usize,
53 padding: u8,
54 pos: usize,
56 mode: SpongeMode,
57}
58
59impl<const ROUNDS: usize> Keccak<ROUNDS> {
61 #[inline]
62 pub(crate) fn new(rate: usize, delimiter: u8) -> Self {
63 debug_assert!(rate > 0 && rate < 200);
64 return Keccak {
65 state: [0u8; 200],
66 rate,
67 padding: delimiter,
68 pos: 0,
69 mode: SpongeMode::Absorbing,
70 };
71 }
72
73 #[inline]
74 pub(crate) fn absorb(&mut self, data: &[u8]) {
75 assert_eq!(self.mode, SpongeMode::Absorbing, "absorb can't be called after squeezing");
76
77 let rate_remainder = min(self.rate - self.pos, data.len());
79 self.absorb_chunk(&data[..rate_remainder]);
80
81 for chunk in data[rate_remainder..].chunks(self.rate) {
83 self.absorb_chunk(chunk);
84 }
85 }
86
87 #[inline]
88 fn absorb_chunk(&mut self, chunk: &[u8]) {
89 xor(&mut self.state[self.pos..self.pos + chunk.len()], &chunk);
91 self.pos += chunk.len();
92
93 if self.pos == self.rate {
95 self.permute_and_reset_pos();
96 }
97 }
98
99 #[inline]
100 pub fn squeeze(&mut self, out: &mut [u8]) {
101 if self.mode == SpongeMode::Absorbing {
103 self.pad_and_permute();
104 self.mode = SpongeMode::Squeezing;
105 }
106
107 let rate_remainder = min(self.rate - self.pos, out.len());
109 self.squeeze_chunk(&mut out[..rate_remainder]);
110
111 for mut chunk in out[rate_remainder..].chunks_mut(self.rate) {
113 self.squeeze_chunk(&mut chunk);
114 }
115 }
116
117 #[inline]
118 fn squeeze_chunk(&mut self, out: &mut [u8]) {
119 if self.pos == self.rate {
120 self.permute_and_reset_pos();
121 }
122
123 out.copy_from_slice(&self.state[self.pos..self.pos + out.len()]);
124 self.pos += out.len();
125 }
126
127 #[inline]
128 fn pad_and_permute(&mut self) {
129 self.state[self.pos] ^= self.padding;
130 self.state[self.rate - 1] ^= 0x80;
131 self.permute_and_reset_pos();
132 }
133
134 #[inline]
135 fn permute_and_reset_pos(&mut self) {
136 let mut state: &mut [u64; 200 / 8] = unsafe { core::mem::transmute(&mut self.state) };
139 p1600::<ROUNDS>(&mut state);
140 self.pos = 0;
141 }
142}
143
144#[allow(unreachable_code)]
146pub fn p1600<const ROUNDS: usize>(state: &mut [u64; 25]) {
147 debug_assert!(ROUNDS <= 24, "A round_count greater than 24 is not supported.");
148
149 #[cfg(target_arch = "aarch64")]
151 unsafe {
152 super::keccak_arm64::p1600_armv8::<ROUNDS>(state);
153 return;
154 }
155
156 let round_consts: &[u64] = &ROUND_CONSTANTS[(24 - ROUNDS)..];
159
160 for &rc in round_consts {
163 let mut array = [0u64; 5];
164
165 for x in 0..5 {
167 for y in 0..5 {
168 array[x] ^= state[5 * y + x];
169 }
170 }
171
172 for x in 0..5 {
173 let t1 = array[(x + 4) % 5];
174 let t2 = array[(x + 1) % 5].rotate_left(1);
175 for y in 0..5 {
176 state[5 * y + x] ^= t1 ^ t2;
177 }
178 }
179
180 let mut last = state[1];
182 for x in 0..24 {
183 array[0] = state[PI[x]];
184 state[PI[x]] = last.rotate_left(RHO[x]);
185 last = array[0];
186 }
187
188 for y_step in 0..5 {
190 let y = 5 * y_step;
191
192 array.copy_from_slice(&state[y..][..5]);
193
194 for x in 0..5 {
195 let t1 = !array[(x + 1) % 5];
196 let t2 = array[(x + 2) % 5];
197 state[y + x] = array[x] ^ (t1 & t2);
198 }
199 }
200
201 state[0] ^= rc;
203 }
204}
205
206#[inline(always)]
208pub fn xor(dest: &mut [u8], source: &[u8]) {
209 dest.iter_mut()
210 .zip(source.iter())
211 .for_each(|(dest, source)| *dest ^= *source);
212}
213
214#[cfg(test)]
215mod tests {
216 use super::p1600;
217
218 fn keccak_f(state_first: [u64; 25], state_second: [u64; 25]) {
219 let mut state = [0u64; 25];
220
221 p1600::<24>(&mut state);
222 assert_eq!(state, state_first);
223
224 p1600::<24>(&mut state);
225 assert_eq!(state, state_second);
226 }
227
228 #[test]
229 fn keccak_f1600() {
230 let state_first = [
233 0xF1258F7940E1DDE7,
234 0x84D5CCF933C0478A,
235 0xD598261EA65AA9EE,
236 0xBD1547306F80494D,
237 0x8B284E056253D057,
238 0xFF97A42D7F8E6FD4,
239 0x90FEE5A0A44647C4,
240 0x8C5BDA0CD6192E76,
241 0xAD30A6F71B19059C,
242 0x30935AB7D08FFC64,
243 0xEB5AA93F2317D635,
244 0xA9A6E6260D712103,
245 0x81A57C16DBCF555F,
246 0x43B831CD0347C826,
247 0x01F22F1A11A5569F,
248 0x05E5635A21D9AE61,
249 0x64BEFEF28CC970F2,
250 0x613670957BC46611,
251 0xB87C5A554FD00ECB,
252 0x8C3EE88A1CCF32C8,
253 0x940C7922AE3A2614,
254 0x1841F924A2C509E4,
255 0x16F53526E70465C2,
256 0x75F644E97F30A13B,
257 0xEAF1FF7B5CECA249,
258 ];
259 let state_second = [
260 0x2D5C954DF96ECB3C,
261 0x6A332CD07057B56D,
262 0x093D8D1270D76B6C,
263 0x8A20D9B25569D094,
264 0x4F9C4F99E5E7F156,
265 0xF957B9A2DA65FB38,
266 0x85773DAE1275AF0D,
267 0xFAF4F247C3D810F7,
268 0x1F1B9EE6F79A8759,
269 0xE4FECC0FEE98B425,
270 0x68CE61B6B9CE68A1,
271 0xDEEA66C4BA8F974F,
272 0x33C43D836EAFB1F5,
273 0xE00654042719DBD9,
274 0x7CF8A9F009831265,
275 0xFD5449A6BF174743,
276 0x97DDAD33D8994B40,
277 0x48EAD5FC5D0BE774,
278 0xE3B8C8EE55B7B03C,
279 0x91A0226E649E42E9,
280 0x900E3129E7BADD7B,
281 0x202A9EC5FAA3CCE8,
282 0x5B3402464E1C3DB6,
283 0x609F4E62A44C1059,
284 0x20D06CD26A8FBF5C,
285 ];
286
287 keccak_f(state_first, state_second);
288 }
289}