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:
parent
7b88208722
commit
0349347e6d
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user