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