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_formatting::Hex;
28use commonware_math::algebra::Random;
29use commonware_utils::{Array, Span};
30use core::{
31 fmt::{Debug, Display},
32 ops::Deref,
33};
34use rand_core::CryptoRngCore;
35use zeroize::Zeroize;
36
37pub type CoreBlake3 = blake3::Hasher;
39
40const DIGEST_LENGTH: usize = blake3::OUT_LEN;
41
42#[cfg_attr(
44 feature = "blake3-parallel",
45 doc = "When the input message is larger than 128KiB, `rayon` is used to parallelize hashing."
46)]
47#[derive(Debug, Default)]
48pub struct Blake3 {
49 hasher: CoreBlake3,
50}
51
52impl Clone for Blake3 {
53 fn clone(&self) -> Self {
54 Self::default()
56 }
57}
58
59impl Hasher for Blake3 {
60 type Digest = Digest;
61
62 fn update(&mut self, message: &[u8]) -> &mut Self {
63 #[cfg(not(feature = "blake3-parallel"))]
64 self.hasher.update(message);
65
66 #[cfg(feature = "blake3-parallel")]
67 {
68 const PARALLEL_THRESHOLD: usize = 2usize.pow(17);
70
71 if message.len() >= PARALLEL_THRESHOLD {
73 self.hasher.update_rayon(message);
74 } else {
75 self.hasher.update(message);
76 }
77 }
78
79 self
80 }
81
82 fn finalize(&mut self) -> Self::Digest {
83 let finalized = self.hasher.finalize();
84 self.hasher.reset();
85 let array: [u8; DIGEST_LENGTH] = finalized.into();
86 Self::Digest::from(array)
87 }
88
89 fn reset(&mut self) -> &mut Self {
90 self.hasher = CoreBlake3::new();
91 self
92 }
93}
94
95#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
97#[repr(transparent)]
98pub struct Digest(pub [u8; DIGEST_LENGTH]);
99
100#[cfg(feature = "arbitrary")]
101impl<'a> arbitrary::Arbitrary<'a> for Digest {
102 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
103 let len = u.int_in_range(0..=256)?;
105 let data = u.bytes(len)?;
106 Ok(Blake3::hash(data))
107 }
108}
109
110impl Write for Digest {
111 fn write(&self, buf: &mut impl BufMut) {
112 self.0.write(buf);
113 }
114}
115
116impl Read for Digest {
117 type Cfg = ();
118
119 fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, CodecError> {
120 let array = <[u8; DIGEST_LENGTH]>::read(buf)?;
121 Ok(Self(array))
122 }
123}
124
125impl FixedSize for Digest {
126 const SIZE: usize = DIGEST_LENGTH;
127}
128
129impl Span for Digest {}
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 core::fmt::Formatter<'_>) -> core::fmt::Result {
160 write!(f, "{}", Hex(&self.0))
161 }
162}
163
164impl Display for Digest {
165 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
166 write!(f, "{}", Hex(&self.0))
167 }
168}
169
170impl crate::Digest for Digest {
171 const EMPTY: Self = Self([0u8; DIGEST_LENGTH]);
172}
173
174impl Random for Digest {
175 fn random(mut rng: impl CryptoRngCore) -> Self {
176 let mut array = [0u8; DIGEST_LENGTH];
177 rng.fill_bytes(&mut array);
178 Self(array)
179 }
180}
181
182impl Zeroize for Digest {
183 fn zeroize(&mut self) {
184 self.0.zeroize();
185 }
186}
187
188#[cfg(test)]
189mod tests {
190 use super::*;
191 use commonware_codec::{DecodeExt, Encode};
192
193 const HELLO_DIGEST: [u8; DIGEST_LENGTH] = commonware_formatting::hex!(
194 "d74981efa70a0c880b8d8c1985d075dbcbf679b99a5f9914e5aaf96b831a9e24"
195 );
196
197 #[test]
198 fn test_blake3() {
199 let msg = b"hello world";
200
201 let mut hasher = Blake3::new();
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 hasher.update(msg);
210 let digest = hasher.finalize();
211 assert!(Digest::decode(digest.as_ref()).is_ok());
212 assert_eq!(digest.as_ref(), HELLO_DIGEST);
213
214 let hash = Blake3::hash(msg);
216 assert_eq!(hash.as_ref(), HELLO_DIGEST);
217 }
218
219 #[test]
220 fn test_blake3_len() {
221 assert_eq!(Digest::SIZE, DIGEST_LENGTH);
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}