-
Notifications
You must be signed in to change notification settings - Fork 262
/
keccak.cairo
114 lines (100 loc) · 4.28 KB
/
keccak.cairo
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
from starkware.cairo.common.alloc import alloc
from starkware.cairo.common.math import split_felt, unsigned_div_rem
from starkware.cairo.common.uint256 import Uint256
// Computes the keccak hash.
// This function is unsafe (not sound): there is no validity enforcement that the result is indeed
// keccak, but an honest prover will compute the keccak.
// Args:
// data - an array of words representing the input data. Each word in the array is 16 bytes of the
// input data, except the last word, which may be less.
// length - the number of bytes in the input.
func unsafe_keccak(data: felt*, length: felt) -> (low: felt, high: felt) {
alloc_locals;
local low;
local high;
%{
from eth_hash.auto import keccak
data, length = ids.data, ids.length
if '__keccak_max_size' in globals():
assert length <= __keccak_max_size, \
f'unsafe_keccak() can only be used with length<={__keccak_max_size}. ' \
f'Got: length={length}.'
keccak_input = bytearray()
for word_i, byte_i in enumerate(range(0, length, 16)):
word = memory[data + word_i]
n_bytes = min(16, length - byte_i)
assert 0 <= word < 2 ** (8 * n_bytes)
keccak_input += word.to_bytes(n_bytes, 'big')
hashed = keccak(keccak_input)
ids.high = int.from_bytes(hashed[:16], 'big')
ids.low = int.from_bytes(hashed[16:32], 'big')
%}
return (low=low, high=high);
}
struct KeccakState {
start_ptr: felt*,
end_ptr: felt*,
}
func unsafe_keccak_init() -> (res: KeccakState) {
let (ptr) = alloc();
return (res=KeccakState(ptr, ptr));
}
func unsafe_keccak_add_felt{keccak_state: KeccakState, range_check_ptr}(num: felt) -> () {
let (high, low) = split_felt(num);
keccak_state.end_ptr[0] = high;
keccak_state.end_ptr[1] = low;
let keccak_state = KeccakState(keccak_state.start_ptr, keccak_state.end_ptr + 2);
return ();
}
func unsafe_keccak_add_uint256{keccak_state: KeccakState, range_check_ptr}(num: Uint256) -> () {
keccak_state.end_ptr[0] = num.high;
keccak_state.end_ptr[1] = num.low;
let keccak_state = KeccakState(keccak_state.start_ptr, keccak_state.end_ptr + 2);
return ();
}
func unsafe_keccak_add_felts{keccak_state: KeccakState, range_check_ptr}(
n_elements: felt, elements: felt*
) -> () {
if (n_elements == 0) {
return ();
}
unsafe_keccak_add_felt([elements]);
return unsafe_keccak_add_felts(n_elements=n_elements - 1, elements=elements + 1);
}
// This function is unsafe (not sound): there is no validity enforcement that the result is indeed
// keccak, but an honest prover will compute the keccak.
func unsafe_keccak_finalize(keccak_state: KeccakState) -> (res: Uint256) {
alloc_locals;
local low;
local high;
%{
from eth_hash.auto import keccak
keccak_input = bytearray()
n_elms = ids.keccak_state.end_ptr - ids.keccak_state.start_ptr
for word in memory.get_range(ids.keccak_state.start_ptr, n_elms):
keccak_input += word.to_bytes(16, 'big')
hashed = keccak(keccak_input)
ids.high = int.from_bytes(hashed[:16], 'big')
ids.low = int.from_bytes(hashed[16:32], 'big')
%}
return (res=Uint256(low=low, high=high));
}
func keccak_felts{range_check_ptr}(n_elements: felt, elements: felt*) -> (res: Uint256) {
let (keccak_state) = unsafe_keccak_init();
unsafe_keccak_add_felts{keccak_state=keccak_state}(n_elements=n_elements, elements=elements);
let (res) = unsafe_keccak_finalize(keccak_state=keccak_state);
return (res=res);
}
// A 160 msb truncated version of keccak, where each value is assumed to also be a truncated
// hash:
// keccak(x << 96, y << 96) >> 96.
func truncated_keccak2{range_check_ptr}(x: felt, y: felt) -> (res: felt) {
let (state) = unsafe_keccak_init();
let (xhigh, xlow) = unsigned_div_rem(x, 2 ** 32);
let (yhigh, ylow) = unsigned_div_rem(y, 2 ** 32);
unsafe_keccak_add_uint256{keccak_state=state}(num=Uint256(low=xlow * 2 ** 96, high=xhigh));
unsafe_keccak_add_uint256{keccak_state=state}(num=Uint256(low=ylow * 2 ** 96, high=yhigh));
let (hash) = unsafe_keccak_finalize(keccak_state=state);
let (low_h, low_l) = unsigned_div_rem(hash.low, 2 ** 96);
return (res=hash.high * 2 ** 32 + low_h);
}