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)]
50#[cfg_attr(feature = "zeroize", derive(Zeroize, ZeroizeOnDrop))]
51pub(crate) struct Keccak<const ROUNDS: usize> {
52 state: [u8; 200],
53 rate: usize,
54 padding: u8,
55 pos: usize,
57 mode: SpongeMode,
58}
59
60impl<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 const {
148 assert!(ROUNDS <= 24, "A round_count greater than 24 is not supported.");
149 }
150
151 #[cfg(target_arch = "aarch64")]
153 unsafe {
154 super::keccak_arm64::p1600_armv8::<ROUNDS>(state);
155 return;
156 }
157
158 let round_consts: &[u64] = &ROUND_CONSTANTS[(24 - ROUNDS)..];
161
162 for &rc in round_consts {
165 let mut array = [0u64; 5];
166
167 for x in 0..5 {
169 for y in 0..5 {
170 array[x] ^= state[5 * y + x];
171 }
172 }
173
174 for x in 0..5 {
175 let t1 = array[(x + 4) % 5];
176 let t2 = array[(x + 1) % 5].rotate_left(1);
177 for y in 0..5 {
178 state[5 * y + x] ^= t1 ^ t2;
179 }
180 }
181
182 let mut last = state[1];
184 for x in 0..24 {
185 array[0] = state[PI[x]];
186 state[PI[x]] = last.rotate_left(RHO[x]);
187 last = array[0];
188 }
189
190 for y_step in 0..5 {
192 let y = 5 * y_step;
193
194 array.copy_from_slice(&state[y..][..5]);
195
196 for x in 0..5 {
197 let t1 = !array[(x + 1) % 5];
198 let t2 = array[(x + 2) % 5];
199 state[y + x] = array[x] ^ (t1 & t2);
200 }
201 }
202
203 state[0] ^= rc;
205 }
206}
207
208#[inline(always)]
210pub fn xor(dest: &mut [u8], source: &[u8]) {
211 dest.iter_mut()
212 .zip(source.iter())
213 .for_each(|(dest, source)| *dest ^= *source);
214}
215
216#[cfg(test)]
217mod tests {
218 use super::p1600;
219
220 fn keccak_f(state_first: [u64; 25], state_second: [u64; 25]) {
221 let mut state = [0u64; 25];
222
223 p1600::<24>(&mut state);
224 assert_eq!(state, state_first);
225
226 p1600::<24>(&mut state);
227 assert_eq!(state, state_second);
228 }
229
230 #[test]
231 fn keccak_f1600() {
232 let state_first = [
235 0xF1258F7940E1DDE7,
236 0x84D5CCF933C0478A,
237 0xD598261EA65AA9EE,
238 0xBD1547306F80494D,
239 0x8B284E056253D057,
240 0xFF97A42D7F8E6FD4,
241 0x90FEE5A0A44647C4,
242 0x8C5BDA0CD6192E76,
243 0xAD30A6F71B19059C,
244 0x30935AB7D08FFC64,
245 0xEB5AA93F2317D635,
246 0xA9A6E6260D712103,
247 0x81A57C16DBCF555F,
248 0x43B831CD0347C826,
249 0x01F22F1A11A5569F,
250 0x05E5635A21D9AE61,
251 0x64BEFEF28CC970F2,
252 0x613670957BC46611,
253 0xB87C5A554FD00ECB,
254 0x8C3EE88A1CCF32C8,
255 0x940C7922AE3A2614,
256 0x1841F924A2C509E4,
257 0x16F53526E70465C2,
258 0x75F644E97F30A13B,
259 0xEAF1FF7B5CECA249,
260 ];
261 let state_second = [
262 0x2D5C954DF96ECB3C,
263 0x6A332CD07057B56D,
264 0x093D8D1270D76B6C,
265 0x8A20D9B25569D094,
266 0x4F9C4F99E5E7F156,
267 0xF957B9A2DA65FB38,
268 0x85773DAE1275AF0D,
269 0xFAF4F247C3D810F7,
270 0x1F1B9EE6F79A8759,
271 0xE4FECC0FEE98B425,
272 0x68CE61B6B9CE68A1,
273 0xDEEA66C4BA8F974F,
274 0x33C43D836EAFB1F5,
275 0xE00654042719DBD9,
276 0x7CF8A9F009831265,
277 0xFD5449A6BF174743,
278 0x97DDAD33D8994B40,
279 0x48EAD5FC5D0BE774,
280 0xE3B8C8EE55B7B03C,
281 0x91A0226E649E42E9,
282 0x900E3129E7BADD7B,
283 0x202A9EC5FAA3CCE8,
284 0x5B3402464E1C3DB6,
285 0x609F4E62A44C1059,
286 0x20D06CD26A8FBF5C,
287 ];
288
289 keccak_f(state_first, state_second);
290 }
291}