Skip to content

Commit

Permalink
Adds prehashed message variant of EcDSA (#437)
Browse files Browse the repository at this point in the history
* verification takes a pre-hashed message : Note: if len(hash) > 32 bytes, then bigfield will fail

* use hashed_message when generating signature

* modify acir structure and function to now use prehashed variant

* message -> hashed_message
  • Loading branch information
kevaundray authored May 11, 2023
1 parent bcabcd8 commit e59eb2c
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ void create_ecdsa_verify_constraints(Composer& composer, const EcdsaSecp256k1Con

auto new_sig = ecdsa_convert_signature(composer, input.signature);

auto message = ecdsa_vector_of_bytes_to_byte_array(composer, input.message);
auto message = ecdsa_vector_of_bytes_to_byte_array(composer, input.hashed_message);
auto pub_key_x_byte_arr = ecdsa_vector_of_bytes_to_byte_array(composer, input.pub_x_indices);
auto pub_key_y_byte_arr = ecdsa_vector_of_bytes_to_byte_array(composer, input.pub_y_indices);

Expand All @@ -113,16 +113,16 @@ void create_ecdsa_verify_constraints(Composer& composer, const EcdsaSecp256k1Con
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]));
for (size_t i = 0; i < input.hashed_message.size(); ++i) {
message[i].assert_equal(field_ct::from_witness_index(&composer, input.hashed_message[i]));
}

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);
stdlib::ecdsa::verify_signature_prehashed_message_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
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@
namespace acir_format {

struct EcdsaSecp256k1Constraint {
// This is just a bunch of bytes
// which need to be interpreted as a string
// Note this must be a bunch of bytes
std::vector<uint32_t> message;
// This is the byte representation of the hashed message.
std::vector<uint32_t> hashed_message;

// This is the supposed public key which signed the
// message, giving rise to the signature.
Expand All @@ -33,7 +31,7 @@ void create_ecdsa_verify_constraints(Composer& composer, const EcdsaSecp256k1Con
template <typename B> inline void read(B& buf, EcdsaSecp256k1Constraint& constraint)
{
using serialize::read;
read(buf, constraint.message);
read(buf, constraint.hashed_message);
read(buf, constraint.signature);
read(buf, constraint.pub_x_indices);
read(buf, constraint.pub_y_indices);
Expand All @@ -43,7 +41,7 @@ template <typename B> inline void read(B& buf, EcdsaSecp256k1Constraint& constra
template <typename B> inline void write(B& buf, EcdsaSecp256k1Constraint const& constraint)
{
using serialize::write;
write(buf, constraint.message);
write(buf, constraint.hashed_message);
write(buf, constraint.signature);
write(buf, constraint.pub_x_indices);
write(buf, constraint.pub_y_indices);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ size_t generate_ecdsa_constraint(acir_format::EcdsaSecp256k1Constraint& ecdsa_co
{
std::string message_string = "Instructions unclear, ask again later.";

// hash the message since the dsl ecdsa gadget uses the prehashed message
// NOTE: If the hash being used outputs more than 32 bytes, then big-field will panic
std::vector<uint8_t> message_buffer;
std::copy(message_string.begin(), message_string.end(), std::back_inserter(message_buffer));
auto hashed_message = sha256::sha256(message_buffer);

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;
Expand All @@ -29,9 +35,9 @@ size_t generate_ecdsa_constraint(acir_format::EcdsaSecp256k1Constraint& ecdsa_co
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) {
for (size_t i = 0; i < hashed_message.size(); ++i) {
message_in.emplace_back(i + offset);
const auto byte = static_cast<uint8_t>(message_string[i]);
const auto byte = static_cast<uint8_t>(hashed_message[i]);
witness_values.emplace_back(byte);
}
offset += message_in.size();
Expand Down Expand Up @@ -63,7 +69,7 @@ size_t generate_ecdsa_constraint(acir_format::EcdsaSecp256k1Constraint& ecdsa_co
witness_values.emplace_back(1);

ecdsa_constraint = acir_format::EcdsaSecp256k1Constraint{
.message = message_in,
.hashed_message = message_in,
.pub_x_indices = pub_x_indices_in,
.pub_y_indices = pub_y_indices_in,
.result = result_in,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ template <typename Composer, typename Curve, typename Fq, typename Fr, typename
bool_t<Composer> verify_signature_noassert(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_prehashed_message_noassert(const stdlib::byte_array<Composer>& hashed_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
Original file line number Diff line number Diff line change
Expand Up @@ -117,20 +117,17 @@ bool_t<Composer> verify_signature(const stdlib::byte_array<Composer>& message,
* @tparam Fq
* @tparam Fr
* @tparam G1
* @param message
* @param hashed_message
* @param public_key
* @param sig
* @return bool_t<Composer>
*/
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)
bool_t<Composer> verify_signature_prehashed_message_noassert(const stdlib::byte_array<Composer>& hashed_message,
const G1& public_key,
const signature<Composer>& sig)
{
Composer* ctx = message.get_context() ? message.get_context() : public_key.x.context;

stdlib::byte_array<Composer> hashed_message =
static_cast<stdlib::byte_array<Composer>>(stdlib::sha256<Composer>(message));
Composer* ctx = hashed_message.get_context() ? hashed_message.get_context() : public_key.x.context;

Fr z(hashed_message);
z.assert_is_in_field();
Expand Down Expand Up @@ -186,6 +183,30 @@ bool_t<Composer> verify_signature_noassert(const stdlib::byte_array<Composer>& m
return output;
}

/**
* @brief Verify ECDSA signature. Returns 0 if signature fails (i.e. does not produce unsatisfiable constraints)
*
* @tparam Composer
* @tparam Curve
* @tparam Fq
* @tparam Fr
* @tparam G1
* @param message
* @param public_key
* @param sig
* @return bool_t<Composer>
*/
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)
{
stdlib::byte_array<Composer> hashed_message =
static_cast<stdlib::byte_array<Composer>>(stdlib::sha256<Composer>(message));

return verify_signature_prehashed_message_noassert<Composer, Curve, Fq, Fr, G1>(hashed_message, public_key, sig);
}

} // namespace ecdsa
} // namespace stdlib
} // namespace proof_system::plonk

0 comments on commit e59eb2c

Please sign in to comment.