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

BLS12-381 Crypto Primitives #1071

Merged
merged 35 commits into from
Jul 18, 2023
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
17eb804
added bls12-381 crypto primitives
mschoenebeck Apr 22, 2023
ece632e
correct bit/byte mistake in comment
mschoenebeck Apr 22, 2023
10776fc
corrected SHA256 hash of BLS_PRIMITIVES protocol feature
mschoenebeck Apr 24, 2023
9b42070
updated submodule
mschoenebeck May 1, 2023
ff765e0
synced with main
mschoenebeck May 2, 2023
66773fd
synced with Antelope main
mschoenebeck May 2, 2023
53e267d
updated submodule bls12_381 (clang fixes)
mschoenebeck May 8, 2023
201a0f7
updated bls lib
mschoenebeck May 8, 2023
06ab49e
synced with antelope/main
mschoenebeck May 8, 2023
22656ad
synced submodules
mschoenebeck May 9, 2023
89773d7
sync with branch 'main' of https://github.com/AntelopeIO/leap
mschoenebeck May 24, 2023
bcbd4a4
sync with antelope/main
mschoenebeck May 24, 2023
362c3ea
updated submodules
mschoenebeck May 24, 2023
c931460
removed bls types from abi_serializer
mschoenebeck May 24, 2023
1a35039
removed bls typedefs from types.hpp and deleted bls_utils.hpp/cpp
mschoenebeck May 24, 2023
e920c99
Merge branch 'main' of https://github.com/AntelopeIO/leap
mschoenebeck Jun 7, 2023
8f7d439
update according to Github comments
mschoenebeck Jun 7, 2023
c4b0e19
Merge branch 'main' of https://github.com/AntelopeIO/leap
mschoenebeck Jun 8, 2023
76c82ae
updated bls12_381::init()
mschoenebeck Jun 8, 2023
14d9bdb
Merge branch 'main' of https://github.com/AntelopeIO/leap
mschoenebeck Jun 10, 2023
06411fa
added yield() to pairing::calculate and g2::multiExp
mschoenebeck Jun 10, 2023
8a0e21f
renamed bls lib
mschoenebeck Jun 14, 2023
81020c3
regenerate deepmind log for new BLS_PRIMITIVES
spoonincode Jun 14, 2023
7159dc2
updated bls module
mschoenebeck Jun 15, 2023
5f827ac
Merge branch 'main' of https://github.com/AntelopeIO/leap
mschoenebeck Jun 16, 2023
1ca2373
updated bls lib
mschoenebeck Jun 16, 2023
0d3203b
added optional return values (instead of throwing exceptions) and ena…
mschoenebeck Jun 27, 2023
3b77508
Merge pull request #2 from AntelopeIO/bls-regen-dmlog
mschoenebeck Jun 27, 2023
233ae01
added unit tests for garbage io
mschoenebeck Jun 28, 2023
76e4fd9
added bls lib to EosioTester to make CDT integration tests work
mschoenebeck Jun 30, 2023
9007161
Merge branch 'main' of https://github.com/AntelopeIO/leap
mschoenebeck Jun 30, 2023
e050376
added test contract for bls primitives
mschoenebeck Jul 17, 2023
1e5822d
synced with main
mschoenebeck Jul 17, 2023
712b1a0
updated eos-vm submodule
mschoenebeck Jul 17, 2023
59cc737
fixed intrinsic mapping and deep-mind.log errors
mschoenebeck Jul 18, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,6 @@
[submodule "libraries/cli11/cli11"]
path = libraries/cli11/cli11
url = https://github.com/AntelopeIO/CLI11.git
[submodule "libraries/libfc/libraries/bls12_381"]
path = libraries/libfc/libraries/bls12_381
url = https://github.com/mschoenebeck/bls12_381.git
19 changes: 19 additions & 0 deletions libraries/chain/controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include <fc/log/logger_config.hpp>
#include <fc/scoped_exit.hpp>
#include <fc/variant_object.hpp>
#include <bls12_381.hpp>

#include <new>
#include <shared_mutex>
Expand Down Expand Up @@ -340,6 +341,8 @@ struct controller_impl {
set_activation_handler<builtin_protocol_feature_t::get_code_hash>();
set_activation_handler<builtin_protocol_feature_t::get_block_num>();
set_activation_handler<builtin_protocol_feature_t::crypto_primitives>();
set_activation_handler<builtin_protocol_feature_t::bls_primitives>();
bls12_381::init();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the correct place for the library's init function? The call to init only needs to happen once when nodeos starts. It is a live dispatcher that checks if adc and bmi2 cpu features are available and if so sets the faster asm routines.


self.irreversible_block.connect([this](const block_state_ptr& bsp) {
// producer_plugin has already asserted irreversible_block signal is
Expand Down Expand Up @@ -3846,6 +3849,22 @@ void controller_impl::on_activation<builtin_protocol_feature_t::crypto_primitive
} );
}

template<>
void controller_impl::on_activation<builtin_protocol_feature_t::bls_primitives>() {
db.modify( db.get<protocol_state_object>(), [&]( auto& ps ) {
add_intrinsic_to_whitelist( ps.whitelisted_intrinsics, "bls_g1_add" );
add_intrinsic_to_whitelist( ps.whitelisted_intrinsics, "bls_g2_add" );
add_intrinsic_to_whitelist( ps.whitelisted_intrinsics, "bls_g1_mul" );
add_intrinsic_to_whitelist( ps.whitelisted_intrinsics, "bls_g2_mul" );
add_intrinsic_to_whitelist( ps.whitelisted_intrinsics, "bls_g1_exp" );
add_intrinsic_to_whitelist( ps.whitelisted_intrinsics, "bls_g2_exp" );
add_intrinsic_to_whitelist( ps.whitelisted_intrinsics, "bls_pairing" );
add_intrinsic_to_whitelist( ps.whitelisted_intrinsics, "bls_g1_map" );
add_intrinsic_to_whitelist( ps.whitelisted_intrinsics, "bls_g2_map" );
add_intrinsic_to_whitelist( ps.whitelisted_intrinsics, "bls_fp_mod" );
} );
}

/// End of protocol feature activation handlers

} } /// eosio::chain
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ enum class builtin_protocol_feature_t : uint32_t {
configurable_wasm_limits = 18, // configurable_wasm_limits2,
crypto_primitives = 19,
get_block_num = 20,
bls_primitives = 21,
reserved_private_fork_protocol_features = 500000,
};

Expand Down
2 changes: 1 addition & 1 deletion libraries/chain/include/eosio/chain/types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ namespace eosio::chain {
using public_key_type = fc::crypto::public_key;
using private_key_type = fc::crypto::private_key;
using signature_type = fc::crypto::signature;

// configurable boost deque (for boost >= 1.71) performs much better than std::deque in our use cases
using block_1024_option_t = boost::container::deque_options< boost::container::block_size<1024u> >::type;
template<typename T>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,17 @@ inline constexpr auto get_intrinsic_table() {
"env.sha3",
"env.blake2_f",
"env.k1_recover",
"env.get_block_num"
"env.get_block_num",
"env.bls_g1_add",
"env.bls_g2_add",
"env.bls_g1_mul",
"env.bls_g2_mul",
"env.bls_g1_exp",
"env.bls_g2_exp",
"env.bls_g1_pairing",
"env.bls_g1_map",
"env.bls_g2_map",
"env.bls_fp_mod"
);
}
inline constexpr std::size_t find_intrinsic_index(std::string_view hf) {
Expand Down
101 changes: 101 additions & 0 deletions libraries/chain/include/eosio/chain/webassembly/interface.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1785,6 +1785,107 @@ namespace webassembly {
*/
int32_t k1_recover( span<const char> signature, span<const char> digest, span<char> pub) const;

/**
* Host function for G1 addition on the elliptic curve bls12-381
*
* @ingroup crypto
* @param op1 - a span containing the first operand G1 point.
* @param op2 - a span containing the second operand G1 point.
* @param[out] result - the result op1 + op2.
*/
void bls_g1_add(span<const char> op1, span<const char> op2, span<char> result) const;

/**
* Host function for G2 addition on the elliptic curve bls12-381
*
* @ingroup crypto
* @param op1 - a span containing the first operand G2 point.
* @param op2 - a span containing the second operand G2 point.
* @param[out] result - the result op1 + op2.
*/
void bls_g2_add(span<const char> op1, span<const char> op2, span<char> result) const;

/**
* Host function for G1 scalar multiplication on the elliptic curve bls12-381
*
* @ingroup crypto
* @param point - a span containing the G1 point operand.
* @param scalar - a span containing the scalar operand.
* @param[out] result - the result: scalar * point.
*/
void bls_g1_mul(span<const char> point, span<const char> scalar, span<char> result) const;

/**
* Host function for G2 scalar multiplication on the elliptic curve bls12-381
*
* @ingroup crypto
* @param point - a span containing the G2 point operand.
* @param scalar - a span containing the scalar operand.
* @param[out] result - the result op1 * op2.
*/
void bls_g2_mul(span<const char> point, span<const char> scalar, span<char> result) const;

/**
* Host function for G1 multi-exponentiation on the elliptic curve bls12-381
*
* @ingroup crypto
* @param points - a span containing a list of G1 points (P0, P1, P2... Pn).
* @param scalars - a span containing a list of scalars (s0, s1, s2... sn).
* @param n - the number of elements in the lists.
* @param[out] result - the result s0 * P0 + s1 * P1 + ... + sn * Pn.
*/
void bls_g1_exp(span<const char> points, span<const char> scalars, const uint32_t n, span<char> result) const;

/**
* Host function for G2 multi-exponentiation on the elliptic curve bls12-381
*
* @ingroup crypto
* @param points - a span containing a list of G2 points (P0, P1, P2... Pn).
* @param scalars - a span containing a list of scalars (s0, s1, s2... sn).
* @param n - the number of elements in the lists.
* @param[out] result - the result s0 * P0 + s1 * P1 + ... + sn * Pn.
*/
void bls_g2_exp(span<const char> points, span<const char> scalars, const uint32_t n, span<char> result) const;

/**
* Host function to calculate the pairing of (G1, G2) pairs on the elliptic curve bls12-381
*
* @ingroup crypto
* @param g1_points - a span containing a list of G1 points (P0, P1, P2... Pn).
* @param g2_points - a span containing a list of G2 points (P0, P1, P2... Pn).
* @param n - the number of elements in the lists.
* @param[out] result - the result e(g1_0, g2_0) * e(g1_1, g2_1) * ... * e(g1_n, g2_n)
*/
void bls_pairing(span<const char> g1_points, span<const char> g2_points, const uint32_t n, span<char> result) const;

/**
* Host function for mapping fp to G1 on the elliptic curve bls12-381
*
* @ingroup crypto
* @param e - a span containing the field element fp to be mapped.
* @param[out] result - the resulting element in G1.
*/
void bls_g1_map(span<const char> e, span<char> result) const;

/**
* Host function for mapping fp2 to G2 on the elliptic curve bls12-381
*
* @ingroup crypto
* @param e - a span containing the field element fp2 to be mapped.
* @param[out] result - the resulting element in G2.
*/
void bls_g2_map(span<const char> e, span<char> result) const;

/**
* Host function for modular reduction of 64 bytes wide scalar to a field element (fp, 48 bytes) of the elliptic curve bls12-381
* Involves Montgomery conversion on the resulting field element.
*
* @ingroup crypto
* @param s - a span containing the 64 bytes wide scalar to be reduced.
* @param[out] result - the resulting field element fp in Montogomery form.
*/
void bls_fp_mod(span<const char> s, span<char> result) const;

// compiler builtins api
void __ashlti3(legacy_ptr<int128_t>, uint64_t, uint64_t, uint32_t) const;
void __ashrti3(legacy_ptr<int128_t>, uint64_t, uint64_t, uint32_t) const;
Expand Down
11 changes: 11 additions & 0 deletions libraries/chain/protocol_feature_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,17 @@ Adds new cryptographic host functions
Builtin protocol feature: GET_BLOCK_NUM

Enables new `get_block_num` intrinsic which returns the current block number.
*/
{}
} )
( builtin_protocol_feature_t::bls_primitives, builtin_protocol_feature_spec{
"BLS_PRIMITIVES",
fc::variant("01969c44de35999b924095ae7f50081a7f274409fdbccb9fc54fa7836c76089c").as<digest_type>(),
// SHA256 hash of the raw message below within the comment delimiters (do not modify message below).
/*
Builtin protocol feature: BLS_PRIMITIVES
Adds new cryptographic host functions
- Add, multiply, multi-exponentiation and pairing functions for the bls12-381 elliptic curve.
*/
{}
} )
Expand Down
102 changes: 102 additions & 0 deletions libraries/chain/webassembly/crypto.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <fc/crypto/sha3.hpp>
#include <fc/crypto/k1_recover.hpp>
#include <bn256/bn256.h>
#include <bls12_381.hpp>

namespace {
uint32_t ceil_log2(uint32_t n)
Expand Down Expand Up @@ -250,4 +251,105 @@ namespace eosio { namespace chain { namespace webassembly {
return return_code::success;
}

void interface::bls_g1_add(span<const char> op1, span<const char> op2, span<char> result) const
{
bls12_381::g1 a = bls12_381::g1::fromJacobianBytesLE({reinterpret_cast<const uint8_t*>(op1.data()), 144}, false, true);
bls12_381::g1 b = bls12_381::g1::fromJacobianBytesLE({reinterpret_cast<const uint8_t*>(op2.data()), 144}, false, true);
bls12_381::g1 c = a.add(b);
c.toJacobianBytesLE({reinterpret_cast<uint8_t*>(result.data()), 144}, true);
}

void interface::bls_g2_add(span<const char> op1, span<const char> op2, span<char> result) const
{
bls12_381::g2 a = bls12_381::g2::fromJacobianBytesLE({reinterpret_cast<const uint8_t*>(op1.data()), 288}, false, true);
bls12_381::g2 b = bls12_381::g2::fromJacobianBytesLE({reinterpret_cast<const uint8_t*>(op2.data()), 288}, false, true);
bls12_381::g2 c = a.add(b);
c.toJacobianBytesLE({reinterpret_cast<uint8_t*>(result.data()), 288}, true);
}

void interface::bls_g1_mul(span<const char> point, span<const char> scalar, span<char> result) const
{
bls12_381::g1 a = bls12_381::g1::fromJacobianBytesLE({reinterpret_cast<const uint8_t*>(point.data()), 144}, false, true);
std::array<uint64_t, 4> b = bls12_381::scalar::fromBytesLE<4>({reinterpret_cast<const uint8_t*>(scalar.data()), 32});
bls12_381::g1 c = a.mulScalar(b);
c.toJacobianBytesLE({reinterpret_cast<uint8_t*>(result.data()), 144}, true);
}

void interface::bls_g2_mul(span<const char> point, span<const char> scalar, span<char> result) const
{
bls12_381::g2 a = bls12_381::g2::fromJacobianBytesLE({reinterpret_cast<const uint8_t*>(point.data()), 288}, false, true);
std::array<uint64_t, 4> b = bls12_381::scalar::fromBytesLE<4>({reinterpret_cast<const uint8_t*>(scalar.data()), 32});
bls12_381::g2 c = a.mulScalar(b);
c.toJacobianBytesLE({reinterpret_cast<uint8_t*>(result.data()), 288}, true);
}

void interface::bls_g1_exp(span<const char> points, span<const char> scalars, const uint32_t n, span<char> result) const
{
std::vector<bls12_381::g1> pv;
std::vector<std::array<uint64_t, 4>> sv;
pv.reserve(n);
sv.reserve(n);
for(uint32_t i = 0; i < n; i++)
{
bls12_381::g1 p = bls12_381::g1::fromJacobianBytesLE({reinterpret_cast<const uint8_t*>(points.data() + i*144), 144}, false, true);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before using the span<>s passed as arguments, need to validate that the size of the span is what is required.

Consider, for example, a contract currently with 64KB of memory calling bls_g1_exp(64KB-8, 4, ..., n=1, ...). The host function validation wrappers will allow this call because the points span is 4 bytes long and within the 0 to 64KB-1 valid range of memory. However, bls12_381::g1::fromJacobianBytesLE({reinterpret_cast<const uint8_t*>(points.data() + i*144), 144} then goes on to use 144 bytes starting from 64KB-8. This will access invalid WASM memory leading to undefined behavior (what will likely happen in this case and at this point of time is a recovered SEGV which leaks the memory allocated by the std::vector<>::reserve() above).

Host functions must ensure they only access memory within the passed span<> ranges. So instead, need to validate that points.size() == n*144. Consider, for example, this host function

int32_t interface::alt_bn128_add(span<const char> op1, span<const char> op2, span<char> result ) const {
if (op1.size() != 64 || op2.size() != 64 || result.size() < 64 ||
bn256::g1_add(std::span<const uint8_t, 64>{(const uint8_t*)op1.data(), 64},
std::span<const uint8_t, 64>{(const uint8_t*)op2.data(), 64},
std::span<uint8_t, 64>{ (uint8_t*)result.data(), 64}) == -1)
return return_code::failure;
return return_code::success;
}

notice how if op1 is not 64 bytes an error is returned. Since bls_g1_exp() does not return anything, the only reasonable option for bls_g1_exp may be to assert that points.size() == n*144 (i.e. fail the transaction).

This same guidance applies to result as well. Notice that the above alt_bn128_add example checks that result is at least 64 bytes. That may not be ideal and may have been an oversight in the original implementation -- I would suggest enforcing that the result here is exactly 144 bytes.

(this feedback applies to all the added host functions)

Copy link
Contributor Author

@mschoenebeck mschoenebeck Jun 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the detailed explanation and example scenario. I was expecting the execution to fail in those cases but didn't consider potential memory leaks. A pretty big deal, especially for network nodes that run indefinitely. Of course, these range checks are very important.

Updates in my most recent commit(s) include:

  • added checks for all span<> ranges
  • make use of return_code::success/return_code::failure as in BN host functions
  • also see related changes in cdt (proper error propagation back to smart contract and then handling with eosio::check, again, exactly as in BN host functions)
  • fixed indentation of tabs (3 ws instead of 4)

std::array<uint64_t, 4> s = bls12_381::scalar::fromBytesLE<4>({reinterpret_cast<const uint8_t*>(scalars.data() + i*32), 32});
pv.push_back(p);
sv.push_back(s);
}
bls12_381::g1 r = bls12_381::g1::multiExp(pv, sv);
r.toJacobianBytesLE({reinterpret_cast<uint8_t*>(result.data()), 144}, true);
}

void interface::bls_g2_exp(span<const char> points, span<const char> scalars, const uint32_t n, span<char> result) const
{
std::vector<bls12_381::g2> pv;
std::vector<std::array<uint64_t, 4>> sv;
pv.reserve(n);
sv.reserve(n);
for(uint32_t i = 0; i < n; i++)
{
bls12_381::g2 p = bls12_381::g2::fromJacobianBytesLE({reinterpret_cast<const uint8_t*>(points.data() + i*288), 288}, false, true);
std::array<uint64_t, 4> s = bls12_381::scalar::fromBytesLE<4>({reinterpret_cast<const uint8_t*>(scalars.data() + i*32), 32});
pv.push_back(p);
sv.push_back(s);
}
bls12_381::g2 r = bls12_381::g2::multiExp(pv, sv);
r.toJacobianBytesLE({reinterpret_cast<uint8_t*>(result.data()), 288}, true);
}

void interface::bls_pairing(span<const char> g1_points, span<const char> g2_points, const uint32_t n, span<char> result) const
{
std::vector<std::tuple<bls12_381::g1, bls12_381::g2>> v;
v.reserve(n);
for(uint32_t i = 0; i < n; i++)
{
bls12_381::g1 p_g1 = bls12_381::g1::fromJacobianBytesLE({reinterpret_cast<const uint8_t*>(g1_points.data() + i*144), 144}, false, true);
bls12_381::g2 p_g2 = bls12_381::g2::fromJacobianBytesLE({reinterpret_cast<const uint8_t*>(g2_points.data() + i*288), 288}, false, true);
bls12_381::pairing::add_pair(v, p_g1, p_g2);
}
bls12_381::fp12 r = bls12_381::pairing::calculate(v);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whenever seeing a loop in a host function, need to be mindful on if a malicious contract can cause the loop to take "too long".

In this case, how long does bls_pairing take if n = 80000 (I believe the approximate realistic maximum currently)?

We don't have hard rules, but historical guidance is that a host function should "yield" (allow the deadline timer to cancel the transaction) at least once per millisecond. But this is a manual yield the host function must perform. As an example,

void interface::sha3( span<const char> input, span<char> output, int32_t keccak ) const {
bool _keccak = keccak == 1;
const size_t bs = eosio::chain::config::hashing_checktime_block_size;
const char* data = input.data();
uint32_t datalen = input.size();
fc::sha3::encoder enc;
while ( datalen > bs ) {
enc.write( data, bs);
data += bs;
datalen -= bs;
context.trx_context.checktime();
}

see how every eosio::chain::config::hashing_checktime_block_size worth of input to hash will call context.trx_context.checktime();

And, general historical guidance is that host functions with conditions that fail to yield in 5+ms would be considered a security defect and must be fixed.

So, we may need to add context.trx_context.checktime() calls in this loop depending on how long it takes to perform these operations.

Copy link
Contributor Author

@mschoenebeck mschoenebeck Jun 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, thanks for taking the time explaining everything so well. I really appreciate it. I already noticed calls to context.trx_context.checktime() here and there, but forgot to ask about it.

There are three host functions with loops. According to your rule of thumb (approx. one check per ms) I did some time measuring on my machine to get an idea on how long each one of the loop iterations run. Here the results and the reasoning behind the numbers I chose for the checktime() intervals:

  • g1_exp: 155ns per loop iter => times 10 = 1.55ms
  • g2_exp: 260ns per loop iter => times 6 = 1.56ms
  • pairng: 395ns per loop iter => times 4 = 1.58ms

Assuming significantly better HW on BP nodes I thought aiming for 1.5ms on my machine should be fine. Also, in case of pairing, having at least 3 pairs going through without checktime() overhead would be a very common case for smart contracts that perform groth16 proof verifications (fwiw).

Updates in the most recent commit(s) include:

  • added context.trx_context.checktime() calls in reasonable intervals

r.toBytesLE({reinterpret_cast<uint8_t*>(result.data()), 576}, true);
}

void interface::bls_g1_map(span<const char> e, span<char> result) const
{
bls12_381::fp a = bls12_381::fp::fromBytesLE({reinterpret_cast<const uint8_t*>(e.data()), 48}, false, true);
bls12_381::g1 c = bls12_381::g1::mapToCurve(a);
c.toJacobianBytesLE({reinterpret_cast<uint8_t*>(result.data()), 144}, true);
}

void interface::bls_g2_map(span<const char> e, span<char> result) const
{
bls12_381::fp2 a = bls12_381::fp2::fromBytesLE({reinterpret_cast<const uint8_t*>(e.data()), 96}, false, true);
bls12_381::g2 c = bls12_381::g2::mapToCurve(a);
c.toJacobianBytesLE({reinterpret_cast<uint8_t*>(result.data()), 288}, true);
}

void interface::bls_fp_mod(span<const char> s, span<char> result) const
{
std::array<uint64_t, 8> k = bls12_381::scalar::fromBytesLE<8>({reinterpret_cast<const uint8_t*>(s.data()), 64});
bls12_381::fp e = bls12_381::fp::modPrime<8>(k);
e.toBytesLE({reinterpret_cast<uint8_t*>(result.data()), 48}, true);
}

}}} // ns eosio::chain::webassembly
12 changes: 12 additions & 0 deletions libraries/chain/webassembly/runtimes/eos-vm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,18 @@ REGISTER_CF_HOST_FUNCTION( blake2_f );
REGISTER_CF_HOST_FUNCTION( sha3 );
REGISTER_CF_HOST_FUNCTION( k1_recover );

// bls_primitives protocol feature
REGISTER_CF_HOST_FUNCTION( bls_g1_add );
REGISTER_CF_HOST_FUNCTION( bls_g2_add );
REGISTER_CF_HOST_FUNCTION( bls_g1_mul );
REGISTER_CF_HOST_FUNCTION( bls_g2_mul );
REGISTER_CF_HOST_FUNCTION( bls_g1_exp );
REGISTER_CF_HOST_FUNCTION( bls_g2_exp );
REGISTER_CF_HOST_FUNCTION( bls_pairing );
REGISTER_CF_HOST_FUNCTION( bls_g1_map );
REGISTER_CF_HOST_FUNCTION( bls_g2_map );
REGISTER_CF_HOST_FUNCTION( bls_fp_mod );

} // namespace webassembly
} // namespace chain
} // namespace eosio
3 changes: 2 additions & 1 deletion libraries/libfc/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
add_subdirectory( secp256k1 )
add_subdirectory( libraries/bn256/src )
add_subdirectory( libraries/bls12_381 )

set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
set(THREADS_PREFER_PTHREAD_FLAG TRUE)
Expand Down Expand Up @@ -131,7 +132,7 @@ if(APPLE)
find_library(corefoundation_framework CoreFoundation)
endif()
target_link_libraries( fc PUBLIC Boost::date_time Boost::chrono Boost::iostreams Threads::Threads
OpenSSL::Crypto ZLIB::ZLIB ${PLATFORM_SPECIFIC_LIBS} ${CMAKE_DL_LIBS} secp256k1 ${security_framework} ${corefoundation_framework})
OpenSSL::Crypto ZLIB::ZLIB ${PLATFORM_SPECIFIC_LIBS} ${CMAKE_DL_LIBS} secp256k1 bls12_381 ${security_framework} ${corefoundation_framework})

# Critically, this ensures that OpenSSL 1.1 & 3.0 both have a variant of BN_zero() with void return value. But it also allows access
# to some obsoleted AES functions in 3.0 too, since 3.0's API_COMPAT is effectively 3.0 by default
Expand Down
1 change: 1 addition & 0 deletions libraries/libfc/libraries/bls12_381
Submodule bls12_381 added at f9b183
1 change: 1 addition & 0 deletions libraries/libfc/test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
add_executable( test_fc
crypto/test_blake2.cpp
crypto/test_bls.cpp
crypto/test_cypher_suites.cpp
crypto/test_hash_functions.cpp
crypto/test_k1_recover.cpp
Expand Down
Loading