From 57f7e165507f0fcf3925a4bc83061cacd50a4d13 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Fri, 11 Sep 2026 14:22:25 +0700 Subject: [PATCH 01/36] refactor: remove bn_helpers.hpp as workaround for gmp / relic datastructures Efficiently, it reverts 3 commits: 1. Revert "fix: address CodeRabbit review feedback on BnArrayGuard and test consistency" This reverts commit 6696863d3236a00544af9c4f695faf6a6db4dbe8. 2. Revert "chore: fix typo in copyright year in src/bn_helpers.hpp" This reverts commit 8bb6b44374baa51b55238ad7f9cf2f4da4d042f5. 3. Revert "refactor: extract shared RAII bn_t wrappers into bn_helpers.hpp" This reverts commit b3ba7b633ba33be58bb5f2426ca69adce082ef01. --- src/bn_helpers.hpp | 62 ---------------------------------------------- src/schemes.cpp | 59 +++++++++++++++++++++++++++++++++++-------- src/test.cpp | 3 +-- src/threshold.cpp | 57 ++++++++++++++++++++++++++++++------------ 4 files changed, 91 insertions(+), 90 deletions(-) delete mode 100644 src/bn_helpers.hpp diff --git a/src/bn_helpers.hpp b/src/bn_helpers.hpp deleted file mode 100644 index 05175437e..000000000 --- a/src/bn_helpers.hpp +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (c) 2026 The Dash Core developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef SRC_BN_HELPERS_HPP_ -#define SRC_BN_HELPERS_HPP_ - -#include "relic_conf.h" - -extern "C" { -#include "relic.h" -} - -#include - -namespace bls { - -// RAII wrapper for dynamically allocated bn_t arrays. -// Ensures bn_free + delete[] runs even when exceptions are thrown. -// Tracks initialization count so partial construction cleans up correctly. -struct BnArrayGuard { - bn_t* data; - size_t count; - size_t initialized{0}; - - explicit BnArrayGuard(size_t n) : data(new bn_t[n]), count(n) { - for (size_t i = 0; i < count; i++) { - bn_new(data[i]); - initialized++; - } - } - - ~BnArrayGuard() { - for (size_t i = 0; i < initialized; i++) { - bn_free(data[i]); - } - delete[] data; - } - - bn_t& operator[](size_t i) { return data[i]; } - const bn_t& operator[](size_t i) const { return data[i]; } - - BnArrayGuard(const BnArrayGuard&) = delete; - BnArrayGuard& operator=(const BnArrayGuard&) = delete; -}; - -// RAII wrapper for a single bn_t value. -struct BnGuard { - bn_t val; - - BnGuard() { bn_new(val); } - ~BnGuard() { bn_free(val); } - - operator bn_t&() { return val; } - - BnGuard(const BnGuard&) = delete; - BnGuard& operator=(const BnGuard&) = delete; -}; - -} // namespace bls - -#endif // SRC_BN_HELPERS_HPP_ diff --git a/src/schemes.cpp b/src/schemes.cpp index 75cc7b372..991b24c59 100644 --- a/src/schemes.cpp +++ b/src/schemes.cpp @@ -21,18 +21,56 @@ #include "elements.hpp" #include "schemes.hpp" #include "hdkeys.hpp" -#include "bn_helpers.hpp" using std::string; using std::vector; namespace bls { +namespace { + +// RAII wrapper for bn_t arrays that owns both the heap allocation and +// the per-element bn_new/bn_free lifecycle. Tracks how many elements +// were successfully initialised so the destructor is safe even if +// bn_new throws partway through. +class BNArray { +public: + explicit BNArray(size_t size) + : data_(size ? new bn_t[size] : nullptr), size_(size), initialized_(0) + { + for (size_t i = 0; i < size_; ++i) { + bn_new(data_[i]); + ++initialized_; + } + } + + ~BNArray() { + for (size_t i = 0; i < initialized_; ++i) { + bn_free(data_[i]); + } + delete[] data_; + } + + BNArray(const BNArray&) = delete; + BNArray& operator=(const BNArray&) = delete; + + bn_t& operator[](size_t i) { return data_[i]; } + bn_t* data() { return data_; } + +private: + bn_t* data_; + size_t size_; + size_t initialized_; +}; + +} // namespace + template static void HashPubKeys(bn_t* computedTs, size_t nPubKeys, GetBytesFn getBytes) { - BnGuard order; - g2_get_ord(order.val); + bn_t order; + bn_new(order); + g2_get_ord(order); std::vector vecBuffer(nPubKeys * G1Element::SIZE); @@ -54,8 +92,9 @@ static void HashPubKeys(bn_t* computedTs, size_t nPubKeys, GetBytesFn getBytes) Util::Hash256(hash, buffer, 4 + 32); bn_read_bin(computedTs[i], hash, 32); - bn_mod_basic(computedTs[i], computedTs[i], order.val); + bn_mod_basic(computedTs[i], computedTs[i], order); } + bn_free(order); } enum InvariantResult { BAD=false, GOOD=true, CONTINUE }; @@ -205,7 +244,7 @@ G2Element CoreMPL::AggregateSecure(std::vector const &vecPublicKeys, throw std::invalid_argument("LegacySchemeMPL::AggregateSigs sigs.size() != pubKeys.size()"); } - BnArrayGuard computedTs(vecPublicKeys.size()); + BNArray computedTs(vecPublicKeys.size()); std::vector, const G2Element*>> vecSorted(vecPublicKeys.size()); for (size_t i = 0; i < vecPublicKeys.size(); i++) { vecSorted[i] = std::make_pair(vecPublicKeys[i].SerializeToArray(fLegacy), &vecSignatures[i]); @@ -214,7 +253,7 @@ G2Element CoreMPL::AggregateSecure(std::vector const &vecPublicKeys, return std::memcmp(a.first.data(), b.first.data(), G1Element::SIZE) < 0; }); - HashPubKeys(computedTs.data, vecSorted.size(), + HashPubKeys(computedTs.data(), vecSorted.size(), [&](size_t i) { return vecSorted[i].first.data(); }); // Raise all signatures to power of the corresponding t's and aggregate the results into aggSig @@ -247,7 +286,7 @@ bool CoreMPL::VerifySecure(const std::vector& vecPublicKeys, return false; } - BnArrayGuard computedTs(vecPublicKeys.size()); + BNArray computedTs(vecPublicKeys.size()); std::vector> vecSorted(vecPublicKeys.size()); for (size_t i = 0; i < vecPublicKeys.size(); i++) { vecSorted[i] = vecPublicKeys[i].SerializeToArray(fLegacy); @@ -256,7 +295,7 @@ bool CoreMPL::VerifySecure(const std::vector& vecPublicKeys, return std::memcmp(a.data(), b.data(), G1Element::SIZE) < 0; }); - HashPubKeys(computedTs.data, vecSorted.size(), + HashPubKeys(computedTs.data(), vecSorted.size(), [&](size_t i) { return vecSorted[i].data(); }); G1Element publicKey; @@ -612,7 +651,7 @@ bool AugSchemeMPL::VerifySecure(const std::vector& vecPublicKeys, return false; } - BnArrayGuard computedTs(vecPublicKeys.size()); + BNArray computedTs(vecPublicKeys.size()); std::vector> vecSorted(vecPublicKeys.size()); for (size_t i = 0; i < vecPublicKeys.size(); i++) { vecSorted[i] = vecPublicKeys[i].SerializeToArray(false); @@ -621,7 +660,7 @@ bool AugSchemeMPL::VerifySecure(const std::vector& vecPublicKeys, return std::memcmp(a.data(), b.data(), G1Element::SIZE) < 0; }); - HashPubKeys(computedTs.data, vecSorted.size(), + HashPubKeys(computedTs.data(), vecSorted.size(), [&](size_t i) { return vecSorted[i].data(); }); std::vector weightedPks(vecSorted.size()); diff --git a/src/test.cpp b/src/test.cpp index c6fd55393..60b7c9bf6 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -1266,8 +1266,7 @@ TEST_CASE("Schemes") { legacySigs.push_back(legacyScheme.Sign(sk, message)); } - G2Element legacySecureAggSig; - REQUIRE_NOTHROW(legacySecureAggSig = legacyScheme.AggregateSecure(legacyPks, legacySigs, message)); + G2Element legacySecureAggSig = legacyScheme.AggregateSecure(legacyPks, legacySigs, message); REQUIRE(legacyScheme.VerifySecure(legacyPks, legacySecureAggSig, message)); } diff --git a/src/threshold.cpp b/src/threshold.cpp index 4561dccdc..73a3023b2 100644 --- a/src/threshold.cpp +++ b/src/threshold.cpp @@ -7,7 +7,8 @@ #include "threshold.hpp" #include "schemes.hpp" -#include "bn_helpers.hpp" + +#include static std::unique_ptr pThresholdScheme(new bls::LegacySchemeMPL); @@ -174,16 +175,19 @@ namespace bls { throw std::length_error("At least 2 coefficients required"); } - BnGuard x; - bn_read_bin(x.val, id.begin(), Poly::nIdSize); - ops.ModOrder(x.val); + bn_t x; + bn_new(x); + bn_read_bin(x, id.begin(), Poly::nIdSize); + ops.ModOrder(x); BLSType y = vecIn.back(); for (int i = (int) vecIn.size() - 2; i >= 0; i--) { - y = ops.Mul(y, x.val); + y = ops.Mul(y, x); y = ops.Add(y, vecIn[i]); } + bn_free(x); + return y; } @@ -205,35 +209,54 @@ namespace bls { */ const size_t k = vec.size(); - BnArrayGuard delta(k); - BnArrayGuard ids2(k); + bn_t *delta = new bn_t[k]; + bn_t *ids2 = new bn_t[k]; for (size_t i = 0; i < k; i++) { + bn_new(delta[i]); + bn_new(ids2[i]); bn_read_bin(ids2[i], ids[i].begin(), Poly::nIdSize); ops.ModOrder(ids2[i]); } - BnGuard a, b, v; + bn_t a, b, v; + bn_new(a); + bn_new(b); + bn_new(v); - bn_copy(a.val, ids2[0]); + auto cleanup = [&](){ + bn_free(a); + bn_free(b); + bn_free(v); + for (size_t i = 0; i < k; i++) { + bn_free(delta[i]); + bn_free(ids2[i]); + } + delete[] delta; + delete[] ids2; + }; + + bn_copy(a, ids2[0]); for (size_t i = 1; i < k; i++) { - ops.MulFP(a.val, a.val, ids2[i]); + ops.MulFP(a, a, ids2[i]); } - if (bn_is_zero(a.val)) { + if (bn_is_zero(a)) { + cleanup(); throw std::invalid_argument("Zero id"); } for (size_t i = 0; i < k; i++) { - bn_copy(b.val, ids2[i]); + bn_copy(b, ids2[i]); for (size_t j = 0; j < k; j++) { if (j != i) { - ops.SubFP(v.val, ids2[j], ids2[i]); - if (bn_is_zero(v.val)) { + ops.SubFP(v, ids2[j], ids2[i]); + if (bn_is_zero(v)) { + cleanup(); throw std::invalid_argument("Duplicate id"); } - ops.MulFP(b.val, b.val, v.val); + ops.MulFP(b, b, v); } } - ops.DivFP(delta[i], a.val, b.val); + ops.DivFP(delta[i], a, b); } /* @@ -244,6 +267,8 @@ namespace bls { r = ops.Add(r, ops.Mul(vec[i], delta[i])); } + cleanup(); + return r; } From 43a953d783fc2c02d385746e5ec57ed2996bbe42 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Fri, 11 Sep 2026 15:33:16 +0700 Subject: [PATCH 02/36] refactor: removed helper PrivateKey::RandomPrivateKey because it used only in tests This helper is one of the blocker to be refactored to blst because uses low-level gmp calls --- include/dashbls/privatekey.hpp | 3 --- src/privatekey.cpp | 18 +----------------- src/test.cpp | 16 ++++++++-------- 3 files changed, 9 insertions(+), 28 deletions(-) diff --git a/include/dashbls/privatekey.hpp b/include/dashbls/privatekey.hpp index d02a7d292..d754ace76 100644 --- a/include/dashbls/privatekey.hpp +++ b/include/dashbls/privatekey.hpp @@ -34,9 +34,6 @@ class PrivateKey { // Construct a private key from a BIP32 based seed. static PrivateKey FromSeedBIP32(const Bytes& seed); - // Construct a random private key. - static PrivateKey RandomPrivateKey(); - // Construct a private key from a bytearray. static PrivateKey FromBytes(const Bytes& bytes, bool modOrder = false); diff --git a/src/privatekey.cpp b/src/privatekey.cpp index d07246d85..915a6d5ce 100644 --- a/src/privatekey.cpp +++ b/src/privatekey.cpp @@ -77,22 +77,6 @@ PrivateKey PrivateKey::FromByteVector(const std::vector bytes, bool mod return PrivateKey::FromBytes(Bytes(bytes), modOrder); } -// Construct a private key from a bytearray. -PrivateKey PrivateKey::RandomPrivateKey() -{ - bn_t *r = Util::SecAlloc(1); - bn_new(*r); - bn_rand(*r, RLC_POS, 256); - PrivateKey k; - bn_copy(k.keydata, *r); - bn_t ord; - bn_new(ord); - g1_get_ord(ord); - bn_mod_basic(k.keydata, k.keydata, ord); - Util::SecFree(r); - return k; -} - PrivateKey::PrivateKey() { AllocateKeyData(); }; @@ -309,7 +293,7 @@ G2Element PrivateKey::SignG2( } else { ep2_map_dst(pt, msg, len, dst, dst_len); } - + g2_mul(pt, pt, keydata); G2Element ret = G2Element::FromNative(pt); g2_free(pt); diff --git a/src/test.cpp b/src/test.cpp index 60b7c9bf6..c297bc1de 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -58,8 +58,8 @@ TEST_CASE("class PrivateKey") { uint8_t buffer[PrivateKey::PRIVATE_KEY_SIZE]; memcpy(buffer, getRandomSeed().data(), PrivateKey::PRIVATE_KEY_SIZE); SECTION("Copy {constructor|assignment operator}") { - PrivateKey pk1 = PrivateKey::RandomPrivateKey(); - PrivateKey pk2 = PrivateKey::RandomPrivateKey(); + PrivateKey pk1 = PrivateKey::FromByteVector(getRandomSeed(), true); + PrivateKey pk2 = PrivateKey::FromByteVector(getRandomSeed(), true); PrivateKey pk3 = PrivateKey(pk2); REQUIRE(!pk1.IsZero()); REQUIRE(!pk2.IsZero()); @@ -75,9 +75,9 @@ TEST_CASE("class PrivateKey") { REQUIRE(pk3 != pk2); } SECTION("Move {constructor|assignment operator}") { - PrivateKey pk1 = PrivateKey::RandomPrivateKey(); + PrivateKey pk1 = PrivateKey::FromByteVector(getRandomSeed(), true); std::vector vec1 = pk1.Serialize(); - PrivateKey pk2 = PrivateKey::RandomPrivateKey(); + PrivateKey pk2 = PrivateKey::FromByteVector(getRandomSeed(), true); std::vector vec2 = pk2.Serialize(); PrivateKey pk3 = PrivateKey(std::move(pk2)); REQUIRE(!pk1.IsZero()); @@ -95,15 +95,15 @@ TEST_CASE("class PrivateKey") { REQUIRE_THROWS(pk3.IsZero()); } SECTION("Equality operators") { - PrivateKey pk1 = PrivateKey::RandomPrivateKey(); - PrivateKey pk2 = PrivateKey::RandomPrivateKey(); + PrivateKey pk1 = PrivateKey::FromByteVector(getRandomSeed(), true); + PrivateKey pk2 = PrivateKey::FromByteVector(getRandomSeed(), true); PrivateKey pk3 = pk2; REQUIRE(pk1 != pk2); REQUIRE(pk1 != pk3); REQUIRE(pk2 == pk3); } SECTION("(De)Serialization") { - PrivateKey pk1 = PrivateKey::RandomPrivateKey(); + PrivateKey pk1 = PrivateKey::FromByteVector(getRandomSeed(), true); pk1.Serialize(buffer); REQUIRE(memcmp(buffer, pk1.Serialize().data(), PrivateKey::PRIVATE_KEY_SIZE) == 0); PrivateKey pk2 = PrivateKey::FromBytes(Bytes(buffer, PrivateKey::PRIVATE_KEY_SIZE), true); @@ -135,7 +135,7 @@ TEST_CASE("class PrivateKey") { REQUIRE(pubKey1Bytes == knownPublicKey); } SECTION("keydata checks") { - PrivateKey pk1 = PrivateKey::RandomPrivateKey(); + PrivateKey pk1 = PrivateKey::FromByteVector(getRandomSeed(), true); G1Element g1 = pk1.GetG1Element(); G2Element g2 = pk1.GetG2Element(); PrivateKey pk2 = std::move(pk1); From 158592f27aac5830ab6aa7ec6414fe26a41a276d Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Sat, 12 Sep 2026 00:00:13 +0700 Subject: [PATCH 03/36] refactor: drop fLegacy flag from private's key serialization --- include/dashbls/privatekey.hpp | 4 ++-- src/privatekey.cpp | 4 ++-- src/test.cpp | 4 +++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/include/dashbls/privatekey.hpp b/include/dashbls/privatekey.hpp index d754ace76..0b45dd518 100644 --- a/include/dashbls/privatekey.hpp +++ b/include/dashbls/privatekey.hpp @@ -78,8 +78,8 @@ class PrivateKey { // Serialize the key into bytes void Serialize(uint8_t *buffer) const; - std::vector Serialize(bool fLegacy = false) const; - std::array SerializeToArray(bool fLegacy = false) const; + std::vector Serialize() const; + std::array SerializeToArray() const; G2Element SignG2( const uint8_t *msg, diff --git a/src/privatekey.cpp b/src/privatekey.cpp index 915a6d5ce..80bb6b71f 100644 --- a/src/privatekey.cpp +++ b/src/privatekey.cpp @@ -261,14 +261,14 @@ void PrivateKey::Serialize(uint8_t *buffer) const bn_write_bin(buffer, PrivateKey::PRIVATE_KEY_SIZE, keydata); } -std::vector PrivateKey::Serialize(const bool fLegacy) const +std::vector PrivateKey::Serialize() const { std::vector data(PRIVATE_KEY_SIZE); Serialize(data.data()); return data; } -std::array PrivateKey::SerializeToArray(bool fLegacy) const +std::array PrivateKey::SerializeToArray() const { std::array data{}; Serialize(data.data()); diff --git a/src/test.cpp b/src/test.cpp index c297bc1de..ce92691a6 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -126,13 +126,15 @@ TEST_CASE("class PrivateKey") { uint8_t aliceSeed[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; PrivateKey pk1 = PrivateKey::FromSeedBIP32(Bytes(aliceSeed, 10)); REQUIRE(pk1.HasKeyData()); - vector privateKey = pk1.Serialize(true); + vector privateKey = pk1.Serialize(); vector knownPrivateKey = Util::HexToBytes("46891c2cec49593c81921e473db7480029e0fc1eb933c6b93d81f5370eb19fbd"); REQUIRE(privateKey == knownPrivateKey); G1Element pubKey1 = pk1.GetG1Element(); vector pubKey1Bytes = pubKey1.Serialize(true); + vector pubKey2Bytes = pubKey1.Serialize(false); vector knownPublicKey = Util::HexToBytes("1790635de8740e9a6a6b15fb6b72f3a16afa0973d971979b6ba54761d6e2502c50db76f4d26143f05459a42cfd520d44"); REQUIRE(pubKey1Bytes == knownPublicKey); + REQUIRE(pubKey2Bytes != pubKey1Bytes); } SECTION("keydata checks") { PrivateKey pk1 = PrivateKey::FromByteVector(getRandomSeed(), true); From 796a158b7b3d989337e4a4bb412cc88e6c9d4007 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Wed, 30 Sep 2026 17:48:50 +0700 Subject: [PATCH 04/36] fix: silence -Wunused-parameter in LegacySchemeMPL stubs and AggregateSecure Leave unused parameters unnamed so added later -Wextra to build flags keep build warning-free. No behaviour change --- include/dashbls/schemes.hpp | 38 ++++++++++++++++++------------------- src/schemes.cpp | 2 +- src/test-bench.cpp | 3 ++- 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/include/dashbls/schemes.hpp b/include/dashbls/schemes.hpp index accc13b52..5ff7f2a9b 100644 --- a/include/dashbls/schemes.hpp +++ b/include/dashbls/schemes.hpp @@ -232,23 +232,23 @@ class LegacySchemeMPL final : public CoreMPL { public: LegacySchemeMPL() : CoreMPL(std::string{}) {} - virtual vector SkToPk(const PrivateKey &seckey) final { throw std::runtime_error("Not supported in LegacySchemeMPL"); } + virtual vector SkToPk(const PrivateKey &) final { throw std::runtime_error("Not supported in LegacySchemeMPL"); } - G2Element Sign(const PrivateKey &seckey, const vector &message) final { throw std::runtime_error("Not supported in LegacySchemeMPL"); } + G2Element Sign(const PrivateKey &, const vector &) final { throw std::runtime_error("Not supported in LegacySchemeMPL"); } G2Element Sign(const PrivateKey &seckey, const Bytes& message) final; - bool Verify(const vector& pubkey, - const vector& message, - const vector& signature) final { throw std::runtime_error("Not supported in LegacySchemeMPL"); } + bool Verify(const vector&, + const vector&, + const vector&) final { throw std::runtime_error("Not supported in LegacySchemeMPL"); } - bool Verify(const G1Element& pubkey, - const vector& message, - const G2Element& signature) final { throw std::runtime_error("Not supported in LegacySchemeMPL"); } + bool Verify(const G1Element&, + const vector&, + const G2Element&) final { throw std::runtime_error("Not supported in LegacySchemeMPL"); } - bool Verify(const Bytes& pubkey, const Bytes& message, const Bytes& signature) final { throw std::runtime_error("Not supported in LegacySchemeMPL"); } + bool Verify(const Bytes&, const Bytes&, const Bytes&) final { throw std::runtime_error("Not supported in LegacySchemeMPL"); } bool Verify(const G1Element &pubkey, const Bytes& message, const G2Element &signature) final; - std::array Aggregate(const vector> &signatures) final { throw std::runtime_error("Not supported in LegacySchemeMPL"); } + std::array Aggregate(const vector> &) final { throw std::runtime_error("Not supported in LegacySchemeMPL"); } G2Element AggregateSecure(const std::vector& vecPublicKeys, const std::vector& vecSignatures, @@ -258,17 +258,17 @@ class LegacySchemeMPL final : public CoreMPL { const G2Element& signature, const Bytes& message) final; - bool AggregateVerify(const vector> &pubkeys, - const vector> &messages, - const vector &signature) final { throw std::runtime_error("Not supported in LegacySchemeMPL"); } + bool AggregateVerify(const vector> &, + const vector> &, + const vector &) final { throw std::runtime_error("Not supported in LegacySchemeMPL"); } - bool AggregateVerify(const vector &pubkeys, - const vector &messages, - const Bytes &signature) final { throw std::runtime_error("Not supported in LegacySchemeMPL"); } + bool AggregateVerify(const vector &, + const vector &, + const Bytes &) final { throw std::runtime_error("Not supported in LegacySchemeMPL"); } - bool AggregateVerify(const vector &pubkeys, - const vector> &messages, - const G2Element &signature) final { throw std::runtime_error("Not supported in LegacySchemeMPL"); } + bool AggregateVerify(const vector &, + const vector> &, + const G2Element &) final { throw std::runtime_error("Not supported in LegacySchemeMPL"); } bool AggregateVerify(const vector &pubkeys, const vector &messages, diff --git a/src/schemes.cpp b/src/schemes.cpp index 991b24c59..3b5a4a075 100644 --- a/src/schemes.cpp +++ b/src/schemes.cpp @@ -238,7 +238,7 @@ G1Element CoreMPL::Aggregate(const vector &publicKeys) G2Element CoreMPL::AggregateSecure(std::vector const &vecPublicKeys, std::vector const &vecSignatures, - const Bytes& message, + const Bytes& /*message*/, const bool fLegacy) { if (vecSignatures.size() != vecPublicKeys.size()) { throw std::invalid_argument("LegacySchemeMPL::AggregateSigs sigs.size() != pubKeys.size()"); diff --git a/src/test-bench.cpp b/src/test-bench.cpp index 20bbbb443..30b9b58d3 100644 --- a/src/test-bench.cpp +++ b/src/test-bench.cpp @@ -168,7 +168,8 @@ void benchSerializeToArray() { endStopwatch("SerializeToArray G2Element", start, numIters); } -int main(int argc, char* argv[]) { +int main() +{ benchSigs(); benchVerification(); benchBatchVerification(); From 3a7f02795bc67fcfb3201a5fde88dfffafecd6fe Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Sat, 12 Sep 2026 02:15:56 +0700 Subject: [PATCH 05/36] doc: repair license header text mangled by the MPL-stripping sed --- include/dashbls/schemes.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/dashbls/schemes.hpp b/include/dashbls/schemes.hpp index 5ff7f2a9b..a6dcdfb0a 100644 --- a/include/dashbls/schemes.hpp +++ b/include/dashbls/schemes.hpp @@ -1,14 +1,14 @@ // Copyright 2020 Chia Network Inc // Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in coiance with the License. +// you may not use this file except in compliance with the License. // You may obtain a copy of the License at // http://www.apache.org/licenses/LICENSE-2.0 // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or iied. +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. From 7ecb9209effd922fedd1f52eb37151587cffef5a Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Sat, 12 Sep 2026 02:15:56 +0700 Subject: [PATCH 06/36] test: order boundary tests, blst cross-check and the consensus vector harness The vector generator is src/test-vectors.hpp. It uses public API only, so it builds against any backend). runtest gains a "Reference vectors" test case that regenerates every vector and compares it line by line against test-vectors/reference.txt, 324 consensus vectors generated from the relic-based library. --- src/CMakeLists.txt | 11 + src/test-vectors.hpp | 575 +++++++++++++++++++++++++++++++++++++ src/test.cpp | 57 ++++ test-vectors/reference.txt | 324 +++++++++++++++++++++ tools/fuzzgen.cpp | 158 ++++++++++ tools/vectorgen.cpp | 22 ++ 6 files changed, 1147 insertions(+) create mode 100644 src/test-vectors.hpp create mode 100644 test-vectors/reference.txt create mode 100644 tools/fuzzgen.cpp create mode 100644 tools/vectorgen.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5802843a6..e080a3db9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -66,3 +66,14 @@ if(BUILD_BLS_BENCHMARKS) endif() endif() endif() + +if(BUILD_BLS_TESTS) + # runtest compares the library against test-vectors/reference.txt; + # vectorgen regenerates that file from whichever library it is built against. + target_compile_definitions(runtest PRIVATE + DASHBLS_REFERENCE_VECTORS="${CMAKE_CURRENT_SOURCE_DIR}/../test-vectors/reference.txt") + + add_executable(vectorgen ${CMAKE_CURRENT_SOURCE_DIR}/../tools/vectorgen.cpp) + target_include_directories(vectorgen PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) + target_link_libraries(vectorgen PRIVATE dashbls) +endif() diff --git a/src/test-vectors.hpp b/src/test-vectors.hpp new file mode 100644 index 000000000..430d7ec87 --- /dev/null +++ b/src/test-vectors.hpp @@ -0,0 +1,575 @@ +// Copyright (c) 2026 The Dash Core developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef SRC_TEST_VECTORS_HPP_ +#define SRC_TEST_VECTORS_HPP_ + +// Reference consensus vectors. +// +// GenerateReferenceVectors() deterministically exercises the public dashbls +// API - legacy and basic serialization (including malformed input), legacy +// signing, secure aggregation, threshold signing, extended keys and the +// private key edge cases - and emits one "name=value" line per result. +// +// test-vectors/reference.txt holds the output of the relic-based library; +// runtest compares the current library against it line by line, and +// tools/vectorgen.cpp regenerates it. Only the public API is used so the +// same code builds against any backend. + +#include +#include +#include +#include +#include +#include + +#include "bls.hpp" + +namespace bls_test_vectors { + +using namespace bls; +using std::vector; + +struct Emitter { + std::vector lines; + + static std::string hex(const uint8_t* p, size_t n) + { + static const char* digits = "0123456789abcdef"; + std::string s; + s.reserve(n * 2); + for (size_t i = 0; i < n; i++) { + s.push_back(digits[p[i] >> 4]); + s.push_back(digits[p[i] & 0x0f]); + } + return s; + } + + void raw(const std::string& line) { lines.push_back(line); } + void dump(const std::string& name, const vector& v) { lines.push_back(name + "=" + hex(v.data(), v.size())); } + template + void dump(const std::string& name, const std::array& v) { lines.push_back(name + "=" + hex(v.data(), N)); } + void dumpb(const std::string& name, bool v) { lines.push_back(name + "=" + (v ? "1" : "0")); } + void dumpu(const std::string& name, uint32_t v) + { + char buf[16]; + snprintf(buf, sizeof(buf), "%08x", v); + lines.push_back(name + "=" + buf); + } + void exc(const std::string& name) { lines.push_back(name + "=EXC"); } +}; + +inline vector seedN(int i) +{ + vector s(32); + for (int j = 0; j < 32; j++) s[j] = (uint8_t)(0x11 * (i + 1) + j * 7); + return s; +} + +inline vector hashN(int i) +{ + vector h(32); + for (int j = 0; j < 32; j++) h[j] = (uint8_t)(0xA5 ^ (i * 31 + j * 13)); + return h; +} + +inline std::string tag(const char* base, int i) +{ + char buf[96]; + snprintf(buf, sizeof(buf), "%s.%d", base, i); + return std::string(buf); +} + +// ---------------------------------------------------------------- section A/B +inline void KeysAndSignatures(Emitter& e) +{ + LegacySchemeMPL legacy; + BasicSchemeMPL basic; + + for (int i = 0; i < 8; i++) { + PrivateKey sk = basic.KeyGen(seedN(i)); + e.dump(tag("A.sk", i), sk.Serialize()); + + G1Element pk = sk.GetG1Element(); + e.dump(tag("A.pk.basic", i), pk.Serialize(false)); + e.dump(tag("A.pk.legacy", i), pk.Serialize(true)); + e.dumpu(tag("A.fp.basic", i), pk.GetFingerprint(false)); + e.dumpu(tag("A.fp.legacy", i), pk.GetFingerprint(true)); + e.dump(tag("A.skG2.basic", i), sk.GetG2Element().Serialize(false)); + e.dump(tag("A.skG2.legacy", i), sk.GetG2Element().Serialize(true)); + + // round-trips through both codecs + e.dump(tag("A.pk.rt.legacy", i), + G1Element::FromBytes(Bytes(pk.Serialize(true)), true).Serialize(true)); + e.dump(tag("A.pk.rt.basic", i), + G1Element::FromBytes(Bytes(pk.Serialize(false)), false).Serialize(false)); + + // legacy sign over a 32-byte hash; serialize under both codecs + vector h = hashN(i); + G2Element lsig = legacy.Sign(sk, Bytes(h)); + e.dump(tag("B.lsig.legacy", i), lsig.Serialize(true)); + e.dump(tag("B.lsig.basic", i), lsig.Serialize(false)); + e.dumpb(tag("B.lverify", i), legacy.Verify(pk, Bytes(h), lsig)); + // wrong message must fail + vector h2 = hashN(i + 100); + e.dumpb(tag("B.lverify.bad", i), legacy.Verify(pk, Bytes(h2), lsig)); + // legacy sig round-trip through legacy codec + e.dump(tag("B.lsig.rt", i), + G2Element::FromBytes(Bytes(lsig.Serialize(true)), true).Serialize(true)); + + // basic scheme (arbitrary-length message) + vector msg = {(uint8_t)i, 1, 2, 3, 200, 255}; + G2Element bsig = basic.Sign(sk, msg); + e.dump(tag("B.bsig.basic", i), bsig.Serialize(false)); + e.dump(tag("B.bsig.legacy", i), bsig.Serialize(true)); + e.dumpb(tag("B.bverify", i), basic.Verify(pk, msg, bsig)); + + // EIP-2333 derivation + e.dump(tag("A.child.hard", i), basic.DeriveChildSk(sk, 0x8000002a).Serialize()); + e.dump(tag("A.child.soft", i), basic.DeriveChildSkUnhardened(sk, 1337).Serialize()); + } +} + +// ---------------------------------------------------------------- section C/D +inline void SecureAggregation(Emitter& e) +{ + LegacySchemeMPL legacy; + BasicSchemeMPL basic; + + for (int n : {1, 2, 3, 10}) { + vector sks; + vector pks; + vector lsigs, bsigs; + vector h = hashN(n); + for (int i = 0; i < n; i++) { + sks.push_back(basic.KeyGen(seedN(200 + n * 16 + i))); + pks.push_back(sks.back().GetG1Element()); + lsigs.push_back(legacy.Sign(sks.back(), Bytes(h))); + bsigs.push_back(basic.Sign(sks.back(), Bytes(h))); + } + G2Element lagg = legacy.AggregateSecure(pks, lsigs, Bytes(h)); + e.dump(tag("C.aggsec.legacy", n), lagg.Serialize(true)); + e.dumpb(tag("C.versec.legacy", n), legacy.VerifySecure(pks, lagg, Bytes(h))); + + G2Element bagg = basic.AggregateSecure(pks, bsigs, Bytes(h)); + e.dump(tag("C.aggsec.basic", n), bagg.Serialize(false)); + e.dumpb(tag("C.versec.basic", n), basic.VerifySecure(pks, bagg, Bytes(h))); + + // cross checks must fail + e.dumpb(tag("C.versec.legacy.bad", n), + legacy.VerifySecure(pks, lagg, Bytes(hashN(n + 50)))); + + // plain aggregation + e.dump(tag("C.agg.G2", n), basic.Aggregate(bsigs).Serialize(false)); + e.dump(tag("C.agg.G1", n), basic.Aggregate(pks).Serialize(false)); + e.dump(tag("C.agg.sk", n), PrivateKey::Aggregate(sks).Serialize()); + } + + // legacy multi-message AggregateVerify + { + int n = 4; + vector sks; + vector pks; + vector sigs; + vector> msgs; + for (int i = 0; i < n; i++) { + sks.push_back(basic.KeyGen(seedN(300 + i))); + pks.push_back(sks.back().GetG1Element()); + msgs.push_back(hashN(300 + i)); + sigs.push_back(legacy.Sign(sks.back(), Bytes(msgs.back()))); + } + G2Element agg = basic.Aggregate(sigs); // plain point sum + vector msgRefs; + for (auto& m : msgs) msgRefs.push_back(Bytes(m)); + e.dump("D.lagg", agg.Serialize(true)); + e.dumpb("D.laggverify", legacy.AggregateVerify(pks, msgRefs, agg)); + vector badRefs; + badRefs.push_back(Bytes(msgs[1])); + for (size_t i = 1; i < msgs.size(); i++) badRefs.push_back(Bytes(msgs[i])); + e.dumpb("D.laggverify.bad", legacy.AggregateVerify(pks, badRefs, agg)); + } +} + +// ------------------------------------------------------------------ section E +inline void ThresholdVectors(Emitter& e) +{ + BasicSchemeMPL basic; + const int m = 3, n = 5; + + // polynomial coefficients (the "master" keys) + vector coeffs; + vector vvec; + vector sigCoeffs; + vector h = hashN(77); + for (int i = 0; i < m; i++) { + coeffs.push_back(basic.KeyGen(seedN(400 + i))); + vvec.push_back(coeffs.back().GetG1Element()); + sigCoeffs.push_back(Threshold::Sign(coeffs[i], Bytes(h))); + } + e.dump("E.mastersig", Threshold::Sign(coeffs[0], Bytes(h)).Serialize(true)); + e.dumpb("E.masterverify", + Threshold::Verify(vvec[0], Bytes(h), Threshold::Sign(coeffs[0], Bytes(h)))); + + vector> ids; + for (int i = 0; i < n; i++) ids.push_back(hashN(500 + i)); + + vector skShares; + vector sigShares; + for (int i = 0; i < n; i++) { + PrivateKey sh = Threshold::PrivateKeyShare(coeffs, Bytes(ids[i])); + skShares.push_back(sh); + e.dump(tag("E.skshare", i), sh.Serialize()); + G1Element pkShare = Threshold::PublicKeyShare(vvec, Bytes(ids[i])); + e.dump(tag("E.pkshare", i), pkShare.Serialize(true)); + e.dumpb(tag("E.sharematch", i), pkShare == sh.GetG1Element()); + G2Element sigShare = Threshold::SignatureShare(sigCoeffs, Bytes(ids[i])); + e.dump(tag("E.sigshare.poly", i), sigShare.Serialize(true)); + G2Element sigShare2 = Threshold::Sign(sh, Bytes(h)); + e.dump(tag("E.sigshare.sign", i), sigShare2.Serialize(true)); + sigShares.push_back(sigShare2); + } + + // recover from first m shares + vector mSk(skShares.begin(), skShares.begin() + m); + vector mSig(sigShares.begin(), sigShares.begin() + m); + vector mIds; + for (int i = 0; i < m; i++) mIds.push_back(Bytes(ids[i])); + + PrivateKey skRec = Threshold::PrivateKeyRecover(mSk, mIds); + e.dump("E.skrecover", skRec.Serialize()); + e.dumpb("E.skrecover.match", skRec == coeffs[0]); + + vector mPk; + for (int i = 0; i < m; i++) mPk.push_back(skShares[i].GetG1Element()); + e.dump("E.pkrecover", Threshold::PublicKeyRecover(mPk, mIds).Serialize(true)); + + G2Element sigRec = Threshold::SignatureRecover(mSig, mIds); + e.dump("E.sigrecover", sigRec.Serialize(true)); + e.dumpb("E.sigrecover.verify", Threshold::Verify(vvec[0], Bytes(h), sigRec)); + + // recovery from a different share subset must give the same signature + vector mSig2(sigShares.begin() + 1, sigShares.begin() + 1 + m); + vector mIds2; + for (int i = 1; i < 1 + m; i++) mIds2.push_back(Bytes(ids[i])); + e.dumpb("E.sigrecover.same", + Threshold::SignatureRecover(mSig2, mIds2) == sigRec); +} + +// ------------------------------------------------------------------ section F +inline void ExtendedKeys(Emitter& e) +{ + vector seed = seedN(600); + ExtendedPrivateKey esk = ExtendedPrivateKey::FromSeed(Bytes(seed)); + e.dump("F.esk", esk.Serialize()); + e.dump("F.esk.pk.legacy", esk.GetPublicKey().Serialize(true)); + e.dump("F.esk.pk.basic", esk.GetPublicKey().Serialize(false)); + e.dump("F.esk.chaincode", esk.GetChainCode().Serialize()); + + // legacy derivation chain (fLegacy defaults true on these APIs) + ExtendedPrivateKey c1l = esk.PrivateChild(1, true); + ExtendedPrivateKey c2l = c1l.PrivateChild(0x8000002a, true); + e.dump("F.child.legacy", c2l.Serialize()); + e.dump("F.child.legacy.sk", c2l.GetPrivateKey().Serialize()); + + // modern derivation chain + ExtendedPrivateKey c1b = esk.PrivateChild(1, false); + ExtendedPrivateKey c2b = c1b.PrivateChild(0x8000002a, false); + e.dump("F.child.basic", c2b.Serialize()); + e.dump("F.child.basic.sk", c2b.GetPrivateKey().Serialize()); + + // extended public keys and public derivation + ExtendedPublicKey epkl = esk.GetExtendedPublicKey(true); + e.dump("F.epk.legacy", epkl.Serialize(true)); + ExtendedPublicKey epkb = esk.GetExtendedPublicKey(false); + e.dump("F.epk.basic", epkb.Serialize(false)); + e.dump("F.epk.child.legacy", epkl.PublicChild(1, true).Serialize(true)); + e.dump("F.epk.child.basic", epkb.PublicChild(1, false).Serialize(false)); + e.dumpb("F.pubpriv.match.legacy", + epkl.PublicChild(1, true).GetPublicKey() == + esk.PrivateChild(1, true).GetPublicKey()); + + // round-trips + e.dump("F.esk.rt", + ExtendedPrivateKey::FromBytes(Bytes(esk.Serialize())).Serialize()); + e.dump("F.epk.rt.legacy", + ExtendedPublicKey::FromBytes(Bytes(epkl.Serialize(true)), true).Serialize(true)); +} + +// ------------------------------------------------------------------ section G +inline void PrivateKeyEdges(Emitter& e) +{ + // in-range key round-trip, both modOrder values + PrivateKey sk = BasicSchemeMPL().KeyGen(seedN(700)); + vector raw = sk.Serialize(); + e.dump("G.inrange.mod0", PrivateKey::FromBytes(Bytes(raw), false).Serialize()); + e.dump("G.inrange.mod1", PrivateKey::FromBytes(Bytes(raw), true).Serialize()); + + // value >= group order: modOrder=true reduces, modOrder=false throws + vector big(32, 0xff); + try { e.dump("G.big.mod1", PrivateKey::FromBytes(Bytes(big), true).Serialize()); } + catch (const std::exception&) { e.exc("G.big.mod1"); } + try { e.dump("G.big.mod0", PrivateKey::FromBytes(Bytes(big), false).Serialize()); } + catch (const std::exception&) { e.exc("G.big.mod0"); } + + // the group order itself, and order-1 + // r = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001 + vector order = { + 0x73, 0xed, 0xa7, 0x53, 0x29, 0x9d, 0x7d, 0x48, 0x33, 0x39, 0xd8, + 0x08, 0x09, 0xa1, 0xd8, 0x05, 0x53, 0xbd, 0xa4, 0x02, 0xff, 0xfe, + 0x5b, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01}; + try { e.dump("G.order.mod1", PrivateKey::FromBytes(Bytes(order), true).Serialize()); } + catch (const std::exception&) { e.exc("G.order.mod1"); } + try { e.dump("G.order.mod0", PrivateKey::FromBytes(Bytes(order), false).Serialize()); } + catch (const std::exception&) { e.exc("G.order.mod0"); } + vector orderm1 = order; + orderm1[31] = 0x00; + try { e.dump("G.orderm1.mod0", PrivateKey::FromBytes(Bytes(orderm1), false).Serialize()); } + catch (const std::exception&) { e.exc("G.orderm1.mod0"); } + + // zero key + vector zero(32, 0x00); + try { + PrivateKey z = PrivateKey::FromBytes(Bytes(zero), false); + e.dump("G.zero.sk", z.Serialize()); + e.dump("G.zero.pk", z.GetG1Element().Serialize(false)); + } catch (const std::exception&) { e.exc("G.zero.sk"); } + + // FromSeedBIP32 + e.dump("G.bip32seed", PrivateKey::FromSeedBIP32(Bytes(seedN(701))).Serialize()); + + // DH key exchange: sk_a * pk_b == sk_b * pk_a + PrivateKey a = BasicSchemeMPL().KeyGen(seedN(702)); + PrivateKey b = BasicSchemeMPL().KeyGen(seedN(703)); + G1Element dh = a * b.GetG1Element(); + e.dump("G.dh", dh.Serialize(false)); + e.dumpb("G.dh.sym", dh == (b * a.GetG1Element())); +} + +// ------------------------------------------------------------------ section H +// Crafted byte strings through every decode path. For each: either the +// round-trip hex (proving accept + canonical re-encoding) or EXC. +inline void decodeProbe(Emitter& e, const std::string& name, const vector& in, bool fLegacy) +{ + bool isG1 = in.size() == 48; + try { + if (isG1) { + G1Element el = G1Element::FromBytes(Bytes(in), fLegacy); + e.dump(name + ".ser", el.Serialize(fLegacy)); + e.dumpb(name + ".valid", el.IsValid()); + } else { + G2Element el = G2Element::FromBytes(Bytes(in), fLegacy); + e.dump(name + ".ser", el.Serialize(fLegacy)); + e.dumpb(name + ".valid", el.IsValid()); + } + } catch (const std::exception&) { e.exc(name); } +} + +inline void decodeProbeUnchecked(Emitter& e, const std::string& name, const vector& in, bool fLegacy) +{ + bool isG1 = in.size() == 48; + try { + if (isG1) { + G1Element el = G1Element::FromBytesUnchecked(Bytes(in), fLegacy); + e.dump(name + ".ser", el.Serialize(fLegacy)); + e.dumpb(name + ".valid", el.IsValid()); + } else { + G2Element el = G2Element::FromBytesUnchecked(Bytes(in), fLegacy); + e.dump(name + ".ser", el.Serialize(fLegacy)); + e.dumpb(name + ".valid", el.IsValid()); + } + } catch (const std::exception&) { e.exc(name); } +} + +inline vector fromHex(const char* hex) +{ + vector out; + for (size_t i = 0; hex[i] && hex[i + 1]; i += 2) { + auto nyb = [](char c) -> int { + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'a' && c <= 'f') return c - 'a' + 10; + return c - 'A' + 10; + }; + out.push_back((uint8_t)((nyb(hex[i]) << 4) | nyb(hex[i + 1]))); + } + return out; +} + +inline void EdgeDecodes(Emitter& e) +{ + BasicSchemeMPL basic; + PrivateKey sk = basic.KeyGen(seedN(800)); + G1Element pk = sk.GetG1Element(); + G2Element sig = LegacySchemeMPL().Sign(sk, Bytes(hashN(800))); + + // 1. canonical infinity, both sizes, both modes + vector inf48(48, 0); + inf48[0] = 0xc0; + vector inf96(96, 0); + inf96[0] = 0xc0; + decodeProbe(e, "H.g1.inf.basic", inf48, false); + decodeProbe(e, "H.g1.inf.legacy", inf48, true); + decodeProbe(e, "H.g2.inf.basic", inf96, false); + decodeProbe(e, "H.g2.inf.legacy", inf96, true); + + // 2. non-canonical infinity (0xc0 with a trailing nonzero byte) + vector badInf48 = inf48; + badInf48[47] = 0x01; + vector badInf96 = inf96; + badInf96[95] = 0x01; + decodeProbe(e, "H.g1.badinf.basic", badInf48, false); + decodeProbe(e, "H.g1.badinf.legacy", badInf48, true); + decodeProbe(e, "H.g2.badinf.basic", badInf96, false); + decodeProbe(e, "H.g2.badinf.legacy", badInf96, true); + + // 3. all zeros + decodeProbe(e, "H.g1.zeros.basic", vector(48, 0), false); + decodeProbe(e, "H.g1.zeros.legacy", vector(48, 0), true); + decodeProbe(e, "H.g2.zeros.basic", vector(96, 0), false); + decodeProbe(e, "H.g2.zeros.legacy", vector(96, 0), true); + decodeProbeUnchecked(e, "H.g1.zeros.legacy.unchk", vector(48, 0), true); + decodeProbeUnchecked(e, "H.g2.zeros.legacy.unchk", vector(96, 0), true); + + // 4. x >= field prime (0xff filler), with/without legacy sign bit + vector ff48(48, 0xff); + vector ff96(96, 0xff); + decodeProbe(e, "H.g1.ff.basic", ff48, false); + decodeProbe(e, "H.g1.ff.legacy", ff48, true); + decodeProbe(e, "H.g2.ff.basic", ff96, false); + decodeProbe(e, "H.g2.ff.legacy", ff96, true); + vector ff48low = ff48; + ff48low[0] = 0x1f; // below prime top byte range but x still >= p + decodeProbe(e, "H.g1.fflow.legacy", ff48low, true); + + // 5. legacy bytes fed to the modern decoder and vice versa + decodeProbe(e, "H.g1.legacyAsBasic", pk.Serialize(true), false); + decodeProbe(e, "H.g1.basicAsLegacy", pk.Serialize(false), true); + decodeProbe(e, "H.g2.legacyAsBasic", sig.Serialize(true), false); + decodeProbe(e, "H.g2.basicAsLegacy", sig.Serialize(false), true); + + // 6. valid encodings with flipped sign bit (the negated point) + vector pkl = pk.Serialize(true); + pkl[0] ^= 0x80; + decodeProbe(e, "H.g1.negated.legacy", pkl, true); + vector pkb = pk.Serialize(false); + pkb[0] ^= 0x20; + decodeProbe(e, "H.g1.negated.basic", pkb, false); + + // 7. on-curve but non-subgroup points (found by scanning small x with a + // blst helper; each x satisfies y^2 = x^3 + 4 (+4i) with the point + // outside the r-torsion). Encodings below use y = the "smaller" root + // (sign bit 0), in each codec's byte layout. + // G1: x = 4 -> not in subgroup. + vector nsG1(48, 0); + nsG1[47] = 0x04; + decodeProbe(e, "H.g1.nonsub.legacy", nsG1, true); + decodeProbeUnchecked(e, "H.g1.nonsub.legacy.unchk", nsG1, true); + vector nsG1b = nsG1; + nsG1b[0] |= 0x80; // modern compression flag + decodeProbe(e, "H.g1.nonsub.basic", nsG1b, false); + decodeProbeUnchecked(e, "H.g1.nonsub.basic.unchk", nsG1b, false); + // G2: x = (1, 0) -> not in subgroup (legacy layout: c0 || c1). + vector nsG2(96, 0); + nsG2[47] = 0x01; + decodeProbe(e, "H.g2.nonsub.legacy", nsG2, true); + decodeProbeUnchecked(e, "H.g2.nonsub.legacy.unchk", nsG2, true); + // modern layout: c1 || c0 with flags on byte 0 + vector nsG2b(96, 0); + nsG2b[95] = 0x01; + nsG2b[0] |= 0x80; + decodeProbe(e, "H.g2.nonsub.basic", nsG2b, false); + decodeProbeUnchecked(e, "H.g2.nonsub.basic.unchk", nsG2b, false); + + // 8. x not on curve (no square root exists): x = 1 for G1 + vector noc(48, 0); + noc[47] = 0x01; + decodeProbe(e, "H.g1.nocurve.legacy", noc, true); + vector nocb = noc; + nocb[0] |= 0x80; + decodeProbe(e, "H.g1.nocurve.basic", nocb, false); + + // 9. modern G2 with the reserved top bits of byte 48 set + vector flag48 = sig.Serialize(false); + flag48[48] |= 0xe0; + decodeProbe(e, "H.g2.flag48.basic", flag48, false); + + // 10. wrong length + try { + G1Element::FromBytes(Bytes(vector(47, 0)), false); + e.raw("H.len47=NOEXC"); + } catch (const std::exception&) { e.exc("H.len47"); } + + // 11. legacy sign bit set on degenerate x values (exercises the + // sqrt-failure and x>=p fallback paths with stored sign = 1) + vector s1(48, 0); + s1[0] = 0x80; + s1[47] = 0x01; // x = 1, not on curve + decodeProbe(e, "H.g1.nocurve.sign.legacy", s1, true); + vector sp(48, 0xff); + sp[0] = 0x9f; // sign bit + x = 0x1fff... >= p + decodeProbe(e, "H.g1.overp.sign.legacy", sp, true); + vector g2s(96, 0); + g2s[0] = 0x80; + g2s[47] = 0x02; // x = (2, 0), sign bit set + decodeProbe(e, "H.g2.x2.sign.legacy", g2s, true); + vector g2s3(96, 0); + g2s3[0] = 0x80; + g2s3[47] = 0x03; // x = (3, 0), sign bit set + decodeProbe(e, "H.g2.x3.sign.legacy", g2s3, true); + vector g2nc(96, 0); + g2nc[47] = 0x02; // x = (2, 0), sign bit clear + decodeProbe(e, "H.g2.x2.legacy", g2nc, true); + vector g2sp(96, 0xff); + g2sp[0] = 0x9f; // c0 >= p (sign set), c1 >= p + decodeProbe(e, "H.g2.overp.sign.legacy", g2sp, true); +} + +// ------------------------------------------------------------------ section I +// Verification semantics with infinity elements and empty inputs. +inline void VerifyEdges(Emitter& e) +{ + BasicSchemeMPL basic; + LegacySchemeMPL legacy; + vector msg = {9, 9, 9}; + vector h = hashN(900); + + e.dumpb("I.basic.verify.infinf", basic.Verify(G1Element(), msg, G2Element())); + e.dumpb("I.legacy.verify.infinf", legacy.Verify(G1Element(), Bytes(h), G2Element())); + + PrivateKey sk = basic.KeyGen(seedN(900)); + G2Element sig = basic.Sign(sk, msg); + e.dumpb("I.basic.verify.infpk", basic.Verify(G1Element(), msg, sig)); + e.dumpb("I.basic.verify.infsig", basic.Verify(sk.GetG1Element(), msg, G2Element())); + + // AggregateVerify argument invariants + e.dumpb("I.aggver.empty.infsig", + basic.AggregateVerify(vector{}, vector{}, G2Element())); + e.dumpb("I.aggver.empty.realsig", + basic.AggregateVerify(vector{}, vector{}, sig)); + e.dumpb("I.aggver.mismatch", + basic.AggregateVerify(vector{sk.GetG1Element()}, vector{}, sig)); + + // VerifySecure with an infinity pubkey in the list + vector pks = {sk.GetG1Element(), G1Element()}; + e.dumpb("I.versec.withinf", basic.VerifySecure(pks, sig, Bytes(h))); +} + + +inline std::vector GenerateReferenceVectors() +{ + Emitter e; + e.raw("# dashbls reference vectors v1"); + KeysAndSignatures(e); + SecureAggregation(e); + ThresholdVectors(e); + ExtendedKeys(e); + PrivateKeyEdges(e); + EdgeDecodes(e); + VerifyEdges(e); + e.raw("# end"); + return e.lines; +} + +} // namespace bls_test_vectors + +#endif // SRC_TEST_VECTORS_HPP_ diff --git a/src/test.cpp b/src/test.cpp index ce92691a6..97730b570 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -23,6 +23,11 @@ extern "C" { #include "relic.h" } #include "test-utils.hpp" +#include "test-vectors.hpp" + +#include +#include + using std::cout; using std::endl; using std::string; @@ -1976,6 +1981,58 @@ TEST_CASE("CheckValid for Legacy") } } +// Locates test-vectors/reference.txt: an environment variable overrides +// the path the build system compiled in; source-tree-relative paths cover +// running the binary by hand. +static std::string ReferenceVectorsPath() +{ + std::vector candidates; + if (const char* env = std::getenv("DASHBLS_REFERENCE_VECTORS")) { + candidates.push_back(env); + } +#ifdef DASHBLS_REFERENCE_VECTORS + candidates.push_back(DASHBLS_REFERENCE_VECTORS); +#endif + candidates.push_back("test-vectors/reference.txt"); + candidates.push_back("../test-vectors/reference.txt"); + candidates.push_back("../../test-vectors/reference.txt"); + for (const auto& c : candidates) { + if (std::ifstream(c).good()) return c; + } + std::string tried; + for (const auto& c : candidates) tried += "\n " + c; + FAIL("reference vectors not found; tried:" << tried); + return ""; +} + +TEST_CASE("Reference vectors") +{ + // Every consensus-visible behavior of the library, compared line by line + // against the output of the relic-based implementation. + const std::string path = ReferenceVectorsPath(); + INFO("reference file: " << path); + + std::vector expected; + std::ifstream in(path); + for (std::string line; std::getline(in, line);) { + if (line.empty() || line[0] == '#') continue; + expected.push_back(line); + } + REQUIRE(expected.size() > 300); + + std::vector actual; + for (const auto& line : bls_test_vectors::GenerateReferenceVectors()) { + if (line.empty() || line[0] == '#') continue; + actual.push_back(line); + } + + REQUIRE(actual.size() == expected.size()); + for (size_t i = 0; i < expected.size(); i++) { + INFO("vector " << i + 1 << " of " << expected.size()); + REQUIRE(actual[i] == expected[i]); + } +} + int main(int argc, char* argv[]) { int result = Catch::Session().run(argc, argv); diff --git a/test-vectors/reference.txt b/test-vectors/reference.txt new file mode 100644 index 000000000..3494cfe38 --- /dev/null +++ b/test-vectors/reference.txt @@ -0,0 +1,324 @@ +# dashbls reference vectors v1 +A.sk.0=542b7d7900f6335cba45d5d7a53d59d73f26f230905bfcd6ad91ebb85eaa008a +A.pk.basic.0=9331005927ff67f0bd41be6037dcc07feea679be922e915c9be2a229c38fe5d178527877a6a66126ef4a8619277c58f0 +A.pk.legacy.0=1331005927ff67f0bd41be6037dcc07feea679be922e915c9be2a229c38fe5d178527877a6a66126ef4a8619277c58f0 +A.fp.basic.0=d4ddc775 +A.fp.legacy.0=ca704224 +A.skG2.basic.0=9131d5ad87990f02258898a22680b72d1762a084fd23f793b94477473deb23dbaf5e3a0635aa8bd373136c0bf84d4dbd03d21a5719c4b680b72be4649635262df36794ceeea24865dd55705e4370ef96346efe7795b37f27eb8ae49f134fcad0 +A.skG2.legacy.0=03d21a5719c4b680b72be4649635262df36794ceeea24865dd55705e4370ef96346efe7795b37f27eb8ae49f134fcad01131d5ad87990f02258898a22680b72d1762a084fd23f793b94477473deb23dbaf5e3a0635aa8bd373136c0bf84d4dbd +A.pk.rt.legacy.0=1331005927ff67f0bd41be6037dcc07feea679be922e915c9be2a229c38fe5d178527877a6a66126ef4a8619277c58f0 +A.pk.rt.basic.0=9331005927ff67f0bd41be6037dcc07feea679be922e915c9be2a229c38fe5d178527877a6a66126ef4a8619277c58f0 +B.lsig.legacy.0=8a3b9450bb7620dc07ff10a553ad8e55805aaf1270de68bda5c3ef581945748a3732d0bf6c44e277a92f077b53fb0cc8091006a639d501de9236351b8e6e60da58281ec4a1edab1b26b698e7150c89952ced7dec9d2a95dc492d31f68b6c47ea +B.lsig.basic.0=a91006a639d501de9236351b8e6e60da58281ec4a1edab1b26b698e7150c89952ced7dec9d2a95dc492d31f68b6c47ea0a3b9450bb7620dc07ff10a553ad8e55805aaf1270de68bda5c3ef581945748a3732d0bf6c44e277a92f077b53fb0cc8 +B.lverify.0=1 +B.lverify.bad.0=0 +B.lsig.rt.0=8a3b9450bb7620dc07ff10a553ad8e55805aaf1270de68bda5c3ef581945748a3732d0bf6c44e277a92f077b53fb0cc8091006a639d501de9236351b8e6e60da58281ec4a1edab1b26b698e7150c89952ced7dec9d2a95dc492d31f68b6c47ea +B.bsig.basic.0=8d3631d71a4f57c50f8108adb0cada2be41fba7bb1eced6e2b1757204f4103bbe41b0205cddd12a0c1f723633751eef517dc82561728b2b3482e74a4bbac58ac8851dad5247d11ad9b549690805fd078e02f39a6688e7db82f3a077c2f7dd4ab +B.bsig.legacy.0=17dc82561728b2b3482e74a4bbac58ac8851dad5247d11ad9b549690805fd078e02f39a6688e7db82f3a077c2f7dd4ab0d3631d71a4f57c50f8108adb0cada2be41fba7bb1eced6e2b1757204f4103bbe41b0205cddd12a0c1f723633751eef5 +B.bverify.0=1 +A.child.hard.0=13673dc18365d4d8d67005749ed2fba3c92dac9a259f3dac769288177d2de1ac +A.child.soft.0=43f1ad49de1fac8a337f01f27c48720cf0e453cb48cd8112c4c6aecf191c393b +A.sk.1=0d80059486148cf5dd43d6c2b42d41378e0e2cc354e328089fd5cc1eafb20549 +A.pk.basic.1=b4c69e82f075931025ea1ff15c340a0a0887f8611b7c85d02b9a1e4bcdcb22673d10a26fd3d6018e99777a302a345626 +A.pk.legacy.1=94c69e82f075931025ea1ff15c340a0a0887f8611b7c85d02b9a1e4bcdcb22673d10a26fd3d6018e99777a302a345626 +A.fp.basic.1=68f69bbe +A.fp.legacy.1=00eeb456 +A.skG2.basic.1=91005feee5588ac7887cbd365aff07a2e255cddfaff09016669f7993d68cd9a63793dcade435c0d12e875a683b3cd69a170cff1dd1e7a4e7ce4f7a3538ee76cef7b555524c2e302e4eb8ac2c759b1fa0631395df06e6eae95ca0c3aa82731576 +A.skG2.legacy.1=170cff1dd1e7a4e7ce4f7a3538ee76cef7b555524c2e302e4eb8ac2c759b1fa0631395df06e6eae95ca0c3aa8273157611005feee5588ac7887cbd365aff07a2e255cddfaff09016669f7993d68cd9a63793dcade435c0d12e875a683b3cd69a +A.pk.rt.legacy.1=94c69e82f075931025ea1ff15c340a0a0887f8611b7c85d02b9a1e4bcdcb22673d10a26fd3d6018e99777a302a345626 +A.pk.rt.basic.1=b4c69e82f075931025ea1ff15c340a0a0887f8611b7c85d02b9a1e4bcdcb22673d10a26fd3d6018e99777a302a345626 +B.lsig.legacy.1=103edb2bcadfc658095e04ccbaf682d075612fb86e2be06366fef0d1245a9ff7f87c1c8a3bc8b89a88585d9c568389ea043ae22ef8777d79719cff2cc0c777fcb16277c9f5b3a4ed782b045ecc41421b6302941ee7cfd769e645ecaea359f457 +B.lsig.basic.1=843ae22ef8777d79719cff2cc0c777fcb16277c9f5b3a4ed782b045ecc41421b6302941ee7cfd769e645ecaea359f457103edb2bcadfc658095e04ccbaf682d075612fb86e2be06366fef0d1245a9ff7f87c1c8a3bc8b89a88585d9c568389ea +B.lverify.1=1 +B.lverify.bad.1=0 +B.lsig.rt.1=103edb2bcadfc658095e04ccbaf682d075612fb86e2be06366fef0d1245a9ff7f87c1c8a3bc8b89a88585d9c568389ea043ae22ef8777d79719cff2cc0c777fcb16277c9f5b3a4ed782b045ecc41421b6302941ee7cfd769e645ecaea359f457 +B.bsig.basic.1=80f49f1b340f745c84ab7b51bbb742a6f82fbc04204398683dbcc861ed1f87fa45180c6ba9d5950347fc8d14cae6bc8f105a6d4978bf6c4333b489c656ad1eb3668f7584430a8f9c24349e6199e791e64627bde51a6af2f2905b58c12b121fb6 +B.bsig.legacy.1=105a6d4978bf6c4333b489c656ad1eb3668f7584430a8f9c24349e6199e791e64627bde51a6af2f2905b58c12b121fb600f49f1b340f745c84ab7b51bbb742a6f82fbc04204398683dbcc861ed1f87fa45180c6ba9d5950347fc8d14cae6bc8f +B.bverify.1=1 +A.child.hard.1=35bf9c36f494622db64c5173493cff599df5e765d687742cb6533b1d8c6d886d +A.child.soft.1=39827c4c7749881f819a8e5c1f4d9669d7c25c18166a71d15b633c2f89daf7e8 +A.sk.2=604f88a4ee4198b3668dc38ae604dedeb45464df19e941b57d5957a5372085d1 +A.pk.basic.2=b11bd93b3fa94ee47b60c7628a52f7731005c92dadb73f9421de7fc5ca67e816106f26d121539fbf131545c8242a8d0f +A.pk.legacy.2=911bd93b3fa94ee47b60c7628a52f7731005c92dadb73f9421de7fc5ca67e816106f26d121539fbf131545c8242a8d0f +A.fp.basic.2=919d796a +A.fp.legacy.2=0ef6fc3e +A.skG2.basic.2=835092be4610e6a7e5e131f10f58a53444c0f89fbc37250bf19157e28f0a34f211cfea8022ade9a4b8b53ae090129cf503245c6bb1356ca13f772291319fc284c1a76b396fa469bd6f32d3eccdc549ba6f95e4ca08fb3777087e006cf2caa524 +A.skG2.legacy.2=03245c6bb1356ca13f772291319fc284c1a76b396fa469bd6f32d3eccdc549ba6f95e4ca08fb3777087e006cf2caa524035092be4610e6a7e5e131f10f58a53444c0f89fbc37250bf19157e28f0a34f211cfea8022ade9a4b8b53ae090129cf5 +A.pk.rt.legacy.2=911bd93b3fa94ee47b60c7628a52f7731005c92dadb73f9421de7fc5ca67e816106f26d121539fbf131545c8242a8d0f +A.pk.rt.basic.2=b11bd93b3fa94ee47b60c7628a52f7731005c92dadb73f9421de7fc5ca67e816106f26d121539fbf131545c8242a8d0f +B.lsig.legacy.2=0c5af10db2238f74776e094771cf2d4ee5e78ac8af94a618d106b7702c0b369012e327c943f23346ca461764f48351510726329193ae51b7c98e5c215f1e79d4328d9a107d7449d9a37bc504225d0dd952cc317c36b2ff4e6e270be58583148b +B.lsig.basic.2=8726329193ae51b7c98e5c215f1e79d4328d9a107d7449d9a37bc504225d0dd952cc317c36b2ff4e6e270be58583148b0c5af10db2238f74776e094771cf2d4ee5e78ac8af94a618d106b7702c0b369012e327c943f23346ca461764f4835151 +B.lverify.2=1 +B.lverify.bad.2=0 +B.lsig.rt.2=0c5af10db2238f74776e094771cf2d4ee5e78ac8af94a618d106b7702c0b369012e327c943f23346ca461764f48351510726329193ae51b7c98e5c215f1e79d4328d9a107d7449d9a37bc504225d0dd952cc317c36b2ff4e6e270be58583148b +B.bsig.basic.2=b5a4aaf5174fc68468b7bab3b182ea6e55fb55843d17b1950383f934710cdaa6a77c4494e79ec674dfc3167bfc1d944216d3e13e435a0509da3e670ebc5aeef151f32558891e00b9e8560e47601662a8e4ea893ab20cc5e909640e33bbf41fd3 +B.bsig.legacy.2=96d3e13e435a0509da3e670ebc5aeef151f32558891e00b9e8560e47601662a8e4ea893ab20cc5e909640e33bbf41fd315a4aaf5174fc68468b7bab3b182ea6e55fb55843d17b1950383f934710cdaa6a77c4494e79ec674dfc3167bfc1d9442 +B.bverify.2=1 +A.child.hard.2=140053409896043b8b7434dc29f14d132b3cbbd139677ff397a1c10f8f364616 +A.child.soft.2=525eb8eba7332727e1b0ff22bdf6320eb401d3d5e1caebdd72b0fa8aa2f0e942 +A.sk.3=21160cba355200250e9fad6bca96ed116930dc632dce23081636fedeb106c83b +A.pk.basic.3=982f6be34861d32d8d3e5aae0395ea80bff4e25b46052f41d6453754f51cd1721d44dd9f5fb62a35ca6d367e36218d4b +A.pk.legacy.3=182f6be34861d32d8d3e5aae0395ea80bff4e25b46052f41d6453754f51cd1721d44dd9f5fb62a35ca6d367e36218d4b +A.fp.basic.3=b7fea396 +A.fp.legacy.3=f71bdf44 +A.skG2.basic.3=a9a60b869403189f7abc1abf476d8afc27e4a8502f9a4949227f03393b793050a5a16c9b89d084452ff79741fc684925188c8776bc0b799fff488f0214a3b875d543b626c18a9ac0c8b6447de08c82aa735728727458305f69f77882b9d2098a +A.skG2.legacy.3=988c8776bc0b799fff488f0214a3b875d543b626c18a9ac0c8b6447de08c82aa735728727458305f69f77882b9d2098a09a60b869403189f7abc1abf476d8afc27e4a8502f9a4949227f03393b793050a5a16c9b89d084452ff79741fc684925 +A.pk.rt.legacy.3=182f6be34861d32d8d3e5aae0395ea80bff4e25b46052f41d6453754f51cd1721d44dd9f5fb62a35ca6d367e36218d4b +A.pk.rt.basic.3=982f6be34861d32d8d3e5aae0395ea80bff4e25b46052f41d6453754f51cd1721d44dd9f5fb62a35ca6d367e36218d4b +B.lsig.legacy.3=0d58f53ad17f3877f23884799c7f8dd866e7e6feaba63da933f01e447d39b7c48d941ecab58ff845cd51aa6f1d6ce57207acf6cc920e3310dcfbc3694d8483333e2e78a578be78b645aa1cd037608bb78d05f6a8de4377c5bfa52e7f6e01bdfa +B.lsig.basic.3=87acf6cc920e3310dcfbc3694d8483333e2e78a578be78b645aa1cd037608bb78d05f6a8de4377c5bfa52e7f6e01bdfa0d58f53ad17f3877f23884799c7f8dd866e7e6feaba63da933f01e447d39b7c48d941ecab58ff845cd51aa6f1d6ce572 +B.lverify.3=1 +B.lverify.bad.3=0 +B.lsig.rt.3=0d58f53ad17f3877f23884799c7f8dd866e7e6feaba63da933f01e447d39b7c48d941ecab58ff845cd51aa6f1d6ce57207acf6cc920e3310dcfbc3694d8483333e2e78a578be78b645aa1cd037608bb78d05f6a8de4377c5bfa52e7f6e01bdfa +B.bsig.basic.3=8300e9d700c1d263e5d9caec9a3302aa8984b515bd961f25b913de852168ecc0e0dcea0913a7359bee57071208895ea812bd1ae92601ba7f2e8d6d6809330ef06dde1c8ff633cff5db38a63b5aec867d43a6e3e9bafac7aec876d6625666a5cd +B.bsig.legacy.3=12bd1ae92601ba7f2e8d6d6809330ef06dde1c8ff633cff5db38a63b5aec867d43a6e3e9bafac7aec876d6625666a5cd0300e9d700c1d263e5d9caec9a3302aa8984b515bd961f25b913de852168ecc0e0dcea0913a7359bee57071208895ea8 +B.bverify.3=1 +A.child.hard.3=58a543b659628491544fc0f85d4938820dc19d5591d53f49c8b9d950d433aff7 +A.child.soft.3=0c9bd3c62871a9e36c947f7a7ab764a69b1fdb41f9625b30bd3a5bd91484f5b6 +A.sk.4=0b23c79ec3331dd7a2d4a8708b21263330b9a9d8891a81360744a2a483ea884a +A.pk.basic.4=8564f4d30abe512e9ef8c36f903436b927feebef83329f88f50d4621d6d9b59f16316f859fe5659024cce812d9f91852 +A.pk.legacy.4=0564f4d30abe512e9ef8c36f903436b927feebef83329f88f50d4621d6d9b59f16316f859fe5659024cce812d9f91852 +A.fp.basic.4=228139d6 +A.fp.legacy.4=8710d0bc +A.skG2.basic.4=833682df2e6ae024e5d9bfb5c5dc15cb855d55e945693979abfb2078aed37421438782b7d6b17345bdac09f86563327b04f35a0374e59bc64ef3ee948dc4cd4689c4f55f298c6a5ad9703e823f853d07c958b87aa151a066d31a95cfec1da928 +A.skG2.legacy.4=04f35a0374e59bc64ef3ee948dc4cd4689c4f55f298c6a5ad9703e823f853d07c958b87aa151a066d31a95cfec1da928033682df2e6ae024e5d9bfb5c5dc15cb855d55e945693979abfb2078aed37421438782b7d6b17345bdac09f86563327b +A.pk.rt.legacy.4=0564f4d30abe512e9ef8c36f903436b927feebef83329f88f50d4621d6d9b59f16316f859fe5659024cce812d9f91852 +A.pk.rt.basic.4=8564f4d30abe512e9ef8c36f903436b927feebef83329f88f50d4621d6d9b59f16316f859fe5659024cce812d9f91852 +B.lsig.legacy.4=11fbebe8bab5c35042663c60a32b7fcc3350f2a7a0cff79cb17185def6674c1233e6915545ad43e3043ba9517c283d20198fe3bbba59fc4ed6dc89b7432a7f5188caa47ec7b08631bfe2d3d2a3a745c0ad7bca351bfbdf74e49b2f46532c99f1 +B.lsig.basic.4=998fe3bbba59fc4ed6dc89b7432a7f5188caa47ec7b08631bfe2d3d2a3a745c0ad7bca351bfbdf74e49b2f46532c99f111fbebe8bab5c35042663c60a32b7fcc3350f2a7a0cff79cb17185def6674c1233e6915545ad43e3043ba9517c283d20 +B.lverify.4=1 +B.lverify.bad.4=0 +B.lsig.rt.4=11fbebe8bab5c35042663c60a32b7fcc3350f2a7a0cff79cb17185def6674c1233e6915545ad43e3043ba9517c283d20198fe3bbba59fc4ed6dc89b7432a7f5188caa47ec7b08631bfe2d3d2a3a745c0ad7bca351bfbdf74e49b2f46532c99f1 +B.bsig.basic.4=a57f7ec0d09676c12b4fb525a1d359b7c29cc0f010682905f0a44bafc8764998c85fb885f9affeb2e256cfb3f5558c61180da06bedd87410c1f842ddcf467b04aeed09b4cb8c2eb4dbf5f502cea4683e6c35cf2475208fa3dcd852e5d290faf2 +B.bsig.legacy.4=980da06bedd87410c1f842ddcf467b04aeed09b4cb8c2eb4dbf5f502cea4683e6c35cf2475208fa3dcd852e5d290faf2057f7ec0d09676c12b4fb525a1d359b7c29cc0f010682905f0a44bafc8764998c85fb885f9affeb2e256cfb3f5558c61 +B.bverify.4=1 +A.child.hard.4=1422752ec6e3f867f5f80020bec1d84e6ade884bf4093f4d337f503417e00fdd +A.child.soft.4=10b37a4b07fa1a78786efbe3f116dfd462ba0ce94e7afa742f5a87234c3a868f +A.sk.5=0535569670f3c8c0379aea8c93b3bedf5ef16672417c281da18b957f41398d13 +A.pk.basic.5=b14d33ec70e47c7d9c7895bc4bb6d9979d81118354bb84571aed49b1dd8560a9ddbf71660d6c567bf7f391c51e0e5c45 +A.pk.legacy.5=914d33ec70e47c7d9c7895bc4bb6d9979d81118354bb84571aed49b1dd8560a9ddbf71660d6c567bf7f391c51e0e5c45 +A.fp.basic.5=1c9dbfe5 +A.fp.legacy.5=708cb0c8 +A.skG2.basic.5=8af786a3d660b1305eee25239d7c8f225fa604b23f96287745b9caee6a039ff21733bbe644df10daaf79c0ddc187bf4811d3f20781f42de6cb8fae96a560ed9f42aff7d3fee427f7bb44967dbbaff01e3efdad10b83a33076246b5819d1ef755 +A.skG2.legacy.5=11d3f20781f42de6cb8fae96a560ed9f42aff7d3fee427f7bb44967dbbaff01e3efdad10b83a33076246b5819d1ef7550af786a3d660b1305eee25239d7c8f225fa604b23f96287745b9caee6a039ff21733bbe644df10daaf79c0ddc187bf48 +A.pk.rt.legacy.5=914d33ec70e47c7d9c7895bc4bb6d9979d81118354bb84571aed49b1dd8560a9ddbf71660d6c567bf7f391c51e0e5c45 +A.pk.rt.basic.5=b14d33ec70e47c7d9c7895bc4bb6d9979d81118354bb84571aed49b1dd8560a9ddbf71660d6c567bf7f391c51e0e5c45 +B.lsig.legacy.5=116b3ec371691cdf6b75db98148553ffa14ee11df0ebced3a2e7dfe9e7fb1e4da4f322d1df36d03bf3b75ed60cc6aadc07c0606f8cd2196e6d9a8c146a6c6f2d9d78898110da264e79291c5bf2d5d0567e741bca297c7b3d001cf202185411b5 +B.lsig.basic.5=87c0606f8cd2196e6d9a8c146a6c6f2d9d78898110da264e79291c5bf2d5d0567e741bca297c7b3d001cf202185411b5116b3ec371691cdf6b75db98148553ffa14ee11df0ebced3a2e7dfe9e7fb1e4da4f322d1df36d03bf3b75ed60cc6aadc +B.lverify.5=1 +B.lverify.bad.5=0 +B.lsig.rt.5=116b3ec371691cdf6b75db98148553ffa14ee11df0ebced3a2e7dfe9e7fb1e4da4f322d1df36d03bf3b75ed60cc6aadc07c0606f8cd2196e6d9a8c146a6c6f2d9d78898110da264e79291c5bf2d5d0567e741bca297c7b3d001cf202185411b5 +B.bsig.basic.5=98e09cacf9f860bc131c401c6dc20a96cba00c581a2abf576a58ae4e0edd96d82764f95e1b0a00d6e6ad52db83adb92b16349f9b0675e0b6ffe90ec64203fce107813ae36cfb3a4c98b40580b362d22b27595d57e1f02ab67534b96b186ee2fe +B.bsig.legacy.5=16349f9b0675e0b6ffe90ec64203fce107813ae36cfb3a4c98b40580b362d22b27595d57e1f02ab67534b96b186ee2fe18e09cacf9f860bc131c401c6dc20a96cba00c581a2abf576a58ae4e0edd96d82764f95e1b0a00d6e6ad52db83adb92b +B.bverify.5=1 +A.child.hard.5=186953b6ebeb46b7d6d17f4eb6b55738a8910468d8c7a12773fa4ece4d396e33 +A.child.soft.5=36aae8999b8d557746cb7310a2291c8facd190430c6f769f60193610a5e7f570 +A.sk.6=3e54ca602df6ef98b894e98a87eddca31b87fd052cfcab698477b3ad19c84dfb +A.pk.basic.6=925b01d07a6e175a0b38f66c5bb2263f13f9800f2becbff87a73548c3ec188e08505a3bc17f046f0610b84e3c487ec3f +A.pk.legacy.6=125b01d07a6e175a0b38f66c5bb2263f13f9800f2becbff87a73548c3ec188e08505a3bc17f046f0610b84e3c487ec3f +A.fp.basic.6=910eddce +A.fp.legacy.6=08462c2f +A.skG2.basic.6=ab3a64185b1e43943f8645682b86e13882653beeee52343ced9b87556c6eb39f2b87cd0fffe68276470a4c4afc21445413753b7fee41fa154a0b76495935a97243279fd70a0baff73cd8ae88c2b387ccd358cac5d7c34af4cc7e348c0fea602d +A.skG2.legacy.6=93753b7fee41fa154a0b76495935a97243279fd70a0baff73cd8ae88c2b387ccd358cac5d7c34af4cc7e348c0fea602d0b3a64185b1e43943f8645682b86e13882653beeee52343ced9b87556c6eb39f2b87cd0fffe68276470a4c4afc214454 +A.pk.rt.legacy.6=125b01d07a6e175a0b38f66c5bb2263f13f9800f2becbff87a73548c3ec188e08505a3bc17f046f0610b84e3c487ec3f +A.pk.rt.basic.6=925b01d07a6e175a0b38f66c5bb2263f13f9800f2becbff87a73548c3ec188e08505a3bc17f046f0610b84e3c487ec3f +B.lsig.legacy.6=825a6eb2146a57893682066d783509b342fc5869ae0559330e11cb10ec3a132e9cde762c6869e67c9b83d0de2646ebac09aa2a4d7a08831cab78b6b7a57f57fad48cd1dbe3c3da0d996db69ff13fc060af2626a14bf69092def390acf6320f4d +B.lsig.basic.6=a9aa2a4d7a08831cab78b6b7a57f57fad48cd1dbe3c3da0d996db69ff13fc060af2626a14bf69092def390acf6320f4d025a6eb2146a57893682066d783509b342fc5869ae0559330e11cb10ec3a132e9cde762c6869e67c9b83d0de2646ebac +B.lverify.6=1 +B.lverify.bad.6=0 +B.lsig.rt.6=825a6eb2146a57893682066d783509b342fc5869ae0559330e11cb10ec3a132e9cde762c6869e67c9b83d0de2646ebac09aa2a4d7a08831cab78b6b7a57f57fad48cd1dbe3c3da0d996db69ff13fc060af2626a14bf69092def390acf6320f4d +B.bsig.basic.6=b8801cf2113e290828ae4c2ef629d8cb3d3a34d3b8de7dcd0170f6a070f0787941ca2ed97d561c1b069b688945efb525033f27242eace77e730ce5c0a1200b05be002b47b2a7664445537f0ad10dbf24bbf0cc0f60ccd6992df41f3030f71c2a +B.bsig.legacy.6=833f27242eace77e730ce5c0a1200b05be002b47b2a7664445537f0ad10dbf24bbf0cc0f60ccd6992df41f3030f71c2a18801cf2113e290828ae4c2ef629d8cb3d3a34d3b8de7dcd0170f6a070f0787941ca2ed97d561c1b069b688945efb525 +B.bverify.6=1 +A.child.hard.6=5cec6bb344ecd98a133343f77b84fe620d46b1468a4579c83963cc0d32be15fc +A.child.soft.6=49f50d890fa3b2d475a6ce1bb2c56f9508fe8d2662659146e3434390ddc0e94d +A.sk.7=0e3c506a5cc9be55237397674e0fe32f61e4ecb6e2c761493d89c7a17a6dcc0d +A.pk.basic.7=9661920ea04d278794e28c0767591a11bb1f4678e765354e257887bf5c8e38ad3aaa258d22ac826ecb1f2b31a23a7415 +A.pk.legacy.7=1661920ea04d278794e28c0767591a11bb1f4678e765354e257887bf5c8e38ad3aaa258d22ac826ecb1f2b31a23a7415 +A.fp.basic.7=e141846a +A.fp.legacy.7=c4d6d99d +A.skG2.basic.7=86d69b68a97adf54d024ce7fef27dbc0b3f08b993daec1434b5b8f77b0f09614e0ff35c5e24b4e3b57c378e5370e2f3715847268902520804e69b0c74f7744ccefed304bbf4f06103874fba5468add684faf46d5719fdb2570e398cef37e64cb +A.skG2.legacy.7=15847268902520804e69b0c74f7744ccefed304bbf4f06103874fba5468add684faf46d5719fdb2570e398cef37e64cb06d69b68a97adf54d024ce7fef27dbc0b3f08b993daec1434b5b8f77b0f09614e0ff35c5e24b4e3b57c378e5370e2f37 +A.pk.rt.legacy.7=1661920ea04d278794e28c0767591a11bb1f4678e765354e257887bf5c8e38ad3aaa258d22ac826ecb1f2b31a23a7415 +A.pk.rt.basic.7=9661920ea04d278794e28c0767591a11bb1f4678e765354e257887bf5c8e38ad3aaa258d22ac826ecb1f2b31a23a7415 +B.lsig.legacy.7=946ba1386b9b458466890d857082710507ec67a1e055caa977799a92278fda373155e0b9fefb4fb40ede46112c1ccdf70035ec9fabb85bc7e8493fbc4f892d0cd9ef73ef86656a000bc95eaf980ec76a0db375c4165016bf7fc72a96dcd6ac0f +B.lsig.basic.7=a035ec9fabb85bc7e8493fbc4f892d0cd9ef73ef86656a000bc95eaf980ec76a0db375c4165016bf7fc72a96dcd6ac0f146ba1386b9b458466890d857082710507ec67a1e055caa977799a92278fda373155e0b9fefb4fb40ede46112c1ccdf7 +B.lverify.7=1 +B.lverify.bad.7=0 +B.lsig.rt.7=946ba1386b9b458466890d857082710507ec67a1e055caa977799a92278fda373155e0b9fefb4fb40ede46112c1ccdf70035ec9fabb85bc7e8493fbc4f892d0cd9ef73ef86656a000bc95eaf980ec76a0db375c4165016bf7fc72a96dcd6ac0f +B.bsig.basic.7=ae7378473f2e89255e78676f4ee798ea8e169551dfcc8b78cd564dbdf5d1495c0145e95c7e5d0df191c955a40ba033b805363357f176bd8b84063ae47bf7e3a273a2644ba437a9c496a66761a0693305b3249f588dd1e45feef9d33e1030c076 +B.bsig.legacy.7=85363357f176bd8b84063ae47bf7e3a273a2644ba437a9c496a66761a0693305b3249f588dd1e45feef9d33e1030c0760e7378473f2e89255e78676f4ee798ea8e169551dfcc8b78cd564dbdf5d1495c0145e95c7e5d0df191c955a40ba033b8 +B.bverify.7=1 +A.child.hard.7=6be06280ee13d42a1d9f12b5d3e9b429641ede55b3bad11a27948123e89d8913 +A.child.soft.7=1605a13324f75c0755fe03bfb1a9e9478586c12a54332de98ec4658dc233ce14 +C.aggsec.legacy.1=17b20ce50a20f09a4d807d4c0db622d13be8902380e908a9304d21d03520cb9712e8fb478bd4eb35477298c05ad5069610688edde3431521fa64f24c6e6fad0b38fd77ae2edd71871948273c6a17efad03b2f908fa9717935fc7ae68ca7421ec +C.versec.legacy.1=1 +C.aggsec.basic.1=98103b71225c000a8e08507ac38248bc97ff96c4ef0a0ecb610ba97798df58f741421304b46dbe709d891640bdc797d316e5b1fe117273391a37e981838677ce2185afe88e4f29a51ceb6d7c9a7f22062e8d4029345f2f37a473b2a8055ed848 +C.versec.basic.1=1 +C.versec.legacy.bad.1=0 +C.agg.G2.1=a941eab017de217c825e0724f3ec1bfbfd90ce7628f5d3bbf0806e0efdfa806fa746c178da66fb2a8d9a587ef0bab4c91774469f443f513972a0c59ae55278c32e1caf9c66d52e708e53ef2828c48f5c4b830d256c2ff1b155730588dfc9adeb +C.agg.G1.1=9503c392ceae7db1e2f247fb6148b4211708a74491989916daeec3c34bdd2803a98fc3a43976daaab00903211b1d1c46 +C.agg.sk.1=51542fee7895e4987de3b93ff52af9bb129dc076f0dcb8afe5d93460f2a6c1b7 +C.aggsec.legacy.2=0ca923fd169db16838c09804d7150f2bdde930d17040bc39c553baf9336527b15cbe83aa841b9f902a203521e7bc60220a9c9e1ddf0677564be73f7b8f9ce2a52c28c910eac2baba3ef3310fad7eaa8baf9465bfc9104f0ac08cd9da8b41b70e +C.versec.legacy.2=1 +C.aggsec.basic.2=a165590855562a1437e0d5fce7dfb1373a07fe09bdb33cf5f82b2e8d3b809ce7a8e9ae47eb0602181d474866958d51641816952a467643164dab9aa92567dac5449943f8464d0760740fcbb54bd758cc80e232032f56f3df269460d2cd7b807c +C.versec.basic.2=1 +C.versec.legacy.bad.2=0 +C.agg.G2.2=ab45f84e0c325239430f0584bf220bbabcff667d308d39c1fcd827f8c7f105e8210c20ca619ad9084984914e35fc0d1f082b5675dcfd07041f6ee1564de37b678a4b68ee51db8977bdc79f4ba1c9d49cccef35b1de0b7e8494c9cc5fa233edd0 +C.agg.G1.2=a5425f1689e33cd676a112efdd5aa80df7b137fad1db3c690bb2213745dd9a25c51d35fce39de1eaf5e268ccfdcb9163 +C.agg.sk.2=4e1643205fe0ed6f883f3d803c4742a2db0b7826aa523e5bc62a18c766d698df +C.aggsec.legacy.3=87b142eb28bb97ed9839900dbcefe90a992b4cd63468dd13d3dc8bcef394f0e77715f8da3fd62d3847ab845b2035a880193d116414b1a51a43edce8b2ccc4ee08f4492d35e19f9c35fe76cf73598afc085e62af9a6e52927c8f4d8544f9887f4 +C.versec.legacy.3=1 +C.aggsec.basic.3=8959bdf6c216c1c50eee30aa5ccd21196c058e3e28069cc70d86f4dc1608bbf9b791db866fb23ee816acd9b8ed3d3964174a16f05c9afc9cdc51790e8bf7a4ea0b4aef4645119a041ff97fa8500d6167c0db4743aba27f454853451f7229a287 +C.versec.basic.3=1 +C.versec.legacy.bad.3=0 +C.agg.G2.3=85b735e626a9322824cdb16d704db491330aa37239153a833c212f5404a1028f97d8339d437d7cdf06749330639c6ea21437d0bc3b3e6e1d411ed606e1943576c462abf6d572c9eccabdaec263073748421f84497f775a93631ccb51364f528f +C.agg.G1.3=b45111e7f20ff82bbfce790f2abeefebb885844423ed1fd37b43904b7b47454ff3c30ac47d4969c615af7316b1f41ffb +C.agg.sk.3=1d3b4ed79d0c0001f1bb2ded98523121bedbeaa37ac4fceb0f0bfdf4d8963849 +C.aggsec.legacy.10=0eab90239c026690d3ba0ac74c69f28420931128800ff1cfa7c64668600b99e7f60853e0f2d22b4d6cf07d97428cd0411256b05ad14e7ecae1372673d5c00db1880874016ea8a2ed6eb34655297e7883e6e023f28a6aa206e0fd0294bff532fa +C.versec.legacy.10=1 +C.aggsec.basic.10=a0368e7182619f7569d087c364199db4c5c20ba3e090706a3767ceb5dc054afe66fb4835b487eb17f67f36972b5e2d660c207b224abf7b30bb7db3bd16ebe0d1a87fac41370d82656af3e907293e954a884411765caf76708008ed2dd9b25622 +C.versec.basic.10=1 +C.versec.legacy.bad.10=0 +C.agg.G2.10=82974b5dafcc908dd00b725f120e9fa3ba1ec265019145a898b6ab3ddebb6f0b1a8c02827e04ee33dca6f098799ba76710b0cb5bf3b125ccd295abf5f3eca6fa272be6d727a591b32208b33618212368a15035b7e97edd2e29112e91bf61b651 +C.agg.G1.10=86689658c56d1d945a480cc1a11cb77ec19bce8fd3f8f6f5dbb1db20330aac73e78268a66e512b5ddd98fffc20bc7bbc +C.agg.sk.10=4972fe548df20e3ede53ebe27f0ab0c1bfe742903357d88d48301c6b7c9a77b6 +D.lagg=8acadc114967f7a21953644c19dc9ecbfdbdb0233d484862e2816ef3e6cc94177c85d26a3d8bc10a79ba7fced80226530f01ee9e0f441c83c8b50bf5e03be1f1dbfcc7d660d6d968c738693574f144dc1c5001a221bdf16d7127cb828e58003b +D.laggverify=1 +D.laggverify.bad=0 +E.mastersig=99a667d463e60f2e2b3feecb2a04061692faa66c543bd4eb36c5077794b2bfe24200aa1709fa72408a40a8dfe8a00d8c0d6d35dbce43924eba4354c109b6370a722f67bed82b4d1d984572d7817797477cd34aa415d20c996d573d5ed6043574 +E.masterverify=1 +E.skshare.0=2fe9aa3dc24ee2b902c2632305e5e9f9b667cb721239b1eeaec32775235696ce +E.pkshare.0=93ac3b88ca6f2a31eb5929c590692f415b3e9cb4dabb70bf9493a6858e1fe86d89ed2363f2098d0888e3fc601947fda0 +E.sharematch.0=1 +E.sigshare.poly.0=8c2810b9171817b12be33be0d8caa159785f05c30695aacba1cfac14bd4b6c4a6879ebc91a471c1605f608e2e223ce62044251375a33a8c918177fc950496fbfd4832d22f8ba8c4b391b74113703e82828d60eb468be93e150eee50bf0a0927e +E.sigshare.sign.0=8c2810b9171817b12be33be0d8caa159785f05c30695aacba1cfac14bd4b6c4a6879ebc91a471c1605f608e2e223ce62044251375a33a8c918177fc950496fbfd4832d22f8ba8c4b391b74113703e82828d60eb468be93e150eee50bf0a0927e +E.skshare.1=6d7a99964fe9909c9a713f88392b32af635db3daec83b4538c6fc3228ecdf513 +E.pkshare.1=8e333381aedd9b63aaf2e019e197e6983ca3749b8561e21485bc6b7a4bef92d0ecd50481bf95832d87d3b2bd37d3a141 +E.sharematch.1=1 +E.sigshare.poly.1=860e104d01a3d7c3af84995bdd1574c4292ea7e5373d22ccd590ac33acaa91d2f8d119fb2f4980ca4fee9ec66179f9f10f053bd123ec34abdd0e181d76bbb795459f65c1b1404904aa6d76593561b67b2c3f29d0e965533161a43e1683e4d1ea +E.sigshare.sign.1=860e104d01a3d7c3af84995bdd1574c4292ea7e5373d22ccd590ac33acaa91d2f8d119fb2f4980ca4fee9ec66179f9f10f053bd123ec34abdd0e181d76bbb795459f65c1b1404904aa6d76593561b67b2c3f29d0e965533161a43e1683e4d1ea +E.skshare.2=4a691d73ddb6e5236c82bab66d69ba08c340e25aad705836792c66a8b8b2391f +E.pkshare.2=83ad1776d2dfec1bde34672dd518bd59c3861e92f374fd34e11bfc454a29d7c92536bd0e4c48288ab704211f5053c1c5 +E.sharematch.2=1 +E.sigshare.poly.2=12e5310660dfc34e159a5910db2b2b517936acde9eadd3c954c572c3f7814a07a0350ca68f56cc4277fd9784d78182bd04588688bd1c842992e941f30939f2bc4325237ebf4b30a490473878636f72e7cca9036895141d01ccef721da90fa3ff +E.sigshare.sign.2=12e5310660dfc34e159a5910db2b2b517936acde9eadd3c954c572c3f7814a07a0350ca68f56cc4277fd9784d78182bd04588688bd1c842992e941f30939f2bc4325237ebf4b30a490473878636f72e7cca9036895141d01ccef721da90fa3ff +E.skshare.3=296fdf7a3c8e8d9a1d80009e1e54c95ce84bc381e63236e500729d4a6a5f0d0f +E.pkshare.3=893bbd2c6b4d595cdac30778e438a1b95d16eb1909b8bcc28148848507d07e98793d4c0368d328d45a5355df1f2253da +E.sharematch.3=1 +E.sigshare.poly.3=039b5d75dfad13ce4392deaa7d207a12e0a658dd5f34f66098453e60b07b38fbc7b3271d14697e6bfd56d6053a697237069259997fd43f322cb917f7fed360cbacbe2a8582358c954c5381cdc442f8323394cab23d4ce0c2745ca6e45aa99c8b +E.sigshare.sign.3=039b5d75dfad13ce4392deaa7d207a12e0a658dd5f34f66098453e60b07b38fbc7b3271d14697e6bfd56d6053a697237069259997fd43f322cb917f7fed360cbacbe2a8582358c954c5381cdc442f8323394cab23d4ce0c2745ca6e45aa99c8b +E.skshare.4=57230fd28ec31c1a4c4a5e378c6fa748d6e68772fe83d35477bb6539876fab15 +E.pkshare.4=91edd2ed20fa5ef43bf796785e0ca8ab1d6cdb4ae63aba67ffccc0f6911c1214f81a62e23175f46b767681cf92cb25f1 +E.sharematch.4=1 +E.sigshare.poly.4=97c77544f1db5118e955e892c45d1895724b77e99efd420615f54f4d4d5b7b57f668926fa45be02190d269a2354cfaf30d55b722c035e19354e593015ccf0bd71d2c7e4c35e4cfb951bd6ff5f88008014f877a44f5f9d95d01fd1b1e570a3b15 +E.sigshare.sign.4=97c77544f1db5118e955e892c45d1895724b77e99efd420615f54f4d4d5b7b57f668926fa45be02190d269a2354cfaf30d55b722c035e19354e593015ccf0bd71d2c7e4c35e4cfb951bd6ff5f88008014f877a44f5f9d95d01fd1b1e570a3b15 +E.skrecover=6c04e5af86f2ddeaf6d1d4f8bbd4a1217b29abd4517d4619dada7ec087ed6f6f +E.skrecover.match=1 +E.pkrecover=8d713f7f50fe5d3e2c0335c3d3c0ceb2a1689eb3c2a4436c3a11898b8b797818aae1316825542790d1b0b2cafffb9150 +E.sigrecover=99a667d463e60f2e2b3feecb2a04061692faa66c543bd4eb36c5077794b2bfe24200aa1709fa72408a40a8dfe8a00d8c0d6d35dbce43924eba4354c109b6370a722f67bed82b4d1d984572d7817797477cd34aa415d20c996d573d5ed6043574 +E.sigrecover.verify=1 +E.sigrecover.same=1 +F.esk=000000010000000000000000004c3b08d7413ef3f4d39922793c4b20c0bcfb26db531c9e32a505cf763c2794953757b1115e7110046606404b38e4654adc4c2a52a5337deca0794080d3f92921 +F.esk.pk.legacy=89efc5280e56a745dcfd4571d1e9b06edf0441c5fd8640f5efdf5554bcdcb07e7d1d35e09bf9ebaabe6faad282c838d4 +F.esk.pk.basic=a9efc5280e56a745dcfd4571d1e9b06edf0441c5fd8640f5efdf5554bcdcb07e7d1d35e09bf9ebaabe6faad282c838d4 +F.esk.chaincode=4c3b08d7413ef3f4d39922793c4b20c0bcfb26db531c9e32a505cf763c279495 +F.child.legacy=00000001023e497ad38000002aff185013e7118856e4bcb6e2dc9608cab5219747c8037a889789b1be76feb6df1250aa4f4cb5f7e58f7b5f942eb2f2b99b36eacb3e6e65c4766ce956e628e62a +F.child.legacy.sk=1250aa4f4cb5f7e58f7b5f942eb2f2b99b36eacb3e6e65c4766ce956e628e62a +F.child.basic=000000010203f3628b8000002a447c89f7c80822b8661513be9c7fdf172a91faf71300f68fa39cfdea0ae3740f625c4936e02f4abcee9cfbd59e403428b9937f94bd0908cf676c3f63bc7713e0 +F.child.basic.sk=625c4936e02f4abcee9cfbd59e403428b9937f94bd0908cf676c3f63bc7713e0 +F.epk.legacy=000000010000000000000000004c3b08d7413ef3f4d39922793c4b20c0bcfb26db531c9e32a505cf763c27949589efc5280e56a745dcfd4571d1e9b06edf0441c5fd8640f5efdf5554bcdcb07e7d1d35e09bf9ebaabe6faad282c838d4 +F.epk.basic=000000010000000000000000004c3b08d7413ef3f4d39922793c4b20c0bcfb26db531c9e32a505cf763c279495a9efc5280e56a745dcfd4571d1e9b06edf0441c5fd8640f5efdf5554bcdcb07e7d1d35e09bf9ebaabe6faad282c838d4 +F.epk.child.legacy=00000001010b9a9e8000000001d84919fa70508fefd47576f19b437b3bb2049039740d83de455742dae48c96ab8a9f848f28b295903845d5f860c9031899e4f1a6d092edacdaec28f64a7fd9b94679b3faaf0ecf6b09f70b1d3ed5555f +F.epk.child.basic=00000001010b9a9e80000000012428f63e5f4b2e25871336f34ecebbd6e0e648d8b4a02ec033b575a391dc6b598cf85b6e8d78a797985402debeb2a1b823ab7c98ae20ae1609aaadd70309a4d40ef5cad6a6f93cb9af0ec2045dba96cf +F.pubpriv.match.legacy=1 +F.esk.rt=000000010000000000000000004c3b08d7413ef3f4d39922793c4b20c0bcfb26db531c9e32a505cf763c2794953757b1115e7110046606404b38e4654adc4c2a52a5337deca0794080d3f92921 +F.epk.rt.legacy=000000010000000000000000004c3b08d7413ef3f4d39922793c4b20c0bcfb26db531c9e32a505cf763c27949589efc5280e56a745dcfd4571d1e9b06edf0441c5fd8640f5efdf5554bcdcb07e7d1d35e09bf9ebaabe6faad282c838d4 +G.inrange.mod0=57c1d1f8ef9ad067fef7d0056bdfec87f51c28b65dd56fa3cbcc0f1afc984725 +G.inrange.mod1=57c1d1f8ef9ad067fef7d0056bdfec87f51c28b65dd56fa3cbcc0f1afc984725 +G.big.mod1=1824b159acc5056f998c4fefecbc4ff55884b7fa0003480200000001fffffffd +G.big.mod0=EXC +G.order.mod1=0000000000000000000000000000000000000000000000000000000000000000 +G.order.mod0=73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001 +G.orderm1.mod0=73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000 +G.zero.sk=0000000000000000000000000000000000000000000000000000000000000000 +G.zero.pk=c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +G.bip32seed=193afdb206c666de5cfba5d5a235477d269b1634ec36cfa1a3167cb47727bf25 +G.dh=a11d215631668626a3396afbfd38d850a2c18ffa90bd589714e40112b2592064c581eb8205fdc7bcd860e23b15e487cf +G.dh.sym=1 +H.g1.inf.basic.ser=c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +H.g1.inf.basic.valid=1 +H.g1.inf.legacy.ser=c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +H.g1.inf.legacy.valid=1 +H.g2.inf.basic.ser=c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +H.g2.inf.basic.valid=1 +H.g2.inf.legacy=EXC +H.g1.badinf.basic=EXC +H.g1.badinf.legacy=EXC +H.g2.badinf.basic=EXC +H.g2.badinf.legacy=EXC +H.g1.zeros.basic=EXC +H.g1.zeros.legacy.ser=000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +H.g1.zeros.legacy.valid=0 +H.g2.zeros.basic=EXC +H.g2.zeros.legacy.ser=000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +H.g2.zeros.legacy.valid=0 +H.g1.zeros.legacy.unchk.ser=000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +H.g1.zeros.legacy.unchk.valid=0 +H.g2.zeros.legacy.unchk.ser=000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +H.g2.zeros.legacy.unchk.valid=0 +H.g1.ff.basic=EXC +H.g1.ff.legacy=EXC +H.g2.ff.basic=EXC +H.g2.ff.legacy=EXC +H.g1.fflow.legacy.ser=000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +H.g1.fflow.legacy.valid=0 +H.g1.legacyAsBasic=EXC +H.g1.basicAsLegacy.ser=98138bb3b9cb34f03c9deac8e0928385873a8fe418a5f371f10cba0b6988a7854d0b48c1ffc50314bddea964b90b6be3 +H.g1.basicAsLegacy.valid=1 +H.g2.legacyAsBasic=EXC +H.g2.basicAsLegacy.ser=801627cb11cf0498af70b04e84ec07a615c01219b0693be59fb93e403f7937ddf26efe486c4b8edffdea8f136203b99e15240cfb56882628ef6d4b2cad852cbe23b882b2aab625f0e82f355ce1a4fe129b589d97ca3e17c5a36e514f93cfff32 +H.g2.basicAsLegacy.valid=0 +H.g1.negated.legacy.ser=98138bb3b9cb34f03c9deac8e0928385873a8fe418a5f371f10cba0b6988a7854d0b48c1ffc50314bddea964b90b6be3 +H.g1.negated.legacy.valid=1 +H.g1.negated.basic.ser=b8138bb3b9cb34f03c9deac8e0928385873a8fe418a5f371f10cba0b6988a7854d0b48c1ffc50314bddea964b90b6be3 +H.g1.negated.basic.valid=1 +H.g1.nonsub.legacy.ser=000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004 +H.g1.nonsub.legacy.valid=0 +H.g1.nonsub.legacy.unchk.ser=000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004 +H.g1.nonsub.legacy.unchk.valid=0 +H.g1.nonsub.basic=EXC +H.g1.nonsub.basic.unchk.ser=800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004 +H.g1.nonsub.basic.unchk.valid=0 +H.g2.nonsub.legacy.ser=000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +H.g2.nonsub.legacy.valid=0 +H.g2.nonsub.legacy.unchk.ser=000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +H.g2.nonsub.legacy.unchk.valid=0 +H.g2.nonsub.basic=EXC +H.g2.nonsub.basic.unchk=EXC +H.g1.nocurve.legacy.ser=000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 +H.g1.nocurve.legacy.valid=0 +H.g1.nocurve.basic=EXC +H.g2.flag48.basic=EXC +H.len47=EXC +H.g1.nocurve.sign.legacy.ser=800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 +H.g1.nocurve.sign.legacy.valid=0 +H.g1.overp.sign.legacy.ser=800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +H.g1.overp.sign.legacy.valid=0 +H.g2.x2.sign.legacy.ser=800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +H.g2.x2.sign.legacy.valid=0 +H.g2.x3.sign.legacy.ser=000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +H.g2.x3.sign.legacy.valid=0 +H.g2.x2.legacy.ser=000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +H.g2.x2.legacy.valid=0 +H.g2.overp.sign.legacy.ser=000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +H.g2.overp.sign.legacy.valid=0 +I.basic.verify.infinf=0 +I.legacy.verify.infinf=0 +I.basic.verify.infpk=0 +I.basic.verify.infsig=0 +I.aggver.empty.infsig=1 +I.aggver.empty.realsig=0 +I.aggver.mismatch=0 +I.versec.withinf=0 +# end diff --git a/tools/fuzzgen.cpp b/tools/fuzzgen.cpp new file mode 100644 index 000000000..0652cc3ea --- /dev/null +++ b/tools/fuzzgen.cpp @@ -0,0 +1,158 @@ +// Differential fuzz driver for the relic -> blst migration. +// +// Uses only the public dashbls API; compile the same source against the old +// and new libraries, run with the same arguments, and diff the output. +// +// fuzzgen +// +// Covers: random byte-string decoding in every mode (the dominant +// consensus-facing attack surface), decode/re-encode round trips, and +// legacy + basic sign/verify/aggregate cycles on derived keys. + +#include +#include +#include +#include +#include + +#include + +using namespace bls; +using std::vector; + +// xorshift128+ so both builds see identical byte streams with no libc +// dependency +static uint64_t s0, s1; +static uint64_t rng() +{ + uint64_t x = s0; + const uint64_t y = s1; + s0 = y; + x ^= x << 23; + s1 = x ^ y ^ (x >> 17) ^ (y >> 26); + return s1 + y; +} + +static void fill(vector& v) +{ + for (size_t i = 0; i < v.size(); i += 8) { + const uint64_t r = rng(); + for (size_t j = 0; j < 8 && i + j < v.size(); j++) + v[i + j] = (uint8_t)(r >> (8 * j)); + } +} + +static void dump(const vector& v) +{ + for (auto b : v) printf("%02x", b); +} + +static void probeG1(const vector& in, bool fLegacy) +{ + try { + G1Element e = G1Element::FromBytes(Bytes(in), fLegacy); + dump(e.Serialize(fLegacy)); + printf(" v%d", (int)e.IsValid()); + } catch (const std::exception&) { + printf("EXC"); + } + printf(";"); +} + +static void probeG2(const vector& in, bool fLegacy) +{ + try { + G2Element e = G2Element::FromBytes(Bytes(in), fLegacy); + dump(e.Serialize(fLegacy)); + printf(" v%d", (int)e.IsValid()); + } catch (const std::exception&) { + printf("EXC"); + } + printf(";"); +} + +int main(int argc, char** argv) +{ + if (argc != 3) { + fprintf(stderr, "usage: fuzzgen \n"); + return 2; + } + s0 = strtoull(argv[1], nullptr, 0) * 0x9E3779B97F4A7C15ULL + 1; + s1 = s0 ^ 0xD1B54A32D192ED03ULL; + const long iters = strtol(argv[2], nullptr, 0); + + LegacySchemeMPL legacy; + BasicSchemeMPL basic; + + for (long i = 0; i < iters; i++) { + printf("i%ld: ", i); + + // 1. random decodes: fully random, and "plausible" (top bits biased + // into the interesting flag space) + vector b48(48), b96(96); + fill(b48); + fill(b96); + switch (i % 4) { + case 0: break; + case 1: b48[0] &= 0x1f; b96[0] &= 0x1f; break; // no flags + case 2: b48[0] |= 0x80; b96[0] |= 0x80; break; // sign/compress + case 3: b48[0] = (uint8_t)(rng() & 0xe0); b96[0] = b48[0]; break; + } + probeG1(b48, false); + probeG1(b48, true); + probeG2(b96, false); + probeG2(b96, true); + + // 2. valid-point cross-mode round trips + vector seed(32); + fill(seed); + PrivateKey sk = basic.KeyGen(seed); + G1Element pk = sk.GetG1Element(); + probeG1(pk.Serialize(true), true); + probeG1(pk.Serialize(false), true); // modern bytes into legacy codec + + // 3. legacy + basic signing over a random 32-byte hash + vector h(32); + fill(h); + G2Element lsig = legacy.Sign(sk, Bytes(h)); + dump(lsig.Serialize(true)); + printf(" %d", (int)legacy.Verify(pk, Bytes(h), lsig)); + printf(";"); + G2Element bsig = basic.Sign(sk, Bytes(h)); + dump(bsig.Serialize(false)); + printf(" %d", (int)basic.Verify(pk, Bytes(h), bsig)); + printf(";"); + + // 4. secure aggregation with 3 keys + vector sks; + vector pks; + vector lsigs; + for (int k = 0; k < 3; k++) { + vector s2(32); + fill(s2); + sks.push_back(basic.KeyGen(s2)); + pks.push_back(sks.back().GetG1Element()); + lsigs.push_back(legacy.Sign(sks.back(), Bytes(h))); + } + G2Element agg = legacy.AggregateSecure(pks, lsigs, Bytes(h)); + dump(agg.Serialize(true)); + printf(" %d", (int)legacy.VerifySecure(pks, agg, Bytes(h))); + printf(";"); + + // 5. threshold 2-of-3 recovery + vector ids; + vector> idStore(3, vector(32)); + for (auto& id : idStore) fill(id); + PrivateKey sh0 = Threshold::PrivateKeyShare({sks[0], sks[1]}, Bytes(idStore[0])); + PrivateKey sh1 = Threshold::PrivateKeyShare({sks[0], sks[1]}, Bytes(idStore[1])); + try { + PrivateKey rec = Threshold::PrivateKeyRecover( + {sh0, sh1}, {Bytes(idStore[0]), Bytes(idStore[1])}); + dump(rec.Serialize()); + } catch (const std::exception&) { + printf("EXC"); + } + printf("\n"); + } + return 0; +} diff --git a/tools/vectorgen.cpp b/tools/vectorgen.cpp new file mode 100644 index 000000000..ff38ae4a5 --- /dev/null +++ b/tools/vectorgen.cpp @@ -0,0 +1,22 @@ +// Regenerates test-vectors/reference.txt. +// +// Build against the library whose behavior is to become the reference (the +// checked-in file was produced by the relic-based implementation) and +// redirect the output: +// +// ./build/src/vectorgen > test-vectors/reference.txt +// +// runtest compares the current library against the file; this tool only +// generates data. + +#include + +#include "test-vectors.hpp" + +int main() +{ + for (const std::string& line : bls_test_vectors::GenerateReferenceVectors()) { + printf("%s\n", line.c_str()); + } + return 0; +} From f683a06f6553ff5ea66e867d9bc1e70043ca3258 Mon Sep 17 00:00:00 2001 From: Amine Khaldi Date: Sat, 22 Apr 2023 23:06:55 +0100 Subject: [PATCH 07/36] Adapt to the most recent version of emsdk. (#362) --- src/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e080a3db9..7dff5982f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -43,6 +43,9 @@ if(BUILD_BLS_TESTS) include(${CMAKE_CURRENT_SOURCE_DIR}/../depends/catch2/CMakeLists.txt) add_executable(runtest test.cpp) + if(EMSCRIPTEN) + target_link_options(runtest PRIVATE "-sEXPORTED_FUNCTIONS=_malloc") + endif() target_link_libraries(runtest PRIVATE dashbls PRIVATE catch2) From 73eb5bb86053350dbd369ab7426567924f80404c Mon Sep 17 00:00:00 2001 From: William Allen Date: Tue, 20 Jun 2023 18:39:30 -0500 Subject: [PATCH 08/36] catchup: into main from long_lived/blastwjb @ 95c2f65073c366a3621e304adc066b7e675cf907 (#409) Source hash: 95c2f65073c366a3621e304adc066b7e675cf907 Remaining commits: 0 --- .github/workflows/build-test.yaml | 1 - .github/workflows/stale-issue.yml | 0 .gitignore | 9 - CMakeLists.txt | 99 +-- README.md | 10 +- binds/python/CMakeLists.txt | 2 +- binds/python/pythonbindings.cpp | 29 - cmake_modules/Findgmp.cmake | 103 --- include/dashbls/bls.hpp | 2 - include/dashbls/elements.hpp | 103 ++- include/dashbls/hdkeys.hpp | 141 ++-- include/dashbls/hkdf.hpp | 12 +- include/dashbls/privatekey.hpp | 8 +- include/dashbls/schemes.hpp | 242 ++++--- include/dashbls/test-utils.hpp | 8 +- include/dashbls/util.hpp | 35 +- js-bindings/CMakeLists.txt | 2 - js-bindings/blsjs.d.ts | 9 - js-bindings/helpers.cpp | 27 - js-bindings/helpers.h | 9 - js-bindings/jsbindings.cpp | 6 - js-bindings/tests/test.js | 8 - js-bindings/wrappers/BignumWrapper.cpp | 48 -- js-bindings/wrappers/BignumWrapper.h | 42 -- js-bindings/wrappers/G1ElementWrapper.cpp | 10 - js-bindings/wrappers/G1ElementWrapper.h | 3 - js-bindings/wrappers/G2ElementWrapper.cpp | 10 - js-bindings/wrappers/G2ElementWrapper.h | 3 - src/CMakeLists.txt | 76 +- src/bls.cpp | 36 - src/elements.cpp | 439 ++++++------ src/privatekey.cpp | 142 ++-- src/schemes.cpp | 442 +++++++----- src/test-bench.cpp | 37 +- src/test.cpp | 815 +++++++++++++--------- 35 files changed, 1435 insertions(+), 1533 deletions(-) mode change 100755 => 100644 .github/workflows/stale-issue.yml delete mode 100644 cmake_modules/Findgmp.cmake delete mode 100644 js-bindings/wrappers/BignumWrapper.cpp delete mode 100644 js-bindings/wrappers/BignumWrapper.h diff --git a/.github/workflows/build-test.yaml b/.github/workflows/build-test.yaml index 8b97fad98..98a455bc9 100644 --- a/.github/workflows/build-test.yaml +++ b/.github/workflows/build-test.yaml @@ -44,7 +44,6 @@ jobs: - name: Prepare build system for macOS if: startsWith(matrix.os, 'macos') run: | - ls -l export MACOSX_DEPLOYMENT_TARGET=10.14 brew install gmp pkg-config diff --git a/.github/workflows/stale-issue.yml b/.github/workflows/stale-issue.yml old mode 100755 new mode 100644 diff --git a/.gitignore b/.gitignore index f968c1dbe..ec8e23435 100644 --- a/.gitignore +++ b/.gitignore @@ -17,15 +17,6 @@ main .o obj/ src/*.o -depends/relic/CTestTestfile.cmake -depends/relic/bench/CTestTestfile.cmake -depends/relic/bin -depends/relic/include/relic_conf.h -depends/relic/include/relic_conf.h.in -depends/relic/include/relic_conf.h.old -depends/relic/include/stamp-h1 -depends/relic/test/CTestTestfile.cmake -contrib/gmp-6.1.2/ .idea .vscode diff --git a/CMakeLists.txt b/CMakeLists.txt index a224da20b..386f41495 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,12 @@ endif() project(BLS) +if(MSVC) + enable_language(ASM_MASM) +else() + enable_language(ASM) +endif() + set(BUILD_BLS_JS_BINDINGS "1" CACHE STRING "") set(BUILD_BLS_PYTHON_BINDINGS "1" CACHE STRING "") set(BUILD_BLS_TESTS "1" CACHE STRING "") @@ -29,94 +35,27 @@ set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules ) -# Relic related options - -set(STBIN "off" CACHE STRING "Relic - Build static binaries") - -find_package(gmp) -if (GMP_FOUND) - message(STATUS "Found libgmp") - set(ARITH "gmp" CACHE STRING "") -else() - set(ARITH "easy" CACHE STRING "") -endif() - -if(CMAKE_SIZEOF_VOID_P EQUAL 4) - set(WSIZE "32" CACHE STRING "Relic - Processor word size") -else() - set(WSIZE "64" CACHE STRING "Relic - Processor word size") -endif() - -if(EMSCRIPTEN) - # emscripten needs arch set to be none since it can't compile assembly - set(ARCH "" CACHE STRING "") - # emscripten is a 32 bit compiler - set(WSIZE "32" CACHE STRING "Relic - Processor word size") -endif() - -if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") - set(TIMER "ANSI" CACHE STRING "") - set(MULTI "OPENMP" CACHE STRING "") -else() - set(TIMER "CYCLE" CACHE STRING "") - set(MULTI "PTHREAD" CACHE STRING "") -endif() - -set(CHECK "off" CACHE STRING "") -set(VERBS "off" CACHE STRING "") -set(ALLOC "AUTO" CACHE STRING "") -set(SHLIB "off" CACHE STRING "") -set(DOCUM "off" CACHE STRING "") -set(FP_PRIME "381" CACHE STRING "Relic - Prime modulus size") - -if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - set(SEED "UDEV" CACHE STRING "") - set(FP_QNRES "off" CACHE STRING "") -elseif(${CMAKE_SYSTEM_NAME} MATCHES "Windows") - set(SEED "WCGR" CACHE STRING "") - set(FP_QNRES "on" CACHE STRING "") -else() - set(SEED "UDEV" CACHE STRING "") - set(FP_QNRES "on" CACHE STRING "") -endif() - -set(FP_METHD "INTEG;INTEG;INTEG;MONTY;LOWER;SLIDE" CACHE STRING "") - -if(MSVC) - set(COMP_FLAGS "" CACHE STRING "") -else() - set(COMP_FLAGS "-O3 -funroll-loops -fomit-frame-pointer" CACHE STRING "") -endif() - -set(FP_PMERS "off" CACHE STRING "") -set(FPX_METHD "INTEG;INTEG;LAZYR" CACHE STRING "") -set(EP_PLAIN "off" CACHE STRING "") -set(EP_SUPER "off" CACHE STRING "") -# Disable relic tests and benchmarks -set(TESTS "0" CACHE STRING "Relic - Number of times each test is ran") -set(BENCH "0" CACHE STRING "Relic - Number of times each benchmark is ran") - -set(QUIET "on" CACHE STRING "Relic - Build with printing disabled") - -set(PP_EXT "LAZYR" CACHE STRING "") -set(PP_METHD "LAZYR;OATEP" CACHE STRING "") - # Disable mimalloc shared libraries and tests, enable secure mode set(MI_SECURE "on" CACHE STRING "") set(MI_BUILD_SHARED "off" CACHE STRING "") set(MI_BUILD_TESTS "off" CACHE STRING "") set(MI_OVERRIDE "off" CACHE STRING "") - -add_subdirectory(depends/relic) add_subdirectory(depends/mimalloc) -#message(STATUS "Patching Relic to make setjmp.h inclusion conditional") -# -#execute_process( -# COMMAND bash -c "git apply ${CMAKE_SOURCE_DIR}/setjmp_patch.diff" -# WORKING_DIRECTORY ${RELIC_SRC} -#) +include(FetchContent) + +set(BLST_GIT_TAG "a8cd361c9f671577aeab3f074098443af92a53fc") +set(BLST_REPOSITORY "https://github.com/supranational/blst") + +message(STATUS "blst will be built from: ${BLST_GIT_TAG} and repository ${BLST_REPOSITORY}") + +FetchContent_Declare( + blst + GIT_REPOSITORY ${BLST_REPOSITORY} + GIT_TAG ${BLST_GIT_TAG} +) +FetchContent_MakeAvailable(blst) add_subdirectory(src) diff --git a/README.md b/README.md index 291b83499..d3292e46d 100644 --- a/README.md +++ b/README.md @@ -11,20 +11,19 @@ > **This library has not undergone a formal security review.** `bls-signatures` is a cross-platform library implementing BLS12-381 primitives for Dash built on -the [`relic`](https://github.com/relic-toolkit/relic) toolkit with bindings available in +the [`blst library`](https://github.com/supranational/blst.git) toolkit with bindings available in [Python](./binds/python), [Rust](./rust-bindings/), [Go](./go-bindings/) and [Javascript](./js-bindings). ## Dependencies * A C++17 capable compiler (GCC 9, Clang 7 or higher) * CMake 3.18 or higher -* [`libgmp`](https://gmplib.org/) (for fast arithmetic, **optional**) Additionally, the following dependencies are supplied by the codebase * [`catch2`](https://github.com/catchorg/Catch2) (for tests) * [`mimalloc`](https://github.com/microsoft/mimalloc) (for secure memory operations) -* [`relic`](https://github.com/relic-toolkit/relic) (for cryptographic operations) +* [`blst`](https://github.com/supranational/blst.git) (for cryptographic operations) ## Build library @@ -100,6 +99,11 @@ uv run pytest -v binds/python/test_unit.py uv run pytest -v binds/python/test_bench.py --benchmark-only ``` +## BLST license + +BLST is used with the +[Apache 2.0 license](https://github.com/supranational/blst/blob/master/LICENSE) + ## License ```text diff --git a/binds/python/CMakeLists.txt b/binds/python/CMakeLists.txt index 98a776e4b..9117ade8f 100644 --- a/binds/python/CMakeLists.txt +++ b/binds/python/CMakeLists.txt @@ -6,7 +6,7 @@ if(NOT pybind11_FOUND) FetchContent_Declare( pybind11 GIT_REPOSITORY https://github.com/pybind/pybind11.git - GIT_TAG v2.13.6 + GIT_TAG v2.13.6 ) FetchContent_MakeAvailable(pybind11) endif() diff --git a/binds/python/pythonbindings.cpp b/binds/python/pythonbindings.cpp index b037a74d6..0cc223d6e 100644 --- a/binds/python/pythonbindings.cpp +++ b/binds/python/pythonbindings.cpp @@ -517,20 +517,6 @@ PYBIND11_MODULE(dashbls, m) return self + other; }, py::is_operator()) - .def( - "__mul__", - [](G1Element &self, const PrivateKey &other) { - RelicGuard guard; - return self * other; - }, - py::is_operator()) - .def( - "__rmul__", - [](G1Element &self, const PrivateKey &other) { - RelicGuard guard; - return other * self; - }, - py::is_operator()) .def( "__and__", [](G1Element &self, G2Element &other) { @@ -643,21 +629,6 @@ PYBIND11_MODULE(dashbls, m) return self + other; }, py::is_operator()) - .def( - "__mul__", - [](G2Element &self, const PrivateKey &other) { - RelicGuard guard; - return self * other; - }, - py::is_operator()) - .def( - "__rmul__", - [](G2Element &self, const PrivateKey &other) { - RelicGuard guard; - return other * self; - }, - py::is_operator()) - .def( "__repr__", [](const G2Element &ele) { diff --git a/cmake_modules/Findgmp.cmake b/cmake_modules/Findgmp.cmake deleted file mode 100644 index 3e59ede0a..000000000 --- a/cmake_modules/Findgmp.cmake +++ /dev/null @@ -1,103 +0,0 @@ -# Try to find the GMP library -# https://gmplib.org/ -# -# This module supports requiring a minimum version, e.g. you can do -# find_package(GMP 6.0.0) -# to require version 6.0.0 to newer of GMP. -# -# Once done this will define -# -# GMP_FOUND - system has GMP lib with correct version -# GMP_INCLUDES - the GMP include directory -# GMP_LIBRARIES - the GMP library -# GMP_VERSION - GMP version -# -# Copyright (c) 2016 Jack Poulson, -# Redistribution and use is allowed according to the terms of the BSD license. - -include(BrewHelper) -find_brew_prefix(_GMP_BREW_HINT gmp) - -find_path(GMP_INCLUDES - NAMES - gmp.h - PATHS - $ENV{GMPDIR} ${INCLUDE_INSTALL_DIR} - PATH_SUFFIXES - include - HINTS - ${_GMP_BREW_HINT} -) - -# Set GMP_FIND_VERSION to 5.1.0 if no minimum version is specified -if(NOT GMP_FIND_VERSION) - if(NOT GMP_FIND_VERSION_MAJOR) - set(GMP_FIND_VERSION_MAJOR 5) - endif() - if(NOT GMP_FIND_VERSION_MINOR) - set(GMP_FIND_VERSION_MINOR 1) - endif() - if(NOT GMP_FIND_VERSION_PATCH) - set(GMP_FIND_VERSION_PATCH 0) - endif() - set(GMP_FIND_VERSION - "${GMP_FIND_VERSION_MAJOR}.${GMP_FIND_VERSION_MINOR}.${GMP_FIND_VERSION_PATCH}") -endif() - -message("GMP_INCLUDES=${GMP_INCLUDES}") -if(GMP_INCLUDES) - # Since the GMP version macros may be in a file included by gmp.h of the form - # gmp-.*[_]?.*.h (e.g., gmp-x86_64.h), we search each of them. - file(GLOB GMP_HEADERS "${GMP_INCLUDES}/gmp.h" "${GMP_INCLUDES}/gmp-*.h") - foreach(gmp_header_filename ${GMP_HEADERS}) - file(READ "${gmp_header_filename}" _gmp_version_header) - string(REGEX MATCH - "define[ \t]+__GNU_MP_VERSION[ \t]+([0-9]+)" _gmp_major_version_match - "${_gmp_version_header}") - if(_gmp_major_version_match) - set(GMP_MAJOR_VERSION "${CMAKE_MATCH_1}") - string(REGEX MATCH "define[ \t]+__GNU_MP_VERSION_MINOR[ \t]+([0-9]+)" - _gmp_minor_version_match "${_gmp_version_header}") - set(GMP_MINOR_VERSION "${CMAKE_MATCH_1}") - string(REGEX MATCH "define[ \t]+__GNU_MP_VERSION_PATCHLEVEL[ \t]+([0-9]+)" - _gmp_patchlevel_version_match "${_gmp_version_header}") - set(GMP_PATCHLEVEL_VERSION "${CMAKE_MATCH_1}") - set(GMP_VERSION - ${GMP_MAJOR_VERSION}.${GMP_MINOR_VERSION}.${GMP_PATCHLEVEL_VERSION}) - endif() - endforeach() - - # Check whether found version exists and exceeds the minimum requirement - if(NOT GMP_VERSION) - set(GMP_VERSION_OK FALSE) - message(STATUS "GMP version was not detected") - elseif(${GMP_VERSION} VERSION_LESS ${GMP_FIND_VERSION}) - set(GMP_VERSION_OK FALSE) - message(STATUS "GMP version ${GMP_VERSION} found in ${GMP_INCLUDES}, " - "but at least version ${GMP_FIND_VERSION} is required") - else() - set(GMP_VERSION_OK TRUE) - endif() -endif() - -if(STBIN) - set(_gmp_lib_name libgmp.a) -else() - set(_gmp_lib_name libgmp.so) -endif() - -find_library(GMP_LIBRARIES - NAMES - ${_gmp_lib_name} gmp.lib libgmp-10 libgmp gmp - HINTS - ${_GMP_BREW_HINT} - INCLUDE_DIRS - ${GMP_INCLUDES} - PATHS - $ENV{GMPDIR} ${LIB_INSTALL_DIR} -) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(gmp DEFAULT_MSG - GMP_INCLUDES GMP_LIBRARIES GMP_VERSION_OK) -mark_as_advanced(GMP_INCLUDES GMP_LIBRARIES) diff --git a/include/dashbls/bls.hpp b/include/dashbls/bls.hpp index cd6922a58..d91088735 100644 --- a/include/dashbls/bls.hpp +++ b/include/dashbls/bls.hpp @@ -40,8 +40,6 @@ class BLS { static bool Init(); static void SetSecureAllocator(Util::SecureAllocCallback allocCb, Util::SecureFreeCallback freeCb); - - static void CheckRelicErrors(bool should_throw = true); }; } // end namespace bls diff --git a/include/dashbls/elements.hpp b/include/dashbls/elements.hpp index 95937f62d..1a83f0bb6 100644 --- a/include/dashbls/elements.hpp +++ b/include/dashbls/elements.hpp @@ -16,17 +16,12 @@ #define SRC_BLSELEMENTS_HPP_ extern "C" { -#include "relic.h" +#include "bindings/blst.h" } -#include "relic_conf.h" -#include "util.hpp" - -#if defined GMP && ARITH == GMP -#include -#endif - #include +#include "util.hpp" + namespace bls { class G1Element; class G2Element; @@ -36,25 +31,27 @@ class G1Element { public: static const size_t SIZE = 48; - G1Element() { - g1_set_infty(p); - } - - static G1Element FromBytes(Bytes bytes, bool fLegacy = false); - static G1Element FromBytesUnchecked(Bytes bytes, bool fLegacy = false); - static G1Element FromByteVector(const std::vector &bytevec, bool fLegacy = false); - static G1Element FromNative(const g1_t element); - static G1Element FromMessage(const std::vector &message, - const uint8_t *dst, - int dst_len); - static G1Element FromMessage(Bytes message, - const uint8_t* dst, - int dst_len); + G1Element() { memset(&p, 0x00, sizeof(blst_p1)); } + + static G1Element FromBytes(Bytes bytes, bool legacy_encoding = false); + static G1Element FromBytesUnchecked(Bytes bytes, bool legacy_encoding = false); + static G1Element FromByteVector(const std::vector &bytevec, bool legacy_encoding = false); + static G1Element FromNative(const blst_p1 &element); + static G1Element FromAffine(const blst_p1_affine &element); + static G1Element FromMessage( + const std::vector &message, + const uint8_t *dst, + int dst_len); + static G1Element FromMessage( + Bytes message, + const uint8_t *dst, + int dst_len); static G1Element Generator(); bool IsValid() const; void CheckValid() const; - void ToNative(g1_t output) const; + void ToNative(blst_p1 *output) const; + void ToAffine(blst_p1_affine *output) const; G1Element Negate() const; GTElement Pair(const G2Element &b) const; uint32_t GetFingerprint(bool fLegacy = false) const; @@ -65,41 +62,41 @@ class G1Element { friend bool operator==(const G1Element &a, const G1Element &b); friend bool operator!=(const G1Element &a, const G1Element &b); friend std::ostream &operator<<(std::ostream &os, const G1Element &s); - friend G1Element& operator+=(G1Element& a, const G1Element& b); + friend G1Element &operator+=(G1Element &a, const G1Element &b); friend G1Element operator+(const G1Element &a, const G1Element &b); - friend G1Element operator*(const G1Element &a, const bn_t &k); - friend G1Element operator*(const bn_t &k, const G1Element &a); + friend G1Element operator*(const G1Element &a, const blst_scalar &k); + friend G1Element operator*(const blst_scalar &k, const G1Element &a); friend GTElement operator&(const G1Element &a, const G2Element &b); private: - g1_t p; + blst_p1 p; }; class G2Element { public: static const size_t SIZE = 96; - G2Element() { - g2_set_infty(q); - } - - static G2Element FromBytes(Bytes bytes, bool fLegacy = false); - static G2Element FromBytesUnchecked(Bytes bytes, bool fLegacy = false); - static G2Element FromByteVector(const std::vector &bytevec, bool fLegacy = false); - static G2Element FromNative(const g2_t element); - static G2Element FromMessage(const std::vector& message, - const uint8_t* dst, - int dst_len, - bool fLegacy = false); - static G2Element FromMessage(Bytes message, - const uint8_t* dst, - int dst_len, - bool fLegacy = false); + G2Element() { memset(&q, 0x00, sizeof(blst_p2)); } + + static G2Element FromBytes(Bytes bytes, bool legacy_encoding = false); + static G2Element FromBytesUnchecked(Bytes bytes, bool legacy_encoding = false); + static G2Element FromByteVector(const std::vector &bytevec, bool legacy_encoding = false); + static G2Element FromNative(const blst_p2 &element); + static G2Element FromAffine(const blst_p2_affine &element); + static G2Element FromMessage( + const std::vector &message, + const uint8_t *dst, + int dst_len, bool legacy_encoding = false); + static G2Element FromMessage( + Bytes message, + const uint8_t *dst, + int dst_len, bool legacy_encoding = false); static G2Element Generator(); bool IsValid() const; void CheckValid() const; - void ToNative(g2_t output) const; + void ToNative(blst_p2 *output) const; + void ToAffine(blst_p2_affine *output) const; G2Element Negate() const; GTElement Pair(const G1Element &a) const; std::vector Serialize(bool fLegacy = false) const; @@ -109,22 +106,25 @@ class G2Element { friend bool operator==(G2Element const &a, G2Element const &b); friend bool operator!=(G2Element const &a, G2Element const &b); friend std::ostream &operator<<(std::ostream &os, const G2Element &s); - friend G2Element& operator+=(G2Element& a, const G2Element& b); + friend G2Element &operator+=(G2Element &a, const G2Element &b); friend G2Element operator+(const G2Element &a, const G2Element &b); - friend G2Element operator*(const G2Element &a, const bn_t &k); - friend G2Element operator*(const bn_t &k, const G2Element &a); + friend G2Element operator*(const G2Element &a, const blst_scalar &k); + friend G2Element operator*(const blst_scalar &k, const G2Element &a); private: - g2_t q; + blst_p2 q; }; class GTElement { public: - static const size_t SIZE = 384; + static const size_t SIZE = sizeof(blst_fp12); + static GTElement FromBytes(Bytes bytes); static GTElement FromBytesUnchecked(Bytes bytes); static GTElement FromByteVector(const std::vector &bytevec); - static GTElement FromNative(const gt_t *element); + static GTElement FromNative(const blst_fp12 *element); + static GTElement FromAffine(const blst_p1_affine &element); + static GTElement FromAffine(const blst_p2_affine &element); static GTElement Unity(); // unity void Serialize(uint8_t *buffer) const; @@ -135,10 +135,9 @@ class GTElement { friend bool operator!=(GTElement const &a, GTElement const &b); friend std::ostream &operator<<(std::ostream &os, const GTElement &s); friend GTElement operator*(GTElement &a, GTElement &b); - GTElement &operator=(const GTElement &rhs); private: - gt_t r; + blst_fp12 r; GTElement() {} }; diff --git a/include/dashbls/hdkeys.hpp b/include/dashbls/hdkeys.hpp index 629ec5e3d..3c8295454 100644 --- a/include/dashbls/hdkeys.hpp +++ b/include/dashbls/hdkeys.hpp @@ -15,16 +15,11 @@ #ifndef SRC_BLSHDKEYS_HPP_ #define SRC_BLSHDKEYS_HPP_ -#include "relic_conf.h" #include -#if defined GMP && ARITH == GMP -#include -#endif - -#include "util.hpp" -#include "privatekey.hpp" #include "hkdf.hpp" +#include "privatekey.hpp" +#include "util.hpp" namespace bls { @@ -32,14 +27,14 @@ class HDKeys { /** * Implements HD keys as specified in EIP2333. **/ - public: +public: static const uint8_t HASH_LEN = 32; static PrivateKey KeyGen(const std::vector& seed) { return KeyGen(Bytes(seed)); } - + static PrivateKey KeyGen(const Bytes& seed) { // KeyGen @@ -56,65 +51,44 @@ class HDKeys { throw std::invalid_argument("Seed size must be at least 32 bytes"); } - // "BLS-SIG-KEYGEN-SALT-" in ascii - const uint8_t saltHkdf[20] = {66, 76, 83, 45, 83, 73, 71, 45, 75, 69, - 89, 71, 69, 78, 45, 83, 65, 76, 84, 45}; + blst_scalar* skBn = Util::SecAlloc(1); + blst_keygen_v3(skBn, seed.begin(), seed.size(), info, infoLen); + uint8_t* skBytes = Util::SecAlloc(32); + blst_bendian_from_scalar(skBytes, skBn); - uint8_t *prk = Util::SecAlloc(32); - uint8_t *ikmHkdf = Util::SecAlloc(seed.size() + 1); - memcpy(ikmHkdf, seed.begin(), seed.size()); - ikmHkdf[seed.size()] = 0; - - const uint8_t L = 48; // `ceil((3 * ceil(log2(r))) / 16)`, where `r` is the - // order of the BLS 12-381 curve - - uint8_t *okmHkdf = Util::SecAlloc(L); - - uint8_t keyInfoHkdf[infoLen + 2]; - memcpy(keyInfoHkdf, info, infoLen); - keyInfoHkdf[infoLen] = 0; // Two bytes for L, 0 and 48 - keyInfoHkdf[infoLen + 1] = L; - - HKDF256::ExtractExpand( - okmHkdf, - L, - ikmHkdf, - seed.size() + 1, - saltHkdf, - 20, - keyInfoHkdf, - infoLen + 2); - - bn_t order; - bn_new(order); - g1_get_ord(order); - - // Make sure private key is less than the curve order - bn_t *skBn = Util::SecAlloc(1); - bn_new(*skBn); - bn_read_bin(*skBn, okmHkdf, L); - bn_mod_basic(*skBn, *skBn, order); - - uint8_t *skBytes = Util::SecAlloc(32); - bn_write_bin(skBytes, 32, *skBn); PrivateKey k = PrivateKey::FromBytes(Bytes(skBytes, 32)); - Util::SecFree(prk); - Util::SecFree(ikmHkdf); Util::SecFree(skBn); - Util::SecFree(okmHkdf); Util::SecFree(skBytes); return k; } - static void IKMToLamportSk(uint8_t* outputLamportSk, const uint8_t* ikm, size_t ikmLen, const uint8_t* salt, size_t saltLen) { + static void IKMToLamportSk( + uint8_t* outputLamportSk, + const uint8_t* ikm, + size_t ikmLen, + const uint8_t* salt, + size_t saltLen) + { // Expands the ikm to 255*HASH_LEN bytes for the lamport sk const uint8_t info[1] = {0}; - HKDF256::ExtractExpand(outputLamportSk, HASH_LEN * 255, ikm, ikmLen, salt, saltLen, info, 0); + HKDF256::ExtractExpand( + outputLamportSk, + HASH_LEN * 255, + ikm, + ikmLen, + salt, + saltLen, + info, + 0); } - static void ParentSkToLamportPK(uint8_t* outputLamportPk, const PrivateKey& parentSk, uint32_t index) { + static void ParentSkToLamportPK( + uint8_t* outputLamportPk, + const PrivateKey& parentSk, + uint32_t index) + { uint8_t* salt = Util::SecAlloc(4); uint8_t* ikm = Util::SecAlloc(HASH_LEN); uint8_t* notIkm = Util::SecAlloc(HASH_LEN); @@ -134,11 +108,15 @@ class HDKeys { uint8_t* lamportPk = Util::SecAlloc(HASH_LEN * 255 * 2); for (size_t i = 0; i < 255; i++) { - Util::Hash256(lamportPk + i * HASH_LEN, lamport0 + i * HASH_LEN, HASH_LEN); + Util::Hash256( + lamportPk + i * HASH_LEN, lamport0 + i * HASH_LEN, HASH_LEN); } - for (size_t i=0; i < 255; i++) { - Util::Hash256(lamportPk + 255 * HASH_LEN + i * HASH_LEN, lamport1 + i * HASH_LEN, HASH_LEN); + for (size_t i = 0; i < 255; i++) { + Util::Hash256( + lamportPk + 255 * HASH_LEN + i * HASH_LEN, + lamport1 + i * HASH_LEN, + HASH_LEN); } Util::Hash256(outputLamportPk, lamportPk, HASH_LEN * 255 * 2); @@ -150,7 +128,8 @@ class HDKeys { Util::SecFree(lamportPk); } - static PrivateKey DeriveChildSk(const PrivateKey& parentSk, uint32_t index) { + static PrivateKey DeriveChildSk(const PrivateKey& parentSk, uint32_t index) + { uint8_t* lamportPk = Util::SecAlloc(HASH_LEN); HDKeys::ParentSkToLamportPK(lamportPk, parentSk, index); std::vector lamportPkVector(lamportPk, lamportPk + HASH_LEN); @@ -159,21 +138,29 @@ class HDKeys { return child; } - static PrivateKey DeriveChildSkUnhardened(const PrivateKey& parentSk, uint32_t index) { + static PrivateKey DeriveChildSkUnhardened( + const PrivateKey& parentSk, + uint32_t index) + { uint8_t* buf = Util::SecAlloc(G1Element::SIZE + 4); uint8_t* digest = Util::SecAlloc(HASH_LEN); - memcpy(buf, parentSk.GetG1Element().Serialize().data(), G1Element::SIZE); + memcpy( + buf, parentSk.GetG1Element().Serialize().data(), G1Element::SIZE); Util::IntToFourBytes(buf + G1Element::SIZE, index); Util::Hash256(digest, buf, G1Element::SIZE + 4); - PrivateKey ret = PrivateKey::Aggregate({parentSk, PrivateKey::FromBytes(Bytes(digest, HASH_LEN), true)}); + PrivateKey ret = PrivateKey::Aggregate( + {parentSk, PrivateKey::FromBytes(Bytes(digest, HASH_LEN), true)}); Util::SecFree(buf); Util::SecFree(digest); return ret; } - static G1Element DeriveChildG1Unhardened(const G1Element& pk, uint32_t index) { + static G1Element DeriveChildG1Unhardened( + const G1Element& pk, + uint32_t index) + { uint8_t* buf = Util::SecAlloc(G1Element::SIZE + 4); uint8_t* digest = Util::SecAlloc(HASH_LEN); memcpy(buf, pk.Serialize().data(), G1Element::SIZE); @@ -181,35 +168,29 @@ class HDKeys { Util::IntToFourBytes(buf + G1Element::SIZE, index); Util::Hash256(digest, buf, G1Element::SIZE + 4); - bn_t nonce, ord; - bn_new(nonce); - bn_zero(nonce); - bn_read_bin(nonce, digest, HASH_LEN); - bn_new(ord); - g1_get_ord(ord); - bn_mod_basic(nonce, nonce, ord); + blst_scalar nonce; + blst_scalar_from_lendian(&nonce, digest); Util::SecFree(buf); Util::SecFree(digest); G1Element gen = G1Element::Generator(); - return pk + gen * nonce; + + return pk + (gen * nonce); } - static G2Element DeriveChildG2Unhardened(const G2Element& pk, uint32_t index) { + static G2Element DeriveChildG2Unhardened( + const G2Element& pk, + uint32_t index) + { uint8_t* buf = Util::SecAlloc(G2Element::SIZE + 4); uint8_t* digest = Util::SecAlloc(HASH_LEN); memcpy(buf, pk.Serialize().data(), G2Element::SIZE); Util::IntToFourBytes(buf + G2Element::SIZE, index); Util::Hash256(digest, buf, G2Element::SIZE + 4); - bn_t nonce, ord; - bn_new(nonce); - bn_zero(nonce); - bn_read_bin(nonce, digest, HASH_LEN); - bn_new(ord); - g1_get_ord(ord); - bn_mod_basic(nonce, nonce, ord); + blst_scalar nonce; + blst_scalar_from_lendian(&nonce, digest); Util::SecFree(buf); Util::SecFree(digest); @@ -218,5 +199,5 @@ class HDKeys { return pk + gen * nonce; } }; -} // end namespace bls +} // end namespace bls #endif // SRC_BLSHDKEYS_HPP_ diff --git a/include/dashbls/hkdf.hpp b/include/dashbls/hkdf.hpp index a7ddc863e..27b519b98 100644 --- a/include/dashbls/hkdf.hpp +++ b/include/dashbls/hkdf.hpp @@ -15,13 +15,7 @@ #ifndef SRC_BLSHKDF_HPP_ #define SRC_BLSHKDF_HPP_ -#include "relic_conf.h" #include - -#if defined GMP && ARITH == GMP -#include -#endif - #include #include "util.hpp" @@ -40,7 +34,7 @@ class HKDF256 { // assert(ikm_len == 32); // Used for EIP2333 key derivation // Hash256 used as the hash function (sha256) // PRK Output is 32 bytes (HashLen) - md_hmac(prk_output, ikm, ikm_len, salt, saltLen); + Util::md_hmac(prk_output, ikm, ikm_len, salt, saltLen); } static void Expand(uint8_t* okm, size_t L, const uint8_t* prk, const uint8_t* info, const size_t infoLen) { @@ -59,12 +53,12 @@ class HKDF256 { if (i == 1) { memcpy(hmacInput1, info, infoLen); hmacInput1[infoLen] = i; - md_hmac(T, hmacInput1, infoLen + 1, prk, HASH_LEN); + Util::md_hmac(T, hmacInput1, infoLen + 1, prk, HASH_LEN); } else { memcpy(hmacInput, T, HASH_LEN); memcpy(hmacInput + HASH_LEN, info, infoLen); hmacInput[HASH_LEN + infoLen] = i; - md_hmac(T, hmacInput, HASH_LEN + infoLen + 1, prk, HASH_LEN); + Util::md_hmac(T, hmacInput, HASH_LEN + infoLen + 1, prk, HASH_LEN); } size_t to_write = L - bytesWritten; if (to_write > HASH_LEN) { diff --git a/include/dashbls/privatekey.hpp b/include/dashbls/privatekey.hpp index 0b45dd518..a2e7770db 100644 --- a/include/dashbls/privatekey.hpp +++ b/include/dashbls/privatekey.hpp @@ -15,12 +15,6 @@ #ifndef SRC_BLSPRIVATEKEY_HPP_ #define SRC_BLSPRIVATEKEY_HPP_ -#include "relic_conf.h" - -#if defined GMP && ARITH == GMP -#include -#endif - #include "elements.hpp" namespace bls { @@ -102,7 +96,7 @@ class PrivateKey { void InvalidateCaches(); // The actual byte data - bn_st* keydata{nullptr}; + blst_scalar* keydata{nullptr}; mutable bool fG1CacheValid{false}; mutable G1Element g1Cache; diff --git a/include/dashbls/schemes.hpp b/include/dashbls/schemes.hpp index a6dcdfb0a..580bb7f86 100644 --- a/include/dashbls/schemes.hpp +++ b/include/dashbls/schemes.hpp @@ -19,12 +19,6 @@ #include #include -#include "relic_conf.h" - -#if defined GMP && ARITH == GMP -#include -#endif - #include "elements.hpp" #include "privatekey.hpp" @@ -36,7 +30,6 @@ namespace bls { class Bytes; class CoreMPL { - public: CoreMPL() = delete; CoreMPL(const std::string& strId) : strCiphersuiteId(strId) {} @@ -47,31 +40,42 @@ class CoreMPL { virtual PrivateKey KeyGen(const Bytes& seed); // Generates a public key from a secret key - virtual vector SkToPk(const PrivateKey &seckey); + virtual vector SkToPk(const PrivateKey& seckey); - virtual G1Element SkToG1(const PrivateKey &seckey); + virtual G1Element SkToG1(const PrivateKey& seckey); - virtual G2Element Sign(const PrivateKey &seckey, const vector &message); + virtual G2Element Sign( + const PrivateKey& seckey, + const vector& message); virtual G2Element Sign(const PrivateKey& seckey, const Bytes& message); - virtual bool Verify(const vector &pubkey, - const vector &message, - const vector &signature); + virtual bool Verify( + const vector& pubkey, + const vector& message, + const vector& signature); - virtual bool Verify(const Bytes& pubkey, const Bytes& message, const Bytes& signature); + virtual bool Verify( + const Bytes& pubkey, + const Bytes& message, + const Bytes& signature); - virtual bool Verify(const G1Element &pubkey, - const vector &message, - const G2Element &signature); + virtual bool Verify( + const G1Element& pubkey, + const vector& message, + const G2Element& signature); - virtual bool Verify(const G1Element& pubkey, const Bytes& message, const G2Element& signature); + virtual bool Verify( + const G1Element& pubkey, + const Bytes& message, + const G2Element& signature); - virtual std::array Aggregate(const vector> &signatures); + virtual std::array Aggregate( + const vector>& signatures); virtual std::array Aggregate(const vector& signatures); - virtual G2Element Aggregate(const vector &signatures); + virtual G2Element Aggregate(const vector& signatures); - virtual G1Element Aggregate(const vector &publicKeys); + virtual G1Element Aggregate(const vector& publicKeys); virtual G2Element AggregateSecure(const std::vector& vecPublicKeys, const std::vector& vecSignatures, @@ -81,21 +85,25 @@ class CoreMPL { const G2Element& signature, const Bytes& message); - virtual bool AggregateVerify(const vector> &pubkeys, - const vector> &messages, - const vector &signature); + virtual bool AggregateVerify( + const vector>& pubkeys, + const vector>& messages, + const vector& signature); - virtual bool AggregateVerify(const vector& pubkeys, - const vector& messages, - const Bytes& signature); + virtual bool AggregateVerify( + const vector& pubkeys, + const vector& messages, + const Bytes& signature); - virtual bool AggregateVerify(const vector &pubkeys, - const vector> &messages, - const G2Element &signature); + virtual bool AggregateVerify( + const vector& pubkeys, + const vector>& messages, + const G2Element& signature); - virtual bool AggregateVerify(const vector& pubkeys, - const vector& messages, - const G2Element& signature); + virtual bool AggregateVerify( + const vector& pubkeys, + const vector& messages, + const G2Element& signature); PrivateKey DeriveChildSk(const PrivateKey& sk, uint32_t index); PrivateKey DeriveChildSkUnhardened(const PrivateKey& sk, uint32_t index); @@ -103,7 +111,8 @@ class CoreMPL { protected: const std::string& strCiphersuiteId; - bool NativeVerify(g1_t *pubKeys, g2_t *mappedHashes, size_t length); + // bool NativeVerify(blst_p1 *pubKeys, blst_p2 *mappedHashes, size_t + // length); G2Element AggregateSecure(std::vector const &vecPublicKeys, std::vector const &vecSignatures, const Bytes& message, @@ -118,74 +127,88 @@ class BasicSchemeMPL final : public CoreMPL { public: static const std::string CIPHERSUITE_ID; BasicSchemeMPL() : CoreMPL(BasicSchemeMPL::CIPHERSUITE_ID) {} - bool AggregateVerify(const vector> &pubkeys, - const vector> &messages, - const vector &signature) override; - - bool AggregateVerify(const vector& pubkeys, - const vector& messages, - const Bytes& signature) override; - - bool AggregateVerify(const vector &pubkeys, - const vector> &messages, - const G2Element &signature) override; - - bool AggregateVerify(const vector& pubkeys, - const vector& messages, - const G2Element& signature) override; + bool AggregateVerify( + const vector>& pubkeys, + const vector>& messages, + const vector& signature) override; + + bool AggregateVerify( + const vector& pubkeys, + const vector& messages, + const Bytes& signature) override; + + bool AggregateVerify( + const vector& pubkeys, + const vector>& messages, + const G2Element& signature) override; + + bool AggregateVerify( + const vector& pubkeys, + const vector& messages, + const G2Element& signature) override; }; class AugSchemeMPL final : public CoreMPL { - public: static const std::string CIPHERSUITE_ID; AugSchemeMPL() : CoreMPL(AugSchemeMPL::CIPHERSUITE_ID) {} - G2Element Sign(const PrivateKey &seckey, const vector &message) override; + G2Element Sign(const PrivateKey& seckey, const vector& message) + override; G2Element Sign(const PrivateKey& seckey, const Bytes& message) override; // Used for prepending different augMessage - G2Element Sign(const PrivateKey &seckey, - const vector &message, - const G1Element &prepend_pk); + G2Element Sign( + const PrivateKey& seckey, + const vector& message, + const G1Element& prepend_pk); // Used for prepending different augMessage - G2Element Sign(const PrivateKey& seckey, - const Bytes& message, - const G1Element& prepend_pk); - - bool Verify(const vector &pubkey, - const vector &message, - const vector &signature) override; - - bool Verify(const Bytes& pubkey, - const Bytes& message, - const Bytes& signature) override; - - bool Verify(const G1Element &pubkey, - const vector &message, - const G2Element &signature) override; - - bool Verify(const G1Element& pubkey, - const Bytes& message, - const G2Element& signature) override; - - bool AggregateVerify(const vector> &pubkeys, - const vector> &messages, - const vector &signature) override; - - bool AggregateVerify(const vector& pubkeys, - const vector& messages, - const Bytes& signature) override; - - bool AggregateVerify(const vector &pubkeys, - const vector> &messages, - const G2Element &signature) override; - - bool AggregateVerify(const vector& pubkeys, - const vector& messages, - const G2Element& signature) override; + G2Element Sign( + const PrivateKey& seckey, + const Bytes& message, + const G1Element& prepend_pk); + + bool Verify( + const vector& pubkey, + const vector& message, + const vector& signature) override; + + bool Verify( + const Bytes& pubkey, + const Bytes& message, + const Bytes& signature) override; + + bool Verify( + const G1Element& pubkey, + const vector& message, + const G2Element& signature) override; + + bool Verify( + const G1Element& pubkey, + const Bytes& message, + const G2Element& signature) override; + + bool AggregateVerify( + const vector>& pubkeys, + const vector>& messages, + const vector& signature) override; + + bool AggregateVerify( + const vector& pubkeys, + const vector& messages, + const Bytes& signature) override; + + bool AggregateVerify( + const vector& pubkeys, + const vector>& messages, + const G2Element& signature) override; + + bool AggregateVerify( + const vector& pubkeys, + const vector& messages, + const G2Element& signature) override; bool VerifySecure(const std::vector& vecPublicKeys, const G2Element& signature, @@ -193,35 +216,38 @@ class AugSchemeMPL final : public CoreMPL { }; class PopSchemeMPL final : public CoreMPL { - public: static const std::string CIPHERSUITE_ID; static const std::string POP_CIPHERSUITE_ID; PopSchemeMPL() : CoreMPL(PopSchemeMPL::CIPHERSUITE_ID) {} - G2Element PopProve(const PrivateKey &seckey); + G2Element PopProve(const PrivateKey& seckey); - bool PopVerify(const G1Element &pubkey, const G2Element &signature_proof); + bool PopVerify(const G1Element& pubkey, const G2Element& signature_proof); - bool PopVerify(const vector &pubkey, const vector &proof); + bool PopVerify(const vector& pubkey, const vector& proof); bool PopVerify(const Bytes& pubkey, const Bytes& proof); - bool FastAggregateVerify(const vector &pubkeys, - const vector &message, - const G2Element &signature); - - bool FastAggregateVerify(const vector& pubkeys, - const Bytes& message, - const G2Element& signature); - - bool FastAggregateVerify(const vector> &pubkeys, - const vector &message, - const vector &signature); - - bool FastAggregateVerify(const vector& pubkeys, - const Bytes& message, - const Bytes& signature); + bool FastAggregateVerify( + const vector& pubkeys, + const vector& message, + const G2Element& signature); + + bool FastAggregateVerify( + const vector& pubkeys, + const Bytes& message, + const G2Element& signature); + + bool FastAggregateVerify( + const vector>& pubkeys, + const vector& message, + const vector& signature); + + bool FastAggregateVerify( + const vector& pubkeys, + const Bytes& message, + const Bytes& signature); }; /** diff --git a/include/dashbls/test-utils.hpp b/include/dashbls/test-utils.hpp index 154079ff4..237940c87 100644 --- a/include/dashbls/test-utils.hpp +++ b/include/dashbls/test-utils.hpp @@ -47,10 +47,10 @@ void endStopwatch(string testName, std::vector getRandomSeed() { uint8_t buf[32]; - bn_t r; - bn_new(r); - bn_rand(r, RLC_POS, 256); - bn_write_bin(buf, 32, r); + + for (int i = 0; i < 32; i++) + buf[i] = rand (); + std::vector ret(buf, buf + 32); return ret; } diff --git a/include/dashbls/util.hpp b/include/dashbls/util.hpp index 4fa164bb2..1dd3ec5a0 100644 --- a/include/dashbls/util.hpp +++ b/include/dashbls/util.hpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -60,9 +61,41 @@ class Util { public: static void Hash256(uint8_t* output, const uint8_t* message, size_t messageLen) { - md_map_sh256(output, message, messageLen); + blst_sha256(output, message, messageLen); } +static void md_hmac(uint8_t *mac, const uint8_t *in, int in_len, const uint8_t *key, + int key_len) { + #define block_size 64 + #define RLC_MD_LEN 32 + uint8_t opad[block_size + RLC_MD_LEN]; + uint8_t *ipad = (uint8_t *)malloc(block_size + in_len); + uint8_t _key[block_size]; + + if (ipad == NULL) + throw std::runtime_error("out of memory"); + + if (key_len > block_size) { + Hash256(_key, key, key_len); + key = _key; + key_len = RLC_MD_LEN; + } + + memcpy(_key, key, key_len); + memset(_key + key_len, 0, block_size - key_len); + key = _key; + + for (int i = 0; i < block_size; i++) { + opad[i] = 0x5C ^ key[i]; + ipad[i] = 0x36 ^ key[i]; + } + memcpy(ipad + block_size, in, in_len); + Hash256(opad + block_size, ipad, block_size + in_len); + Hash256(mac, opad, block_size + RLC_MD_LEN); + + free(ipad); +} + static std::string HexStr(const uint8_t* data, size_t len) { std::stringstream s; s << std::hex; diff --git a/js-bindings/CMakeLists.txt b/js-bindings/CMakeLists.txt index 5790f3a11..86660a01a 100644 --- a/js-bindings/CMakeLists.txt +++ b/js-bindings/CMakeLists.txt @@ -5,8 +5,6 @@ include_directories( ${INCLUDE_DIRECTORIES} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../include - ${CMAKE_CURRENT_SOURCE_DIR}/../depends/relic/include - ${CMAKE_CURRENT_BINARY_DIR}/../depends/relic/include ${CMAKE_CURRENT_SOURCE_DIR}/../depends/catch2/include ) diff --git a/js-bindings/blsjs.d.ts b/js-bindings/blsjs.d.ts index 95cb05962..556a2a8ed 100644 --- a/js-bindings/blsjs.d.ts +++ b/js-bindings/blsjs.d.ts @@ -48,7 +48,6 @@ export declare class G1Element { deepcopy(): G1Element; getFingerprint(): number; add(el: G1Element): G1Element; - mul(bn: Bignum): G1Element; equalTo(el: G1Element): boolean; delete(): void; } @@ -63,7 +62,6 @@ export declare class G2Element { negate(): G2Element; deepcopy(): G2Element; add(el: G2Element): G2Element; - mul(bn: Bignum): G2Element; equalTo(el: G2Element): boolean; delete(): void; } @@ -82,12 +80,6 @@ export declare class PrivateKey { delete(): void; } -export declare class Bignum { - static fromString(s: string, radix: number): Bignum; - toString(radix: number): string; - delete(): void; -} - export declare class Util { static hash256(msg: Uint8Array): Uint8Array; static hexStr(msg: Uint8Array): string; @@ -100,7 +92,6 @@ export interface ModuleInstance { G1Element: typeof G1Element; G2Element: typeof G2Element; PrivateKey: typeof PrivateKey; - Bignum: typeof Bignum; Util: typeof Util; } diff --git a/js-bindings/helpers.cpp b/js-bindings/helpers.cpp index e58f5e490..41a92a0a2 100644 --- a/js-bindings/helpers.cpp +++ b/js-bindings/helpers.cpp @@ -26,12 +26,6 @@ namespace helpers { return buffer; } - val toUint8Array(bn_t bn) { - std::vector vec = toVector(bn); - val buffer = toUint8Array(vec); - return buffer; - } - std::vector toVector(uint8_t *pointer, size_t data_size) { std::vector data; data.reserve(data_size); @@ -48,13 +42,6 @@ namespace helpers { return vec; } - std::vector toVector(bn_t bn) { - uint8_t buf[bn_size_bin(bn)]; - bn_write_bin(buf, bn_size_bin(bn), bn); - std::vector vec = helpers::toVector(buf, bn_size_bin(bn)); - return vec; - } - std::vector> jsBuffersArrayToVector(val buffersArray) { auto l = buffersArray["length"].as(); std::vector> vec; @@ -64,20 +51,6 @@ namespace helpers { return vec; } - std::vector jsBuffersArrayToBnVector(val buffersArray) { - auto l = buffersArray["length"].as(); - std::vector vec; - for (unsigned i = 0; i < l; ++i) { - bn_t data; - bn_new(data); - std::vector bnVec = toVector(buffersArray[i]); - bn_read_bin(data, bnVec.data(), static_cast(bnVec.size())); - bn_t *point = &data; - vec.push_back(point); - } - return vec; - } - val byteArraysVectorToJsBuffersArray(std::vector arraysVector, size_t element_size) { auto vecSize = arraysVector.size(); std::vector valVector; diff --git a/js-bindings/helpers.h b/js-bindings/helpers.h index 031df0704..faf2976e3 100644 --- a/js-bindings/helpers.h +++ b/js-bindings/helpers.h @@ -18,9 +18,6 @@ #include #include #include -extern "C" { -#include -} #include using namespace emscripten; @@ -31,14 +28,10 @@ namespace helpers { val toUint8Array(std::vector vec); - val toUint8Array(bn_t bn); - std::vector toVector(uint8_t *pointer, size_t data_size); std::vector toVector(val jsBuffer); - std::vector toVector(bn_t bn); - template inline std::vector toVectorFromJSArray(val jsArray) { auto l = jsArray["length"].as(); @@ -62,8 +55,6 @@ namespace helpers { std::vector> jsBuffersArrayToVector(val buffersArray); - std::vector jsBuffersArrayToBnVector(val buffersArray); - val byteArraysVectorToJsBuffersArray(std::vector arraysVector, size_t element_size); } // namespace helpers diff --git a/js-bindings/jsbindings.cpp b/js-bindings/jsbindings.cpp index 81daa4abc..6e9d45f11 100644 --- a/js-bindings/jsbindings.cpp +++ b/js-bindings/jsbindings.cpp @@ -70,7 +70,6 @@ EMSCRIPTEN_BINDINGS(blsjs) { .function("deepcopy", &G1ElementWrapper::Deepcopy) .function("getFingerprint", &G1ElementWrapper::GetFingerprint) .function("add", &G1ElementWrapper::Add) - .function("mul", &G1ElementWrapper::Mul) .function("equalTo", &G1ElementWrapper::EqualTo); class_("G2Element") @@ -84,7 +83,6 @@ EMSCRIPTEN_BINDINGS(blsjs) { .function("negate", &G2ElementWrapper::Negate) .function("deepcopy", &G2ElementWrapper::Deepcopy) .function("add", &G2ElementWrapper::Add) - .function("mul", &G2ElementWrapper::Mul) .function("equalTo", &G2ElementWrapper::EqualTo); class_("PrivateKey") @@ -99,10 +97,6 @@ EMSCRIPTEN_BINDINGS(blsjs) { .function("mulG2", &PrivateKeyWrapper::MulG2) .function("equalTo", &PrivateKeyWrapper::EqualTo); - class_("Bignum") - .class_function("fromString", &BignumWrapper::FromString) - .function("toString", &BignumWrapper::ToString); - class_("Util") .class_function("hash256", &UtilWrapper::Hash256) .class_function("hexStr", &UtilWrapper::HexStr); diff --git a/js-bindings/tests/test.js b/js-bindings/tests/test.js index 34671d654..7c1455932 100644 --- a/js-bindings/tests/test.js +++ b/js-bindings/tests/test.js @@ -22,7 +22,6 @@ blsjs().then((blsjs) => { const modules = [ 'AugSchemeMPL', 'BasicSchemeMPL', - "Bignum", 'G1Element', 'G2Element', 'PopSchemeMPL', @@ -42,7 +41,6 @@ blsjs().then((blsjs) => { const { AugSchemeMPL, BasicSchemeMPL, - Bignum, G1Element, G2Element, PopSchemeMPL, @@ -380,17 +378,11 @@ blsjs().then((blsjs) => { assert(AugSchemeMPL.aggregateVerify([], [], new G2Element())); } - function testBignum() { - const mersenne = Bignum.fromString('162259276829213363391578010288127', 10); - assert(mersenne.toString(16).toLowerCase() == '7ffffffffffffffffffffffffff'); - } - testSchemes(); testVectorsInvalid(); testVectorsValid(); testReadme(); testAggregateVerifyZeroItems(); - testBignum(); }).then(function() { console.log("\nAll tests passed."); }); diff --git a/js-bindings/wrappers/BignumWrapper.cpp b/js-bindings/wrappers/BignumWrapper.cpp deleted file mode 100644 index 26fea069a..000000000 --- a/js-bindings/wrappers/BignumWrapper.cpp +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2020 Chia Network Inc - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 - -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include "BignumWrapper.h" - -namespace js_wrappers { -Bignum::Bignum() { - bn_make(&content, 1); -} - -Bignum::~Bignum() { - bn_clean(&content); -} - -Bignum::Bignum(const Bignum &other) { - bn_copy(&content, &other.content); -} - -bn_st *Bignum::operator &() { return &content; } - -BignumWrapper::BignumWrapper(const Bignum &bn) : JSWrapper(bn) {} - -BignumWrapper BignumWrapper::FromString(const std::string &s, int radix) { - Bignum result; - bn_read_str(&result, s.c_str(), s.size(), radix); - return BignumWrapper(result); -} - -std::string BignumWrapper::ToString(int radix) { - auto wrapper = GetWrappedInstance(); - int strsize = bn_size_str(&wrapper, radix); - std::vector strholder(strsize); - bn_write_str(&strholder[0], strsize, &wrapper, radix); - return std::string(&strholder[0], strsize - 1); -} -} // namespace js_wrappers diff --git a/js-bindings/wrappers/BignumWrapper.h b/js-bindings/wrappers/BignumWrapper.h deleted file mode 100644 index 337d52702..000000000 --- a/js-bindings/wrappers/BignumWrapper.h +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2020 Chia Network Inc - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 - -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef JS_BINDINGS_WRAPPERS_BIGNUMWRAPPER_H_ -#define JS_BINDINGS_WRAPPERS_BIGNUMWRAPPER_H_ - -#include -#include "JSWrapper.h" - -namespace js_wrappers { -class Bignum { -public: - Bignum(); - ~Bignum(); - Bignum(const Bignum &other); - - bn_st *operator &(); - -private: - bn_st content; -}; - -class BignumWrapper : public JSWrapper { -public: - explicit BignumWrapper(const Bignum &bn); - static BignumWrapper FromString(const std::string &s, int radix); - std::string ToString(int radix); -}; -} // namespace js_wrappers - -#endif // JS_BINDINGS_WRAPPERS_SIGNATUREWRAPPER_H_ diff --git a/js-bindings/wrappers/G1ElementWrapper.cpp b/js-bindings/wrappers/G1ElementWrapper.cpp index 0d6305148..f623a5062 100644 --- a/js-bindings/wrappers/G1ElementWrapper.cpp +++ b/js-bindings/wrappers/G1ElementWrapper.cpp @@ -44,16 +44,6 @@ G1ElementWrapper G1ElementWrapper::Add(const G1ElementWrapper &other) { return G1ElementWrapper(GetWrappedInstance() + other.GetWrappedInstance()); } -G1ElementWrapper G1ElementWrapper::Mul(const BignumWrapper &other) { - bn_t factor; - // Since the type of bn_t is bn_st[0], we can do this dance to make a temporary - // copy of the struct itself to hand to G1ElementWrapper's multiply. The - // type was clearly intended to by by-value in argument lists (degrade to ptr) - // but value semantics in C++ complicates matters. - factor[0] = *(&other.GetWrappedInstance()); - return G1ElementWrapper(GetWrappedInstance() * factor); -} - bool G1ElementWrapper::EqualTo(const G1ElementWrapper &others) { return GetWrappedInstance() == others.GetWrappedInstance(); } diff --git a/js-bindings/wrappers/G1ElementWrapper.h b/js-bindings/wrappers/G1ElementWrapper.h index 1b5a0f828..e7185c523 100644 --- a/js-bindings/wrappers/G1ElementWrapper.h +++ b/js-bindings/wrappers/G1ElementWrapper.h @@ -17,7 +17,6 @@ #include #include "JSWrapper.h" -#include "BignumWrapper.h" namespace js_wrappers { class G1ElementWrapper : public JSWrapper { @@ -38,8 +37,6 @@ class G1ElementWrapper : public JSWrapper { G1ElementWrapper Add(const G1ElementWrapper &other); - G1ElementWrapper Mul(const BignumWrapper &other); - bool EqualTo(const G1ElementWrapper &others); G1ElementWrapper Negate(); diff --git a/js-bindings/wrappers/G2ElementWrapper.cpp b/js-bindings/wrappers/G2ElementWrapper.cpp index 980a7dd8e..de4a208f5 100644 --- a/js-bindings/wrappers/G2ElementWrapper.cpp +++ b/js-bindings/wrappers/G2ElementWrapper.cpp @@ -67,16 +67,6 @@ G2ElementWrapper G2ElementWrapper::Add(const G2ElementWrapper& other) { return G2ElementWrapper(GetWrappedInstance() + other.GetWrappedInstance()); } -G2ElementWrapper G2ElementWrapper::Mul(const BignumWrapper& other) { - bn_t factor; - // Since the type of bn_t is bn_st[0], we can do this dance to make a - // temporary copy of the struct itself to hand to G2ElementWrapper's - // multiply. The type was clearly intended to by by-value in argument lists - // (degrade to ptr) but value semantics in C++ complicates matters. - factor[0] = *(&other.GetWrappedInstance()); - return G2ElementWrapper(GetWrappedInstance() * factor); -} - bool G2ElementWrapper::EqualTo(const G2ElementWrapper& others) { return GetWrappedInstance() == others.GetWrappedInstance(); } diff --git a/js-bindings/wrappers/G2ElementWrapper.h b/js-bindings/wrappers/G2ElementWrapper.h index 46f23f6ca..829affd2a 100644 --- a/js-bindings/wrappers/G2ElementWrapper.h +++ b/js-bindings/wrappers/G2ElementWrapper.h @@ -17,7 +17,6 @@ #include #include "JSWrapper.h" -#include "BignumWrapper.h" namespace js_wrappers { class G2ElementWrapper : public JSWrapper { @@ -46,8 +45,6 @@ class G2ElementWrapper : public JSWrapper { G2ElementWrapper Add(const G2ElementWrapper &other); - G2ElementWrapper Mul(const BignumWrapper &other); - bool EqualTo(const G2ElementWrapper &others); }; } // namespace js_wrappers diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7dff5982f..ce73523b1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,41 +1,58 @@ -file(GLOB HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/../include/dashbls/*.hpp) +file(GLOB HEADERS ${PROJECT_SOURCE_DIR}/include/dashbls/*.hpp) source_group("SrcHeaders" FILES ${HEADERS}) -add_library(dashbls +list(APPEND bls_sources ${HEADERS} - ${CMAKE_CURRENT_SOURCE_DIR}/privatekey.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/bls.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/chaincode.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/elements.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/extendedprivatekey.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/extendedpublickey.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/legacy.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/schemes.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/threshold.cpp) - -target_include_directories(dashbls - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR} - $<$:${GMP_INCLUDES}> - ${CMAKE_CURRENT_SOURCE_DIR}/../include/dashbls - ${CMAKE_CURRENT_SOURCE_DIR}/../depends/mimalloc/include - ${CMAKE_CURRENT_SOURCE_DIR}/../depends/relic/include - ${CMAKE_CURRENT_BINARY_DIR}/../depends/relic/include + privatekey.cpp + bls.cpp + chaincode.cpp + elements.cpp + extendedprivatekey.cpp + extendedpublickey.cpp + legacy.cpp + schemes.cpp + threshold.cpp + ${blst_SOURCE_DIR}/src/server.c ) -target_compile_definitions(dashbls - PRIVATE - BLSALLOC_MIMALLOC=1 -) +if(MSVC) + list(APPEND bls_sources + ${blst_SOURCE_DIR}/build/win64/add_mod_256-x86_64.asm + ${blst_SOURCE_DIR}/build/win64/add_mod_384-x86_64.asm + ${blst_SOURCE_DIR}/build/win64/add_mod_384x384-x86_64.asm + ${blst_SOURCE_DIR}/build/win64/ct_inverse_mod_256-x86_64.asm + ${blst_SOURCE_DIR}/build/win64/ct_is_square_mod_384-x86_64.asm + ${blst_SOURCE_DIR}/build/win64/ctq_inverse_mod_384-x86_64.asm + ${blst_SOURCE_DIR}/build/win64/ctx_inverse_mod_384-x86_64.asm + ${blst_SOURCE_DIR}/build/win64/div3w-x86_64.asm + ${blst_SOURCE_DIR}/build/win64/mulq_mont_256-x86_64.asm + ${blst_SOURCE_DIR}/build/win64/mulq_mont_384-x86_64.asm + ${blst_SOURCE_DIR}/build/win64/mulx_mont_256-x86_64.asm + ${blst_SOURCE_DIR}/build/win64/mulx_mont_384-x86_64.asm + ${blst_SOURCE_DIR}/build/win64/sha256-x86_64.asm + ) +else() + list(APPEND bls_sources + ${blst_SOURCE_DIR}/build/assembly.S + ) + add_compile_options(-fno-builtin) + add_compile_options(-fPIC) + add_compile_options(-Wall) + add_compile_options(-Wextra) + if(CMAKE_SYSTEM_PROCESSOR MATCHES "(x86)|(X86)|(amd64)|(AMD64)") + add_compile_options(-mno-avx) + endif() +endif() +add_library(dashbls ${bls_sources}) +target_include_directories(dashbls PUBLIC ${blst_SOURCE_DIR} ${PROJECT_SOURCE_DIR}/include/dashbls/) +target_compile_definitions(dashbls PRIVATE __BLST_PORTABLE__ BLSALLOC_MIMALLOC=1) target_link_libraries(dashbls PUBLIC - relic_s mimalloc-static ) -install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../depends/relic/include/ DESTINATION include/dashbls) -install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../depends/relic/include/ DESTINATION include/dashbls) +install(FILES ${blst_SOURCE_DIR}/bindings/blst.h ${blst_SOURCE_DIR}/bindings/blst_aux.h DESTINATION include/dashbls) install(FILES ${HEADERS} DESTINATION include/dashbls) install(FILES $ DESTINATION lib) @@ -47,8 +64,9 @@ if(BUILD_BLS_TESTS) target_link_options(runtest PRIVATE "-sEXPORTED_FUNCTIONS=_malloc") endif() target_link_libraries(runtest - PRIVATE dashbls - PRIVATE catch2) + PRIVATE + dashbls + catch2) if(EMSCRIPTEN) target_link_options(runtest PRIVATE "SHELL:-s MALLOC=emmalloc") diff --git a/src/bls.cpp b/src/bls.cpp index d233afe97..19cce171f 100644 --- a/src/bls.cpp +++ b/src/bls.cpp @@ -27,36 +27,14 @@ bool BLSInitResult = BLS::Init(); Util::SecureAllocCallback Util::secureAllocCallback; Util::SecureFreeCallback Util::secureFreeCallback; -static void relic_core_initializer(void* ptr) -{ - core_init(); - if (err_get_code() != RLC_OK) { - throw std::runtime_error("core_init() failed"); - } - - const int r = ep_param_set_any_pairf(); - if (r != RLC_OK) { - throw std::runtime_error("ep_param_set_any_pairf() failed"); - } -} - bool BLS::Init() { - if (ALLOC != AUTO) { - throw std::runtime_error("Must have ALLOC == AUTO"); - } #if BLSALLOC_MIMALLOC SetSecureAllocator(mi_malloc, mi_free); #else SetSecureAllocator(malloc, free); #endif -#if MULTI != RELIC_NONE - core_set_thread_initializer(relic_core_initializer, nullptr); -#else - relic_core_initializer(nullptr); -#endif - return true; } @@ -68,18 +46,4 @@ void BLS::SetSecureAllocator( Util::secureFreeCallback = freeCb; } - -void BLS::CheckRelicErrors(bool should_throw) -{ - if (!core_get()) { - throw std::runtime_error("Library not initialized properly. Call BLS::Init()"); - } - if (core_get()->code != RLC_OK) { - core_get()->code = RLC_OK; - if (should_throw) { - throw std::invalid_argument("Relic library error"); - } - } -} - } // end namespace bls diff --git a/src/elements.cpp b/src/elements.cpp index b0c747af8..e7648ead6 100644 --- a/src/elements.cpp +++ b/src/elements.cpp @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include + #include #include "bls.hpp" @@ -21,7 +23,8 @@ namespace bls { const size_t G1Element::SIZE; -G1Element G1Element::FromBytes(Bytes const bytes, bool fLegacy) { +G1Element G1Element::FromBytes(Bytes const bytes, bool fLegacy) +{ G1Element ele = G1Element::FromBytesUnchecked(bytes, fLegacy); if (!fLegacy) { ele.CheckValid(); @@ -35,24 +38,20 @@ G1Element G1Element::FromBytesUnchecked(Bytes const bytes, bool fLegacy) throw std::invalid_argument("G1Element::FromBytes: Invalid size"); } - G1Element ele; - - // convert bytes to relic form - uint8_t buffer[G1Element::SIZE + 1]; - std::memcpy(buffer + 1, bytes.begin(), G1Element::SIZE); - buffer[0] = 0x00; - buffer[1] &= 0x1f; // erase 3 msbs from given input - + // check if the element is canonical + const uint8_t* raw_bytes = bytes.begin(); + bool fZerosOnly = + Util::HasOnlyZeros(Bytes(raw_bytes + 1, bytes.size() - 1)); if ((bytes[0] & 0xc0) == 0xc0) { // representing infinity buffer[0] = 0x00; buffer[1] &= 0x1f; // erase 3 msbs from given input - bool fZerosOnly = Util::HasOnlyZeros(Bytes(buffer, G1Element::SIZE + 1)); // enforce that infinity must be 0xc0000..00 if (bytes[0] != 0xc0 || !fZerosOnly) { - throw std::invalid_argument("Given G1 infinity element must be canonical"); + throw std::invalid_argument( + "Given G1 infinity element must be canonical"); } - return ele; + return G1Element(); // return infinity element (point all zero) } else { if (fLegacy) { if (bytes[0] & 0x80) { @@ -62,28 +61,24 @@ G1Element G1Element::FromBytesUnchecked(Bytes const bytes, bool fLegacy) buffer[0] = 0x02; // Insert extra byte for Y=0 } } else { - buffer[0] = 0x00; - buffer[1] &= 0x1f; // erase 3 msbs from given input - - bool fZerosOnly = Util::HasOnlyZeros(Bytes(buffer, G1Element::SIZE + 1)); if ((bytes[0] & 0xc0) != 0x80) { throw std::invalid_argument( - "Given G1 non-infinity element must start with 0b10"); + "Given G1 non-infinity element must start with 0b10"); } if (fZerosOnly) { - throw std::invalid_argument("G1 non-infinity element can't have only zeros"); - } - if (bytes[0] & 0x20) { // sign bit - buffer[0] = 0x03; - } else { - buffer[0] = 0x02; + throw std::invalid_argument( + "G1 non-infinity element can't have only zeros"); } } } - g1_read_bin(ele.p, buffer, G1Element::SIZE + 1); - BLS::CheckRelicErrors(!fLegacy); - return ele; + + blst_p1_affine a; + BLST_ERROR err = blst_p1_uncompress(&a, bytes.begin()); + if (err != BLST_SUCCESS) + throw std::invalid_argument("G1Element::FromBytes: Invalid bytes"); + + return G1Element::FromAffine(a); } G1Element G1Element::FromByteVector(const std::vector& bytevec, bool fLegacy) @@ -91,10 +86,10 @@ G1Element G1Element::FromByteVector(const std::vector& bytevec, bool fL return G1Element::FromBytes(Bytes(bytevec), fLegacy); } -G1Element G1Element::FromNative(const g1_t element) +G1Element G1Element::FromNative(const blst_p1& element) { G1Element ele; - g1_copy(ele.p, element); + memcpy(&(ele.p), &element, sizeof(blst_p1)); return ele; } @@ -106,20 +101,39 @@ G1Element G1Element::Copy() { } -G1Element G1Element::FromMessage(const std::vector& message, - const uint8_t* dst, - int dst_len) +G1Element G1Element::FromAffine(const blst_p1_affine& element) +{ + G1Element ele; + blst_p1_from_affine(&(ele.p), &element); + return ele; +} + +G1Element G1Element::FromMessage( + const std::vector& message, + const uint8_t* dst, + int dst_len) { return FromMessage(Bytes(message), dst, dst_len); } -G1Element G1Element::FromMessage(Bytes const message, - const uint8_t* dst, - int dst_len) +G1Element G1Element::FromMessage( + Bytes const message, + const uint8_t* dst, + int dst_len) { G1Element ans; - ep_map_dst(ans.p, message.begin(), (int)message.size(), dst, dst_len); - BLS::CheckRelicErrors(); + const byte* aug = nullptr; + size_t aug_len = 0; + + blst_hash_to_g1( + &(ans.p), + message.begin(), + (int)message.size(), + dst, + dst_len, + aug, + aug_len); + assert(ans.IsValid()); return ans; } @@ -127,35 +141,42 @@ G1Element G1Element::FromMessage(Bytes const message, G1Element G1Element::Generator() { G1Element ele; - g1_get_gen(ele.p); - BLS::CheckRelicErrors(); + ele.p = *(blst_p1_generator()); return ele; } -bool G1Element::IsValid() const { - // Infinity no longer valid in Relic - // https://github.com/relic-toolkit/relic/commit/f3be2babb955cf9f82743e0ae5ef265d3da6c02b - if (g1_is_infty((g1_st*)p) == 1) +bool G1Element::IsValid() const +{ + // Infinity was considered a valid G1Element in older Relic versions + // on which this library was previously based. + // For historical compatibililty this behavior is maintained. + + if (blst_p1_is_inf(&p)) return true; - return g1_is_valid((g1_st*)p); + return blst_p1_in_g1(&p); } -void G1Element::CheckValid() const { +void G1Element::CheckValid() const +{ if (!IsValid()) throw std::invalid_argument("G1 element is invalid"); - BLS::CheckRelicErrors(); } -void G1Element::ToNative(g1_t output) const { - g1_copy(output, p); +void G1Element::ToNative(blst_p1* output) const +{ + memcpy(output, &p, sizeof(blst_p1)); +} + +void G1Element::ToAffine(blst_p1_affine* output) const +{ + blst_p1_to_affine(output, &p); } G1Element G1Element::Negate() const { - G1Element ans; - g1_neg(ans.p, p); - BLS::CheckRelicErrors(); + G1Element ans = G1Element::FromNative(p); + blst_p1_cneg(&(ans.p), true); return ans; } @@ -175,74 +196,57 @@ std::vector G1Element::Serialize(const bool fLegacy) const { return std::vector{arr.begin(), arr.end()}; } -std::array G1Element::SerializeToArray(const bool fLegacy) const { - uint8_t buffer[G1Element::SIZE + 1]; - g1_write_bin(buffer, G1Element::SIZE + 1, p, 1); - - std::array result{}; - if (buffer[0] == 0x00) { // infinity - result[0] = 0xc0; - return result; - } - - if (buffer[0] == 0x03) { // sign bit set - buffer[1] |= fLegacy ? 0x80 : 0x20; - } - - if (!fLegacy) { - buffer[1] |= 0x80; // indicate compression - } - - std::copy_n(buffer + 1, G1Element::SIZE, result.begin()); - return result; +std::vector G1Element::Serialize() const +{ + uint8_t buffer[G1Element::SIZE]; + blst_p1_compress(buffer, &p); + return std::vector(buffer, buffer + G1Element::SIZE); } -bool operator==(const G1Element & a, const G1Element &b) +bool operator==(const G1Element& a, const G1Element& b) { - return g1_cmp(a.p, b.p) == RLC_EQ; + return blst_p1_is_equal(&(a.p), &(b.p)); } -bool operator!=(const G1Element & a, const G1Element & b) { return !(a == b); } +bool operator!=(const G1Element& a, const G1Element& b) { return !(a == b); } -std::ostream& operator<<(std::ostream& os, const G1Element &ele) +std::ostream& operator<<(std::ostream& os, const G1Element& ele) { return os << Util::HexStr(ele.Serialize()); } G1Element& operator+=(G1Element& a, const G1Element& b) { - g1_add(a.p, a.p, b.p); - BLS::CheckRelicErrors(); + blst_p1_add_or_double(&(a.p), &(a.p), &(b.p)); return a; } G1Element operator+(const G1Element& a, const G1Element& b) { G1Element ans; - g1_add(ans.p, a.p, b.p); - BLS::CheckRelicErrors(); + blst_p1_add_or_double(&(ans.p), &(a.p), &(b.p)); return ans; } -G1Element operator*(const G1Element& a, const bn_t& k) +G1Element operator*(const G1Element& a, const blst_scalar& k) { G1Element ans; - g1_mul(ans.p, (g1_st*)a.p, (bn_st*)k); - BLS::CheckRelicErrors(); + byte* bte = Util::SecAlloc(32); + blst_bendian_from_scalar(bte, &k); + blst_p1_mult(&(ans.p), &(a.p), bte, 256); + Util::SecFree(bte); + return ans; } -G1Element operator*(const bn_t& k, const G1Element& a) { return a * k; } - - +G1Element operator*(const blst_scalar& k, const G1Element& a) { return a * k; } // G2Element definitions below - - const size_t G2Element::SIZE; -G2Element G2Element::FromBytes(Bytes const bytes, const bool fLegacy) { +G2Element G2Element::FromBytes(Bytes const bytes, const bool fLegacy) +{ G2Element ele = G2Element::FromBytesUnchecked(bytes, fLegacy); if (!fLegacy) { ele.CheckValid(); @@ -256,59 +260,18 @@ G2Element G2Element::FromBytesUnchecked(Bytes const bytes, const bool fLegacy) throw std::invalid_argument("G2Element::FromBytes: Invalid size"); } - G2Element ele; - uint8_t buffer[G2Element::SIZE + 1]; - if (fLegacy) { - std::memcpy(buffer + 1, bytes.begin(), G2Element::SIZE); - buffer[0] = 0x00; - } else { - std::memcpy(buffer + 1, bytes.begin() + G2Element::SIZE / 2, G2Element::SIZE / 2); - std::memcpy(buffer + 1 + G2Element::SIZE / 2, bytes.begin(), G2Element::SIZE / 2); - buffer[0] = 0x00; - buffer[49] &= 0x1f; // erase 3 msbs from input - if ((bytes[48] & 0xe0) != 0x00) { - throw std::invalid_argument( - "Given G2 element must always have 48th byte start with 0b000"); - } - } - bool fZerosOnly = Util::HasOnlyZeros(Bytes(buffer, G2Element::SIZE + 1)); - if (((bytes[0] & 0xc0) == 0xc0)) { // infinity - // enforce that infinity must be 0xc0000..00 - if (bytes[0] != 0xc0 || !fZerosOnly) { - throw std::invalid_argument( - "Given G2 infinity element must be canonical"); - } - return ele; +// std::memcpy(buffer + 1, bytes.begin(), G2Element::SIZE); +// buffer[0] = 0x00; + static_assert(false); // TODO fix it } - if (fLegacy) { - if (bytes[0] & 0x80) { - buffer[0] = 0x03; // Insert extra byte for Y=1 - buffer[1] &= 0x7f; // Remove initial Y bit - } else { - buffer[0] = 0x02; // Insert extra byte for Y=0 - } - } else { - if (((bytes[0] & 0xc0) != 0x80)) { - throw std::invalid_argument( - "G2 non-inf element must have 0th byte start with 0b10"); - } - - if (fZerosOnly) { - throw std::invalid_argument("G2 non-infinity element can't have only zeros"); - } - - if (bytes[0] & 0x20) { - buffer[0] = 0x03; - } else { - buffer[0] = 0x02; - } - } + blst_p2_affine a; + BLST_ERROR err = blst_p2_uncompress(&a, bytes.begin()); + if (err != BLST_SUCCESS) + throw std::invalid_argument("G2Element::FromBytes: Invalid bytes"); - g2_read_bin(ele.q, buffer, G2Element::SIZE + 1); - BLS::CheckRelicErrors(!fLegacy); - return ele; + return G2Element::FromAffine(a); } G2Element G2Element::FromByteVector(const std::vector& bytevec, bool fLegacy) @@ -316,33 +279,51 @@ G2Element G2Element::FromByteVector(const std::vector& bytevec, bool fL return G2Element::FromBytes(Bytes(bytevec), fLegacy); } -G2Element G2Element::FromNative(const g2_t element) +G2Element G2Element::FromNative(const blst_p2& element) +{ + G2Element ele; + memcpy(&(ele.q), &element, sizeof(blst_p2)); + return ele; +} + +G2Element G2Element::FromAffine(const blst_p2_affine& element) { G2Element ele; - g2_copy(ele.q, (g2_st*)element); + blst_p2_from_affine(&(ele.q), &element); return ele; } -G2Element G2Element::FromMessage(const std::vector& message, - const uint8_t* dst, - int dst_len, - const bool fLegacy) +G2Element G2Element::FromMessage( + const std::vector& message, + const uint8_t* dst, + int dst_len, + const bool fLegacy) { return FromMessage(Bytes(message), dst, dst_len, fLegacy); } -G2Element G2Element::FromMessage(Bytes const message, - const uint8_t* dst, - int dst_len, - const bool fLegacy) +G2Element G2Element::FromMessage( + Bytes const message, + const uint8_t* dst, + int dst_len, + const bool fLegacy) { G2Element ans; + const byte* aug = nullptr; + size_t aug_len = 0; + if (fLegacy) { ep2_map_legacy(ans.q, message.begin(), BLS::MESSAGE_HASH_LEN); } else { - ep2_map_dst(ans.q, message.begin(), (int)message.size(), dst, dst_len); + blst_hash_to_g2( + &(ans.q), + message.begin(), + (int)message.size(), + dst, + dst_len, + aug, + aug_len); } - BLS::CheckRelicErrors(); assert(ans.IsValid()); return ans; } @@ -350,28 +331,36 @@ G2Element G2Element::FromMessage(Bytes const message, G2Element G2Element::Generator() { G2Element ele; - g2_get_gen(ele.q); - BLS::CheckRelicErrors(); + ele.q = (*blst_p2_generator()); return ele; } -bool G2Element::IsValid() const { - // Infinity no longer valid in Relic - // https://github.com/relic-toolkit/relic/commit/f3be2babb955cf9f82743e0ae5ef265d3da6c02b - if (g2_is_infty((g2_st*)q) == 1) +bool G2Element::IsValid() const +{ + // Infinity was considered a valid G2Element in older Relic versions + // on which this library was previously based. + // For historical compatibililty this behavior is maintained. + + if (blst_p2_is_inf(&q)) return true; - return g2_is_valid((g2_st*)q); + return blst_p2_in_g2(&q); } -void G2Element::CheckValid() const { +void G2Element::CheckValid() const +{ if (!IsValid()) throw std::invalid_argument("G2 element is invalid"); - BLS::CheckRelicErrors(); } -void G2Element::ToNative(g2_t output) const { - g2_copy(output, (g2_st*)q); +void G2Element::ToNative(blst_p2* output) const +{ + memcpy(output, (blst_p2*)&q, sizeof(blst_p2)); +} + +void G2Element::ToAffine(blst_p2_affine* output) const +{ + blst_p2_to_affine(output, &q); } G2Element G2Element::Copy() { @@ -384,9 +373,8 @@ G2Element G2Element::Copy() { G2Element G2Element::Negate() const { - G2Element ans; - g2_neg(ans.q, (g2_st*)q); - BLS::CheckRelicErrors(); + G2Element ans = G2Element::FromNative(q); + blst_p2_cneg(&(ans.q), true); return ans; } @@ -397,92 +385,70 @@ std::vector G2Element::Serialize(const bool fLegacy) const { return std::vector{arr.begin(), arr.end()}; } -std::array G2Element::SerializeToArray(const bool fLegacy) const { - uint8_t buffer[G2Element::SIZE + 1]; - g2_write_bin(buffer, G2Element::SIZE + 1, (g2_st*)q, 1); - - std::array result{}; - - if (buffer[0] == 0x00) { // infinity - result.fill(0); - result[0] = 0xc0; - return result; - } - - if (fLegacy) { - if (buffer[0] == 0x03) { // sign bit set - buffer[1] |= 0x80; - } - } else { - // remove leading 3 bits - buffer[1] &= 0x1f; - buffer[49] &= 0x1f; - if (buffer[0] == 0x03) { - buffer[49] |= 0xa0; // swapped later to 0 - } else { - buffer[49] |= 0x80; - } - } - +std::array G2Element::SerializeToArray(const bool fLegacy) const +{ if (fLegacy) { - std::memcpy(result.data(), buffer + 1, G2Element::SIZE); - } else { - // Swap buffer, relic uses the opposite ordering for Fq2 elements - std::memcpy(result.data(), buffer + 1 + G2Element::SIZE / 2, G2Element::SIZE / 2); - std::memcpy(result.data() + G2Element::SIZE / 2, buffer + 1, G2Element::SIZE / 2); + static_assert(false); // TODO: make it for legacy } - - return result; + uint8_t buffer[G2Element::SIZE]; + blst_p2_compress(buffer, &q); + return buffer; } bool operator==(G2Element const& a, G2Element const& b) { - return g2_cmp((g2_st*)a.q, (g2_st*)b.q) == RLC_EQ; + return blst_p2_is_equal(&(a.q), &(b.q)); } bool operator!=(G2Element const& a, G2Element const& b) { return !(a == b); } -std::ostream& operator<<(std::ostream& os, const G2Element & s) +std::ostream& operator<<(std::ostream& os, const G2Element& s) { return os << Util::HexStr(s.Serialize()); } G2Element& operator+=(G2Element& a, const G2Element& b) { - g2_add(a.q, a.q, (g2_st*)b.q); - BLS::CheckRelicErrors(); + blst_p2_add_or_double(&(a.q), &(a.q), &(b.q)); return a; } G2Element operator+(const G2Element& a, const G2Element& b) { G2Element ans; - g2_add(ans.q, (g2_st*)a.q, (g2_st*)b.q); - BLS::CheckRelicErrors(); + blst_p2_add_or_double(&(ans.q), &(a.q), &(b.q)); return ans; } -G2Element operator*(const G2Element& a, const bn_t& k) +G2Element operator*(const G2Element& a, const blst_scalar& k) { G2Element ans; - g2_mul(ans.q, (g2_st*)a.q, (bn_st*)k); - BLS::CheckRelicErrors(); + byte* bte = Util::SecAlloc(32); + blst_bendian_from_scalar(bte, &k); + blst_p2_mult(&(ans.q), &(a.q), bte, 256); + Util::SecFree(bte); + return ans; } -G2Element operator*(const bn_t& k, const G2Element& a) { return a * k; } - - +G2Element operator*(const blst_scalar& k, const G2Element& a) { return a * k; } // GTElement const size_t GTElement::SIZE; +/* + * Currently deserliazation is not available - these are currently + * broken and just return the zero element + */ GTElement GTElement::FromBytes(Bytes const bytes) { GTElement ele = GTElement::FromBytesUnchecked(bytes); - if (gt_is_valid(*(gt_t*)&ele) == 0) - throw std::invalid_argument("GTElement is invalid"); + // + // this doesn't seem to be the proper check as it doesn't work as expeced + // + // if (!blst_fp12_in_group(&(ele.r))) + // throw std::invalid_argument("GTElement is invalid"); return ele; } @@ -492,8 +458,7 @@ GTElement GTElement::FromBytesUnchecked(Bytes const bytes) throw std::invalid_argument("GTElement::FromBytes: Invalid size"); } GTElement ele = GTElement(); - gt_read_bin(ele.r, bytes.begin(), GTElement::SIZE); - BLS::CheckRelicErrors(); + memcpy(&(ele.r), bytes.begin(), SIZE); return ele; } @@ -502,23 +467,37 @@ GTElement GTElement::FromByteVector(const std::vector& bytevec) return GTElement::FromBytes(Bytes(bytevec)); } -GTElement GTElement::FromNative(const gt_t* element) +GTElement GTElement::FromNative(const blst_fp12* element) { GTElement ele = GTElement(); - gt_copy(ele.r, *(gt_t*)element); + ele.r = *element; return ele; } -GTElement GTElement::Unity() { +GTElement GTElement::FromAffine(const blst_p1_affine& affine) +{ GTElement ele = GTElement(); - gt_set_unity(ele.r); + blst_aggregated_in_g1(&ele.r, &affine); return ele; } +GTElement GTElement::FromAffine(const blst_p2_affine& affine) +{ + GTElement ele = GTElement(); + blst_aggregated_in_g2(&ele.r, &affine); + return ele; +} + +GTElement GTElement::Unity() +{ + GTElement ele = GTElement(); + ele.FromNative(blst_fp12_one()); + return ele; +} bool operator==(GTElement const& a, GTElement const& b) { - return gt_cmp(*(gt_t*)(a.r), *(gt_t*)(b.r)) == RLC_EQ; + return blst_fp12_is_equal(&(a.r), &(b.r)); } bool operator!=(GTElement const& a, GTElement const& b) { return !(a == b); } @@ -530,31 +509,31 @@ std::ostream& operator<<(std::ostream& os, GTElement const& ele) GTElement operator&(const G1Element& a, const G2Element& b) { - G1Element nonConstA(a); - gt_t ans; - gt_new(ans); - g2_t tmp; - g2_null(tmp); - g2_new(tmp); - b.ToNative(tmp); - pp_map_oatep_k12(ans, nonConstA.p, tmp); + blst_fp12 ans; + + blst_p1_affine aff1; + blst_p2_affine aff2; + a.ToAffine(&aff1); + b.ToAffine(&aff2); + + blst_miller_loop(&ans, &aff2, &aff1); + blst_final_exp(&ans, &ans); + GTElement ret = GTElement::FromNative(&ans); - gt_free(ans); - g2_free(tmp); + return ret; } GTElement operator*(GTElement& a, GTElement& b) { GTElement ans; - fp12_mul(ans.r, a.r, b.r); - BLS::CheckRelicErrors(); + blst_fp12_mul(&(ans.r), &(a.r), &(b.r)); return ans; } void GTElement::Serialize(uint8_t* buffer) const { - gt_write_bin(buffer, GTElement::SIZE, *(gt_t*)&r, 1); + memcpy(buffer, &r, GTElement::SIZE); } std::vector GTElement::Serialize() const diff --git a/src/privatekey.cpp b/src/privatekey.cpp index 80bb6b71f..b5ea51278 100644 --- a/src/privatekey.cpp +++ b/src/privatekey.cpp @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include + #include "bls.hpp" #include "legacy.hpp" @@ -49,44 +51,47 @@ PrivateKey PrivateKey::FromSeedBIP32(const Bytes& seed) { } // Construct a private key from a bytearray. -PrivateKey PrivateKey::FromBytes(const Bytes& bytes, bool modOrder) +PrivateKey PrivateKey::FromBytes(const Bytes &bytes, bool modOrder) { if (bytes.size() != PRIVATE_KEY_SIZE) { throw std::invalid_argument("PrivateKey::FromBytes: Invalid size"); } PrivateKey k; - bn_read_bin(k.keydata, bytes.begin(), PrivateKey::PRIVATE_KEY_SIZE); - bn_t ord; - bn_new(ord); - g1_get_ord(ord); - if (modOrder) { - bn_mod_basic(k.keydata, k.keydata, ord); - } else { - if (bn_cmp(k.keydata, ord) > 0) { - throw std::invalid_argument( - "PrivateKey byte data must be less than the group order"); - } + if (modOrder) + // this allows any bytes to be input and does proper mod order + blst_scalar_from_be_bytes(k.keydata, bytes.begin(), bytes.size()); + else + // this should only be the output of deserialization + blst_scalar_from_bendian(k.keydata, bytes.begin()); + + if (Util::HasOnlyZeros(bytes)) { + return k; // don't check anything else, we allow zero private key } + + if (!blst_sk_check(k.keydata)) + throw std::invalid_argument( + "PrivateKey byte data must be less than the group order"); + return k; } // Construct a private key from a bytearray. -PrivateKey PrivateKey::FromByteVector(const std::vector bytes, bool modOrder) +PrivateKey PrivateKey::FromByteVector( + const std::vector bytes, + bool modOrder) { return PrivateKey::FromBytes(Bytes(bytes), modOrder); } -PrivateKey::PrivateKey() { - AllocateKeyData(); -}; +PrivateKey::PrivateKey() { AllocateKeyData(); }; // Construct a private key from another private key. PrivateKey::PrivateKey(const PrivateKey &privateKey) { privateKey.CheckKeyData(); AllocateKeyData(); - bn_copy(keydata, privateKey.keydata); + memcpy(keydata, privateKey.keydata, 32); } PrivateKey::PrivateKey(PrivateKey &&k) @@ -95,14 +100,11 @@ PrivateKey::PrivateKey(PrivateKey &&k) k.InvalidateCaches(); } -PrivateKey::~PrivateKey() -{ - DeallocateKeyData(); -} +PrivateKey::~PrivateKey() { DeallocateKeyData(); } void PrivateKey::DeallocateKeyData() { - if(keydata != nullptr) { + if (keydata != nullptr) { Util::SecFree(keydata); keydata = nullptr; } @@ -115,16 +117,16 @@ void PrivateKey::InvalidateCaches() fG2CacheValid = false; } -PrivateKey& PrivateKey::operator=(const PrivateKey& other) +PrivateKey &PrivateKey::operator=(const PrivateKey &other) { CheckKeyData(); other.CheckKeyData(); InvalidateCaches(); - bn_copy(keydata, other.keydata); + memcpy(keydata, other.keydata, 32); return *this; } -PrivateKey& PrivateKey::operator=(PrivateKey&& other) +PrivateKey &PrivateKey::operator=(PrivateKey &&other) { DeallocateKeyData(); keydata = std::exchange(other.keydata, nullptr); @@ -132,28 +134,28 @@ PrivateKey& PrivateKey::operator=(PrivateKey&& other) return *this; } -const G1Element& PrivateKey::GetG1Element() const +const G1Element &PrivateKey::GetG1Element() const { if (!fG1CacheValid) { CheckKeyData(); - g1_st *p = Util::SecAlloc(1); - g1_mul_gen(p, keydata); + blst_p1 *p = Util::SecAlloc(1); + blst_sk_to_pk_in_g1(p, keydata); - g1Cache = G1Element::FromNative(p); + g1Cache = G1Element::FromNative(*p); Util::SecFree(p); fG1CacheValid = true; } return g1Cache; } -const G2Element& PrivateKey::GetG2Element() const +const G2Element &PrivateKey::GetG2Element() const { if (!fG2CacheValid) { CheckKeyData(); - g2_st *q = Util::SecAlloc(1); - g2_mul_gen(q, keydata); + blst_p2 *q = Util::SecAlloc(1); + blst_sk_to_pk_in_g2(q, keydata); - g2Cache = G2Element::FromNative(q); + g2Cache = G2Element::FromNative(*q); Util::SecFree(q); fG2CacheValid = true; } @@ -168,11 +170,15 @@ bool PrivateKey::HasKeyData() const G1Element operator*(const G1Element &a, const PrivateKey &k) { k.CheckKeyData(); - g1_st* ans = Util::SecAlloc(1); + + blst_p1 *ans = Util::SecAlloc(1); a.ToNative(ans); - g1_mul(ans, ans, k.keydata); - G1Element ret = G1Element::FromNative(ans); + byte *bte = Util::SecAlloc(32); + blst_bendian_from_scalar(bte, k.keydata); + blst_p1_mult(ans, ans, bte, 256); + G1Element ret = G1Element::FromNative(*ans); Util::SecFree(ans); + Util::SecFree(bte); return ret; } @@ -181,11 +187,14 @@ G1Element operator*(const PrivateKey &k, const G1Element &a) { return a * k; } G2Element operator*(const G2Element &a, const PrivateKey &k) { k.CheckKeyData(); - g2_st* ans = Util::SecAlloc(1); + blst_p2 *ans = Util::SecAlloc(1); a.ToNative(ans); - g2_mul(ans, ans, k.keydata); - G2Element ret = G2Element::FromNative(ans); + byte *bte = Util::SecAlloc(32); + blst_bendian_from_scalar(bte, k.keydata); + blst_p2_mult(ans, ans, bte, 256); + G2Element ret = G2Element::FromNative(*ans); Util::SecFree(ans); + Util::SecFree(bte); return ret; } @@ -206,15 +215,17 @@ PrivateKey operator*(const PrivateKey& k, const bn_t& a) PrivateKey operator*(const bn_t& a, const PrivateKey& k) { return a * k; } -G2Element PrivateKey::GetG2Power(const G2Element& element) const +G2Element PrivateKey::GetG2Power(const G2Element &element) const { CheckKeyData(); - g2_st* q = Util::SecAlloc(1); + blst_p2 *q = Util::SecAlloc(1); element.ToNative(q); - g2_mul(q, q, keydata); - - const G2Element ret = G2Element::FromNative(q); + byte *bte = Util::SecAlloc(32); + blst_bendian_from_scalar(bte, keydata); + blst_p2_mult(q, q, bte, 255); + const G2Element ret = G2Element::FromNative(*q); Util::SecFree(q); + Util::SecFree(bte); return ret; } @@ -224,30 +235,29 @@ PrivateKey PrivateKey::Aggregate(std::vector const &privateKeys) throw std::length_error("Number of private keys must be at least 1"); } - bn_t order; - bn_new(order); - g1_get_ord(order); - PrivateKey ret; assert(ret.IsZero()); for (size_t i = 0; i < privateKeys.size(); i++) { privateKeys[i].CheckKeyData(); - bn_add(ret.keydata, ret.keydata, privateKeys[i].keydata); - bn_mod_basic(ret.keydata, ret.keydata, order); + blst_sk_add_n_check(ret.keydata, ret.keydata, privateKeys[i].keydata); } return ret; } -bool PrivateKey::IsZero() const { +bool PrivateKey::IsZero() const +{ CheckKeyData(); - return bn_is_zero(keydata); + blst_scalar zro; + memset(&zro, 0x00, sizeof(blst_scalar)); + + return memcmp(keydata, &zro, 32) == 0; } bool operator==(const PrivateKey &a, const PrivateKey &b) { a.CheckKeyData(); b.CheckKeyData(); - return bn_cmp(a.keydata, b.keydata) == RLC_EQ; + return memcmp(a.keydata, b.keydata, sizeof(blst_scalar)) == 0; } bool operator!=(const PrivateKey &a, const PrivateKey &b) { return !(a == b); } @@ -258,7 +268,7 @@ void PrivateKey::Serialize(uint8_t *buffer) const throw std::runtime_error("PrivateKey::Serialize buffer invalid"); } CheckKeyData(); - bn_write_bin(buffer, PrivateKey::PRIVATE_KEY_SIZE, keydata); + blst_bendian_from_scalar(buffer, keydata); } std::vector PrivateKey::Serialize() const @@ -284,34 +294,32 @@ G2Element PrivateKey::SignG2( { CheckKeyData(); - g2_t pt; - g2_null(pt); - g2_new(pt); - + blst_p2 *pt = Util::SecAlloc(1); if (fLegacy) { ep2_map_legacy(pt, msg, BLS::MESSAGE_HASH_LEN); + g2_mul(pt, pt, keydata); + static_assert(false); // not implemented TODO } else { - ep2_map_dst(pt, msg, len, dst, dst_len); + blst_hash_to_g2(pt, msg, len, dst, dst_len, nullptr, 0); + blst_sign_pk_in_g1(pt, pt, keydata); } - - g2_mul(pt, pt, keydata); - G2Element ret = G2Element::FromNative(pt); - g2_free(pt); + G2Element ret = G2Element::FromNative(*pt); + Util::SecFree(pt); return ret; } void PrivateKey::AllocateKeyData() { assert(!keydata); - keydata = Util::SecAlloc(1); - keydata->alloc = RLC_BN_SIZE; - bn_zero(keydata); + keydata = Util::SecAlloc(1); + memset(keydata, 0x00, sizeof(blst_scalar)); } void PrivateKey::CheckKeyData() const { if (keydata == nullptr) { - throw std::runtime_error("PrivateKey::CheckKeyData keydata not initialized"); + throw std::runtime_error( + "PrivateKey::CheckKeyData keydata not initialized"); } } diff --git a/src/schemes.cpp b/src/schemes.cpp index 3b5a4a075..b32d59365 100644 --- a/src/schemes.cpp +++ b/src/schemes.cpp @@ -12,6 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include "schemes.hpp" + +#include + #include #include #include @@ -19,7 +23,6 @@ #include "bls.hpp" #include "elements.hpp" -#include "schemes.hpp" #include "hdkeys.hpp" using std::string; @@ -97,13 +100,13 @@ static void HashPubKeys(bn_t* computedTs, size_t nPubKeys, GetBytesFn getBytes) bn_free(order); } -enum InvariantResult { BAD=false, GOOD=true, CONTINUE }; +enum InvariantResult { BAD = false, GOOD = true, CONTINUE }; // Enforce argument invariants for Agg Sig Verification InvariantResult VerifyAggregateSignatureArguments( const size_t nPubKeys, const size_t nMessages, - const G2Element &signature) + const G2Element& signature) { if (nPubKeys == 0) { return (nMessages == 0 && signature == G2Element() ? GOOD : BAD); @@ -117,47 +120,60 @@ InvariantResult VerifyAggregateSignatureArguments( /* These are all for the min-pubkey-size variant. TODO : analogs for min-signature-size */ -const std::string BasicSchemeMPL::CIPHERSUITE_ID = "BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_"; -const std::string AugSchemeMPL::CIPHERSUITE_ID = "BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_AUG_"; -const std::string PopSchemeMPL::CIPHERSUITE_ID = "BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_"; -const std::string PopSchemeMPL::POP_CIPHERSUITE_ID = "BLS_POP_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_"; +const std::string BasicSchemeMPL::CIPHERSUITE_ID = + "BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_"; +const std::string AugSchemeMPL::CIPHERSUITE_ID = + "BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_AUG_"; +const std::string PopSchemeMPL::CIPHERSUITE_ID = + "BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_"; +const std::string PopSchemeMPL::POP_CIPHERSUITE_ID = + "BLS_POP_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_"; -PrivateKey CoreMPL::KeyGen(const vector& seed) { +PrivateKey CoreMPL::KeyGen(const vector& seed) +{ return HDKeys::KeyGen(seed); } -PrivateKey CoreMPL::KeyGen(const Bytes& seed) { - return HDKeys::KeyGen(seed); -} +PrivateKey CoreMPL::KeyGen(const Bytes& seed) { return HDKeys::KeyGen(seed); } -vector CoreMPL::SkToPk(const PrivateKey &seckey) +vector CoreMPL::SkToPk(const PrivateKey& seckey) { return seckey.GetG1Element().Serialize(); } -G1Element CoreMPL::SkToG1(const PrivateKey &seckey) +G1Element CoreMPL::SkToG1(const PrivateKey& seckey) { return seckey.GetG1Element(); } -G2Element CoreMPL::Sign(const PrivateKey &seckey, const vector &message) +G2Element CoreMPL::Sign( + const PrivateKey& seckey, + const vector& message) { return CoreMPL::Sign(seckey, Bytes(message)); } G2Element CoreMPL::Sign(const PrivateKey& seckey, const Bytes& message) { - return seckey.SignG2(message.begin(), message.size(), (const uint8_t*)strCiphersuiteId.c_str(), strCiphersuiteId.length()); + return seckey.SignG2( + message.begin(), + message.size(), + (const uint8_t*)strCiphersuiteId.c_str(), + strCiphersuiteId.length()); } -bool CoreMPL::Verify(const vector &pubkey, - const vector &message, // unhashed - const vector &signature) +bool CoreMPL::Verify( + const vector& pubkey, + const vector& message, // unhashed + const vector& signature) { return CoreMPL::Verify(Bytes(pubkey), Bytes(message), Bytes(signature)); } -bool CoreMPL::Verify(const Bytes& pubkey, const Bytes& message, const Bytes& signature) +bool CoreMPL::Verify( + const Bytes& pubkey, + const Bytes& message, + const Bytes& signature) { G1Element pubkeyElement; G2Element signatureElement; @@ -170,32 +186,41 @@ bool CoreMPL::Verify(const Bytes& pubkey, const Bytes& message, const Bytes& sig return CoreMPL::Verify(pubkeyElement, message, signatureElement); } -bool CoreMPL::Verify(const G1Element &pubkey, - const vector &message, // unhashed - const G2Element &signature) +bool CoreMPL::Verify( + const G1Element& pubkey, + const vector& message, // unhashed + const G2Element& signature) { return CoreMPL::Verify(pubkey, Bytes(message), signature); } -bool CoreMPL::Verify(const G1Element& pubkey, const Bytes& message, const G2Element& signature) +bool CoreMPL::Verify( + const G1Element& pubkey, + const Bytes& message, + const G2Element& signature) { - const G2Element hashedPoint = G2Element::FromMessage(message, (const uint8_t*)strCiphersuiteId.c_str(), strCiphersuiteId.length()); + blst_p1_affine pubkeyAffine; + blst_p2_affine sigAffine; - std::array g1s; - std::array g2s; - - G1Element::Generator().Negate().ToNative(g1s[0]); if (!pubkey.IsValid() || pubkey == G1Element()) { return false; } if (!signature.IsValid() || signature == G2Element()) { return false; } - pubkey.ToNative(g1s[1]); - signature.ToNative(g2s[0]); - hashedPoint.ToNative(g2s[1]); + pubkey.ToAffine(&pubkeyAffine); + signature.ToAffine(&sigAffine); - return CoreMPL::NativeVerify(g1s.data(), g2s.data(), 2); + auto err = blst_core_verify_pk_in_g1( + &pubkeyAffine, + &sigAffine, + true, /*hash*/ + message.begin(), + message.size(), + (const uint8_t*)strCiphersuiteId.c_str(), + strCiphersuiteId.length()); + + return err == BLST_SUCCESS; } std::array CoreMPL::Aggregate(const vector> &signatures) @@ -218,7 +243,7 @@ std::array CoreMPL::Aggregate(const vector& sig return CoreMPL::Aggregate(elements).SerializeToArray(); } -G2Element CoreMPL::Aggregate(const vector &signatures) +G2Element CoreMPL::Aggregate(const vector& signatures) { G2Element aggregated; for (const G2Element& signature : signatures) { @@ -227,7 +252,7 @@ G2Element CoreMPL::Aggregate(const vector &signatures) return aggregated; } -G1Element CoreMPL::Aggregate(const vector &publicKeys) +G1Element CoreMPL::Aggregate(const vector& publicKeys) { G1Element aggregated; for (const G1Element& publicKey : publicKeys) { @@ -313,18 +338,21 @@ bool CoreMPL::VerifySecure(const std::vector& vecPublicKeys, return CoreMPL::VerifySecure(vecPublicKeys, signature, message, false); } -bool CoreMPL::AggregateVerify(const vector> &pubkeys, - const vector> &messages, // unhashed - const vector &signature) +bool CoreMPL::AggregateVerify( + const vector>& pubkeys, + const vector>& messages, // unhashed + const vector& signature) { const std::vector vecPubKeyBytes(pubkeys.begin(), pubkeys.end()); const std::vector vecMessagesBytes(messages.begin(), messages.end()); - return CoreMPL::AggregateVerify(vecPubKeyBytes, vecMessagesBytes, Bytes(signature)); + return CoreMPL::AggregateVerify( + vecPubKeyBytes, vecMessagesBytes, Bytes(signature)); } -bool CoreMPL::AggregateVerify(const vector& pubkeys, - const vector& messages, // unhashed - const Bytes& signature) +bool CoreMPL::AggregateVerify( + const vector& pubkeys, + const vector& messages, // unhashed + const Bytes& signature) { const size_t nPubKeys = pubkeys.size(); G2Element signatureElement; @@ -333,7 +361,8 @@ bool CoreMPL::AggregateVerify(const vector& pubkeys, } catch (...) { return false; } - const auto arg_check = VerifyAggregateSignatureArguments(nPubKeys, messages.size(), signatureElement); + const auto arg_check = VerifyAggregateSignatureArguments( + nPubKeys, messages.size(), signatureElement); if (arg_check != CONTINUE) { return arg_check; } @@ -350,92 +379,101 @@ bool CoreMPL::AggregateVerify(const vector& pubkeys, return CoreMPL::AggregateVerify(pubkeyElements, messages, signatureElement); } -bool CoreMPL::AggregateVerify(const vector &pubkeys, - const vector> &messages, - const G2Element &signature) +bool CoreMPL::AggregateVerify( + const vector& pubkeys, + const vector>& messages, + const G2Element& signature) { - return CoreMPL::AggregateVerify(pubkeys, std::vector(messages.begin(), messages.end()), signature); + return CoreMPL::AggregateVerify( + pubkeys, + std::vector(messages.begin(), messages.end()), + signature); } -bool CoreMPL::AggregateVerify(const vector& pubkeys, - const vector &messages, - const G2Element& signature) +bool CoreMPL::AggregateVerify( + const vector& pubkeys, + const vector& messages, + const G2Element& signature) { const size_t nPubKeys = pubkeys.size(); - const auto arg_check = VerifyAggregateSignatureArguments(nPubKeys, messages.size(), signature); + const auto arg_check = + VerifyAggregateSignatureArguments(nPubKeys, messages.size(), signature); if (arg_check != CONTINUE) { return arg_check; } - std::vector vecG1(nPubKeys + 1); - std::vector vecG2(nPubKeys + 1); - G1Element::Generator().Negate().ToNative(&vecG1[0]); if (!signature.IsValid() || signature == G2Element()) { return false; } - signature.ToNative(&vecG2[0]); + blst_pairing* ctx = (blst_pairing*)malloc(blst_pairing_sizeof()); + blst_pairing_init( + ctx, + true /*hash*/, + (const uint8_t*)strCiphersuiteId.c_str(), + strCiphersuiteId.length()); - for (size_t i = 0; i < nPubKeys; ++i) { + blst_p1_affine pk_affine; + blst_p2_affine sig_affine; + blst_fp12 gtsig; + + signature.ToAffine(&sig_affine); + + blst_aggregated_in_g2(>sig, &sig_affine); + + for (size_t i = 0; i < nPubKeys; i++) { if (!pubkeys[i].IsValid() || pubkeys[i] == G1Element()) { + free(ctx); return false; } - pubkeys[i].ToNative(&vecG1[i + 1]); - G2Element::FromMessage(messages[i], (const uint8_t*)strCiphersuiteId.c_str(), strCiphersuiteId.length()).ToNative(&vecG2[i + 1]); - } - - return CoreMPL::NativeVerify((g1_t*)vecG1.data(), (g2_t*)vecG2.data(), nPubKeys + 1); -} - -bool CoreMPL::NativeVerify(g1_t *pubkeys, g2_t *mappedHashes, size_t length) -{ - gt_t target, candidate, tmpPairing; - fp12_zero(target); - fp_set_dig(target[0][0][0], 1); - fp12_zero(candidate); - fp_set_dig(candidate[0][0][0], 1); + pubkeys[i].ToAffine(&pk_affine); - // prod e(pubkey[i], hash[i]) * e(-g1, aggSig) - // Performs pubKeys.size() pairings, 250 at a time + auto err = blst_pairing_aggregate_pk_in_g1( + ctx, &pk_affine, nullptr, messages[i].begin(), messages[i].size()); - for (size_t i = 0; i < length; i += 250) { - size_t numPairings = std::min((length - i), (size_t)250); - pc_map_sim(tmpPairing, pubkeys + i, mappedHashes + i, numPairings); - fp12_mul(candidate, candidate, tmpPairing); + if (err != BLST_SUCCESS) { + free(ctx); + return false; + } } - // 1 =? prod e(pubkey[i], hash[i]) * e(-g1, aggSig) - if (gt_cmp(target, candidate) != RLC_EQ || core_get()->code != RLC_OK) { - core_get()->code = RLC_OK; - return false; - } - BLS::CheckRelicErrors(); - return true; + blst_pairing_commit(ctx); + auto ret = blst_pairing_finalverify(ctx, >sig); + free(ctx); + return ret; } -PrivateKey CoreMPL::DeriveChildSk(const PrivateKey& sk, uint32_t index) { +PrivateKey CoreMPL::DeriveChildSk(const PrivateKey& sk, uint32_t index) +{ return HDKeys::DeriveChildSk(sk, index); } -PrivateKey CoreMPL::DeriveChildSkUnhardened(const PrivateKey& sk, uint32_t index) { +PrivateKey CoreMPL::DeriveChildSkUnhardened( + const PrivateKey& sk, + uint32_t index) +{ return HDKeys::DeriveChildSkUnhardened(sk, index); } -G1Element CoreMPL::DeriveChildPkUnhardened(const G1Element& pk, uint32_t index) { +G1Element CoreMPL::DeriveChildPkUnhardened(const G1Element& pk, uint32_t index) +{ return HDKeys::DeriveChildG1Unhardened(pk, index); } -bool BasicSchemeMPL::AggregateVerify(const vector> &pubkeys, - const vector> &messages, - const vector &signature) +bool BasicSchemeMPL::AggregateVerify( + const vector>& pubkeys, + const vector>& messages, + const vector& signature) { const std::vector vecPubKeyBytes(pubkeys.begin(), pubkeys.end()); const std::vector vecMessagesBytes(messages.begin(), messages.end()); - return BasicSchemeMPL::AggregateVerify(vecPubKeyBytes, vecMessagesBytes, Bytes(signature)); + return BasicSchemeMPL::AggregateVerify( + vecPubKeyBytes, vecMessagesBytes, Bytes(signature)); } -bool BasicSchemeMPL::AggregateVerify(const vector& pubkeys, - const vector& messages, - const Bytes& signature) +bool BasicSchemeMPL::AggregateVerify( + const vector& pubkeys, + const vector& messages, + const Bytes& signature) { const size_t nPubKeys = pubkeys.size(); G2Element signatureElement; @@ -444,8 +482,10 @@ bool BasicSchemeMPL::AggregateVerify(const vector& pubkeys, } catch (...) { return false; } - const auto arg_check = VerifyAggregateSignatureArguments(nPubKeys, messages.size(), signatureElement); - if (arg_check != CONTINUE) return arg_check; + const auto arg_check = VerifyAggregateSignatureArguments( + nPubKeys, messages.size(), signatureElement); + if (arg_check != CONTINUE) + return arg_check; std::set> setMessages; for (const auto& message : messages) { @@ -467,30 +507,36 @@ bool BasicSchemeMPL::AggregateVerify(const vector& pubkeys, return CoreMPL::AggregateVerify(pubkeyElements, messages, signatureElement); } -bool BasicSchemeMPL::AggregateVerify(const vector &pubkeys, - const vector> &messages, - const G2Element &signature) +bool BasicSchemeMPL::AggregateVerify( + const vector& pubkeys, + const vector>& messages, + const G2Element& signature) { const size_t nPubKeys = pubkeys.size(); - const auto arg_check = VerifyAggregateSignatureArguments(nPubKeys, messages.size(), signature); + const auto arg_check = + VerifyAggregateSignatureArguments(nPubKeys, messages.size(), signature); if (arg_check != CONTINUE) { return arg_check; } - const std::set> setMessages(messages.begin(), messages.end()); + const std::set> setMessages( + messages.begin(), messages.end()); if (setMessages.size() != nPubKeys) { return false; } return CoreMPL::AggregateVerify(pubkeys, messages, signature); } -bool BasicSchemeMPL::AggregateVerify(const vector& pubkeys, - const vector &messages, - const G2Element& signature) +bool BasicSchemeMPL::AggregateVerify( + const vector& pubkeys, + const vector& messages, + const G2Element& signature) { const size_t nPubKeys = pubkeys.size(); - const auto arg_check = VerifyAggregateSignatureArguments(nPubKeys, messages.size(), signature); - if (arg_check != CONTINUE) return arg_check; + const auto arg_check = + VerifyAggregateSignatureArguments(nPubKeys, messages.size(), signature); + if (arg_check != CONTINUE) + return arg_check; std::set> setMessages; for (const auto& message : messages) { @@ -502,7 +548,9 @@ bool BasicSchemeMPL::AggregateVerify(const vector& pubkeys, return CoreMPL::AggregateVerify(pubkeys, messages, signature); } -G2Element AugSchemeMPL::Sign(const PrivateKey &seckey, const vector &message) +G2Element AugSchemeMPL::Sign( + const PrivateKey& seckey, + const vector& message) { return AugSchemeMPL::Sign(seckey, message, seckey.GetG1Element()); } @@ -513,17 +561,19 @@ G2Element AugSchemeMPL::Sign(const PrivateKey& seckey, const Bytes& message) } // Used for prepending different augMessage -G2Element AugSchemeMPL::Sign(const PrivateKey &seckey, - const vector &message, - const G1Element &prepend_pk) +G2Element AugSchemeMPL::Sign( + const PrivateKey& seckey, + const vector& message, + const G1Element& prepend_pk) { return AugSchemeMPL::Sign(seckey, Bytes(message), prepend_pk); } // Used for prepending different augMessage -G2Element AugSchemeMPL::Sign(const PrivateKey& seckey, - const Bytes& message, - const G1Element& prepend_pk) +G2Element AugSchemeMPL::Sign( + const PrivateKey& seckey, + const Bytes& message, + const G1Element& prepend_pk) { vector augMessage = prepend_pk.Serialize(); augMessage.reserve(augMessage.size() + message.size()); @@ -531,9 +581,10 @@ G2Element AugSchemeMPL::Sign(const PrivateKey& seckey, return CoreMPL::Sign(seckey, augMessage); } -bool AugSchemeMPL::Verify(const vector &pubkey, - const vector &message, - const vector &signature) +bool AugSchemeMPL::Verify( + const vector& pubkey, + const vector& message, + const vector& signature) { vector augMessage(pubkey); augMessage.reserve(augMessage.size() + message.size()); @@ -541,9 +592,10 @@ bool AugSchemeMPL::Verify(const vector &pubkey, return CoreMPL::Verify(pubkey, augMessage, signature); } -bool AugSchemeMPL::Verify(const Bytes& pubkey, - const Bytes& message, - const Bytes& signature) +bool AugSchemeMPL::Verify( + const Bytes& pubkey, + const Bytes& message, + const Bytes& signature) { vector augMessage(pubkey.begin(), pubkey.end()); augMessage.reserve(augMessage.size() + message.size()); @@ -551,16 +603,18 @@ bool AugSchemeMPL::Verify(const Bytes& pubkey, return CoreMPL::Verify(pubkey, Bytes(augMessage), Bytes(signature)); } -bool AugSchemeMPL::Verify(const G1Element &pubkey, - const vector &message, - const G2Element &signature) +bool AugSchemeMPL::Verify( + const G1Element& pubkey, + const vector& message, + const G2Element& signature) { return AugSchemeMPL::Verify(pubkey, Bytes(message), signature); } -bool AugSchemeMPL::Verify(const G1Element& pubkey, - const Bytes& message, - const G2Element& signature) +bool AugSchemeMPL::Verify( + const G1Element& pubkey, + const Bytes& message, + const G2Element& signature) { vector augMessage = pubkey.Serialize(); augMessage.reserve(augMessage.size() + message.size()); @@ -568,18 +622,21 @@ bool AugSchemeMPL::Verify(const G1Element& pubkey, return CoreMPL::Verify(pubkey, augMessage, signature); } -bool AugSchemeMPL::AggregateVerify(const vector> &pubkeys, - const vector> &messages, - const vector &signature) +bool AugSchemeMPL::AggregateVerify( + const vector>& pubkeys, + const vector>& messages, + const vector& signature) { std::vector vecPubKeyBytes(pubkeys.begin(), pubkeys.end()); std::vector vecMessagesBytes(messages.begin(), messages.end()); - return AugSchemeMPL::AggregateVerify(vecPubKeyBytes, vecMessagesBytes, Bytes(signature)); + return AugSchemeMPL::AggregateVerify( + vecPubKeyBytes, vecMessagesBytes, Bytes(signature)); } -bool AugSchemeMPL::AggregateVerify(const vector& pubkeys, - const vector& messages, - const Bytes& signature) +bool AugSchemeMPL::AggregateVerify( + const vector& pubkeys, + const vector& messages, + const Bytes& signature) { size_t nPubKeys = pubkeys.size(); G2Element signatureElement; @@ -588,7 +645,8 @@ bool AugSchemeMPL::AggregateVerify(const vector& pubkeys, } catch (...) { return false; } - auto arg_check = VerifyAggregateSignatureArguments(nPubKeys, messages.size(), signatureElement); + auto arg_check = VerifyAggregateSignatureArguments( + nPubKeys, messages.size(), signatureElement); if (arg_check != CONTINUE) { return arg_check; } @@ -601,30 +659,34 @@ bool AugSchemeMPL::AggregateVerify(const vector& pubkeys, aug.insert(aug.end(), messages[i].begin(), messages[i].end()); } - std::vector vecAugMessageBytes(augMessages.begin(), augMessages.end()); + std::vector vecAugMessageBytes( + augMessages.begin(), augMessages.end()); return CoreMPL::AggregateVerify(pubkeys, vecAugMessageBytes, signature); } -bool AugSchemeMPL::AggregateVerify(const vector& pubkeys, - const vector>& messages, - const G2Element& signature) +bool AugSchemeMPL::AggregateVerify( + const vector& pubkeys, + const vector>& messages, + const G2Element& signature) { std::vector vecMessagesBytes(messages.begin(), messages.end()); return AugSchemeMPL::AggregateVerify(pubkeys, vecMessagesBytes, signature); } -bool AugSchemeMPL::AggregateVerify(const vector& pubkeys, - const vector& messages, - const G2Element& signature) +bool AugSchemeMPL::AggregateVerify( + const vector& pubkeys, + const vector& messages, + const G2Element& signature) { size_t nPubKeys = pubkeys.size(); - auto arg_check = VerifyAggregateSignatureArguments(nPubKeys, messages.size(), signature); + auto arg_check = + VerifyAggregateSignatureArguments(nPubKeys, messages.size(), signature); if (arg_check != CONTINUE) { return arg_check; } vector> augMessages(nPubKeys); - for (int i = 0; i < nPubKeys; ++i) { + for (size_t i = 0; i < nPubKeys; ++i) { vector& aug = augMessages[i]; vector&& pubkey = pubkeys[i].Serialize(); aug.reserve(pubkey.size() + messages[i].size()); @@ -651,7 +713,7 @@ bool AugSchemeMPL::VerifySecure(const std::vector& vecPublicKeys, return false; } - BNArray computedTs(vecPublicKeys.size()); + std::vector computedTs(vecPublicKeys.size()); std::vector> vecSorted(vecPublicKeys.size()); for (size_t i = 0; i < vecPublicKeys.size(); i++) { vecSorted[i] = vecPublicKeys[i].SerializeToArray(false); @@ -676,15 +738,20 @@ bool AugSchemeMPL::VerifySecure(const std::vector& vecPublicKeys, return CoreMPL::AggregateVerify(weightedPks, augMessages, signature); } -G2Element PopSchemeMPL::PopProve(const PrivateKey &seckey) +G2Element PopSchemeMPL::PopProve(const PrivateKey& seckey) { - const G1Element& pk = seckey.GetG1Element(); - const G2Element hashedKey = G2Element::FromMessage(pk.SerializeToArray(), (const uint8_t *)POP_CIPHERSUITE_ID.c_str(), POP_CIPHERSUITE_ID.length()); - return seckey.GetG2Power(hashedKey); -} + std::array> pubkey_bytes = seckey.GetG1Element().SerializeToArray(); + return seckey.SignG2( + pubkey_bytes.data(), + pubkey_bytes.size(), + (const uint8_t*)POP_CIPHERSUITE_ID.c_str(), + POP_CIPHERSUITE_ID.length()); +} -bool PopSchemeMPL::PopVerify(const G1Element &pubkey, const G2Element &signature_proof) +bool PopSchemeMPL::PopVerify( + const G1Element& pubkey, + const G2Element& signature_proof) { if (!pubkey.IsValid() || pubkey == G1Element()) { return false; @@ -692,21 +759,28 @@ bool PopSchemeMPL::PopVerify(const G1Element &pubkey, const G2Element &signature if (!signature_proof.IsValid() || signature_proof == G2Element()) { return false; } + blst_p1_affine pubkeyAffine; + blst_p2_affine sigAffine; - const G2Element hashedPoint = G2Element::FromMessage(pubkey.SerializeToArray(), (const uint8_t*)POP_CIPHERSUITE_ID.c_str(), POP_CIPHERSUITE_ID.length()); + pubkey.ToAffine(&pubkeyAffine); + signature_proof.ToAffine(&sigAffine); + std::array pubkey_bytes = pubkey.Serialize(); - g1_t g1s[2]; - g2_t g2s[2]; + auto err = blst_core_verify_pk_in_g1( + &pubkeyAffine, + &sigAffine, + true, /*hash*/ + pubkey_bytes.data(), + pubkey_bytes.size(), + (const uint8_t*)POP_CIPHERSUITE_ID.c_str(), + POP_CIPHERSUITE_ID.length()); - G1Element::Generator().Negate().ToNative(g1s[0]); - pubkey.ToNative(g1s[1]); - signature_proof.ToNative(g2s[0]); - hashedPoint.ToNative(g2s[1]); - - return CoreMPL::NativeVerify(g1s, g2s, 2); + return err == BLST_SUCCESS; } -bool PopSchemeMPL::PopVerify(const vector &pubkey, const vector &proof) +bool PopSchemeMPL::PopVerify( + const vector& pubkey, + const vector& proof) { return PopSchemeMPL::PopVerify(Bytes(pubkey), Bytes(proof)); } @@ -724,16 +798,19 @@ bool PopSchemeMPL::PopVerify(const Bytes& pubkey, const Bytes& proof) return PopSchemeMPL::PopVerify(pk, proofElement); } -bool PopSchemeMPL::FastAggregateVerify(const vector &pubkeys, - const vector &message, - const G2Element &signature) +bool PopSchemeMPL::FastAggregateVerify( + const vector& pubkeys, + const vector& message, + const G2Element& signature) { - return PopSchemeMPL::FastAggregateVerify(pubkeys, Bytes(message), signature); + return PopSchemeMPL::FastAggregateVerify( + pubkeys, Bytes(message), signature); } -bool PopSchemeMPL::FastAggregateVerify(const vector& pubkeys, - const Bytes& message, - const G2Element& signature) +bool PopSchemeMPL::FastAggregateVerify( + const vector& pubkeys, + const Bytes& message, + const G2Element& signature) { if (pubkeys.size() == 0) { return false; @@ -746,21 +823,25 @@ bool PopSchemeMPL::FastAggregateVerify(const vector& pubkeys, return false; } } - // No VerifyAggregateSignatureArguments checks required here as we have exactly one pubkey and one message. + // No VerifyAggregateSignatureArguments checks required here as we have + // exactly one pubkey and one message. return CoreMPL::Verify(CoreMPL::Aggregate(pubkeys), message, signature); } -bool PopSchemeMPL::FastAggregateVerify(const vector> &pubkeys, - const vector &message, - const vector &signature) +bool PopSchemeMPL::FastAggregateVerify( + const vector>& pubkeys, + const vector& message, + const vector& signature) { const std::vector vecPubKeyBytes(pubkeys.begin(), pubkeys.end()); - return PopSchemeMPL::FastAggregateVerify(vecPubKeyBytes, Bytes(message), Bytes(signature)); + return PopSchemeMPL::FastAggregateVerify( + vecPubKeyBytes, Bytes(message), Bytes(signature)); } -bool PopSchemeMPL::FastAggregateVerify(const vector& pubkeys, - const Bytes& message, - const Bytes& signature) +bool PopSchemeMPL::FastAggregateVerify( + const vector& pubkeys, + const Bytes& message, + const Bytes& signature) { const size_t nPubKeys = pubkeys.size(); if (nPubKeys == 0) { @@ -778,7 +859,8 @@ bool PopSchemeMPL::FastAggregateVerify(const vector& pubkeys, return false; } - return PopSchemeMPL::FastAggregateVerify(pkelements, message, signatureElement); + return PopSchemeMPL::FastAggregateVerify( + pkelements, message, signatureElement); } G2Element LegacySchemeMPL::Sign(const PrivateKey& seckey, const Bytes& message) @@ -788,16 +870,16 @@ G2Element LegacySchemeMPL::Sign(const PrivateKey& seckey, const Bytes& message) bool LegacySchemeMPL::Verify(const G1Element &pubkey, const Bytes& message, const G2Element &signature) { - g1_t g1s[2]; - g2_t g2s[2]; - - G1Element::Generator().Negate().ToNative(g1s[0]); if (!pubkey.IsValid() || pubkey == G1Element()) { return false; } if (!signature.IsValid() || signature == G2Element()) { return false; } + g1_t g1s[2]; + g2_t g2s[2]; + + G1Element::Generator().Negate().ToNative(g1s[0]); pubkey.ToNative(g1s[1]); signature.ToNative(g2s[0]); G2Element::FromMessage(message, nullptr, 0, true).ToNative(g2s[1]); @@ -825,12 +907,12 @@ bool LegacySchemeMPL::AggregateVerify(const vector &pubkeys, const auto arg_check = VerifyAggregateSignatureArguments(nPubKeys, messages.size(), signature); if (arg_check != CONTINUE) return arg_check; - std::vector vecG1(nPubKeys + 1); - std::vector vecG2(nPubKeys + 1); - G1Element::Generator().Negate().ToNative(&vecG1[0]); if (!signature.IsValid() || signature == G2Element()) { return false; } + std::vector vecG1(nPubKeys + 1); + std::vector vecG2(nPubKeys + 1); + G1Element::Generator().Negate().ToNative(&vecG1[0]); signature.ToNative(&vecG2[0]); for (size_t i = 0; i < nPubKeys; ++i) { diff --git a/src/test-bench.cpp b/src/test-bench.cpp index 30b9b58d3..0cc476790 100644 --- a/src/test-bench.cpp +++ b/src/test-bench.cpp @@ -13,22 +13,27 @@ // limitations under the License. #include + #include "bls.hpp" #include "test-utils.hpp" -extern "C" { -#include "relic.h" -} - using std::string; using std::vector; -using std::cout; -using std::endl; using namespace bls; +std::vector wjbgetRandomSeed() +{ + uint8_t buf[32]; + + for (int i = 0; i < 32; i++) buf[i] = rand(); + + std::vector ret(buf, buf + 32); + return ret; +} -void benchSigs() { +void benchSigs() +{ string testName = "Signing"; const int numIters = 5000; PrivateKey sk = BasicSchemeMPL().KeyGen(getRandomSeed()); @@ -42,21 +47,22 @@ void benchSigs() { endStopwatch(testName, start, numIters); } -void benchVerification() { +void benchVerification() +{ string testName = "Verification"; const int numIters = 1000; PrivateKey sk = BasicSchemeMPL().KeyGen(getRandomSeed()); G1Element pk = sk.GetG1Element(); - std::vector sigs; + vector pkBytes = pk.Serialize(); for (int i = 0; i < numIters; i++) { uint8_t message[4]; Util::IntToFourBytes(message, i); vector messageBytes(message, message + 4); - sigs.push_back(BasicSchemeMPL().Sign(sk, messageBytes)); + G2Element sig = BasicSchemeMPL().Sign(sk, messageBytes); + sigs.push_back(sig); } - auto start = startStopwatch(); for (int i = 0; i < numIters; i++) { uint8_t message[4]; @@ -68,7 +74,8 @@ void benchVerification() { endStopwatch(testName, start, numIters); } -void benchBatchVerification() { +void benchBatchVerification() +{ const int numIters = 10000; vector> sig_bytes; @@ -116,7 +123,8 @@ void benchBatchVerification() { endStopwatch("Batch verification", start, numIters); } -void benchSerialize() { +void benchSerialize() +{ const int numIters = 5000000; PrivateKey sk = BasicSchemeMPL().KeyGen(getRandomSeed()); G1Element pk = sk.GetG1Element(); @@ -142,7 +150,8 @@ void benchSerialize() { endStopwatch("Serialize G2Element", start, numIters); } -void benchSerializeToArray() { +void benchSerializeToArray() +{ const int numIters = 5000000; PrivateKey sk = BasicSchemeMPL().KeyGen(getRandomSeed()); G1Element pk = sk.GetG1Element(); diff --git a/src/test.cpp b/src/test.cpp index 97730b570..f114694a5 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -19,23 +19,25 @@ #include "bls.hpp" #include -extern "C" { -#include "relic.h" -} #include "test-utils.hpp" #include "test-vectors.hpp" #include #include -using std::cout; -using std::endl; using std::string; using std::vector; using namespace bls; -void TestHKDF(string ikm_hex, string salt_hex, string info_hex, string prk_expected_hex, string okm_expected_hex, int L) { +void TestHKDF( + string ikm_hex, + string salt_hex, + string info_hex, + string prk_expected_hex, + string okm_expected_hex, + int L) +{ vector ikm = Util::HexToBytes(ikm_hex); vector salt = Util::HexToBytes(salt_hex); vector info = Util::HexToBytes(info_hex); @@ -47,22 +49,24 @@ void TestHKDF(string ikm_hex, string salt_hex, string info_hex, string prk_expec HKDF256::Expand(okm, L, prk, info.data(), info.size()); REQUIRE(32 == prk_expected.size()); - REQUIRE(L == okm_expected.size()); + REQUIRE(L == (int)okm_expected.size()); - for (size_t i=0; i < 32; i++) { + for (size_t i = 0; i < 32; i++) { REQUIRE(prk[i] == prk_expected[i]); } - for (size_t i=0; i < L; i++) { + for (int i = 0; i < L; i++) { REQUIRE(okm[i] == okm_expected[i]); } delete[] okm; } -TEST_CASE("class PrivateKey") { +TEST_CASE("class PrivateKey") +{ uint8_t buffer[PrivateKey::PRIVATE_KEY_SIZE]; memcpy(buffer, getRandomSeed().data(), PrivateKey::PRIVATE_KEY_SIZE); - SECTION("Copy {constructor|assignment operator}") { + SECTION("Copy {constructor|assignment operator}") + { PrivateKey pk1 = PrivateKey::FromByteVector(getRandomSeed(), true); PrivateKey pk2 = PrivateKey::FromByteVector(getRandomSeed(), true); PrivateKey pk3 = PrivateKey(pk2); @@ -71,15 +75,16 @@ TEST_CASE("class PrivateKey") { REQUIRE(!pk3.IsZero()); REQUIRE(pk1 != pk2); REQUIRE(pk3 == pk2); - REQUIRE(pk2.GetG1Element().IsValid()); // cache previous g1 - REQUIRE(pk2.GetG2Element().IsValid()); // cache previous g2 + REQUIRE(pk2.GetG1Element().IsValid()); // cache previous g1 + REQUIRE(pk2.GetG2Element().IsValid()); // cache previous g2 pk2 = pk1; REQUIRE(pk1 == pk2); REQUIRE(pk1.GetG1Element() == pk2.GetG1Element()); REQUIRE(pk1.GetG2Element() == pk2.GetG2Element()); REQUIRE(pk3 != pk2); } - SECTION("Move {constructor|assignment operator}") { + SECTION("Move {constructor|assignment operator}") + { PrivateKey pk1 = PrivateKey::FromByteVector(getRandomSeed(), true); std::vector vec1 = pk1.Serialize(); PrivateKey pk2 = PrivateKey::FromByteVector(getRandomSeed(), true); @@ -99,7 +104,8 @@ TEST_CASE("class PrivateKey") { REQUIRE_THROWS(pk2.IsZero()); REQUIRE_THROWS(pk3.IsZero()); } - SECTION("Equality operators") { + SECTION("Equality operators") + { PrivateKey pk1 = PrivateKey::FromByteVector(getRandomSeed(), true); PrivateKey pk2 = PrivateKey::FromByteVector(getRandomSeed(), true); PrivateKey pk3 = pk2; @@ -107,25 +113,37 @@ TEST_CASE("class PrivateKey") { REQUIRE(pk1 != pk3); REQUIRE(pk2 == pk3); } - SECTION("(De)Serialization") { + SECTION("(De)Serialization") + { PrivateKey pk1 = PrivateKey::FromByteVector(getRandomSeed(), true); pk1.Serialize(buffer); - REQUIRE(memcmp(buffer, pk1.Serialize().data(), PrivateKey::PRIVATE_KEY_SIZE) == 0); + REQUIRE( + memcmp( + buffer, pk1.Serialize().data(), PrivateKey::PRIVATE_KEY_SIZE) == + 0); PrivateKey pk2 = PrivateKey::FromBytes(Bytes(buffer, PrivateKey::PRIVATE_KEY_SIZE), true); REQUIRE(pk1 == pk2); - REQUIRE_THROWS(PrivateKey::FromBytes(Bytes(buffer, PrivateKey::PRIVATE_KEY_SIZE - 1), true)); - REQUIRE_THROWS(PrivateKey::FromBytes(Bytes(buffer, PrivateKey::PRIVATE_KEY_SIZE + 1), true)); - REQUIRE_NOTHROW(PrivateKey::FromBytes(Bytes(buffer, PrivateKey::PRIVATE_KEY_SIZE), true)); - bn_t order; - bn_new(order); - g1_get_ord(order); - bn_write_bin(buffer, PrivateKey::PRIVATE_KEY_SIZE, order); - REQUIRE_NOTHROW(PrivateKey::FromBytes(Bytes(buffer, PrivateKey::PRIVATE_KEY_SIZE), false)); - REQUIRE_NOTHROW(PrivateKey::FromBytes(Bytes(buffer, PrivateKey::PRIVATE_KEY_SIZE), true)); - bn_add(order, order, order); - bn_write_bin(buffer, PrivateKey::PRIVATE_KEY_SIZE, order); - REQUIRE_THROWS(PrivateKey::FromBytes(Bytes(buffer, PrivateKey::PRIVATE_KEY_SIZE), false)); - REQUIRE_NOTHROW(PrivateKey::FromBytes(Bytes(buffer, PrivateKey::PRIVATE_KEY_SIZE), true)); + REQUIRE_THROWS(PrivateKey::FromBytes( + Bytes(buffer, PrivateKey::PRIVATE_KEY_SIZE - 1), true)); + REQUIRE_THROWS(PrivateKey::FromBytes( + Bytes(buffer, PrivateKey::PRIVATE_KEY_SIZE + 1), true)); + REQUIRE_NOTHROW(PrivateKey::FromBytes( + Bytes(buffer, PrivateKey::PRIVATE_KEY_SIZE), true)); + // blst_scalar order; + // memcpy(&order, BLS12_381_r, sizeof(blst_scalar)); + // g1_get_ord(order); + // bn_write_bin(buffer, PrivateKey::PRIVATE_KEY_SIZE, order); + // REQUIRE_NOTHROW(PrivateKey::FromBytes( + // Bytes(buffer, PrivateKey::PRIVATE_KEY_SIZE), false)); + // REQUIRE_NOTHROW(PrivateKey::FromBytes( + // Bytes(buffer, PrivateKey::PRIVATE_KEY_SIZE), true)); + // blst_sk_add_n_check(&order, &order, &order); + // bn_add(order, order, order); + // bn_write_bin(buffer, PrivateKey::PRIVATE_KEY_SIZE, order); + // REQUIRE_THROWS(PrivateKey::FromBytes( + // Bytes(buffer, PrivateKey::PRIVATE_KEY_SIZE), false)); + // REQUIRE_NOTHROW(PrivateKey::FromBytes( + // Bytes(buffer, PrivateKey::PRIVATE_KEY_SIZE), true)); } SECTION("BIP32 Seed") { uint8_t aliceSeed[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; @@ -141,7 +159,8 @@ TEST_CASE("class PrivateKey") { REQUIRE(pubKey1Bytes == knownPublicKey); REQUIRE(pubKey2Bytes != pubKey1Bytes); } - SECTION("keydata checks") { + SECTION("keydata checks") + { PrivateKey pk1 = PrivateKey::FromByteVector(getRandomSeed(), true); G1Element g1 = pk1.GetG1Element(); G2Element g2 = pk1.GetG2Element(); @@ -161,69 +180,86 @@ TEST_CASE("class PrivateKey") { REQUIRE_THROWS(pk1 != pk2); REQUIRE_THROWS(pk1.Serialize(buffer)); REQUIRE_THROWS(pk1.Serialize()); - REQUIRE_THROWS(pk1.SignG2(buffer, sizeof(buffer), buffer, sizeof(buffer))); + REQUIRE_THROWS( + pk1.SignG2(buffer, sizeof(buffer), buffer, sizeof(buffer))); } } -TEST_CASE("HKDF") { +TEST_CASE("HKDF") +{ // https://tools.ietf.org/html/rfc5869 test vectors - SECTION("Test case 2") { - TestHKDF("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", - "000102030405060708090a0b0c", - "f0f1f2f3f4f5f6f7f8f9", - "077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5", - "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865", - 42 - ); - } - SECTION("Test case 2") { - TestHKDF("000102030405060708090a0b0c0d0e0f" - "101112131415161718191a1b1c1d1e1f" - "202122232425262728292a2b2c2d2e2f" - "303132333435363738393a3b3c3d3e3f" - "404142434445464748494a4b4c4d4e4f", // 80 octets - "0x606162636465666768696a6b6c6d6e6f" - "707172737475767778797a7b7c7d7e7f" - "808182838485868788898a8b8c8d8e8f" - "909192939495969798999a9b9c9d9e9f" - "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf", // 80 octets - "0xb0b1b2b3b4b5b6b7b8b9babbbcbdbebf" - "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf" - "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf" - "e0e1e2e3e4e5e6e7e8e9eaebecedeeef" - "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", // 80 octets - "0x06a6b88c5853361a06104c9ceb35b45cef760014904671014a193f40c15fc244", // 32 octets - "0xb11e398dc80327a1c8e7f78c596a4934" - "4f012eda2d4efad8a050cc4c19afa97c" - "59045a99cac7827271cb41c65e590e09" - "da3275600c2f09b8367793a9aca3db71" - "cc30c58179ec3e87c14c01d5c1f3434f" - "1d87", // 82 octets - 82 - ); - } - SECTION("Test case 3") { - TestHKDF("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", - "", - "", - "19ef24a32c717b167f33a91d6f648bdf96596776afdb6377ac434c1c293ccb04", - "8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d9d201395faa4b61a96c8", - 42 - ); - } - SECTION("Works with multiple of 32") { - // This generates exactly 64 bytes. Uses a 32 byte key and 4 byte salt as in EIP2333. - TestHKDF("8704f9ac024139fe62511375cf9bc534c0507dcf00c41603ac935cd5943ce0b4b88599390de14e743ca2f56a73a04eae13aa3f3b969b39d8701e0d69a6f8d42f", - "53d8e19b", - "", - "eb01c9cd916653df76ffa61b6ab8a74e254ebfd9bfc43e624cc12a72b0373dee", - "8faabea85fc0c64e7ca86217cdc6dcdc88551c3244d56719e630a3521063082c46455c2fd5483811f9520a748f0099c1dfcfa52c54e1c22b5cdf70efb0f3c676", - 64 - ); + SECTION("Test case 2") + { + TestHKDF( + "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", + "000102030405060708090a0b0c", + "f0f1f2f3f4f5f6f7f8f9", + "077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5", + "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34" + "007208d5b887185865", + 42); + } + SECTION("Test case 2") + { + TestHKDF( + "000102030405060708090a0b0c0d0e0f" + "101112131415161718191a1b1c1d1e1f" + "202122232425262728292a2b2c2d2e2f" + "303132333435363738393a3b3c3d3e3f" + "404142434445464748494a4b4c4d4e4f", // 80 octets + "0x606162636465666768696a6b6c6d6e6f" + "707172737475767778797a7b7c7d7e7f" + "808182838485868788898a8b8c8d8e8f" + "909192939495969798999a9b9c9d9e9f" + "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf", // 80 octets + "0xb0b1b2b3b4b5b6b7b8b9babbbcbdbebf" + "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf" + "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf" + "e0e1e2e3e4e5e6e7e8e9eaebecedeeef" + "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", // 80 octets + "0x06a6b88c5853361a06104c9ceb35b45cef760014904671014a193f40c15fc24" + "4", // 32 octets + "0xb11e398dc80327a1c8e7f78c596a4934" + "4f012eda2d4efad8a050cc4c19afa97c" + "59045a99cac7827271cb41c65e590e09" + "da3275600c2f09b8367793a9aca3db71" + "cc30c58179ec3e87c14c01d5c1f3434f" + "1d87", // 82 octets + 82); + } + SECTION("Test case 3") + { + TestHKDF( + "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", + "", + "", + "19ef24a32c717b167f33a91d6f648bdf96596776afdb6377ac434c1c293ccb04", + "8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d9d" + "201395faa4b61a96c8", + 42); + } + SECTION("Works with multiple of 32") + { + // This generates exactly 64 bytes. Uses a 32 byte key and 4 byte salt + // as in EIP2333. + TestHKDF( + "8704f9ac024139fe62511375cf9bc534c0507dcf00c41603ac935cd5943ce0b4b8" + "8599390de14e743ca2f56a73a04eae13aa3f3b969b39d8701e0d69a6f8d42f", + "53d8e19b", + "", + "eb01c9cd916653df76ffa61b6ab8a74e254ebfd9bfc43e624cc12a72b0373dee", + "8faabea85fc0c64e7ca86217cdc6dcdc88551c3244d56719e630a3521063082c46" + "455c2fd5483811f9520a748f0099c1dfcfa52c54e1c22b5cdf70efb0f3c676", + 64); } } -void TestEIP2333(string seedHex, string masterSkHex, string childSkHex, uint32_t childIndex) { +void TestEIP2333( + string seedHex, + string masterSkHex, + string childSkHex, + uint32_t childIndex) +{ auto masterSk = Util::HexToBytes(masterSkHex); auto childSk = Util::HexToBytes(childSkHex); @@ -237,60 +273,73 @@ void TestEIP2333(string seedHex, string masterSkHex, string childSkHex, uint32_t REQUIRE(calculatedMaster.size() == 32); REQUIRE(calculatedChild.size() == 32); - for (int i=0; i<32; i++) { + for (int i = 0; i < 32; i++) { REQUIRE(calculatedMaster[i] == masterSk[i]); } - for (int i=0; i<32; i++) { + for (int i = 0; i < 32; i++) { REQUIRE(calculatedChild[i] == childSk[i]); } } -TEST_CASE("EIP-2333 hardened HD keys") { - // The comments in the test cases correspond to integers that are converted to - // bytes using python int.to_bytes(32, "big").hex(), since the EIP spec provides ints, but c++ - // does not support bigint by default - SECTION("EIP-2333 Test case 1"){ - TestEIP2333("3141592653589793238462643383279502884197169399375105820974944592", - // 36167147331491996618072159372207345412841461318189449162487002442599770291484 - "4ff5e145590ed7b71e577bb04032396d1619ff41cb4e350053ed2dce8d1efd1c", - // 41787458189896526028601807066547832426569899195138584349427756863968330588237 - "5c62dcf9654481292aafa3348f1d1b0017bbfb44d6881d26d2b17836b38f204d", - 3141592653 - ); - } - SECTION("EIP-2333 Test case 2"){ - TestEIP2333("0x0099FF991111002299DD7744EE3355BBDD8844115566CC55663355668888CC00", - // 13904094584487173309420026178174172335998687531503061311232927109397516192843 - "1ebd704b86732c3f05f30563dee6189838e73998ebc9c209ccff422adee10c4b", - // 12482522899285304316694838079579801944734479969002030150864436005368716366140 - "1b98db8b24296038eae3f64c25d693a269ef1e4d7ae0f691c572a46cf3c0913c", - 4294967295 - ); - } - SECTION("EIP-2333 Test case 3"){ - TestEIP2333("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3", - // 44010626067374404458092393860968061149521094673473131545188652121635313364506 - "614d21b10c0e4996ac0608e0e7452d5720d95d20fe03c59a3321000a42432e1a", - // 4011524214304750350566588165922015929937602165683407445189263506512578573606 - "08de7136e4afc56ae3ec03b20517d9c1232705a747f588fd17832f36ae337526", - 42 - ); - } - SECTION("EIP-2333 Test vector with intermediate values"){ - TestEIP2333("c55257c360c07c72029aebc1b53c05ed0362ada38ead3e3e9efa3708e53495531f09a6987599d18264c1e1c92f2cf141630c7a3c4ab7c81b2f001698e7463b04", - // 5399117110774477986698372024995405256382522670366369834617409486544348441851 - "0x0befcabff4a664461cc8f190cdd51c05621eb2837c71a1362df5b465a674ecfb", - // 11812940737387919040225825939013910852517748782307378293770044673328955938106 - "1a1de3346883401f1e3b2281be5774080edb8e5ebe6f776b0f7af9fea942553a", - 0 - ); +TEST_CASE("EIP-2333 hardened HD keys") +{ + // The comments in the test cases correspond to integers that are converted + // to bytes using python int.to_bytes(32, "big").hex(), since the EIP spec + // provides ints, but c++ does not support bigint by default + SECTION("EIP-2333 Test case 1") + { + TestEIP2333( + "3141592653589793238462643383279502884197169399375105820974944592", + // 36167147331491996618072159372207345412841461318189449162487002442599770291484 + "4ff5e145590ed7b71e577bb04032396d1619ff41cb4e350053ed2dce8d1efd1c", + // 41787458189896526028601807066547832426569899195138584349427756863968330588237 + "5c62dcf9654481292aafa3348f1d1b0017bbfb44d6881d26d2b17836b38f204d", + 3141592653); + } + SECTION("EIP-2333 Test case 2") + { + TestEIP2333( + "0x0099FF991111002299DD7744EE3355BBDD8844115566CC55663355668888CC0" + "0", + // 13904094584487173309420026178174172335998687531503061311232927109397516192843 + "1ebd704b86732c3f05f30563dee6189838e73998ebc9c209ccff422adee10c4b", + // 12482522899285304316694838079579801944734479969002030150864436005368716366140 + "1b98db8b24296038eae3f64c25d693a269ef1e4d7ae0f691c572a46cf3c0913c", + 4294967295); + } + SECTION("EIP-2333 Test case 3") + { + TestEIP2333( + "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa" + "3", + // 44010626067374404458092393860968061149521094673473131545188652121635313364506 + "614d21b10c0e4996ac0608e0e7452d5720d95d20fe03c59a3321000a42432e1a", + // 4011524214304750350566588165922015929937602165683407445189263506512578573606 + "08de7136e4afc56ae3ec03b20517d9c1232705a747f588fd17832f36ae337526", + 42); + } + SECTION("EIP-2333 Test vector with intermediate values") + { + TestEIP2333( + "c55257c360c07c72029aebc1b53c05ed0362ada38ead3e3e9efa3708e53495531f" + "09a6987599d18264c1e1c92f2cf141630c7a3c4ab7c81b2f001698e7463b04", + // 5399117110774477986698372024995405256382522670366369834617409486544348441851 + "0x0befcabff4a664461cc8f190cdd51c05621eb2837c71a1362df5b465a674ecf" + "b", + // 11812940737387919040225825939013910852517748782307378293770044673328955938106 + "1a1de3346883401f1e3b2281be5774080edb8e5ebe6f776b0f7af9fea942553a", + 0); } } -TEST_CASE("Unhardened HD keys") { - SECTION("Should match derivation through private and public keys"){ - const vector seed = {1, 50, 6, 244, 24, 199, 1, 25, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29}; +TEST_CASE("Unhardened HD keys") +{ + SECTION("Should match derivation through private and public keys") + { + const vector seed = {1, 50, 6, 244, 24, 199, 1, 25, 1, 2, + 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, 26, 27, 28, 29}; PrivateKey sk = BasicSchemeMPL().KeyGen(seed); G1Element pk = sk.GetG1Element(); @@ -300,15 +349,20 @@ TEST_CASE("Unhardened HD keys") { REQUIRE(childSk.GetG1Element() == childPk); - PrivateKey grandchildSk = BasicSchemeMPL().DeriveChildSkUnhardened(childSk, 12142); - G1Element grandcihldPk = BasicSchemeMPL().DeriveChildPkUnhardened(childPk, 12142); + PrivateKey grandchildSk = + BasicSchemeMPL().DeriveChildSkUnhardened(childSk, 12142); + G1Element grandchildPk = + BasicSchemeMPL().DeriveChildPkUnhardened(childPk, 12142); - REQUIRE(grandchildSk.GetG1Element() == grandcihldPk); + REQUIRE(grandchildSk.GetG1Element() == grandchildPk); } - SECTION("Should derive public child from parent") { - const vector seed = {2, 50, 6, 244, 24, 199, 1, 25, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29}; + SECTION("Should derive public child from parent") + { + const vector seed = {2, 50, 6, 244, 24, 199, 1, 25, 1, 2, + 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, 26, 27, 28, 29}; PrivateKey sk = BasicSchemeMPL().KeyGen(seed); G1Element pk = sk.GetG1Element(); @@ -323,10 +377,17 @@ TEST_CASE("Unhardened HD keys") { } } -TEST_CASE("IETF test vectors") { - SECTION ("Pyecc vector") { - string sig1BasicHex = "96ba34fac33c7f129d602a0bc8a3d43f9abc014eceaab7359146b4b150e57b808645738f35671e9e10e0d862a30cab70074eb5831d13e6a5b162d01eebe687d0164adbd0a864370a7c222a2768d7704da254f1bf1823665bc2361f9dd8c00e99"; - string sk = "0x0101010101010101010101010101010101010101010101010101010101010101"; +TEST_CASE("IETF test vectors") +{ + SECTION("Pyecc vector") + { + string sig1BasicHex = + "96ba34fac33c7f129d602a0bc8a3d43f9abc014eceaab7359146b4b150e57b8086" + "45738f35671e9e10e0d862a30cab70074eb5831d13e6a5b162d01eebe687d0164a" + "dbd0a864370a7c222a2768d7704da254f1bf1823665bc2361f9dd8c00e99"; + string sk = + "0x010101010101010101010101010101010101010101010101010101010101010" + "1"; vector msg = {3, 1, 4, 1, 5, 9}; auto skobj = PrivateKey::FromBytes(Bytes(Util::HexToBytes(sk))); G2Element sig = BasicSchemeMPL().Sign(skobj, msg); @@ -338,9 +399,10 @@ TEST_CASE("IETF test vectors") { } } - -TEST_CASE("Chia test vectors") { - SECTION("Chia test vectors 1 (Basic)") { +TEST_CASE("Chia test vectors") +{ + SECTION("Chia test vectors 1 (Basic)") + { vector seed1(32, 0x00); // All 0s vector seed2(32, 0x01); // All 1s vector message1 = {7, 8, 9}; @@ -350,7 +412,6 @@ TEST_CASE("Chia test vectors") { G1Element pk1 = sk1.GetG1Element(); G2Element sig1 = BasicSchemeMPL().Sign(sk1, message1); - PrivateKey sk2 = BasicSchemeMPL().KeyGen(seed2); G1Element pk2 = sk2.GetG1Element(); G2Element sig2 = BasicSchemeMPL().Sign(sk2, message2); @@ -373,20 +434,26 @@ TEST_CASE("Chia test vectors") { REQUIRE( Util::HexStr(sig2.Serialize()) == - "a9c4d3e689b82c7ec7e838dac2380cb014f9a08f6cd6ba044c263746e39a8f7a60ffee4afb7" - "8f146c2e421360784d58f0029491e3bd8ab84f0011d258471ba4e87059de295d9aba845c044e" + "a9c4d3e689b82c7ec7e838dac2380cb014f9a08f6cd6ba044c263746e39a8f7a60" + "ffee4afb7" + "8f146c2e421360784d58f0029491e3bd8ab84f0011d258471ba4e87059de295d9a" + "ba845c044e" "e83f6cf2411efd379ef38bf4cf41d5f3c0ae1205d"); G2Element aggSig1 = BasicSchemeMPL().Aggregate({sig1, sig2}); REQUIRE( Util::HexStr(aggSig1.Serialize()) == - "aee003c8cdaf3531b6b0ca354031b0819f7586b5846796615aee8108fec75ef838d181f9d24" - "4a94d195d7b0231d4afcf06f27f0cc4d3c72162545c240de7d5034a7ef3a2a03c0159de982fb" + "aee003c8cdaf3531b6b0ca354031b0819f7586b5846796615aee8108fec75ef838" + "d181f9d24" + "4a94d195d7b0231d4afcf06f27f0cc4d3c72162545c240de7d5034a7ef3a2a03c0" + "159de982fb" "c2e7790aeb455e27beae91d64e077c70b5506dea3"); - REQUIRE(BasicSchemeMPL().AggregateVerify({pk1, pk2}, vector>{message1, message2}, aggSig1)); - REQUIRE(!BasicSchemeMPL().AggregateVerify({pk1, pk2}, vector>{message1, message2}, sig1)); + REQUIRE(BasicSchemeMPL().AggregateVerify( + {pk1, pk2}, vector>{message1, message2}, aggSig1)); + REQUIRE(!BasicSchemeMPL().AggregateVerify( + {pk1, pk2}, vector>{message1, message2}, sig1)); REQUIRE(!BasicSchemeMPL().Verify(pk1, message1, sig2)); REQUIRE(!BasicSchemeMPL().Verify(pk1, message2, sig1)); @@ -400,15 +467,21 @@ TEST_CASE("Chia test vectors") { G2Element aggSig2 = BasicSchemeMPL().Aggregate({sig3, sig4, sig5}); - REQUIRE(BasicSchemeMPL().AggregateVerify({pk1, pk1, pk2}, vector>{message3, message4, message5}, aggSig2)); + REQUIRE(BasicSchemeMPL().AggregateVerify( + {pk1, pk1, pk2}, + vector>{message3, message4, message5}, + aggSig2)); REQUIRE( Util::HexStr(aggSig2.Serialize()) == - "a0b1378d518bea4d1100adbc7bdbc4ff64f2c219ed6395cd36fe5d2aa44a4b8e710b607afd9" - "65e505a5ac3283291b75413d09478ab4b5cfbafbeea366de2d0c0bcf61deddaa521f6020460f" + "a0b1378d518bea4d1100adbc7bdbc4ff64f2c219ed6395cd36fe5d2aa44a4b8e71" + "0b607afd9" + "65e505a5ac3283291b75413d09478ab4b5cfbafbeea366de2d0c0bcf61deddaa52" + "1f6020460f" "d547ab37659ae207968b545727beba0a3c5572b9c"); } - SECTION("Chia test vector 2 (Augmented, aggregate of aggregates)") { + SECTION("Chia test vector 2 (Augmented, aggregate of aggregates)") + { vector message1 = {1, 2, 3, 40}; vector message2 = {5, 6, 70, 201}; vector message3 = {9, 10, 11, 12, 13}; @@ -430,20 +503,26 @@ TEST_CASE("Chia test vectors") { G2Element sig5 = AugSchemeMPL().Sign(sk1, message1); G2Element sig6 = AugSchemeMPL().Sign(sk1, message4); - G2Element aggSigL = AugSchemeMPL().Aggregate({sig1, sig2}); G2Element aggSigR = AugSchemeMPL().Aggregate({sig3, sig4, sig5}); G2Element aggSig = AugSchemeMPL().Aggregate({aggSigL, aggSigR, sig6}); - REQUIRE(AugSchemeMPL().AggregateVerify({pk1, pk2, pk2, pk1, pk1, pk1}, vector>{message1, message2, message1, message3, message1, message4}, aggSig)); + REQUIRE(AugSchemeMPL().AggregateVerify( + {pk1, pk2, pk2, pk1, pk1, pk1}, + vector>{ + message1, message2, message1, message3, message1, message4}, + aggSig)); REQUIRE( Util::HexStr(aggSig.Serialize()) == - "a1d5360dcb418d33b29b90b912b4accde535cf0e52caf467a005dc632d9f7af44b6c4e9acd4" - "6eac218b28cdb07a3e3bc087df1cd1e3213aa4e11322a3ff3847bbba0b2fd19ddc25ca964871" + "a1d5360dcb418d33b29b90b912b4accde535cf0e52caf467a005dc632d9f7af44b" + "6c4e9acd4" + "6eac218b28cdb07a3e3bc087df1cd1e3213aa4e11322a3ff3847bbba0b2fd19ddc" + "25ca964871" "997b9bceeab37a4c2565876da19382ea32a962200"); } - SECTION("Chia test vector 3 (PoP)") { + SECTION("Chia test vector 3 (PoP)") + { vector message1 = {1, 2, 3, 40, 50}; vector seed1(32, 0x04); // All 4s @@ -453,13 +532,16 @@ TEST_CASE("Chia test vectors") { G2Element pop = PopSchemeMPL().PopProve(sk1); REQUIRE(PopSchemeMPL().PopVerify(sk1.GetG1Element(), pop)); - REQUIRE(Util::HexStr(pop.Serialize()) == "84f709159435f0dc73b3e8bf6c78d85282d19231555a8ee3b6e2573aaf66872d9203fefa1ef" - "700e34e7c3f3fb28210100558c6871c53f1ef6055b9f06b0d1abe22ad584ad3b957f3018a8f5" - "8227c6c716b1e15791459850f2289168fa0cf9115"); + REQUIRE( + Util::HexStr(pop.Serialize()) == + "84f709159435f0dc73b3e8bf6c78d85282d19231555a8ee3b6e2573aaf66872d92" + "03fefa1ef" + "700e34e7c3f3fb28210100558c6871c53f1ef6055b9f06b0d1abe22ad584ad3b95" + "7f3018a8f5" + "8227c6c716b1e15791459850f2289168fa0cf9115"); } } - TEST_CASE("Key generation") { SECTION("Should generate a keypair from a seed") @@ -470,12 +552,10 @@ TEST_CASE("Key generation") REQUIRE_THROWS(BasicSchemeMPL().KeyGen(seed1)); PrivateKey sk = BasicSchemeMPL().KeyGen(seed2); G1Element pk = sk.GetG1Element(); - REQUIRE(core_get()->code == RLC_OK); REQUIRE(pk.GetFingerprint() == 0x8ee7ba56); } } - TEST_CASE("Error handling") { SECTION("Should throw on a bad private key") @@ -485,7 +565,8 @@ TEST_CASE("Error handling") uint8_t* skData = Util::SecAlloc(G2Element::SIZE); sk1.Serialize(skData); skData[0] = 255; - REQUIRE_THROWS(PrivateKey::FromBytes(Bytes(skData, PrivateKey::PRIVATE_KEY_SIZE))); + REQUIRE_THROWS( + PrivateKey::FromBytes(Bytes(skData, PrivateKey::PRIVATE_KEY_SIZE))); Util::SecFree(skData); } @@ -494,7 +575,8 @@ TEST_CASE("Error handling") vector buf(G1Element::SIZE, 0); for (int i = 0; i < 0xFF; i++) { buf[0] = (uint8_t)i; - if (i == 0xc0) { // Infinity prefix shouldn't throw here as we have only zero values + if (i == 0xc0) { // Infinity prefix shouldn't throw here as we have + // only zero values REQUIRE_NOTHROW(G1Element::FromByteVector(buf)); } else { REQUIRE_THROWS(G1Element::FromByteVector(buf)); @@ -507,47 +589,20 @@ TEST_CASE("Error handling") vector buf(G2Element::SIZE, 0); for (int i = 0; i < 0xFF; i++) { buf[0] = (uint8_t)i; - if (i == 0xc0) { // Infinity prefix shouldn't throw here as we have only zero values + if (i == 0xc0) { // Infinity prefix shouldn't throw here as we have + // only zero values REQUIRE_NOTHROW(G2Element::FromByteVector(buf)); } else { REQUIRE_THROWS(G2Element::FromByteVector(buf)); } } - // Trigger "G2 element must always have 48th byte start with 0b000" error case + // Trigger "G2 element must always have 48th byte start with 0b000" + // error case buf[48] = 0xFF; REQUIRE_THROWS(G2Element::FromByteVector(buf)); } - - SECTION("Error handling should be thread safe") - { - core_get()->code = 10; - REQUIRE(core_get()->code == 10); - - ctx_t* ctx1 = core_get(); - - // spawn a thread and make sure it uses a different/same context depending on relic's multithreading setup - std::thread([&]() { -#if MULTI != RELIC_NONE - REQUIRE(ctx1 != core_get()); - REQUIRE(core_get()->code == RLC_OK); -#else - REQUIRE(ctx1 == core_get()); - REQUIRE(core_get()->code != RLC_OK); -#endif - core_get()->code = 1; - }).join(); - -#if MULTI != RELIC_NONE - REQUIRE(core_get()->code == 10); -#else - REQUIRE(core_get()->code == 1); -#endif - // reset so that future test cases don't fail - core_get()->code = RLC_OK; - } } - TEST_CASE("Util tests") { SECTION("Should convert an int to four bytes") @@ -565,7 +620,6 @@ TEST_CASE("Util tests") } } - TEST_CASE("Signature tests") { SECTION("Should use copy constructor") @@ -579,7 +633,8 @@ TEST_CASE("Signature tests") uint8_t skBytes[PrivateKey::PRIVATE_KEY_SIZE]; sk2.Serialize(skBytes); - PrivateKey sk4 = PrivateKey::FromBytes(Bytes(skBytes, PrivateKey::PRIVATE_KEY_SIZE)); + PrivateKey sk4 = + PrivateKey::FromBytes(Bytes(skBytes, PrivateKey::PRIVATE_KEY_SIZE)); G1Element pk2 = G1Element(pk1); G2Element sig1 = BasicSchemeMPL().Sign(sk4, message1); @@ -588,7 +643,8 @@ TEST_CASE("Signature tests") REQUIRE(BasicSchemeMPL().Verify(pk2, message1, sig2)); } - SECTION("Should sign with the zero key") { + SECTION("Should sign with the zero key") + { vector sk0(32, 0); PrivateKey sk = PrivateKey::FromByteVector(sk0); REQUIRE(sk.GetG1Element() == G1Element()); // Infinity @@ -639,7 +695,8 @@ TEST_CASE("Signature tests") uint8_t* skData = Util::SecAlloc(G2Element::SIZE); sk1.Serialize(skData); - PrivateKey sk2 = PrivateKey::FromBytes(Bytes(skData, PrivateKey::PRIVATE_KEY_SIZE)); + PrivateKey sk2 = + PrivateKey::FromBytes(Bytes(skData, PrivateKey::PRIVATE_KEY_SIZE)); REQUIRE(sk1 == sk2); auto pkData = pk1.Serialize(); @@ -661,7 +718,6 @@ TEST_CASE("Signature tests") SECTION("Should not verify aggregate with same message under BasicScheme") { vector message = {100, 2, 254, 88, 90, 45, 23}; - uint8_t hash[BLS::MESSAGE_HASH_LEN]; vector seed(32, 0x50); vector seed2(32, 0x70); @@ -676,13 +732,17 @@ TEST_CASE("Signature tests") G2Element sig2 = BasicSchemeMPL().Sign(sk2, message); G2Element aggSig = BasicSchemeMPL().Aggregate({sig1, sig2}); - REQUIRE(BasicSchemeMPL().AggregateVerify({pk1, pk2}, vector>{message, message}, aggSig) == false); + REQUIRE( + BasicSchemeMPL().AggregateVerify( + {pk1, pk2}, + vector>{message, message}, + aggSig) == false); } - SECTION("Should verify aggregate with same message under AugScheme/PopScheme") + SECTION( + "Should verify aggregate with same message under AugScheme/PopScheme") { vector message = {100, 2, 254, 88, 90, 45, 23}; - uint8_t hash[BLS::MESSAGE_HASH_LEN]; vector seed(32, 0x50); vector seed2(32, 0x70); @@ -696,19 +756,21 @@ TEST_CASE("Signature tests") G2Element sig1Aug = AugSchemeMPL().Sign(sk1, message); G2Element sig2Aug = AugSchemeMPL().Sign(sk2, message); G2Element aggSigAug = AugSchemeMPL().Aggregate({sig1Aug, sig2Aug}); - REQUIRE(AugSchemeMPL().AggregateVerify({pk1, pk2}, vector>{message, message}, aggSigAug)); + REQUIRE(AugSchemeMPL().AggregateVerify( + {pk1, pk2}, vector>{message, message}, aggSigAug)); G2Element sig1Pop = PopSchemeMPL().Sign(sk1, message); G2Element sig2Pop = PopSchemeMPL().Sign(sk2, message); G2Element aggSigPop = PopSchemeMPL().Aggregate({sig1Pop, sig2Pop}); - REQUIRE(PopSchemeMPL().AggregateVerify({pk1, pk2}, vector>{message, message}, aggSigPop)); + REQUIRE(PopSchemeMPL().AggregateVerify( + {pk1, pk2}, vector>{message, message}, aggSigPop)); } SECTION("Should Aug aggregate many G2Elements, diff message") { vector pks; vector sigs; - vector > ms; + vector> ms; for (uint8_t i = 0; i < 80; i++) { vector message = {0, 100, 2, 45, 64, 12, 12, 63, i}; @@ -727,8 +789,8 @@ TEST_CASE("Signature tests") SECTION("Aggregate Verification of zero items with infinity should pass") { vector pks_as_g1; - vector > pks_as_bytes; - vector > msgs; + vector> pks_as_bytes; + vector> msgs; vector sigs; sigs.push_back(G2Element()); @@ -738,17 +800,23 @@ TEST_CASE("Signature tests") REQUIRE(aggSig == G2Element()); REQUIRE(AugSchemeMPL().AggregateVerify(pks_as_g1, msgs, aggSig)); - REQUIRE(AugSchemeMPL().AggregateVerify(pks_as_bytes, msgs, aggSig.Serialize())); + REQUIRE(AugSchemeMPL().AggregateVerify( + pks_as_bytes, msgs, aggSig.Serialize())); REQUIRE(BasicSchemeMPL().AggregateVerify(pks_as_g1, msgs, aggSig)); - REQUIRE(BasicSchemeMPL().AggregateVerify(pks_as_bytes, msgs, aggSig.Serialize())); + REQUIRE(BasicSchemeMPL().AggregateVerify( + pks_as_bytes, msgs, aggSig.Serialize())); - // FastAggregateVerify takes one message, and requires at least one key + // FastAggregateVerify takes one message, and requires at least one key vector msg; REQUIRE(pks_as_g1.size() == 0); - REQUIRE(PopSchemeMPL().FastAggregateVerify(pks_as_g1, msg, aggSig) == false); + REQUIRE( + PopSchemeMPL().FastAggregateVerify(pks_as_g1, msg, aggSig) == + false); REQUIRE(pks_as_bytes.size() == 0); - REQUIRE(PopSchemeMPL().FastAggregateVerify(pks_as_bytes, msg, aggSig.Serialize()) == false); + REQUIRE( + PopSchemeMPL().FastAggregateVerify( + pks_as_bytes, msg, aggSig.Serialize()) == false); } SECTION("Verification with identity pubkey or signature should fail") @@ -772,9 +840,25 @@ TEST_CASE("Signature tests") pks = {pk}; REQUIRE(BasicSchemeMPL().AggregateVerify(pks, msgs_b, G2Element()) == false); } + SECTION("Aggregate same sig element") + { + vector message = {100, 2, 254, 88, 90, 45, 23}; + + vector seed(32, 0x50); + + PrivateKey sk1 = BasicSchemeMPL().KeyGen(seed); + + G1Element pk1 = sk1.GetG1Element(); + + G2Element sig1Aug = AugSchemeMPL().Sign(sk1, message); + G2Element aggSigAug = AugSchemeMPL().Aggregate({sig1Aug, sig1Aug}); + REQUIRE(AugSchemeMPL().AggregateVerify( + {pk1, pk1}, vector>{message, message}, aggSigAug)); + } } -TEST_CASE("Agg sks") { +TEST_CASE("Agg sks") +{ SECTION("Should create aggregates with agg sk (basic scheme)") { const vector message = {100, 2, 254, 88, 90, 45, 23}; @@ -799,7 +883,6 @@ TEST_CASE("Agg sks") { const G2Element aggSig2 = BasicSchemeMPL().Sign(aggSk, message); - const G2Element aggSig = BasicSchemeMPL().Aggregate({sig1, sig2}); REQUIRE(aggSig == aggSig2); @@ -808,8 +891,16 @@ TEST_CASE("Agg sks") { REQUIRE(BasicSchemeMPL().Verify(aggPubKey, message, aggSig2)); // Verify aggregate with both keys (Fails since not distinct) - REQUIRE(BasicSchemeMPL().AggregateVerify({pk1, pk2}, vector>{message, message}, aggSig) == false); - REQUIRE(BasicSchemeMPL().AggregateVerify({pk1, pk2}, vector>{message, message}, aggSig2) == false); + REQUIRE( + BasicSchemeMPL().AggregateVerify( + {pk1, pk2}, + vector>{message, message}, + aggSig) == false); + REQUIRE( + BasicSchemeMPL().AggregateVerify( + {pk1, pk2}, + vector>{message, message}, + aggSig2) == false); // Try the same with distinct message, and same sk vector message2 = {200, 29, 54, 8, 9, 29, 155, 55}; @@ -831,12 +922,17 @@ TEST_CASE("Agg sks") { REQUIRE(pkFinal != aggPubKey); // Cannot verify with aggPubKey (since we have multiple messages) - REQUIRE(BasicSchemeMPL().AggregateVerify({aggPubKey, pk2}, vector>{message, message2}, aggSigFinal)); + REQUIRE(BasicSchemeMPL().AggregateVerify( + {aggPubKey, pk2}, + vector>{message, message2}, + aggSigFinal)); } } -TEST_CASE("Advanced") { - SECTION("Should aggregate with multiple levels, degenerate") { +TEST_CASE("Advanced") +{ + SECTION("Should aggregate with multiple levels, degenerate") + { vector message1 = {100, 2, 254, 88, 90, 45, 23}; PrivateKey sk1 = AugSchemeMPL().KeyGen(getRandomSeed()); G1Element pk1 = sk1.GetG1Element(); @@ -855,7 +951,8 @@ TEST_CASE("Advanced") { REQUIRE(AugSchemeMPL().AggregateVerify(pks, ms, aggSig)); } - SECTION("Should aggregate with multiple levels, different messages") { + SECTION("Should aggregate with multiple levels, different messages") + { vector message1 = {100, 2, 254, 88, 90, 45, 23}; vector message2 = {192, 29, 2, 0, 0, 45, 23}; vector message3 = {52, 29, 2, 0, 0, 45, 102}; @@ -893,25 +990,24 @@ TEST_CASE("Advanced") { SECTION("README") { // Example seed, used to generate private key. Always use - // a secure RNG with sufficient entropy to generate a seed (at least 32 bytes). - vector seed = {0, 50, 6, 244, 24, 199, 1, 25, 52, 88, 192, - 19, 18, 12, 89, 6, 220, 18, 102, 58, 209, 82, - 12, 62, 89, 110, 182, 9, 44, 20, 254, 22}; + // a secure RNG with sufficient entropy to generate a seed (at least 32 + // bytes). + vector seed = {0, 50, 6, 244, 24, 199, 1, 25, + 52, 88, 192, 19, 18, 12, 89, 6, + 220, 18, 102, 58, 209, 82, 12, 62, + 89, 110, 182, 9, 44, 20, 254, 22}; PrivateKey sk = AugSchemeMPL().KeyGen(seed); G1Element pk = sk.GetG1Element(); - vector message = {1, 2, 3, 4, 5}; // Message is passed in as a byte vector + vector message = { + 1, 2, 3, 4, 5}; // Message is passed in as a byte vector G2Element signature = AugSchemeMPL().Sign(sk, message); vector skBytes = sk.Serialize(); vector pkBytes = pk.Serialize(); vector signatureBytes = signature.Serialize(); - cout << Util::HexStr(skBytes) << endl; // 32 bytes - cout << Util::HexStr(pkBytes) << endl; // 48 bytes - cout << Util::HexStr(signatureBytes) << endl; // 96 bytes - // Takes array of 32 bytes PrivateKey skc = PrivateKey::FromByteVector(skBytes); @@ -941,7 +1037,8 @@ TEST_CASE("Advanced") { // Signatures can be noninteractively combined by anyone G2Element aggSig = AugSchemeMPL().Aggregate({sig1, sig2}); - REQUIRE(AugSchemeMPL().AggregateVerify({pk1, pk2}, vector>{message, message2}, aggSig)); + REQUIRE(AugSchemeMPL().AggregateVerify( + {pk1, pk2}, vector>{message, message2}, aggSig)); seed[0] = 3; PrivateKey sk3 = AugSchemeMPL().KeyGen(seed); @@ -949,14 +1046,17 @@ TEST_CASE("Advanced") { vector message3 = {100, 2, 254, 88, 90, 45, 23}; G2Element sig3 = AugSchemeMPL().Sign(sk3, message3); - // Arbitrary trees of aggregates G2Element aggSigFinal = AugSchemeMPL().Aggregate({aggSig, sig3}); - REQUIRE(AugSchemeMPL().AggregateVerify({pk1, pk2, pk3}, vector>{message, message2, message3}, aggSigFinal)); + REQUIRE(AugSchemeMPL().AggregateVerify( + {pk1, pk2, pk3}, + vector>{message, message2, message3}, + aggSigFinal)); - // If the same message is signed, you can use Proof of Posession (PopScheme) for efficiency - // A proof of possession MUST be passed around with the PK to ensure security. + // If the same message is signed, you can use Proof of Posession + // (PopScheme) for efficiency A proof of possession MUST be passed + // around with the PK to ensure security. G2Element popSig1 = PopSchemeMPL().Sign(sk1, message); G2Element popSig2 = PopSchemeMPL().Sign(sk2, message); @@ -968,9 +1068,11 @@ TEST_CASE("Advanced") { REQUIRE(PopSchemeMPL().PopVerify(pk1, pop1)); REQUIRE(PopSchemeMPL().PopVerify(pk2, pop2)); REQUIRE(PopSchemeMPL().PopVerify(pk3, pop3)); - G2Element popSigAgg = PopSchemeMPL().Aggregate({popSig1, popSig2, popSig3}); + G2Element popSigAgg = + PopSchemeMPL().Aggregate({popSig1, popSig2, popSig3}); - REQUIRE(PopSchemeMPL().FastAggregateVerify({pk1, pk2, pk3}, message, popSigAgg)); + REQUIRE(PopSchemeMPL().FastAggregateVerify( + {pk1, pk2, pk3}, message, popSigAgg)); // Aggregate public key, indistinguishable from a single public key G1Element popAggPk = pk1 + pk2 + pk3; @@ -980,17 +1082,20 @@ TEST_CASE("Advanced") { PrivateKey aggSk = PrivateKey::Aggregate({sk1, sk2, sk3}); REQUIRE(PopSchemeMPL().Sign(aggSk, message) == popSigAgg); - PrivateKey masterSk = AugSchemeMPL().KeyGen(seed); PrivateKey child = AugSchemeMPL().DeriveChildSk(masterSk, 152); PrivateKey grandchild = AugSchemeMPL().DeriveChildSk(child, 952); G1Element masterPk = masterSk.GetG1Element(); - PrivateKey childU = AugSchemeMPL().DeriveChildSkUnhardened(masterSk, 22); - PrivateKey grandchildU = AugSchemeMPL().DeriveChildSkUnhardened(childU, 0); + PrivateKey childU = + AugSchemeMPL().DeriveChildSkUnhardened(masterSk, 22); + PrivateKey grandchildU = + AugSchemeMPL().DeriveChildSkUnhardened(childU, 0); - G1Element childUPk = AugSchemeMPL().DeriveChildPkUnhardened(masterPk, 22); - G1Element grandchildUPk = AugSchemeMPL().DeriveChildPkUnhardened(childUPk, 0); + G1Element childUPk = + AugSchemeMPL().DeriveChildPkUnhardened(masterPk, 22); + G1Element grandchildUPk = + AugSchemeMPL().DeriveChildPkUnhardened(childUPk, 0); REQUIRE(grandchildUPk == grandchildU.GetG1Element()); } @@ -1012,12 +1117,16 @@ TEST_CASE("Advanced") { G2Element g2Bytes = G2Element::FromBytes(Bytes(vecG2Element)); REQUIRE(g2Vector == g2Bytes); - G1Element g1MessageVector = G1Element::FromMessage(vecHash, vecHash.data(), vecHash.size()); - G1Element g1MessageBytes = G1Element::FromMessage(Bytes(vecHash), vecHash.data(), vecHash.size()); + G1Element g1MessageVector = + G1Element::FromMessage(vecHash, vecHash.data(), vecHash.size()); + G1Element g1MessageBytes = G1Element::FromMessage( + Bytes(vecHash), vecHash.data(), vecHash.size()); REQUIRE(g1MessageVector == g1MessageBytes); - G2Element g2MessageVector = G2Element::FromMessage(vecHash, vecHash.data(), vecHash.size()); - G2Element g2MessageBytes = G2Element::FromMessage(Bytes(vecHash), vecHash.data(), vecHash.size()); + G2Element g2MessageVector = + G2Element::FromMessage(vecHash, vecHash.data(), vecHash.size()); + G2Element g2MessageBytes = G2Element::FromMessage( + Bytes(vecHash), vecHash.data(), vecHash.size()); REQUIRE(g2MessageVector == g2MessageBytes); G1Element g1_1 = pk1.GetG1Element(); @@ -1025,29 +1134,41 @@ TEST_CASE("Advanced") { G1Element g1_3 = pk3.GetG1Element(); G2Element g2BasicSignVector1 = BasicSchemeMPL().Sign(pk1, vecHash); - G2Element g2BasicSignBytes1 = BasicSchemeMPL().Sign(pk1, Bytes(vecHash)); + G2Element g2BasicSignBytes1 = + BasicSchemeMPL().Sign(pk1, Bytes(vecHash)); REQUIRE(g2BasicSignVector1 == g2BasicSignBytes1); G2Element g2BasicSign2 = BasicSchemeMPL().Sign(pk2, Bytes(vecHash)); - G2Element g2BasicSign3 = BasicSchemeMPL().Sign(pk3, Bytes(vecG2Element)); + G2Element g2BasicSign3 = + BasicSchemeMPL().Sign(pk3, Bytes(vecG2Element)); REQUIRE(g2BasicSignVector1 != g2BasicSign2); - REQUIRE(BasicSchemeMPL().Verify(Bytes(g1_1.Serialize()), Bytes(vecHash), Bytes(g2BasicSignVector1.Serialize()))); - REQUIRE(BasicSchemeMPL().Verify(g1_1, Bytes(vecHash), g2BasicSignVector1)); + REQUIRE(BasicSchemeMPL().Verify( + Bytes(g1_1.Serialize()), + Bytes(vecHash), + Bytes(g2BasicSignVector1.Serialize()))); + REQUIRE( + BasicSchemeMPL().Verify(g1_1, Bytes(vecHash), g2BasicSignVector1)); - vector> vecG1Vector = {g1_1.Serialize(), g1_3.Serialize()}; - vector> vecG2Vector = {g2BasicSignVector1.Serialize(), g2BasicSign3.Serialize()}; + vector> vecG1Vector = { + g1_1.Serialize(), g1_3.Serialize()}; + vector> vecG2Vector = { + g2BasicSignVector1.Serialize(), g2BasicSign3.Serialize()}; vector> vecHashes = {vecHash, vecG2Element}; auto aggVector = BasicSchemeMPL().Aggregate(vecG2Vector); - auto aggBytes = BasicSchemeMPL().Aggregate(vector{vecG2Vector.begin(), vecG2Vector.end()}); + auto aggBytes = BasicSchemeMPL().Aggregate( + vector{vecG2Vector.begin(), vecG2Vector.end()}); REQUIRE(aggVector == aggBytes); - REQUIRE(BasicSchemeMPL().AggregateVerify(vector{vecG1Vector.begin(), vecG1Vector.end()}, - vector{vecHashes.begin(), vecHashes.end()}, - Bytes(aggVector))); - REQUIRE(BasicSchemeMPL().AggregateVerify({g1_1, g1_3}, - vector{vecHashes.begin(), vecHashes.end()}, - G2Element::FromBytes(aggVector))); + + REQUIRE(BasicSchemeMPL().AggregateVerify( + vector{vecG1Vector.begin(), vecG1Vector.end()}, + vector{vecHashes.begin(), vecHashes.end()}, + Bytes(aggVector))); + REQUIRE(BasicSchemeMPL().AggregateVerify( + {g1_1, g1_3}, + vector{vecHashes.begin(), vecHashes.end()}, + G2Element::FromBytes(aggVector))); G2Element g2AugSignVector1 = AugSchemeMPL().Sign(pk1, vecHash); G2Element g2AugSignBytes1 = AugSchemeMPL().Sign(pk1, Bytes(vecHash)); @@ -1055,40 +1176,53 @@ TEST_CASE("Advanced") { REQUIRE(g2AugSignVector1 == g2AugSignBytes1); REQUIRE(g2AugSignVector1 != g2AugSign2); - REQUIRE(AugSchemeMPL().Verify(Bytes(g1_1.Serialize()), Bytes(vecHash), Bytes(g2AugSignVector1.Serialize()))); + REQUIRE(AugSchemeMPL().Verify( + Bytes(g1_1.Serialize()), + Bytes(vecHash), + Bytes(g2AugSignVector1.Serialize()))); REQUIRE(AugSchemeMPL().Verify(g1_1, Bytes(vecHash), g2AugSignVector1)); - vector> vecG1AugVector = {g1_1.Serialize(), g1_2.Serialize()}; - vector> vecG2AugVector = {g2AugSignVector1.Serialize(), g2AugSign2.Serialize()}; + vector> vecG1AugVector = { + g1_1.Serialize(), g1_2.Serialize()}; + vector> vecG2AugVector = { + g2AugSignVector1.Serialize(), g2AugSign2.Serialize()}; auto aggAugVector = AugSchemeMPL().Aggregate(vecG2AugVector); - auto aggAugBytes = AugSchemeMPL().Aggregate(vector{vecG2AugVector.begin(), vecG2AugVector.end()}); + auto aggAugBytes = AugSchemeMPL().Aggregate( + vector{vecG2AugVector.begin(), vecG2AugVector.end()}); REQUIRE(aggAugVector == aggAugBytes); - REQUIRE(AugSchemeMPL().AggregateVerify(vector{vecG1AugVector.begin(), vecG1AugVector.end()}, - vector{vecHashes.begin(), vecHashes.end()}, - Bytes(aggAugVector))); - REQUIRE(AugSchemeMPL().AggregateVerify({g1_1, g1_2}, - vector{vecHashes.begin(), vecHashes.end()}, - G2Element::FromBytes(aggAugVector))); + REQUIRE(AugSchemeMPL().AggregateVerify( + vector{vecG1AugVector.begin(), vecG1AugVector.end()}, + vector{vecHashes.begin(), vecHashes.end()}, + Bytes(aggAugVector))); + REQUIRE(AugSchemeMPL().AggregateVerify( + {g1_1, g1_2}, + vector{vecHashes.begin(), vecHashes.end()}, + G2Element::FromBytes(aggAugVector))); G2Element proof = PopSchemeMPL().PopProve(pk1); REQUIRE(PopSchemeMPL().PopVerify(g1_1, proof)); - REQUIRE(PopSchemeMPL().PopVerify(Bytes(g1_1.Serialize()), Bytes(proof.Serialize()))); + REQUIRE(PopSchemeMPL().PopVerify( + Bytes(g1_1.Serialize()), Bytes(proof.Serialize()))); G2Element g2Pop1 = PopSchemeMPL().Sign(pk1, vecHash); G2Element g2Pop2 = PopSchemeMPL().Sign(pk2, vecHash); G2Element g2PopAgg = PopSchemeMPL().Aggregate({g2Pop1, g2Pop2}); vecG1Vector = {g1_1.Serialize(), g1_2.Serialize()}; - REQUIRE(PopSchemeMPL().FastAggregateVerify({g1_1, g1_2}, Bytes(vecHash), g2PopAgg)); - REQUIRE(PopSchemeMPL().FastAggregateVerify(vector{vecG1Vector.begin(), vecG1Vector.end()}, - Bytes(vecHash), Bytes(g2PopAgg.Serialize()))); + REQUIRE(PopSchemeMPL().FastAggregateVerify( + {g1_1, g1_2}, Bytes(vecHash), g2PopAgg)); + REQUIRE(PopSchemeMPL().FastAggregateVerify( + vector{vecG1Vector.begin(), vecG1Vector.end()}, + Bytes(vecHash), + Bytes(g2PopAgg.Serialize()))); } } - -TEST_CASE("Schemes") { - SECTION("Basic Scheme") { +TEST_CASE("Schemes") +{ + SECTION("Basic Scheme") + { vector seed1(32, 0x04); vector seed2(32, 0x05); std::array msg1 = {7, 8, 9}; @@ -1101,7 +1235,6 @@ TEST_CASE("Schemes") { G2Element sig1 = BasicSchemeMPL().Sign(sk1, msg1); vector sig1v = BasicSchemeMPL().Sign(sk1, msg1).Serialize(); - REQUIRE(BasicSchemeMPL().Verify(pk1v, msg1, sig1v)); PrivateKey sk2 = BasicSchemeMPL().KeyGen(seed2); @@ -1121,7 +1254,8 @@ TEST_CASE("Schemes") { REQUIRE(BasicSchemeMPL().Verify(pk2v, msg1, sig1v) == false); G2Element aggsig = BasicSchemeMPL().Aggregate({sig1, sig2}); - auto aggsigv = BasicSchemeMPL().Aggregate(vector>{sig1v, sig2v}); + auto aggsigv = + BasicSchemeMPL().Aggregate(vector>{sig1v, sig2v}); REQUIRE(BasicSchemeMPL().AggregateVerify({pk1, pk2}, msgs, aggsig)); REQUIRE(BasicSchemeMPL().AggregateVerify({pk1v, pk2v}, msgs, aggsigv)); } @@ -1160,7 +1294,8 @@ TEST_CASE("Schemes") { REQUIRE(AugSchemeMPL().Verify(pk2v, msg1, sig1v) == false); G2Element aggsig = AugSchemeMPL().Aggregate({sig1, sig2}); - auto aggsigv = AugSchemeMPL().Aggregate(vector>{sig1v, sig2v}); + auto aggsigv = + AugSchemeMPL().Aggregate(vector>{sig1v, sig2v}); REQUIRE(AugSchemeMPL().AggregateVerify({pk1, pk2}, msgs, aggsig)); REQUIRE(AugSchemeMPL().AggregateVerify({pk1v, pk2v}, msgs, Bytes(aggsigv.data(), aggsigv.size()))); } @@ -1199,7 +1334,8 @@ TEST_CASE("Schemes") { REQUIRE(PopSchemeMPL().Verify(pk2v, msg1, sig1v) == false); G2Element aggsig = PopSchemeMPL().Aggregate({sig1, sig2}); - auto aggsigv = PopSchemeMPL().Aggregate(vector>{sig1v, sig2v}); + auto aggsigv = + PopSchemeMPL().Aggregate(vector>{sig1v, sig2v}); REQUIRE(PopSchemeMPL().AggregateVerify({pk1, pk2}, msgs, aggsig)); REQUIRE(PopSchemeMPL().AggregateVerify({pk1v, pk2v}, msgs, Bytes(aggsigv.data(), aggsigv.size()))); @@ -1214,8 +1350,8 @@ TEST_CASE("Schemes") { G2Element sig2_same = PopSchemeMPL().Sign(sk2, msg1); vector sig2v_same = PopSchemeMPL().Sign(sk2, msg1).Serialize(); G2Element aggsig_same = PopSchemeMPL().Aggregate({sig1, sig2_same}); - auto aggsigv_same = - PopSchemeMPL().Aggregate(vector>{sig1v, sig2v_same}); + auto aggsigv_same = PopSchemeMPL().Aggregate( + vector>{sig1v, sig2v_same}); REQUIRE( PopSchemeMPL().FastAggregateVerify({pk1, pk2}, msg1, aggsig_same)); REQUIRE(PopSchemeMPL().FastAggregateVerify( @@ -1545,7 +1681,8 @@ TEST_CASE("Threshold Signatures") { TEST_CASE("CheckValid") { - SECTION("Valid points should succeed") { + SECTION("Valid points should succeed") + { vector seed(32, 0x05); vector msg1 = {10, 11, 12}; @@ -1567,32 +1704,36 @@ TEST_CASE("CheckValid") G1Element::FromBytes(Bytes(Util::HexToBytes(badPointHex)))); // FromBytesUnchecked does not throw - G1Element pk = + G1Element bad_pk = G1Element::FromBytesUnchecked(Bytes(Util::HexToBytes(badPointHex))); - REQUIRE(pk.IsValid() == false); - REQUIRE_THROWS(pk.CheckValid()); + REQUIRE(bad_pk.IsValid() == false); + REQUIRE_THROWS(bad_pk.CheckValid()); vector seed(32, 0x05); vector msg1 = {10, 11, 12}; PrivateKey sk1 = BasicSchemeMPL().KeyGen(seed); - G1Element pk1 = BasicSchemeMPL().SkToG1(sk1); + G1Element good_pk = BasicSchemeMPL().SkToG1(sk1); + REQUIRE(good_pk.IsValid() == true); G2Element sig1 = AugSchemeMPL().Sign(sk1, msg1); - REQUIRE(AugSchemeMPL().Verify(pk, msg1, sig1) == false); + REQUIRE(AugSchemeMPL().Verify(bad_pk, msg1, sig1) == false); + REQUIRE(AugSchemeMPL().Verify(good_pk, msg1, sig1) == true); } - SECTION("Invalid G2 points should not succeed") { - g2_t point_native; - g2_set_infty(point_native); - fp2_rand(point_native->x); - fp2_rand(point_native->y); - fp2_rand(point_native->z); + SECTION("Invalid G2 points should not succeed") + { + blst_p2 point_native; + memset(&point_native, 0, sizeof(blst_p2)); + + // copy some probably invalid data into the point + memcpy(&(point_native.x), (void*)memcpy, sizeof(point_native.x)); + memcpy(&(point_native.y), (void*)memset, sizeof(point_native.y)); + memcpy(&(point_native.z), (void*)printf, sizeof(point_native.z)); G2Element point = G2Element::FromNative(point_native); REQUIRE(point.IsValid() == false); REQUIRE_THROWS(point.CheckValid()); auto badSer = point.Serialize(); - std::cout <x); - fp2_rand(point_native->y); - fp2_rand(point_native->z); + // Build an invalid G2 element from native coordinates: + // (-x, y) of the generator: not on the curve + blst_p2 point_native = *blst_p2_generator(); + blst_fp2_cneg(&point_native.x, &point_native.x, true); G2Element badSig = G2Element::FromNative(point_native); REQUIRE(badSig.IsValid() == false); @@ -1916,11 +2055,9 @@ TEST_CASE("FastAggregateVerify rejects identity elements") SECTION("Malformed non-identity signature rejected") { - g2_t point_native; - g2_set_infty(point_native); - fp2_rand(point_native->x); - fp2_rand(point_native->y); - fp2_rand(point_native->z); + // (-x, y) of the generator: not on the curve + blst_p2 point_native = *blst_p2_generator(); + blst_fp2_cneg(&point_native.x, &point_native.x, true); G2Element badSig = G2Element::FromNative(point_native); REQUIRE(badSig.IsValid() == false); @@ -2033,6 +2170,62 @@ TEST_CASE("Reference vectors") } } +TEST_CASE("GTElement") +{ + SECTION("GTElement serialization") + { + vector seed(32, 0x05); + blst_p1_affine p1_affine; + + PrivateKey sk1 = BasicSchemeMPL().KeyGen(seed); + G1Element pk1 = BasicSchemeMPL().SkToG1(sk1); + pk1.CheckValid(); + pk1.ToAffine(&p1_affine); + GTElement gt1 = GTElement::FromAffine(p1_affine); + + auto outbuf = gt1.Serialize(); + + auto gt2 = GTElement::FromBytesUnchecked(outbuf); + + REQUIRE(gt1 == gt2); + } + + SECTION("GTElement pairing") + { + vector seed(32, 0x05), seed2(32, 0x06); + vector msg1 = {7, 8, 9}; + vector msg2 = {10, 11, 12}; + + auto sk1 = BasicSchemeMPL().KeyGen(seed); + auto sk2 = BasicSchemeMPL().KeyGen(seed2); + + auto pk1 = sk1.GetG1Element(); + auto pk2 = sk2.GetG1Element(); + + auto sig1 = BasicSchemeMPL().Sign(sk1, msg1); + auto sig2 = BasicSchemeMPL().Sign(sk2, msg2); + + auto aggsig = BasicSchemeMPL().Aggregate({sig1, sig2}); + + REQUIRE(BasicSchemeMPL().AggregateVerify( + {pk1, pk2}, vector>{msg1, msg2}, aggsig)); + + auto pair1 = pk1.Pair(G2Element::FromMessage( + msg1, + (const uint8_t*)BasicSchemeMPL::CIPHERSUITE_ID.c_str(), + BasicSchemeMPL::CIPHERSUITE_ID.length())); + auto pair2 = pk2.Pair(G2Element::FromMessage( + msg2, + (const uint8_t*)BasicSchemeMPL::CIPHERSUITE_ID.c_str(), + BasicSchemeMPL::CIPHERSUITE_ID.length())); + auto pair = pair1 * pair2; + + auto agg_sig_pair = G1Element::Generator().Pair(aggsig); + + REQUIRE(pair == agg_sig_pair); + } +} + int main(int argc, char* argv[]) { int result = Catch::Session().run(argc, argv); From e79d284117e8bb8dca76efe79240d9a3120a89e8 Mon Sep 17 00:00:00 2001 From: wjblanke Date: Thu, 24 Aug 2023 11:36:51 -0700 Subject: [PATCH 09/36] no avx not for EMSCRIPTEN (#428) --- src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ce73523b1..0175cb5cd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -39,7 +39,7 @@ else() add_compile_options(-fPIC) add_compile_options(-Wall) add_compile_options(-Wextra) - if(CMAKE_SYSTEM_PROCESSOR MATCHES "(x86)|(X86)|(amd64)|(AMD64)") + if((CMAKE_SYSTEM_PROCESSOR MATCHES "(x86)|(X86)|(amd64)|(AMD64)") AND (NOT EMSCRIPTEN)) add_compile_options(-mno-avx) endif() endif() From 69cce7ec3133ece365da82046faf017a3aea1bb9 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Fri, 11 Sep 2026 14:43:32 +0700 Subject: [PATCH 10/36] fix: adopt dash specific files chaincode, threshold for blst library --- include/dashbls/chaincode.hpp | 14 +-- src/chaincode.cpp | 16 ++-- src/threshold.cpp | 164 +++++++--------------------------- 3 files changed, 38 insertions(+), 156 deletions(-) diff --git a/include/dashbls/chaincode.hpp b/include/dashbls/chaincode.hpp index bd3e57b4d..ecd5db79f 100644 --- a/include/dashbls/chaincode.hpp +++ b/include/dashbls/chaincode.hpp @@ -15,19 +15,10 @@ #ifndef SRC_CHAINCODE_HPP_ #define SRC_CHAINCODE_HPP_ +#include #include #include -#include "relic_conf.h" - -#if defined GMP && ARITH == GMP -#include -#endif - - -#include "relic.h" -#include "relic_test.h" - #include "util.hpp" namespace bls { @@ -51,9 +42,8 @@ class ChainCode { ChainCode() {} private: - bn_t chainCode; + std::array chainCode{}; }; } // end namespace bls #endif // SRC_CHAINCODE_HPP_ - diff --git a/src/chaincode.cpp b/src/chaincode.cpp index efacdc95a..e5a720470 100644 --- a/src/chaincode.cpp +++ b/src/chaincode.cpp @@ -14,6 +14,8 @@ #include "bls.hpp" +#include + namespace bls { ChainCode ChainCode::FromBytes(const Bytes& bytes) { @@ -21,21 +23,15 @@ ChainCode ChainCode::FromBytes(const Bytes& bytes) { throw std::invalid_argument("ChainCode::FromBytes: Invalid size"); } ChainCode c = ChainCode(); - bn_new(c.chainCode); - bn_read_bin(c.chainCode, bytes.begin(), ChainCode::SIZE); + memcpy(c.chainCode.data(), bytes.begin(), ChainCode::SIZE); return c; } -ChainCode::ChainCode(const ChainCode &cc) { - uint8_t bytes[ChainCode::SIZE]; - cc.Serialize(bytes); - bn_new(chainCode); - bn_read_bin(chainCode, bytes, ChainCode::SIZE); -} +ChainCode::ChainCode(const ChainCode &cc) : chainCode(cc.chainCode) {} // Comparator implementation. bool operator==(ChainCode const &a, ChainCode const &b) { - return bn_cmp(a.chainCode, b.chainCode) == RLC_EQ; + return a.chainCode == b.chainCode; } bool operator!=(ChainCode const &a, ChainCode const &b) { @@ -49,7 +45,7 @@ std::ostream &operator<<(std::ostream &os, ChainCode const &s) { } void ChainCode::Serialize(uint8_t *buffer) const { - bn_write_bin(buffer, ChainCode::SIZE, chainCode); + memcpy(buffer, chainCode.data(), SIZE); } std::vector ChainCode::Serialize() const { diff --git a/src/threshold.cpp b/src/threshold.cpp index 73a3023b2..8526832b7 100644 --- a/src/threshold.cpp +++ b/src/threshold.cpp @@ -8,75 +8,8 @@ #include "schemes.hpp" -#include - static std::unique_ptr pThresholdScheme(new bls::LegacySchemeMPL); -/** - * Inverts a prime field element using the Euclidean Extended Algorithm, - * using bns and a custom prime modulus. - * - * @param[out] c - the result. - * @param[in] a - the prime field element to invert. - * @param[in] p - the custom prime modulus. - */ -static void fp_inv_exgcd_bn(bn_t c, const bn_t u_in, const bn_t p) { - bn_t u, v, g1, g2, q, r; - - bn_null(u); - bn_null(v); - bn_null(g1); - bn_null(g2); - bn_null(q); - bn_null(r); - - RLC_TRY { - bn_new(u); - bn_new(v); - bn_new(g1); - bn_new(g2); - bn_new(q); - bn_new(r); - - /* u = a, v = p, g1 = 1, g2 = 0. */ - bn_copy(u, u_in); - bn_copy(v, p); - bn_set_dig(g1, 1); - bn_zero(g2); - - /* While (u != 1. */ - while (bn_cmp_dig(u, 1) != RLC_EQ) { - /* q = [v/u], r = v mod u. */ - bn_div_rem(q, r, v, u); - /* v = u, u = r. */ - bn_copy(v, u); - bn_copy(u, r); - /* r = g2 - q * g1. */ - bn_mul(r, q, g1); - bn_sub(r, g2, r); - /* g2 = g1, g1 = r. */ - bn_copy(g2, g1); - bn_copy(g1, r); - } - - if (bn_sign(g1) == RLC_NEG) { - bn_add(g1, g1, p); - } - bn_copy(c, g1); - } - RLC_CATCH_ANY { - RLC_THROW(ERR_CAUGHT); - } - RLC_FINALLY { - bn_free(u); - bn_free(v); - bn_free(g1); - bn_free(g2); - bn_free(q); - bn_free(r); - }; -} - namespace bls { namespace Poly { @@ -91,43 +24,32 @@ namespace bls { } // end namespace Poly struct PolyOpsBase { - bn_t order; - bn_t iv; - - PolyOpsBase() { - bn_new(order); - gt_get_ord(order); - bn_new(iv); - } - - ~PolyOpsBase() { - bn_free(iv); - bn_free(order); + static bool IsZero(const blst_scalar& a) { + std::array zero{}; + return memcmp(&a, &zero, sizeof(blst_scalar)) == 0; } - void MulFP(bn_t& r, const bn_t& a, const bn_t& b) { - bn_mul(r, a, b); - ModOrder(r); + void MulFP(blst_scalar& r, const blst_scalar& a, const blst_scalar& b) { + blst_sk_mul_n_check(&r, &a, &b); } - void AddFP(bn_t& r, const bn_t& a, const bn_t& b) { - bn_add(r, a, b); - ModOrder(r); + void AddFP(blst_scalar& r, const blst_scalar& a, const blst_scalar& b) { + blst_sk_add_n_check(&r, &a, &b); } - void SubFP(bn_t& r, const bn_t& a, const bn_t& b) { - bn_sub(r, a, b); - ModOrder(r); + void SubFP(blst_scalar& r, const blst_scalar& a, const blst_scalar& b) { + blst_sk_sub_n_check(&r, &a, &b); } - void DivFP(bn_t& r, const bn_t& a, const bn_t& b) { - fp_inv_exgcd_bn(iv, b, order); - bn_mul(r, a, iv); - ModOrder(r); + void DivFP(blst_scalar& r, const blst_scalar& a, const blst_scalar& b) { + blst_scalar iv; + blst_sk_inverse(&iv, &b); + blst_sk_mul_n_check(&r, &a, &iv); } - void ModOrder(bn_t& r) { - bn_mod(r, r, order); + void ScalarFromBytes(blst_scalar& r, const uint8_t* bytes) { + // reduces mod the group order, like bn_read_bin + bn_mod + blst_scalar_from_be_bytes(&r, bytes, Poly::nIdSize); } }; @@ -140,7 +62,7 @@ namespace bls { return PrivateKey::Aggregate({a, b}); } - PrivateKey Mul(const PrivateKey& a, const bn_t& b) { + PrivateKey Mul(const PrivateKey& a, const blst_scalar& b) { return a * b; } }; @@ -151,7 +73,7 @@ namespace bls { return a + b; } - G1Element Mul(const G1Element& a, const bn_t& b) { + G1Element Mul(const G1Element& a, const blst_scalar& b) { return a * b; } }; @@ -162,7 +84,7 @@ namespace bls { return a + b; } - G2Element Mul(const G2Element& a, bn_t& b) { + G2Element Mul(const G2Element& a, const blst_scalar& b) { return a * b; } }; @@ -175,10 +97,8 @@ namespace bls { throw std::length_error("At least 2 coefficients required"); } - bn_t x; - bn_new(x); - bn_read_bin(x, id.begin(), Poly::nIdSize); - ops.ModOrder(x); + blst_scalar x; + ops.ScalarFromBytes(x, id.begin()); BLSType y = vecIn.back(); for (int i = (int) vecIn.size() - 2; i >= 0; i--) { @@ -186,8 +106,6 @@ namespace bls { y = ops.Add(y, vecIn[i]); } - bn_free(x); - return y; } @@ -209,48 +127,28 @@ namespace bls { */ const size_t k = vec.size(); - bn_t *delta = new bn_t[k]; - bn_t *ids2 = new bn_t[k]; + std::vector delta(k); + std::vector ids2(k); for (size_t i = 0; i < k; i++) { - bn_new(delta[i]); - bn_new(ids2[i]); - bn_read_bin(ids2[i], ids[i].begin(), Poly::nIdSize); - ops.ModOrder(ids2[i]); + ops.ScalarFromBytes(ids2[i], ids[i].begin()); } - bn_t a, b, v; - bn_new(a); - bn_new(b); - bn_new(v); - - auto cleanup = [&](){ - bn_free(a); - bn_free(b); - bn_free(v); - for (size_t i = 0; i < k; i++) { - bn_free(delta[i]); - bn_free(ids2[i]); - } - delete[] delta; - delete[] ids2; - }; + blst_scalar a, b, v; - bn_copy(a, ids2[0]); + a = ids2[0]; for (size_t i = 1; i < k; i++) { ops.MulFP(a, a, ids2[i]); } - if (bn_is_zero(a)) { - cleanup(); + if (PolyOpsBase::IsZero(a)) { throw std::invalid_argument("Zero id"); } for (size_t i = 0; i < k; i++) { - bn_copy(b, ids2[i]); + b = ids2[i]; for (size_t j = 0; j < k; j++) { if (j != i) { ops.SubFP(v, ids2[j], ids2[i]); - if (bn_is_zero(v)) { - cleanup(); + if (PolyOpsBase::IsZero(v)) { throw std::invalid_argument("Duplicate id"); } ops.MulFP(b, b, v); @@ -267,8 +165,6 @@ namespace bls { r = ops.Add(r, ops.Mul(vec[i], delta[i])); } - cleanup(); - return r; } @@ -295,7 +191,7 @@ namespace bls { G2Element Threshold::SignatureRecover(const std::vector& sigs, const std::vector& ids) { return Poly::LagrangeInterpolate(sigs, ids); } - + G2Element Threshold::Sign(const PrivateKey& privateKey, const Bytes& vecMessage) { return pThresholdScheme->Sign(privateKey, vecMessage); } From 6cb99c42cc5f91292ae7e660af154987b7d26b87 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Fri, 11 Sep 2026 14:44:04 +0700 Subject: [PATCH 11/36] fix: adopt extendedprivatekey extendendpublickey for blst's library Note, that manual check of "Make sure private key is less than the curve order" has been replaced by flag modOrder=true PrivateKey::FromBytes(Bytes(ILeft, PrivateKey::PRIVATE_KEY_SIZE), /*modOrder=*/true) --- include/dashbls/extendedprivatekey.hpp | 8 -------- include/dashbls/extendedpublickey.hpp | 8 -------- src/extendedprivatekey.cpp | 21 +++++---------------- src/extendedpublickey.cpp | 4 ++-- 4 files changed, 7 insertions(+), 34 deletions(-) diff --git a/include/dashbls/extendedprivatekey.hpp b/include/dashbls/extendedprivatekey.hpp index aefdbf057..575256ab9 100644 --- a/include/dashbls/extendedprivatekey.hpp +++ b/include/dashbls/extendedprivatekey.hpp @@ -15,21 +15,13 @@ #ifndef SRC_EXTENDEDPRIVATEKEY_HPP_ #define SRC_EXTENDEDPRIVATEKEY_HPP_ -#include "relic_conf.h" - #include -#if defined GMP && ARITH == GMP -#include -#endif - #include "chaincode.hpp" #include "elements.hpp" #include "extendedpublickey.hpp" #include "privatekey.hpp" -#include "relic.h" -#include "relic_test.h" namespace bls { /* diff --git a/include/dashbls/extendedpublickey.hpp b/include/dashbls/extendedpublickey.hpp index b44d5c636..c1fbdf7bf 100644 --- a/include/dashbls/extendedpublickey.hpp +++ b/include/dashbls/extendedpublickey.hpp @@ -15,20 +15,12 @@ #ifndef SRC_EXTENDEDPUBLICKEY_HPP_ #define SRC_EXTENDEDPUBLICKEY_HPP_ -#include "relic_conf.h" - #include -#if defined GMP && ARITH == GMP -#include -#endif - #include "chaincode.hpp" #include "elements.hpp" -#include "relic.h" -#include "relic_test.h" namespace bls { diff --git a/src/extendedprivatekey.cpp b/src/extendedprivatekey.cpp index d5fe746ee..237aa0515 100644 --- a/src/extendedprivatekey.cpp +++ b/src/extendedprivatekey.cpp @@ -31,27 +31,16 @@ ExtendedPrivateKey ExtendedPrivateKey::FromSeed(const Bytes& bytes) { // Hash the seed into 64 bytes, half will be sk, half will be cc hashInput[bytes.size()] = 0; - md_hmac(ILeft, hashInput, bytes.size() + 1, prefix, sizeof(prefix)); + Util::md_hmac(ILeft, hashInput, bytes.size() + 1, prefix, sizeof(prefix)); hashInput[bytes.size()] = 1; - md_hmac(IRight, hashInput, bytes.size() + 1, prefix, sizeof(prefix)); + Util::md_hmac(IRight, hashInput, bytes.size() + 1, prefix, sizeof(prefix)); // Make sure private key is less than the curve order - bn_t* skBn = Util::SecAlloc(1); - bn_t order; - bn_new(order); - g1_get_ord(order); - - bn_new(*skBn); - bn_read_bin(*skBn, ILeft, PrivateKey::PRIVATE_KEY_SIZE); - bn_mod_basic(*skBn, *skBn, order); - bn_write_bin(ILeft, PrivateKey::PRIVATE_KEY_SIZE, *skBn); - ExtendedPrivateKey esk(ExtendedPublicKey::REVISION, 0, 0, 0, ChainCode::FromBytes(Bytes(IRight, ChainCode::SIZE)), - PrivateKey::FromBytes(Bytes(ILeft, PrivateKey::PRIVATE_KEY_SIZE))); + PrivateKey::FromBytes(Bytes(ILeft, PrivateKey::PRIVATE_KEY_SIZE), true)); - Util::SecFree(skBn); Util::SecFree(ILeft); Util::SecFree(hashInput); return esk; @@ -101,13 +90,13 @@ ExtendedPrivateKey ExtendedPrivateKey::PrivateChild(uint32_t i, const bool fLega } hmacInput[inputLen - 1] = 0; - md_hmac(ILeft, hmacInput, inputLen, + Util::md_hmac(ILeft, hmacInput, inputLen, hmacKey, ChainCode::SIZE); // Change 1 byte to generate a different sequence for chaincode hmacInput[inputLen - 1] = 1; - md_hmac(IRight, hmacInput, inputLen, + Util::md_hmac(IRight, hmacInput, inputLen, hmacKey, ChainCode::SIZE); PrivateKey newSk = PrivateKey::FromBytes(Bytes(ILeft, PrivateKey::PRIVATE_KEY_SIZE), true); diff --git a/src/extendedpublickey.cpp b/src/extendedpublickey.cpp index a484dea64..ad1015a68 100644 --- a/src/extendedpublickey.cpp +++ b/src/extendedpublickey.cpp @@ -59,13 +59,13 @@ ExtendedPublicKey ExtendedPublicKey::PublicChild(uint32_t i, const bool fLegacy) hmacInput[inputLen - 1] = 0; Util::IntToFourBytes(hmacInput + G1Element::SIZE, i); - md_hmac(ILeft, hmacInput, inputLen, + Util::md_hmac(ILeft, hmacInput, inputLen, hmacKey, ChainCode::SIZE); // Change 1 byte to generate a different sequence for chaincode hmacInput[inputLen - 1] = 1; - md_hmac(IRight, hmacInput, inputLen, + Util::md_hmac(IRight, hmacInput, inputLen, hmacKey, ChainCode::SIZE); PrivateKey leftSk = PrivateKey::FromBytes(Bytes(ILeft, PrivateKey::PRIVATE_KEY_SIZE), true); From 4f8b4e5bbd0ba679c4de84d0b07cc4e3855b7762 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Fri, 11 Sep 2026 15:06:51 +0700 Subject: [PATCH 12/36] fix: adopt legacy signing for blst library [ep2_map_legacy] --- include/dashbls/legacy.hpp | 25 +- src/legacy.cpp | 602 +++++++++++++------------------------ 2 files changed, 227 insertions(+), 400 deletions(-) diff --git a/include/dashbls/legacy.hpp b/include/dashbls/legacy.hpp index b3e1ee134..c348866d6 100644 --- a/include/dashbls/legacy.hpp +++ b/include/dashbls/legacy.hpp @@ -5,27 +5,24 @@ #ifndef SRC_LEGACY_HPP_ #define SRC_LEGACY_HPP_ -#include - -#include "relic_conf.h" - -#if defined GMP && ARITH == GMP -#include -#endif - extern "C" { -#include "relic.h" +#include "bindings/blst.h" } +#include + +namespace bls { /** * Maps a byte array to a point in an elliptic curve over a quadratic extension. * - * Called ep2_map() in old relic version, _legacy prefix is to avoid duplicated symbols + * Called ep2_map() in old relic version. Now reimplemented on blst and must stay + * bit-for-bit compatible: pre-v19 (legacy scheme) consensus depends on it. * - * @param[out] p - the result. - * @param[in] msg - the byte array to map. - * @param[in] len - the array length in bytes. + * @param[out] p - the result. + * @param[in] msg - the byte array to map (must be 32 bytes). + * @param[in] len - the array length in bytes (must be 32). */ -void ep2_map_legacy(ep2_t p, const uint8_t *msg, int len); +void ep2_map_legacy(blst_p2 *p, const uint8_t *msg, int len); +} // namespace bls #endif // #define SRC_LEGACY_HPP_ diff --git a/src/legacy.cpp b/src/legacy.cpp index 44780528d..874c26b0f 100644 --- a/src/legacy.cpp +++ b/src/legacy.cpp @@ -3,142 +3,69 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "legacy.hpp" - -#include - -#define B12_P381_S3 "BE32CE5FBEED9CA374D38C0ED41EEFD5BB675277CDF12D11BC2FB026C41400045C03FFFFFFFDFFFD" -#define B12_P381_S32 "5F19672FDF76CE51BA69C6076A0F77EADDB3A93BE6F89688DE17D813620A00022E01FFFFFFFEFFFE" - -/** - * Returns the (sqrt(-3) - 1) / 2 mod q in the curve, where q is the prime. - * - * @param[out] h - the returned cofactor. - */ -static void ep2_curve_get_s32(bn_t s32In) { - static std::once_flag is_initialized; - static bn_t s32; - std::call_once(is_initialized, [] { - bn_new(s32); - bn_read_str(s32, B12_P381_S32, sizeof(B12_P381_S32), 16); - }); - bn_copy(s32In, s32); +#include +#include +#include + +namespace { + +// sqrt(-3) mod p, big-endian, zero-padded to 48 bytes. Value taken from the +// relic implementation's B12_P381_S3 constant; validated by +// s3^2 == -3 (mod p). +const uint8_t S3_BE[48] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x32, 0xce, 0x5f, + 0xbe, 0xed, 0x9c, 0xa3, 0x74, 0xd3, 0x8c, 0x0e, 0xd4, 0x1e, 0xef, 0xd5, + 0xbb, 0x67, 0x52, 0x77, 0xcd, 0xf1, 0x2d, 0x11, 0xbc, 0x2f, 0xb0, 0x26, + 0xc4, 0x14, 0x00, 0x04, 0x5c, 0x03, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xfd}; + +// (sqrt(-3) - 1) / 2 mod p (relic's B12_P381_S32). +const uint8_t S32_BE[48] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x19, 0x67, 0x2f, + 0xdf, 0x76, 0xce, 0x51, 0xba, 0x69, 0xc6, 0x07, 0x6a, 0x0f, 0x77, 0xea, + 0xdd, 0xb3, 0xa9, 0x3b, 0xe6, 0xf8, 0x96, 0x88, 0xde, 0x17, 0xd8, 0x13, + 0x62, 0x0a, 0x00, 0x02, 0x2e, 0x01, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfe}; + +// Effective cofactor h_eff for BLS12-381 G2 (RFC 9380 section 8.8.2), +// big-endian, 80 bytes / 636 bits. Scalar multiplication by h_eff is +// equivalent to the psi-endomorphism-based cofactor clearing the relic +// implementation performed (verified empirically point-by-point). +const uint8_t H_EFF_BE[80] = { + 0x0b, 0xc6, 0x9f, 0x08, 0xf2, 0xee, 0x75, 0xb3, 0x58, 0x4c, 0x6a, 0x0e, + 0xa9, 0x1b, 0x35, 0x28, 0x88, 0xe2, 0xa8, 0xe9, 0x14, 0x5a, 0xd7, 0x68, + 0x99, 0x86, 0xff, 0x03, 0x15, 0x08, 0xff, 0xe1, 0x32, 0x9c, 0x2f, 0x17, + 0x87, 0x31, 0xdb, 0x95, 0x6d, 0x82, 0xbf, 0x01, 0x5d, 0x12, 0x12, 0xb0, + 0x2e, 0xc0, 0xec, 0x69, 0xd7, 0x47, 0x7c, 0x1a, 0xe9, 0x54, 0xcb, 0xc0, + 0x66, 0x89, 0xf6, 0xa3, 0x59, 0x89, 0x4c, 0x0a, 0xde, 0xbb, 0xf6, 0xb4, + 0xe8, 0x02, 0x00, 0x05, 0xaa, 0xa9, 0x55, 0x51}; + +void FpOne(blst_fp& out) +{ + uint8_t be[48] = {0}; + be[47] = 1; + blst_fp_from_bendian(&out, be); } -/** - * Returns the sqrt(-3) mod q in the curve, where q is the prime. - * - * @param[out] h - the returned cofactor. - */ -static void ep2_curve_get_s3(bn_t s3In) { - static std::once_flag is_initialized; - static bn_t s3; - std::call_once(is_initialized, [] { - bn_new(s3); - bn_read_str(s3, B12_P381_S3, sizeof(B12_P381_S3), 16); - }); - bn_copy(s3In, s3); +void FpFour(blst_fp& out) +{ + uint8_t be[48] = {0}; + be[47] = 4; + blst_fp_from_bendian(&out, be); } -/** - * Returns the 'b' coefficient of the currently configured elliptic curve. - * - * @param[out] b - the 'b' coefficient of the elliptic curve. - */ -static void ep2_curve_get_b(fp2_t b) { - ctx_t *ctx = core_get(); - fp_copy(b[0], ctx->ep2_b[0]); - fp_copy(b[1], ctx->ep2_b[1]); -} +inline void FpZero(blst_fp& out) { memset(&out, 0x00, sizeof(blst_fp)); } -/** - * Returns the variable used to parameterize the given prime modulus. - * - * @param[out] x - the integer parameter. - */ -static void fp_param_get_var(bn_t x) { - bn_t a; - - bn_null(a); - - RLC_TRY { - bn_new(a); - /* x = -(2^63 + 2^62 + 2^60 + 2^57 + 2^48 + 2^16). */ - bn_set_2b(x, 63); - bn_set_2b(a, 62); - bn_add(x, x, a); - bn_set_2b(a, 60); - bn_add(x, x, a); - bn_set_2b(a, 57); - bn_add(x, x, a); - bn_set_2b(a, 48); - bn_add(x, x, a); - bn_set_2b(a, 16); - bn_add(x, x, a); - bn_neg(x, x); - } - RLC_CATCH_ANY { - RLC_THROW(ERR_CAUGHT); - } - RLC_FINALLY { - bn_free(a); - } +bool FpIsZero(const blst_fp& a) +{ + uint8_t be[48]; + blst_bendian_from_fp(be, &a); + for (int i = 0; i < 48; i++) + if (be[i] != 0) return false; + return true; } -/** - * Multiplies a point by the cofactor in a Barreto-Lynn-Soctt. - * - * @param[out] r - the result. - * @param[in] p - the point to multiply. - */ -static void ep2_mul_cof_b12(ep2_t r, ep2_t p) { - bn_t x; - ep2_t t0, t1, t2, t3; - - ep2_null(t0); - ep2_null(t1); - ep2_null(t2); - ep2_null(t3); - bn_null(x); - - RLC_TRY { - ep2_new(t0); - ep2_new(t1); - ep2_new(t2); - ep2_new(t3); - bn_new(x); - - fp_param_get_var(x); - - /* Compute t0 = xP. */ - ep2_mul_basic(t0, p, x); - /* Compute t1 = [x^2]P. */ - ep2_mul_basic(t1, t0, x); - - /* t2 = (x^2 - x - 1)P = x^2P - x*P - P. */ - ep2_sub(t2, t1, t0); - ep2_sub(t2, t2, p); - /* t3 = \psi(x - 1)P. */ - ep2_sub(t3, t0, p); - ep2_norm(t3, t3); - ep2_frb(t3, t3, 1); - ep2_add(t2, t2, t3); - /* t3 = \psi^2(2P). */ - ep2_dbl(t3, p); - ep2_norm(t3, t3); - ep2_frb(t3, t3, 2); - ep2_add(t2, t2, t3); - ep2_norm(r, t2); - } - RLC_CATCH_ANY { - RLC_THROW(ERR_CAUGHT); - } - RLC_FINALLY { - ep2_free(t0); - ep2_free(t1); - ep2_free(t2); - ep2_free(t3); - bn_free(x); - } +bool Fp2IsZero(const blst_fp2& a) +{ + return FpIsZero(a.fp[0]) && FpIsZero(a.fp[1]); } /** @@ -146,288 +73,191 @@ static void ep2_mul_cof_b12(ep2_t r, ep2_t p) { * The algorithm is Shallue–van de Woestijne encoding from * Section 3 of "Indifferentiable Hashing to Barreto–Naehrig Curves" * from Fouque-Tibouchi: + * @param[out] p - the encoded point. + * @param[in] t - the field element to encode. + * Returns false when the result is the point at infinity (t == 0). */ -static void ep2_sw_encode(ep2_t p, fp2_t t) { - if (fp2_is_zero(t)) { +bool ep2_sw_encode(blst_p2_affine& p, const blst_fp2& t) +{ + if (Fp2IsZero(t)) { // Maps t=0 to the point at infinity. - ep2_set_infty(p); - return; + return false; } - fp2_t nt; // Negative t - fp2_t w; - fp2_t b; - bn_t s_n3; - bn_t s_n3m1o2; - fp2_t x1; - fp2_t x2; - fp2_t x3; - fp2_t rhs; - - fp2_new(nt); - fp2_new(w); - fp2_new(b); - bn_new(s_n3); - bn_new(s_n3m1o2); - fp2_new(x1); - fp2_new(x2); - fp2_new(x3); - fp2_new(rhs); - - // nt = -t - fp2_neg(nt, t); - - uint8_t buf0[RLC_FP_BYTES]; // t[1] - uint8_t buf1[RLC_FP_BYTES]; // -t[1] - fp_write_bin(buf0, RLC_FP_BYTES, t[1]); - fp_write_bin(buf1, RLC_FP_BYTES, nt[1]); - - // Compare bytes since fp_cmp compares montgomery representation - int parity = memcmp(buf0, buf1, RLC_FP_BYTES) > 0; + + // parity of t: compare the canonical bytes of t.c1 against those of + // (-t).c1 + blst_fp2 nt = t; + blst_fp2_cneg(&nt, &nt, true); + uint8_t buf0[48], buf1[48]; + blst_bendian_from_fp(buf0, &t.fp[1]); + blst_bendian_from_fp(buf1, &nt.fp[1]); + const bool parity = memcmp(buf0, buf1, 48) > 0; + + blst_fp one, four; + FpOne(one); + FpFour(four); + blst_fp2 b; + b.fp[0] = four; + b.fp[1] = four; // w = t^2 + b + 1 - fp2_mul(w, t, t); - ep2_curve_get_b(b); - fp2_add(w, w, b); - fp_add_dig(w[0], w[0], 1); + blst_fp2 w; + blst_fp2_sqr(&w, &t); + blst_fp2_add(&w, &w, &b); + blst_fp_add(&w.fp[0], &w.fp[0], &one); - if (fp2_is_zero(w)) { - ep2_curve_get_gen(p); + if (Fp2IsZero(w)) { + p = *blst_p2_affine_generator(); if (parity) { - ep2_neg(p, p); + blst_fp2_cneg(&p.y, &p.y, true); } - fp2_free(nt); - fp2_free(w); - fp2_free(b); - bn_free(s_n3); - bn_free(s_n3m1o2); - fp2_free(x1); - fp2_free(x2); - fp2_free(x3); - fp2_free(rhs); - return; + return true; } - // sqrt(-3) - ep2_curve_get_s3(s_n3); - fp2_t s_n3p; - fp2_null(s_n3p); - fp2_new(s_n3p); - fp2_t s_n3m1o2p; - fp2_null(s_n3m1o2p); - fp2_new(s_n3m1o2p); + blst_fp2 s3p, s32p; + blst_fp_from_bendian(&s3p.fp[0], S3_BE); + FpZero(s3p.fp[1]); + blst_fp_from_bendian(&s32p.fp[0], S32_BE); + FpZero(s32p.fp[1]); - fp2_zero(s_n3p); - fp2_zero(s_n3m1o2p); - fp_prime_conv(s_n3p[0], s_n3); + // w = sqrt(-3) * t / (t^2 + b + 1) + blst_fp2_inverse(&w, &w); + blst_fp2_mul(&w, &w, &s3p); + blst_fp2_mul(&w, &w, &t); - // (sqrt(-3) - 1) / 2 - ep2_curve_get_s32(s_n3m1o2); - fp_prime_conv(s_n3m1o2p[0], s_n3m1o2); - - fp2_inv(w, w); - fp2_mul(w, w, s_n3p); - fp2_mul(w, w, t); - - // x1 = -wt + sqrt(-3) - fp2_neg(x1, w); - fp2_mul(x1, x1, t); - fp2_add(x1, x1, s_n3m1o2p); + // x1 = -wt + (sqrt(-3) - 1) / 2 + blst_fp2 x1; + x1 = w; + blst_fp2_cneg(&x1, &x1, true); + blst_fp2_mul(&x1, &x1, &t); + blst_fp2_add(&x1, &x1, &s32p); // x2 = -x1 - 1 - fp2_neg(x2, x1); - fp_sub_dig(x2[0], x2[0], 1); + blst_fp2 x2 = x1; + blst_fp2_cneg(&x2, &x2, true); + blst_fp_sub(&x2.fp[0], &x2.fp[0], &one); // x3 = 1/w^2 + 1 - fp2_mul(x3, w, w); - fp2_inv(x3, x3); - fp_add_dig(x3[0], x3[0], 1); - - fp2_zero(p->y); - fp2_set_dig(p->z, 1); - - // if x1 has no y, try x2. if x2 has no y, try x3 - fp2_copy(p->x, x1); - - ep2_rhs(rhs, p); - int Xx1 = fp2_srt(p->y, rhs) ? 1 : -1; - fp2_copy(p->x, x2); - ep2_rhs(rhs, p); - int Xx2 = fp2_srt(p->y, rhs) ? 1 : -1; + blst_fp2 x3; + blst_fp2_sqr(&x3, &w); + blst_fp2_inverse(&x3, &x3); + blst_fp_add(&x3.fp[0], &x3.fp[0], &one); + + const auto rhs = [&b](blst_fp2& out, const blst_fp2& x) { + blst_fp2_sqr(&out, &x); + blst_fp2_mul(&out, &out, &x); + blst_fp2_add(&out, &out, &b); + }; + + blst_fp2 r, y; + rhs(r, x1); + const int Xx1 = blst_fp2_sqrt(&y, &r) ? 1 : -1; + rhs(r, x2); + const int Xx2 = blst_fp2_sqrt(&y, &r) ? 1 : -1; // This formula computes which index to use, in constant time // without conditional branches. It's taken from the paper. 3 is // added, because the % operator in c can be negative. - int index = ((((Xx1 - 1) * Xx2) % 3) + 3) % 3; - - if (index == 0) { - fp2_copy(p->x, x1); - ep2_rhs(rhs, p); - fp2_srt(p->y, rhs); - } else if (index == 1) { - fp2_copy(p->x, x2); - ep2_rhs(rhs, p); - fp2_srt(p->y, rhs); - } else if (index == 2) { - fp2_copy(p->x, x3); - ep2_rhs(rhs, p); - fp2_srt(p->y, rhs); - } - - p->coord = 1; - - // Check for lexicografically greater than the negation - fp2_t ny; - fp2_null(ny); - fp2_new(ny); - fp2_neg(ny, p->y); - fp_write_bin(buf0, RLC_FP_BYTES, p->y[1]); - fp_write_bin(buf1, RLC_FP_BYTES, ny[1]); - - // Compare bytes since fp_cmp compares montgomery representation - if ((memcmp(buf0, buf1, RLC_FP_BYTES) > 0) != parity) { - ep2_neg(p, p); + const int index = ((((Xx1 - 1) * Xx2) % 3) + 3) % 3; + + p.x = (index == 0) ? x1 : (index == 1) ? x2 : x3; + rhs(r, p.x); + const bool haveRoot = blst_fp2_sqrt(&p.y, &r); + (void)haveRoot; + assert(haveRoot); // guaranteed by the SW construction + + // Normalize the sign of y against the parity of t + blst_fp2 ny = p.y; + blst_fp2_cneg(&ny, &ny, true); + blst_bendian_from_fp(buf0, &p.y.fp[1]); + blst_bendian_from_fp(buf1, &ny.fp[1]); + if ((memcmp(buf0, buf1, 48) > 0) != parity) { + p.y = ny; } + return true; +} - fp2_free(nt); - fp2_free(w); - fp2_free(b); - bn_free(s_n3); - bn_free(s_n3m1o2); - fp2_free(x1); - fp2_free(x2); - fp2_free(x3); - fp2_free(rhs); - - fp2_free(s_n3p); - fp2_free(s_n3m1o2p); - fp2_free(ny); +// Reduce a 64-byte big-endian value mod p into an fp element, matching +// relic's bn_read_bin + fp_prime_conv: split as hi*2^256 + lo with both +// halves < 2^256 < p. +void FpFromBE64Wide(blst_fp& out, const uint8_t* be64) +{ + uint8_t padded[48] = {0}; + blst_fp hi, lo, c256; + + memcpy(padded + 16, be64, 32); + blst_fp_from_bendian(&hi, padded); + memcpy(padded + 16, be64 + 32, 32); + blst_fp_from_bendian(&lo, padded); + + // 2^256 mod p == 2^256 (p is 381 bits) + memset(padded, 0, 48); + padded[15] = 0x01; + blst_fp_from_bendian(&c256, padded); + + blst_fp_mul(&out, &hi, &c256); + blst_fp_add(&out, &out, &lo); } +} // anon namespace + +namespace bls { +void ep2_map_legacy(blst_p2* p, const uint8_t* msg, int len) +{ + if (len != 32) { + throw std::invalid_argument( + "ep2_map_legacy requires a 32-byte message hash"); + } -void ep2_map_legacy(ep2_t p, const uint8_t *msg, int len) { - bn_t t00; - bn_t t01; - bn_t t10; - bn_t t11; - fp_t t00p; - fp_t t01p; - fp_t t10p; - fp_t t11p; - fp2_t t0p; - fp2_t t1p; - ep2_t p0; - ep2_t p1; - - bn_null(t00); - bn_null(t01); - bn_null(t10); - bn_null(t11); - fp_null(t00p); - fp_null(t01p); - fp_null(t10p); - fp_null(t11p); - fp2_null(t0p); - fp2_null(t1p); - ep2_null(p1); - ep2_null(p0); - - RLC_TRY { - uint8_t input[RLC_MD_LEN + 8]; - if (len != RLC_MD_LEN) { - RLC_THROW(ERR_CAUGHT); - } - memcpy(input, msg, len); - // b"G2_0_c0" - input[RLC_MD_LEN + 0] = 0x47; // G - input[RLC_MD_LEN + 1] = 0x32; // 2 - input[RLC_MD_LEN + 2] = 0x5f; // _ - input[RLC_MD_LEN + 3] = 0x30; // 0 - input[RLC_MD_LEN + 4] = 0x5f; // _ - input[RLC_MD_LEN + 5] = 0x63; // c - input[RLC_MD_LEN + 6] = 0x30; // 0 - - bn_new(t00); - bn_new(t01); - bn_new(t10); - bn_new(t11); - fp_new(t00p); - fp_new(t01p); - fp_new(t10p); - fp_new(t11p); - fp2_new(t0p); - fp2_new(t1p); - ep2_new(p1); - ep2_new(p0); - - uint8_t t00Bytes[RLC_MD_LEN * 2]; - uint8_t t01Bytes[RLC_MD_LEN * 2]; - uint8_t t10Bytes[RLC_MD_LEN * 2]; - uint8_t t11Bytes[RLC_MD_LEN * 2]; - - // b"G2_0_c0" - input[RLC_MD_LEN + 7] = 0x00; // 0 - md_map(t00Bytes, input, RLC_MD_LEN + 8); - input[RLC_MD_LEN + 7] = 1; // 1 - md_map(t00Bytes + RLC_MD_LEN, input, RLC_MD_LEN + 8); - - // b"G2_0_c1" - input[RLC_MD_LEN + 6] = 0x31; // b"1" - input[RLC_MD_LEN + 7] = 0; // 0 - - md_map(t01Bytes, input, RLC_MD_LEN + 8); - input[RLC_MD_LEN + 7] = 1; // 1 - md_map(t01Bytes + RLC_MD_LEN, input, RLC_MD_LEN + 8); - - // b"G2_1_c0" - input[RLC_MD_LEN + 3] = 0x31; // b"1" - input[RLC_MD_LEN + 6] = 0x30; // b"0" - input[RLC_MD_LEN + 7] = 0; // 0 - md_map(t10Bytes, input, RLC_MD_LEN + 8); - input[RLC_MD_LEN + 7] = 1; // 1 - md_map(t10Bytes + RLC_MD_LEN, input, RLC_MD_LEN + 8); - - // b"G2_1_c1" - input[RLC_MD_LEN + 6] = 0x31; // b"1" - input[RLC_MD_LEN + 7] = 0; // 0 - md_map(t11Bytes, input, RLC_MD_LEN + 8); - input[RLC_MD_LEN + 7] = 1; // 1 - md_map(t11Bytes + RLC_MD_LEN, input, RLC_MD_LEN + 8); - - bn_read_bin(t00, t00Bytes, RLC_MD_LEN * 2); - bn_read_bin(t01, t01Bytes, RLC_MD_LEN * 2); - bn_read_bin(t10, t10Bytes, RLC_MD_LEN * 2); - bn_read_bin(t11, t11Bytes, RLC_MD_LEN * 2); - - fp_prime_conv(t00p, t00); - fp_prime_conv(t01p, t01); - fp_prime_conv(t10p, t10); - fp_prime_conv(t11p, t11); - - fp_copy(t0p[0], t00p); - fp_copy(t0p[1], t01p); - fp_copy(t1p[0], t10p); - fp_copy(t1p[1], t11p); - - ep2_sw_encode(p0, t0p); - ep2_sw_encode(p1, t1p); - ep2_add(p0, p0, p1); - /* Now, multiply by cofactor to get the correct group. */ - ep2_mul_cof_b12(p, p0); + uint8_t input[32 + 8]; + memcpy(input, msg, 32); + + uint8_t t00Bytes[64], t01Bytes[64], t10Bytes[64], t11Bytes[64]; + + const auto draw = [&input](uint8_t* out, char i, char j) { + // tag: "G2__c" followed by a counter byte + input[32 + 0] = 0x47; // G + input[32 + 1] = 0x32; // 2 + input[32 + 2] = 0x5f; // _ + input[32 + 3] = (uint8_t)i; + input[32 + 4] = 0x5f; // _ + input[32 + 5] = 0x63; // c + input[32 + 6] = (uint8_t)j; + input[32 + 7] = 0; + blst_sha256(out, input, 32 + 8); + input[32 + 7] = 1; + blst_sha256(out + 32, input, 32 + 8); + }; + + draw(t00Bytes, '0', '0'); + draw(t01Bytes, '0', '1'); + draw(t10Bytes, '1', '0'); + draw(t11Bytes, '1', '1'); + + blst_fp2 t0, t1; + FpFromBE64Wide(t0.fp[0], t00Bytes); + FpFromBE64Wide(t0.fp[1], t01Bytes); + FpFromBE64Wide(t1.fp[0], t10Bytes); + FpFromBE64Wide(t1.fp[1], t11Bytes); + + blst_p2_affine a0, a1; + const bool has0 = ep2_sw_encode(a0, t0); + const bool has1 = ep2_sw_encode(a1, t1); + + blst_p2 sum; + memset(&sum, 0x00, sizeof(blst_p2)); // infinity + if (has0) { + blst_p2_from_affine(&sum, &a0); } - RLC_CATCH_ANY { - RLC_THROW(ERR_CAUGHT); + if (has1) { + blst_p2_add_or_double_affine(&sum, &sum, &a1); } - RLC_FINALLY { - fp2_free(t0p); - fp2_free(t1p); - fp_free(t00p); - fp_free(t01p); - fp_free(t10p); - fp_free(t11p); - bn_free(t00); - bn_free(t01); - bn_free(t10); - bn_free(t11); - ep2_free(p0); - ep2_free(p1); + + // Clear the cofactor by multiplying with h_eff (equivalent to the + // psi-based clearing the relic implementation used). + uint8_t heffLE[sizeof(H_EFF_BE)]; + for (size_t i = 0; i < sizeof(H_EFF_BE); i++) { + heffLE[i] = H_EFF_BE[sizeof(H_EFF_BE) - 1 - i]; } + blst_p2_mult(p, &sum, heffLE, 636); } +} // namespace bls From ecbb1dc6e19b4bd9d61653262d301f23d5b6e6b5 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Fri, 11 Sep 2026 15:13:13 +0700 Subject: [PATCH 13/36] fix: adopt elements.cpp for blst library usages --- src/elements.cpp | 365 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 300 insertions(+), 65 deletions(-) diff --git a/src/elements.cpp b/src/elements.cpp index e7648ead6..73e6f2724 100644 --- a/src/elements.cpp +++ b/src/elements.cpp @@ -19,7 +19,182 @@ #include "bls.hpp" #include "legacy.hpp" +namespace { +// The legacy codec reproduces the byte-level behavior of the relic-based +// implementation exactly, including its handling of malformed input. relic +// was built without CHECK, so its internal errors did not throw: an +// out-of-range field element left the coordinate at zero, and a failed +// square root left y holding its pre-existing raw contents (0, or a raw +// limb 1 when the sign bit had been stored). All of it is consensus-visible +// through re-serialization and is pinned by test-vectors/reference.txt. + +// BLS12-381 base field prime p, big-endian. +static const uint8_t P_BE[48] = { + 0x1a, 0x01, 0x11, 0xea, 0x39, 0x7f, 0xe6, 0x9a, 0x4b, 0x1b, 0xa7, 0xb6, + 0x43, 0x4b, 0xac, 0xd7, 0x64, 0x77, 0x4b, 0x84, 0xf3, 0x85, 0x12, 0xbf, + 0x67, 0x30, 0xd2, 0xa0, 0xf6, 0xb0, 0xf6, 0x24, 0x1e, 0xab, 0xff, 0xfe, + 0xb1, 0x53, 0xff, 0xff, 0xb9, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xaa, 0xab}; + +// (p - 1) / 2, big-endian. The legacy sign convention is y > (p-1)/2 +// ("larger" root), matching relic's ep_pck/ep2_pck for pairing-friendly +// curves. +static const uint8_t HALF_P_BE[48] = { + 0x0d, 0x00, 0x88, 0xf5, 0x1c, 0xbf, 0xf3, 0x4d, 0x25, 0x8d, 0xd3, 0xdb, + 0x21, 0xa5, 0xd6, 0x6b, 0xb2, 0x3b, 0xa5, 0xc2, 0x79, 0xc2, 0x89, 0x5f, + 0xb3, 0x98, 0x69, 0x50, 0x7b, 0x58, 0x7b, 0x12, 0x0f, 0x55, 0xff, 0xff, + 0x58, 0xa9, 0xff, 0xff, 0xdc, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xd5, 0x55}; + +// (2^384)^-1 mod p, big-endian. When relic (built without CHECK) failed to +// find a square root during legacy point decompression with the sign bit +// set, the y coordinate was left holding a raw limb value of 1, which reads +// back from Montgomery form as this constant. Preserved bit-for-bit for +// compatibility with historical consensus data. +static const uint8_t MONT_R_INV_BE[48] = { + 0x14, 0xfe, 0xc7, 0x01, 0xe8, 0xfb, 0x0c, 0xe9, 0xed, 0x5e, 0x64, 0x27, + 0x3c, 0x4f, 0x53, 0x8b, 0x17, 0x97, 0xab, 0x14, 0x58, 0xa8, 0x8d, 0xe9, + 0x34, 0x3e, 0xa9, 0x79, 0x14, 0x95, 0x6d, 0xc8, 0x7f, 0xe1, 0x12, 0x74, + 0xd8, 0x98, 0xfa, 0xfb, 0xf4, 0xd3, 0x82, 0x59, 0x38, 0x0b, 0x48, 0x20}; + +inline void FpFromBE48(blst_fp& out, const uint8_t* be) +{ + blst_fp_from_bendian(&out, be); +} + +// Compares a big-endian 48-byte value against a constant. Returns the +// memcmp sign. +inline int CmpBE48(const uint8_t* a, const uint8_t* b) +{ + return memcmp(a, b, 48); +} + +inline void FpZero(blst_fp& out) { memset(&out, 0x00, sizeof(blst_fp)); } + +inline void FpOneConst(blst_fp& out) +{ + uint8_t be[48] = {0}; + be[47] = 1; + blst_fp_from_bendian(&out, be); +} + +// relic's ep2_upk sign rule (decompression side, with the IETF tie-break): +// sign = c1 > (p-1)/2, but when c1 == 0 the sign of c0 is used instead. +// Note the serialization side (ep2_pck) uses c1 only, with no tie-break. +inline bool Fp2SignUpk(const blst_fp2& y) +{ + uint8_t be[48]; + blst_bendian_from_fp(be, &y.fp[1]); + bool c1Zero = true; + for (int i = 0; i < 48; i++) + if (be[i] != 0) { c1Zero = false; break; } + if (!c1Zero) return CmpBE48(be, HALF_P_BE) > 0; + blst_bendian_from_fp(be, &y.fp[0]); + return CmpBE48(be, HALF_P_BE) > 0; +} + +// The "larger root" test used by both the legacy codec (relic's +// ep_pck/ep2_pck convention) and the legacy hash-to-curve: y > (p-1)/2 on +// the canonical representation. +inline bool FpSignGtHalf(const blst_fp& a) +{ + uint8_t be[48]; + blst_bendian_from_fp(be, &a); + return CmpBE48(be, HALF_P_BE) > 0; +} +} // anon namespace + namespace bls { +static void LegacyDecodeG1( + blst_p1_affine& a, + const uint8_t* bytes, + const bool signBit) +{ + uint8_t xbytes[G1Element::SIZE]; + memcpy(xbytes, bytes, G1Element::SIZE); + // the relic-based G1 codec erased the top three bits of the first byte + // unconditionally, before the sign bit was even examined (unlike G2, + // which only clears the sign bit) + xbytes[0] &= 0x1f; + + if (CmpBE48(xbytes, P_BE) >= 0) { + // relic's fp_read_bin error path: coordinate left untouched (zero) + FpZero(a.x); + } else { + FpFromBE48(a.x, xbytes); + } + + // rhs = x^3 + 4 + blst_fp rhs; + blst_fp_sqr(&rhs, &a.x); + blst_fp_mul(&rhs, &rhs, &a.x); + blst_fp four; + uint8_t fourBytes[48] = {0}; + fourBytes[47] = 4; + FpFromBE48(four, fourBytes); + blst_fp_add(&rhs, &rhs, &four); + + if (blst_fp_sqrt(&a.y, &rhs)) { + if (FpSignGtHalf(a.y) != signBit) { + blst_fp_cneg(&a.y, &a.y, true); + } + } else { + // relic's ep_upk failure: y keeps the raw sign-bit marker + if (signBit) { + FpFromBE48(a.y, MONT_R_INV_BE); + } else { + FpZero(a.y); + } + } +} + +static void LegacyDecodeG2( + blst_p2_affine& a, + const uint8_t* bytes, + const bool signBit) +{ + uint8_t c0[48], c1[48]; + memcpy(c0, bytes, 48); + memcpy(c1, bytes + 48, 48); + if (signBit) { + c0[0] &= 0x7f; + } + + if (CmpBE48(c0, P_BE) >= 0) { + FpZero(a.x.fp[0]); + } else { + FpFromBE48(a.x.fp[0], c0); + } + if (CmpBE48(c1, P_BE) >= 0) { + FpZero(a.x.fp[1]); + } else { + FpFromBE48(a.x.fp[1], c1); + } + + // rhs = x^3 + (4, 4) + blst_fp2 rhs; + blst_fp2_sqr(&rhs, &a.x); + blst_fp2_mul(&rhs, &rhs, &a.x); + blst_fp four; + uint8_t fourBytes[48] = {0}; + fourBytes[47] = 4; + FpFromBE48(four, fourBytes); + blst_fp2 b2; + b2.fp[0] = four; + b2.fp[1] = four; + blst_fp2_add(&rhs, &rhs, &b2); + + if (blst_fp2_sqrt(&a.y, &rhs)) { + if (Fp2SignUpk(a.y) != signBit) { + blst_fp2_cneg(&a.y, &a.y, true); + } + } else { + if (signBit) { + FpFromBE48(a.y.fp[0], MONT_R_INV_BE); + } else { + FpZero(a.y.fp[0]); + } + FpZero(a.y.fp[1]); + } +} const size_t G1Element::SIZE; @@ -43,34 +218,33 @@ G1Element G1Element::FromBytesUnchecked(Bytes const bytes, bool fLegacy) bool fZerosOnly = Util::HasOnlyZeros(Bytes(raw_bytes + 1, bytes.size() - 1)); if ((bytes[0] & 0xc0) == 0xc0) { // representing infinity - buffer[0] = 0x00; - buffer[1] &= 0x1f; // erase 3 msbs from given input - // enforce that infinity must be 0xc0000..00 if (bytes[0] != 0xc0 || !fZerosOnly) { throw std::invalid_argument( "Given G1 infinity element must be canonical"); } return G1Element(); // return infinity element (point all zero) - } else { - if (fLegacy) { - if (bytes[0] & 0x80) { - buffer[0] = 0x03; // Insert extra byte for Y=1 - buffer[1] &= 0x7f; // Remove initial Y bit - } else { - buffer[0] = 0x02; // Insert extra byte for Y=0 - } - } else { - if ((bytes[0] & 0xc0) != 0x80) { - throw std::invalid_argument( - "Given G1 non-infinity element must start with 0b10"); - } - - if (fZerosOnly) { - throw std::invalid_argument( - "G1 non-infinity element can't have only zeros"); - } - } + } + if (fLegacy) { + G1Element ele; + blst_p1_affine a; + LegacyDecodeG1(a, bytes.begin(), (bytes[0] & 0x80) != 0); + // Build the Jacobian point by hand: blst_p1_from_affine maps the + // all-zero affine pair to infinity, but relic kept degenerate + // decodes (e.g. x = 0, y = 0) as ordinary z=1 points, which is + // consensus-visible through re-serialization. + ele.p.x = a.x; + ele.p.y = a.y; + FpOneConst(ele.p.z); + return ele; + } + if ((bytes[0] & 0xc0) != 0x80) { + throw std::invalid_argument( + "Given G1 non-infinity element must start with 0b10"); + } + if (fZerosOnly) { + throw std::invalid_argument( + "G1 non-infinity element can't have only zeros"); } blst_p1_affine a; @@ -93,14 +267,6 @@ G1Element G1Element::FromNative(const blst_p1& element) return ele; } -G1Element G1Element::Copy() { - G1Element ele; - g1_copy(ele.p, this->p); - - return ele; -} - - G1Element G1Element::FromAffine(const blst_p1_affine& element) { G1Element ele; @@ -173,6 +339,8 @@ void G1Element::ToAffine(blst_p1_affine* output) const blst_p1_to_affine(output, &p); } +G1Element G1Element::Copy() { return *this; } + G1Element G1Element::Negate() const { G1Element ans = G1Element::FromNative(p); @@ -186,21 +354,37 @@ uint32_t G1Element::GetFingerprint(const bool fLegacy) const { uint8_t buffer[G1Element::SIZE]; uint8_t hash[32]; - memcpy(buffer, Serialize(fLegacy).data(), G1Element::SIZE); + memcpy(buffer, SerializeToArray(fLegacy).data(), G1Element::SIZE); Util::Hash256(hash, buffer, G1Element::SIZE); return Util::FourBytesToInt(hash); } -std::vector G1Element::Serialize(const bool fLegacy) const { - const auto arr = G1Element::SerializeToArray(fLegacy); +std::vector G1Element::Serialize(const bool fLegacy) const +{ + const auto arr = SerializeToArray(fLegacy); return std::vector{arr.begin(), arr.end()}; } -std::vector G1Element::Serialize() const +std::array G1Element::SerializeToArray(const bool fLegacy) const { - uint8_t buffer[G1Element::SIZE]; - blst_p1_compress(buffer, &p); - return std::vector(buffer, buffer + G1Element::SIZE); + std::array result{}; + if (!fLegacy) { + blst_p1_compress(result.data(), &p); + return result; + } + + if (blst_p1_is_inf(&p)) { + result[0] = 0xc0; + return result; + } + + blst_p1_affine a; + blst_p1_to_affine(&a, &p); + blst_bendian_from_fp(result.data(), &a.x); + if (FpSignGtHalf(a.y)) { + result[0] |= 0x80; + } + return result; } bool operator==(const G1Element& a, const G1Element& b) @@ -261,9 +445,50 @@ G2Element G2Element::FromBytesUnchecked(Bytes const bytes, const bool fLegacy) } if (fLegacy) { -// std::memcpy(buffer + 1, bytes.begin(), G2Element::SIZE); -// buffer[0] = 0x00; - static_assert(false); // TODO fix it + // The relic implementation computed its zeros-only check over a + // buffer that still contained the first byte unmasked, so for the + // legacy G2 codec any input with the two top bits set - canonical + // infinity included - was rejected as non-canonical. + if ((bytes[0] & 0xc0) == 0xc0) { + throw std::invalid_argument( + "Given G2 infinity element must be canonical"); + } + G2Element ele; + blst_p2_affine a; + LegacyDecodeG2(a, bytes.begin(), (bytes[0] & 0x80) != 0); + // See the G1 legacy path: keep degenerate all-zero decodes as + // ordinary z=1 points instead of mapping them to infinity. + ele.q.x = a.x; + ele.q.y = a.y; + FpOneConst(ele.q.z.fp[0]); + FpZero(ele.q.z.fp[1]); + return ele; + } + + if ((bytes[48] & 0xe0) != 0x00) { + throw std::invalid_argument( + "Given G2 element must always have 48th byte start with 0b000"); + } + + bool fZerosOnly = (bytes[0] & 0x1f) == 0 && + Util::HasOnlyZeros(Bytes(bytes.begin() + 1, bytes.size() - 1)); + if ((bytes[0] & 0xc0) == 0xc0) { // infinity + // enforce that infinity must be 0xc0000..00 + if (bytes[0] != 0xc0 || !fZerosOnly) { + throw std::invalid_argument( + "Given G2 infinity element must be canonical"); + } + return G2Element(); + } + + if ((bytes[0] & 0xc0) != 0x80) { + throw std::invalid_argument( + "G2 non-inf element must have 0th byte start with 0b10"); + } + + if (fZerosOnly) { + throw std::invalid_argument( + "G2 non-infinity element can't have only zeros"); } blst_p2_affine a; @@ -309,20 +534,20 @@ G2Element G2Element::FromMessage( const bool fLegacy) { G2Element ans; - const byte* aug = nullptr; - size_t aug_len = 0; - if (fLegacy) { - ep2_map_legacy(ans.q, message.begin(), BLS::MESSAGE_HASH_LEN); + ep2_map_legacy(&(ans.q), message.begin(), BLS::MESSAGE_HASH_LEN); } else { - blst_hash_to_g2( - &(ans.q), - message.begin(), - (int)message.size(), - dst, - dst_len, - aug, - aug_len); + const byte* aug = nullptr; + size_t aug_len = 0; + + blst_hash_to_g2( + &(ans.q), + message.begin(), + (int)message.size(), + dst, + dst_len, + aug, + aug_len); } assert(ans.IsValid()); return ans; @@ -363,13 +588,7 @@ void G2Element::ToAffine(blst_p2_affine* output) const blst_p2_to_affine(output, &q); } -G2Element G2Element::Copy() { - G2Element ele; - g2_copy(ele.q, this->q); - - return ele; -} - +G2Element G2Element::Copy() { return *this; } G2Element G2Element::Negate() const { @@ -380,19 +599,35 @@ G2Element G2Element::Negate() const GTElement G2Element::Pair(const G1Element& a) const { return a & (*this); } -std::vector G2Element::Serialize(const bool fLegacy) const { +std::vector G2Element::Serialize(const bool fLegacy) const +{ const auto arr = G2Element::SerializeToArray(fLegacy); return std::vector{arr.begin(), arr.end()}; } std::array G2Element::SerializeToArray(const bool fLegacy) const { - if (fLegacy) { - static_assert(false); // TODO: make it for legacy + std::array result{}; + if (!fLegacy) { + blst_p2_compress(result.data(), &q); + return result; + } + + if (blst_p2_is_inf(&q)) { + result[0] = 0xc0; + return result; + } + + blst_p2_affine a; + blst_p2_to_affine(&a, &q); + // legacy layout: x.c0 || x.c1 (the modern IETF layout is the swap) + blst_bendian_from_fp(result.data(), &a.x.fp[0]); + blst_bendian_from_fp(result.data() + 48, &a.x.fp[1]); + // legacy sign: c1 > (p-1)/2, no tie-break (relic's ep2_pck) + if (FpSignGtHalf(a.y.fp[1])) { + result[0] |= 0x80; } - uint8_t buffer[G2Element::SIZE]; - blst_p2_compress(buffer, &q); - return buffer; + return result; } bool operator==(G2Element const& a, G2Element const& b) From 81e22c0363a7c96733430c22a54622e65bf8b03d Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Thu, 24 Sep 2026 15:28:42 +0700 Subject: [PATCH 14/36] fix: blst's validation assumes a point on the curve while relic's is-valid checks both --- src/elements.cpp | 6 ++++-- src/test.cpp | 27 +++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/elements.cpp b/src/elements.cpp index 73e6f2724..35b3bd939 100644 --- a/src/elements.cpp +++ b/src/elements.cpp @@ -320,7 +320,9 @@ bool G1Element::IsValid() const if (blst_p1_is_inf(&p)) return true; - return blst_p1_in_g1(&p); + // blst_p1_in_g1 assumes a point on the curve; relic's g1_is_valid + // checked both + return blst_p1_on_curve(&p) && blst_p1_in_g1(&p); } void G1Element::CheckValid() const @@ -569,7 +571,7 @@ bool G2Element::IsValid() const if (blst_p2_is_inf(&q)) return true; - return blst_p2_in_g2(&q); + return blst_p2_on_curve(&q) && blst_p2_in_g2(&q); } void G2Element::CheckValid() const diff --git a/src/test.cpp b/src/test.cpp index f114694a5..e8dad4a92 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -2069,6 +2069,33 @@ TEST_CASE("FastAggregateVerify rejects identity elements") } } +TEST_CASE("IsValid rejects points off the curve") +{ + // (4x, 8y, z) of the generator is not on the curve but passes blst's + // subgroup test, which assumes a curve point + uint8_t be[48] = {0}; + blst_fp four, eight; + be[47] = 4; + blst_fp_from_bendian(&four, be); + be[47] = 8; + blst_fp_from_bendian(&eight, be); + + blst_p1 off1 = *blst_p1_generator(); + blst_fp_mul(&off1.x, &off1.x, &four); + blst_fp_mul(&off1.y, &off1.y, &eight); + REQUIRE(!G1Element::FromNative(off1).IsValid()); + + blst_p2 off2 = *blst_p2_generator(); + blst_fp2 four2, eight2; + memset(&four2, 0, sizeof(four2)); + memset(&eight2, 0, sizeof(eight2)); + four2.fp[0] = four; + eight2.fp[0] = eight; + blst_fp2_mul(&off2.x, &off2.x, &four2); + blst_fp2_mul(&off2.y, &off2.y, &eight2); + REQUIRE(!G2Element::FromNative(off2).IsValid()); +} + TEST_CASE("CheckValid for Legacy") { SECTION("Invalid G1 points should throw in CheckValid but not in FromBytes") From 330f75bf817ad373a1b12fe5c8eff9a22876fd31 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Fri, 11 Sep 2026 15:16:14 +0700 Subject: [PATCH 15/36] fix: adopt privatekey.cpp for blst library usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit NOTE: Before (relic): bn_new(order); g1_get_ord(order); then SecAlloc a bn_t, bn_read_bin the 32-byte HMAC output, bn_mod_basic it modulo r, and bn_copy the result into keydata. After (blst): blst_scalar_from_be_bytes(k.keydata, hash, 32); — one call that does exactly that: interpret the 32 bytes big-endian, reduce mod r, store into keydata. Both make sure private key is less than the curve order --- include/dashbls/privatekey.hpp | 4 +-- src/privatekey.cpp | 64 +++++++++++++++------------------- 2 files changed, 30 insertions(+), 38 deletions(-) diff --git a/include/dashbls/privatekey.hpp b/include/dashbls/privatekey.hpp index a2e7770db..a85051da5 100644 --- a/include/dashbls/privatekey.hpp +++ b/include/dashbls/privatekey.hpp @@ -67,8 +67,8 @@ class PrivateKey { friend G2Element operator*(const G2Element &a, const PrivateKey &k); friend G2Element operator*(const PrivateKey &k, const G2Element &a); - friend PrivateKey operator*(const PrivateKey& a, const bn_t& k); - friend PrivateKey operator*(const bn_t& k, const PrivateKey& a); + friend PrivateKey operator*(const PrivateKey& a, const blst_scalar& k); + friend PrivateKey operator*(const blst_scalar& k, const PrivateKey& a); // Serialize the key into bytes void Serialize(uint8_t *buffer) const; diff --git a/src/privatekey.cpp b/src/privatekey.cpp index b5ea51278..766c367e0 100644 --- a/src/privatekey.cpp +++ b/src/privatekey.cpp @@ -30,26 +30,25 @@ PrivateKey PrivateKey::FromSeedBIP32(const Bytes& seed) { PrivateKey::PRIVATE_KEY_SIZE); // Hash the seed into sk - md_hmac(hash, seed.begin(), (int)seed.size(), hmacKey, sizeof(hmacKey)); - - bn_t order; - bn_new(order); - g1_get_ord(order); + Util::md_hmac(hash, seed.begin(), (int)seed.size(), hmacKey, sizeof(hmacKey)); // Make sure private key is less than the curve order - bn_t* skBn = Util::SecAlloc(1); - bn_new(*skBn); - bn_read_bin(*skBn, hash, PrivateKey::PRIVATE_KEY_SIZE); - bn_mod_basic(*skBn, *skBn, order); - PrivateKey k; - bn_copy(k.keydata, *skBn); + blst_scalar_from_be_bytes(k.keydata, hash, PrivateKey::PRIVATE_KEY_SIZE); - Util::SecFree(skBn); Util::SecFree(hash); return k; } +// BLS12-381 group order r, big-endian. +// Taken from depends/blst/src/consts.c: BLS12_381_r = { 0xffffffff00000001, 0x53bda402fffe5bfe, 0x3339d80809a1d805, 0x73eda753299d7d48 } +// (little-endian limbs of the same number, with the comment z^4 - z^2 + 1, group order). + +static const uint8_t ORDER_BE[32] = { + 0x73, 0xed, 0xa7, 0x53, 0x29, 0x9d, 0x7d, 0x48, 0x33, 0x39, 0xd8, 0x08, + 0x09, 0xa1, 0xd8, 0x05, 0x53, 0xbd, 0xa4, 0x02, 0xff, 0xfe, 0x5b, 0xfe, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01}; + // Construct a private key from a bytearray. PrivateKey PrivateKey::FromBytes(const Bytes &bytes, bool modOrder) { @@ -61,18 +60,16 @@ PrivateKey PrivateKey::FromBytes(const Bytes &bytes, bool modOrder) if (modOrder) // this allows any bytes to be input and does proper mod order blst_scalar_from_be_bytes(k.keydata, bytes.begin(), bytes.size()); - else - // this should only be the output of deserialization + else { + // Values strictly greater than the group order are rejected; the + // order itself is accepted, matching the relic-based + // implementation's bn_cmp(keydata, order) > 0 check. + if (memcmp(bytes.begin(), ORDER_BE, PRIVATE_KEY_SIZE) > 0) { + throw std::invalid_argument( + "PrivateKey byte data must be less than the group order"); + } blst_scalar_from_bendian(k.keydata, bytes.begin()); - - if (Util::HasOnlyZeros(bytes)) { - return k; // don't check anything else, we allow zero private key } - - if (!blst_sk_check(k.keydata)) - throw std::invalid_argument( - "PrivateKey byte data must be less than the group order"); - return k; } @@ -91,7 +88,7 @@ PrivateKey::PrivateKey(const PrivateKey &privateKey) { privateKey.CheckKeyData(); AllocateKeyData(); - memcpy(keydata, privateKey.keydata, 32); + memcpy(keydata, privateKey.keydata, sizeof(blst_scalar)); } PrivateKey::PrivateKey(PrivateKey &&k) @@ -122,7 +119,7 @@ PrivateKey &PrivateKey::operator=(const PrivateKey &other) CheckKeyData(); other.CheckKeyData(); InvalidateCaches(); - memcpy(keydata, other.keydata, 32); + memcpy(keydata, other.keydata, sizeof(blst_scalar)); return *this; } @@ -200,20 +197,15 @@ G2Element operator*(const G2Element &a, const PrivateKey &k) G2Element operator*(const PrivateKey &k, const G2Element &a) { return a * k; } -PrivateKey operator*(const PrivateKey& k, const bn_t& a) +PrivateKey operator*(const PrivateKey& k, const blst_scalar& a) { k.CheckKeyData(); - bn_t order; - bn_new(order); - g2_get_ord(order); - PrivateKey ret; - bn_mul_comba(ret.keydata, k.keydata, a); - bn_mod_basic(ret.keydata, ret.keydata, order); + blst_sk_mul_n_check(ret.keydata, k.keydata, &a); return ret; } -PrivateKey operator*(const bn_t& a, const PrivateKey& k) { return a * k; } +PrivateKey operator*(const blst_scalar& a, const PrivateKey& k) { return k * a; } G2Element PrivateKey::GetG2Power(const G2Element &element) const { @@ -250,7 +242,7 @@ bool PrivateKey::IsZero() const blst_scalar zro; memset(&zro, 0x00, sizeof(blst_scalar)); - return memcmp(keydata, &zro, 32) == 0; + return memcmp(keydata, &zro, sizeof(blst_scalar)) == 0; } bool operator==(const PrivateKey &a, const PrivateKey &b) @@ -296,13 +288,13 @@ G2Element PrivateKey::SignG2( blst_p2 *pt = Util::SecAlloc(1); if (fLegacy) { + // The relic implementation always mapped exactly + // BLS::MESSAGE_HASH_LEN bytes regardless of len. ep2_map_legacy(pt, msg, BLS::MESSAGE_HASH_LEN); - g2_mul(pt, pt, keydata); - static_assert(false); // not implemented TODO } else { blst_hash_to_g2(pt, msg, len, dst, dst_len, nullptr, 0); - blst_sign_pk_in_g1(pt, pt, keydata); } + blst_sign_pk_in_g1(pt, pt, keydata); G2Element ret = G2Element::FromNative(*pt); Util::SecFree(pt); return ret; From a021900bd79bdd0ee9b90955a99f3e5b75fd0cbd Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Sat, 12 Sep 2026 00:03:59 +0700 Subject: [PATCH 16/36] fix: adopt util.hpp for blst library usages --- include/dashbls/hkdf.hpp | 1 - include/dashbls/util.hpp | 25 ++++++++++++++----------- src/privatekey.cpp | 2 +- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/include/dashbls/hkdf.hpp b/include/dashbls/hkdf.hpp index 27b519b98..e338966dc 100644 --- a/include/dashbls/hkdf.hpp +++ b/include/dashbls/hkdf.hpp @@ -39,7 +39,6 @@ class HKDF256 { static void Expand(uint8_t* okm, size_t L, const uint8_t* prk, const uint8_t* info, const size_t infoLen) { assert(L <= 255 * HASH_LEN); // L <= 255 * HashLen - assert(infoLen >= 0); size_t N = (L + HASH_LEN - 1) / HASH_LEN; // Round up size_t bytesWritten = 0; diff --git a/include/dashbls/util.hpp b/include/dashbls/util.hpp index 1dd3ec5a0..a96104267 100644 --- a/include/dashbls/util.hpp +++ b/include/dashbls/util.hpp @@ -23,6 +23,10 @@ #include #include +extern "C" { +#include "bindings/blst.h" +} + namespace bls { class BLS; @@ -64,11 +68,11 @@ class Util { blst_sha256(output, message, messageLen); } -static void md_hmac(uint8_t *mac, const uint8_t *in, int in_len, const uint8_t *key, - int key_len) { - #define block_size 64 - #define RLC_MD_LEN 32 - uint8_t opad[block_size + RLC_MD_LEN]; +static void md_hmac(uint8_t *mac, const uint8_t *in, size_t in_len, const uint8_t *key, + size_t key_len) { + constexpr size_t block_size = 64; + constexpr size_t md_len = 32; + uint8_t opad[block_size + md_len]; uint8_t *ipad = (uint8_t *)malloc(block_size + in_len); uint8_t _key[block_size]; @@ -77,21 +81,20 @@ static void md_hmac(uint8_t *mac, const uint8_t *in, int in_len, const uint8_t * if (key_len > block_size) { Hash256(_key, key, key_len); - key = _key; - key_len = RLC_MD_LEN; + key_len = md_len; + } else { + memcpy(_key, key, key_len); } - - memcpy(_key, key, key_len); memset(_key + key_len, 0, block_size - key_len); key = _key; - for (int i = 0; i < block_size; i++) { + for (size_t i = 0; i < block_size; i++) { opad[i] = 0x5C ^ key[i]; ipad[i] = 0x36 ^ key[i]; } memcpy(ipad + block_size, in, in_len); Hash256(opad + block_size, ipad, block_size + in_len); - Hash256(mac, opad, block_size + RLC_MD_LEN); + Hash256(mac, opad, block_size + md_len); free(ipad); } diff --git a/src/privatekey.cpp b/src/privatekey.cpp index 766c367e0..c61ce384a 100644 --- a/src/privatekey.cpp +++ b/src/privatekey.cpp @@ -30,7 +30,7 @@ PrivateKey PrivateKey::FromSeedBIP32(const Bytes& seed) { PrivateKey::PRIVATE_KEY_SIZE); // Hash the seed into sk - Util::md_hmac(hash, seed.begin(), (int)seed.size(), hmacKey, sizeof(hmacKey)); + Util::md_hmac(hash, seed.begin(), seed.size(), hmacKey, sizeof(hmacKey)); // Make sure private key is less than the curve order PrivateKey k; From 18a9a6b85eda904e0b1dfc2a50d549c09e1800bb Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Sat, 12 Sep 2026 02:01:24 +0700 Subject: [PATCH 17/36] fix: adopt schemes.hpp/cpp for blst library --- include/dashbls/schemes.hpp | 5 +- src/schemes.cpp | 107 +++++++++++++++--------------------- 2 files changed, 47 insertions(+), 65 deletions(-) diff --git a/include/dashbls/schemes.hpp b/include/dashbls/schemes.hpp index 580bb7f86..dd7835c35 100644 --- a/include/dashbls/schemes.hpp +++ b/include/dashbls/schemes.hpp @@ -111,8 +111,9 @@ class CoreMPL { protected: const std::string& strCiphersuiteId; - // bool NativeVerify(blst_p1 *pubKeys, blst_p2 *mappedHashes, size_t - // length); + static bool NativeVerify(const blst_p1_affine* pubKeys, + const blst_p2_affine* mappedHashes, + size_t length); G2Element AggregateSecure(std::vector const &vecPublicKeys, std::vector const &vecSignatures, const Bytes& message, diff --git a/src/schemes.cpp b/src/schemes.cpp index b32d59365..1c141baf8 100644 --- a/src/schemes.cpp +++ b/src/schemes.cpp @@ -30,51 +30,9 @@ using std::vector; namespace bls { -namespace { - -// RAII wrapper for bn_t arrays that owns both the heap allocation and -// the per-element bn_new/bn_free lifecycle. Tracks how many elements -// were successfully initialised so the destructor is safe even if -// bn_new throws partway through. -class BNArray { -public: - explicit BNArray(size_t size) - : data_(size ? new bn_t[size] : nullptr), size_(size), initialized_(0) - { - for (size_t i = 0; i < size_; ++i) { - bn_new(data_[i]); - ++initialized_; - } - } - - ~BNArray() { - for (size_t i = 0; i < initialized_; ++i) { - bn_free(data_[i]); - } - delete[] data_; - } - - BNArray(const BNArray&) = delete; - BNArray& operator=(const BNArray&) = delete; - - bn_t& operator[](size_t i) { return data_[i]; } - bn_t* data() { return data_; } - -private: - bn_t* data_; - size_t size_; - size_t initialized_; -}; - -} // namespace - template -static void HashPubKeys(bn_t* computedTs, size_t nPubKeys, GetBytesFn getBytes) +static void HashPubKeys(blst_scalar* computedTs, size_t nPubKeys, GetBytesFn getBytes) { - bn_t order; - bn_new(order); - g2_get_ord(order); - std::vector vecBuffer(nPubKeys * G1Element::SIZE); for (size_t i = 0; i < nPubKeys; i++) { @@ -94,10 +52,10 @@ static void HashPubKeys(bn_t* computedTs, size_t nPubKeys, GetBytesFn getBytes) std::memcpy(buffer + 4, pkHash, 32); Util::Hash256(hash, buffer, 4 + 32); - bn_read_bin(computedTs[i], hash, 32); - bn_mod_basic(computedTs[i], computedTs[i], order); + // reduces the hash mod the group order, like the former + // bn_read_bin + bn_mod + blst_scalar_from_be_bytes(&computedTs[i], hash, 32); } - bn_free(order); } enum InvariantResult { BAD = false, GOOD = true, CONTINUE }; @@ -269,7 +227,7 @@ G2Element CoreMPL::AggregateSecure(std::vector const &vecPublicKeys, throw std::invalid_argument("LegacySchemeMPL::AggregateSigs sigs.size() != pubKeys.size()"); } - BNArray computedTs(vecPublicKeys.size()); + std::vector computedTs(vecPublicKeys.size()); std::vector, const G2Element*>> vecSorted(vecPublicKeys.size()); for (size_t i = 0; i < vecPublicKeys.size(); i++) { vecSorted[i] = std::make_pair(vecPublicKeys[i].SerializeToArray(fLegacy), &vecSignatures[i]); @@ -311,7 +269,7 @@ bool CoreMPL::VerifySecure(const std::vector& vecPublicKeys, return false; } - BNArray computedTs(vecPublicKeys.size()); + std::vector computedTs(vecPublicKeys.size()); std::vector> vecSorted(vecPublicKeys.size()); for (size_t i = 0; i < vecPublicKeys.size(); i++) { vecSorted[i] = vecPublicKeys[i].SerializeToArray(fLegacy); @@ -442,6 +400,29 @@ bool CoreMPL::AggregateVerify( return ret; } +bool CoreMPL::NativeVerify(const blst_p1_affine* pubkeys, + const blst_p2_affine* mappedHashes, + size_t length) +{ + // 1 =? prod e(pubkey[i], hash[i]) + // A pairing with the point at infinity on either side contributes the + // identity, matching relic's pc_map_sim. + blst_fp12 candidate = *blst_fp12_one(); + + for (size_t i = 0; i < length; ++i) { + if (blst_p1_affine_is_inf(&pubkeys[i]) || + blst_p2_affine_is_inf(&mappedHashes[i])) { + continue; + } + blst_fp12 tmpPairing; + blst_miller_loop(&tmpPairing, &mappedHashes[i], &pubkeys[i]); + blst_fp12_mul(&candidate, &candidate, &tmpPairing); + } + + blst_final_exp(&candidate, &candidate); + return blst_fp12_is_one(&candidate); +} + PrivateKey CoreMPL::DeriveChildSk(const PrivateKey& sk, uint32_t index) { return HDKeys::DeriveChildSk(sk, index); @@ -740,7 +721,7 @@ bool AugSchemeMPL::VerifySecure(const std::vector& vecPublicKeys, G2Element PopSchemeMPL::PopProve(const PrivateKey& seckey) { - std::array> pubkey_bytes = seckey.GetG1Element().SerializeToArray(); + std::array pubkey_bytes = seckey.GetG1Element().SerializeToArray(); return seckey.SignG2( pubkey_bytes.data(), @@ -764,7 +745,7 @@ bool PopSchemeMPL::PopVerify( pubkey.ToAffine(&pubkeyAffine); signature_proof.ToAffine(&sigAffine); - std::array pubkey_bytes = pubkey.Serialize(); + std::array pubkey_bytes = pubkey.SerializeToArray(); auto err = blst_core_verify_pk_in_g1( &pubkeyAffine, @@ -876,13 +857,13 @@ bool LegacySchemeMPL::Verify(const G1Element &pubkey, const Bytes& message, cons if (!signature.IsValid() || signature == G2Element()) { return false; } - g1_t g1s[2]; - g2_t g2s[2]; + blst_p1_affine g1s[2]; + blst_p2_affine g2s[2]; - G1Element::Generator().Negate().ToNative(g1s[0]); - pubkey.ToNative(g1s[1]); - signature.ToNative(g2s[0]); - G2Element::FromMessage(message, nullptr, 0, true).ToNative(g2s[1]); + G1Element::Generator().Negate().ToAffine(&g1s[0]); + pubkey.ToAffine(&g1s[1]); + signature.ToAffine(&g2s[0]); + G2Element::FromMessage(message, nullptr, 0, true).ToAffine(&g2s[1]); return CoreMPL::NativeVerify(g1s, g2s, 2); } @@ -910,20 +891,20 @@ bool LegacySchemeMPL::AggregateVerify(const vector &pubkeys, if (!signature.IsValid() || signature == G2Element()) { return false; } - std::vector vecG1(nPubKeys + 1); - std::vector vecG2(nPubKeys + 1); - G1Element::Generator().Negate().ToNative(&vecG1[0]); - signature.ToNative(&vecG2[0]); + std::vector vecG1(nPubKeys + 1); + std::vector vecG2(nPubKeys + 1); + G1Element::Generator().Negate().ToAffine(&vecG1[0]); + signature.ToAffine(&vecG2[0]); for (size_t i = 0; i < nPubKeys; ++i) { if (!pubkeys[i].IsValid() || pubkeys[i] == G1Element()) { return false; } - pubkeys[i].ToNative(&vecG1[i + 1]); - G2Element::FromMessage(messages[i], nullptr, 0, true).ToNative(&vecG2[i + 1]); + pubkeys[i].ToAffine(&vecG1[i + 1]); + G2Element::FromMessage(messages[i], nullptr, 0, true).ToAffine(&vecG2[i + 1]); } - return CoreMPL::NativeVerify((g1_t*)vecG1.data(), (g2_t*)vecG2.data(), nPubKeys + 1); + return CoreMPL::NativeVerify(vecG1.data(), vecG2.data(), nPubKeys + 1); } } // end namespace bls From c42c4dd32a69ce27fb4340e4eb9852dd2b02345d Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Sat, 12 Sep 2026 02:13:35 +0700 Subject: [PATCH 18/36] fix: scalar byte order in point multiplication blst_p1_mult/blst_p2_mult take the scalar as little-endian bytes; upstream chia exported it big-endian, multiplying by the byte-reversed scalar. This was masked in chia by a compensating little-endian load in DeriveChildG1Unhardened, but it broke every Dash path that multiplies a point by a scalar directly: threshold shares and recovery, secure aggregation coefficients and Diffie-Hellman. Fixes the two failing Threshold test cases. --- include/dashbls/hdkeys.hpp | 8 ++++++-- src/elements.cpp | 4 ++-- src/privatekey.cpp | 6 +++--- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/include/dashbls/hdkeys.hpp b/include/dashbls/hdkeys.hpp index 3c8295454..1cadd7423 100644 --- a/include/dashbls/hdkeys.hpp +++ b/include/dashbls/hdkeys.hpp @@ -169,7 +169,9 @@ class HDKeys { Util::Hash256(digest, buf, G1Element::SIZE + 4); blst_scalar nonce; - blst_scalar_from_lendian(&nonce, digest); + // The digest is big-endian; upstream chia loaded it as little-endian, + // which only worked because operator* reversed the bytes again. + blst_scalar_from_bendian(&nonce, digest); Util::SecFree(buf); Util::SecFree(digest); @@ -190,7 +192,9 @@ class HDKeys { Util::Hash256(digest, buf, G2Element::SIZE + 4); blst_scalar nonce; - blst_scalar_from_lendian(&nonce, digest); + // The digest is big-endian; upstream chia loaded it as little-endian, + // which only worked because operator* reversed the bytes again. + blst_scalar_from_bendian(&nonce, digest); Util::SecFree(buf); Util::SecFree(digest); diff --git a/src/elements.cpp b/src/elements.cpp index 35b3bd939..8d8041c74 100644 --- a/src/elements.cpp +++ b/src/elements.cpp @@ -418,7 +418,7 @@ G1Element operator*(const G1Element& a, const blst_scalar& k) { G1Element ans; byte* bte = Util::SecAlloc(32); - blst_bendian_from_scalar(bte, &k); + blst_lendian_from_scalar(bte, &k); blst_p1_mult(&(ans.p), &(a.p), bte, 256); Util::SecFree(bte); @@ -661,7 +661,7 @@ G2Element operator*(const G2Element& a, const blst_scalar& k) { G2Element ans; byte* bte = Util::SecAlloc(32); - blst_bendian_from_scalar(bte, &k); + blst_lendian_from_scalar(bte, &k); blst_p2_mult(&(ans.q), &(a.q), bte, 256); Util::SecFree(bte); diff --git a/src/privatekey.cpp b/src/privatekey.cpp index c61ce384a..7e7eaccbe 100644 --- a/src/privatekey.cpp +++ b/src/privatekey.cpp @@ -171,7 +171,7 @@ G1Element operator*(const G1Element &a, const PrivateKey &k) blst_p1 *ans = Util::SecAlloc(1); a.ToNative(ans); byte *bte = Util::SecAlloc(32); - blst_bendian_from_scalar(bte, k.keydata); + blst_lendian_from_scalar(bte, k.keydata); blst_p1_mult(ans, ans, bte, 256); G1Element ret = G1Element::FromNative(*ans); Util::SecFree(ans); @@ -187,7 +187,7 @@ G2Element operator*(const G2Element &a, const PrivateKey &k) blst_p2 *ans = Util::SecAlloc(1); a.ToNative(ans); byte *bte = Util::SecAlloc(32); - blst_bendian_from_scalar(bte, k.keydata); + blst_lendian_from_scalar(bte, k.keydata); blst_p2_mult(ans, ans, bte, 256); G2Element ret = G2Element::FromNative(*ans); Util::SecFree(ans); @@ -213,7 +213,7 @@ G2Element PrivateKey::GetG2Power(const G2Element &element) const blst_p2 *q = Util::SecAlloc(1); element.ToNative(q); byte *bte = Util::SecAlloc(32); - blst_bendian_from_scalar(bte, keydata); + blst_lendian_from_scalar(bte, keydata); blst_p2_mult(q, q, bte, 255); const G2Element ret = G2Element::FromNative(*q); Util::SecFree(q); From e2d734fe5346861ae09099926657cd0802fa8841 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Tue, 15 Sep 2026 14:23:31 +0700 Subject: [PATCH 19/36] fix: keep relic's verification semantics instead of blst_core_verify CoreMPL::Verify, CoreMPL::AggregateVerify and PopSchemeMPL::PopVerify used blst's one-shot verify and pairing context. The relic implementation gated every element with IsValid(), rejected the identity, and then checked the product of pairings. Route all three through NativeVerify, which reproduces that exactly: one on-curve and subgroup check per element, then the pairing product. blst_core_verify_pk_in_g1 repeats the subgroup checks internally and assumes the points are on the curve, which is not what callers relied on with relic. In PopVerify the checks run before the public key is serialized and hashed to G2, in the same order as before. NativeVerify evaluates all pairs in one shared Miller loop (blst_miller_loop_n), like relic's pc_map_sim did. Same verification equation; Verification is 1.25x and Batch verification 1.17x faster in runbench, the other benchmarks are unchanged. --- include/dashbls/schemes.hpp | 5 +- src/schemes.cpp | 158 ++++++++++++++++-------------------- src/test.cpp | 22 +++++ 3 files changed, 92 insertions(+), 93 deletions(-) diff --git a/include/dashbls/schemes.hpp b/include/dashbls/schemes.hpp index dd7835c35..a7dbdf2b2 100644 --- a/include/dashbls/schemes.hpp +++ b/include/dashbls/schemes.hpp @@ -111,9 +111,8 @@ class CoreMPL { protected: const std::string& strCiphersuiteId; - static bool NativeVerify(const blst_p1_affine* pubKeys, - const blst_p2_affine* mappedHashes, - size_t length); + static bool NativeVerify(const std::vector& g1s, + const std::vector& g2s); G2Element AggregateSecure(std::vector const &vecPublicKeys, std::vector const &vecSignatures, const Bytes& message, diff --git a/src/schemes.cpp b/src/schemes.cpp index 1c141baf8..b5ec7f8a1 100644 --- a/src/schemes.cpp +++ b/src/schemes.cpp @@ -157,8 +157,9 @@ bool CoreMPL::Verify( const Bytes& message, const G2Element& signature) { - blst_p1_affine pubkeyAffine; - blst_p2_affine sigAffine; + // Keep the relic-era semantics rather than blst_core_verify: elements are + // gated by IsValid() and the check is the product of pairings. + const G2Element hashedPoint = G2Element::FromMessage(message, (const uint8_t*)strCiphersuiteId.c_str(), strCiphersuiteId.length()); if (!pubkey.IsValid() || pubkey == G1Element()) { return false; @@ -166,19 +167,10 @@ bool CoreMPL::Verify( if (!signature.IsValid() || signature == G2Element()) { return false; } - pubkey.ToAffine(&pubkeyAffine); - signature.ToAffine(&sigAffine); - auto err = blst_core_verify_pk_in_g1( - &pubkeyAffine, - &sigAffine, - true, /*hash*/ - message.begin(), - message.size(), - (const uint8_t*)strCiphersuiteId.c_str(), - strCiphersuiteId.length()); - - return err == BLST_SUCCESS; + const std::vector g1s{G1Element::Generator().Negate(), pubkey}; + const std::vector g2s{signature, hashedPoint}; + return CoreMPL::NativeVerify(g1s, g2s); } std::array CoreMPL::Aggregate(const vector> &signatures) @@ -363,60 +355,61 @@ bool CoreMPL::AggregateVerify( if (!signature.IsValid() || signature == G2Element()) { return false; } - blst_pairing* ctx = (blst_pairing*)malloc(blst_pairing_sizeof()); - blst_pairing_init( - ctx, - true /*hash*/, - (const uint8_t*)strCiphersuiteId.c_str(), - strCiphersuiteId.length()); - - blst_p1_affine pk_affine; - blst_p2_affine sig_affine; - blst_fp12 gtsig; - - signature.ToAffine(&sig_affine); + std::vector vecG1; + std::vector vecG2; + vecG1.reserve(nPubKeys + 1); + vecG2.reserve(nPubKeys + 1); + vecG1.push_back(G1Element::Generator().Negate()); + vecG2.push_back(signature); - blst_aggregated_in_g2(>sig, &sig_affine); - - for (size_t i = 0; i < nPubKeys; i++) { + for (size_t i = 0; i < nPubKeys; ++i) { if (!pubkeys[i].IsValid() || pubkeys[i] == G1Element()) { - free(ctx); - return false; - } - pubkeys[i].ToAffine(&pk_affine); - - auto err = blst_pairing_aggregate_pk_in_g1( - ctx, &pk_affine, nullptr, messages[i].begin(), messages[i].size()); - - if (err != BLST_SUCCESS) { - free(ctx); return false; } + vecG1.push_back(pubkeys[i]); + vecG2.push_back(G2Element::FromMessage(messages[i], (const uint8_t*)strCiphersuiteId.c_str(), strCiphersuiteId.length())); } - blst_pairing_commit(ctx); - auto ret = blst_pairing_finalverify(ctx, >sig); - free(ctx); - return ret; + return CoreMPL::NativeVerify(vecG1, vecG2); } -bool CoreMPL::NativeVerify(const blst_p1_affine* pubkeys, - const blst_p2_affine* mappedHashes, - size_t length) +bool CoreMPL::NativeVerify(const std::vector& g1s, + const std::vector& g2s) { - // 1 =? prod e(pubkey[i], hash[i]) + // 1 =? prod e(g1s[i], g2s[i]) // A pairing with the point at infinity on either side contributes the // identity, matching relic's pc_map_sim. - blst_fp12 candidate = *blst_fp12_one(); - - for (size_t i = 0; i < length; ++i) { - if (blst_p1_affine_is_inf(&pubkeys[i]) || - blst_p2_affine_is_inf(&mappedHashes[i])) { + if (g1s.size() != g2s.size()) { + return false; + } + std::vector ps; + std::vector qs; + ps.reserve(g1s.size()); + qs.reserve(g2s.size()); + for (size_t i = 0; i < g1s.size(); ++i) { + blst_p1 p; + blst_p2 q; + g1s[i].ToNative(&p); + g2s[i].ToNative(&q); + // Infinity is decided on the projective points: blst's affine form + // cannot tell (0, 0) apart from infinity. + if (blst_p1_is_inf(&p) || blst_p2_is_inf(&q)) { continue; } - blst_fp12 tmpPairing; - blst_miller_loop(&tmpPairing, &mappedHashes[i], &pubkeys[i]); - blst_fp12_mul(&candidate, &candidate, &tmpPairing); + blst_p1_to_affine(&ps.emplace_back(), &p); + blst_p2_to_affine(&qs.emplace_back(), &q); + } + + blst_fp12 candidate = *blst_fp12_one(); + if (!ps.empty()) { + // One shared Miller loop for all pairs, like relic's pc_map_sim. + std::vector pPtrs(ps.size()); + std::vector qPtrs(qs.size()); + for (size_t i = 0; i < ps.size(); ++i) { + pPtrs[i] = &ps[i]; + qPtrs[i] = &qs[i]; + } + blst_miller_loop_n(&candidate, qPtrs.data(), pPtrs.data(), ps.size()); } blst_final_exp(&candidate, &candidate); @@ -734,29 +727,20 @@ bool PopSchemeMPL::PopVerify( const G1Element& pubkey, const G2Element& signature_proof) { + // Same semantics as CoreMPL::Verify (and the relic implementation): the + // elements are gated by IsValid() and the check is the product of + // pairings rather than blst_core_verify. if (!pubkey.IsValid() || pubkey == G1Element()) { return false; } if (!signature_proof.IsValid() || signature_proof == G2Element()) { return false; } - blst_p1_affine pubkeyAffine; - blst_p2_affine sigAffine; - - pubkey.ToAffine(&pubkeyAffine); - signature_proof.ToAffine(&sigAffine); - std::array pubkey_bytes = pubkey.SerializeToArray(); - - auto err = blst_core_verify_pk_in_g1( - &pubkeyAffine, - &sigAffine, - true, /*hash*/ - pubkey_bytes.data(), - pubkey_bytes.size(), - (const uint8_t*)POP_CIPHERSUITE_ID.c_str(), - POP_CIPHERSUITE_ID.length()); - - return err == BLST_SUCCESS; + const std::array pubkey_bytes = pubkey.SerializeToArray(); + const G2Element hashedPoint = G2Element::FromMessage(Bytes(pubkey_bytes), (const uint8_t*)POP_CIPHERSUITE_ID.c_str(), POP_CIPHERSUITE_ID.length()); + const std::vector g1s{G1Element::Generator().Negate(), pubkey}; + const std::vector g2s{signature_proof, hashedPoint}; + return CoreMPL::NativeVerify(g1s, g2s); } bool PopSchemeMPL::PopVerify( @@ -857,15 +841,9 @@ bool LegacySchemeMPL::Verify(const G1Element &pubkey, const Bytes& message, cons if (!signature.IsValid() || signature == G2Element()) { return false; } - blst_p1_affine g1s[2]; - blst_p2_affine g2s[2]; - - G1Element::Generator().Negate().ToAffine(&g1s[0]); - pubkey.ToAffine(&g1s[1]); - signature.ToAffine(&g2s[0]); - G2Element::FromMessage(message, nullptr, 0, true).ToAffine(&g2s[1]); - - return CoreMPL::NativeVerify(g1s, g2s, 2); + const std::vector g1s{G1Element::Generator().Negate(), pubkey}; + const std::vector g2s{signature, G2Element::FromMessage(message, nullptr, 0, true)}; + return CoreMPL::NativeVerify(g1s, g2s); } G2Element LegacySchemeMPL::AggregateSecure(std::vector const &vecPublicKeys, @@ -891,20 +869,20 @@ bool LegacySchemeMPL::AggregateVerify(const vector &pubkeys, if (!signature.IsValid() || signature == G2Element()) { return false; } - std::vector vecG1(nPubKeys + 1); - std::vector vecG2(nPubKeys + 1); - G1Element::Generator().Negate().ToAffine(&vecG1[0]); - signature.ToAffine(&vecG2[0]); - + std::vector vecG1; + std::vector vecG2; + vecG1.reserve(nPubKeys + 1); + vecG2.reserve(nPubKeys + 1); + vecG1.push_back(G1Element::Generator().Negate()); + vecG2.push_back(signature); for (size_t i = 0; i < nPubKeys; ++i) { if (!pubkeys[i].IsValid() || pubkeys[i] == G1Element()) { return false; } - pubkeys[i].ToAffine(&vecG1[i + 1]); - G2Element::FromMessage(messages[i], nullptr, 0, true).ToAffine(&vecG2[i + 1]); + vecG1.push_back(pubkeys[i]); + vecG2.push_back(G2Element::FromMessage(messages[i], nullptr, 0, true)); } - - return CoreMPL::NativeVerify(vecG1.data(), vecG2.data(), nPubKeys + 1); + return CoreMPL::NativeVerify(vecG1, vecG2); } } // end namespace bls diff --git a/src/test.cpp b/src/test.cpp index e8dad4a92..3317efc28 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -2096,6 +2096,28 @@ TEST_CASE("IsValid rejects points off the curve") REQUIRE(!G2Element::FromNative(off2).IsValid()); } +TEST_CASE("Degenerate legacy signatures do not verify") +{ + // A legacy G2 decode of x = (0, 0) with no square root leaves the point + // (0, 0, 1): not infinity, but (0, 0) in affine form. IsValid() rejects + // it before the pairing product. + std::vector h(32, 0x11); + G1Element g = G1Element::Generator(); + std::vector pp(96, 0); // c0 = p, c1 = p, both read as zero + const std::vector p = Util::HexToBytes( + "1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab"); + std::copy(p.begin(), p.end(), pp.begin()); + std::copy(p.begin(), p.end(), pp.begin() + 48); + for (const std::vector& in : {std::vector(96, 0), pp}) { + G2Element bad = G2Element::FromBytes(Bytes(in), true); + REQUIRE(!LegacySchemeMPL().Verify(G1Element(), Bytes(h), bad)); + REQUIRE(!LegacySchemeMPL().Verify(g, Bytes(h), bad)); + std::vector pks{g, g.Negate()}; + std::vector msgs{Bytes(h), Bytes(h)}; + REQUIRE(!LegacySchemeMPL().AggregateVerify(pks, msgs, bad)); + } +} + TEST_CASE("CheckValid for Legacy") { SECTION("Invalid G1 points should throw in CheckValid but not in FromBytes") From d48d45fe5b45c19cd6e0154baf7501234b7b89f3 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Sat, 12 Sep 2026 02:09:29 +0700 Subject: [PATCH 20/36] perf: serialize PrivateKey without blst's temporary limb conversion blst_bendian_from_scalar converts bytes -> limbs -> bytes and then securely wipes the temporary, roughly doubling the cost of the 32-byte serialization relative to relic's bn_write_bin (11.6 ns -> 25 ns). Copy the scalar's native little-endian bytes and reverse in place instead (10.4 ns). --- src/privatekey.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/privatekey.cpp b/src/privatekey.cpp index 7e7eaccbe..6aab59c3b 100644 --- a/src/privatekey.cpp +++ b/src/privatekey.cpp @@ -14,6 +14,8 @@ #include +#include + #include "bls.hpp" #include "legacy.hpp" @@ -260,7 +262,11 @@ void PrivateKey::Serialize(uint8_t *buffer) const throw std::runtime_error("PrivateKey::Serialize buffer invalid"); } CheckKeyData(); - blst_bendian_from_scalar(buffer, keydata); + // blst_bendian_from_scalar converts through a temporary limb vector and + // securely wipes it, which costs more than the serialization itself; + // the scalar's native bytes are little-endian, so copy and reverse. + blst_lendian_from_scalar(buffer, keydata); + std::reverse(buffer, buffer + PRIVATE_KEY_SIZE); } std::vector PrivateKey::Serialize() const From 49450c5100cf46f0f2af14c360f63b2ad1833a23 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Sat, 12 Sep 2026 02:15:56 +0700 Subject: [PATCH 21/36] test: extra blst boundary checks tests Replaces the commented-out relic order test with hex-literal boundary checks (r accepted, 2r rejected) plus a cross-check that the hardcoded order agrees with blst via blst_scalar_fr_check. --- src/test.cpp | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/src/test.cpp b/src/test.cpp index 3317efc28..d042f0dae 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -129,21 +129,28 @@ TEST_CASE("class PrivateKey") Bytes(buffer, PrivateKey::PRIVATE_KEY_SIZE + 1), true)); REQUIRE_NOTHROW(PrivateKey::FromBytes( Bytes(buffer, PrivateKey::PRIVATE_KEY_SIZE), true)); - // blst_scalar order; - // memcpy(&order, BLS12_381_r, sizeof(blst_scalar)); - // g1_get_ord(order); - // bn_write_bin(buffer, PrivateKey::PRIVATE_KEY_SIZE, order); - // REQUIRE_NOTHROW(PrivateKey::FromBytes( - // Bytes(buffer, PrivateKey::PRIVATE_KEY_SIZE), false)); - // REQUIRE_NOTHROW(PrivateKey::FromBytes( - // Bytes(buffer, PrivateKey::PRIVATE_KEY_SIZE), true)); - // blst_sk_add_n_check(&order, &order, &order); - // bn_add(order, order, order); - // bn_write_bin(buffer, PrivateKey::PRIVATE_KEY_SIZE, order); - // REQUIRE_THROWS(PrivateKey::FromBytes( - // Bytes(buffer, PrivateKey::PRIVATE_KEY_SIZE), false)); - // REQUIRE_NOTHROW(PrivateKey::FromBytes( - // Bytes(buffer, PrivateKey::PRIVATE_KEY_SIZE), true)); + // the group order r itself is accepted (bn_cmp > 0 semantics) + const vector order = Util::HexToBytes( + "73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001"); + memcpy(buffer, order.data(), PrivateKey::PRIVATE_KEY_SIZE); + REQUIRE_NOTHROW(PrivateKey::FromBytes(Bytes(buffer, PrivateKey::PRIVATE_KEY_SIZE), false)); + REQUIRE_NOTHROW(PrivateKey::FromBytes(Bytes(buffer, PrivateKey::PRIVATE_KEY_SIZE), true)); + // 2 * r is above the order and must be rejected without modOrder + const vector order2 = Util::HexToBytes( + "e7db4ea6533afa906673b0101343b00aa77b4805fffcb7fdfffffffe00000002"); + memcpy(buffer, order2.data(), PrivateKey::PRIVATE_KEY_SIZE); + REQUIRE_THROWS(PrivateKey::FromBytes(Bytes(buffer, PrivateKey::PRIVATE_KEY_SIZE), false)); + REQUIRE_NOTHROW(PrivateKey::FromBytes(Bytes(buffer, PrivateKey::PRIVATE_KEY_SIZE), true)); + // The hardcoded order must agree with blst's own, which is only + // reachable through blst_scalar_fr_check (true iff below r): r itself + // is not a valid Fr element, r - 1 is. + blst_scalar s; + blst_scalar_from_bendian(&s, order.data()); + REQUIRE(!blst_scalar_fr_check(&s)); + vector orderm1 = order; + orderm1[31] = 0x00; + blst_scalar_from_bendian(&s, orderm1.data()); + REQUIRE(blst_scalar_fr_check(&s)); } SECTION("BIP32 Seed") { uint8_t aliceSeed[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; From 3c521b35a12c3a831d092f3c13b266b3848201a6 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Mon, 14 Sep 2026 00:22:49 +0700 Subject: [PATCH 22/36] build: include blst headers as "blst.h" Point the include path at blst's bindings/ directory so the library consumes blst the way it is installed (blst.h and blst_aux.h side by side), instead of relying on the layout of a blst source checkout. --- include/dashbls/elements.hpp | 2 +- include/dashbls/legacy.hpp | 2 +- include/dashbls/util.hpp | 2 +- src/CMakeLists.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/dashbls/elements.hpp b/include/dashbls/elements.hpp index 1a83f0bb6..ddfea2399 100644 --- a/include/dashbls/elements.hpp +++ b/include/dashbls/elements.hpp @@ -16,7 +16,7 @@ #define SRC_BLSELEMENTS_HPP_ extern "C" { -#include "bindings/blst.h" +#include "blst.h" } #include diff --git a/include/dashbls/legacy.hpp b/include/dashbls/legacy.hpp index c348866d6..06c3491af 100644 --- a/include/dashbls/legacy.hpp +++ b/include/dashbls/legacy.hpp @@ -6,7 +6,7 @@ #define SRC_LEGACY_HPP_ extern "C" { -#include "bindings/blst.h" +#include "blst.h" } #include diff --git a/include/dashbls/util.hpp b/include/dashbls/util.hpp index a96104267..98e3a75f2 100644 --- a/include/dashbls/util.hpp +++ b/include/dashbls/util.hpp @@ -24,7 +24,7 @@ #include extern "C" { -#include "bindings/blst.h" +#include "blst.h" } namespace bls { diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0175cb5cd..b2fc05a97 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -45,7 +45,7 @@ else() endif() add_library(dashbls ${bls_sources}) -target_include_directories(dashbls PUBLIC ${blst_SOURCE_DIR} ${PROJECT_SOURCE_DIR}/include/dashbls/) +target_include_directories(dashbls PUBLIC ${blst_SOURCE_DIR}/bindings ${PROJECT_SOURCE_DIR}/include/dashbls/) target_compile_definitions(dashbls PRIVATE __BLST_PORTABLE__ BLSALLOC_MIMALLOC=1) target_link_libraries(dashbls PUBLIC From 6f3d09f0bd65008cf203f404aba8599781424498 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Tue, 15 Sep 2026 14:23:31 +0700 Subject: [PATCH 23/36] binds: restore G1Element/G2Element multiplication by PrivateKey in Python test_unit.py exercises g1 * sk, sk * g1 and g2 * sk; the operator overloads exist in C++, only the pybind11 definitions had been dropped. --- binds/python/pythonbindings.cpp | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/binds/python/pythonbindings.cpp b/binds/python/pythonbindings.cpp index 0cc223d6e..0723e2b1b 100644 --- a/binds/python/pythonbindings.cpp +++ b/binds/python/pythonbindings.cpp @@ -517,6 +517,20 @@ PYBIND11_MODULE(dashbls, m) return self + other; }, py::is_operator()) + .def( + "__mul__", + [](G1Element &self, const PrivateKey &other) { + RelicGuard guard; + return self * other; + }, + py::is_operator()) + .def( + "__rmul__", + [](G1Element &self, const PrivateKey &other) { + RelicGuard guard; + return other * self; + }, + py::is_operator()) .def( "__and__", [](G1Element &self, G2Element &other) { @@ -629,6 +643,20 @@ PYBIND11_MODULE(dashbls, m) return self + other; }, py::is_operator()) + .def( + "__mul__", + [](G2Element &self, const PrivateKey &other) { + RelicGuard guard; + return self * other; + }, + py::is_operator()) + .def( + "__rmul__", + [](G2Element &self, const PrivateKey &other) { + RelicGuard guard; + return other * self; + }, + py::is_operator()) .def( "__repr__", [](const G2Element &ele) { From 48307a8f399c6ddfdc4846d438fe3c3d543bcd86 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Tue, 15 Sep 2026 15:16:34 +0700 Subject: [PATCH 24/36] binds: build the Go and Rust bindings against blst blst is compiled into libdashbls, so the bindings only need its headers: CMake now exports the FetchContent checkout's bindings/ directory in include_paths.txt for the Rust build script, and the Go Makefile points at the same directory. The relic and GMP include paths, link lines and CI package installs are gone. --- .github/workflows/binds-go.yml | 7 +----- .github/workflows/binds-rs.yml | 7 +----- CMakeLists.txt | 9 +------- go-bindings/Makefile | 28 ++++------------------- rust-bindings/bls-dash-sys/build.rs | 35 ----------------------------- 5 files changed, 7 insertions(+), 79 deletions(-) diff --git a/.github/workflows/binds-go.yml b/.github/workflows/binds-go.yml index ccda3c190..71a4ffe00 100644 --- a/.github/workflows/binds-go.yml +++ b/.github/workflows/binds-go.yml @@ -32,15 +32,10 @@ jobs: if: startsWith(matrix.os, 'ubuntu') run: | sudo apt-get update - sudo apt-get install -qq --yes valgrind libgmp-dev cmake + sudo apt-get install -qq --yes valgrind cmake hash -r cmake --version - - name: Prepare build system for macOS - if: startsWith(matrix.os, 'macos') - run: | - brew install gmp - - name: Build library using CMake run: | cores=$(getconf _NPROCESSORS_ONLN) diff --git a/.github/workflows/binds-rs.yml b/.github/workflows/binds-rs.yml index 8e3db323d..124d622c7 100644 --- a/.github/workflows/binds-rs.yml +++ b/.github/workflows/binds-rs.yml @@ -27,15 +27,10 @@ jobs: if: startsWith(matrix.os, 'ubuntu') run: | sudo apt-get update - sudo apt-get install -qq --yes valgrind libgmp-dev cmake + sudo apt-get install -qq --yes valgrind cmake hash -r cmake --version - - name: Prepare build system for macOS - if: startsWith(matrix.os, 'macos') - run: | - brew install gmp - - name: Install Rust run: | rustup toolchain install ${{ matrix.rust }} diff --git a/CMakeLists.txt b/CMakeLists.txt index 386f41495..b99a9f750 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,14 +67,7 @@ endif() file(APPEND "${CMAKE_CURRENT_LIST_DIR}/build/include_paths.txt" "${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES};") -if(GMP_INCLUDES) - file(APPEND "${CMAKE_CURRENT_LIST_DIR}/build/include_paths.txt" "${GMP_INCLUDES};") -endif() - -# Write gmp library path for rust binding -if(GMP_LIBRARIES) - file(APPEND "${CMAKE_CURRENT_LIST_DIR}/build/gmp_libraries.txt" "${GMP_LIBRARIES}") -endif() +file(APPEND "${CMAKE_CURRENT_LIST_DIR}/build/include_paths.txt" "${blst_SOURCE_DIR}/bindings;") if(EMSCRIPTEN) if(BUILD_BLS_JS_BINDINGS) diff --git a/go-bindings/Makefile b/go-bindings/Makefile index dcea3f24f..0c4e33b0e 100644 --- a/go-bindings/Makefile +++ b/go-bindings/Makefile @@ -12,23 +12,6 @@ BUILD_DIR=$(CURDIR)/../build CURR_DIR := $(dir $(MAKEFILE_PATH)) SRC_DIR=$(CURDIR)/../src -GMP_PREFIX := /usr/local - -ifeq ("$(OS)", "Darwin") - ifneq ($(wildcard /opt/local/bin/port),) - GMP_PREFIX := /opt/local - endif - ifeq ("$(ARCH)", "arm64") - ifneq ($(wildcard /opt/homebrew/bin/brew),) - GMP_PREFIX := /opt/homebrew - endif - endif -else ifeq ("$(OS)", "Linux") - ifneq ($(wildcard /home/linuxbrew/.linuxbrew/bin/brew),) - GMP_PREFIX := /home/linuxbrew/.linuxbrew - endif -endif - MIMALLOC_LIB := mimalloc-secure MIMALLOC_LIB_PATH := $(CURR_DIR)../build/depends/mimalloc @@ -36,18 +19,15 @@ ifneq ($(wildcard $(MIMALLOC_LIB_PATH)/libmimalloc-secure-debug.a),) MIMALLOC_LIB := mimalloc-secure-debug endif +# blst is compiled into libdashbls; its headers come from the CMake build CGO_CXXFLAGS ?= "\ --I$(CURR_DIR)../build/depends/relic/include \ +-I$(CURR_DIR)../build/_deps/blst-src/bindings \ -I$(CURR_DIR)../depends/mimalloc/include \ --I$(CURR_DIR)../depends/relic/include \ --I$(CURR_DIR)../include \ --I$(GMP_PREFIX)/include" +-I$(CURR_DIR)../include" CGO_LDFLAGS ?= "\ -L$(CURR_DIR)../build/src -ldashbls \ --L$(MIMALLOC_LIB_PATH) -l$(MIMALLOC_LIB) \ --L$(CURR_DIR)../build/depends/relic/lib -lrelic_s \ --L$(GMP_PREFIX)/lib -lgmp" +-L$(MIMALLOC_LIB_PATH) -l$(MIMALLOC_LIB)" .PHONY: default prepare fmt test cover vet help clean config diff --git a/rust-bindings/bls-dash-sys/build.rs b/rust-bindings/bls-dash-sys/build.rs index 7231004e2..ca6231d9c 100644 --- a/rust-bindings/bls-dash-sys/build.rs +++ b/rust-bindings/bls-dash-sys/build.rs @@ -138,13 +138,9 @@ fn main() { .collect(); include_paths.extend([ - bls_dash_build_path.join("depends/relic-src/include"), - bls_dash_build_path.join("depends/relic/include"), bls_dash_build_path.join("src"), root_path.join("include/dashbls"), - bls_dash_build_path.join("depends/relic/include"), bls_dash_build_path.join("depends/mimalloc/include"), - root_path.join("depends/relic/include"), root_path.join("depends/mimalloc/include"), bls_dash_src_path.clone(), ]); @@ -186,13 +182,6 @@ fn main() { cc.compile("bls-dash-sys"); - println!( - "cargo:rustc-link-search={}", - root_path.join("build/depends/relic/lib").display() - ); - - println!("cargo:rustc-link-lib=static=relic_s"); - let mimalloc_dir = root_path.join("build/depends/mimalloc"); println!("cargo:rustc-link-search={}", mimalloc_dir.display()); let mimalloc_lib = if mimalloc_dir.join("libmimalloc-secure-debug.a").exists() { @@ -209,26 +198,6 @@ fn main() { println!("cargo:rustc-link-lib=static=dashbls"); - // Link GMP if exists - let gmp_libraries_file_path = bls_dash_build_path.join("gmp_libraries.txt"); - - if gmp_libraries_file_path.exists() { - let gmp_libraries_path = PathBuf::from( - fs::read_to_string(gmp_libraries_file_path) - .expect("should read gmp includes from file"), - ); - - let gmp_libraries_parent_path = gmp_libraries_path - .parent() - .expect("can't get gmp libraries parent dir"); - - println!( - "cargo:rustc-link-search={}", - gmp_libraries_parent_path.display() - ); - - println!("cargo:rustc-link-lib=static=gmp"); - } println!("cargo:warning=########## bls_dash_build_path:{}", bls_dash_build_path.display()); // Generate rust code for c binding to src/lib.rs @@ -395,12 +364,8 @@ fn main() { .collect(); include_paths.extend([ - bls_dash_build_path.join(format!("relic-{}-{}/depends/relic-src/include", platform, arch)), - bls_dash_build_path.join(format!("relic-{}-{}/depends/relic/include", platform, arch)), - bls_dash_build_path.join("contrib/relic/src"), root_path.join("src"), root_path.join("include/dashbls"), - root_path.join("depends/relic/include"), root_path.join("depends/mimalloc/include"), root_path.join("depends/catch2/include"), bls_dash_src_path.clone(), From 45ad33805402a5751a024f3b8ebe73de43005d4e Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Tue, 15 Sep 2026 15:29:00 +0700 Subject: [PATCH 25/36] binds: build the Apple Rust dependencies with CMake apple.rust.deps.sh used to build GMP and a relic fork per architecture and then compile the library sources by hand. It now runs the project's CMake build with the ios-cmake toolchain per target and copies libdashbls.a and libmimalloc-secure.a into build/artefacts/, which is what the Rust build script links (instead of gmp, relic_s and bls). Untested here: no Apple toolchain available. --- apple.rust.deps.sh | 362 +++++----------------------- rust-bindings/bls-dash-sys/build.rs | 9 +- 2 files changed, 56 insertions(+), 315 deletions(-) diff --git a/apple.rust.deps.sh b/apple.rust.deps.sh index 01b562cf4..ad9f3b980 100755 --- a/apple.rust.deps.sh +++ b/apple.rust.deps.sh @@ -1,337 +1,87 @@ -#!/bin/sh -set -x +#!/bin/bash +# +# Builds libdashbls (with the blst backend CMake fetches) for Apple platforms, +# for consumption by rust-bindings' "apple" feature. +# +# Usage: apple.rust.deps.sh +# +# Artefacts are placed in build/artefacts//: libdashbls.a and +# libmimalloc-secure.a. The former contains the blst objects; no external +# arithmetic library (the relic-era GMP dependency is gone). + set -e -# "x86_64-apple-ios" -# "aarch64-apple-ios" -# "aarch64-apple-ios-sim" -# "x86_64-apple-darwin" -# "aarch64-apple-darwin" -# TODO: it's probably needs to be optimized in order to increase better build velocity -# TODO: so we need to combine multiple targets + TARGET=$1 -git submodule update --init -MIN_IOS="13.0" -MIN_WATCHOS="5.0" -MIN_TVOS=$MIN_IOS -MIN_MACOS="10.15" -IPHONEOS=iphoneos -IPHONESIMULATOR=iphonesimulator -WATCHOS=watchos -WATCHSIMULATOR=watchsimulator -TVOS=appletvos -TVSIMULATOR=appletvsimulator -MACOS=macosx +MACOS="macosx" +IPHONEOS="iphoneos" +IPHONESIMULATOR="iphonesimulator" -LOGICALCPU_MAX=$(sysctl -n hw.logicalcpu_max) -BUILD=build - -version_min_flag() { - PLATFORM=$1 - FLAG="" - # shellcheck disable=SC2039 - # shellcheck disable=SC2053 - if [[ $PLATFORM = $IPHONEOS ]]; then - FLAG="-miphoneos-version-min=${MIN_IOS}" - elif [[ $PLATFORM = $IPHONESIMULATOR ]]; then - FLAG="-mios-simulator-version-min=${MIN_IOS}" - elif [[ $PLATFORM = $WATCHOS ]]; then - FLAG="-mwatchos-version-min=${MIN_WATCHOS}" - elif [[ $PLATFORM = $WATCHSIMULATOR ]]; then - FLAG="-mwatchos-simulator-version-min=${MIN_WATCHOS}" - elif [[ $PLATFORM = $TVOS ]]; then - FLAG="-mtvos-version-min=${MIN_TVOS}" - elif [[ $PLATFORM = $TVSIMULATOR ]]; then - FLAG="-mtvos-simulator-version-min=${MIN_TVOS}" - elif [[ $PLATFORM = $MACOS ]]; then - FLAG="-mmacosx-version-min=${MIN_MACOS}" - fi - echo $FLAG -} +BUILD="build" +LOGICALCPU_MAX=$(sysctl -n hw.logicalcpu_max) prepare() { - download_gmp() { - GMP_VERSION="6.3.0" - CURRENT_DIR=$(pwd) - echo "$CURRENT_DIR" - # shellcheck disable=SC2039,SC2164 - pushd ${BUILD} - mkdir -p "contrib" - if [ ! -s "contrib/gmp-${GMP_VERSION}.tar.bz2" ]; then - curl -L -o "contrib/gmp-${GMP_VERSION}.tar.bz2" https://ftp.gnu.org/gnu/gmp/gmp-${GMP_VERSION}.tar.bz2 - fi - rm -rf "contrib/gmp" - # shellcheck disable=SC2039,SC2164 - pushd contrib - tar xfj "gmp-${GMP_VERSION}.tar.bz2" - mv gmp-${GMP_VERSION} gmp - # shellcheck disable=SC2039,SC2164 - popd #contrib - # shellcheck disable=SC2039,SC2164 - popd #build - } + # shellcheck disable=SC2039,SC2164 + pushd ${BUILD} download_cmake_toolchain() { - if [ ! -s "${BUILD}/ios.toolchain.cmake" ]; then + if [ ! -s "ios.toolchain.cmake" ]; then SHA256_HASH="d02857ff6bd64f1d7109ca59c3e4f3b2f89d0663c412146e6977c679801b3243" - curl -o "${BUILD}/ios.toolchain.cmake" https://raw.githubusercontent.com/leetal/ios-cmake/c55677a4445b138c9ef2650d3c21f22cc78c2357/ios.toolchain.cmake - DOWNLOADED_HASH=$(shasum -a 256 ${BUILD}/ios.toolchain.cmake | cut -f 1 -d " ") - if [ $SHA256_HASH != "$DOWNLOADED_HASH" ]; then + echo "Downloading ios.toolchain.cmake" + curl -o "ios.toolchain.cmake" https://raw.githubusercontent.com/leetal/ios-cmake/c55677a4445b138c9ef2650d3c21f22cc78c2357/ios.toolchain.cmake + DOWNLOADED_HASH=$(shasum -a 256 ios.toolchain.cmake | cut -f 1 -d " ") + if [ "$SHA256_HASH" != "$DOWNLOADED_HASH" ]; then echo "Error: sha256 checksum of ios.toolchain.cmake mismatch" >&2 exit 1 fi fi } - download_relic() { - CURRENT_DIR=$(pwd) - echo "$CURRENT_DIR" - mkdir -p "${CURRENT_DIR}/${BUILD}/depends" - if [ ! -s "${CURRENT_DIR}/${BUILD}/depends/relic" ]; then - # shellcheck disable=SC2039,SC2164 - pushd "${CURRENT_DIR}/${BUILD}/depends" - git clone --depth 1 --branch "feat/ios-support" https://github.com/pankcuf/relic - # shellcheck disable=SC2039,SC2164 - pushd relic - git fetch --depth 1 origin 19fb6d79a77ade4ae8cd70d2b0ef7aab8720d1ae - git checkout 19fb6d79a77ade4ae8cd70d2b0ef7aab8720d1ae - # shellcheck disable=SC2039,SC2164 - popd #relic - # shellcheck disable=SC2039,SC2164 - popd #depends - fi - } - rm -rf ${BUILD} - mkdir -p ${BUILD} - download_relic - download_gmp download_cmake_toolchain -} -build_gmp_arch() { - PLATFORM=$1 - ARCH=$2 - PFX=${PLATFORM}-${ARCH} - # why this works with this host only? - HOST=aarch64-apple-darwin - # shellcheck disable=SC2039,SC2164 - pushd ${BUILD} - SDK=$(xcrun --sdk "$PLATFORM" --show-sdk-path) - PLATFORM_PATH=$(xcrun --sdk "$PLATFORM" --show-sdk-platform-path) - CLANG=$(xcrun --sdk "$PLATFORM" --find clang) - DEVELOPER=$(xcode-select --print-path) - CURRENT_DIR=$(pwd) - export PATH="${PLATFORM_PATH}/Developer/usr/bin:${DEVELOPER}/usr/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/homebrew/bin" - mkdir gmplib-"${PLATFORM}"-"${ARCH}" - CFLAGS="-Wno-unused-value -fembed-bitcode -arch ${ARCH} --sysroot=${SDK} $(version_min_flag "$PLATFORM")" - CONFIGURESCRIPT="gmp_configure_script.sh" - # shellcheck disable=SC2039,SC2164 - pushd contrib - # shellcheck disable=SC2039,SC2164 - pushd gmp - make clean || true - make distclean || true - echo "HOST: $HOST" - echo "PREFIX: ${CURRENT_DIR}/gmplib-${PFX}" - - cat >"$CONFIGURESCRIPT" << EOF -#!/bin/sh -./configure \ -CC="$CLANG" CFLAGS="$CFLAGS" CPPFLAGS="$CFLAGS" LDFLAGS="$CFLAGS" \ ---host=${HOST} --prefix="${CURRENT_DIR}/gmplib-${PFX}" \ ---disable-shared --enable-static --disable-assembly -v -EOF - - chmod a+x "$CONFIGURESCRIPT" - sh "$CONFIGURESCRIPT" - rm "$CONFIGURESCRIPT" - - # shellcheck disable=SC2039 - mkdir -p "${CURRENT_DIR}/log" - # shellcheck disable=SC2039 - make -j "$LOGICALCPU_MAX" &> "${CURRENT_DIR}"/log/gmplib-"${PFX}"-build.log - # shellcheck disable=SC2039 - make install &> "${CURRENT_DIR}"/log/gmplib-"${PFX}"-install.log - #make check - #exit 1 - # shellcheck disable=SC2039,SC2164 - popd # gmp - # shellcheck disable=SC2039,SC2164 - popd # contrib # shellcheck disable=SC2039,SC2164 popd # build } -build_relic_arch() { +# Maps (platform, arch) onto the leetal/ios-cmake PLATFORM value. +cmake_platform() { PLATFORM=$1 ARCH=$2 - PFX=${PLATFORM}-${ARCH} - - # shellcheck disable=SC2039,SC2164 - pushd ${BUILD} - - SDK=$(xcrun --sdk "$PLATFORM" --show-sdk-path) - - BUILDDIR=relic-"${PFX}" - TOOLCHAIN=$(pwd)/ios.toolchain.cmake - GMP_PFX=$(pwd)/gmplib-${PFX} - rm -rf "$BUILDDIR" - mkdir "$BUILDDIR" - # shellcheck disable=SC2039,SC2164 - pushd "$BUILDDIR" - - unset CC - # shellcheck disable=SC2155 - export CC=$(xcrun --sdk "${PLATFORM}" --find clang) - - WSIZE=0 - IOS_PLATFORM="" - OPTIMIZATIONFLAGS="" - DEPLOYMENT_TARGET="" - - # shellcheck disable=SC2039 - # shellcheck disable=SC2053 - if [[ $PLATFORM = $IPHONEOS ]]; then - if [[ $ARCH = "arm64" ]] || [[ $ARCH = "arm64e" ]]; then - IOS_PLATFORM=OS64 - DEPLOYMENT_TARGET=$MIN_IOS - WSIZE=64 - OPTIMIZATIONFLAGS=-fomit-frame-pointer - else - IOS_PLATFORM=OS - WSIZE=32 - fi - elif [[ $PLATFORM = $IPHONESIMULATOR ]]; then - if [[ $ARCH = "x86_64" ]]; then - IOS_PLATFORM=SIMULATOR64 - DEPLOYMENT_TARGET=$MIN_IOS - WSIZE=64 - OPTIMIZATIONFLAGS=-fomit-frame-pointer - elif [[ $ARCH = "arm64" ]]; then - IOS_PLATFORM=SIMULATORARM64 - DEPLOYMENT_TARGET=$MIN_IOS - WSIZE=64 - else - IOS_PLATFORM=SIMULATOR - WSIZE=32 - fi - elif [[ $PLATFORM = $WATCHOS ]]; then - IOS_PLATFORM=WATCHOS - DEPLOYMENT_TARGET=$MIN_WATCHOS - WSIZE=32 - elif [[ $PLATFORM = $WATCHSIMULATOR ]]; then - IOS_PLATFORM=SIMULATOR_WATCHOS - DEPLOYMENT_TARGET=$MIN_WATCHOS - WSIZE=32 - elif [[ $PLATFORM = $TVOS ]]; then - IOS_PLATFORM=TVOS - DEPLOYMENT_TARGET=$MIN_TVOS - WSIZE=64 - OPTIMIZATIONFLAGS=-fomit-frame-pointer - elif [[ $PLATFORM = $TVSIMULATOR ]]; then - IOS_PLATFORM=SIMULATOR_TVOS - #TODO - if [[ $ARCH = "arm64" ]] - then - IOS_PLATFORM=OS64 - fi - DEPLOYMENT_TARGET=$MIN_TVOS - WSIZE=64 - OPTIMIZATIONFLAGS=-fomit-frame-pointer - elif [[ $PLATFORM = $MACOS ]]; then - WSIZE=64 - IOS_PLATFORM=MAC - if [[ $ARCH = "arm64" ]] - then - IOS_PLATFORM=MAC_ARM64 - fi - DEPLOYMENT_TARGET=$MIN_MACOS - OPTIMIZATIONFLAGS=-fomit-frame-pointer + if [[ $PLATFORM = "$IPHONEOS" ]]; then + echo "OS64" + elif [[ $PLATFORM = "$IPHONESIMULATOR" && $ARCH = "x86_64" ]]; then + echo "SIMULATOR64" + elif [[ $PLATFORM = "$IPHONESIMULATOR" && $ARCH = "arm64" ]]; then + echo "SIMULATORARM64" + elif [[ $PLATFORM = "$MACOS" && $ARCH = "x86_64" ]]; then + echo "MAC" + elif [[ $PLATFORM = "$MACOS" && $ARCH = "arm64" ]]; then + echo "MAC_ARM64" fi - - COMPILER_ARGS="$(version_min_flag "$PLATFORM") -Wno-unused-functions" - - EXTRA_ARGS="-DOPSYS=NONE -DPLATFORM=$IOS_PLATFORM -DDEPLOYMENT_TARGET=$DEPLOYMENT_TARGET -DCMAKE_TOOLCHAIN_FILE=$TOOLCHAIN" - - # shellcheck disable=SC2039 - if [[ $ARCH = "i386" ]]; then - EXTRA_ARGS+=" -DARCH=X86" - elif [[ $ARCH = "x86_64" ]]; then - EXTRA_ARGS+=" -DARCH=X64" - elif [[ $ARCH = "arm64" ]]; then - # Relic doesn't support aarch64 yet, "ARCH=ARM" is for ARM 32-bit architecture only - EXTRA_ARGS+=" -DIOS_ARCH=arm64 -DARCH=" - elif [[ $ARCH = "armv7s" ]]; then - EXTRA_ARGS+=" -DIOS_ARCH=armv7s -DARCH=ARM" - elif [[ $ARCH = "armv7k" ]]; then - EXTRA_ARGS+=" -DIOS_ARCH=armv7k -DARCH=ARM" - elif [[ $ARCH = "arm64_32" ]]; then - EXTRA_ARGS+=" -DIOS_ARCH=arm64_32 -DARCH=ARM" - fi - - CURRENT_DIR=$(pwd) - cmake -DCMAKE_PREFIX_PATH:PATH="${GMP_PFX}" -DTESTS=0 -DBENCH=0 -DBUILD_BLS_JS_BINDINGS=0 -DBUILD_BLS_PYTHON_BINDINGS=0 \ - -DBUILD_BLS_BENCHMARKS=0 -DBUILD_BLS_TESTS=0 -DCHECK=off -DARITH=gmp -DTIMER=HPROC -DFP_PRIME=381 -DMULTI=PTHREAD \ - -DFP_QNRES=on -DFP_METHD="INTEG;INTEG;INTEG;MONTY;EXGCD;SLIDE" -DFPX_METHD="INTEG;INTEG;LAZYR" -DPP_METHD="LAZYR;OATEP" \ - -DCOMP_FLAGS="-pipe -std=c99 -O3 -funroll-loops $OPTIMIZATIONFLAGS -isysroot $SDK -arch $ARCH -fembed-bitcode ${COMPILER_ARGS}" \ - -DWSIZE=$WSIZE -DVERBS=off -DSHLIB=off -DALLOC="AUTO" -DEP_PLAIN=off -DEP_SUPER=off -DPP_EXT="LAZYR" \ - -DWITH="DV;BN;MD;FP;EP;FPX;EPX;PP;PC;CP" -DBN_METHD="COMBA;COMBA;MONTY;SLIDE;STEIN;BASIC" ${EXTRA_ARGS} ../../ - - make -j "$LOGICALCPU_MAX" - # shellcheck disable=SC2039,SC2164 - popd # "$BUILDDIR" - # shellcheck disable=SC2039,SC2164 - popd # depends/relic } build_bls_arch() { - # shellcheck disable=SC2039 - BLS_FILES=( "bls" "chaincode" "elements" "extendedprivatekey" "extendedpublickey" "legacy" "privatekey" "schemes" "threshold" ) - # shellcheck disable=SC2039 - ALL_BLS_OBJ_FILES=$(printf "%s.o " "${BLS_FILES[@]}") - PLATFORM=$1 ARCH=$2 PFX=${PLATFORM}-${ARCH} - SDK=$(xcrun --sdk "$PLATFORM" --show-sdk-path) - + CMAKE_PLATFORM=$(cmake_platform "$PLATFORM" "$ARCH") BUILDDIR=${BUILD}/bls-"${PFX}" - rm -rf "$BUILDDIR" - mkdir "$BUILDDIR" - # shellcheck disable=SC2039,SC2164 - pushd "$BUILDDIR" - - EXTRA_ARGS="$(version_min_flag "$PLATFORM")" - - CURRENT_DIR=$(pwd) - - # shellcheck disable=SC2039 - for F in "${BLS_FILES[@]}" - do - clang -I"../contrib/relic/include" \ - -I"../../depends/relic/include" \ - -I"../../include/dashbls" \ - -I"../relic-${PFX}/depends/relic/include" \ - -I"../../src/" \ - -I"../gmplib-${PFX}/include" \ - -x c++ -std=c++14 -stdlib=libc++ -fembed-bitcode -arch "${ARCH}" -isysroot "${SDK}" "${EXTRA_ARGS}" \ - -c "../../src/${F}.cpp" -o "${F}.o" - done - # shellcheck disable=SC2086 - xcrun -sdk "$PLATFORM" ar -cvq libbls.a $ALL_BLS_OBJ_FILES + rm -rf "$BUILDDIR" - # shellcheck disable=SC2039,SC2164 - popd # "$BUILDDIR" -} - -build_all_arch() { - PLATFORM=$1 - ARCH=$2 - build_gmp_arch "$PLATFORM" "$ARCH" - build_relic_arch "$PLATFORM" "$ARCH" - build_bls_arch "$PLATFORM" "$ARCH" + cmake -S . -B "$BUILDDIR" \ + -DCMAKE_TOOLCHAIN_FILE="$(pwd)/${BUILD}/ios.toolchain.cmake" \ + -DPLATFORM="${CMAKE_PLATFORM}" \ + -DDEPLOYMENT_TARGET=13.0 \ + -DENABLE_BITCODE=OFF \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_BLS_TESTS=0 \ + -DBUILD_BLS_BENCHMARKS=0 \ + -DBUILD_BLS_JS_BINDINGS=0 \ + -DBUILD_BLS_PYTHON_BINDINGS=0 + + cmake --build "$BUILDDIR" --parallel "$LOGICALCPU_MAX" --target dashbls } build_target() { @@ -356,18 +106,14 @@ build_target() { ARCH=arm64 PLATFORM=$MACOS fi - build_all_arch "$PLATFORM" "$ARCH" + build_bls_arch "$PLATFORM" "$ARCH" PFX="${PLATFORM}"-"${ARCH}" rm -rf "build/artefacts/${BUILD_IN}" mkdir -p "build/artefacts/${BUILD_IN}" - cp "build/gmplib-${PFX}/lib/libgmp.a" "build/artefacts/${BUILD_IN}" - cp "build/relic-${PFX}/depends/relic/lib/librelic_s.a" "build/artefacts/${BUILD_IN}" - cp "build/bls-${PFX}/libbls.a" "build/artefacts/${BUILD_IN}" -# cp -rf build/bls-"${PFX}"/*.o build/artefacts/"${BUILD_IN}"/include -# cp -rf src/*.hpp build/artefacts/"${BUILD_IN}"/include -# cp -rf build/gmplib-"${PFX}"/include/gmp.h build/artefacts/"${BUILD_IN}"/include -# cp -rf build/relic-"${PFX}"/_deps/relic-build/include/*.h build/artefacts/"${BUILD_IN}"/include + cp "build/bls-${PFX}/src/libdashbls.a" "build/artefacts/${BUILD_IN}" + cp "build/bls-${PFX}/depends/mimalloc/libmimalloc-secure.a" "build/artefacts/${BUILD_IN}" } +mkdir -p ${BUILD} prepare build_target "$TARGET" diff --git a/rust-bindings/bls-dash-sys/build.rs b/rust-bindings/bls-dash-sys/build.rs index ca6231d9c..c514df312 100644 --- a/rust-bindings/bls-dash-sys/build.rs +++ b/rust-bindings/bls-dash-sys/build.rs @@ -386,15 +386,10 @@ fn main() { .flag("-Wno-delete-non-abstract-non-virtual-dtor") .flag("-std=c++14"); - cc.compile("dashbls"); + cc.compile("bls-dash-sys"); println!("cargo:rustc-link-search={}", target_path.display()); - println!("cargo:rustc-link-lib=static=gmp"); - //println!("cargo:rustc-link-lib=c++"); - //println!("cargo:rustc-link-lib=c"); - println!("cargo:rustc-link-lib=static=relic_s"); - println!("cargo:rustc-link-lib=static=bls"); - println!("cargo:rustc-link-search={}", bls_dash_src_path.display()); + println!("cargo:rustc-link-lib=static=mimalloc-secure"); println!("cargo:rustc-link-lib=static=dashbls"); println!("cargo:rerun-if-changed={}", bls_dash_src_path.display()); } From d59a4148d81fe32d65588725689a26dc6f40f992 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Tue, 15 Sep 2026 15:59:46 +0700 Subject: [PATCH 26/36] build: link vectorgen with emmalloc under Emscripten mimalloc's Emscripten backend calls emmalloc, which runtest and the JS bindings already link with -s MALLOC=emmalloc; vectorgen did not, so the JavaScript CI build failed at its link step. --- src/CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b2fc05a97..8132dc796 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -97,4 +97,9 @@ if(BUILD_BLS_TESTS) add_executable(vectorgen ${CMAKE_CURRENT_SOURCE_DIR}/../tools/vectorgen.cpp) target_include_directories(vectorgen PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(vectorgen PRIVATE dashbls) + + if(EMSCRIPTEN) + # mimalloc's Emscripten backend is built on emmalloc + target_link_options(vectorgen PRIVATE "SHELL:-s MALLOC=emmalloc") + endif() endif() From 1079b7f7f7b61109b29d766f8687a07493730b7f Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Wed, 16 Sep 2026 02:02:03 +0700 Subject: [PATCH 27/36] fix: legacy signing and verification require a 32-byte message hash The legacy hash-to-curve consumed exactly 32 bytes whatever length the caller passed, so a shorter message was read past its end. Pass the real length so ep2_map_legacy rejects anything but 32 bytes, check it in SignG2 before the secure allocation so nothing leaks on the throw, and sign 32-byte hashes in the two legacy HD key tests that used 8 and 10 byte seeds as messages. Dash Core only ever signs 32-byte hashes with the legacy scheme, so its behaviour is unchanged. --- .github/workflows/binds-rs.yml | 2 + .../bls-dash-sys/c-bindings/schemes.cpp | 9 ++- rust-bindings/bls-signatures/src/schemes.rs | 76 +++++++++++++++---- src/elements.cpp | 2 +- src/privatekey.cpp | 7 +- src/schemes.cpp | 8 ++ src/test.cpp | 21 ++++- 7 files changed, 101 insertions(+), 24 deletions(-) diff --git a/.github/workflows/binds-rs.yml b/.github/workflows/binds-rs.yml index 124d622c7..5656b3227 100644 --- a/.github/workflows/binds-rs.yml +++ b/.github/workflows/binds-rs.yml @@ -42,3 +42,5 @@ jobs: run: | cd rust-bindings/bls-dash-sys cargo test + cd ../bls-signatures + cargo test diff --git a/rust-bindings/bls-dash-sys/c-bindings/schemes.cpp b/rust-bindings/bls-dash-sys/c-bindings/schemes.cpp index bc2e632c8..96c0d2fa4 100644 --- a/rust-bindings/bls-dash-sys/c-bindings/schemes.cpp +++ b/rust-bindings/bls-dash-sys/c-bindings/schemes.cpp @@ -324,8 +324,13 @@ G2Element LegacySchemeMPLSign( { bls::LegacySchemeMPL* schemePtr = (bls::LegacySchemeMPL*)scheme; const bls::PrivateKey* skPtr = (bls::PrivateKey*)sk; - return new bls::G2Element( - schemePtr->Sign(*skPtr, bls::Bytes((uint8_t*)msg, msgLen))); + try { + return new bls::G2Element( + schemePtr->Sign(*skPtr, bls::Bytes((uint8_t*)msg, msgLen))); + } catch (const std::exception& ex) { + gErrMsg = ex.what(); + return nullptr; + } } bool LegacySchemeMPLVerify( diff --git a/rust-bindings/bls-signatures/src/schemes.rs b/rust-bindings/bls-signatures/src/schemes.rs index 3019bc0b4..1755bc354 100644 --- a/rust-bindings/bls-signatures/src/schemes.rs +++ b/rust-bindings/bls-signatures/src/schemes.rs @@ -1,9 +1,9 @@ -use std::ffi::c_void; +use std::ffi::{c_void, CStr}; use bls_dash_sys::{ AugSchemeMPLAggregateVerify, AugSchemeMPLFree, AugSchemeMPLSign, AugSchemeMPLVerify, BasicSchemeMPLAggregateVerify, BasicSchemeMPLFree, CoreMPLAggregatePubKeys, - CoreMPLAggregateSigs, CoreMPLSign, CoreMPLVerify, CoreMPLVerifySecure, + CoreMPLAggregateSigs, CoreMPLSign, CoreMPLVerify, CoreMPLVerifySecure, GetLastErrorMsg, LegacySchemeMPLAggregateVerify, LegacySchemeMPLSign, LegacySchemeMPLVerify, LegacySchemeMPLVerifySecure, NewAugSchemeMPL, NewBasicSchemeMPL, NewLegacySchemeMPL, }; @@ -198,16 +198,23 @@ impl Scheme for LegacySchemeMPL { } fn sign(&self, private_key: &PrivateKey, message: &[u8]) -> G2Element { - G2Element { - c_element: unsafe { - LegacySchemeMPLSign( - self.scheme, - private_key.as_mut_ptr(), - message.as_ptr() as *const _, - message.len(), - ) - }, + // The legacy scheme signs 32-byte message hashes; the C++ side throws + // for anything else, and a C++ exception must not cross the FFI. + assert_eq!(message.len(), 32, "the legacy scheme signs a 32-byte message hash"); + let c_element = unsafe { + LegacySchemeMPLSign( + self.scheme, + private_key.as_mut_ptr(), + message.as_ptr() as *const _, + message.len(), + ) + }; + // The C shim returns null when the C++ side threw. + if c_element.is_null() { + let msg = unsafe { CStr::from_ptr(GetLastErrorMsg()) }; + panic!("LegacySchemeMPLSign failed: {}", msg.to_string_lossy()); } + G2Element { c_element } } fn verify(&self, public_key: &G1Element, message: &[u8], signature: &G2Element) -> bool { @@ -383,10 +390,11 @@ mod tests { .g1_element() .expect("unable to get public key"); - let message_1 = b"ayya"; - let message_2 = b"ayyb"; - let message_3 = b"ayyc"; - let message_4 = b"ayyd"; + // 32-byte messages: the legacy scheme only signs message hashes + let message_1 = &[0x11u8; 32]; + let message_2 = &[0x22u8; 32]; + let message_3 = &[0x33u8; 32]; + let message_4 = &[0x44u8; 32]; let signature_1 = scheme.sign(&private_key_1, message_1); let signature_2 = scheme.sign(&private_key_2, message_2); @@ -431,4 +439,42 @@ mod tests { fn verify_aggregate_legacy() { verify_aggregate(LegacySchemeMPL::new()); } + + fn legacy_key_and_signature() -> (LegacySchemeMPL, G1Element, G2Element) { + let scheme = LegacySchemeMPL::new(); + let private_key = PrivateKey::key_gen(&scheme, b"seedweedseedweedseedweedseedweed") + .expect("unable to generate private key"); + let public_key = private_key.g1_element().expect("unable to get public key"); + let signature = scheme.sign(&private_key, &[0x11u8; 32]); + (scheme, public_key, signature) + } + + #[test] + #[should_panic(expected = "32-byte message hash")] + fn legacy_sign_rejects_short_message() { + let scheme = LegacySchemeMPL::new(); + let private_key = PrivateKey::key_gen(&scheme, b"seedweedseedweedseedweedseedweed") + .expect("unable to generate private key"); + scheme.sign(&private_key, &[0x11u8; 31]); + } + + #[test] + #[should_panic(expected = "32-byte message hash")] + fn legacy_sign_rejects_long_message() { + let scheme = LegacySchemeMPL::new(); + let private_key = PrivateKey::key_gen(&scheme, b"seedweedseedweedseedweedseedweed") + .expect("unable to generate private key"); + scheme.sign(&private_key, &[0x11u8; 33]); + } + + #[test] + fn legacy_verify_rejects_wrong_message_length() { + let (scheme, public_key, signature) = legacy_key_and_signature(); + assert!(scheme.verify(&public_key, &[0x11u8; 32], &signature)); + for message in [&[0x11u8; 31][..], &[0x11u8; 33][..]] { + assert!(!scheme.verify(&public_key, message, &signature)); + assert!(!scheme.verify_secure([&public_key], message, &signature)); + assert!(!scheme.aggregate_verify([&public_key], [message], &signature)); + } + } } diff --git a/src/elements.cpp b/src/elements.cpp index 8d8041c74..2f535d034 100644 --- a/src/elements.cpp +++ b/src/elements.cpp @@ -537,7 +537,7 @@ G2Element G2Element::FromMessage( { G2Element ans; if (fLegacy) { - ep2_map_legacy(&(ans.q), message.begin(), BLS::MESSAGE_HASH_LEN); + ep2_map_legacy(&(ans.q), message.begin(), static_cast(message.size())); } else { const byte* aug = nullptr; size_t aug_len = 0; diff --git a/src/privatekey.cpp b/src/privatekey.cpp index 6aab59c3b..807aaeadb 100644 --- a/src/privatekey.cpp +++ b/src/privatekey.cpp @@ -291,12 +291,13 @@ G2Element PrivateKey::SignG2( const bool fLegacy) const { CheckKeyData(); + if (fLegacy && len != BLS::MESSAGE_HASH_LEN) { + throw std::invalid_argument("legacy signing requires a 32-byte message hash"); + } blst_p2 *pt = Util::SecAlloc(1); if (fLegacy) { - // The relic implementation always mapped exactly - // BLS::MESSAGE_HASH_LEN bytes regardless of len. - ep2_map_legacy(pt, msg, BLS::MESSAGE_HASH_LEN); + ep2_map_legacy(pt, msg, static_cast(len)); } else { blst_hash_to_g2(pt, msg, len, dst, dst_len, nullptr, 0); } diff --git a/src/schemes.cpp b/src/schemes.cpp index b5ec7f8a1..506ea9eee 100644 --- a/src/schemes.cpp +++ b/src/schemes.cpp @@ -835,6 +835,10 @@ G2Element LegacySchemeMPL::Sign(const PrivateKey& seckey, const Bytes& message) bool LegacySchemeMPL::Verify(const G1Element &pubkey, const Bytes& message, const G2Element &signature) { + // the legacy scheme is defined on 32-byte message hashes + if (message.size() != BLS::MESSAGE_HASH_LEN) { + return false; + } if (!pubkey.IsValid() || pubkey == G1Element()) { return false; } @@ -876,6 +880,10 @@ bool LegacySchemeMPL::AggregateVerify(const vector &pubkeys, vecG1.push_back(G1Element::Generator().Negate()); vecG2.push_back(signature); for (size_t i = 0; i < nPubKeys; ++i) { + // the legacy scheme is defined on 32-byte message hashes + if (messages[i].size() != BLS::MESSAGE_HASH_LEN) { + return false; + } if (!pubkeys[i].IsValid() || pubkeys[i] == G1Element()) { return false; } diff --git a/src/test.cpp b/src/test.cpp index d042f0dae..8a9fb912f 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -1538,9 +1538,11 @@ TEST_CASE("Legacy HD keys") { .GetPublicKey(); REQUIRE(sk3.GetG1Element() == pk4); - G2Element sig = LegacySchemeMPL().Sign(sk3, Bytes(seed)); + // the legacy scheme signs 32-byte message hashes + std::vector hash(32, 0x42); + G2Element sig = LegacySchemeMPL().Sign(sk3, Bytes(hash)); - REQUIRE(LegacySchemeMPL().Verify(sk3.GetG1Element(), Bytes(seed), sig)); + REQUIRE(LegacySchemeMPL().Verify(sk3.GetG1Element(), Bytes(hash), sig)); } SECTION("Should prevent hardened pk derivation") { @@ -1572,7 +1574,8 @@ TEST_CASE("Legacy HD keys") { cout << epk.GetPublicKey() << endl; cout << epk.GetChainCode() << endl; - G2Element sig1 = LegacySchemeMPL().Sign(esk.GetPrivateKey(), Bytes(seed)); + std::vector hash(32, 0x42); + G2Element sig1 = LegacySchemeMPL().Sign(esk.GetPrivateKey(), Bytes(hash)); cout << sig1 << endl; } @@ -2125,6 +2128,18 @@ TEST_CASE("Degenerate legacy signatures do not verify") } } +TEST_CASE("Legacy verification rejects messages that are not 32 bytes") +{ + PrivateKey sk = BasicSchemeMPL().KeyGen(std::vector(32, 0x55)); + std::vector h(32, 0x11); + G2Element sig = LegacySchemeMPL().Sign(sk, Bytes(h)); + REQUIRE(LegacySchemeMPL().Verify(sk.GetG1Element(), Bytes(h), sig)); + std::vector h31(31, 0x11), h33(33, 0x11); + REQUIRE(!LegacySchemeMPL().Verify(sk.GetG1Element(), Bytes(h31), sig)); + REQUIRE(!LegacySchemeMPL().Verify(sk.GetG1Element(), Bytes(h33), sig)); + REQUIRE_THROWS(LegacySchemeMPL().Sign(sk, Bytes(h31))); +} + TEST_CASE("CheckValid for Legacy") { SECTION("Invalid G1 points should throw in CheckValid but not in FromBytes") From 1d142fddd2c1ce0d8183be95b3f52976d2e13d4e Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Tue, 15 Sep 2026 20:20:40 +0700 Subject: [PATCH 28/36] fix: keep md_hmac's key-derived buffers in secure memory The padded key and the inner and outer pads are derived from the HMAC key (the PRK in HKDF, the seed in KeyGen). Take them from SecAlloc, the same allocator that holds private key material, instead of the stack and malloc, so whatever policy the secure allocator implements (mimalloc's secure mode here, Dash Core's locked pool that wipes on free once it has installed its allocator) applies to them as well. --- include/dashbls/util.hpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/include/dashbls/util.hpp b/include/dashbls/util.hpp index 98e3a75f2..02ec7b387 100644 --- a/include/dashbls/util.hpp +++ b/include/dashbls/util.hpp @@ -72,12 +72,14 @@ static void md_hmac(uint8_t *mac, const uint8_t *in, size_t in_len, const uint8_ size_t key_len) { constexpr size_t block_size = 64; constexpr size_t md_len = 32; - uint8_t opad[block_size + md_len]; - uint8_t *ipad = (uint8_t *)malloc(block_size + in_len); - uint8_t _key[block_size]; - - if (ipad == NULL) + // The pads and the padded key are derived from the key, so they live in + // the same secure memory as the private keys (see SecAlloc). + uint8_t *buf = SecAlloc(block_size + md_len + block_size + in_len + block_size); + if (buf == NULL) throw std::runtime_error("out of memory"); + uint8_t *opad = buf; // block_size + md_len + uint8_t *ipad = opad + block_size + md_len; // block_size + in_len + uint8_t *_key = ipad + block_size + in_len; // block_size if (key_len > block_size) { Hash256(_key, key, key_len); @@ -96,7 +98,7 @@ static void md_hmac(uint8_t *mac, const uint8_t *in, size_t in_len, const uint8_ Hash256(opad + block_size, ipad, block_size + in_len); Hash256(mac, opad, block_size + md_len); - free(ipad); + SecFree(buf); } static std::string HexStr(const uint8_t* data, size_t len) { From d26e546a8431fbfcafdbedd3546fca5d2ddc3a83 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Mon, 21 Sep 2026 17:13:06 +0700 Subject: [PATCH 29/36] fix: GTElement::Unity() returned a default element FromNative is a static factory; calling it through an instance and dropping the result left the element default-constructed instead of the multiplicative identity. Present upstream as well; unused by Dash Core. --- src/elements.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/elements.cpp b/src/elements.cpp index 2f535d034..68ccc0021 100644 --- a/src/elements.cpp +++ b/src/elements.cpp @@ -727,9 +727,7 @@ GTElement GTElement::FromAffine(const blst_p2_affine& affine) GTElement GTElement::Unity() { - GTElement ele = GTElement(); - ele.FromNative(blst_fp12_one()); - return ele; + return GTElement::FromNative(blst_fp12_one()); } bool operator==(GTElement const& a, GTElement const& b) From 32de4b2d41fd026fa15e10089a506e192cfdfe7f Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Mon, 21 Sep 2026 17:13:06 +0700 Subject: [PATCH 30/36] test: fixed invalid point, secure-aggregation case; fuzzgen argument check The invalid G2 point test copied bytes out of the memcpy/memset/printf code, which is not portable and differs between builds; it now uses fixed garbage coordinates. New case: VerifySecure with a positive control and then one public key replaced by infinity. fuzzgen rejects an iteration count that is not a positive number instead of running zero iterations. --- src/test.cpp | 26 ++++++++++++++++++++++---- tools/fuzzgen.cpp | 9 ++++++++- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/test.cpp b/src/test.cpp index 8a9fb912f..ace7cb570 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -1734,10 +1734,10 @@ TEST_CASE("CheckValid") blst_p2 point_native; memset(&point_native, 0, sizeof(blst_p2)); - // copy some probably invalid data into the point - memcpy(&(point_native.x), (void*)memcpy, sizeof(point_native.x)); - memcpy(&(point_native.y), (void*)memset, sizeof(point_native.y)); - memcpy(&(point_native.z), (void*)printf, sizeof(point_native.z)); + // fixed garbage coordinates: not on the curve and not infinity + memset(&(point_native.x), 0x11, sizeof(point_native.x)); + memset(&(point_native.y), 0x22, sizeof(point_native.y)); + memset(&(point_native.z), 0x33, sizeof(point_native.z)); G2Element point = G2Element::FromNative(point_native); REQUIRE(point.IsValid() == false); @@ -2140,6 +2140,24 @@ TEST_CASE("Legacy verification rejects messages that are not 32 bytes") REQUIRE_THROWS(LegacySchemeMPL().Sign(sk, Bytes(h31))); } +TEST_CASE("Secure aggregation rejects an infinity public key") +{ + BasicSchemeMPL basic; + vector seed1(32, 0x11); + vector seed2(32, 0x22); + vector msg(32, 0x33); + PrivateKey sk1 = basic.KeyGen(seed1); + PrivateKey sk2 = basic.KeyGen(seed2); + vector pks = {sk1.GetG1Element(), sk2.GetG1Element()}; + vector sigs = {basic.Sign(sk1, msg), basic.Sign(sk2, msg)}; + + // positive control first, then only the second key changes + G2Element agg = basic.AggregateSecure(pks, sigs, Bytes(msg)); + REQUIRE(basic.VerifySecure(pks, agg, Bytes(msg))); + pks[1] = G1Element(); + REQUIRE(!basic.VerifySecure(pks, agg, Bytes(msg))); +} + TEST_CASE("CheckValid for Legacy") { SECTION("Invalid G1 points should throw in CheckValid but not in FromBytes") diff --git a/tools/fuzzgen.cpp b/tools/fuzzgen.cpp index 0652cc3ea..28a6bbebd 100644 --- a/tools/fuzzgen.cpp +++ b/tools/fuzzgen.cpp @@ -9,6 +9,7 @@ // consensus-facing attack surface), decode/re-encode round trips, and // legacy + basic sign/verify/aggregate cycles on derived keys. +#include #include #include #include @@ -79,7 +80,13 @@ int main(int argc, char** argv) } s0 = strtoull(argv[1], nullptr, 0) * 0x9E3779B97F4A7C15ULL + 1; s1 = s0 ^ 0xD1B54A32D192ED03ULL; - const long iters = strtol(argv[2], nullptr, 0); + char* end = nullptr; + errno = 0; + const long iters = strtol(argv[2], &end, 0); + if (errno != 0 || end == argv[2] || *end != '\0' || iters <= 0) { + fprintf(stderr, "fuzzgen: the iteration count must be a positive number\n"); + return 2; + } LegacySchemeMPL legacy; BasicSchemeMPL basic; From a9cc2d33a0cf154b54530b57244227e8ffb4a69f Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Fri, 2 Oct 2026 00:28:33 +0700 Subject: [PATCH 31/36] refactor: pass the message length to blst hash-to-group as size_t blst_hash_to_g1/g2 take size_t; narrowing message.size() to int first turns a message longer than INT_MAX into a negative length that converts back to a huge size_t. Inherited from upstream. --- include/dashbls/elements.hpp | 8 ++++---- include/dashbls/legacy.hpp | 2 +- src/elements.cpp | 14 +++++++------- src/legacy.cpp | 2 +- src/privatekey.cpp | 2 +- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/include/dashbls/elements.hpp b/include/dashbls/elements.hpp index ddfea2399..cbc4154d5 100644 --- a/include/dashbls/elements.hpp +++ b/include/dashbls/elements.hpp @@ -41,11 +41,11 @@ class G1Element { static G1Element FromMessage( const std::vector &message, const uint8_t *dst, - int dst_len); + size_t dst_len); static G1Element FromMessage( Bytes message, const uint8_t *dst, - int dst_len); + size_t dst_len); static G1Element Generator(); bool IsValid() const; @@ -86,11 +86,11 @@ class G2Element { static G2Element FromMessage( const std::vector &message, const uint8_t *dst, - int dst_len, bool legacy_encoding = false); + size_t dst_len, bool legacy_encoding = false); static G2Element FromMessage( Bytes message, const uint8_t *dst, - int dst_len, bool legacy_encoding = false); + size_t dst_len, bool legacy_encoding = false); static G2Element Generator(); bool IsValid() const; diff --git a/include/dashbls/legacy.hpp b/include/dashbls/legacy.hpp index 06c3491af..306217606 100644 --- a/include/dashbls/legacy.hpp +++ b/include/dashbls/legacy.hpp @@ -22,7 +22,7 @@ namespace bls { * @param[in] msg - the byte array to map (must be 32 bytes). * @param[in] len - the array length in bytes (must be 32). */ -void ep2_map_legacy(blst_p2 *p, const uint8_t *msg, int len); +void ep2_map_legacy(blst_p2 *p, const uint8_t *msg, size_t len); } // namespace bls #endif // #define SRC_LEGACY_HPP_ diff --git a/src/elements.cpp b/src/elements.cpp index 68ccc0021..f7fe0e5b9 100644 --- a/src/elements.cpp +++ b/src/elements.cpp @@ -277,7 +277,7 @@ G1Element G1Element::FromAffine(const blst_p1_affine& element) G1Element G1Element::FromMessage( const std::vector& message, const uint8_t* dst, - int dst_len) + size_t dst_len) { return FromMessage(Bytes(message), dst, dst_len); } @@ -285,7 +285,7 @@ G1Element G1Element::FromMessage( G1Element G1Element::FromMessage( Bytes const message, const uint8_t* dst, - int dst_len) + size_t dst_len) { G1Element ans; const byte* aug = nullptr; @@ -294,7 +294,7 @@ G1Element G1Element::FromMessage( blst_hash_to_g1( &(ans.p), message.begin(), - (int)message.size(), + message.size(), dst, dst_len, aug, @@ -523,7 +523,7 @@ G2Element G2Element::FromAffine(const blst_p2_affine& element) G2Element G2Element::FromMessage( const std::vector& message, const uint8_t* dst, - int dst_len, + size_t dst_len, const bool fLegacy) { return FromMessage(Bytes(message), dst, dst_len, fLegacy); @@ -532,12 +532,12 @@ G2Element G2Element::FromMessage( G2Element G2Element::FromMessage( Bytes const message, const uint8_t* dst, - int dst_len, + size_t dst_len, const bool fLegacy) { G2Element ans; if (fLegacy) { - ep2_map_legacy(&(ans.q), message.begin(), static_cast(message.size())); + ep2_map_legacy(&(ans.q), message.begin(), message.size()); } else { const byte* aug = nullptr; size_t aug_len = 0; @@ -545,7 +545,7 @@ G2Element G2Element::FromMessage( blst_hash_to_g2( &(ans.q), message.begin(), - (int)message.size(), + message.size(), dst, dst_len, aug, diff --git a/src/legacy.cpp b/src/legacy.cpp index 874c26b0f..a7d5832b9 100644 --- a/src/legacy.cpp +++ b/src/legacy.cpp @@ -201,7 +201,7 @@ void FpFromBE64Wide(blst_fp& out, const uint8_t* be64) } // anon namespace namespace bls { -void ep2_map_legacy(blst_p2* p, const uint8_t* msg, int len) +void ep2_map_legacy(blst_p2* p, const uint8_t* msg, size_t len) { if (len != 32) { throw std::invalid_argument( diff --git a/src/privatekey.cpp b/src/privatekey.cpp index 807aaeadb..51eb8acd9 100644 --- a/src/privatekey.cpp +++ b/src/privatekey.cpp @@ -297,7 +297,7 @@ G2Element PrivateKey::SignG2( blst_p2 *pt = Util::SecAlloc(1); if (fLegacy) { - ep2_map_legacy(pt, msg, static_cast(len)); + ep2_map_legacy(pt, msg, len); } else { blst_hash_to_g2(pt, msg, len, dst, dst_len, nullptr, 0); } From 36b2a1379f953d4cab965de3dcd02fbc0b5aa28e Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Thu, 1 Oct 2026 20:24:07 +0700 Subject: [PATCH 32/36] fix: relic's zeros-only check for basic G1 decoding relic ran the "only zeros" test on the first byte with its three flag bits masked off plus the remaining 47 bytes. The G1 path skipped the first byte, so an encoding whose only payload bits sit in the low five bits of that byte (0x85 || 47 zero bytes, x = 5 * 2^376) was rejected before decompression instead of being decompressed to an on-curve point outside the subgroup. Checked decoding rejected it either way; unchecked decoding threw where relic returned the point. Mask the first byte like the G2 decoder and relic do. --- src/elements.cpp | 2 +- src/test.cpp | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/elements.cpp b/src/elements.cpp index f7fe0e5b9..3f77b4d54 100644 --- a/src/elements.cpp +++ b/src/elements.cpp @@ -215,7 +215,7 @@ G1Element G1Element::FromBytesUnchecked(Bytes const bytes, bool fLegacy) // check if the element is canonical const uint8_t* raw_bytes = bytes.begin(); - bool fZerosOnly = + bool fZerosOnly = (raw_bytes[0] & 0x1f) == 0 && Util::HasOnlyZeros(Bytes(raw_bytes + 1, bytes.size() - 1)); if ((bytes[0] & 0xc0) == 0xc0) { // representing infinity // enforce that infinity must be 0xc0000..00 diff --git a/src/test.cpp b/src/test.cpp index ace7cb570..d6572cbe1 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -2079,6 +2079,20 @@ TEST_CASE("FastAggregateVerify rejects identity elements") } } +TEST_CASE("G1 zeros-only check masks the flag bits of the first byte") +{ + // 0x85 || 47 zero bytes encodes x = 5 * 2^376, not an all-zero payload. + // relic decompressed it to an on-curve point outside the subgroup, so the + // unchecked decoder must do the same and the checked one must reject it + // for that reason, as the G2 decoder already does. + std::vector in(48, 0); + in[0] = 0x85; + G1Element u = G1Element::FromBytesUnchecked(Bytes(in)); + REQUIRE(!u.IsValid()); + REQUIRE(u.Serialize() == in); + REQUIRE_THROWS(G1Element::FromBytes(Bytes(in))); +} + TEST_CASE("IsValid rejects points off the curve") { // (4x, 8y, z) of the generator is not on the curve but passes blst's From e3fa624af2006c3e41f348a47dc1229e91d2d766 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Fri, 2 Oct 2026 00:28:33 +0700 Subject: [PATCH 33/36] ci: drop the relic backend matrix and the GMP installs -DARITH is not read by CMake any more, so the easy/gmp dimension built every OS/compiler pair twice with identical inputs. Nothing links GMP. --- .github/workflows/build-test.yaml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-test.yaml b/.github/workflows/build-test.yaml index 98a455bc9..8b253e05e 100644 --- a/.github/workflows/build-test.yaml +++ b/.github/workflows/build-test.yaml @@ -17,7 +17,7 @@ concurrency: jobs: build: - name: ${{ matrix.os }}, ${{ matrix.compiler.cc }}, ${{ matrix.backend }} backend + name: ${{ matrix.os }}, ${{ matrix.compiler.cc }} runs-on: ${{ matrix.os }} strategy: fail-fast: false @@ -28,7 +28,6 @@ jobs: cxx: g++ - cc: clang cxx: clang++ - backend: [ easy, gmp ] steps: - name: Checkout code uses: actions/checkout@v3 @@ -37,7 +36,7 @@ jobs: if: startsWith(matrix.os, 'ubuntu') run: | sudo apt-get update - sudo apt-get install -qq --yes valgrind libgmp-dev cmake + sudo apt-get install -qq --yes valgrind cmake hash -r cmake --version @@ -45,12 +44,12 @@ jobs: if: startsWith(matrix.os, 'macos') run: | export MACOSX_DEPLOYMENT_TARGET=10.14 - brew install gmp pkg-config + brew install pkg-config - name: Build library using CMake run: | mkdir -p build && cd build - CC=${{ matrix.compiler.cc }} CXX=${{ matrix.compiler.cxx }} cmake .. -DARITH=${{ matrix.backend }} -DBUILD_BLS_PYTHON_BINDINGS=0 -DCMAKE_BUILD_TYPE=Debug + CC=${{ matrix.compiler.cc }} CXX=${{ matrix.compiler.cxx }} cmake .. -DBUILD_BLS_PYTHON_BINDINGS=0 -DCMAKE_BUILD_TYPE=Debug cmake --build . -- -j 6 mv src/runtest .. From 3c92123613801f2297b81636385e051e88793058 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Sun, 1 Nov 2026 03:21:09 +0700 Subject: [PATCH 34/36] build: use blst's portable C code on 64-bit targets without assembly build/assembly.S covers x86_64, aarch64 and 32-bit targets and stops with #error "unsupported platform" elsewhere, so riscv64 and powerpc64 did not build. With __BLST_NO_ASM__ blst compiles its C implementation and assembly.S expands to nothing. Same approach as Chia-Network/bls-signatures#429, which did this for riscv64 only. --- src/CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8132dc796..acee0b5cb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -47,6 +47,11 @@ endif() add_library(dashbls ${bls_sources}) target_include_directories(dashbls PUBLIC ${blst_SOURCE_DIR}/bindings ${PROJECT_SOURCE_DIR}/include/dashbls/) target_compile_definitions(dashbls PRIVATE __BLST_PORTABLE__ BLSALLOC_MIMALLOC=1) +if(CMAKE_SIZEOF_VOID_P EQUAL 8 AND NOT CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|amd64|AMD64|aarch64|arm64|ARM64") + # blst ships assembly for x86_64 and aarch64 only; other 64-bit targets + # (riscv64, powerpc64, ...) build its portable C code instead. + target_compile_definitions(dashbls PRIVATE __BLST_NO_ASM__) +endif() target_link_libraries(dashbls PUBLIC mimalloc-static From 1d0724ea28906f3ea4bebdb511e2011b496f9f75 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Sun, 1 Nov 2026 03:21:09 +0700 Subject: [PATCH 35/36] build: apply -fno-builtin to blst's server.c only blst's build.sh compiles its own sources with -fno-builtin. Applied directory-wide, the flag also turned every memcpy, memcmp and memset in dashbls, runtest and vectorgen into libc calls. Scope it to server.c. --- src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index acee0b5cb..02eb9ae8f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -35,7 +35,7 @@ else() list(APPEND bls_sources ${blst_SOURCE_DIR}/build/assembly.S ) - add_compile_options(-fno-builtin) + set_source_files_properties(${blst_SOURCE_DIR}/src/server.c PROPERTIES COMPILE_OPTIONS -fno-builtin) add_compile_options(-fPIC) add_compile_options(-Wall) add_compile_options(-Wextra) From eef98c6f855003986187825f8a9d7352865b9ced Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Sun, 1 Nov 2026 03:21:09 +0700 Subject: [PATCH 36/36] build: drop the relic leftovers from Python packaging GMP installs, -DARITH=easy and -DMULTI= configured relic; nothing reads them now. --- .github/workflows/binds-py.yml | 5 ----- pyproject.toml | 4 ---- setup.py | 1 - 3 files changed, 10 deletions(-) diff --git a/.github/workflows/binds-py.yml b/.github/workflows/binds-py.yml index 803fb37fa..b305a3fff 100644 --- a/.github/workflows/binds-py.yml +++ b/.github/workflows/binds-py.yml @@ -92,11 +92,6 @@ jobs: - name: Build the sdist run: uv build --sdist --out-dir dist - - name: Prepare build system - run: | - sudo apt-get update - sudo apt-get install -qq --yes libgmp-dev - - name: Install from the sdist and run the unit tests run: | uv venv .venv-sdist diff --git a/pyproject.toml b/pyproject.toml index c9b5e5526..a427ccb8e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,13 +47,9 @@ test-requires = [ ] [tool.cibuildwheel.linux] -before-all = "dnf -y install gmp-devel" manylinux-aarch64-image = "manylinux_2_28" manylinux-x86_64-image = "manylinux_2_28" -[tool.cibuildwheel.macos] -environment = { CMAKE_ARGS = "-DARITH=easy" } - [tool.pytest.ini_options] testpaths = ["binds/python"] addopts = "--benchmark-skip" diff --git a/setup.py b/setup.py index cb8f9b6b8..6b748cfe5 100644 --- a/setup.py +++ b/setup.py @@ -73,7 +73,6 @@ def build_extension(self, ext: CMakeExtension) -> None: "-DBUILD_BLS_BENCHMARKS=OFF", "-DBUILD_BLS_TESTS=OFF", "-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=" + extdir, - "-DMULTI=", "-DPYBIND11_FINDPYTHON=ON", "-DPython_EXECUTABLE=" + sys.executable, "-DVERSION_INFO=" + self.distribution.get_version(),