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 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 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 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#[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 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 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 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 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}