diff options
author | Sarang Noether <32460187+SarangNoether@users.noreply.github.com> | 2019-08-29 11:35:12 +0000 |
---|---|---|
committer | moneromooo-monero <moneromooo-monero@users.noreply.github.com> | 2020-08-27 12:43:29 +0000 |
commit | 4b328c66161d11ddb240ca9c10298b0581aaa6b5 (patch) | |
tree | b01f090bb79a5fa4bf37064b45accd4d5e6c62da /src/ringct | |
parent | Merge pull request #6771 (diff) | |
download | monero-4b328c66161d11ddb240ca9c10298b0581aaa6b5.tar.xz |
CLSAG signatures
Diffstat (limited to 'src/ringct')
-rw-r--r-- | src/ringct/rctOps.cpp | 17 | ||||
-rw-r--r-- | src/ringct/rctOps.h | 4 | ||||
-rw-r--r-- | src/ringct/rctSigs.cpp | 238 | ||||
-rw-r--r-- | src/ringct/rctSigs.h | 3 | ||||
-rw-r--r-- | src/ringct/rctTypes.h | 15 |
5 files changed, 277 insertions, 0 deletions
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 |