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::ScalarFieldNative;
26 using FqNative = Curve::BaseFieldNative;
27 using G1Native = Curve::GroupNative;
28
29 // Stdlib types
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 void tampering(std::string message_string,
74 ecdsa_signature& signature,
76 {
77 switch (mode) {
79 // Invalidate the circuit by passing a public key with x >= q
80 // Do nothing here, tampering happens in circuit
81 break;
82 }
84 // Invalidate the circuit by passing a public key with y >= q
85 // Do nothing here, tampering happens in circuit
86 break;
87 }
89 // Invalidate the signature by changing r.
90 FrNative r = FrNative::serialize_from_buffer(&signature.r[0]);
91 r += FrNative::one();
92
93 FrNative::serialize_to_buffer(r, &signature.r[0]);
94 break;
95 }
97 // Invalidate the signature by changing s.
98 FrNative s = FrNative::serialize_from_buffer(&signature.s[0]);
99 s += FrNative::one();
100
101 FrNative::serialize_to_buffer(s, &signature.s[0]);
102 break;
103 }
105 // Invalidate the signature by changing s to -s.
106 FrNative s = FrNative::serialize_from_buffer(&signature.s[0]);
107 s = -s;
108
109 FrNative::serialize_to_buffer(s, &signature.s[0]);
110 break;
111 }
113 // Invalidate signature by setting r to 0
114 signature.r = std::array<uint8_t, 32>{};
115 break;
116 }
118 // Invalidate signature by setting s to 0
119 signature.s = std::array<uint8_t, 32>{};
120 break;
121 }
123 // Invalidate the signature by making making u1 * G + u2 * P return the point at infinity
124
125 // Compute H(m)
126 std::vector<uint8_t> buffer;
127 std::ranges::copy(message_string, std::back_inserter(buffer));
128 auto hash = Sha256Hasher::hash(buffer);
129
130 // Override the public key: new public key is (-hash) * r^{-1} * G
131 FrNative fr_hash = FrNative::serialize_from_buffer(&hash[0]);
132 FrNative r = FrNative::serialize_from_buffer(&signature.r[0]);
133 FrNative r_inverse = r.invert();
134 FrNative modified_private_key = r_inverse * (-fr_hash);
135 account.public_key = G1Native::one * modified_private_key;
136
137 // Verify that the result is the point at infinity
138 auto P = G1Native::one * fr_hash + account.public_key * r;
139 BB_ASSERT_EQ(P.is_point_at_infinity(), true);
140 break;
141 }
143 // Invalidate the circuit by passing a public key which is not on the curve
144 account.public_key.x = account.public_key.y;
145 BB_ASSERT_EQ(account.public_key.on_curve(), false);
146 break;
147 }
149 // Invalidate the circuit by passing a public key which is not on the curve
150 account.public_key.self_set_infinity();
151 BB_ASSERT_EQ(account.public_key.is_point_at_infinity(), true);
152 break;
153 }
155 break;
156 }
157
158 // Natively verify that the tampering was successfull
159 bool is_signature_valid = ecdsa_verify_signature<Sha256Hasher, FqNative, FrNative, G1Native>(
160 message_string, account.public_key, signature);
162 // In these tampering modes nothing has changed and the tampering happens in circuit, so we override the
163 // result and set it to false
164 is_signature_valid = false;
165 }
166
167 bool expected = mode == TamperingMode::None;
168 BB_ASSERT_EQ(is_signature_valid,
169 expected,
170 "Signature verification returned a different result from the expected one. If the signature was "
171 "randomly generated, there is a (very) small chance this is not a bug.");
172 }
173
177 const ecdsa_signature& signature,
178 const TamperingMode mode)
179 {
180 // We construct the point via its x,y-coordinates with an explicit infinity flag.
181 // This avoids:
182 // 1. The on-curve check of G1::from_witness (so we can test invalid points)
183 // 2. The expensive infinity auto-detection of the 2-arg constructor
184 Fq x;
185 Fq y;
188 for (size_t idx = 0; idx < 32; idx++) {
189 stdlib::byte_array<Builder> to_write(stdlib::field_t<Builder>(0xff), /*length*/ 1);
190 x_bytes.write(to_write);
191 }
192 x = Fq(x_bytes);
193 y = Fq::from_witness(&builder, account.public_key.y);
196 for (size_t idx = 0; idx < 32; idx++) {
197 stdlib::byte_array<Builder> to_write(stdlib::field_t<Builder>(0xff), /*length*/ 1);
198 y_bytes.write(to_write);
199 }
200 x = Fq::from_witness(&builder, account.public_key.x);
201 y = Fq(y_bytes);
202 } else {
203 x = Fq::from_witness(&builder, account.public_key.x);
204 y = Fq::from_witness(&builder, account.public_key.y);
205 }
206
208 // Override coordinates to (0, 0) for infinity
209 x = Fq::from_witness(&builder, FqNative::zero());
210 y = Fq::from_witness(&builder, FqNative::zero());
211 }
212
213 // Create explicit infinity flag witness from the native point's infinity status
214 bool_t is_infinity(
215 stdlib::witness_t<Builder>(&builder, account.public_key.is_point_at_infinity() ? fr::one() : fr::zero()),
216 false);
217
218 // Use the test accessor to create element with explicit infinity flag (avoids expensive auto-detection)
219 G1 pub_key = stdlib::element_default::element_test_accessor::
220 create_element_with_explicit_infinity<Builder, Fq, Fr, G1Native>(
221 x, y, is_infinity, /*assert_on_curve=*/false);
222 pub_key.set_free_witness_tag();
223 BB_ASSERT_EQ(pub_key.is_point_at_infinity().get_value(), account.public_key.is_point_at_infinity());
224
225 std::vector<uint8_t> rr(signature.r.begin(), signature.r.end());
226 std::vector<uint8_t> ss(signature.s.begin(), signature.s.end());
227
230
231 return { pub_key, sig };
232 }
233
235 const stdlib::byte_array<Builder>& hashed_message,
237 const ecdsa_signature& signature,
238 const bool signature_verification_result,
239 const bool expected_circuit_result,
240 const TamperingMode mode)
241
242 {
243 auto [public_key, sig] = create_stdlib_ecdsa_data(builder, account, signature, mode);
244
245 // Verify signature
246 stdlib::bool_t<Builder> signature_result =
247 stdlib::ecdsa_verify_signature<Builder, Curve, Fq, Fr, G1>(hashed_message, public_key, sig);
248
249 // Enforce verification returns the expected result
250 signature_result.assert_equal(stdlib::bool_t<Builder>(signature_verification_result));
251
252 // Check native values
253 EXPECT_EQ(signature_result.get_value(), signature_verification_result);
254
255 // Circuit checker
256 bool is_circuit_satisfied = CircuitChecker::check(builder);
257 EXPECT_EQ(is_circuit_satisfied, expected_circuit_result);
258 }
259
260 void test_verify_signature(bool random_signature, TamperingMode mode)
261 {
262 // Map tampering mode to signature verification result
263 bool signature_verification_result = (mode == TamperingMode::None);
264
265 std::string message_string = "Goblin";
266 std::vector<uint8_t> message_bytes(message_string.begin(), message_string.end());
267 std::array<uint8_t, 32> hashed_message_bytes_ = Sha256Hasher::hash(message_bytes);
268 std::vector<uint8_t> hashed_message_bytes;
269 hashed_message_bytes.reserve(32);
270 for (auto byte : hashed_message_bytes_) {
271 hashed_message_bytes.emplace_back(byte);
272 }
273
274 auto [account, signature] = generate_dummy_ecdsa_data(message_string, /*random_signature=*/random_signature);
275
276 // Tamper with the signature
277 tampering(message_string, account, signature, mode);
278
279 // Create ECDSA verification circuit
281 stdlib::byte_array<Builder> hashed_message(&builder, hashed_message_bytes);
282
283 // ECDSA verification
285 builder, hashed_message, account, signature, signature_verification_result, true, mode);
286 }
287
288 // Test deterministic failure when P = ±G for secp256r1
289 void test_signature_generator(bool is_minus_generator, bool signature_result, bool expected_circuit_result)
290 {
291 std::string message_string = "Goblin";
292 std::vector<uint8_t> message_bytes(message_string.begin(), message_string.end());
293 std::array<uint8_t, 32> hashed_message_bytes_ = Sha256Hasher::hash(message_bytes);
294 std::vector<uint8_t> hashed_message_bytes;
295 hashed_message_bytes.reserve(32);
296 for (auto byte : hashed_message_bytes_) {
297 hashed_message_bytes.emplace_back(byte);
298 }
299
300 // Generate a signature with P = ±G
302
303 account.private_key = is_minus_generator ? FrNative(-1) : FrNative(1);
304 account.public_key = G1Native::one * account.private_key;
305
306 ecdsa_signature signature =
307 ecdsa_construct_signature<Sha256Hasher, FqNative, FrNative, G1Native>(message_string, account);
308
309 // Create ECDSA verification circuit
311 stdlib::byte_array<Builder> hashed_message(&builder, hashed_message_bytes);
312
313 // ECDSA verification
315 hashed_message,
316 account,
317 signature,
318 signature_result,
319 expected_circuit_result,
321 }
322
323 // This test covers a specific behaviour of the implementation. If either the x or y coordinate of the public key
324 // are bigger than the base field modulus, the ECDSA verification algorithm substitutes the public key with 2G to
325 // avoid circuit failures. This means that an attacker could craft a valid signature for 2G and submit such
326 // signature together with a public key with x coordinate overflowing. The ECDSA equation would then be satisfied
327 // because of the internal substitution. The attack should not be succesful because the result of the ECDSA
328 // verification algorithm takes into account whether either the x or y coordinate overflowed.
330 {
331 std::string message_string = "Goblin";
332 std::vector<uint8_t> message_bytes(message_string.begin(), message_string.end());
333 std::array<uint8_t, 32> hashed_message_bytes_ = Sha256Hasher::hash(message_bytes);
334 std::vector<uint8_t> hashed_message_bytes;
335 hashed_message_bytes.reserve(32);
336 for (auto byte : hashed_message_bytes_) {
337 hashed_message_bytes.emplace_back(byte);
338 }
339
340 // Generate a signature with P = 2G
342
343 account.private_key = FrNative(2);
344 account.public_key = G1Native::one * account.private_key;
345
346 ecdsa_signature signature =
347 ecdsa_construct_signature<Sha256Hasher, FqNative, FrNative, G1Native>(message_string, account);
348
349 // Tamper with the signature by making the x coordinate overflow
350 tampering(message_string, account, signature, TamperingMode::XCoordinateOverflow);
351
352 // Create ECDSA verification circuit
354 stdlib::byte_array<Builder> hashed_message(&builder, hashed_message_bytes);
355
356 // ECDSA verification
358 builder, hashed_message, account, signature, false, true, TamperingMode::XCoordinateOverflow);
359 }
360
367 {
368 for (auto test : tests) {
369 // Keypair
371 account.private_key = FrNative::one(); // Dummy value, unused
372 account.public_key = typename G1Native::affine_element(test.x, test.y);
373
374 // Signature
375 std::array<uint8_t, 32> r;
376 std::array<uint8_t, 32> s;
377 uint8_t v = 0; // Dummy value, unused
378 FrNative::serialize_to_buffer(test.r, &r[0]);
379 FrNative::serialize_to_buffer(test.s, &s[0]);
380
381 // Hashed message
382 std::array<uint8_t, 32> hashed_message_bytes_ = Sha256Hasher::hash(test.message);
383 std::vector<uint8_t> hashed_message_bytes;
384 hashed_message_bytes.reserve(32);
385 for (auto byte : hashed_message_bytes_) {
386 hashed_message_bytes.emplace_back(byte);
387 }
388
389 // Create ECDSA verification circuit
391 stdlib::byte_array<Builder> hashed_message(&builder, hashed_message_bytes);
392
393 // ECDSA verification
395 hashed_message,
396 account,
397 { r, s, v },
398 test.is_valid_signature,
399 test.is_circuit_satisfied,
401 }
402 }
403};
404
405using Curves = testing::Types<stdlib::secp256k1<UltraCircuitBuilder>,
409
411
412TYPED_TEST(EcdsaTests, VerifyRandomSignature)
413{
414 TestFixture::test_verify_signature(/*random_signature=*/true, TestFixture::TamperingMode::None);
415}
416
417TYPED_TEST(EcdsaTests, VerifySignature)
418{
419 TestFixture::test_verify_signature(/*random_signature=*/false, TestFixture::TamperingMode::None);
420}
421
422TYPED_TEST(EcdsaTests, XCoordinateOverflow)
423{
425 TestFixture::test_verify_signature(/*random_signature=*/false, TestFixture::TamperingMode::XCoordinateOverflow);
426}
427
428TYPED_TEST(EcdsaTests, YCoordinateOverflow)
429{
431 TestFixture::test_verify_signature(/*random_signature=*/false, TestFixture::TamperingMode::YCoordinateOverflow);
432}
433
435{
436 TestFixture::test_verify_signature(/*random_signature=*/false, TestFixture::TamperingMode::InvalidR);
437}
438
440{
441 TestFixture::test_verify_signature(/*random_signature=*/false, TestFixture::TamperingMode::InvalidS);
442}
443
445{
446 TestFixture::test_verify_signature(/*random_signature=*/false, TestFixture::TamperingMode::HighS);
447}
448
450{
451 TestFixture::test_verify_signature(/*random_signature=*/false, TestFixture::TamperingMode::ZeroR);
452}
453
455{
456 TestFixture::test_verify_signature(/*random_signature=*/false, TestFixture::TamperingMode::ZeroS);
457}
458
459TYPED_TEST(EcdsaTests, InvalidPubKey)
460{
461 TestFixture::test_verify_signature(/*random_signature=*/false, TestFixture::TamperingMode::InvalidPubKey);
462}
463
464TYPED_TEST(EcdsaTests, InfinityPubKey)
465{
466 TestFixture::test_verify_signature(/*random_signature=*/false, TestFixture::TamperingMode::InfinityPubKey);
467}
468
469TYPED_TEST(EcdsaTests, InfinityScalarMul)
470{
471 TestFixture::test_verify_signature(/*random_signature=*/false, TestFixture::TamperingMode::InfinityScalarMul);
472}
473
475{
476 if constexpr (TypeParam::type == bb::CurveType::SECP256K1) {
477 TestFixture::test_wycherproof(stdlib::secp256k1_tests);
478 } else {
479 TestFixture::test_wycherproof(stdlib::secp256r1_tests);
480 }
481}
482
483TYPED_TEST(EcdsaTests, SignatureDoubleGenerator)
484{
485 TestFixture::test_signature_double_generator();
486}
487
488TYPED_TEST(EcdsaTests, SignatureGenerator)
489{
490 // Circuit works for Secp256k1, fails for Secp256r1
491 bool signature_result = (TypeParam::type == bb::CurveType::SECP256K1);
492 bool expected_circuit_result = (TypeParam::type == bb::CurveType::SECP256K1);
493
494 TestFixture::test_signature_generator(false, signature_result, expected_circuit_result);
495 TestFixture::test_signature_generator(true, signature_result, expected_circuit_result);
496}
497
498TEST(EcdsaTests, Secp256k1PointAtInfinityRegression)
499{
501
502 using FqNative = Curve::BaseFieldNative;
503 using FrNative = Curve::ScalarFieldNative;
504 using G1Native = Curve::GroupNative;
505
506 using Builder = Curve::Builder;
507 using FrStdlib = Curve::ScalarField;
508 using FqStdlib = Curve::BaseField;
509 using G1Stdlib = Curve::Group;
510
511 // Attack parameters for P = 5*G
512 // These are crafted so that u1*G + u2*P = O (point at infinity)
513 const std::array<uint8_t, 32> pub_x_bytes = { 0x2f, 0x8b, 0xde, 0x4d, 0x1a, 0x07, 0x20, 0x93, 0x55, 0xb4, 0xa7,
514 0x25, 0x0a, 0x5c, 0x51, 0x28, 0xe8, 0x8b, 0x84, 0xbd, 0xdc, 0x61,
515 0x9a, 0xb7, 0xcb, 0xa8, 0xd5, 0x69, 0xb2, 0x40, 0xef, 0xe4 };
516 const std::array<uint8_t, 32> pub_y_bytes = { 0xd8, 0xac, 0x22, 0x26, 0x36, 0xe5, 0xe3, 0xd6, 0xd4, 0xdb, 0xa9,
517 0xdd, 0xa6, 0xc9, 0xc4, 0x26, 0xf7, 0x88, 0x27, 0x1b, 0xab, 0x0d,
518 0x68, 0x40, 0xdc, 0xa8, 0x7d, 0x3a, 0xa6, 0xac, 0x62, 0xd6 };
519 const std::array<uint8_t, 32> r_bytes = { 0xa8, 0x41, 0x94, 0xc3, 0x71, 0xc6, 0x7b, 0xa2, 0x59, 0x2f, 0x59,
520 0xc6, 0x20, 0xad, 0x30, 0x4c, 0xb7, 0x6d, 0x7a, 0x88, 0x25, 0x6b,
521 0xb5, 0x0d, 0xc4, 0x1c, 0x66, 0x57, 0x44, 0xbf, 0x78, 0x61 };
522 const std::array<uint8_t, 32> s_bytes = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
523 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
524 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64 };
525 const std::array<uint8_t, 32> z_bytes = { 0xb6, 0xb8, 0x18, 0x2e, 0xc7, 0x1f, 0x95, 0xd4, 0x42, 0x13, 0x3f,
526 0x21, 0x5c, 0x9e, 0x0e, 0x7b, 0x55, 0x98, 0x0e, 0xf2, 0x02, 0x07,
527 0xf7, 0xaa, 0x2a, 0xbb, 0x7a, 0x7e, 0xe9, 0x1b, 0xab, 0x1f };
528
529 FqNative pub_x = FqNative::serialize_from_buffer(pub_x_bytes.data());
530 FqNative pub_y = FqNative::serialize_from_buffer(pub_y_bytes.data());
531 typename G1Native::affine_element public_key_native(pub_x, pub_y);
532 ASSERT_TRUE(public_key_native.on_curve()) << "Public key must be on curve";
533
534 // Native verification
535 const std::string message_string(z_bytes.begin(), z_bytes.end());
536 ecdsa_signature sig;
537 sig.r = r_bytes;
538 sig.s = s_bytes;
539 sig.v = 27;
540 bool native_verification =
541 ecdsa_verify_signature<Sha256Hasher, FqNative, FrNative, G1Native>(message_string, public_key_native, sig);
542
543 bool stdlib_verification;
544 {
546
547 const G1Stdlib public_key_ct = G1Stdlib::from_witness(&builder, public_key_native);
548
549 const std::vector<uint8_t> r_vec(r_bytes.begin(), r_bytes.end());
550 const std::vector<uint8_t> s_vec(s_bytes.begin(), s_bytes.end());
553
554 const std::vector<uint8_t> z_vec(z_bytes.begin(), z_bytes.end());
555 const stdlib::byte_array<Builder> hashed_message_ct(&builder, z_vec);
556
557 const stdlib::bool_t<Builder> signature_result =
558 stdlib::ecdsa_verify_signature<Builder, Curve, FqStdlib, FrStdlib, G1Stdlib>(
559 hashed_message_ct, public_key_ct, sig_ct);
560
561 stdlib_verification = signature_result.get_value();
562
563 const bool circuit_valid = CircuitChecker::check(builder);
564
565 // Circuit should be satisfied but signature should be rejected
566 ASSERT_TRUE(circuit_valid);
567 }
568
569 // Both native and stdlib should reject this invalid signature
570 EXPECT_FALSE(native_verification);
571 EXPECT_FALSE(stdlib_verification);
572 EXPECT_EQ(native_verification, stdlib_verification);
573}
574
575TEST(EcdsaTests, Secp256r1NativeStdlibDiscrepancyRegression)
576{
578
579 using FqNative = Curve::BaseFieldNative;
580 using FrNative = Curve::ScalarFieldNative;
581 using G1Native = Curve::GroupNative;
582
583 using Builder = Curve::Builder;
584 using FrStdlib = Curve::ScalarField;
585 using FqStdlib = Curve::BaseField;
586 using G1Stdlib = Curve::Group;
587
588 const std::array<uint8_t, 32> pub_x_bytes = { 0x79, 0x9f, 0x2a, 0xba, 0xfa, 0x27, 0x16, 0x4b, 0x09, 0x50, 0xf2,
589 0xc8, 0x82, 0xf0, 0xd1, 0x67, 0xe1, 0xd2, 0x16, 0x74, 0x87, 0xd5,
590 0x2e, 0xa7, 0x23, 0x0b, 0x5d, 0x96, 0xc2, 0xa8, 0x74, 0x00 };
591 const std::array<uint8_t, 32> pub_y_bytes = { 0xda, 0xa5, 0x79, 0xf4, 0xf1, 0x61, 0xe9, 0xdc, 0xa1, 0xa1, 0x34,
592 0x35, 0x92, 0x16, 0xb9, 0x35, 0xea, 0xd0, 0x97, 0x2d, 0x76, 0x3f,
593 0xe3, 0x33, 0xc7, 0x12, 0xee, 0x8d, 0x18, 0x4b, 0xd8, 0x11 };
594 const std::array<uint8_t, 32> r_bytes = { 0xb1, 0x99, 0xa1, 0x62, 0x72, 0x66, 0x61, 0xba, 0x23, 0x3c, 0xd6,
595 0xc6, 0x6e, 0x99, 0x0b, 0x01, 0x2e, 0x1e, 0x76, 0x04, 0xb1, 0x1f,
596 0x76, 0x19, 0x3b, 0x2a, 0xf5, 0xca, 0x36, 0xc1, 0x01, 0x76 };
597 const std::array<uint8_t, 32> s_bytes = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
598 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
599 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05 };
600 const std::array<uint8_t, 32> z_bytes = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
601 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
602 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05 };
603
604 const FqNative pub_x = FqNative::serialize_from_buffer(pub_x_bytes.data());
605 const FqNative pub_y = FqNative::serialize_from_buffer(pub_y_bytes.data());
606 const typename G1Native::affine_element public_key_native(pub_x, pub_y);
607 ASSERT_TRUE(public_key_native.on_curve()) << "Public key must be on curve";
608
609 // Native verification
610 const std::string message_string(z_bytes.begin(), z_bytes.end());
611 ecdsa_signature sig;
612 sig.r = r_bytes;
613 sig.s = s_bytes;
614 sig.v = 27;
615 bool native_verification =
616 ecdsa_verify_signature<Sha256Hasher, FqNative, FrNative, G1Native>(message_string, public_key_native, sig);
617
618 // Stdlib verification
620 const G1Stdlib public_key_ct = G1Stdlib::from_witness(&builder, public_key_native);
621
622 const std::vector<uint8_t> r_vec(r_bytes.begin(), r_bytes.end());
623 const std::vector<uint8_t> s_vec(s_bytes.begin(), s_bytes.end());
626
627 const std::vector<uint8_t> z_vec(z_bytes.begin(), z_bytes.end());
628 const stdlib::byte_array<Builder> hashed_message_ct(&builder, z_vec);
629
630 const stdlib::bool_t<Builder> signature_result =
631 stdlib::ecdsa_verify_signature<Builder, Curve, FqStdlib, FrStdlib, G1Stdlib>(
632 hashed_message_ct, public_key_ct, sig_ct);
633
634 bool stdlib_verification = signature_result.get_value();
635
636 const bool circuit_valid = CircuitChecker::check(builder);
637 ASSERT_TRUE(circuit_valid);
638
639 EXPECT_EQ(native_verification, stdlib_verification);
640}
641
642TEST(EcdsaTests, Secp256r1NafOverflowRegression)
643{
645
646 using FqNative = Curve::BaseFieldNative;
647 using G1Native = Curve::GroupNative;
648
649 using Builder = Curve::Builder;
650 using FrStdlib = Curve::ScalarField;
651 using FqStdlib = Curve::BaseField;
652 using G1Stdlib = Curve::Group;
653
654 const std::array<uint8_t, 32> pub_x_bytes = { 0xbd, 0xae, 0xdd, 0xff, 0x80, 0x69, 0x8b, 0xd0, 0xb5, 0xdb, 0x79,
655 0x10, 0xe1, 0xc6, 0x56, 0x9d, 0xc3, 0x4e, 0x77, 0x3b, 0xda, 0x69,
656 0x5e, 0x61, 0x5c, 0x87, 0xf5, 0x4e, 0x6a, 0x70, 0x7e, 0xd6 };
657 const std::array<uint8_t, 32> pub_y_bytes = { 0xc1, 0xb3, 0x69, 0x9c, 0x7e, 0xea, 0x97, 0xbe, 0x5e, 0x52, 0x3d,
658 0x47, 0x5c, 0x5f, 0x72, 0x00, 0x97, 0x2c, 0x61, 0x23, 0xf2, 0xcd,
659 0x3d, 0x59, 0x33, 0x54, 0xe7, 0x4d, 0x35, 0xd1, 0x85, 0x11 };
660 const std::array<uint8_t, 32> r_bytes = { 0x4d, 0xc6, 0x6f, 0x06, 0x26, 0x12, 0x5d, 0x49, 0xb5, 0xa7, 0x7d,
661 0x36, 0xc3, 0xf9, 0x7e, 0x9b, 0xb8, 0x29, 0x48, 0xf2, 0xbd, 0xdc,
662 0xf8, 0x43, 0xe5, 0xee, 0x13, 0x3c, 0xc8, 0x96, 0xf1, 0xd8 };
663 const std::array<uint8_t, 32> s_bytes = { 0x52, 0x59, 0x33, 0x34, 0x92, 0xf6, 0x68, 0x42, 0x1a, 0xe0, 0x63,
664 0x3f, 0x2d, 0x16, 0x92, 0x4b, 0x9f, 0x3b, 0xe9, 0x36, 0xee, 0xd3,
665 0xf1, 0x96, 0x28, 0x6e, 0x0a, 0x57, 0x5d, 0xe2, 0x9f, 0xb7 };
666 const std::array<uint8_t, 32> z_bytes = { 0x93, 0xd5, 0x0c, 0x3d, 0xd6, 0xd6, 0xc3, 0xf7, 0xf7, 0x55, 0x0e,
667 0x69, 0x76, 0x40, 0x2b, 0x89, 0xaa, 0xf2, 0xd6, 0x8a, 0x7a, 0x94,
668 0x80, 0x44, 0x69, 0xaa, 0x69, 0x03, 0x15, 0xb8, 0x64, 0x31 };
669
670 const FqNative pub_x = FqNative::serialize_from_buffer(pub_x_bytes.data());
671 const FqNative pub_y = FqNative::serialize_from_buffer(pub_y_bytes.data());
672 const typename G1Native::affine_element public_key_native(pub_x, pub_y);
673 ASSERT_TRUE(public_key_native.on_curve()) << "Public key must be on curve";
674
676 const G1Stdlib public_key_ct = G1Stdlib::from_witness(&builder, public_key_native);
677
678 const std::vector<uint8_t> r_vec(r_bytes.begin(), r_bytes.end());
679 const std::vector<uint8_t> s_vec(s_bytes.begin(), s_bytes.end());
682
683 const std::vector<uint8_t> z_vec(z_bytes.begin(), z_bytes.end());
684 const stdlib::byte_array<Builder> hashed_message_ct(&builder, z_vec);
685
686 const stdlib::bool_t<Builder> signature_result =
687 stdlib::ecdsa_verify_signature<Builder, Curve, FqStdlib, FrStdlib, G1Stdlib>(
688 hashed_message_ct, public_key_ct, sig_ct);
689
690 // Verification should succeed for this valid signature
691 EXPECT_TRUE(signature_result.get_value());
692
693 const bool circuit_valid = CircuitChecker::check(builder);
694 ASSERT_TRUE(circuit_valid);
695}
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:83
#define BB_DISABLE_ASSERTS()
Definition assert.hpp:33
Curve::ScalarFieldNative FrNative
Curve::BaseField Fq
void tampering(std::string message_string, ecdsa_key_pair< FrNative, G1Native > &account, ecdsa_signature &signature, TamperingMode mode)
void 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 expected_circuit_result, const TamperingMode mode)
void test_signature_double_generator()
static constexpr FrNative private_key
Curve::BaseFieldNative FqNative
void test_verify_signature(bool random_signature, TamperingMode mode)
Curve::GroupNative G1Native
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)
void test_signature_generator(bool is_minus_generator, bool signature_result, bool expected_circuit_result)
Curve::Group G1
void test_wycherproof(std::vector< stdlib::WycherproofTest< CurveType > > tests)
Construct tests based on data fetched from the Wycherproof project.
Curve::Builder Builder
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)
static bool check(const Builder &circuit)
Check the witness satisifies the circuit.
typename grumpkin::g1 Group
Definition grumpkin.hpp:63
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.
byte_array & write(byte_array const &other)
Appends the contents of another byte_array (other) to the end of this one.
static byte_array constant_padding(Builder *parent_context, size_t num_bytes, uint8_t value=0)
#define info(...)
Definition log.hpp:93
AluTraceBuilder builder
Definition alu.test.cpp:124
std::unique_ptr< uint8_t[]> buffer
Definition engine.cpp:50
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)
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:24
std::array< uint8_t, 32 > r
Definition ecdsa.hpp:31
std::array< uint8_t, 32 > s
Definition ecdsa.hpp:32
static constexpr field one()
static constexpr field zero()