Skip to main content

bouncycastle_sha3/
shake.rs

1use crate::SHAKEParams;
2use crate::keccak::{
3    KeccakInternal, KeccakSize, SHA3_FAMILY_STATE_LEN, SUSPENDED_SHA3_STATE_LEN,
4    deserialize_sha3_family_state, serialize_sha3_family_state,
5};
6use bouncycastle_core::errors::{HashError, KDFError, SuspendableError};
7use bouncycastle_core::key_material;
8use bouncycastle_core::key_material::{KeyMaterial, KeyMaterialTrait, KeyType};
9use bouncycastle_core::suspendable_state::{add_lib_ver, check_lib_ver};
10use bouncycastle_core::traits::{Algorithm, KDF, SecurityStrength, Suspendable, XOF};
11use bouncycastle_utils::{max, min};
12
13/// Internal struct for SHAKE.
14/// This uses a private bound so that you cannot instantiate it directly and have to use the
15/// provided and NIST-approved parameters.
16///
17/// Note that even though SHAKE is physically capable of acting as a hash function, and in fact is secure
18/// as such if the provided message includes the requested length, SHAKE does not implement the [`Hash`] trait.
19/// FIPS 202 section 7 states:
20///
21///   "SHAKE128 and SHAKE256 are approved XOFs, whose approved uses will be specified in
22/// NIST Special Publications. Although some of those uses may overlap with the uses of approved
23/// hash functions, the XOFs are not approved as hash functions, due to the property that is
24/// discussed in Sec. A.2."
25///
26/// Section A.2 describes how SHAKE does not internally diversify its output based on the requested length.
27/// For example, the first 32 bytes of SHAKE128("message", 64) and SHAKE128("message", 128), will be identical
28/// and equal to SHAKE128("message", 32). Proper hash functions don't do this, and NIST is concerned that
29/// this could lead to application vulnerabilities.
30#[derive(Clone)]
31pub struct SHAKEInternal<PARAMS: SHAKEParams> {
32    _phantomdata: core::marker::PhantomData<PARAMS>,
33    keccak: KeccakInternal,
34    kdf_key_type: KeyType,
35    kdf_security_strength: SecurityStrength,
36    kdf_entropy: usize,
37}
38
39impl<PARAMS: SHAKEParams> Algorithm for SHAKEInternal<PARAMS> {
40    const ALG_NAME: &'static str = PARAMS::ALG_NAME;
41    const MAX_SECURITY_STRENGTH: SecurityStrength = PARAMS::MAX_SECURITY_STRENGTH;
42}
43
44impl<PARAMS: SHAKEParams> SHAKEInternal<PARAMS> {
45    /// Get a new SHA3 instance, ready for use.
46    pub fn new() -> Self {
47        Self {
48            _phantomdata: core::marker::PhantomData,
49            keccak: KeccakInternal::new(PARAMS::SIZE),
50            kdf_key_type: KeyType::Zeroized,
51            kdf_security_strength: SecurityStrength::None,
52            kdf_entropy: 0,
53        }
54    }
55
56    /// Swallows errors and simply returns an empty Vec<u8> if the hashes fails for whatever reason.
57    fn hash_internal(mut self, data: &[u8], result_len: usize) -> Vec<u8> {
58        // Infallible: this is the only absorb, and it precedes the squeeze below.
59        self.absorb(data).expect("absorb precedes squeeze on a fresh SHAKE");
60        self.squeeze(result_len)
61    }
62
63    fn hash_internal_out(mut self, data: &[u8], output: &mut [u8]) -> usize {
64        output.fill(0);
65
66        // Infallible: this is the only absorb, and it precedes the squeeze below.
67        self.absorb(data).expect("absorb precedes squeeze on a fresh SHAKE");
68        self.squeeze_out(output)
69    }
70
71    fn mix_key_internal(&mut self, key: &impl KeyMaterialTrait) {
72        // track the strongest input key type
73        self.kdf_key_type = *max(&self.kdf_key_type, &key.key_type());
74
75        // track input entropy
76        if key.is_full_entropy() {
77            self.kdf_entropy += key.key_len();
78            self.kdf_security_strength =
79                max(&self.kdf_security_strength, &key.security_strength()).clone();
80            self.kdf_security_strength = min(
81                &self.kdf_security_strength,
82                &SecurityStrength::from_bits(PARAMS::SIZE as usize),
83            )
84            .clone();
85        }
86
87        // Infallible: mix_key_internal is only called during the absorb phase, before any squeeze.
88        self.absorb(key.ref_to_bytes()).expect("absorb precedes squeeze during key mixing");
89    }
90
91    fn derive_key_final_internal(
92        mut self,
93        additional_input: &[u8],
94    ) -> Result<Box<dyn KeyMaterialTrait>, KDFError> {
95        // At the moment, oversized KeyMaterial is returned for most cases. 
96        let mut output_key = KeyMaterial::<64>::new();
97        self.derive_key_out_final_internal(additional_input, &mut output_key)?;
98
99        // truncate
100        // 128 => 32, 256 => 64
101        match PARAMS::SIZE {
102            KeccakSize::_128 => output_key.set_key_len(32).expect("truncate should be infallible"),
103            KeccakSize::_256 => output_key.set_key_len(64).expect("truncate should be infallible"),
104            _ => unreachable!(),
105        }
106        Ok(Box::new(output_key))
107    }
108
109    fn derive_key_out_final_internal(
110        &mut self,
111        additional_input: &[u8],
112        output_key: &mut impl KeyMaterialTrait,
113    ) -> Result<usize, KDFError> {
114        // For the KDF to be considered "fully-seeded" and be capable of outputting full-entropy KeyMaterials,
115        // it requires full-entropy input that is at least 2x the bit size (ie 256 bits for SHAKE128, and 512 bits for SHAKE256).
116        // TODO: citation needed (NIST)
117        // TODO: The intuition behind this is that SHAKE256 and SHA3-256 are both KECCAK[512], and SHAKE128 is KECCAK[256],
118        // TODO: However, it is necessary to find an actual reference for this "fully-seeded" threshold.
119        if self.kdf_entropy < 2 * (PARAMS::SIZE as usize) / 8 {
120            self.kdf_key_type = min(&self.kdf_key_type, &KeyType::Unknown).clone();
121            self.kdf_security_strength = SecurityStrength::None; // BytesLowEntropy can't have a securtiy level.
122        }
123
124        // Infallible: additional_input is absorbed before the squeeze below, and this method is only
125        // reached during the absorb phase.
126        self.absorb(additional_input).expect("absorb precedes squeeze during key derivation");
127
128        let mut bytes_written: usize = 0;
129        key_material::do_hazardous_operations(output_key, |output_key| {
130            bytes_written = self.squeeze_out(
131                output_key.ref_to_bytes_mut().expect("Infallible within do_hazardous_operations"),
132            );
133            output_key.set_key_len(bytes_written)
134        })?;
135
136        // since computation has been performed, the result will not actually be zeroized, even if all input key material was zeroized.
137        if self.kdf_key_type == KeyType::Zeroized {
138            self.kdf_key_type = KeyType::Unknown;
139        }
140        key_material::do_hazardous_operations(output_key, |output_key| {
141            output_key.set_key_type(self.kdf_key_type)?;
142            output_key.set_security_strength(
143                min(&self.kdf_security_strength, &SecurityStrength::from_bits(bytes_written * 8))
144                    .clone(),
145            )
146        })?;
147        Ok(bytes_written)
148    }
149}
150
151impl<PARAMS: SHAKEParams> Suspendable<SUSPENDED_SHA3_STATE_LEN> for SHAKEInternal<PARAMS> {
152    fn suspend(self) -> [u8; SUSPENDED_SHA3_STATE_LEN] {
153        let mut out_to_return = [0u8; SUSPENDED_SHA3_STATE_LEN];
154
155        // insert the version tag
156        let out: &mut [u8; SHA3_FAMILY_STATE_LEN] =
157            add_lib_ver(&mut out_to_return).try_into().unwrap();
158
159        serialize_sha3_family_state(
160            out,
161            PARAMS::STATE_TAG,
162            &self.keccak,
163            self.kdf_key_type,
164            self.kdf_security_strength,
165            self.kdf_entropy,
166        );
167
168        out_to_return
169    }
170
171    fn from_suspended(
172        serialized_state: [u8; SUSPENDED_SHA3_STATE_LEN],
173    ) -> Result<Self, SuspendableError> {
174        // check the version tag. At the moment, we have no not_before version to specify.
175        let input: &[u8; SHA3_FAMILY_STATE_LEN] =
176            check_lib_ver(&serialized_state, None)?.try_into().unwrap();
177
178        // The variant tag rejects states from any other SHA3/SHAKE variant; the rate is then the
179        // correct one to rebuild with (both are fully determined by the algorithm parameters).
180        let rate = 1600 - ((PARAMS::SIZE as usize) << 1);
181        let (keccak, kdf_key_type, kdf_security_strength, kdf_entropy) =
182            deserialize_sha3_family_state(input, PARAMS::STATE_TAG, rate)?;
183
184        Ok(SHAKEInternal {
185            _phantomdata: core::marker::PhantomData,
186            keccak,
187            kdf_key_type,
188            kdf_security_strength,
189            kdf_entropy,
190        })
191    }
192}
193
194impl<PARAMS: SHAKEParams> KDF for SHAKEInternal<PARAMS> {
195    /// Returns a [`KeyMaterial`].
196    /// For the KDF to be considered "fully-seeded" and be capable of outputting full-entropy KeyMaterials,
197    /// it requires full-entropy input that is at least 2x the bit size (ie 256 bits for SHAKE128, and 512 bits for SHAKE256).
198    /// Returns a 32 byte key for SHAKE128 and a 64 byte key for SHAKE256.
199    /// To produce longer keys, use [`KDF::derive_key_out`].
200    /// To produce shorter keys, either use [`KDF::derive_key_out`], truncate this result in place with
201    /// [`KeyMaterial::set_key_len`], or copy it into a smaller [`KeyMaterial`] with
202    /// [`KeyMaterialTrait::truncate`].
203    fn derive_key(
204        mut self,
205        key: &impl KeyMaterialTrait,
206        additional_input: &[u8],
207    ) -> Result<Box<dyn KeyMaterialTrait>, KDFError> {
208        // self.derive_key_from_multiple(&[key], additional_input)
209        self.mix_key_internal(key);
210        self.derive_key_final_internal(additional_input)
211    }
212
213    fn derive_key_out(
214        mut self,
215        key: &impl KeyMaterialTrait,
216        additional_input: &[u8],
217        output_key: &mut impl KeyMaterialTrait,
218    ) -> Result<usize, KDFError> {
219        // self.derive_key_from_multiple_out(&[key], additional_input, output)
220        self.mix_key_internal(key);
221        self.derive_key_out_final_internal(additional_input, output_key)
222    }
223
224    /// Always returns a full [`KeyMaterial`]; ie that fills the internal buffer of the
225    /// appropriately-sized key material for the underlying cryptographic hash function.
226    /// This can be truncated down in place with [`KeyMaterial::set_key_len`], or copied into a smaller
227    /// [`KeyMaterial`] with [`KeyMaterialTrait::truncate`].
228    /// Returns a 32 byte key for SHAKE128 and a 64 byte key for SHAKE256.
229    /// To produce longer keys, use [`KDF::derive_key_out`].
230    /// To produce shorter keys, either use [`KDF::derive_key_out`], truncate this result in place with
231    /// [`KeyMaterial::set_key_len`], or copy it into a smaller [`KeyMaterial`] with
232    /// [`KeyMaterialTrait::truncate`].
233    fn derive_key_from_multiple(
234        mut self,
235        keys: &[&impl KeyMaterialTrait],
236        additional_input: &[u8],
237    ) -> Result<Box<dyn KeyMaterialTrait>, KDFError> {
238        for key in keys {
239            self.mix_key_internal(*key);
240        }
241        self.derive_key_final_internal(additional_input)
242    }
243
244    fn derive_key_from_multiple_out(
245        mut self,
246        keys: &[&impl KeyMaterialTrait],
247        additional_input: &[u8],
248        output_key: &mut impl KeyMaterialTrait,
249    ) -> Result<usize, KDFError> {
250        for key in keys {
251            self.mix_key_internal(*key);
252        }
253        self.derive_key_out_final_internal(additional_input, output_key)
254    }
255
256    fn max_security_strength(&self) -> SecurityStrength {
257        SecurityStrength::from_bits(PARAMS::SIZE as usize)
258    }
259}
260
261impl<PARAMS: SHAKEParams> Default for SHAKEInternal<PARAMS> {
262    fn default() -> Self {
263        Self::new()
264    }
265}
266
267impl<PARAMS: SHAKEParams> XOF for SHAKEInternal<PARAMS> {
268    fn hash_xof(self, data: &[u8], result_len: usize) -> Vec<u8> {
269        self.hash_internal(data, result_len)
270    }
271
272    fn hash_xof_out(self, data: &[u8], output: &mut [u8]) -> usize {
273        output.fill(0);
274
275        self.hash_internal_out(data, output)
276    }
277
278    /// This can throw a [`HashError::InvalidState`] if called after squeezing has begun,
279    /// but is safe to consider infallible otherwise -- IE feel free to use `.unwrap()` or `.expect()`
280    /// on the result if you are confident that your code cannot call `absorb` after squeezing.
281    ///
282    /// A rejected call leaves the SHAKE object untouched so the output stream continues consistently.
283    /// IE it is safe to attempt to feed in more input and do nothing if the absorb fails
284    /// ("safe" in the sense that it won't panic, but it may still produce an incorrect output which
285    /// could be insecure in the sense of being predictable or low-entropy).
286    fn absorb(&mut self, data: &[u8]) -> Result<(), HashError> {
287        // A sponge XOF cannot return to absorbing once squeezing has begun (FIPS 202 defines SHAKE as
288        // a single function of the whole message; re-absorbing would be an unapproved duplex).
289        if self.keccak.squeezing {
290            return Err(HashError::InvalidState("cannot absorb after squeezing has begun"));
291        }
292        self.keccak.absorb(data);
293        Ok(())
294    }
295
296    /// Switches to squeezing.
297    fn absorb_last_partial_byte(
298        &mut self,
299        partial_byte: u8,
300        num_partial_bits: usize,
301    ) -> Result<(), HashError> {
302        // Same phase rule as absorb(): reject a partial-byte absorb once squeezing has begun. Checked
303        // before any state mutation so a rejected call leaves the sponge untouched.
304        if self.keccak.squeezing {
305            return Err(HashError::InvalidState("cannot absorb after squeezing has begun"));
306        }
307        if !(1..=7).contains(&num_partial_bits) {
308            return Err(HashError::InvalidLength("must be in the range [0,7]"));
309        }
310        // Mutants note: This is just bit-setting into empty space. 
311        // It works the same regardless of whether it's OR or XOR.
312        let mut final_input: u16 =
313            ((partial_byte as u16) & ((1 << num_partial_bits) - 1)) | (0x0F << num_partial_bits);
314        let mut final_bits = num_partial_bits + 4;
315
316        if final_bits >= 8 {
317            self.keccak.absorb(&[final_input as u8]);
318            final_bits -= 8;
319            final_input >>= 8;
320        }
321
322        // Infallible: guarded above (not squeezing), the queue is byte-aligned here, and final_bits is
323        // in 0..=7 by construction.
324        self.keccak.absorb_bits(final_input as u8, final_bits).expect("Absorb failed.");
325
326        Ok(())
327    }
328
329    fn squeeze(&mut self, num_bytes: usize) -> Vec<u8> {
330        let mut out: Vec<u8> = vec![0u8; num_bytes];
331        self.squeeze_out(&mut out);
332        out
333    }
334
335    fn squeeze_out(&mut self, output: &mut [u8]) -> usize {
336        output.fill(0);
337
338        if !self.keccak.squeezing {
339            self.keccak.absorb_bits(0x0F, 4).expect("Absorb_bits failed");
340        };
341
342        self.keccak.squeeze(output)
343    }
344
345    fn squeeze_partial_byte_final(self, num_bits: usize) -> Result<u8, HashError> {
346        let mut output: u8 = 0;
347        self.squeeze_partial_byte_final_out(num_bits, &mut output)?;
348        Ok(output)
349    }
350
351    /// Result is the number of bits squezed into `output`.
352    fn squeeze_partial_byte_final_out(
353        mut self,
354        num_bits: usize,
355        output: &mut u8,
356    ) -> Result<(), HashError> {
357        if !(1..=7).contains(&num_bits) {
358            return Err(HashError::InvalidLength("must be in the range [0,7]"));
359        }
360
361        *output = 0;
362
363        let mut buf = [0u8; 1];
364        self.keccak.squeeze(&mut buf);
365        *output = buf[0] >> 8 - num_bits;
366        Ok(())
367    }
368
369    fn max_security_strength(&self) -> SecurityStrength {
370        SecurityStrength::from_bits(PARAMS::SIZE as usize)
371    }
372}