Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
eccvm.fuzzer.cpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: not started, auditors: [], date: YYYY-MM-DD }
3// external_1: { status: not started, auditors: [], date: YYYY-MM-DD }
4// external_2: { status: not started, auditors: [], date: YYYY-MM-DD }
5// =====================
6
13#include <cassert>
14#include <cstdint>
15#include <iostream>
16#include <vector>
17
18using namespace bb;
19using G1 = bb::g1;
20using Fr = typename G1::Fr;
22
23// Security note: This fuzzer generates random ECC operations to test the ECCVM circuit builder
24// and trace checker. It focuses on the check_circuit mechanism without full proving to avoid
25// potential security issues with proving key generation or proof verification.
26
27// Operation types for the fuzzer
28enum class OpType : uint8_t { ADD = 0, MUL = 1, EQ_AND_RESET = 2, MERGE = 3, EMPTY_ROW = 4, MAX_OP = 5 };
30 std::array<uint8_t, 64> data;
31};
32
38
43
45 size_t op_index;
51
52 OperationDetail(size_t idx, OpType type, size_t gen_idx, const Fr& sc, bool infinity, bool negate = false)
53 : op_index(idx)
54 , op_type(type)
55 , generator_index(gen_idx)
56 , scalar(sc)
57 , is_infinity(infinity)
58 , should_negate(negate)
59 {}
60};
61static constexpr size_t NUM_GENERATORS = 4;
62// Helper function to print operation details
63void print_operation_details(size_t op_index,
64 OpType op_type,
65 size_t generator_index,
66 const Fr& scalar,
67 bool is_infinity,
68 bool should_negate = false)
69{
70 std::cout << "Operation " << op_index << ": ";
71 switch (op_type) {
72 case OpType::ADD:
73 std::cout << "ADD(generator=" << generator_index << (should_negate ? ", negated" : "")
74 << (is_infinity ? ", infinity" : "") << ")";
75 break;
76 case OpType::MUL:
77 std::cout << "MUL(generator=" << generator_index << ", scalar=" << scalar << (should_negate ? ", negated" : "")
78 << (is_infinity ? ", infinity" : "") << ")";
79 break;
81 std::cout << "EQ_AND_RESET";
82 break;
83 case OpType::MERGE:
84 std::cout << "MERGE";
85 break;
87 std::cout << "EMPTY_ROW";
88 break;
89 default:
90 std::cout << "UNKNOWN(" << static_cast<int>(op_type) << ")";
91 break;
92 }
94}
95
96extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size)
97{
98
99 if (Size < sizeof(FuzzerTuple)) {
100 return 0; // Invalid input size
101 }
102
103 const FuzzerTuple* input = reinterpret_cast<const FuzzerTuple*>(Data);
104
105 // Validate input parameters
106 size_t num_operations = (Size) / sizeof(FuzzerTuple);
107 if (num_operations == 0) {
108 return 0;
109 }
110
111 auto total_fieldvm_data_size = num_operations * sizeof(FieldVMDataChunk);
112 std::vector<uint8_t> all_fieldvm_data(total_fieldvm_data_size);
113 for (size_t i = 0; i < num_operations; ++i) {
115 all_fieldvm_data.data() + i * sizeof(FieldVMDataChunk), &input[i].fieldvm_data, sizeof(FieldVMDataChunk));
116 }
117
118 // Pre-compute scalars using FieldVM
119 std::vector<Fr> precomputed_scalars;
120 // Create FieldVM instance for scalar computation
121 FieldVM<Fr> field_vm(false, 65536); // Disable debug, max 65536 steps
122
123 // Disable heavy operations for better performance
124 field_vm.settings.enable_inv = false; // Disable inversion
125 field_vm.settings.enable_sqrt = false; // Disable square root
126 field_vm.settings.enable_batch_invert = false; // Disable batch inversion
127 field_vm.settings.enable_pow = false; // Disable power operation
128 field_vm.settings.enable_div = false; // Disable division
129 field_vm.settings.enable_div_assign = false; // Disable division assignment
130
131 // Run FieldVM with the controlled amount of data
132 field_vm.run(all_fieldvm_data.data(), total_fieldvm_data_size);
133
134 // Extract all field elements from FieldVM state as potential scalars
135 for (size_t i = 0; i < 32; ++i) { // Use all 32 internal state elements
136 Fr scalar = field_vm.field_internal_state[i];
137 precomputed_scalars.push_back(scalar);
138 }
139
140 // Create base generators (always create 4 base generators)
141 auto base_generators = G1::derive_generators("eccvm_fuzzer_generators", NUM_GENERATORS);
143
144 // Use the first 16 FieldVM elements to create 4 linear combinations of base generators
145 for (size_t i = 0; i < 4; ++i) {
146 // Create linear combination: sum of base_generators[j] * precomputed_scalars[i*4 + j]
147 typename G1::element combined_point = G1::point_at_infinity;
148 for (size_t j = 0; j < 4; ++j) {
149 Fr scalar = precomputed_scalars[i * 4 + j];
150 combined_point = combined_point + (base_generators[j] * scalar);
151 }
152 points.push_back(combined_point);
153 }
154
155 // Create op queue
157
158 // Store operation details for potential failure reporting
159 std::vector<OperationDetail> operation_details;
160
161 // Process operations
162 for (size_t i = 0; i < num_operations; ++i) {
163 const auto& op = input[i].operation;
164 OpType op_type = op.op_type;
165
166 switch (op_type) {
167 case OpType::ADD: {
168 // Use modulo to ensure valid generator index (lower 7 bits)
169 size_t generator_index = (op.generator_index & 0x7F) % points.size();
170 bool should_negate = (op.generator_index & 0x80) != 0; // Top bit controls negation
171
172 typename G1::element point_to_add = points[generator_index];
173 if (should_negate) {
174 point_to_add = -point_to_add; // Negate the point
175 }
176
177 bool is_infinity = point_to_add.is_point_at_infinity();
178 operation_details.emplace_back(i, op_type, generator_index, Fr(0), is_infinity, should_negate);
179 op_queue->add_accumulate(point_to_add);
180 break;
181 }
182 case OpType::MUL: {
183 // Use modulo to ensure valid generator index (lower 7 bits)
184 size_t generator_index = (op.generator_index & 0x7F) % points.size();
185 bool should_negate = (op.generator_index & 0x80) != 0; // Top bit controls negation
186
187 // Use pre-computed scalar selected by scalar_indices
188 Fr scalar = precomputed_scalars[op.scalar_index % precomputed_scalars.size()];
189 // TODO(@Rumata888): remove this if we fix the completeness issue
190 // Convert scalar to endomorphism scalars and check that none exceed 128 bits
191 auto converted = scalar.from_montgomery_form();
192 uint256_t converted_u256(scalar);
193 uint256_t k1_u256, k2_u256;
194 Fr z_1 = 0;
195 Fr z_2 = 0;
196
197 if (converted_u256.get_msb() <= 128) {
198 k1_u256 = converted_u256;
199 k2_u256 = 0;
200 } else {
201 bb::fr::split_into_endomorphism_scalars(converted, z_1, z_2);
202 k1_u256 = uint256_t(z_1.to_montgomery_form());
203 k2_u256 = uint256_t(z_2.to_montgomery_form());
204 }
205
206 if (k1_u256.get_msb() >= 128 || k2_u256.get_msb() >= 128) {
207 // Skip this operation if endomorphism scalars are too large
208 continue;
209 }
210
211 typename G1::element point_to_multiply = points[generator_index];
212 if (should_negate) {
213 point_to_multiply = -point_to_multiply; // Negate the point
214 }
215
216 bool is_infinity = point_to_multiply.is_point_at_infinity();
217 operation_details.emplace_back(i, op_type, generator_index, scalar, is_infinity, should_negate);
218 op_queue->mul_accumulate(point_to_multiply, scalar);
219 break;
220 }
222 operation_details.emplace_back(i, op_type, 0, Fr(0), false, false);
223 op_queue->eq_and_reset();
224 break;
225 }
226 case OpType::MERGE: {
227 operation_details.emplace_back(i, op_type, 0, Fr(0), false, false);
228
229 op_queue->eq_and_reset();
230 op_queue->merge();
231 break;
232 }
233 case OpType::EMPTY_ROW: {
234 operation_details.emplace_back(i, op_type, 0, Fr(0), false, false);
235 op_queue->empty_row_for_testing();
236 break;
237 }
238 default:
239 operation_details.emplace_back(i, op_type, 0, Fr(0), false, false);
240 break;
241 }
242 }
243
244 // Always merge at the end to finalize the circuit
245 operation_details.emplace_back(num_operations, OpType::EQ_AND_RESET, 0, Fr(0), false, false);
246 op_queue->eq_and_reset();
247
248 operation_details.emplace_back(num_operations + 1, OpType::MERGE, 0, Fr(0), false, false);
249 op_queue->merge();
250
251 // Create circuit builder
252 ECCVMCircuitBuilder circuit{ op_queue };
253
254 // Test the check_circuit mechanism
255 bool result = ECCVMTraceChecker::check(circuit, nullptr, /* disable_fixed_dyadic_trace_size= */ true);
256 // The circuit should always be valid if our operations are well-formed
257 // If check fails, it might indicate a bug in the circuit builder or trace checker
258 if (!result) {
259 std::cout << "ERROR: ECCVMTraceChecker::check returned false!" << std::endl;
260 std::cout << "Input parameters:" << std::endl;
261 std::cout << " num_operations: " << num_operations << std::endl;
262 std::cout << " operations: ";
263 for (size_t i = 0; i < num_operations; ++i) {
264 std::cout << static_cast<int>(input[i].operation.op_type) << " ";
265 }
267 std::cout << " generator_indices: ";
268 for (size_t i = 0; i < num_operations; ++i) {
269 std::cout << static_cast<int>(input[i].operation.generator_index) << " ";
270 }
272
273 // Print operation details that led to the failure
274 std::cout << "Operation sequence that caused failure:" << std::endl;
275 for (const auto& op : operation_details) {
277 op.op_index, op.op_type, op.generator_index, op.scalar, op.is_infinity, op.should_negate);
278 }
279 }
280
281 assert(result == true);
282
283 return 0;
284}
static bool check(ECCVMCircuitBuilder &, numeric::RNG *engine_ptr=nullptr)
typename Group::element Element
Definition bn254.hpp:21
static constexpr element point_at_infinity
Definition group.hpp:47
group_elements::element< Fq, Fr, Params > element
Definition group.hpp:41
Fr_ Fr
Definition group.hpp:40
static std::vector< affine_element > derive_generators(const std::vector< uint8_t > &domain_separator_bytes, const size_t num_generators, const size_t starting_index=0)
Derives generator points via hash-to-curve.
Definition group.hpp:87
constexpr uint64_t get_msb() const
Field arithmetic fuzzer for testing cryptographic field operations.
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)
void print_operation_details(size_t op_index, OpType op_type, size_t generator_index, const Fr &scalar, bool is_infinity, bool should_negate=false)
typename G1::Fr Fr
OpType
@ EQ_AND_RESET
Entry point for Barretenberg command-line interface.
group< fq, fr, Bn254G1Params > g1
Definition g1.hpp:33
@ MUL
Multiply two field elements.
@ ADD
Add two field elements.
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
Curve::Element Element
Curve::AffineElement G1
std::array< uint8_t, 64 > data
FieldVMDataChunk fieldvm_data
SingleOp operation
OperationDetail(size_t idx, OpType type, size_t gen_idx, const Fr &sc, bool infinity, bool negate=false)
OpType op_type
uint8_t scalar_index
uint8_t generator_index
Virtual machine for field arithmetic operations.
size_t run(const unsigned char *Data, size_t Size, bool reset_steps=true)
Run the VM on input data.
std::array< Field, INTERNAL_STATE_SIZE > field_internal_state
Internal state array of field elements.
VMSettings settings
VM settings controlling which operations are enabled.
bool enable_inv
Enable INV operations.
bool enable_batch_invert
Enable BATCH_INVERT operations.
bool enable_div
Enable DIV operations.
bool enable_div_assign
Enable DIV_ASSIGN operations.
bool enable_sqrt
Enable SQRT operations.
bool enable_pow
Enable POW operations.
BB_INLINE constexpr field to_montgomery_form() const noexcept
static void split_into_endomorphism_scalars(const field &k, field &k1, field &k2)
BB_INLINE constexpr field from_montgomery_form() const noexcept