diff --git a/src/cryptonote_core/cryptonote_boost_serialization.h b/src/cryptonote_core/cryptonote_boost_serialization.h index 19b1a687e..663ef5070 100644 --- a/src/cryptonote_core/cryptonote_boost_serialization.h +++ b/src/cryptonote_core/cryptonote_boost_serialization.h @@ -207,11 +207,11 @@ namespace boost } template - inline void serialize(Archive &a, rct::asnlSig &x, const boost::serialization::version_type ver) + inline void serialize(Archive &a, rct::boroSig &x, const boost::serialization::version_type ver) { - a & x.L1; - a & x.s2; - a & x.s; + a & x.s0; + a & x.s1; + a & x.ee; } template diff --git a/src/ringct/rctOps.cpp b/src/ringct/rctOps.cpp index cf55897a7..21f29ccf5 100644 --- a/src/ringct/rctOps.cpp +++ b/src/ringct/rctOps.cpp @@ -267,7 +267,7 @@ namespace rct { ge_p3_tobytes(AB.bytes, &A2); } - //checks if A, B are equal as curve points + //checks if A, B are equal in terms of bytes (may say no if one is a non-reduced scalar) //without doing curve operations bool equalKeys(const key & a, const key & b) { bool rv = true; @@ -359,6 +359,19 @@ namespace rct { return rv; } + key cn_fast_hash(const key64 keys) { + key rv; + cn_fast_hash(rv, &keys[0], 64 * sizeof(keys[0])); + //dp(rv); + return rv; + } + + key hash_to_scalar(const key64 keys) { + key rv = cn_fast_hash(keys); + sc_reduce32(rv.bytes); + return rv; + } + key hashToPointSimple(const key & hh) { key pointk; ge_p1p1 point2; diff --git a/src/ringct/rctOps.h b/src/ringct/rctOps.h index cd3a6dc0d..90f54b050 100644 --- a/src/ringct/rctOps.h +++ b/src/ringct/rctOps.h @@ -158,6 +158,9 @@ namespace rct { //for mg sigs key cn_fast_hash(const keyV &keys); key hash_to_scalar(const keyV &keys); + //for ANSL + key cn_fast_hash(const key64 keys); + key hash_to_scalar(const key64 keys); //returns hashToPoint as described in https://github.com/ShenNoether/ge_fromfe_writeup key hashToPointSimple(const key &in); diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index b773be1e5..fb67c77c4 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -40,94 +40,67 @@ 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 + namespace { + struct verRangeWrapper_ { + void operator()(const key & C, const rangeSig & as, bool &result) const { + result = verRange(C, as); + } + }; + constexpr const verRangeWrapper_ verRangeWrapper{}; + + struct verRctMGSimpleWrapper_ { + void operator()(const key &message, const mgSig &mg, const ctkeyV & pubs, const key & C, bool &result) const { + result = verRctMGSimple(message, mg, pubs, C); + } + }; + constexpr const verRctMGSimpleWrapper_ verRctMGSimpleWrapper{}; + } - void GenSchnorrNonLinkable(key & L1, key & s1, key & s2, const key & x, const key & P1, const key & P2, unsigned 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); + //Borromean (c.f. gmax/andytoshi's paper) + boroSig genBorromean(const key64 x, const key64 P1, const key64 P2, const bits indices) { + key64 L[2], c[2], s[2], alpha, P[2]; + int naught = 0, prime = 0, ii = 0, jj=0; + for (ii = 0 ; ii < 64 ; ii++) { + naught = indices[ii]; prime = (indices[ii] + 1) % 2; + copy(P[0][ii], P1[ii]); //could probably user pointers + copy(P[1][ii], P2[ii]); + skGen(alpha[ii]); + scalarmultBase(L[naught][ii], alpha[ii]); + c[prime][ii] = hash_to_scalar(L[naught][ii]); + skGen(s[prime][ii]); + addKeys2(L[prime][ii], s[prime][ii], c[prime][ii], P[prime][ii]); } - 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); + boroSig bb; + bb.ee = cn_fast_hash(L[1]); //or L[1].. + key LL, cc; + for (jj = 0 ; jj < 64 ; jj++) { + naught = indices[jj]; prime = (indices[jj] + 1) % 2; + if (!indices[jj]) { + sc_mulsub(bb.s0[jj].bytes, x[jj].bytes, bb.ee.bytes, alpha[jj].bytes); + copy(bb.s1[jj], s[1][jj]); + } else { + copy(bb.s0[jj], s[0][jj]); + addKeys2(LL, bb.s0[jj], bb.ee, P[0][jj]); //different L0 + cc = hash_to_scalar(LL); + sc_mulsub(bb.s1[jj].bytes, x[jj].bytes, cc.bytes, alpha[jj].bytes); + } } - else { - throw std::runtime_error("GenSchnorrNonLinkable: invalid index (should be 0 or 1)"); + return bb; + } + + //see above. + bool verifyBorromean(const boroSig &bb, const key64 P1, const key64 P2) { + key64 Lv1, chash; key LL; + int ii = 0; + for (ii = 0 ; ii < 64 ; ii++) { + addKeys2(LL, bb.s0[ii], bb.ee, P1[ii]); + chash[ii] = hash_to_scalar(LL); + addKeys2(Lv1[ii], bb.s1[ii], chash[ii], P2[ii]); } + key eeComputed = cn_fast_hash(Lv1); //hash function fine + return equalKeys(eeComputed, bb.ee); } - //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], 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) { - PERF_TIMER(VerASNL); - 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. @@ -323,7 +296,7 @@ namespace rct { sc_add(mask.bytes, mask.bytes, ai[i].bytes); addKeys(C, C, sig.Ci[i]); } - sig.asig = GenASNL(ai, sig.Ci, CiH, b); + sig.asig = genBorromean(ai, sig.Ci, CiH, b); return sig; } @@ -345,7 +318,7 @@ namespace rct { } if (!equalKeys(C, Ctmp)) return false; - if (!VerASNL(as.Ci, CiH, as.asig)) + if (!verifyBorromean(as.asig, as.Ci, CiH)) return false; return true; } @@ -371,10 +344,10 @@ namespace rct { for (auto r: rv.p.rangeSigs) { for (size_t n = 0; n < 64; ++n) - kv.push_back(r.asig.L1[n]); + kv.push_back(r.asig.s0[n]); for (size_t n = 0; n < 64; ++n) - kv.push_back(r.asig.s2[n]); - kv.push_back(r.asig.s); + kv.push_back(r.asig.s1[n]); + kv.push_back(r.asig.ee); for (size_t n = 0; n < 64; ++n) kv.push_back(r.Ci[n]); } diff --git a/src/ringct/rctSigs.h b/src/ringct/rctSigs.h index a4fecade4..1fe4aa074 100644 --- a/src/ringct/rctSigs.h +++ b/src/ringct/rctSigs.h @@ -66,21 +66,8 @@ using namespace crypto; 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, unsigned int index); - bool VerSchnorrNonLinkable(const key & P1, const key & P2, const key & L1, const key & s1, const key & s2); - - //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); - bool VerASNL(const key64 P1, const key64 P2, const asnlSig &as); + boroSig genBorromean(const key64 x, const key64 P1, const key64 P2, const bits indices); + bool verifyBorromean(const boroSig &bb, const key64 P1, const key64 P2); //Multilayered Spontaneous Anonymous Group Signatures (MLSAG signatures) //These are aka MG signatutes in earlier drafts of the ring ct paper diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index b1921b71a..71cc61ddc 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -125,12 +125,10 @@ namespace rct { typedef unsigned int bits[ATOMS]; typedef key key64[64]; - //just contains the necessary keys to represent asnlSigs - //c.f. http://eprint.iacr.org/2015/1098 - struct asnlSig { - key64 L1; - key64 s2; - key s; + struct boroSig { + key64 s0; + key64 s1; + key ee; }; //Container for precomp @@ -151,14 +149,14 @@ namespace rct { // FIELD(II) - not serialized, it can be reconstructed END_SERIALIZE() }; - //contains the data for an asnl sig + //contains the data for an Borromean sig // also contains the "Ci" values such that // \sum Ci = C // and the signature proves that each Ci is either // a Pedersen commitment to 0 or to 2^i //thus proving that C is in the range of [0, 2^64] struct rangeSig { - asnlSig asig; + boroSig asig; key64 Ci; BEGIN_SERIALIZE_OBJECT() @@ -452,7 +450,7 @@ inline std::ostream &operator <<(std::ostream &o, const rct::key &v) { return pr BLOB_SERIALIZER(rct::key); BLOB_SERIALIZER(rct::key64); BLOB_SERIALIZER(rct::ctkey); -BLOB_SERIALIZER(rct::asnlSig); +BLOB_SERIALIZER(rct::boroSig); VARIANT_TAG(debug_archive, rct::key, "rct::key"); VARIANT_TAG(debug_archive, rct::key64, "rct::key64"); @@ -464,7 +462,7 @@ VARIANT_TAG(debug_archive, rct::ctkeyM, "rct::ctkeyM"); VARIANT_TAG(debug_archive, rct::ecdhTuple, "rct::ecdhTuple"); VARIANT_TAG(debug_archive, rct::mgSig, "rct::mgSig"); VARIANT_TAG(debug_archive, rct::rangeSig, "rct::rangeSig"); -VARIANT_TAG(debug_archive, rct::asnlSig, "rct::asnlSig"); +VARIANT_TAG(debug_archive, rct::boroSig, "rct::boroSig"); VARIANT_TAG(debug_archive, rct::rctSig, "rct::rctSig"); VARIANT_TAG(binary_archive, rct::key, 0x90); @@ -477,7 +475,7 @@ VARIANT_TAG(binary_archive, rct::ctkeyM, 0x96); VARIANT_TAG(binary_archive, rct::ecdhTuple, 0x97); VARIANT_TAG(binary_archive, rct::mgSig, 0x98); VARIANT_TAG(binary_archive, rct::rangeSig, 0x99); -VARIANT_TAG(binary_archive, rct::asnlSig, 0x9a); +VARIANT_TAG(binary_archive, rct::boroSig, 0x9a); VARIANT_TAG(binary_archive, rct::rctSig, 0x9b); VARIANT_TAG(json_archive, rct::key, "rct_key"); @@ -490,7 +488,7 @@ VARIANT_TAG(json_archive, rct::ctkeyM, "rct_ctkeyM"); VARIANT_TAG(json_archive, rct::ecdhTuple, "rct_ecdhTuple"); VARIANT_TAG(json_archive, rct::mgSig, "rct_mgSig"); VARIANT_TAG(json_archive, rct::rangeSig, "rct_rangeSig"); -VARIANT_TAG(json_archive, rct::asnlSig, "rct_asnlSig"); +VARIANT_TAG(json_archive, rct::boroSig, "rct_boroSig"); VARIANT_TAG(json_archive, rct::rctSig, "rct_rctSig"); #endif /* RCTTYPES_H */ diff --git a/tests/unit_tests/ringct.cpp b/tests/unit_tests/ringct.cpp index 1abf2511d..5ab77d4aa 100644 --- a/tests/unit_tests/ringct.cpp +++ b/tests/unit_tests/ringct.cpp @@ -40,29 +40,12 @@ using namespace crypto; using namespace rct; -TEST(ringct, SNL) -{ - key x, P1; - skpkGen(x, P1); - - key P2 = pkGen(); - key P3 = pkGen(); - - key L1, s1, s2; - GenSchnorrNonLinkable(L1, s1, s2, x, P1, P2, 0); - - // a valid one - // an invalid one - ASSERT_TRUE(VerSchnorrNonLinkable(P1, P2, L1, s1, s2)); - ASSERT_FALSE(VerSchnorrNonLinkable(P1, P3, L1, s1, s2)); -} - -TEST(ringct, ASNL) +TEST(ringct, Borromean) { int j = 0; - //Tests for ASNL - //#ASNL true one, false one, C != sum Ci, and one out of the range.. + //Tests for Borromean signatures + //#boro true one, false one, C != sum Ci, and one out of the range.. int N = 64; key64 xv; key64 P1v; @@ -86,22 +69,22 @@ TEST(ringct, ASNL) } //#true one - asnlSig L1s2s = GenASNL(xv, P1v, P2v, indi); - ASSERT_TRUE(VerASNL(P1v, P2v, L1s2s)); + boro bb = genBorromean(xv, P1v, P2v, indi); + ASSERT_TRUE(verifyBorromean(bb, P1v, P2v)); //#false one indi[3] = (indi[3] + 1) % 2; - L1s2s = GenASNL(xv, P1v, P2v, indi); - ASSERT_FALSE(VerASNL(P1v, P2v, L1s2s)); + bb = genBorromean(xv, P1v, P2v, indi); + ASSERT_FALSE(verifyBorromean(bb, P1v, P2v)); //#true one again indi[3] = (indi[3] + 1) % 2; - L1s2s = GenASNL(xv, P1v, P2v, indi); - ASSERT_TRUE(VerASNL(P1v, P2v, L1s2s)); + bb = genBorromean(xv, P1v, P2v, indi); + ASSERT_TRUE(verifyBorromean(bb, P1v, P2v)); //#false one - L1s2s = GenASNL(xv, P2v, P1v, indi); - ASSERT_FALSE(VerASNL(P1v, P2v, L1s2s)); + bb = genBorromean(xv, P2v, P1v, indi); + ASSERT_FALSE(verifyBorromean(bb, P1v, P2v)); } TEST(ringct, MG_sigs) diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp index b592f456b..f8afb2e94 100644 --- a/tests/unit_tests/serialization.cpp +++ b/tests/unit_tests/serialization.cpp @@ -457,7 +457,7 @@ TEST(Serialization, serializes_ringct_types) rct::ctkeyV ctkeyv0, ctkeyv1; rct::ctkeyM ctkeym0, ctkeym1; rct::ecdhTuple ecdh0, ecdh1; - rct::asnlSig asnl0, asnl1; + rct::boroSig boro0, boro1; rct::mgSig mg0, mg1; rct::rangeSig rg0, rg1; rct::rctSig s0, s1; @@ -541,13 +541,13 @@ TEST(Serialization, serializes_ringct_types) for (size_t n = 0; n < 64; ++n) { - asnl0.L1[n] = rct::skGen(); - asnl0.s2[n] = rct::skGen(); + boro0.s0[n] = rct::skGen(); + boro0.s1[n] = rct::skGen(); } - asnl0.s = rct::skGen(); - ASSERT_TRUE(serialization::dump_binary(asnl0, blob)); - ASSERT_TRUE(serialization::parse_binary(blob, asnl1)); - ASSERT_TRUE(!memcmp(&asnl0, &asnl1, sizeof(asnl0))); + boro0.ee = rct::skGen(); + ASSERT_TRUE(serialization::dump_binary(boro0, blob)); + ASSERT_TRUE(serialization::parse_binary(blob, boro1)); + ASSERT_TRUE(!memcmp(&boro0, &boro1, sizeof(boro0))); // create a full rct signature to use its innards rct::ctkeyV sc, pc;