Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
ecdsa_constraints.cpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: completed, auditors: [Federico], date: 2025-10-24 }
3// external_1: { status: not started, auditors: [], date: YYYY-MM-DD }
4// external_2: { status: not started, auditors: [], date: YYYY-MM-DD }
5// =====================
6
12
13namespace acir_format {
14
15using namespace bb;
16
40template <typename Curve>
41void create_ecdsa_verify_constraints(typename Curve::Builder& builder, const EcdsaConstraint& input)
42{
43 using Builder = Curve::Builder;
44
45 using Fq = Curve::fq_ct;
46 using Fr = Curve::bigfr_ct;
47 using G1 = Curve::g1_bigfr_ct;
48
50 using bool_ct = bb::stdlib::bool_t<Builder>;
52
53 // Define builder variables based on the witness indices
54 std::vector<field_ct> hashed_message_fields = fields_from_witnesses(builder, input.hashed_message);
55 std::vector<field_ct> r_fields = fields_from_witnesses(builder, std::span(input.signature.begin(), 32));
56 std::vector<field_ct> s_fields = fields_from_witnesses(builder, std::span(input.signature.begin() + 32, 32));
57 std::vector<field_ct> pub_x_fields = fields_from_witnesses(builder, input.pub_x_indices);
58 std::vector<field_ct> pub_y_fields = fields_from_witnesses(builder, input.pub_y_indices);
60 bool_ct predicate(to_field_ct(input.predicate, builder)); // Constructor enforces predicate = 0 or 1
61
62 if (builder.is_write_vk_mode()) {
63 // Fill builder variables in case of empty witness assignment
64 create_dummy_ecdsa_constraint<Curve>(
65 builder, hashed_message_fields, r_fields, s_fields, pub_x_fields, pub_y_fields, result_field);
66 }
67
68 // Step 1: Conditionally assign field values when predicate is false
69 if (!predicate.is_constant()) {
70 // Set r = s = H(m) = 1 when the predicate is false
71 for (size_t idx = 0; idx < 32; idx++) {
72 r_fields[idx] = field_ct::conditional_assign(predicate, r_fields[idx], field_ct(idx == 0 ? 1 : 0));
73 s_fields[idx] = field_ct::conditional_assign(predicate, s_fields[idx], field_ct(idx == 0 ? 1 : 0));
74 hashed_message_fields[idx] =
75 field_ct::conditional_assign(predicate, hashed_message_fields[idx], field_ct(idx == 0 ? 1 : 0));
76 }
77
78 // Set public key to 2*generator when predicate is false
79 // The choice of 2*generator is arbitrary; it just needs to be a valid point on the curve and different from G
80 // or (-G). For secp256r1, the batch multiplication requires that the two points do not have the same x
81 // coordinate (so as to create a valid lookup table).
82 // Compute as native type to get byte representation
83 typename Curve::AffineElementNative default_point_native(Curve::g1::one + Curve::g1::one);
84 std::array<uint8_t, 32> default_x_bytes;
85 std::array<uint8_t, 32> default_y_bytes;
86 Curve::fq::serialize_to_buffer(default_point_native.x, default_x_bytes.data());
87 Curve::fq::serialize_to_buffer(default_point_native.y, default_y_bytes.data());
88
89 for (size_t i = 0; i < 32; ++i) {
90 pub_x_fields[i] = field_ct::conditional_assign(predicate, pub_x_fields[i], field_ct(default_x_bytes[i]));
91 pub_y_fields[i] = field_ct::conditional_assign(predicate, pub_y_fields[i], field_ct(default_y_bytes[i]));
92 }
93 } else {
94 BB_ASSERT(input.predicate.value, "Creating ECDSA constraints with a constant predicate equal to false.");
95 }
96
97 // Step 2: Convert conditionally-assigned fields to byte arrays (adds range constraints on the correct values)
98 byte_array_ct hashed_message = fields_to_bytes(builder, hashed_message_fields);
99 byte_array_ct pub_x_bytes = fields_to_bytes(builder, pub_x_fields);
100 byte_array_ct pub_y_bytes = fields_to_bytes(builder, pub_y_fields);
103 bool_ct result(result_field); // Constructor enforces result = 0 or 1
104
105 // Step 3: Construct public key from byte arrays
106 Fq pub_x(pub_x_bytes);
107 Fq pub_y(pub_y_bytes);
108 // This constructor sets the infinity flag of public_key to false. This is OK because the point at infinity is not a
109 // point on the curve and we check tha public_key is on the curve in the ecdsa verification circuit.
110 G1 public_key(pub_x, pub_y, /*assert_on_curve=*/false);
111
112 // Step 4.
113 bool_ct signature_result =
114 stdlib::ecdsa_verify_signature<Builder, Curve, Fq, Fr, G1>(hashed_message, public_key, { r, s });
115
116 // Step 5.
117 // Ensure the circuit is satisfied when predicate is witness false
118 signature_result.assert_equal(bool_ct::conditional_assign(predicate, result, signature_result));
119}
120
126template <typename Curve>
127void create_dummy_ecdsa_constraint(typename Curve::Builder& builder,
128 const std::vector<stdlib::field_t<typename Curve::Builder>>& hashed_message_fields,
129 const std::vector<stdlib::field_t<typename Curve::Builder>>& r_fields,
130 const std::vector<stdlib::field_t<typename Curve::Builder>>& s_fields,
131 const std::vector<stdlib::field_t<typename Curve::Builder>>& pub_x_fields,
132 const std::vector<stdlib::field_t<typename Curve::Builder>>& pub_y_fields,
134{
135 using FqNative = Curve::fq;
136 using G1Native = Curve::g1;
137
138 // Vector of 32 copies of bb::fr::zero()
139 std::vector<bb::fr> mock_zeros(32, bb::fr::zero());
140
141 // Hashed message
142 populate_fields(builder, hashed_message_fields, mock_zeros);
143
144 // Signature
145 populate_fields(builder, r_fields, mock_zeros);
146 populate_fields(builder, s_fields, mock_zeros);
147
148 // Pub key
149 std::array<uint8_t, 32> buffer_x;
150 std::array<uint8_t, 32> buffer_y;
151 std::vector<bb::fr> mock_pub_x;
152 std::vector<bb::fr> mock_pub_y;
153 FqNative::serialize_to_buffer(G1Native::one.x, &buffer_x[0]);
154 FqNative::serialize_to_buffer(G1Native::one.y, &buffer_y[0]);
155 for (auto [byte_x, byte_y] : zip_view(buffer_x, buffer_y)) {
156 mock_pub_x.emplace_back(bb::fr(byte_x));
157 mock_pub_y.emplace_back(bb::fr(byte_y));
158 }
159 populate_fields(builder, pub_x_fields, mock_pub_x);
160 populate_fields(builder, pub_y_fields, mock_pub_y);
161
162 // Result
163 builder.set_variable(result_field.get_witness_index(), bb::fr::one());
164}
165
166template void create_ecdsa_verify_constraints<stdlib::secp256k1<UltraCircuitBuilder>>(UltraCircuitBuilder& builder,
167 const EcdsaConstraint& input);
168template void create_ecdsa_verify_constraints<stdlib::secp256k1<MegaCircuitBuilder>>(MegaCircuitBuilder& builder,
169 const EcdsaConstraint& input);
170template void create_ecdsa_verify_constraints<stdlib::secp256r1<UltraCircuitBuilder>>(UltraCircuitBuilder& builder,
171 const EcdsaConstraint& input);
172template void create_ecdsa_verify_constraints<stdlib::secp256r1<MegaCircuitBuilder>>(MegaCircuitBuilder& builder,
173 const EcdsaConstraint& input);
174
175template void create_dummy_ecdsa_constraint<stdlib::secp256k1<UltraCircuitBuilder>>(
183
184template void create_dummy_ecdsa_constraint<stdlib::secp256r1<UltraCircuitBuilder>>(
192
193} // namespace acir_format
#define BB_ASSERT(expression,...)
Definition assert.hpp:67
Implements boolean logic in-circuit.
Definition bool.hpp:59
Represents a dynamic array of bytes in-circuit.
static field_t from_witness_index(Builder *ctx, uint32_t witness_index)
Definition field.cpp:62
static field_t conditional_assign(const bool_t< Builder > &predicate, const field_t &lhs, const field_t &rhs)
Definition field.hpp:372
uint32_t get_witness_index() const
Get the witness index of the current field element.
Definition field.hpp:506
AluTraceBuilder builder
Definition alu.test.cpp:124
void populate_fields(Builder &builder, const std::vector< field_t< Builder > > &fields, const std::vector< bb::fr > &values)
Populate fields in the builder with the given values. To be used in mocking situations.
Definition utils.hpp:122
stdlib::field_t< Builder > field_ct
void create_ecdsa_verify_constraints(typename Curve::Builder &builder, const EcdsaConstraint &input)
Create constraints to verify an ECDSA signature.
byte_array< Builder > fields_to_bytes(Builder &builder, std::vector< field_t< Builder > > &fields)
Convert a vector of field_t elements to a byte_array enforcing each element to be a boolean.
Definition utils.hpp:47
void create_dummy_ecdsa_constraint(typename Curve::Builder &builder, const std::vector< stdlib::field_t< typename Curve::Builder > > &hashed_message_fields, const std::vector< stdlib::field_t< typename Curve::Builder > > &r_fields, const std::vector< stdlib::field_t< typename Curve::Builder > > &s_fields, const std::vector< stdlib::field_t< typename Curve::Builder > > &pub_x_fields, const std::vector< stdlib::field_t< typename Curve::Builder > > &pub_y_fields, const stdlib::field_t< typename Curve::Builder > &result_field)
Generate dummy ECDSA constraints when the builder doesn't have witnesses.
bb::stdlib::field_t< Builder > to_field_ct(const WitnessOrConstant< typename Builder::FF > &input, Builder &builder)
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
Curve::AffineElement G1
std::array< uint32_t, 32 > pub_x_indices
std::array< uint32_t, 32 > hashed_message
WitnessOrConstant< bb::fr > predicate
std::array< uint32_t, 64 > signature
std::array< uint32_t, 32 > pub_y_indices
static constexpr field one()
static constexpr field zero()