Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
ecdsa_impl.hpp
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
7#pragma once
8
13
14namespace bb::stdlib {
15
16namespace {
18}
19
73template <typename Builder, typename Curve, typename Fq, typename Fr, typename G1>
75 const G1& public_key,
76 const ecdsa_signature<Builder>& sig)
77{
78 // Fetch the context
79 Builder* builder = hashed_message.get_context();
80 builder = validate_context(builder, public_key.get_context());
82 BB_ASSERT_EQ(builder != nullptr, true, "At least one of the inputs should be non-constant.");
83
84 // Turn the hashed message into an element of Fr
85 // The assertion means that an honest prover has a small probability of not being able to generate a valid proof if
86 // H(m) >= n. Enforcing this condition introduces a small number of gates, and ensures that signatures cannot be
87 // forged by finding a collision of H modulo n. While finding such a collision is supposed to be hard even modulo n,
88 // we protect against this case with this cheap check.
89 Fr z(hashed_message);
90 z.assert_is_in_field(
91 "ECDSA input validation: the hash of the message is bigger than the order of the elliptic curve.");
92
93 // Step 1.
94 public_key.validate_on_curve("ECDSA input validation: the public key is not a point on the elliptic curve.");
95
96 // Step 2.
97 public_key.is_point_at_infinity().assert_equal(bool_t<Builder>(false),
98 "ECDSA input validation: the public key is the point at infinity.");
99
100 // Step 3.
101 Fr r(sig.r);
102 r.assert_is_in_field("ECDSA input validation: the r component of the signature is bigger than the order of the "
103 "elliptic curve."); // r < n
104 r.assert_is_not_equal(Fr::zero(), "ECDSA input validation: the r component of the signature is zero."); // 0 < r
105
106 // Step 4.
107 Fr s(sig.s);
108 s.assert_less_than(
109 (Fr::modulus + 1) / 2,
110 "ECDSA input validation: the s component of the signature is bigger than Fr::modulus - s."); // s < (n+1)/2
111 s.assert_is_not_equal(Fr::zero(), "ECDSA input validation: the s component of the signature is zero."); // 0 < s
112
113 // Step 5.
114 Fr u1 = z.div_without_denominator_check(s);
115 Fr u2 = r.div_without_denominator_check(s);
116
117 G1 result;
118 if constexpr (Curve::type == bb::CurveType::SECP256K1) {
119 result = G1::secp256k1_ecdsa_mul(public_key, u1, u2);
120 } else {
121 // This error comes from the lookup tables used in batch_mul. We could get rid of it by setting with_edgecase =
122 // true. However, this would increase the gate count, and it would handle a case that should not appear in
123 // general: someone using plus or minus the generator as a public key.
124 if ((public_key.get_value().x == Curve::g1::affine_one.x) && (!builder->failed())) {
125 builder->failure("ECDSA input validation: the public key is equal to plus or minus the generator point.");
126 }
127 result = G1::batch_mul({ G1::one(builder), public_key }, { u1, u2 });
128 }
129
130 // Step 6.
131 result.is_point_at_infinity().assert_equal(
132 bool_t<Builder>(false), "ECDSA validation: the result of the batch multiplication is the point at infinity.");
133
134 // Step 7.
135 // We reduce result.x to 2^s, where s is the smallest s.t. 2^s > q. It is cheap in terms of constraints, and avoids
136 // possible edge cases
137 result.x.self_reduce();
138
139 // Transfer Fq value result.x to Fr (this is just moving from a C++ class to another)
140 Fr result_x_mod_r = Fr::unsafe_construct_from_limbs(result.x.binary_basis_limbs[0].element,
141 result.x.binary_basis_limbs[1].element,
142 result.x.binary_basis_limbs[2].element,
143 result.x.binary_basis_limbs[3].element);
144 // Copy maximum limb values from Fq to Fr: this is needed by the subtraction happening in the == operator
145 for (size_t idx = 0; idx < 4; idx++) {
146 result_x_mod_r.binary_basis_limbs[idx].maximum_value = result.x.binary_basis_limbs[idx].maximum_value;
147 }
148
149 // Check result.x = r mod n
150 bool_t<Builder> is_signature_valid = result_x_mod_r == r;
151
152 // Logging
153 if (is_signature_valid.get_value()) {
154 vinfo("ECDSA signature verification succeeded.");
155 } else {
156 vinfo("ECDSA signature verification failed");
157 }
158
159 return is_signature_valid;
160}
161
169template <typename Builder> void generate_ecdsa_verification_test_circuit(Builder& builder, size_t num_iterations)
170{
172
173 // Native types
174 using FrNative = typename Curve::fr;
175 using FqNative = typename Curve::fq;
176 using G1Native = typename Curve::g1;
177
178 // Stdlib types
179 using Fr = typename Curve::bigfr_ct;
180 using Fq = typename Curve::fq_ct;
181 using G1 = typename Curve::g1_bigfr_ct;
182
183 std::string message_string = "Instructions unclear, ask again later.";
184
186 for (size_t i = 0; i < num_iterations; i++) {
187 // Generate unique signature for each iteration
188 account.private_key = FrNative::random_element(&engine);
189 account.public_key = G1Native::one * account.private_key;
190
191 crypto::ecdsa_signature signature =
192 crypto::ecdsa_construct_signature<crypto::Sha256Hasher, FqNative, FrNative, G1Native>(message_string,
193 account);
194
195 bool native_verification = crypto::ecdsa_verify_signature<crypto::Sha256Hasher, FqNative, FrNative, G1Native>(
196 message_string, account.public_key, signature);
197 BB_ASSERT_EQ(native_verification, true, "Native ECDSA verification failed while generating test circuit.");
198
199 std::vector<uint8_t> rr(signature.r.begin(), signature.r.end());
200 std::vector<uint8_t> ss(signature.s.begin(), signature.s.end());
201
202 G1 public_key = G1::from_witness(&builder, account.public_key);
203
205
206 byte_array<Builder> message(&builder, message_string);
207
208 // Compute H(m)
209 stdlib::byte_array<Builder> hashed_message =
211
212 // Verify ecdsa signature
213 bool_t<Builder> result =
214 stdlib::ecdsa_verify_signature<Builder, Curve, Fq, Fr, G1>(hashed_message, public_key, sig);
215 result.assert_equal(bool_t<Builder>(true));
216 }
217}
218
219} // namespace bb::stdlib
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:88
static byte_array< Builder > hash(const byte_array_ct &input)
Definition sha256.cpp:308
Implements boolean logic in-circuit.
Definition bool.hpp:59
bool get_value() const
Definition bool.hpp:111
Represents a dynamic array of bytes in-circuit.
Builder * get_context() const
#define vinfo(...)
Definition log.hpp:79
AluTraceBuilder builder
Definition alu.test.cpp:123
RNG & get_debug_randomness(bool reset, std::uint_fast64_t seed)
Definition engine.cpp:190
void generate_ecdsa_verification_test_circuit(Builder &builder, size_t num_iterations)
Generate a simple ecdsa verification circuit for testing purposes.
bool_t< Builder > ecdsa_verify_signature(const stdlib::byte_array< Builder > &hashed_message, const G1 &public_key, const ecdsa_signature< Builder > &sig)
Verify ECDSA signature. Returns bool_t(true/false) depending on whether the signature is valid or not...
T * validate_context(T *ptr)
Definition field.hpp:16
@ SECP256K1
Definition types.hpp:10
Curve::AffineElement G1
G1::affine_element public_key
Definition ecdsa.hpp:20
std::array< uint8_t, 32 > r
Definition ecdsa.hpp:26
std::array< uint8_t, 32 > s
Definition ecdsa.hpp:27
static constexpr uint256_t modulus
static constexpr field zero()
stdlib::byte_array< Builder > s
Definition ecdsa.hpp:16
Builder * get_context() const
Definition ecdsa.hpp:18
stdlib::byte_array< Builder > r
Definition ecdsa.hpp:15