Skip to main content

lib_q_hash/
shake.rs

1use core::fmt;
2
3use digest::block_api::{
4    AlgorithmName,
5    BlockSizeUser,
6    ExtendableOutputCore,
7    Reset,
8    UpdateCore,
9    XofReaderCore,
10};
11use digest::block_buffer::{
12    EagerBuffer,
13    ReadBuffer,
14};
15use digest::consts::{
16    U0,
17    U16,
18    U32,
19    U136,
20    U168,
21};
22use digest::{
23    CollisionResistance,
24    ExtendableOutput,
25    ExtendableOutputReset,
26    HashMarker,
27    Update,
28    XofReader,
29};
30
31use crate::internal_block_api::{
32    Sha3HasherCore,
33    Sha3ReaderCore,
34};
35use crate::{
36    DEFAULT_ROUND_COUNT,
37    SHAKE_PAD,
38};
39
40macro_rules! impl_shake {
41    (
42        $name:ident, $reader_name:ident, $rate:ty, $alg_name:expr
43    ) => {
44        #[doc = $alg_name]
45        #[doc = " hasher."]
46        #[derive(Clone)]
47        pub struct $name {
48            core: Sha3HasherCore<$rate, U0, SHAKE_PAD, DEFAULT_ROUND_COUNT>,
49            buffer: EagerBuffer<$rate>,
50        }
51
52        impl Default for $name {
53            #[inline]
54            fn default() -> Self {
55                Self {
56                    core: Default::default(),
57                    buffer: Default::default(),
58                }
59            }
60        }
61
62        impl HashMarker for $name {}
63
64        impl BlockSizeUser for $name {
65            type BlockSize = $rate;
66        }
67
68        impl Update for $name {
69            #[inline]
70            fn update(&mut self, data: &[u8]) {
71                let Self { core, buffer } = self;
72                buffer.digest_blocks(data, |blocks| core.update_blocks(blocks));
73            }
74        }
75
76        impl ExtendableOutput for $name {
77            type Reader = $reader_name;
78
79            #[inline]
80            fn finalize_xof(mut self) -> Self::Reader {
81                let Self { core, buffer } = &mut self;
82                let core = core.finalize_xof_core(buffer);
83                let buffer = Default::default();
84                Self::Reader { core, buffer }
85            }
86        }
87
88        impl ExtendableOutputReset for $name {
89            #[inline]
90            fn finalize_xof_reset(&mut self) -> Self::Reader {
91                let Self { core, buffer } = self;
92                let core = core.finalize_xof_core(buffer);
93                self.reset();
94                let buffer = Default::default();
95                Self::Reader { core, buffer }
96            }
97        }
98
99        impl Reset for $name {
100            #[inline]
101            fn reset(&mut self) {
102                *self = Default::default();
103            }
104        }
105
106        impl AlgorithmName for $name {
107            fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
108                f.write_str($alg_name)
109            }
110        }
111
112        impl fmt::Debug for $name {
113            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
114                f.write_str(concat!(stringify!($name), " { ... }"))
115            }
116        }
117
118        #[cfg(feature = "zeroize")]
119        impl digest::zeroize::ZeroizeOnDrop for $name {}
120
121        #[doc = $alg_name]
122        #[doc = " XOF reader."]
123        #[derive(Clone)]
124        pub struct $reader_name {
125            core: Sha3ReaderCore<$rate, DEFAULT_ROUND_COUNT>,
126            buffer: ReadBuffer<$rate>,
127        }
128
129        impl XofReader for $reader_name {
130            #[inline]
131            fn read(&mut self, buf: &mut [u8]) {
132                let Self { core, buffer } = self;
133                buffer.read(buf, |block| {
134                    *block = core.read_block();
135                });
136            }
137        }
138
139        impl fmt::Debug for $reader_name {
140            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141                f.write_str(concat!(stringify!($reader_name), " { ... }"))
142            }
143        }
144    };
145}
146
147impl_shake!(Shake128, Shake128Reader, U168, "SHAKE128");
148impl_shake!(Shake256, Shake256Reader, U136, "SHAKE256");
149
150// `digest::CollisionResistance` is documented as "Collision resistance in BYTES ... applies to
151// an output size of at least `2 * CollisionResistance` bytes" (digest 0.11.3, src/lib.rs:230).
152// These previously carried U168 and U136 — the SHAKE sponge *rates*, copied from the
153// `impl_shake!` lines above — which overstated both by roughly 10x. See card `t_c6851177`.
154//
155// FIPS 202 Table 4 caps SHAKE128 collision resistance at 128 bits and SHAKE256 at 256 bits, so
156// the byte counts are 16 and 32. Every other `CollisionResistance` impl in this workspace and
157// in the RustCrypto reference already uses these values.
158impl CollisionResistance for Shake128 {
159    // https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf#page=31 — 128 bits / 8.
160    type CollisionResistance = U16;
161}
162
163impl CollisionResistance for Shake256 {
164    // https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf#page=31 — 256 bits / 8.
165    type CollisionResistance = U32;
166}
167
168#[cfg(test)]
169mod tests {
170    use digest::CollisionResistance;
171    use digest::consts::{
172        U16,
173        U32,
174    };
175    use digest::typenum::Unsigned;
176
177    use super::{
178        Shake128,
179        Shake256,
180    };
181
182    /// Pin the collision-resistance byte counts so the rate constants cannot drift back in.
183    ///
184    /// The defect this guards was a type-level overclaim with no runtime effect and no in-tree
185    /// consumer, so nothing else in the suite could have caught it: a workspace-wide grep for
186    /// `::CollisionResistance` finds no code that binds on or branches over the associated
187    /// type. That is exactly why it needs an explicit assertion rather than being left to be
188    /// noticed.
189    #[test]
190    fn collision_resistance_is_in_bytes_not_the_sponge_rate() {
191        assert_eq!(
192            <Shake128 as CollisionResistance>::CollisionResistance::USIZE,
193            U16::USIZE,
194            "SHAKE128 collision resistance must be 16 bytes (128 bits), not the 168-byte rate"
195        );
196        assert_eq!(
197            <Shake256 as CollisionResistance>::CollisionResistance::USIZE,
198            U32::USIZE,
199            "SHAKE256 collision resistance must be 32 bytes (256 bits), not the 136-byte rate"
200        );
201
202        // Positive control: the two values must also differ from each other, so a change that
203        // collapsed both to one constant could not satisfy the asserts above by accident.
204        assert_ne!(U16::USIZE, U32::USIZE);
205    }
206}