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: 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
12
13namespace acir_format {
14
15using namespace bb;
16
41template <typename Curve>
42void create_ecdsa_verify_constraints(typename Curve::Builder& builder,
43 const EcdsaConstraint& input,
44 bool has_valid_witness_assignments)
45{
46 using Builder = Curve::Builder;
47
48 using Fq = Curve::fq_ct;
49 using Fr = Curve::bigfr_ct;
50 using G1 = Curve::g1_bigfr_ct;
51
55
56 // Lambda to convert std::vector<field_ct> to byte_array_ct
57 auto fields_to_bytes = [](Builder& builder, std::vector<field_ct>& fields) -> byte_array_ct {
58 byte_array_ct result(&builder);
59 for (auto& field : fields) {
60 // Construct byte array of length 1 from the field element
61 // The constructor enforces that `field` fits in one byte
62 byte_array_ct byte_to_append(field, /*num_bytes=*/1);
63 // Append the new byte to the result
64 result.write(byte_to_append);
65 }
66
67 return result;
68 };
69
70 // Define builder variables based on the witness indices
71 std::vector<field_ct> hashed_message_fields = fields_from_witnesses(builder, input.hashed_message);
72 std::vector<field_ct> r_fields = fields_from_witnesses(builder, std::span(input.signature.begin(), 32));
73 std::vector<field_ct> s_fields = fields_from_witnesses(builder, std::span(input.signature.begin() + 32, 32));
74 std::vector<field_ct> pub_x_fields = fields_from_witnesses(builder, input.pub_x_indices);
75 std::vector<field_ct> pub_y_fields = fields_from_witnesses(builder, input.pub_y_indices);
77
78 if (!has_valid_witness_assignments) {
79 // Fill builder variables in case of empty witness assignment
80 create_dummy_ecdsa_constraint<Curve>(
81 builder, hashed_message_fields, r_fields, s_fields, pub_x_fields, pub_y_fields, result_field);
82 }
83
84 // Step 1.
85 // Construct inputs to signature verification from witness indices
86 byte_array_ct hashed_message = fields_to_bytes(builder, hashed_message_fields);
87 byte_array_ct pub_x_bytes = fields_to_bytes(builder, pub_x_fields);
88 byte_array_ct pub_y_bytes = fields_to_bytes(builder, pub_y_fields);
89 byte_array_ct r = fields_to_bytes(builder, r_fields);
90 byte_array_ct s = fields_to_bytes(builder, s_fields);
91 bool_ct result = static_cast<bool_ct>(result_field); // Constructor enforces result_field = 0 or 1
92
93 // Step 2.
94 // Reconstruct the public key from the byte representations of its coordinates
95 Fq pub_x(pub_x_bytes);
96 Fq pub_y(pub_y_bytes);
97
98 // Step 3.
99 // Update it depending on the predicate: if predicate is false, set public key to 2*G1.one
100 if (!input.predicate.is_constant) {
101 // We need to use a point which is different from the generator otherwise secp256r1 ECDSA verification fails
102 auto default_point = Curve::g1::one + Curve::g1::one;
103 // Addition is done on affine coordinates, so we need to normalize it
104 default_point = default_point.normalize();
105 bool_ct predicate_witness = bool_ct::from_witness_index_unsafe(&builder, input.predicate.index);
106 pub_x = Fq::conditional_assign(predicate_witness, pub_x, default_point.x);
107 pub_y = Fq::conditional_assign(predicate_witness, pub_y, default_point.y);
108 // Avoid mixing constant/witness coordinates because of issue
109 // https://github.com/AztecProtocol/aztec-packages/issues/17514
110 if (pub_x.is_constant() != pub_y.is_constant()) {
111 if (pub_x.is_constant()) {
112 pub_x.convert_constant_to_fixed_witness(&builder);
113 } else if (pub_y.is_constant()) {
114 pub_y.convert_constant_to_fixed_witness(&builder);
115 }
116 }
117 } else {
119 input.predicate.value, true, "Creating ECDSA constraints with a constant predicate equal to false.");
120 }
121 G1 public_key(pub_x, pub_y);
122 // Step 4.
123 // Ensure uniqueness of the public key by asserting each of its coordinates is smaller than the modulus of the base
124 // field
125 pub_x.assert_is_in_field("ECDSA input validation: the x coordinate of the public key is larger than Fq::modulus");
126 pub_y.assert_is_in_field("ECDSA input validation: the y coordinate of the public key is larger than Fq::modulus");
127
128 // Step 5.
129 bool_ct signature_result =
130 stdlib::ecdsa_verify_signature<Builder, Curve, Fq, Fr, G1>(hashed_message, public_key, { r, s });
131
132 // Step 6.
133 // Assert that signature verification returned the expected result
134 signature_result.assert_equal(result);
135}
136
142template <typename Curve>
143void create_dummy_ecdsa_constraint(typename Curve::Builder& builder,
144 const std::vector<stdlib::field_t<typename Curve::Builder>>& hashed_message_fields,
145 const std::vector<stdlib::field_t<typename Curve::Builder>>& r_fields,
146 const std::vector<stdlib::field_t<typename Curve::Builder>>& s_fields,
147 const std::vector<stdlib::field_t<typename Curve::Builder>>& pub_x_fields,
148 const std::vector<stdlib::field_t<typename Curve::Builder>>& pub_y_fields,
150{
151 using Builder = Curve::Builder;
152 using FqNative = Curve::fq;
153 using G1Native = Curve::g1;
155
156 // Lambda to populate builder variables from vector of field values
157 auto populate_fields = [&builder](const std::vector<field_ct>& fields, const std::vector<bb::fr>& values) {
158 for (auto [field, value] : zip_view(fields, values)) {
159 builder.set_variable(field.witness_index, value);
160 }
161 };
162
163 // Vector of 32 copies of bb::fr::zero()
164 std::vector<bb::fr> mock_zeros(32, bb::fr::zero());
165
166 // Hashed message
167 populate_fields(hashed_message_fields, mock_zeros);
168
169 // Signature
170 populate_fields(r_fields, mock_zeros);
171 populate_fields(s_fields, mock_zeros);
172
173 // Pub key
174 std::array<uint8_t, 32> buffer_x;
175 std::array<uint8_t, 32> buffer_y;
176 std::vector<bb::fr> mock_pub_x;
177 std::vector<bb::fr> mock_pub_y;
178 FqNative::serialize_to_buffer(G1Native::one.x, &buffer_x[0]);
179 FqNative::serialize_to_buffer(G1Native::one.y, &buffer_y[0]);
180 for (auto [byte_x, byte_y] : zip_view(buffer_x, buffer_y)) {
181 mock_pub_x.emplace_back(bb::fr(byte_x));
182 mock_pub_y.emplace_back(bb::fr(byte_y));
183 }
184 populate_fields(pub_x_fields, mock_pub_x);
185 populate_fields(pub_y_fields, mock_pub_y);
186
187 // Result
188 builder.set_variable(result_field.witness_index, bb::fr::one());
189}
190
191template void create_ecdsa_verify_constraints<stdlib::secp256k1<UltraCircuitBuilder>>(
192 UltraCircuitBuilder& builder, const EcdsaConstraint& input, bool has_valid_witness_assignments);
193template void create_ecdsa_verify_constraints<stdlib::secp256k1<MegaCircuitBuilder>>(
194 MegaCircuitBuilder& builder, const EcdsaConstraint& input, bool has_valid_witness_assignments);
195template void create_ecdsa_verify_constraints<stdlib::secp256r1<UltraCircuitBuilder>>(
196 UltraCircuitBuilder& builder, const EcdsaConstraint& input, bool has_valid_witness_assignments);
197template void create_ecdsa_verify_constraints<stdlib::secp256r1<MegaCircuitBuilder>>(
198 MegaCircuitBuilder& builder, const EcdsaConstraint& input, bool has_valid_witness_assignments);
199
200template void create_dummy_ecdsa_constraint<stdlib::secp256k1<UltraCircuitBuilder>>(
208
209template void create_dummy_ecdsa_constraint<stdlib::secp256r1<UltraCircuitBuilder>>(
217
218} // namespace acir_format
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:88
Implements boolean logic in-circuit.
Definition bool.hpp:59
void assert_equal(const bool_t &rhs, std::string const &msg="bool_t::assert_equal") const
Implements copy constraint for bool_t elements.
Definition bool.cpp:423
static bool_t from_witness_index_unsafe(Builder *ctx, uint32_t witness_index)
Create a bool_t from a witness index that is known to contain a constrained bool value.
Definition bool.cpp:96
Represents a dynamic array of bytes in-circuit.
byte_array & write(byte_array const &other)
Appends the contents of another byte_array (other) to the end of this one.
static field_t from_witness_index(Builder *ctx, uint32_t witness_index)
Definition field.cpp:61
uint32_t witness_index
Definition field.hpp:132
AluTraceBuilder builder
Definition alu.test.cpp:123
void create_ecdsa_verify_constraints(typename Curve::Builder &builder, const EcdsaConstraint &input, bool has_valid_witness_assignments)
Create constraints to verify an ECDSA signature.
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.
Entry point for Barretenberg command-line interface.
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()