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