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    /// Computes the hash of a sequence of words using the Rescue Prime Optimized (RPO) hash
117    /// function.
118    ///
119    /// This maps to the `std::crypto::hashes::rpo::hash_memory_words` procedure in the Miden
120    /// stdlib.
121    ///
122    /// Input: The start and end addresses (in field elements) of the words to hash.
123    /// Output: One digest (4 field elements)
124    /// The output is passed back to the caller via a pointer.
125    #[link_name = "std::crypto::hashes::rpo::hash_memory_words"]
126    pub fn extern_hash_memory_words(start_addr: u32, end_addr: u32, result_ptr: *mut Felt);
127}
128
129/// Hashes a 32-byte input to a 32-byte output using the given hash function.
130#[inline(always)]
131fn hash_1to1(
132    input: [u8; 32],
133    extern_hash_1to1: unsafe extern "C" fn(u32, u32, u32, u32, u32, u32, u32, u32, *mut u8),
134) -> [u8; 32] {
135    use crate::intrinsics::WordAligned;
136    let input = unsafe { core::mem::transmute::<[u8; 32], [u32; 8]>(input) };
137    unsafe {
138        let mut ret_area = ::core::mem::MaybeUninit::<WordAligned<[u8; 32]>>::uninit();
139        let ptr = ret_area.as_mut_ptr() as *mut u8;
140        extern_hash_1to1(
141            input[0], input[1], input[2], input[3], input[4], input[5], input[6], input[7], ptr,
142        );
143        ret_area.assume_init().into_inner()
144    }
145}
146
147/// Hashes a 64-byte input to a 32-byte output using the given hash function.
148#[inline(always)]
149fn hash_2to1(
150    input: [u8; 64],
151    extern_hash_2to1: unsafe extern "C" fn(
152        u32,
153        u32,
154        u32,
155        u32,
156        u32,
157        u32,
158        u32,
159        u32,
160        u32,
161        u32,
162        u32,
163        u32,
164        u32,
165        u32,
166        u32,
167        u32,
168        *mut u8,
169    ),
170) -> [u8; 32] {
171    let input = unsafe { core::mem::transmute::<[u8; 64], [u32; 16]>(input) };
172    unsafe {
173        let mut ret_area = ::core::mem::MaybeUninit::<[u8; 32]>::uninit();
174        let ptr = ret_area.as_mut_ptr() as *mut u8;
175        extern_hash_2to1(
176            input[0], input[1], input[2], input[3], input[4], input[5], input[6], input[7],
177            input[8], input[9], input[10], input[11], input[12], input[13], input[14], input[15],
178            ptr,
179        );
180        ret_area.assume_init()
181    }
182}
183
184/// Hashes a 32-byte input to a 32-byte output using the BLAKE3 hash function.
185#[inline]
186pub fn blake3_hash_1to1(input: [u8; 32]) -> [u8; 32] {
187    hash_1to1(input, extern_blake3_hash_1to1)
188}
189
190/// Hashes a 64-byte input to a 32-byte output using the BLAKE3 hash function.
191#[inline]
192pub fn blake3_hash_2to1(input: [u8; 64]) -> [u8; 32] {
193    hash_2to1(input, extern_blake3_hash_2to1)
194}
195
196/// Hashes a 32-byte input to a 32-byte output using the SHA256 hash function.
197#[inline]
198pub fn sha256_hash_1to1(input: [u8; 32]) -> [u8; 32] {
199    hash_1to1(input, extern_sha256_hash_1to1)
200}
201
202/// Hashes a 64-byte input to a 32-byte output using the SHA256 hash function.
203#[inline]
204pub fn sha256_hash_2to1(input: [u8; 64]) -> [u8; 32] {
205    hash_2to1(input, extern_sha256_hash_2to1)
206}
207
208/// Computes the hash of a sequence of field elements using the Rescue Prime Optimized (RPO)
209/// hash function.
210///
211/// This maps to the `std::crypto::rpo::hash_memory` procedure in the Miden stdlib and to the
212/// `std::crypto::hashes::rpo::hash_memory_words` word-optimized variant when the input length is a
213/// multiple of 4.
214///
215/// # Arguments
216/// * `elements` - A Vec of field elements to be hashed
217#[inline]
218pub fn hash_elements(elements: Vec<Felt>) -> Digest {
219    let rust_ptr = elements.as_ptr().addr() as u32;
220    let element_count = elements.len();
221    let num_elements = element_count as u32;
222
223    unsafe {
224        let mut ret_area = core::mem::MaybeUninit::<Word>::uninit();
225        let result_ptr = ret_area.as_mut_ptr() as *mut Felt;
226        let miden_ptr = rust_ptr / 4;
227        // Since our BumpAlloc produces word-aligned allocations the pointer should be word-aligned
228        assert_eq(Felt::from_u32(miden_ptr % 4), felt!(0));
229
230        if element_count.is_multiple_of(4) {
231            let start_addr = miden_ptr;
232            let end_addr = start_addr + num_elements;
233            extern_hash_memory_words(start_addr, end_addr, result_ptr);
234        } else {
235            extern_hash_memory(miden_ptr, num_elements, result_ptr);
236        }
237
238        Digest::from_word(ret_area.assume_init().reverse())
239    }
240}
241
242/// Computes the hash of a sequence of words using the Rescue Prime Optimized (RPO)
243/// hash function.
244///
245/// This maps to the `std::crypto::hashes::rpo::hash_memory_words` procedure in the Miden stdlib.
246///
247/// # Arguments
248/// * `words` - A slice of words to be hashed
249#[inline]
250pub fn hash_words(words: &[Word]) -> Digest {
251    let rust_ptr = words.as_ptr().addr() as u32;
252
253    unsafe {
254        let mut ret_area = core::mem::MaybeUninit::<Word>::uninit();
255        let result_ptr = ret_area.as_mut_ptr() as *mut Felt;
256        let miden_ptr = rust_ptr / 4;
257        // It's safe to assume the `words` ptr is word-aligned.
258        assert_eq(Felt::from_u32(miden_ptr % 4), felt!(0));
259
260        let start_addr = miden_ptr;
261        let end_addr = start_addr + (words.len() as u32 * 4);
262        extern_hash_memory_words(start_addr, end_addr, result_ptr);
263
264        Digest::from_word(ret_area.assume_init().reverse())
265    }
266}