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/// BLAKE3 hasher.
41#[cfg_attr(
42    feature = "parallel",
43    doc = "When the input message is larger than 128KiB, `rayon` is used to parallelize hashing."
44)]
45#[derive(Debug, Default)]
46pub struct Blake3 {
47    hasher: CoreBlake3,
48}
49
50impl Clone for Blake3 {
51    fn clone(&self) -> Self {
52        // We manually implement `Clone` to avoid cloning the hasher state.
53        Self::default()
54    }
55}
56
57impl Hasher for Blake3 {
58    type Digest = Digest;
59
60    fn update(&mut self, message: &[u8]) -> &mut Self {
61        #[cfg(not(feature = "parallel"))]
62        self.hasher.update(message);
63
64        #[cfg(feature = "parallel")]
65        {
66            // 128 KiB
67            const PARALLEL_THRESHOLD: usize = 2usize.pow(17);
68
69            // Heuristic defined @ https://docs.rs/blake3/latest/blake3/struct.Hasher.html#method.update_rayon
70            if message.len() >= PARALLEL_THRESHOLD {
71                self.hasher.update_rayon(message);
72            } else {
73                self.hasher.update(message);
74            }
75        }
76
77        self
78    }
79
80    fn finalize(&mut self) -> Self::Digest {
81        let finalized = self.hasher.finalize();
82        self.hasher.reset();
83        let array: [u8; DIGEST_LENGTH] = finalized.into();
84        Self::Digest::from(array)
85    }
86
87    fn reset(&mut self) -> &mut Self {
88        self.hasher = CoreBlake3::new();
89        self
90    }
91
92    fn empty() -> Self::Digest {
93        Self::default().finalize()
94    }
95}
96
97/// Digest of a BLAKE3 hashing operation.
98#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
99#[repr(transparent)]
100pub struct Digest(pub [u8; DIGEST_LENGTH]);
101
102impl Write for Digest {
103    fn write(&self, buf: &mut impl BufMut) {
104        self.0.write(buf);
105    }
106}
107
108impl Read for Digest {
109    type Cfg = ();
110
111    fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, CodecError> {
112        let array = <[u8; DIGEST_LENGTH]>::read(buf)?;
113        Ok(Self(array))
114    }
115}
116
117impl FixedSize for Digest {
118    const SIZE: usize = DIGEST_LENGTH;
119}
120
121impl Span for Digest {}
122
123impl Array for Digest {}
124
125impl From<Hash> for Digest {
126    fn from(value: Hash) -> Self {
127        Self(value.into())
128    }
129}
130
131impl From<[u8; DIGEST_LENGTH]> for Digest {
132    fn from(value: [u8; DIGEST_LENGTH]) -> Self {
133        Self(value)
134    }
135}
136
137impl AsRef<[u8]> for Digest {
138    fn as_ref(&self) -> &[u8] {
139        &self.0
140    }
141}
142
143impl Deref for Digest {
144    type Target = [u8];
145    fn deref(&self) -> &[u8] {
146        &self.0
147    }
148}
149
150impl Debug for Digest {
151    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
152        write!(f, "{}", hex(&self.0))
153    }
154}
155
156impl Display for Digest {
157    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
158        write!(f, "{}", hex(&self.0))
159    }
160}
161
162impl crate::Digest for Digest {
163    fn random<R: CryptoRngCore>(rng: &mut R) -> Self {
164        let mut array = [0u8; DIGEST_LENGTH];
165        rng.fill_bytes(&mut array);
166        Self(array)
167    }
168}
169
170impl Zeroize for Digest {
171    fn zeroize(&mut self) {
172        self.0.zeroize();
173    }
174}
175
176#[cfg(test)]
177mod tests {
178    use super::*;
179    use commonware_codec::{DecodeExt, Encode};
180    use commonware_utils::hex;
181
182    const HELLO_DIGEST: [u8; DIGEST_LENGTH] =
183        hex!("d74981efa70a0c880b8d8c1985d075dbcbf679b99a5f9914e5aaf96b831a9e24");
184    const EMPTY_DIGEST: [u8; DIGEST_LENGTH] =
185        hex!("af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262");
186
187    #[test]
188    fn test_blake3() {
189        let msg = b"hello world";
190
191        // Generate initial hash
192        let mut hasher = Blake3::new();
193        hasher.update(msg);
194        let digest = hasher.finalize();
195        assert!(Digest::decode(digest.as_ref()).is_ok());
196        assert_eq!(digest.as_ref(), HELLO_DIGEST);
197
198        // Reuse hasher
199        hasher.update(msg);
200        let digest = hasher.finalize();
201        assert!(Digest::decode(digest.as_ref()).is_ok());
202        assert_eq!(digest.as_ref(), HELLO_DIGEST);
203
204        // Test simple hasher
205        let hash = Blake3::hash(msg);
206        assert_eq!(hash.as_ref(), HELLO_DIGEST);
207    }
208
209    #[test]
210    fn test_blake3_len() {
211        assert_eq!(Digest::SIZE, DIGEST_LENGTH);
212    }
213
214    #[test]
215    fn test_hash_empty() {
216        let digest1 = Blake3::empty();
217        let digest2 = Blake3::empty();
218
219        assert_eq!(digest1, digest2);
220    }
221
222    #[test]
223    fn test_blake3_empty() {
224        let empty_digest = Blake3::empty();
225        let expected_digest = Digest::from(EMPTY_DIGEST);
226
227        assert_eq!(empty_digest, expected_digest);
228    }
229
230    #[test]
231    fn test_codec() {
232        let msg = b"hello world";
233        let mut hasher = Blake3::new();
234        hasher.update(msg);
235        let digest = hasher.finalize();
236
237        let encoded = digest.encode();
238        assert_eq!(encoded.len(), DIGEST_LENGTH);
239        assert_eq!(encoded, digest.as_ref());
240
241        let decoded = Digest::decode(encoded).unwrap();
242        assert_eq!(digest, decoded);
243    }
244}