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.
This commit is contained in:
moneromooo-monero 2020-01-09 17:29:36 +00:00
parent 7b88208722
commit 0349347e6d
No known key found for this signature in database
GPG key ID: 686F07454D6CEFC3

View file

@ -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<uint64_t> &ring)
static std::string compress_ring(const std::vector<uint64_t> &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<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;
int read = 0;
@ -81,6 +84,13 @@ static std::vector<uint64_t> 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<uint64_t> &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);