diff --git a/src/device/device.hpp b/src/device/device.hpp index 1c29018..ddf42d3 100644 --- a/src/device/device.hpp +++ b/src/device/device.hpp @@ -236,6 +236,8 @@ namespace hw { virtual bool clsag_hash(const rct::keyV &data, rct::key &hash) = 0; virtual bool clsag_sign(const rct::key &c, const rct::key &a, const rct::key &p, const rct::key &z, const rct::key &mu_P, const rct::key &mu_C, rct::key &s) = 0; + virtual bool clsag_prepare_carrot(const rct::key &p, const rct::key &z, rct::key &I, rct::key &D, const rct::key &H, rct::key &a, rct::key &aG, rct::key &b, rct::key &bT, rct::key &aH) = 0; + virtual bool close_tx(void) = 0; virtual bool has_ki_cold_sync(void) const { return false; } diff --git a/src/device/device_default.cpp b/src/device/device_default.cpp index 30bb4a2..76504d7 100644 --- a/src/device/device_default.cpp +++ b/src/device/device_default.cpp @@ -33,6 +33,7 @@ #include "device_default.hpp" #include "int-util.h" #include "crypto/wallet/crypto.h" +#include "crypto/generators.h" #include "cryptonote_basic/account.h" #include "cryptonote_basic/subaddress_index.h" #include "cryptonote_core/cryptonote_tx_utils.h" @@ -424,6 +425,19 @@ namespace hw { return true; } + bool device_default::clsag_prepare_carrot(const rct::key &p, const rct::key &z, rct::key &I, rct::key &D, const rct::key &H, rct::key &a, rct::key &aG, rct::key &b, rct::key &bT, rct::key &aH) { + rct::skpkGen(a,aG); // aG = a*G + rct::scalarmultKey(aH,H,a); // aH = a*H + rct::scalarmultKey(I,H,p); // I = p*H + rct::scalarmultKey(D,H,z); // D = z*H + + // bT = b*T + rct::skGen(b); + bT = rct::scalarmultKey(b, rct::pk2rct(crypto::get_T())); + + return true; + } + bool device_default::clsag_hash(const rct::keyV &data, rct::key &hash) { hash = rct::hash_to_scalar(data); return true; @@ -432,9 +446,10 @@ namespace hw { bool device_default::clsag_sign(const rct::key &c, const rct::key &a, const rct::key &p, const rct::key &z, const rct::key &mu_P, const rct::key &mu_C, rct::key &s) { rct::key s0_p_mu_P; sc_mul(s0_p_mu_P.bytes,mu_P.bytes,p.bytes); + rct::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(s.bytes,c.bytes,s0_add_z_mu_C.bytes,a.bytes); + sc_muladd(s0_add_z_mu_C.bytes, mu_C.bytes, z.bytes, s0_p_mu_P.bytes); + sc_mulsub(s.bytes, c.bytes, s0_add_z_mu_C.bytes, a.bytes); return true; } diff --git a/src/device/device_default.hpp b/src/device/device_default.hpp index 7ee9f15..65393c6 100644 --- a/src/device/device_default.hpp +++ b/src/device/device_default.hpp @@ -140,6 +140,7 @@ namespace hw { bool clsag_hash(const rct::keyV &data, rct::key &hash) override; bool clsag_sign(const rct::key &c, const rct::key &a, const rct::key &p, const rct::key &z, const rct::key &mu_P, const rct::key &mu_C, rct::key &s) override; + bool clsag_prepare_carrot(const rct::key &p, const rct::key &z, rct::key &I, rct::key &D, const rct::key &H, rct::key &a, rct::key &aG, rct::key &b, rct::key &bT, rct::key &aH); bool close_tx(void) override; }; diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp index 3d8cd7a..c842a09 100644 --- a/src/device/device_ledger.cpp +++ b/src/device/device_ledger.cpp @@ -2255,6 +2255,10 @@ namespace hw { return true; } + bool device_ledger::clsag_prepare_carrot(const rct::key &p, const rct::key &z, rct::key &I, rct::key &D, const rct::key &H, rct::key &a, rct::key &aG, rct::key &b, rct::key &bT, rct::key &aH) { + return true; // Not implemented for Ledger + } + bool device_ledger::clsag_hash(const rct::keyV &data, rct::key &hash) { AUTO_LOCK_CMD(); diff --git a/src/device/device_ledger.hpp b/src/device/device_ledger.hpp index b65943f..521c528 100644 --- a/src/device/device_ledger.hpp +++ b/src/device/device_ledger.hpp @@ -282,6 +282,8 @@ namespace hw { bool clsag_hash(const rct::keyV &data, rct::key &hash) override; bool clsag_sign(const rct::key &c, const rct::key &a, const rct::key &p, const rct::key &z, const rct::key &mu_P, const rct::key &mu_C, rct::key &s) override; + bool clsag_prepare_carrot(const rct::key &p, const rct::key &z, rct::key &I, rct::key &D, const rct::key &H, rct::key &a, rct::key &aG, rct::key &b, rct::key &bT, rct::key &aH) override; + bool close_tx(void) override; diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index 6357800..5375db9 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -34,6 +34,7 @@ #include "common/perf_timer.h" #include "common/threadpool.h" #include "common/util.h" +#include "crypto/generators.h" #include "rctSigs.h" #include "bulletproofs.h" #include "bulletproofs_plus.h" @@ -364,6 +365,152 @@ namespace rct { return sig; } + clsagCarrot CLSAG_Gen_Carrot( + const key &message, + const keyV & P, + const key & x, // x term of P + const key & y, // y term of P + const keyV & C, + const key & z, + const keyV & C_nonzero, + const key & C_offset, + const unsigned int l, + hw::device &hwdev) + { + clsagCarrot 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!"); + + // 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; + key b; + key bT; + + hwdev.clsag_prepare_carrot(x,z,sig.I,D,H,a,aG,b,bT,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; + + c_to_hash[2*n+3] = addKeys(aG, bT); // we use aG + bT instead of 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.sx = keyV(n); + sig.sy = 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.sx[i] = skGen(); + sig.sy[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.sx[i], c_p, P_precomp.k, c_c, C_precomp.k); + // add the T term + key rT = rct::scalarmultKey(b, rct::pk2rct(crypto::get_T())); + L = addKeys(L, rT); + + // Compute R + hash_to_p3(Hi_p3,P[i]); + ge_dsm_precomp(H_precomp.k, &Hi_p3); + addKeys_aAbBcC(R, sig.sx[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 scalars + hwdev.clsag_sign(c, a, x, z, mu_P, mu_C, sig.sx[l]); + hwdev.clsag_sign(c, b, y, z, mu_P, mu_C, sig.sy[l]); + memwipe(&a, sizeof(key)); + memwipe(&b, sizeof(key)); + + 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, hw::get_device("default")); } @@ -810,9 +957,9 @@ namespace rct { keyM M(cols, tmp); keyV P, C, C_nonzero; - P.reserve(pubs.size()); - C.reserve(pubs.size()); - C_nonzero.reserve(pubs.size()); + P.reserve(pubs.size()); + C.reserve(pubs.size()); + C_nonzero.reserve(pubs.size()); for (const ctkey &k: pubs) { P.push_back(k.dest); @@ -822,13 +969,41 @@ namespace rct { C.push_back(tmp); } - sk[0] = copy(inSk.dest); - sc_sub(sk[1].bytes, inSk.mask.bytes, a.bytes); + 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, hwdev); memwipe(sk.data(), sk.size() * sizeof(key)); return result; } + clsagCarrot proveRctCLSAGSSimpleCarrot(const key &message, const ctkeyV &pubs, const key &x, const key &y, const key &mask, const key &a, const key &Cout, 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"); + keyV tmp(rows + 1); + keyM M(cols, tmp); + + keyV P, C, C_nonzero; + P.reserve(pubs.size()); // pubkeys + C.reserve(pubs.size()); // commitments to 0. + C_nonzero.reserve(pubs.size()); // commitments + 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); + } + + key sk; + sc_sub(sk.bytes, mask.bytes, a.bytes); // private key of the output commitment + clsagCarrot result = CLSAG_Gen_Carrot(message, P, x, y, C, sk, C_nonzero, Cout, index, hwdev); + memwipe(&sk, sizeof(key)); + return result; + } + //Ring-ct MG sigs //Prove: @@ -1021,6 +1196,125 @@ namespace rct { catch (...) { return false; } } + bool verRctCLSAGSimpleCarrot(const key &message, const clsagCarrot &sig, const ctkeyV & pubs, const key & C_offset) { + try + { + PERF_TIMER(verRctCLSAGSimpleCarrot); + const size_t n = pubs.size(); + + // Check data + CHECK_AND_ASSERT_MES(n >= 1, false, "Empty pubs"); + CHECK_AND_ASSERT_MES(n == sig.sx.size(), false, "Signature scalar vector x is the wrong size!"); + CHECK_AND_ASSERT_MES(n == sig.sy.size(), false, "Signature scalar vector y is the wrong size!"); + for (size_t i = 0; i < n; ++i) { + CHECK_AND_ASSERT_MES(sc_check(sig.sx[i].bytes) == 0, false, "Bad signature scalar!"); + CHECK_AND_ASSERT_MES(sc_check(sig.sy[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; + 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.sx[i], c_p, P_precomp.k, c_c, C_precomp.k); + // add the T term + key rT = rct::scalarmultKey(sig.sy[i], rct::pk2rct(crypto::get_T())); + L = addKeys(L, rT); + + // Compute R + hash_to_p3(hash8_p3,pubs[i].dest); + ge_dsm_precomp(hash_precomp.k, &hash8_p3); + addKeys_aAbBcC(R,sig.sx[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 diff --git a/src/ringct/rctSigs.h b/src/ringct/rctSigs.h index a2c9d5b..c3d6186 100644 --- a/src/ringct/rctSigs.h +++ b/src/ringct/rctSigs.h @@ -85,6 +85,10 @@ namespace rct { clsag proveRctCLSAGSimple(const key &, const ctkeyV &, const ctkey &, const key &, const key &, unsigned int, hw::device &); bool verRctCLSAGSimple(const key &, const clsag &, const ctkeyV &, const key &); + clsagCarrot CLSAG_Gen_Carrot(const key &message, const keyV & P, const key & x, const key & y, const keyV & C, const key & z, const keyV & C_nonzero, const key & C_offset, const unsigned int l, hw::device &hwdev); + clsagCarrot proveRctCLSAGSSimpleCarrot(const key &message, const ctkeyV &pubs, const key &x, const key &y, const key &mask, const key &a, const key &Cout, unsigned int index, hw::device &hwdev); + bool verRctCLSAGSimpleCarrot(const key &message, const clsagCarrot &sig, const ctkeyV & pubs, const key & C_offset); + zk_proof PRProof_Gen(const rct::key &difference); bool PRProof_Ver(const rct::key &C, const zk_proof &proof); diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index 630222c..ca2508a 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -207,6 +207,24 @@ namespace rct { END_SERIALIZE() }; + // CLSAG signature + struct clsagCarrot { + keyV sx; // x scalars(responses) + keyV sy; // y scalars(responses) + key c1; + + key I; // signing key image + key D; // commitment key image + + BEGIN_SERIALIZE_OBJECT() + FIELD(sx) + FIELD(sy) + FIELD(c1) + // FIELD(I) - not serialized, it can be reconstructed + FIELD(D) + END_SERIALIZE() + }; + //contains the data for an Borromean sig // also contains the "Ci" values such that // \sum Ci = C diff --git a/tests/unit_tests/ringct.cpp b/tests/unit_tests/ringct.cpp index cff59de..01daf51 100644 --- a/tests/unit_tests/ringct.cpp +++ b/tests/unit_tests/ringct.cpp @@ -37,6 +37,7 @@ #include "ringct/rctTypes.h" #include "ringct/rctSigs.h" #include "ringct/rctOps.h" +#include "crypto/generators.h" #include "device/device.hpp" #include "string_tools.h" @@ -299,6 +300,47 @@ TEST(ringct, CLSAG) ASSERT_TRUE(rct::verRctCLSAGSimple(message,clsag,pubs,Cout)); } +TEST(ringct, CLSAG_CARROT) +{ + const size_t N = 16; + const size_t idx = 5; + ctkeyV pubs; + key x, y, t, t2, u; + const key message = identity(); + ctkey backup; + clsagCarrot clsag; + + for (size_t i = 0; i < N; ++i) + { + key sk; + ctkey tmp; + + skpkGen(sk, tmp.dest); + skpkGen(sk, tmp.mask); + + pubs.push_back(tmp); + } + + // Set P[idx] + x = skGen(); + y = skGen(); + addKeys2(pubs[idx].dest, x, y, rct::pk2rct(crypto::get_T())); + + // Set C[idx] + t = skGen(); + u = skGen(); + addKeys2(pubs[idx].mask,t,u,H); + + // Set commitment offset + key Cout; + t2 = skGen(); + addKeys2(Cout,t2,u,H); + + // generate the signature + clsag = rct::proveRctCLSAGSSimpleCarrot(message, pubs, x, y, t, t2, Cout, idx, hw::get_device("default")); + ASSERT_TRUE(rct::verRctCLSAGSimpleCarrot(message, clsag, pubs, Cout)); +} + TEST(ringct, range_proofs) { //Ring CT Stuff diff --git a/tests/unit_tests/tx_construction_helpers.cpp b/tests/unit_tests/tx_construction_helpers.cpp index e6db207..8c08f94 100644 --- a/tests/unit_tests/tx_construction_helpers.cpp +++ b/tests/unit_tests/tx_construction_helpers.cpp @@ -75,7 +75,7 @@ bool construct_miner_tx_fake_reward_1out(const size_t height, height, coinbase_enotes); - tx = carrot::store_carrot_to_coinbase_transaction_v1(coinbase_enotes, ""); + tx = carrot::store_carrot_to_coinbase_transaction_v1(coinbase_enotes, "", cryptonote::transaction_type::MINER); } else // !is_carrot {