Skip to content

Commit 8259df0

Browse files
committed
Merge pull request monero-project#9860
dbc8402 wallet: fix wallet_keys_unlocker (jeffro256)
2 parents 7fe199f + dbc8402 commit 8259df0

File tree

4 files changed

+254
-62
lines changed

4 files changed

+254
-62
lines changed

src/simplewallet/simplewallet.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ using tools::fee_priority;
120120
LOCK_IDLE_SCOPE(); \
121121
boost::optional<tools::password_container> pwd_container = boost::none; \
122122
if (m_wallet->ask_password() && !(pwd_container = get_and_verify_password())) { code; } \
123-
tools::wallet_keys_unlocker unlocker(*m_wallet, pwd_container);
123+
tools::wallet_keys_unlocker unlocker(*m_wallet, pwd_container ? &pwd_container->password() : nullptr);
124124

125125
#define SCOPED_WALLET_UNLOCK() SCOPED_WALLET_UNLOCK_ON_BAD_PASSWORD(return true;)
126126

src/wallet/wallet2.cpp

Lines changed: 36 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
#include <algorithm>
3232
#include <numeric>
33+
#include <optional>
3334
#include <string>
3435
#include <tuple>
3536
#include <queue>
@@ -1098,50 +1099,46 @@ uint64_t gamma_picker::pick()
10981099
};
10991100

11001101
boost::mutex wallet_keys_unlocker::lockers_lock;
1101-
unsigned int wallet_keys_unlocker::lockers = 0;
1102-
wallet_keys_unlocker::wallet_keys_unlocker(wallet2 &w, const boost::optional<tools::password_container> &password):
1102+
std::map<wallet2*, std::size_t> wallet_keys_unlocker::lockers_per_wallet = {};
1103+
wallet_keys_unlocker::wallet_keys_unlocker(wallet2 &w, const epee::wipeable_string *password):
11031104
w(w),
1104-
locked(password != boost::none)
1105+
should_relock(true)
11051106
{
1106-
boost::lock_guard<boost::mutex> lock(lockers_lock);
1107-
if (lockers++ > 0)
1108-
locked = false;
1109-
if (!locked || w.is_unattended() || w.ask_password() != tools::wallet2::AskPasswordToDecrypt || w.watch_only() || w.is_background_syncing())
1107+
static_assert(!std::is_move_assignable_v<wallet2> && !std::is_move_constructible_v<wallet2>,
1108+
"Keeping track of wallet instances by pointer doesn't work if wallet2 can be moved");
1109+
1110+
if (password == nullptr || !w.is_key_encryption_enabled())
11101111
{
1111-
locked = false;
1112+
should_relock = false;
11121113
return;
11131114
}
1114-
const epee::wipeable_string pass = password->password();
1115-
w.generate_chacha_key_from_password(pass, key);
1116-
w.decrypt_keys(key);
1117-
}
11181115

1119-
wallet_keys_unlocker::wallet_keys_unlocker(wallet2 &w, bool locked, const epee::wipeable_string &password):
1120-
w(w),
1121-
locked(locked)
1122-
{
1116+
w.generate_chacha_key_from_password(*password, key);
1117+
11231118
boost::lock_guard<boost::mutex> lock(lockers_lock);
1124-
if (lockers++ > 0)
1125-
locked = false;
1126-
if (!locked)
1119+
if (lockers_per_wallet[std::addressof(w)]++ > 0)
11271120
return;
1128-
w.generate_chacha_key_from_password(password, key);
1121+
11291122
w.decrypt_keys(key);
11301123
}
11311124

11321125
wallet_keys_unlocker::~wallet_keys_unlocker()
11331126
{
1127+
if (!should_relock)
1128+
return;
1129+
11341130
try
11351131
{
11361132
boost::lock_guard<boost::mutex> lock(lockers_lock);
1137-
if (lockers == 0)
1133+
wallet2* w_ptr = std::addressof(w);
1134+
if (lockers_per_wallet[w_ptr] == 0)
11381135
{
11391136
MERROR("There are no lockers in wallet_keys_unlocker dtor");
11401137
return;
11411138
}
1142-
--lockers;
1143-
if (!locked)
1144-
return;
1139+
if (--lockers_per_wallet[w_ptr] > 0)
1140+
return; // there are other unlock-ers for this wallet, do nothing for now
1141+
lockers_per_wallet.erase(w_ptr);
11451142
w.encrypt_keys(key);
11461143
}
11471144
catch (...)
@@ -2206,7 +2203,7 @@ void wallet2::scan_output(const cryptonote::transaction &tx, bool miner_tx, cons
22062203
boost::optional<epee::wipeable_string> pwd = m_callback->on_get_password(pool ? "output found in pool" : "output received");
22072204
THROW_WALLET_EXCEPTION_IF(!pwd, error::password_needed, tr("Password is needed to compute key image for incoming monero"));
22082205
THROW_WALLET_EXCEPTION_IF(!verify_password(*pwd), error::password_needed, tr("Invalid password: password is needed to compute key image for incoming monero"));
2209-
m_encrypt_keys_after_refresh.reset(new wallet_keys_unlocker(*this, m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only, *pwd));
2206+
m_encrypt_keys_after_refresh.reset(new wallet_keys_unlocker(*this, &*pwd));
22102207
}
22112208
}
22122209

@@ -5438,6 +5435,14 @@ bool wallet2::verify_password(const std::string& keys_file_name, const epee::wip
54385435
return r;
54395436
}
54405437

5438+
bool wallet2::is_key_encryption_enabled() const
5439+
{
5440+
return !is_unattended()
5441+
&& ask_password() == tools::wallet2::AskPasswordToDecrypt
5442+
&& !watch_only()
5443+
&& !is_background_syncing();
5444+
}
5445+
54415446
void wallet2::encrypt_keys(const crypto::chacha_key &key)
54425447
{
54435448
m_account.encrypt_keys(key);
@@ -5838,27 +5843,6 @@ void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& p
58385843
}
58395844
}
58405845
//----------------------------------------------------------------------------------------------------
5841-
epee::misc_utils::auto_scope_leave_caller wallet2::decrypt_account_for_multisig(const epee::wipeable_string &password)
5842-
{
5843-
// decrypt account keys
5844-
// note: this conditional's clauses are old and undocumented
5845-
epee::misc_utils::auto_scope_leave_caller keys_reencryptor;
5846-
if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only)
5847-
{
5848-
crypto::chacha_key chacha_key;
5849-
crypto::generate_chacha_key(password.data(), password.size(), chacha_key, m_kdf_rounds);
5850-
this->decrypt_keys(chacha_key);
5851-
keys_reencryptor = epee::misc_utils::create_scope_leave_handler(
5852-
[this, chacha_key]()
5853-
{
5854-
this->encrypt_keys(chacha_key);
5855-
}
5856-
);
5857-
}
5858-
5859-
return keys_reencryptor;
5860-
}
5861-
//----------------------------------------------------------------------------------------------------
58625846
void wallet2::get_uninitialized_multisig_account(multisig::multisig_account &account_out) const
58635847
{
58645848
// create uninitialized multisig account
@@ -5904,7 +5888,7 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
59045888
const std::uint32_t threshold)
59055889
{
59065890
// decrypt account keys
5907-
epee::misc_utils::auto_scope_leave_caller keys_reencryptor{this->decrypt_account_for_multisig(password)};
5891+
std::optional<wallet_keys_unlocker> unlocker(std::in_place, *this, &password);
59085892

59095893
// create multisig account
59105894
multisig::multisig_account multisig_account;
@@ -5982,7 +5966,7 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
59825966
m_account_public_address.m_spend_public_key = multisig_account.get_multisig_pubkey();
59835967

59845968
// re-encrypt keys
5985-
keys_reencryptor = epee::misc_utils::auto_scope_leave_caller();
5969+
unlocker.reset();
59865970

59875971
if (!m_wallet_file.empty())
59885972
this->create_keys_file(m_wallet_file, false, password, boost::filesystem::exists(m_wallet_file + ".address.txt"));
@@ -6003,7 +5987,7 @@ std::string wallet2::exchange_multisig_keys(const epee::wipeable_string &passwor
60035987
CHECK_AND_ASSERT_THROW_MES(ms_status.multisig_is_active, "The wallet is not multisig");
60045988

60055989
// decrypt account keys
6006-
epee::misc_utils::auto_scope_leave_caller keys_reencryptor{this->decrypt_account_for_multisig(password)};
5990+
std::optional<wallet_keys_unlocker> unlocker(std::in_place, *this, &password);
60075991

60085992
// reconstruct multisig account
60095993
multisig::multisig_account multisig_account;
@@ -6055,7 +6039,7 @@ std::string wallet2::exchange_multisig_keys(const epee::wipeable_string &passwor
60556039
if (multisig_account.multisig_is_ready())
60566040
{
60576041
// keys are encrypted again
6058-
keys_reencryptor = epee::misc_utils::auto_scope_leave_caller();
6042+
unlocker.reset();
60596043

60606044
if (!m_wallet_file.empty())
60616045
{
@@ -6101,7 +6085,7 @@ std::string wallet2::get_multisig_key_exchange_booster(const epee::wipeable_stri
61016085
CHECK_AND_ASSERT_THROW_MES(kex_messages.size() > 0, "No key exchange messages passed in.");
61026086

61036087
// decrypt account keys
6104-
epee::misc_utils::auto_scope_leave_caller keys_reencryptor{this->decrypt_account_for_multisig(password)};
6088+
wallet_keys_unlocker unlocker(*this, &password);
61056089

61066090
// prepare multisig account
61076091
multisig::multisig_account multisig_account;
@@ -6489,7 +6473,7 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
64896473
THROW_WALLET_EXCEPTION_IF(true, error::file_read_error, "failed to load keys from buffer");
64906474
}
64916475

6492-
wallet_keys_unlocker unlocker(*this, m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only && !m_is_background_wallet, password);
6476+
wallet_keys_unlocker unlocker(*this, &password);
64936477

64946478
//keys loaded ok!
64956479
//try to load wallet cache. but even if we failed, it is not big problem

src/wallet/wallet2.h

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -115,15 +115,14 @@ namespace tools
115115
class wallet_keys_unlocker
116116
{
117117
public:
118-
wallet_keys_unlocker(wallet2 &w, const boost::optional<tools::password_container> &password);
119-
wallet_keys_unlocker(wallet2 &w, bool locked, const epee::wipeable_string &password);
118+
wallet_keys_unlocker(wallet2 &w, const epee::wipeable_string *password);
120119
~wallet_keys_unlocker();
121120
private:
122121
wallet2 &w;
123-
bool locked;
122+
bool should_relock;
124123
crypto::chacha_key key;
125124
static boost::mutex lockers_lock;
126-
static unsigned int lockers;
125+
static std::map<wallet2*, std::size_t> lockers_per_wallet;
127126
};
128127

129128
class i_wallet2_callback
@@ -954,11 +953,6 @@ namespace tools
954953
void restore(const std::string& wallet_, const epee::wipeable_string& password, const std::string &device_name, bool create_address_file = false);
955954

956955
private:
957-
/*!
958-
* \brief Decrypts the account keys
959-
* \return an RAII reencryptor for the account keys
960-
*/
961-
epee::misc_utils::auto_scope_leave_caller decrypt_account_for_multisig(const epee::wipeable_string &password);
962956
/*!
963957
* \brief Creates an uninitialized multisig account
964958
* \outparam: the uninitialized multisig account
@@ -1063,6 +1057,7 @@ namespace tools
10631057
cryptonote::account_base& get_account(){return m_account;}
10641058
const cryptonote::account_base& get_account()const{return m_account;}
10651059

1060+
bool is_key_encryption_enabled() const;
10661061
void encrypt_keys(const crypto::chacha_key &key);
10671062
void encrypt_keys(const epee::wipeable_string &password);
10681063
void decrypt_keys(const crypto::chacha_key &key);

0 commit comments

Comments
 (0)