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 <cstddef>
4
8
9namespace bb::avm2::simulation {
10
11namespace {
12
19MemoryValue unconstrained_rotate_left(MemoryValue x, uint8_t len)
20{
21 // We avoid an undefined behavior in the shift below: "x_uint64_t >> (64 - len)"
22 // if it were evaluated with len = 0. (cpp standard on bitwise shifts requires rhs
23 // to be less than the number of bits in lhs).
24 if (len == 0) {
25 return x;
26 }
27
28 const auto x_uint64_t = x.as<uint64_t>();
29 BB_ASSERT_LT(len, 64, "Length out of bounds");
30 const auto out_uint64_t = (x_uint64_t << len) | x_uint64_t >> (64 - len);
31 return MemoryValue::from(out_uint64_t);
32}
33
37template <size_t N, size_t M>
38std::array<std::array<uint64_t, M>, N> two_dim_array_to_uint64(const std::array<std::array<MemoryValue, M>, N>& input)
39{
41 for (size_t i = 0; i < N; i++) {
42 for (size_t j = 0; j < M; j++) {
43 output[i][j] = input[i][j].template as<uint64_t>();
44 }
45 }
46 return output;
47}
48
52template <size_t N> std::array<uint64_t, N> array_to_uint64(const std::array<MemoryValue, N>& input)
53{
55 for (size_t i = 0; i < N; i++) {
56 output.at(i) = input.at(i).template as<uint64_t>();
57 }
58 return output;
59}
60
61} // namespace
62
77{
78 KeccakF1600Event keccakf1600_event;
80 keccakf1600_event.dst_addr = dst_addr;
81 keccakf1600_event.src_addr = src_addr;
82
83 try {
84 // We need to perform two bound checks to determine whether dst_addr and src_addr correspond to
85 // a memory slice which is out-of-range.
86 constexpr MemoryAddress HIGHEST_SLICE_ADDRESS = AVM_HIGHEST_MEM_ADDRESS - AVM_KECCAKF1600_STATE_SIZE + 1;
87
88 // We group both possible out-of-range errors in the same temporality group.
89 // Therefore, we perform both bound checks no matter what.
90 bool src_out_of_range = gt.gt(static_cast<uint128_t>(src_addr), static_cast<uint128_t>(HIGHEST_SLICE_ADDRESS));
91 bool dst_out_of_range = gt.gt(static_cast<uint128_t>(dst_addr), static_cast<uint128_t>(HIGHEST_SLICE_ADDRESS));
92
93 keccakf1600_event.src_out_of_range = src_out_of_range;
94 keccakf1600_event.dst_out_of_range = dst_out_of_range;
95 keccakf1600_event.space_id = memory.get_space_id();
96
97 if (src_out_of_range) {
98 throw KeccakF1600Exception(format("Read slice out of range: ", src_addr));
99 }
100 if (dst_out_of_range) {
101 throw KeccakF1600Exception(format("Write slice out of range: ", dst_addr));
102 }
103
104 // We work with MemoryValue as this type is required for bitwise operations handled
105 // by the bitwise sub-trace simulator. We continue by operating over Memory values and convert
106 // them back only at the end (event emission).
107 std::array<MemoryValue, AVM_KECCAKF1600_STATE_SIZE> src_mem_values{ MemoryValue::from<uint64_t>(0) };
108
109 // Slice read and tag check
110 for (size_t k = 0; k < AVM_KECCAKF1600_STATE_SIZE; k++) {
111 const auto addr = src_addr + static_cast<MemoryAddress>(k);
112 const MemoryValue& mem_val = memory.get(addr);
113 const MemoryTag tag = mem_val.get_tag();
114 src_mem_values[k] = mem_val;
115
116 if (tag != MemoryTag::U64) {
117 keccakf1600_event.tag_error = true;
118 keccakf1600_event.src_mem_values = src_mem_values;
119
121 format("Read slice tag invalid - addr: ", addr, " tag: ", static_cast<uint32_t>(tag)));
122 }
123 }
124
125 keccakf1600_event.src_mem_values = src_mem_values;
126
127 // Initialize state input values with values read from memory.
128 // Standard Keccak layout: memory[(y * 5) + x] = A[x][y], so linear index k maps to (x=k%5, y=k/5)
129 KeccakF1600StateMemValues state_input_values;
130 for (size_t k = 0; k < AVM_KECCAKF1600_STATE_SIZE; k++) {
131 state_input_values[k % 5][k / 5] = src_mem_values[k];
132 }
133
135
136 for (uint8_t round_idx = 0; round_idx < AVM_KECCAKF1600_NUM_ROUNDS; round_idx++) {
137 std::array<std::array<MemoryValue, 4>, 5> theta_xor_values;
138
139 // Theta xor computations
140 for (size_t i = 0; i < 5; ++i) {
141 MemoryValue xor_accumulator = state_input_values[i][0];
142 for (size_t j = 0; j < 4; ++j) {
143 xor_accumulator = bitwise.xor_op(xor_accumulator, state_input_values[i][j + 1]);
144 theta_xor_values[i][j] = xor_accumulator;
145 }
146 }
147
148 // Theta xor values left rotated by 1
149 std::array<MemoryValue, 5> theta_xor_row_rotl1_values;
150 for (size_t i = 0; i < 5; ++i) {
151 theta_xor_row_rotl1_values.at(i) = unconstrained_rotate_left(theta_xor_values[i][3], 1);
152 }
153
154 // Theta combined xor computation
155 std::array<MemoryValue, 5> theta_combined_xor_values;
156 for (size_t i = 0; i < 5; ++i) {
157 theta_combined_xor_values.at(i) =
158 bitwise.xor_op(theta_xor_values[(i + 4) % 5][3], theta_xor_row_rotl1_values.at((i + 1) % 5));
159 }
160
161 // State theta values
162 std::array<std::array<MemoryValue, 5>, 5> state_theta_values;
163 for (size_t i = 0; i < 5; ++i) {
164 for (size_t j = 0; j < 5; ++j) {
165 state_theta_values[i][j] =
166 bitwise.xor_op(state_input_values[i][j], theta_combined_xor_values.at(i));
167 }
168 }
169
170 // State rho values
171 KeccakF1600StateMemValues state_rho_values;
172
173 // Handle range checks related to Rho round function.
174 // For i,j, such that 0 < rotation_len[i][j] <= 32, we range check
175 // the highest rotation_len[i][j] number of bits of state_theta_values[i][j].
176 // Otherwise, we range check the lowest 64 - rotation_len[i][j] bits.
177 for (size_t i = 0; i < 5; ++i) {
178 for (size_t j = 0; j < 5; ++j) {
179 const uint8_t& len = keccak_rotation_len[i][j];
180 // Compute state values after Rho function.
181 state_rho_values[i][j] = unconstrained_rotate_left(state_theta_values[i][j], len);
182 if (len > 0 && len <= 32) {
183 range_check.assert_range(state_theta_values[i][j].as<uint64_t>() >> (64 - len), len);
184 } else if (len > 32) {
185 range_check.assert_range(state_theta_values[i][j].as<uint64_t>() & ((1ULL << (64 - len)) - 1),
186 64 - len);
187 }
188 }
189 }
190
191 // state pi values
192 // state "not pi" values
193 KeccakF1600StateMemValues state_pi_values;
194 KeccakF1600StateMemValues state_pi_not_values;
195 for (size_t i = 0; i < 5; ++i) {
196 for (size_t j = 0; j < 5; ++j) {
197 state_pi_values[i][j] = state_rho_values[keccak_pi_rho_x_coords[i][j]][i];
198 state_pi_not_values[i][j] = ~state_pi_values[i][j];
199 }
200 }
201
202 // state "pi and" values
203 KeccakF1600StateMemValues state_pi_and_values;
204 // state chi values
205 KeccakF1600StateMemValues state_chi_values;
206 for (size_t i = 0; i < 5; ++i) {
207 for (size_t j = 0; j < 5; ++j) {
208 state_pi_and_values[i][j] =
209 bitwise.and_op(state_pi_not_values[(i + 1) % 5][j], state_pi_values[(i + 2) % 5][j]);
210 state_chi_values[i][j] = bitwise.xor_op(state_pi_values[i][j], state_pi_and_values[i][j]);
211 }
212 }
213
214 // state iota_00 value
215 // Recall that round starts with 1
216 MemoryValue iota_00_value =
217 bitwise.xor_op(state_chi_values[0][0], MemoryValue::from(keccak_round_constants.at(round_idx)));
218
219 rounds_data.at(round_idx) = {
220 .state = two_dim_array_to_uint64(state_input_values),
221 .theta_xor = two_dim_array_to_uint64(theta_xor_values),
222 .theta_xor_row_rotl1 = array_to_uint64(theta_xor_row_rotl1_values),
223 .theta_combined_xor = array_to_uint64(theta_combined_xor_values),
224 .state_theta = two_dim_array_to_uint64(state_theta_values),
225 .state_rho = two_dim_array_to_uint64(state_rho_values),
226 .state_pi_not = two_dim_array_to_uint64(state_pi_not_values),
227 .state_pi_and = two_dim_array_to_uint64(state_pi_and_values),
228 .state_chi = two_dim_array_to_uint64(state_chi_values),
229 .state_iota_00 = iota_00_value.as<uint64_t>(),
230 };
231
232 state_input_values = state_chi_values;
233 state_input_values[0][0] = iota_00_value;
234 }
235
236 // Slice write
237 for (size_t i = 0; i < 5; i++) {
238 for (size_t j = 0; j < 5; j++) {
239 memory.set(dst_addr + static_cast<MemoryAddress>((j * 5) + i), state_input_values[i][j]);
240 }
241 }
242
243 keccakf1600_event.rounds = rounds_data;
244 perm_events.emit(KeccakF1600Event(keccakf1600_event));
245 } catch (const KeccakF1600Exception& e) {
246 perm_events.emit(KeccakF1600Event(keccakf1600_event));
247 throw;
248 }
249}
250
251} // namespace bb::avm2::simulation
#define BB_ASSERT_LT(left, right,...)
Definition assert.hpp:143
#define AVM_KECCAKF1600_STATE_SIZE
#define AVM_HIGHEST_MEM_ADDRESS
#define AVM_KECCAKF1600_NUM_ROUNDS
static TaggedValue from(T value)
virtual uint32_t get_execution_id() const =0
EventEmitterInterface< KeccakF1600Event > & perm_events
void permutation(MemoryInterface &memory, MemoryAddress dst_addr, MemoryAddress src_addr) override
Perform the Keccak-f[1600] permutation (24 rounds) over a 25-word (5x5) 64-bit state.
ExecutionIdManagerInterface & execution_id_manager
std::string format(Args... args)
Definition log.hpp:23
uint32_t src_addr
uint32_t dst_addr
AVM range check gadget for witness generation.
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
5x5 matrix of MemoryValue representing the Keccak state (used during simulation).
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
unsigned __int128 uint128_t
Definition serialize.hpp:45
Event emitted by the Keccak-f[1600] simulation for trace generation.
std::array< MemoryValue, AVM_KECCAKF1600_STATE_SIZE > src_mem_values
std::array< KeccakF1600RoundData, AVM_KECCAKF1600_NUM_ROUNDS > rounds
Per-round intermediate data.
Exception thrown on errors during the Keccak-f[1600] permutation.