Skip to main content

miden_crypto/hash/sha2/
mod.rs

1//! SHA2 hash function wrappers (SHA-256 and SHA-512).
2
3use core::mem::size_of;
4
5use p3_field::{BasedVectorSpace, PrimeField64};
6use sha2::Digest as Sha2Digest;
7
8use super::{
9    Felt, HasherExt,
10    digest::{DIGEST256_BYTES, DIGEST512_BYTES, Digest256, Digest512},
11};
12
13#[cfg(test)]
14mod tests;
15
16// SHA256 DIGEST
17// ================================================================================================
18
19/// SHA-256 digest (32 bytes).
20///
21/// This is a type alias to the generic `Digest256` type.
22pub type Sha256Digest = Digest256;
23
24// SHA256 HASHER
25// ================================================================================================
26
27/// SHA-256 hash function.
28#[derive(Debug, Copy, Clone, Eq, PartialEq)]
29pub struct Sha256;
30
31impl HasherExt for Sha256 {
32    type Digest = Sha256Digest;
33
34    fn hash_iter<'a>(slices: impl Iterator<Item = &'a [u8]>) -> Self::Digest {
35        let mut hasher = sha2::Sha256::new();
36        for slice in slices {
37            hasher.update(slice);
38        }
39        Sha256Digest::from(<[u8; DIGEST256_BYTES]>::from(hasher.finalize()))
40    }
41}
42
43impl Sha256 {
44    /// SHA-256 collision resistance is 128-bits for 32-bytes output.
45    pub const COLLISION_RESISTANCE: u32 = 128;
46
47    pub fn hash(bytes: &[u8]) -> Sha256Digest {
48        let mut hasher = sha2::Sha256::new();
49        hasher.update(bytes);
50        Sha256Digest::from(<[u8; DIGEST256_BYTES]>::from(hasher.finalize()))
51    }
52
53    pub fn merge(values: &[Sha256Digest; 2]) -> Sha256Digest {
54        Self::hash(Sha256Digest::digests_as_bytes(values))
55    }
56
57    pub fn merge_many(values: &[Sha256Digest]) -> Sha256Digest {
58        let data = Sha256Digest::digests_as_bytes(values);
59        let mut hasher = sha2::Sha256::new();
60        hasher.update(data);
61        Sha256Digest::from(<[u8; DIGEST256_BYTES]>::from(hasher.finalize()))
62    }
63
64    pub fn merge_with_int(seed: Sha256Digest, value: u64) -> Sha256Digest {
65        let mut hasher = sha2::Sha256::new();
66        hasher.update(&*seed);
67        hasher.update(value.to_le_bytes());
68        Sha256Digest::from(<[u8; DIGEST256_BYTES]>::from(hasher.finalize()))
69    }
70
71    /// Returns a hash of the provided field elements.
72    #[inline(always)]
73    pub fn hash_elements<E: BasedVectorSpace<Felt>>(elements: &[E]) -> Sha256Digest {
74        Sha256Digest::from(hash_elements_256(elements))
75    }
76
77    /// Hashes an iterator of byte slices.
78    #[inline(always)]
79    pub fn hash_iter<'a>(slices: impl Iterator<Item = &'a [u8]>) -> Sha256Digest {
80        <Self as HasherExt>::hash_iter(slices)
81    }
82}
83
84// SHA512 DIGEST
85// ================================================================================================
86
87/// SHA-512 digest (64 bytes).
88///
89/// This is a type alias to the generic `Digest512` type.
90pub type Sha512Digest = Digest512;
91
92// SHA512 HASHER
93// ================================================================================================
94
95/// SHA-512 hash function.
96#[derive(Debug, Copy, Clone, Eq, PartialEq)]
97pub struct Sha512;
98
99impl HasherExt for Sha512 {
100    type Digest = Sha512Digest;
101
102    fn hash_iter<'a>(slices: impl Iterator<Item = &'a [u8]>) -> Self::Digest {
103        let mut hasher = sha2::Sha512::new();
104        for slice in slices {
105            hasher.update(slice);
106        }
107        Sha512Digest::from(<[u8; DIGEST512_BYTES]>::from(hasher.finalize()))
108    }
109}
110
111impl Sha512 {
112    /// Returns a hash of the provided sequence of bytes.
113    #[inline(always)]
114    pub fn hash(bytes: &[u8]) -> Sha512Digest {
115        let mut hasher = sha2::Sha512::new();
116        hasher.update(bytes);
117        Sha512Digest::from(<[u8; DIGEST512_BYTES]>::from(hasher.finalize()))
118    }
119
120    /// Returns a hash of two digests. This method is intended for use in construction of
121    /// Merkle trees and verification of Merkle paths.
122    #[inline(always)]
123    pub fn merge(values: &[Sha512Digest; 2]) -> Sha512Digest {
124        Self::hash(Sha512Digest::digests_as_bytes(values))
125    }
126
127    /// Returns a hash of the provided digests.
128    #[inline(always)]
129    pub fn merge_many(values: &[Sha512Digest]) -> Sha512Digest {
130        let data = Sha512Digest::digests_as_bytes(values);
131        let mut hasher = sha2::Sha512::new();
132        hasher.update(data);
133        Sha512Digest::from(<[u8; DIGEST512_BYTES]>::from(hasher.finalize()))
134    }
135
136    /// Returns a hash of the provided field elements.
137    #[inline(always)]
138    pub fn hash_elements<E>(elements: &[E]) -> Sha512Digest
139    where
140        E: BasedVectorSpace<Felt>,
141    {
142        Sha512Digest::from(hash_elements_512(elements))
143    }
144
145    /// Hashes an iterator of byte slices.
146    #[inline(always)]
147    pub fn hash_iter<'a>(slices: impl Iterator<Item = &'a [u8]>) -> Sha512Digest {
148        <Self as HasherExt>::hash_iter(slices)
149    }
150}
151
152// HELPER FUNCTIONS
153// ================================================================================================
154
155/// Hash the elements into bytes for SHA-256.
156fn hash_elements_256<E>(elements: &[E]) -> [u8; DIGEST256_BYTES]
157where
158    E: BasedVectorSpace<Felt>,
159{
160    let digest = {
161        const FELT_BYTES: usize = size_of::<u64>();
162        const { assert!(FELT_BYTES == 8, "buffer arithmetic assumes 8-byte field elements") };
163
164        let mut hasher = sha2::Sha256::new();
165        // SHA-256 block size: 64 bytes
166        let mut buf = [0_u8; 64];
167        let mut buf_offset = 0;
168
169        for elem in elements.iter() {
170            for &felt in E::as_basis_coefficients_slice(elem) {
171                buf[buf_offset..buf_offset + FELT_BYTES]
172                    .copy_from_slice(&felt.as_canonical_u64().to_le_bytes());
173                buf_offset += FELT_BYTES;
174
175                if buf_offset == 64 {
176                    hasher.update(buf);
177                    buf_offset = 0;
178                }
179            }
180        }
181
182        if buf_offset > 0 {
183            hasher.update(&buf[..buf_offset]);
184        }
185
186        hasher.finalize()
187    };
188    digest.into()
189}
190
191/// Hash the elements into bytes for SHA-512.
192fn hash_elements_512<E>(elements: &[E]) -> [u8; DIGEST512_BYTES]
193where
194    E: BasedVectorSpace<Felt>,
195{
196    let digest = {
197        const FELT_BYTES: usize = size_of::<u64>();
198        const { assert!(FELT_BYTES == 8, "buffer arithmetic assumes 8-byte field elements") };
199
200        let mut hasher = sha2::Sha512::new();
201        // SHA-512 block size: 128 bytes
202        let mut buf = [0_u8; 128];
203        let mut buf_offset = 0;
204
205        for elem in elements.iter() {
206            for &felt in E::as_basis_coefficients_slice(elem) {
207                buf[buf_offset..buf_offset + FELT_BYTES]
208                    .copy_from_slice(&felt.as_canonical_u64().to_le_bytes());
209                buf_offset += FELT_BYTES;
210
211                if buf_offset == 128 {
212                    hasher.update(buf);
213                    buf_offset = 0;
214                }
215            }
216        }
217
218        if buf_offset > 0 {
219            hasher.update(&buf[..buf_offset]);
220        }
221
222        hasher.finalize()
223    };
224    digest.into()
225}