Skip to content

Commit dd1be6e

Browse files
committed
test(bb): prove invalid ECDSA verification returns false
1 parent 615bde9 commit dd1be6e

1 file changed

Lines changed: 70 additions & 3 deletions

File tree

barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_constraints.test.cpp

Lines changed: 70 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,12 @@
66
#include "barretenberg/dsl/acir_format/witness_constant.hpp"
77
#include "barretenberg/stdlib/primitives/curves/secp256k1.hpp"
88
#include "barretenberg/stdlib/primitives/curves/secp256r1.hpp"
9+
#include "barretenberg/ultra_honk/ultra_prover.hpp"
10+
#include "barretenberg/ultra_honk/ultra_verifier.hpp"
911

1012
#include <algorithm>
1113
#include <gtest/gtest.h>
14+
#include <memory>
1215
#include <vector>
1316

1417
using namespace bb;
@@ -31,7 +34,7 @@ template <class Curve> class EcdsaTestingFunctions {
3134
ZeroR, // Set R=0 (tests ECDSA validation)
3235
ZeroS, // Set S=0 (tests ECDSA validation)
3336
HighS, // Set S=high (tests malleability protection)
34-
P, // Invalidate public key
37+
P, // Make public key fail the curve equation
3538
Result // Invalid signature with claimed valid result
3639
};
3740

@@ -43,7 +46,8 @@ template <class Curve> class EcdsaTestingFunctions {
4346

4447
static std::vector<std::string> get_labels()
4548
{
46-
return { "None", "Hash is not a byte array", "Zero R", "Zero S", "High S", "Public key", "Result" };
49+
return { "None", "Hash is not a byte array", "Zero R", "Zero S",
50+
"High S", "Public key not on curve", "Result" };
4751
}
4852
};
4953

@@ -53,6 +57,20 @@ template <class Curve> class EcdsaTestingFunctions {
5357

5458
static ProgramMetadata generate_metadata() { return ProgramMetadata{}; }
5559

60+
static std::pair<AcirConstraint, WitnessVector> generate_invalid_verification_result_constraints(
61+
const InvalidWitness::Target& invalid_witness_target)
62+
{
63+
AcirConstraint ecdsa_constraint;
64+
WitnessVector witness_values;
65+
generate_constraints(ecdsa_constraint, witness_values);
66+
67+
auto [invalid_constraint, invalid_witness_values] =
68+
invalidate_witness(ecdsa_constraint, witness_values, invalid_witness_target);
69+
70+
invalid_witness_values[invalid_constraint.result] = bb::fr(0);
71+
return { invalid_constraint, invalid_witness_values };
72+
}
73+
5674
static std::pair<AcirConstraint, WitnessVector> invalidate_witness(
5775
AcirConstraint ecdsa_constraints,
5876
WitnessVector witness_values,
@@ -94,7 +112,7 @@ template <class Curve> class EcdsaTestingFunctions {
94112
};
95113
break;
96114
case InvalidWitness::Target::P:
97-
// Invalidate public key
115+
// Invalidate public key so signature verification returns false.
98116
witness_values[ecdsa_constraints.pub_x_indices[0]] += bb::fr(1);
99117
break;
100118
case InvalidWitness::Target::Result:
@@ -169,6 +187,24 @@ template <class Curve> class EcdsaTestingFunctions {
169187
}
170188
};
171189

190+
template <typename Flavor> bool construct_and_verify_honk_proof(typename Flavor::CircuitBuilder& builder)
191+
{
192+
using Prover = UltraProver_<Flavor>;
193+
using Verifier = UltraVerifier_<Flavor, DefaultIO>;
194+
using ProverInstance = ProverInstance_<Flavor>;
195+
using VerificationKey = typename Flavor::VerificationKey;
196+
197+
auto prover_instance = std::make_shared<ProverInstance>(builder);
198+
auto verification_key = std::make_shared<VerificationKey>(prover_instance->get_precomputed());
199+
auto vk_and_hash = std::make_shared<typename Flavor::VKAndHash>(verification_key);
200+
201+
Prover prover(prover_instance, verification_key);
202+
auto proof = prover.construct_proof();
203+
204+
Verifier verifier(vk_and_hash);
205+
return verifier.verify_proof(proof).result;
206+
}
207+
172208
template <class Curve>
173209
class EcdsaConstraintsTest : public ::testing::Test, public TestClassWithPredicate<EcdsaTestingFunctions<Curve>> {
174210
protected:
@@ -220,3 +256,34 @@ TYPED_TEST(EcdsaConstraintsTest, InvalidWitnesses)
220256
BB_DISABLE_ASSERTS();
221257
[[maybe_unused]] std::vector<std::string> _ = TestFixture::test_invalid_witnesses();
222258
}
259+
260+
TYPED_TEST(EcdsaConstraintsTest, InvalidVerificationInputsReturnFalseAndProve)
261+
{
262+
BB_DISABLE_ASSERTS();
263+
using Builder = typename TypeParam::Builder;
264+
using Flavor = std::conditional_t<std::is_same_v<Builder, UltraCircuitBuilder>, UltraFlavor, MegaFlavor>;
265+
using InvalidWitnessTarget = typename TestFixture::InvalidWitnessTarget;
266+
267+
const std::vector<InvalidWitnessTarget> invalid_targets = {
268+
InvalidWitnessTarget::ZeroR,
269+
InvalidWitnessTarget::ZeroS,
270+
InvalidWitnessTarget::P,
271+
};
272+
const std::vector<std::string> target_labels = { "zero r", "zero s", "public key not on curve" };
273+
274+
for (auto [invalid_target, target_label] : zip_view(invalid_targets, target_labels)) {
275+
SCOPED_TRACE(target_label);
276+
277+
auto [constraint, witness_values] =
278+
TestFixture::Base::generate_invalid_verification_result_constraints(invalid_target);
279+
ASSERT_EQ(witness_values[constraint.result], bb::fr(0));
280+
281+
AcirFormat constraint_system = constraint_to_acir_format(constraint);
282+
AcirProgram program{ constraint_system, witness_values };
283+
auto builder = create_circuit<Builder>(program, TestFixture::Base::generate_metadata());
284+
285+
EXPECT_TRUE(CircuitChecker::check(builder));
286+
EXPECT_FALSE(builder.failed()) << builder.err();
287+
EXPECT_TRUE(construct_and_verify_honk_proof<Flavor>(builder));
288+
}
289+
}

0 commit comments

Comments
 (0)