Merge pull request #1413

648ea6be blockchain: bring the v4 fork height one block forward (luigi1111)
46a0dcc1 ringct: luigi1111's changes to fix and speedup Borromean sigs (luigi1111)
76958fc7 ringct: switch to Borromean signatures (Shen Noether)
This commit is contained in:
Riccardo Spagni 2016-12-08 23:43:40 +02:00
commit 22e16e88e3
No known key found for this signature in database
GPG key ID: 55432DF31CCD4FCD
9 changed files with 116 additions and 164 deletions

View file

@ -91,8 +91,8 @@ static const struct {
// version 3 starts from block 1141317, which is on or around the 24th of September, 2016. Fork time finalised on 2016-03-21. // version 3 starts from block 1141317, which is on or around the 24th of September, 2016. Fork time finalised on 2016-03-21.
{ 3, 1141317, 0, 1458558528 }, { 3, 1141317, 0, 1458558528 },
// version 4 starts from block 1220517, which is on or around the 5th of January, 2017. Fork time finalised on 2016-09-18. // version 4 starts from block 1220516, which is on or around the 5th of January, 2017. Fork time finalised on 2016-09-18.
{ 4, 1220517, 0, 1483574400 }, { 4, 1220516, 0, 1483574400 },
// version 5 starts from block 1406997, which is on or around the 20th of September, 2017. Fork time finalised on 2016-09-18. // version 5 starts from block 1406997, which is on or around the 20th of September, 2017. Fork time finalised on 2016-09-18.
{ 5, 1406997, 0, 1505865600 }, { 5, 1406997, 0, 1505865600 },
@ -113,7 +113,7 @@ static const struct {
// versions 3-5 were passed in rapid succession from September 18th, 2016 // versions 3-5 were passed in rapid succession from September 18th, 2016
{ 3, 800500, 0, 1472415034 }, { 3, 800500, 0, 1472415034 },
{ 4, 801220, 0, 1472415035 }, { 4, 801219, 0, 1472415035 },
{ 5, 802660, 0, 1472415036 }, { 5, 802660, 0, 1472415036 },
}; };
static const uint64_t testnet_hard_fork_version_1_till = 624633; static const uint64_t testnet_hard_fork_version_1_till = 624633;

View file

@ -207,11 +207,11 @@ namespace boost
} }
template <class Archive> template <class Archive>
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.s0;
a & x.s2; a & x.s1;
a & x.s; a & x.ee;
} }
template <class Archive> template <class Archive>

View file

@ -267,7 +267,7 @@ namespace rct {
ge_p3_tobytes(AB.bytes, &A2); 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 //without doing curve operations
bool equalKeys(const key & a, const key & b) { bool equalKeys(const key & a, const key & b) {
bool rv = true; bool rv = true;
@ -359,6 +359,19 @@ namespace rct {
return rv; 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 hashToPointSimple(const key & hh) {
key pointk; key pointk;
ge_p1p1 point2; ge_p1p1 point2;

View file

@ -158,6 +158,9 @@ namespace rct {
//for mg sigs //for mg sigs
key cn_fast_hash(const keyV &keys); key cn_fast_hash(const keyV &keys);
key hash_to_scalar(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 //returns hashToPoint as described in https://github.com/ShenNoether/ge_fromfe_writeup
key hashToPointSimple(const key &in); key hashToPointSimple(const key &in);

View file

@ -40,92 +40,64 @@ using namespace crypto;
using namespace std; using namespace std;
namespace rct { namespace rct {
//Schnorr Non-linkable namespace {
//Gen Gives a signature (L1, s1, s2) proving that the sender knows "x" such that xG = one of P1 or P2 struct verRangeWrapper_ {
//Ver Verifies that signer knows an "x" such that xG = one of P1 or P2 void operator()(const key & C, const rangeSig & as, bool &result) const {
//These are called in the below ASNL sig generation result = verRange(C, as);
}
};
constexpr const verRangeWrapper_ verRangeWrapper{};
void GenSchnorrNonLinkable(key & L1, key & s1, key & s2, const key & x, const key & P1, const key & P2, unsigned int index) { struct verRctMGSimpleWrapper_ {
key c1, c2, L2; void operator()(const key &message, const mgSig &mg, const ctkeyV & pubs, const key & C, bool &result) const {
key a = skGen(); result = verRctMGSimple(message, mg, pubs, C);
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)");
} }
};
constexpr const verRctMGSimpleWrapper_ verRctMGSimpleWrapper{};
} }
//Schnorr Non-linkable //Borromean (c.f. gmax/andytoshi's paper)
//Gen Gives a signature (L1, s1, s2) proving that the sender knows "x" such that xG = one of P1 or P2 boroSig genBorromean(const key64 x, const key64 P1, const key64 P2, const bits indices) {
//Ver Verifies that signer knows an "x" such that xG = one of P1 or P2 key64 L[2], alpha;
//These are called in the below ASNL sig generation key c;
bool VerSchnorrNonLinkable(const key & P1, const key & P2, const key & L1, const key & s1, const key & s2) { int naught = 0, prime = 0, ii = 0, jj=0;
key c2, L2, c1, L1p; boroSig bb;
hash_to_scalar(c2, L1); for (ii = 0 ; ii < 64 ; ii++) {
addKeys2(L2, s2, c2, P2); naught = indices[ii]; prime = (indices[ii] + 1) % 2;
hash_to_scalar(c1, L2); skGen(alpha[ii]);
addKeys2(L1p, s1, c1, P1); scalarmultBase(L[naught][ii], alpha[ii]);
if (naught == 0) {
return equalKeys(L1, L1p); skGen(bb.s1[ii]);
c = hash_to_scalar(L[naught][ii]);
addKeys2(L[prime][ii], bb.s1[ii], c, P2[ii]);
}
}
bb.ee = hash_to_scalar(L[1]); //or L[1]..
key LL, cc;
for (jj = 0 ; jj < 64 ; jj++) {
if (!indices[jj]) {
sc_mulsub(bb.s0[jj].bytes, x[jj].bytes, bb.ee.bytes, alpha[jj].bytes);
} else {
skGen(bb.s0[jj]);
addKeys2(LL, bb.s0[jj], bb.ee, P1[jj]); //different L0
cc = hash_to_scalar(LL);
sc_mulsub(bb.s1[jj].bytes, x[jj].bytes, cc.bytes, alpha[jj].bytes);
}
}
return bb;
} }
//Aggregate Schnorr Non-linkable Ring Signature (ASNL) //see above.
// c.f. http://eprint.iacr.org/2015/1098 section 5. bool verifyBorromean(const boroSig &bb, const key64 P1, const key64 P2) {
// These are used in range proofs (alternatively Borromean could be used) key64 Lv1; key chash, LL;
// Gen gives a signature which proves the signer knows, for each i, int ii = 0;
// an x[i] such that x[i]G = one of P1[i] or P2[i] for (ii = 0 ; ii < 64 ; ii++) {
// Ver Verifies the signer knows a key for one of P1[i], P2[i] at each i addKeys2(LL, bb.s0[ii], bb.ee, P1[ii]);
asnlSig GenASNL(key64 x, key64 P1, key64 P2, bits indices) { chash = hash_to_scalar(LL);
DP("Generating Aggregate Schnorr Non-linkable Ring Signature\n"); addKeys2(Lv1[ii], bb.s1[ii], chash, P2[ii]);
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; key eeComputed = hash_to_scalar(Lv1); //hash function fine
} return equalKeys(eeComputed, bb.ee);
//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) //Multilayered Spontaneous Anonymous Group Signatures (MLSAG signatures)
@ -323,7 +295,7 @@ namespace rct {
sc_add(mask.bytes, mask.bytes, ai[i].bytes); sc_add(mask.bytes, mask.bytes, ai[i].bytes);
addKeys(C, C, sig.Ci[i]); addKeys(C, C, sig.Ci[i]);
} }
sig.asig = GenASNL(ai, sig.Ci, CiH, b); sig.asig = genBorromean(ai, sig.Ci, CiH, b);
return sig; return sig;
} }
@ -345,7 +317,7 @@ namespace rct {
} }
if (!equalKeys(C, Ctmp)) if (!equalKeys(C, Ctmp))
return false; return false;
if (!VerASNL(as.Ci, CiH, as.asig)) if (!verifyBorromean(as.asig, as.Ci, CiH))
return false; return false;
return true; return true;
} }
@ -371,10 +343,10 @@ namespace rct {
for (auto r: rv.p.rangeSigs) for (auto r: rv.p.rangeSigs)
{ {
for (size_t n = 0; n < 64; ++n) 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) for (size_t n = 0; n < 64; ++n)
kv.push_back(r.asig.s2[n]); kv.push_back(r.asig.s1[n]);
kv.push_back(r.asig.s); kv.push_back(r.asig.ee);
for (size_t n = 0; n < 64; ++n) for (size_t n = 0; n < 64; ++n)
kv.push_back(r.Ci[n]); kv.push_back(r.Ci[n]);
} }

View file

@ -66,21 +66,8 @@ using namespace crypto;
namespace rct { namespace rct {
//Schnorr Non-linkable boroSig genBorromean(const key64 x, const key64 P1, const key64 P2, const bits indices);
//Gen Gives a signature (L1, s1, s2) proving that the sender knows "x" such that xG = one of P1 or P2 bool verifyBorromean(const boroSig &bb, const key64 P1, const key64 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);
//Multilayered Spontaneous Anonymous Group Signatures (MLSAG signatures) //Multilayered Spontaneous Anonymous Group Signatures (MLSAG signatures)
//These are aka MG signatutes in earlier drafts of the ring ct paper //These are aka MG signatutes in earlier drafts of the ring ct paper

View file

@ -125,12 +125,10 @@ namespace rct {
typedef unsigned int bits[ATOMS]; typedef unsigned int bits[ATOMS];
typedef key key64[64]; typedef key key64[64];
//just contains the necessary keys to represent asnlSigs struct boroSig {
//c.f. http://eprint.iacr.org/2015/1098 key64 s0;
struct asnlSig { key64 s1;
key64 L1; key ee;
key64 s2;
key s;
}; };
//Container for precomp //Container for precomp
@ -151,14 +149,14 @@ namespace rct {
// FIELD(II) - not serialized, it can be reconstructed // FIELD(II) - not serialized, it can be reconstructed
END_SERIALIZE() END_SERIALIZE()
}; };
//contains the data for an asnl sig //contains the data for an Borromean sig
// also contains the "Ci" values such that // also contains the "Ci" values such that
// \sum Ci = C // \sum Ci = C
// and the signature proves that each Ci is either // and the signature proves that each Ci is either
// a Pedersen commitment to 0 or to 2^i // a Pedersen commitment to 0 or to 2^i
//thus proving that C is in the range of [0, 2^64] //thus proving that C is in the range of [0, 2^64]
struct rangeSig { struct rangeSig {
asnlSig asig; boroSig asig;
key64 Ci; key64 Ci;
BEGIN_SERIALIZE_OBJECT() 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::key);
BLOB_SERIALIZER(rct::key64); BLOB_SERIALIZER(rct::key64);
BLOB_SERIALIZER(rct::ctkey); 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::key, "rct::key");
VARIANT_TAG(debug_archive, rct::key64, "rct::key64"); 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::ecdhTuple, "rct::ecdhTuple");
VARIANT_TAG(debug_archive, rct::mgSig, "rct::mgSig"); VARIANT_TAG(debug_archive, rct::mgSig, "rct::mgSig");
VARIANT_TAG(debug_archive, rct::rangeSig, "rct::rangeSig"); 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(debug_archive, rct::rctSig, "rct::rctSig");
VARIANT_TAG(binary_archive, rct::key, 0x90); 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::ecdhTuple, 0x97);
VARIANT_TAG(binary_archive, rct::mgSig, 0x98); VARIANT_TAG(binary_archive, rct::mgSig, 0x98);
VARIANT_TAG(binary_archive, rct::rangeSig, 0x99); 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(binary_archive, rct::rctSig, 0x9b);
VARIANT_TAG(json_archive, rct::key, "rct_key"); 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::ecdhTuple, "rct_ecdhTuple");
VARIANT_TAG(json_archive, rct::mgSig, "rct_mgSig"); VARIANT_TAG(json_archive, rct::mgSig, "rct_mgSig");
VARIANT_TAG(json_archive, rct::rangeSig, "rct_rangeSig"); 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"); VARIANT_TAG(json_archive, rct::rctSig, "rct_rctSig");
#endif /* RCTTYPES_H */ #endif /* RCTTYPES_H */

View file

@ -40,29 +40,12 @@
using namespace crypto; using namespace crypto;
using namespace rct; using namespace rct;
TEST(ringct, SNL) TEST(ringct, Borromean)
{
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)
{ {
int j = 0; int j = 0;
//Tests for ASNL //Tests for Borromean signatures
//#ASNL true one, false one, C != sum Ci, and one out of the range.. //#boro true one, false one, C != sum Ci, and one out of the range..
int N = 64; int N = 64;
key64 xv; key64 xv;
key64 P1v; key64 P1v;
@ -74,34 +57,30 @@ TEST(ringct, ASNL)
xv[j] = skGen(); xv[j] = skGen();
if ( (int)indi[j] == 0 ) { if ( (int)indi[j] == 0 ) {
P1v[j] = scalarmultBase(xv[j]); scalarmultBase(P1v[j], xv[j]);
P2v[j] = pkGen();
} else { } else {
addKeys1(P1v[j], xv[j], H2[j]);
P2v[j] = scalarmultBase(xv[j]);
P1v[j] = pkGen();
} }
subKeys(P2v[j], P1v[j], H2[j]);
} }
//#true one //#true one
asnlSig L1s2s = GenASNL(xv, P1v, P2v, indi); boroSig bb = genBorromean(xv, P1v, P2v, indi);
ASSERT_TRUE(VerASNL(P1v, P2v, L1s2s)); ASSERT_TRUE(verifyBorromean(bb, P1v, P2v));
//#false one //#false one
indi[3] = (indi[3] + 1) % 2; indi[3] = (indi[3] + 1) % 2;
L1s2s = GenASNL(xv, P1v, P2v, indi); bb = genBorromean(xv, P1v, P2v, indi);
ASSERT_FALSE(VerASNL(P1v, P2v, L1s2s)); ASSERT_FALSE(verifyBorromean(bb, P1v, P2v));
//#true one again //#true one again
indi[3] = (indi[3] + 1) % 2; indi[3] = (indi[3] + 1) % 2;
L1s2s = GenASNL(xv, P1v, P2v, indi); bb = genBorromean(xv, P1v, P2v, indi);
ASSERT_TRUE(VerASNL(P1v, P2v, L1s2s)); ASSERT_TRUE(verifyBorromean(bb, P1v, P2v));
//#false one //#false one
L1s2s = GenASNL(xv, P2v, P1v, indi); bb = genBorromean(xv, P2v, P1v, indi);
ASSERT_FALSE(VerASNL(P1v, P2v, L1s2s)); ASSERT_FALSE(verifyBorromean(bb, P1v, P2v));
} }
TEST(ringct, MG_sigs) TEST(ringct, MG_sigs)

View file

@ -457,7 +457,7 @@ TEST(Serialization, serializes_ringct_types)
rct::ctkeyV ctkeyv0, ctkeyv1; rct::ctkeyV ctkeyv0, ctkeyv1;
rct::ctkeyM ctkeym0, ctkeym1; rct::ctkeyM ctkeym0, ctkeym1;
rct::ecdhTuple ecdh0, ecdh1; rct::ecdhTuple ecdh0, ecdh1;
rct::asnlSig asnl0, asnl1; rct::boroSig boro0, boro1;
rct::mgSig mg0, mg1; rct::mgSig mg0, mg1;
rct::rangeSig rg0, rg1; rct::rangeSig rg0, rg1;
rct::rctSig s0, s1; rct::rctSig s0, s1;
@ -541,13 +541,13 @@ TEST(Serialization, serializes_ringct_types)
for (size_t n = 0; n < 64; ++n) for (size_t n = 0; n < 64; ++n)
{ {
asnl0.L1[n] = rct::skGen(); boro0.s0[n] = rct::skGen();
asnl0.s2[n] = rct::skGen(); boro0.s1[n] = rct::skGen();
} }
asnl0.s = rct::skGen(); boro0.ee = rct::skGen();
ASSERT_TRUE(serialization::dump_binary(asnl0, blob)); ASSERT_TRUE(serialization::dump_binary(boro0, blob));
ASSERT_TRUE(serialization::parse_binary(blob, asnl1)); ASSERT_TRUE(serialization::parse_binary(blob, boro1));
ASSERT_TRUE(!memcmp(&asnl0, &asnl1, sizeof(asnl0))); ASSERT_TRUE(!memcmp(&boro0, &boro1, sizeof(boro0)));
// create a full rct signature to use its innards // create a full rct signature to use its innards
rct::ctkeyV sc, pc; rct::ctkeyV sc, pc;