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};
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([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 Array for Digest {}
132
133impl From<Hash> for Digest {
134 fn from(value: Hash) -> Self {
135 Self(value.into())
136 }
137}
138
139impl From<[u8; DIGEST_LENGTH]> for Digest {
140 fn from(value: [u8; DIGEST_LENGTH]) -> Self {
141 Self(value)
142 }
143}
144
145impl AsRef<[u8]> for Digest {
146 fn as_ref(&self) -> &[u8] {
147 &self.0
148 }
149}
150
151impl Deref for Digest {
152 type Target = [u8];
153 fn deref(&self) -> &[u8] {
154 &self.0
155 }
156}
157
158impl Debug for Digest {
159 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
160 write!(f, "{}", hex(&self.0))
161 }
162}
163
164impl Display for Digest {
165 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
166 write!(f, "{}", hex(&self.0))
167 }
168}
169
170impl crate::Digest for Digest {
171 fn random<R: Rng + CryptoRng>(rng: &mut R) -> Self {
172 let mut array = [0u8; DIGEST_LENGTH];
173 rng.fill_bytes(&mut array);
174 Self(array)
175 }
176}
177
178impl Zeroize for Digest {
179 fn zeroize(&mut self) {
180 self.0.zeroize();
181 }
182}
183
184#[cfg(test)]
185mod tests {
186 use super::*;
187 use commonware_codec::{DecodeExt, Encode};
188 use commonware_utils::hex;
189
190 const HELLO_DIGEST: &str = "d74981efa70a0c880b8d8c1985d075dbcbf679b99a5f9914e5aaf96b831a9e24";
191
192 #[test]
193 fn test_blake3() {
194 let msg = b"hello world";
195
196 let mut hasher = Blake3::new();
198 hasher.update(msg);
199 let digest = hasher.finalize();
200 assert!(Digest::decode(digest.as_ref()).is_ok());
201 assert_eq!(hex(digest.as_ref()), HELLO_DIGEST);
202
203 hasher.update(msg);
205 let digest = hasher.finalize();
206 assert!(Digest::decode(digest.as_ref()).is_ok());
207 assert_eq!(hex(digest.as_ref()), HELLO_DIGEST);
208
209 let hash = hash(msg);
211 assert_eq!(hex(hash.as_ref()), HELLO_DIGEST);
212 }
213
214 #[test]
215 fn test_blake3_len() {
216 assert_eq!(Digest::SIZE, DIGEST_LENGTH);
217 }
218
219 #[test]
220 fn test_hash_empty() {
221 let digest1 = Blake3::empty();
222 let digest2 = Blake3::empty();
223
224 assert_eq!(digest1, digest2);
225 }
226
227 #[test]
228 fn test_blake3_empty() {
229 let empty_digest = Blake3::empty();
230
231 let expected_bytes = [
234 0xaf, 0x13, 0x49, 0xb9, 0xf5, 0xf9, 0xa1, 0xa6, 0xa0, 0x40, 0x4d, 0xea, 0x36, 0xdc,
235 0xc9, 0x49, 0x9b, 0xcb, 0x25, 0xc9, 0xad, 0xc1, 0x12, 0xb7, 0xcc, 0x9a, 0x93, 0xca,
236 0xe4, 0x1f, 0x32, 0x62,
237 ];
238 let expected_digest = Digest::from(expected_bytes);
239
240 assert_eq!(empty_digest, expected_digest);
241 }
242
243 #[test]
244 fn test_codec() {
245 let msg = b"hello world";
246 let mut hasher = Blake3::new();
247 hasher.update(msg);
248 let digest = hasher.finalize();
249
250 let encoded = digest.encode();
251 assert_eq!(encoded.len(), DIGEST_LENGTH);
252 assert_eq!(encoded, digest.as_ref());
253
254 let decoded = Digest::decode(encoded).unwrap();
255 assert_eq!(digest, decoded);
256 }
257}