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

View File

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

View File

@ -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;

View File

@ -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);

View File

@ -40,92 +40,64 @@ 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{};
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);
}
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)");
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{};
}
//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);
//Borromean (c.f. gmax/andytoshi's paper)
boroSig genBorromean(const key64 x, const key64 P1, const key64 P2, const bits indices) {
key64 L[2], alpha;
key c;
int naught = 0, prime = 0, ii = 0, jj=0;
boroSig bb;
for (ii = 0 ; ii < 64 ; ii++) {
naught = indices[ii]; prime = (indices[ii] + 1) % 2;
skGen(alpha[ii]);
scalarmultBase(L[naught][ii], alpha[ii]);
if (naught == 0) {
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)
// 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);
//see above.
bool verifyBorromean(const boroSig &bb, const key64 P1, const key64 P2) {
key64 Lv1; key chash, LL;
int ii = 0;
for (ii = 0 ; ii < 64 ; ii++) {
addKeys2(LL, bb.s0[ii], bb.ee, P1[ii]);
chash = hash_to_scalar(LL);
addKeys2(Lv1[ii], bb.s1[ii], chash, P2[ii]);
}
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;
key eeComputed = hash_to_scalar(Lv1); //hash function fine
return equalKeys(eeComputed, bb.ee);
}
//Multilayered Spontaneous Anonymous Group Signatures (MLSAG signatures)
@ -323,7 +295,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 +317,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 +343,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]);
}

View File

@ -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

View File

@ -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 */

View File

@ -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;
@ -74,34 +57,30 @@ TEST(ringct, ASNL)
xv[j] = skGen();
if ( (int)indi[j] == 0 ) {
P1v[j] = scalarmultBase(xv[j]);
P2v[j] = pkGen();
scalarmultBase(P1v[j], xv[j]);
} else {
P2v[j] = scalarmultBase(xv[j]);
P1v[j] = pkGen();
addKeys1(P1v[j], xv[j], H2[j]);
}
subKeys(P2v[j], P1v[j], H2[j]);
}
//#true one
asnlSig L1s2s = GenASNL(xv, P1v, P2v, indi);
ASSERT_TRUE(VerASNL(P1v, P2v, L1s2s));
boroSig 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)

View File

@ -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;