Skip to main content

sha3/
cshake.rs

1use crate::{CSHAKE_PAD, SHAKE_PAD, Sha3ReaderCore, block_api::xor_block};
2use core::fmt;
3use digest::{
4    CollisionResistance, CustomizedInit, HashMarker, Reset,
5    block_api::{
6        AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, Eager, ExtendableOutputCore,
7        UpdateCore,
8    },
9    common::hazmat::{DeserializeStateError, SerializableState, SerializedState},
10    consts::{U16, U32, U136, U168, U400},
11    typenum::Unsigned,
12};
13use keccak::{F1600_ROUNDS as ROUNDS, Keccak, State1600};
14
15macro_rules! impl_cshake {
16    (
17        $name:ident, $full_name:ident, $reader_name:ident, $rate:ident, $alg_name:expr
18    ) => {
19        #[doc = $alg_name]
20        #[doc = " core hasher."]
21        #[derive(Clone, Default)]
22        pub struct $name {
23            state: State1600,
24            initial_state: State1600,
25            keccak: Keccak,
26        }
27
28        impl $name {
29            /// Creates a new CSHAKE instance with the given function name and customization.
30            ///
31            /// Note that the function name is intended for use by NIST and should only be set to
32            /// values defined by NIST. You probably don't need to use this function.
33            pub fn new_with_function_name(function_name: &[u8], customization: &[u8]) -> Self {
34                let mut state = Self::default();
35
36                if function_name.is_empty() && customization.is_empty() {
37                    return state;
38                }
39
40                #[inline(always)]
41                pub(crate) fn left_encode(val: u64, b: &mut [u8; 9]) -> &[u8] {
42                    b[1..].copy_from_slice(&val.to_be_bytes());
43                    let i = b[1..8].iter().take_while(|&&a| a == 0).count();
44                    b[i] = (8 - i) as u8;
45                    &b[i..]
46                }
47
48                let mut buffer = Buffer::<Self>::default();
49                let mut b = [0u8; 9];
50                buffer.digest_blocks(left_encode($rate::to_u64(), &mut b), |blocks| {
51                    state.update_blocks(blocks)
52                });
53                buffer.digest_blocks(
54                    left_encode(8 * (function_name.len() as u64), &mut b),
55                    |blocks| state.update_blocks(blocks),
56                );
57                buffer.digest_blocks(function_name, |blocks| state.update_blocks(blocks));
58                buffer.digest_blocks(
59                    left_encode(8 * (customization.len() as u64), &mut b),
60                    |blocks| state.update_blocks(blocks),
61                );
62                buffer.digest_blocks(customization, |blocks| state.update_blocks(blocks));
63                state.update_blocks(&[buffer.pad_with_zeros()]);
64                state.initial_state = state.state;
65                state
66            }
67        }
68
69        impl CustomizedInit for $name {
70            #[inline]
71            fn new_customized(customization: &[u8]) -> Self {
72                Self::new_with_function_name(&[], customization)
73            }
74        }
75
76        impl BufferKindUser for $name {
77            type BufferKind = Eager;
78        }
79
80        impl HashMarker for $name {}
81
82        impl BlockSizeUser for $name {
83            type BlockSize = $rate;
84        }
85
86        impl UpdateCore for $name {
87            #[inline]
88            fn update_blocks(&mut self, blocks: &[Block<Self>]) {
89                self.keccak.with_p1600::<ROUNDS>(|p1600| {
90                    for block in blocks {
91                        xor_block(&mut self.state, block);
92                        p1600(&mut self.state);
93                    }
94                })
95            }
96        }
97
98        impl ExtendableOutputCore for $name {
99            type ReaderCore = Sha3ReaderCore<$rate>;
100
101            #[inline]
102            fn finalize_xof_core(&mut self, buffer: &mut Buffer<Self>) -> Self::ReaderCore {
103                let pos = buffer.get_pos();
104                let mut block = buffer.pad_with_zeros();
105                let pad = if self.initial_state == State1600::default() {
106                    SHAKE_PAD
107                } else {
108                    CSHAKE_PAD
109                };
110                block[pos] = pad;
111                let n = block.len();
112                block[n - 1] |= 0x80;
113
114                self.keccak.with_p1600::<ROUNDS>(|p1600| {
115                    xor_block(&mut self.state, &block);
116                    p1600(&mut self.state);
117                });
118
119                Sha3ReaderCore::new(&self.state, self.keccak)
120            }
121        }
122
123        impl Reset for $name {
124            #[inline]
125            fn reset(&mut self) {
126                self.state = self.initial_state;
127            }
128        }
129
130        impl AlgorithmName for $name {
131            fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
132                f.write_str($alg_name)
133            }
134        }
135
136        impl fmt::Debug for $name {
137            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138                f.write_str(concat!(stringify!($name), " { ... }"))
139            }
140        }
141
142        impl Drop for $name {
143            fn drop(&mut self) {
144                #[cfg(feature = "zeroize")]
145                {
146                    use digest::zeroize::Zeroize;
147                    self.state.zeroize();
148                    self.initial_state.zeroize();
149                }
150            }
151        }
152
153        #[cfg(feature = "zeroize")]
154        impl digest::zeroize::ZeroizeOnDrop for $name {}
155
156        impl SerializableState for $name {
157            type SerializedStateSize = U400;
158
159            fn serialize(&self) -> SerializedState<Self> {
160                let mut serialized_state = SerializedState::<Self>::default();
161                let mut chunks = serialized_state.chunks_exact_mut(8);
162
163                for (val, chunk) in self.state.iter().zip(&mut chunks) {
164                    chunk.copy_from_slice(&val.to_le_bytes());
165                }
166                for (val, chunk) in self.initial_state.iter().zip(&mut chunks) {
167                    chunk.copy_from_slice(&val.to_le_bytes());
168                }
169
170                serialized_state
171            }
172
173            fn deserialize(
174                serialized_state: &SerializedState<Self>,
175            ) -> Result<Self, DeserializeStateError> {
176                let (state_src, initial_state_src) = serialized_state.split_at(200);
177                let state = core::array::from_fn(|i| {
178                    let chunk = state_src[8 * i..][..8].try_into().unwrap();
179                    u64::from_le_bytes(chunk)
180                });
181                let initial_state = core::array::from_fn(|i| {
182                    let chunk = initial_state_src[8 * i..][..8].try_into().unwrap();
183                    u64::from_le_bytes(chunk)
184                });
185                Ok(Self{ state, initial_state, keccak: Keccak::new() })
186            }
187        }
188
189        digest::buffer_xof!(
190            #[doc = $alg_name]
191            #[doc = " hasher."]
192            pub struct $full_name($name);
193            // TODO: Use `XofHasherTraits CustomizedInit` after serialization for buffers is fixed
194            impl: Debug AlgorithmName Clone Default BlockSizeUser CoreProxy HashMarker Update Reset ExtendableOutputReset CustomizedInit;
195            #[doc = $alg_name]
196            #[doc = " XOF reader."]
197            pub struct $reader_name(Sha3ReaderCore<$rate>);
198            impl: XofReaderTraits;
199        );
200
201        impl $full_name {
202            /// Creates a new cSHAKE instance with the given function name and customization.
203            ///
204            /// Note that the function name is intended for use by NIST and should only be set to
205            /// values defined by NIST. You probably don't need to use this function.
206            pub fn new_with_function_name(function_name: &[u8], customization: &[u8]) -> Self {
207                Self {
208                    core: $name::new_with_function_name(function_name, customization),
209                    buffer: Default::default(),
210                }
211            }
212        }
213    };
214}
215
216impl_cshake!(CShake128Core, CShake128, CShake128Reader, U168, "cSHAKE128");
217impl_cshake!(CShake256Core, CShake256, CShake256Reader, U136, "cSHAKE256");
218
219impl CollisionResistance for CShake128 {
220    // https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-185.pdf#[{"num":68,"gen":0},{"name":"XYZ"},108,440,null]
221    type CollisionResistance = U16;
222}
223
224impl CollisionResistance for CShake256 {
225    // https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-185.pdf#[{"num":68,"gen":0},{"name":"XYZ"},108,440,null]
226    type CollisionResistance = U32;
227}