Skip to content

Commit

Permalink
Add ECDSA test for ACIR and fix (#435)
Browse files Browse the repository at this point in the history
---------

Co-authored-by: zac-williamson <[email protected]>
  • Loading branch information
kevaundray and zac-williamson authored May 9, 2023
1 parent ad615ee commit bb08dfa
Show file tree
Hide file tree
Showing 6 changed files with 352 additions and 9 deletions.
1 change: 1 addition & 0 deletions cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <gtest/gtest.h>
#include <vector>
#include "barretenberg/common/streams.hpp"
#include "ecdsa_secp256k1.hpp"

TEST(acir_format, test_logic_gate_from_noir_circuit)
{
Expand Down
25 changes: 17 additions & 8 deletions cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ crypto::ecdsa::signature ecdsa_convert_signature(Composer& composer, std::vector
signature_cr.s[i - 32] = fr_bytes.back();
}

signature_cr.v = 27;

return signature_cr;
}

Expand Down Expand Up @@ -104,17 +106,24 @@ void create_ecdsa_verify_constraints(Composer& composer, const EcdsaSecp256k1Con

pub_key_x_fq.assert_is_in_field();
pub_key_y_fq.assert_is_in_field();

secp256k1_ct::g1_bigfr_ct public_key = secp256k1_ct::g1_bigfr_ct(pub_key_x_fq, pub_key_y_fq);
for (size_t i = 0; i < 32; ++i) {
sig.r[i].assert_equal(field_ct::from_witness_index(&composer, input.signature[i]));
sig.s[i].assert_equal(field_ct::from_witness_index(&composer, input.signature[i + 32]));
pub_key_x_byte_arr[i].assert_equal(field_ct::from_witness_index(&composer, input.pub_x_indices[i]));
pub_key_y_byte_arr[i].assert_equal(field_ct::from_witness_index(&composer, input.pub_y_indices[i]));
}
for (size_t i = 0; i < input.message.size(); ++i) {
message[i].assert_equal(field_ct::from_witness_index(&composer, input.message[i]));
}

bool_ct signature_result = stdlib::ecdsa::verify_signature<Composer,
secp256k1_ct,
secp256k1_ct::fq_ct,
secp256k1_ct::bigfr_ct,
secp256k1_ct::g1_bigfr_ct>(message, public_key, sig);

bool_ct signature_result =
stdlib::ecdsa::verify_signature_noassert<Composer,
secp256k1_ct,
secp256k1_ct::fq_ct,
secp256k1_ct::bigfr_ct,
secp256k1_ct::g1_bigfr_ct>(message, public_key, sig);
bool_ct signature_result_normalized = signature_result.normalize();

composer.assert_equal(signature_result_normalized.witness_index, input.result);
}

Expand Down
144 changes: 144 additions & 0 deletions cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
#include "acir_format.hpp"
#include "ecdsa_secp256k1.hpp"
#include "barretenberg/plonk/proof_system/types/proof.hpp"
#include "barretenberg/plonk/proof_system/verification_key/verification_key.hpp"
#include "barretenberg/crypto/ecdsa/ecdsa.hpp"

#include <gtest/gtest.h>
#include <vector>

using curve = proof_system::plonk::stdlib::secp256k1<acir_format::Composer>;

size_t generate_ecdsa_constraint(acir_format::EcdsaSecp256k1Constraint& ecdsa_constraint,
std::vector<fr>& witness_values)
{
std::string message_string = "Instructions unclear, ask again later.";

crypto::ecdsa::key_pair<curve::fr, curve::g1> account;
account.private_key = curve::fr::random_element();
account.public_key = curve::g1::one * account.private_key;

crypto::ecdsa::signature signature =
crypto::ecdsa::construct_signature<Sha256Hasher, curve::fq, curve::fr, curve::g1>(message_string, account);

uint256_t pub_x_value = account.public_key.x;
uint256_t pub_y_value = account.public_key.y;

std::vector<uint32_t> message_in;
std::vector<uint32_t> pub_x_indices_in;
std::vector<uint32_t> pub_y_indices_in;
std::vector<uint32_t> signature_in;
size_t offset = 1;
for (size_t i = 0; i < message_string.size(); ++i) {
message_in.emplace_back(i + offset);
const auto byte = static_cast<uint8_t>(message_string[i]);
witness_values.emplace_back(byte);
}
offset += message_in.size();

for (size_t i = 0; i < 32; ++i) {
pub_x_indices_in.emplace_back(i + offset);
witness_values.emplace_back(pub_x_value.slice(248 - i * 8, 256 - i * 8));
}
offset += pub_x_indices_in.size();
for (size_t i = 0; i < 32; ++i) {
pub_y_indices_in.emplace_back(i + offset);
witness_values.emplace_back(pub_y_value.slice(248 - i * 8, 256 - i * 8));
}
offset += pub_y_indices_in.size();
for (size_t i = 0; i < 32; ++i) {
signature_in.emplace_back(i + offset);
witness_values.emplace_back(signature.r[i]);
}
offset += signature.r.size();
for (size_t i = 0; i < 32; ++i) {
signature_in.emplace_back(i + offset);
witness_values.emplace_back(signature.s[i]);
}
offset += signature.s.size();

witness_values.emplace_back(1);
const auto result_in = static_cast<uint32_t>(offset);
offset += 1;
witness_values.emplace_back(1);

ecdsa_constraint = acir_format::EcdsaSecp256k1Constraint{
.message = message_in,
.pub_x_indices = pub_x_indices_in,
.pub_y_indices = pub_y_indices_in,
.result = result_in,
.signature = signature_in,
};
return offset;
}

TEST(ECDSASecp256k1, TestECDSAConstraintSucceed)
{
acir_format::EcdsaSecp256k1Constraint ecdsa_constraint;
std::vector<fr> witness_values;
size_t num_variables = generate_ecdsa_constraint(ecdsa_constraint, witness_values);
acir_format::acir_format constraint_system{
.varnum = static_cast<uint32_t>(num_variables),
.public_inputs = {},
.fixed_base_scalar_mul_constraints = {},
.logic_constraints = {},
.range_constraints = {},
.schnorr_constraints = {},
.ecdsa_constraints = { ecdsa_constraint },
.sha256_constraints = {},
.blake2s_constraints = {},
.keccak_constraints = {},
.hash_to_field_constraints = {},
.pedersen_constraints = {},
.compute_merkle_root_constraints = {},
.constraints = {},
};

auto composer = acir_format::create_circuit_with_witness(constraint_system, witness_values);

EXPECT_EQ(composer.get_variable(ecdsa_constraint.result), 1);
auto prover = composer.create_prover();

auto proof = prover.construct_proof();
auto verifier = composer.create_verifier();
EXPECT_EQ(verifier.verify_proof(proof), true);
}

TEST(ECDSASecp256k1, TestECDSAConstraintFail)
{
acir_format::EcdsaSecp256k1Constraint ecdsa_constraint;
std::vector<fr> witness_values;
size_t num_variables = generate_ecdsa_constraint(ecdsa_constraint, witness_values);

// set result value to be false
witness_values[witness_values.size() - 1] = 0;

// tamper with signature
witness_values[witness_values.size() - 20] += 1;

acir_format::acir_format constraint_system{
.varnum = static_cast<uint32_t>(num_variables),
.public_inputs = {},
.fixed_base_scalar_mul_constraints = {},
.logic_constraints = {},
.range_constraints = {},
.schnorr_constraints = {},
.ecdsa_constraints = { ecdsa_constraint },
.sha256_constraints = {},
.blake2s_constraints = {},
.keccak_constraints = {},
.hash_to_field_constraints = {},
.pedersen_constraints = {},
.compute_merkle_root_constraints = {},
.constraints = {},
};

auto composer = acir_format::create_circuit_with_witness(constraint_system, witness_values);

EXPECT_EQ(composer.get_variable(ecdsa_constraint.result), 0);
auto prover = composer.create_prover();

auto proof = prover.construct_proof();
auto verifier = composer.create_verifier();
EXPECT_EQ(verifier.verify_proof(proof), true);
}
5 changes: 5 additions & 0 deletions cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ bool_t<Composer> verify_signature(const stdlib::byte_array<Composer>& message,
const G1& public_key,
const signature<Composer>& sig);

template <typename Composer, typename Curve, typename Fq, typename Fr, typename G1>
bool_t<Composer> verify_signature_noassert(const stdlib::byte_array<Composer>& message,
const G1& public_key,
const signature<Composer>& sig);

template <typename Composer>
static signature<Composer> from_witness(Composer* ctx, const crypto::ecdsa::signature& input)
{
Expand Down
94 changes: 94 additions & 0 deletions cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,98 @@ TEST(stdlib_ecdsa, verify_signature)
bool proof_result = verifier.verify_proof(proof);
EXPECT_EQ(proof_result, true);
}

TEST(stdlib_ecdsa, verify_signature_noassert_succeed)
{
Composer composer = Composer();

// whaaablaghaaglerijgeriij
std::string message_string = "Instructions unclear, ask again later.";

crypto::ecdsa::key_pair<curve::fr, curve::g1> account;
account.private_key = curve::fr::random_element();
account.public_key = curve::g1::one * account.private_key;

crypto::ecdsa::signature signature =
crypto::ecdsa::construct_signature<Sha256Hasher, curve::fq, curve::fr, curve::g1>(message_string, account);

bool first_result = crypto::ecdsa::verify_signature<Sha256Hasher, curve::fq, curve::fr, curve::g1>(
message_string, account.public_key, signature);
EXPECT_EQ(first_result, true);

curve::g1_bigfr_ct public_key = curve::g1_bigfr_ct::from_witness(&composer, account.public_key);

std::vector<uint8_t> rr(signature.r.begin(), signature.r.end());
std::vector<uint8_t> ss(signature.s.begin(), signature.s.end());
uint8_t vv = signature.v;

stdlib::ecdsa::signature<Composer> sig{
curve::byte_array_ct(&composer, rr),
curve::byte_array_ct(&composer, ss),
stdlib::uint8<Composer>(&composer, vv),
};

curve::byte_array_ct message(&composer, message_string);

curve::bool_ct signature_result =
stdlib::ecdsa::verify_signature_noassert<Composer, curve, curve::fq_ct, curve::bigfr_ct, curve::g1_bigfr_ct>(
message, public_key, sig);

EXPECT_EQ(signature_result.get_value(), true);

std::cerr << "composer gates = " << composer.get_num_gates() << std::endl;
benchmark_info("UltraComposer", "ECDSA", "Signature Verification Test", "Gate Count", composer.get_num_gates());
auto prover = composer.create_prover();
auto verifier = composer.create_verifier();
auto proof = prover.construct_proof();
bool proof_result = verifier.verify_proof(proof);
EXPECT_EQ(proof_result, true);
}

TEST(stdlib_ecdsa, verify_signature_noassert_fail)
{
Composer composer = Composer();

// whaaablaghaaglerijgeriij
std::string message_string = "Instructions unclear, ask again later.";

crypto::ecdsa::key_pair<curve::fr, curve::g1> account;
account.private_key = curve::fr::random_element();
account.public_key = curve::g1::one * account.private_key;

crypto::ecdsa::signature signature =
crypto::ecdsa::construct_signature<Sha256Hasher, curve::fq, curve::fr, curve::g1>(message_string, account);

// tamper w. signature to make fail
signature.r[0] += 1;

bool first_result = crypto::ecdsa::verify_signature<Sha256Hasher, curve::fq, curve::fr, curve::g1>(
message_string, account.public_key, signature);
EXPECT_EQ(first_result, false);

curve::g1_bigfr_ct public_key = curve::g1_bigfr_ct::from_witness(&composer, account.public_key);

std::vector<uint8_t> rr(signature.r.begin(), signature.r.end());
std::vector<uint8_t> ss(signature.s.begin(), signature.s.end());

stdlib::ecdsa::signature<Composer> sig{ curve::byte_array_ct(&composer, rr),
curve::byte_array_ct(&composer, ss),
27 };

curve::byte_array_ct message(&composer, message_string);

curve::bool_ct signature_result =
stdlib::ecdsa::verify_signature_noassert<Composer, curve, curve::fq_ct, curve::bigfr_ct, curve::g1_bigfr_ct>(
message, public_key, sig);

EXPECT_EQ(signature_result.get_value(), false);

std::cerr << "composer gates = " << composer.get_num_gates() << std::endl;
benchmark_info("UltraComposer", "ECDSA", "Signature Verification Test", "Gate Count", composer.get_num_gates());
auto prover = composer.create_prover();
auto verifier = composer.create_verifier();
auto proof = prover.construct_proof();
bool proof_result = verifier.verify_proof(proof);
EXPECT_EQ(proof_result, true);
}
} // namespace test_stdlib_ecdsa
Loading

0 comments on commit bb08dfa

Please sign in to comment.