Line data Source code
1 : // Copyright (c) 2016-2020 The ZCash developers
2 : // Copyright (c) 2020 The PIVX Core developers
3 : // Distributed under the MIT software license, see the accompanying
4 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 :
6 : #include "test/test_pivx.h"
7 :
8 : #include <array>
9 : #include <stdexcept>
10 :
11 : #include "crypto/sha256.h"
12 : #include "sapling/address.h"
13 : #include "sapling/note.h"
14 : #include "sapling/noteencryption.h"
15 : #include "sapling/prf.h"
16 : #include "sapling/sapling_util.h"
17 :
18 : #include <boost/test/unit_test.hpp>
19 : #include <librustzcash.h>
20 : #include <sodium.h>
21 :
22 : BOOST_FIXTURE_TEST_SUITE(noteencryption_tests, BasicTestingSetup)
23 :
24 2 : BOOST_AUTO_TEST_CASE(note_plain_text_test)
25 : {
26 2 : auto xsk = libzcash::SaplingSpendingKey(uint256()).expanded_spending_key();
27 1 : auto fvk = xsk.full_viewing_key();
28 1 : auto ivk = fvk.in_viewing_key();
29 2 : libzcash::SaplingPaymentAddress addr = *ivk.address({0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0});
30 :
31 1 : std::array<unsigned char, ZC_MEMO_SIZE> memo;
32 513 : for (size_t i = 0; i < ZC_MEMO_SIZE; i++) {
33 : // Fill the message with dummy data
34 512 : memo[i] = (unsigned char) i;
35 : }
36 :
37 1 : libzcash::SaplingNote note(addr, 39393);
38 1 : auto cmu_opt = note.cmu();
39 1 : if (!cmu_opt) {
40 0 : BOOST_ERROR("SaplingNote cm failed");
41 : }
42 1 : uint256 cmu = cmu_opt.get();
43 1 : libzcash::SaplingNotePlaintext pt(note, memo);
44 :
45 1 : auto res = pt.encrypt(addr.pk_d);
46 1 : if (!res) {
47 0 : BOOST_ERROR("SaplingNotePlaintext encrypt failed");
48 : }
49 :
50 1 : auto enc = res.get();
51 :
52 1 : auto ct = enc.first;
53 1 : auto encryptor = enc.second;
54 1 : auto epk = encryptor.get_epk();
55 :
56 : // Try to decrypt with incorrect commitment
57 3 : BOOST_CHECK(!libzcash::SaplingNotePlaintext::decrypt(
58 : ct,
59 : ivk,
60 : epk,
61 : uint256()
62 : ));
63 :
64 : // Try to decrypt with correct commitment
65 1 : auto foo = libzcash::SaplingNotePlaintext::decrypt(
66 : ct,
67 : ivk,
68 : epk,
69 : cmu
70 1 : );
71 :
72 1 : if (!foo) {
73 0 : BOOST_ERROR("SaplingNotePlaintext decrypt failed");
74 : }
75 :
76 1 : auto bar = foo.get();
77 :
78 2 : BOOST_CHECK(bar.value() == pt.value());
79 2 : BOOST_CHECK(bar.memo() == pt.memo());
80 2 : BOOST_CHECK(bar.d == pt.d);
81 2 : BOOST_CHECK(bar.rcm == pt.rcm);
82 :
83 1 : auto foobar = bar.note(ivk);
84 :
85 1 : if (!foobar) {
86 : // Improve this message
87 0 : BOOST_ERROR("Invalid note");
88 : }
89 :
90 2 : auto new_note = foobar.get();
91 :
92 2 : BOOST_CHECK(note.value() == new_note.value());
93 2 : BOOST_CHECK(note.d == new_note.d);
94 2 : BOOST_CHECK(note.pk_d == new_note.pk_d);
95 2 : BOOST_CHECK(note.r == new_note.r);
96 2 : BOOST_CHECK(note.cmu() == new_note.cmu());
97 :
98 1 : libzcash::SaplingOutgoingPlaintext out_pt;
99 1 : out_pt.pk_d = note.pk_d;
100 1 : out_pt.esk = encryptor.get_esk();
101 :
102 1 : auto ovk = random_uint256();
103 1 : auto cv = random_uint256();
104 1 : auto cm = random_uint256();
105 :
106 1 : auto out_ct = out_pt.encrypt(
107 : ovk,
108 : cv,
109 : cm,
110 : encryptor
111 1 : );
112 :
113 1 : auto decrypted_out_ct = out_pt.decrypt(
114 : out_ct,
115 : ovk,
116 : cv,
117 : cm,
118 2 : encryptor.get_epk()
119 2 : );
120 :
121 1 : if (!decrypted_out_ct) {
122 0 : BOOST_ERROR("SaplingOutgoingPlaintext decrypt failed");
123 : }
124 :
125 1 : auto decrypted_out_ct_unwrapped = decrypted_out_ct.get();
126 :
127 2 : BOOST_CHECK(decrypted_out_ct_unwrapped.pk_d == out_pt.pk_d);
128 2 : BOOST_CHECK(decrypted_out_ct_unwrapped.esk == out_pt.esk);
129 :
130 : // Test sender won't accept invalid commitments
131 3 : BOOST_CHECK(!libzcash::SaplingNotePlaintext::decrypt(
132 : ct,
133 : epk,
134 : decrypted_out_ct_unwrapped.esk,
135 : decrypted_out_ct_unwrapped.pk_d,
136 : uint256()
137 : ));
138 :
139 : // Test sender can decrypt the note ciphertext.
140 1 : foo = libzcash::SaplingNotePlaintext::decrypt(
141 : ct,
142 : epk,
143 : decrypted_out_ct_unwrapped.esk,
144 : decrypted_out_ct_unwrapped.pk_d,
145 : cmu
146 1 : );
147 :
148 1 : if (!foo) {
149 0 : BOOST_ERROR("Sender decrypt note ciphertext failed.");
150 : }
151 :
152 1 : bar = foo.get();
153 :
154 2 : BOOST_CHECK(bar.value() == pt.value());
155 2 : BOOST_CHECK(bar.memo() == pt.memo());
156 2 : BOOST_CHECK(bar.d == pt.d);
157 2 : BOOST_CHECK(bar.rcm == pt.rcm);
158 1 : }
159 :
160 2 : BOOST_AUTO_TEST_CASE(SaplingApi_test)
161 : {
162 : // Create recipient addresses
163 2 : auto sk = libzcash::SaplingSpendingKey(uint256()).expanded_spending_key();
164 1 : auto vk = sk.full_viewing_key();
165 1 : auto ivk = vk.in_viewing_key();
166 2 : libzcash::SaplingPaymentAddress pk_1 = *ivk.address({0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0});
167 2 : libzcash::SaplingPaymentAddress pk_2 = *ivk.address({4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0});
168 :
169 : // Blob of stuff we're encrypting
170 1 : std::array<unsigned char, ZC_SAPLING_ENCPLAINTEXT_SIZE> message;
171 565 : for (size_t i = 0; i < ZC_SAPLING_ENCPLAINTEXT_SIZE; i++) {
172 : // Fill the message with dummy data
173 564 : message[i] = (unsigned char) i;
174 : }
175 :
176 : std::array<unsigned char, ZC_SAPLING_OUTPLAINTEXT_SIZE> small_message;
177 65 : for (size_t i = 0; i < ZC_SAPLING_OUTPLAINTEXT_SIZE; i++) {
178 : // Fill the message with dummy data
179 64 : small_message[i] = (unsigned char) i;
180 : }
181 :
182 : // Invalid diversifier
183 2 : BOOST_CHECK(boost::none == libzcash::SaplingNoteEncryption::FromDiversifier({1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}));
184 :
185 : // Encrypt to pk_1
186 2 : auto enc = *libzcash::SaplingNoteEncryption::FromDiversifier(pk_1.d);
187 1 : auto ciphertext_1 = *enc.encrypt_to_recipient(
188 : pk_1.pk_d,
189 : message
190 1 : );
191 1 : auto epk_1 = enc.get_epk();
192 1 : {
193 1 : uint256 test_epk;
194 1 : uint256 test_esk = enc.get_esk();
195 2 : BOOST_CHECK(librustzcash_sapling_ka_derivepublic(pk_1.d.begin(), test_esk.begin(), test_epk.begin()));
196 2 : BOOST_CHECK(test_epk == epk_1);
197 : }
198 1 : auto cv_1 = random_uint256();
199 1 : auto cm_1 = random_uint256();
200 1 : auto out_ciphertext_1 = enc.encrypt_to_ourselves(
201 : sk.ovk,
202 : cv_1,
203 : cm_1,
204 : small_message
205 1 : );
206 :
207 : // Encrypt to pk_2
208 2 : enc = *libzcash::SaplingNoteEncryption::FromDiversifier(pk_2.d);
209 1 : auto ciphertext_2 = *enc.encrypt_to_recipient(
210 : pk_2.pk_d,
211 : message
212 : );
213 1 : auto epk_2 = enc.get_epk();
214 :
215 1 : auto cv_2 = random_uint256();
216 1 : auto cm_2 = random_uint256();
217 1 : auto out_ciphertext_2 = enc.encrypt_to_ourselves(
218 : sk.ovk,
219 : cv_2,
220 : cm_2,
221 : small_message
222 1 : );
223 :
224 : // Test nonce-reuse resistance of API
225 1 : {
226 2 : auto tmp_enc = *libzcash::SaplingNoteEncryption::FromDiversifier(pk_1.d);
227 :
228 1 : tmp_enc.encrypt_to_recipient(
229 : pk_1.pk_d,
230 : message
231 : );
232 :
233 2 : BOOST_CHECK_THROW(tmp_enc.encrypt_to_recipient(
234 : pk_1.pk_d,
235 : message
236 : ), std::logic_error);
237 :
238 2 : tmp_enc.encrypt_to_ourselves(
239 : sk.ovk,
240 : cv_2,
241 : cm_2,
242 : small_message
243 1 : );
244 :
245 2 : BOOST_CHECK_THROW(tmp_enc.encrypt_to_ourselves(
246 : sk.ovk,
247 : cv_2,
248 : cm_2,
249 : small_message
250 : ), std::logic_error);
251 : }
252 :
253 : // Try to decrypt
254 1 : auto plaintext_1 = *AttemptSaplingEncDecryption(
255 : ciphertext_1,
256 : ivk,
257 : epk_1
258 1 : );
259 2 : BOOST_CHECK(message == plaintext_1);
260 :
261 1 : auto small_plaintext_1 = *libzcash::AttemptSaplingOutDecryption(
262 : out_ciphertext_1,
263 : sk.ovk,
264 : cv_1,
265 : cm_1,
266 : epk_1
267 1 : );
268 2 : BOOST_CHECK(small_message == small_plaintext_1);
269 :
270 1 : auto plaintext_2 = *AttemptSaplingEncDecryption(
271 : ciphertext_2,
272 : ivk,
273 : epk_2
274 1 : );
275 2 : BOOST_CHECK(message == plaintext_2);
276 :
277 1 : auto small_plaintext_2 = *libzcash::AttemptSaplingOutDecryption(
278 : out_ciphertext_2,
279 : sk.ovk,
280 : cv_2,
281 : cm_2,
282 : epk_2
283 1 : );
284 2 : BOOST_CHECK(small_message == small_plaintext_2);
285 :
286 : // Try to decrypt out ciphertext with wrong key material
287 2 : BOOST_CHECK(!libzcash::AttemptSaplingOutDecryption(
288 : out_ciphertext_1,
289 : random_uint256(),
290 : cv_1,
291 : cm_1,
292 : epk_1
293 : ));
294 2 : BOOST_CHECK(!libzcash::AttemptSaplingOutDecryption(
295 : out_ciphertext_1,
296 : sk.ovk,
297 : random_uint256(),
298 : cm_1,
299 : epk_1
300 : ));
301 2 : BOOST_CHECK(!libzcash::AttemptSaplingOutDecryption(
302 : out_ciphertext_1,
303 : sk.ovk,
304 : cv_1,
305 : random_uint256(),
306 : epk_1
307 : ));
308 2 : BOOST_CHECK(!libzcash::AttemptSaplingOutDecryption(
309 : out_ciphertext_1,
310 : sk.ovk,
311 : cv_1,
312 : cm_1,
313 : random_uint256()
314 : ));
315 :
316 : // Try to decrypt with wrong ephemeral key
317 2 : BOOST_CHECK(!AttemptSaplingEncDecryption(
318 : ciphertext_1,
319 : ivk,
320 : epk_2
321 : ));
322 2 : BOOST_CHECK(!AttemptSaplingEncDecryption(
323 : ciphertext_2,
324 : ivk,
325 : epk_1
326 : ));
327 :
328 : // Try to decrypt with wrong ivk
329 3 : BOOST_CHECK(!libzcash::AttemptSaplingEncDecryption(
330 : ciphertext_1,
331 : uint256(),
332 : epk_1
333 : ));
334 3 : BOOST_CHECK(!libzcash::AttemptSaplingEncDecryption(
335 : ciphertext_2,
336 : uint256(),
337 : epk_2
338 : ));
339 1 : }
340 :
341 : BOOST_AUTO_TEST_SUITE_END()
|