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