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