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()
|