LCOV - code coverage report
Current view: top level - src/test - bls_tests.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 234 234 100.0 %
Date: 2025-02-23 09:33:43 Functions: 20 20 100.0 %

          Line data    Source code
       1             : // Copyright (c) 2019-2020 The Dash Core developers
       2             : // Distributed under the MIT software license, see the accompanying
       3             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       4             : 
       5             : #include "test/test_pivx.h"
       6             : 
       7             : #include "bls/bls_batchverifier.h"
       8             : #include "bls/bls_ies.h"
       9             : #include "bls/bls_worker.h"
      10             : #include "bls/bls_wrapper.h"
      11             : #include "bls/key_io.h"
      12             : #include "random.h"
      13             : 
      14             : #include <boost/test/unit_test.hpp>
      15             : 
      16             : BOOST_FIXTURE_TEST_SUITE(bls_tests, BasicTestingSetup)
      17             : 
      18           2 : BOOST_AUTO_TEST_CASE(bls_sig_tests)
      19             : {
      20           3 :     CBLSSecretKey sk1, sk2;
      21           1 :     sk1.MakeNewKey();
      22           1 :     sk2.MakeNewKey();
      23             : 
      24           1 :     uint256 msgHash1 = uint256S("0000000000000000000000000000000000000000000000000000000000000001");
      25           1 :     uint256 msgHash2 = uint256S("0000000000000000000000000000000000000000000000000000000000000002");
      26             : 
      27           1 :     auto sig1 = sk1.Sign(msgHash1);
      28           1 :     auto sig2 = sk2.Sign(msgHash1);
      29             : 
      30           2 :     BOOST_CHECK(sig1.VerifyInsecure(sk1.GetPublicKey(), msgHash1));
      31           2 :     BOOST_CHECK(!sig1.VerifyInsecure(sk1.GetPublicKey(), msgHash2));
      32             : 
      33           2 :     BOOST_CHECK(sig2.VerifyInsecure(sk2.GetPublicKey(), msgHash1));
      34           2 :     BOOST_CHECK(!sig2.VerifyInsecure(sk2.GetPublicKey(), msgHash2));
      35             : 
      36           2 :     BOOST_CHECK(!sig1.VerifyInsecure(sk2.GetPublicKey(), msgHash1));
      37           2 :     BOOST_CHECK(!sig1.VerifyInsecure(sk2.GetPublicKey(), msgHash2));
      38           2 :     BOOST_CHECK(!sig2.VerifyInsecure(sk1.GetPublicKey(), msgHash1));
      39           1 :     BOOST_CHECK(!sig2.VerifyInsecure(sk1.GetPublicKey(), msgHash2));
      40           1 : }
      41             : 
      42           1 : static BLSIdVector GetRandomBLSIds(size_t n)
      43             : {
      44           1 :     BLSIdVector v;
      45          41 :     for (size_t i = 0; i < n; i++) {
      46          40 :         v.emplace_back(GetRandHash());
      47             :     }
      48           1 :     return v;
      49             : }
      50             : 
      51           2 : std::vector<size_t> GetRandomElements(size_t m, size_t n)
      52             : {
      53           2 :     assert(m <= n);
      54           2 :     std::vector<size_t> idxs;
      55          82 :     for (size_t i = 0; i < n; i++) {
      56          80 :         idxs.emplace_back(i);
      57             :     }
      58           2 :     Shuffle(idxs.begin(), idxs.end(), FastRandomContext());
      59           4 :     return std::vector<size_t>(idxs.begin(), idxs.begin() + m);
      60             : }
      61             : 
      62             : struct Member
      63             : {
      64             :     CBLSId id;
      65             :     BLSVerificationVectorPtr vecP;
      66             :     CBLSIESMultiRecipientObjects<CBLSSecretKey> contributions;
      67             :     CBLSSecretKey skShare;
      68             : 
      69             :     // member (operator) keys for encryption/decryption of contributions
      70             :     CBLSSecretKey sk;
      71             :     CBLSPublicKey pk;
      72             : 
      73         120 :     explicit Member(const CBLSId& _id): id(_id)
      74             :     {
      75          40 :         sk.MakeNewKey();
      76          40 :         pk = sk.GetPublicKey();
      77          40 :     }
      78             : };
      79             : 
      80           2 : BOOST_AUTO_TEST_CASE(dkg)
      81             : {
      82           1 :     CBLSWorker worker;
      83           1 :     const size_t N = 40;     // quorum size
      84           1 :     const size_t M = 30;     // threshold
      85             : 
      86           1 :     worker.Start();
      87             : 
      88             :     // Create N Members first
      89           2 :     const BLSIdVector& ids = GetRandomBLSIds(N);
      90           2 :     std::vector<Member> quorum;
      91          41 :     for (const auto& id : ids) {
      92          40 :         quorum.emplace_back(Member(id));
      93             :     }
      94             : 
      95             :     // Then generate contributions for each one
      96          41 :     for (Member& m : quorum) {
      97             :         // Generate contributions (plain text)
      98          80 :         BLSSecretKeyVector pt_contributions;
      99          40 :         worker.GenerateContributions((int)M, ids, m.vecP, pt_contributions);
     100          40 :         BOOST_CHECK_EQUAL(m.vecP->size(), M);
     101          40 :         BOOST_CHECK_EQUAL(pt_contributions.size(), N);
     102             :         // Init encrypted multi-recipient object
     103          40 :         m.contributions.InitEncrypt(N);
     104        1640 :         for (size_t j = 0; j < N; j++) {
     105        1600 :             const CBLSSecretKey& plaintext = pt_contributions[j];
     106             :             // Verify contribution against verification vector
     107        3200 :             BOOST_CHECK(worker.VerifyContributionShare(ids[j], m.vecP, plaintext));
     108             :             // Encrypt each contribution with the recipient pk
     109        3200 :             BOOST_CHECK(m.contributions.Encrypt(j, quorum[j].pk, plaintext, PROTOCOL_VERSION));
     110             :         }
     111             :     }
     112             : 
     113             :     // Aggregate received contributions for each Member to produce key shares
     114          41 :     for (size_t i = 0; i < N; i++) {
     115          40 :         Member& m = quorum[i];
     116             :         // Decrypt contributions received by m with m's secret key
     117          40 :         BLSSecretKeyVector rcvSkContributions;
     118        1640 :         for (size_t j = 0; j < N; j++) {
     119        3200 :             CBLSSecretKey contribution;
     120        3200 :             BOOST_CHECK(quorum[j].contributions.Decrypt(i, m.sk, contribution, PROTOCOL_VERSION));
     121        1600 :             rcvSkContributions.emplace_back(std::move(contribution));
     122             :         }
     123          40 :         m.skShare = worker.AggregateSecretKeys(rcvSkContributions);
     124             :         // Recover public key share for m, and check against the secret key share
     125          80 :         BLSPublicKeyVector rcvPkContributions;
     126        1640 :         for (size_t j = 0; j < N; j++) {
     127        1600 :             CBLSPublicKey pkContribution = worker.BuildPubKeyShare(quorum[j].vecP, m.id);
     128             :             // This is implied by VerifyContributionShare, but let's double check
     129        4800 :             BOOST_CHECK(rcvSkContributions[j].GetPublicKey() == pkContribution);
     130        1600 :             rcvPkContributions.emplace_back(pkContribution);
     131             :         }
     132          40 :         CBLSPublicKey pkShare = worker.AggregatePublicKeys(rcvPkContributions);
     133         120 :         BOOST_CHECK(m.skShare.GetPublicKey() == pkShare);
     134             :     }
     135             : 
     136             :     // Each member signs a message with its key share producing a signature share
     137           1 :     const uint256& msg = GetRandHash();
     138           2 :     BLSSignatureVector allSigShares;
     139          41 :     for (const Member& m : quorum) {
     140          40 :         allSigShares.emplace_back(m.skShare.Sign(msg));
     141             :     }
     142             : 
     143             :     // Pick M (random) key shares and recover threshold secret/public key
     144           2 :     const auto& idxs = GetRandomElements(M, N);
     145           2 :     BLSSecretKeyVector skShares;
     146           1 :     BLSIdVector random_ids;
     147          31 :     for (size_t i : idxs) {
     148          30 :         skShares.emplace_back(quorum[i].skShare);
     149          30 :         random_ids.emplace_back(quorum[i].id);
     150             :     }
     151           2 :     CBLSSecretKey thresholdSk;
     152           2 :     BOOST_CHECK(thresholdSk.Recover(skShares, random_ids));
     153           1 :     const CBLSPublicKey& thresholdPk = thresholdSk.GetPublicKey();
     154             : 
     155             :     // Check that the recovered threshold public key equals the verification
     156             :     // vector free coefficient
     157           2 :     std::vector<BLSVerificationVectorPtr> v;
     158          41 :     for (const Member& m : quorum) v.emplace_back(m.vecP);
     159           1 :     CBLSPublicKey pk = worker.BuildQuorumVerificationVector(v)->at(0);
     160           3 :     BOOST_CHECK(pk == thresholdPk);
     161             : 
     162             :     // Pick M (random, different BLSids than before) signature shares, and recover
     163             :     // the threshold signature
     164           2 :     const auto& idxs2 = GetRandomElements(M, N);
     165           2 :     BLSSignatureVector sigShares;
     166           1 :     BLSIdVector random_ids2;
     167          31 :     for (size_t i : idxs2) {
     168          30 :         sigShares.emplace_back(allSigShares[i]);
     169          30 :         random_ids2.emplace_back(quorum[i].id);
     170             :     }
     171           1 :     CBLSSignature thresholdSig;
     172           2 :     BOOST_CHECK(thresholdSig.Recover(sigShares, random_ids2));
     173             : 
     174             :     // Verify threshold signature against threshold public key
     175           2 :     BOOST_CHECK(thresholdSig.VerifyInsecure(thresholdPk, msg));
     176             : 
     177             :     // Now replace a signature share with an invalid signature, recover the threshold
     178             :     // signature again, and check that verification fails with the threshold public key
     179           2 :     CBLSSecretKey dummy_sk;
     180           1 :     dummy_sk.MakeNewKey();
     181           1 :     CBLSSignature dummy_sig = dummy_sk.Sign(msg);
     182           3 :     BOOST_CHECK(dummy_sig != sigShares[0]);
     183           1 :     sigShares[0] = dummy_sig;
     184           2 :     BOOST_CHECK(thresholdSig.Recover(sigShares, random_ids2));
     185           2 :     BOOST_CHECK(!thresholdSig.VerifyInsecure(thresholdPk, msg));
     186             : 
     187           1 :     worker.Stop();
     188           1 : }
     189             : 
     190           2 : BOOST_AUTO_TEST_CASE(bls_ies_tests)
     191             : {
     192             :     // Test basic encryption and decryption of the BLS Integrated Encryption Scheme.
     193           1 :     CBLSSecretKey aliceSk;
     194           1 :     aliceSk.MakeNewKey();
     195           1 :     const CBLSPublicKey alicePk = aliceSk.GetPublicKey();
     196           2 :     BOOST_CHECK(aliceSk.IsValid());
     197             : 
     198           2 :     CBLSSecretKey bobSk;
     199           1 :     bobSk.MakeNewKey();
     200           1 :     const CBLSPublicKey bobPk = bobSk.GetPublicKey();
     201           2 :     BOOST_CHECK(bobSk.IsValid());
     202             : 
     203             :     // Encrypt a std::string object
     204           2 :     CBLSIESEncryptedObject<std::string> iesEnc;
     205             : 
     206             :     // Since no pad is allowed, serialized length must be a multiple of AES_BLOCKSIZE (16)
     207           3 :     BOOST_CHECK(!iesEnc.Encrypt(bobPk, "message of length 20", PROTOCOL_VERSION));
     208             : 
     209             :     // Message of valid length (15 + 1 byte for the total len in serialization)
     210           2 :     std::string message = ".mess of len 15";
     211           2 :     BOOST_CHECK(iesEnc.Encrypt(bobPk, message, PROTOCOL_VERSION));
     212             : 
     213             :     // valid decryption.
     214           2 :     std::string decrypted_message;
     215           2 :     BOOST_CHECK(iesEnc.Decrypt(bobSk, decrypted_message, PROTOCOL_VERSION));
     216           1 :     BOOST_CHECK_EQUAL(decrypted_message, message);
     217             : 
     218             :     // Invalid decryption sk
     219           2 :     std::string decrypted_message2;
     220           1 :     iesEnc.Decrypt(aliceSk, decrypted_message2, PROTOCOL_VERSION);
     221           2 :     BOOST_CHECK(decrypted_message2 != message);
     222             : 
     223             :     // Invalid ephemeral pubkey
     224           1 :     decrypted_message2.clear();
     225           1 :     auto iesEphemeralPk = iesEnc.ephemeralPubKey;
     226           1 :     iesEnc.ephemeralPubKey = alicePk;
     227           1 :     iesEnc.Decrypt(bobSk, decrypted_message2, PROTOCOL_VERSION);
     228           2 :     BOOST_CHECK(decrypted_message2 != message);
     229           1 :     iesEnc.ephemeralPubKey = iesEphemeralPk;
     230             : 
     231             :     // Invalid iv
     232           1 :     decrypted_message2.clear();
     233           1 :     GetRandBytes(iesEnc.iv, sizeof(iesEnc.iv));
     234           1 :     iesEnc.Decrypt(bobSk, decrypted_message2, PROTOCOL_VERSION);
     235           2 :     BOOST_CHECK(decrypted_message2 != message);
     236           1 : }
     237             : 
     238             : template<typename BLSKey>
     239           2 : BLSKey FromHex(const std::string& str)
     240             : {
     241           2 :     BLSKey k;
     242           2 :     k.SetByteVector(ParseHex(str));
     243           2 :     return k;
     244             : }
     245             : 
     246           2 : BOOST_AUTO_TEST_CASE(bls_sk_io_tests)
     247             : {
     248           1 :     const auto& params = Params();
     249             : 
     250           1 :     CBLSSecretKey sk = FromHex<CBLSSecretKey>("2eb071f4c520b3102e8cb9f520783da252d33993dba0313b501d69d113af9d39");
     251           1 :     BOOST_ASSERT(sk.IsValid());
     252             : 
     253             :     // Basic encoding-decoding roundtrip
     254           2 :     std::string encodedSk = bls::EncodeSecret(params, sk);
     255           2 :     auto opSk2 = bls::DecodeSecret(params, encodedSk);
     256           2 :     BOOST_CHECK(opSk2 != nullopt);
     257           2 :     CBLSSecretKey sk2 = *opSk2;
     258           3 :     BOOST_CHECK(sk == sk2);
     259             : 
     260             :     // Invalid sk, one extra char
     261           1 :     encodedSk.push_back('f');
     262           2 :     auto opSk3 = bls::DecodeSecret(params, encodedSk);
     263           2 :     BOOST_CHECK(opSk3 == nullopt);
     264             : 
     265             :     // Invalid sk, one less char
     266           1 :     encodedSk.pop_back();
     267           1 :     encodedSk.pop_back();
     268           2 :     auto opSk4 = bls::DecodeSecret(params, encodedSk);
     269           2 :     BOOST_CHECK(opSk4 == nullopt);
     270           1 : }
     271             : 
     272           2 : BOOST_AUTO_TEST_CASE(bls_pk_io_tests)
     273             : {
     274           1 :     const auto& params = Params();
     275             : 
     276           1 :     CBLSPublicKey pk = FromHex<CBLSPublicKey>("901138a12a352c7e30408c071b1ec097f32ab735a12c8dbb43c637612a3f805668a6bb73894982366d287cf0b02aaf5b");
     277           1 :     BOOST_ASSERT(pk.IsValid());
     278             : 
     279             :     // Basic encoding-decoding roundtrip
     280           1 :     std::string encodedPk = bls::EncodePublic(params, pk);
     281           1 :     auto opPk2 = bls::DecodePublic(params, encodedPk);
     282           2 :     BOOST_CHECK(opPk2 != nullopt);
     283           1 :     CBLSPublicKey pk2 = *opPk2;
     284           3 :     BOOST_CHECK(pk == pk2);
     285             : 
     286             :     // Invalid pk, one extra char
     287           1 :     encodedPk.push_back('f');
     288           1 :     auto oppk3 = bls::DecodePublic(params, encodedPk);
     289           2 :     BOOST_CHECK(oppk3 == nullopt);
     290             : 
     291             :     // Invalid pk, one less char
     292           1 :     encodedPk.pop_back();
     293           1 :     encodedPk.pop_back();
     294           2 :     auto oppk4 = bls::DecodePublic(params, encodedPk);
     295           2 :     BOOST_CHECK(oppk4 == nullopt);
     296           1 : }
     297             : 
     298          47 : struct Message {
     299             :     uint32_t sourceId;
     300             :     uint32_t msgId;
     301             :     uint256 msgHash;
     302             :     CBLSSecretKey sk;
     303             :     CBLSPublicKey pk;
     304             :     CBLSSignature sig;
     305             :     bool valid;
     306             : };
     307             : 
     308          16 : static void AddMessage(std::vector<Message>& vec, uint32_t sourceId, uint32_t msgId, uint32_t msgHash, bool valid)
     309             : {
     310          16 :     Message m;
     311          16 :     m.sourceId = sourceId;
     312          16 :     m.msgId = msgId;
     313          16 :     *((uint32_t*)m.msgHash.begin()) = msgHash;
     314          16 :     m.sk.MakeNewKey();
     315          16 :     m.pk = m.sk.GetPublicKey();
     316          16 :     m.sig = m.sk.Sign(m.msgHash);
     317          16 :     m.valid = valid;
     318             : 
     319          16 :     if (!valid) {
     320           4 :         CBLSSecretKey tmp;
     321           2 :         tmp.MakeNewKey();
     322           2 :         m.sig = tmp.Sign(m.msgHash);
     323             :     }
     324             : 
     325          16 :     vec.emplace_back(m);
     326          16 : }
     327             : 
     328          28 : static void Verify(std::vector<Message>& vec, bool secureVerification, bool perMessageFallback)
     329             : {
     330          28 :     CBLSBatchVerifier<uint32_t, uint32_t> batchVerifier(secureVerification, perMessageFallback);
     331             : 
     332          28 :     std::set<uint32_t> expectedBadMessages;
     333          28 :     std::set<uint32_t> expectedBadSources;
     334         212 :     for (auto& m : vec) {
     335         184 :         if (!m.valid) {
     336          16 :             expectedBadMessages.emplace(m.msgId);
     337          16 :             expectedBadSources.emplace(m.sourceId);
     338             :         }
     339             : 
     340         184 :         batchVerifier.PushMessage(m.sourceId, m.msgId, m.msgHash, m.sig, m.pk);
     341             :     }
     342             : 
     343          28 :     batchVerifier.Verify();
     344             : 
     345          56 :     BOOST_CHECK(batchVerifier.badSources == expectedBadSources);
     346             : 
     347          28 :     if (perMessageFallback) {
     348          28 :         BOOST_CHECK(batchVerifier.badMessages == expectedBadMessages);
     349             :     } else {
     350          28 :         BOOST_CHECK(batchVerifier.badMessages.empty());
     351             :     }
     352          28 : }
     353             : 
     354           7 : static void Verify(std::vector<Message>& vec)
     355             : {
     356           7 :     Verify(vec, false, false);
     357           7 :     Verify(vec, true, false);
     358           7 :     Verify(vec, false, true);
     359           7 :     Verify(vec, true, true);
     360           7 : }
     361             : 
     362           2 : BOOST_AUTO_TEST_CASE(batch_verifier_tests)
     363             : {
     364           2 :     std::vector<Message> msgs;
     365             : 
     366             :     // distinct messages from distinct sources
     367           1 :     AddMessage(msgs, 1, 1, 1, true);
     368           1 :     AddMessage(msgs, 2, 2, 2, true);
     369           1 :     AddMessage(msgs, 3, 3, 3, true);
     370           1 :     Verify(msgs);
     371             : 
     372             :     // distinct messages from same source
     373           1 :     AddMessage(msgs, 4, 4, 4, true);
     374           1 :     AddMessage(msgs, 4, 5, 5, true);
     375           1 :     AddMessage(msgs, 4, 6, 6, true);
     376           1 :     Verify(msgs);
     377             : 
     378             :     // invalid sig
     379           1 :     AddMessage(msgs, 7, 7, 7, false);
     380           1 :     Verify(msgs);
     381             : 
     382             :     // same message as before, but from another source and with valid sig
     383           1 :     AddMessage(msgs, 8, 8, 7, true);
     384           1 :     Verify(msgs);
     385             : 
     386             :     // same message as before, but from another source and signed with another key
     387           1 :     AddMessage(msgs, 9, 9, 7, true);
     388           1 :     Verify(msgs);
     389             : 
     390           1 :     msgs.clear();
     391             :     // same message, signed by multiple keys
     392           1 :     AddMessage(msgs, 1, 1, 1, true);
     393           1 :     AddMessage(msgs, 1, 2, 1, true);
     394           1 :     AddMessage(msgs, 1, 3, 1, true);
     395           1 :     AddMessage(msgs, 2, 4, 1, true);
     396           1 :     AddMessage(msgs, 2, 5, 1, true);
     397           1 :     AddMessage(msgs, 2, 6, 1, true);
     398           1 :     Verify(msgs);
     399             : 
     400             :     // last message invalid from one source
     401           1 :     AddMessage(msgs, 1, 7, 1, false);
     402           1 :     Verify(msgs);
     403           1 : }
     404             : 
     405             : BOOST_AUTO_TEST_SUITE_END()

Generated by: LCOV version 1.14