arnaucube
arnaucube/miden-cryptoPublic Self-hosted repository
miden-crypto/src/hash/blake/mod.rs
src/hash/blake/mod.rsRaw
  1use super::{Digest, ElementHasher, Felt, FieldElement, Hasher, StarkField};
  2use crate::utils::{
  3    bytes_to_hex_string, hex_to_bytes, string::String, ByteReader, ByteWriter, Deserializable,
  4    DeserializationError, HexParseError, Serializable,
  5};
  6use core::{
  7    mem::{size_of, transmute, transmute_copy},
  8    ops::Deref,
  9    slice::from_raw_parts,
 10};
 11
 12#[cfg(test)]
 13mod tests;
 14
 15// CONSTANTS
 16// ================================================================================================
 17
 18const DIGEST32_BYTES: usize = 32;
 19const DIGEST24_BYTES: usize = 24;
 20const DIGEST20_BYTES: usize = 20;
 21
 22// BLAKE3 N-BIT OUTPUT
 23// ================================================================================================
 24
 25/// N-bytes output of a blake3 function.
 26///
 27/// Note: `N` can't be greater than `32` because [`Digest::as_bytes`] currently supports only 32
 28/// bytes.
 29#[derive(Debug, Copy, Clone, Eq, PartialEq)]
 30#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
 31#[cfg_attr(feature = "serde", serde(into = "String", try_from = "&str"))]
 32pub struct Blake3Digest<const N: usize>([u8; N]);
 33
 34impl<const N: usize> Default for Blake3Digest<N> {
 35    fn default() -> Self {
 36        Self([0; N])
 37    }
 38}
 39
 40impl<const N: usize> Deref for Blake3Digest<N> {
 41    type Target = [u8];
 42
 43    fn deref(&self) -> &Self::Target {
 44        &self.0
 45    }
 46}
 47
 48impl<const N: usize> From<Blake3Digest<N>> for [u8; N] {
 49    fn from(value: Blake3Digest<N>) -> Self {
 50        value.0
 51    }
 52}
 53
 54impl<const N: usize> From<[u8; N]> for Blake3Digest<N> {
 55    fn from(value: [u8; N]) -> Self {
 56        Self(value)
 57    }
 58}
 59
 60impl<const N: usize> From<Blake3Digest<N>> for String {
 61    fn from(value: Blake3Digest<N>) -> Self {
 62        bytes_to_hex_string(value.as_bytes())
 63    }
 64}
 65
 66impl<const N: usize> TryFrom<&str> for Blake3Digest<N> {
 67    type Error = HexParseError;
 68
 69    fn try_from(value: &str) -> Result<Self, Self::Error> {
 70        hex_to_bytes(value).map(|v| v.into())
 71    }
 72}
 73
 74impl<const N: usize> Serializable for Blake3Digest<N> {
 75    fn write_into<W: ByteWriter>(&self, target: &mut W) {
 76        target.write_bytes(&self.0);
 77    }
 78}
 79
 80impl<const N: usize> Deserializable for Blake3Digest<N> {
 81    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
 82        source.read_array().map(Self)
 83    }
 84}
 85
 86impl<const N: usize> Digest for Blake3Digest<N> {
 87    fn as_bytes(&self) -> [u8; 32] {
 88        // compile-time assertion
 89        assert!(N <= 32, "digest currently supports only 32 bytes!");
 90        expand_bytes(&self.0)
 91    }
 92}
 93
 94// BLAKE3 256-BIT OUTPUT
 95// ================================================================================================
 96
 97/// 256-bit output blake3 hasher.
 98#[derive(Debug, Copy, Clone, Eq, PartialEq)]
 99pub struct Blake3_256;
100
101impl Hasher for Blake3_256 {
102    /// Blake3 collision resistance is 128-bits for 32-bytes output.
103    const COLLISION_RESISTANCE: u32 = 128;
104
105    type Digest = Blake3Digest<32>;
106
107    fn hash(bytes: &[u8]) -> Self::Digest {
108        Blake3Digest(blake3::hash(bytes).into())
109    }
110
111    fn merge(values: &[Self::Digest; 2]) -> Self::Digest {
112        Self::hash(prepare_merge(values))
113    }
114
115    fn merge_with_int(seed: Self::Digest, value: u64) -> Self::Digest {
116        let mut hasher = blake3::Hasher::new();
117        hasher.update(&seed.0);
118        hasher.update(&value.to_le_bytes());
119        Blake3Digest(hasher.finalize().into())
120    }
121}
122
123impl ElementHasher for Blake3_256 {
124    type BaseField = Felt;
125
126    fn hash_elements<E>(elements: &[E]) -> Self::Digest
127    where
128        E: FieldElement<BaseField = Self::BaseField>,
129    {
130        Blake3Digest(hash_elements(elements))
131    }
132}
133
134impl Blake3_256 {
135    /// Returns a hash of the provided sequence of bytes.
136    #[inline(always)]
137    pub fn hash(bytes: &[u8]) -> Blake3Digest<DIGEST32_BYTES> {
138        <Self as Hasher>::hash(bytes)
139    }
140
141    /// Returns a hash of two digests. This method is intended for use in construction of
142    /// Merkle trees and verification of Merkle paths.
143    #[inline(always)]
144    pub fn merge(values: &[Blake3Digest<DIGEST32_BYTES>; 2]) -> Blake3Digest<DIGEST32_BYTES> {
145        <Self as Hasher>::merge(values)
146    }
147
148    /// Returns a hash of the provided field elements.
149    #[inline(always)]
150    pub fn hash_elements<E>(elements: &[E]) -> Blake3Digest<DIGEST32_BYTES>
151    where
152        E: FieldElement<BaseField = Felt>,
153    {
154        <Self as ElementHasher>::hash_elements(elements)
155    }
156}
157
158// BLAKE3 192-BIT OUTPUT
159// ================================================================================================
160
161/// 192-bit output blake3 hasher.
162#[derive(Debug, Copy, Clone, Eq, PartialEq)]
163pub struct Blake3_192;
164
165impl Hasher for Blake3_192 {
166    /// Blake3 collision resistance is 96-bits for 24-bytes output.
167    const COLLISION_RESISTANCE: u32 = 96;
168
169    type Digest = Blake3Digest<24>;
170
171    fn hash(bytes: &[u8]) -> Self::Digest {
172        Blake3Digest(*shrink_bytes(&blake3::hash(bytes).into()))
173    }
174
175    fn merge(values: &[Self::Digest; 2]) -> Self::Digest {
176        Self::hash(prepare_merge(values))
177    }
178
179    fn merge_with_int(seed: Self::Digest, value: u64) -> Self::Digest {
180        let mut hasher = blake3::Hasher::new();
181        hasher.update(&seed.0);
182        hasher.update(&value.to_le_bytes());
183        Blake3Digest(*shrink_bytes(&hasher.finalize().into()))
184    }
185}
186
187impl ElementHasher for Blake3_192 {
188    type BaseField = Felt;
189
190    fn hash_elements<E>(elements: &[E]) -> Self::Digest
191    where
192        E: FieldElement<BaseField = Self::BaseField>,
193    {
194        Blake3Digest(hash_elements(elements))
195    }
196}
197
198impl Blake3_192 {
199    /// Returns a hash of the provided sequence of bytes.
200    #[inline(always)]
201    pub fn hash(bytes: &[u8]) -> Blake3Digest<DIGEST24_BYTES> {
202        <Self as Hasher>::hash(bytes)
203    }
204
205    /// Returns a hash of two digests. This method is intended for use in construction of
206    /// Merkle trees and verification of Merkle paths.
207    #[inline(always)]
208    pub fn merge(values: &[Blake3Digest<DIGEST24_BYTES>; 2]) -> Blake3Digest<DIGEST24_BYTES> {
209        <Self as Hasher>::merge(values)
210    }
211
212    /// Returns a hash of the provided field elements.
213    #[inline(always)]
214    pub fn hash_elements<E>(elements: &[E]) -> Blake3Digest<DIGEST24_BYTES>
215    where
216        E: FieldElement<BaseField = Felt>,
217    {
218        <Self as ElementHasher>::hash_elements(elements)
219    }
220}
221
222// BLAKE3 160-BIT OUTPUT
223// ================================================================================================
224
225/// 160-bit output blake3 hasher.
226#[derive(Debug, Copy, Clone, Eq, PartialEq)]
227pub struct Blake3_160;
228
229impl Hasher for Blake3_160 {
230    /// Blake3 collision resistance is 80-bits for 20-bytes output.
231    const COLLISION_RESISTANCE: u32 = 80;
232
233    type Digest = Blake3Digest<20>;
234
235    fn hash(bytes: &[u8]) -> Self::Digest {
236        Blake3Digest(*shrink_bytes(&blake3::hash(bytes).into()))
237    }
238
239    fn merge(values: &[Self::Digest; 2]) -> Self::Digest {
240        Self::hash(prepare_merge(values))
241    }
242
243    fn merge_with_int(seed: Self::Digest, value: u64) -> Self::Digest {
244        let mut hasher = blake3::Hasher::new();
245        hasher.update(&seed.0);
246        hasher.update(&value.to_le_bytes());
247        Blake3Digest(*shrink_bytes(&hasher.finalize().into()))
248    }
249}
250
251impl ElementHasher for Blake3_160 {
252    type BaseField = Felt;
253
254    fn hash_elements<E>(elements: &[E]) -> Self::Digest
255    where
256        E: FieldElement<BaseField = Self::BaseField>,
257    {
258        Blake3Digest(hash_elements(elements))
259    }
260}
261
262impl Blake3_160 {
263    /// Returns a hash of the provided sequence of bytes.
264    #[inline(always)]
265    pub fn hash(bytes: &[u8]) -> Blake3Digest<DIGEST20_BYTES> {
266        <Self as Hasher>::hash(bytes)
267    }
268
269    /// Returns a hash of two digests. This method is intended for use in construction of
270    /// Merkle trees and verification of Merkle paths.
271    #[inline(always)]
272    pub fn merge(values: &[Blake3Digest<DIGEST20_BYTES>; 2]) -> Blake3Digest<DIGEST20_BYTES> {
273        <Self as Hasher>::merge(values)
274    }
275
276    /// Returns a hash of the provided field elements.
277    #[inline(always)]
278    pub fn hash_elements<E>(elements: &[E]) -> Blake3Digest<DIGEST20_BYTES>
279    where
280        E: FieldElement<BaseField = Felt>,
281    {
282        <Self as ElementHasher>::hash_elements(elements)
283    }
284}
285
286// HELPER FUNCTIONS
287// ================================================================================================
288
289/// Zero-copy ref shrink to array.
290fn shrink_bytes<const M: usize, const N: usize>(bytes: &[u8; M]) -> &[u8; N] {
291    // compile-time assertion
292    assert!(M >= N, "N should fit in M so it can be safely transmuted into a smaller slice!");
293    // safety: bytes len is asserted
294    unsafe { transmute(bytes) }
295}
296
297/// Hash the elements into bytes and shrink the output.
298fn hash_elements<const N: usize, E>(elements: &[E]) -> [u8; N]
299where
300    E: FieldElement<BaseField = Felt>,
301{
302    // don't leak assumptions from felt and check its actual implementation.
303    // this is a compile-time branch so it is for free
304    let digest = if Felt::IS_CANONICAL {
305        blake3::hash(E::elements_as_bytes(elements))
306    } else {
307        let mut hasher = blake3::Hasher::new();
308
309        // BLAKE3 state is 64 bytes - so, we can absorb 64 bytes into the state in a single
310        // permutation. we move the elements into the hasher via the buffer to give the CPU
311        // a chance to process multiple element-to-byte conversions in parallel
312        let mut buf = [0_u8; 64];
313        let mut chunk_iter = E::slice_as_base_elements(elements).chunks_exact(8);
314        for chunk in chunk_iter.by_ref() {
315            for i in 0..8 {
316                buf[i * 8..(i + 1) * 8].copy_from_slice(&chunk[i].as_int().to_le_bytes());
317            }
318            hasher.update(&buf);
319        }
320
321        for element in chunk_iter.remainder() {
322            hasher.update(&element.as_int().to_le_bytes());
323        }
324
325        hasher.finalize()
326    };
327    *shrink_bytes(&digest.into())
328}
329
330/// Owned bytes expansion.
331fn expand_bytes<const M: usize, const N: usize>(bytes: &[u8; M]) -> [u8; N] {
332    // compile-time assertion
333    assert!(M <= N, "M should fit in N so M can be expanded!");
334    // this branch is constant so it will be optimized to be either one of the variants in release
335    // mode
336    if M == N {
337        // safety: the sizes are checked to be the same
338        unsafe { transmute_copy(bytes) }
339    } else {
340        let mut expanded = [0u8; N];
341        expanded[..M].copy_from_slice(bytes);
342        expanded
343    }
344}
345
346// Cast the slice into contiguous bytes.
347fn prepare_merge<const N: usize, D>(args: &[D; N]) -> &[u8]
348where
349    D: Deref<Target = [u8]>,
350{
351    // compile-time assertion
352    assert!(N > 0, "N shouldn't represent an empty slice!");
353    let values = args.as_ptr() as *const u8;
354    let len = size_of::<D>() * N;
355    // safety: the values are tested to be contiguous
356    let bytes = unsafe { from_raw_parts(values, len) };
357    debug_assert_eq!(args[0].deref(), &bytes[..len / N]);
358    bytes
359}