commonware_cryptography/blake3/
mod.rs1use crate::Hasher;
22use blake3::Hash;
23use bytes::{Buf, BufMut};
24use commonware_codec::{Error as CodecError, FixedArray, FixedSize, Read, ReadExt, Write};
25use commonware_formatting::Hex;
26use commonware_math::algebra::Random;
27use commonware_utils::{Array, Span};
28use core::{
29 fmt::{Debug, Display},
30 ops::Deref,
31};
32use rand_core::CryptoRng;
33use zeroize::Zeroize;
34
35pub type CoreBlake3 = blake3::Hasher;
37
38const DIGEST_LENGTH: usize = blake3::OUT_LEN;
39
40#[cfg_attr(
42 feature = "blake3-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 Hasher for Blake3 {
51 type Digest = Digest;
52
53 fn hash(parts: &[&[u8]]) -> Self::Digest {
54 let mut hasher = Self::default();
55 for part in parts {
56 hasher.update(part);
57 }
58 hasher.finalize().1
59 }
60
61 fn hash_pair(left: &[&[u8]], right: &[&[u8]]) -> (Self::Digest, Self::Digest) {
62 (Self::hash(left), Self::hash(right))
63 }
64
65 fn update(&mut self, message: &[u8]) -> &mut Self {
66 #[cfg(not(feature = "blake3-parallel"))]
67 self.hasher.update(message);
68
69 #[cfg(feature = "blake3-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, Self::Digest) {
86 let finalized = self.hasher.finalize();
87 self.hasher.reset();
88 let array: [u8; DIGEST_LENGTH] = finalized.into();
89 (self, Self::Digest::from(array))
90 }
91}
92
93#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, FixedArray)]
95#[fixed_array(infallible)]
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 AsRef<[u8]> for Digest {
139 fn as_ref(&self) -> &[u8] {
140 &self.0
141 }
142}
143
144impl Deref for Digest {
145 type Target = [u8];
146 fn deref(&self) -> &[u8] {
147 &self.0
148 }
149}
150
151impl Debug for Digest {
152 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
153 write!(f, "{}", Hex(&self.0))
154 }
155}
156
157impl Display for Digest {
158 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
159 write!(f, "{}", Hex(&self.0))
160 }
161}
162
163impl crate::Digest for Digest {
164 const EMPTY: Self = Self([0u8; DIGEST_LENGTH]);
165}
166
167impl Random for Digest {
168 fn random(mut rng: impl CryptoRng) -> Self {
169 let mut array = [0u8; DIGEST_LENGTH];
170 rng.fill_bytes(&mut array);
171 Self(array)
172 }
173}
174
175impl Zeroize for Digest {
176 fn zeroize(&mut self) {
177 self.0.zeroize();
178 }
179}
180
181#[cfg(test)]
182mod tests {
183 use super::*;
184 use commonware_codec::{DecodeExt, Encode};
185
186 const HELLO_DIGEST: [u8; DIGEST_LENGTH] = commonware_formatting::hex!(
187 "d74981efa70a0c880b8d8c1985d075dbcbf679b99a5f9914e5aaf96b831a9e24"
188 );
189
190 #[test]
191 fn test_blake3() {
192 let msg = b"hello world";
193
194 let mut hasher = Blake3::default();
196 hasher.update(msg);
197 let (hasher, digest) = hasher.finalize();
198 assert!(Digest::decode(digest.as_ref()).is_ok());
199 assert_eq!(digest.as_ref(), HELLO_DIGEST);
200
201 let mut hasher = hasher;
203 hasher.update(msg);
204 let (_, digest) = hasher.finalize();
205 assert!(Digest::decode(digest.as_ref()).is_ok());
206 assert_eq!(digest.as_ref(), HELLO_DIGEST);
207
208 let hash = Blake3::hash(&[msg]);
210 assert_eq!(hash.as_ref(), HELLO_DIGEST);
211
212 let hash = Blake3::hash(&[b"hello", b" world"]);
214 assert_eq!(hash.as_ref(), HELLO_DIGEST);
215 }
216
217 #[test]
218 fn test_blake3_len() {
219 assert_eq!(Digest::SIZE, DIGEST_LENGTH);
220 }
221
222 #[test]
223 fn test_codec() {
224 let msg = b"hello world";
225 let mut hasher = Blake3::default();
226 hasher.update(msg);
227 let (_, digest) = hasher.finalize();
228
229 let encoded = digest.encode();
230 assert_eq!(encoded.len(), DIGEST_LENGTH);
231 assert_eq!(encoded, digest.as_ref());
232
233 let decoded = Digest::decode(encoded).unwrap();
234 assert_eq!(digest, decoded);
235 }
236
237 #[cfg(feature = "arbitrary")]
238 mod conformance {
239 use super::*;
240 use commonware_codec::conformance::CodecConformance;
241
242 commonware_conformance::conformance_tests! {
243 CodecConformance<Digest>,
244 }
245 }
246}