commonware_cryptography/blake3/
mod.rs

1//! BLAKE3 implementation of the [Hasher] trait.
2//!
3//! This implementation uses the [blake3] crate to generate BLAKE3 digests.
4//!
5//! # Example
6//! ```rust
7//! use commonware_cryptography::{Hasher, blake3::Blake3};
8//!
9//! // Create a new BLAKE3 hasher
10//! let mut hasher = Blake3::new();
11//!
12//! // Update the hasher with some messages
13//! hasher.update(b"hello,");
14//! hasher.update(b"world!");
15//!
16//! // Finalize the hasher to get the digest
17//! let digest = hasher.finalize();
18//!
19//! // Print the digest
20//! println!("digest: {:?}", digest);
21//! ```
22
23use crate::Hasher;
24use blake3::Hash;
25use bytes::{Buf, BufMut};
26use commonware_codec::{Error as CodecError, FixedSize, Read, ReadExt, Write};
27use commonware_utils::{hex, Array, Span};
28use core::{
29    fmt::{Debug, Display},
30    ops::Deref,
31};
32use rand_core::CryptoRngCore;
33use zeroize::Zeroize;
34
35/// Re-export [blake3::Hasher] as `CoreBlake3` for external use if needed.
36pub type CoreBlake3 = blake3::Hasher;
37
38const DIGEST_LENGTH: usize = blake3::OUT_LEN;
39
40/// Generate a BLAKE3 [Digest] from a message.
41pub fn hash(message: &[u8]) -> Digest {
42    let mut hasher = Blake3::new();
43    hasher.update(message);
44    hasher.finalize()
45}
46
47/// BLAKE3 hasher.
48#[cfg_attr(
49    feature = "parallel",
50    doc = "When the input message is larger than 128KiB, `rayon` is used to parallelize hashing."
51)]
52#[derive(Debug, Default)]
53pub struct Blake3 {
54    hasher: CoreBlake3,
55}
56
57impl Clone for Blake3 {
58    fn clone(&self) -> Self {
59        // We manually implement `Clone` to avoid cloning the hasher state.
60        Self::default()
61    }
62}
63
64impl Hasher for Blake3 {
65    type Digest = Digest;
66
67    fn update(&mut self, message: &[u8]) -> &mut Self {
68        #[cfg(not(feature = "parallel"))]
69        self.hasher.update(message);
70
71        #[cfg(feature = "parallel")]
72        {
73            // 128 KiB
74            const PARALLEL_THRESHOLD: usize = 2usize.pow(17);
75
76            // Heuristic defined @ https://docs.rs/blake3/latest/blake3/struct.Hasher.html#method.update_rayon
77            if message.len() >= PARALLEL_THRESHOLD {
78                self.hasher.update_rayon(message);
79            } else {
80                self.hasher.update(message);
81            }
82        }
83
84        self
85    }
86
87    fn finalize(&mut self) -> Self::Digest {
88        let finalized = self.hasher.finalize();
89        self.hasher.reset();
90        let array: [u8; DIGEST_LENGTH] = finalized.into();
91        Self::Digest::from(array)
92    }
93
94    fn reset(&mut self) -> &mut Self {
95        self.hasher = CoreBlake3::new();
96        self
97    }
98
99    fn empty() -> Self::Digest {
100        Self::default().finalize()
101    }
102}
103
104/// Digest of a BLAKE3 hashing operation.
105#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
106#[repr(transparent)]
107pub struct Digest(pub [u8; DIGEST_LENGTH]);
108
109impl Write for Digest {
110    fn write(&self, buf: &mut impl BufMut) {
111        self.0.write(buf);
112    }
113}
114
115impl Read for Digest {
116    type Cfg = ();
117
118    fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, CodecError> {
119        let array = <[u8; DIGEST_LENGTH]>::read(buf)?;
120        Ok(Self(array))
121    }
122}
123
124impl FixedSize for Digest {
125    const SIZE: usize = DIGEST_LENGTH;
126}
127
128impl Span for Digest {}
129
130impl Array for Digest {}
131
132impl From<Hash> for Digest {
133    fn from(value: Hash) -> Self {
134        Self(value.into())
135    }
136}
137
138impl From<[u8; DIGEST_LENGTH]> for Digest {
139    fn from(value: [u8; DIGEST_LENGTH]) -> Self {
140        Self(value)
141    }
142}
143
144impl AsRef<[u8]> for Digest {
145    fn as_ref(&self) -> &[u8] {
146        &self.0
147    }
148}
149
150impl Deref for Digest {
151    type Target = [u8];
152    fn deref(&self) -> &[u8] {
153        &self.0
154    }
155}
156
157impl Debug for Digest {
158    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
159        write!(f, "{}", hex(&self.0))
160    }
161}
162
163impl Display for Digest {
164    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
165        write!(f, "{}", hex(&self.0))
166    }
167}
168
169impl crate::Digest for Digest {
170    fn random<R: CryptoRngCore>(rng: &mut R) -> Self {
171        let mut array = [0u8; DIGEST_LENGTH];
172        rng.fill_bytes(&mut array);
173        Self(array)
174    }
175}
176
177impl Zeroize for Digest {
178    fn zeroize(&mut self) {
179        self.0.zeroize();
180    }
181}
182
183#[cfg(test)]
184mod tests {
185    use super::*;
186    use commonware_codec::{DecodeExt, Encode};
187    use commonware_utils::hex;
188
189    const HELLO_DIGEST: &str = "d74981efa70a0c880b8d8c1985d075dbcbf679b99a5f9914e5aaf96b831a9e24";
190
191    #[test]
192    fn test_blake3() {
193        let msg = b"hello world";
194
195        // Generate initial hash
196        let mut hasher = Blake3::new();
197        hasher.update(msg);
198        let digest = hasher.finalize();
199        assert!(Digest::decode(digest.as_ref()).is_ok());
200        assert_eq!(hex(digest.as_ref()), HELLO_DIGEST);
201
202        // Reuse hasher
203        hasher.update(msg);
204        let digest = hasher.finalize();
205        assert!(Digest::decode(digest.as_ref()).is_ok());
206        assert_eq!(hex(digest.as_ref()), HELLO_DIGEST);
207
208        // Test simple hasher
209        let hash = hash(msg);
210        assert_eq!(hex(hash.as_ref()), HELLO_DIGEST);
211    }
212
213    #[test]
214    fn test_blake3_len() {
215        assert_eq!(Digest::SIZE, DIGEST_LENGTH);
216    }
217
218    #[test]
219    fn test_hash_empty() {
220        let digest1 = Blake3::empty();
221        let digest2 = Blake3::empty();
222
223        assert_eq!(digest1, digest2);
224    }
225
226    #[test]
227    fn test_blake3_empty() {
228        let empty_digest = Blake3::empty();
229
230        // BLAKE3 hash of empty string:
231        // af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262
232        let expected_bytes = [
233            0xaf, 0x13, 0x49, 0xb9, 0xf5, 0xf9, 0xa1, 0xa6, 0xa0, 0x40, 0x4d, 0xea, 0x36, 0xdc,
234            0xc9, 0x49, 0x9b, 0xcb, 0x25, 0xc9, 0xad, 0xc1, 0x12, 0xb7, 0xcc, 0x9a, 0x93, 0xca,
235            0xe4, 0x1f, 0x32, 0x62,
236        ];
237        let expected_digest = Digest::from(expected_bytes);
238
239        assert_eq!(empty_digest, expected_digest);
240    }
241
242    #[test]
243    fn test_codec() {
244        let msg = b"hello world";
245        let mut hasher = Blake3::new();
246        hasher.update(msg);
247        let digest = hasher.finalize();
248
249        let encoded = digest.encode();
250        assert_eq!(encoded.len(), DIGEST_LENGTH);
251        assert_eq!(encoded, digest.as_ref());
252
253        let decoded = Digest::decode(encoded).unwrap();
254        assert_eq!(digest, decoded);
255    }
256}