ringct: encode 8 byte amount, saving 24 bytes per output

Found by knaccc
This commit is contained in:
moneromooo-monero 2019-01-06 19:49:52 +00:00
parent 77f8f45459
commit 6ba3a11637
No known key found for this signature in database
GPG key ID: 686F07454D6CEFC3
14 changed files with 93 additions and 34 deletions

View file

@ -45,6 +45,8 @@
#include "ringct/rctTypes.h" #include "ringct/rctTypes.h"
#include "ringct/rctOps.h" #include "ringct/rctOps.h"
BOOST_CLASS_VERSION(rct::ecdhTuple, 1)
//namespace cryptonote { //namespace cryptonote {
namespace boost namespace boost
{ {
@ -248,7 +250,15 @@ namespace boost
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; a & x.mask;
a & x.amount; if (ver < 1)
{
a & x.amount;
return;
}
crypto::hash8 &amount = (crypto::hash8&)x.amount;
if (!Archive::is_saving::value)
memset(&x.amount, 0, sizeof(x.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
} }

View file

@ -188,8 +188,8 @@ namespace hw {
return encrypt_payment_id(payment_id, public_key, secret_key); return encrypt_payment_id(payment_id, public_key, secret_key);
} }
virtual bool ecdhEncode(rct::ecdhTuple & unmasked, const rct::key & sharedSec) = 0; virtual bool ecdhEncode(rct::ecdhTuple & unmasked, const rct::key & sharedSec, bool short_amount) = 0;
virtual bool ecdhDecode(rct::ecdhTuple & masked, const rct::key & sharedSec) = 0; virtual bool ecdhDecode(rct::ecdhTuple & masked, const rct::key & sharedSec, bool short_amount) = 0;
virtual bool add_output_key_mapping(const crypto::public_key &Aout, const crypto::public_key &Bout, const bool is_subaddress, const size_t real_output_index, virtual bool add_output_key_mapping(const crypto::public_key &Aout, const crypto::public_key &Bout, const bool is_subaddress, const size_t real_output_index,
const rct::key &amount_key, const crypto::public_key &out_eph_public_key) = 0; const rct::key &amount_key, const crypto::public_key &out_eph_public_key) = 0;

View file

@ -302,13 +302,13 @@ namespace hw {
return true; return true;
} }
bool device_default::ecdhEncode(rct::ecdhTuple & unmasked, const rct::key & sharedSec) { bool device_default::ecdhEncode(rct::ecdhTuple & unmasked, const rct::key & sharedSec, bool short_amount) {
rct::ecdhEncode(unmasked, sharedSec); rct::ecdhEncode(unmasked, sharedSec, short_amount);
return true; return true;
} }
bool device_default::ecdhDecode(rct::ecdhTuple & masked, const rct::key & sharedSec) { bool device_default::ecdhDecode(rct::ecdhTuple & masked, const rct::key & sharedSec, bool short_amount) {
rct::ecdhDecode(masked, sharedSec); rct::ecdhDecode(masked, sharedSec, short_amount);
return true; return true;
} }

View file

@ -111,8 +111,8 @@ namespace hw {
bool encrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key) override; bool encrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key) override;
bool ecdhEncode(rct::ecdhTuple & unmasked, const rct::key & sharedSec) override; bool ecdhEncode(rct::ecdhTuple & unmasked, const rct::key & sharedSec, bool short_amount) override;
bool ecdhDecode(rct::ecdhTuple & masked, const rct::key & sharedSec) override; bool ecdhDecode(rct::ecdhTuple & masked, const rct::key & sharedSec, bool short_amount) override;
bool add_output_key_mapping(const crypto::public_key &Aout, const crypto::public_key &Bout, const bool is_subaddress, const size_t real_output_index, bool add_output_key_mapping(const crypto::public_key &Aout, const crypto::public_key &Bout, const bool is_subaddress, const size_t real_output_index,
const rct::key &amount_key, const crypto::public_key &out_eph_public_key) override; const rct::key &amount_key, const crypto::public_key &out_eph_public_key) override;

View file

@ -1142,13 +1142,13 @@ namespace hw {
return true; return true;
} }
bool device_ledger::ecdhEncode(rct::ecdhTuple & unmasked, const rct::key & AKout) { bool device_ledger::ecdhEncode(rct::ecdhTuple & unmasked, const rct::key & AKout, bool short_amount) {
AUTO_LOCK_CMD(); AUTO_LOCK_CMD();
#ifdef DEBUG_HWDEVICE #ifdef DEBUG_HWDEVICE
const rct::key AKout_x = hw::ledger::decrypt(AKout); const rct::key AKout_x = hw::ledger::decrypt(AKout);
rct::ecdhTuple unmasked_x = unmasked; rct::ecdhTuple unmasked_x = unmasked;
this->controle_device->ecdhEncode(unmasked_x, AKout_x); this->controle_device->ecdhEncode(unmasked_x, AKout_x, short_amount);
#endif #endif
int offset = set_command_header_noopt(INS_BLIND); int offset = set_command_header_noopt(INS_BLIND);
@ -1179,13 +1179,13 @@ namespace hw {
return true; return true;
} }
bool device_ledger::ecdhDecode(rct::ecdhTuple & masked, const rct::key & AKout) { bool device_ledger::ecdhDecode(rct::ecdhTuple & masked, const rct::key & AKout, bool short_amount) {
AUTO_LOCK_CMD(); AUTO_LOCK_CMD();
#ifdef DEBUG_HWDEVICE #ifdef DEBUG_HWDEVICE
const rct::key AKout_x = hw::ledger::decrypt(AKout); const rct::key AKout_x = hw::ledger::decrypt(AKout);
rct::ecdhTuple masked_x = masked; rct::ecdhTuple masked_x = masked;
this->controle_device->ecdhDecode(masked_x, AKout_x); this->controle_device->ecdhDecode(masked_x, AKout_x, short_amount);
#endif #endif
int offset = set_command_header_noopt(INS_UNBLIND); int offset = set_command_header_noopt(INS_UNBLIND);

View file

@ -190,8 +190,8 @@ namespace hw {
bool encrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key) override; bool encrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key) override;
bool ecdhEncode(rct::ecdhTuple & unmasked, const rct::key & sharedSec) override; bool ecdhEncode(rct::ecdhTuple & unmasked, const rct::key & sharedSec, bool short_format) override;
bool ecdhDecode(rct::ecdhTuple & masked, const rct::key & sharedSec) override; bool ecdhDecode(rct::ecdhTuple & masked, const rct::key & sharedSec, bool short_format) override;
bool add_output_key_mapping(const crypto::public_key &Aout, const crypto::public_key &Bout, const bool is_subaddress, const size_t real_output_index, bool add_output_key_mapping(const crypto::public_key &Aout, const crypto::public_key &Bout, const bool is_subaddress, const size_t real_output_index,
const rct::key &amount_key, const crypto::public_key &out_eph_public_key) override; const rct::key &amount_key, const crypto::public_key &out_eph_public_key) override;

View file

@ -487,18 +487,38 @@ 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) { 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;
}
static void xor8(key &v, const key &k)
{
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 sharedSec1 = hash_to_scalar(sharedSec);
key sharedSec2 = hash_to_scalar(sharedSec1); key sharedSec2 = hash_to_scalar(sharedSec1);
//encode //encode
sc_add(unmasked.mask.bytes, unmasked.mask.bytes, sharedSec1.bytes); sc_add(unmasked.mask.bytes, unmasked.mask.bytes, sharedSec1.bytes);
sc_add(unmasked.amount.bytes, unmasked.amount.bytes, sharedSec2.bytes); if (short_amount)
xor8(unmasked.amount, ecdhHash(sharedSec));
else
sc_add(unmasked.amount.bytes, unmasked.amount.bytes, sharedSec2.bytes);
} }
void ecdhDecode(ecdhTuple & masked, const key & sharedSec) { void ecdhDecode(ecdhTuple & masked, const key & sharedSec, bool short_amount) {
key sharedSec1 = hash_to_scalar(sharedSec); key sharedSec1 = hash_to_scalar(sharedSec);
key sharedSec2 = hash_to_scalar(sharedSec1); key sharedSec2 = hash_to_scalar(sharedSec1);
//decode //decode
sc_sub(masked.mask.bytes, masked.mask.bytes, sharedSec1.bytes); sc_sub(masked.mask.bytes, masked.mask.bytes, sharedSec1.bytes);
sc_sub(masked.amount.bytes, masked.amount.bytes, sharedSec2.bytes); if (short_amount)
xor8(masked.amount, ecdhHash(sharedSec));
else
sc_sub(masked.amount.bytes, masked.amount.bytes, sharedSec2.bytes);
} }
} }

View file

@ -182,7 +182,7 @@ 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); void ecdhEncode(ecdhTuple & unmasked, const key & sharedSec, bool short_amount);
void ecdhDecode(ecdhTuple & masked, const key & sharedSec); void ecdhDecode(ecdhTuple & masked, const key & sharedSec, bool short_amount);
} }
#endif /* RCTOPS_H */ #endif /* RCTOPS_H */

View file

@ -682,7 +682,7 @@ namespace rct {
//mask amount and mask //mask amount and mask
rv.ecdhInfo[i].mask = copy(outSk[i].mask); rv.ecdhInfo[i].mask = copy(outSk[i].mask);
rv.ecdhInfo[i].amount = d2h(amounts[i]); rv.ecdhInfo[i].amount = d2h(amounts[i]);
hwdev.ecdhEncode(rv.ecdhInfo[i], amount_keys[i]); hwdev.ecdhEncode(rv.ecdhInfo[i], amount_keys[i], rv.type == RCTTypeBulletproof2);
} }
//set txn fee //set txn fee
@ -803,7 +803,7 @@ namespace rct {
//mask amount and mask //mask amount and mask
rv.ecdhInfo[i].mask = copy(outSk[i].mask); rv.ecdhInfo[i].mask = copy(outSk[i].mask);
rv.ecdhInfo[i].amount = d2h(outamounts[i]); rv.ecdhInfo[i].amount = d2h(outamounts[i]);
hwdev.ecdhEncode(rv.ecdhInfo[i], amount_keys[i]); hwdev.ecdhEncode(rv.ecdhInfo[i], amount_keys[i], rv.type == RCTTypeBulletproof2);
} }
//set txn fee //set txn fee
@ -1102,7 +1102,7 @@ namespace rct {
//mask amount and mask //mask amount and mask
ecdhTuple ecdh_info = rv.ecdhInfo[i]; ecdhTuple ecdh_info = rv.ecdhInfo[i];
hwdev.ecdhDecode(ecdh_info, sk); hwdev.ecdhDecode(ecdh_info, sk, rv.type == RCTTypeBulletproof2);
mask = ecdh_info.mask; mask = ecdh_info.mask;
key amount = ecdh_info.amount; key amount = ecdh_info.amount;
key C = rv.outPk[i].mask; key C = rv.outPk[i].mask;
@ -1132,7 +1132,7 @@ namespace rct {
//mask amount and mask //mask amount and mask
ecdhTuple ecdh_info = rv.ecdhInfo[i]; ecdhTuple ecdh_info = rv.ecdhInfo[i];
hwdev.ecdhDecode(ecdh_info, sk); hwdev.ecdhDecode(ecdh_info, sk, rv.type == RCTTypeBulletproof2);
mask = ecdh_info.mask; mask = ecdh_info.mask;
key amount = ecdh_info.amount; key amount = ecdh_info.amount;
key C = rv.outPk[i].mask; key C = rv.outPk[i].mask;

View file

@ -282,7 +282,20 @@ namespace rct {
return false; return false;
for (size_t i = 0; i < outputs; ++i) for (size_t i = 0; i < outputs; ++i)
{ {
FIELDS(ecdhInfo[i]) 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;
FIELD(amount);
ar.end_object();
}
else
{
FIELDS(ecdhInfo[i])
}
if (outputs - i > 1) if (outputs - i > 1)
ar.delimit_array(); ar.delimit_array();
} }

View file

@ -9637,7 +9637,7 @@ void wallet2::check_tx_key_helper(const crypto::hash &txid, const crypto::key_de
crypto::secret_key scalar1; crypto::secret_key scalar1;
hwdev.derivation_to_scalar(found_derivation, n, scalar1); hwdev.derivation_to_scalar(found_derivation, n, scalar1);
rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[n]; rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[n];
hwdev.ecdhDecode(ecdh_info, rct::sk2rct(scalar1)); hwdev.ecdhDecode(ecdh_info, rct::sk2rct(scalar1), tx.rct_signatures.type == rct::RCTTypeBulletproof2);
const rct::key C = tx.rct_signatures.outPk[n].mask; const rct::key C = tx.rct_signatures.outPk[n].mask;
rct::key Ctmp; rct::key Ctmp;
THROW_WALLET_EXCEPTION_IF(sc_check(ecdh_info.mask.bytes) != 0, error::wallet_internal_error, "Bad ECDH input mask"); THROW_WALLET_EXCEPTION_IF(sc_check(ecdh_info.mask.bytes) != 0, error::wallet_internal_error, "Bad ECDH input mask");
@ -10142,7 +10142,7 @@ bool wallet2::check_reserve_proof(const cryptonote::account_public_address &addr
crypto::secret_key shared_secret; crypto::secret_key shared_secret;
crypto::derivation_to_scalar(derivation, proof.index_in_tx, shared_secret); crypto::derivation_to_scalar(derivation, proof.index_in_tx, shared_secret);
rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[proof.index_in_tx]; rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[proof.index_in_tx];
rct::ecdhDecode(ecdh_info, rct::sk2rct(shared_secret)); rct::ecdhDecode(ecdh_info, rct::sk2rct(shared_secret), tx.rct_signatures.type == rct::RCTTypeBulletproof2);
amount = rct::h2d(ecdh_info.amount); amount = rct::h2d(ecdh_info.amount);
} }
total += amount; total += amount;

View file

@ -455,7 +455,7 @@ bool gen_multisig_tx_validation_base::generate_with(std::vector<test_event_entry
crypto::secret_key scalar1; crypto::secret_key scalar1;
crypto::derivation_to_scalar(derivation, n, scalar1); crypto::derivation_to_scalar(derivation, n, scalar1);
rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[n]; rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[n];
rct::ecdhDecode(ecdh_info, rct::sk2rct(scalar1)); rct::ecdhDecode(ecdh_info, rct::sk2rct(scalar1), tx.rct_signatures.type == rct::RCTTypeBulletproof2);
rct::key C = tx.rct_signatures.outPk[n].mask; rct::key C = tx.rct_signatures.outPk[n].mask;
rct::addKeys2(Ctmp, ecdh_info.mask, ecdh_info.amount, rct::H); rct::addKeys2(Ctmp, ecdh_info.mask, ecdh_info.amount, rct::H);
CHECK_AND_ASSERT_MES(rct::equalKeys(C, Ctmp), false, "Failed to decode amount"); CHECK_AND_ASSERT_MES(rct::equalKeys(C, Ctmp), false, "Failed to decode amount");

View file

@ -114,7 +114,7 @@ TEST(device, ops)
ASSERT_EQ(ki0, ki1); ASSERT_EQ(ki0, ki1);
} }
TEST(device, ecdh) TEST(device, ecdh32)
{ {
hw::core::device_default dev; hw::core::device_default dev;
rct::ecdhTuple tuple, tuple2; rct::ecdhTuple tuple, tuple2;
@ -123,8 +123,24 @@ TEST(device, ecdh)
tuple.amount = rct::skGen(); tuple.amount = rct::skGen();
tuple.senderPk = rct::pkGen(); tuple.senderPk = rct::pkGen();
tuple2 = tuple; tuple2 = tuple;
dev.ecdhEncode(tuple, key); dev.ecdhEncode(tuple, key, false);
dev.ecdhDecode(tuple, key); dev.ecdhDecode(tuple, key, false);
ASSERT_EQ(tuple2.mask, tuple.mask);
ASSERT_EQ(tuple2.amount, tuple.amount);
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.mask, tuple.mask);
ASSERT_EQ(tuple2.amount, tuple.amount); ASSERT_EQ(tuple2.amount, tuple.amount);
ASSERT_EQ(tuple2.senderPk, tuple.senderPk); ASSERT_EQ(tuple2.senderPk, tuple.senderPk);

View file

@ -843,8 +843,8 @@ TEST(ringct, ecdh_roundtrip)
t0.amount = d2h(amount); t0.amount = d2h(amount);
t1 = t0; t1 = t0;
ecdhEncode(t1, k); ecdhEncode(t1, k, true);
ecdhDecode(t1, k); ecdhDecode(t1, k, true);
ASSERT_TRUE(t0.mask == t1.mask); ASSERT_TRUE(t0.mask == t1.mask);
ASSERT_TRUE(equalKeys(t0.mask, t1.mask)); ASSERT_TRUE(equalKeys(t0.mask, t1.mask));
ASSERT_TRUE(t0.amount == t1.amount); ASSERT_TRUE(t0.amount == t1.amount);