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}