Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
keccakf1600.cpp
Go to the documentation of this file.
2
3#include <array>
4#include <cassert>
5#include <cstddef>
6#include <cstdint>
7
12
13namespace bb::avm2::simulation {
14
15namespace {
16
17MemoryValue unconstrained_rotate_left(MemoryValue x, uint8_t len)
18{
19 // We avoid an undefined behavior in the shift below: "x_uint64_t >> (64 - len)"
20 // if it were evaluated with len = 0. (cpp standard on bitwise shifts requires rhs
21 // to be less than the number of bits in lhs).
22 if (len == 0) {
23 return x;
24 }
25
26 const auto x_uint64_t = x.as<uint64_t>();
27 assert(len < 64);
28 const auto out_uint64_t = (x_uint64_t << len) | x_uint64_t >> (64 - len);
29 return MemoryValue::from(out_uint64_t);
30}
31
32// A function which transforms any two-dimensional array of MemoryValue's into a two-dimensional array of uint64_t.
33template <size_t N, size_t M>
34std::array<std::array<uint64_t, M>, N> two_dim_array_to_uint64(const std::array<std::array<MemoryValue, M>, N>& input)
35{
37 for (size_t i = 0; i < N; i++) {
38 for (size_t j = 0; j < M; j++) {
39 output[i][j] = input[i][j].template as<uint64_t>();
40 }
41 }
42 return output;
43}
44
45// A function which transforms any array of MemoryValue's into an array of uint64_t.
46template <size_t N> std::array<uint64_t, N> array_to_uint64(const std::array<MemoryValue, N>& input)
47{
49 for (size_t i = 0; i < N; i++) {
50 output.at(i) = input.at(i).template as<uint64_t>();
51 }
52 return output;
53}
54
55} // namespace
56
57// TODO: For fast simulation, we might directly call ethash_keccakf1600 from barretenberg, instead of
58// the following with no event emission. In this case, we will probably need two KeccakF1600 classes.
68{
69 KeccakF1600Event keccakf1600_event;
71 keccakf1600_event.dst_addr = dst_addr;
72 keccakf1600_event.src_addr = src_addr;
73
74 try {
75 // We need to perform two range checks to determine whether dst_addr and src_addr correspond to
76 // a memory slice which is out-of-range. This is a clear circuit leakage into simulation.
77 constexpr MemoryAddress HIGHEST_SLICE_ADDRESS = AVM_HIGHEST_MEM_ADDRESS - AVM_KECCAKF1600_STATE_SIZE + 1;
78 bool src_out_of_range = src_addr > HIGHEST_SLICE_ADDRESS;
79 bool dst_out_of_range = dst_addr > HIGHEST_SLICE_ADDRESS;
80
81 MemoryAddress src_abs_diff =
82 src_out_of_range ? src_addr - HIGHEST_SLICE_ADDRESS - 1 : HIGHEST_SLICE_ADDRESS - src_addr;
83 MemoryAddress dst_abs_diff =
84 dst_out_of_range ? dst_addr - HIGHEST_SLICE_ADDRESS - 1 : HIGHEST_SLICE_ADDRESS - dst_addr;
85
86 keccakf1600_event.src_out_of_range = src_out_of_range;
87 keccakf1600_event.dst_out_of_range = dst_out_of_range;
88 keccakf1600_event.src_abs_diff = src_abs_diff;
89 keccakf1600_event.dst_abs_diff = dst_abs_diff;
90
91 keccakf1600_event.space_id = memory.get_space_id();
92
93 // We group both possible out-of-range errors in the same temporality group.
94 // Therefore, we perform both range checks no matter what.
95 range_check.assert_range(src_abs_diff, AVM_MEMORY_NUM_BITS);
96 range_check.assert_range(dst_abs_diff, AVM_MEMORY_NUM_BITS);
97
98 if (src_out_of_range) {
99 throw KeccakF1600Exception(format("Read slice out of range: ", src_addr));
100 }
101 if (dst_out_of_range) {
102 throw KeccakF1600Exception(format("Write slice out of range: ", dst_addr));
103 }
104
105 // We work with MemoryValue as this type is required for bitwise operations handled
106 // by the bitwise sub-trace simulator. We continue by operating over Memory values and convert
107 // them back only at the end (event emission).
108 KeccakF1600StateMemValues src_mem_values;
109 src_mem_values.fill(std::array<MemoryValue, 5>{ MemoryValue::from<uint64_t>(0) });
110
111 // Slice read and tag check
112 for (size_t i = 0; i < 5; i++) {
113 for (size_t j = 0; j < 5; j++) {
114 const auto addr = src_addr + static_cast<MemoryAddress>((i * 5) + j);
115 const MemoryValue& mem_val = memory.get(addr);
116 const MemoryTag tag = mem_val.get_tag();
117 src_mem_values[i][j] = mem_val;
118
119 if (tag != MemoryTag::U64) {
120 keccakf1600_event.tag_error = true;
121 keccakf1600_event.src_mem_values = src_mem_values;
122
124 format("Read slice tag invalid - addr: ", addr, " tag: ", static_cast<uint32_t>(tag)));
125 }
126 }
127 }
128
129 // Initialize state input values with values read from memory.
130 KeccakF1600StateMemValues state_input_values = src_mem_values;
131 keccakf1600_event.src_mem_values = src_mem_values;
132
134
135 for (uint8_t round_idx = 0; round_idx < AVM_KECCAKF1600_NUM_ROUNDS; round_idx++) {
136 std::array<std::array<MemoryValue, 4>, 5> theta_xor_values;
137
138 // Theta xor computations
139 for (size_t i = 0; i < 5; ++i) {
140 MemoryValue xor_accumulator = state_input_values[i][0];
141 for (size_t j = 0; j < 4; ++j) {
142 xor_accumulator = bitwise.xor_op(xor_accumulator, state_input_values[i][j + 1]);
143 theta_xor_values[i][j] = xor_accumulator;
144 }
145 }
146
147 // Theta xor values left rotated by 1
148 std::array<MemoryValue, 5> theta_xor_row_rotl1_values;
149 for (size_t i = 0; i < 5; ++i) {
150 theta_xor_row_rotl1_values.at(i) = unconstrained_rotate_left(theta_xor_values[i][3], 1);
151 }
152
153 // Theta combined xor computation
154 std::array<MemoryValue, 5> theta_combined_xor_values;
155 for (size_t i = 0; i < 5; ++i) {
156 theta_combined_xor_values.at(i) =
157 bitwise.xor_op(theta_xor_values[(i + 4) % 5][3], theta_xor_row_rotl1_values.at((i + 1) % 5));
158 }
159
160 // State theta values
161 std::array<std::array<MemoryValue, 5>, 5> state_theta_values;
162 for (size_t i = 0; i < 5; ++i) {
163 for (size_t j = 0; j < 5; ++j) {
164 state_theta_values[i][j] =
165 bitwise.xor_op(state_input_values[i][j], theta_combined_xor_values.at(i));
166 }
167 }
168
169 // State rho values
170 KeccakF1600StateMemValues state_rho_values;
171
172 // Handle range checks related to Rho round function.
173 // For i,j, such that 0 < rotation_len[i][j] <= 32, we range check
174 // the highest rotation_len[i][j] number of bits of state_theta_values[i][j].
175 // Otherwise, we range check the lowest 64 - rotation_len[i][j] bits.
176 for (size_t i = 0; i < 5; ++i) {
177 for (size_t j = 0; j < 5; ++j) {
178 const uint8_t& len = keccak_rotation_len[i][j];
179 // Compute state values after Rho function.
180 state_rho_values[i][j] = unconstrained_rotate_left(state_theta_values[i][j], len);
181 if (len > 0 && len <= 32) {
182 range_check.assert_range(state_theta_values[i][j].as<uint64_t>() >> (64 - len), len);
183 } else if (len > 32) {
184 range_check.assert_range(state_theta_values[i][j].as<uint64_t>() & ((1U << (64 - len)) - 1),
185 64 - len);
186 }
187 }
188 }
189
190 // state pi values
191 // state "not pi" values
192 KeccakF1600StateMemValues state_pi_values;
193 KeccakF1600StateMemValues state_pi_not_values;
194 for (size_t i = 0; i < 5; ++i) {
195 for (size_t j = 0; j < 5; ++j) {
196 state_pi_values[i][j] = state_rho_values[keccak_pi_rho_x_coords[i][j]][i];
197 state_pi_not_values[i][j] = ~state_pi_values[i][j];
198 }
199 }
200
201 // state "pi and" values
202 KeccakF1600StateMemValues state_pi_and_values;
203 // state chi values
204 KeccakF1600StateMemValues state_chi_values;
205 for (size_t i = 0; i < 5; ++i) {
206 for (size_t j = 0; j < 5; ++j) {
207 state_pi_and_values[i][j] =
208 bitwise.and_op(state_pi_not_values[(i + 1) % 5][j], state_pi_values[(i + 2) % 5][j]);
209 state_chi_values[i][j] = bitwise.xor_op(state_pi_values[i][j], state_pi_and_values[i][j]);
210 }
211 }
212
213 // state iota_00 value
214 // Recall that round starts with 1
215 MemoryValue iota_00_value =
216 bitwise.xor_op(state_chi_values[0][0], MemoryValue::from(keccak_round_constants.at(round_idx)));
217
218 rounds_data.at(round_idx) = {
219 .state = two_dim_array_to_uint64(state_input_values),
220 .theta_xor = two_dim_array_to_uint64(theta_xor_values),
221 .theta_xor_row_rotl1 = array_to_uint64(theta_xor_row_rotl1_values),
222 .theta_combined_xor = array_to_uint64(theta_combined_xor_values),
223 .state_theta = two_dim_array_to_uint64(state_theta_values),
224 .state_rho = two_dim_array_to_uint64(state_rho_values),
225 .state_pi_not = two_dim_array_to_uint64(state_pi_not_values),
226 .state_pi_and = two_dim_array_to_uint64(state_pi_and_values),
227 .state_chi = two_dim_array_to_uint64(state_chi_values),
228 .state_iota_00 = iota_00_value.as<uint64_t>(),
229 };
230
231 state_input_values = state_chi_values;
232 state_input_values[0][0] = iota_00_value;
233 }
234
235 // Slice write
236 for (size_t i = 0; i < 5; i++) {
237 for (size_t j = 0; j < 5; j++) {
238 memory.set(dst_addr + static_cast<MemoryAddress>((i * 5) + j), state_input_values[i][j]);
239 }
240 }
241
242 keccakf1600_event.rounds = rounds_data;
243 perm_events.emit(KeccakF1600Event(keccakf1600_event));
244 } catch (const KeccakF1600Exception& e) {
245 perm_events.emit(KeccakF1600Event(keccakf1600_event));
246 throw e;
247 }
248}
249
250} // namespace bb::avm2::simulation
#define AVM_KECCAKF1600_STATE_SIZE
#define AVM_HIGHEST_MEM_ADDRESS
#define AVM_KECCAKF1600_NUM_ROUNDS
#define AVM_MEMORY_NUM_BITS
static TaggedValue from(T value)
ValueTag get_tag() const
virtual uint32_t get_execution_id() const =0
EventEmitterInterface< KeccakF1600Event > & perm_events
void permutation(MemoryInterface &memory, MemoryAddress dst_addr, MemoryAddress src_addr) override
Permutation Keccak-f[1600] consisting in AVM_KECCAKF1600_NUM_ROUNDS (24) rounds and a state of 25 64-...
ExecutionIdManagerInterface & execution_id_manager
std::string format(Args... args)
Definition log.hpp:20
uint32_t dst_addr
constexpr std::array< std::array< uint8_t, 5 >, 5 > keccak_pi_rho_x_coords
constexpr std::array< uint64_t, 24 > keccak_round_constants
std::array< std::array< MemoryValue, 5 >, 5 > KeccakF1600StateMemValues
constexpr std::array< std::array< uint8_t, 5 >, 5 > keccak_rotation_len
TaggedValue MemoryValue
uint32_t MemoryAddress
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
uint8_t len
std::array< KeccakF1600RoundData, AVM_KECCAKF1600_NUM_ROUNDS > rounds