arnaucube
arnaucube/miden-cryptoPublic Self-hosted repository
miden-crypto/src/hash/blake/mod.rs
src/hash/blake/mod.rsRaw
  1use crate::{
  2    ByteReader, ByteWriter, Deserializable, DeserializationError, Digest, ElementHasher, Felt,
  3    FieldElement, HashFn, Serializable, StarkField,
  4};
  5use core::{
  6    mem::{size_of, transmute, transmute_copy},
  7    ops::Deref,
  8    slice::from_raw_parts,
  9};
 10
 11#[cfg(test)]
 12mod tests;
 13
 14// BLAKE3 N-BIT OUTPUT
 15// ================================================================================================
 16
 17/// N-bytes output of a blake3 function.
 18///
 19/// Note: `N` can't be greater than `32` because [`Digest::as_bytes`] currently supports only 32
 20/// bytes.
 21#[derive(Debug, Copy, Clone, Eq, PartialEq)]
 22pub struct Blake3Digest<const N: usize>([u8; N]);
 23
 24impl<const N: usize> Default for Blake3Digest<N> {
 25    fn default() -> Self {
 26        Self([0; N])
 27    }
 28}
 29
 30impl<const N: usize> Deref for Blake3Digest<N> {
 31    type Target = [u8];
 32
 33    fn deref(&self) -> &Self::Target {
 34        &self.0
 35    }
 36}
 37
 38impl<const N: usize> Serializable for Blake3Digest<N> {
 39    fn write_into<W: ByteWriter>(&self, target: &mut W) {
 40        target.write_u8_slice(&self.0);
 41    }
 42}
 43
 44impl<const N: usize> Deserializable for Blake3Digest<N> {
 45    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
 46        source.read_u8_array().map(Self)
 47    }
 48}
 49
 50impl<const N: usize> Digest for Blake3Digest<N> {
 51    fn as_bytes(&self) -> [u8; 32] {
 52        // compile-time assertion
 53        assert!(N <= 32, "digest currently supports only 32 bytes!");
 54        expand_bytes(&self.0)
 55    }
 56}
 57
 58// BLAKE3 256-BIT OUTPUT
 59// ================================================================================================
 60
 61/// 256-bit output blake3 hasher.
 62pub struct Blake3_256;
 63
 64impl HashFn for Blake3_256 {
 65    type Digest = Blake3Digest<32>;
 66
 67    fn hash(bytes: &[u8]) -> Self::Digest {
 68        Blake3Digest(blake3::hash(bytes).into())
 69    }
 70
 71    fn merge(values: &[Self::Digest; 2]) -> Self::Digest {
 72        Self::hash(prepare_merge(values))
 73    }
 74
 75    fn merge_with_int(seed: Self::Digest, value: u64) -> Self::Digest {
 76        let mut hasher = blake3::Hasher::new();
 77        hasher.update(&seed.0);
 78        hasher.update(&value.to_le_bytes());
 79        Blake3Digest(hasher.finalize().into())
 80    }
 81}
 82
 83impl ElementHasher for Blake3_256 {
 84    type BaseField = Felt;
 85
 86    fn hash_elements<E>(elements: &[E]) -> Self::Digest
 87    where
 88        E: FieldElement<BaseField = Self::BaseField>,
 89    {
 90        Blake3Digest(hash_elements(elements))
 91    }
 92}
 93
 94// BLAKE3 192-BIT OUTPUT
 95// ================================================================================================
 96
 97/// 192-bit output blake3 hasher.
 98pub struct Blake3_192;
 99
100impl HashFn for Blake3_192 {
101    type Digest = Blake3Digest<24>;
102
103    fn hash(bytes: &[u8]) -> Self::Digest {
104        Blake3Digest(*shrink_bytes(&blake3::hash(bytes).into()))
105    }
106
107    fn merge(values: &[Self::Digest; 2]) -> Self::Digest {
108        Self::hash(prepare_merge(values))
109    }
110
111    fn merge_with_int(seed: Self::Digest, value: u64) -> Self::Digest {
112        let mut hasher = blake3::Hasher::new();
113        hasher.update(&seed.0);
114        hasher.update(&value.to_le_bytes());
115        Blake3Digest(*shrink_bytes(&hasher.finalize().into()))
116    }
117}
118
119impl ElementHasher for Blake3_192 {
120    type BaseField = Felt;
121
122    fn hash_elements<E>(elements: &[E]) -> Self::Digest
123    where
124        E: FieldElement<BaseField = Self::BaseField>,
125    {
126        Blake3Digest(hash_elements(elements))
127    }
128}
129
130// BLAKE3 160-BIT OUTPUT
131// ================================================================================================
132
133/// 160-bit output blake3 hasher.
134pub struct Blake3_160;
135
136impl HashFn for Blake3_160 {
137    type Digest = Blake3Digest<20>;
138
139    fn hash(bytes: &[u8]) -> Self::Digest {
140        Blake3Digest(*shrink_bytes(&blake3::hash(bytes).into()))
141    }
142
143    fn merge(values: &[Self::Digest; 2]) -> Self::Digest {
144        Self::hash(prepare_merge(values))
145    }
146
147    fn merge_with_int(seed: Self::Digest, value: u64) -> Self::Digest {
148        let mut hasher = blake3::Hasher::new();
149        hasher.update(&seed.0);
150        hasher.update(&value.to_le_bytes());
151        Blake3Digest(*shrink_bytes(&hasher.finalize().into()))
152    }
153}
154
155impl ElementHasher for Blake3_160 {
156    type BaseField = Felt;
157
158    fn hash_elements<E>(elements: &[E]) -> Self::Digest
159    where
160        E: FieldElement<BaseField = Self::BaseField>,
161    {
162        Blake3Digest(hash_elements(elements))
163    }
164}
165
166// HELPER FUNCTIONS
167// ================================================================================================
168
169/// Zero-copy ref shrink to array.
170fn shrink_bytes<const M: usize, const N: usize>(bytes: &[u8; M]) -> &[u8; N] {
171    // compile-time assertion
172    assert!(
173        M >= N,
174        "N should fit in M so it can be safely transmuted into a smaller slice!"
175    );
176    // safety: bytes len is asserted
177    unsafe { transmute(bytes) }
178}
179
180/// Hash the elements into bytes and shrink the output.
181fn hash_elements<const N: usize, E>(elements: &[E]) -> [u8; N]
182where
183    E: FieldElement<BaseField = Felt>,
184{
185    // don't leak assumptions from felt and check its actual implementation.
186    // this is a compile-time branch so it is for free
187    let digest = if Felt::IS_CANONICAL {
188        blake3::hash(E::elements_as_bytes(elements))
189    } else {
190        E::as_base_elements(elements)
191            .iter()
192            .fold(blake3::Hasher::new(), |mut hasher, felt| {
193                hasher.update(&felt.as_int().to_le_bytes());
194                hasher
195            })
196            .finalize()
197    };
198    *shrink_bytes(&digest.into())
199}
200
201/// Owned bytes expansion.
202fn expand_bytes<const M: usize, const N: usize>(bytes: &[u8; M]) -> [u8; N] {
203    // compile-time assertion
204    assert!(M <= N, "M should fit in N so M can be expanded!");
205    // this branch is constant so it will be optimized to be either one of the variants in release
206    // mode
207    if M == N {
208        // safety: the sizes are checked to be the same
209        unsafe { transmute_copy(bytes) }
210    } else {
211        let mut expanded = [0u8; N];
212        expanded[..M].copy_from_slice(bytes);
213        expanded
214    }
215}
216
217// Cast the slice into contiguous bytes.
218fn prepare_merge<const N: usize, D>(args: &[D; N]) -> &[u8]
219where
220    D: Deref<Target = [u8]>,
221{
222    // compile-time assertion
223    assert!(N > 0, "N shouldn't represent an empty slice!");
224    let values = args.as_ptr() as *const u8;
225    let len = size_of::<D>() * N;
226    // safety: the values are tested to be contiguous
227    let bytes = unsafe { from_raw_parts(values, len) };
228    debug_assert_eq!(args[0].deref(), &bytes[..len / N]);
229    bytes
230}