ringct: the commitment mask is now deterministic

saves space in the tx and is safe

Found by knaccc
This commit is contained in:
moneromooo-monero 2019-01-08 16:05:18 +00:00
parent 6ba3a11637
commit 0b18fa54c4
No known key found for this signature in database
GPG key ID: 686F07454D6CEFC3
7 changed files with 57 additions and 72 deletions

View file

@ -249,15 +249,18 @@ namespace boost
template <class Archive>
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
}

View file

@ -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];
}
void ecdhEncode(ecdhTuple & unmasked, const key & sharedSec, bool short_amount) {
key sharedSec1 = hash_to_scalar(sharedSec);
key sharedSec2 = hash_to_scalar(sharedSec1);
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 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);
}
}
}

View file

@ -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 */

View file

@ -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<uint64_t> &amounts, const std::vector<key> &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<uint64_t> &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<key> 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<uint64_t> 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<key> 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

View file

@ -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<W>::is_saving())
memset(ecdhInfo[i].amount.bytes, 0, sizeof(ecdhInfo[i].amount.bytes));
crypto::hash8 &amount = (crypto::hash8&)ecdhInfo[i].amount;

View file

@ -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);
}

View file

@ -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;