Skip to main content

crypto/sha3/
shake256.rs

1use super::keccak::Keccak;
2use crate::{Hash, Hasher, Xof, bytes::Bytes};
3
4pub(crate) const SHAKE256_RATE: usize = 136;
5const CSHAKE256_DOMAIN_SEPARATOR: u8 = 0x04;
6const SHAKE256_DOMAIN_SEPARATOR: u8 = 0x1f;
7
8// fixed-size buffer to avoid allocations when encoding inputs
9type EncodedBytes = Bytes<9>;
10
11#[derive(Clone)]
12#[cfg_attr(feature = "zeroize", derive(zeroize::Zeroize, zeroize::ZeroizeOnDrop))]
13pub struct Shake256 {
14    keccak: Keccak<24>,
15}
16
17impl Shake256 {
18    #[inline]
19    pub fn hash(data: &[u8], output: &mut [u8]) {
20        let mut hasher = Shake256::new();
21        hasher.absorb(data);
22        hasher.squeeze(output);
23    }
24
25    #[inline]
26    pub fn new() -> Self {
27        return Shake256 {
28            keccak: Keccak::new(SHAKE256_RATE, SHAKE256_DOMAIN_SEPARATOR),
29        };
30    }
31}
32
33impl Xof for Shake256 {
34    #[inline]
35    fn absorb(&mut self, data: &[u8]) {
36        self.keccak.absorb(data);
37    }
38
39    #[inline]
40    fn squeeze(&mut self, out: &mut [u8]) {
41        self.keccak.squeeze(out);
42    }
43}
44
45impl Hasher for Shake256 {
46    const BLOCK_SIZE: usize = SHAKE256_RATE;
47    const OUTPUT_SIZE: usize = 64;
48
49    #[inline]
50    fn new() -> Self {
51        return Shake256::new();
52    }
53
54    #[inline]
55    fn update(&mut self, data: &[u8]) {
56        self.absorb(data);
57    }
58
59    #[inline]
60    fn sum(mut self) -> Hash {
61        let mut hash = Hash::with_length(Self::OUTPUT_SIZE);
62        self.squeeze(hash.as_mut());
63        return hash;
64    }
65}
66
67#[derive(Clone)]
68#[cfg_attr(feature = "zeroize", derive(zeroize::Zeroize, zeroize::ZeroizeOnDrop))]
69pub struct CShake256 {
70    keccak: Keccak<24>,
71}
72
73impl CShake256 {
74    #[inline]
75    pub fn hash(data: &[u8], function_name: &[u8], customization: &[u8], output: &mut [u8]) {
76        let mut xof = CShake256::new(function_name, customization);
77        xof.absorb(data);
78        xof.squeeze(output);
79    }
80
81    #[inline]
82    pub fn new(function_name: &[u8], customization: &[u8]) -> Self {
83        if function_name.is_empty() && customization.is_empty() {
84            return CShake256 {
85                keccak: Keccak::new(SHAKE256_RATE, SHAKE256_DOMAIN_SEPARATOR),
86            };
87        }
88
89        let mut keccak = Keccak::new(SHAKE256_RATE, CSHAKE256_DOMAIN_SEPARATOR);
90
91        // absorb bytepad(encode_string(N) || encode_string(S), w)
92
93        // bytepad: left_encode(w)
94        let enc_w = left_encode(SHAKE256_RATE);
95        keccak.absorb(enc_w.as_ref());
96
97        // encode_string(N): left_encode(bitlen(N)) || N
98        let enc_n = left_encode(function_name.len() * 8);
99        keccak.absorb(enc_n.as_ref());
100        keccak.absorb(function_name);
101
102        // encode_string(S): left_encode(bitlen(S)) || S
103        let enc_s = left_encode(customization.len() * 8);
104        keccak.absorb(enc_s.as_ref());
105        keccak.absorb(customization);
106
107        // bytepad: zero-pad to block boundary
108        let total = enc_w.len() + enc_n.len() + function_name.len() + enc_s.len() + customization.len();
109        let pad = (SHAKE256_RATE - (total % SHAKE256_RATE)) % SHAKE256_RATE;
110        if pad > 0 {
111            let zeros = [0u8; SHAKE256_RATE];
112            keccak.absorb(&zeros[..pad]);
113        }
114
115        return CShake256 {
116            keccak,
117        };
118    }
119}
120
121impl Xof for CShake256 {
122    #[inline]
123    fn absorb(&mut self, data: &[u8]) {
124        self.keccak.absorb(data);
125    }
126
127    #[inline]
128    fn squeeze(&mut self, out: &mut [u8]) {
129        self.keccak.squeeze(out);
130    }
131}
132
133// SP 800-185 encoding helpers
134
135#[inline]
136pub(crate) fn left_encode(x: usize) -> EncodedBytes {
137    let bytes = x.to_be_bytes();
138    let first_non_zero = bytes.iter().position(|&b| b != 0).unwrap_or(bytes.len() - 1);
139    let n = bytes.len() - first_non_zero;
140
141    let mut out = Bytes::new();
142    out.push(n as u8);
143    out.append(&bytes[first_non_zero..]);
144    return out;
145}
146
147#[inline]
148pub(crate) fn right_encode(x: usize) -> EncodedBytes {
149    let mut bytes = left_encode(x);
150    let out = bytes.as_mut();
151    let n = out[0];
152    out[0] = out[1];
153    for i in 1..(n as usize) {
154        out[i] = out[i + 1];
155    }
156    out[n as usize] = n;
157    return bytes;
158}
159
160// #[inline]
161// pub(crate) fn encode_string(s: &[u8]) -> Vec<u8> {
162//     let encoded = left_encode(s.len() * 8);
163//     let mut out = Vec::with_capacity(s.len() + encoded.len());
164//     out.extend_from_slice(encoded.as_ref());
165//     out.extend_from_slice(s);
166//     return out;
167// }
168
169// #[inline]
170// pub(crate) fn bytepad(x: &[u8], w: usize) -> Vec<u8> {
171//     let encoded = left_encode(w);
172//     // the length of left_encode(w) || X
173//     let wx_length = encoded.len() + x.len();
174//     let pad_length = (w - (wx_length % w)) % w;
175
176//     let mut out = Vec::with_capacity(wx_length + pad_length);
177//     out.extend_from_slice(encoded.as_ref());
178//     out.extend_from_slice(x);
179//     out.resize(out.len() + pad_length, 0);
180//     return out;
181// }
182
183#[cfg(test)]
184mod tests {
185    use super::{CShake256, Shake256};
186    use crate::{Hasher, Xof};
187
188    // ── Shake256 vectors ──────────────────────────────────────────────────────
189
190    fn vectors_shake256() -> Vec<(Vec<u8>, usize, &'static str)> {
191        vec![
192            (
193                b"".to_vec(),
194                64,
195                "46b9dd2b0ba88d13233b3feb743eeb243fcd52ea62b81b82b50c27646ed5762fd75dc4ddd8c0f200cb05019d67b592f6fc821c49479ab48640292eacb3b7c4be",
196            ),
197            (
198                b"".to_vec(),
199                128,
200                "46b9dd2b0ba88d13233b3feb743eeb243fcd52ea62b81b82b50c27646ed5762fd75dc4ddd8c0f200cb05019d67b592f6fc821c49479ab48640292eacb3b7c4be141e96616fb13957692cc7edd0b45ae3dc07223c8e92937bef84bc0eab862853349ec75546f58fb7c2775c38462c5010d846c185c15111e595522a6bcd16cf86",
201            ),
202            (
203                b"abc".to_vec(),
204                64,
205                "483366601360a8771c6863080cc4114d8db44530f8f1e1ee4f94ea37e78b5739d5a15bef186a5386c75744c0527e1faa9f8726e462a12a4feb06bd8801e751e4",
206            ),
207            (
208                b"hello world".to_vec(),
209                64,
210                "369771bb2cb9d2b04c1d54cca487e372d9f187f73f7ba3f65b95c8ee7798c527f4f3c2d55c2d46a29f2e945d469c3df27853a8735271f5cc2d9e889544357116",
211            ),
212            (
213                b"The quick brown fox jumps over the lazy dog".to_vec(),
214                64,
215                "2f671343d9b2e1604dc9dcf0753e5fe15c7c64a0d283cbbf722d411a0e36f6ca1d01d1369a23539cd80f7c054b6e5daf9c962cad5b8ed5bd11998b40d5734442",
216            ),
217            (
218                b"The quick brown fox jumps over the lazy dog.".to_vec(),
219                64,
220                "bd225bfc8b255f3036f0c8866010ed0053b5163a3cae111e723c0c8e704eca4e5d0f1e2a2fa18c8a219de6b88d5917ff5dd75b5fb345e7409a3b333b508a65fb",
221            ),
222            (
223                vec![b'a'; 1_000_000],
224                64,
225                "3578a7a4ca9137569cdf76ed617d31bb994fca9c1bbf8b184013de8234dfd13a3fd124d4df76c0a539ee7dd2f6e1ec346124c815d9410e145eb561bcd97b18ab",
226            ),
227        ]
228    }
229
230    #[test]
231    fn known_vectors() {
232        for (input, output_len, expected) in vectors_shake256() {
233            let mut output = vec![0u8; output_len];
234            Shake256::hash(&input, &mut output);
235            assert_eq!(hex::encode(output), expected);
236        }
237    }
238
239    #[test]
240    fn incremental_and_streaming_read() {
241        let mut one_shot = vec![0u8; 128];
242        Shake256::hash(b"", &mut one_shot);
243
244        let mut shake = Shake256::new();
245        shake.absorb(b"");
246        let mut first = [0u8; 64];
247        let mut second = [0u8; 64];
248        shake.squeeze(&mut first);
249        shake.squeeze(&mut second);
250
251        let mut combined = vec![0u8; 128];
252        combined[..64].copy_from_slice(&first);
253        combined[64..].copy_from_slice(&second);
254
255        assert_eq!(combined, one_shot);
256    }
257
258    #[test]
259    fn hasher_trait_impl() {
260        let expected = "369771bb2cb9d2b04c1d54cca487e372d9f187f73f7ba3f65b95c8ee7798c527f4f3c2d55c2d46a29f2e945d469c3df27853a8735271f5cc2d9e889544357116";
261        let digest = <Shake256 as Hasher>::hash(b"hello world");
262        assert_eq!(hex::encode(digest.as_ref()), expected);
263    }
264
265    #[test]
266    fn xof_trait_impl() {
267        let mut xof = Shake256::new();
268        xof.absorb(b"abc");
269        let mut out = [0u8; 64];
270        xof.squeeze(&mut out);
271        assert_eq!(
272            hex::encode(out),
273            "483366601360a8771c6863080cc4114d8db44530f8f1e1ee4f94ea37e78b5739d5a15bef186a5386c75744c0527e1faa9f8726e462a12a4feb06bd8801e751e4"
274        );
275    }
276
277    // ── CShake256 vectors (NIST SP 800-185) ──────────────────────────────────
278
279    const EMAIL_SIGNATURE: &[u8] = b"Email Signature";
280    const SAMPLE_3_EXPECTED: &str = "d008828e2b80ac9d2218ffee1d070c48b8e4c87bff32c9699d5b6896eee0edd164020e2be0560858d9c00c037e34a96937c561a74c412bb4c746469527281c8c";
281    const SAMPLE_4_EXPECTED: &str = "07dc27b11e51fbac75bc7b3c1d983e8b4b85fb1defaf218912ac86430273091727f42b17ed1df63e8ec118f04b23633c1dfb1574c8fb55cb45da8e25afb092bb";
282
283    #[test]
284    fn cshake256_nist_sample_3() {
285        let mut out = [0u8; 64];
286        CShake256::hash(&[0x00, 0x01, 0x02, 0x03], b"", EMAIL_SIGNATURE, &mut out);
287        assert_eq!(hex::encode(out), SAMPLE_3_EXPECTED);
288    }
289
290    #[test]
291    fn cshake256_nist_sample_4() {
292        let input: Vec<u8> = (0u8..200).collect();
293        let mut out = [0u8; 64];
294        CShake256::hash(&input, b"", EMAIL_SIGNATURE, &mut out);
295        assert_eq!(hex::encode(out), SAMPLE_4_EXPECTED);
296    }
297
298    #[test]
299    fn cshake256_incremental_matches_one_shot() {
300        let input: Vec<u8> = (0u8..200).collect();
301        let mut one_shot = [0u8; 64];
302        CShake256::hash(&input, b"", EMAIL_SIGNATURE, &mut one_shot);
303
304        let mut cshake = CShake256::new(b"", EMAIL_SIGNATURE);
305        for chunk in input.chunks(9) {
306            cshake.absorb(chunk);
307        }
308        let mut streamed = [0u8; 64];
309        cshake.squeeze(&mut streamed);
310        assert_eq!(streamed, one_shot);
311    }
312
313    #[test]
314    fn cshake256_empty_params_passes_shake256_vectors() {
315        for (input, output_len, expected) in vectors_shake256() {
316            let mut cshake_out = vec![0u8; output_len];
317            CShake256::hash(&input, b"", b"", &mut cshake_out);
318            assert_eq!(hex::encode(cshake_out), expected);
319        }
320    }
321
322    #[test]
323    fn cshake256_xof_trait_impl() {
324        let mut xof = CShake256::new(b"", b"");
325        xof.absorb(b"abc");
326        let mut out = [0u8; 64];
327        xof.squeeze(&mut out);
328        assert_eq!(
329            hex::encode(out),
330            "483366601360a8771c6863080cc4114d8db44530f8f1e1ee4f94ea37e78b5739d5a15bef186a5386c75744c0527e1faa9f8726e462a12a4feb06bd8801e751e4"
331        );
332    }
333}