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 new() -> Self {
68        Self {
69            hasher: CoreBlake3::new(),
70        }
71    }
72
73    fn update(&mut self, message: &[u8]) -> &mut Self {
74        #[cfg(not(feature = "parallel"))]
75        self.hasher.update(message);
76
77        #[cfg(feature = "parallel")]
78        {
79            // 128 KiB
80            const PARALLEL_THRESHOLD: usize = 2usize.pow(17);
81
82            // Heuristic defined @ https://docs.rs/blake3/latest/blake3/struct.Hasher.html#method.update_rayon
83            if message.len() >= PARALLEL_THRESHOLD {
84                self.hasher.update_rayon(message);
85            } else {
86                self.hasher.update(message);
87            }
88        }
89
90        self
91    }
92
93    fn finalize(&mut self) -> Self::Digest {
94        let finalized = self.hasher.finalize();
95        self.hasher.reset();
96        let array: [u8; DIGEST_LENGTH] = finalized.into();
97        Self::Digest::from(array)
98    }
99
100    fn reset(&mut self) -> &mut Self {
101        self.hasher = CoreBlake3::new();
102        self
103    }
104
105    fn empty() -> Self::Digest {
106        Self::default().finalize()
107    }
108}
109
110/// Digest of a BLAKE3 hashing operation.
111#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
112#[repr(transparent)]
113pub struct Digest(pub [u8; DIGEST_LENGTH]);
114
115impl Write for Digest {
116    fn write(&self, buf: &mut impl BufMut) {
117        self.0.write(buf);
118    }
119}
120
121impl Read for Digest {
122    type Cfg = ();
123
124    fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, CodecError> {
125        let array = <[u8; DIGEST_LENGTH]>::read(buf)?;
126        Ok(Self(array))
127    }
128}
129
130impl FixedSize for Digest {
131    const SIZE: usize = DIGEST_LENGTH;
132}
133
134impl Span for Digest {}
135
136impl Array for Digest {}
137
138impl From<Hash> for Digest {
139    fn from(value: Hash) -> Self {
140        Self(value.into())
141    }
142}
143
144impl From<[u8; DIGEST_LENGTH]> for Digest {
145    fn from(value: [u8; DIGEST_LENGTH]) -> Self {
146        Self(value)
147    }
148}
149
150impl AsRef<[u8]> for Digest {
151    fn as_ref(&self) -> &[u8] {
152        &self.0
153    }
154}
155
156impl Deref for Digest {
157    type Target = [u8];
158    fn deref(&self) -> &[u8] {
159        &self.0
160    }
161}
162
163impl Debug for Digest {
164    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
165        write!(f, "{}", hex(&self.0))
166    }
167}
168
169impl Display for Digest {
170    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
171        write!(f, "{}", hex(&self.0))
172    }
173}
174
175impl crate::Digest for Digest {
176    fn random<R: CryptoRngCore>(rng: &mut R) -> Self {
177        let mut array = [0u8; DIGEST_LENGTH];
178        rng.fill_bytes(&mut array);
179        Self(array)
180    }
181}
182
183impl Zeroize for Digest {
184    fn zeroize(&mut self) {
185        self.0.zeroize();
186    }
187}
188
189#[cfg(test)]
190mod tests {
191    use super::*;
192    use commonware_codec::{DecodeExt, Encode};
193    use commonware_utils::hex;
194
195    const HELLO_DIGEST: &str = "d74981efa70a0c880b8d8c1985d075dbcbf679b99a5f9914e5aaf96b831a9e24";
196
197    #[test]
198    fn test_blake3() {
199        let msg = b"hello world";
200
201        // Generate initial hash
202        let mut hasher = Blake3::new();
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        // Reuse hasher
209        hasher.update(msg);
210        let digest = hasher.finalize();
211        assert!(Digest::decode(digest.as_ref()).is_ok());
212        assert_eq!(hex(digest.as_ref()), HELLO_DIGEST);
213
214        // Test simple hasher
215        let hash = hash(msg);
216        assert_eq!(hex(hash.as_ref()), HELLO_DIGEST);
217    }
218
219    #[test]
220    fn test_blake3_len() {
221        assert_eq!(Digest::SIZE, DIGEST_LENGTH);
222    }
223
224    #[test]
225    fn test_hash_empty() {
226        let digest1 = Blake3::empty();
227        let digest2 = Blake3::empty();
228
229        assert_eq!(digest1, digest2);
230    }
231
232    #[test]
233    fn test_blake3_empty() {
234        let empty_digest = Blake3::empty();
235
236        // BLAKE3 hash of empty string:
237        // af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262
238        let expected_bytes = [
239            0xaf, 0x13, 0x49, 0xb9, 0xf5, 0xf9, 0xa1, 0xa6, 0xa0, 0x40, 0x4d, 0xea, 0x36, 0xdc,
240            0xc9, 0x49, 0x9b, 0xcb, 0x25, 0xc9, 0xad, 0xc1, 0x12, 0xb7, 0xcc, 0x9a, 0x93, 0xca,
241            0xe4, 0x1f, 0x32, 0x62,
242        ];
243        let expected_digest = Digest::from(expected_bytes);
244
245        assert_eq!(empty_digest, expected_digest);
246    }
247
248    #[test]
249    fn test_codec() {
250        let msg = b"hello world";
251        let mut hasher = Blake3::new();
252        hasher.update(msg);
253        let digest = hasher.finalize();
254
255        let encoded = digest.encode();
256        assert_eq!(encoded.len(), DIGEST_LENGTH);
257        assert_eq!(encoded, digest.as_ref());
258
259        let decoded = Digest::decode(encoded).unwrap();
260        assert_eq!(digest, decoded);
261    }
262}