ringct: switch to Borromean signatures
This commit is contained in:
parent
45bb393577
commit
76958fc75a
8 changed files with 111 additions and 154 deletions
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue