diff options
Diffstat (limited to 'src/ringct/rctSigs.cpp')
-rw-r--r-- | src/ringct/rctSigs.cpp | 875 |
1 files changed, 875 insertions, 0 deletions
diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp new file mode 100644 index 000000000..f4dbd65c5 --- /dev/null +++ b/src/ringct/rctSigs.cpp @@ -0,0 +1,875 @@ +// Copyright (c) 2016, Monero Research Labs +// +// Author: Shen Noether <shen.noether@gmx.com> +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "misc_log_ex.h" +#include "rctSigs.h" +#include "cryptonote_core/cryptonote_format_utils.h" + +using namespace crypto; +using namespace std; + +namespace rct { + + //Schnorr Non-linkable + //Gen Gives a signature (L1, s1, s2) proving that the sender knows "x" such that xG = one of P1 or P2 + //Ver Verifies that signer knows an "x" such that xG = one of P1 or P2 + //These are called in the below ASNL sig generation + + void GenSchnorrNonLinkable(key & L1, key & s1, key & s2, const key & x, const key & P1, const key & P2, int index) { + key c1, c2, L2; + key a = skGen(); + if (index == 0) { + scalarmultBase(L1, a); + hash_to_scalar(c2, L1); + skGen(s2); + addKeys2(L2, s2, c2, P2); + hash_to_scalar(c1, L2); + //s1 = a - x * c1 + sc_mulsub(s1.bytes, x.bytes, c1.bytes, a.bytes); + } + else if (index == 1) { + scalarmultBase(L2, a); + hash_to_scalar(c1, L2); + skGen(s1); + addKeys2(L1, s1, c1, P1); + hash_to_scalar(c2, L1); + sc_mulsub(s2.bytes, x.bytes, c2.bytes, a.bytes); + } + else { + throw std::runtime_error("GenSchnorrNonLinkable: invalid index (should be 0 or 1)"); + } + } + + //Schnorr Non-linkable + //Gen Gives a signature (L1, s1, s2) proving that the sender knows "x" such that xG = one of P1 or P2 + //Ver Verifies that signer knows an "x" such that xG = one of P1 or P2 + //These are called in the below ASNL sig generation + bool VerSchnorrNonLinkable(const key & P1, const key & P2, const key & L1, const key & s1, const key & s2) { + key c2, L2, c1, L1p; + hash_to_scalar(c2, L1); + addKeys2(L2, s2, c2, P2); + hash_to_scalar(c1, L2); + addKeys2(L1p, s1, c1, P1); + + return equalKeys(L1, L1p); + } + + //Aggregate Schnorr Non-linkable Ring Signature (ASNL) + // c.f. http://eprint.iacr.org/2015/1098 section 5. + // These are used in range proofs (alternatively Borromean could be used) + // Gen gives a signature which proves the signer knows, for each i, + // an x[i] such that x[i]G = one of P1[i] or P2[i] + // Ver Verifies the signer knows a key for one of P1[i], P2[i] at each i + asnlSig GenASNL(key64 x, key64 P1, key64 P2, bits indices) { + DP("Generating Aggregate Schnorr Non-linkable Ring Signature\n"); + key64 s1; + int j = 0; + asnlSig rv; + rv.s = zero(); + for (j = 0; j < ATOMS; j++) { + GenSchnorrNonLinkable(rv.L1[j], s1[j], rv.s2[j], x[j], P1[j], P2[j], (int)indices[j]); + sc_add(rv.s.bytes, rv.s.bytes, s1[j].bytes); + } + return rv; + } + + //Aggregate Schnorr Non-linkable Ring Signature (ASNL) + // c.f. http://eprint.iacr.org/2015/1098 section 5. + // These are used in range proofs (alternatively Borromean could be used) + // Gen gives a signature which proves the signer knows, for each i, + // an x[i] such that x[i]G = one of P1[i] or P2[i] + // Ver Verifies the signer knows a key for one of P1[i], P2[i] at each i + bool VerASNL(const key64 P1, const key64 P2, const asnlSig &as) { + DP("Verifying Aggregate Schnorr Non-linkable Ring Signature\n"); + key LHS = identity(); + key RHS = scalarmultBase(as.s); + key c2, L2, c1; + int j = 0; + for (j = 0; j < ATOMS; j++) { + hash_to_scalar(c2, as.L1[j]); + addKeys2(L2, as.s2[j], c2, P2[j]); + addKeys(LHS, LHS, as.L1[j]); + hash_to_scalar(c1, L2); + addKeys(RHS, RHS, scalarmultKey(P1[j], c1)); + } + key cc; + sc_sub(cc.bytes, LHS.bytes, RHS.bytes); + return sc_isnonzero(cc.bytes) == 0; + } + + //Multilayered Spontaneous Anonymous Group Signatures (MLSAG signatures) + //These are aka MG signatutes in earlier drafts of the ring ct paper + // c.f. http://eprint.iacr.org/2015/1098 section 2. + // keyImageV just does I[i] = xx[i] * Hash(xx[i] * G) for each i + // Gen creates a signature which proves that for some column in the keymatrix "pk" + // the signer knows a secret key for each row in that column + // Ver verifies that the MG sig was created correctly + keyV keyImageV(const keyV &xx) { + keyV II(xx.size()); + size_t i = 0; + for (i = 0; i < xx.size(); i++) { + II[i] = scalarmultKey(hashToPoint(scalarmultBase(xx[i])), xx[i]); + } + return II; + } + + + //Multilayered Spontaneous Anonymous Group Signatures (MLSAG signatures) + //This is a just slghtly more efficient version than the ones described below + //(will be explained in more detail in Ring Multisig paper + //These are aka MG signatutes in earlier drafts of the ring ct paper + // c.f. http://eprint.iacr.org/2015/1098 section 2. + // keyImageV just does I[i] = xx[i] * Hash(xx[i] * G) for each i + // Gen creates a signature which proves that for some column in the keymatrix "pk" + // the signer knows a secret key for each row in that column + // Ver verifies that the MG sig was created correctly + mgSig MLSAG_Gen(key message, const keyM & pk, const keyV & xx, const unsigned int index, size_t dsRows) { + mgSig rv; + size_t cols = pk.size(); + CHECK_AND_ASSERT_THROW_MES(cols >= 2, "Error! What is c if cols = 1!"); + CHECK_AND_ASSERT_THROW_MES(index < cols, "Index out of range"); + size_t rows = pk[0].size(); + CHECK_AND_ASSERT_THROW_MES(rows >= 1, "Empty pk"); + for (size_t i = 1; i < cols; ++i) { + CHECK_AND_ASSERT_THROW_MES(pk[i].size() == rows, "pk is not rectangular"); + } + CHECK_AND_ASSERT_THROW_MES(xx.size() == rows, "Bad xx size"); + CHECK_AND_ASSERT_THROW_MES(dsRows <= rows, "Bad dsRows size"); + + size_t i = 0, j = 0, ii = 0; + key c, c_old, L, R, Hi; + sc_0(c_old.bytes); + vector<geDsmp> Ip(dsRows); + rv.II = keyV(dsRows); + keyV alpha(rows); + keyV aG(rows); + rv.ss = keyM(cols, aG); + keyV aHP(dsRows); + keyV toHash(1 + 3 * dsRows + 2 * (rows - dsRows)); + toHash[0] = message; + DP("here1"); + for (i = 0; i < dsRows; i++) { + skpkGen(alpha[i], aG[i]); //need to save alphas for later.. + Hi = hashToPoint(pk[index][i]); + aHP[i] = scalarmultKey(Hi, alpha[i]); + toHash[3 * i + 1] = pk[index][i]; + toHash[3 * i + 2] = aG[i]; + toHash[3 * i + 3] = aHP[i]; + rv.II[i] = scalarmultKey(Hi, xx[i]); + precomp(Ip[i].k, rv.II[i]); + } + size_t ndsRows = 3 * dsRows; //non Double Spendable Rows (see identity chains paper) + for (i = dsRows, ii = 0 ; i < rows ; i++, ii++) { + skpkGen(alpha[i], aG[i]); //need to save alphas for later.. + toHash[ndsRows + 2 * ii + 1] = pk[index][i]; + toHash[ndsRows + 2 * ii + 2] = aG[i]; + } + + c_old = hash_to_scalar(toHash); + + + i = (index + 1) % cols; + if (i == 0) { + copy(rv.cc, c_old); + } + while (i != index) { + + rv.ss[i] = skvGen(rows); + sc_0(c.bytes); + for (j = 0; j < dsRows; j++) { + addKeys2(L, rv.ss[i][j], c_old, pk[i][j]); + hashToPoint(Hi, pk[i][j]); + addKeys3(R, rv.ss[i][j], Hi, c_old, Ip[j].k); + toHash[3 * j + 1] = pk[i][j]; + toHash[3 * j + 2] = L; + toHash[3 * j + 3] = R; + } + for (j = dsRows, ii = 0; j < rows; j++, ii++) { + addKeys2(L, rv.ss[i][j], c_old, pk[i][j]); + toHash[ndsRows + 2 * ii + 1] = pk[i][j]; + toHash[ndsRows + 2 * ii + 2] = L; + } + c = hash_to_scalar(toHash); + copy(c_old, c); + i = (i + 1) % cols; + + if (i == 0) { + copy(rv.cc, c_old); + } + } + for (j = 0; j < rows; j++) { + sc_mulsub(rv.ss[index][j].bytes, c.bytes, xx[j].bytes, alpha[j].bytes); + } + return rv; + } + + //Multilayered Spontaneous Anonymous Group Signatures (MLSAG signatures) + //This is a just slghtly more efficient version than the ones described below + //(will be explained in more detail in Ring Multisig paper + //These are aka MG signatutes in earlier drafts of the ring ct paper + // c.f. http://eprint.iacr.org/2015/1098 section 2. + // keyImageV just does I[i] = xx[i] * Hash(xx[i] * G) for each i + // Gen creates a signature which proves that for some column in the keymatrix "pk" + // the signer knows a secret key for each row in that column + // Ver verifies that the MG sig was created correctly + bool MLSAG_Ver(key message, const keyM & pk, const mgSig & rv, size_t dsRows) { + + size_t cols = pk.size(); + CHECK_AND_ASSERT_MES(cols >= 2, false, "Error! What is c if cols = 1!"); + size_t rows = pk[0].size(); + CHECK_AND_ASSERT_MES(rows >= 1, false, "Empty pk"); + for (size_t i = 1; i < cols; ++i) { + CHECK_AND_ASSERT_MES(pk[i].size() == rows, false, "pk is not rectangular"); + } + CHECK_AND_ASSERT_MES(rv.II.size() == dsRows, false, "Bad II size"); + CHECK_AND_ASSERT_MES(rv.ss.size() == cols, false, "Bad rv.ss size"); + for (size_t i = 0; i < cols; ++i) { + CHECK_AND_ASSERT_MES(rv.ss[i].size() == rows, false, "rv.ss is not rectangular"); + } + CHECK_AND_ASSERT_MES(dsRows <= rows, false, "Bad dsRows value"); + + size_t i = 0, j = 0, ii = 0; + key c, L, R, Hi; + key c_old = copy(rv.cc); + vector<geDsmp> Ip(dsRows); + for (i = 0 ; i < dsRows ; i++) { + precomp(Ip[i].k, rv.II[i]); + } + size_t ndsRows = 3 * dsRows; //non Double Spendable Rows (see identity chains paper + keyV toHash(1 + 3 * dsRows + 2 * (rows - dsRows)); + toHash[0] = message; + i = 0; + while (i < cols) { + sc_0(c.bytes); + for (j = 0; j < dsRows; j++) { + addKeys2(L, rv.ss[i][j], c_old, pk[i][j]); + hashToPoint(Hi, pk[i][j]); + addKeys3(R, rv.ss[i][j], Hi, c_old, Ip[j].k); + toHash[3 * j + 1] = pk[i][j]; + toHash[3 * j + 2] = L; + toHash[3 * j + 3] = R; + } + for (j = dsRows, ii = 0 ; j < rows ; j++, ii++) { + addKeys2(L, rv.ss[i][j], c_old, pk[i][j]); + toHash[ndsRows + 2 * ii + 1] = pk[i][j]; + toHash[ndsRows + 2 * ii + 2] = L; + } + c = hash_to_scalar(toHash); + copy(c_old, c); + i = (i + 1); + } + sc_sub(c.bytes, c_old.bytes, rv.cc.bytes); + return sc_isnonzero(c.bytes) == 0; + } + + + + //proveRange and verRange + //proveRange gives C, and mask such that \sumCi = C + // c.f. http://eprint.iacr.org/2015/1098 section 5.1 + // and Ci is a commitment to either 0 or 2^i, i=0,...,63 + // thus this proves that "amount" is in [0, 2^64] + // mask is a such that C = aG + bH, and b = amount + //verRange verifies that \sum Ci = C and that each Ci is a commitment to 0 or 2^i + rangeSig proveRange(key & C, key & mask, const xmr_amount & amount) { + sc_0(mask.bytes); + identity(C); + bits b; + d2b(b, amount); + rangeSig sig; + key64 ai; + key64 CiH; + int i = 0; + for (i = 0; i < ATOMS; i++) { + skGen(ai[i]); + if (b[i] == 0) { + scalarmultBase(sig.Ci[i], ai[i]); + } + if (b[i] == 1) { + addKeys1(sig.Ci[i], ai[i], H2[i]); + } + subKeys(CiH[i], sig.Ci[i], H2[i]); + sc_add(mask.bytes, mask.bytes, ai[i].bytes); + addKeys(C, C, sig.Ci[i]); + } + sig.asig = GenASNL(ai, sig.Ci, CiH, b); + return sig; + } + + //proveRange and verRange + //proveRange gives C, and mask such that \sumCi = C + // c.f. http://eprint.iacr.org/2015/1098 section 5.1 + // and Ci is a commitment to either 0 or 2^i, i=0,...,63 + // thus this proves that "amount" is in [0, 2^64] + // mask is a such that C = aG + bH, and b = amount + //verRange verifies that \sum Ci = C and that each Ci is a commitment to 0 or 2^i + bool verRange(const key & C, const rangeSig & as) { + key64 CiH; + int i = 0; + key Ctmp = identity(); + for (i = 0; i < 64; i++) { + subKeys(CiH[i], as.Ci[i], H2[i]); + addKeys(Ctmp, Ctmp, as.Ci[i]); + } + if (!equalKeys(C, Ctmp)) + return false; + if (!VerASNL(as.Ci, CiH, as.asig)) + return false; + return true; + } + + key get_pre_mlsag_hash(const rctSig &rv) + { + keyV hashes; + hashes.push_back(rv.message); + crypto::hash h; + cryptonote::get_blob_hash(cryptonote::t_serializable_object_to_blob((const rctSigBase&)rv), h); + hashes.push_back(hash2rct(h)); + keyV kv; + for (auto r: rv.p.rangeSigs) + { + for (size_t n = 0; n < 64; ++n) + kv.push_back(r.asig.L1[n]); + for (size_t n = 0; n < 64; ++n) + kv.push_back(r.asig.s2[n]); + kv.push_back(r.asig.s); + for (size_t n = 0; n < 64; ++n) + kv.push_back(r.Ci[n]); + } + hashes.push_back(cn_fast_hash(kv)); + + return cn_fast_hash(hashes); + } + + //Ring-ct MG sigs + //Prove: + // c.f. http://eprint.iacr.org/2015/1098 section 4. definition 10. + // This does the MG sig on the "dest" part of the given key matrix, and + // the last row is the sum of input commitments from that column - sum output commitments + // this shows that sum inputs = sum outputs + //Ver: + // verifies the above sig is created corretly + mgSig proveRctMG(const key &message, const ctkeyM & pubs, const ctkeyV & inSk, const ctkeyV &outSk, const ctkeyV & outPk, unsigned int index, key txnFeeKey) { + mgSig mg; + //setup vars + size_t cols = pubs.size(); + CHECK_AND_ASSERT_THROW_MES(cols >= 1, "Empty pubs"); + size_t rows = pubs[0].size(); + CHECK_AND_ASSERT_THROW_MES(rows >= 1, "Empty pubs"); + for (size_t i = 1; i < cols; ++i) { + CHECK_AND_ASSERT_THROW_MES(pubs[i].size() == rows, "pubs is not rectangular"); + } + CHECK_AND_ASSERT_THROW_MES(inSk.size() == rows, "Bad inSk size"); + CHECK_AND_ASSERT_THROW_MES(outSk.size() == outPk.size(), "Bad outSk/outPk size"); + + keyV sk(rows + 1); + keyV tmp(rows + 1); + size_t i = 0, j = 0; + for (i = 0; i < rows + 1; i++) { + sc_0(sk[i].bytes); + identity(tmp[i]); + } + keyM M(cols, tmp); + //create the matrix to mg sig + for (i = 0; i < cols; i++) { + M[i][rows] = identity(); + for (j = 0; j < rows; j++) { + M[i][j] = pubs[i][j].dest; + addKeys(M[i][rows], M[i][rows], pubs[i][j].mask); //add input commitments in last row + } + } + sc_0(sk[rows].bytes); + for (j = 0; j < rows; j++) { + sk[j] = copy(inSk[j].dest); + sc_add(sk[rows].bytes, sk[rows].bytes, inSk[j].mask.bytes); //add masks in last row + } + for (i = 0; i < cols; i++) { + for (size_t j = 0; j < outPk.size(); j++) { + subKeys(M[i][rows], M[i][rows], outPk[j].mask); //subtract output Ci's in last row + } + //subtract txn fee output in last row + subKeys(M[i][rows], M[i][rows], txnFeeKey); + } + for (size_t j = 0; j < outPk.size(); j++) { + sc_sub(sk[rows].bytes, sk[rows].bytes, outSk[j].mask.bytes); //subtract output masks in last row.. + } + return MLSAG_Gen(message, M, sk, index, rows); + } + + + //Ring-ct MG sigs Simple + // Simple version for when we assume only + // post rct inputs + // here pubs is a vector of (P, C) length mixin + // inSk is x, a_in corresponding to signing index + // a_out, Cout is for the output commitment + // index is the signing index.. + mgSig proveRctMGSimple(const key &message, const ctkeyV & pubs, const ctkey & inSk, const key &a , const key &Cout, unsigned int index) { + mgSig mg; + //setup vars + size_t rows = 1; + size_t cols = pubs.size(); + CHECK_AND_ASSERT_THROW_MES(cols >= 1, "Empty pubs"); + keyV tmp(rows + 1); + keyV sk(rows + 1); + size_t i; + keyM M(cols, tmp); + for (i = 0; i < cols; i++) { + M[i][0] = pubs[i].dest; + subKeys(M[i][1], pubs[i].mask, Cout); + sk[0] = copy(inSk.dest); + sc_sub(sk[1].bytes, inSk.mask.bytes, a.bytes); + } + return MLSAG_Gen(message, M, sk, index, rows); + } + + + //Ring-ct MG sigs + //Prove: + // c.f. http://eprint.iacr.org/2015/1098 section 4. definition 10. + // This does the MG sig on the "dest" part of the given key matrix, and + // the last row is the sum of input commitments from that column - sum output commitments + // this shows that sum inputs = sum outputs + //Ver: + // verifies the above sig is created corretly + bool verRctMG(const mgSig &mg, const ctkeyM & pubs, const ctkeyV & outPk, key txnFeeKey, const key &message) { + //setup vars + size_t cols = pubs.size(); + CHECK_AND_ASSERT_MES(cols >= 1, false, "Empty pubs"); + size_t rows = pubs[0].size(); + CHECK_AND_ASSERT_MES(rows >= 1, false, "Empty pubs"); + for (size_t i = 1; i < cols; ++i) { + CHECK_AND_ASSERT_MES(pubs[i].size() == rows, false, "pubs is not rectangular"); + } + + keyV tmp(rows + 1); + size_t i = 0, j = 0; + for (i = 0; i < rows + 1; i++) { + identity(tmp[i]); + } + keyM M(cols, tmp); + + //create the matrix to mg sig + for (j = 0; j < rows; j++) { + for (i = 0; i < cols; i++) { + M[i][j] = pubs[i][j].dest; + addKeys(M[i][rows], M[i][rows], pubs[i][j].mask); //add Ci in last row + } + } + for (i = 0; i < cols; i++) { + for (j = 0; j < outPk.size(); j++) { + subKeys(M[i][rows], M[i][rows], outPk[j].mask); //subtract output Ci's in last row + } + //subtract txn fee output in last row + subKeys(M[i][rows], M[i][rows], txnFeeKey); + } + return MLSAG_Ver(message, M, mg, rows); + } + + //Ring-ct Simple MG sigs + //Ver: + //This does a simplified version, assuming only post Rct + //inputs + bool verRctMGSimple(const key &message, const mgSig &mg, const ctkeyV & pubs, const key & C) { + //setup vars + size_t rows = 1; + size_t cols = pubs.size(); + CHECK_AND_ASSERT_MES(cols >= 1, false, "Empty pubs"); + keyV tmp(rows + 1); + size_t i; + keyM M(cols, tmp); + //create the matrix to mg sig + for (i = 0; i < cols; i++) { + M[i][0] = pubs[i].dest; + subKeys(M[i][1], pubs[i].mask, C); + } + //DP(C); + return MLSAG_Ver(message, M, mg, rows); + } + + //These functions get keys from blockchain + //replace these when connecting blockchain + //getKeyFromBlockchain grabs a key from the blockchain at "reference_index" to mix with + //populateFromBlockchain creates a keymatrix with "mixin" columns and one of the columns is inPk + // the return value are the key matrix, and the index where inPk was put (random). + void getKeyFromBlockchain(ctkey & a, size_t reference_index) { + a.mask = pkGen(); + a.dest = pkGen(); + } + + //These functions get keys from blockchain + //replace these when connecting blockchain + //getKeyFromBlockchain grabs a key from the blockchain at "reference_index" to mix with + //populateFromBlockchain creates a keymatrix with "mixin" + 1 columns and one of the columns is inPk + // the return value are the key matrix, and the index where inPk was put (random). + tuple<ctkeyM, xmr_amount> populateFromBlockchain(ctkeyV inPk, int mixin) { + int rows = inPk.size(); + ctkeyM rv(mixin + 1, inPk); + int index = randXmrAmount(mixin); + int i = 0, j = 0; + for (i = 0; i <= mixin; i++) { + if (i != index) { + for (j = 0; j < rows; j++) { + getKeyFromBlockchain(rv[i][j], (size_t)randXmrAmount); + } + } + } + return make_tuple(rv, index); + } + + //These functions get keys from blockchain + //replace these when connecting blockchain + //getKeyFromBlockchain grabs a key from the blockchain at "reference_index" to mix with + //populateFromBlockchain creates a keymatrix with "mixin" columns and one of the columns is inPk + // the return value are the key matrix, and the index where inPk was put (random). + xmr_amount populateFromBlockchainSimple(ctkeyV & mixRing, const ctkey & inPk, int mixin) { + int index = randXmrAmount(mixin); + int i = 0; + for (i = 0; i <= mixin; i++) { + if (i != index) { + getKeyFromBlockchain(mixRing[i], (size_t)randXmrAmount(1000)); + } else { + mixRing[i] = inPk; + } + } + return index; + } + + //RingCT protocol + //genRct: + // creates an rctSig with all data necessary to verify the rangeProofs and that the signer owns one of the + // columns that are claimed as inputs, and that the sum of inputs = sum of outputs. + // Also contains masked "amount" and "mask" so the receiver can see how much they received + //verRct: + // verifies that all signatures (rangeProogs, MG sig, sum inputs = outputs) are correct + //decodeRct: (c.f. http://eprint.iacr.org/2015/1098 section 5.1.1) + // uses the attached ecdh info to find the amounts represented by each output commitment + // must know the destination private key to find the correct amount, else will return a random number + // Note: For txn fees, the last index in the amounts vector should contain that + // Thus the amounts vector will be "one" longer than the destinations vectort + rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector<xmr_amount> & amounts, const ctkeyM &mixRing, const keyV &amount_keys, unsigned int index, ctkeyV &outSk) { + CHECK_AND_ASSERT_THROW_MES(amounts.size() == destinations.size() || amounts.size() == destinations.size() + 1, "Different number of amounts/destinations"); + CHECK_AND_ASSERT_THROW_MES(index < mixRing.size(), "Bad index into mixRing"); + for (size_t n = 0; n < mixRing.size(); ++n) { + CHECK_AND_ASSERT_THROW_MES(mixRing[n].size() == inSk.size(), "Bad mixRing size"); + } + + rctSig rv; + rv.type = RCTTypeFull; + rv.message = message; + rv.outPk.resize(destinations.size()); + rv.p.rangeSigs.resize(destinations.size()); + rv.ecdhInfo.resize(destinations.size()); + + size_t i = 0; + keyV masks(destinations.size()); //sk mask.. + outSk.resize(destinations.size()); + for (i = 0; i < destinations.size(); i++) { + //add destination to sig + rv.outPk[i].dest = copy(destinations[i]); + //compute range proof + rv.p.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, amounts[i]); + #ifdef DBG + CHECK_AND_ASSERT_THROW_MES(verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]), "verRange failed on newly created proof"); + #endif + + //mask amount and mask + rv.ecdhInfo[i].mask = copy(outSk[i].mask); + rv.ecdhInfo[i].amount = d2h(amounts[i]); + ecdhEncode(rv.ecdhInfo[i], amount_keys[i]); + + } + + //set txn fee + if (amounts.size() > destinations.size()) + { + rv.txnFee = amounts[destinations.size()]; + } + else + { + rv.txnFee = 0; + } + key txnFeeKey = scalarmultH(d2h(rv.txnFee)); + + rv.mixRing = mixRing; + rv.p.MGs.push_back(proveRctMG(get_pre_mlsag_hash(rv), rv.mixRing, inSk, outSk, rv.outPk, index, txnFeeKey)); + return rv; + } + + rctSig genRct(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector<xmr_amount> & amounts, const keyV &amount_keys, const int mixin) { + unsigned int index; + ctkeyM mixRing; + ctkeyV outSk; + tie(mixRing, index) = populateFromBlockchain(inPk, mixin); + return genRct(message, inSk, destinations, amounts, mixRing, amount_keys, index, outSk); + } + + //RCT simple + //for post-rct only + rctSig genRctSimple(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector<xmr_amount> &inamounts, const vector<xmr_amount> &outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector<unsigned int> & index, ctkeyV &outSk) { + CHECK_AND_ASSERT_THROW_MES(inamounts.size() > 0, "Empty inamounts"); + CHECK_AND_ASSERT_THROW_MES(inamounts.size() == inSk.size(), "Different number of inamounts/inSk"); + CHECK_AND_ASSERT_THROW_MES(outamounts.size() == destinations.size(), "Different number of amounts/destinations"); + CHECK_AND_ASSERT_THROW_MES(index.size() == inSk.size(), "Different number of index/inSk"); + CHECK_AND_ASSERT_THROW_MES(mixRing.size() == inSk.size(), "Different number of mixRing/inSk"); + for (size_t n = 0; n < mixRing.size(); ++n) { + CHECK_AND_ASSERT_THROW_MES(index[n] < mixRing[n].size(), "Bad index into mixRing"); + } + + rctSig rv; + rv.type = RCTTypeSimple; + rv.message = message; + rv.outPk.resize(destinations.size()); + rv.p.rangeSigs.resize(destinations.size()); + rv.ecdhInfo.resize(destinations.size()); + + size_t i; + keyV masks(destinations.size()); //sk mask.. + outSk.resize(destinations.size()); + key sumout = zero(); + for (i = 0; i < destinations.size(); i++) { + + //add destination to sig + rv.outPk[i].dest = copy(destinations[i]); + //compute range proof + rv.p.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, outamounts[i]); + #ifdef DBG + verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); + #endif + + sc_add(sumout.bytes, outSk[i].mask.bytes, sumout.bytes); + + //mask amount and mask + rv.ecdhInfo[i].mask = copy(outSk[i].mask); + rv.ecdhInfo[i].amount = d2h(outamounts[i]); + ecdhEncode(rv.ecdhInfo[i], amount_keys[i]); + } + + //set txn fee + rv.txnFee = txnFee; +// TODO: unused ?? +// key txnFeeKey = scalarmultH(d2h(rv.txnFee)); + rv.mixRing = mixRing; + rv.pseudoOuts.resize(inamounts.size()); + rv.p.MGs.resize(inamounts.size()); + key sumpouts = zero(); //sum pseudoOut masks + keyV a(inamounts.size()); + for (i = 0 ; i < inamounts.size() - 1; i++) { + skGen(a[i]); + sc_add(sumpouts.bytes, a[i].bytes, sumpouts.bytes); + genC(rv.pseudoOuts[i], a[i], inamounts[i]); + } + rv.mixRing = mixRing; + sc_sub(a[i].bytes, sumout.bytes, sumpouts.bytes); + genC(rv.pseudoOuts[i], a[i], inamounts[i]); + DP(rv.pseudoOuts[i]); + + key full_message = get_pre_mlsag_hash(rv); + for (i = 0 ; i < inamounts.size(); i++) { + rv.p.MGs[i] = proveRctMGSimple(full_message, rv.mixRing[i], inSk[i], a[i], rv.pseudoOuts[i], index[i]); + } + return rv; + } + + rctSig genRctSimple(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector<xmr_amount> &inamounts, const vector<xmr_amount> &outamounts, const keyV &amount_keys, xmr_amount txnFee, unsigned int mixin) { + std::vector<unsigned int> index; + index.resize(inPk.size()); + ctkeyM mixRing; + ctkeyV outSk; + mixRing.resize(inPk.size()); + for (size_t i = 0; i < inPk.size(); ++i) { + mixRing[i].resize(mixin+1); + index[i] = populateFromBlockchainSimple(mixRing[i], inPk[i], mixin); + } + return genRctSimple(message, inSk, destinations, inamounts, outamounts, txnFee, mixRing, amount_keys, index, outSk); + } + + //RingCT protocol + //genRct: + // creates an rctSig with all data necessary to verify the rangeProofs and that the signer owns one of the + // columns that are claimed as inputs, and that the sum of inputs = sum of outputs. + // Also contains masked "amount" and "mask" so the receiver can see how much they received + //verRct: + // verifies that all signatures (rangeProogs, MG sig, sum inputs = outputs) are correct + //decodeRct: (c.f. http://eprint.iacr.org/2015/1098 section 5.1.1) + // uses the attached ecdh info to find the amounts represented by each output commitment + // must know the destination private key to find the correct amount, else will return a random number + bool verRct(const rctSig & rv) { + CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull, false, "verRct called on non-full rctSig"); + CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.p.rangeSigs"); + CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of outPk and rv.ecdhInfo"); + CHECK_AND_ASSERT_MES(rv.p.MGs.size() == 1, false, "full rctSig has not one MG"); + + // some rct ops can throw + try + { + size_t i = 0; + bool tmp; + DP("range proofs verified?"); + for (i = 0; i < rv.outPk.size(); i++) { + tmp = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); + DP(tmp); + if (!tmp) { + LOG_ERROR("Range proof verification failed for input " << i); + return false; + } + } + //compute txn fee + key txnFeeKey = scalarmultH(d2h(rv.txnFee)); + bool mgVerd = verRctMG(rv.p.MGs[0], rv.mixRing, rv.outPk, txnFeeKey, get_pre_mlsag_hash(rv)); + DP("mg sig verified?"); + DP(mgVerd); + if (!mgVerd) { + LOG_ERROR("MG signature verification failed"); + return false; + } + + return true; + } + catch(...) + { + return false; + } + } + + //ver RingCT simple + //assumes only post-rct style inputs (at least for max anonymity) + bool verRctSimple(const rctSig & rv) { + size_t i = 0; + + CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple, false, "verRctSimple called on non simple rctSig"); + CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.p.rangeSigs"); + CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of outPk and rv.ecdhInfo"); + CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == rv.p.MGs.size(), false, "Mismatched sizes of rv.pseudoOuts and rv.p.MGs"); + CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == rv.mixRing.size(), false, "Mismatched sizes of rv.pseudoOuts and mixRing"); + + key sumOutpks = identity(); + for (i = 0; i < rv.outPk.size(); i++) { + if (!verRange(rv.outPk[i].mask, rv.p.rangeSigs[i])) { + LOG_ERROR("Range proof verified failed for input " << i); + return false; + } + addKeys(sumOutpks, sumOutpks, rv.outPk[i].mask); + } + DP(sumOutpks); + key txnFeeKey = scalarmultH(d2h(rv.txnFee)); + addKeys(sumOutpks, txnFeeKey, sumOutpks); + + bool tmpb = false; + key message = get_pre_mlsag_hash(rv); + key sumPseudoOuts = identity(); + for (i = 0 ; i < rv.mixRing.size() ; i++) { + tmpb = verRctMGSimple(message, rv.p.MGs[i], rv.mixRing[i], rv.pseudoOuts[i]); + addKeys(sumPseudoOuts, sumPseudoOuts, rv.pseudoOuts[i]); + DP(tmpb); + if (!tmpb) { + LOG_ERROR("verRctMGSimple failed for input " << i); + return false; + } + } + DP(sumPseudoOuts); + + //check pseudoOuts vs Outs.. + if (!equalKeys(sumPseudoOuts, sumOutpks)) { + LOG_ERROR("Sum check failed"); + return false; + } + + return true; + } + + //RingCT protocol + //genRct: + // creates an rctSig with all data necessary to verify the rangeProofs and that the signer owns one of the + // columns that are claimed as inputs, and that the sum of inputs = sum of outputs. + // Also contains masked "amount" and "mask" so the receiver can see how much they received + //verRct: + // verifies that all signatures (rangeProogs, MG sig, sum inputs = outputs) are correct + //decodeRct: (c.f. http://eprint.iacr.org/2015/1098 section 5.1.1) + // uses the attached ecdh info to find the amounts represented by each output commitment + // must know the destination private key to find the correct amount, else will return a random number + xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, key & mask) { + CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull, false, "decodeRct called on non-full rctSig"); + CHECK_AND_ASSERT_THROW_MES(rv.p.rangeSigs.size() > 0, "Empty rv.p.rangeSigs"); + CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.p.rangeSigs.size(), "Mismatched sizes of rv.outPk and rv.p.rangeSigs"); + CHECK_AND_ASSERT_THROW_MES(i < rv.ecdhInfo.size(), "Bad index"); + + //mask amount and mask + ecdhTuple ecdh_info = rv.ecdhInfo[i]; + ecdhDecode(ecdh_info, sk); + mask = ecdh_info.mask; + key amount = ecdh_info.amount; + key C = rv.outPk[i].mask; + DP("C"); + DP(C); + key Ctmp; + addKeys2(Ctmp, mask, amount, H); + DP("Ctmp"); + DP(Ctmp); + if (equalKeys(C, Ctmp) == false) { + CHECK_AND_ASSERT_THROW_MES(false, "warning, amount decoded incorrectly, will be unable to spend"); + } + return h2d(amount); + } + + xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i) { + key mask; + return decodeRct(rv, sk, i, mask); + } + + xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i, key &mask) { + CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple, false, "decodeRct called on non simple rctSig"); + CHECK_AND_ASSERT_THROW_MES(rv.p.rangeSigs.size() > 0, "Empty rv.p.rangeSigs"); + CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.p.rangeSigs.size(), "Mismatched sizes of rv.outPk and rv.p.rangeSigs"); + CHECK_AND_ASSERT_THROW_MES(i < rv.ecdhInfo.size(), "Bad index"); + + //mask amount and mask + ecdhTuple ecdh_info = rv.ecdhInfo[i]; + ecdhDecode(ecdh_info, sk); + mask = ecdh_info.mask; + key amount = ecdh_info.amount; + key C = rv.outPk[i].mask; + DP("C"); + DP(C); + key Ctmp; + addKeys2(Ctmp, mask, amount, H); + DP("Ctmp"); + DP(Ctmp); + if (equalKeys(C, Ctmp) == false) { + CHECK_AND_ASSERT_THROW_MES(false, "warning, amount decoded incorrectly, will be unable to spend"); + } + return h2d(amount); + } + + xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i) { + key mask; + return decodeRctSimple(rv, sk, i, mask); + } +} |