1use super::{Digest, Xof};
18
19const RHO: [u32; 25] = [
22 0, 1, 62, 28, 27, 36, 44, 6, 55, 20, 3, 10, 43, 25, 39, 41, 45, 15, 21, 8, 18, 2, 61, 56, 14,
23];
24
25const PI: [usize; 25] = [
28 0, 10, 20, 5, 15, 16, 1, 11, 21, 6, 7, 17, 2, 12, 22, 23, 8, 18, 3, 13, 14, 24, 9, 19, 4,
29];
30
31const RC: [u64; 24] = [
33 0x0000_0000_0000_0001,
34 0x0000_0000_0000_8082,
35 0x8000_0000_0000_808A,
36 0x8000_0000_8000_8000,
37 0x0000_0000_0000_808B,
38 0x0000_0000_8000_0001,
39 0x8000_0000_8000_8081,
40 0x8000_0000_0000_8009,
41 0x0000_0000_0000_008A,
42 0x0000_0000_0000_0088,
43 0x0000_0000_8000_8009,
44 0x0000_0000_8000_000A,
45 0x0000_0000_8000_808B,
46 0x8000_0000_0000_008B,
47 0x8000_0000_0000_8089,
48 0x8000_0000_0000_8003,
49 0x8000_0000_0000_8002,
50 0x8000_0000_0000_0080,
51 0x0000_0000_0000_800A,
52 0x8000_0000_8000_000A,
53 0x8000_0000_8000_8081,
54 0x8000_0000_0000_8080,
55 0x0000_0000_8000_0001,
56 0x8000_0000_8000_8008,
57];
58
59#[inline]
61fn keccak_f1600(state: &mut [u64; 25]) {
62 #[cfg(target_arch = "aarch64")]
63 {
64 if std::arch::is_aarch64_feature_detected!("sha3") {
65 unsafe {
67 return keccak_f1600_sha3(state);
68 }
69 }
70 }
71 keccak_f1600_soft(state);
72}
73
74fn keccak_f1600_soft(state: &mut [u64; 25]) {
76 for &rc in &RC {
77 let mut c = [0u64; 5];
79 for x in 0..5 {
80 c[x] = state[x] ^ state[x + 5] ^ state[x + 10] ^ state[x + 15] ^ state[x + 20];
81 }
82
83 let mut d = [0u64; 5];
85 for x in 0..5 {
86 d[x] = c[(x + 4) % 5] ^ c[(x + 1) % 5].rotate_left(1);
87 }
88
89 for y in 0..5 {
91 for x in 0..5 {
92 state[x + 5 * y] ^= d[x];
93 }
94 }
95
96 let mut b = [0u64; 25];
98 for i in 0..25 {
99 b[PI[i]] = state[i].rotate_left(RHO[i]);
100 }
101
102 for y in 0..5 {
104 let row = 5 * y;
105 for x in 0..5 {
106 state[row + x] = b[row + x] ^ ((!b[row + ((x + 1) % 5)]) & b[row + ((x + 2) % 5)]);
107 }
108 }
109
110 state[0] ^= rc;
112 }
113}
114
115#[cfg(target_arch = "aarch64")]
122#[target_feature(enable = "sha3")]
123unsafe fn keccak_f1600_sha3(state: &mut [u64; 25]) {
124 use core::arch::aarch64::*;
125
126 #[inline(always)]
128 unsafe fn u64x2(a: u64, b: u64) -> uint64x2_t {
129 vcombine_u64(vdup_n_u64(a), vdup_n_u64(b))
130 }
131
132 for &rc in &RC {
133 let c01 = {
139 let t = veor3q_u64(
140 u64x2(state[0], state[1]),
141 u64x2(state[5], state[6]),
142 u64x2(state[10], state[11]),
143 );
144 veor3q_u64(t, u64x2(state[15], state[16]), u64x2(state[20], state[21]))
145 };
146 let c23 = {
148 let t = veor3q_u64(
149 u64x2(state[2], state[3]),
150 u64x2(state[7], state[8]),
151 u64x2(state[12], state[13]),
152 );
153 veor3q_u64(t, u64x2(state[17], state[18]), u64x2(state[22], state[23]))
154 };
155 let c4 = state[4] ^ state[9] ^ state[14] ^ state[19] ^ state[24];
157
158 let c0 = vgetq_lane_u64::<0>(c01);
159 let c1 = vgetq_lane_u64::<1>(c01);
160 let c2 = vgetq_lane_u64::<0>(c23);
161 let c3 = vgetq_lane_u64::<1>(c23);
162
163 let d01 = vrax1q_u64(u64x2(c4, c0), u64x2(c1, c2));
168 let d23 = vrax1q_u64(u64x2(c1, c2), u64x2(c3, c4));
169 let d4 = c3 ^ c0.rotate_left(1); let d = [
172 vgetq_lane_u64::<0>(d01),
173 vgetq_lane_u64::<1>(d01),
174 vgetq_lane_u64::<0>(d23),
175 vgetq_lane_u64::<1>(d23),
176 d4,
177 ];
178
179 for y in 0..5 {
181 for x in 0..5 {
182 state[x + 5 * y] ^= d[x];
183 }
184 }
185
186 let mut b = [0u64; 25];
191 for i in 0..25 {
192 b[PI[i]] = state[i].rotate_left(RHO[i]);
193 }
194
195 for y in 0..5 {
201 let r = y * 5;
202 let chi01 = vbcaxq_u64(
204 u64x2(b[r], b[r + 1]),
205 u64x2(b[r + 2], b[r + 3]),
206 u64x2(b[r + 1], b[r + 2]),
207 );
208 let chi23 = vbcaxq_u64(
210 u64x2(b[r + 2], b[r + 3]),
211 u64x2(b[r + 4], b[r]),
212 u64x2(b[r + 3], b[r + 4]),
213 );
214 state[r] = vgetq_lane_u64::<0>(chi01);
215 state[r + 1] = vgetq_lane_u64::<1>(chi01);
216 state[r + 2] = vgetq_lane_u64::<0>(chi23);
217 state[r + 3] = vgetq_lane_u64::<1>(chi23);
218 state[r + 4] = b[r + 4] ^ (b[r + 1] & !b[r]);
220 }
221
222 state[0] ^= rc;
224 }
225}
226
227#[inline]
228fn absorb_block<const RATE: usize>(state: &mut [u64; 25], block: &[u8; RATE]) {
229 debug_assert_eq!(RATE % 8, 0, "Keccak rate must be lane-aligned");
230 let lanes = RATE / 8;
231 let mut i = 0usize;
232 while i < lanes {
233 let lane = u64::from_le_bytes(block[i * 8..i * 8 + 8].try_into().unwrap());
234 state[i] ^= lane;
235 i += 1;
236 }
237 keccak_f1600(state);
238}
239
240#[derive(Clone)]
241struct Keccak<const RATE: usize> {
242 state: [u64; 25],
243 block: [u8; RATE],
244 pos: usize,
245}
246
247#[derive(Clone)]
248struct KeccakSponge<const RATE: usize> {
249 state: [u64; 25],
250 block: [u8; RATE],
251 offset: usize,
252}
253
254#[derive(Clone)]
255enum XofState<const RATE: usize> {
256 Absorbing(Keccak<RATE>),
257 Squeezing(KeccakSponge<RATE>),
258}
259
260impl<const RATE: usize> XofState<RATE> {
261 fn zeroize(&mut self) {
262 match self {
263 XofState::Absorbing(inner) => {
264 crate::ct::zeroize_slice(inner.state.as_mut_slice());
265 crate::ct::zeroize_slice(inner.block.as_mut_slice());
266 inner.pos = 0;
267 }
268 XofState::Squeezing(sponge) => {
269 crate::ct::zeroize_slice(sponge.state.as_mut_slice());
270 crate::ct::zeroize_slice(sponge.block.as_mut_slice());
271 sponge.offset = 0;
272 }
273 }
274 }
275}
276
277#[inline]
278fn state_to_rate_bytes<const RATE: usize>(state: &[u64; 25]) -> [u8; RATE] {
279 let mut rate_bytes = [0u8; RATE];
280 let lanes = RATE / 8;
281 let mut i = 0usize;
282 while i < lanes {
283 rate_bytes[i * 8..i * 8 + 8].copy_from_slice(&state[i].to_le_bytes());
284 i += 1;
285 }
286 rate_bytes
287}
288
289impl<const RATE: usize> Keccak<RATE> {
290 fn new() -> Self {
291 Self {
292 state: [0u64; 25],
293 block: [0u8; RATE],
294 pos: 0,
295 }
296 }
297
298 fn update(&mut self, mut data: &[u8]) {
299 while !data.is_empty() {
300 let take = (RATE - self.pos).min(data.len());
301 self.block[self.pos..self.pos + take].copy_from_slice(&data[..take]);
302 self.pos += take;
303 data = &data[take..];
304
305 if self.pos == RATE {
306 absorb_block(&mut self.state, &self.block);
307 self.block = [0u8; RATE];
308 self.pos = 0;
309 }
310 }
311 }
312
313 fn finalize_sponge(mut self, suffix: u8) -> KeccakSponge<RATE> {
314 self.block[self.pos] ^= suffix;
315 self.block[RATE - 1] ^= 0x80;
316 absorb_block(&mut self.state, &self.block);
317 KeccakSponge {
318 block: state_to_rate_bytes(&self.state),
319 state: self.state,
320 offset: 0,
321 }
322 }
323
324 fn finalize<const OUT: usize>(self) -> [u8; OUT] {
325 let mut sponge = self.finalize_sponge(0x06);
326 let mut out = [0u8; OUT];
327 sponge.squeeze(&mut out);
328 out
329 }
330
331 fn finalize_into_reset<const OUT: usize>(&mut self, suffix: u8, out: &mut [u8; OUT]) {
332 self.block[self.pos] ^= suffix;
333 self.block[RATE - 1] ^= 0x80;
334 absorb_block(&mut self.state, &self.block);
335
336 let mut sponge: KeccakSponge<RATE> = KeccakSponge {
337 block: state_to_rate_bytes(&self.state),
338 state: self.state,
339 offset: 0,
340 };
341 sponge.squeeze(out);
342
343 crate::ct::zeroize_slice(sponge.state.as_mut_slice());
344 crate::ct::zeroize_slice(sponge.block.as_mut_slice());
345
346 crate::ct::zeroize_slice(self.state.as_mut_slice());
347 crate::ct::zeroize_slice(self.block.as_mut_slice());
348 self.pos = 0;
349 }
350}
351
352impl<const RATE: usize> KeccakSponge<RATE> {
353 fn squeeze(&mut self, out: &mut [u8]) {
354 let mut produced = 0usize;
355 while produced < out.len() {
356 if self.offset == RATE {
357 keccak_f1600(&mut self.state);
358 self.block = state_to_rate_bytes(&self.state);
359 self.offset = 0;
360 }
361
362 let take = (out.len() - produced).min(RATE - self.offset);
363 out[produced..produced + take]
364 .copy_from_slice(&self.block[self.offset..self.offset + take]);
365 produced += take;
366 self.offset += take;
367 }
368 }
369}
370
371macro_rules! define_sha3 {
372 ($name:ident, $rate:expr, $out_len:expr) => {
373 #[derive(Clone)]
374 pub struct $name {
375 inner: Keccak<$rate>,
376 }
377
378 impl Default for $name {
379 fn default() -> Self {
380 Self::new()
381 }
382 }
383
384 impl $name {
385 pub const BLOCK_LEN: usize = $rate;
386 pub const OUTPUT_LEN: usize = $out_len;
387
388 #[must_use]
389 pub fn new() -> Self {
390 Self {
391 inner: Keccak::new(),
392 }
393 }
394
395 pub fn update(&mut self, data: &[u8]) {
396 self.inner.update(data);
397 }
398
399 #[must_use]
400 pub fn finalize(self) -> [u8; $out_len] {
401 self.inner.finalize()
402 }
403
404 #[must_use]
405 pub fn digest(data: &[u8]) -> [u8; $out_len] {
406 let mut h = Self::new();
407 h.update(data);
408 h.finalize()
409 }
410 }
411
412 impl Digest for $name {
413 const BLOCK_LEN: usize = $rate;
414 const OUTPUT_LEN: usize = $out_len;
415
416 fn new() -> Self {
417 $name {
418 inner: Keccak::new(),
419 }
420 }
421
422 fn update(&mut self, data: &[u8]) {
423 self.inner.update(data);
424 }
425
426 fn finalize_into(self, out: &mut [u8]) {
427 assert_eq!(out.len(), $out_len, "wrong digest length");
428 let digest = self.inner.finalize::<$out_len>();
429 out.copy_from_slice(&digest);
430 }
431
432 fn finalize_reset(&mut self, out: &mut [u8]) {
433 let out: &mut [u8; $out_len] = out.try_into().expect("wrong digest length");
434 self.inner.finalize_into_reset::<$out_len>(0x06, out);
435 }
436
437 fn zeroize(&mut self) {
438 crate::ct::zeroize_slice(self.inner.state.as_mut_slice());
439 crate::ct::zeroize_slice(self.inner.block.as_mut_slice());
440 self.inner.pos = 0;
441 }
442 }
443 };
444}
445
446define_sha3!(Sha3_224, 144, 28);
447define_sha3!(Sha3_256, 136, 32);
448define_sha3!(Sha3_384, 104, 48);
449define_sha3!(Sha3_512, 72, 64);
450
451macro_rules! define_shake {
452 ($name:ident, $rate:expr) => {
453 #[derive(Clone)]
454 pub struct $name {
455 inner: XofState<$rate>,
456 }
457
458 impl Default for $name {
459 fn default() -> Self {
460 Self::new()
461 }
462 }
463
464 impl $name {
465 pub const BLOCK_LEN: usize = $rate;
466
467 #[must_use]
468 pub fn new() -> Self {
469 Self {
470 inner: XofState::Absorbing(Keccak::new()),
471 }
472 }
473
474 pub fn digest(data: &[u8], out: &mut [u8]) {
475 let mut xof = Self::new();
476 xof.update(data);
477 xof.squeeze(out);
478 }
479 }
480
481 impl Xof for $name {
482 fn update(&mut self, data: &[u8]) {
483 match &mut self.inner {
484 XofState::Absorbing(inner) => inner.update(data),
485 XofState::Squeezing(_) => panic!("cannot absorb after squeezing"),
486 }
487 }
488
489 fn squeeze(&mut self, out: &mut [u8]) {
490 if let XofState::Absorbing(_) = self.inner {
491 let prev =
492 core::mem::replace(&mut self.inner, XofState::Absorbing(Keccak::new()));
493 let sponge = match prev {
494 XofState::Absorbing(inner) => inner.finalize_sponge(0x1f),
495 XofState::Squeezing(sponge) => sponge,
496 };
497 self.inner = XofState::Squeezing(sponge);
498 }
499
500 match &mut self.inner {
501 XofState::Absorbing(_) => unreachable!(),
502 XofState::Squeezing(sponge) => sponge.squeeze(out),
503 }
504 }
505 }
506
507 impl Drop for $name {
508 fn drop(&mut self) {
509 self.inner.zeroize();
510 }
511 }
512 };
513}
514
515define_shake!(Shake128, 168);
516define_shake!(Shake256, 136);
517
518#[cfg(test)]
519mod tests {
520 use super::*;
521
522 fn hex(bytes: &[u8]) -> String {
523 let mut out = String::with_capacity(bytes.len() * 2);
524 for b in bytes {
525 use core::fmt::Write;
526 let _ = write!(&mut out, "{b:02x}");
527 }
528 out
529 }
530
531 #[test]
532 fn sha3_224_empty() {
533 assert_eq!(
534 hex(&Sha3_224::digest(b"")),
535 "6b4e03423667dbb73b6e15454f0eb1ab".to_owned() + "d4597f9a1b078e3f5b5a6bc7"
536 );
537 }
538
539 #[test]
540 fn sha3_256_empty() {
541 assert_eq!(
542 hex(&Sha3_256::digest(b"")),
543 "a7ffc6f8bf1ed76651c14756a061d662".to_owned() + "f580ff4de43b49fa82d80a4b80f8434a"
544 );
545 }
546
547 #[test]
548 fn sha3_256_abc_streaming() {
549 let mut h = Sha3_256::new();
550 h.update(b"a");
551 h.update(b"b");
552 h.update(b"c");
553 assert_eq!(
554 hex(&h.finalize()),
555 "3a985da74fe225b2045c172d6bd390bd".to_owned() + "855f086e3e9d525b46bfe24511431532"
556 );
557 }
558
559 #[test]
560 fn sha3_384_empty() {
561 assert_eq!(
562 hex(&Sha3_384::digest(b"")),
563 "0c63a75b845e4f7d01107d852e4c2485".to_owned()
564 + "c51a50aaaa94fc61995e71bbee983a2a"
565 + "c3713831264adb47fb6bd1e058d5f004"
566 );
567 }
568
569 #[test]
570 fn sha3_512_empty() {
571 assert_eq!(
572 hex(&Sha3_512::digest(b"")),
573 "a69f73cca23a9ac5c8b567dc185a756e".to_owned()
574 + "97c982164fe25859e0d1dcc1475c80a6"
575 + "15b2123af1f5f94c11e3e9402c3ac558"
576 + "f500199d95b6d3e301758586281dcd26"
577 );
578 }
579
580 #[test]
581 fn shake128_empty_32() {
582 let mut out = [0u8; 32];
583 Shake128::digest(b"", &mut out);
584 assert_eq!(
585 hex(&out),
586 "7f9c2ba4e88f827d616045507605853e".to_owned() + "d73b8093f6efbc88eb1a6eacfa66ef26"
587 );
588 }
589
590 #[test]
591 fn shake128_abc_streaming_32() {
592 let mut xof = Shake128::new();
593 xof.update(b"a");
594 xof.update(b"b");
595 xof.update(b"c");
596 let mut out = [0u8; 32];
597 xof.squeeze(&mut out);
598 assert_eq!(
599 hex(&out),
600 "5881092dd818bf5cf8a3ddb793fbcba7".to_owned() + "4097d5c526a6d35f97b83351940f2cc8"
601 );
602 }
603
604 #[test]
605 fn shake128_chunked_squeeze_matches_one_shot() {
606 let mut one_shot = Shake128::new();
607 one_shot.update(b"abc");
608 let mut full = [0u8; 64];
609 one_shot.squeeze(&mut full);
610
611 let mut chunked = Shake128::new();
612 chunked.update(b"abc");
613 let mut left = [0u8; 32];
614 let mut right = [0u8; 32];
615 chunked.squeeze(&mut left);
616 chunked.squeeze(&mut right);
617
618 assert_eq!([left.as_slice(), right.as_slice()].concat(), full);
619 }
620
621 #[test]
622 fn shake256_empty_64() {
623 let mut out = [0u8; 64];
624 Shake256::digest(b"", &mut out);
625 assert_eq!(
626 hex(&out),
627 "46b9dd2b0ba88d13233b3feb743eeb24".to_owned()
628 + "3fcd52ea62b81b82b50c27646ed5762f"
629 + "d75dc4ddd8c0f200cb05019d67b592f6"
630 + "fc821c49479ab48640292eacb3b7c4be"
631 );
632 }
633
634 #[test]
635 fn sha3_224_matches_openssl() {
636 let msg = b"The quick brown fox jumps over the lazy dog";
637 let Some(expected) = crate::test_utils::run_openssl(&["dgst", "-sha3-224", "-binary"], msg)
638 else {
639 return;
640 };
641 assert_eq!(Sha3_224::digest(msg).as_slice(), expected.as_slice());
642 }
643
644 #[test]
645 fn sha3_256_matches_openssl() {
646 let msg = b"The quick brown fox jumps over the lazy dog";
647 let Some(expected) = crate::test_utils::run_openssl(&["dgst", "-sha3-256", "-binary"], msg)
648 else {
649 return;
650 };
651 assert_eq!(Sha3_256::digest(msg).as_slice(), expected.as_slice());
652 }
653
654 #[test]
655 fn sha3_384_matches_openssl() {
656 let msg = b"The quick brown fox jumps over the lazy dog";
657 let Some(expected) = crate::test_utils::run_openssl(&["dgst", "-sha3-384", "-binary"], msg)
658 else {
659 return;
660 };
661 assert_eq!(Sha3_384::digest(msg).as_slice(), expected.as_slice());
662 }
663
664 #[test]
665 fn sha3_512_matches_openssl() {
666 let msg = b"The quick brown fox jumps over the lazy dog";
667 let Some(expected) = crate::test_utils::run_openssl(&["dgst", "-sha3-512", "-binary"], msg)
668 else {
669 return;
670 };
671 assert_eq!(Sha3_512::digest(msg).as_slice(), expected.as_slice());
672 }
673}