Skip to main content

bouncycastle_sha3/
shake.rs

1use bouncycastle_core_interface::errors::{HashError, KDFError};
2use bouncycastle_core_interface::key_material::{KeyMaterialInternal, KeyType};
3use bouncycastle_core_interface::traits::{Algorithm, KeyMaterial, SecurityStrength, KDF, XOF};
4use bouncycastle_utils::{max, min};
5use crate::keccak::KeccakDigest;
6use crate::SHAKEParams;
7
8
9/// Note: FIPS 202 section 7 states:
10///
11///   "SHAKE128 and SHAKE256 are approved XOFs, whose approved uses will be specified in
12/// NIST Special Publications. Although some of those uses may overlap with the uses of approved
13/// hash functions, the XOFs are not approved as hash functions, due to the property that is
14/// discussed in Sec. A.2."
15///
16/// Section A.2 describes how SHAKE does not internally diversify its output based on the requested length.
17/// For example, the first 32 bytes of SHAKE128("message", 64) and SHAKE128("message", 128), will be identical
18/// and equal to SHAKE128("message", 32). Proper hash functions don't do this, and NIST is concerned that
19/// this could lead to application vulnerabilities.
20///
21/// As such, even though SHAKE is physically capable of acting as a hash function, and in fact is secure
22/// as such if the provided message includes the requested length, SHAKE does not implement the [Hash] trait.
23#[derive(Clone)]
24pub struct SHAKE<PARAMS: SHAKEParams> {
25    _phantomdata: std::marker::PhantomData<PARAMS>,
26    keccak: KeccakDigest,
27    kdf_key_type: KeyType,
28    kdf_security_strength: SecurityStrength,
29    kdf_entropy: usize,
30}
31
32// Note: don't need a zeroizing Drop here because all the sensitive info is in KeccakDigest, which has one.
33
34impl<PARAMS: SHAKEParams> Algorithm for SHAKE<PARAMS> {
35    const ALG_NAME: &'static str = PARAMS::ALG_NAME;
36    const MAX_SECURITY_STRENGTH: SecurityStrength = PARAMS::MAX_SECURITY_STRENGTH;
37}
38
39impl<PARAMS: SHAKEParams> SHAKE<PARAMS> {
40    pub fn new() -> Self {
41        Self {
42            _phantomdata: std::marker::PhantomData,
43            keccak: KeccakDigest::new(PARAMS::SIZE),
44            kdf_key_type: KeyType::Zeroized,
45            kdf_security_strength: SecurityStrength::None,
46            kdf_entropy: 0,
47        }
48    }
49
50    /// Swallows errors and simply returns an empty Vec<u8> if the hashes fails for whatever reason.
51    fn hash_internal(mut self, data: &[u8], result_len: usize) -> Vec<u8> {
52        self.absorb(data).expect("Should be infallible.");
53        self.squeeze(result_len).expect(".squeeze() should be infallible.") // This should be Infallible ... figure out a clean way to do this
54    }
55
56    fn hash_internal_out(mut self, data: &[u8], output: &mut [u8]) -> usize {
57        self.absorb(data).expect("Should be infallible.");
58        self.squeeze_out(output).expect(".squeeze_out() should be infallible.")
59    }
60
61    fn mix_key_internal(&mut self, key: &impl KeyMaterial) -> Result<(), HashError> {
62        // track the strongest input key type
63        self.kdf_key_type = *max(&self.kdf_key_type, &key.key_type());
64
65        // track input entropy
66        if key.is_full_entropy() {
67            self.kdf_entropy += key.key_len();
68            self.kdf_security_strength =
69                max(&self.kdf_security_strength, &key.security_strength()).clone();
70            self.kdf_security_strength =
71                min(&self.kdf_security_strength, &SecurityStrength::from_bits(PARAMS::SIZE as usize))
72                    .clone();
73        }
74
75        self.absorb(key.ref_to_bytes())
76    }
77
78    fn derive_key_final_internal(
79        mut self,
80        additional_input: &[u8],
81    ) -> Result<Box<dyn KeyMaterial>, KDFError> {
82        // It's unfortunate to return an oversized KeyMaterial most of the time, but I've had enough
83        // of fighting with Rust traits for now ...
84        let mut output_key = KeyMaterialInternal::<64>::new();
85        self.derive_key_out_final_internal(additional_input, &mut output_key)?;
86
87        // 128 => 32, 256 => 64
88        output_key.truncate(2 * (PARAMS::SIZE as usize) / 8)?;
89        Ok(Box::new(output_key))
90    }
91
92    fn derive_key_out_final_internal(
93        &mut self,
94        additional_input: &[u8],
95        output_key: &mut impl KeyMaterial,
96    ) -> Result<usize, KDFError> {
97        // For the KDF to be considered "fully-seeded" and be capable of outputting full-entropy KeyMaterials,
98        // it requires full-entropy input that is at least 2x the bit size (ie 256 bits for SHAKE128, and 512 bits for SHAKE256).
99        // TODO: citation needed, which NIST spec did I get this from?
100        // TODO: intuitivitely this makes sense since SHAKE256 and SHA3-256 are both KECCAK[512], and SHAKE128 is KECCAK[256],
101        // TODO: but I would rather find an actual reference for this "fully-seeded" threshold.
102        if self.kdf_entropy < 2 * (PARAMS::SIZE as usize) / 8 {
103            self.kdf_key_type = min(&self.kdf_key_type, &KeyType::BytesLowEntropy).clone();
104            self.kdf_security_strength = SecurityStrength::None; // BytesLowEntropy can't have a securtiy level.
105        }
106
107        self.absorb(additional_input)?;
108
109        // let mut buf = [0u8; 64];
110        output_key.allow_hazardous_operations();
111        let bytes_written = self.squeeze_out(output_key.mut_ref_to_bytes().expect("We just set .allow_hazardous_operations(), so this should be fine."))?;
112        output_key.set_key_len(bytes_written)?;
113
114        // since we've done some computation, the result will not actually be zeroized, even if all input key material was zeroized.
115        if self.kdf_key_type == KeyType::Zeroized {
116            self.kdf_key_type = KeyType::BytesLowEntropy;
117        }
118        output_key.set_key_type(self.kdf_key_type)?;
119        output_key.set_security_strength(
120            min(&self.kdf_security_strength, &SecurityStrength::from_bits(bytes_written * 8))
121                .clone(),
122        )?;
123        output_key.drop_hazardous_operations();
124        Ok(bytes_written)
125    }
126}
127
128impl<PARAMS: SHAKEParams> KDF for SHAKE<PARAMS> {
129    /// Returns a [KeyMaterialInternal].
130    /// For the KDF to be considered "fully-seeded" and be capable of outputting full-entropy KeyMaterials,
131    /// it requires full-entropy input that is at least 2x the bit size (ie 256 bits for SHAKE128, and 512 bits for SHAKE256).
132    /// Returns a 32 byte key for SHAKE128 and a 64 byte key for SHAKE256.
133    /// To produce longer keys, use [KDF::derive_key_out].
134    /// To produce shorter keys, either use [KDF::derive_key_out] or truncate this result down with
135    /// [KeyMaterialInternal::truncate].
136    fn derive_key(
137        mut self,
138        key: &impl KeyMaterial,
139        additional_input: &[u8],
140    ) -> Result<Box<dyn KeyMaterial>, KDFError> {
141        // self.derive_key_from_multiple(&[key], additional_input)
142        self.mix_key_internal(key)?;
143        self.derive_key_final_internal(additional_input)
144    }
145
146    fn derive_key_out(
147        mut self,
148        key: &impl KeyMaterial,
149        additional_input: &[u8],
150        output_key: &mut impl KeyMaterial,
151    ) -> Result<usize, KDFError> {
152        // self.derive_key_from_multiple_out(&[key], additional_input, output)
153        self.mix_key_internal(key)?;
154        self.derive_key_out_final_internal(additional_input, output_key)
155    }
156
157    /// Always returns a full [KeyMaterialInternal]; ie that fills the internal buffer of the
158    /// appropriately-sized key material for the underlying cryptographic hash function.
159    /// This can be truncated down with [KeyMaterialInternal::truncate].
160    /// Returns a 32 byte key for SHAKE128 and a 64 byte key for SHAKE256.
161    /// To produce longer keys, use [KDF::derive_key_out].
162    /// To produce shorter keys, either use [KDF::derive_key_out] or truncate this result down with [KeyMaterialInternal::truncate].
163    fn derive_key_from_multiple(
164        mut self,
165        keys: &[&impl KeyMaterial],
166        additional_input: &[u8],
167    ) -> Result<Box<dyn KeyMaterial>, KDFError> {
168        for key in keys {
169            self.mix_key_internal(*key)?;
170        }
171        self.derive_key_final_internal(additional_input)
172    }
173
174    fn derive_key_from_multiple_out(
175        mut self,
176        keys: &[&impl KeyMaterial],
177        additional_input: &[u8],
178        output_key: &mut impl KeyMaterial,
179    ) -> Result<usize, KDFError> {
180        for key in keys {
181            self.mix_key_internal(*key)?;
182        }
183        self.derive_key_out_final_internal(additional_input, output_key)
184    }
185
186    fn max_security_strength(&self) -> SecurityStrength {
187        SecurityStrength::from_bits(PARAMS::SIZE as usize)
188    }
189}
190
191impl<PARAMS: SHAKEParams> Default for SHAKE<PARAMS> {
192    fn default() -> Self {
193        Self::new()
194    }
195}
196
197impl<PARAMS: SHAKEParams> XOF for SHAKE<PARAMS> {
198    fn hash_xof(self, data: &[u8], result_len: usize) -> Vec<u8> {
199        self.hash_internal(data, result_len)
200    }
201
202    fn hash_xof_out(self, data: &[u8], output: &mut [u8]) -> usize {
203        self.hash_internal_out(data, output)
204    }
205
206    fn absorb(&mut self, data: &[u8]) -> Result<(), HashError> {
207        Ok(self.keccak.absorb(data))
208    }
209
210    /// Switches to squeezing.
211    fn absorb_last_partial_byte(
212        &mut self,
213        partial_byte: u8,
214        num_partial_bits: usize,
215    ) -> Result<(), HashError> {
216        if !(1..=7).contains(&num_partial_bits) {
217            return Err(HashError::InvalidLength("must be in the range [0,7]"));
218        }
219        // Mutants note: yep, this is just bit-setting into empty space, so it doesn't matter whether it's OR or XOR.
220        let mut final_input: u16 =
221            ((partial_byte as u16) & ((1 << num_partial_bits) - 1)) | (0x0F << num_partial_bits);
222        let mut final_bits = num_partial_bits + 4;
223
224        if final_bits >= 8 {
225            self.keccak.absorb(&[final_input as u8]);
226            final_bits -= 8;
227            final_input >>= 8;
228        }
229
230        self.keccak.absorb_bits(final_input as u8, final_bits).expect("Absorb failed.");
231
232        Ok(())
233    }
234
235    /// Is infallible.
236    fn squeeze(&mut self, num_bytes: usize) -> Result<Vec<u8>, HashError> {
237        let mut out: Vec<u8> = vec![0u8; num_bytes];
238        self.squeeze_out(&mut out)?;
239        Ok(out)
240    }
241
242    /// Is infallible.
243    fn squeeze_out(&mut self, output: &mut [u8]) -> Result<usize, HashError> {
244        if !self.keccak.squeezing {
245            self.keccak.absorb_bits(0x0F, 4).expect("Absorb_bits failed");
246        };
247
248        Ok(self.keccak.squeeze(output))
249    }
250
251    fn squeeze_partial_byte_final(self, num_bits: usize) -> Result<u8, HashError> {
252        let mut output: u8 = 0;
253        self.squeeze_partial_byte_final_out(num_bits, &mut output)?;
254        Ok(output)
255    }
256
257    /// Result is the number of bits squezed into `output`.
258    fn squeeze_partial_byte_final_out(
259        mut self,
260        num_bits: usize,
261        output: &mut u8,
262    ) -> Result<(), HashError> {
263        if !(1..=7).contains(&num_bits) {
264            return Err(HashError::InvalidLength("must be in the range [0,7]"));
265        }
266
267        let mut buf = [0u8; 1];
268        self.keccak.squeeze(&mut buf);
269        *output = buf[0] >> 8 - num_bits;
270        Ok(())
271    }
272
273    fn max_security_strength(&self) -> SecurityStrength {
274        SecurityStrength::from_bits(PARAMS::SIZE as usize)
275    }
276}