mirror of
https://github.com/monero-project/monero.git
synced 2024-12-23 03:59:33 +00:00
ringct: the commitment mask is now deterministic
saves space in the tx and is safe Found by knaccc
This commit is contained in:
parent
99d946e619
commit
7d37598158
7 changed files with 55 additions and 63 deletions
|
@ -249,15 +249,18 @@ namespace boost
|
||||||
template <class Archive>
|
template <class Archive>
|
||||||
inline void serialize(Archive &a, rct::ecdhTuple &x, const boost::serialization::version_type ver)
|
inline void serialize(Archive &a, rct::ecdhTuple &x, const boost::serialization::version_type ver)
|
||||||
{
|
{
|
||||||
a & x.mask;
|
|
||||||
if (ver < 1)
|
if (ver < 1)
|
||||||
{
|
{
|
||||||
|
a & x.mask;
|
||||||
a & x.amount;
|
a & x.amount;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
crypto::hash8 &amount = (crypto::hash8&)x.amount;
|
crypto::hash8 &amount = (crypto::hash8&)x.amount;
|
||||||
if (!Archive::is_saving::value)
|
if (!Archive::is_saving::value)
|
||||||
|
{
|
||||||
|
memset(&x.mask, 0, sizeof(x.mask));
|
||||||
memset(&x.amount, 0, sizeof(x.amount));
|
memset(&x.amount, 0, sizeof(x.amount));
|
||||||
|
}
|
||||||
a & amount;
|
a & amount;
|
||||||
// a & x.senderPk; // not serialized, as we do not use it in monero currently
|
// a & x.senderPk; // not serialized, as we do not use it in monero currently
|
||||||
}
|
}
|
||||||
|
|
|
@ -672,36 +672,56 @@ namespace rct {
|
||||||
// where C= aG + bH
|
// where C= aG + bH
|
||||||
static key ecdhHash(const key &k)
|
static key ecdhHash(const key &k)
|
||||||
{
|
{
|
||||||
char data[38];
|
char data[38];
|
||||||
rct::key hash;
|
rct::key hash;
|
||||||
memcpy(data, "amount", 6);
|
memcpy(data, "amount", 6);
|
||||||
memcpy(data + 6, &k, sizeof(k));
|
memcpy(data + 6, &k, sizeof(k));
|
||||||
cn_fast_hash(hash, data, sizeof(data));
|
cn_fast_hash(hash, data, sizeof(data));
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
static void xor8(key &v, const key &k)
|
static void xor8(key &v, const key &k)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 8; ++i)
|
for (int i = 0; i < 8; ++i)
|
||||||
v.bytes[i] ^= k.bytes[i];
|
v.bytes[i] ^= k.bytes[i];
|
||||||
}
|
}
|
||||||
void ecdhEncode(ecdhTuple & unmasked, const key & sharedSec, bool short_amount) {
|
key genCommitmentMask(const key &sk)
|
||||||
key sharedSec1 = hash_to_scalar(sharedSec);
|
{
|
||||||
key sharedSec2 = hash_to_scalar(sharedSec1);
|
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
|
//encode
|
||||||
sc_add(unmasked.mask.bytes, unmasked.mask.bytes, sharedSec1.bytes);
|
if (v2)
|
||||||
if (short_amount)
|
{
|
||||||
|
unmasked.mask = zero();
|
||||||
xor8(unmasked.amount, ecdhHash(sharedSec));
|
xor8(unmasked.amount, ecdhHash(sharedSec));
|
||||||
|
}
|
||||||
else
|
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);
|
sc_add(unmasked.amount.bytes, unmasked.amount.bytes, sharedSec2.bytes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
void ecdhDecode(ecdhTuple & masked, const key & sharedSec, bool short_amount) {
|
void ecdhDecode(ecdhTuple & masked, const key & sharedSec, bool v2) {
|
||||||
key sharedSec1 = hash_to_scalar(sharedSec);
|
|
||||||
key sharedSec2 = hash_to_scalar(sharedSec1);
|
|
||||||
//decode
|
//decode
|
||||||
sc_sub(masked.mask.bytes, masked.mask.bytes, sharedSec1.bytes);
|
if (v2)
|
||||||
if (short_amount)
|
{
|
||||||
|
masked.mask = genCommitmentMask(sharedSec);
|
||||||
xor8(masked.amount, ecdhHash(sharedSec));
|
xor8(masked.amount, ecdhHash(sharedSec));
|
||||||
|
}
|
||||||
else
|
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);
|
sc_sub(masked.amount.bytes, masked.amount.bytes, sharedSec2.bytes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -182,7 +182,8 @@ namespace rct {
|
||||||
|
|
||||||
//Elliptic Curve Diffie Helman: encodes and decodes the amount b and mask a
|
//Elliptic Curve Diffie Helman: encodes and decodes the amount b and mask a
|
||||||
// where C= aG + bH
|
// where C= aG + bH
|
||||||
void ecdhEncode(ecdhTuple & unmasked, const key & sharedSec, bool short_amount);
|
key genCommitmentMask(const key &sk);
|
||||||
void ecdhDecode(ecdhTuple & masked, const key & sharedSec, bool short_amount);
|
void ecdhEncode(ecdhTuple & unmasked, const key & sharedSec, bool v2);
|
||||||
|
void ecdhDecode(ecdhTuple & masked, const key & sharedSec, bool v2);
|
||||||
}
|
}
|
||||||
#endif /* RCTOPS_H */
|
#endif /* RCTOPS_H */
|
||||||
|
|
|
@ -79,9 +79,12 @@ namespace
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace rct {
|
namespace rct {
|
||||||
Bulletproof proveRangeBulletproof(keyV &C, keyV &masks, const std::vector<uint64_t> &amounts)
|
Bulletproof proveRangeBulletproof(keyV &C, keyV &masks, const std::vector<uint64_t> &amounts, epee::span<const key> sk)
|
||||||
{
|
{
|
||||||
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);
|
Bulletproof proof = bulletproof_PROVE(amounts, masks);
|
||||||
CHECK_AND_ASSERT_THROW_MES(proof.V.size() == amounts.size(), "V does not have the expected size");
|
CHECK_AND_ASSERT_THROW_MES(proof.V.size() == amounts.size(), "V does not have the expected size");
|
||||||
C = proof.V;
|
C = proof.V;
|
||||||
|
@ -803,7 +806,8 @@ namespace rct {
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, outamounts));
|
const epee::span<const key> keys{&amount_keys[0], amount_keys.size()};
|
||||||
|
rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, outamounts, keys));
|
||||||
#ifdef DBG
|
#ifdef DBG
|
||||||
CHECK_AND_ASSERT_THROW_MES(verBulletproof(rv.p.bulletproofs.back()), "verBulletproof failed on newly created proof");
|
CHECK_AND_ASSERT_THROW_MES(verBulletproof(rv.p.bulletproofs.back()), "verBulletproof failed on newly created proof");
|
||||||
#endif
|
#endif
|
||||||
|
@ -831,7 +835,8 @@ namespace rct {
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, batch_amounts));
|
const epee::span<const key> keys{&amount_keys[amounts_proved], batch_size};
|
||||||
|
rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, batch_amounts, keys));
|
||||||
#ifdef DBG
|
#ifdef DBG
|
||||||
CHECK_AND_ASSERT_THROW_MES(verBulletproof(rv.p.bulletproofs.back()), "verBulletproof failed on newly created proof");
|
CHECK_AND_ASSERT_THROW_MES(verBulletproof(rv.p.bulletproofs.back()), "verBulletproof failed on newly created proof");
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -128,7 +128,7 @@ namespace rct {
|
||||||
key senderPk;
|
key senderPk;
|
||||||
|
|
||||||
BEGIN_SERIALIZE_OBJECT()
|
BEGIN_SERIALIZE_OBJECT()
|
||||||
FIELD(mask)
|
FIELD(mask) // not saved from v2 BPs
|
||||||
FIELD(amount)
|
FIELD(amount)
|
||||||
// FIELD(senderPk) // not serialized, as we do not use it in monero currently
|
// FIELD(senderPk) // not serialized, as we do not use it in monero currently
|
||||||
END_SERIALIZE()
|
END_SERIALIZE()
|
||||||
|
@ -286,7 +286,6 @@ namespace rct {
|
||||||
if (type == RCTTypeBulletproof2)
|
if (type == RCTTypeBulletproof2)
|
||||||
{
|
{
|
||||||
ar.begin_object();
|
ar.begin_object();
|
||||||
FIELD_N("mask", ecdhInfo[i].mask);
|
|
||||||
if (!typename Archive<W>::is_saving())
|
if (!typename Archive<W>::is_saving())
|
||||||
memset(ecdhInfo[i].amount.bytes, 0, sizeof(ecdhInfo[i].amount.bytes));
|
memset(ecdhInfo[i].amount.bytes, 0, sizeof(ecdhInfo[i].amount.bytes));
|
||||||
crypto::hash8 &amount = (crypto::hash8&)ecdhInfo[i].amount;
|
crypto::hash8 &amount = (crypto::hash8&)ecdhInfo[i].amount;
|
||||||
|
|
|
@ -130,18 +130,3 @@ TEST(device, ecdh32)
|
||||||
ASSERT_EQ(tuple2.senderPk, tuple.senderPk);
|
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);
|
|
||||||
}
|
|
||||||
|
|
|
@ -831,27 +831,6 @@ TEST(ringct, HPow2)
|
||||||
|
|
||||||
static const xmr_amount test_amounts[]={0, 1, 2, 3, 4, 5, 10000, 10000000000000000000ull, 10203040506070809000ull, 123456789123456789};
|
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)
|
TEST(ringct, d2h)
|
||||||
{
|
{
|
||||||
key k, P1;
|
key k, P1;
|
||||||
|
|
Loading…
Reference in a new issue