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 fn update(&mut self, message: &[u8]) -> &mut Self {
62 #[cfg(not(feature = "parallel"))]
63 self.hasher.update(message);
64
65 #[cfg(feature = "parallel")]
66 {
67 const PARALLEL_THRESHOLD: usize = 2usize.pow(17);
69
70 if message.len() >= PARALLEL_THRESHOLD {
72 self.hasher.update_rayon(message);
73 } else {
74 self.hasher.update(message);
75 }
76 }
77
78 self
79 }
80
81 fn finalize(&mut self) -> Self::Digest {
82 let finalized = self.hasher.finalize();
83 self.hasher.reset();
84 let array: [u8; DIGEST_LENGTH] = finalized.into();
85 Self::Digest::from(array)
86 }
87
88 fn reset(&mut self) -> &mut Self {
89 self.hasher = CoreBlake3::new();
90 self
91 }
92}
93
94#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
96#[repr(transparent)]
97pub struct Digest(pub [u8; DIGEST_LENGTH]);
98
99#[cfg(feature = "arbitrary")]
100impl<'a> arbitrary::Arbitrary<'a> for Digest {
101 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
102 let len = u.int_in_range(0..=256)?;
104 let data = u.bytes(len)?;
105 Ok(Blake3::hash(data))
106 }
107}
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 const EMPTY: Self = Self([0u8; DIGEST_LENGTH]);
171}
172
173impl Random for Digest {
174 fn random(mut rng: impl CryptoRngCore) -> Self {
175 let mut array = [0u8; DIGEST_LENGTH];
176 rng.fill_bytes(&mut array);
177 Self(array)
178 }
179}
180
181impl Zeroize for Digest {
182 fn zeroize(&mut self) {
183 self.0.zeroize();
184 }
185}
186
187#[cfg(test)]
188mod tests {
189 use super::*;
190 use commonware_codec::{DecodeExt, Encode};
191 use commonware_utils::hex;
192
193 const HELLO_DIGEST: [u8; DIGEST_LENGTH] =
194 hex!("d74981efa70a0c880b8d8c1985d075dbcbf679b99a5f9914e5aaf96b831a9e24");
195
196 #[test]
197 fn test_blake3() {
198 let msg = b"hello world";
199
200 let mut hasher = Blake3::new();
202 hasher.update(msg);
203 let digest = hasher.finalize();
204 assert!(Digest::decode(digest.as_ref()).is_ok());
205 assert_eq!(digest.as_ref(), HELLO_DIGEST);
206
207 hasher.update(msg);
209 let digest = hasher.finalize();
210 assert!(Digest::decode(digest.as_ref()).is_ok());
211 assert_eq!(digest.as_ref(), HELLO_DIGEST);
212
213 let hash = Blake3::hash(msg);
215 assert_eq!(hash.as_ref(), HELLO_DIGEST);
216 }
217
218 #[test]
219 fn test_blake3_len() {
220 assert_eq!(Digest::SIZE, DIGEST_LENGTH);
221 }
222
223 #[test]
224 fn test_codec() {
225 let msg = b"hello world";
226 let mut hasher = Blake3::new();
227 hasher.update(msg);
228 let digest = hasher.finalize();
229
230 let encoded = digest.encode();
231 assert_eq!(encoded.len(), DIGEST_LENGTH);
232 assert_eq!(encoded, digest.as_ref());
233
234 let decoded = Digest::decode(encoded).unwrap();
235 assert_eq!(digest, decoded);
236 }
237
238 #[cfg(feature = "arbitrary")]
239 mod conformance {
240 use super::*;
241 use commonware_codec::conformance::CodecConformance;
242
243 commonware_conformance::conformance_tests! {
244 CodecConformance<Digest>,
245 }
246 }
247}