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