From 33a76e92f4b7acacb1c321b73bfffd7e075a46d0 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 8 Jan 2019 16:05:18 +0000 Subject: [PATCH] ringct: the commitment mask is now deterministic saves space in the tx and is safe Found by knaccc --- .../cryptonote_boost_serialization.h | 5 +- src/ringct/rctOps.cpp | 56 +++++++++++++------ src/ringct/rctOps.h | 5 +- src/ringct/rctSigs.cpp | 24 ++++---- src/ringct/rctTypes.h | 3 +- tests/unit_tests/device.cpp | 15 ----- tests/unit_tests/ringct.cpp | 21 ------- 7 files changed, 57 insertions(+), 72 deletions(-) diff --git a/src/cryptonote_basic/cryptonote_boost_serialization.h b/src/cryptonote_basic/cryptonote_boost_serialization.h index 6f26d8756..8a527b898 100644 --- a/src/cryptonote_basic/cryptonote_boost_serialization.h +++ b/src/cryptonote_basic/cryptonote_boost_serialization.h @@ -249,15 +249,18 @@ namespace boost template inline void serialize(Archive &a, rct::ecdhTuple &x, const boost::serialization::version_type ver) { - a & x.mask; if (ver < 1) { + a & x.mask; a & x.amount; return; } crypto::hash8 &amount = (crypto::hash8&)x.amount; if (!Archive::is_saving::value) + { + memset(&x.mask, 0, sizeof(x.mask)); memset(&x.amount, 0, sizeof(x.amount)); + } a & amount; // a & x.senderPk; // not serialized, as we do not use it in monero currently } diff --git a/src/ringct/rctOps.cpp b/src/ringct/rctOps.cpp index b4609caab..747b686cd 100644 --- a/src/ringct/rctOps.cpp +++ b/src/ringct/rctOps.cpp @@ -489,36 +489,56 @@ namespace rct { // where C= aG + bH static key ecdhHash(const key &k) { - char data[38]; - rct::key hash; - memcpy(data, "amount", 6); - memcpy(data + 6, &k, sizeof(k)); - cn_fast_hash(hash, data, sizeof(data)); - return hash; + char data[38]; + rct::key hash; + memcpy(data, "amount", 6); + memcpy(data + 6, &k, sizeof(k)); + cn_fast_hash(hash, data, sizeof(data)); + return hash; } static void xor8(key &v, const key &k) { - for (int i = 0; i < 8; ++i) - v.bytes[i] ^= k.bytes[i]; + for (int i = 0; i < 8; ++i) + v.bytes[i] ^= k.bytes[i]; + } + key genCommitmentMask(const key &sk) + { + char data[15 + sizeof(key)]; + memcpy(data, "commitment_mask", 15); + memcpy(data + 15, &sk, sizeof(sk)); + key scalar; + hash_to_scalar(scalar, data, sizeof(data)); + return scalar; } - void ecdhEncode(ecdhTuple & unmasked, const key & sharedSec, bool short_amount) { - key sharedSec1 = hash_to_scalar(sharedSec); - key sharedSec2 = hash_to_scalar(sharedSec1); + + void ecdhEncode(ecdhTuple & unmasked, const key & sharedSec, bool v2) { //encode - sc_add(unmasked.mask.bytes, unmasked.mask.bytes, sharedSec1.bytes); - if (short_amount) + if (v2) + { + unmasked.mask = zero(); xor8(unmasked.amount, ecdhHash(sharedSec)); + } else + { + key sharedSec1 = hash_to_scalar(sharedSec); + key sharedSec2 = hash_to_scalar(sharedSec1); + sc_add(unmasked.mask.bytes, unmasked.mask.bytes, sharedSec1.bytes); sc_add(unmasked.amount.bytes, unmasked.amount.bytes, sharedSec2.bytes); + } } - void ecdhDecode(ecdhTuple & masked, const key & sharedSec, bool short_amount) { - key sharedSec1 = hash_to_scalar(sharedSec); - key sharedSec2 = hash_to_scalar(sharedSec1); + void ecdhDecode(ecdhTuple & masked, const key & sharedSec, bool v2) { //decode - sc_sub(masked.mask.bytes, masked.mask.bytes, sharedSec1.bytes); - if (short_amount) + if (v2) + { + masked.mask = genCommitmentMask(sharedSec); xor8(masked.amount, ecdhHash(sharedSec)); + } else + { + key sharedSec1 = hash_to_scalar(sharedSec); + key sharedSec2 = hash_to_scalar(sharedSec1); + sc_sub(masked.mask.bytes, masked.mask.bytes, sharedSec1.bytes); sc_sub(masked.amount.bytes, masked.amount.bytes, sharedSec2.bytes); + } } } diff --git a/src/ringct/rctOps.h b/src/ringct/rctOps.h index 01cdd6fd7..dd6d87593 100644 --- a/src/ringct/rctOps.h +++ b/src/ringct/rctOps.h @@ -182,7 +182,8 @@ namespace rct { //Elliptic Curve Diffie Helman: encodes and decodes the amount b and mask a // where C= aG + bH - void ecdhEncode(ecdhTuple & unmasked, const key & sharedSec, bool short_amount); - void ecdhDecode(ecdhTuple & masked, const key & sharedSec, bool short_amount); + key genCommitmentMask(const key &sk); + void ecdhEncode(ecdhTuple & unmasked, const key & sharedSec, bool v2); + void ecdhDecode(ecdhTuple & masked, const key & sharedSec, bool v2); } #endif /* RCTOPS_H */ diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index 28d523e4f..a9f6d6a53 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -45,18 +45,12 @@ using namespace std; #define CHECK_AND_ASSERT_MES_L1(expr, ret, message) {if(!(expr)) {MCERROR("verify", message); return ret;}} namespace rct { - Bulletproof proveRangeBulletproof(key &C, key &mask, uint64_t amount) + Bulletproof proveRangeBulletproof(keyV &C, keyV &masks, const std::vector &amounts, const std::vector &sk) { - mask = rct::skGen(); - Bulletproof proof = bulletproof_PROVE(amount, mask); - CHECK_AND_ASSERT_THROW_MES(proof.V.size() == 1, "V has not exactly one element"); - C = proof.V[0]; - return proof; - } - - Bulletproof proveRangeBulletproof(keyV &C, keyV &masks, const std::vector &amounts) - { - masks = rct::skvGen(amounts.size()); + CHECK_AND_ASSERT_THROW_MES(amounts.size() == sk.size(), "Invalid amounts/sk sizes"); + masks.resize(amounts.size()); + for (size_t i = 0; i < masks.size(); ++i) + masks[i] = genCommitmentMask(sk[i]); Bulletproof proof = bulletproof_PROVE(amounts, masks); CHECK_AND_ASSERT_THROW_MES(proof.V.size() == amounts.size(), "V does not have the expected size"); C = proof.V; @@ -762,7 +756,8 @@ namespace rct { if (rct_config.range_proof_type == RangeProofPaddedBulletproof) { rct::keyV C, masks; - rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, outamounts)); + const std::vector keys(amount_keys.begin(), amount_keys.end()); + rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, outamounts, keys)); #ifdef DBG CHECK_AND_ASSERT_THROW_MES(verBulletproof(rv.p.bulletproofs.back()), "verBulletproof failed on newly created proof"); #endif @@ -782,7 +777,10 @@ namespace rct { std::vector batch_amounts(batch_size); for (i = 0; i < batch_size; ++i) batch_amounts[i] = outamounts[i + amounts_proved]; - rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, batch_amounts)); + std::vector keys(batch_size); + for (size_t j = 0; j < batch_size; ++j) + keys[j] = amount_keys[amounts_proved + j]; + rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, batch_amounts, keys)); #ifdef DBG CHECK_AND_ASSERT_THROW_MES(verBulletproof(rv.p.bulletproofs.back()), "verBulletproof failed on newly created proof"); #endif diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index de026226e..c6c21ad22 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -128,7 +128,7 @@ namespace rct { key senderPk; BEGIN_SERIALIZE_OBJECT() - FIELD(mask) + FIELD(mask) // not saved from v2 BPs FIELD(amount) // FIELD(senderPk) // not serialized, as we do not use it in monero currently END_SERIALIZE() @@ -285,7 +285,6 @@ namespace rct { if (type == RCTTypeBulletproof2) { ar.begin_object(); - FIELD_N("mask", ecdhInfo[i].mask); if (!typename Archive::is_saving()) memset(ecdhInfo[i].amount.bytes, 0, sizeof(ecdhInfo[i].amount.bytes)); crypto::hash8 &amount = (crypto::hash8&)ecdhInfo[i].amount; diff --git a/tests/unit_tests/device.cpp b/tests/unit_tests/device.cpp index 3ae748145..064a7028e 100644 --- a/tests/unit_tests/device.cpp +++ b/tests/unit_tests/device.cpp @@ -130,18 +130,3 @@ TEST(device, ecdh32) ASSERT_EQ(tuple2.senderPk, tuple.senderPk); } -TEST(device, ecdh8) -{ - hw::core::device_default dev; - rct::ecdhTuple tuple, tuple2; - rct::key key = rct::skGen(); - tuple.mask = rct::skGen(); - tuple.amount = rct::skGen(); - tuple.senderPk = rct::pkGen(); - tuple2 = tuple; - dev.ecdhEncode(tuple, key, true); - dev.ecdhDecode(tuple, key, true); - ASSERT_EQ(tuple2.mask, tuple.mask); - ASSERT_EQ(tuple2.amount, tuple.amount); - ASSERT_EQ(tuple2.senderPk, tuple.senderPk); -} diff --git a/tests/unit_tests/ringct.cpp b/tests/unit_tests/ringct.cpp index 6aa530743..cd514798b 100644 --- a/tests/unit_tests/ringct.cpp +++ b/tests/unit_tests/ringct.cpp @@ -831,27 +831,6 @@ TEST(ringct, HPow2) static const xmr_amount test_amounts[]={0, 1, 2, 3, 4, 5, 10000, 10000000000000000000ull, 10203040506070809000ull, 123456789123456789}; -TEST(ringct, ecdh_roundtrip) -{ - key k; - ecdhTuple t0, t1; - - for (auto amount: test_amounts) { - skGen(k); - - t0.mask = skGen(); - t0.amount = d2h(amount); - - t1 = t0; - ecdhEncode(t1, k, true); - ecdhDecode(t1, k, true); - ASSERT_TRUE(t0.mask == t1.mask); - ASSERT_TRUE(equalKeys(t0.mask, t1.mask)); - ASSERT_TRUE(t0.amount == t1.amount); - ASSERT_TRUE(equalKeys(t0.amount, t1.amount)); - } -} - TEST(ringct, d2h) { key k, P1;