sigma_proofs/duplex_sponge/
keccak.rs1use crate::duplex_sponge::DuplexSpongeInterface;
7use alloc::vec::Vec;
8use zerocopy::IntoBytes;
9
10const RATE: usize = 136;
11const LENGTH: usize = 136 + 64;
12
13#[derive(Clone, Default)]
15pub struct KeccakPermutationState([u64; LENGTH / 8]);
16
17impl KeccakPermutationState {
18 pub fn new(iv: [u8; 64]) -> Self {
19 let mut state = Self::default();
20 state.as_mut()[RATE..RATE + 64].copy_from_slice(&iv);
21 state
22 }
23
24 pub fn permute(&mut self) {
25 keccak::f1600(&mut self.0);
26 }
27}
28
29impl AsRef<[u8]> for KeccakPermutationState {
30 fn as_ref(&self) -> &[u8] {
31 self.0.as_bytes()
32 }
33}
34
35impl AsMut<[u8]> for KeccakPermutationState {
36 fn as_mut(&mut self) -> &mut [u8] {
37 self.0.as_mut_bytes()
38 }
39}
40
41#[derive(Clone)]
43pub struct KeccakDuplexSponge {
44 state: KeccakPermutationState,
45 absorb_index: usize,
46 squeeze_index: usize,
47}
48
49impl KeccakDuplexSponge {
50 pub fn new(iv: [u8; 64]) -> Self {
51 let state = KeccakPermutationState::new(iv);
52 KeccakDuplexSponge {
53 state,
54 absorb_index: 0,
55 squeeze_index: RATE,
56 }
57 }
58}
59
60impl DuplexSpongeInterface for KeccakDuplexSponge {
61 fn new(iv: [u8; 64]) -> Self {
62 KeccakDuplexSponge::new(iv)
63 }
64
65 fn absorb(&mut self, mut input: &[u8]) {
66 self.squeeze_index = RATE;
67
68 while !input.is_empty() {
69 if self.absorb_index == RATE {
70 self.state.permute();
71 self.absorb_index = 0;
72 }
73
74 let chunk_size = usize::min(RATE - self.absorb_index, input.len());
75 let dest = &mut self.state.as_mut()[self.absorb_index..self.absorb_index + chunk_size];
76 dest.copy_from_slice(&input[..chunk_size]);
77 self.absorb_index += chunk_size;
78 input = &input[chunk_size..];
79 }
80 }
81
82 fn squeeze(&mut self, mut length: usize) -> Vec<u8> {
83 let mut output = Vec::new();
84 while length != 0 {
85 if self.squeeze_index == RATE {
86 self.state.permute();
87 self.squeeze_index = 0;
88 self.absorb_index = 0;
89 }
90
91 let chunk_size = usize::min(RATE - self.squeeze_index, length);
92 output.extend_from_slice(
93 &self.state.as_mut()[self.squeeze_index..self.squeeze_index + chunk_size],
94 );
95 self.squeeze_index += chunk_size;
96 length -= chunk_size;
97 }
98 output
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105 use crate::duplex_sponge::DuplexSpongeInterface;
106 use hex_literal::hex;
107
108 #[test]
109 fn test_associativity_of_absorb() {
110 let expected_output =
111 hex!("efc1c34f94c0d9cfe051561f8206543056ce660fd17834b2eeb9431a4c65bc77");
112 let tag = *b"absorb-associativity-domain-----absorb-associativity-domain-----";
113
114 let mut sponge1 = KeccakDuplexSponge::new(tag);
116 sponge1.absorb(b"hello world");
117 let out1 = sponge1.squeeze(32);
118
119 let mut sponge2 = KeccakDuplexSponge::new(tag);
121 sponge2.absorb(b"hello");
122 sponge2.absorb(b" world");
123 let out2 = sponge2.squeeze(32);
124
125 assert_eq!(out1, expected_output);
126 assert_eq!(out2, expected_output);
127 }
128}