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 sha3::Digest as Sha3Digest;
9
10use super::{Digest, ElementHasher, Felt, FieldElement, Hasher};
11use crate::utils::{
12    ByteReader, ByteWriter, Deserializable, DeserializationError, HexParseError, Serializable,
13    bytes_to_hex_string, hex_to_bytes,
14};
15
16#[cfg(test)]
17mod tests;
18
19// CONSTANTS
20// ================================================================================================
21
22const DIGEST_BYTES: usize = 32;
23
24// DIGEST
25// ================================================================================================
26
27/// Keccak digest
28#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
29pub struct Keccak256Digest([u8; DIGEST_BYTES]);
30
31impl Keccak256Digest {
32    pub fn digests_as_bytes(digests: &[Keccak256Digest]) -> &[u8] {
33        let p = digests.as_ptr();
34        let len = digests.len() * DIGEST_BYTES;
35        unsafe { slice::from_raw_parts(p as *const u8, len) }
36    }
37}
38
39impl Default for Keccak256Digest {
40    fn default() -> Self {
41        Self([0; DIGEST_BYTES])
42    }
43}
44
45impl Deref for Keccak256Digest {
46    type Target = [u8];
47
48    fn deref(&self) -> &Self::Target {
49        &self.0
50    }
51}
52
53impl From<Keccak256Digest> for [u8; DIGEST_BYTES] {
54    fn from(value: Keccak256Digest) -> Self {
55        value.0
56    }
57}
58
59impl From<[u8; DIGEST_BYTES]> for Keccak256Digest {
60    fn from(value: [u8; DIGEST_BYTES]) -> Self {
61        Self(value)
62    }
63}
64
65impl From<Keccak256Digest> for String {
66    fn from(value: Keccak256Digest) -> Self {
67        bytes_to_hex_string(value.as_bytes())
68    }
69}
70
71impl TryFrom<&str> for Keccak256Digest {
72    type Error = HexParseError;
73
74    fn try_from(value: &str) -> Result<Self, Self::Error> {
75        hex_to_bytes(value).map(|v| v.into())
76    }
77}
78
79impl Serializable for Keccak256Digest {
80    fn write_into<W: ByteWriter>(&self, target: &mut W) {
81        target.write_bytes(&self.0);
82    }
83}
84
85impl Deserializable for Keccak256Digest {
86    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
87        source.read_array().map(Self)
88    }
89}
90
91impl Digest for Keccak256Digest {
92    fn as_bytes(&self) -> [u8; 32] {
93        self.0
94    }
95}
96
97// KECCAK256 HASHER
98// ================================================================================================
99
100/// Keccak256 hash function
101#[derive(Debug, Copy, Clone, Eq, PartialEq)]
102pub struct Keccak256;
103
104impl Hasher for Keccak256 {
105    /// Keccak256 collision resistance is 128-bits for 32-bytes output.
106    const COLLISION_RESISTANCE: u32 = 128;
107
108    type Digest = Keccak256Digest;
109
110    fn hash(bytes: &[u8]) -> Self::Digest {
111        let mut hasher = sha3::Keccak256::new();
112        hasher.update(bytes);
113
114        Keccak256Digest(hasher.finalize().into())
115    }
116
117    fn merge(values: &[Self::Digest; 2]) -> Self::Digest {
118        Self::hash(prepare_merge(values))
119    }
120
121    fn merge_many(values: &[Self::Digest]) -> Self::Digest {
122        let data = Keccak256Digest::digests_as_bytes(values);
123        let mut hasher = sha3::Keccak256::new();
124        hasher.update(data);
125
126        Keccak256Digest(hasher.finalize().into())
127    }
128
129    fn merge_with_int(seed: Self::Digest, value: u64) -> Self::Digest {
130        let mut hasher = sha3::Keccak256::new();
131        hasher.update(seed.0);
132        hasher.update(value.to_le_bytes());
133
134        Keccak256Digest(hasher.finalize().into())
135    }
136}
137
138impl ElementHasher for Keccak256 {
139    type BaseField = Felt;
140
141    fn hash_elements<E>(elements: &[E]) -> Self::Digest
142    where
143        E: FieldElement<BaseField = Self::BaseField>,
144    {
145        Keccak256Digest(hash_elements(elements))
146    }
147}
148
149impl Keccak256 {
150    /// Returns a hash of the provided sequence of bytes.
151    #[inline(always)]
152    pub fn hash(bytes: &[u8]) -> Keccak256Digest {
153        <Self as Hasher>::hash(bytes)
154    }
155
156    /// Returns a hash of two digests. This method is intended for use in construction of
157    /// Merkle trees and verification of Merkle paths.
158    #[inline(always)]
159    pub fn merge(values: &[Keccak256Digest; 2]) -> Keccak256Digest {
160        <Self as Hasher>::merge(values)
161    }
162
163    /// Returns a hash of the provided field elements.
164    #[inline(always)]
165    pub fn hash_elements<E>(elements: &[E]) -> Keccak256Digest
166    where
167        E: FieldElement<BaseField = Felt>,
168    {
169        <Self as ElementHasher>::hash_elements(elements)
170    }
171}
172
173// HELPER FUNCTIONS
174// ================================================================================================
175
176/// Hash the elements into bytes and shrink the output.
177fn hash_elements<E>(elements: &[E]) -> [u8; DIGEST_BYTES]
178where
179    E: FieldElement<BaseField = Felt>,
180{
181    // don't leak assumptions from felt and check its actual implementation.
182    // this is a compile-time branch so it is for free
183    let digest = if Felt::IS_CANONICAL {
184        let mut hasher = sha3::Keccak256::new();
185        hasher.update(E::elements_as_bytes(elements));
186        hasher.finalize()
187    } else {
188        let mut hasher = sha3::Keccak256::new();
189        // The Keccak-p permutation has a state of size 1600 bits. For Keccak256, the capacity
190        // is set to 512 bits and the rate is thus of size 1088 bits.
191        // This means that we can absorb 136 bytes into the rate portion of the state per invocation
192        // of the permutation function.
193        // we move the elements into the hasher via the buffer to give the CPU a chance to process
194        // multiple element-to-byte conversions in parallel
195        let mut buf = [0_u8; 136];
196        let mut chunk_iter = E::slice_as_base_elements(elements).chunks_exact(17);
197        for chunk in chunk_iter.by_ref() {
198            for i in 0..17 {
199                buf[i * 8..(i + 1) * 8].copy_from_slice(&chunk[i].as_int().to_le_bytes());
200            }
201            hasher.update(buf);
202        }
203
204        for element in chunk_iter.remainder() {
205            hasher.update(element.as_int().to_le_bytes());
206        }
207
208        hasher.finalize()
209    };
210    digest.into()
211}
212
213// Cast the slice into contiguous bytes.
214fn prepare_merge<const N: usize, D>(args: &[D; N]) -> &[u8]
215where
216    D: Deref<Target = [u8]>,
217{
218    // compile-time assertion
219    assert!(N > 0, "N shouldn't represent an empty slice!");
220    let values = args.as_ptr() as *const u8;
221    let len = size_of::<D>() * N;
222    // safety: the values are tested to be contiguous
223    let bytes = unsafe { from_raw_parts(values, len) };
224    debug_assert_eq!(args[0].deref(), &bytes[..len / N]);
225    bytes
226}