From 4fd01f2bee0a64a72d67129e2f84007125e81ad2 Mon Sep 17 00:00:00 2001 From: Shen Noether Date: Sat, 9 Jul 2016 19:30:28 +0100 Subject: [PATCH] ringct: "simple" ringct variant Allows the fake outs to be in different positions for each ring. For rct inputs only. --- .../cryptonote_boost_serialization.h | 13 ++ src/ringct/rctOps.cpp | 6 + src/ringct/rctOps.h | 2 + src/ringct/rctSigs.cpp | 211 ++++++++++++++++++ src/ringct/rctSigs.h | 9 +- src/ringct/rctTypes.h | 24 ++ 6 files changed, 262 insertions(+), 3 deletions(-) diff --git a/src/cryptonote_core/cryptonote_boost_serialization.h b/src/cryptonote_core/cryptonote_boost_serialization.h index ccdd55883..01239c5ae 100644 --- a/src/cryptonote_core/cryptonote_boost_serialization.h +++ b/src/cryptonote_core/cryptonote_boost_serialization.h @@ -232,6 +232,19 @@ namespace boost a & x.txnFee; // a & x.bash_hash; bash_hash is not serialized, as it can be reconstructed from the tx data } + + template + inline void serialize(Archive &a, rct::sRctSig &x, const boost::serialization::version_type ver) + { + // a & x.message; message is not serialized, as it can be reconstructed from the tx data + a & x.rangeSigs; + a & x.MG; + // a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets + a & x.pseudoOuts; + a & x.ecdhInfo; + a & x.outPk; + a & x.txnFee; + } } } diff --git a/src/ringct/rctOps.cpp b/src/ringct/rctOps.cpp index e870f3aed..3fa870fd0 100644 --- a/src/ringct/rctOps.cpp +++ b/src/ringct/rctOps.cpp @@ -150,6 +150,12 @@ namespace rct { return make_tuple(sk, pk); } + //generates C =aG + bH from b, a is given.. + void genC(key & C, const key & a, xmr_amount amount) { + key bH = scalarmultH(d2h(amount)); + addKeys1(C, a, bH); + } + //generates a / Pedersen commitment to the amount tuple ctskpkGen(xmr_amount amount) { ctkey sk, pk; diff --git a/src/ringct/rctOps.h b/src/ringct/rctOps.h index 3eb51be28..ad6c520da 100644 --- a/src/ringct/rctOps.h +++ b/src/ringct/rctOps.h @@ -94,6 +94,8 @@ namespace rct { tuple skpkGen(); //generates a / Pedersen commitment to the amount tuple ctskpkGen(xmr_amount amount); + //generates C =aG + bH from b, a is random + void genC(key & C, const key & a, xmr_amount amount); //this one is mainly for testing, can take arbitrary amounts.. tuple ctskpkGen(key bH); // make a pedersen commitment with given key diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index 917ac54cd..c9e34ddb6 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -385,6 +385,33 @@ namespace rct { } + //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); + } + + //Ring-ct MG sigs //Prove: // c.f. http://eprint.iacr.org/2015/1098 section 4. definition 10. @@ -432,6 +459,27 @@ namespace rct { return MLSAG_Ver(message, M, mg, II); } + //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 keyV &II, 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, II); + } + //These functions get keys from blockchain //replace these when connecting blockchain //getKeyFromBlockchain grabs a key from the blockchain at "reference_index" to mix with @@ -462,6 +510,24 @@ namespace rct { 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 @@ -529,6 +595,82 @@ namespace rct { return genRct(inSk, destinations, amounts, mixRing, base_hash, index); } + //RCT simple + //for post-rct only + sRctSig genRctSimple(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector &inamounts, const vector &outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const std::vector & index) { + CHECK_AND_ASSERT_THROW_MES(inamounts.size() > 0, "Empty inamounts"); + CHECK_AND_ASSERT_THROW_MES(inPk.size() == inSk.size(), "Different number of inPk/inSk"); + 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"); + } + + sRctSig rv; + rv.message = message; + rv.outPk.resize(destinations.size()); + rv.rangeSigs.resize(destinations.size()); + rv.ecdhInfo.resize(destinations.size()); + + size_t i; + keyV masks(destinations.size()); //sk mask.. + ctkeyV outSk(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.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, outamounts[i]); + #ifdef DBG + verRange(rv.outPk[i].mask, rv.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], destinations[i]); + } + + //set txn fee + rv.txnFee = txnFee; +// TODO: unused ?? +// key txnFeeKey = scalarmultH(d2h(rv.txnFee)); + rv.mixRing = mixRing; + rv.pseudoOuts.resize(inamounts.size()); + rv.MG.resize(inamounts.size()); + key sumpouts = zero(); //sum pseudoOut masks + key a; + for (i = 0 ; i < inamounts.size() - 1; i++) { + skGen(a); + sc_add(sumpouts.bytes, a.bytes, sumpouts.bytes); + genC(rv.pseudoOuts[i], a, inamounts[i]); + rv.MG[i] = proveRctMGSimple(message, rv.mixRing[i], inSk[i], a, rv.pseudoOuts[i], index[i]); + } + rv.mixRing = mixRing; + sc_sub(a.bytes, sumout.bytes, sumpouts.bytes); + genC(rv.pseudoOuts[i], a, inamounts[i]); + DP(rv.pseudoOuts[i]); + rv.MG[i] = proveRctMGSimple(message, rv.mixRing[i], inSk[i], a, rv.pseudoOuts[i], index[i]); + return rv; + } + + sRctSig genRctSimple(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector &inamounts, const vector &outamounts, xmr_amount txnFee, unsigned int mixin) { + std::vector index; + index.resize(inPk.size()); + ctkeyM mixRing; + 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, inPk, destinations, inamounts, outamounts, txnFee, mixRing, index); + } + //RingCT protocol //genRct: // creates an rctSig with all data necessary to verify the rangeProofs and that the signer owns one of the @@ -572,6 +714,52 @@ namespace rct { return verRct(rv, rv.mixRing, rv.MG.II, rv.base_hash); } + //ver RingCT simple + //assumes only post-rct style inputs (at least for max anonymity) + bool verRctSimple(const sRctSig & rv) { + size_t i = 0; + bool rvb = true; + + CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.rangeSigs.size(), false, "Mismatched sizes of rv.outPk and rv.rangeSigs"); + CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of rv.outPk and rv.ecdhInfo"); + CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == rv.MGs.size(), false, "Mismatched sizes of rv.pseudoOuts and rv.MGs"); + CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == rv.mixRing.size(), false, "Mismatched sizes of rv.pseudoOuts and rv.MGs"); + + key sumOutpks = identity(); + for (i = 0; i < rv.outPk.size(); i++) { + if (!verRange(rv.outPk[i].mask, rv.rangeSigs[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 sumPseudoOuts = identity(); + for (i = 0 ; i < rv.mixRing.size() ; i++) { + tmpb = verRctMGSimple(rv.message, rv.MG[i], rv.MG[i].II, rv.mixRing[i], rv.pseudoOuts[i]); + addKeys(sumPseudoOuts, sumPseudoOuts, rv.pseudoOuts[i]); + DP(tmpb); + if (!tmpb) { + return false; + } + } + DP(sumPseudoOuts); + bool mgVerd = true; + + //check pseudoOuts vs Outs.. + if (!equalKeys(sumPseudoOuts, sumOutpks)) { + return false; + } + + DP("mg sig verified?"); + DP(mgVerd); + + return (rvb && mgVerd); + } + //RingCT protocol //genRct: // creates an rctSig with all data necessary to verify the rangeProofs and that the signer owns one of the @@ -609,4 +797,27 @@ namespace rct { key mask; return decodeRct(rv, sk, i, mask); } + + xmr_amount decodeRct(const sRctSig & rv, const key & sk, unsigned int i) { + CHECK_AND_ASSERT_THROW_MES(rv.rangeSigs.size() > 0, "Empty rv.rangeSigs"); + CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.rangeSigs.size(), "Mismatched sizes of rv.outPk and rv.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); + key 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); + } } diff --git a/src/ringct/rctSigs.h b/src/ringct/rctSigs.h index f87312fa8..7682510cd 100644 --- a/src/ringct/rctSigs.h +++ b/src/ringct/rctSigs.h @@ -113,7 +113,9 @@ namespace rct { //Ver: // verifies the above sig is created corretly mgSig proveRctMG(const ctkeyM & pubs, const ctkeyV & inSk, const keyV &outMasks, const ctkeyV & outPk, unsigned int index, key txnFee, const key &base_hash); + mgSig proveRctMGSimple(const key & message, const ctkeyV & pubs, const ctkey & inSk, const key &a , const key &Cout, unsigned int index); bool verRctMG(mgSig mg, const ctkeyM & pubs, const ctkeyV & outPk, key txnFee, const key &base_hash); + bool verRctMGSimple(const key &message, const mgSig &mg, const keyV &II, const ctkeyV & pubs, const key & C); //These functions get keys from blockchain //replace these when connecting blockchain @@ -135,13 +137,14 @@ namespace rct { // must know the destination private key to find the correct amount, else will return a random number rctSig genRct(const ctkeyV & inSk, const keyV & destinations, const vector amounts, const ctkeyM &mixRing, const key &bash_hash, unsigned int index); rctSig genRct(const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector amounts, const key &bash_hash, const int mixin); + sRctSig genRctSimple(const key & message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector & inamounts, const vector & outamounts, xmr_amount txnFee, unsigned int mixin); + sRctSig genRctSimple(const key & message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector & inamounts, const vector & outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const std::vector & index); bool verRct(const rctSig & rv); bool verRct(const rctSig & rv, const ctkeyM &mixRing, const keyV &II, const key &base_hash); + bool verRctSimple(const sRctSig & rv); xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, key & mask); xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i); - - - + xmr_amount decodeRct(const sRctSig & rv, const key & sk, unsigned int i); } #endif /* RCTSIGS_H */ diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index 2dc4718db..f270da70a 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -203,6 +203,30 @@ namespace rct { END_SERIALIZE() }; + //rct simple variant + struct sRctSig { + key message; + vector rangeSigs; + vector MG; + vector mixRing; //the set of all pubkeys / copy + //pairs that you mix with + keyV pseudoOuts; //C + vector ecdhInfo; + ctkeyV outPk; + xmr_amount txnFee; // contains b + + BEGIN_SERIALIZE_OBJECT() + // FIELD(message) - not serialized, it can be reconstructed + FIELD(rangeSigs) + FIELD(MG) + // FIELD(mixRing) - not serialized, it can be reconstructed + FIELD(pseudoOuts) + FIELD(ecdhInfo) + FIELD(outPk) + FIELD(txnFee) + END_SERIALIZE() + }; + //other basepoint H = toPoint(cn_fast_hash(G)), G the basepoint static const key H = { {0x8b, 0x65, 0x59, 0x70, 0x15, 0x37, 0x99, 0xaf, 0x2a, 0xea, 0xdc, 0x9f, 0xf1, 0xad, 0xd0, 0xea, 0x6c, 0x72, 0x51, 0xd5, 0x41, 0x54, 0xcf, 0xa9, 0x2c, 0x17, 0x3a, 0x0d, 0xd3, 0x9c, 0x1f, 0x94} };