Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add initial ECDSA test for ACIR #435

Merged
merged 12 commits into from
May 9, 2023
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