From f507a43a239358b1645f383f6974f0862de2999b Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 9 Jan 2020 12:48:57 +0000 Subject: [PATCH 1/3] wallet2: do not remove known rings when a tx fails Even if it fails, the ring composition is known to a potential adversary, and so we should reuse the same ring next time --- src/wallet/wallet2.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index ea556ec45..58a531557 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -2907,7 +2907,6 @@ void wallet2::update_pool_state(std::vectorsecond.m_state = wallet2::unconfirmed_transfer_details::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) { if (pit->second.m_tx.vin[vini].type() == typeid(txin_to_key)) From 7b88208722910a9c7f83676afa867c7896d1449d Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 9 Jan 2020 12:54:43 +0000 Subject: [PATCH 2/3] simplewallet: reword mixin in user message in terms of ring size --- src/simplewallet/simplewallet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index a35ee40ae..30cf104ac 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -1690,7 +1690,7 @@ bool simple_wallet::print_ring(const std::vector &args) rings.push_back({key_image, ring}); 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; } From 0349347e6d2805e632d5972a50ae418296e9c479 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 9 Jan 2020 17:29:36 +0000 Subject: [PATCH 3/3] ringdb: use a different iv for key and data in rings table This is technically a record encrypted in two pieces, so the iv needs to be different. Some backward compatibility is added to read data written by existing code, but new data is written with the new code. --- src/wallet/ringdb.cpp | 55 +++++++++++++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 18 deletions(-) diff --git a/src/wallet/ringdb.cpp b/src/wallet/ringdb.cpp index b7efdd75c..5e88ea788 100644 --- a/src/wallet/ringdb.cpp +++ b/src/wallet/ringdb.cpp @@ -39,6 +39,8 @@ #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "wallet.ringdb" +#define V1TAG ((uint64_t)798237759845202) + static const char zerokey[8] = {0}; 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; } -static std::string compress_ring(const std::vector &ring) +static std::string compress_ring(const std::vector &ring, uint64_t tag) { std::string s; + s += tools::get_varint_data(tag); for (uint64_t out: ring) s += tools::get_varint_data(out); return s; } -static std::vector decompress_ring(const std::string &s) +static std::vector decompress_ring(const std::string &s, uint64_t tag) { std::vector ring; int read = 0; @@ -81,6 +84,13 @@ static std::vector decompress_ring(const std::string &s) std::string tmp(i, s.cend()); 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"); + if (tag) + { + if (tag != out) + return {}; + tag = 0; + continue; + } ring.push_back(out); } return ring; @@ -93,25 +103,27 @@ std::string get_rings_filename(boost::filesystem::path filename) 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"; - 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 + sizeof(key_image), &key, sizeof(key)); 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::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"); crypto::chacha_iv iv; memcpy(&iv, &hash, CHACHA_IV_SIZE); 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; ciphertext.resize(plaintext.size() + 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; } -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; THROW_WALLET_EXCEPTION_IF(ciphertext.size() < sizeof(iv), tools::error::wallet_internal_error, "Bad ciphertext text"); 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 &relative_ring, const crypto::chacha_key &chacha_key) { 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_size = key_ciphertext.size(); - std::string compressed_ring = compress_ring(relative_ring); - std::string data_ciphertext = encrypt(compressed_ring, key_image, chacha_key); + std::string compressed_ring = compress_ring(relative_ring, V1TAG); + std::string data_ciphertext = encrypt(compressed_ring, key_image, chacha_key, 1); data.mv_size = data_ciphertext.size(); data.mv_data = (void*)data_ciphertext.c_str(); 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) { 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_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; 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_size = key_ciphertext.size(); 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; 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); - outs = decompress_ring(data_plaintext); + bool try_v0 = false; + 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("Relative: " << boost::join(outs | boost::adaptors::transformed([](uint64_t out){return std::to_string(out);}), " ")); outs = cryptonote::relative_output_offsets_to_absolute(outs);