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 chunk in out[rate_remainder..].chunks_mut(self.rate) {
104 self.squeeze_chunk(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 state: &mut [u64; 200 / 8] = unsafe { core::mem::transmute(&mut self.state) };
141 p1600::<ROUNDS>(state);
142 self.pos = 0;
143 }
144}
145
146pub fn p1600<const ROUNDS: usize>(state: &mut [u64; 25]) {
152 const {
153 assert!(ROUNDS <= 24, "A round_count greater than 24 is not supported.");
154 }
155
156 #[cfg(target_arch = "aarch64")]
159 unsafe {
160 #[cfg(feature = "std")]
164 if std::arch::is_aarch64_feature_detected!("sha3") {
165 return super::keccak_arm64::p1600_armv8::<ROUNDS>(state);
166 }
167
168 #[cfg(all(not(feature = "std"), target_feature = "sha3"))]
169 return super::keccak_arm64::p1600_armv8::<ROUNDS>(state);
170 }
171
172 p1600_soft::<ROUNDS>(state);
173}
174
175#[inline(always)]
177fn p1600_soft<const ROUNDS: usize>(state: &mut [u64; 25]) {
178 let round_consts: &[u64] = &ROUND_CONSTANTS[(24 - ROUNDS)..];
181
182 for &rc in round_consts {
185 let mut array = [0u64; 5];
186
187 for x in 0..5 {
189 for y in 0..5 {
190 array[x] ^= state[5 * y + x];
191 }
192 }
193
194 for x in 0..5 {
195 let t1 = array[(x + 4) % 5];
196 let t2 = array[(x + 1) % 5].rotate_left(1);
197 for y in 0..5 {
198 state[5 * y + x] ^= t1 ^ t2;
199 }
200 }
201
202 let mut last = state[1];
204 for x in 0..24 {
205 array[0] = state[PI[x]];
206 state[PI[x]] = last.rotate_left(RHO[x]);
207 last = array[0];
208 }
209
210 for y_step in 0..5 {
212 let y = 5 * y_step;
213
214 array.copy_from_slice(&state[y..][..5]);
215
216 for x in 0..5 {
217 let t1 = !array[(x + 1) % 5];
218 let t2 = array[(x + 2) % 5];
219 state[y + x] = array[x] ^ (t1 & t2);
220 }
221 }
222
223 state[0] ^= rc;
225 }
226}
227
228#[inline(always)]
230fn xor(dest: &mut [u8], source: &[u8]) {
231 dest.iter_mut()
232 .zip(source.iter())
233 .for_each(|(dest, source)| *dest ^= *source);
234}
235
236#[cfg(test)]
237mod tests {
238 use super::{p1600, p1600_soft};
239
240 fn keccak_f(permute: impl Fn(&mut [u64; 25]), state_first: [u64; 25], state_second: [u64; 25]) {
241 let mut state = [0u64; 25];
242
243 permute(&mut state);
244 assert_eq!(state, state_first);
245
246 permute(&mut state);
247 assert_eq!(state, state_second);
248 }
249
250 fn keccak_f1600_vectors() -> ([u64; 25], [u64; 25]) {
253 let state_first = [
254 0xF1258F7940E1DDE7,
255 0x84D5CCF933C0478A,
256 0xD598261EA65AA9EE,
257 0xBD1547306F80494D,
258 0x8B284E056253D057,
259 0xFF97A42D7F8E6FD4,
260 0x90FEE5A0A44647C4,
261 0x8C5BDA0CD6192E76,
262 0xAD30A6F71B19059C,
263 0x30935AB7D08FFC64,
264 0xEB5AA93F2317D635,
265 0xA9A6E6260D712103,
266 0x81A57C16DBCF555F,
267 0x43B831CD0347C826,
268 0x01F22F1A11A5569F,
269 0x05E5635A21D9AE61,
270 0x64BEFEF28CC970F2,
271 0x613670957BC46611,
272 0xB87C5A554FD00ECB,
273 0x8C3EE88A1CCF32C8,
274 0x940C7922AE3A2614,
275 0x1841F924A2C509E4,
276 0x16F53526E70465C2,
277 0x75F644E97F30A13B,
278 0xEAF1FF7B5CECA249,
279 ];
280 let state_second = [
281 0x2D5C954DF96ECB3C,
282 0x6A332CD07057B56D,
283 0x093D8D1270D76B6C,
284 0x8A20D9B25569D094,
285 0x4F9C4F99E5E7F156,
286 0xF957B9A2DA65FB38,
287 0x85773DAE1275AF0D,
288 0xFAF4F247C3D810F7,
289 0x1F1B9EE6F79A8759,
290 0xE4FECC0FEE98B425,
291 0x68CE61B6B9CE68A1,
292 0xDEEA66C4BA8F974F,
293 0x33C43D836EAFB1F5,
294 0xE00654042719DBD9,
295 0x7CF8A9F009831265,
296 0xFD5449A6BF174743,
297 0x97DDAD33D8994B40,
298 0x48EAD5FC5D0BE774,
299 0xE3B8C8EE55B7B03C,
300 0x91A0226E649E42E9,
301 0x900E3129E7BADD7B,
302 0x202A9EC5FAA3CCE8,
303 0x5B3402464E1C3DB6,
304 0x609F4E62A44C1059,
305 0x20D06CD26A8FBF5C,
306 ];
307
308 (state_first, state_second)
309 }
310
311 #[test]
312 fn keccak_f1600() {
313 let (state_first, state_second) = keccak_f1600_vectors();
314 keccak_f(|state| p1600::<24>(state), state_first, state_second);
315 }
316
317 #[test]
318 fn keccak_f1600_soft() {
319 let (state_first, state_second) = keccak_f1600_vectors();
320 keccak_f(|state| p1600_soft::<24>(state), state_first, state_second);
321 }
322}