1 #include <gtest/gtest.h>
7 #include "zcash/Note.hpp"
8 #include "zcash/NoteEncryption.hpp"
10 #include "zcash/Address.hpp"
11 #include "crypto/sha256.h"
12 #include "librustzcash.h"
14 class TestNoteDecryption : public ZCNoteDecryption {
16 TestNoteDecryption(uint256 sk_enc) : ZCNoteDecryption(sk_enc) {}
18 void change_pk_enc(uint256 to) {
23 TEST(noteencryption, NotePlaintext)
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});
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;
37 SaplingNote note(addr, 39393);
38 auto cmu_opt = note.cm();
42 uint256 cmu = cmu_opt.get();
43 SaplingNotePlaintext pt(note, memo);
45 auto res = pt.encrypt(addr.pk_d);
53 auto encryptor = enc.second;
54 auto epk = encryptor.get_epk();
56 // Try to decrypt with incorrect commitment
57 ASSERT_FALSE(SaplingNotePlaintext::decrypt(
64 // Try to decrypt with correct commitment
65 auto foo = SaplingNotePlaintext::decrypt(
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);
83 auto foobar = bar.note(ivk);
89 auto new_note = foobar.get();
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());
97 SaplingOutgoingPlaintext out_pt;
98 out_pt.pk_d = note.pk_d;
99 out_pt.esk = encryptor.get_esk();
101 auto ovk = random_uint256();
102 auto cv = random_uint256();
103 auto cm = random_uint256();
105 auto out_ct = out_pt.encrypt(
112 auto decrypted_out_ct = out_pt.decrypt(
120 if (!decrypted_out_ct) {
124 auto decrypted_out_ct_unwrapped = decrypted_out_ct.get();
126 ASSERT_TRUE(decrypted_out_ct_unwrapped.pk_d == out_pt.pk_d);
127 ASSERT_TRUE(decrypted_out_ct_unwrapped.esk == out_pt.esk);
129 // Test sender won't accept invalid commitments
131 SaplingNotePlaintext::decrypt(
134 decrypted_out_ct_unwrapped.esk,
135 decrypted_out_ct_unwrapped.pk_d,
140 // Test sender can decrypt the note ciphertext.
141 foo = SaplingNotePlaintext::decrypt(
144 decrypted_out_ct_unwrapped.esk,
145 decrypted_out_ct_unwrapped.pk_d,
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);
161 TEST(noteencryption, SaplingApi)
163 using namespace libzcash;
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});
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;
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;
185 // Invalid diversifier
186 ASSERT_EQ(boost::none, SaplingNoteEncryption::FromDiversifier({1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}));
189 auto enc = *SaplingNoteEncryption::FromDiversifier(pk_1.d);
190 auto ciphertext_1 = *enc.encrypt_to_recipient(
194 auto epk_1 = enc.get_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);
201 auto cv_1 = random_uint256();
202 auto cm_1 = random_uint256();
203 auto out_ciphertext_1 = enc.encrypt_to_ourselves(
211 enc = *SaplingNoteEncryption::FromDiversifier(pk_2.d);
212 auto ciphertext_2 = *enc.encrypt_to_recipient(
216 auto epk_2 = enc.get_epk();
218 auto cv_2 = random_uint256();
219 auto cm_2 = random_uint256();
220 auto out_ciphertext_2 = enc.encrypt_to_ourselves(
227 // Test nonce-reuse resistance of API
229 auto tmp_enc = *SaplingNoteEncryption::FromDiversifier(pk_1.d);
231 tmp_enc.encrypt_to_recipient(
236 ASSERT_THROW(tmp_enc.encrypt_to_recipient(
239 ), std::logic_error);
241 tmp_enc.encrypt_to_ourselves(
248 ASSERT_THROW(tmp_enc.encrypt_to_ourselves(
253 ), std::logic_error);
257 auto plaintext_1 = *AttemptSaplingEncDecryption(
262 ASSERT_TRUE(message == plaintext_1);
264 auto small_plaintext_1 = *AttemptSaplingOutDecryption(
271 ASSERT_TRUE(small_message == small_plaintext_1);
273 auto plaintext_2 = *AttemptSaplingEncDecryption(
278 ASSERT_TRUE(message == plaintext_2);
280 auto small_plaintext_2 = *AttemptSaplingOutDecryption(
287 ASSERT_TRUE(small_message == small_plaintext_2);
289 // Try to decrypt out ciphertext with wrong key material
290 ASSERT_FALSE(AttemptSaplingOutDecryption(
297 ASSERT_FALSE(AttemptSaplingOutDecryption(
304 ASSERT_FALSE(AttemptSaplingOutDecryption(
311 ASSERT_FALSE(AttemptSaplingOutDecryption(
319 // Try to decrypt with wrong ephemeral key
320 ASSERT_FALSE(AttemptSaplingEncDecryption(
325 ASSERT_FALSE(AttemptSaplingEncDecryption(
331 // Try to decrypt with wrong ivk
332 ASSERT_FALSE(AttemptSaplingEncDecryption(
337 ASSERT_FALSE(AttemptSaplingEncDecryption(
344 TEST(noteencryption, api)
346 uint256 sk_enc = ZCNoteEncryption::generate_privkey(uint252(uint256S("21035d60bc1983e37950ce4803418a8fb33ea68d5b937ca382ecbae7564d6a07")));
347 uint256 pk_enc = ZCNoteEncryption::generate_pubkey(sk_enc);
349 ZCNoteEncryption b = ZCNoteEncryption(uint256());
350 for (size_t i = 0; i < 100; i++)
352 ZCNoteEncryption c = ZCNoteEncryption(uint256());
354 ASSERT_TRUE(b.get_epk() != c.get_epk());
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;
363 for (int i = 0; i < 255; i++) {
364 auto ciphertext = b.encrypt(pk_enc, message);
367 ZCNoteDecryption decrypter(sk_enc);
370 auto plaintext = decrypter.decrypt(ciphertext, b.get_epk(), uint256(), i);
371 ASSERT_TRUE(plaintext == message);
374 ASSERT_THROW(decrypter.decrypt(ciphertext, b.get_epk(), uint256(), (i == 0) ? 1 : (i - 1)),
375 libzcash::note_decryption_failed);
377 // Test wrong ephemeral key
379 ZCNoteEncryption c = ZCNoteEncryption(uint256());
381 ASSERT_THROW(decrypter.decrypt(ciphertext, c.get_epk(), uint256(), i),
382 libzcash::note_decryption_failed);
386 ASSERT_THROW(decrypter.decrypt(ciphertext, b.get_epk(), uint256S("11035d60bc1983e37950ce4803418a8fb33ea68d5b937ca382ecbae7564d6a77"), i),
387 libzcash::note_decryption_failed);
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;
397 // Test wrong private key
398 uint256 sk_enc_2 = ZCNoteEncryption::generate_privkey(uint252());
399 ZCNoteDecryption decrypter(sk_enc_2);
401 ASSERT_THROW(decrypter.decrypt(ciphertext, b.get_epk(), uint256(), i),
402 libzcash::note_decryption_failed);
406 TestNoteDecryption decrypter(sk_enc);
409 auto plaintext = decrypter.decrypt(ciphertext, b.get_epk(), uint256(), i);
410 ASSERT_TRUE(plaintext == message);
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);
419 // Nonce space should run out here
421 b.encrypt(pk_enc, message);
422 FAIL() << "Expected std::logic_error";
424 catch(std::logic_error const & err) {
425 EXPECT_EQ(err.what(), std::string("no additional nonce space for KDF"));
428 FAIL() << "Expected std::logic_error";
433 unsigned char distinguisher,
437 uint256 x = seed_x.inner();
439 *x.begin() |= distinguisher;
441 hasher.Write(x.begin(), 32);
442 hasher.Write(y.begin(), 32);
445 hasher.FinalizeNoPadding(ret.begin());
449 TEST(noteencryption, prf_addr)
451 for (size_t i = 0; i < 100; i++) {
452 uint252 a_sk = libzcash::random_uint252();
455 test_prf(0xc0, a_sk, rest) == PRF_addr_a_pk(a_sk)
459 for (size_t i = 0; i < 100; i++) {
460 uint252 a_sk = libzcash::random_uint252();
462 *rest.begin() = 0x01;
464 test_prf(0xc0, a_sk, rest) == PRF_addr_sk_enc(a_sk)
469 TEST(noteencryption, prf_nf)
471 for (size_t i = 0; i < 100; i++) {
472 uint252 a_sk = libzcash::random_uint252();
473 uint256 rho = libzcash::random_uint256();
475 test_prf(0xe0, a_sk, rho) == PRF_nf(a_sk, rho)
480 TEST(noteencryption, prf_pk)
482 for (size_t i = 0; i < 100; i++) {
483 uint252 a_sk = libzcash::random_uint252();
484 uint256 h_sig = libzcash::random_uint256();
486 test_prf(0x00, a_sk, h_sig) == PRF_pk(a_sk, 0, h_sig)
490 for (size_t i = 0; i < 100; i++) {
491 uint252 a_sk = libzcash::random_uint252();
492 uint256 h_sig = libzcash::random_uint256();
494 test_prf(0x40, a_sk, h_sig) == PRF_pk(a_sk, 1, h_sig)
500 ASSERT_THROW(PRF_pk(dummy_a, 2, dummy_b), std::domain_error);
503 TEST(noteencryption, prf_rho)
505 for (size_t i = 0; i < 100; i++) {
506 uint252 phi = libzcash::random_uint252();
507 uint256 h_sig = libzcash::random_uint256();
509 test_prf(0x20, phi, h_sig) == PRF_rho(phi, 0, h_sig)
513 for (size_t i = 0; i < 100; i++) {
514 uint252 phi = libzcash::random_uint252();
515 uint256 h_sig = libzcash::random_uint256();
517 test_prf(0x60, phi, h_sig) == PRF_rho(phi, 1, h_sig)
523 ASSERT_THROW(PRF_rho(dummy_a, 2, dummy_b), std::domain_error);
526 TEST(noteencryption, uint252)
528 ASSERT_THROW(uint252(uint256S("f6da8716682d600f74fc16bd0187faad6a26b4aa4c24d5c055b216d94516847e")), std::domain_error);