Skip to content

Commit

Permalink
GH-2334 Add bls_aggregate_signature and change serialization of bls_s…
Browse files Browse the repository at this point in the history
…ignature to be affine non-montgomery little-endian
  • Loading branch information
heifner committed Mar 26, 2024
1 parent 1fdd181 commit 0fd2540
Show file tree
Hide file tree
Showing 14 changed files with 228 additions and 163 deletions.
2 changes: 1 addition & 1 deletion libraries/chain/block_state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ void block_state::verify_qc(const valid_quorum_certificate& qc) const {
}

// validate aggregated signature
EOS_ASSERT( bls12_381::aggregate_verify(pubkeys, digests, qc._sig._sig), //jacobian_montgomery_le()),
EOS_ASSERT( bls12_381::aggregate_verify(pubkeys, digests, qc._sig.jacobian_montgomery_le()),
invalid_qc_claim, "signature validation failed" );
}

Expand Down
18 changes: 4 additions & 14 deletions libraries/chain/hotstuff/hotstuff.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,15 @@ vote_status pending_quorum_certificate::votes_t::add_vote(std::span<const uint8_
return vote_status::invalid_signature;
}
_bitset.set(index);
_sig = fc::crypto::blslib::aggregate(std::array{_sig, new_sig}); // works even if _sig is default initialized (fp2::zero())
_sig.aggregate(new_sig); // works even if _sig is default initialized (fp2::zero())
return vote_status::success;
}

void pending_quorum_certificate::votes_t::reset(size_t num_finalizers) {
if (num_finalizers != _bitset.size())
_bitset.resize(num_finalizers);
_bitset.reset();
_sig = bls_signature();
_sig = bls_aggregate_signature();
}

pending_quorum_certificate::pending_quorum_certificate()
Expand Down Expand Up @@ -149,7 +149,8 @@ valid_quorum_certificate pending_quorum_certificate::to_valid_quorum_certificate
} else if (is_quorum_met_no_lock()) {
valid_qc._strong_votes = _strong_votes._bitset;
valid_qc._weak_votes = _weak_votes._bitset;
valid_qc._sig = fc::crypto::blslib::aggregate(std::array{_strong_votes._sig, _weak_votes._sig});
valid_qc._sig = _strong_votes._sig;
valid_qc._sig.aggregate(_weak_votes._sig);
} else
assert(0); // this should be called only when we have a valid qc.

Expand All @@ -160,15 +161,4 @@ bool pending_quorum_certificate::is_quorum_met_no_lock() const {
return is_quorum_met(_state);
}

valid_quorum_certificate::valid_quorum_certificate(
const std::vector<uint32_t>& strong_votes, // bitset encoding, following canonical order
const std::vector<uint32_t>& weak_votes, // bitset encoding, following canonical order
const bls_signature& sig)
: _sig(sig) {
if (!strong_votes.empty())
_strong_votes = vector_to_bitset(strong_votes);
if (!weak_votes.empty())
_weak_votes = vector_to_bitset(weak_votes);
}

} // namespace eosio::chain
26 changes: 9 additions & 17 deletions libraries/chain/include/eosio/chain/hotstuff/hotstuff.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@

namespace eosio::chain {

using bls_public_key = fc::crypto::blslib::bls_public_key;
using bls_signature = fc::crypto::blslib::bls_signature;
using bls_private_key = fc::crypto::blslib::bls_private_key;
using bls_public_key = fc::crypto::blslib::bls_public_key;
using bls_signature = fc::crypto::blslib::bls_signature;
using bls_aggregate_signature = fc::crypto::blslib::bls_aggregate_signature;
using bls_private_key = fc::crypto::blslib::bls_private_key;

using hs_bitset = boost::dynamic_bitset<uint32_t>;
using hs_bitset = boost::dynamic_bitset<uint32_t>;
using bls_key_map_t = std::map<bls_public_key, bls_private_key>;

struct vote_message {
Expand All @@ -39,22 +40,13 @@ namespace eosio::chain {
using bls_private_key = fc::crypto::blslib::bls_private_key;

// valid_quorum_certificate
class valid_quorum_certificate {
public:
valid_quorum_certificate(const std::vector<uint32_t>& strong_votes, //bitset encoding, following canonical order
const std::vector<uint32_t>& weak_votes, //bitset encoding, following canonical order
const bls_signature& sig);

valid_quorum_certificate() = default;
valid_quorum_certificate(const valid_quorum_certificate&) = default;

struct valid_quorum_certificate {
bool is_weak() const { return !!_weak_votes; }
bool is_strong() const { return !_weak_votes; }

friend struct fc::reflector<valid_quorum_certificate>;
std::optional<hs_bitset> _strong_votes;
std::optional<hs_bitset> _weak_votes;
bls_signature _sig;
bls_aggregate_signature _sig;
};

// quorum_certificate
Expand Down Expand Up @@ -87,8 +79,8 @@ namespace eosio::chain {
};

struct votes_t {
hs_bitset _bitset;
bls_signature _sig;
hs_bitset _bitset;
bls_aggregate_signature _sig;

void resize(size_t num_finalizers) { _bitset.resize(num_finalizers); }
size_t count() const { return _bitset.count(); }
Expand Down
2 changes: 1 addition & 1 deletion libraries/libfc/include/fc/crypto/bls_public_key.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ namespace fc::crypto::blslib {
bls_public_key& operator=(const bls_public_key&) = default;
bls_public_key& operator=(bls_public_key&&) = default;

// throws if unable to convert to valie bls12_381::g1
// throws if unable to convert to valid bls12_381::g1
explicit bls_public_key(std::span<const uint8_t, 96> affine_non_montgomery_le);

// affine non-montgomery base64url with bls_public_key_prefix
Expand Down
141 changes: 113 additions & 28 deletions libraries/libfc/include/fc/crypto/bls_signature.hpp
Original file line number Diff line number Diff line change
@@ -1,51 +1,136 @@
#pragma once
#include <fc/static_variant.hpp>
#include <fc/crypto/elliptic.hpp>
#include <fc/crypto/elliptic_r1.hpp>
#include <fc/crypto/elliptic_webauthn.hpp>
#include <fc/crypto/bls_public_key.hpp>
#include <fc/reflect/reflect.hpp>
#include <fc/reflect/variant.hpp>
#include <fc/exception/exception.hpp>
#include <bls12-381/bls12-381.hpp>


namespace fc::crypto::blslib {

namespace config {
const std::string bls_signature_prefix = "SIG_BLS_";
};

class bls_signature
{
public:

bls_signature() = default;
bls_signature( bls_signature&& ) = default;
bls_signature( const bls_signature& ) = default;
explicit bls_signature( const bls12_381::g2& sig ){_sig = sig;}
// Immutable after construction.
// Provides an efficient wrapper around bls12_381::g2.
// 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_signature {
public:
bls_signature() = default;
bls_signature(bls_signature&&) = default;
bls_signature(const bls_signature&) = default;
bls_signature& operator=(const bls_signature&) = default;
bls_signature& operator=(bls_signature&&) = default;

// throws if unable to convert to valid bls12_381::g2
explicit bls_signature(std::span<const uint8_t, 192> affine_non_montgomery_le);

// affine non-montgomery base64url with bls_signature_prefix
explicit bls_signature(const std::string& base64urlstr);

// affine non-montgomery base64url with bls_signature_prefix
std::string to_string() const;

const bls12_381::g2& jacobian_montgomery_le() const { return _jacobian_montgomery_le; }
const std::array<uint8_t, 192>& affine_non_montgomery_le() const { return _affine_non_montgomery_le; }

// affine non-montgomery base64url with bls_signature_prefix
explicit bls_signature(const std::string& base64urlstr);
bool equal(const bls_signature& sig) const {
return _jacobian_montgomery_le.equal(sig._jacobian_montgomery_le);
}

template<typename T>
friend T& operator<<(T& ds, const bls_signature& sig) {
ds.write(reinterpret_cast<const char*>(sig._affine_non_montgomery_le.data()), sig._affine_non_montgomery_le.size()*sizeof(uint8_t));
return ds;
}

// Could use FC_REFLECT, but to make it obvious serialization matches bls_aggregate_signature implement via operator
template<typename T>
friend T& operator>>(T& ds, bls_signature& sig) {
ds.read(reinterpret_cast<char*>(sig._affine_non_montgomery_le.data()), sig._affine_non_montgomery_le.size()*sizeof(uint8_t));
constexpr bool check = true; // check if invalid
constexpr bool raw = false; // non-montgomery
std::optional<bls12_381::g2> g2 = bls12_381::g2::fromAffineBytesLE(sig._affine_non_montgomery_le, check, raw);
FC_ASSERT(g2, "Invalid bls signature ${k}", ("k", sig._affine_non_montgomery_le));
sig._jacobian_montgomery_le = *g2;
return ds;
}

private:
std::array<uint8_t, 192> _affine_non_montgomery_le{};
bls12_381::g2 _jacobian_montgomery_le; // cached g2
};

bls_signature& operator= (const bls_signature& ) = default;
// 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_aggregate_signature {
public:
bls_aggregate_signature() = default;
bls_aggregate_signature(bls_aggregate_signature&&) = default;
bls_aggregate_signature(const bls_aggregate_signature&) = default;
bls_aggregate_signature& operator=(const bls_aggregate_signature&) = default;
bls_aggregate_signature& operator=(bls_aggregate_signature&&) = default;

// affine non-montgomery base64url with bls_signature_prefix
std::string to_string(const yield_function_t& yield = yield_function_t()) const;
// affine non-montgomery base64url with bls_signature_prefix
explicit bls_aggregate_signature(const std::string& base64urlstr);

bool equal( const bls_signature& sig ) const;
explicit bls_aggregate_signature(const bls_signature& sig)
: _jacobian_montgomery_le(sig.jacobian_montgomery_le()) {}

bls12_381::g2 _sig;
// aggregate signature into this
void aggregate(const bls_signature& sig) {
_jacobian_montgomery_le.addAssign(sig.jacobian_montgomery_le());
}
// aggregate signature into this
void aggregate(const bls_aggregate_signature& sig) {
_jacobian_montgomery_le.addAssign(sig.jacobian_montgomery_le());
}

}; // bls_signature
// affine non-montgomery base64url with bls_signature_prefix
std::string to_string() const;

const bls12_381::g2& jacobian_montgomery_le() const { return _jacobian_montgomery_le; }

bool equal( const bls_aggregate_signature& sig) const {
return _jacobian_montgomery_le.equal(sig._jacobian_montgomery_le);
}

template<typename T>
friend T& operator<<(T& ds, const bls_aggregate_signature& sig) {
constexpr bool raw = false;
std::array<uint8_t, 192> affine_non_montgomery_le = sig._jacobian_montgomery_le.toAffineBytesLE(raw);
ds.write(reinterpret_cast<const char*>(affine_non_montgomery_le.data()), affine_non_montgomery_le.size()*sizeof(uint8_t));
return ds;
}

// Could use FC_REFLECT, but to make it obvious serialization matches bls_signature implement via operator
template<typename T>
friend T& operator>>(T& ds, bls_aggregate_signature& sig) {
std::array<uint8_t, 192> affine_non_montgomery_le;
ds.read(reinterpret_cast<char*>(affine_non_montgomery_le.data()), affine_non_montgomery_le.size()*sizeof(uint8_t));
constexpr bool check = true; // check if invalid
constexpr bool raw = false; // non-montgomery
std::optional<bls12_381::g2> g2 = bls12_381::g2::fromAffineBytesLE(affine_non_montgomery_le, check, raw);
FC_ASSERT(g2, "Invalid bls aggregate signature ${k}", ("k", affine_non_montgomery_le));
sig._jacobian_montgomery_le = *g2;
return ds;
}

private:
bls12_381::g2 _jacobian_montgomery_le;
};

} // fc::crypto::blslib

namespace fc {
void to_variant(const crypto::blslib::bls_signature& var, variant& vo, const yield_function_t& yield = yield_function_t());

void to_variant(const crypto::blslib::bls_signature& var, variant& vo);
void from_variant(const variant& var, crypto::blslib::bls_signature& vo);
} // namespace fc
void to_variant(const crypto::blslib::bls_aggregate_signature& var, variant& vo);
void from_variant(const variant& var, crypto::blslib::bls_aggregate_signature& vo);

FC_REFLECT(bls12_381::fp, (d))
FC_REFLECT(bls12_381::fp2, (c0)(c1))
FC_REFLECT(bls12_381::g2, (x)(y)(z))
FC_REFLECT(crypto::blslib::bls_signature, (_sig) )
} // namespace fc
2 changes: 0 additions & 2 deletions libraries/libfc/include/fc/crypto/bls_utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,4 @@ namespace fc::crypto::blslib {
std::span<const uint8_t> message,
const bls_signature& signature);

bls_signature aggregate(std::span<const bls_signature> signatures);

} // fc::crypto::blslib
6 changes: 4 additions & 2 deletions libraries/libfc/src/crypto/bls_private_key.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@ namespace fc::crypto::blslib {
bls_signature bls_private_key::proof_of_possession() const
{
bls12_381::g2 proof = bls12_381::pop_prove(_sk);
return bls_signature(proof);
constexpr bool raw = false;
return bls_signature(proof.toAffineBytesLE(raw));
}

bls_signature bls_private_key::sign( std::span<const uint8_t> message ) const
{
bls12_381::g2 sig = bls12_381::sign(_sk, message);
return bls_signature(sig);
constexpr bool raw = false;
return bls_signature(sig.toAffineBytesLE(raw));
}

bls_private_key bls_private_key::generate() {
Expand Down
62 changes: 37 additions & 25 deletions libraries/libfc/src/crypto/bls_signature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,17 @@

namespace fc::crypto::blslib {

static bls12_381::g2 sig_parse_base64url(const std::string& base64urlstr)
{
try {
bls_signature::bls_signature(std::span<const uint8_t, 192> 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 g2 = bls12_381::g2::fromAffineBytesLE(affine_non_montgomery_le, check, raw);
FC_ASSERT(g2, "Invalid bls_signature");
_jacobian_montgomery_le = *g2;
}

static std::tuple<bls12_381::g2, std::array<uint8_t, 192>> sig_parse_base64url(const std::string& base64urlstr) {
try {
auto res = std::mismatch(config::bls_signature_prefix.begin(), config::bls_signature_prefix.end(),
base64urlstr.begin());
FC_ASSERT(res.first == config::bls_signature_prefix.end(), "BLS Signature has invalid format : ${str}", ("str", base64urlstr));
Expand All @@ -21,42 +28,47 @@ namespace fc::crypto::blslib {
constexpr bool raw = false; // non-montgomery
std::optional<bls12_381::g2> g2 = bls12_381::g2::fromAffineBytesLE(bytes, check, raw);
FC_ASSERT(g2);
return *g2;

return {*g2, bytes};
} FC_RETHROW_EXCEPTIONS( warn, "error parsing bls_signature", ("str", base64urlstr ) )
}

bls_signature::bls_signature(const std::string& base64urlstr)
:_sig(sig_parse_base64url(base64urlstr))
{}

std::string bls_signature::to_string(const yield_function_t& yield) const
{

constexpr bool raw = false; // non-montgomery
std::array<uint8_t, 192> bytes = _sig.toAffineBytesLE(raw);

std::string data_str = fc::crypto::blslib::serialize_base64url<std::array<uint8_t, 192>>(bytes);
bls_signature::bls_signature(const std::string& base64urlstr) {
std::tie(_jacobian_montgomery_le, _affine_non_montgomery_le) = sig_parse_base64url(base64urlstr);
}

std::string bls_signature::to_string() const {
std::string data_str = fc::crypto::blslib::serialize_base64url<std::array<uint8_t, 192>>(_affine_non_montgomery_le);
return config::bls_signature_prefix + data_str;
}

bls_aggregate_signature::bls_aggregate_signature(const std::string& base64urlstr) {
std::tie(_jacobian_montgomery_le, std::ignore) = sig_parse_base64url(base64urlstr);
}

bool bls_signature::equal( const bls_signature& sig) const {
return _sig.equal(sig._sig);
std::string bls_aggregate_signature::to_string() const {
constexpr bool raw = false;
std::array<uint8_t, 192> affine_non_montgomery_le = _jacobian_montgomery_le.toAffineBytesLE(raw);
std::string data_str = fc::crypto::blslib::serialize_base64url<std::array<uint8_t, 192>>(affine_non_montgomery_le);
return config::bls_signature_prefix + data_str;
}

} // fc::crypto::blslib

namespace fc
{
void to_variant(const crypto::blslib::bls_signature& var, variant& vo, const yield_function_t& yield)
{
vo = var.to_string(yield);
namespace fc {

void to_variant(const crypto::blslib::bls_signature& var, variant& vo) {
vo = var.to_string();
}

void from_variant(const variant& var, crypto::blslib::bls_signature& vo)
{
void from_variant(const variant& var, crypto::blslib::bls_signature& vo) {
vo = crypto::blslib::bls_signature(var.as_string());
}

void to_variant(const crypto::blslib::bls_aggregate_signature& var, variant& vo) {
vo = var.to_string();
}

void from_variant(const variant& var, crypto::blslib::bls_aggregate_signature& vo) {
vo = crypto::blslib::bls_aggregate_signature(var.as_string());
}
} // fc
Loading

0 comments on commit 0fd2540

Please sign in to comment.