Electroneum
Loading...
Searching...
No Matches
rctSigs.cpp
Go to the documentation of this file.
1// Copyright (c) 2016, Electroneum Research Labs
2//
3// Author: Shen Noether <shen.noether@gmx.com>
4//
5// All rights reserved.
6//
7// Redistribution and use in source and binary forms, with or without modification, are
8// permitted provided that the following conditions are met:
9//
10// 1. Redistributions of source code must retain the above copyright notice, this list of
11// conditions and the following disclaimer.
12//
13// 2. Redistributions in binary form must reproduce the above copyright notice, this list
14// of conditions and the following disclaimer in the documentation and/or other
15// materials provided with the distribution.
16//
17// 3. Neither the name of the copyright holder nor the names of its contributors may be
18// used to endorse or promote products derived from this software without specific
19// prior written permission.
20//
21// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
22// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
23// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
24// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
29// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31#include "misc_log_ex.h"
32#include "common/perf_timer.h"
33#include "common/threadpool.h"
34#include "common/util.h"
35#include "rctSigs.h"
36#include "bulletproofs.h"
38
39using namespace crypto;
40using namespace std;
41
42#undef ELECTRONEUM_DEFAULT_LOG_CATEGORY
43#define ELECTRONEUM_DEFAULT_LOG_CATEGORY "ringct"
44
45#define CHECK_AND_ASSERT_MES_L1(expr, ret, message) {if(!(expr)) {MCERROR("verify", message); return ret;}}
46
47namespace
48{
49 rct::Bulletproof make_dummy_bulletproof(const std::vector<uint64_t> &outamounts, rct::keyV &C, rct::keyV &masks)
50 {
51 const size_t n_outs = outamounts.size();
52 const rct::key I = rct::identity();
53 size_t nrl = 0;
54 while ((1u << nrl) < n_outs)
55 ++nrl;
56 nrl += 6;
57
58 C.resize(n_outs);
59 masks.resize(n_outs);
60 for (size_t i = 0; i < n_outs; ++i)
61 {
62 masks[i] = I;
63 rct::key sv8, sv;
64 sv = rct::zero();
65 sv.bytes[0] = outamounts[i] & 255;
66 sv.bytes[1] = (outamounts[i] >> 8) & 255;
67 sv.bytes[2] = (outamounts[i] >> 16) & 255;
68 sv.bytes[3] = (outamounts[i] >> 24) & 255;
69 sv.bytes[4] = (outamounts[i] >> 32) & 255;
70 sv.bytes[5] = (outamounts[i] >> 40) & 255;
71 sv.bytes[6] = (outamounts[i] >> 48) & 255;
72 sv.bytes[7] = (outamounts[i] >> 56) & 255;
73 sc_mul(sv8.bytes, sv.bytes, rct::INV_EIGHT.bytes);
74 rct::addKeys2(C[i], rct::INV_EIGHT, sv8, rct::H);
75 }
76
77 return rct::Bulletproof{rct::keyV(n_outs, I), I, I, I, I, I, I, rct::keyV(nrl, I), rct::keyV(nrl, I), I, I, I};
78 }
79}
80
81namespace rct {
82 Bulletproof proveRangeBulletproof(keyV &C, keyV &masks, const std::vector<uint64_t> &amounts, epee::span<const key> sk, hw::device &hwdev)
83 {
84 CHECK_AND_ASSERT_THROW_MES(amounts.size() == sk.size(), "Invalid amounts/sk sizes");
85 masks.resize(amounts.size());
86 for (size_t i = 0; i < masks.size(); ++i)
87 masks[i] = hwdev.genCommitmentMask(sk[i]);
88 Bulletproof proof = bulletproof_PROVE(amounts, masks);
89 CHECK_AND_ASSERT_THROW_MES(proof.V.size() == amounts.size(), "V does not have the expected size");
90 C = proof.V;
91 return proof;
92 }
93
94 bool verBulletproof(const Bulletproof &proof)
95 {
96 try { return bulletproof_VERIFY(proof); }
97 // we can get deep throws from ge_frombytes_vartime if input isn't valid
98 catch (...) { return false; }
99 }
100
101 bool verBulletproof(const std::vector<const Bulletproof*> &proofs)
102 {
103 try { return bulletproof_VERIFY(proofs); }
104 // we can get deep throws from ge_frombytes_vartime if input isn't valid
105 catch (...) { return false; }
106 }
107
108 //Borromean (c.f. gmax/andytoshi's paper)
109 boroSig genBorromean(const key64 x, const key64 P1, const key64 P2, const bits indices) {
110 key64 L[2], alpha;
111 key c;
112 int naught = 0, prime = 0, ii = 0, jj=0;
113 boroSig bb;
114 for (ii = 0 ; ii < 64 ; ii++) {
115 naught = indices[ii]; prime = (indices[ii] + 1) % 2;
116 skGen(alpha[ii]);
117 scalarmultBase(L[naught][ii], alpha[ii]);
118 if (naught == 0) {
119 skGen(bb.s1[ii]);
120 c = hash_to_scalar(L[naught][ii]);
121 addKeys2(L[prime][ii], bb.s1[ii], c, P2[ii]);
122 }
123 }
124 bb.ee = hash_to_scalar(L[1]); //or L[1]..
125 key LL, cc;
126 for (jj = 0 ; jj < 64 ; jj++) {
127 if (!indices[jj]) {
128 sc_mulsub(bb.s0[jj].bytes, x[jj].bytes, bb.ee.bytes, alpha[jj].bytes);
129 } else {
130 skGen(bb.s0[jj]);
131 addKeys2(LL, bb.s0[jj], bb.ee, P1[jj]); //different L0
132 cc = hash_to_scalar(LL);
133 sc_mulsub(bb.s1[jj].bytes, x[jj].bytes, cc.bytes, alpha[jj].bytes);
134 }
135 }
136 return bb;
137 }
138
139 //see above.
140 bool verifyBorromean(const boroSig &bb, const ge_p3 P1[64], const ge_p3 P2[64]) {
141 key64 Lv1; key chash, LL;
142 int ii = 0;
143 ge_p2 p2;
144 for (ii = 0 ; ii < 64 ; ii++) {
145 // equivalent of: addKeys2(LL, bb.s0[ii], bb.ee, P1[ii]);
146 ge_double_scalarmult_base_vartime(&p2, bb.ee.bytes, &P1[ii], bb.s0[ii].bytes);
147 ge_tobytes(LL.bytes, &p2);
148 chash = hash_to_scalar(LL);
149 // equivalent of: addKeys2(Lv1[ii], bb.s1[ii], chash, P2[ii]);
150 ge_double_scalarmult_base_vartime(&p2, chash.bytes, &P2[ii], bb.s1[ii].bytes);
151 ge_tobytes(Lv1[ii].bytes, &p2);
152 }
153 key eeComputed = hash_to_scalar(Lv1); //hash function fine
154 return equalKeys(eeComputed, bb.ee);
155 }
156
157 bool verifyBorromean(const boroSig &bb, const key64 P1, const key64 P2) {
158 ge_p3 P1_p3[64], P2_p3[64];
159 for (size_t i = 0 ; i < 64 ; ++i) {
160 CHECK_AND_ASSERT_MES_L1(ge_frombytes_vartime(&P1_p3[i], P1[i].bytes) == 0, false, "point conv failed");
161 CHECK_AND_ASSERT_MES_L1(ge_frombytes_vartime(&P2_p3[i], P2[i].bytes) == 0, false, "point conv failed");
162 }
163 return verifyBorromean(bb, P1_p3, P2_p3);
164 }
165
166 //Multilayered Spontaneous Anonymous Group Signatures (MLSAG signatures)
167 //This is a just slghtly more efficient version than the ones described below
168 //(will be explained in more detail in Ring Multisig paper
169 //These are aka MG signatutes in earlier drafts of the ring ct paper
170 // c.f. https://eprint.iacr.org/2015/1098 section 2.
171 // Gen creates a signature which proves that for some column in the keymatrix "pk"
172 // the signer knows a secret key for each row in that column
173 // Ver verifies that the MG sig was created correctly
174 mgSig MLSAG_Gen(const key &message, const keyM & pk, const keyV & xx, const multisig_kLRki *kLRki, key *mscout, const unsigned int index, size_t dsRows, hw::device &hwdev) {
175 mgSig rv;
176 size_t cols = pk.size();
177 CHECK_AND_ASSERT_THROW_MES(cols >= 2, "Error! What is c if cols = 1!");
178 CHECK_AND_ASSERT_THROW_MES(index < cols, "Index out of range");
179 size_t rows = pk[0].size();
180 CHECK_AND_ASSERT_THROW_MES(rows >= 1, "Empty pk");
181 for (size_t i = 1; i < cols; ++i) {
182 CHECK_AND_ASSERT_THROW_MES(pk[i].size() == rows, "pk is not rectangular");
183 }
184 CHECK_AND_ASSERT_THROW_MES(xx.size() == rows, "Bad xx size");
185 CHECK_AND_ASSERT_THROW_MES(dsRows <= rows, "Bad dsRows size");
186 CHECK_AND_ASSERT_THROW_MES((kLRki && mscout) || (!kLRki && !mscout), "Only one of kLRki/mscout is present");
187 CHECK_AND_ASSERT_THROW_MES(!kLRki || dsRows == 1, "Multisig requires exactly 1 dsRows");
188
189 size_t i = 0, j = 0, ii = 0;
190 key c, c_old, L, R, Hi;
191 sc_0(c_old.bytes);
192 vector<geDsmp> Ip(dsRows);
193 rv.II = keyV(dsRows);
194 keyV alpha(rows);
195 keyV aG(rows);
196 rv.ss = keyM(cols, aG);
197 keyV aHP(dsRows);
198 keyV toHash(1 + 3 * dsRows + 2 * (rows - dsRows));
199 toHash[0] = message;
200 DP("here1");
201 for (i = 0; i < dsRows; i++) {
202 toHash[3 * i + 1] = pk[index][i];
203 if (kLRki) {
204 // multisig
205 alpha[i] = kLRki->k;
206 toHash[3 * i + 2] = kLRki->L;
207 toHash[3 * i + 3] = kLRki->R;
208 rv.II[i] = kLRki->ki;
209 }
210 else {
211 Hi = hashToPoint(pk[index][i]);
212 hwdev.mlsag_prepare(Hi, xx[i], alpha[i] , aG[i] , aHP[i] , rv.II[i]);
213 toHash[3 * i + 2] = aG[i];
214 toHash[3 * i + 3] = aHP[i];
215 }
216 precomp(Ip[i].k, rv.II[i]);
217 }
218 size_t ndsRows = 3 * dsRows; //non Double Spendable Rows (see identity chains paper)
219 for (i = dsRows, ii = 0 ; i < rows ; i++, ii++) {
220 skpkGen(alpha[i], aG[i]); //need to save alphas for later..
221 toHash[ndsRows + 2 * ii + 1] = pk[index][i];
222 toHash[ndsRows + 2 * ii + 2] = aG[i];
223 }
224
225 hwdev.mlsag_hash(toHash, c_old);
226
227
228 i = (index + 1) % cols;
229 if (i == 0) {
230 copy(rv.cc, c_old);
231 }
232 while (i != index) {
233
234 rv.ss[i] = skvGen(rows);
235 sc_0(c.bytes);
236 for (j = 0; j < dsRows; j++) {
237 addKeys2(L, rv.ss[i][j], c_old, pk[i][j]);
238 hashToPoint(Hi, pk[i][j]);
239 addKeys3(R, rv.ss[i][j], Hi, c_old, Ip[j].k);
240 toHash[3 * j + 1] = pk[i][j];
241 toHash[3 * j + 2] = L;
242 toHash[3 * j + 3] = R;
243 }
244 for (j = dsRows, ii = 0; j < rows; j++, ii++) {
245 addKeys2(L, rv.ss[i][j], c_old, pk[i][j]);
246 toHash[ndsRows + 2 * ii + 1] = pk[i][j];
247 toHash[ndsRows + 2 * ii + 2] = L;
248 }
249 hwdev.mlsag_hash(toHash, c);
250 copy(c_old, c);
251 i = (i + 1) % cols;
252
253 if (i == 0) {
254 copy(rv.cc, c_old);
255 }
256 }
257 hwdev.mlsag_sign(c, xx, alpha, rows, dsRows, rv.ss[index]);
258 if (mscout)
259 *mscout = c;
260 return rv;
261 }
262
263 //Multilayered Spontaneous Anonymous Group Signatures (MLSAG signatures)
264 //This is a just slghtly more efficient version than the ones described below
265 //(will be explained in more detail in Ring Multisig paper
266 //These are aka MG signatutes in earlier drafts of the ring ct paper
267 // c.f. https://eprint.iacr.org/2015/1098 section 2.
268 // Gen creates a signature which proves that for some column in the keymatrix "pk"
269 // the signer knows a secret key for each row in that column
270 // Ver verifies that the MG sig was created correctly
271 bool MLSAG_Ver(const key &message, const keyM & pk, const mgSig & rv, size_t dsRows) {
272
273 size_t cols = pk.size();
274 CHECK_AND_ASSERT_MES(cols >= 2, false, "Error! What is c if cols = 1!");
275 size_t rows = pk[0].size();
276 CHECK_AND_ASSERT_MES(rows >= 1, false, "Empty pk");
277 for (size_t i = 1; i < cols; ++i) {
278 CHECK_AND_ASSERT_MES(pk[i].size() == rows, false, "pk is not rectangular");
279 }
280 CHECK_AND_ASSERT_MES(rv.II.size() == dsRows, false, "Bad II size");
281 CHECK_AND_ASSERT_MES(rv.ss.size() == cols, false, "Bad rv.ss size");
282 for (size_t i = 0; i < cols; ++i) {
283 CHECK_AND_ASSERT_MES(rv.ss[i].size() == rows, false, "rv.ss is not rectangular");
284 }
285 CHECK_AND_ASSERT_MES(dsRows <= rows, false, "Bad dsRows value");
286
287 for (size_t i = 0; i < rv.ss.size(); ++i)
288 for (size_t j = 0; j < rv.ss[i].size(); ++j)
289 CHECK_AND_ASSERT_MES(sc_check(rv.ss[i][j].bytes) == 0, false, "Bad ss slot");
290 CHECK_AND_ASSERT_MES(sc_check(rv.cc.bytes) == 0, false, "Bad cc");
291
292 size_t i = 0, j = 0, ii = 0;
293 key c, L, R, Hi;
294 key c_old = copy(rv.cc);
295 vector<geDsmp> Ip(dsRows);
296 for (i = 0 ; i < dsRows ; i++) {
297 precomp(Ip[i].k, rv.II[i]);
298 }
299 size_t ndsRows = 3 * dsRows; //non Double Spendable Rows (see identity chains paper
300 keyV toHash(1 + 3 * dsRows + 2 * (rows - dsRows));
301 toHash[0] = message;
302 i = 0;
303 while (i < cols) {
304 sc_0(c.bytes);
305 for (j = 0; j < dsRows; j++) {
306 addKeys2(L, rv.ss[i][j], c_old, pk[i][j]);
307 hashToPoint(Hi, pk[i][j]);
308 CHECK_AND_ASSERT_MES(!(Hi == rct::identity()), false, "Data hashed to point at infinity");
309 addKeys3(R, rv.ss[i][j], Hi, c_old, Ip[j].k);
310 toHash[3 * j + 1] = pk[i][j];
311 toHash[3 * j + 2] = L;
312 toHash[3 * j + 3] = R;
313 }
314 for (j = dsRows, ii = 0 ; j < rows ; j++, ii++) {
315 addKeys2(L, rv.ss[i][j], c_old, pk[i][j]);
316 toHash[ndsRows + 2 * ii + 1] = pk[i][j];
317 toHash[ndsRows + 2 * ii + 2] = L;
318 }
319 c = hash_to_scalar(toHash);
320 copy(c_old, c);
321 i = (i + 1);
322 }
323 sc_sub(c.bytes, c_old.bytes, rv.cc.bytes);
324 return sc_isnonzero(c.bytes) == 0;
325 }
326
327
328
329 //proveRange and verRange
330 //proveRange gives C, and mask such that \sumCi = C
331 // c.f. https://eprint.iacr.org/2015/1098 section 5.1
332 // and Ci is a commitment to either 0 or 2^i, i=0,...,63
333 // thus this proves that "amount" is in [0, 2^64]
334 // mask is a such that C = aG + bH, and b = amount
335 //verRange verifies that \sum Ci = C and that each Ci is a commitment to 0 or 2^i
336 rangeSig proveRange(key & C, key & mask, const etn_amount & amount) {
337 sc_0(mask.bytes);
338 identity(C);
339 bits b;
340 d2b(b, amount);
341 rangeSig sig;
342 key64 ai;
343 key64 CiH;
344 int i = 0;
345 for (i = 0; i < ATOMS; i++) {
346 skGen(ai[i]);
347 if (b[i] == 0) {
348 scalarmultBase(sig.Ci[i], ai[i]);
349 }
350 if (b[i] == 1) {
351 addKeys1(sig.Ci[i], ai[i], H2[i]);
352 }
353 subKeys(CiH[i], sig.Ci[i], H2[i]);
354 sc_add(mask.bytes, mask.bytes, ai[i].bytes);
355 addKeys(C, C, sig.Ci[i]);
356 }
357 sig.asig = genBorromean(ai, sig.Ci, CiH, b);
358 return sig;
359 }
360
361 //proveRange and verRange
362 //proveRange gives C, and mask such that \sumCi = C
363 // c.f. https://eprint.iacr.org/2015/1098 section 5.1
364 // and Ci is a commitment to either 0 or 2^i, i=0,...,63
365 // thus this proves that "amount" is in [0, 2^64]
366 // mask is a such that C = aG + bH, and b = amount
367 //verRange verifies that \sum Ci = C and that each Ci is a commitment to 0 or 2^i
368 bool verRange(const key & C, const rangeSig & as) {
369 try
370 {
372 ge_p3 CiH[64], asCi[64];
373 int i = 0;
374 ge_p3 Ctmp_p3 = ge_p3_identity;
375 for (i = 0; i < 64; i++) {
376 // faster equivalent of:
377 // subKeys(CiH[i], as.Ci[i], H2[i]);
378 // addKeys(Ctmp, Ctmp, as.Ci[i]);
379 ge_cached cached;
380 ge_p3 p3;
381 ge_p1p1 p1;
382 CHECK_AND_ASSERT_MES_L1(ge_frombytes_vartime(&p3, H2[i].bytes) == 0, false, "point conv failed");
383 ge_p3_to_cached(&cached, &p3);
384 CHECK_AND_ASSERT_MES_L1(ge_frombytes_vartime(&asCi[i], as.Ci[i].bytes) == 0, false, "point conv failed");
385 ge_sub(&p1, &asCi[i], &cached);
386 ge_p3_to_cached(&cached, &asCi[i]);
387 ge_p1p1_to_p3(&CiH[i], &p1);
388 ge_add(&p1, &Ctmp_p3, &cached);
389 ge_p1p1_to_p3(&Ctmp_p3, &p1);
390 }
391 key Ctmp;
392 ge_p3_tobytes(Ctmp.bytes, &Ctmp_p3);
393 if (!equalKeys(C, Ctmp))
394 return false;
395 if (!verifyBorromean(as.asig, asCi, CiH))
396 return false;
397 return true;
398 }
399 // we can get deep throws from ge_frombytes_vartime if input isn't valid
400 catch (...) { return false; }
401 }
402
404 {
405 keyV hashes;
406 hashes.reserve(3);
407 hashes.push_back(rv.message);
408 crypto::hash h;
409
410 std::stringstream ss;
412 CHECK_AND_ASSERT_THROW_MES(!rv.mixRing.empty(), "Empty mixRing");
413 const size_t inputs = is_rct_simple(rv.type) ? rv.mixRing.size() : rv.mixRing[0].size();
414 const size_t outputs = rv.ecdhInfo.size();
415 key prehash;
416 CHECK_AND_ASSERT_THROW_MES(const_cast<rctSig&>(rv).serialize_rctsig_base(ba, inputs, outputs),
417 "Failed to serialize rctSigBase");
418 cryptonote::get_blob_hash(ss.str(), h);
419 hashes.push_back(hash2rct(h));
420
421 keyV kv;
423 {
424 kv.reserve((6*2+9) * rv.p.bulletproofs.size());
425 for (const auto &p: rv.p.bulletproofs)
426 {
427 // V are not hashed as they're expanded from outPk.mask
428 // (and thus hashed as part of rctSigBase above)
429 kv.push_back(p.A);
430 kv.push_back(p.S);
431 kv.push_back(p.T1);
432 kv.push_back(p.T2);
433 kv.push_back(p.taux);
434 kv.push_back(p.mu);
435 for (size_t n = 0; n < p.L.size(); ++n)
436 kv.push_back(p.L[n]);
437 for (size_t n = 0; n < p.R.size(); ++n)
438 kv.push_back(p.R[n]);
439 kv.push_back(p.a);
440 kv.push_back(p.b);
441 kv.push_back(p.t);
442 }
443 }
444 else
445 {
446 kv.reserve((64*3+1) * rv.p.rangeSigs.size());
447 for (const auto &r: rv.p.rangeSigs)
448 {
449 for (size_t n = 0; n < 64; ++n)
450 kv.push_back(r.asig.s0[n]);
451 for (size_t n = 0; n < 64; ++n)
452 kv.push_back(r.asig.s1[n]);
453 kv.push_back(r.asig.ee);
454 for (size_t n = 0; n < 64; ++n)
455 kv.push_back(r.Ci[n]);
456 }
457 }
458 hashes.push_back(cn_fast_hash(kv));
459 hwdev.mlsag_prehash(ss.str(), inputs, outputs, hashes, rv.outPk, prehash);
460 return prehash;
461 }
462
463 //Ring-ct MG sigs
464 //Prove:
465 // c.f. https://eprint.iacr.org/2015/1098 section 4. definition 10.
466 // This does the MG sig on the "dest" part of the given key matrix, and
467 // the last row is the sum of input commitments from that column - sum output commitments
468 // this shows that sum inputs = sum outputs
469 //Ver:
470 // verifies the above sig is created corretly
471 mgSig proveRctMG(const key &message, const ctkeyM & pubs, const ctkeyV & inSk, const ctkeyV &outSk, const ctkeyV & outPk, const multisig_kLRki *kLRki, key *mscout, unsigned int index, const key &txnFeeKey, hw::device &hwdev) {
472 //setup vars
473 size_t cols = pubs.size();
474 CHECK_AND_ASSERT_THROW_MES(cols >= 1, "Empty pubs");
475 size_t rows = pubs[0].size();
476 CHECK_AND_ASSERT_THROW_MES(rows >= 1, "Empty pubs");
477 for (size_t i = 1; i < cols; ++i) {
478 CHECK_AND_ASSERT_THROW_MES(pubs[i].size() == rows, "pubs is not rectangular");
479 }
480 CHECK_AND_ASSERT_THROW_MES(inSk.size() == rows, "Bad inSk size");
481 CHECK_AND_ASSERT_THROW_MES(outSk.size() == outPk.size(), "Bad outSk/outPk size");
482 CHECK_AND_ASSERT_THROW_MES((kLRki && mscout) || (!kLRki && !mscout), "Only one of kLRki/mscout is present");
483
484 keyV sk(rows + 1);
485 keyV tmp(rows + 1);
486 size_t i = 0, j = 0;
487 for (i = 0; i < rows + 1; i++) {
488 sc_0(sk[i].bytes);
489 identity(tmp[i]);
490 }
491 keyM M(cols, tmp);
492 //create the matrix to mg sig
493 for (i = 0; i < cols; i++) {
494 M[i][rows] = identity();
495 for (j = 0; j < rows; j++) {
496 M[i][j] = pubs[i][j].dest;
497 addKeys(M[i][rows], M[i][rows], pubs[i][j].mask); //add input commitments in last row
498 }
499 }
500 sc_0(sk[rows].bytes);
501 for (j = 0; j < rows; j++) {
502 sk[j] = copy(inSk[j].dest);
503 sc_add(sk[rows].bytes, sk[rows].bytes, inSk[j].mask.bytes); //add masks in last row
504 }
505 for (i = 0; i < cols; i++) {
506 for (size_t j = 0; j < outPk.size(); j++) {
507 subKeys(M[i][rows], M[i][rows], outPk[j].mask); //subtract output Ci's in last row
508 }
509 //subtract txn fee output in last row
510 subKeys(M[i][rows], M[i][rows], txnFeeKey);
511 }
512 for (size_t j = 0; j < outPk.size(); j++) {
513 sc_sub(sk[rows].bytes, sk[rows].bytes, outSk[j].mask.bytes); //subtract output masks in last row..
514 }
515 mgSig result = MLSAG_Gen(message, M, sk, kLRki, mscout, index, rows, hwdev);
516 memwipe(sk.data(), sk.size() * sizeof(key));
517 return result;
518 }
519
520
521 //Ring-ct MG sigs Simple
522 // Simple version for when we assume only
523 // post rct inputs
524 // here pubs is a vector of (P, C) length mixin
525 // inSk is x, a_in corresponding to signing index
526 // a_out, Cout is for the output commitment
527 // index is the signing index..
528 mgSig proveRctMGSimple(const key &message, const ctkeyV & pubs, const ctkey & inSk, const key &a , const key &Cout, const multisig_kLRki *kLRki, key *mscout, unsigned int index, hw::device &hwdev) {
529 //setup vars
530 size_t rows = 1;
531 size_t cols = pubs.size();
532 CHECK_AND_ASSERT_THROW_MES(cols >= 1, "Empty pubs");
533 CHECK_AND_ASSERT_THROW_MES((kLRki && mscout) || (!kLRki && !mscout), "Only one of kLRki/mscout is present");
534 keyV tmp(rows + 1);
535 keyV sk(rows + 1);
536 size_t i;
537 keyM M(cols, tmp);
538
539 sk[0] = copy(inSk.dest);
540 sc_sub(sk[1].bytes, inSk.mask.bytes, a.bytes);
541 for (i = 0; i < cols; i++) {
542 M[i][0] = pubs[i].dest;
543 subKeys(M[i][1], pubs[i].mask, Cout);
544 }
545 mgSig result = MLSAG_Gen(message, M, sk, kLRki, mscout, index, rows, hwdev);
546 memwipe(&sk[0], sizeof(key));
547 return result;
548 }
549
550
551 //Ring-ct MG sigs
552 //Prove:
553 // c.f. https://eprint.iacr.org/2015/1098 section 4. definition 10.
554 // This does the MG sig on the "dest" part of the given key matrix, and
555 // the last row is the sum of input commitments from that column - sum output commitments
556 // this shows that sum inputs = sum outputs
557 //Ver:
558 // verifies the above sig is created corretly
559 bool verRctMG(const mgSig &mg, const ctkeyM & pubs, const ctkeyV & outPk, const key &txnFeeKey, const key &message) {
561 //setup vars
562 size_t cols = pubs.size();
563 CHECK_AND_ASSERT_MES(cols >= 1, false, "Empty pubs");
564 size_t rows = pubs[0].size();
565 CHECK_AND_ASSERT_MES(rows >= 1, false, "Empty pubs");
566 for (size_t i = 1; i < cols; ++i) {
567 CHECK_AND_ASSERT_MES(pubs[i].size() == rows, false, "pubs is not rectangular");
568 }
569
570 keyV tmp(rows + 1);
571 size_t i = 0, j = 0;
572 for (i = 0; i < rows + 1; i++) {
573 identity(tmp[i]);
574 }
575 keyM M(cols, tmp);
576
577 //create the matrix to mg sig
578 for (j = 0; j < rows; j++) {
579 for (i = 0; i < cols; i++) {
580 M[i][j] = pubs[i][j].dest;
581 addKeys(M[i][rows], M[i][rows], pubs[i][j].mask); //add Ci in last row
582 }
583 }
584 for (i = 0; i < cols; i++) {
585 for (j = 0; j < outPk.size(); j++) {
586 subKeys(M[i][rows], M[i][rows], outPk[j].mask); //subtract output Ci's in last row
587 }
588 //subtract txn fee output in last row
589 subKeys(M[i][rows], M[i][rows], txnFeeKey);
590 }
591 return MLSAG_Ver(message, M, mg, rows);
592 }
593
594 //Ring-ct Simple MG sigs
595 //Ver:
596 //This does a simplified version, assuming only post Rct
597 //inputs
598 bool verRctMGSimple(const key &message, const mgSig &mg, const ctkeyV & pubs, const key & C) {
599 try
600 {
602 //setup vars
603 size_t rows = 1;
604 size_t cols = pubs.size();
605 CHECK_AND_ASSERT_MES(cols >= 1, false, "Empty pubs");
606 keyV tmp(rows + 1);
607 size_t i;
608 keyM M(cols, tmp);
609 ge_p3 Cp3;
610 CHECK_AND_ASSERT_MES_L1(ge_frombytes_vartime(&Cp3, C.bytes) == 0, false, "point conv failed");
611 ge_cached Ccached;
612 ge_p3_to_cached(&Ccached, &Cp3);
613 ge_p1p1 p1;
614 //create the matrix to mg sig
615 for (i = 0; i < cols; i++) {
616 M[i][0] = pubs[i].dest;
617 ge_p3 p3;
618 CHECK_AND_ASSERT_MES_L1(ge_frombytes_vartime(&p3, pubs[i].mask.bytes) == 0, false, "point conv failed");
619 ge_sub(&p1, &p3, &Ccached);
620 ge_p1p1_to_p3(&p3, &p1);
621 ge_p3_tobytes(M[i][1].bytes, &p3);
622 }
623 //DP(C);
624 return MLSAG_Ver(message, M, mg, rows);
625 }
626 catch (...) { return false; }
627 }
628
629
630 //These functions get keys from blockchain
631 //replace these when connecting blockchain
632 //getKeyFromBlockchain grabs a key from the blockchain at "reference_index" to mix with
633 //populateFromBlockchain creates a keymatrix with "mixin" columns and one of the columns is inPk
634 // the return value are the key matrix, and the index where inPk was put (random).
635 void getKeyFromBlockchain(ctkey & a, size_t reference_index) {
636 a.mask = pkGen();
637 a.dest = pkGen();
638 }
639
640 //These functions get keys from blockchain
641 //replace these when connecting blockchain
642 //getKeyFromBlockchain grabs a key from the blockchain at "reference_index" to mix with
643 //populateFromBlockchain creates a keymatrix with "mixin" + 1 columns and one of the columns is inPk
644 // the return value are the key matrix, and the index where inPk was put (random).
645 tuple<ctkeyM, etn_amount> populateFromBlockchain(ctkeyV inPk, int mixin) {
646 int rows = inPk.size();
647 ctkeyM rv(mixin + 1, inPk);
648 int index = randEtnAmount(mixin);
649 int i = 0, j = 0;
650 for (i = 0; i <= mixin; i++) {
651 if (i != index) {
652 for (j = 0; j < rows; j++) {
653 getKeyFromBlockchain(rv[i][j], (size_t)randEtnAmount);
654 }
655 }
656 }
657 return make_tuple(rv, index);
658 }
659
660 //These functions get keys from blockchain
661 //replace these when connecting blockchain
662 //getKeyFromBlockchain grabs a key from the blockchain at "reference_index" to mix with
663 //populateFromBlockchain creates a keymatrix with "mixin" columns and one of the columns is inPk
664 // the return value are the key matrix, and the index where inPk was put (random).
665 etn_amount populateFromBlockchainSimple(ctkeyV & mixRing, const ctkey & inPk, int mixin) {
666 int index = randEtnAmount(mixin);
667 int i = 0;
668 for (i = 0; i <= mixin; i++) {
669 if (i != index) {
670 getKeyFromBlockchain(mixRing[i], (size_t)randEtnAmount(1000));
671 } else {
672 mixRing[i] = inPk;
673 }
674 }
675 return index;
676 }
677
678 //RingCT protocol
679 //genRct:
680 // creates an rctSig with all data necessary to verify the rangeProofs and that the signer owns one of the
681 // columns that are claimed as inputs, and that the sum of inputs = sum of outputs.
682 // Also contains masked "amount" and "mask" so the receiver can see how much they received
683 //verRct:
684 // verifies that all signatures (rangeProogs, MG sig, sum inputs = outputs) are correct
685 //decodeRct: (c.f. https://eprint.iacr.org/2015/1098 section 5.1.1)
686 // uses the attached ecdh info to find the amounts represented by each output commitment
687 // must know the destination private key to find the correct amount, else will return a random number
688 // Note: For txn fees, the last index in the amounts vector should contain that
689 // Thus the amounts vector will be "one" longer than the destinations vectort
690 rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector<etn_amount> & amounts, const ctkeyM &mixRing, const keyV &amount_keys, const multisig_kLRki *kLRki, multisig_out *msout, unsigned int index, ctkeyV &outSk, const RCTConfig &rct_config, hw::device &hwdev) {
691 CHECK_AND_ASSERT_THROW_MES(amounts.size() == destinations.size() || amounts.size() == destinations.size() + 1, "Different number of amounts/destinations");
692 CHECK_AND_ASSERT_THROW_MES(amount_keys.size() == destinations.size(), "Different number of amount_keys/destinations");
693 CHECK_AND_ASSERT_THROW_MES(index < mixRing.size(), "Bad index into mixRing");
694 for (size_t n = 0; n < mixRing.size(); ++n) {
695 CHECK_AND_ASSERT_THROW_MES(mixRing[n].size() == inSk.size(), "Bad mixRing size");
696 }
697 CHECK_AND_ASSERT_THROW_MES((kLRki && msout) || (!kLRki && !msout), "Only one of kLRki/msout is present");
698 CHECK_AND_ASSERT_THROW_MES(inSk.size() < 2, "genRct is not suitable for 2+ rings");
699
700 rctSig rv;
701 rv.type = RCTTypeFull;
702 rv.message = message;
703 rv.outPk.resize(destinations.size());
704 rv.p.rangeSigs.resize(destinations.size());
705 rv.ecdhInfo.resize(destinations.size());
706
707 size_t i = 0;
708 keyV masks(destinations.size()); //sk mask..
709 outSk.resize(destinations.size());
710 for (i = 0; i < destinations.size(); i++) {
711 //add destination to sig
712 rv.outPk[i].dest = copy(destinations[i]);
713 //compute range proof
714 rv.p.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, amounts[i]);
715 #ifdef DBG
716 CHECK_AND_ASSERT_THROW_MES(verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]), "verRange failed on newly created proof");
717 #endif
718 //mask amount and mask
719 rv.ecdhInfo[i].mask = copy(outSk[i].mask);
720 rv.ecdhInfo[i].amount = d2h(amounts[i]);
721 hwdev.ecdhEncode(rv.ecdhInfo[i], amount_keys[i], rv.type == RCTTypeBulletproof2);
722 }
723
724 //set txn fee
725 if (amounts.size() > destinations.size())
726 {
727 rv.txnFee = amounts[destinations.size()];
728 }
729 else
730 {
731 rv.txnFee = 0;
732 }
733 key txnFeeKey = scalarmultH(d2h(rv.txnFee));
734
735 rv.mixRing = mixRing;
736 if (msout)
737 msout->c.resize(1);
738 rv.p.MGs.push_back(proveRctMG(get_pre_mlsag_hash(rv, hwdev), rv.mixRing, inSk, outSk, rv.outPk, kLRki, msout ? &msout->c[0] : NULL, index, txnFeeKey,hwdev));
739 return rv;
740 }
741
742 rctSig genRct(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector<etn_amount> & amounts, const keyV &amount_keys, const multisig_kLRki *kLRki, multisig_out *msout, const int mixin, const RCTConfig &rct_config, hw::device &hwdev) {
743 unsigned int index;
744 ctkeyM mixRing;
745 ctkeyV outSk;
746 tie(mixRing, index) = populateFromBlockchain(inPk, mixin);
747 return genRct(message, inSk, destinations, amounts, mixRing, amount_keys, kLRki, msout, index, outSk, rct_config, hwdev);
748 }
749
750 //RCT simple
751 //for post-rct only
752 rctSig genRctSimple(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector<etn_amount> &inamounts, const vector<etn_amount> &outamounts, etn_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector<multisig_kLRki> *kLRki, multisig_out *msout, const std::vector<unsigned int> & index, ctkeyV &outSk, const RCTConfig &rct_config, hw::device &hwdev) {
753 const bool bulletproof = rct_config.range_proof_type != RangeProofBorromean;
754 CHECK_AND_ASSERT_THROW_MES(inamounts.size() > 0, "Empty inamounts");
755 CHECK_AND_ASSERT_THROW_MES(inamounts.size() == inSk.size(), "Different number of inamounts/inSk");
756 CHECK_AND_ASSERT_THROW_MES(outamounts.size() == destinations.size(), "Different number of amounts/destinations");
757 CHECK_AND_ASSERT_THROW_MES(amount_keys.size() == destinations.size(), "Different number of amount_keys/destinations");
758 CHECK_AND_ASSERT_THROW_MES(index.size() == inSk.size(), "Different number of index/inSk");
759 CHECK_AND_ASSERT_THROW_MES(mixRing.size() == inSk.size(), "Different number of mixRing/inSk");
760 for (size_t n = 0; n < mixRing.size(); ++n) {
761 CHECK_AND_ASSERT_THROW_MES(index[n] < mixRing[n].size(), "Bad index into mixRing");
762 }
763 CHECK_AND_ASSERT_THROW_MES((kLRki && msout) || (!kLRki && !msout), "Only one of kLRki/msout is present");
764 if (kLRki && msout) {
765 CHECK_AND_ASSERT_THROW_MES(kLRki->size() == inamounts.size(), "Mismatched kLRki/inamounts sizes");
766 }
767
768 rctSig rv;
769 rv.type = bulletproof ? (rct_config.bp_version == 0 || rct_config.bp_version >= 2 ? RCTTypeBulletproof2 : RCTTypeBulletproof) : RCTTypeSimple;
770 rv.message = message;
771 rv.outPk.resize(destinations.size());
772 if (!bulletproof)
773 rv.p.rangeSigs.resize(destinations.size());
774 rv.ecdhInfo.resize(destinations.size());
775
776 size_t i;
777 keyV masks(destinations.size()); //sk mask..
778 outSk.resize(destinations.size());
779 for (i = 0; i < destinations.size(); i++) {
780
781 //add destination to sig
782 rv.outPk[i].dest = copy(destinations[i]);
783 //compute range proof
784 if (!bulletproof)
785 rv.p.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, outamounts[i]);
786 #ifdef DBG
787 if (!bulletproof)
788 CHECK_AND_ASSERT_THROW_MES(verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]), "verRange failed on newly created proof");
789 #endif
790 }
791
792 rv.p.bulletproofs.clear();
793 if (bulletproof)
794 {
795 size_t n_amounts = outamounts.size();
796 size_t amounts_proved = 0;
798 {
799 rct::keyV C, masks;
801 {
802 // use a fake bulletproof for speed
803 rv.p.bulletproofs.push_back(make_dummy_bulletproof(outamounts, C, masks));
804 }
805 else
806 {
807 const epee::span<const key> keys{&amount_keys[0], amount_keys.size()};
808 rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, outamounts, keys, hwdev));
809 #ifdef DBG
810 CHECK_AND_ASSERT_THROW_MES(verBulletproof(rv.p.bulletproofs.back()), "verBulletproof failed on newly created proof");
811 #endif
812 }
813 for (i = 0; i < outamounts.size(); ++i)
814 {
815 rv.outPk[i].mask = rct::scalarmult8(C[i]);
816 outSk[i].mask = masks[i];
817 }
818 }
819 else while (amounts_proved < n_amounts)
820 {
821 size_t batch_size = 1;
823 while (batch_size * 2 + amounts_proved <= n_amounts && batch_size * 2 <= BULLETPROOF_MAX_OUTPUTS)
824 batch_size *= 2;
825 rct::keyV C, masks;
826 std::vector<uint64_t> batch_amounts(batch_size);
827 for (i = 0; i < batch_size; ++i)
828 batch_amounts[i] = outamounts[i + amounts_proved];
830 {
831 // use a fake bulletproof for speed
832 rv.p.bulletproofs.push_back(make_dummy_bulletproof(batch_amounts, C, masks));
833 }
834 else
835 {
836 const epee::span<const key> keys{&amount_keys[amounts_proved], batch_size};
837 rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, batch_amounts, keys, hwdev));
838 #ifdef DBG
839 CHECK_AND_ASSERT_THROW_MES(verBulletproof(rv.p.bulletproofs.back()), "verBulletproof failed on newly created proof");
840 #endif
841 }
842 for (i = 0; i < batch_size; ++i)
843 {
844 rv.outPk[i + amounts_proved].mask = rct::scalarmult8(C[i]);
845 outSk[i + amounts_proved].mask = masks[i];
846 }
847 amounts_proved += batch_size;
848 }
849 }
850
851 key sumout = zero();
852 for (i = 0; i < outSk.size(); ++i)
853 {
854 sc_add(sumout.bytes, outSk[i].mask.bytes, sumout.bytes);
855
856 //mask amount and mask
857 rv.ecdhInfo[i].mask = copy(outSk[i].mask);
858 rv.ecdhInfo[i].amount = d2h(outamounts[i]);
859 hwdev.ecdhEncode(rv.ecdhInfo[i], amount_keys[i], rv.type == RCTTypeBulletproof2);
860 }
861
862 //set txn fee
863 rv.txnFee = txnFee;
864// TODO: unused ??
865// key txnFeeKey = scalarmultH(d2h(rv.txnFee));
866 rv.mixRing = mixRing;
867 keyV &pseudoOuts = bulletproof ? rv.p.pseudoOuts : rv.pseudoOuts;
868 pseudoOuts.resize(inamounts.size());
869 rv.p.MGs.resize(inamounts.size());
870 key sumpouts = zero(); //sum pseudoOut masks
871 keyV a(inamounts.size());
872 for (i = 0 ; i < inamounts.size() - 1; i++) {
873 skGen(a[i]);
874 sc_add(sumpouts.bytes, a[i].bytes, sumpouts.bytes);
875 genC(pseudoOuts[i], a[i], inamounts[i]);
876 }
877 sc_sub(a[i].bytes, sumout.bytes, sumpouts.bytes);
878 genC(pseudoOuts[i], a[i], inamounts[i]);
879 DP(pseudoOuts[i]);
880
881 key full_message = get_pre_mlsag_hash(rv,hwdev);
882 if (msout)
883 msout->c.resize(inamounts.size());
884 for (i = 0 ; i < inamounts.size(); i++) {
885 rv.p.MGs[i] = proveRctMGSimple(full_message, rv.mixRing[i], inSk[i], a[i], pseudoOuts[i], kLRki ? &(*kLRki)[i]: NULL, msout ? &msout->c[i] : NULL, index[i], hwdev);
886 }
887 return rv;
888 }
889
890 rctSig genRctSimple(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector<etn_amount> &inamounts, const vector<etn_amount> &outamounts, const keyV &amount_keys, const std::vector<multisig_kLRki> *kLRki, multisig_out *msout, etn_amount txnFee, unsigned int mixin, const RCTConfig &rct_config, hw::device &hwdev) {
891 std::vector<unsigned int> index;
892 index.resize(inPk.size());
893 ctkeyM mixRing;
894 ctkeyV outSk;
895 mixRing.resize(inPk.size());
896 for (size_t i = 0; i < inPk.size(); ++i) {
897 mixRing[i].resize(mixin+1);
898 index[i] = populateFromBlockchainSimple(mixRing[i], inPk[i], mixin);
899 }
900 return genRctSimple(message, inSk, destinations, inamounts, outamounts, txnFee, mixRing, amount_keys, kLRki, msout, index, outSk, rct_config, hwdev);
901 }
902
903 //RingCT protocol
904 //genRct:
905 // creates an rctSig with all data necessary to verify the rangeProofs and that the signer owns one of the
906 // columns that are claimed as inputs, and that the sum of inputs = sum of outputs.
907 // Also contains masked "amount" and "mask" so the receiver can see how much they received
908 //verRct:
909 // verifies that all signatures (rangeProogs, MG sig, sum inputs = outputs) are correct
910 //decodeRct: (c.f. https://eprint.iacr.org/2015/1098 section 5.1.1)
911 // uses the attached ecdh info to find the amounts represented by each output commitment
912 // must know the destination private key to find the correct amount, else will return a random number
913 bool verRct(const rctSig & rv, bool semantics) {
915 CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull, false, "verRct called on non-full rctSig");
916 if (semantics)
917 {
918 CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.p.rangeSigs");
919 CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of outPk and rv.ecdhInfo");
920 CHECK_AND_ASSERT_MES(rv.p.MGs.size() == 1, false, "full rctSig has not one MG");
921 }
922 else
923 {
924 // semantics check is early, we don't have the MGs resolved yet
925 }
926
927 // some rct ops can throw
928 try
929 {
930 if (semantics) {
933 std::deque<bool> results(rv.outPk.size(), false);
934 DP("range proofs verified?");
935 for (size_t i = 0; i < rv.outPk.size(); i++)
936 tpool.submit(&waiter, [&, i] { results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); });
937 waiter.wait(&tpool);
938
939 for (size_t i = 0; i < results.size(); ++i) {
940 if (!results[i]) {
941 LOG_PRINT_L1("Range proof verified failed for proof " << i);
942 return false;
943 }
944 }
945 }
946
947 if (!semantics) {
948 //compute txn fee
949 key txnFeeKey = scalarmultH(d2h(rv.txnFee));
950 bool mgVerd = verRctMG(rv.p.MGs[0], rv.mixRing, rv.outPk, txnFeeKey, get_pre_mlsag_hash(rv, hw::get_device("default")));
951 DP("mg sig verified?");
952 DP(mgVerd);
953 if (!mgVerd) {
954 LOG_PRINT_L1("MG signature verification failed");
955 return false;
956 }
957 }
958
959 return true;
960 }
961 catch (const std::exception &e)
962 {
963 LOG_PRINT_L1("Error in verRct: " << e.what());
964 return false;
965 }
966 catch (...)
967 {
968 LOG_PRINT_L1("Error in verRct, but not an actual exception");
969 return false;
970 }
971 }
972
973 //ver RingCT simple
974 //assumes only post-rct style inputs (at least for max anonymity)
975 bool verRctSemanticsSimple(const std::vector<const rctSig*> & rvv) {
976 try
977 {
979
982 std::deque<bool> results;
983 std::vector<const Bulletproof*> proofs;
984 size_t max_non_bp_proofs = 0, offset = 0;
985
986 for (const rctSig *rvp: rvv)
987 {
988 CHECK_AND_ASSERT_MES(rvp, false, "rctSig pointer is NULL");
989 const rctSig &rv = *rvp;
991 false, "verRctSemanticsSimple called on non simple rctSig");
992 const bool bulletproof = is_rct_bulletproof(rv.type);
993 if (bulletproof)
994 {
995 CHECK_AND_ASSERT_MES(rv.outPk.size() == n_bulletproof_amounts(rv.p.bulletproofs), false, "Mismatched sizes of outPk and bulletproofs");
996 CHECK_AND_ASSERT_MES(rv.p.pseudoOuts.size() == rv.p.MGs.size(), false, "Mismatched sizes of rv.p.pseudoOuts and rv.p.MGs");
997 CHECK_AND_ASSERT_MES(rv.pseudoOuts.empty(), false, "rv.pseudoOuts is not empty");
998 }
999 else
1000 {
1001 CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.p.rangeSigs");
1002 CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == rv.p.MGs.size(), false, "Mismatched sizes of rv.pseudoOuts and rv.p.MGs");
1003 CHECK_AND_ASSERT_MES(rv.p.pseudoOuts.empty(), false, "rv.p.pseudoOuts is not empty");
1004 }
1005 CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of outPk and rv.ecdhInfo");
1006
1007 if (!bulletproof)
1008 max_non_bp_proofs += rv.p.rangeSigs.size();
1009 }
1010
1011 results.resize(max_non_bp_proofs);
1012 for (const rctSig *rvp: rvv)
1013 {
1014 const rctSig &rv = *rvp;
1015
1016 const bool bulletproof = is_rct_bulletproof(rv.type);
1017 const keyV &pseudoOuts = bulletproof ? rv.p.pseudoOuts : rv.pseudoOuts;
1018
1019 rct::keyV masks(rv.outPk.size());
1020 for (size_t i = 0; i < rv.outPk.size(); i++) {
1021 masks[i] = rv.outPk[i].mask;
1022 }
1023 key sumOutpks = addKeys(masks);
1024 DP(sumOutpks);
1025 const key txnFeeKey = scalarmultH(d2h(rv.txnFee));
1026 addKeys(sumOutpks, txnFeeKey, sumOutpks);
1027
1028 key sumPseudoOuts = addKeys(pseudoOuts);
1029 DP(sumPseudoOuts);
1030
1031 //check pseudoOuts vs Outs..
1032 if (!equalKeys(sumPseudoOuts, sumOutpks)) {
1033 LOG_PRINT_L1("Sum check failed");
1034 return false;
1035 }
1036
1037 if (bulletproof)
1038 {
1039 for (size_t i = 0; i < rv.p.bulletproofs.size(); i++)
1040 proofs.push_back(&rv.p.bulletproofs[i]);
1041 }
1042 else
1043 {
1044 for (size_t i = 0; i < rv.p.rangeSigs.size(); i++)
1045 tpool.submit(&waiter, [&, i, offset] { results[i+offset] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); });
1046 offset += rv.p.rangeSigs.size();
1047 }
1048 }
1049 if (!proofs.empty() && !verBulletproof(proofs))
1050 {
1051 LOG_PRINT_L1("Aggregate range proof verified failed");
1052 return false;
1053 }
1054
1055 waiter.wait(&tpool);
1056 for (size_t i = 0; i < results.size(); ++i) {
1057 if (!results[i]) {
1058 LOG_PRINT_L1("Range proof verified failed for proof " << i);
1059 return false;
1060 }
1061 }
1062
1063 return true;
1064 }
1065 // we can get deep throws from ge_frombytes_vartime if input isn't valid
1066 catch (const std::exception &e)
1067 {
1068 LOG_PRINT_L1("Error in verRctSemanticsSimple: " << e.what());
1069 return false;
1070 }
1071 catch (...)
1072 {
1073 LOG_PRINT_L1("Error in verRctSemanticsSimple, but not an actual exception");
1074 return false;
1075 }
1076 }
1077
1079 {
1080 return verRctSemanticsSimple(std::vector<const rctSig*>(1, &rv));
1081 }
1082
1083 //ver RingCT simple
1084 //assumes only post-rct style inputs (at least for max anonymity)
1086 try
1087 {
1089
1091 false, "verRctNonSemanticsSimple called on non simple rctSig");
1092 const bool bulletproof = is_rct_bulletproof(rv.type);
1093 // semantics check is early, and mixRing/MGs aren't resolved yet
1094 if (bulletproof)
1095 CHECK_AND_ASSERT_MES(rv.p.pseudoOuts.size() == rv.mixRing.size(), false, "Mismatched sizes of rv.p.pseudoOuts and mixRing");
1096 else
1097 CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == rv.mixRing.size(), false, "Mismatched sizes of rv.pseudoOuts and mixRing");
1098
1099 const size_t threads = std::max(rv.outPk.size(), rv.mixRing.size());
1100
1101 std::deque<bool> results(threads);
1104
1105 const keyV &pseudoOuts = bulletproof ? rv.p.pseudoOuts : rv.pseudoOuts;
1106
1107 const key message = get_pre_mlsag_hash(rv, hw::get_device("default"));
1108
1109 results.clear();
1110 results.resize(rv.mixRing.size());
1111 for (size_t i = 0 ; i < rv.mixRing.size() ; i++) {
1112 tpool.submit(&waiter, [&, i] {
1113 results[i] = verRctMGSimple(message, rv.p.MGs[i], rv.mixRing[i], pseudoOuts[i]);
1114 });
1115 }
1116 waiter.wait(&tpool);
1117
1118 for (size_t i = 0; i < results.size(); ++i) {
1119 if (!results[i]) {
1120 LOG_PRINT_L1("verRctMGSimple failed for input " << i);
1121 return false;
1122 }
1123 }
1124
1125 return true;
1126 }
1127 // we can get deep throws from ge_frombytes_vartime if input isn't valid
1128 catch (const std::exception &e)
1129 {
1130 LOG_PRINT_L1("Error in verRctNonSemanticsSimple: " << e.what());
1131 return false;
1132 }
1133 catch (...)
1134 {
1135 LOG_PRINT_L1("Error in verRctNonSemanticsSimple, but not an actual exception");
1136 return false;
1137 }
1138 }
1139
1140 //RingCT protocol
1141 //genRct:
1142 // creates an rctSig with all data necessary to verify the rangeProofs and that the signer owns one of the
1143 // columns that are claimed as inputs, and that the sum of inputs = sum of outputs.
1144 // Also contains masked "amount" and "mask" so the receiver can see how much they received
1145 //verRct:
1146 // verifies that all signatures (rangeProogs, MG sig, sum inputs = outputs) are correct
1147 //decodeRct: (c.f. https://eprint.iacr.org/2015/1098 section 5.1.1)
1148 // uses the attached ecdh info to find the amounts represented by each output commitment
1149 // must know the destination private key to find the correct amount, else will return a random number
1150 etn_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, key & mask, hw::device &hwdev) {
1151 CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull, false, "decodeRct called on non-full rctSig");
1152 CHECK_AND_ASSERT_THROW_MES(i < rv.ecdhInfo.size(), "Bad index");
1153 CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.ecdhInfo.size(), "Mismatched sizes of rv.outPk and rv.ecdhInfo");
1154
1155 //mask amount and mask
1156 ecdhTuple ecdh_info = rv.ecdhInfo[i];
1157 hwdev.ecdhDecode(ecdh_info, sk, rv.type == RCTTypeBulletproof2);
1158 mask = ecdh_info.mask;
1159 key amount = ecdh_info.amount;
1160 key C = rv.outPk[i].mask;
1161 DP("C");
1162 DP(C);
1163 key Ctmp;
1164 CHECK_AND_ASSERT_THROW_MES(sc_check(mask.bytes) == 0, "warning, bad ECDH mask");
1165 CHECK_AND_ASSERT_THROW_MES(sc_check(amount.bytes) == 0, "warning, bad ECDH amount");
1166 addKeys2(Ctmp, mask, amount, H);
1167 DP("Ctmp");
1168 DP(Ctmp);
1169 if (equalKeys(C, Ctmp) == false) {
1170 CHECK_AND_ASSERT_THROW_MES(false, "warning, amount decoded incorrectly, will be unable to spend");
1171 }
1172 return h2d(amount);
1173 }
1174
1175 etn_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, hw::device &hwdev) {
1176 key mask;
1177 return decodeRct(rv, sk, i, mask, hwdev);
1178 }
1179
1180 etn_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i, key &mask, hw::device &hwdev) {
1181 CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2, false, "decodeRct called on non simple rctSig");
1182 CHECK_AND_ASSERT_THROW_MES(i < rv.ecdhInfo.size(), "Bad index");
1183 CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.ecdhInfo.size(), "Mismatched sizes of rv.outPk and rv.ecdhInfo");
1184
1185 //mask amount and mask
1186 ecdhTuple ecdh_info = rv.ecdhInfo[i];
1187 hwdev.ecdhDecode(ecdh_info, sk, rv.type == RCTTypeBulletproof2);
1188 mask = ecdh_info.mask;
1189 key amount = ecdh_info.amount;
1190 key C = rv.outPk[i].mask;
1191 DP("C");
1192 DP(C);
1193 key Ctmp;
1194 CHECK_AND_ASSERT_THROW_MES(sc_check(mask.bytes) == 0, "warning, bad ECDH mask");
1195 CHECK_AND_ASSERT_THROW_MES(sc_check(amount.bytes) == 0, "warning, bad ECDH amount");
1196 addKeys2(Ctmp, mask, amount, H);
1197 DP("Ctmp");
1198 DP(Ctmp);
1199 if (equalKeys(C, Ctmp) == false) {
1200 CHECK_AND_ASSERT_THROW_MES(false, "warning, amount decoded incorrectly, will be unable to spend");
1201 }
1202 return h2d(amount);
1203 }
1204
1205 etn_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i, hw::device &hwdev) {
1206 key mask;
1207 return decodeRctSimple(rv, sk, i, mask, hwdev);
1208 }
1209
1210 bool signMultisig(rctSig &rv, const std::vector<unsigned int> &indices, const keyV &k, const multisig_out &msout, const key &secret_key) {
1212 false, "unsupported rct type");
1213 CHECK_AND_ASSERT_MES(indices.size() == k.size(), false, "Mismatched k/indices sizes");
1214 CHECK_AND_ASSERT_MES(k.size() == rv.p.MGs.size(), false, "Mismatched k/MGs size");
1215 CHECK_AND_ASSERT_MES(k.size() == msout.c.size(), false, "Mismatched k/msout.c size");
1216 if (rv.type == RCTTypeFull)
1217 {
1218 CHECK_AND_ASSERT_MES(rv.p.MGs.size() == 1, false, "MGs not a single element");
1219 }
1220 for (size_t n = 0; n < indices.size(); ++n) {
1221 CHECK_AND_ASSERT_MES(indices[n] < rv.p.MGs[n].ss.size(), false, "Index out of range");
1222 CHECK_AND_ASSERT_MES(!rv.p.MGs[n].ss[indices[n]].empty(), false, "empty ss line");
1223 }
1224
1225 for (size_t n = 0; n < indices.size(); ++n) {
1226 rct::key diff;
1227 sc_mulsub(diff.bytes, msout.c[n].bytes, secret_key.bytes, k[n].bytes);
1228 sc_add(rv.p.MGs[n].ss[indices[n]][0].bytes, rv.p.MGs[n].ss[indices[n]][0].bytes, diff.bytes);
1229 }
1230 return true;
1231 }
1232}
Non-owning sequence of data. Does not deep copy.
Definition span.h:57
constexpr std::size_t size() const noexcept
Definition span.h:111
virtual bool ecdhDecode(rct::ecdhTuple &masked, const rct::key &sharedSec, bool short_amount)=0
virtual bool ecdhEncode(rct::ecdhTuple &unmasked, const rct::key &sharedSec, bool short_amount)=0
@ TRANSACTION_CREATE_FAKE
Definition device.hpp:101
virtual bool mlsag_sign(const rct::key &c, const rct::keyV &xx, const rct::keyV &alpha, const size_t rows, const size_t dsRows, rct::keyV &ss)=0
virtual device_mode get_mode() const
Definition device.hpp:131
virtual bool mlsag_hash(const rct::keyV &long_message, rct::key &c)=0
virtual bool mlsag_prehash(const std::string &blob, size_t inputs_size, size_t outputs_size, const rct::keyV &hashes, const rct::ctkeyV &outPk, rct::key &prehash)=0
virtual bool mlsag_prepare(const rct::key &H, const rct::key &xx, rct::key &a, rct::key &aG, rct::key &aHP, rct::key &rvII)=0
virtual rct::key genCommitmentMask(const rct::key &amount_key)=0
void wait(threadpool *tpool)
A global thread pool.
Definition threadpool.h:44
void submit(waiter *waiter, std::function< void()> f, bool leaf=false)
static threadpool & getInstance()
Definition threadpool.h:46
void sc_0(unsigned char *)
int ge_frombytes_vartime(ge_p3 *, const unsigned char *)
void sc_sub(unsigned char *, const unsigned char *, const unsigned char *)
void sc_mulsub(unsigned char *, const unsigned char *, const unsigned char *, const unsigned char *)
int sc_check(const unsigned char *)
void ge_double_scalarmult_base_vartime(ge_p2 *, const unsigned char *, const ge_p3 *, const unsigned char *)
int sc_isnonzero(const unsigned char *)
const ge_p3 ge_p3_identity
void sc_add(unsigned char *, const unsigned char *, const unsigned char *)
#define BULLETPROOF_MAX_OUTPUTS
std::string message("Message requiring signing")
void * memwipe(void *src, size_t n)
#define CHECK_AND_ASSERT_MES(expr, fail_ret_val, message)
#define LOG_PRINT_L1(x)
#define CHECK_AND_ASSERT_THROW_MES(expr, message)
crypto namespace.
Definition crypto.cpp:58
epee::mlocked< tools::scrubbed< ec_scalar > > secret_key
Definition crypto.h:82
void sc_mul(unsigned char *, const unsigned char *, const unsigned char *)
int rows
Definition crypto.h:86
POD_CLASS hash
Definition hash.h:50
void get_blob_hash(const epee::span< const char > &blob, crypto::hash &res)
device & get_device(const std::string &device_descriptor)
Definition device.cpp:95
void scalarmultBase(key &aG, const key &a)
Definition rctOps.cpp:350
tuple< ctkeyM, etn_amount > populateFromBlockchain(ctkeyV inPk, int mixin)
Definition rctSigs.cpp:645
bool bulletproof_VERIFY(const Bulletproof &proof)
void hash_to_scalar(key &hash, const void *data, const std::size_t l)
Definition rctOps.cpp:536
etn_amount decodeRctSimple(const rctSig &rv, const key &sk, unsigned int i, key &mask, hw::device &hwdev)
Definition rctSigs.cpp:1180
void cn_fast_hash(key &hash, const void *data, const std::size_t l)
Definition rctOps.cpp:532
uint64_t etn_amount
Definition rctTypes.h:135
etn_amount populateFromBlockchainSimple(ctkeyV &mixRing, const ctkey &inPk, int mixin)
Definition rctSigs.cpp:665
std::vector< key > keyV
Definition rctTypes.h:88
std::vector< keyV > keyM
Definition rctTypes.h:89
std::vector< ctkeyV > ctkeyM
Definition rctTypes.h:101
key scalarmultH(const key &a)
Definition rctOps.cpp:389
size_t n_bulletproof_amounts(const Bulletproof &proof)
Definition rctTypes.cpp:251
void getKeyFromBlockchain(ctkey &a, size_t reference_index)
Definition rctSigs.cpp:635
bool verRange(const key &C, const rangeSig &as)
Definition rctSigs.cpp:368
key skGen()
Definition rctOps.cpp:258
void subKeys(key &AB, const key &A, const key &B)
Definition rctOps.cpp:505
key get_pre_mlsag_hash(const rctSig &rv, hw::device &hwdev)
Definition rctSigs.cpp:403
etn_amount decodeRct(const rctSig &rv, const key &sk, unsigned int i, key &mask, hw::device &hwdev)
Definition rctSigs.cpp:1150
bool is_rct_bulletproof(int type)
Definition rctTypes.cpp:227
Bulletproof bulletproof_PROVE(const rct::key &v, const rct::key &gamma)
void genC(key &C, const key &a, etn_amount amount)
Definition rctOps.cpp:297
boroSig genBorromean(const key64 x, const key64 P1, const key64 P2, const bits indices)
Definition rctSigs.cpp:109
key key64[64]
Definition rctTypes.h:137
bool verRct(const rctSig &rv, bool semantics)
Definition rctSigs.cpp:913
tuple< key, key > skpkGen()
Definition rctOps.cpp:290
unsigned int bits[ATOMS]
Definition rctTypes.h:136
rctSig genRct(const key &message, const ctkeyV &inSk, const keyV &destinations, const vector< etn_amount > &amounts, const ctkeyM &mixRing, const keyV &amount_keys, const multisig_kLRki *kLRki, multisig_out *msout, unsigned int index, ctkeyV &outSk, const RCTConfig &rct_config, hw::device &hwdev)
Definition rctSigs.cpp:690
etn_amount h2d(const key &test)
Definition rctTypes.cpp:161
void addKeys1(key &aGB, const key &a, const key &B)
Definition rctOps.cpp:459
std::vector< ctkey > ctkeyV
Definition rctTypes.h:100
@ RCTTypeFull
Definition rctTypes.h:230
@ RCTTypeSimple
Definition rctTypes.h:231
@ RCTTypeBulletproof2
Definition rctTypes.h:233
@ RCTTypeBulletproof
Definition rctTypes.h:232
mgSig proveRctMG(const key &message, const ctkeyM &pubs, const ctkeyV &inSk, const ctkeyV &outSk, const ctkeyV &outPk, const multisig_kLRki *kLRki, key *mscout, unsigned int index, const key &txnFeeKey, hw::device &hwdev)
Definition rctSigs.cpp:471
keyV skvGen(size_t rows)
Definition rctOps.cpp:266
void addKeys(key &AB, const key &A, const key &B)
Definition rctOps.cpp:420
bool verRctSemanticsSimple(const std::vector< const rctSig * > &rvv)
Definition rctSigs.cpp:975
bool verRctNonSemanticsSimple(const rctSig &rv)
Definition rctSigs.cpp:1085
key scalarmult8(const key &P)
Definition rctOps.cpp:398
mgSig MLSAG_Gen(const key &message, const keyM &pk, const keyV &xx, const multisig_kLRki *kLRki, key *mscout, const unsigned int index, size_t dsRows, hw::device &hwdev)
Definition rctSigs.cpp:174
void d2h(key &amounth, const etn_amount in)
Definition rctTypes.cpp:119
bool signMultisig(rctSig &rv, const std::vector< unsigned int > &indices, const keyV &k, const multisig_out &msout, const key &secret_key)
Definition rctSigs.cpp:1210
key zero()
Definition rctOps.h:70
Bulletproof proveRangeBulletproof(keyV &C, keyV &masks, const std::vector< uint64_t > &amounts, epee::span< const key > sk, hw::device &hwdev)
Definition rctSigs.cpp:82
void addKeys2(key &aGbB, const key &a, const key &b, const key &B)
Definition rctOps.cpp:466
key pkGen()
Definition rctOps.cpp:277
void copy(key &AA, const key &A)
Definition rctOps.h:79
bool is_rct_simple(int type)
Definition rctTypes.cpp:214
bool verRctMGSimple(const key &message, const mgSig &mg, const ctkeyV &pubs, const key &C)
Definition rctSigs.cpp:598
key hashToPoint(const key &hh)
Definition rctOps.cpp:638
void precomp(ge_dsmp rv, const key &B)
Definition rctOps.cpp:476
@ RangeProofBorromean
Definition rctTypes.h:235
@ RangeProofMultiOutputBulletproof
Definition rctTypes.h:235
@ RangeProofPaddedBulletproof
Definition rctTypes.h:235
void d2b(bits amountb, etn_amount val)
Definition rctTypes.cpp:145
rctSig genRctSimple(const key &message, const ctkeyV &inSk, const keyV &destinations, const vector< etn_amount > &inamounts, const vector< etn_amount > &outamounts, etn_amount txnFee, const ctkeyM &mixRing, const keyV &amount_keys, const std::vector< multisig_kLRki > *kLRki, multisig_out *msout, const std::vector< unsigned int > &index, ctkeyV &outSk, const RCTConfig &rct_config, hw::device &hwdev)
Definition rctSigs.cpp:752
bool verBulletproof(const Bulletproof &proof)
Definition rctSigs.cpp:94
mgSig proveRctMGSimple(const key &message, const ctkeyV &pubs, const ctkey &inSk, const key &a, const key &Cout, const multisig_kLRki *kLRki, key *mscout, unsigned int index, hw::device &hwdev)
Definition rctSigs.cpp:528
bool MLSAG_Ver(const key &message, const keyM &pk, const mgSig &rv, size_t dsRows)
Definition rctSigs.cpp:271
void addKeys3(key &aAbB, const key &a, const key &A, const key &b, const ge_dsmp B)
Definition rctOps.cpp:485
bool equalKeys(const key &a, const key &b)
Definition rctOps.cpp:519
rangeSig proveRange(key &C, key &mask, const etn_amount &amount)
Definition rctSigs.cpp:336
bool verifyBorromean(const boroSig &bb, const ge_p3 P1[64], const ge_p3 P2[64])
Definition rctSigs.cpp:140
key identity()
Definition rctOps.h:73
etn_amount randEtnAmount(etn_amount upperlimit)
Definition rctOps.cpp:343
bool verRctMG(const mgSig &mg, const ctkeyM &pubs, const ctkeyV &outPk, const key &txnFeeKey, const key &message)
Definition rctSigs.cpp:559
STL namespace.
#define PERF_TIMER(name)
Definition perf_timer.h:82
const GenericPointer< typename T::ValueType > T2 T::AllocatorType & a
Definition pointer.h:1124
#define DP(x)
Definition rctOps.h:55
#define CHECK_AND_ASSERT_MES_L1(expr, ret, message)
Definition rctSigs.cpp:45
#define ATOMS
Definition rctTypes.h:65
#define ge_sub
Definition ge.h:70
#define ge_p1p1_to_p3
Definition ge.h:63
#define ge_p3_tobytes
Definition ge.h:55
#define ge_p3_to_cached
Definition ge.h:61
#define ge_add
Definition ge.h:69
#define ge_tobytes
Definition ge.h:54
CXA_THROW_INFO_T void(* dest)(void *))
rct::keyV V
Definition rctTypes.h:181
RangeProofType range_proof_type
Definition rctTypes.h:237
unsigned char bytes[32]
Definition rctTypes.h:86
std::vector< key > c
Definition rctTypes.h:112
boroSig asig
Definition rctTypes.h:170
etn_amount txnFee
Definition rctTypes.h:248
std::vector< ecdhTuple > ecdhInfo
Definition rctTypes.h:246
rctSigPrunable p
Definition rctTypes.h:437
std::vector< mgSig > MGs
Definition rctTypes.h:321
std::vector< rangeSig > rangeSigs
Definition rctTypes.h:319
std::vector< Bulletproof > bulletproofs
Definition rctTypes.h:320
crypto::hash chash
Definition main.cpp:47
struct hash_func hashes[]