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 core::{
29 fmt::{Debug, Display},
30 ops::Deref,
31};
32use rand_core::CryptoRngCore;
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",
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 update(&mut self, message: &[u8]) -> &mut Self {
68 #[cfg(not(feature = "parallel"))]
69 self.hasher.update(message);
70
71 #[cfg(feature = "parallel")]
72 {
73 const PARALLEL_THRESHOLD: usize = 2usize.pow(17);
75
76 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#[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 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 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 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 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}