aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSarang Noether <32460187+SarangNoether@users.noreply.github.com>2019-08-29 11:35:12 +0000
committermoneromooo-monero <moneromooo-monero@users.noreply.github.com>2020-08-27 12:43:29 +0000
commit4b328c66161d11ddb240ca9c10298b0581aaa6b5 (patch)
treeb01f090bb79a5fa4bf37064b45accd4d5e6c62da /src
parentMerge pull request #6771 (diff)
downloadmonero-4b328c66161d11ddb240ca9c10298b0581aaa6b5.tar.xz
CLSAG signatures
Diffstat (limited to 'src')
-rw-r--r--src/crypto/crypto-ops.c100
-rw-r--r--src/crypto/crypto-ops.h2
-rw-r--r--src/cryptonote_config.h3
-rw-r--r--src/ringct/rctOps.cpp17
-rw-r--r--src/ringct/rctOps.h4
-rw-r--r--src/ringct/rctSigs.cpp238
-rw-r--r--src/ringct/rctSigs.h3
-rw-r--r--src/ringct/rctTypes.h15
8 files changed, 382 insertions, 0 deletions
diff --git a/src/crypto/crypto-ops.c b/src/crypto/crypto-ops.c
index 3110d3ce7..508709280 100644
--- a/src/crypto/crypto-ops.c
+++ b/src/crypto/crypto-ops.c
@@ -1234,6 +1234,56 @@ void ge_double_scalarmult_base_vartime(ge_p2 *r, const unsigned char *a, const g
}
}
+// Computes aG + bB + cC (G is the fixed basepoint)
+void ge_triple_scalarmult_base_vartime(ge_p2 *r, const unsigned char *a, const unsigned char *b, const ge_dsmp Bi, const unsigned char *c, const ge_dsmp Ci) {
+ signed char aslide[256];
+ signed char bslide[256];
+ signed char cslide[256];
+ ge_p1p1 t;
+ ge_p3 u;
+ int i;
+
+ slide(aslide, a);
+ slide(bslide, b);
+ slide(cslide, c);
+
+ ge_p2_0(r);
+
+ for (i = 255; i >= 0; --i) {
+ if (aslide[i] || bslide[i] || cslide[i]) break;
+ }
+
+ for (; i >= 0; --i) {
+ ge_p2_dbl(&t, r);
+
+ if (aslide[i] > 0) {
+ ge_p1p1_to_p3(&u, &t);
+ ge_madd(&t, &u, &ge_Bi[aslide[i]/2]);
+ } else if (aslide[i] < 0) {
+ ge_p1p1_to_p3(&u, &t);
+ ge_msub(&t, &u, &ge_Bi[(-aslide[i])/2]);
+ }
+
+ if (bslide[i] > 0) {
+ ge_p1p1_to_p3(&u, &t);
+ ge_add(&t, &u, &Bi[bslide[i]/2]);
+ } else if (bslide[i] < 0) {
+ ge_p1p1_to_p3(&u, &t);
+ ge_sub(&t, &u, &Bi[(-bslide[i])/2]);
+ }
+
+ if (cslide[i] > 0) {
+ ge_p1p1_to_p3(&u, &t);
+ ge_add(&t, &u, &Ci[cslide[i]/2]);
+ } else if (cslide[i] < 0) {
+ ge_p1p1_to_p3(&u, &t);
+ ge_sub(&t, &u, &Ci[(-cslide[i])/2]);
+ }
+
+ ge_p1p1_to_p2(r, &t);
+ }
+}
+
void ge_double_scalarmult_base_vartime_p3(ge_p3 *r3, const unsigned char *a, const ge_p3 *A, const unsigned char *b) {
signed char aslide[256];
signed char bslide[256];
@@ -2148,6 +2198,56 @@ void ge_double_scalarmult_precomp_vartime2(ge_p2 *r, const unsigned char *a, con
}
}
+// Computes aA + bB + cC (all points require precomputation)
+void ge_triple_scalarmult_precomp_vartime(ge_p2 *r, const unsigned char *a, const ge_dsmp Ai, const unsigned char *b, const ge_dsmp Bi, const unsigned char *c, const ge_dsmp Ci) {
+ signed char aslide[256];
+ signed char bslide[256];
+ signed char cslide[256];
+ ge_p1p1 t;
+ ge_p3 u;
+ int i;
+
+ slide(aslide, a);
+ slide(bslide, b);
+ slide(cslide, c);
+
+ ge_p2_0(r);
+
+ for (i = 255; i >= 0; --i) {
+ if (aslide[i] || bslide[i] || cslide[i]) break;
+ }
+
+ for (; i >= 0; --i) {
+ ge_p2_dbl(&t, r);
+
+ if (aslide[i] > 0) {
+ ge_p1p1_to_p3(&u, &t);
+ ge_add(&t, &u, &Ai[aslide[i]/2]);
+ } else if (aslide[i] < 0) {
+ ge_p1p1_to_p3(&u, &t);
+ ge_sub(&t, &u, &Ai[(-aslide[i])/2]);
+ }
+
+ if (bslide[i] > 0) {
+ ge_p1p1_to_p3(&u, &t);
+ ge_add(&t, &u, &Bi[bslide[i]/2]);
+ } else if (bslide[i] < 0) {
+ ge_p1p1_to_p3(&u, &t);
+ ge_sub(&t, &u, &Bi[(-bslide[i])/2]);
+ }
+
+ if (cslide[i] > 0) {
+ ge_p1p1_to_p3(&u, &t);
+ ge_add(&t, &u, &Ci[cslide[i]/2]);
+ } else if (cslide[i] < 0) {
+ ge_p1p1_to_p3(&u, &t);
+ ge_sub(&t, &u, &Ci[(-cslide[i])/2]);
+ }
+
+ ge_p1p1_to_p2(r, &t);
+ }
+}
+
void ge_double_scalarmult_precomp_vartime2_p3(ge_p3 *r3, const unsigned char *a, const ge_dsmp Ai, const unsigned char *b, const ge_dsmp Bi) {
signed char aslide[256];
signed char bslide[256];
diff --git a/src/crypto/crypto-ops.h b/src/crypto/crypto-ops.h
index eeb94669b..22f76974b 100644
--- a/src/crypto/crypto-ops.h
+++ b/src/crypto/crypto-ops.h
@@ -79,6 +79,7 @@ typedef ge_cached ge_dsmp[8];
extern const ge_precomp ge_Bi[8];
void ge_dsm_precomp(ge_dsmp r, const ge_p3 *s);
void ge_double_scalarmult_base_vartime(ge_p2 *, const unsigned char *, const ge_p3 *, const unsigned char *);
+void ge_triple_scalarmult_base_vartime(ge_p2 *, const unsigned char *, const unsigned char *, const ge_dsmp, const unsigned char *, const ge_dsmp);
void ge_double_scalarmult_base_vartime_p3(ge_p3 *, const unsigned char *, const ge_p3 *, const unsigned char *);
/* From ge_frombytes.c, modified */
@@ -130,6 +131,7 @@ void sc_reduce(unsigned char *);
void ge_scalarmult(ge_p2 *, const unsigned char *, const ge_p3 *);
void ge_scalarmult_p3(ge_p3 *, const unsigned char *, const ge_p3 *);
void ge_double_scalarmult_precomp_vartime(ge_p2 *, const unsigned char *, const ge_p3 *, const unsigned char *, const ge_dsmp);
+void ge_triple_scalarmult_precomp_vartime(ge_p2 *, const unsigned char *, const ge_dsmp, const unsigned char *, const ge_dsmp, const unsigned char *, const ge_dsmp);
void ge_double_scalarmult_precomp_vartime2(ge_p2 *, const unsigned char *, const ge_dsmp, const unsigned char *, const ge_dsmp);
void ge_double_scalarmult_precomp_vartime2_p3(ge_p3 *, const unsigned char *, const ge_dsmp, const unsigned char *, const ge_dsmp);
void ge_mul8(ge_p1p1 *, const ge_p2 *);
diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h
index 8051ee9fa..7c240423a 100644
--- a/src/cryptonote_config.h
+++ b/src/cryptonote_config.h
@@ -226,6 +226,9 @@ namespace config
const unsigned char HASH_KEY_MEMORY = 'k';
const unsigned char HASH_KEY_MULTISIG[] = {'M', 'u', 'l', 't' , 'i', 's', 'i', 'g', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
const unsigned char HASH_KEY_TXPROOF_V2[] = "TXPROOF_V2";
+ const unsigned char HASH_KEY_CLSAG_ROUND[] = "CLSAG_round";
+ const unsigned char HASH_KEY_CLSAG_AGG_0[] = "CLSAG_agg_0";
+ const unsigned char HASH_KEY_CLSAG_AGG_1[] = "CLSAG_agg_1";
namespace testnet
{
diff --git a/src/ringct/rctOps.cpp b/src/ringct/rctOps.cpp
index b2dd32ada..245a3f477 100644
--- a/src/ringct/rctOps.cpp
+++ b/src/ringct/rctOps.cpp
@@ -511,6 +511,23 @@ namespace rct {
ge_tobytes(aAbB.bytes, &rv);
}
+ // addKeys_aGbBcC
+ // computes aG + bB + cC
+ // G is the fixed basepoint and B,C require precomputation
+ void addKeys_aGbBcC(key &aGbBcC, const key &a, const key &b, const ge_dsmp B, const key &c, const ge_dsmp C) {
+ ge_p2 rv;
+ ge_triple_scalarmult_base_vartime(&rv, a.bytes, b.bytes, B, c.bytes, C);
+ ge_tobytes(aGbBcC.bytes, &rv);
+ }
+
+ // addKeys_aAbBcC
+ // computes aA + bB + cC
+ // A,B,C require precomputation
+ void addKeys_aAbBcC(key &aAbBcC, const key &a, const ge_dsmp A, const key &b, const ge_dsmp B, const key &c, const ge_dsmp C) {
+ ge_p2 rv;
+ ge_triple_scalarmult_precomp_vartime(&rv, a.bytes, A, b.bytes, B, c.bytes, C);
+ ge_tobytes(aAbBcC.bytes, &rv);
+ }
//subtract Keys (subtracts curve points)
//AB = A - B where A, B are curve points
diff --git a/src/ringct/rctOps.h b/src/ringct/rctOps.h
index 74e0ad833..679ed1441 100644
--- a/src/ringct/rctOps.h
+++ b/src/ringct/rctOps.h
@@ -145,6 +145,10 @@ namespace rct {
//B must be input after applying "precomp"
void addKeys3(key &aAbB, const key &a, const key &A, const key &b, const ge_dsmp B);
void addKeys3(key &aAbB, const key &a, const ge_dsmp A, const key &b, const ge_dsmp B);
+
+ void addKeys_aGbBcC(key &aGbBcC, const key &a, const key &b, const ge_dsmp B, const key &c, const ge_dsmp C);
+ void addKeys_aAbBcC(key &aAbBcC, const key &a, const ge_dsmp A, const key &b, const ge_dsmp B, const key &c, const ge_dsmp C);
+
//AB = A - B where A, B are curve points
void subKeys(key &AB, const key &A, const key &B);
//checks if A, B are equal as curve points
diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp
index 2e3e7007e..cb702ed15 100644
--- a/src/ringct/rctSigs.cpp
+++ b/src/ringct/rctSigs.cpp
@@ -36,6 +36,7 @@
#include "rctSigs.h"
#include "bulletproofs.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
+#include "cryptonote_config.h"
using namespace crypto;
using namespace std;
@@ -165,6 +166,243 @@ namespace rct {
return verifyBorromean(bb, P1_p3, P2_p3);
}
+ // Generate a CLSAG signature
+ // See paper by Goodell et al. (https://eprint.iacr.org/2019/654)
+ clsag CLSAG_Gen(const key &message, const keyV & P, const key & p, const keyV & C, const key & z, const unsigned int l, const multisig_kLRki *kLRki) {
+ clsag sig;
+ size_t n = P.size(); // ring size
+ CHECK_AND_ASSERT_THROW_MES(n == C.size(), "Signing and commitment key vector sizes must match!");
+ CHECK_AND_ASSERT_THROW_MES(l < n, "Signing index out of range!");
+
+ // Key images
+ ge_p3 H_p3;
+ hash_to_p3(H_p3,P[l]);
+ key H;
+ ge_p3_tobytes(H.bytes,&H_p3);
+
+ key D;
+ scalarmultKey(D,H,z);
+
+ // Multisig
+ if (kLRki)
+ {
+ sig.I = kLRki->ki;
+ }
+ else
+ {
+ scalarmultKey(sig.I,H,p);
+ }
+
+ geDsmp I_precomp;
+ geDsmp D_precomp;
+ precomp(I_precomp.k,sig.I);
+ precomp(D_precomp.k,D);
+
+ // Offset key image
+ scalarmultKey(sig.D,D,INV_EIGHT);
+
+ // Initial values
+ key a;
+ key aG;
+ key aH;
+ skpkGen(a,aG);
+ scalarmultKey(aH,H,a);
+
+ // Aggregation hashes
+ keyV mu_P_to_hash(2*n+3); // domain, I, D, P, C
+ keyV mu_C_to_hash(2*n+3); // domain, I, D, P, C
+ sc_0(mu_P_to_hash[0].bytes);
+ memcpy(mu_P_to_hash[0].bytes,config::HASH_KEY_CLSAG_AGG_0,sizeof(config::HASH_KEY_CLSAG_AGG_0)-1);
+ sc_0(mu_C_to_hash[0].bytes);
+ memcpy(mu_C_to_hash[0].bytes,config::HASH_KEY_CLSAG_AGG_1,sizeof(config::HASH_KEY_CLSAG_AGG_1)-1);
+ for (size_t i = 1; i < n+1; ++i) {
+ mu_P_to_hash[i] = P[i-1];
+ mu_C_to_hash[i] = P[i-1];
+ }
+ for (size_t i = n+1; i < 2*n+1; ++i) {
+ mu_P_to_hash[i] = C[i-n-1];
+ mu_C_to_hash[i] = C[i-n-1];
+ }
+ mu_P_to_hash[2*n+1] = sig.I;
+ mu_P_to_hash[2*n+2] = sig.D;
+ mu_C_to_hash[2*n+1] = sig.I;
+ mu_C_to_hash[2*n+2] = sig.D;
+ key mu_P, mu_C;
+ mu_P = hash_to_scalar(mu_P_to_hash);
+ mu_C = hash_to_scalar(mu_C_to_hash);
+
+ // Initial commitment
+ keyV c_to_hash(2*n+4); // domain, P, C, message, aG, aH
+ key c;
+ sc_0(c_to_hash[0].bytes);
+ memcpy(c_to_hash[0].bytes,config::HASH_KEY_CLSAG_ROUND,sizeof(config::HASH_KEY_CLSAG_ROUND)-1);
+ for (size_t i = 1; i < n+1; ++i)
+ {
+ c_to_hash[i] = P[i-1];
+ c_to_hash[i+n] = C[i-1];
+ }
+ c_to_hash[2*n+1] = message;
+
+ // Multisig data is present
+ if (kLRki)
+ {
+ a = kLRki->k;
+ c_to_hash[2*n+2] = kLRki->L;
+ c_to_hash[2*n+3] = kLRki->R;
+ }
+ else
+ {
+ c_to_hash[2*n+2] = aG;
+ c_to_hash[2*n+3] = aH;
+ }
+ c = hash_to_scalar(c_to_hash);
+
+ size_t i;
+ i = (l + 1) % n;
+ if (i == 0)
+ copy(sig.c1, c);
+
+ // Decoy indices
+ sig.s = keyV(n);
+ key c_new;
+ key L;
+ key R;
+ key c_p; // = c[i]*mu_P
+ key c_c; // = c[i]*mu_C
+ geDsmp P_precomp;
+ geDsmp C_precomp;
+ geDsmp H_precomp;
+ ge_p3 Hi_p3;
+
+ while (i != l) {
+ sig.s[i] = skGen();
+ sc_0(c_new.bytes);
+ sc_mul(c_p.bytes,mu_P.bytes,c.bytes);
+ sc_mul(c_c.bytes,mu_C.bytes,c.bytes);
+
+ // Precompute points
+ precomp(P_precomp.k,P[i]);
+ precomp(C_precomp.k,C[i]);
+
+ // Compute L
+ addKeys_aGbBcC(L,sig.s[i],c_p,P_precomp.k,c_c,C_precomp.k);
+
+ // Compute R
+ hash_to_p3(Hi_p3,P[i]);
+ ge_dsm_precomp(H_precomp.k, &Hi_p3);
+ addKeys_aAbBcC(R,sig.s[i],H_precomp.k,c_p,I_precomp.k,c_c,D_precomp.k);
+
+ c_to_hash[2*n+2] = L;
+ c_to_hash[2*n+3] = R;
+ c_new = hash_to_scalar(c_to_hash);
+ copy(c,c_new);
+
+ i = (i + 1) % n;
+ if (i == 0)
+ copy(sig.c1,c);
+ }
+
+ // Compute final scalar
+ key s0_p_mu_P;
+ sc_mul(s0_p_mu_P.bytes,mu_P.bytes,p.bytes);
+ key s0_add_z_mu_C;
+ sc_muladd(s0_add_z_mu_C.bytes,mu_C.bytes,z.bytes,s0_p_mu_P.bytes);
+ sc_mulsub(sig.s[l].bytes,c.bytes,s0_add_z_mu_C.bytes,a.bytes);
+
+ return sig;
+ }
+
+ // Verify a CLSAG signature
+ // See paper by Goodell et al. (https://eprint.iacr.org/2019/654)
+ bool CLSAG_Ver(const key &message, const keyV & P, const keyV & C, const clsag & sig)
+ {
+ size_t n = P.size(); // ring size
+ CHECK_AND_ASSERT_MES(n == C.size(), false, "Signing and commitment key vector sizes must match!");
+ CHECK_AND_ASSERT_MES(n == sig.s.size(), false, "Signature scalar vector is the wrong size!");
+ for (size_t i = 0; i < n; ++i)
+ CHECK_AND_ASSERT_MES(sc_check(sig.s[i].bytes) == 0, false, "Bad signature scalar!");
+ CHECK_AND_ASSERT_MES(sc_check(sig.c1.bytes) == 0, false, "Bad signature commitment!");
+
+ key c = copy(sig.c1);
+ key D_8 = scalarmult8(sig.D);
+ geDsmp I_precomp;
+ geDsmp D_precomp;
+ precomp(I_precomp.k,sig.I);
+ precomp(D_precomp.k,D_8);
+
+ // Aggregation hashes
+ keyV mu_P_to_hash(2*n+3); // domain, I, D, P, C
+ keyV mu_C_to_hash(2*n+3); // domain, I, D, P, C
+ sc_0(mu_P_to_hash[0].bytes);
+ memcpy(mu_P_to_hash[0].bytes,config::HASH_KEY_CLSAG_AGG_0,sizeof(config::HASH_KEY_CLSAG_AGG_0)-1);
+ sc_0(mu_C_to_hash[0].bytes);
+ memcpy(mu_C_to_hash[0].bytes,config::HASH_KEY_CLSAG_AGG_1,sizeof(config::HASH_KEY_CLSAG_AGG_1)-1);
+ for (size_t i = 1; i < n+1; ++i) {
+ mu_P_to_hash[i] = P[i-1];
+ mu_C_to_hash[i] = P[i-1];
+ }
+ for (size_t i = n+1; i < 2*n+1; ++i) {
+ mu_P_to_hash[i] = C[i-n-1];
+ mu_C_to_hash[i] = C[i-n-1];
+ }
+ mu_P_to_hash[2*n+1] = sig.I;
+ mu_P_to_hash[2*n+2] = sig.D;
+ mu_C_to_hash[2*n+1] = sig.I;
+ mu_C_to_hash[2*n+2] = sig.D;
+ key mu_P, mu_C;
+ mu_P = hash_to_scalar(mu_P_to_hash);
+ mu_C = hash_to_scalar(mu_C_to_hash);
+
+ keyV c_to_hash(2*n+4); // domain, P, C, message, L, R
+ sc_0(c_to_hash[0].bytes);
+ memcpy(c_to_hash[0].bytes,config::HASH_KEY_CLSAG_ROUND,sizeof(config::HASH_KEY_CLSAG_ROUND)-1);
+ for (size_t i = 1; i < n+1; ++i)
+ {
+ c_to_hash[i] = P[i-1];
+ c_to_hash[i+n] = C[i-1];
+ }
+ c_to_hash[2*n+1] = message;
+ key c_p; // = c[i]*mu_P
+ key c_c; // = c[i]*mu_C
+ key c_new;
+ key L;
+ key R;
+ geDsmp P_precomp;
+ geDsmp C_precomp;
+ geDsmp H_precomp;
+ size_t i = 0;
+ ge_p3 hash8_p3;
+ geDsmp hash_precomp;
+
+ while (i < n) {
+ sc_0(c_new.bytes);
+ sc_mul(c_p.bytes,mu_P.bytes,c.bytes);
+ sc_mul(c_c.bytes,mu_C.bytes,c.bytes);
+
+ // Precompute points
+ precomp(P_precomp.k,P[i]);
+ precomp(C_precomp.k,C[i]);
+
+ // Compute L
+ addKeys_aGbBcC(L,sig.s[i],c_p,P_precomp.k,c_c,C_precomp.k);
+
+ // Compute R
+ hash_to_p3(hash8_p3,P[i]);
+ ge_dsm_precomp(hash_precomp.k, &hash8_p3);
+ addKeys_aAbBcC(R,sig.s[i],hash_precomp.k,c_p,I_precomp.k,c_c,D_precomp.k);
+
+ c_to_hash[2*n+2] = L;
+ c_to_hash[2*n+3] = R;
+ c_new = hash_to_scalar(c_to_hash);
+ CHECK_AND_ASSERT_MES(!(c_new == rct::zero()), false, "Bad signature hash");
+ copy(c,c_new);
+
+ i = i + 1;
+ }
+ sc_sub(c_new.bytes,c.bytes,sig.c1.bytes);
+ return sc_isnonzero(c_new.bytes) == 0;
+ }
+
// MLSAG signatures
// See paper by Noether (https://eprint.iacr.org/2015/1098)
// This generalization allows for some dimensions not to require linkability;
diff --git a/src/ringct/rctSigs.h b/src/ringct/rctSigs.h
index 9227eab1e..87d2b994b 100644
--- a/src/ringct/rctSigs.h
+++ b/src/ringct/rctSigs.h
@@ -76,6 +76,9 @@ namespace rct {
// Ver verifies that the MG sig was created correctly
mgSig MLSAG_Gen(const key &message, const keyM & pk, const keyV & xx, const multisig_kLRki *kLRki, key *mscout, const unsigned int index, size_t dsRows, hw::device &hwdev);
bool MLSAG_Ver(const key &message, const keyM &pk, const mgSig &sig, size_t dsRows);
+
+ clsag CLSAG_Gen(const key &message, const keyV & P, const key & p, const keyV & C, const key & z, const unsigned int l, const multisig_kLRki *kLRki);
+ bool CLSAG_Ver(const key &message, const keyV & P, const keyV & C, const clsag & sig);
//mgSig MLSAG_Gen_Old(const keyM & pk, const keyV & xx, const int index);
//proveRange and verRange
diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h
index ce11981ad..b83c267d0 100644
--- a/src/ringct/rctTypes.h
+++ b/src/ringct/rctTypes.h
@@ -163,6 +163,21 @@ namespace rct {
// FIELD(II) - not serialized, it can be reconstructed
END_SERIALIZE()
};
+
+ // CLSAG signature
+ struct clsag {
+ keyV s; // scalars
+ key c1;
+
+ key I; // signing key image
+ key D; // commitment key image
+
+ BEGIN_SERIALIZE_OBJECT()
+ FIELD(s)
+ FIELD(c1)
+ END_SERIALIZE()
+ };
+
//contains the data for an Borromean sig
// also contains the "Ci" values such that
// \sum Ci = C