Skip to main content

dryoc/
xof.rs

1//! # Extendable-output functions
2//!
3//! An extendable-output function (XOF) hashes input of any length into
4//! output of any length. It can serve as a hash with a chosen output size, a
5//! key-derivation step that turns one secret into several keys, or a
6//! deterministic generator that expands a seed.
7//!
8//! [`Shake128`] and [`Shake256`] are the SHAKE functions from FIPS 202.
9//! [`TurboShake128`] and [`TurboShake256`] (RFC 9861) run the same sponge
10//! with 12 Keccak rounds instead of 24, so they are about twice as fast with
11//! the same security claims. The 128 and 256 suffixes give the security
12//! level in bits.
13//!
14//! Absorb input with `update`, then call `finalize` to get a reader. Each
15//! `squeeze` call continues the same output stream, so squeezing 32 bytes
16//! twice gives the same bytes as squeezing 64 once. `with_domain` selects a
17//! custom domain byte in `0x01..=0x7f`; different domains give unrelated
18//! outputs for the same input.
19//!
20//! An XOF is not a MAC. Output is only secret if the input is.
21//!
22//! ## Example
23//!
24//! ```
25//! # #[cfg(feature = "alloc")]
26//! # {
27//! use dryoc::xof::TurboShake128;
28//!
29//! let mut xof = TurboShake128::new();
30//! xof.update(b"input keying material");
31//! let mut reader = xof.finalize();
32//! let encryption_key = reader.squeeze_to_vec(32);
33//! let mac_key = reader.squeeze_to_vec(32);
34//! assert_ne!(encryption_key, mac_key);
35//!
36//! // One-shot output of any length.
37//! let digest = dryoc::xof::Shake256::compute_to_vec(b"hello", 64);
38//! assert_eq!(digest.len(), 64);
39//! # }
40//! ```
41
42#[cfg(feature = "alloc")]
43use alloc::vec::Vec;
44
45use crate::error::{Error, ErrorContext};
46use crate::keccak::{DOMAIN_SHAKE, RATE_128, RATE_256, ROUNDS_FULL, ROUNDS_TURBO, Sponge};
47use crate::types::*;
48
49/// Absorbing or squeezing sponge with its domain byte, shared by the
50/// Rustaceous XOF types and the Classic `crypto_xof_*` states.
51#[derive(Clone)]
52pub(crate) struct XofCore<const RATE: usize, const ROUNDS: usize> {
53    sponge: Sponge<RATE, ROUNDS>,
54    domain: u8,
55    squeezing: bool,
56}
57
58impl<const RATE: usize, const ROUNDS: usize> XofCore<RATE, ROUNDS> {
59    pub(crate) fn new() -> Self {
60        Self {
61            sponge: Sponge::new(),
62            domain: DOMAIN_SHAKE,
63            squeezing: false,
64        }
65    }
66
67    /// Returns a core with a custom `domain` byte.
68    ///
69    /// The byte carries the suffix bits and the first padding bit, so it
70    /// must be nonzero and must leave the top bit clear for the final
71    /// padding bit.
72    pub(crate) fn with_domain(domain: u8) -> Result<Self, Error> {
73        validate_value!(0x01u8, 0x7fu8, domain, ErrorContext::Domain);
74        Ok(Self {
75            domain,
76            ..Self::new()
77        })
78    }
79
80    /// Absorbs `input`; fails once squeezing has started.
81    pub(crate) fn update(&mut self, input: &[u8]) -> Result<(), Error> {
82        if self.squeezing {
83            return Err(Error::invalid_state(ErrorContext::Xof));
84        }
85        self.sponge.absorb(input);
86        Ok(())
87    }
88
89    /// Pads on the first call, then continues the output stream.
90    pub(crate) fn squeeze(&mut self, output: &mut [u8]) {
91        if !self.squeezing {
92            self.sponge.pad(self.domain);
93            self.squeezing = true;
94        }
95        self.sponge.squeeze(output);
96    }
97}
98
99/// Defines an absorbing XOF type and its reader.
100///
101/// - `$name` / `$reader`: the absorbing and squeezing types; leading attributes
102///   (docs) are applied to `$name`.
103/// - `$algo`: the algorithm name for the generated docs.
104/// - `$rate` / `$rounds`: the sponge rate in bytes and the Keccak rounds.
105macro_rules! xof {
106    (
107        $(#[$meta:meta])*
108        $name:ident, $reader:ident, $algo:literal, $rate:expr, $rounds:expr $(,)?
109    ) => {
110        $(#[$meta])*
111        #[derive(Clone)]
112        pub struct $name {
113            core: XofCore<$rate, $rounds>,
114        }
115
116        impl $name {
117            #[doc = concat!("Returns a new ", $algo, " instance with the standard domain.")]
118            #[must_use]
119            pub fn new() -> Self {
120                Self {
121                    core: XofCore::new(),
122                }
123            }
124
125            #[doc = concat!("Returns a new ", $algo, " instance with a custom `domain` byte.")]
126            ///
127            /// # Errors
128            ///
129            /// Returns [`Error::InvalidValue`] unless `domain` is in
130            /// `0x01..=0x7f`.
131            pub fn with_domain(domain: u8) -> Result<Self, Error> {
132                Ok(Self {
133                    core: XofCore::with_domain(domain)?,
134                })
135            }
136
137            /// Absorbs `input`.
138            pub fn update<Input: Bytes + ?Sized>(&mut self, input: &Input) {
139                // Only the reader squeezes, so this core is still absorbing.
140                self.core.sponge.absorb(input.as_slice())
141            }
142
143            /// Finishes absorbing and returns a reader for the output stream.
144            #[must_use]
145            pub fn finalize(self) -> $reader {
146                $reader { core: self.core }
147            }
148
149            #[doc = concat!(
150                "Computes ", $algo, " of `input` with the standard domain, filling `output`."
151            )]
152            pub fn compute_into_bytes<Output: MutBytes + ?Sized, Input: Bytes + ?Sized>(
153                output: &mut Output,
154                input: &Input,
155            ) {
156                let mut xof = Self::new();
157                xof.update(input);
158                xof.finalize().squeeze(output);
159            }
160
161            #[doc = concat!(
162                "Computes `len` bytes of ", $algo, " of `input` with the standard domain."
163            )]
164            #[cfg(feature = "alloc")]
165            #[must_use]
166            pub fn compute_to_vec<Input: Bytes + ?Sized>(input: &Input, len: usize) -> Vec<u8> {
167                let mut output = vec![0u8; len];
168                Self::compute_into_bytes(&mut output, input);
169                output
170            }
171        }
172
173        impl Default for $name {
174            fn default() -> Self {
175                Self::new()
176            }
177        }
178
179        #[doc = concat!("Output stream of a finalized [`", stringify!($name), "`].")]
180        #[derive(Clone)]
181        pub struct $reader {
182            core: XofCore<$rate, $rounds>,
183        }
184
185        impl $reader {
186            /// Fills `output` with the next bytes of the output stream.
187            pub fn squeeze<Output: MutBytes + ?Sized>(&mut self, output: &mut Output) {
188                self.core.squeeze(output.as_mut_slice())
189            }
190
191            /// Returns the next `len` bytes of the output stream.
192            #[cfg(feature = "alloc")]
193            #[must_use]
194            pub fn squeeze_to_vec(&mut self, len: usize) -> Vec<u8> {
195                let mut output = vec![0u8; len];
196                self.squeeze(&mut output);
197                output
198            }
199        }
200    };
201}
202
203xof! {
204    /// SHAKE128 extendable-output function (FIPS 202).
205    Shake128, Shake128Reader, "SHAKE128", RATE_128, ROUNDS_FULL,
206}
207
208xof! {
209    /// SHAKE256 extendable-output function (FIPS 202).
210    Shake256, Shake256Reader, "SHAKE256", RATE_256, ROUNDS_FULL,
211}
212
213xof! {
214    /// TurboSHAKE128 extendable-output function (RFC 9861).
215    TurboShake128, TurboShake128Reader, "TurboSHAKE128", RATE_128, ROUNDS_TURBO,
216}
217
218xof! {
219    /// TurboSHAKE256 extendable-output function (RFC 9861).
220    TurboShake256, TurboShake256Reader, "TurboSHAKE256", RATE_256, ROUNDS_TURBO,
221}
222
223/// Known answers shared with the Classic `crypto_xof_*` tests.
224///
225/// SHAKE answers are the FIPS 202 empty-message outputs plus messages at the
226/// rate boundaries (rate - 1, rate, rate + 1 and twice the rate) in the
227/// `(i * 31 % 251)` pattern used by the SHA-2 and SHA-3 tests, all computed
228/// with Python's `hashlib` (OpenSSL). TurboSHAKE answers are the RFC 9861
229/// section 5 test vectors.
230#[cfg(test)]
231pub(crate) mod test_vectors {
232    pub(crate) use crate::keccak::{RATE_128, RATE_256};
233    use crate::test_prelude::*;
234    use crate::utils::test_util::hex;
235
236    /// One known answer: `output` is the first `output.len()` bytes, or,
237    /// when `skip` is nonzero, the bytes after skipping `skip` bytes.
238    pub(crate) struct Vector {
239        pub(crate) message: Vec<u8>,
240        pub(crate) domain: u8,
241        pub(crate) skip: usize,
242        pub(crate) output: Vec<u8>,
243    }
244
245    fn pattern(len: usize) -> Vec<u8> {
246        (0..len as u32).map(|i| (i * 31 % 251) as u8).collect()
247    }
248
249    /// RFC 9861 `ptn(n)`: `00 01 .. FA` repeated and truncated to `n` bytes.
250    fn ptn(len: usize) -> Vec<u8> {
251        (0..len).map(|i| (i % 251) as u8).collect()
252    }
253
254    fn standard(message: Vec<u8>, output: &str) -> Vector {
255        Vector {
256            message,
257            domain: 0x1f,
258            skip: 0,
259            output: hex(output),
260        }
261    }
262
263    fn shake(rate: usize, answers: [&str; 6], empty_long: &str, million: &str) -> Vec<Vector> {
264        let [empty, abc, below, at, above, twice] = answers;
265        let mut vectors = vec![
266            standard(vec![], empty),
267            standard(b"abc".to_vec(), abc),
268            standard(pattern(rate - 1), below),
269            standard(pattern(rate), at),
270            standard(pattern(rate + 1), above),
271            standard(pattern(2 * rate), twice),
272            // Squeezes across two permutations.
273            standard(vec![], empty_long),
274        ];
275        // Rate boundaries above cover buffering under Miri; keep the
276        // million-byte stress vector in the native suite.
277        if !cfg!(miri) {
278            vectors.push(standard(vec![b'a'; 1_000_000], million));
279        }
280        vectors
281    }
282
283    pub(crate) fn shake128() -> Vec<Vector> {
284        shake(
285            RATE_128,
286            [
287                "7f9c2ba4e88f827d616045507605853ed73b8093f6efbc88eb1a6eacfa66ef26",
288                "5881092dd818bf5cf8a3ddb793fbcba74097d5c526a6d35f97b83351940f2cc8",
289                "7e0e12d510e6c678e349578a047e64663fcdc6161f7da575e04e371efc327987",
290                "443571f674f6eb618c233bec2531cc5d4c7b6b965d6082057a723f99435125a5",
291                "696c3b1a8439985a424c6f13f61efbaaec6823bbb382b7125c925f288e9de43e",
292                "f8d2cd2eb2f33dc5da2195f5f389889bae2c5cc75a98453688eca464c7f52cb6",
293            ],
294            concat!(
295                "7f9c2ba4e88f827d616045507605853ed73b8093f6efbc88eb1a6eacfa66ef26",
296                "3cb1eea988004b93103cfb0aeefd2a686e01fa4a58e8a3639ca8a1e3f9ae57e2",
297                "35b8cc873c23dc62b8d260169afa2f75ab916a58d974918835d25e6a435085b2",
298                "badfd6dfaac359a5efbb7bcc4b59d538df9a04302e10c8bc1cbf1a0b3a5120ea",
299                "17cda7cfad765f5623474d368ccca8af0007cd9f5e4c849f167a580b14aabdef",
300                "aee7eef47cb0fca9767be1fda69419dfb927e9df07348b196691abaeb580b32d",
301                "ef58538b8d23f87732ea63b02b4fa0f4873360e2841928cd60dd4cee8cc0d4c9",
302                "22a96188d032675c8ac850933c7aff1533b94c834adbb69c6115bad4692d8619",
303                "f90b0cdf8a7b9c264029ac185b70b83f2801f2f4b3f70c593ea3aeeb613a7f1b",
304                "1de33fd75081f592305f2e4526edc09631b10958f464d889f31ba010250fda7f",
305                "1368ec2967fc84ef2ae9aff268e0b1700a",
306            ),
307            "9d222c79c4ff9d092cf6ca86143aa411e369973808ef97093255826c5572ef58",
308        )
309    }
310
311    pub(crate) fn shake256() -> Vec<Vector> {
312        shake(
313            RATE_256,
314            [
315                "46b9dd2b0ba88d13233b3feb743eeb243fcd52ea62b81b82b50c27646ed5762f",
316                "483366601360a8771c6863080cc4114d8db44530f8f1e1ee4f94ea37e78b5739",
317                "1a7339556bb5aa2e7cba9470e03d5e6ee95bf2d6e4702e61d6a07a150c1ca5e1",
318                "02e30a82e7d18bcd455f3379d1cf015b644314d23860bd26e230a32d933730fb",
319                "6789fd741e422e3221d117b08930492886fce3d26d0e515b900bcb59191db23a",
320                "51954833fffb74e3b3d7b59d453dc5016fb167056f5851eaebe55af357cc80aa",
321            ],
322            concat!(
323                "46b9dd2b0ba88d13233b3feb743eeb243fcd52ea62b81b82b50c27646ed5762f",
324                "d75dc4ddd8c0f200cb05019d67b592f6fc821c49479ab48640292eacb3b7c4be",
325                "141e96616fb13957692cc7edd0b45ae3dc07223c8e92937bef84bc0eab862853",
326                "349ec75546f58fb7c2775c38462c5010d846c185c15111e595522a6bcd16cf86",
327                "f3d122109e3b1fdd943b6aec468a2d621a7c06c6a957c62b54dafc3be87567d6",
328                "77231395f6147293b68ceab7a9e0c58d864e8efde4e1b9a46cbe854713672f5c",
329                "aaae314ed9083dab4b099f8e300f01b8650f1f4b1d8fcf3f3cb53fb8e9eb2ea2",
330                "03bdc970f50ae55428a91f7f53ac266b28419c3778a15fd248d339ede785fb7f",
331                "5a1aaa96d313eacc890936c173cdcd0fab",
332            ),
333            "3578a7a4ca9137569cdf76ed617d31bb994fca9c1bbf8b184013de8234dfd13a",
334        )
335    }
336
337    /// Builds the RFC 9861 vectors shared by both TurboSHAKE variants:
338    /// the empty message (short output and the last 32 of 10032 bytes),
339    /// `ptn(17^i)` for `i` in `0..=6`, and the custom-domain cases.
340    fn turboshake(
341        empty: &str,
342        empty_tail: &str,
343        ptn_answers: [&str; 7],
344        domains: [(&[u8], u8, &str); 6],
345    ) -> Vec<Vector> {
346        let mut vectors = vec![
347            standard(vec![], empty),
348            Vector {
349                message: vec![],
350                domain: 0x1f,
351                skip: 10032 - 32,
352                output: hex(empty_tail),
353            },
354        ];
355        for (i, output) in ptn_answers.into_iter().enumerate() {
356            // `ptn(17^6)` is 24 MB; keep it out of the Miri run.
357            if cfg!(miri) && i > 3 {
358                continue;
359            }
360            vectors.push(standard(ptn(17usize.pow(i as u32)), output));
361        }
362        for (message, domain, output) in domains {
363            vectors.push(Vector {
364                message: message.to_vec(),
365                domain,
366                skip: 0,
367                output: hex(output),
368            });
369        }
370        vectors
371    }
372
373    pub(crate) fn turboshake128() -> Vec<Vector> {
374        turboshake(
375            concat!(
376                "1E 41 5F 1C 59 83 AF F2 16 92 17 27 7D 17 BB 53 8C D9 45 A3 97 DD EC 54 1F 1C E4 \
377                 1A F2 C1 B7 4C",
378                "3E 8C CA E2 A4 DA E5 6C 84 A0 4C 23 85 C0 3C 15 E8 19 3B DF 58 73 73 63 32 16 91 \
379                 C0 54 62 C8 DF",
380            ),
381            "A3 B9 B0 38 59 00 CE 76 1F 22 AE D5 48 E7 54 DA 10 A5 24 2D 62 E8 C6 58 E3 F3 A9 23 \
382             A7 55 56 07",
383            [
384                "55 CE DD 6F 60 AF 7B B2 9A 40 42 AE 83 2E F3 F5 8D B7 29 9F 89 3E BB 92 47 24 7D \
385                 85 69 58 DA A9",
386                "9C 97 D0 36 A3 BA C8 19 DB 70 ED E0 CA 55 4E C6 E4 C2 A1 A4 FF BF D9 EC 26 9C A6 \
387                 A1 11 16 12 33",
388                "96 C7 7C 27 9E 01 26 F7 FC 07 C9 B0 7F 5C DA E1 E0 BE 60 BD BE 10 62 00 40 E7 5D \
389                 72 23 A6 24 D2",
390                "D4 97 6E B5 6B CF 11 85 20 58 2B 70 9F 73 E1 D6 85 3E 00 1F DA F8 0E 1B 13 E0 D0 \
391                 59 9D 5F B3 72",
392                "DA 67 C7 03 9E 98 BF 53 0C F7 A3 78 30 C6 66 4E 14 CB AB 7F 54 0F 58 40 3B 1B 82 \
393                 95 13 18 EE 5C",
394                "B9 7A 90 6F BF 83 EF 7C 81 25 17 AB F3 B2 D0 AE A0 C4 F6 03 18 CE 11 CF 10 39 25 \
395                 12 7F 59 EE CD",
396                "35 CD 49 4A DE DE D2 F2 52 39 AF 09 A7 B8 EF 0C 4D 1C A4 FE 2D 1A C3 70 FA 63 21 \
397                 6F E7 B4 C2 B1",
398            ],
399            [
400                (
401                    &[0xff; 3],
402                    0x01,
403                    "BF 32 3F 94 04 94 E8 8E E1 C5 40 FE 66 0B E8 A0 C9 3F 43 D1 5E C0 06 99 84 \
404                     62 FA 99 4E ED 5D AB",
405                ),
406                (
407                    &[0xff],
408                    0x06,
409                    "8E C9 C6 64 65 ED 0D 4A 6C 35 D1 35 06 71 8D 68 7A 25 CB 05 C7 4C CA 1E 42 \
410                     50 1A BD 83 87 4A 67",
411                ),
412                (
413                    &[0xff; 3],
414                    0x07,
415                    "B6 58 57 60 01 CA D9 B1 E5 F3 99 A9 F7 77 23 BB A0 54 58 04 2D 68 20 6F 72 \
416                     52 68 2D BA 36 63 ED",
417                ),
418                (
419                    &[0xff; 7],
420                    0x0b,
421                    "8D EE AA 1A EC 47 CC EE 56 9F 65 9C 21 DF A8 E1 12 DB 3C EE 37 B1 81 78 B2 \
422                     AC D8 05 B7 99 CC 37",
423                ),
424                (
425                    &[0xff],
426                    0x30,
427                    "55 31 22 E2 13 5E 36 3C 32 92 BE D2 C6 42 1F A2 32 BA B0 3D AA 07 C7 D6 63 \
428                     66 03 28 65 06 32 5B",
429                ),
430                (
431                    &[0xff; 3],
432                    0x7f,
433                    "16 27 4C C6 56 D4 4C EF D4 22 39 5D 0F 90 53 BD A6 D2 8E 12 2A BA 15 C7 65 \
434                     E5 AD 0E 6E AF 26 F9",
435                ),
436            ],
437        )
438    }
439
440    pub(crate) fn turboshake256() -> Vec<Vector> {
441        turboshake(
442            concat!(
443                "36 7A 32 9D AF EA 87 1C 78 02 EC 67 F9 05 AE 13 C5 76 95 DC 2C 66 63 C6 10 35 F5 \
444                 9A 18 F8 E7 DB",
445                "11 ED C0 E1 2E 91 EA 60 EB 6B 32 DF 06 DD 7F 00 2F BA FA BB 6E 13 EC 1C C2 0D 99 \
446                 55 47 60 0D B0",
447            ),
448            "AB EF A1 16 30 C6 61 26 92 49 74 26 85 EC 08 2F 20 72 65 DC CF 2F 43 53 4E 9C 61 BA \
449             0C 9D 1D 75",
450            [
451                concat!(
452                    "3E 17 12 F9 28 F8 EA F1 05 46 32 B2 AA 0A 24 6E D8 B0 C3 78 72 8F 60 BC 97 \
453                     04 10 15 5C 28 82 0E",
454                    "90 CC 90 D8 A3 00 6A A2 37 2C 5C 5E A1 76 B0 68 2B F2 2B AE 74 67 AC 94 F7 \
455                     4D 43 D3 9B 04 82 E2",
456                ),
457                concat!(
458                    "B3 BA B0 30 0E 6A 19 1F BE 61 37 93 98 35 92 35 78 79 4E A5 48 43 F5 01 10 \
459                     90 FA 2F 37 80 A9 E5",
460                    "CB 22 C5 9D 78 B4 0A 0F BF F9 E6 72 C0 FB E0 97 0B D2 C8 45 09 1C 60 44 D6 \
461                     87 05 4D A5 D8 E9 C7",
462                ),
463                concat!(
464                    "66 B8 10 DB 8E 90 78 04 24 C0 84 73 72 FD C9 57 10 88 2F DE 31 C6 DF 75 BE \
465                     B9 D4 CD 93 05 CF CA",
466                    "E3 5E 7B 83 E8 B7 E6 EB 4B 78 60 58 80 11 63 16 FE 2C 07 8A 09 B9 4A D7 B8 \
467                     21 3C 0A 73 8B 65 C0",
468                ),
469                concat!(
470                    "C7 4E BC 91 9A 5B 3B 0D D1 22 81 85 BA 02 D2 9E F4 42 D6 9D 3D 42 76 A9 3E \
471                     FE 0B F9 A1 6A 7D C0",
472                    "CD 4E AB AD AB 8C D7 A5 ED D9 66 95 F5 D3 60 AB E0 9E 2C 65 11 A3 EC 39 7D \
473                     A3 B7 6B 9E 16 74 FB",
474                ),
475                concat!(
476                    "02 CC 3A 88 97 E6 F4 F6 CC B6 FD 46 63 1B 1F 52 07 B6 6C 6D E9 C7 B5 5B 2D \
477                     1A 23 13 4A 17 0A FD",
478                    "AC 23 4E AB A9 A7 7C FF 88 C1 F0 20 B7 37 24 61 8C 56 87 B3 62 C4 30 B2 48 \
479                     CD 38 64 7F 84 8A 1D",
480                ),
481                concat!(
482                    "AD D5 3B 06 54 3E 58 4B 58 23 F6 26 99 6A EE 50 FE 45 ED 15 F2 02 43 A7 16 \
483                     54 85 AC B4 AA 76 B4",
484                    "FF DA 75 CE DF 6D 8C DC 95 C3 32 BD 56 F4 B9 86 B5 8B B1 7D 17 78 BF C1 B1 \
485                     A9 75 45 CD F4 EC 9F",
486                ),
487                concat!(
488                    "9E 11 BC 59 C2 4E 73 99 3C 14 84 EC 66 35 8E F7 1D B7 4A EF D8 4E 12 3F 78 \
489                     00 BA 9C 48 53 E0 2C",
490                    "FE 70 1D 9E 6B B7 65 A3 04 F0 DC 34 A4 EE 3B A8 2C 41 0F 0D A7 0E 86 BF BD \
491                     90 EA 87 7C 2D 61 04",
492                ),
493            ],
494            [
495                (
496                    &[0xff; 3],
497                    0x01,
498                    concat!(
499                        "D2 1C 6F BB F5 87 FA 22 82 F2 9A EA 62 01 75 FB 02 57 41 3A F7 8A 0B 1B \
500                         2A 87 41 9C E0 31 D9 33",
501                        "AE 7A 4D 38 33 27 A8 A1 76 41 A3 4F 8A 1D 10 03 AD 7D A6 B7 2D BA 84 BB \
502                         62 FE F2 8F 62 F1 24 24",
503                    ),
504                ),
505                (
506                    &[0xff],
507                    0x06,
508                    concat!(
509                        "73 8D 7B 4E 37 D1 8B 7F 22 AD 1B 53 13 E3 57 E3 DD 7D 07 05 6A 26 A3 03 \
510                         C4 33 FA 35 33 45 52 80",
511                        "F4 F5 A7 D4 F7 00 EF B4 37 FE 6D 28 14 05 E0 7B E3 2A 0A 97 2E 22 E6 3A \
512                         DC 1B 09 0D AE FE 00 4B",
513                    ),
514                ),
515                (
516                    &[0xff; 3],
517                    0x07,
518                    concat!(
519                        "18 B3 B5 B7 06 1C 2E 67 C1 75 3A 00 E6 AD 7E D7 BA 1C 90 6C F9 3E FB 70 \
520                         92 EA F2 7F BE EB B7 55",
521                        "AE 6E 29 24 93 C1 10 E4 8D 26 00 28 49 2B 8E 09 B5 50 06 12 B8 F2 57 89 \
522                         85 DE D5 35 7D 00 EC 67",
523                    ),
524                ),
525                (
526                    &[0xff; 7],
527                    0x0b,
528                    concat!(
529                        "BB 36 76 49 51 EC 97 E9 D8 5F 7E E9 A6 7A 77 18 FC 00 5C F4 25 56 BE 79 \
530                         CE 12 C0 BD E5 0E 57 36",
531                        "D6 63 2B 0D 0D FB 20 2D 1B BB 8F FE 3D D7 4C B0 08 34 FA 75 6C B0 34 71 \
532                         BA B1 3A 1E 2C 16 B3 C0",
533                    ),
534                ),
535                (
536                    &[0xff],
537                    0x30,
538                    concat!(
539                        "F3 FE 12 87 3D 34 BC BB 2E 60 87 79 D6 B7 0E 7F 86 BE C7 E9 0B F1 13 CB \
540                         D4 FD D0 C4 E2 F4 62 5E",
541                        "14 8D D7 EE 1A 52 77 6C F7 7F 24 05 14 D9 CC FC 3B 5D DA B8 EE 25 5E 39 \
542                         EE 38 90 72 96 2C 11 1A",
543                    ),
544                ),
545                (
546                    &[0xff; 3],
547                    0x7f,
548                    concat!(
549                        "AB E5 69 C1 F7 7E C3 40 F0 27 05 E7 D3 7C 9A B7 E1 55 51 6E 4A 6A 15 00 \
550                         21 D7 0B 6F AC 0B B4 0C",
551                        "06 9F 9A 98 28 A0 D5 75 CD 99 F9 BA E4 35 AB 1A CF 7E D9 11 0B A9 7C E0 \
552                         38 8D 07 4B AC 76 87 76",
553                    ),
554                ),
555            ],
556        )
557    }
558}
559
560#[cfg(all(test, feature = "alloc"))]
561mod tests {
562    use super::test_vectors::*;
563    use super::*;
564
565    /// Checks one type against its vectors: absorbing in one call and in
566    /// rate-sized pieces, and squeezing in one call and in pieces that cross
567    /// the rate boundary.
568    macro_rules! check_known_answers {
569        ($xof:ty, $vectors:expr, $rate:expr) => {
570            for Vector {
571                message,
572                domain,
573                skip,
574                output,
575            } in $vectors
576            {
577                let len = message.len();
578                let total = skip + output.len();
579                let new = || <$xof>::with_domain(domain).expect("valid domain");
580
581                let mut one = new();
582                one.update(&message);
583                let mut all = vec![0u8; total];
584                one.finalize().squeeze(&mut all);
585                assert_eq!(&all[skip..], output, "one-shot len {len}");
586
587                if len > 2 * $rate {
588                    continue;
589                }
590                let mut blocks = new();
591                for chunk in message.chunks($rate) {
592                    blocks.update(chunk);
593                }
594                let mut reader = blocks.finalize();
595                let mut pieces = Vec::with_capacity(total);
596                while pieces.len() < total {
597                    let take = (total - pieces.len()).min($rate - 1);
598                    pieces.extend(reader.squeeze_to_vec(take));
599                }
600                assert_eq!(&pieces[skip..], output, "chunked len {len}");
601
602                if domain == 0x1f && skip == 0 {
603                    assert_eq!(
604                        <$xof>::compute_to_vec(&message, output.len()),
605                        output,
606                        "compute_to_vec len {len}"
607                    );
608                }
609            }
610        };
611    }
612
613    #[test]
614    fn test_shake128_known_answers() {
615        check_known_answers!(Shake128, shake128(), RATE_128);
616    }
617
618    #[test]
619    fn test_shake256_known_answers() {
620        check_known_answers!(Shake256, shake256(), RATE_256);
621    }
622
623    #[test]
624    fn test_turboshake128_known_answers() {
625        check_known_answers!(TurboShake128, turboshake128(), RATE_128);
626    }
627
628    #[test]
629    fn test_turboshake256_known_answers() {
630        check_known_answers!(TurboShake256, turboshake256(), RATE_256);
631    }
632
633    /// The domain byte must leave room for the final padding bit and must
634    /// not be zero; both ends of the valid range are accepted.
635    #[test]
636    fn test_with_domain_rejects_out_of_range() {
637        for domain in [0x00u8, 0x80, 0xff] {
638            assert!(matches!(
639                TurboShake128::with_domain(domain),
640                Err(Error::InvalidValue {
641                    context: ErrorContext::Domain,
642                    ..
643                })
644            ));
645        }
646        for domain in [0x01u8, 0x7f] {
647            assert!(Shake256::with_domain(domain).is_ok());
648        }
649    }
650}