openvm_keccak256_guest/
lib.rs

1#![no_std]
2
3#[cfg(target_os = "zkvm")]
4use openvm_platform::alloc::AlignedBuf;
5
6pub const OPCODE: u8 = 0x0b;
7pub const KECCAKF_FUNCT3: u8 = 0b100;
8pub const KECCAKF_FUNCT7: u8 = 0;
9pub const XORIN_FUNCT3: u8 = 0b100;
10pub const XORIN_FUNCT7: u8 = 1;
11
12pub const KECCAK_WIDTH_BYTES: usize = 200;
13pub const KECCAK_RATE: usize = 136;
14pub const KECCAK_OUTPUT_SIZE: usize = 32;
15pub const MIN_ALIGN: usize = 4;
16
17/// XOR `len` bytes from `input` into `buffer` using the native XORIN instruction.
18///
19/// # Panics
20///
21/// Panics if `len > KECCAK_RATE` (136): the XORIN circuit absorbs at most `KECCAK_RATE` bytes
22/// per instruction, so a larger length would execute but fail to prove.
23///
24/// # Safety
25///
26/// - `buffer` must point to a buffer of at least `len` bytes.
27/// - `input` must point to a buffer of at least `len` bytes.
28#[cfg(target_os = "zkvm")]
29#[no_mangle]
30pub unsafe extern "C" fn native_xorin(buffer: *mut u8, input: *const u8, len: usize) {
31    assert!(
32        len <= KECCAK_RATE,
33        "native_xorin: len exceeds the XORIN circuit's maximum rate of {} bytes",
34        KECCAK_RATE
35    );
36    if len == 0 {
37        return;
38    }
39    unsafe {
40        let buffer_aligned = buffer as usize % MIN_ALIGN == 0;
41        let input_aligned = input as usize % MIN_ALIGN == 0;
42        let len_aligned = len % MIN_ALIGN == 0;
43        let all_aligned = buffer_aligned && input_aligned && len_aligned;
44
45        if all_aligned {
46            __native_xorin(buffer, input, len);
47        } else {
48            let adjusted_len = len.next_multiple_of(MIN_ALIGN);
49            let aligned_buffer;
50            let aligned_input;
51
52            let actual_buffer = if buffer_aligned && len_aligned {
53                buffer
54            } else {
55                aligned_buffer = AlignedBuf::uninit(adjusted_len, MIN_ALIGN);
56                core::ptr::copy_nonoverlapping(buffer, aligned_buffer.ptr, len);
57                aligned_buffer.ptr
58            };
59
60            let actual_input = if input_aligned && len_aligned {
61                input
62            } else {
63                aligned_input = AlignedBuf::uninit(adjusted_len, MIN_ALIGN);
64                core::ptr::copy_nonoverlapping(input, aligned_input.ptr, len);
65                aligned_input.ptr
66            };
67
68            __native_xorin(actual_buffer, actual_input, adjusted_len);
69
70            if !buffer_aligned || !len_aligned {
71                core::ptr::copy_nonoverlapping(actual_buffer as *const u8, buffer, len);
72            }
73        }
74    }
75}
76
77/// Apply the Keccak-f[1600] permutation to the 200-byte state buffer.
78///
79/// # Safety
80///
81/// - `buffer` must point to a buffer of at least `KECCAK_WIDTH_BYTES` (200) bytes.
82#[cfg(target_os = "zkvm")]
83#[no_mangle]
84pub unsafe extern "C" fn native_keccakf(buffer: *mut u8) {
85    unsafe {
86        if buffer as usize % MIN_ALIGN == 0 {
87            __native_keccakf(buffer);
88        } else {
89            let aligned_buffer = AlignedBuf::new(buffer, KECCAK_WIDTH_BYTES, MIN_ALIGN);
90            __native_keccakf(aligned_buffer.ptr);
91            core::ptr::copy_nonoverlapping(
92                aligned_buffer.ptr as *const u8,
93                buffer,
94                KECCAK_WIDTH_BYTES,
95            );
96        }
97    }
98}
99
100#[cfg(target_os = "zkvm")]
101#[inline(always)]
102fn __native_xorin(mut buffer: *mut u8, input: *const u8, len: usize) {
103    openvm_platform::custom_insn_r!(
104        opcode = OPCODE,
105        funct3 = XORIN_FUNCT3,
106        funct7 = XORIN_FUNCT7,
107        rd = InOut buffer,
108        rs1 = In input,
109        rs2 = In len
110    );
111}
112
113#[cfg(target_os = "zkvm")]
114#[inline(always)]
115fn __native_keccakf(mut buffer: *mut u8) {
116    openvm_platform::custom_insn_r!(
117        opcode = OPCODE,
118        funct3 = KECCAKF_FUNCT3,
119        funct7 = KECCAKF_FUNCT7,
120        rd = InOut buffer,
121        rs1 = Const "x0",
122        rs2 = Const "x0",
123    );
124}