-
Notifications
You must be signed in to change notification settings - Fork 103
/
Copy pathhash.rs
140 lines (122 loc) · 4.03 KB
/
hash.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
use core::arch::asm;
use core::convert::TryInto;
use core::mem::MaybeUninit;
use crate::goldilocks::{Goldilocks, OpaqueGoldilocks};
use powdr_riscv_syscalls::Syscall;
pub fn native_hash(data: &mut [u64; 12]) -> &[u64; 4] {
unsafe {
ecall!(Syscall::NativeHash, in("a0") data);
}
data[..4].try_into().unwrap()
}
/// Calls the low level Poseidon PIL machine, where the last 4 elements are the
/// "cap", the return value is placed in data[..4] and the reference to this
/// sub-array is returned.
///
/// TODO: remove poseidon_gl and use poseidon2_gl for everything, instead.
pub fn poseidon_gl(data: &mut [Goldilocks; 12]) -> &[Goldilocks; 4] {
unsafe {
ecall!(Syscall::PoseidonGL, in("a0") data);
}
data[..4].try_into().unwrap()
}
/// Perform one Poseidon2 permutation with 8 Goldilocks field elements in-place.
pub fn poseidon2_gl_inplace(data: &mut [OpaqueGoldilocks; 8]) {
unsafe {
ecall!(Syscall::Poseidon2GL, in("a0") data, in("a1") data, in("a2") 3);
}
}
#[repr(u32)]
pub enum Poseidon2OutputHalf {
//None = 0,
FirstHalf = 1,
SecondHalf = 2,
//Full = 3,
}
/// Perform one Poseidon2 compression with 8 Goldilocks field elements.
pub fn poseidon2_gl_compression(
data: &[OpaqueGoldilocks; 8],
output_half: Poseidon2OutputHalf,
) -> [OpaqueGoldilocks; 4] {
unsafe {
let mut output: MaybeUninit<[OpaqueGoldilocks; 4]> = MaybeUninit::uninit();
ecall!(Syscall::Poseidon2GL, in("a0") data, in("a1") output.as_mut_ptr(), in("a2") output_half as u32);
output.assume_init()
}
}
/// Calls the keccakf machine.
/// Return value is placed in the output array.
pub fn keccakf(input: &[u64; 25], output: &mut [u64; 25]) {
unsafe {
// Syscall inputs: memory pointer to input array and memory pointer to output array.
ecall!(Syscall::KeccakF, in("a0") input, in("a1") output);
}
}
pub struct Keccak {
state: [u64; 25],
next_word: usize,
input_buffer: u64,
next_byte: usize,
}
impl Keccak {
const RATE: usize = 17; // Rate in u64 words
pub fn v256() -> Self {
Self {
state: [0u64; 25],
next_word: 0,
input_buffer: 0,
next_byte: 0,
}
}
fn xor_word_to_state(&mut self, word: u64) {
self.state[self.next_word] ^= word;
self.next_word += 1;
if self.next_word == Self::RATE {
let mut state_out = [0u64; 25];
keccakf(&self.state, &mut state_out);
self.state = state_out;
self.next_word = 0;
}
}
pub fn update(&mut self, data: &[u8]) {
unsafe {
let (prefix, words, suffix) = data.align_to::<u64>();
self.update_unaligned(prefix);
for &word in words {
if self.next_byte == 0 {
self.xor_word_to_state(word);
} else {
self.xor_word_to_state(self.input_buffer | (word << (8 * self.next_byte)));
self.input_buffer = word >> (64 - 8 * self.next_byte);
}
}
self.update_unaligned(suffix);
}
}
fn update_unaligned(&mut self, bytes: &[u8]) {
for &byte in bytes {
self.input_buffer |= (byte as u64) << (8 * self.next_byte);
self.next_byte += 1;
if self.next_byte == 8 {
self.xor_word_to_state(self.input_buffer);
self.input_buffer = 0;
self.next_byte = 0;
}
}
}
pub fn finalize(&mut self, output: &mut [u8]) {
if self.next_byte > 0 {
self.input_buffer |= 0x01 << (8 * self.next_byte);
self.xor_word_to_state(self.input_buffer);
} else {
self.xor_word_to_state(0x01);
}
while self.next_word < Self::RATE - 1 {
self.xor_word_to_state(0);
}
self.xor_word_to_state(0x8000000000000000);
for i in 0..4 {
output[i * 8..(i + 1) * 8].copy_from_slice(&self.state[i].to_ne_bytes());
}
}
}