miden_stdlib_sys/stdlib/crypto/
hashes.rs

1//! Contains procedures for computing hashes using BLAKE3 and SHA256 hash
2//! functions. The input and output elements are assumed to contain one 32-bit
3//! value per element.
4
5use alloc::vec::Vec;
6
7use crate::{
8    felt,
9    intrinsics::{assert_eq, Digest, Felt, Word},
10};
11
12extern "C" {
13    /// Computes BLAKE3 1-to-1 hash.
14    ///
15    /// Input: 32-bytes stored in the first 8 elements of the stack (32 bits per element).
16    /// Output: A 32-byte digest stored in the first 8 elements of stack (32 bits per element).
17    /// The output is passed back to the caller via a pointer.
18    #[link_name = "std::crypto::hashes::blake3::hash_1to1"]
19    fn extern_blake3_hash_1to1(
20        e1: u32,
21        e2: u32,
22        e3: u32,
23        e4: u32,
24        e5: u32,
25        e6: u32,
26        e7: u32,
27        e8: u32,
28        ptr: *mut u8,
29    );
30
31    /// Computes BLAKE3 2-to-1 hash.
32    ///
33    /// Input: 64-bytes stored in the first 16 elements of the stack (32 bits per element).
34    /// Output: A 32-byte digest stored in the first 8 elements of stack (32 bits per element)
35    /// The output is passed back to the caller via a pointer.
36    #[link_name = "std::crypto::hashes::blake3::hash_2to1"]
37    fn extern_blake3_hash_2to1(
38        e1: u32,
39        e2: u32,
40        e3: u32,
41        e4: u32,
42        e5: u32,
43        e6: u32,
44        e7: u32,
45        e8: u32,
46        e9: u32,
47        e10: u32,
48        e11: u32,
49        e12: u32,
50        e13: u32,
51        e14: u32,
52        e15: u32,
53        e16: u32,
54        ptr: *mut u8,
55    );
56}
57
58extern "C" {
59    /// Computes SHA256 1-to-1 hash.
60    ///
61    /// Input: 32-bytes stored in the first 8 elements of the stack (32 bits per element).
62    /// Output: A 32-byte digest stored in the first 8 elements of stack (32 bits per element).
63    /// The output is passed back to the caller via a pointer.
64    #[link_name = "std::crypto::hashes::sha256::hash_1to1"]
65    fn extern_sha256_hash_1to1(
66        e1: u32,
67        e2: u32,
68        e3: u32,
69        e4: u32,
70        e5: u32,
71        e6: u32,
72        e7: u32,
73        e8: u32,
74        ptr: *mut u8,
75    );
76
77    /// Computes SHA256 2-to-1 hash.
78    ///
79    /// Input: 64-bytes stored in the first 16 elements of the stack (32 bits per element).
80    /// Output: A 32-byte digest stored in the first 8 elements of stack (32 bits per element).
81    /// The output is passed back to the caller via a pointer.
82    #[link_name = "std::crypto::hashes::sha256::hash_2to1"]
83    fn extern_sha256_hash_2to1(
84        e1: u32,
85        e2: u32,
86        e3: u32,
87        e4: u32,
88        e5: u32,
89        e6: u32,
90        e7: u32,
91        e8: u32,
92        e9: u32,
93        e10: u32,
94        e11: u32,
95        e12: u32,
96        e13: u32,
97        e14: u32,
98        e15: u32,
99        e16: u32,
100        ptr: *mut u8,
101    );
102}
103
104extern "C" {
105    /// Computes the hash of a sequence of field elements using the Rescue Prime Optimized (RPO)
106    /// hash function.
107    ///
108    /// This maps to the `std::crypto::rpo::hash_memory` procedure in the Miden stdlib.
109    ///
110    /// Input: A pointer to the memory location and the number of elements to hash
111    /// Output: One digest (4 field elements)
112    /// The output is passed back to the caller via a pointer.
113    #[link_name = "std::crypto::hashes::rpo::hash_memory"]
114    pub fn extern_hash_memory(ptr: u32, num_elements: u32, result_ptr: *mut Felt);
115}
116
117/// Hashes a 32-byte input to a 32-byte output using the given hash function.
118#[inline(always)]
119fn hash_1to1(
120    input: [u8; 32],
121    extern_hash_1to1: unsafe extern "C" fn(u32, u32, u32, u32, u32, u32, u32, u32, *mut u8),
122) -> [u8; 32] {
123    use crate::intrinsics::WordAligned;
124    let input = unsafe { core::mem::transmute::<[u8; 32], [u32; 8]>(input) };
125    unsafe {
126        let mut ret_area = ::core::mem::MaybeUninit::<WordAligned<[u8; 32]>>::uninit();
127        let ptr = ret_area.as_mut_ptr() as *mut u8;
128        extern_hash_1to1(
129            input[0], input[1], input[2], input[3], input[4], input[5], input[6], input[7], ptr,
130        );
131        ret_area.assume_init().into_inner()
132    }
133}
134
135/// Hashes a 64-byte input to a 32-byte output using the given hash function.
136#[inline(always)]
137fn hash_2to1(
138    input: [u8; 64],
139    extern_hash_2to1: unsafe extern "C" fn(
140        u32,
141        u32,
142        u32,
143        u32,
144        u32,
145        u32,
146        u32,
147        u32,
148        u32,
149        u32,
150        u32,
151        u32,
152        u32,
153        u32,
154        u32,
155        u32,
156        *mut u8,
157    ),
158) -> [u8; 32] {
159    let input = unsafe { core::mem::transmute::<[u8; 64], [u32; 16]>(input) };
160    unsafe {
161        let mut ret_area = ::core::mem::MaybeUninit::<[u8; 32]>::uninit();
162        let ptr = ret_area.as_mut_ptr() as *mut u8;
163        extern_hash_2to1(
164            input[0], input[1], input[2], input[3], input[4], input[5], input[6], input[7],
165            input[8], input[9], input[10], input[11], input[12], input[13], input[14], input[15],
166            ptr,
167        );
168        ret_area.assume_init()
169    }
170}
171
172/// Hashes a 32-byte input to a 32-byte output using the BLAKE3 hash function.
173#[inline]
174pub fn blake3_hash_1to1(input: [u8; 32]) -> [u8; 32] {
175    hash_1to1(input, extern_blake3_hash_1to1)
176}
177
178/// Hashes a 64-byte input to a 32-byte output using the BLAKE3 hash function.
179#[inline]
180pub fn blake3_hash_2to1(input: [u8; 64]) -> [u8; 32] {
181    hash_2to1(input, extern_blake3_hash_2to1)
182}
183
184/// Hashes a 32-byte input to a 32-byte output using the SHA256 hash function.
185#[inline]
186pub fn sha256_hash_1to1(input: [u8; 32]) -> [u8; 32] {
187    hash_1to1(input, extern_sha256_hash_1to1)
188}
189
190/// Hashes a 64-byte input to a 32-byte output using the SHA256 hash function.
191#[inline]
192pub fn sha256_hash_2to1(input: [u8; 64]) -> [u8; 32] {
193    hash_2to1(input, extern_sha256_hash_2to1)
194}
195
196/// Computes the hash of a sequence of field elements using the Rescue Prime Optimized (RPO)
197/// hash function.
198///
199/// This maps to the `std::crypto::rpo::hash_memory` procedure in the Miden stdlib.
200///
201/// # Arguments
202/// * `elements` - A Vec of field elements to be hashed
203#[inline]
204pub fn hash_elements(elements: Vec<Felt>) -> Digest {
205    let rust_ptr = elements.as_ptr().addr() as u32;
206
207    unsafe {
208        let mut ret_area = core::mem::MaybeUninit::<Word>::uninit();
209        let result_ptr = ret_area.as_mut_ptr() as *mut Felt;
210        let miden_ptr = rust_ptr / 4;
211        // Since our BumpAlloc produces word-aligned allocations the pointer should be word-aligned
212        assert_eq(Felt::from_u32(miden_ptr % 4), felt!(0));
213
214        extern_hash_memory(miden_ptr, elements.len() as u32, result_ptr);
215
216        Digest::from_word(ret_area.assume_init().reverse())
217    }
218}
219
220/// Computes the hash of a sequence of words using the Rescue Prime Optimized (RPO)
221/// hash function.
222///
223/// This maps to the `std::crypto::rpo::hash_memory` procedure in the Miden stdlib treating the
224/// `words` as an array of fielt elements.
225///
226/// # Arguments
227/// * `words` - A slice of words to be hashed
228#[inline]
229pub fn hash_words(words: &[Word]) -> Digest {
230    let rust_ptr = words.as_ptr().addr() as u32;
231
232    unsafe {
233        let mut ret_area = core::mem::MaybeUninit::<Word>::uninit();
234        let result_ptr = ret_area.as_mut_ptr() as *mut Felt;
235        let miden_ptr = rust_ptr / 4;
236        // It's safe to assume the `words` ptr is word-aligned.
237        assert_eq(Felt::from_u32(miden_ptr % 4), felt!(0));
238
239        let num_elements = (words.len() * 4) as u32;
240        extern_hash_memory(miden_ptr, num_elements, result_ptr);
241
242        Digest::from_word(ret_area.assume_init().reverse())
243    }
244}