miden_crypto/hash/sha2/
mod.rs1use 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
16pub type Sha256Digest = Digest256;
23
24#[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 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 #[inline(always)]
73 pub fn hash_elements<E: BasedVectorSpace<Felt>>(elements: &[E]) -> Sha256Digest {
74 Sha256Digest::from(hash_elements_256(elements))
75 }
76
77 #[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
84pub type Sha512Digest = Digest512;
91
92#[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 #[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 #[inline(always)]
123 pub fn merge(values: &[Sha512Digest; 2]) -> Sha512Digest {
124 Self::hash(Sha512Digest::digests_as_bytes(values))
125 }
126
127 #[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 #[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 #[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
152fn 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 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
191fn 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 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}