miden_crypto/hash/keccak/
mod.rs1use alloc::string::String;
2use core::{
3 mem::size_of,
4 ops::Deref,
5 slice::{self, from_raw_parts},
6};
7
8use p3_field::BasedVectorSpace;
9use sha3::Digest as Sha3Digest;
10
11use super::{Felt, HasherExt};
12use crate::{
13 field::PrimeField64,
14 utils::{
15 ByteReader, ByteWriter, Deserializable, DeserializationError, HexParseError, Serializable,
16 bytes_to_hex_string, hex_to_bytes,
17 },
18};
19
20#[cfg(test)]
21mod tests;
22
23const DIGEST_BYTES: usize = 32;
27
28#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
33#[repr(transparent)]
34pub struct Keccak256Digest([u8; DIGEST_BYTES]);
35
36impl Keccak256Digest {
37 pub fn as_bytes(&self) -> [u8; 32] {
38 self.0
39 }
40
41 pub fn digests_as_bytes(digests: &[Keccak256Digest]) -> &[u8] {
42 let p = digests.as_ptr();
43 let len = digests.len() * DIGEST_BYTES;
44 unsafe { slice::from_raw_parts(p as *const u8, len) }
45 }
46}
47
48impl Default for Keccak256Digest {
49 fn default() -> Self {
50 Self([0; DIGEST_BYTES])
51 }
52}
53
54impl Deref for Keccak256Digest {
55 type Target = [u8];
56
57 fn deref(&self) -> &Self::Target {
58 &self.0
59 }
60}
61
62impl From<Keccak256Digest> for [u8; DIGEST_BYTES] {
63 fn from(value: Keccak256Digest) -> Self {
64 value.0
65 }
66}
67
68impl From<[u8; DIGEST_BYTES]> for Keccak256Digest {
69 fn from(value: [u8; DIGEST_BYTES]) -> Self {
70 Self(value)
71 }
72}
73
74impl From<Keccak256Digest> for String {
75 fn from(value: Keccak256Digest) -> Self {
76 bytes_to_hex_string(value.as_bytes())
77 }
78}
79
80impl TryFrom<&str> for Keccak256Digest {
81 type Error = HexParseError;
82
83 fn try_from(value: &str) -> Result<Self, Self::Error> {
84 hex_to_bytes(value).map(|v| v.into())
85 }
86}
87
88impl Serializable for Keccak256Digest {
89 fn write_into<W: ByteWriter>(&self, target: &mut W) {
90 target.write_bytes(&self.0);
91 }
92}
93
94impl Deserializable for Keccak256Digest {
95 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
96 source.read_array().map(Self)
97 }
98}
99
100#[derive(Debug, Copy, Clone, Eq, PartialEq)]
105pub struct Keccak256;
106
107impl HasherExt for Keccak256 {
108 type Digest = Keccak256Digest;
109
110 fn hash_iter<'a>(slices: impl Iterator<Item = &'a [u8]>) -> Self::Digest {
111 let mut hasher = sha3::Keccak256::new();
112 for slice in slices {
113 hasher.update(slice);
114 }
115 Keccak256Digest(hasher.finalize().into())
116 }
117}
118
119impl Keccak256 {
120 pub const COLLISION_RESISTANCE: u32 = 128;
122
123 pub fn hash(bytes: &[u8]) -> Keccak256Digest {
124 let mut hasher = sha3::Keccak256::new();
125 hasher.update(bytes);
126
127 Keccak256Digest(hasher.finalize().into())
128 }
129
130 pub fn merge(values: &[Keccak256Digest; 2]) -> Keccak256Digest {
131 Self::hash(prepare_merge(values))
132 }
133
134 pub fn merge_many(values: &[Keccak256Digest]) -> Keccak256Digest {
135 let data = Keccak256Digest::digests_as_bytes(values);
136 let mut hasher = sha3::Keccak256::new();
137 hasher.update(data);
138
139 Keccak256Digest(hasher.finalize().into())
140 }
141
142 pub fn merge_with_int(seed: Keccak256Digest, value: u64) -> Keccak256Digest {
143 let mut hasher = sha3::Keccak256::new();
144 hasher.update(seed.0);
145 hasher.update(value.to_le_bytes());
146
147 Keccak256Digest(hasher.finalize().into())
148 }
149
150 #[inline(always)]
152 pub fn hash_elements<E>(elements: &[E]) -> Keccak256Digest
153 where
154 E: BasedVectorSpace<Felt>,
155 {
156 hash_elements(elements).into()
157 }
158
159 #[inline(always)]
161 pub fn hash_iter<'a>(slices: impl Iterator<Item = &'a [u8]>) -> Keccak256Digest {
162 <Self as HasherExt>::hash_iter(slices)
163 }
164}
165
166fn hash_elements<E>(elements: &[E]) -> [u8; DIGEST_BYTES]
171where
172 E: BasedVectorSpace<Felt>,
173{
174 let digest = {
176 const FELT_BYTES: usize = size_of::<u64>();
177 const { assert!(FELT_BYTES == 8, "buffer arithmetic assumes 8-byte field elements") };
178
179 let mut hasher = sha3::Keccak256::new();
180 let mut buf = [0_u8; 136];
182 let mut buf_offset = 0;
183
184 for elem in elements.iter() {
185 for &felt in E::as_basis_coefficients_slice(elem) {
186 buf[buf_offset..buf_offset + FELT_BYTES]
187 .copy_from_slice(&felt.as_canonical_u64().to_le_bytes());
188 buf_offset += FELT_BYTES;
189
190 if buf_offset == 136 {
191 hasher.update(buf);
192 buf_offset = 0;
193 }
194 }
195 }
196
197 if buf_offset > 0 {
198 hasher.update(&buf[..buf_offset]);
199 }
200
201 hasher.finalize()
202 };
203 digest.into()
204}
205
206fn prepare_merge<const N: usize, D>(args: &[D; N]) -> &[u8]
208where
209 D: Deref<Target = [u8]>,
210{
211 assert!(N > 0, "N shouldn't represent an empty slice!");
213 let values = args.as_ptr() as *const u8;
214 let len = size_of::<D>() * N;
215 let bytes = unsafe { from_raw_parts(values, len) };
217 debug_assert_eq!(args[0].deref(), &bytes[..len / N]);
218 bytes
219}