openvm_sha2_guest/lib.rs
1#![no_std]
2
3#[cfg(target_os = "zkvm")]
4use openvm_platform::alloc::AlignedBuf;
5
6/// This is custom-0 defined in RISC-V spec document
7pub const OPCODE: u8 = 0x0b;
8pub const SHA2_FUNCT3: u8 = 0b100;
9
10// There is no Sha384 enum variant because the SHA-384 compression function is
11// the same as the SHA-512 compression function.
12#[derive(Debug, Copy, Clone, PartialEq, Eq)]
13#[repr(u8)]
14pub enum Sha2BaseFunct7 {
15 Sha256 = 0x2,
16 Sha512 = 0x3,
17}
18
19/// zkvm native implementation of sha256 compression function
20/// # Safety
21///
22/// The VM accepts the previous hash state and the next block of input, and writes the
23/// new hash state.
24/// - `state` must point to a buffer of at least 32 bytes, storing the previous hash state as 8
25/// 32-bit words in little-endian order
26/// - `input` must point to a buffer of at least 64 bytes
27/// - `output` must point to a buffer of at least 32 bytes. It will be filled with the new hash
28/// state as 8 32-bit words in little-endian order
29///
30/// [`sha2-256`]: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf
31#[cfg(target_os = "zkvm")]
32#[inline(always)]
33#[no_mangle]
34pub unsafe extern "C" fn zkvm_sha256_impl(state: *const u8, input: *const u8, output: *mut u8) {
35 // SAFETY: we handle all cases where `prev_state`, `input`, or `output` are not aligned to 4
36 // bytes.
37
38 // The minimum alignment required for the buffers
39 const MIN_ALIGN: usize = 4;
40 unsafe {
41 let state_is_aligned = state as usize % MIN_ALIGN == 0;
42 let input_is_aligned = input as usize % MIN_ALIGN == 0;
43 let output_is_aligned = output as usize % MIN_ALIGN == 0;
44
45 // Keep temporary aligned buffers alive until after the intrinsic returns.
46 let aligned_state;
47 let state_ptr = if state_is_aligned {
48 state
49 } else {
50 aligned_state = AlignedBuf::new(state, 32, MIN_ALIGN);
51 aligned_state.ptr as *const u8
52 };
53
54 let aligned_input;
55 let input_ptr = if input_is_aligned {
56 input
57 } else {
58 aligned_input = AlignedBuf::new(input, 64, MIN_ALIGN);
59 aligned_input.ptr as *const u8
60 };
61
62 let aligned_output;
63 let output_ptr = if output_is_aligned {
64 output
65 } else {
66 aligned_output = AlignedBuf::uninit(32, MIN_ALIGN);
67 aligned_output.ptr
68 };
69
70 __native_sha256_compress(state_ptr, input_ptr, output_ptr);
71
72 if !output_is_aligned {
73 core::ptr::copy_nonoverlapping(output_ptr, output, 32);
74 }
75 }
76}
77
78/// zkvm native implementation of sha512 compression function
79/// # Safety
80///
81/// The VM accepts the previous hash state and the next block of input, and writes the
82/// new hash state.
83/// - `state` must point to a buffer of at least 64 bytes, storing the previous hash state as 8
84/// 64-bit words in little-endian order
85/// - `input` must point to a buffer of at least 128 bytes
86/// - `output` must point to a buffer of at least 64 bytes. It will be filled with the new hash
87/// state as 8 64-bit words in little-endian order
88///
89/// [`sha2-512`]: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf
90#[cfg(target_os = "zkvm")]
91#[inline(always)]
92#[no_mangle]
93pub unsafe extern "C" fn zkvm_sha512_impl(state: *const u8, input: *const u8, output: *mut u8) {
94 // SAFETY: we handle all cases where `prev_state`, `input`, or `output` are not aligned to 4
95 // bytes.
96
97 // The minimum alignment required for the buffers
98 const MIN_ALIGN: usize = 4;
99 unsafe {
100 let state_is_aligned = state as usize % MIN_ALIGN == 0;
101 let input_is_aligned = input as usize % MIN_ALIGN == 0;
102 let output_is_aligned = output as usize % MIN_ALIGN == 0;
103
104 // Keep temporary aligned buffers alive until after the intrinsic returns.
105 let aligned_state;
106 let state_ptr = if state_is_aligned {
107 state
108 } else {
109 aligned_state = AlignedBuf::new(state, 64, MIN_ALIGN);
110 aligned_state.ptr as *const u8
111 };
112
113 let aligned_input;
114 let input_ptr = if input_is_aligned {
115 input
116 } else {
117 aligned_input = AlignedBuf::new(input, 128, MIN_ALIGN);
118 aligned_input.ptr as *const u8
119 };
120
121 let aligned_output;
122 let output_ptr = if output_is_aligned {
123 output
124 } else {
125 aligned_output = AlignedBuf::uninit(64, MIN_ALIGN);
126 aligned_output.ptr
127 };
128
129 __native_sha512_compress(state_ptr, input_ptr, output_ptr);
130
131 if !output_is_aligned {
132 core::ptr::copy_nonoverlapping(output_ptr, output, 64);
133 }
134 }
135}
136
137/// sha256 compression function intrinsic binding
138///
139/// # Safety
140///
141/// The VM accepts the previous hash state and the next block of input, and writes the
142/// 32-byte hash.
143/// - `prev_state` must point to a buffer of at least 32 bytes, storing the previous hash state as 8
144/// 32-bit words in little-endian order
145/// - `input` must point to a buffer of at least 64 bytes
146/// - `output` must point to a buffer of at least 32 bytes. It will be filled with the new hash
147/// state as 8 32-bit words in little-endian order
148#[cfg(target_os = "zkvm")]
149#[inline(always)]
150fn __native_sha256_compress(prev_state: *const u8, input: *const u8, output: *mut u8) {
151 openvm_platform::custom_insn_r!(opcode = OPCODE, funct3 = SHA2_FUNCT3, funct7 = Sha2BaseFunct7::Sha256 as u8, rd = In output, rs1 = In prev_state, rs2 = In input);
152}
153
154/// sha512 intrinsic binding
155///
156/// # Safety
157///
158/// The VM accepts the previous hash state and the next block of input, and writes the
159/// 64-byte hash.
160/// - `prev_state` must point to a buffer of at least 32 bytes, storing the previous hash state as 8
161/// 64-bit words in little-endian order
162/// - `input` must point to a buffer of at least 128 bytes
163/// - `output` must point to a buffer of at least 64 bytes. It will be filled with the new hash
164/// state as 8 64-bit words in little-endian order
165#[cfg(target_os = "zkvm")]
166#[inline(always)]
167fn __native_sha512_compress(prev_state: *const u8, input: *const u8, output: *mut u8) {
168 openvm_platform::custom_insn_r!(opcode = OPCODE, funct3 = SHA2_FUNCT3, funct7 = Sha2BaseFunct7::Sha512 as u8, rd = In output, rs1 = In prev_state, rs2 = In input);
169}