Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
ecdsa_constraints.test.cpp
Go to the documentation of this file.
2#include "acir_format.hpp"
10
11#include <algorithm>
12#include <gtest/gtest.h>
13#include <vector>
14
15using namespace bb;
16using namespace bb::crypto;
17using namespace acir_format;
18
19template <class Curve> class EcdsaTestingFunctions {
20 public:
21 using Builder = Curve::Builder;
22 using FrNative = Curve::fr;
23 using FqNative = Curve::fq;
24 using G1Native = Curve::g1;
26
28 public:
29 enum class Target : uint8_t {
30 None,
31 HashIsNotAByteArray, // Set one element of the hash > 255
32 ZeroR, // Set R=0 (tests ECDSA validation)
33 ZeroS, // Set S=0 (tests ECDSA validation)
34 HighS, // Set S=high (tests malleability protection)
35 P, // Invalidate public key
36 Result // Invalid signature with claimed valid result
37 };
38
44
45 static std::vector<std::string> get_labels()
46 {
47 return { "None", "Hash is not a byte array", "Zero R", "Zero S", "High S", "Public key", "Result" };
48 }
49 };
50
51 // Reproducible test
52 static constexpr FrNative private_key =
53 FrNative("0xd67abee717b3fc725adf59e2cc8cd916435c348b277dd814a34e3ceb279436c2");
54
55 static void invalidate_witness(EcdsaConstraint& ecdsa_constraints,
56 WitnessVector& witness_values,
57 const InvalidWitness::Target& invalid_witness_target)
58 {
59 // For most ECDSA invalidation cases, we set result=0 to ensure that the failure mode caught by the test is
60 // specific to the particular case being tested, not just simple verification failure.
61 // For the "Result" case we test the mismatch between actual and claimed result.
62 if (invalid_witness_target != InvalidWitness::Target::None &&
63 invalid_witness_target != InvalidWitness::Target::Result) {
64 witness_values[ecdsa_constraints.result] = bb::fr(0);
65 }
66
67 switch (invalid_witness_target) {
69 // Set all bytes of hash to 256 (invalid as it doesn't fit in one byte)
70 for (size_t idx = 0; idx < 32; idx++) {
71 witness_values[ecdsa_constraints.hashed_message[idx]] = bb::fr(256);
72 };
73 break;
75 // Set r = 0 (invalid ECDSA signature component)
76 for (size_t idx = 0; idx < 32; idx++) {
77 witness_values[ecdsa_constraints.signature[idx]] = bb::fr(0);
78 };
79 break;
81 // Set s = 0 (tests ECDSA validation: s must be non-zero)
82 for (size_t idx = 32; idx < 64; idx++) {
83 witness_values[ecdsa_constraints.signature[idx]] = bb::fr(0);
84 };
85 break;
87 // Set s = high value (tests signature malleability protection)
88 for (size_t idx = 32; idx < 64; idx++) {
89 witness_values[ecdsa_constraints.signature[idx]] = bb::fr(255);
90 };
91 break;
93 // Invalidate public key
94 witness_values[ecdsa_constraints.pub_x_indices[0]] += bb::fr(1);
95 break;
97 // Test enforcement of verification result: tamper signature but claim it's valid
98 witness_values[ecdsa_constraints.signature[31]] = bb::fr(0);
99 witness_values[ecdsa_constraints.result] = bb::fr(1);
100 break;
102 break;
103 }
104 }
105
109 static void generate_constraints(EcdsaConstraint& ecdsa_constraint, WitnessVector& witness_values)
110 {
111 std::string message_string = "Instructions unclear, ask again later.";
112
113 // Hash the message
114 std::vector<uint8_t> message_buffer(message_string.begin(), message_string.end());
115 std::array<uint8_t, 32> hashed_message = Sha256Hasher::hash(message_buffer);
116
117 // Generate ECDSA key pair
119 account.private_key = private_key;
120 account.public_key = G1Native::one * account.private_key;
121
122 // Generate signature
123 ecdsa_signature signature =
124 ecdsa_construct_signature<Sha256Hasher, FqNative, FrNative, G1Native>(message_string, account);
125
126 // Serialize public key coordinates into bytes
127 std::array<uint8_t, 32> buffer_x;
128 std::array<uint8_t, 32> buffer_y;
129 FqNative::serialize_to_buffer(account.public_key.x, &buffer_x[0]);
130 FqNative::serialize_to_buffer(account.public_key.y, &buffer_y[0]);
131
132 // Create witness indices and witnesses
133 std::array<uint32_t, 32> hashed_message_indices =
134 add_to_witness_and_track_indices<std::span<uint8_t>, 32>(witness_values, std::span(hashed_message));
135
136 std::array<uint32_t, 32> pub_x_indices =
137 add_to_witness_and_track_indices<std::span<uint8_t>, 32>(witness_values, std::span(buffer_x));
138
139 std::array<uint32_t, 32> pub_y_indices =
140 add_to_witness_and_track_indices<std::span<uint8_t>, 32>(witness_values, std::span(buffer_y));
141
142 std::array<uint32_t, 32> r_indices =
143 add_to_witness_and_track_indices<std::span<uint8_t>, 32>(witness_values, std::span(signature.r));
144
145 std::array<uint32_t, 32> s_indices =
146 add_to_witness_and_track_indices<std::span<uint8_t>, 32>(witness_values, std::span(signature.s));
147
148 uint32_t result_index = add_to_witness_and_track_indices(witness_values, bb::fr(1));
149
150 uint32_t predicate_index = add_to_witness_and_track_indices(witness_values, bb::fr(1));
151
152 // Restructure vectors into array
153 std::array<uint32_t, 64> signature_indices;
154 std::ranges::copy(r_indices, signature_indices.begin());
155 std::ranges::copy(s_indices, signature_indices.begin() + 32);
156
157 ecdsa_constraint = EcdsaConstraint{ .type = Curve::type,
158 .hashed_message = hashed_message_indices,
159 .signature = signature_indices,
160 .pub_x_indices = pub_x_indices,
161 .pub_y_indices = pub_y_indices,
162 .predicate = WitnessOrConstant<bb::fr>::from_index(predicate_index),
163 .result = result_index };
164 }
165};
166
167template <class Curve>
168class EcdsaConstraintsTest : public ::testing::Test, public TestClassWithPredicate<EcdsaTestingFunctions<Curve>> {
169 protected:
171};
172
173using CurveTypes = testing::Types<stdlib::secp256k1<UltraCircuitBuilder>,
177
179
180TYPED_TEST(EcdsaConstraintsTest, GenerateVKFromConstraints)
181{
182 using Flavor =
184 TestFixture::template test_vk_independence<Flavor>();
185}
186
188{
190 TestFixture::test_constant_true(TestFixture::InvalidWitnessTarget::Result);
191}
192
194{
196 TestFixture::test_witness_true(TestFixture::InvalidWitnessTarget::Result);
197}
198
200{
202 TestFixture::test_witness_false();
203}
204
206{
207 // This test is equal to WitnessFalse but also checks that each configuration would have failed if the
208 // predicate were witness true. It can be useful for debugging.
210 TestFixture::test_witness_false_slow();
211}
212
214{
216 [[maybe_unused]] std::vector<std::string> _ = TestFixture::test_invalid_witnesses();
217}
#define BB_DISABLE_ASSERTS()
Definition assert.hpp:32
static constexpr FrNative private_key
static void generate_constraints(EcdsaConstraint &ecdsa_constraint, WitnessVector &witness_values)
Generate valid ECDSA constraint with witness predicate equal to true.
static void invalidate_witness(EcdsaConstraint &ecdsa_constraints, WitnessVector &witness_values, const InvalidWitness::Target &invalid_witness_target)
Test class for ACIR constraints that contain a predicate.
std::vector< bb::fr > WitnessVector
std::vector< uint32_t > add_to_witness_and_track_indices(WitnessVector &witness, const T &input)
Append values to a witness vector and track their indices.
Definition utils.hpp:70
std::filesystem::path bb_crs_path()
void init_file_crs_factory(const std::filesystem::path &path)
TYPED_TEST_SUITE(BoomerangRecursiveVerifierTest, Flavors)
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
field< Bn254FrParams > fr
Definition fr.hpp:174
::testing::Types< curve::BN254, curve::Grumpkin > CurveTypes
TYPED_TEST(ShpleminiTest, CorrectnessOfMultivariateClaimBatching)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
static std::vector< std::string > get_labels()
std::array< uint32_t, 32 > pub_x_indices
std::array< uint32_t, 32 > hashed_message
std::array< uint32_t, 64 > signature
static WitnessOrConstant from_index(uint32_t index)
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