Merge pull request #6288

0349347e ringdb: use a different iv for key and data in rings table (moneromooo-monero)
7b882087 simplewallet: reword mixin in user message in terms of ring size (moneromooo-monero)
f507a43a wallet2: do not remove known rings when a tx fails (moneromooo-monero)
This commit is contained in:
Alexander Blair 2020-03-27 12:23:58 -07:00
commit 5ba6eef272
No known key found for this signature in database
GPG key ID: C64552D877C32479
3 changed files with 38 additions and 20 deletions

View file

@ -1691,7 +1691,7 @@ bool simple_wallet::print_ring(const std::vector<std::string> &args)
rings.push_back({key_image, ring}); rings.push_back({key_image, ring});
else if (!m_wallet->get_rings(txid, rings)) else if (!m_wallet->get_rings(txid, rings))
{ {
fail_msg_writer() << tr("Key image either not spent, or spent with mixin 0"); fail_msg_writer() << tr("Key image either not spent, or spent with ring size 1");
return true; return true;
} }

View file

@ -39,6 +39,8 @@
#undef MONERO_DEFAULT_LOG_CATEGORY #undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "wallet.ringdb" #define MONERO_DEFAULT_LOG_CATEGORY "wallet.ringdb"
#define V1TAG ((uint64_t)798237759845202)
static const char zerokey[8] = {0}; static const char zerokey[8] = {0};
static const MDB_val zerokeyval = { sizeof(zerokey), (void *)zerokey }; static const MDB_val zerokeyval = { sizeof(zerokey), (void *)zerokey };
@ -63,15 +65,16 @@ static int compare_uint64(const MDB_val *a, const MDB_val *b)
return va < vb ? -1 : va > vb; return va < vb ? -1 : va > vb;
} }
static std::string compress_ring(const std::vector<uint64_t> &ring) static std::string compress_ring(const std::vector<uint64_t> &ring, uint64_t tag)
{ {
std::string s; std::string s;
s += tools::get_varint_data(tag);
for (uint64_t out: ring) for (uint64_t out: ring)
s += tools::get_varint_data(out); s += tools::get_varint_data(out);
return s; return s;
} }
static std::vector<uint64_t> decompress_ring(const std::string &s) static std::vector<uint64_t> decompress_ring(const std::string &s, uint64_t tag)
{ {
std::vector<uint64_t> ring; std::vector<uint64_t> ring;
int read = 0; int read = 0;
@ -81,6 +84,13 @@ static std::vector<uint64_t> decompress_ring(const std::string &s)
std::string tmp(i, s.cend()); std::string tmp(i, s.cend());
read = tools::read_varint(tmp.begin(), tmp.end(), out); read = tools::read_varint(tmp.begin(), tmp.end(), out);
THROW_WALLET_EXCEPTION_IF(read <= 0 || read > 256, tools::error::wallet_internal_error, "Internal error decompressing ring"); THROW_WALLET_EXCEPTION_IF(read <= 0 || read > 256, tools::error::wallet_internal_error, "Internal error decompressing ring");
if (tag)
{
if (tag != out)
return {};
tag = 0;
continue;
}
ring.push_back(out); ring.push_back(out);
} }
return ring; return ring;
@ -93,25 +103,27 @@ std::string get_rings_filename(boost::filesystem::path filename)
return filename.string(); return filename.string();
} }
static crypto::chacha_iv make_iv(const crypto::key_image &key_image, const crypto::chacha_key &key) static crypto::chacha_iv make_iv(const crypto::key_image &key_image, const crypto::chacha_key &key, uint8_t field)
{ {
static const char salt[] = "ringdsb"; static const char salt[] = "ringdsb";
uint8_t buffer[sizeof(key_image) + sizeof(key) + sizeof(salt)]; uint8_t buffer[sizeof(key_image) + sizeof(key) + sizeof(salt) + sizeof(field)];
memcpy(buffer, &key_image, sizeof(key_image)); memcpy(buffer, &key_image, sizeof(key_image));
memcpy(buffer + sizeof(key_image), &key, sizeof(key)); memcpy(buffer + sizeof(key_image), &key, sizeof(key));
memcpy(buffer + sizeof(key_image) + sizeof(key), salt, sizeof(salt)); memcpy(buffer + sizeof(key_image) + sizeof(key), salt, sizeof(salt));
memcpy(buffer + sizeof(key_image) + sizeof(key) + sizeof(salt), &field, sizeof(field));
crypto::hash hash; crypto::hash hash;
crypto::cn_fast_hash(buffer, sizeof(buffer), hash.data); // if field is 0, backward compat mode: hash without the field
crypto::cn_fast_hash(buffer, sizeof(buffer) - !field, hash.data);
static_assert(sizeof(hash) >= CHACHA_IV_SIZE, "Incompatible hash and chacha IV sizes"); static_assert(sizeof(hash) >= CHACHA_IV_SIZE, "Incompatible hash and chacha IV sizes");
crypto::chacha_iv iv; crypto::chacha_iv iv;
memcpy(&iv, &hash, CHACHA_IV_SIZE); memcpy(&iv, &hash, CHACHA_IV_SIZE);
return iv; return iv;
} }
static std::string encrypt(const std::string &plaintext, const crypto::key_image &key_image, const crypto::chacha_key &key) static std::string encrypt(const std::string &plaintext, const crypto::key_image &key_image, const crypto::chacha_key &key, uint8_t field)
{ {
const crypto::chacha_iv iv = make_iv(key_image, key); const crypto::chacha_iv iv = make_iv(key_image, key, field);
std::string ciphertext; std::string ciphertext;
ciphertext.resize(plaintext.size() + sizeof(iv)); ciphertext.resize(plaintext.size() + sizeof(iv));
crypto::chacha20(plaintext.data(), plaintext.size(), key, iv, &ciphertext[sizeof(iv)]); crypto::chacha20(plaintext.data(), plaintext.size(), key, iv, &ciphertext[sizeof(iv)]);
@ -119,14 +131,14 @@ static std::string encrypt(const std::string &plaintext, const crypto::key_image
return ciphertext; return ciphertext;
} }
static std::string encrypt(const crypto::key_image &key_image, const crypto::chacha_key &key) static std::string encrypt(const crypto::key_image &key_image, const crypto::chacha_key &key, uint8_t field)
{ {
return encrypt(std::string((const char*)&key_image, sizeof(key_image)), key_image, key); return encrypt(std::string((const char*)&key_image, sizeof(key_image)), key_image, key, field);
} }
static std::string decrypt(const std::string &ciphertext, const crypto::key_image &key_image, const crypto::chacha_key &key) static std::string decrypt(const std::string &ciphertext, const crypto::key_image &key_image, const crypto::chacha_key &key, uint8_t field)
{ {
const crypto::chacha_iv iv = make_iv(key_image, key); const crypto::chacha_iv iv = make_iv(key_image, key, field);
std::string plaintext; std::string plaintext;
THROW_WALLET_EXCEPTION_IF(ciphertext.size() < sizeof(iv), tools::error::wallet_internal_error, "Bad ciphertext text"); THROW_WALLET_EXCEPTION_IF(ciphertext.size() < sizeof(iv), tools::error::wallet_internal_error, "Bad ciphertext text");
plaintext.resize(ciphertext.size() - sizeof(iv)); plaintext.resize(ciphertext.size() - sizeof(iv));
@ -137,11 +149,11 @@ static std::string decrypt(const std::string &ciphertext, const crypto::key_imag
static void store_relative_ring(MDB_txn *txn, MDB_dbi &dbi, const crypto::key_image &key_image, const std::vector<uint64_t> &relative_ring, const crypto::chacha_key &chacha_key) static void store_relative_ring(MDB_txn *txn, MDB_dbi &dbi, const crypto::key_image &key_image, const std::vector<uint64_t> &relative_ring, const crypto::chacha_key &chacha_key)
{ {
MDB_val key, data; MDB_val key, data;
std::string key_ciphertext = encrypt(key_image, chacha_key); std::string key_ciphertext = encrypt(key_image, chacha_key, 0);
key.mv_data = (void*)key_ciphertext.data(); key.mv_data = (void*)key_ciphertext.data();
key.mv_size = key_ciphertext.size(); key.mv_size = key_ciphertext.size();
std::string compressed_ring = compress_ring(relative_ring); std::string compressed_ring = compress_ring(relative_ring, V1TAG);
std::string data_ciphertext = encrypt(compressed_ring, key_image, chacha_key); std::string data_ciphertext = encrypt(compressed_ring, key_image, chacha_key, 1);
data.mv_size = data_ciphertext.size(); data.mv_size = data_ciphertext.size();
data.mv_data = (void*)data_ciphertext.c_str(); data.mv_data = (void*)data_ciphertext.c_str();
int dbr = mdb_put(txn, dbi, &key, &data, 0); int dbr = mdb_put(txn, dbi, &key, &data, 0);
@ -297,7 +309,7 @@ bool ringdb::remove_rings(const crypto::chacha_key &chacha_key, const std::vecto
for (const crypto::key_image &key_image: key_images) for (const crypto::key_image &key_image: key_images)
{ {
MDB_val key, data; MDB_val key, data;
std::string key_ciphertext = encrypt(key_image, chacha_key); std::string key_ciphertext = encrypt(key_image, chacha_key, 0);
key.mv_data = (void*)key_ciphertext.data(); key.mv_data = (void*)key_ciphertext.data();
key.mv_size = key_ciphertext.size(); key.mv_size = key_ciphertext.size();
@ -349,7 +361,7 @@ bool ringdb::get_ring(const crypto::chacha_key &chacha_key, const crypto::key_im
tx_active = true; tx_active = true;
MDB_val key, data; MDB_val key, data;
std::string key_ciphertext = encrypt(key_image, chacha_key); std::string key_ciphertext = encrypt(key_image, chacha_key, 0);
key.mv_data = (void*)key_ciphertext.data(); key.mv_data = (void*)key_ciphertext.data();
key.mv_size = key_ciphertext.size(); key.mv_size = key_ciphertext.size();
dbr = mdb_get(txn, dbi_rings, &key, &data); dbr = mdb_get(txn, dbi_rings, &key, &data);
@ -358,8 +370,15 @@ bool ringdb::get_ring(const crypto::chacha_key &chacha_key, const crypto::key_im
return false; return false;
THROW_WALLET_EXCEPTION_IF(data.mv_size <= 0, tools::error::wallet_internal_error, "Invalid ring data size"); THROW_WALLET_EXCEPTION_IF(data.mv_size <= 0, tools::error::wallet_internal_error, "Invalid ring data size");
std::string data_plaintext = decrypt(std::string((const char*)data.mv_data, data.mv_size), key_image, chacha_key); bool try_v0 = false;
outs = decompress_ring(data_plaintext); std::string data_plaintext = decrypt(std::string((const char*)data.mv_data, data.mv_size), key_image, chacha_key, 1);
try { outs = decompress_ring(data_plaintext, V1TAG); if (outs.empty()) try_v0 = true; }
catch(...) { try_v0 = true; }
if (try_v0)
{
data_plaintext = decrypt(std::string((const char*)data.mv_data, data.mv_size), key_image, chacha_key, 0);
outs = decompress_ring(data_plaintext, 0);
}
MDEBUG("Found ring for key image " << key_image << ":"); MDEBUG("Found ring for key image " << key_image << ":");
MDEBUG("Relative: " << boost::join(outs | boost::adaptors::transformed([](uint64_t out){return std::to_string(out);}), " ")); MDEBUG("Relative: " << boost::join(outs | boost::adaptors::transformed([](uint64_t out){return std::to_string(out);}), " "));
outs = cryptonote::relative_output_offsets_to_absolute(outs); outs = cryptonote::relative_output_offsets_to_absolute(outs);

View file

@ -2934,7 +2934,6 @@ void wallet2::update_pool_state(std::vector<std::tuple<cryptonote::transaction,
pit->second.m_state = wallet2::unconfirmed_transfer_details::failed; pit->second.m_state = wallet2::unconfirmed_transfer_details::failed;
// the inputs aren't spent anymore, since the tx failed // the inputs aren't spent anymore, since the tx failed
remove_rings(pit->second.m_tx);
for (size_t vini = 0; vini < pit->second.m_tx.vin.size(); ++vini) for (size_t vini = 0; vini < pit->second.m_tx.vin.size(); ++vini)
{ {
if (pit->second.m_tx.vin[vini].type() == typeid(txin_to_key)) if (pit->second.m_tx.vin[vini].type() == typeid(txin_to_key))