miden_crypto/hash/keccak/
mod.rs

1use 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
23// CONSTANTS
24// ================================================================================================
25
26const DIGEST_BYTES: usize = 32;
27
28// DIGEST
29// ================================================================================================
30
31/// Keccak digest
32#[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// KECCAK256 HASHER
101// ================================================================================================
102
103/// Keccak256 hash function
104#[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    /// Keccak256 collision resistance is 128-bits for 32-bytes output.
121    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    /// Returns a hash of the provided field elements.
151    #[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    /// Hashes an iterator of byte slices.
160    #[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
166// HELPER FUNCTIONS
167// ================================================================================================
168
169/// Hash the elements into bytes and shrink the output.
170fn hash_elements<E>(elements: &[E]) -> [u8; DIGEST_BYTES]
171where
172    E: BasedVectorSpace<Felt>,
173{
174    // don't leak assumptions from felt and check its actual implementation.
175    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        // Keccak256 rate: 1600 bits (state) - 512 bits (capacity) = 1088 bits = 136 bytes
181        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
206// Cast the slice into contiguous bytes.
207fn prepare_merge<const N: usize, D>(args: &[D; N]) -> &[u8]
208where
209    D: Deref<Target = [u8]>,
210{
211    // compile-time assertion
212    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    // safety: the values are tested to be contiguous
216    let bytes = unsafe { from_raw_parts(values, len) };
217    debug_assert_eq!(args[0].deref(), &bytes[..len / N]);
218    bytes
219}