From 6c2570428a2a9d89dfc4863e76736eddeab55c86 Mon Sep 17 00:00:00 2001 From: Suyash Bagad Date: Thu, 4 May 2023 07:52:10 +0530 Subject: [PATCH] Pending bb work for aztec3 (https://github.com/AztecProtocol/barretenberg/pull/368) * update js vk (because we now use UP for merkle hashing) * Helpers for ECDSA in A3 (https://github.com/AztecProtocol/barretenberg/pull/364) * Add `stdlib_keccak` in cmake. Correct an assertion in `to_byte_array` in bigfield. * Add `random_element` to affine element. * negate y conditionally. * Change pedersen hash c_bind to use `pedersen_hash::lookup`. * c_binds and other ECDSA related fixes (https://github.com/AztecProtocol/barretenberg/pull/407) * Add v to stdlib ecdsa. * create an engine if its empty. * Add ecdsa c_bind. * print v as a uint32. * Add secp256k1 cbind. add c_bind.hpp Change hpp to h. remove hpp. * Add ecdsa in cmakelists. remove stdlib_ecdsa from build. * chore: align BARRETENBERG_CRYPTO_GENERATOR_PARAMETERS_HACK usage (https://github.com/AztecProtocol/barretenberg/pull/411) * Update join_split test * Tweaks to comments * Add comment for the assertion in bigfield. * Expanded on ecdsa comment. --------- Co-authored-by: ludamad Co-authored-by: ludamad Co-authored-by: codygunton --- barretenberg/cpp/src/CMakeLists.txt | 7 ++ .../src/barretenberg/crypto/ecdsa/c_bind.cpp | 70 +++++++++++++++++++ .../src/barretenberg/crypto/ecdsa/c_bind.h | 29 ++++++++ .../src/barretenberg/crypto/ecdsa/ecdsa.hpp | 6 +- .../crypto/generators/generator_data.cpp | 2 +- .../crypto/pedersen_hash/c_bind.cpp | 9 +-- .../dsl/acir_format/ecdsa_secp256k1.cpp | 4 +- .../ecc/curves/secp256k1/c_bind.cpp | 30 ++++++++ .../ecc/curves/secp256k1/c_bind.hpp | 12 ++++ .../ecc/groups/affine_element.hpp | 7 ++ .../ecc/groups/affine_element_impl.hpp | 33 +++++++++ .../stdlib/encryption/ecdsa/ecdsa.hpp | 4 ++ .../stdlib/encryption/ecdsa/ecdsa.test.cpp | 5 +- .../stdlib/encryption/ecdsa/ecdsa_impl.hpp | 30 ++++++++ .../stdlib/primitives/bigfield/bigfield.hpp | 8 ++- 15 files changed, 246 insertions(+), 10 deletions(-) create mode 100644 barretenberg/cpp/src/barretenberg/crypto/ecdsa/c_bind.cpp create mode 100644 barretenberg/cpp/src/barretenberg/crypto/ecdsa/c_bind.h create mode 100644 barretenberg/cpp/src/barretenberg/ecc/curves/secp256k1/c_bind.cpp create mode 100644 barretenberg/cpp/src/barretenberg/ecc/curves/secp256k1/c_bind.hpp diff --git a/barretenberg/cpp/src/CMakeLists.txt b/barretenberg/cpp/src/CMakeLists.txt index 350392c1877..69d867a2f7c 100644 --- a/barretenberg/cpp/src/CMakeLists.txt +++ b/barretenberg/cpp/src/CMakeLists.txt @@ -76,6 +76,7 @@ if(WASM) $ $ $ + $ $ $ $ @@ -91,6 +92,7 @@ if(WASM) $ $ $ + $ $ $ $ @@ -124,6 +126,7 @@ if(WASM) $ $ $ + $ $ $ $ @@ -178,6 +181,7 @@ if(WASM) $ $ $ + $ $ $ $ @@ -193,6 +197,7 @@ if(WASM) $ $ $ + $ $ $ $ @@ -213,6 +218,7 @@ else() $ $ $ + $ $ $ $ @@ -228,6 +234,7 @@ else() $ $ $ + $ $ $ $ diff --git a/barretenberg/cpp/src/barretenberg/crypto/ecdsa/c_bind.cpp b/barretenberg/cpp/src/barretenberg/crypto/ecdsa/c_bind.cpp new file mode 100644 index 00000000000..b7a8d6f5a91 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/crypto/ecdsa/c_bind.cpp @@ -0,0 +1,70 @@ +#include "ecdsa.hpp" +#include + +#define WASM_EXPORT __attribute__((visibility("default"))) + +extern "C" { + +WASM_EXPORT void ecdsa__compute_public_key(uint8_t const* private_key, uint8_t* public_key_buf) +{ + auto priv_key = from_buffer(private_key); + secp256k1::g1::affine_element pub_key = secp256k1::g1::one * priv_key; + write(public_key_buf, pub_key); +} + +WASM_EXPORT void ecdsa__construct_signature(uint8_t const* message, + size_t msg_len, + uint8_t const* private_key, + uint8_t* output_sig_r, + uint8_t* output_sig_s, + uint8_t* output_sig_v) +{ + using serialize::write; + auto priv_key = from_buffer(private_key); + secp256k1::g1::affine_element pub_key = secp256k1::g1::one * priv_key; + crypto::ecdsa::key_pair key_pair = { priv_key, pub_key }; + + auto sig = crypto::ecdsa::construct_signature( + std::string((char*)message, msg_len), key_pair); + write(output_sig_r, sig.r); + write(output_sig_s, sig.s); + write(output_sig_v, sig.v); +} + +WASM_EXPORT void ecdsa__recover_public_key_from_signature(uint8_t const* message, + size_t msg_len, + uint8_t const* sig_r, + uint8_t const* sig_s, + uint8_t* sig_v, + uint8_t* output_pub_key) +{ + std::array r, s; + std::copy(sig_r, sig_r + 32, r.begin()); + std::copy(sig_s, sig_s + 32, s.begin()); + const uint8_t v = *sig_v; + + crypto::ecdsa::signature sig = { r, s, v }; + auto recovered_pub_key = + crypto::ecdsa::recover_public_key( + std::string((char*)message, msg_len), sig); + write(output_pub_key, recovered_pub_key); +} + +WASM_EXPORT bool ecdsa__verify_signature(uint8_t const* message, + size_t msg_len, + uint8_t const* pub_key, + uint8_t const* sig_r, + uint8_t const* sig_s, + uint8_t const* sig_v) +{ + auto pubk = from_buffer(pub_key); + std::array r, s; + std::copy(sig_r, sig_r + 32, r.begin()); + std::copy(sig_s, sig_s + 32, s.begin()); + const uint8_t v = *sig_v; + + crypto::ecdsa::signature sig = { r, s, v }; + return crypto::ecdsa::verify_signature( + std::string((char*)message, msg_len), pubk, sig); +} +} \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/crypto/ecdsa/c_bind.h b/barretenberg/cpp/src/barretenberg/crypto/ecdsa/c_bind.h new file mode 100644 index 00000000000..897202a3727 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/crypto/ecdsa/c_bind.h @@ -0,0 +1,29 @@ +#include + +#define WASM_EXPORT __attribute__((visibility("default"))) + +extern "C" { + +WASM_EXPORT void ecdsa__compute_public_key(uint8_t const* private_key, uint8_t* public_key_buf); + +WASM_EXPORT void ecdsa__construct_signature(uint8_t const* message, + size_t msg_len, + uint8_t const* private_key, + uint8_t* output_sig_r, + uint8_t* output_sig_s, + uint8_t* output_sig_v); + +WASM_EXPORT void ecdsa__recover_public_key_from_signature(uint8_t const* message, + size_t msg_len, + uint8_t const* sig_r, + uint8_t const* sig_s, + uint8_t* sig_v, + uint8_t* output_pub_key); + +WASM_EXPORT bool ecdsa__verify_signature(uint8_t const* message, + size_t msg_len, + uint8_t const* pub_key, + uint8_t const* sig_r, + uint8_t const* sig_s, + uint8_t const* sig_v); +} diff --git a/barretenberg/cpp/src/barretenberg/crypto/ecdsa/ecdsa.hpp b/barretenberg/cpp/src/barretenberg/crypto/ecdsa/ecdsa.hpp index dea2e186581..57e9074a1bd 100644 --- a/barretenberg/cpp/src/barretenberg/crypto/ecdsa/ecdsa.hpp +++ b/barretenberg/cpp/src/barretenberg/crypto/ecdsa/ecdsa.hpp @@ -30,17 +30,18 @@ bool verify_signature(const std::string& message, inline bool operator==(signature const& lhs, signature const& rhs) { - return lhs.r == rhs.r && lhs.s == rhs.s; + return lhs.r == rhs.r && lhs.s == rhs.s && lhs.v == rhs.v; } inline std::ostream& operator<<(std::ostream& os, signature const& sig) { - os << "{ " << sig.r << ", " << sig.s << " }"; + os << "{ " << sig.r << ", " << sig.s << ", " << static_cast(sig.v) << " }"; return os; } template inline void read(B& it, signature& sig) { + using serialize::read; read(it, sig.r); read(it, sig.s); read(it, sig.v); @@ -48,6 +49,7 @@ template inline void read(B& it, signature& sig) template inline void write(B& buf, signature const& sig) { + using serialize::write; write(buf, sig.r); write(buf, sig.s); write(buf, sig.v); diff --git a/barretenberg/cpp/src/barretenberg/crypto/generators/generator_data.cpp b/barretenberg/cpp/src/barretenberg/crypto/generators/generator_data.cpp index e48e21e4475..20345bae16c 100644 --- a/barretenberg/cpp/src/barretenberg/crypto/generators/generator_data.cpp +++ b/barretenberg/cpp/src/barretenberg/crypto/generators/generator_data.cpp @@ -294,4 +294,4 @@ const fixed_base_ladder* generator_data::get_hash_ladder(size_t num_bits) const } } // namespace generators -} // namespace crypto +} // namespace crypto \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/crypto/pedersen_hash/c_bind.cpp b/barretenberg/cpp/src/barretenberg/crypto/pedersen_hash/c_bind.cpp index fc28d6fff0a..ffffc314dcf 100644 --- a/barretenberg/cpp/src/barretenberg/crypto/pedersen_hash/c_bind.cpp +++ b/barretenberg/cpp/src/barretenberg/crypto/pedersen_hash/c_bind.cpp @@ -11,6 +11,7 @@ extern "C" { WASM_EXPORT void pedersen_hash__init() { + // TODO: do we need this if we are using lookup-pedersen in merkle trees? crypto::generators::init_generator_data(); } @@ -18,7 +19,7 @@ WASM_EXPORT void pedersen__hash_pair(uint8_t const* left, uint8_t const* right, { auto lhs = barretenberg::fr::serialize_from_buffer(left); auto rhs = barretenberg::fr::serialize_from_buffer(right); - auto r = crypto::pedersen_hash::hash_multiple({ lhs, rhs }); + auto r = crypto::pedersen_hash::lookup::hash_multiple({ lhs, rhs }); barretenberg::fr::serialize_to_buffer(r, result); } @@ -26,7 +27,7 @@ WASM_EXPORT void pedersen__hash_multiple(uint8_t const* inputs_buffer, uint8_t* { std::vector to_compress; read(inputs_buffer, to_compress); - auto r = crypto::pedersen_hash::hash_multiple(to_compress); + auto r = crypto::pedersen_hash::lookup::hash_multiple(to_compress); barretenberg::fr::serialize_to_buffer(r, output); } @@ -36,7 +37,7 @@ WASM_EXPORT void pedersen__hash_multiple_with_hash_index(uint8_t const* inputs_b { std::vector to_compress; read(inputs_buffer, to_compress); - auto r = crypto::pedersen_hash::hash_multiple(to_compress, hash_index); + auto r = crypto::pedersen_hash::lookup::hash_multiple(to_compress, hash_index); barretenberg::fr::serialize_to_buffer(r, output); } @@ -54,7 +55,7 @@ WASM_EXPORT uint8_t* pedersen__hash_to_tree(uint8_t const* data) fields.reserve(num_outputs); for (size_t i = 0; fields.size() < num_outputs; i += 2) { - fields.push_back(crypto::pedersen_hash::hash_multiple({ fields[i], fields[i + 1] })); + fields.push_back(crypto::pedersen_hash::lookup::hash_multiple({ fields[i], fields[i + 1] })); } auto buf_size = 4 + num_outputs * sizeof(grumpkin::fq); diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.cpp index c18a426e202..21abc4e8e24 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.cpp @@ -96,9 +96,11 @@ void create_ecdsa_verify_constraints(Composer& composer, const EcdsaSecp256k1Con std::vector rr(new_sig.r.begin(), new_sig.r.end()); std::vector ss(new_sig.s.begin(), new_sig.s.end()); + uint8_t vv = new_sig.v; stdlib::ecdsa::signature sig{ stdlib::byte_array(&composer, rr), - stdlib::byte_array(&composer, ss) }; + stdlib::byte_array(&composer, ss), + stdlib::uint8(&composer, vv) }; pub_key_x_fq.assert_is_in_field(); pub_key_y_fq.assert_is_in_field(); diff --git a/barretenberg/cpp/src/barretenberg/ecc/curves/secp256k1/c_bind.cpp b/barretenberg/cpp/src/barretenberg/ecc/curves/secp256k1/c_bind.cpp new file mode 100644 index 00000000000..107d889d4c2 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/ecc/curves/secp256k1/c_bind.cpp @@ -0,0 +1,30 @@ +#include "secp256k1.hpp" + +#define WASM_EXPORT __attribute__((visibility("default"))) + +extern "C" { + +WASM_EXPORT void ecc_secp256k1__mul(uint8_t const* point_buf, uint8_t const* scalar_buf, uint8_t* result) +{ + auto point = from_buffer(point_buf); + auto scalar = from_buffer(scalar_buf); + secp256k1::g1::affine_element r = point * scalar; + write(result, r); +} + +WASM_EXPORT void ecc_secp256k1__get_random_scalar_mod_circuit_modulus(uint8_t* result) +{ + barretenberg::fr output = barretenberg::fr::random_element(); + write(result, output); +} + +WASM_EXPORT void ecc_secp256k1__reduce512_buffer_mod_circuit_modulus(uint8_t* input, uint8_t* result) +{ + uint512_t bigint_input = from_buffer(input); + + uint512_t barretenberg_modulus(barretenberg::fr::modulus); + + uint512_t target_output = bigint_input % barretenberg_modulus; + write(result, target_output.lo); +} +} \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/ecc/curves/secp256k1/c_bind.hpp b/barretenberg/cpp/src/barretenberg/ecc/curves/secp256k1/c_bind.hpp new file mode 100644 index 00000000000..b1b19832393 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/ecc/curves/secp256k1/c_bind.hpp @@ -0,0 +1,12 @@ +#include "secp256k1.hpp" + +#define WASM_EXPORT __attribute__((visibility("default"))) + +extern "C" { + +WASM_EXPORT void ecc_secp256k1__mul(uint8_t const* point_buf, uint8_t const* scalar_buf, uint8_t* result); + +WASM_EXPORT void ecc_secp256k1__get_random_scalar_mod_circuit_modulus(uint8_t* result); + +WASM_EXPORT void ecc_secp256k1__reduce512_buffer_mod_circuit_modulus(uint8_t* input, uint8_t* result); +} diff --git a/barretenberg/cpp/src/barretenberg/ecc/groups/affine_element.hpp b/barretenberg/cpp/src/barretenberg/ecc/groups/affine_element.hpp index 82ba93bfefc..8d85003a513 100644 --- a/barretenberg/cpp/src/barretenberg/ecc/groups/affine_element.hpp +++ b/barretenberg/cpp/src/barretenberg/ecc/groups/affine_element.hpp @@ -62,6 +62,13 @@ template class alignas(64) affine_el constexpr bool on_curve() const noexcept; + /** + * @brief Samples a random point on the curve. + * + * @return A randomly chosen point on the curve + */ + static affine_element random_element(numeric::random::Engine* engine = nullptr) noexcept; + /** * @brief Hash a seed value to curve. * diff --git a/barretenberg/cpp/src/barretenberg/ecc/groups/affine_element_impl.hpp b/barretenberg/cpp/src/barretenberg/ecc/groups/affine_element_impl.hpp index f5b2e1705ab..c1e61e729f4 100644 --- a/barretenberg/cpp/src/barretenberg/ecc/groups/affine_element_impl.hpp +++ b/barretenberg/cpp/src/barretenberg/ecc/groups/affine_element_impl.hpp @@ -229,5 +229,38 @@ affine_element affine_element::hash_to_curve(const uint64_ return affine_element(x_out, y_out_); } + +template +affine_element affine_element::random_element(numeric::random::Engine* engine) noexcept +{ + if (engine == nullptr) { + engine = &numeric::random::get_engine(); + } + + bool found_one = false; + Fq yy; + Fq x; + Fq y; + while (!found_one) { + // Sample a random x-coordinate and check if it satisfies curve equation. + x = Fq::random_element(engine); + yy = x.sqr() * x + T::b; + if constexpr (T::has_a) { + yy += (x * T::a); + } + auto [found_root, y1] = yy.sqrt(); + y = y1; + + // Negate the y-coordinate based on a randomly sampled bit. + bool random_bit = (engine->get_random_uint8() & 1); + if (random_bit) { + y = -y; + } + + found_one = found_root; + } + return affine_element(x, y); +} + } // namespace group_elements } // namespace barretenberg diff --git a/barretenberg/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa.hpp b/barretenberg/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa.hpp index 1864557af40..ea5f890fd5b 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa.hpp @@ -2,6 +2,7 @@ #include "barretenberg/crypto/ecdsa/ecdsa.hpp" #include "../../primitives/byte_array/byte_array.hpp" +#include "../../primitives/uint/uint.hpp" #include "../../primitives/composers/composers_fwd.hpp" namespace proof_system::plonk { @@ -11,6 +12,7 @@ namespace ecdsa { template struct signature { stdlib::byte_array r; stdlib::byte_array s; + stdlib::uint8 v; }; template @@ -25,9 +27,11 @@ static signature from_witness(Composer* ctx, const crypto::ecdsa::sign std::vector s_vec(std::begin(input.s), std::end(input.s)); stdlib::byte_array r(ctx, r_vec); stdlib::byte_array s(ctx, s_vec); + stdlib::uint8 v(ctx, input.v); signature out; out.r = r; out.s = s; + out.v = v; return out; } diff --git a/barretenberg/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa.test.cpp index b1edc24d847..405d28f33c8 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa.test.cpp @@ -35,8 +35,11 @@ TEST(stdlib_ecdsa, verify_signature) std::vector rr(signature.r.begin(), signature.r.end()); std::vector ss(signature.s.begin(), signature.s.end()); + uint8_t vv = signature.v; - stdlib::ecdsa::signature sig{ curve::byte_array_ct(&composer, rr), curve::byte_array_ct(&composer, ss) }; + stdlib::ecdsa::signature sig{ curve::byte_array_ct(&composer, rr), + curve::byte_array_ct(&composer, ss), + stdlib::uint8(&composer, vv) }; curve::byte_array_ct message(&composer, message_string); diff --git a/barretenberg/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa_impl.hpp b/barretenberg/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa_impl.hpp index 12355453d66..4c23d3d66c9 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa_impl.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa_impl.hpp @@ -13,6 +13,36 @@ bool_t verify_signature(const stdlib::byte_array& message, { Composer* ctx = message.get_context() ? message.get_context() : public_key.x.context; + /** + * Check if recovery id v is either 27 ot 28. + * + * The v in an (r, s, v) ecdsa signature is the 8-bit recovery id s.t. v ∈ {0, 1, 2, 3}. + * It is used to recover signing public key from an ecdsa signature. In practice, the value + * of v is offset by 27 following the convention from the original bitcoin whitepaper. + * + * The value of v depends on the the point R = (x, y) s.t. r = x % |Fr| + * 0: y is even && x < |Fr| (x = r) + * 1: y is odd && x < |Fr| (x = r) + * 2: y is even && |Fr| <= x < |Fq| (x = r + |Fr|) + * 3: y is odd && |Fr| <= x < |Fq| (x = r + |Fr|) + * + * It is highly unlikely for x be be in [|Fr|, |Fq|) for the secp256k1 curve because: + * P(|Fr| <= x < |Fq|) = 1 - |Fr|/|Fq| ≈ 0. + * Therefore, it is reasonable to assume that the value of v will always be 0 or 1 + * (i.e. 27 or 28 with offset). In fact, the ethereum yellow paper [1] only allows v to be 27 or 28 + * and considers signatures with v ∈ {29, 30} to be non-standard. + * + * TODO(Suyash): EIP-155 allows v > 35 to ensure different v on different chains. + * Do we need to consider that in our circuits? + * + * References: + * [1] Ethereum yellow paper, Appendix E: https://ethereum.github.io/yellowpaper/paper.pdf + * [2] EIP-155: https://eips.ethereum.org/EIPS/eip-155 + * + */ + field_t(sig.v).assert_is_in_set({ field_t(27), field_t(28) }, + "signature is non-standard"); + stdlib::byte_array hashed_message = static_cast>(stdlib::sha256(message)); diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield.hpp index 2a1829ce225..3b3bd34f675 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/bigfield/bigfield.hpp @@ -160,7 +160,13 @@ template class bigfield { field_t lo = binary_basis_limbs[0].element + (binary_basis_limbs[1].element * shift_1); field_t hi = binary_basis_limbs[2].element + (binary_basis_limbs[3].element * shift_1); // n.b. this only works if NUM_LIMB_BITS * 2 is divisible by 8 - ASSERT((NUM_LIMB_BITS / 8) * 8 == NUM_LIMB_BITS); + // + // We are packing two bigfield limbs each into the field elements `lo` and `hi`. + // Thus, each of `lo` and `hi` will contain (NUM_LIMB_BITS * 2) bits. We then convert + // `lo` and `hi` to `byte_array` each containing ((NUM_LIMB_BITS * 2) / 8) bytes. + // Therefore, it is necessary for (NUM_LIMB_BITS * 2) to be divisible by 8 for correctly + // converting `lo` and `hi` to `byte_array`s. + ASSERT((NUM_LIMB_BITS * 2 / 8) * 8 == NUM_LIMB_BITS * 2); result.write(byte_array(hi, 32 - (NUM_LIMB_BITS / 4))); result.write(byte_array(lo, (NUM_LIMB_BITS / 4))); return result;