aboutsummaryrefslogtreecommitdiff
path: root/src/ringct/rctSigs.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ringct/rctSigs.cpp')
-rw-r--r--src/ringct/rctSigs.cpp421
1 files changed, 404 insertions, 17 deletions
diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp
index 2e3e7007e..5fd7ac06d 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,167 @@ 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)
+ //
+ // The keys are set as follows:
+ // P[l] == p*G
+ // C[l] == z*G
+ // C[i] == C_nonzero[i] - C_offset (for hashing purposes) for all i
+ clsag CLSAG_Gen(const key &message, const keyV & P, const key & p, const keyV & C, const key & z, const keyV & C_nonzero, const key & C_offset, const unsigned int l, const multisig_kLRki *kLRki, key *mscout, key *mspout, hw::device &hwdev) {
+ 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(n == C_nonzero.size(), "Signing and commitment key vector sizes must match!");
+ CHECK_AND_ASSERT_THROW_MES(l < n, "Signing index out of range!");
+ CHECK_AND_ASSERT_THROW_MES((kLRki && mscout) || (!kLRki && !mscout), "Only one of kLRki/mscout is present");
+ CHECK_AND_ASSERT_THROW_MES((mscout && mspout) || !kLRki, "Multisig pointers are not all present");
+
+ // Key images
+ ge_p3 H_p3;
+ hash_to_p3(H_p3,P[l]);
+ key H;
+ ge_p3_tobytes(H.bytes,&H_p3);
+
+ key D;
+
+ // Initial values
+ key a;
+ key aG;
+ key aH;
+
+ // Multisig
+ if (kLRki)
+ {
+ sig.I = kLRki->ki;
+ scalarmultKey(D,H,z);
+ }
+ else
+ {
+ hwdev.clsag_prepare(p,z,sig.I,D,H,a,aG,aH);
+ }
+
+ 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);
+
+ // Aggregation hashes
+ keyV mu_P_to_hash(2*n+4); // domain, I, D, P, C, C_offset
+ keyV mu_C_to_hash(2*n+4); // domain, I, D, P, C, C_offset
+ 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_nonzero[i-n-1];
+ mu_C_to_hash[i] = C_nonzero[i-n-1];
+ }
+ mu_P_to_hash[2*n+1] = sig.I;
+ mu_P_to_hash[2*n+2] = sig.D;
+ mu_P_to_hash[2*n+3] = C_offset;
+ mu_C_to_hash[2*n+1] = sig.I;
+ mu_C_to_hash[2*n+2] = sig.D;
+ mu_C_to_hash[2*n+3] = C_offset;
+ 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+5); // domain, P, C, C_offset, 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_nonzero[i-1];
+ }
+ c_to_hash[2*n+1] = C_offset;
+ c_to_hash[2*n+2] = message;
+
+ // Multisig data is present
+ if (kLRki)
+ {
+ a = kLRki->k;
+ c_to_hash[2*n+3] = kLRki->L;
+ c_to_hash[2*n+4] = kLRki->R;
+ }
+ else
+ {
+ c_to_hash[2*n+3] = aG;
+ c_to_hash[2*n+4] = aH;
+ }
+ hwdev.clsag_hash(c_to_hash,c);
+
+ 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+3] = L;
+ c_to_hash[2*n+4] = R;
+ hwdev.clsag_hash(c_to_hash,c_new);
+ copy(c,c_new);
+
+ i = (i + 1) % n;
+ if (i == 0)
+ copy(sig.c1,c);
+ }
+
+ // Compute final scalar
+ hwdev.clsag_sign(c,a,p,z,mu_P,mu_C,sig.s[l]);
+ memwipe(&a, sizeof(key));
+
+ if (mscout)
+ *mscout = c;
+ if (mspout)
+ *mspout = mu_P;
+
+ return sig;
+ }
+
+ clsag CLSAG_Gen(const key &message, const keyV & P, const key & p, const keyV & C, const key & z, const keyV & C_nonzero, const key & C_offset, const unsigned int l) {
+ return CLSAG_Gen(message, P, p, C, z, C_nonzero, C_offset, l, NULL, NULL, NULL, hw::get_device("default"));
+ }
+
// MLSAG signatures
// See paper by Noether (https://eprint.iacr.org/2015/1098)
// This generalization allows for some dimensions not to require linkability;
@@ -427,7 +589,7 @@ namespace rct {
hashes.push_back(hash2rct(h));
keyV kv;
- if (rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2)
+ if (rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG)
{
kv.reserve((6*2+9) * rv.p.bulletproofs.size());
for (const auto &p: rv.p.bulletproofs)
@@ -555,6 +717,37 @@ namespace rct {
return result;
}
+ clsag proveRctCLSAGSimple(const key &message, const ctkeyV &pubs, const ctkey &inSk, const key &a, const key &Cout, const multisig_kLRki *kLRki, key *mscout, key *mspout, unsigned int index, hw::device &hwdev) {
+ //setup vars
+ size_t rows = 1;
+ size_t cols = pubs.size();
+ CHECK_AND_ASSERT_THROW_MES(cols >= 1, "Empty pubs");
+ CHECK_AND_ASSERT_THROW_MES((kLRki && mscout) || (!kLRki && !mscout), "Only one of kLRki/mscout is present");
+ keyV tmp(rows + 1);
+ keyV sk(rows + 1);
+ size_t i;
+ keyM M(cols, tmp);
+
+ keyV P, C, C_nonzero;
+ P.reserve(pubs.size());
+ C.reserve(pubs.size());
+ C_nonzero.reserve(pubs.size());
+ for (const ctkey &k: pubs)
+ {
+ P.push_back(k.dest);
+ C_nonzero.push_back(k.mask);
+ rct::key tmp;
+ subKeys(tmp, k.mask, Cout);
+ C.push_back(tmp);
+ }
+
+ sk[0] = copy(inSk.dest);
+ sc_sub(sk[1].bytes, inSk.mask.bytes, a.bytes);
+ clsag result = CLSAG_Gen(message, P, sk[0], C, sk[1], C_nonzero, Cout, index, kLRki, mscout, mspout, hwdev);
+ memwipe(sk.data(), sk.size() * sizeof(key));
+ return result;
+ }
+
//Ring-ct MG sigs
//Prove:
@@ -634,6 +827,120 @@ namespace rct {
catch (...) { return false; }
}
+ bool verRctCLSAGSimple(const key &message, const clsag &sig, const ctkeyV & pubs, const key & C_offset) {
+ try
+ {
+ PERF_TIMER(verRctCLSAGSimple);
+ const size_t n = pubs.size();
+
+ // Check data
+ CHECK_AND_ASSERT_MES(n >= 1, false, "Empty pubs");
+ 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!");
+ CHECK_AND_ASSERT_MES(!(sig.I == rct::identity()), false, "Bad key image!");
+
+ // Cache commitment offset for efficient subtraction later
+ ge_p3 C_offset_p3;
+ CHECK_AND_ASSERT_MES(ge_frombytes_vartime(&C_offset_p3, C_offset.bytes) == 0, false, "point conv failed");
+ ge_cached C_offset_cached;
+ ge_p3_to_cached(&C_offset_cached, &C_offset_p3);
+
+ // Prepare key images
+ key c = copy(sig.c1);
+ key D_8 = scalarmult8(sig.D);
+ CHECK_AND_ASSERT_MES(!(D_8 == rct::identity()), false, "Bad auxiliary key image!");
+ 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+4); // domain, I, D, P, C, C_offset
+ keyV mu_C_to_hash(2*n+4); // domain, I, D, P, C, C_offset
+ 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] = pubs[i-1].dest;
+ mu_C_to_hash[i] = pubs[i-1].dest;
+ }
+ for (size_t i = n+1; i < 2*n+1; ++i) {
+ mu_P_to_hash[i] = pubs[i-n-1].mask;
+ mu_C_to_hash[i] = pubs[i-n-1].mask;
+ }
+ mu_P_to_hash[2*n+1] = sig.I;
+ mu_P_to_hash[2*n+2] = sig.D;
+ mu_P_to_hash[2*n+3] = C_offset;
+ mu_C_to_hash[2*n+1] = sig.I;
+ mu_C_to_hash[2*n+2] = sig.D;
+ mu_C_to_hash[2*n+3] = C_offset;
+ key mu_P, mu_C;
+ mu_P = hash_to_scalar(mu_P_to_hash);
+ mu_C = hash_to_scalar(mu_C_to_hash);
+
+ // Set up round hash
+ keyV c_to_hash(2*n+5); // domain, P, C, C_offset, 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] = pubs[i-1].dest;
+ c_to_hash[i+n] = pubs[i-1].mask;
+ }
+ c_to_hash[2*n+1] = C_offset;
+ c_to_hash[2*n+2] = 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;
+ ge_p3 temp_p3;
+ ge_p1p1 temp_p1;
+
+ 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 for L/R
+ precomp(P_precomp.k,pubs[i].dest);
+
+ CHECK_AND_ASSERT_MES(ge_frombytes_vartime(&temp_p3, pubs[i].mask.bytes) == 0, false, "point conv failed");
+ ge_sub(&temp_p1,&temp_p3,&C_offset_cached);
+ ge_p1p1_to_p3(&temp_p3,&temp_p1);
+ ge_dsm_precomp(C_precomp.k,&temp_p3);
+
+ // 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,pubs[i].dest);
+ 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+3] = L;
+ c_to_hash[2*n+4] = 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;
+ }
+ catch (...) { return false; }
+ }
+
//These functions get keys from blockchain
//replace these when connecting blockchain
@@ -726,7 +1033,7 @@ namespace rct {
//mask amount and mask
rv.ecdhInfo[i].mask = copy(outSk[i].mask);
rv.ecdhInfo[i].amount = d2h(amounts[i]);
- hwdev.ecdhEncode(rv.ecdhInfo[i], amount_keys[i], rv.type == RCTTypeBulletproof2);
+ hwdev.ecdhEncode(rv.ecdhInfo[i], amount_keys[i], rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG);
}
//set txn fee
@@ -774,7 +1081,27 @@ namespace rct {
}
rctSig rv;
- rv.type = bulletproof ? (rct_config.bp_version == 0 || rct_config.bp_version >= 2 ? RCTTypeBulletproof2 : RCTTypeBulletproof) : RCTTypeSimple;
+ if (bulletproof)
+ {
+ switch (rct_config.bp_version)
+ {
+ case 0:
+ case 3:
+ rv.type = RCTTypeCLSAG;
+ break;
+ case 2:
+ rv.type = RCTTypeBulletproof2;
+ break;
+ case 1:
+ rv.type = RCTTypeBulletproof;
+ break;
+ default:
+ ASSERT_MES_AND_THROW("Unsupported BP version: " << rct_config.bp_version);
+ }
+ }
+ else
+ rv.type = RCTTypeSimple;
+
rv.message = message;
rv.outPk.resize(destinations.size());
if (!bulletproof)
@@ -864,7 +1191,7 @@ namespace rct {
//mask amount and mask
rv.ecdhInfo[i].mask = copy(outSk[i].mask);
rv.ecdhInfo[i].amount = d2h(outamounts[i]);
- hwdev.ecdhEncode(rv.ecdhInfo[i], amount_keys[i], rv.type == RCTTypeBulletproof2);
+ hwdev.ecdhEncode(rv.ecdhInfo[i], amount_keys[i], rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG);
}
//set txn fee
@@ -874,7 +1201,10 @@ namespace rct {
rv.mixRing = mixRing;
keyV &pseudoOuts = bulletproof ? rv.p.pseudoOuts : rv.pseudoOuts;
pseudoOuts.resize(inamounts.size());
- rv.p.MGs.resize(inamounts.size());
+ if (rv.type == RCTTypeCLSAG)
+ rv.p.CLSAGs.resize(inamounts.size());
+ else
+ rv.p.MGs.resize(inamounts.size());
key sumpouts = zero(); //sum pseudoOut masks
keyV a(inamounts.size());
for (i = 0 ; i < inamounts.size() - 1; i++) {
@@ -888,9 +1218,20 @@ namespace rct {
key full_message = get_pre_mlsag_hash(rv,hwdev);
if (msout)
- msout->c.resize(inamounts.size());
- for (i = 0 ; i < inamounts.size(); i++) {
- rv.p.MGs[i] = proveRctMGSimple(full_message, rv.mixRing[i], inSk[i], a[i], pseudoOuts[i], kLRki ? &(*kLRki)[i]: NULL, msout ? &msout->c[i] : NULL, index[i], hwdev);
+ {
+ msout->c.resize(inamounts.size());
+ msout->mu_p.resize(rv.type == RCTTypeCLSAG ? inamounts.size() : 0);
+ }
+ for (i = 0 ; i < inamounts.size(); i++)
+ {
+ if (rv.type == RCTTypeCLSAG)
+ {
+ rv.p.CLSAGs[i] = proveRctCLSAGSimple(full_message, rv.mixRing[i], inSk[i], a[i], pseudoOuts[i], kLRki ? &(*kLRki)[i]: NULL, msout ? &msout->c[i] : NULL, msout ? &msout->mu_p[i] : NULL, index[i], hwdev);
+ }
+ else
+ {
+ rv.p.MGs[i] = proveRctMGSimple(full_message, rv.mixRing[i], inSk[i], a[i], pseudoOuts[i], kLRki ? &(*kLRki)[i]: NULL, msout ? &msout->c[i] : NULL, index[i], hwdev);
+ }
}
return rv;
}
@@ -995,13 +1336,22 @@ namespace rct {
{
CHECK_AND_ASSERT_MES(rvp, false, "rctSig pointer is NULL");
const rctSig &rv = *rvp;
- CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2,
+ CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG,
false, "verRctSemanticsSimple called on non simple rctSig");
const bool bulletproof = is_rct_bulletproof(rv.type);
if (bulletproof)
{
CHECK_AND_ASSERT_MES(rv.outPk.size() == n_bulletproof_amounts(rv.p.bulletproofs), false, "Mismatched sizes of outPk and bulletproofs");
- CHECK_AND_ASSERT_MES(rv.p.pseudoOuts.size() == rv.p.MGs.size(), false, "Mismatched sizes of rv.p.pseudoOuts and rv.p.MGs");
+ if (rv.type == RCTTypeCLSAG)
+ {
+ CHECK_AND_ASSERT_MES(rv.p.MGs.empty(), false, "MGs are not empty for CLSAG");
+ CHECK_AND_ASSERT_MES(rv.p.pseudoOuts.size() == rv.p.CLSAGs.size(), false, "Mismatched sizes of rv.p.pseudoOuts and rv.p.CLSAGs");
+ }
+ else
+ {
+ CHECK_AND_ASSERT_MES(rv.p.CLSAGs.empty(), false, "CLSAGs are not empty for MLSAG");
+ CHECK_AND_ASSERT_MES(rv.p.pseudoOuts.size() == rv.p.MGs.size(), false, "Mismatched sizes of rv.p.pseudoOuts and rv.p.MGs");
+ }
CHECK_AND_ASSERT_MES(rv.pseudoOuts.empty(), false, "rv.pseudoOuts is not empty");
}
else
@@ -1095,7 +1445,7 @@ namespace rct {
{
PERF_TIMER(verRctNonSemanticsSimple);
- CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2,
+ CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG,
false, "verRctNonSemanticsSimple called on non simple rctSig");
const bool bulletproof = is_rct_bulletproof(rv.type);
// semantics check is early, and mixRing/MGs aren't resolved yet
@@ -1118,14 +1468,19 @@ namespace rct {
results.resize(rv.mixRing.size());
for (size_t i = 0 ; i < rv.mixRing.size() ; i++) {
tpool.submit(&waiter, [&, i] {
- results[i] = verRctMGSimple(message, rv.p.MGs[i], rv.mixRing[i], pseudoOuts[i]);
+ if (rv.type == RCTTypeCLSAG)
+ {
+ results[i] = verRctCLSAGSimple(message, rv.p.CLSAGs[i], rv.mixRing[i], pseudoOuts[i]);
+ }
+ else
+ results[i] = verRctMGSimple(message, rv.p.MGs[i], rv.mixRing[i], pseudoOuts[i]);
});
}
waiter.wait(&tpool);
for (size_t i = 0; i < results.size(); ++i) {
if (!results[i]) {
- LOG_PRINT_L1("verRctMGSimple failed for input " << i);
+ LOG_PRINT_L1("verRctMGSimple/verRctCLSAGSimple failed for input " << i);
return false;
}
}
@@ -1162,7 +1517,7 @@ namespace rct {
//mask amount and mask
ecdhTuple ecdh_info = rv.ecdhInfo[i];
- hwdev.ecdhDecode(ecdh_info, sk, rv.type == RCTTypeBulletproof2);
+ hwdev.ecdhDecode(ecdh_info, sk, rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG);
mask = ecdh_info.mask;
key amount = ecdh_info.amount;
key C = rv.outPk[i].mask;
@@ -1186,13 +1541,13 @@ namespace rct {
}
xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i, key &mask, hw::device &hwdev) {
- CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2, false, "decodeRct called on non simple rctSig");
+ CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG, false, "decodeRct called on non simple rctSig");
CHECK_AND_ASSERT_THROW_MES(i < rv.ecdhInfo.size(), "Bad index");
CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.ecdhInfo.size(), "Mismatched sizes of rv.outPk and rv.ecdhInfo");
//mask amount and mask
ecdhTuple ecdh_info = rv.ecdhInfo[i];
- hwdev.ecdhDecode(ecdh_info, sk, rv.type == RCTTypeBulletproof2);
+ hwdev.ecdhDecode(ecdh_info, sk, rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeCLSAG);
mask = ecdh_info.mask;
key amount = ecdh_info.amount;
key C = rv.outPk[i].mask;
@@ -1215,12 +1570,13 @@ namespace rct {
return decodeRctSimple(rv, sk, i, mask, hwdev);
}
- bool signMultisig(rctSig &rv, const std::vector<unsigned int> &indices, const keyV &k, const multisig_out &msout, const key &secret_key) {
+ bool signMultisigMLSAG(rctSig &rv, const std::vector<unsigned int> &indices, const keyV &k, const multisig_out &msout, const key &secret_key) {
CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull || rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2,
false, "unsupported rct type");
CHECK_AND_ASSERT_MES(indices.size() == k.size(), false, "Mismatched k/indices sizes");
CHECK_AND_ASSERT_MES(k.size() == rv.p.MGs.size(), false, "Mismatched k/MGs size");
CHECK_AND_ASSERT_MES(k.size() == msout.c.size(), false, "Mismatched k/msout.c size");
+ CHECK_AND_ASSERT_MES(rv.p.CLSAGs.empty(), false, "CLSAGs not empty for MLSAGs");
if (rv.type == RCTTypeFull)
{
CHECK_AND_ASSERT_MES(rv.p.MGs.size() == 1, false, "MGs not a single element");
@@ -1230,6 +1586,8 @@ namespace rct {
CHECK_AND_ASSERT_MES(!rv.p.MGs[n].ss[indices[n]].empty(), false, "empty ss line");
}
+ // MLSAG: each player contributes a share to the secret-index ss: k - cc*secret_key_share
+ // cc: msout.c[n], secret_key_share: secret_key
for (size_t n = 0; n < indices.size(); ++n) {
rct::key diff;
sc_mulsub(diff.bytes, msout.c[n].bytes, secret_key.bytes, k[n].bytes);
@@ -1237,4 +1595,33 @@ namespace rct {
}
return true;
}
+
+ bool signMultisigCLSAG(rctSig &rv, const std::vector<unsigned int> &indices, const keyV &k, const multisig_out &msout, const key &secret_key) {
+ CHECK_AND_ASSERT_MES(rv.type == RCTTypeCLSAG, false, "unsupported rct type");
+ CHECK_AND_ASSERT_MES(indices.size() == k.size(), false, "Mismatched k/indices sizes");
+ CHECK_AND_ASSERT_MES(k.size() == rv.p.CLSAGs.size(), false, "Mismatched k/CLSAGs size");
+ CHECK_AND_ASSERT_MES(k.size() == msout.c.size(), false, "Mismatched k/msout.c size");
+ CHECK_AND_ASSERT_MES(rv.p.MGs.empty(), false, "MGs not empty for CLSAGs");
+ CHECK_AND_ASSERT_MES(msout.c.size() == msout.mu_p.size(), false, "Bad mu_p size");
+ for (size_t n = 0; n < indices.size(); ++n) {
+ CHECK_AND_ASSERT_MES(indices[n] < rv.p.CLSAGs[n].s.size(), false, "Index out of range");
+ }
+
+ // CLSAG: each player contributes a share to the secret-index ss: k - cc*mu_p*secret_key_share
+ // cc: msout.c[n], mu_p, msout.mu_p[n], secret_key_share: secret_key
+ for (size_t n = 0; n < indices.size(); ++n) {
+ rct::key diff, sk;
+ sc_mul(sk.bytes, msout.mu_p[n].bytes, secret_key.bytes);
+ sc_mulsub(diff.bytes, msout.c[n].bytes, sk.bytes, k[n].bytes);
+ sc_add(rv.p.CLSAGs[n].s[indices[n]].bytes, rv.p.CLSAGs[n].s[indices[n]].bytes, diff.bytes);
+ }
+ return true;
+ }
+
+ bool signMultisig(rctSig &rv, const std::vector<unsigned int> &indices, const keyV &k, const multisig_out &msout, const key &secret_key) {
+ if (rv.type == RCTTypeCLSAG)
+ return signMultisigCLSAG(rv, indices, k, msout, secret_key);
+ else
+ return signMultisigMLSAG(rv, indices, k, msout, secret_key);
+ }
}