Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,4 @@ blsbench.*
*.whl
venv
yarn-error.log
/.project
10 changes: 1 addition & 9 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,9 @@ ENDIF()

project(BLS)

set(BUILD_BLS_PYTHON_BINDINGS 1 CACHE INTEGER "")
set(BUILD_BLS_TESTS 1 CACHE INTEGER "")
set(BUILD_BLS_BENCHMARKS 1 CACHE INTEGER "")

message(STATUS "Build python bindings: ${BUILD_BLS_PYTHON_BINDINGS}")
message(STATUS "Build tests: ${BUILD_BLS_TESTS}")
message(STATUS "Build benchmarks: ${BUILD_BLS_BENCHMARKS}")

Expand All @@ -22,12 +20,6 @@ set(CMAKE_MODULE_PATH
${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules
)

# Make sure to statically link libsodium when building wheels on ci
if(DEFINED ENV{CIBUILDWHEEL})
set(sodium_USE_STATIC_LIBS TRUE)
message("libsodium will be statically linked because $CIBUILDWHEEL is defined.")
endif()

find_package(sodium)
if (SODIUM_FOUND)
message(STATUS "Found libsodium")
Expand All @@ -36,10 +28,10 @@ if (SODIUM_FOUND)
include_directories(${sodium_INCLUDE_DIR})
endif()

set(STBIN TRUE)
find_package(gmp)
if (GMP_FOUND)
message(STATUS "Found libgmp")
include_directories(${GMP_INCLUDE_DIR})
set(ARITH "gmp" CACHE STRING "")
else()
set(ARITH "easy" CACHE STRING "")
Expand Down
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ add_library(blstmp ${HEADERS}
${CMAKE_CURRENT_SOURCE_DIR}/bls.cpp
${CMAKE_CURRENT_SOURCE_DIR}/elements.cpp
${CMAKE_CURRENT_SOURCE_DIR}/schemes.cpp
${CMAKE_CURRENT_SOURCE_DIR}/threshold.cpp
)

set(OPREFIX object_)
Expand Down
1 change: 1 addition & 0 deletions src/bls.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "elements.hpp"
#include "hkdf.hpp"
#include "hdkeys.hpp"
#include "threshold.hpp"

namespace bls {

Expand Down
15 changes: 15 additions & 0 deletions src/privatekey.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,21 @@ 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)
{
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);
return ret;
}

PrivateKey operator*(const bn_t& a, const PrivateKey& k) { return a * k; }
Copy link

Choose a reason for hiding this comment

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

how this makes any sense? it's an infinite recursion.
a * k will call internally a * k that will call internally a * k...


G2Element PrivateKey::GetG2Power(const G2Element& element) const
{
CheckKeyData();
Expand Down
8 changes: 5 additions & 3 deletions src/privatekey.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
namespace bls {
class PrivateKey {
public:
PrivateKey();

// Private keys are represented as 32 byte field elements. Note that
// not all 32 byte integers are valid keys, the private key must be
// less than the group order (which is in bls.hpp).
Expand Down Expand Up @@ -67,6 +69,9 @@ 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);

// Serialize the key into bytes
void Serialize(uint8_t *buffer) const;
Expand All @@ -79,9 +84,6 @@ class PrivateKey {
size_t dst_len) const;

private:
// Don't allow public construction, force static methods
PrivateKey();

// Allocate memory for private key
void AllocateKeyData();
/// Throw an error if keydata isn't initialized
Expand Down
102 changes: 102 additions & 0 deletions src/schemes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// limitations under the License.

#include <algorithm>
#include <cstring>
#include <set>

#include "bls.hpp"
Expand Down Expand Up @@ -50,6 +51,35 @@ const std::string AugSchemeMPL::CIPHERSUITE_ID = "BLS_SIG_BLS12381G2_XMD:SHA-256
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_";

static void HashPubKeys(bn_t* computedTs, std::vector<Bytes> vecPubKeyBytes)
{
bn_t order;
bn_new(order);
g2_get_ord(order);

std::vector<uint8_t> vecBuffer(vecPubKeyBytes.size() * G1Element::SIZE);

for (size_t i = 0; i < vecPubKeyBytes.size(); i++) {
memcpy(vecBuffer.data() + i * G1Element::SIZE, vecPubKeyBytes[i].begin(), G1Element::SIZE);
}

uint8_t pkHash[32];
Util::Hash256(pkHash, vecBuffer.data(), vecPubKeyBytes.size() * G1Element::SIZE);
for (size_t i = 0; i < vecPubKeyBytes.size(); ++i) {
uint8_t hash[32];
uint8_t buffer[4 + 32];
memset(buffer, 0, 4);
// Set first 4 bytes to index, to generate different ts
Util::IntToFourBytes(buffer, i);
// Set next 32 bytes as the hash of all the public keys
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);
}
}

PrivateKey CoreMPL::KeyGen(const vector<uint8_t>& seed) {
return HDKeys::KeyGen(seed);
}
Expand Down Expand Up @@ -150,6 +180,78 @@ G1Element CoreMPL::Aggregate(const vector<G1Element> &publicKeys)
return aggregated;
}

G2Element CoreMPL::AggregateSecure(std::vector<G1Element> const &vecPublicKeys,
std::vector<G2Element> const &vecSignatures,
const Bytes& message) {
if (vecSignatures.size() != vecPublicKeys.size()) {
throw std::invalid_argument("AggregateSigs sigs.size() != pubKeys.size()");
}

bn_t* computedTs = new bn_t[vecPublicKeys.size()];
std::vector<std::pair<vector<uint8_t>, const G2Element*>> vecSorted(vecPublicKeys.size());
for (size_t i = 0; i < vecPublicKeys.size(); i++) {
bn_new(computedTs[i]);
vecSorted[i] = std::make_pair(vecPublicKeys[i].Serialize(), &vecSignatures[i]);
}
std::sort(vecSorted.begin(), vecSorted.end(), [](const auto& a, const auto& b) {
return std::memcmp(a.first.data(), b.first.data(), G1Element::SIZE) < 0;
});

std::vector<Bytes> vecPublicKeyBytes;
vecPublicKeyBytes.reserve(vecPublicKeys.size());
for (const auto& it : vecSorted) {
vecPublicKeyBytes.push_back(Bytes{it.first});
}

HashPubKeys(computedTs, vecPublicKeyBytes);

// Raise all signatures to power of the corresponding t's and aggregate the results into aggSig
// Also accumulates aggregation info for each signature
std::vector<G2Element> expSigs;
expSigs.reserve(vecSorted.size());
for (size_t i = 0; i < vecSorted.size(); i++) {
expSigs.emplace_back(*vecSorted[i].second * computedTs[i]);
}

G2Element aggSig = CoreMPL::Aggregate(expSigs);

delete[] computedTs;

return aggSig;
}

bool CoreMPL::VerifySecure(const std::vector<G1Element>& vecPublicKeys,
const G2Element& signature,
const Bytes& message) {
bn_t one;
bn_new(one);
bn_zero(one);
bn_set_dig(one, 1);

bn_t* computedTs = new bn_t[vecPublicKeys.size()];
std::vector<vector<uint8_t>> vecSorted(vecPublicKeys.size());
for (size_t i = 0; i < vecPublicKeys.size(); i++) {
bn_new(computedTs[i]);
vecSorted[i] = vecPublicKeys[i].Serialize();
}
std::sort(vecSorted.begin(), vecSorted.end(), [](const auto& a, const auto& b) -> bool {
return std::memcmp(a.data(), b.data(), G1Element::SIZE) < 0;
});

HashPubKeys(computedTs, {vecSorted.begin(), vecSorted.end()});

G1Element publicKey;
for (size_t i = 0; i < vecSorted.size(); ++i) {
G1Element g1 = G1Element::FromBytes(Bytes(vecSorted[i]));
publicKey = CoreMPL::Aggregate({publicKey, g1 * computedTs[i]});
}

bn_free(one);
delete[] computedTs;

return AggregateVerify({publicKey}, {message}, {signature});
}

bool CoreMPL::AggregateVerify(const vector<vector<uint8_t>> &pubkeys,
const vector<vector<uint8_t>> &messages, // unhashed
const vector<uint8_t> &signature)
Expand Down
8 changes: 8 additions & 0 deletions src/schemes.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,14 @@ class CoreMPL {
virtual G2Element Aggregate(const vector<G2Element> &signatures);

virtual G1Element Aggregate(const vector<G1Element> &publicKeys);

virtual G2Element AggregateSecure(const std::vector<G1Element>& vecPublicKeys,
const std::vector<G2Element>& vecSignatures,
const Bytes& message);

virtual bool VerifySecure(const std::vector<G1Element>& vecPublicKeys,
const G2Element& signature,
const Bytes& message);

virtual bool AggregateVerify(const vector<vector<uint8_t>> &pubkeys,
const vector<vector<uint8_t>> &messages,
Expand Down
80 changes: 80 additions & 0 deletions src/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1167,6 +1167,86 @@ TEST_CASE("Schemes") {
}
}

TEST_CASE("Threshold Signatures") {
SECTION("Secret Key Shares") {
size_t m = 3;
size_t n = 5;

std::vector<PrivateKey> sks;
std::vector<G1Element> pks;
std::vector<G2Element> sigs;
std::vector<std::vector<uint8_t>> idHashes(n);
std::vector<Bytes> ids;
std::vector<PrivateKey> skShares;
std::vector<G1Element> pkShares;
std::vector<G2Element> sigShares;

std::vector<uint8_t> vecHash = getRandomSeed();

for (size_t i = 0; i < n; i++) {
idHashes[i] = getRandomSeed();
}
ids = std::vector<Bytes>(idHashes.begin(), idHashes.end());

for (size_t i = 0; i < m; i++) {
std::vector<uint8_t> buf = getRandomSeed();

PrivateKey sk = PrivateKey::FromByteVector(buf, true);
sks.push_back(sk);
pks.push_back(sk.GetG1Element());
sigs.push_back(bls::Threshold::Sign(sk, Bytes(vecHash)));
ASSERT(bls::Threshold::Verify(sk.GetG1Element(), Bytes(vecHash), sigs.back()));
}

G2Element sig = bls::Threshold::Sign(sks[0], Bytes(vecHash));

REQUIRE(bls::Threshold::Verify({pks[0]}, Bytes{vecHash}, {sig}));

for (size_t i = 0; i < n; i++) {
PrivateKey skShare = bls::Threshold::PrivateKeyShare(sks, ids[i]);
G1Element pkShare = bls::Threshold::PublicKeyShare(pks, ids[i]);
G2Element sigShare1 = bls::Threshold::SignatureShare(sigs, ids[i]);
REQUIRE(skShare.GetG1Element() == pkShare);

G2Element sigShare2 = bls::Threshold::Sign(skShare, Bytes(vecHash));
REQUIRE(sigShare1 == sigShare2);
REQUIRE(bls::Threshold::Verify({pkShare}, Bytes(vecHash), {sigShare1}));

skShares.push_back(skShare);
pkShares.push_back(pkShare);
sigShares.push_back(sigShare1);
}

std::vector<PrivateKey> rsks;
std::vector<G1Element> rpks;
std::vector<G2Element> rsigs;
std::vector<Bytes> rids;
for (size_t i = 0; i < 2; i++) {
rsks.push_back(skShares[i]);
rpks.push_back(pkShares[i]);
rsigs.push_back(sigShares[i]);
rids.push_back(ids[i]);
}
PrivateKey recSk = bls::Threshold::PrivateKeyRecover(rsks, rids);
G1Element recPk = bls::Threshold::PublicKeyRecover(rpks, rids);
G2Element recSig = bls::Threshold::SignatureRecover(rsigs, rids);
REQUIRE(recSk != sks[0]);
REQUIRE(recPk != pks[0]);
REQUIRE(recSig != sig);

rsks.push_back(skShares[2]);
rpks.push_back(pkShares[2]);
rsigs.push_back(sigShares[2]);
rids.push_back(ids[2]);
recSk = bls::Threshold::PrivateKeyRecover(rsks, rids);
recPk = bls::Threshold::PublicKeyRecover(rpks, rids);
recSig = bls::Threshold::SignatureRecover(rsigs, rids);
REQUIRE(recSk == sks[0]);
REQUIRE(recPk == pks[0]);
REQUIRE(recSig == sig);
}
}

int main(int argc, char* argv[])
{
int result = Catch::Session().run(argc, argv);
Expand Down
Loading