1use alloc::vec;
28use core::cmp;
29
30use crate::cryptoutil::{read_u64v_le, write_u64v_le, zero};
31
32pub(super) const B: usize = 200;
33const NROUNDS: usize = 24;
34const RC: [u64; 24] = [
35 0x0000000000000001,
36 0x0000000000008082,
37 0x800000000000808a,
38 0x8000000080008000,
39 0x000000000000808b,
40 0x0000000080000001,
41 0x8000000080008081,
42 0x8000000000008009,
43 0x000000000000008a,
44 0x0000000000000088,
45 0x0000000080008009,
46 0x000000008000000a,
47 0x000000008000808b,
48 0x800000000000008b,
49 0x8000000000008089,
50 0x8000000000008003,
51 0x8000000000008002,
52 0x8000000000000080,
53 0x000000000000800a,
54 0x800000008000000a,
55 0x8000000080008081,
56 0x8000000000008080,
57 0x0000000080000001,
58 0x8000000080008008,
59];
60const ROTC: [u32; 24] = [
61 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44,
62];
63const PIL: [usize; 24] = [
64 10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1,
65];
66const M5: [usize; 10] = [0, 1, 2, 3, 4, 0, 1, 2, 3, 4];
67
68#[allow(clippy::needless_range_loop)]
70fn keccak_f(state: &mut [u8; B]) {
71 let mut s: [u64; 25] = [0; 25];
72 let mut t: [u64; 1] = [0; 1];
73 let mut c: [u64; 5] = [0; 5];
74
75 read_u64v_le(&mut s, state);
76
77 for round in 0..NROUNDS {
78 for x in 0..5 {
80 c[x] = s[x] ^ s[5 + x] ^ s[10 + x] ^ s[15 + x] ^ s[20 + x];
81 }
82 for x in 0..5 {
83 t[0] = c[M5[x + 4]] ^ c[M5[x + 1]].rotate_left(1);
84 for y in 0..5 {
85 s[y * 5 + x] ^= t[0];
86 }
87 }
88
89 t[0] = s[1];
91 for x in 0..24 {
92 c[0] = s[PIL[x]];
93 s[PIL[x]] = t[0].rotate_left(ROTC[x]);
94 t[0] = c[0];
95 }
96
97 for y in 0..5 {
99 for x in 0..5 {
100 c[x] = s[y * 5 + x];
101 }
102 for x in 0..5 {
103 s[y * 5 + x] = c[x] ^ (!c[M5[x + 1]] & c[M5[x + 2]]);
104 }
105 }
106
107 s[0] ^= RC[round];
109 }
110
111 write_u64v_le(state, &s);
112}
113
114#[derive(Clone)]
118pub(super) struct Engine<const DIGESTLEN: usize, const DSLEN: usize> {
119 state: [u8; B], can_absorb: bool, can_squeeze: bool, offset: usize, }
125
126impl<const DIGESTLEN: usize, const DSLEN: usize> Engine<DIGESTLEN, DSLEN> {
127 fn rate(&self) -> usize {
130 B - (DIGESTLEN * 2)
131 }
132
133 pub const fn new() -> Self {
135 Self {
136 state: [0; B],
137 can_absorb: true,
138 can_squeeze: true,
139 offset: 0,
140 }
141 }
142
143 pub(super) fn finalize(&mut self) {
144 assert!(self.can_absorb);
145
146 fn set_domain_sep(out_len: usize, buf: &mut [u8]) {
147 assert!(!buf.is_empty());
148 if out_len != 0 {
149 buf[0] &= 0xfe;
151 buf[0] |= 0x2;
152 } else {
153 buf[0] |= 0xf;
155 }
156 }
157
158 fn pad_len<const DSLEN: usize>(offset: usize, rate: usize) -> usize {
160 assert!(rate % 8 == 0 && offset % 8 == 0);
161 let r: i64 = rate as i64;
162 let m: i64 = (offset + DSLEN) as i64;
163 let zeros = (((-m - 2) + 2 * r) % r) as usize;
164 assert!((m as usize + zeros + 2) % 8 == 0);
165 (DSLEN + zeros + 2) / 8
166 }
167
168 fn set_pad<const DSLEN: usize>(buf: &mut [u8]) {
169 let offset = DSLEN;
171 let s = offset / 8;
172 let buflen = buf.len();
173 buf[s] |= 1 << (offset % 8);
174 for i in (offset % 8) + 1..8 {
175 buf[s] &= !(1 << i);
176 }
177 for b in buf[s + 1..].iter_mut() {
178 *b = 0;
179 }
180 buf[buflen - 1] |= 0x80;
181 }
182
183 let p_len = pad_len::<DSLEN>(self.offset * 8, self.rate() * 8);
184
185 let mut p = vec::from_elem(0, p_len);
186
187 if DSLEN != 0 {
188 set_domain_sep(DIGESTLEN * 8, &mut p);
189 }
190
191 set_pad::<DSLEN>(&mut p);
192
193 self.process(&p);
194 self.can_absorb = false;
195 }
196
197 pub(super) fn process(&mut self, data: &[u8]) {
198 if !self.can_absorb {
199 panic!("Invalid state, absorb phase already finalized.");
200 }
201
202 let r = self.rate();
203 assert!(self.offset < r);
204
205 let in_len = data.len();
206 let mut in_pos: usize = 0;
207
208 while in_pos < in_len {
210 let offset = self.offset;
211 let nread = cmp::min(r - offset, in_len - in_pos);
212 for i in 0..nread {
213 self.state[offset + i] ^= data[in_pos + i];
214 }
215 in_pos += nread;
216
217 if offset + nread != r {
218 self.offset += nread;
219 break;
220 }
221
222 self.offset = 0;
223 keccak_f(&mut self.state);
224 }
225 }
226
227 pub(super) fn reset(&mut self) {
228 self.can_absorb = true;
229 self.can_squeeze = true;
230 self.offset = 0;
231 zero(&mut self.state);
232 }
233
234 pub(super) fn output(&mut self, out: &mut [u8]) {
235 if !self.can_squeeze {
236 panic!("Nothing left to squeeze.");
237 }
238
239 if self.can_absorb {
240 self.finalize();
241 }
242
243 let r = self.rate();
244 if DIGESTLEN != 0 {
245 assert!(self.offset < DIGESTLEN);
246 } else {
247 assert!(self.offset < r);
249 }
250
251 let in_len = out.len();
252 let mut in_pos: usize = 0;
253
254 while in_pos < in_len {
256 let offset = self.offset % r;
257 let mut nread = cmp::min(r - offset, in_len - in_pos);
258 if DIGESTLEN != 0 {
259 nread = cmp::min(nread, DIGESTLEN - self.offset);
260 }
261
262 out[in_pos..(nread + in_pos)].copy_from_slice(&self.state[offset..(nread + offset)]);
263 in_pos += nread;
264
265 if offset + nread != r {
266 self.offset += nread;
267 break;
268 }
269
270 if DIGESTLEN == 0 {
271 self.offset = 0;
272 } else {
273 self.offset += nread;
274 }
275
276 keccak_f(&mut self.state);
277 }
278
279 if DIGESTLEN != 0 && DIGESTLEN == self.offset {
280 self.can_squeeze = false;
281 }
282 }
283}
284
285macro_rules! sha3_impl {
297 ($C: ident, $context:ident, $digestlength:literal, $doc:expr) => {
298 #[doc=$doc]
299 #[doc = " Algorithm"]
300 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
301 pub struct $C;
302
303 impl $C {
304 pub const OUTPUT_BITS: usize = $digestlength * 8;
306 pub const BLOCK_BYTES: usize = B - ($digestlength * 2);
309
310 pub fn new() -> $context {
312 $context::new()
313 }
314 }
315
316 #[doc=$doc]
317 #[doc = " Context"]
318 #[derive(Clone)]
319 pub struct $context(Engine<$digestlength, 2>);
320
321 impl $context {
322 pub const fn new() -> Self {
324 Self(Engine::new())
325 }
326
327 pub fn update_mut(&mut self, data: &[u8]) {
331 self.0.process(data)
332 }
333
334 pub fn update(mut self, data: &[u8]) -> Self {
336 self.0.process(data);
337 self
338 }
339
340 pub fn finalize_reset(&mut self) -> [u8; $digestlength] {
343 let mut out = [0; $digestlength];
344 self.0.output(&mut out);
345 self.0.reset();
346 out
347 }
348
349 pub fn finalize(mut self) -> [u8; $digestlength] {
355 let mut out = [0; $digestlength];
356 self.0.output(&mut out);
357 out
358 }
359
360 pub fn reset(&mut self) {
362 self.0.reset()
363 }
364 }
365 };
366}
367
368sha3_impl!(Sha3_224, Context224, 28, "SHA3 224");
369sha3_impl!(Sha3_256, Context256, 32, "SHA3 256");
370sha3_impl!(Sha3_384, Context384, 48, "SHA3 384");
371sha3_impl!(Sha3_512, Context512, 64, "SHA3 512");
372
373#[cfg(test)]
374mod tests {
375 use super::super::tests::{test_hashing, Test};
376 use super::*;
377
378 #[test]
379 fn test_sha3_224() {
380 let tests = [
381 Test {
382 input: b"",
383 output: [
384 0x6b, 0x4e, 0x03, 0x42, 0x36, 0x67, 0xdb, 0xb7, 0x3b, 0x6e, 0x15, 0x45, 0x4f,
385 0x0e, 0xb1, 0xab, 0xd4, 0x59, 0x7f, 0x9a, 0x1b, 0x07, 0x8e, 0x3f, 0x5b, 0x5a,
386 0x6b, 0xc7,
387 ],
388 },
389 Test {
390 input: b"The quick brown fox jumps over the lazy dog",
391 output: [
392 0xd1, 0x5d, 0xad, 0xce, 0xaa, 0x4d, 0x5d, 0x7b, 0xb3, 0xb4, 0x8f, 0x44, 0x64,
393 0x21, 0xd5, 0x42, 0xe0, 0x8a, 0xd8, 0x88, 0x73, 0x05, 0xe2, 0x8d, 0x58, 0x33,
394 0x57, 0x95,
395 ],
396 },
397 Test {
398 input: b"The quick brown fox jumps over the lazy dog.",
399 output: [
400 0x2d, 0x07, 0x08, 0x90, 0x38, 0x33, 0xaf, 0xab, 0xdd, 0x23, 0x2a, 0x20, 0x20,
401 0x11, 0x76, 0xe8, 0xb5, 0x8c, 0x5b, 0xe8, 0xa6, 0xfe, 0x74, 0x26, 0x5a, 0xc5,
402 0x4d, 0xb0,
403 ],
404 },
405 ];
406 test_hashing(
407 &tests,
408 Sha3_224,
409 |_| Context224::new(),
410 |ctx, input| ctx.update(input),
411 |ctx, input| ctx.update_mut(input),
412 |ctx| ctx.finalize(),
413 |ctx| ctx.finalize_reset(),
414 |ctx| ctx.reset(),
415 )
416 }
417
418 #[test]
419 fn test_sha3_256() {
420 let tests = [
421 Test {
422 input: b"",
423 output: [
424 0xa7, 0xff, 0xc6, 0xf8, 0xbf, 0x1e, 0xd7, 0x66, 0x51, 0xc1, 0x47, 0x56, 0xa0,
425 0x61, 0xd6, 0x62, 0xf5, 0x80, 0xff, 0x4d, 0xe4, 0x3b, 0x49, 0xfa, 0x82, 0xd8,
426 0x0a, 0x4b, 0x80, 0xf8, 0x43, 0x4a,
427 ],
428 },
429 Test {
430 input: b"The quick brown fox jumps over the lazy dog",
431 output: [
432 0x69, 0x07, 0x0d, 0xda, 0x01, 0x97, 0x5c, 0x8c, 0x12, 0x0c, 0x3a, 0xad, 0xa1,
433 0xb2, 0x82, 0x39, 0x4e, 0x7f, 0x03, 0x2f, 0xa9, 0xcf, 0x32, 0xf4, 0xcb, 0x22,
434 0x59, 0xa0, 0x89, 0x7d, 0xfc, 0x04,
435 ],
436 },
437 Test {
438 input: b"The quick brown fox jumps over the lazy dog.",
439 output: [
440 0xa8, 0x0f, 0x83, 0x9c, 0xd4, 0xf8, 0x3f, 0x6c, 0x3d, 0xaf, 0xc8, 0x7f, 0xea,
441 0xe4, 0x70, 0x04, 0x5e, 0x4e, 0xb0, 0xd3, 0x66, 0x39, 0x7d, 0x5c, 0x6c, 0xe3,
442 0x4b, 0xa1, 0x73, 0x9f, 0x73, 0x4d,
443 ],
444 },
445 ];
446 test_hashing(
447 &tests,
448 Sha3_256,
449 |_| Context256::new(),
450 |ctx, input| ctx.update(input),
451 |ctx, input| ctx.update_mut(input),
452 |ctx| ctx.finalize(),
453 |ctx| ctx.finalize_reset(),
454 |ctx| ctx.reset(),
455 )
456 }
457
458 #[test]
459 fn test_sha3_384() {
460 let tests = [
461 Test {
462 input: b"",
463 output: [
464 0x0c, 0x63, 0xa7, 0x5b, 0x84, 0x5e, 0x4f, 0x7d, 0x01, 0x10, 0x7d, 0x85, 0x2e,
465 0x4c, 0x24, 0x85, 0xc5, 0x1a, 0x50, 0xaa, 0xaa, 0x94, 0xfc, 0x61, 0x99, 0x5e,
466 0x71, 0xbb, 0xee, 0x98, 0x3a, 0x2a, 0xc3, 0x71, 0x38, 0x31, 0x26, 0x4a, 0xdb,
467 0x47, 0xfb, 0x6b, 0xd1, 0xe0, 0x58, 0xd5, 0xf0, 0x04,
468 ],
469 },
470 Test {
471 input: b"The quick brown fox jumps over the lazy dog",
472 output: [
473 0x70, 0x63, 0x46, 0x5e, 0x08, 0xa9, 0x3b, 0xce, 0x31, 0xcd, 0x89, 0xd2, 0xe3,
474 0xca, 0x8f, 0x60, 0x24, 0x98, 0x69, 0x6e, 0x25, 0x35, 0x92, 0xed, 0x26, 0xf0,
475 0x7b, 0xf7, 0xe7, 0x03, 0xcf, 0x32, 0x85, 0x81, 0xe1, 0x47, 0x1a, 0x7b, 0xa7,
476 0xab, 0x11, 0x9b, 0x1a, 0x9e, 0xbd, 0xf8, 0xbe, 0x41,
477 ],
478 },
479 Test {
480 input: b"The quick brown fox jumps over the lazy dog.",
481 output: [
482 0x1a, 0x34, 0xd8, 0x16, 0x95, 0xb6, 0x22, 0xdf, 0x17, 0x8b, 0xc7, 0x4d, 0xf7,
483 0x12, 0x4f, 0xe1, 0x2f, 0xac, 0x0f, 0x64, 0xba, 0x52, 0x50, 0xb7, 0x8b, 0x99,
484 0xc1, 0x27, 0x3d, 0x4b, 0x08, 0x01, 0x68, 0xe1, 0x06, 0x52, 0x89, 0x4e, 0xca,
485 0xd5, 0xf1, 0xf4, 0xd5, 0xb9, 0x65, 0x43, 0x7f, 0xb9,
486 ],
487 },
488 ];
489 test_hashing(
490 &tests,
491 Sha3_384,
492 |_| Context384::new(),
493 |ctx, input| ctx.update(input),
494 |ctx, input| ctx.update_mut(input),
495 |ctx| ctx.finalize(),
496 |ctx| ctx.finalize_reset(),
497 |ctx| ctx.reset(),
498 )
499 }
500
501 #[test]
502 fn test_sha3_512() {
503 let tests = [
504 Test {
505 input: b"",
506 output: [
507 0xa6, 0x9f, 0x73, 0xcc, 0xa2, 0x3a, 0x9a, 0xc5, 0xc8, 0xb5, 0x67, 0xdc, 0x18,
508 0x5a, 0x75, 0x6e, 0x97, 0xc9, 0x82, 0x16, 0x4f, 0xe2, 0x58, 0x59, 0xe0, 0xd1,
509 0xdc, 0xc1, 0x47, 0x5c, 0x80, 0xa6, 0x15, 0xb2, 0x12, 0x3a, 0xf1, 0xf5, 0xf9,
510 0x4c, 0x11, 0xe3, 0xe9, 0x40, 0x2c, 0x3a, 0xc5, 0x58, 0xf5, 0x00, 0x19, 0x9d,
511 0x95, 0xb6, 0xd3, 0xe3, 0x01, 0x75, 0x85, 0x86, 0x28, 0x1d, 0xcd, 0x26,
512 ],
513 },
514 Test {
515 input: b"The quick brown fox jumps over the lazy dog",
516 output: [
517 0x01, 0xde, 0xdd, 0x5d, 0xe4, 0xef, 0x14, 0x64, 0x24, 0x45, 0xba, 0x5f, 0x5b,
518 0x97, 0xc1, 0x5e, 0x47, 0xb9, 0xad, 0x93, 0x13, 0x26, 0xe4, 0xb0, 0x72, 0x7c,
519 0xd9, 0x4c, 0xef, 0xc4, 0x4f, 0xff, 0x23, 0xf0, 0x7b, 0xf5, 0x43, 0x13, 0x99,
520 0x39, 0xb4, 0x91, 0x28, 0xca, 0xf4, 0x36, 0xdc, 0x1b, 0xde, 0xe5, 0x4f, 0xcb,
521 0x24, 0x02, 0x3a, 0x08, 0xd9, 0x40, 0x3f, 0x9b, 0x4b, 0xf0, 0xd4, 0x50,
522 ],
523 },
524 Test {
525 input: b"The quick brown fox jumps over the lazy dog.",
526 output: [
527 0x18, 0xf4, 0xf4, 0xbd, 0x41, 0x96, 0x03, 0xf9, 0x55, 0x38, 0x83, 0x70, 0x03,
528 0xd9, 0xd2, 0x54, 0xc2, 0x6c, 0x23, 0x76, 0x55, 0x65, 0x16, 0x22, 0x47, 0x48,
529 0x3f, 0x65, 0xc5, 0x03, 0x03, 0x59, 0x7b, 0xc9, 0xce, 0x4d, 0x28, 0x9f, 0x21,
530 0xd1, 0xc2, 0xf1, 0xf4, 0x58, 0x82, 0x8e, 0x33, 0xdc, 0x44, 0x21, 0x00, 0x33,
531 0x1b, 0x35, 0xe7, 0xeb, 0x03, 0x1b, 0x5d, 0x38, 0xba, 0x64, 0x60, 0xf8,
532 ],
533 },
534 ];
535 test_hashing(
536 &tests,
537 Sha3_512,
538 |_| Context512::new(),
539 |ctx, input| ctx.update(input),
540 |ctx, input| ctx.update_mut(input),
541 |ctx| ctx.finalize(),
542 |ctx| ctx.finalize_reset(),
543 |ctx| ctx.reset(),
544 )
545 }
546}