ringct: switch to Borromean signatures

This commit is contained in:
Shen Noether 2016-11-17 23:17:21 +00:00 committed by moneromooo-monero
parent 45bb393577
commit 76958fc75a
No known key found for this signature in database
GPG Key ID: 686F07454D6CEFC3
8 changed files with 111 additions and 154 deletions

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,94 +40,67 @@ 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{};
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) { //Borromean (c.f. gmax/andytoshi's paper)
key c1, c2, L2; boroSig genBorromean(const key64 x, const key64 P1, const key64 P2, const bits indices) {
key a = skGen(); key64 L[2], c[2], s[2], alpha, P[2];
if (index == 0) { int naught = 0, prime = 0, ii = 0, jj=0;
scalarmultBase(L1, a); for (ii = 0 ; ii < 64 ; ii++) {
hash_to_scalar(c2, L1); naught = indices[ii]; prime = (indices[ii] + 1) % 2;
skGen(s2); copy(P[0][ii], P1[ii]); //could probably user pointers
addKeys2(L2, s2, c2, P2); copy(P[1][ii], P2[ii]);
hash_to_scalar(c1, L2); skGen(alpha[ii]);
//s1 = a - x * c1 scalarmultBase(L[naught][ii], alpha[ii]);
sc_mulsub(s1.bytes, x.bytes, c1.bytes, a.bytes); 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) { boroSig bb;
scalarmultBase(L2, a); bb.ee = cn_fast_hash(L[1]); //or L[1]..
hash_to_scalar(c1, L2); key LL, cc;
skGen(s1); for (jj = 0 ; jj < 64 ; jj++) {
addKeys2(L1, s1, c1, P1); naught = indices[jj]; prime = (indices[jj] + 1) % 2;
hash_to_scalar(c2, L1); if (!indices[jj]) {
sc_mulsub(s2.bytes, x.bytes, c2.bytes, a.bytes); 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 { return bb;
throw std::runtime_error("GenSchnorrNonLinkable: invalid index (should be 0 or 1)"); }
//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) //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
// c.f. http://eprint.iacr.org/2015/1098 section 2. // 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); 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 +318,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 +344,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;
@ -86,22 +69,22 @@ TEST(ringct, ASNL)
} }
//#true one //#true one
asnlSig L1s2s = GenASNL(xv, P1v, P2v, indi); boro 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;