Skip to main content

lib_q_sha3/
cshake.rs

1//! cSHAKE-128 and cSHAKE-256 per [NIST SP 800-185](https://csrc.nist.gov/pubs/sp/800/185/final).
2//!
3//! The **function name** and **customization string** (NIST “N” and “S”) are encoded in the sponge state before message data. If both are empty, cSHAKE reduces to SHAKE for that rate (per the standard). The high-level types [`CShake128`](crate::CShake128) and [`CShake256`](crate::CShake256) are re-exported at the crate root.
4//!
5//! Internal **core** types (`CShake128Core`, `CShake256Core`) support [`digest::common::hazmat::SerializableState`](https://docs.rs/digest/latest/digest/common/hazmat/trait.SerializableState.html) for advanced use; prefer the façade types for normal hashing.
6
7use core::fmt;
8
9use block_buffer::LazyBuffer;
10use digest::block_api::{
11    AlgorithmName,
12    Block,
13    BlockSizeUser,
14    Buffer,
15    BufferKindUser,
16    Eager,
17    ExtendableOutputCore,
18    UpdateCore,
19};
20use digest::common::hazmat::{
21    DeserializeStateError,
22    SerializableState,
23    SerializedState,
24};
25use digest::consts::{
26    U16,
27    U32,
28    U136,
29    U168,
30    U400,
31};
32use digest::typenum::Unsigned;
33use digest::{
34    CollisionResistance,
35    CustomizedInit,
36    HashMarker,
37    Reset,
38};
39
40use crate::block_api::xor_block;
41use crate::{
42    CSHAKE_PAD,
43    DEFAULT_ROUND_COUNT as ROUNDS,
44    PLEN,
45    SHAKE_PAD,
46    SpongeReaderCore,
47};
48
49#[inline]
50fn plen_little_endian_from_serialized_bytes(
51    src: &[u8],
52) -> Result<[u64; PLEN], DeserializeStateError> {
53    let (lanes, remainder) = src.as_chunks::<8>();
54    if !remainder.is_empty() || lanes.len() != PLEN {
55        return Err(DeserializeStateError);
56    }
57    Ok(core::array::from_fn(|i| u64::from_le_bytes(lanes[i])))
58}
59
60macro_rules! impl_cshake {
61    (
62        $name:ident, $full_name:ident, $reader_name:ident, $rate:ident, $alg_name:expr
63    ) => {
64        #[doc = $alg_name]
65        #[doc = " core hasher."]
66        #[derive(Clone, Default)]
67        pub struct $name {
68            state: [u64; PLEN],
69            initial_state: [u64; PLEN],
70        }
71
72        impl $name {
73            /// Creates a new CSHAKE instance with the given function name and customization.
74            ///
75            /// Note that the function name is intended for use by NIST and should only be set to
76            /// values defined by NIST. You probably don't need to use this function.
77            pub fn new_with_function_name(function_name: &[u8], customization: &[u8]) -> Self {
78                let mut state = Self::default();
79
80                if function_name.is_empty() && customization.is_empty() {
81                    return state;
82                }
83
84                #[inline(always)]
85                pub(crate) fn left_encode(val: u64, b: &mut [u8; 9]) -> &[u8] {
86                    b[1..].copy_from_slice(&val.to_be_bytes());
87                    let i = b[1..8].iter().take_while(|&&a| a == 0).count();
88                    b[i] = (8 - i) as u8;
89                    &b[i..]
90                }
91
92                // `LazyBuffer` (not `Eager`) for init: when byte-pad output is block-aligned,
93                // an extra full zero block must not be absorbed (RustCrypto/hashes#834).
94                let mut buffer: LazyBuffer<$rate> = Default::default();
95                let mut b = [0u8; 9];
96                buffer.digest_blocks(left_encode($rate::to_u64(), &mut b), |blocks| {
97                    state.update_blocks(blocks)
98                });
99                let mut encode_str = |str: &[u8]| {
100                    let str_bits_len = 8 * (str.len() as u64);
101                    let encoded_len = left_encode(str_bits_len, &mut b);
102                    buffer.digest_blocks(encoded_len, |blocks| state.update_blocks(blocks));
103                    buffer.digest_blocks(str, |blocks| state.update_blocks(blocks));
104                };
105                encode_str(function_name);
106                encode_str(customization);
107                state.update_blocks(&[buffer.pad_with_zeros()]);
108                state.initial_state = state.state;
109                state
110            }
111        }
112
113        impl CustomizedInit for $name {
114            #[inline]
115            fn new_customized(customization: &[u8]) -> Self {
116                Self::new_with_function_name(&[], customization)
117            }
118        }
119
120        impl BufferKindUser for $name {
121            type BufferKind = Eager;
122        }
123
124        impl HashMarker for $name {}
125
126        impl BlockSizeUser for $name {
127            type BlockSize = $rate;
128        }
129
130        impl UpdateCore for $name {
131            #[inline]
132            fn update_blocks(&mut self, blocks: &[Block<Self>]) {
133                for block in blocks {
134                    xor_block(&mut self.state, block);
135                    lib_q_keccak::p1600(&mut self.state, ROUNDS);
136                }
137            }
138        }
139
140        impl ExtendableOutputCore for $name {
141            type ReaderCore = SpongeReaderCore<$rate>;
142
143            #[inline]
144            fn finalize_xof_core(&mut self, buffer: &mut Buffer<Self>) -> Self::ReaderCore {
145                let pos = buffer.get_pos();
146                let mut block = buffer.pad_with_zeros();
147                let pad = if self.initial_state == [0; PLEN] {
148                    SHAKE_PAD
149                } else {
150                    CSHAKE_PAD
151                };
152                block[pos] = pad;
153                let n = block.len();
154                block[n - 1] |= 0x80;
155
156                xor_block(&mut self.state, &block);
157                lib_q_keccak::p1600(&mut self.state, ROUNDS);
158
159                SpongeReaderCore::new(&self.state)
160            }
161        }
162
163        impl Reset for $name {
164            #[inline]
165            fn reset(&mut self) {
166                self.state = self.initial_state;
167            }
168        }
169
170        impl AlgorithmName for $name {
171            fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
172                f.write_str($alg_name)
173            }
174        }
175
176        impl fmt::Debug for $name {
177            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
178                f.write_str(concat!(stringify!($name), " { ... }"))
179            }
180        }
181
182        impl Drop for $name {
183            fn drop(&mut self) {
184                #[cfg(feature = "zeroize")]
185                {
186                    use digest::zeroize::Zeroize;
187                    self.state.zeroize();
188                    self.initial_state.zeroize();
189                }
190            }
191        }
192
193        #[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))]
194        #[cfg(feature = "zeroize")]
195        impl digest::zeroize::ZeroizeOnDrop for $name {}
196
197        impl SerializableState for $name {
198            type SerializedStateSize = U400;
199
200            fn serialize(&self) -> SerializedState<Self> {
201                let mut serialized_state = SerializedState::<Self>::default();
202                let mut chunks = serialized_state.chunks_exact_mut(8);
203
204                for (val, chunk) in self.state.iter().zip(&mut chunks) {
205                    chunk.copy_from_slice(&val.to_le_bytes());
206                }
207                for (val, chunk) in self.initial_state.iter().zip(&mut chunks) {
208                    chunk.copy_from_slice(&val.to_le_bytes());
209                }
210
211                serialized_state
212            }
213
214            fn deserialize(
215                serialized_state: &SerializedState<Self>,
216            ) -> Result<Self, DeserializeStateError> {
217                let bytes: &[u8] = serialized_state.as_ref();
218                let (state_src, initial_state_src) = bytes
219                    .split_at_checked(200)
220                    .ok_or(DeserializeStateError)?;
221                Ok(Self {
222                    state: plen_little_endian_from_serialized_bytes(state_src)?,
223                    initial_state: plen_little_endian_from_serialized_bytes(
224                        initial_state_src,
225                    )?,
226                })
227            }
228        }
229
230        digest::buffer_xof!(
231            #[doc = $alg_name]
232            #[doc = " hasher."]
233            pub struct $full_name($name);
234            // `XofHasherTraits`+`CustomizedInit` is not used here: digest’s wrapper `SerializableState`
235            // for that path includes the `block_buffer` queue and **does not** match the
236            // core-only `U400` layout that downstream crates (e.g. `lib-q-hash` KMAC / ParallelHash)
237            // expect when they delegate `serialize` to `CShake*`. See RustCrypto/hashes#834 and the
238            // `LazyBuffer` / `Eager` split in `new_with_function_name` above.
239            impl: Debug AlgorithmName Clone Default BlockSizeUser CoreProxy HashMarker Update Reset ExtendableOutputReset CustomizedInit;
240            #[doc = $alg_name]
241            #[doc = " XOF reader."]
242            pub struct $reader_name(SpongeReaderCore<$rate>);
243            impl: XofReaderTraits;
244        );
245
246        impl $full_name {
247            /// Creates a new cSHAKE instance with the given function name and customization.
248            ///
249            /// Note that the function name is intended for use by NIST and should only be set to
250            /// values defined by NIST. You probably don't need to use this function.
251            pub fn new_with_function_name(function_name: &[u8], customization: &[u8]) -> Self {
252                Self {
253                    core: $name::new_with_function_name(function_name, customization),
254                    buffer: Default::default(),
255                }
256            }
257        }
258    };
259}
260
261impl_cshake!(CShake128Core, CShake128, CShake128Reader, U168, "cSHAKE128");
262impl_cshake!(CShake256Core, CShake256, CShake256Reader, U136, "cSHAKE256");
263
264/// Core-only [`SerializedState`](SerializableState) for the public types: the `block_buffer` is
265/// cleared on deserialize. **Do not** use this to snapshot mid-stream if the rate buffer holds
266/// unprocessed bytes (finish a full rate block or use the core type). Layout matches
267/// `lib-q-hash` KMAC / ParallelHash delegation (`U400`).
268impl SerializableState for CShake128 {
269    type SerializedStateSize = U400;
270
271    fn serialize(&self) -> SerializedState<Self> {
272        self.core.serialize()
273    }
274
275    fn deserialize(
276        serialized_state: &SerializedState<Self>,
277    ) -> Result<Self, DeserializeStateError> {
278        let core = CShake128Core::deserialize(serialized_state)?;
279        Ok(Self {
280            core,
281            buffer: Default::default(),
282        })
283    }
284}
285
286impl SerializableState for CShake256 {
287    type SerializedStateSize = U400;
288
289    fn serialize(&self) -> SerializedState<Self> {
290        self.core.serialize()
291    }
292
293    fn deserialize(
294        serialized_state: &SerializedState<Self>,
295    ) -> Result<Self, DeserializeStateError> {
296        let core = CShake256Core::deserialize(serialized_state)?;
297        Ok(Self {
298            core,
299            buffer: Default::default(),
300        })
301    }
302}
303
304impl CollisionResistance for CShake128 {
305    // https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-185.pdf#[{"num":68,"gen":0},{"name":"XYZ"},108,440,null]
306    type CollisionResistance = U16;
307}
308
309impl CollisionResistance for CShake256 {
310    // https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-185.pdf#[{"num":68,"gen":0},{"name":"XYZ"},108,440,null]
311    type CollisionResistance = U32;
312}