1use crate::api::traits::Hash;
9use blake3;
10use sha3::{Digest, Sha3_256, Sha3_512};
11
12pub struct Blake3Hasher {
14 hasher: blake3::Hasher,
15}
16
17#[derive(Clone, Debug, PartialEq, Eq)]
19pub struct Blake3Output(blake3::Hash);
20
21impl AsRef<[u8]> for Blake3Output {
22 fn as_ref(&self) -> &[u8] {
23 self.0.as_bytes()
24 }
25}
26
27impl Hash for Blake3Hasher {
28 type Output = Blake3Output;
29
30 fn new() -> Self {
31 Self {
32 hasher: blake3::Hasher::new(),
33 }
34 }
35
36 fn update(&mut self, data: &[u8]) {
37 self.hasher.update(data);
38 }
39
40 fn finalize(self) -> Self::Output {
41 Blake3Output(self.hasher.finalize())
42 }
43
44 fn output_size() -> usize {
45 32 }
47
48 fn name() -> &'static str {
49 "BLAKE3"
50 }
51}
52
53pub struct Sha3_256Hasher {
55 hasher: Sha3_256,
56}
57
58#[derive(Clone, Debug, PartialEq, Eq)]
60pub struct Sha3_256Output([u8; 32]);
61
62impl AsRef<[u8]> for Sha3_256Output {
63 fn as_ref(&self) -> &[u8] {
64 &self.0
65 }
66}
67
68impl Hash for Sha3_256Hasher {
69 type Output = Sha3_256Output;
70
71 fn new() -> Self {
72 Self {
73 hasher: Sha3_256::new(),
74 }
75 }
76
77 fn update(&mut self, data: &[u8]) {
78 self.hasher.update(data);
79 }
80
81 fn finalize(self) -> Self::Output {
82 let result = self.hasher.finalize();
83 let mut output = [0u8; 32];
84 output.copy_from_slice(&result);
85 Sha3_256Output(output)
86 }
87
88 fn output_size() -> usize {
89 32 }
91
92 fn name() -> &'static str {
93 "SHA3-256"
94 }
95}
96
97pub struct Sha3_512Hasher {
99 hasher: Sha3_512,
100}
101
102#[derive(Clone, Debug, PartialEq, Eq)]
104pub struct Sha3_512Output([u8; 64]);
105
106impl AsRef<[u8]> for Sha3_512Output {
107 fn as_ref(&self) -> &[u8] {
108 &self.0
109 }
110}
111
112impl Hash for Sha3_512Hasher {
113 type Output = Sha3_512Output;
114
115 fn new() -> Self {
116 Self {
117 hasher: Sha3_512::new(),
118 }
119 }
120
121 fn update(&mut self, data: &[u8]) {
122 self.hasher.update(data);
123 }
124
125 fn finalize(self) -> Self::Output {
126 let result = self.hasher.finalize();
127 let mut output = [0u8; 64];
128 output.copy_from_slice(&result);
129 Sha3_512Output(output)
130 }
131
132 fn output_size() -> usize {
133 64 }
135
136 fn name() -> &'static str {
137 "SHA3-512"
138 }
139}
140
141pub struct Shake256Xof;
144
145impl Shake256Xof {
146 #[must_use]
148 pub fn shake256(data: &[u8], output_len: usize) -> Vec<u8> {
149 use sha3::digest::{ExtendableOutput, Update, XofReader};
150 use sha3::Shake256;
151
152 let mut hasher = Shake256::default();
153 Update::update(&mut hasher, data);
154 let mut reader = hasher.finalize_xof();
155 let mut output = vec![0u8; output_len];
156 XofReader::read(&mut reader, &mut output);
157 output
158 }
159}
160
161#[derive(Debug, Clone, Copy, PartialEq, Eq)]
163pub enum HashAlgorithm {
164 Blake3,
166 Sha3_256,
168 Sha3_512,
170}
171
172impl HashAlgorithm {
173 #[must_use]
175 pub fn hash(&self, data: &[u8]) -> Vec<u8> {
176 match self {
177 Self::Blake3 => Blake3Hasher::hash(data).as_ref().to_vec(),
178 Self::Sha3_256 => Sha3_256Hasher::hash(data).as_ref().to_vec(),
179 Self::Sha3_512 => Sha3_512Hasher::hash(data).as_ref().to_vec(),
180 }
181 }
182
183 #[must_use]
185 pub fn output_size(&self) -> usize {
186 match self {
187 Self::Blake3 => Blake3Hasher::output_size(),
188 Self::Sha3_256 => Sha3_256Hasher::output_size(),
189 Self::Sha3_512 => Sha3_512Hasher::output_size(),
190 }
191 }
192
193 #[must_use]
195 pub const fn name(&self) -> &'static str {
196 match self {
197 Self::Blake3 => "BLAKE3",
198 Self::Sha3_256 => "SHA3-256",
199 Self::Sha3_512 => "SHA3-512",
200 }
201 }
202}
203
204pub mod helpers {
206 use super::{blake3, Blake3Hasher, Hash, Sha3_256Hasher, Sha3_512Hasher, Shake256Xof};
207
208 #[must_use]
210 pub fn blake3(data: &[u8]) -> [u8; 32] {
211 let output = Blake3Hasher::hash(data);
212 let mut result = [0u8; 32];
213 result.copy_from_slice(output.as_ref());
214 result
215 }
216
217 #[must_use]
219 pub fn sha3_256(data: &[u8]) -> [u8; 32] {
220 let output = Sha3_256Hasher::hash(data);
221 let mut result = [0u8; 32];
222 result.copy_from_slice(output.as_ref());
223 result
224 }
225
226 #[must_use]
228 pub fn sha3_512(data: &[u8]) -> [u8; 64] {
229 let output = Sha3_512Hasher::hash(data);
230 let mut result = [0u8; 64];
231 result.copy_from_slice(output.as_ref());
232 result
233 }
234
235 #[must_use]
237 pub fn shake256(data: &[u8], output_len: usize) -> Vec<u8> {
238 Shake256Xof::shake256(data, output_len)
239 }
240
241 #[must_use]
243 pub fn derive_key_blake3(context: &str, key_material: &[u8]) -> [u8; 32] {
244 let mut hasher = blake3::Hasher::new_derive_key(context);
245 hasher.update(key_material);
246 let hash = hasher.finalize();
247 *hash.as_bytes()
248 }
249}
250
251#[cfg(test)]
252#[allow(clippy::indexing_slicing)]
253#[allow(clippy::unwrap_used, clippy::expect_used)]
254mod tests {
255 use super::*;
256
257 #[test]
258 fn test_blake3_basic() {
259 let data = b"test data";
260 let hash1 = Blake3Hasher::hash(data);
261 let hash2 = helpers::blake3(data);
262 assert_eq!(hash1.as_ref(), &hash2);
263 assert_eq!(Blake3Hasher::output_size(), 32);
264 assert_eq!(Blake3Hasher::name(), "BLAKE3");
265 }
266
267 #[test]
268 fn test_sha3_256_basic() {
269 let data = b"test data";
270 let hash1 = Sha3_256Hasher::hash(data);
271 let hash2 = helpers::sha3_256(data);
272 assert_eq!(hash1.as_ref(), &hash2);
273 assert_eq!(Sha3_256Hasher::output_size(), 32);
274 assert_eq!(Sha3_256Hasher::name(), "SHA3-256");
275 }
276
277 #[test]
278 fn test_sha3_512_basic() {
279 let data = b"test data";
280 let hash1 = Sha3_512Hasher::hash(data);
281 let hash2 = helpers::sha3_512(data);
282 assert_eq!(hash1.as_ref(), &hash2);
283 assert_eq!(Sha3_512Hasher::output_size(), 64);
284 assert_eq!(Sha3_512Hasher::name(), "SHA3-512");
285 }
286
287 #[test]
288 fn test_shake256_variable_output() {
289 let data = b"test data";
290 let output_32 = helpers::shake256(data, 32);
291 let output_64 = helpers::shake256(data, 64);
292 let output_128 = helpers::shake256(data, 128);
293
294 assert_eq!(output_32.len(), 32);
295 assert_eq!(output_64.len(), 64);
296 assert_eq!(output_128.len(), 128);
297
298 assert_eq!(&output_64[..32], &output_32[..]);
300 assert_eq!(&output_128[..32], &output_32[..]);
301 }
302
303 #[test]
304 fn test_hash_algorithm_enum() {
305 let data = b"test data";
306
307 let blake3_hash = HashAlgorithm::Blake3.hash(data);
308 assert_eq!(blake3_hash.len(), 32);
309 assert_eq!(HashAlgorithm::Blake3.output_size(), 32);
310 assert_eq!(HashAlgorithm::Blake3.name(), "BLAKE3");
311
312 let sha3_256_hash = HashAlgorithm::Sha3_256.hash(data);
313 assert_eq!(sha3_256_hash.len(), 32);
314
315 let sha3_512_hash = HashAlgorithm::Sha3_512.hash(data);
316 assert_eq!(sha3_512_hash.len(), 64);
317 }
318
319 #[test]
320 fn test_blake3_key_derivation() {
321 let context = "test context";
322 let key_material = b"secret key material";
323
324 let key1 = helpers::derive_key_blake3(context, key_material);
325 let key2 = helpers::derive_key_blake3(context, key_material);
326 assert_eq!(key1, key2); let key3 = helpers::derive_key_blake3("different context", key_material);
329 assert_ne!(key1, key3); }
331
332 #[test]
333 fn test_incremental_hashing() {
334 let data1 = b"first part";
335 let data2 = b"second part";
336 let combined = b"first partsecond part";
337
338 let mut hasher = Blake3Hasher::new();
340 hasher.update(data1);
341 hasher.update(data2);
342 let incremental_hash = hasher.finalize();
343
344 let direct_hash = Blake3Hasher::hash(combined);
345 assert_eq!(incremental_hash.as_ref(), direct_hash.as_ref());
346
347 let mut hasher = Sha3_256Hasher::new();
349 hasher.update(data1);
350 hasher.update(data2);
351 let incremental_hash = hasher.finalize();
352
353 let direct_hash = Sha3_256Hasher::hash(combined);
354 assert_eq!(incremental_hash.as_ref(), direct_hash.as_ref());
355 }
356}