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