@@ -1003,6 +1003,24 @@ uint64_t num_priv_multisig_keys_post_setup(uint64_t threshold, uint64_t total)
1003
1003
return n_multisig_keys;
1004
1004
}
1005
1005
1006
+ /**
1007
+ * @brief Derives the chacha key to encrypt wallet cache files given the chacha key to encrypt the wallet keys files
1008
+ *
1009
+ * @param keys_data_key the chacha key that encrypts wallet keys files
1010
+ * @return crypto::chacha_key the chacha key that encrypts the wallet cache files
1011
+ */
1012
+ crypto::chacha_key derive_cache_key(const crypto::chacha_key& keys_data_key)
1013
+ {
1014
+ static_assert(HASH_SIZE == sizeof(crypto::chacha_key), "Mismatched sizes of hash and chacha key");
1015
+
1016
+ crypto::chacha_key cache_key;
1017
+ epee::mlocked<tools::scrubbed_arr<char, HASH_SIZE+1>> cache_key_data;
1018
+ memcpy(cache_key_data.data(), &keys_data_key, HASH_SIZE);
1019
+ cache_key_data[HASH_SIZE] = config::HASH_KEY_WALLET_CACHE;
1020
+ cn_fast_hash(cache_key_data.data(), HASH_SIZE+1, (crypto::hash&) cache_key);
1021
+
1022
+ return cache_key;
1023
+ }
1006
1024
//-----------------------------------------------------------------
1007
1025
} //namespace
1008
1026
@@ -4406,6 +4424,10 @@ boost::optional<wallet2::keys_file_data> wallet2::get_keys_file_data(const epee:
4406
4424
crypto::chacha_key key;
4407
4425
crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds);
4408
4426
4427
+ // We use m_cache_key as a deterministic test to see if given key corresponds to original password
4428
+ const crypto::chacha_key cache_key = derive_cache_key(key);
4429
+ THROW_WALLET_EXCEPTION_IF(cache_key != m_cache_key, error::invalid_password);
4430
+
4409
4431
if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
4410
4432
{
4411
4433
account.encrypt_viewkey(key);
@@ -4630,11 +4652,8 @@ void wallet2::setup_keys(const epee::wipeable_string &password)
4630
4652
m_account.decrypt_viewkey(key);
4631
4653
}
4632
4654
4633
- static_assert(HASH_SIZE == sizeof(crypto::chacha_key), "Mismatched sizes of hash and chacha key");
4634
- epee::mlocked<tools::scrubbed_arr<char, HASH_SIZE+1>> cache_key_data;
4635
- memcpy(cache_key_data.data(), &key, HASH_SIZE);
4636
- cache_key_data[HASH_SIZE] = config::HASH_KEY_WALLET_CACHE;
4637
- cn_fast_hash(cache_key_data.data(), HASH_SIZE+1, (crypto::hash&)m_cache_key);
4655
+ m_cache_key = derive_cache_key(key);
4656
+
4638
4657
get_ringdb_key();
4639
4658
}
4640
4659
//----------------------------------------------------------------------------------------------------
@@ -4643,9 +4662,8 @@ void wallet2::change_password(const std::string &filename, const epee::wipeable_
4643
4662
if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
4644
4663
decrypt_keys(original_password);
4645
4664
setup_keys(new_password);
4646
- rewrite(filename, new_password);
4647
4665
if (!filename.empty())
4648
- store();
4666
+ store_to(filename, new_password, true); // force rewrite keys file to possible new location
4649
4667
}
4650
4668
//----------------------------------------------------------------------------------------------------
4651
4669
/*!
@@ -5151,6 +5169,10 @@ void wallet2::encrypt_keys(const crypto::chacha_key &key)
5151
5169
5152
5170
void wallet2::decrypt_keys(const crypto::chacha_key &key)
5153
5171
{
5172
+ // We use m_cache_key as a deterministic test to see if given key corresponds to original password
5173
+ const crypto::chacha_key cache_key = derive_cache_key(key);
5174
+ THROW_WALLET_EXCEPTION_IF(cache_key != m_cache_key, error::invalid_password);
5175
+
5154
5176
m_account.encrypt_viewkey(key);
5155
5177
m_account.decrypt_keys(key);
5156
5178
}
@@ -6311,22 +6333,32 @@ void wallet2::store()
6311
6333
store_to("", epee::wipeable_string());
6312
6334
}
6313
6335
//----------------------------------------------------------------------------------------------------
6314
- void wallet2::store_to(const std::string &path, const epee::wipeable_string &password)
6336
+ void wallet2::store_to(const std::string &path, const epee::wipeable_string &password, bool force_rewrite_keys )
6315
6337
{
6316
6338
trim_hashchain();
6317
6339
6340
+ const bool had_old_wallet_files = !m_wallet_file.empty();
6341
+ THROW_WALLET_EXCEPTION_IF(!had_old_wallet_files && path.empty(), error::wallet_internal_error,
6342
+ "Cannot resave wallet to current file since wallet was not loaded from file to begin with");
6343
+
6318
6344
// if file is the same, we do:
6319
- // 1. save wallet to the *.new file
6320
- // 2. remove old wallet file
6321
- // 3. rename *.new to wallet_name
6345
+ // 1. overwrite the keys file iff force_rewrite_keys is specified
6346
+ // 2. save cache to the *.new file
6347
+ // 3. rename *.new to wallet_name, replacing old cache file
6348
+ // else we do:
6349
+ // 1. prepare new file names with "path" variable
6350
+ // 2. store new keys files
6351
+ // 3. remove old keys file
6352
+ // 4. store new cache file
6353
+ // 5. remove old cache file
6322
6354
6323
6355
// handle if we want just store wallet state to current files (ex store() replacement);
6324
- bool same_file = true ;
6325
- if (!path.empty())
6356
+ bool same_file = had_old_wallet_files && path.empty() ;
6357
+ if (had_old_wallet_files && !path.empty())
6326
6358
{
6327
- std::string canonical_path = boost::filesystem::canonical(m_wallet_file).string();
6328
- size_t pos = canonical_path.find (path);
6329
- same_file = pos != std::string::npos ;
6359
+ const std::string canonical_old_path = boost::filesystem::canonical(m_wallet_file).string();
6360
+ const std::string canonical_new_path = boost::filesystem::weakly_canonical (path).string( );
6361
+ same_file = canonical_old_path == canonical_new_path ;
6330
6362
}
6331
6363
6332
6364
@@ -6347,7 +6379,7 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas
6347
6379
}
6348
6380
6349
6381
// get wallet cache data
6350
- boost::optional<wallet2::cache_file_data> cache_file_data = get_cache_file_data(password );
6382
+ boost::optional<wallet2::cache_file_data> cache_file_data = get_cache_file_data();
6351
6383
THROW_WALLET_EXCEPTION_IF(cache_file_data == boost::none, error::wallet_internal_error, "failed to generate wallet cache data");
6352
6384
6353
6385
const std::string new_file = same_file ? m_wallet_file + ".new" : path;
@@ -6356,12 +6388,20 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas
6356
6388
const std::string old_address_file = m_wallet_file + ".address.txt";
6357
6389
const std::string old_mms_file = m_mms_file;
6358
6390
6359
- // save keys to the new file
6360
- // if we here, main wallet file is saved and we only need to save keys and address files
6361
- if (!same_file) {
6391
+ if (!same_file)
6392
+ {
6362
6393
prepare_file_names(path);
6394
+ }
6395
+
6396
+ if (!same_file || force_rewrite_keys)
6397
+ {
6363
6398
bool r = store_keys(m_keys_file, password, false);
6364
6399
THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file);
6400
+ }
6401
+
6402
+ if (!same_file && had_old_wallet_files)
6403
+ {
6404
+ bool r = false;
6365
6405
if (boost::filesystem::exists(old_address_file))
6366
6406
{
6367
6407
// save address to the new file
@@ -6374,11 +6414,6 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas
6374
6414
LOG_ERROR("error removing file: " << old_address_file);
6375
6415
}
6376
6416
}
6377
- // remove old wallet file
6378
- r = boost::filesystem::remove(old_file);
6379
- if (!r) {
6380
- LOG_ERROR("error removing file: " << old_file);
6381
- }
6382
6417
// remove old keys file
6383
6418
r = boost::filesystem::remove(old_keys_file);
6384
6419
if (!r) {
@@ -6392,8 +6427,9 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas
6392
6427
LOG_ERROR("error removing file: " << old_mms_file);
6393
6428
}
6394
6429
}
6395
- } else {
6396
- // save to new file
6430
+ }
6431
+
6432
+ // Save cache to new file. If storing to the same file, the temp path has the ".new" extension
6397
6433
#ifdef WIN32
6398
6434
// On Windows avoid using std::ofstream which does not work with UTF-8 filenames
6399
6435
// The price to pay is temporary higher memory consumption for string stream + binary archive
@@ -6413,10 +6449,20 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas
6413
6449
THROW_WALLET_EXCEPTION_IF(!success || !ostr.good(), error::file_save_error, new_file);
6414
6450
#endif
6415
6451
6452
+ if (same_file)
6453
+ {
6416
6454
// here we have "*.new" file, we need to rename it to be without ".new"
6417
6455
std::error_code e = tools::replace_file(new_file, m_wallet_file);
6418
6456
THROW_WALLET_EXCEPTION_IF(e, error::file_save_error, m_wallet_file, e);
6419
6457
}
6458
+ else if (!same_file && had_old_wallet_files)
6459
+ {
6460
+ // remove old wallet file
6461
+ bool r = boost::filesystem::remove(old_file);
6462
+ if (!r) {
6463
+ LOG_ERROR("error removing file: " << old_file);
6464
+ }
6465
+ }
6420
6466
6421
6467
if (m_message_store.get_active())
6422
6468
{
@@ -6426,7 +6472,7 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas
6426
6472
}
6427
6473
}
6428
6474
//----------------------------------------------------------------------------------------------------
6429
- boost::optional<wallet2::cache_file_data> wallet2::get_cache_file_data(const epee::wipeable_string &passwords )
6475
+ boost::optional<wallet2::cache_file_data> wallet2::get_cache_file_data()
6430
6476
{
6431
6477
trim_hashchain();
6432
6478
try
0 commit comments