miden_crypto/hash/sha2/
mod.rs

1//! SHA2 hash function wrappers (SHA-256 and SHA-512).
2//!
3//! # Note on SHA-512 and the Digest trait
4//!
5//! `Sha512Digest` does not implement the `Digest` trait because Winterfell's `Digest` trait
6//! requires a fixed 32-byte output via `as_bytes() -> [u8; 32]`, which is incompatible with
7//! SHA-512's native 64-byte output. Truncating to 32 bytes would create confusion with
8//! SHA-512/256 (which uses different initialization vectors per FIPS 180-4).
9//!
10//! See <https://github.com/facebook/winterfell/issues/406> for a proposal to make the
11//! `Digest` trait generic over output size.
12
13use alloc::string::String;
14use core::{
15    mem::size_of,
16    ops::Deref,
17    slice::{self, from_raw_parts},
18};
19
20use p3_field::{BasedVectorSpace, PrimeField64};
21use sha2::Digest as Sha2Digest;
22
23use super::{Felt, HasherExt};
24use crate::utils::{
25    ByteReader, ByteWriter, Deserializable, DeserializationError, HexParseError, Serializable,
26    bytes_to_hex_string, hex_to_bytes,
27};
28
29#[cfg(test)]
30mod tests;
31
32// CONSTANTS
33// ================================================================================================
34
35const DIGEST256_BYTES: usize = 32;
36const DIGEST512_BYTES: usize = 64;
37
38// SHA256 DIGEST
39// ================================================================================================
40
41/// SHA-256 digest (32 bytes).
42#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
43#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
44#[cfg_attr(feature = "serde", serde(into = "String", try_from = "&str"))]
45#[repr(transparent)]
46pub struct Sha256Digest([u8; DIGEST256_BYTES]);
47
48impl Sha256Digest {
49    pub fn as_bytes(&self) -> &[u8; DIGEST256_BYTES] {
50        &self.0
51    }
52
53    pub fn digests_as_bytes(digests: &[Sha256Digest]) -> &[u8] {
54        let p = digests.as_ptr();
55        let len = digests.len() * DIGEST256_BYTES;
56        unsafe { slice::from_raw_parts(p as *const u8, len) }
57    }
58}
59
60impl Default for Sha256Digest {
61    fn default() -> Self {
62        Self([0; DIGEST256_BYTES])
63    }
64}
65
66impl Deref for Sha256Digest {
67    type Target = [u8];
68
69    fn deref(&self) -> &Self::Target {
70        &self.0
71    }
72}
73
74impl From<Sha256Digest> for [u8; DIGEST256_BYTES] {
75    fn from(value: Sha256Digest) -> Self {
76        value.0
77    }
78}
79
80impl From<[u8; DIGEST256_BYTES]> for Sha256Digest {
81    fn from(value: [u8; DIGEST256_BYTES]) -> Self {
82        Self(value)
83    }
84}
85
86impl From<Sha256Digest> for String {
87    fn from(value: Sha256Digest) -> Self {
88        bytes_to_hex_string(*value.as_bytes())
89    }
90}
91
92impl TryFrom<&str> for Sha256Digest {
93    type Error = HexParseError;
94
95    fn try_from(value: &str) -> Result<Self, Self::Error> {
96        hex_to_bytes(value).map(|v| v.into())
97    }
98}
99
100impl Serializable for Sha256Digest {
101    fn write_into<W: ByteWriter>(&self, target: &mut W) {
102        target.write_bytes(&self.0);
103    }
104}
105
106impl Deserializable for Sha256Digest {
107    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
108        source.read_array().map(Self)
109    }
110}
111
112// SHA256 HASHER
113// ================================================================================================
114
115/// SHA-256 hash function.
116#[derive(Debug, Copy, Clone, Eq, PartialEq)]
117pub struct Sha256;
118
119impl HasherExt for Sha256 {
120    type Digest = Sha256Digest;
121
122    fn hash_iter<'a>(slices: impl Iterator<Item = &'a [u8]>) -> Self::Digest {
123        let mut hasher = sha2::Sha256::new();
124        for slice in slices {
125            hasher.update(slice);
126        }
127        Sha256Digest(hasher.finalize().into())
128    }
129}
130
131impl Sha256 {
132    /// SHA-256 collision resistance is 128-bits for 32-bytes output.
133    pub const COLLISION_RESISTANCE: u32 = 128;
134
135    pub fn hash(bytes: &[u8]) -> Sha256Digest {
136        let mut hasher = sha2::Sha256::new();
137        hasher.update(bytes);
138
139        Sha256Digest(hasher.finalize().into())
140    }
141
142    pub fn merge(values: &[Sha256Digest; 2]) -> Sha256Digest {
143        Self::hash(prepare_merge(values))
144    }
145
146    pub fn merge_many(values: &[Sha256Digest]) -> Sha256Digest {
147        let data = Sha256Digest::digests_as_bytes(values);
148        let mut hasher = sha2::Sha256::new();
149        hasher.update(data);
150
151        Sha256Digest(hasher.finalize().into())
152    }
153
154    pub fn merge_with_int(seed: Sha256Digest, value: u64) -> Sha256Digest {
155        let mut hasher = sha2::Sha256::new();
156        hasher.update(seed.0);
157        hasher.update(value.to_le_bytes());
158
159        Sha256Digest(hasher.finalize().into())
160    }
161
162    /// Returns a hash of the provided field elements.
163    #[inline(always)]
164    pub fn hash_elements<E: BasedVectorSpace<Felt>>(elements: &[E]) -> Sha256Digest {
165        Sha256Digest(hash_elements_256(elements))
166    }
167
168    /// Hashes an iterator of byte slices.
169    #[inline(always)]
170    pub fn hash_iter<'a>(slices: impl Iterator<Item = &'a [u8]>) -> Sha256Digest {
171        <Self as HasherExt>::hash_iter(slices)
172    }
173}
174
175// SHA512 DIGEST
176// ================================================================================================
177
178/// SHA-512 digest (64 bytes).
179#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
180#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
181#[cfg_attr(feature = "serde", serde(into = "String", try_from = "&str"))]
182#[repr(transparent)]
183pub struct Sha512Digest([u8; DIGEST512_BYTES]);
184
185impl Sha512Digest {
186    pub fn digests_as_bytes(digests: &[Sha512Digest]) -> &[u8] {
187        let p = digests.as_ptr();
188        let len = digests.len() * DIGEST512_BYTES;
189        unsafe { slice::from_raw_parts(p as *const u8, len) }
190    }
191}
192
193impl Default for Sha512Digest {
194    fn default() -> Self {
195        Self([0; DIGEST512_BYTES])
196    }
197}
198
199impl Deref for Sha512Digest {
200    type Target = [u8];
201
202    fn deref(&self) -> &Self::Target {
203        &self.0
204    }
205}
206
207impl From<Sha512Digest> for [u8; DIGEST512_BYTES] {
208    fn from(value: Sha512Digest) -> Self {
209        value.0
210    }
211}
212
213impl From<[u8; DIGEST512_BYTES]> for Sha512Digest {
214    fn from(value: [u8; DIGEST512_BYTES]) -> Self {
215        Self(value)
216    }
217}
218
219impl From<Sha512Digest> for String {
220    fn from(value: Sha512Digest) -> Self {
221        bytes_to_hex_string(value.0)
222    }
223}
224
225impl TryFrom<&str> for Sha512Digest {
226    type Error = HexParseError;
227
228    fn try_from(value: &str) -> Result<Self, Self::Error> {
229        hex_to_bytes(value).map(|v| v.into())
230    }
231}
232
233impl Serializable for Sha512Digest {
234    fn write_into<W: ByteWriter>(&self, target: &mut W) {
235        target.write_bytes(&self.0);
236    }
237}
238
239impl Deserializable for Sha512Digest {
240    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
241        source.read_array().map(Self)
242    }
243}
244
245// NOTE: Sha512 intentionally does not implement the Hasher, HasherExt, ElementHasher,
246// or Digest traits. See the module-level documentation for details.
247
248// SHA512 HASHER
249// ================================================================================================
250
251/// SHA-512 hash function.
252///
253/// Unlike [Sha256], this struct does not implement the `Hasher`, `HasherExt`, or `ElementHasher`
254/// traits because those traits require `Digest`, which mandates a 32-byte output. SHA-512
255/// produces a 64-byte digest, and truncating it would create confusion with SHA-512/256.
256#[derive(Debug, Copy, Clone, Eq, PartialEq)]
257pub struct Sha512;
258
259impl Sha512 {
260    /// Returns a hash of the provided sequence of bytes.
261    #[inline(always)]
262    pub fn hash(bytes: &[u8]) -> Sha512Digest {
263        let mut hasher = sha2::Sha512::new();
264        hasher.update(bytes);
265        Sha512Digest(hasher.finalize().into())
266    }
267
268    /// Returns a hash of two digests. This method is intended for use in construction of
269    /// Merkle trees and verification of Merkle paths.
270    #[inline(always)]
271    pub fn merge(values: &[Sha512Digest; 2]) -> Sha512Digest {
272        Self::hash(prepare_merge(values))
273    }
274
275    /// Returns a hash of the provided digests.
276    #[inline(always)]
277    pub fn merge_many(values: &[Sha512Digest]) -> Sha512Digest {
278        let data = Sha512Digest::digests_as_bytes(values);
279        let mut hasher = sha2::Sha512::new();
280        hasher.update(data);
281        Sha512Digest(hasher.finalize().into())
282    }
283
284    /// Returns a hash of the provided field elements.
285    #[inline(always)]
286    pub fn hash_elements<E>(elements: &[E]) -> Sha512Digest
287    where
288        E: BasedVectorSpace<Felt>,
289    {
290        Sha512Digest(hash_elements_512(elements))
291    }
292
293    /// Hashes an iterator of byte slices.
294    #[inline(always)]
295    pub fn hash_iter<'a>(slices: impl Iterator<Item = &'a [u8]>) -> Sha512Digest {
296        let mut hasher = sha2::Sha512::new();
297        for slice in slices {
298            hasher.update(slice);
299        }
300        Sha512Digest(hasher.finalize().into())
301    }
302}
303
304// HELPER FUNCTIONS
305// ================================================================================================
306
307/// Hash the elements into bytes for SHA-256.
308fn hash_elements_256<E>(elements: &[E]) -> [u8; DIGEST256_BYTES]
309where
310    E: BasedVectorSpace<Felt>,
311{
312    let digest = {
313        const FELT_BYTES: usize = size_of::<u64>();
314        const { assert!(FELT_BYTES == 8, "buffer arithmetic assumes 8-byte field elements") };
315
316        let mut hasher = sha2::Sha256::new();
317        // SHA-256 block size: 64 bytes
318        let mut buf = [0_u8; 64];
319        let mut buf_offset = 0;
320
321        for elem in elements.iter() {
322            for &felt in E::as_basis_coefficients_slice(elem) {
323                buf[buf_offset..buf_offset + FELT_BYTES]
324                    .copy_from_slice(&felt.as_canonical_u64().to_le_bytes());
325                buf_offset += FELT_BYTES;
326
327                if buf_offset == 64 {
328                    hasher.update(buf);
329                    buf_offset = 0;
330                }
331            }
332        }
333
334        if buf_offset > 0 {
335            hasher.update(&buf[..buf_offset]);
336        }
337
338        hasher.finalize()
339    };
340    digest.into()
341}
342
343/// Hash the elements into bytes for SHA-512.
344fn hash_elements_512<E>(elements: &[E]) -> [u8; DIGEST512_BYTES]
345where
346    E: BasedVectorSpace<Felt>,
347{
348    let digest = {
349        const FELT_BYTES: usize = size_of::<u64>();
350        const { assert!(FELT_BYTES == 8, "buffer arithmetic assumes 8-byte field elements") };
351
352        let mut hasher = sha2::Sha512::new();
353        // SHA-512 block size: 128 bytes
354        let mut buf = [0_u8; 128];
355        let mut buf_offset = 0;
356
357        for elem in elements.iter() {
358            for &felt in E::as_basis_coefficients_slice(elem) {
359                buf[buf_offset..buf_offset + FELT_BYTES]
360                    .copy_from_slice(&felt.as_canonical_u64().to_le_bytes());
361                buf_offset += FELT_BYTES;
362
363                if buf_offset == 128 {
364                    hasher.update(buf);
365                    buf_offset = 0;
366                }
367            }
368        }
369
370        if buf_offset > 0 {
371            hasher.update(&buf[..buf_offset]);
372        }
373
374        hasher.finalize()
375    };
376    digest.into()
377}
378
379/// Cast the slice into contiguous bytes.
380fn prepare_merge<const N: usize, D>(args: &[D; N]) -> &[u8]
381where
382    D: Deref<Target = [u8]>,
383{
384    // compile-time assertion
385    assert!(N > 0, "N shouldn't represent an empty slice!");
386    let values = args.as_ptr() as *const u8;
387    let len = size_of::<D>() * N;
388    // safety: the values are tested to be contiguous
389    let bytes = unsafe { from_raw_parts(values, len) };
390    debug_assert_eq!(args[0].deref(), &bytes[..len / N]);
391    bytes
392}