]> Git Repo - VerusCoin.git/blob - src/gtest/test_noteencryption.cpp
Build fix
[VerusCoin.git] / src / gtest / test_noteencryption.cpp
1 #include <gtest/gtest.h>
2 #include "sodium.h"
3
4 #include <array>
5 #include <stdexcept>
6
7 #include "zcash/Note.hpp"
8 #include "zcash/NoteEncryption.hpp"
9 #include "zcash/prf.h"
10 #include "zcash/Address.hpp"
11 #include "crypto/sha256.h"
12 #include "librustzcash.h"
13
14 class TestNoteDecryption : public ZCNoteDecryption {
15 public:
16     TestNoteDecryption(uint256 sk_enc) : ZCNoteDecryption(sk_enc) {}
17
18     void change_pk_enc(uint256 to) {
19         pk_enc = to;
20     }
21 };
22
23 TEST(noteencryption, NotePlaintext)
24 {
25     using namespace libzcash;
26     auto xsk = SaplingSpendingKey(uint256()).expanded_spending_key();
27     auto fvk = xsk.full_viewing_key();
28     auto ivk = fvk.in_viewing_key();
29     SaplingPaymentAddress addr = *ivk.address({0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0});
30
31     std::array<unsigned char, ZC_MEMO_SIZE> memo;
32     for (size_t i = 0; i < ZC_MEMO_SIZE; i++) {
33         // Fill the message with dummy data
34         memo[i] = (unsigned char) i;
35     }
36
37     SaplingNote note(addr, 39393);
38     auto cmu_opt = note.cm();
39     if (!cmu_opt) {
40         FAIL();
41     }
42     uint256 cmu = cmu_opt.get();
43     SaplingNotePlaintext pt(note, memo);
44
45     auto res = pt.encrypt(addr.pk_d);
46     if (!res) {
47         FAIL();
48     }
49
50     auto enc = res.get();
51
52     auto ct = enc.first;
53     auto encryptor = enc.second;
54     auto epk = encryptor.get_epk();
55
56     // Try to decrypt with incorrect commitment
57     ASSERT_FALSE(SaplingNotePlaintext::decrypt(
58         ct,
59         ivk,
60         epk,
61         uint256()
62     ));
63
64     // Try to decrypt with correct commitment
65     auto foo = SaplingNotePlaintext::decrypt(
66         ct,
67         ivk,
68         epk,
69         cmu
70     );
71
72     if (!foo) {
73         FAIL();
74     }
75
76     auto bar = foo.get();
77
78     ASSERT_TRUE(bar.value() == pt.value());
79     ASSERT_TRUE(bar.memo() == pt.memo());
80     ASSERT_TRUE(bar.d == pt.d);
81     ASSERT_TRUE(bar.rcm == pt.rcm);
82
83     auto foobar = bar.note(ivk);
84
85     if (!foobar) {
86         FAIL();
87     }
88
89     auto new_note = foobar.get();
90
91     ASSERT_TRUE(note.value() == new_note.value());
92     ASSERT_TRUE(note.d == new_note.d);
93     ASSERT_TRUE(note.pk_d == new_note.pk_d);
94     ASSERT_TRUE(note.r == new_note.r);
95     ASSERT_TRUE(note.cm() == new_note.cm());
96
97     SaplingOutgoingPlaintext out_pt;
98     out_pt.pk_d = note.pk_d;
99     out_pt.esk = encryptor.get_esk();
100
101     auto ovk = random_uint256();
102     auto cv = random_uint256();
103     auto cm = random_uint256();
104
105     auto out_ct = out_pt.encrypt(
106         ovk,
107         cv,
108         cm,
109         encryptor
110     );
111
112     auto decrypted_out_ct = out_pt.decrypt(
113         out_ct,
114         ovk,
115         cv,
116         cm,
117         encryptor.get_epk()
118     );
119
120     if (!decrypted_out_ct) {
121         FAIL();
122     }
123
124     auto decrypted_out_ct_unwrapped = decrypted_out_ct.get();
125
126     ASSERT_TRUE(decrypted_out_ct_unwrapped.pk_d == out_pt.pk_d);
127     ASSERT_TRUE(decrypted_out_ct_unwrapped.esk == out_pt.esk);
128
129     // Test sender won't accept invalid commitments
130     ASSERT_FALSE(
131         SaplingNotePlaintext::decrypt(
132             ct,
133             epk,
134             decrypted_out_ct_unwrapped.esk,
135             decrypted_out_ct_unwrapped.pk_d,
136             uint256()
137         )
138     );
139
140     // Test sender can decrypt the note ciphertext.
141     foo = SaplingNotePlaintext::decrypt(
142         ct,
143         epk,
144         decrypted_out_ct_unwrapped.esk,
145         decrypted_out_ct_unwrapped.pk_d,
146         cmu
147     );
148
149     if (!foo) {
150         FAIL();
151     }
152
153     bar = foo.get();
154
155     ASSERT_TRUE(bar.value() == pt.value());
156     ASSERT_TRUE(bar.memo() == pt.memo());
157     ASSERT_TRUE(bar.d == pt.d);
158     ASSERT_TRUE(bar.rcm == pt.rcm);
159 }
160
161 TEST(noteencryption, SaplingApi)
162 {
163     using namespace libzcash;
164
165     // Create recipient addresses
166     auto sk = SaplingSpendingKey(uint256()).expanded_spending_key();
167     auto vk = sk.full_viewing_key();
168     auto ivk = vk.in_viewing_key();
169     SaplingPaymentAddress pk_1 = *ivk.address({0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0});
170     SaplingPaymentAddress pk_2 = *ivk.address({4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0});
171
172     // Blob of stuff we're encrypting
173     std::array<unsigned char, ZC_SAPLING_ENCPLAINTEXT_SIZE> message;
174     for (size_t i = 0; i < ZC_SAPLING_ENCPLAINTEXT_SIZE; i++) {
175         // Fill the message with dummy data
176         message[i] = (unsigned char) i;
177     }
178
179     std::array<unsigned char, ZC_SAPLING_OUTPLAINTEXT_SIZE> small_message;
180     for (size_t i = 0; i < ZC_SAPLING_OUTPLAINTEXT_SIZE; i++) {
181         // Fill the message with dummy data
182         small_message[i] = (unsigned char) i;
183     }
184
185     // Invalid diversifier
186     ASSERT_EQ(boost::none, SaplingNoteEncryption::FromDiversifier({1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}));
187
188     // Encrypt to pk_1
189     auto enc = *SaplingNoteEncryption::FromDiversifier(pk_1.d);
190     auto ciphertext_1 = *enc.encrypt_to_recipient(
191         pk_1.pk_d,
192         message
193     );
194     auto epk_1 = enc.get_epk();
195     {
196         uint256 test_epk;
197         uint256 test_esk = enc.get_esk();
198         ASSERT_TRUE(librustzcash_sapling_ka_derivepublic(pk_1.d.begin(), test_esk.begin(), test_epk.begin()));
199         ASSERT_TRUE(test_epk == epk_1);
200     }
201     auto cv_1 = random_uint256();
202     auto cm_1 = random_uint256();
203     auto out_ciphertext_1 = enc.encrypt_to_ourselves(
204         sk.ovk,
205         cv_1,
206         cm_1,
207         small_message
208     );
209
210     // Encrypt to pk_2
211     enc = *SaplingNoteEncryption::FromDiversifier(pk_2.d);
212     auto ciphertext_2 = *enc.encrypt_to_recipient(
213         pk_2.pk_d,
214         message
215     );
216     auto epk_2 = enc.get_epk();
217
218     auto cv_2 = random_uint256();
219     auto cm_2 = random_uint256();
220     auto out_ciphertext_2 = enc.encrypt_to_ourselves(
221         sk.ovk,
222         cv_2,
223         cm_2,
224         small_message
225     );
226
227     // Test nonce-reuse resistance of API
228     {
229         auto tmp_enc = *SaplingNoteEncryption::FromDiversifier(pk_1.d);
230         
231         tmp_enc.encrypt_to_recipient(
232             pk_1.pk_d,
233             message
234         );
235
236         ASSERT_THROW(tmp_enc.encrypt_to_recipient(
237             pk_1.pk_d,
238             message
239         ), std::logic_error);
240
241         tmp_enc.encrypt_to_ourselves(
242             sk.ovk,
243             cv_2,
244             cm_2,
245             small_message
246         );
247
248         ASSERT_THROW(tmp_enc.encrypt_to_ourselves(
249             sk.ovk,
250             cv_2,
251             cm_2,
252             small_message
253         ), std::logic_error);
254     }
255
256     // Try to decrypt
257     auto plaintext_1 = *AttemptSaplingEncDecryption(
258         ciphertext_1,
259         ivk,
260         epk_1
261     );
262     ASSERT_TRUE(message == plaintext_1);
263
264     auto small_plaintext_1 = *AttemptSaplingOutDecryption(
265         out_ciphertext_1,
266         sk.ovk,
267         cv_1,
268         cm_1,
269         epk_1
270     );
271     ASSERT_TRUE(small_message == small_plaintext_1);
272
273     auto plaintext_2 = *AttemptSaplingEncDecryption(
274         ciphertext_2,
275         ivk,
276         epk_2
277     );
278     ASSERT_TRUE(message == plaintext_2);
279
280     auto small_plaintext_2 = *AttemptSaplingOutDecryption(
281         out_ciphertext_2,
282         sk.ovk,
283         cv_2,
284         cm_2,
285         epk_2
286     );
287     ASSERT_TRUE(small_message == small_plaintext_2);
288
289     // Try to decrypt out ciphertext with wrong key material
290     ASSERT_FALSE(AttemptSaplingOutDecryption(
291         out_ciphertext_1,
292         random_uint256(),
293         cv_1,
294         cm_1,
295         epk_1
296     ));
297     ASSERT_FALSE(AttemptSaplingOutDecryption(
298         out_ciphertext_1,
299         sk.ovk,
300         random_uint256(),
301         cm_1,
302         epk_1
303     ));
304     ASSERT_FALSE(AttemptSaplingOutDecryption(
305         out_ciphertext_1,
306         sk.ovk,
307         cv_1,
308         random_uint256(),
309         epk_1
310     ));
311     ASSERT_FALSE(AttemptSaplingOutDecryption(
312         out_ciphertext_1,
313         sk.ovk,
314         cv_1,
315         cm_1,
316         random_uint256()
317     ));
318
319     // Try to decrypt with wrong ephemeral key
320     ASSERT_FALSE(AttemptSaplingEncDecryption(
321         ciphertext_1,
322         ivk,
323         epk_2
324     ));
325     ASSERT_FALSE(AttemptSaplingEncDecryption(
326         ciphertext_2,
327         ivk,
328         epk_1
329     ));
330
331     // Try to decrypt with wrong ivk
332     ASSERT_FALSE(AttemptSaplingEncDecryption(
333         ciphertext_1,
334         uint256(),
335         epk_1
336     ));
337     ASSERT_FALSE(AttemptSaplingEncDecryption(
338         ciphertext_2,
339         uint256(),
340         epk_2
341     ));
342 }
343
344 TEST(noteencryption, api)
345 {
346     uint256 sk_enc = ZCNoteEncryption::generate_privkey(uint252(uint256S("21035d60bc1983e37950ce4803418a8fb33ea68d5b937ca382ecbae7564d6a07")));
347     uint256 pk_enc = ZCNoteEncryption::generate_pubkey(sk_enc);
348
349     ZCNoteEncryption b = ZCNoteEncryption(uint256());
350     for (size_t i = 0; i < 100; i++)
351     {
352         ZCNoteEncryption c = ZCNoteEncryption(uint256());
353
354         ASSERT_TRUE(b.get_epk() != c.get_epk());
355     }
356
357     std::array<unsigned char, ZC_NOTEPLAINTEXT_SIZE> message;
358     for (size_t i = 0; i < ZC_NOTEPLAINTEXT_SIZE; i++) {
359         // Fill the message with dummy data
360         message[i] = (unsigned char) i;
361     }
362
363     for (int i = 0; i < 255; i++) {
364         auto ciphertext = b.encrypt(pk_enc, message);
365
366         {
367             ZCNoteDecryption decrypter(sk_enc);
368
369             // Test decryption
370             auto plaintext = decrypter.decrypt(ciphertext, b.get_epk(), uint256(), i);
371             ASSERT_TRUE(plaintext == message);
372
373             // Test wrong nonce
374             ASSERT_THROW(decrypter.decrypt(ciphertext, b.get_epk(), uint256(), (i == 0) ? 1 : (i - 1)),
375                          libzcash::note_decryption_failed);
376         
377             // Test wrong ephemeral key
378             {
379                 ZCNoteEncryption c = ZCNoteEncryption(uint256());
380
381                 ASSERT_THROW(decrypter.decrypt(ciphertext, c.get_epk(), uint256(), i),
382                              libzcash::note_decryption_failed);
383             }
384         
385             // Test wrong seed
386             ASSERT_THROW(decrypter.decrypt(ciphertext, b.get_epk(), uint256S("11035d60bc1983e37950ce4803418a8fb33ea68d5b937ca382ecbae7564d6a77"), i),
387                          libzcash::note_decryption_failed);
388         
389             // Test corrupted ciphertext
390             ciphertext[10] ^= 0xff;
391             ASSERT_THROW(decrypter.decrypt(ciphertext, b.get_epk(), uint256(), i),
392                          libzcash::note_decryption_failed);
393             ciphertext[10] ^= 0xff;
394         }
395
396         {
397             // Test wrong private key
398             uint256 sk_enc_2 = ZCNoteEncryption::generate_privkey(uint252());
399             ZCNoteDecryption decrypter(sk_enc_2);
400
401             ASSERT_THROW(decrypter.decrypt(ciphertext, b.get_epk(), uint256(), i),
402                          libzcash::note_decryption_failed);
403         }
404
405         {
406             TestNoteDecryption decrypter(sk_enc);
407
408             // Test decryption
409             auto plaintext = decrypter.decrypt(ciphertext, b.get_epk(), uint256(), i);
410             ASSERT_TRUE(plaintext == message);
411
412             // Test wrong public key (test of KDF)
413             decrypter.change_pk_enc(uint256());
414             ASSERT_THROW(decrypter.decrypt(ciphertext, b.get_epk(), uint256(), i),
415                          libzcash::note_decryption_failed);
416         }
417     }
418
419     // Nonce space should run out here
420     try {
421         b.encrypt(pk_enc, message);
422         FAIL() << "Expected std::logic_error";
423     }
424     catch(std::logic_error const & err) {
425         EXPECT_EQ(err.what(), std::string("no additional nonce space for KDF"));
426     }
427     catch(...) {
428         FAIL() << "Expected std::logic_error";
429     }
430 }
431
432 uint256 test_prf(
433     unsigned char distinguisher,
434     uint252 seed_x,
435     uint256 y
436 ) {
437     uint256 x = seed_x.inner();
438     *x.begin() &= 0x0f;
439     *x.begin() |= distinguisher;
440     CSHA256 hasher;
441     hasher.Write(x.begin(), 32);
442     hasher.Write(y.begin(), 32);
443
444     uint256 ret;
445     hasher.FinalizeNoPadding(ret.begin());
446     return ret;
447 }
448
449 TEST(noteencryption, prf_addr)
450 {
451     for (size_t i = 0; i < 100; i++) {
452         uint252 a_sk = libzcash::random_uint252();
453         uint256 rest;
454         ASSERT_TRUE(
455             test_prf(0xc0, a_sk, rest) == PRF_addr_a_pk(a_sk)
456         );
457     }
458
459     for (size_t i = 0; i < 100; i++) {
460         uint252 a_sk = libzcash::random_uint252();
461         uint256 rest;
462         *rest.begin() = 0x01;
463         ASSERT_TRUE(
464             test_prf(0xc0, a_sk, rest) == PRF_addr_sk_enc(a_sk)
465         );
466     }
467 }
468
469 TEST(noteencryption, prf_nf)
470 {
471     for (size_t i = 0; i < 100; i++) {
472         uint252 a_sk = libzcash::random_uint252();
473         uint256 rho = libzcash::random_uint256();
474         ASSERT_TRUE(
475             test_prf(0xe0, a_sk, rho) == PRF_nf(a_sk, rho)
476         );
477     }
478 }
479
480 TEST(noteencryption, prf_pk)
481 {
482     for (size_t i = 0; i < 100; i++) {
483         uint252 a_sk = libzcash::random_uint252();
484         uint256 h_sig = libzcash::random_uint256();
485         ASSERT_TRUE(
486             test_prf(0x00, a_sk, h_sig) == PRF_pk(a_sk, 0, h_sig)
487         );
488     }
489
490     for (size_t i = 0; i < 100; i++) {
491         uint252 a_sk = libzcash::random_uint252();
492         uint256 h_sig = libzcash::random_uint256();
493         ASSERT_TRUE(
494             test_prf(0x40, a_sk, h_sig) == PRF_pk(a_sk, 1, h_sig)
495         );
496     }
497
498     uint252 dummy_a;
499     uint256 dummy_b;
500     ASSERT_THROW(PRF_pk(dummy_a, 2, dummy_b), std::domain_error);
501 }
502
503 TEST(noteencryption, prf_rho)
504 {
505     for (size_t i = 0; i < 100; i++) {
506         uint252 phi = libzcash::random_uint252();
507         uint256 h_sig = libzcash::random_uint256();
508         ASSERT_TRUE(
509             test_prf(0x20, phi, h_sig) == PRF_rho(phi, 0, h_sig)
510         );
511     }
512
513     for (size_t i = 0; i < 100; i++) {
514         uint252 phi = libzcash::random_uint252();
515         uint256 h_sig = libzcash::random_uint256();
516         ASSERT_TRUE(
517             test_prf(0x60, phi, h_sig) == PRF_rho(phi, 1, h_sig)
518         );
519     }
520
521     uint252 dummy_a;
522     uint256 dummy_b;
523     ASSERT_THROW(PRF_rho(dummy_a, 2, dummy_b), std::domain_error);
524 }
525
526 TEST(noteencryption, uint252)
527 {
528     ASSERT_THROW(uint252(uint256S("f6da8716682d600f74fc16bd0187faad6a26b4aa4c24d5c055b216d94516847e")), std::domain_error);
529 }
This page took 0.053175 seconds and 4 git commands to generate.