Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
ecdsa.test.cpp
Go to the documentation of this file.
2#include "../../primitives/bigfield/bigfield.hpp"
3#include "../../primitives/biggroup/biggroup.hpp"
4#include "../../primitives/curves/secp256k1.hpp"
5#include "../../primitives/curves/secp256r1.hpp"
8#include "ecdsa.hpp"
10
11#include <gtest/gtest.h>
12
13#include <algorithm>
14
15using namespace bb;
16using namespace bb::crypto;
17
18template <class Curve> class EcdsaTests : public ::testing::Test {
19 public:
20 using Builder = Curve::Builder;
21 using CurveType =
23
24 // Native Types
25 using FrNative = Curve::fr;
26 using FqNative = Curve::fq;
27 using G1Native = Curve::g1;
28
29 // Stdlib types
30 using Fr = Curve::bigfr_ct;
31 using Fq = Curve::fq_ct;
32 using G1 = Curve::g1_bigfr_ct;
33 using bool_t = Curve::bool_ct;
34
35 // Reproducible signature
36 static constexpr FrNative private_key =
37 FrNative("0xd67abee717b3fc725adf59e2cc8cd916435c348b277dd814a34e3ceb279436c2");
38
52
54 bool random_signature)
55 {
57
58 account.private_key = random_signature ? FrNative::random_element() : private_key;
59 account.public_key = G1Native::one * account.private_key;
60
61 ecdsa_signature signature =
62 ecdsa_construct_signature<Sha256Hasher, FqNative, FrNative, G1Native>(message_string, account);
63
64 if (random_signature) {
65 // Logging in case of random signature
66 info("The private key used generate this signature is: ", private_key);
67 }
68
69 return { account, signature };
70 }
71
72 std::string tampering(std::string message_string,
74 ecdsa_signature& signature,
76 {
77 std::string failure_msg;
78
79 switch (mode) {
81 // Invalidate the circuit by passing a public key with x >= q
82 // Do nothing here, tampering happens in circuit
83 failure_msg = "ECDSA input validation: coordinate(s) of the public key bigger than the base field modulus. "
84 "(x coordinate): hi limb.";
85 break;
86 }
88 // Invalidate the circuit by passing a public key with y >= q
89 // Do nothing here, tampering happens in circuit
90 failure_msg = "ECDSA input validation: coordinate(s) of the public key bigger than the base field modulus. "
91 "(y coordinate): hi limb.";
92 break;
93 }
95 // Invalidate the signature by changing r.
96 FrNative r = FrNative::serialize_from_buffer(&signature.r[0]);
97 r += FrNative::one();
98
99 FrNative::serialize_to_buffer(r, &signature.r[0]);
100 break;
101 }
103 // Invalidate the signature by changing s.
104 FrNative s = FrNative::serialize_from_buffer(&signature.s[0]);
105 s += FrNative::one();
106
107 FrNative::serialize_to_buffer(s, &signature.s[0]);
108 break;
109 }
111 // Invalidate the signature by changing s to -s.
112 FrNative s = FrNative::serialize_from_buffer(&signature.s[0]);
113 s = -s;
114
115 FrNative::serialize_to_buffer(s, &signature.s[0]);
116 failure_msg =
117 "ECDSA input validation: the s component of the signature is bigger than (Fr::modulus + 1)/2.: "
118 "hi limb."; // The second part of the message is added by the range constraint
119 break;
120 }
122 // Invalidate signature by setting r to 0
123 signature.r = std::array<uint8_t, 32>{};
124
125 failure_msg = "ECDSA input validation: the r component of the signature is zero.";
126 break;
127 }
129 // Invalidate signature by setting s to 0
130 signature.s = std::array<uint8_t, 32>{};
131
132 failure_msg = "ECDSA input validation: the s component of the signature is zero.";
133 break;
134 }
136 // Invalidate the signature by making making u1 * G + u2 * P return the point at infinity
137
138 // Compute H(m)
139 std::vector<uint8_t> buffer;
140 std::ranges::copy(message_string, std::back_inserter(buffer));
141 auto hash = Sha256Hasher::hash(buffer);
142
143 // Override the public key: new public key is (-hash) * r^{-1} * G
144 FrNative fr_hash = FrNative::serialize_from_buffer(&hash[0]);
145 FrNative r = FrNative::serialize_from_buffer(&signature.r[0]);
146 FrNative r_inverse = r.invert();
147 FrNative modified_private_key = r_inverse * (-fr_hash);
148 account.public_key = G1Native::one * modified_private_key;
149
150 // Verify that the result is the point at infinity
151 auto P = G1Native::one * fr_hash + account.public_key * r;
152 BB_ASSERT_EQ(P.is_point_at_infinity(), true);
153
154 failure_msg = "ECDSA validation: the result of the batch multiplication is the point at infinity.";
155 break;
156 }
158 // Invalidate the circuit by passing a public key which is not on the curve
159 account.public_key.x = account.public_key.y;
160 BB_ASSERT_EQ(account.public_key.on_curve(), false);
161
162 failure_msg = "ECDSA input validation: the public key is not a point on the elliptic curve.";
163 break;
164 }
166 // Invalidate the circuit by passing a public key which is not on the curve
167 account.public_key.self_set_infinity();
168 BB_ASSERT_EQ(account.public_key.is_point_at_infinity(), true);
169
170 failure_msg = "ECDSA input validation: the public key is the point at infinity.";
171 break;
172 }
174 break;
175 }
176
177 // Natively verify that the tampering was successfull
178 bool is_signature_valid = ecdsa_verify_signature<Sha256Hasher, FqNative, FrNative, G1Native>(
179 message_string, account.public_key, signature);
181 // If either s >= (n+1)/2 or the result of the scalar multiplication is the point at infinity, then the
182 // verification function raises an error, we treat it as an invalid signature
183 is_signature_valid = false;
184 }
186 // In these tampering modes nothing has changed and the tampering happens in circuit, so we override the
187 // result and set it to false
188 is_signature_valid = false;
189 }
190
191 bool expected = mode == TamperingMode::None;
192 BB_ASSERT_EQ(is_signature_valid,
193 expected,
194 "Signature verification returned a different result from the expected one. If the signature was "
195 "randomly generated, there is a (very) small chance this is not a bug.");
196
197 return failure_msg;
198 }
199
203 const ecdsa_signature& signature,
204 const TamperingMode mode)
205 {
206 std::vector<uint8_t> rr(signature.r.begin(), signature.r.end());
207 std::vector<uint8_t> ss(signature.s.begin(), signature.s.end());
208
211
212 // We construct the point via its x,y-coordinates with an explicit infinity flag.
213 // This avoids:
214 // 1. The on-curve check of G1::from_witness (so we can test invalid points)
215 // 2. The expensive infinity auto-detection of the 2-arg constructor
216 Fq x = Fq::from_witness(&builder, account.public_key.x);
217 Fq y = Fq::from_witness(&builder, account.public_key.y);
219 // To test the case in which one of the two coordinates is above the modulus of the base field, we need
220 // to override the limbs of the coordinates
221 uint256_t max_uint = (static_cast<uint256_t>(1) << 256) - 1;
222 for (size_t idx = 0; idx < 4; idx++) {
224 ? x.binary_basis_limbs[idx].element.get_witness_index()
225 : y.binary_basis_limbs[idx].element.get_witness_index(),
226 bb::fr(max_uint.slice(64 * idx, 64 * (idx + 1))));
227 }
228 }
230 // Override coordinates to (0, 0) for infinity
231 x = Fq::from_witness(&builder, FqNative::zero());
232 y = Fq::from_witness(&builder, FqNative::zero());
233 }
234
235 // Create explicit infinity flag witness from the native point's infinity status
236 bool_t is_infinity(
237 stdlib::witness_t<Builder>(&builder, account.public_key.is_point_at_infinity() ? fr::one() : fr::zero()),
238 false);
239
240 // Use the test accessor to create element with explicit infinity flag (avoids expensive auto-detection)
241 G1 pub_key = stdlib::element_default::element_test_accessor::
242 create_element_with_explicit_infinity<Builder, Fq, Fr, G1Native>(
243 x, y, is_infinity, /*assert_on_curve=*/false);
244 pub_key.set_free_witness_tag();
245
246 return { pub_key, sig };
247 }
248
250 const stdlib::byte_array<Builder>& hashed_message,
252 const ecdsa_signature& signature,
253 const bool signature_verification_result,
254 const bool circuit_checker_result,
255 const std::string failure_msg,
256 const TamperingMode mode)
257
258 {
259 auto [public_key, sig] = create_stdlib_ecdsa_data(builder, account, signature, mode);
260
261 // Verify signature
262 stdlib::bool_t<Builder> signature_result =
263 stdlib::ecdsa_verify_signature<Builder, Curve, Fq, Fr, G1>(hashed_message, public_key, sig);
264
265 // Enforce verification returns the expected result
266 signature_result.assert_equal(stdlib::bool_t<Builder>(signature_verification_result));
267
268 // Check native values
269 EXPECT_EQ(signature_result.get_value(), signature_verification_result);
270
271 // Log data
272 size_t finalized_num_gates = builder.get_num_finalized_gates_inefficient();
273 info("num gates = ", finalized_num_gates);
274 benchmark_info(Builder::NAME_STRING, "ECDSA", "Signature Verification Test", "Gate Count", finalized_num_gates);
275
276 // Circuit checker
277 bool is_circuit_satisfied = CircuitChecker::check(builder);
278 EXPECT_EQ(is_circuit_satisfied, circuit_checker_result);
279
280 // Check the error
281 EXPECT_EQ(builder.err(), failure_msg);
282
283 return finalized_num_gates;
284 }
285
286 size_t test_verify_signature(bool random_signature, TamperingMode mode)
287 {
288 // Map tampering mode to signature verification result
289 bool signature_verification_result = (mode == TamperingMode::None) || (mode == TamperingMode::HighS);
290 // Map tampering mode to circuit checker result
291 bool circuit_checker_result =
293
294 std::string message_string = "Goblin";
295 std::vector<uint8_t> message_bytes(message_string.begin(), message_string.end());
296 std::array<uint8_t, 32> hashed_message_bytes_ = Sha256Hasher::hash(message_bytes);
297 std::vector<uint8_t> hashed_message_bytes;
298 hashed_message_bytes.reserve(32);
299 for (auto byte : hashed_message_bytes_) {
300 hashed_message_bytes.emplace_back(byte);
301 }
302
303 auto [account, signature] = generate_dummy_ecdsa_data(message_string, /*random_signature=*/random_signature);
304
305 // Tamper with the signature
306 std::string failure_msg = tampering(message_string, account, signature, mode);
307
308 // Create ECDSA verification circuit
310 stdlib::byte_array<Builder> hashed_message(&builder, hashed_message_bytes);
311
312 // ECDSA verification
314 hashed_message,
315 account,
316 signature,
317 signature_verification_result,
318 circuit_checker_result,
319 failure_msg,
320 mode);
321 }
322
329 {
330 for (auto test : tests) {
331 // Keypair
333 account.private_key = FrNative::one(); // Dummy value, unused
334 account.public_key = typename G1Native::affine_element(test.x, test.y);
335
336 // Signature
337 std::array<uint8_t, 32> r;
338 std::array<uint8_t, 32> s;
339 uint8_t v = 0; // Dummy value, unused
340 FrNative::serialize_to_buffer(test.r, &r[0]);
341 FrNative::serialize_to_buffer(test.s, &s[0]);
342
343 // Hashed message
344 std::array<uint8_t, 32> hashed_message_bytes_ = Sha256Hasher::hash(test.message);
345 std::vector<uint8_t> hashed_message_bytes;
346 hashed_message_bytes.reserve(32);
347 for (auto byte : hashed_message_bytes_) {
348 hashed_message_bytes.emplace_back(byte);
349 }
350
351 // Create ECDSA verification circuit
353 stdlib::byte_array<Builder> hashed_message(&builder, hashed_message_bytes);
354
355 // ECDSA verification
357 hashed_message,
358 account,
359 { r, s, v },
360 test.is_valid_signature,
361 test.is_circuit_satisfied,
362 test.failure_msg,
364 }
365 }
366};
367
368using Curves = testing::Types<stdlib::secp256k1<UltraCircuitBuilder>,
372
374
375TYPED_TEST(EcdsaTests, VerifyRandomSignature)
376{
377 TestFixture::test_verify_signature(/*random_signature=*/true, TestFixture::TamperingMode::None);
378}
379
380TYPED_TEST(EcdsaTests, VerifySignature)
381{
382 using Curve = TypeParam;
383
384 size_t finalized_num_gates =
385 TestFixture::test_verify_signature(/*random_signature=*/false, TestFixture::TamperingMode::None);
386 static constexpr size_t NUM_GATES_SECP256K1 = 42284;
387 static constexpr size_t NUM_GATES_SECP256R1 = IsMegaBuilder<typename Curve::Builder> ? 72057 : 72055;
388 BB_ASSERT_EQ(finalized_num_gates,
389 Curve::type == bb::CurveType::SECP256K1 ? NUM_GATES_SECP256K1 : NUM_GATES_SECP256R1,
390 "There has been a change in the number of gates for ECDSA verification");
391}
392
393TYPED_TEST(EcdsaTests, XCoordinateOverflow)
394{
396 TestFixture::test_verify_signature(/*random_signature=*/false, TestFixture::TamperingMode::XCoordinateOverflow);
397}
398
399TYPED_TEST(EcdsaTests, YCoordinateOverflow)
400{
402 TestFixture::test_verify_signature(/*random_signature=*/false, TestFixture::TamperingMode::YCoordinateOverflow);
403}
404
406{
407 TestFixture::test_verify_signature(/*random_signature=*/false, TestFixture::TamperingMode::InvalidR);
408}
409
411{
412 TestFixture::test_verify_signature(/*random_signature=*/false, TestFixture::TamperingMode::InvalidS);
413}
414
416{
417 // Disable asserts because native ecdsa verification raises an error if s >= (n+1)/2
419 TestFixture::test_verify_signature(/*random_signature=*/false, TestFixture::TamperingMode::HighS);
420}
421
423{
424 TestFixture::test_verify_signature(/*random_signature=*/false, TestFixture::TamperingMode::ZeroR);
425}
426
428{
429 TestFixture::test_verify_signature(/*random_signature=*/false, TestFixture::TamperingMode::ZeroS);
430}
431
432TYPED_TEST(EcdsaTests, InvalidPubKey)
433{
434 // Disable asserts because `validate_on_curve` raises an error in the `mult_madd` function:
435 // BB_ASSERT_EQ(remainder_1024.lo, uint512_t(0))
437 TestFixture::test_verify_signature(/*random_signature=*/false, TestFixture::TamperingMode::InvalidPubKey);
438}
439
440TYPED_TEST(EcdsaTests, InfinityPubKey)
441{
442 // Disable asserts to avoid errors trying to invert zero
444 TestFixture::test_verify_signature(/*random_signature=*/false, TestFixture::TamperingMode::InfinityPubKey);
445}
446
447TYPED_TEST(EcdsaTests, InfinityScalarMul)
448{
449 // Disable asserts because native ecdsa verification raises an error if the result of the scalar multiplication is
450 // the point at infinity
452 TestFixture::test_verify_signature(/*random_signature=*/false, TestFixture::TamperingMode::InfinityScalarMul);
453}
454
456{
457 if constexpr (TypeParam::type == bb::CurveType::SECP256K1) {
458 TestFixture::test_wycherproof(stdlib::secp256k1_tests);
459 } else {
460 TestFixture::test_wycherproof(stdlib::secp256r1_tests);
461 }
462}
463
464TEST(EcdsaTests, Secp256k1PointAtInfinityRegression)
465{
466 // Disable asserts because native ecdsa verification raises an error if the result of the scalar multiplication is
467 // the point at infinity
470
471 using FqNative = Curve::fq;
472 using FrNative = Curve::fr;
473 using G1Native = Curve::g1;
474
475 using Builder = Curve::Builder;
476 using FrStdlib = Curve::bigfr_ct;
477 using FqStdlib = Curve::fq_ct;
478 using G1Stdlib = Curve::g1_bigfr_ct;
479
480 // Attack parameters for P = 5*G
481 // These are crafted so that u1*G + u2*P = O (point at infinity)
482 const std::array<uint8_t, 32> pub_x_bytes = { 0x2f, 0x8b, 0xde, 0x4d, 0x1a, 0x07, 0x20, 0x93, 0x55, 0xb4, 0xa7,
483 0x25, 0x0a, 0x5c, 0x51, 0x28, 0xe8, 0x8b, 0x84, 0xbd, 0xdc, 0x61,
484 0x9a, 0xb7, 0xcb, 0xa8, 0xd5, 0x69, 0xb2, 0x40, 0xef, 0xe4 };
485 const std::array<uint8_t, 32> pub_y_bytes = { 0xd8, 0xac, 0x22, 0x26, 0x36, 0xe5, 0xe3, 0xd6, 0xd4, 0xdb, 0xa9,
486 0xdd, 0xa6, 0xc9, 0xc4, 0x26, 0xf7, 0x88, 0x27, 0x1b, 0xab, 0x0d,
487 0x68, 0x40, 0xdc, 0xa8, 0x7d, 0x3a, 0xa6, 0xac, 0x62, 0xd6 };
488 const std::array<uint8_t, 32> r_bytes = { 0xa8, 0x41, 0x94, 0xc3, 0x71, 0xc6, 0x7b, 0xa2, 0x59, 0x2f, 0x59,
489 0xc6, 0x20, 0xad, 0x30, 0x4c, 0xb7, 0x6d, 0x7a, 0x88, 0x25, 0x6b,
490 0xb5, 0x0d, 0xc4, 0x1c, 0x66, 0x57, 0x44, 0xbf, 0x78, 0x61 };
491 const std::array<uint8_t, 32> s_bytes = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
492 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
493 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64 };
494 const std::array<uint8_t, 32> z_bytes = { 0xb6, 0xb8, 0x18, 0x2e, 0xc7, 0x1f, 0x95, 0xd4, 0x42, 0x13, 0x3f,
495 0x21, 0x5c, 0x9e, 0x0e, 0x7b, 0x55, 0x98, 0x0e, 0xf2, 0x02, 0x07,
496 0xf7, 0xaa, 0x2a, 0xbb, 0x7a, 0x7e, 0xe9, 0x1b, 0xab, 0x1f };
497
498 FqNative pub_x = FqNative::serialize_from_buffer(pub_x_bytes.data());
499 FqNative pub_y = FqNative::serialize_from_buffer(pub_y_bytes.data());
500 typename G1Native::affine_element public_key_native(pub_x, pub_y);
501 ASSERT_TRUE(public_key_native.on_curve()) << "Public key must be on curve";
502
503 // Native verification
504 const std::string message_string(z_bytes.begin(), z_bytes.end());
505 ecdsa_signature sig;
506 sig.r = r_bytes;
507 sig.s = s_bytes;
508 sig.v = 27;
509 bool native_verification =
510 ecdsa_verify_signature<Sha256Hasher, FqNative, FrNative, G1Native>(message_string, public_key_native, sig);
511
512 bool stdlib_verification;
513 {
515
516 const G1Stdlib public_key_ct = G1Stdlib::from_witness(&builder, public_key_native);
517
518 const std::vector<uint8_t> r_vec(r_bytes.begin(), r_bytes.end());
519 const std::vector<uint8_t> s_vec(s_bytes.begin(), s_bytes.end());
522
523 const std::vector<uint8_t> z_vec(z_bytes.begin(), z_bytes.end());
524 const stdlib::byte_array<Builder> hashed_message_ct(&builder, z_vec);
525
526 const stdlib::bool_t<Builder> signature_result =
527 stdlib::ecdsa_verify_signature<Builder, Curve, FqStdlib, FrStdlib, G1Stdlib>(
528 hashed_message_ct, public_key_ct, sig_ct);
529
530 stdlib_verification = signature_result.get_value();
531
532 const bool circuit_valid = CircuitChecker::check(builder);
533
534 // Circuit should fail because the result of the scalar multiplication is the point at infinity
535 ASSERT_FALSE(circuit_valid);
536 EXPECT_EQ(builder.err(), "ECDSA validation: the result of the batch multiplication is the point at infinity.");
537 }
538
539 // Both native and stdlib should reject this invalid signature
540 EXPECT_FALSE(native_verification);
541 EXPECT_FALSE(stdlib_verification);
542 EXPECT_EQ(native_verification, stdlib_verification);
543}
544
545TEST(EcdsaTests, Secp256r1NativeStdlibDiscrepancyRegression)
546{
548
549 using FqNative = Curve::fq;
550 using FrNative = Curve::fr;
551 using G1Native = Curve::g1;
552
553 using Builder = Curve::Builder;
554 using FrStdlib = Curve::bigfr_ct;
555 using FqStdlib = Curve::fq_ct;
556 using G1Stdlib = Curve::g1_bigfr_ct;
557
558 const std::array<uint8_t, 32> pub_x_bytes = { 0x79, 0x9f, 0x2a, 0xba, 0xfa, 0x27, 0x16, 0x4b, 0x09, 0x50, 0xf2,
559 0xc8, 0x82, 0xf0, 0xd1, 0x67, 0xe1, 0xd2, 0x16, 0x74, 0x87, 0xd5,
560 0x2e, 0xa7, 0x23, 0x0b, 0x5d, 0x96, 0xc2, 0xa8, 0x74, 0x00 };
561 const std::array<uint8_t, 32> pub_y_bytes = { 0xda, 0xa5, 0x79, 0xf4, 0xf1, 0x61, 0xe9, 0xdc, 0xa1, 0xa1, 0x34,
562 0x35, 0x92, 0x16, 0xb9, 0x35, 0xea, 0xd0, 0x97, 0x2d, 0x76, 0x3f,
563 0xe3, 0x33, 0xc7, 0x12, 0xee, 0x8d, 0x18, 0x4b, 0xd8, 0x11 };
564 const std::array<uint8_t, 32> r_bytes = { 0xb1, 0x99, 0xa1, 0x62, 0x72, 0x66, 0x61, 0xba, 0x23, 0x3c, 0xd6,
565 0xc6, 0x6e, 0x99, 0x0b, 0x01, 0x2e, 0x1e, 0x76, 0x04, 0xb1, 0x1f,
566 0x76, 0x19, 0x3b, 0x2a, 0xf5, 0xca, 0x36, 0xc1, 0x01, 0x76 };
567 const std::array<uint8_t, 32> s_bytes = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
568 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
569 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05 };
570 const std::array<uint8_t, 32> z_bytes = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
571 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
572 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05 };
573
574 const FqNative pub_x = FqNative::serialize_from_buffer(pub_x_bytes.data());
575 const FqNative pub_y = FqNative::serialize_from_buffer(pub_y_bytes.data());
576 const typename G1Native::affine_element public_key_native(pub_x, pub_y);
577 ASSERT_TRUE(public_key_native.on_curve()) << "Public key must be on curve";
578
579 // Native verification
580 const std::string message_string(z_bytes.begin(), z_bytes.end());
581 ecdsa_signature sig;
582 sig.r = r_bytes;
583 sig.s = s_bytes;
584 sig.v = 27;
585 bool native_verification =
586 ecdsa_verify_signature<Sha256Hasher, FqNative, FrNative, G1Native>(message_string, public_key_native, sig);
587
588 // Stdlib verification
590 const G1Stdlib public_key_ct = G1Stdlib::from_witness(&builder, public_key_native);
591
592 const std::vector<uint8_t> r_vec(r_bytes.begin(), r_bytes.end());
593 const std::vector<uint8_t> s_vec(s_bytes.begin(), s_bytes.end());
596
597 const std::vector<uint8_t> z_vec(z_bytes.begin(), z_bytes.end());
598 const stdlib::byte_array<Builder> hashed_message_ct(&builder, z_vec);
599
600 const stdlib::bool_t<Builder> signature_result =
601 stdlib::ecdsa_verify_signature<Builder, Curve, FqStdlib, FrStdlib, G1Stdlib>(
602 hashed_message_ct, public_key_ct, sig_ct);
603
604 bool stdlib_verification = signature_result.get_value();
605
606 const bool circuit_valid = CircuitChecker::check(builder);
607 ASSERT_TRUE(circuit_valid);
608
609 EXPECT_EQ(native_verification, stdlib_verification);
610}
611
612TEST(EcdsaTests, Secp256r1NafOverflowRegression)
613{
615
616 using FqNative = Curve::fq;
617 using G1Native = Curve::g1;
618
619 using Builder = Curve::Builder;
620 using FrStdlib = Curve::bigfr_ct;
621 using FqStdlib = Curve::fq_ct;
622 using G1Stdlib = Curve::g1_bigfr_ct;
623
624 const std::array<uint8_t, 32> pub_x_bytes = { 0xbd, 0xae, 0xdd, 0xff, 0x80, 0x69, 0x8b, 0xd0, 0xb5, 0xdb, 0x79,
625 0x10, 0xe1, 0xc6, 0x56, 0x9d, 0xc3, 0x4e, 0x77, 0x3b, 0xda, 0x69,
626 0x5e, 0x61, 0x5c, 0x87, 0xf5, 0x4e, 0x6a, 0x70, 0x7e, 0xd6 };
627 const std::array<uint8_t, 32> pub_y_bytes = { 0xc1, 0xb3, 0x69, 0x9c, 0x7e, 0xea, 0x97, 0xbe, 0x5e, 0x52, 0x3d,
628 0x47, 0x5c, 0x5f, 0x72, 0x00, 0x97, 0x2c, 0x61, 0x23, 0xf2, 0xcd,
629 0x3d, 0x59, 0x33, 0x54, 0xe7, 0x4d, 0x35, 0xd1, 0x85, 0x11 };
630 const std::array<uint8_t, 32> r_bytes = { 0x4d, 0xc6, 0x6f, 0x06, 0x26, 0x12, 0x5d, 0x49, 0xb5, 0xa7, 0x7d,
631 0x36, 0xc3, 0xf9, 0x7e, 0x9b, 0xb8, 0x29, 0x48, 0xf2, 0xbd, 0xdc,
632 0xf8, 0x43, 0xe5, 0xee, 0x13, 0x3c, 0xc8, 0x96, 0xf1, 0xd8 };
633 const std::array<uint8_t, 32> s_bytes = { 0x52, 0x59, 0x33, 0x34, 0x92, 0xf6, 0x68, 0x42, 0x1a, 0xe0, 0x63,
634 0x3f, 0x2d, 0x16, 0x92, 0x4b, 0x9f, 0x3b, 0xe9, 0x36, 0xee, 0xd3,
635 0xf1, 0x96, 0x28, 0x6e, 0x0a, 0x57, 0x5d, 0xe2, 0x9f, 0xb7 };
636 const std::array<uint8_t, 32> z_bytes = { 0x93, 0xd5, 0x0c, 0x3d, 0xd6, 0xd6, 0xc3, 0xf7, 0xf7, 0x55, 0x0e,
637 0x69, 0x76, 0x40, 0x2b, 0x89, 0xaa, 0xf2, 0xd6, 0x8a, 0x7a, 0x94,
638 0x80, 0x44, 0x69, 0xaa, 0x69, 0x03, 0x15, 0xb8, 0x64, 0x31 };
639
640 const FqNative pub_x = FqNative::serialize_from_buffer(pub_x_bytes.data());
641 const FqNative pub_y = FqNative::serialize_from_buffer(pub_y_bytes.data());
642 const typename G1Native::affine_element public_key_native(pub_x, pub_y);
643 ASSERT_TRUE(public_key_native.on_curve()) << "Public key must be on curve";
644
646 const G1Stdlib public_key_ct = G1Stdlib::from_witness(&builder, public_key_native);
647
648 const std::vector<uint8_t> r_vec(r_bytes.begin(), r_bytes.end());
649 const std::vector<uint8_t> s_vec(s_bytes.begin(), s_bytes.end());
652
653 const std::vector<uint8_t> z_vec(z_bytes.begin(), z_bytes.end());
654 const stdlib::byte_array<Builder> hashed_message_ct(&builder, z_vec);
655
656 const stdlib::bool_t<Builder> signature_result =
657 stdlib::ecdsa_verify_signature<Builder, Curve, FqStdlib, FrStdlib, G1Stdlib>(
658 hashed_message_ct, public_key_ct, sig_ct);
659
660 // Verification should succeed for this valid signature
661 EXPECT_TRUE(signature_result.get_value());
662
663 const bool circuit_valid = CircuitChecker::check(builder);
664 ASSERT_TRUE(circuit_valid);
665}
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:83
#define BB_DISABLE_ASSERTS()
Definition assert.hpp:33
size_t ecdsa_verification_circuit(Builder &builder, const stdlib::byte_array< Builder > &hashed_message, const ecdsa_key_pair< FrNative, G1Native > &account, const ecdsa_signature &signature, const bool signature_verification_result, const bool circuit_checker_result, const std::string failure_msg, const TamperingMode mode)
Curve::bigfr_ct Fr
Curve::g1_bigfr_ct G1
static constexpr FrNative private_key
Curve::fr FrNative
std::string tampering(std::string message_string, ecdsa_key_pair< FrNative, G1Native > &account, ecdsa_signature &signature, TamperingMode mode)
std::conditional_t< Curve::type==bb::CurveType::SECP256K1, bb::curve::SECP256K1, bb::curve::SECP256R1 > CurveType
std::pair< ecdsa_key_pair< FrNative, G1Native >, ecdsa_signature > generate_dummy_ecdsa_data(std::string message_string, bool random_signature)
size_t test_verify_signature(bool random_signature, TamperingMode mode)
Curve::fq FqNative
void test_wycherproof(std::vector< stdlib::WycherproofTest< CurveType > > tests)
Construct tests based on data fetched from the Wycherproof project.
Curve::fq_ct Fq
Curve::Builder Builder
Curve::bool_ct bool_t
std::pair< G1, stdlib::ecdsa_signature< Builder > > create_stdlib_ecdsa_data(Builder &builder, const ecdsa_key_pair< FrNative, G1Native > &account, const ecdsa_signature &signature, const TamperingMode mode)
Curve::g1 G1Native
static bool check(const Builder &circuit)
Check the witness satisifies the circuit.
constexpr uint256_t slice(uint64_t start, uint64_t end) const
Implements boolean logic in-circuit.
Definition bool.hpp:60
bool get_value() const
Definition bool.hpp:125
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:433
Represents a dynamic array of bytes in-circuit.
#define info(...)
Definition log.hpp:93
void benchmark_info(Args...)
Info used to store circuit statistics during CI/CD with concrete structure. Writes straight to log.
Definition log.hpp:121
AluTraceBuilder builder
Definition alu.test.cpp:124
uint8_t buffer[RANDOM_BUFFER_SIZE]
Definition engine.cpp:48
const std::vector< WycherproofSecp256k1 > secp256k1_tests
Test for Secp256k1 ECDSA signatures taken from the Wycherproof project.
const std::vector< WycherproofSecp256r1 > secp256r1_tests
Test for Secp256r1 ECDSA signatures taken from the Wycherproof project.
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
TYPED_TEST_SUITE(CommitmentKeyTest, Curves)
field< Bn254FrParams > fr
Definition fr.hpp:174
TYPED_TEST(CommitmentKeyTest, CommitToZeroPoly)
TEST(BoomerangMegaCircuitBuilder, BasicCircuit)
@ SECP256K1
Definition types.hpp:10
::testing::Types< curve::BN254, curve::Grumpkin > Curves
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
static auto hash(const B &message)
Definition hashers.hpp:36
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 field one()
static constexpr field zero()