From 6bb9d361c82e24f7082b77976e11ba0861829597 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Tue, 26 Mar 2024 08:31:42 -0500 Subject: [PATCH] GH-2334 Change bls_public_key serialization format and optimize --- libraries/chain/block_state.cpp | 15 +++-- libraries/chain/webassembly/privileged.cpp | 11 ++-- .../libfc/include/fc/crypto/bls_common.hpp | 9 +-- .../include/fc/crypto/bls_private_key.hpp | 5 +- .../include/fc/crypto/bls_public_key.hpp | 66 +++++++++++++------ .../libfc/include/fc/crypto/bls_utils.hpp | 6 -- .../libfc/src/crypto/bls_private_key.cpp | 9 +-- libraries/libfc/src/crypto/bls_public_key.cpp | 53 ++++++++------- libraries/libfc/src/crypto/bls_utils.cpp | 24 +------ libraries/libfc/test/test_bls.cpp | 20 +++--- libraries/testing/tester.cpp | 2 +- programs/leap-util/actions/bls.cpp | 6 +- unittests/finality_test_cluster.cpp | 11 ++-- 13 files changed, 116 insertions(+), 121 deletions(-) diff --git a/libraries/chain/block_state.cpp b/libraries/chain/block_state.cpp index 1e11816d6d..159a53067e 100644 --- a/libraries/chain/block_state.cpp +++ b/libraries/chain/block_state.cpp @@ -187,21 +187,24 @@ void block_state::verify_qc(const valid_quorum_certificate& qc) const { ("s", strong_weights)("w", weak_weights)("t", active_finalizer_policy->threshold) ); } - std::vector pubkeys; + // no reason to use bls_public_key wrapper + std::vector pubkeys; + pubkeys.reserve(2); std::vector> digests; + digests.reserve(2); // utility to aggregate public keys for verification - auto aggregate_pubkeys = [&](const auto& votes_bitset) -> bls_public_key { + auto aggregate_pubkeys = [&](const auto& votes_bitset) -> bls12_381::g1 { const auto n = std::min(num_finalizers, votes_bitset.size()); - std::vector pubkeys_to_aggregate; + std::vector pubkeys_to_aggregate; pubkeys_to_aggregate.reserve(n); for(auto i = 0u; i < n; ++i) { if (votes_bitset[i]) { // ith finalizer voted - pubkeys_to_aggregate.emplace_back(finalizers[i].public_key); + pubkeys_to_aggregate.emplace_back(finalizers[i].public_key.jacobian_montgomery_le()); } } - return fc::crypto::blslib::aggregate(pubkeys_to_aggregate); + return bls12_381::aggregate_public_keys(pubkeys_to_aggregate); }; // aggregate public keys and digests for strong and weak votes @@ -216,7 +219,7 @@ void block_state::verify_qc(const valid_quorum_certificate& qc) const { } // validate aggregated signature - EOS_ASSERT( fc::crypto::blslib::aggregate_verify( pubkeys, digests, qc._sig ), + EOS_ASSERT( bls12_381::aggregate_verify(pubkeys, digests, qc._sig._sig), //jacobian_montgomery_le()), invalid_qc_claim, "signature validation failed" ); } diff --git a/libraries/chain/webassembly/privileged.cpp b/libraries/chain/webassembly/privileged.cpp index ec1bc95fea..23cfd4c475 100644 --- a/libraries/chain/webassembly/privileged.cpp +++ b/libraries/chain/webassembly/privileged.cpp @@ -174,7 +174,7 @@ namespace eosio { namespace chain { namespace webassembly { EOS_ASSERT( finalizers.size() <= config::max_finalizers, wasm_execution_error, "Finalizer policy exceeds the maximum finalizer count for this chain" ); EOS_ASSERT( finalizers.size() > 0, wasm_execution_error, "Finalizers cannot be empty" ); - std::set unique_finalizer_keys; + std::set unique_finalizer_keys; uint64_t weight_sum = 0; @@ -185,15 +185,12 @@ namespace eosio { namespace chain { namespace webassembly { "Finalizer description greater than ${s}", ("s", config::max_finalizer_description_size) ); EOS_ASSERT(std::numeric_limits::max() - weight_sum >= f.weight, wasm_execution_error, "sum of weights causes uint64_t overflow"); weight_sum += f.weight; - constexpr bool check = true; // always validate key - constexpr bool raw = false; // non-montgomery EOS_ASSERT(f.public_key.size() == 96, wasm_execution_error, "Invalid bls public key length"); - std::optional pk = bls12_381::g1::fromAffineBytesLE(std::span(f.public_key.data(), 96), check, raw); - EOS_ASSERT( pk, wasm_execution_error, "Invalid public key for: ${d}", ("d", f.description) ); - EOS_ASSERT( unique_finalizer_keys.insert(*pk).second, wasm_execution_error, "Duplicate public key: ${pk}", ("pk", fc::crypto::blslib::bls_public_key{*pk}.to_string()) ); + fc::crypto::blslib::bls_public_key pk(std::span(f.public_key.data(), 96)); + EOS_ASSERT( unique_finalizer_keys.insert(pk).second, wasm_execution_error, "Duplicate public key: ${pk}", ("pk", pk.to_string()) ); finpol.finalizers.push_back(chain::finalizer_authority{.description = std::move(f.description), .weight = f.weight, - .public_key{fc::crypto::blslib::bls_public_key{*pk}}}); + .public_key{pk}}); } EOS_ASSERT( weight_sum >= finpol.threshold && finpol.threshold > weight_sum / 2, wasm_execution_error, "Finalizer policy threshold (${t}) must be greater than half of the sum of the weights (${w}), and less than or equal to the sum of the weights", ("t", finpol.threshold)("w", weight_sum) ); diff --git a/libraries/libfc/include/fc/crypto/bls_common.hpp b/libraries/libfc/include/fc/crypto/bls_common.hpp index ce7fd7a8d6..132d6822fa 100644 --- a/libraries/libfc/include/fc/crypto/bls_common.hpp +++ b/libraries/libfc/include/fc/crypto/bls_common.hpp @@ -4,11 +4,8 @@ namespace fc::crypto::blslib { template - static Container deserialize_base64url(const std::string& data_str) - { - + static Container deserialize_base64url(const std::string& data_str) { using wrapper = checksummed_data; - wrapper wrapped; auto bin = fc::base64url_decode(data_str); @@ -22,10 +19,8 @@ namespace fc::crypto::blslib { } template - static std::string serialize_base64url( Container data) { - + static std::string serialize_base64url(const Container& data) { using wrapper = checksummed_data; - wrapper wrapped; wrapped.data = data; diff --git a/libraries/libfc/include/fc/crypto/bls_private_key.hpp b/libraries/libfc/include/fc/crypto/bls_private_key.hpp index 29ae617931..c99f49346f 100644 --- a/libraries/libfc/include/fc/crypto/bls_private_key.hpp +++ b/libraries/libfc/include/fc/crypto/bls_private_key.hpp @@ -24,7 +24,7 @@ namespace fc::crypto::blslib { bls_private_key& operator=( const bls_private_key& ) = default; - std::string to_string(const yield_function_t& yield = yield_function_t()) const; + std::string to_string() const; bls_public_key get_public_key() const; @@ -42,8 +42,7 @@ namespace fc::crypto::blslib { } // fc::crypto::blslib namespace fc { - void to_variant(const crypto::blslib::bls_private_key& var, variant& vo, - const yield_function_t& yield = yield_function_t()); + void to_variant(const crypto::blslib::bls_private_key& var, variant& vo); void from_variant(const variant& var, crypto::blslib::bls_private_key& vo); } // namespace fc diff --git a/libraries/libfc/include/fc/crypto/bls_public_key.hpp b/libraries/libfc/include/fc/crypto/bls_public_key.hpp index 5fa8f31a3d..744948350d 100644 --- a/libraries/libfc/include/fc/crypto/bls_public_key.hpp +++ b/libraries/libfc/include/fc/crypto/bls_public_key.hpp @@ -3,6 +3,7 @@ #include #include #include +#include namespace fc::crypto::blslib { @@ -10,36 +11,59 @@ namespace fc::crypto::blslib { const std::string bls_public_key_prefix = "PUB_BLS_"; }; - class bls_public_key - { - public: - - bls_public_key() = default; - bls_public_key( bls_public_key&& ) = default; - bls_public_key( const bls_public_key& ) = default; - explicit bls_public_key( const bls12_381::g1& pkey ) {_pkey = pkey;} - // affine non-montgomery base64url with bls_public_key_prefix - explicit bls_public_key(const std::string& base64urlstr); + // Immutable after construction. + // Provides an efficient wrapper around bls12_381::g1. + // Serialization form: + // Non-Montgomery form and little-endian encoding for the field elements. + // Affine form for the group element (the z component is 1 and not included in the serialization). + // Binary serialization encodes x component first followed by y component. + class bls_public_key : fc::reflect_init { + public: + bls_public_key() = default; + bls_public_key(bls_public_key&&) = default; + bls_public_key(const bls_public_key&) = default; + bls_public_key& operator=(const bls_public_key&) = default; + bls_public_key& operator=(bls_public_key&&) = default; - bls_public_key& operator=(const bls_public_key&) = default; + // throws if unable to convert to valie bls12_381::g1 + explicit bls_public_key(std::span affine_non_montgomery_le); - // affine non-montgomery base64url with bls_public_key_prefix - std::string to_string(const yield_function_t& yield = yield_function_t()) const; + // affine non-montgomery base64url with bls_public_key_prefix + explicit bls_public_key(const std::string& base64urlstr); - bool equal(const bls_public_key& pkey) const; - auto operator<=>(const bls_public_key&) const = default; + // affine non-montgomery base64url with bls_public_key_prefix + std::string to_string() const; - bls12_381::g1 _pkey; + const bls12_381::g1& jacobian_montgomery_le() const { return _jacobian_montgomery_le; } + const std::array& affine_non_montgomery_le() const { return _affine_non_montgomery_le; } - }; // bls_public_key + bool equal(const bls_public_key& pkey) const { + return _jacobian_montgomery_le.equal(pkey._jacobian_montgomery_le); + } + + auto operator<=>(const bls_public_key& rhs) const { + return _affine_non_montgomery_le <=> rhs._affine_non_montgomery_le; + } + auto operator==(const bls_public_key& rhs) const { + return _affine_non_montgomery_le == rhs._affine_non_montgomery_le; + } + + private: + friend struct fc::reflector; + friend struct fc::reflector_init_visitor; + friend struct fc::has_reflector_init; + void reflector_init(); + + std::array _affine_non_montgomery_le{}; + bls12_381::g1 _jacobian_montgomery_le; // cached g1 + }; } // fc::crypto::blslib namespace fc { - void to_variant(const crypto::blslib::bls_public_key& var, variant& vo, const yield_function_t& yield = yield_function_t()); - + void to_variant(const crypto::blslib::bls_public_key& var, variant& vo); void from_variant(const variant& var, crypto::blslib::bls_public_key& vo); } // namespace fc -FC_REFLECT(bls12_381::g1, (x)(y)(z)) -FC_REFLECT(crypto::blslib::bls_public_key, (_pkey) ) +// Do not reflect cached g1, serialized form is Affine non-Montgomery little-endian +FC_REFLECT(crypto::blslib::bls_public_key, (_affine_non_montgomery_le) ) diff --git a/libraries/libfc/include/fc/crypto/bls_utils.hpp b/libraries/libfc/include/fc/crypto/bls_utils.hpp index fd06277589..0dc2a6f9bf 100644 --- a/libraries/libfc/include/fc/crypto/bls_utils.hpp +++ b/libraries/libfc/include/fc/crypto/bls_utils.hpp @@ -9,12 +9,6 @@ namespace fc::crypto::blslib { std::span message, const bls_signature& signature); - bls_public_key aggregate(std::span keys); - bls_signature aggregate(std::span signatures); - bool aggregate_verify(std::span pubkeys, - std::span> messages, - const bls_signature& signature); - } // fc::crypto::blslib diff --git a/libraries/libfc/src/crypto/bls_private_key.cpp b/libraries/libfc/src/crypto/bls_private_key.cpp index 72dfc6968e..59b2821253 100644 --- a/libraries/libfc/src/crypto/bls_private_key.cpp +++ b/libraries/libfc/src/crypto/bls_private_key.cpp @@ -10,7 +10,8 @@ namespace fc::crypto::blslib { bls_public_key bls_private_key::get_public_key() const { bls12_381::g1 pk = bls12_381::public_key(_sk); - return bls_public_key(pk); + constexpr bool raw = false; + return bls_public_key(pk.toAffineBytesLE(raw)); } bls_signature bls_private_key::proof_of_possession() const @@ -48,7 +49,7 @@ namespace fc::crypto::blslib { :_sk(priv_parse_base64url(base64urlstr)) {} - std::string bls_private_key::to_string(const yield_function_t& yield) const + std::string bls_private_key::to_string() const { std::string data_str = fc::crypto::blslib::serialize_base64url>(_sk); @@ -63,9 +64,9 @@ namespace fc::crypto::blslib { namespace fc { - void to_variant(const crypto::blslib::bls_private_key& var, variant& vo, const yield_function_t& yield) + void to_variant(const crypto::blslib::bls_private_key& var, variant& vo) { - vo = var.to_string(yield); + vo = var.to_string(); } void from_variant(const variant& var, crypto::blslib::bls_private_key& vo) diff --git a/libraries/libfc/src/crypto/bls_public_key.cpp b/libraries/libfc/src/crypto/bls_public_key.cpp index 726c7d7489..884ca5baa6 100644 --- a/libraries/libfc/src/crypto/bls_public_key.cpp +++ b/libraries/libfc/src/crypto/bls_public_key.cpp @@ -5,8 +5,16 @@ namespace fc::crypto::blslib { - static bls12_381::g1 pub_parse_base64url(const std::string& base64urlstr) - { + bls_public_key::bls_public_key(std::span affine_non_montgomery_le) { + std::ranges::copy(affine_non_montgomery_le, _affine_non_montgomery_le.begin()); + constexpr bool check = true; // verify + constexpr bool raw = false; // to montgomery + auto g1 = bls12_381::g1::fromAffineBytesLE(affine_non_montgomery_le, check, raw); + FC_ASSERT(g1, "Invalid bls_public_key"); + _jacobian_montgomery_le = *g1; + } + + static std::tuple> pub_parse_base64url(const std::string& base64urlstr) { auto res = std::mismatch(config::bls_public_key_prefix.begin(), config::bls_public_key_prefix.end(), base64urlstr.begin()); FC_ASSERT(res.first == config::bls_public_key_prefix.end(), "BLS Public Key has invalid format : ${str}", ("str", base64urlstr)); @@ -19,40 +27,37 @@ namespace fc::crypto::blslib { constexpr bool raw = false; // non-montgomery std::optional g1 = bls12_381::g1::fromAffineBytesLE(bytes, check, raw); FC_ASSERT(g1); - return *g1; + return {*g1, bytes}; } - bls_public_key::bls_public_key(const std::string& base64urlstr) - :_pkey(pub_parse_base64url(base64urlstr)) - {} - - std::string bls_public_key::to_string(const yield_function_t& yield)const { - - constexpr bool raw = false; // non-montgomery - std::array bytes = _pkey.toAffineBytesLE(raw); - - std::string data_str = fc::crypto::blslib::serialize_base64url>(bytes); + bls_public_key::bls_public_key(const std::string& base64urlstr) { + std::tie(_jacobian_montgomery_le, _affine_non_montgomery_le) = pub_parse_base64url(base64urlstr); + } + std::string bls_public_key::to_string() const { + std::string data_str = fc::crypto::blslib::serialize_base64url>(_affine_non_montgomery_le); return config::bls_public_key_prefix + data_str; - } - bool bls_public_key::equal( const bls_public_key& pkey) const { - return _pkey.equal(pkey._pkey); + void bls_public_key::reflector_init() { + // called after construction, but always on the same thread and before bls_public_key passed to any other threads + static_assert(fc::raw::has_feature_reflector_init_on_unpacked_reflected_types, + "FC unpack needs to call reflector_init otherwise unpacked_trx will not be initialized"); + std::optional g1 = bls12_381::g1::fromAffineBytesLE(_affine_non_montgomery_le); + FC_ASSERT(g1, "Invalid bls public key ${k}", ("k", _affine_non_montgomery_le)); + _jacobian_montgomery_le = *g1; } } // fc::crypto::blslib -namespace fc -{ - using namespace std; - void to_variant(const crypto::blslib::bls_public_key& var, variant& vo, const yield_function_t& yield) - { - vo = var.to_string(yield); +namespace fc { + + void to_variant(const crypto::blslib::bls_public_key& var, variant& vo) { + vo = var.to_string(); } - void from_variant(const variant& var, crypto::blslib::bls_public_key& vo) - { + void from_variant(const variant& var, crypto::blslib::bls_public_key& vo) { vo = crypto::blslib::bls_public_key(var.as_string()); } + } // fc diff --git a/libraries/libfc/src/crypto/bls_utils.cpp b/libraries/libfc/src/crypto/bls_utils.cpp index b96aa58626..58ea6479d3 100644 --- a/libraries/libfc/src/crypto/bls_utils.cpp +++ b/libraries/libfc/src/crypto/bls_utils.cpp @@ -5,17 +5,7 @@ namespace fc::crypto::blslib { bool verify(const bls_public_key& pubkey, std::span message, const bls_signature& signature) { - return bls12_381::verify(pubkey._pkey, message, signature._sig); - }; - - bls_public_key aggregate(std::span keys) { - std::vector ks; - ks.reserve(keys.size()); - for( const auto& k : keys ) { - ks.push_back(k._pkey); - } - bls12_381::g1 agg = bls12_381::aggregate_public_keys(ks); - return bls_public_key(agg); + return bls12_381::verify(pubkey.jacobian_montgomery_le(), message, signature._sig); //jacobian_montgomery_le()); }; bls_signature aggregate(std::span signatures) { @@ -29,16 +19,4 @@ namespace fc::crypto::blslib { return bls_signature{agg}; }; - bool aggregate_verify(std::span pubkeys, - std::span> messages, // should be `std::span>` - const bls_signature& signature) { - std::vector ks; - ks.reserve(pubkeys.size()); - for( const auto& k : pubkeys ) { - ks.push_back(k._pkey); - } - - return bls12_381::aggregate_verify(ks, messages, signature._sig); - }; - } // fc::crypto::blslib diff --git a/libraries/libfc/test/test_bls.cpp b/libraries/libfc/test/test_bls.cpp index efef0783bf..b3fb9012b9 100644 --- a/libraries/libfc/test/test_bls.cpp +++ b/libraries/libfc/test/test_bls.cpp @@ -91,16 +91,16 @@ BOOST_AUTO_TEST_CASE(bls_sig_verif_hotstuff_types) try { bls_signature signature = sk.sign(v); - bls_public_key agg_pk = pk; + bls12_381::g1 agg_pk = pk.jacobian_montgomery_le(); bls_signature agg_signature = signature; for (int i = 1 ; i< 21 ;i++){ - agg_pk = aggregate(std::array{agg_pk, pk}); + agg_pk = bls12_381::aggregate_public_keys(std::array{agg_pk, pk.jacobian_montgomery_le()}); agg_signature = aggregate(std::array{agg_signature, signature}); } // Verify the signature - bool ok = verify(agg_pk, v, agg_signature); + bool ok = bls12_381::verify(agg_pk, v, agg_signature._sig); //jacobian_montgomery_le()); BOOST_CHECK_EQUAL(ok, true); @@ -120,11 +120,11 @@ BOOST_AUTO_TEST_CASE(bls_agg_sig_verif) try { bls_signature sig2 = sk2.sign(message_1); - bls_public_key aggKey = aggregate(std::array{pk1, pk2}); - bls_signature aggSig = aggregate(std::array{sig1, sig2}); + bls12_381::g1 agg_key = bls12_381::aggregate_public_keys(std::array{pk1.jacobian_montgomery_le(), pk2.jacobian_montgomery_le()}); + bls_signature agg_sig = aggregate(std::array{sig1, sig2}); // Verify the signature - bool ok = verify(aggKey, message_1, aggSig); + bool ok = bls12_381::verify(agg_key, message_1, agg_sig._sig);//.jacobian_montgomery_le()); BOOST_CHECK_EQUAL(ok, true); @@ -146,11 +146,11 @@ BOOST_AUTO_TEST_CASE(bls_agg_tree_verif) try { bls_signature aggSig = aggregate(std::array{sig1, sig2}); - std::vector pubkeys = {pk1, pk2}; + std::vector pubkeys = {pk1.jacobian_montgomery_le(), pk2.jacobian_montgomery_le()}; std::vector> messages = {message_1, message_2}; // Verify the signature - bool ok = aggregate_verify(pubkeys, messages, aggSig); + bool ok = bls12_381::aggregate_verify(pubkeys, messages, aggSig._sig); //jacobian_montgomery_le()); BOOST_CHECK_EQUAL(ok, true); @@ -347,11 +347,11 @@ BOOST_AUTO_TEST_CASE(bls_variant) try { std::string s; v = prk; s = fc::json::to_string(v, {}); - BOOST_CHECK_EQUAL(s, "\"" + prk.to_string({}) + "\""); + BOOST_CHECK_EQUAL(s, "\"" + prk.to_string() + "\""); v = pk; s = fc::json::to_string(v, {}); - BOOST_CHECK_EQUAL(s, "\"" + pk.to_string({}) + "\""); + BOOST_CHECK_EQUAL(s, "\"" + pk.to_string() + "\""); v = sig; s = fc::json::to_string(v, {}); diff --git a/libraries/testing/tester.cpp b/libraries/testing/tester.cpp index dc11e80887..64344fd138 100644 --- a/libraries/testing/tester.cpp +++ b/libraries/testing/tester.cpp @@ -1209,7 +1209,7 @@ namespace eosio { namespace testing { fc::mutable_variant_object() ("description", f.name.to_string() + " description") ("weight", f.weight) - ("public_key", pubkey.to_string({})) + ("public_key", pubkey.to_string()) ("pop", pop.to_string({}))); } diff --git a/programs/leap-util/actions/bls.cpp b/programs/leap-util/actions/bls.cpp index 972de6db89..9b37c9572b 100644 --- a/programs/leap-util/actions/bls.cpp +++ b/programs/leap-util/actions/bls.cpp @@ -58,8 +58,8 @@ int bls_actions::create_key() { const bls_signature pop = private_key.proof_of_possession(); // prepare output - std::string out_str = "Private key: " + private_key.to_string({}) + "\n"; - out_str += "Public key: " + public_key.to_string({}) + "\n"; + std::string out_str = "Private key: " + private_key.to_string() + "\n"; + out_str += "Public key: " + public_key.to_string() + "\n"; out_str += "Proof of Possession: " + pop.to_string({}) + "\n"; if (opt->print_console) { std::cout << out_str; @@ -109,7 +109,7 @@ int bls_actions::create_pop() { const bls_signature pop = private_key.proof_of_possession(); std::cout << "Proof of Possession: " << pop.to_string({})<< "\n"; - std::cout << "Public key: " << public_key.to_string({}) << "\n"; + std::cout << "Public key: " << public_key.to_string() << "\n"; return 0; } diff --git a/unittests/finality_test_cluster.cpp b/unittests/finality_test_cluster.cpp index 8b4824c3d1..b9e597c577 100644 --- a/unittests/finality_test_cluster.cpp +++ b/unittests/finality_test_cluster.cpp @@ -113,12 +113,11 @@ void finality_test_cluster::node1_corrupt_vote_proposal_id() { void finality_test_cluster::node1_corrupt_vote_finalizer_key() { node1_orig_vote = node1.votes[0]; - // corrupt the finalizer_key - if( node1.votes[0].finalizer_key._pkey.x.d[0] == 1 ) { - node1.votes[0].finalizer_key._pkey.x.d[0] = 2; - } else { - node1.votes[0].finalizer_key._pkey.x.d[0] = 1; - } + // corrupt the finalizer_key (manipulate so it is different) + auto g1 = node1.votes[0].finalizer_key.jacobian_montgomery_le(); + g1 = bls12_381::aggregate_public_keys(std::array{g1, g1}); + auto affine = g1.toAffineBytesLE(false); + node1.votes[0].finalizer_key = fc::crypto::blslib::bls_public_key(affine); } void finality_test_cluster::node1_corrupt_vote_signature() {