Skip to main content

bouncycastle_sha3/
sha3.rs

1use bouncycastle_core::errors::{HashError, KDFError};
2use bouncycastle_core::key_material::{KeyMaterial, KeyMaterialTrait, KeyType};
3use bouncycastle_core::traits::{Hash, SecurityStrength, KDF};
4use bouncycastle_utils::{max, min};
5use crate::keccak::KeccakDigest;
6use crate::SHA3Params;
7
8#[derive(Clone)]
9pub struct SHA3<PARAMS: SHA3Params> {
10    _params: std::marker::PhantomData<PARAMS>,
11    keccak: KeccakDigest,
12    kdf_key_type: KeyType,
13    kdf_security_strength: SecurityStrength,
14    kdf_entropy: usize,
15}
16
17// Note: don't need a zeroizing Drop here because all the sensitive info is in KeccakDigest, which has one.
18
19impl<PARAMS: SHA3Params> SHA3<PARAMS> {
20    pub fn new() -> Self {
21        Self {
22            _params: std::marker::PhantomData,
23            keccak: KeccakDigest::new(PARAMS::SIZE),
24            kdf_key_type: KeyType::Zeroized,
25            kdf_security_strength: SecurityStrength::None,
26            kdf_entropy: 0,
27        }
28    }
29
30    /// Swallows errors and simply returns an empty Vec<u8> if the hashes fails for whatever reason.
31    fn hash_internal(mut self, data: &[u8], output: &mut [u8]) -> usize {
32        self.do_update(data);
33        self.do_final_out(output)
34    }
35
36    fn mix_key_internal(&mut self, key: &impl KeyMaterialTrait) {
37        // track the strongest input key type
38        self.kdf_key_type = *max(&self.kdf_key_type, &key.key_type());
39
40        // track input entropy
41        if key.is_full_entropy() {
42            self.kdf_entropy += key.key_len();
43            self.kdf_security_strength =
44                max(&self.kdf_security_strength, &key.security_strength()).clone();
45            self.kdf_security_strength = min(
46                &self.kdf_security_strength,
47                &SecurityStrength::from_bits(PARAMS::OUTPUT_LEN * 8 / 2),
48            )
49                .clone();
50        }
51
52        self.do_update(key.ref_to_bytes())
53    }
54
55    fn derive_key_final_internal(
56        self,
57        additional_input: &[u8],
58    ) -> Result<Box<dyn KeyMaterialTrait>, KDFError> {
59        let mut output_key = KeyMaterial::<64>::new();
60        self.derive_key_out_final_internal(additional_input, &mut output_key)?;
61
62        Ok(Box::new(output_key))
63    }
64
65    fn derive_key_out_final_internal(
66        mut self,
67        additional_input: &[u8],
68        output_key: &mut impl KeyMaterialTrait,
69    ) -> Result<usize, KDFError> {
70        // For the KDF to be considered "fully-seeded" and be capable of outputting full-entropy KeyMaterials,
71        // it requires full-entropy input that is at least block length.
72        // TODO: citation needed, which NIST spec did I get this from?
73        if self.kdf_entropy < PARAMS::OUTPUT_LEN {
74            self.kdf_key_type = min(&self.kdf_key_type, &KeyType::BytesLowEntropy).clone();
75            self.kdf_security_strength = SecurityStrength::None; // BytesLowEntropy can't have a securtiy level.
76        }
77
78        self.do_update(additional_input);
79
80        let mut key_type = self.kdf_key_type.clone();
81        let output_security_strength = self.kdf_security_strength.clone();
82        output_key.allow_hazardous_operations();
83        let bytes_written = self.do_final_out(output_key.mut_ref_to_bytes()?);
84        output_key.set_key_len(bytes_written)?;
85
86        // since we've done some computation, the result will not actually be zeroized, even if all input key material was zeroized.
87        if key_type == KeyType::Zeroized {
88            key_type = KeyType::BytesLowEntropy;
89        }
90        output_key.set_key_type(key_type)?;
91        output_key.set_security_strength(
92            min(&output_security_strength, &SecurityStrength::from_bits(bytes_written * 8)).clone(),
93        )?;
94        output_key.drop_hazardous_operations();
95        output_key.truncate(min(&output_key.key_len(), &PARAMS::OUTPUT_LEN).clone())?;
96        Ok(bytes_written)
97    }
98}
99
100impl<PARAMS: SHA3Params> Default for SHA3<PARAMS> {
101    fn default() -> Self {
102        Self::new()
103    }
104}
105
106impl<PARAMS: SHA3Params> Hash for SHA3<PARAMS> {
107    /// As per FIPS 202 Table 3.
108    /// Required, for example, to compute the pad lengths in HMAC.
109    fn block_bitlen(&self) -> usize {
110        PARAMS::BLOCK_LEN * 8
111    }
112
113    fn output_len(&self) -> usize {
114        PARAMS::OUTPUT_LEN
115    }
116
117    fn hash(self, data: &[u8]) -> Vec<u8> {
118        let mut output: Vec<u8> = vec![0u8; PARAMS::OUTPUT_LEN];
119        _ = self.hash_internal(data, &mut output[..]);
120        output
121    }
122
123    fn hash_out(self, data: &[u8], mut output: &mut [u8]) -> usize {
124        self.hash_internal(data, &mut output)
125    }
126
127    fn do_update(&mut self, data: &[u8]) {
128        self.keccak.absorb(data)
129    }
130
131    fn do_final(self) -> Vec<u8> {
132        let dbg_rslt_len = self.output_len();
133        let mut output: Vec<u8> = vec![0u8; self.output_len()];
134        let bytes_written = self.do_final_out(output.as_mut_slice());
135        debug_assert_eq!(bytes_written, dbg_rslt_len);
136
137        output
138    }
139
140    // todo -- why doesn't this take a &mut [u8; HASH_LEN] ?
141    //  That's probably more user-friendly than this auto-truncating that I have here.
142    fn do_final_out(mut self, output: &mut [u8]) -> usize {
143        self.keccak.absorb_bits(0x02, 2).expect("do_final_out: keccak.absorb_bits failed."); // this shouldn't fail because by construction you can only enter this function once, and this is the only way to absorb partial bits.
144
145        let bytes_written = if output.len() <= self.output_len() {
146            self.keccak.squeeze(output)
147        } else {
148            let min =
149                if output.len() >= self.output_len() { self.output_len() } else { output.len() };
150            self.keccak.squeeze(&mut output[..min])
151        };
152        bytes_written
153    }
154
155    fn do_final_partial_bits(
156        self,
157        partial_byte: u8,
158        num_partial_bits: usize,
159    ) -> Result<Vec<u8>, HashError> {
160        let dbg_rslt_len = self.output_len();
161        let mut output: Vec<u8> = vec![0u8; self.output_len()];
162        let bytes_written =
163            self.do_final_partial_bits_out(partial_byte, num_partial_bits, output.as_mut_slice())?;
164        debug_assert_eq!(bytes_written, dbg_rslt_len);
165
166        Ok(output)
167    }
168
169    fn do_final_partial_bits_out(
170        mut self,
171        partial_byte: u8,
172        num_partial_bits: usize,
173        output: &mut [u8],
174    ) -> Result<usize, HashError> {
175        // Mutants note: yep, this is just bit-setting into empty space, so it doesn't matter whether it's OR or XOR.
176        let mut final_input: u16 =
177            ((partial_byte as u16) & ((1 << num_partial_bits) - 1)) | (0x02 << num_partial_bits);
178        let mut final_bits = num_partial_bits + 2;
179
180        if final_bits >= 8 {
181            self.keccak.absorb(&[final_input as u8]);
182            final_bits -= 8;
183            final_input >>= 8;
184        }
185
186        self.keccak.absorb_bits(final_input as u8, final_bits)?;
187
188        let min = if output.len() >= self.output_len() { self.output_len() } else { output.len() };
189        Ok(self.keccak.squeeze(&mut output[..min]))
190    }
191
192    fn max_security_strength(&self) -> SecurityStrength {
193        SecurityStrength::from_bytes(PARAMS::OUTPUT_LEN / 2)
194    }
195}
196
197/// SHA3 is allowed to be used as a KDF in the form HASH(X) as per NIST SP 800-56C.
198impl<PARAMS: SHA3Params> KDF for SHA3<PARAMS> {
199    /// Returns a [KeyMaterial].
200    /// For the KDF to be considered "fully-seeded" and be capable of outputting full-entropy KeyMaterials,
201    /// it requires full-entropy input that is at least the bit size (ie 256 bits for SHA3-256, etc).
202    fn derive_key(
203        mut self,
204        key: &impl KeyMaterialTrait,
205        additional_input: &[u8],
206    ) -> Result<Box<dyn KeyMaterialTrait>, KDFError> {
207        self.mix_key_internal(key);
208        self.derive_key_final_internal(additional_input)
209    }
210
211    fn derive_key_out(
212        mut self,
213        key: &impl KeyMaterialTrait,
214        additional_input: &[u8],
215        output_key: &mut impl KeyMaterialTrait,
216    ) -> Result<usize, KDFError> {
217        // self.derive_key_from_multiple_out(&[key], additional_input, output_key)
218        self.mix_key_internal(key);
219        self.derive_key_out_final_internal(additional_input, output_key)
220    }
221
222    fn derive_key_from_multiple(
223        mut self,
224        keys: &[&impl KeyMaterialTrait],
225        additional_input: &[u8],
226    ) -> Result<Box<dyn KeyMaterialTrait>, KDFError> {
227        for key in keys {
228            self.mix_key_internal(*key);
229        }
230        self.derive_key_final_internal(additional_input)
231    }
232
233    fn derive_key_from_multiple_out(
234        mut self,
235        keys: &[&impl KeyMaterialTrait],
236        additional_input: &[u8],
237        output_key: &mut impl KeyMaterialTrait,
238    ) -> Result<usize, KDFError> {
239        // self.derive_key_from_multiple_internal(keys, additional_input, output_key)
240        for key in keys {
241            self.mix_key_internal(*key);
242        }
243        self.derive_key_out_final_internal(additional_input, output_key)
244    }
245
246    fn max_security_strength(&self) -> SecurityStrength {
247        SecurityStrength::from_bytes(PARAMS::OUTPUT_LEN / 2)
248    }
249}