store secret keys encrypted where possible
The secret spend key is kept encrypted in memory, and decrypted on the fly when needed. Both spend and view secret keys are kept encrypted in a JSON field in the keys file. This avoids leaving the keys in memory due to being manipulated by the JSON I/O API.
This commit is contained in:
parent
ea37614efe
commit
e9ffa91257
|
@ -85,6 +85,14 @@ public: \
|
|||
static_assert(std::is_pod<decltype(this_ref.varialble)>::value, "t_type must be a POD type."); \
|
||||
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(varialble, val_name)
|
||||
|
||||
#define KV_SERIALIZE_VAL_POD_AS_BLOB_OPT_N(varialble, val_name, default_value) \
|
||||
do { \
|
||||
static_assert(std::is_pod<decltype(this_ref.varialble)>::value, "t_type must be a POD type."); \
|
||||
bool ret = KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(varialble, val_name); \
|
||||
if (!ret) \
|
||||
epee::serialize_default(this_ref.varialble, default_value); \
|
||||
} while(0);
|
||||
|
||||
#define KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(varialble, val_name) \
|
||||
epee::serialization::selector<is_store>::serialize_stl_container_pod_val_as_blob(this_ref.varialble, stg, hparent_section, val_name);
|
||||
|
||||
|
@ -92,6 +100,7 @@ public: \
|
|||
|
||||
#define KV_SERIALIZE(varialble) KV_SERIALIZE_N(varialble, #varialble)
|
||||
#define KV_SERIALIZE_VAL_POD_AS_BLOB(varialble) KV_SERIALIZE_VAL_POD_AS_BLOB_N(varialble, #varialble)
|
||||
#define KV_SERIALIZE_VAL_POD_AS_BLOB_OPT(varialble, def) KV_SERIALIZE_VAL_POD_AS_BLOB_OPT_N(varialble, #varialble, def)
|
||||
#define KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(varialble) KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(varialble, #varialble) //skip is_pod compile time check
|
||||
#define KV_SERIALIZE_CONTAINER_POD_AS_BLOB(varialble) KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(varialble, #varialble)
|
||||
#define KV_SERIALIZE_OPT(variable,default_value) KV_SERIALIZE_OPT_N(variable, #variable, default_value)
|
||||
|
|
|
@ -44,6 +44,9 @@ extern "C"
|
|||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||
#define MONERO_DEFAULT_LOG_CATEGORY "account"
|
||||
|
||||
#define KEYS_ENCRYPTION_SALT 'k'
|
||||
|
||||
|
||||
using namespace std;
|
||||
|
||||
DISABLE_VS_WARNINGS(4244 4345)
|
||||
|
@ -60,7 +63,70 @@ DISABLE_VS_WARNINGS(4244 4345)
|
|||
m_device = &hwdev;
|
||||
MCDEBUG("device", "account_keys::set_device device type: "<<typeid(hwdev).name());
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
static void derive_key(const crypto::chacha_key &base_key, crypto::chacha_key &key)
|
||||
{
|
||||
static_assert(sizeof(base_key) == sizeof(crypto::hash), "chacha key and hash should be the same size");
|
||||
tools::scrubbed_arr<char, sizeof(base_key)+1> data;
|
||||
memcpy(data.data(), &base_key, sizeof(base_key));
|
||||
data[sizeof(base_key)] = KEYS_ENCRYPTION_SALT;
|
||||
crypto::generate_chacha_key(data.data(), sizeof(data), key, 1);
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
static epee::wipeable_string get_key_stream(const crypto::chacha_key &base_key, const crypto::chacha_iv &iv, size_t bytes)
|
||||
{
|
||||
// derive a new key
|
||||
crypto::chacha_key key;
|
||||
derive_key(base_key, key);
|
||||
|
||||
// chacha
|
||||
epee::wipeable_string buffer0(std::string(bytes, '\0'));
|
||||
epee::wipeable_string buffer1 = buffer0;
|
||||
crypto::chacha20(buffer0.data(), buffer0.size(), key, iv, buffer1.data());
|
||||
return buffer1;
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
void account_keys::xor_with_key_stream(const crypto::chacha_key &key)
|
||||
{
|
||||
// encrypt a large enough byte stream with chacha20
|
||||
epee::wipeable_string key_stream = get_key_stream(key, m_encryption_iv, sizeof(crypto::secret_key) * (2 + m_multisig_keys.size()));
|
||||
const char *ptr = key_stream.data();
|
||||
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
|
||||
m_spend_secret_key.data[i] ^= *ptr++;
|
||||
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
|
||||
m_view_secret_key.data[i] ^= *ptr++;
|
||||
for (crypto::secret_key &k: m_multisig_keys)
|
||||
{
|
||||
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
|
||||
k.data[i] ^= *ptr++;
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
void account_keys::encrypt(const crypto::chacha_key &key)
|
||||
{
|
||||
m_encryption_iv = crypto::rand<crypto::chacha_iv>();
|
||||
xor_with_key_stream(key);
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
void account_keys::decrypt(const crypto::chacha_key &key)
|
||||
{
|
||||
xor_with_key_stream(key);
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
void account_keys::encrypt_viewkey(const crypto::chacha_key &key)
|
||||
{
|
||||
// encrypt a large enough byte stream with chacha20
|
||||
epee::wipeable_string key_stream = get_key_stream(key, m_encryption_iv, sizeof(crypto::secret_key) * 2);
|
||||
const char *ptr = key_stream.data();
|
||||
ptr += sizeof(crypto::secret_key);
|
||||
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
|
||||
m_view_secret_key.data[i] ^= *ptr++;
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
void account_keys::decrypt_viewkey(const crypto::chacha_key &key)
|
||||
{
|
||||
encrypt_viewkey(key);
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
account_base::account_base()
|
||||
{
|
||||
|
|
|
@ -44,18 +44,29 @@ namespace cryptonote
|
|||
crypto::secret_key m_view_secret_key;
|
||||
std::vector<crypto::secret_key> m_multisig_keys;
|
||||
hw::device *m_device = &hw::get_device("default");
|
||||
crypto::chacha_iv m_encryption_iv;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(m_account_address)
|
||||
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(m_spend_secret_key)
|
||||
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(m_view_secret_key)
|
||||
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(m_multisig_keys)
|
||||
const crypto::chacha_iv default_iv{{0, 0, 0, 0, 0, 0, 0, 0}};
|
||||
KV_SERIALIZE_VAL_POD_AS_BLOB_OPT(m_encryption_iv, default_iv)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
|
||||
account_keys& operator=(account_keys const&) = default;
|
||||
|
||||
void encrypt(const crypto::chacha_key &key);
|
||||
void decrypt(const crypto::chacha_key &key);
|
||||
void encrypt_viewkey(const crypto::chacha_key &key);
|
||||
void decrypt_viewkey(const crypto::chacha_key &key);
|
||||
|
||||
hw::device& get_device() const ;
|
||||
void set_device( hw::device &hwdev) ;
|
||||
|
||||
private:
|
||||
void xor_with_key_stream(const crypto::chacha_key &key);
|
||||
};
|
||||
|
||||
/************************************************************************/
|
||||
|
@ -87,6 +98,11 @@ namespace cryptonote
|
|||
void forget_spend_key();
|
||||
const std::vector<crypto::secret_key> &get_multisig_keys() const { return m_keys.m_multisig_keys; }
|
||||
|
||||
void encrypt_keys(const crypto::chacha_key &key) { m_keys.encrypt(key); }
|
||||
void decrypt_keys(const crypto::chacha_key &key) { m_keys.decrypt(key); }
|
||||
void encrypt_viewkey(const crypto::chacha_key &key) { m_keys.encrypt_viewkey(key); }
|
||||
void decrypt_viewkey(const crypto::chacha_key &key) { m_keys.decrypt_viewkey(key); }
|
||||
|
||||
template <class t_archive>
|
||||
inline void serialize(t_archive &a, const unsigned int /*ver*/)
|
||||
{
|
||||
|
|
|
@ -92,7 +92,7 @@ static bool generate_multisig(uint32_t threshold, uint32_t total, const std::str
|
|||
{
|
||||
std::string name = basename + "-" + std::to_string(n + 1);
|
||||
wallets[n].reset(new tools::wallet2(nettype));
|
||||
wallets[n]->init("");
|
||||
wallets[n]->init(false, "");
|
||||
wallets[n]->generate(name, pwd_container->password(), rct::rct2sk(rct::skGen()), false, false, create_address_file);
|
||||
}
|
||||
|
||||
|
@ -101,11 +101,13 @@ static bool generate_multisig(uint32_t threshold, uint32_t total, const std::str
|
|||
std::vector<crypto::public_key> pk(total);
|
||||
for (size_t n = 0; n < total; ++n)
|
||||
{
|
||||
wallets[n]->decrypt_keys(pwd_container->password());
|
||||
if (!tools::wallet2::verify_multisig_info(wallets[n]->get_multisig_info(), sk[n], pk[n]))
|
||||
{
|
||||
tools::fail_msg_writer() << tr("Failed to verify multisig info");
|
||||
return false;
|
||||
}
|
||||
wallets[n]->encrypt_keys(pwd_container->password());
|
||||
}
|
||||
|
||||
// make the wallets multisig
|
||||
|
|
|
@ -106,6 +106,12 @@ typedef cryptonote::simple_wallet sw;
|
|||
m_auto_refresh_enabled.store(auto_refresh_enabled, std::memory_order_relaxed); \
|
||||
})
|
||||
|
||||
#define SCOPED_WALLET_UNLOCK() \
|
||||
LOCK_IDLE_SCOPE(); \
|
||||
boost::optional<tools::password_container> pwd_container = boost::none; \
|
||||
if (m_wallet->ask_password() && !m_wallet->watch_only() && !(pwd_container = get_and_verify_password())) { return true; } \
|
||||
tools::wallet_keys_unlocker unlocker(*m_wallet, pwd_container);
|
||||
|
||||
enum TransferType {
|
||||
TransferOriginal,
|
||||
TransferNew,
|
||||
|
@ -553,6 +559,18 @@ namespace
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void print_secret_key(const crypto::secret_key &k)
|
||||
{
|
||||
static constexpr const char hex[] = u8"0123456789abcdef";
|
||||
const uint8_t *ptr = (const uint8_t*)k.data;
|
||||
for (size_t i = 0, sz = sizeof(k); i < sz; ++i)
|
||||
{
|
||||
putchar(hex[*ptr >> 4]);
|
||||
putchar(hex[*ptr & 15]);
|
||||
++ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool parse_priority(const std::string& arg, uint32_t& priority)
|
||||
|
@ -602,12 +620,15 @@ std::string simple_wallet::get_command_usage(const std::vector<std::string> &arg
|
|||
|
||||
bool simple_wallet::viewkey(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
|
||||
{
|
||||
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
|
||||
SCOPED_WALLET_UNLOCK();
|
||||
// don't log
|
||||
PAUSE_READLINE();
|
||||
if (m_wallet->key_on_device()) {
|
||||
std::cout << "secret: On device. Not available" << std::endl;
|
||||
} else {
|
||||
std::cout << "secret: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_view_secret_key) << std::endl;
|
||||
printf("secret: ");
|
||||
print_secret_key(m_wallet->get_account().get_keys().m_view_secret_key);
|
||||
putchar('\n');
|
||||
}
|
||||
std::cout << "public: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_account_address.m_view_public_key) << std::endl;
|
||||
|
||||
|
@ -621,12 +642,15 @@ bool simple_wallet::spendkey(const std::vector<std::string> &args/* = std::vecto
|
|||
fail_msg_writer() << tr("wallet is watch-only and has no spend key");
|
||||
return true;
|
||||
}
|
||||
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
|
||||
SCOPED_WALLET_UNLOCK();
|
||||
// don't log
|
||||
PAUSE_READLINE();
|
||||
if (m_wallet->key_on_device()) {
|
||||
std::cout << "secret: On device. Not available" << std::endl;
|
||||
} else {
|
||||
std::cout << "secret: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_spend_secret_key) << std::endl;
|
||||
printf("secret: ");
|
||||
print_secret_key(m_wallet->get_account().get_keys().m_spend_secret_key);
|
||||
putchar('\n');
|
||||
}
|
||||
std::cout << "public: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_account_address.m_spend_public_key) << std::endl;
|
||||
|
||||
|
@ -649,7 +673,8 @@ bool simple_wallet::print_seed(bool encrypted)
|
|||
fail_msg_writer() << tr("wallet is watch-only and has no seed");
|
||||
return true;
|
||||
}
|
||||
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
|
||||
|
||||
SCOPED_WALLET_UNLOCK();
|
||||
|
||||
multisig = m_wallet->multisig(&ready);
|
||||
if (multisig)
|
||||
|
@ -718,22 +743,36 @@ bool simple_wallet::seed_set_language(const std::vector<std::string> &args/* = s
|
|||
fail_msg_writer() << tr("wallet is watch-only and has no seed");
|
||||
return true;
|
||||
}
|
||||
if (!m_wallet->is_deterministic())
|
||||
{
|
||||
fail_msg_writer() << tr("wallet is non-deterministic and has no seed");
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto pwd_container = get_and_verify_password();
|
||||
if (pwd_container)
|
||||
{
|
||||
std::string mnemonic_language = get_mnemonic_language();
|
||||
if (mnemonic_language.empty())
|
||||
return true;
|
||||
|
||||
m_wallet->set_seed_language(std::move(mnemonic_language));
|
||||
m_wallet->rewrite(m_wallet_file, pwd_container->password());
|
||||
epee::wipeable_string password;
|
||||
{
|
||||
SCOPED_WALLET_UNLOCK();
|
||||
|
||||
if (!m_wallet->is_deterministic())
|
||||
{
|
||||
fail_msg_writer() << tr("wallet is non-deterministic and has no seed");
|
||||
return true;
|
||||
}
|
||||
|
||||
// we need the password, even if ask-password is unset
|
||||
if (!pwd_container)
|
||||
{
|
||||
pwd_container = get_and_verify_password();
|
||||
if (pwd_container == boost::none)
|
||||
{
|
||||
fail_msg_writer() << tr("Incorrect password");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
password = pwd_container->password();
|
||||
}
|
||||
|
||||
std::string mnemonic_language = get_mnemonic_language();
|
||||
if (mnemonic_language.empty())
|
||||
return true;
|
||||
|
||||
m_wallet->set_seed_language(std::move(mnemonic_language));
|
||||
m_wallet->rewrite(m_wallet_file, password);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -754,8 +793,7 @@ bool simple_wallet::change_password(const std::vector<std::string> &args)
|
|||
|
||||
try
|
||||
{
|
||||
m_wallet->rewrite(m_wallet_file, pwd_container->password());
|
||||
m_wallet->store();
|
||||
m_wallet->change_password(m_wallet_file, orig_pwd_container->password(), pwd_container->password());
|
||||
}
|
||||
catch (const tools::error::wallet_logic_error& e)
|
||||
{
|
||||
|
@ -858,12 +896,7 @@ bool simple_wallet::prepare_multisig(const std::vector<std::string> &args)
|
|||
return true;
|
||||
}
|
||||
|
||||
const auto orig_pwd_container = get_and_verify_password();
|
||||
if(orig_pwd_container == boost::none)
|
||||
{
|
||||
fail_msg_writer() << tr("Your password is incorrect.");
|
||||
return true;
|
||||
}
|
||||
SCOPED_WALLET_UNLOCK();
|
||||
|
||||
std::string multisig_info = m_wallet->get_multisig_info();
|
||||
success_msg_writer() << multisig_info;
|
||||
|
@ -896,13 +929,6 @@ bool simple_wallet::make_multisig(const std::vector<std::string> &args)
|
|||
return true;
|
||||
}
|
||||
|
||||
const auto orig_pwd_container = get_and_verify_password();
|
||||
if(orig_pwd_container == boost::none)
|
||||
{
|
||||
fail_msg_writer() << tr("Your original password was incorrect.");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.size() < 2)
|
||||
{
|
||||
fail_msg_writer() << tr("usage: make_multisig <threshold> <multisiginfo1> [<multisiginfo2>...]");
|
||||
|
@ -917,6 +943,13 @@ bool simple_wallet::make_multisig(const std::vector<std::string> &args)
|
|||
return true;
|
||||
}
|
||||
|
||||
const auto orig_pwd_container = get_and_verify_password();
|
||||
if(orig_pwd_container == boost::none)
|
||||
{
|
||||
fail_msg_writer() << tr("Your original password was incorrect.");
|
||||
return true;
|
||||
}
|
||||
|
||||
LOCK_IDLE_SCOPE();
|
||||
|
||||
try
|
||||
|
@ -958,6 +991,14 @@ bool simple_wallet::finalize_multisig(const std::vector<std::string> &args)
|
|||
fail_msg_writer() << tr("command not supported by HW wallet");
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto pwd_container = get_and_verify_password();
|
||||
if(pwd_container == boost::none)
|
||||
{
|
||||
fail_msg_writer() << tr("Your original password was incorrect.");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!m_wallet->multisig(&ready))
|
||||
{
|
||||
fail_msg_writer() << tr("This wallet is not multisig");
|
||||
|
@ -969,12 +1010,7 @@ bool simple_wallet::finalize_multisig(const std::vector<std::string> &args)
|
|||
return true;
|
||||
}
|
||||
|
||||
const auto orig_pwd_container = get_and_verify_password();
|
||||
if(orig_pwd_container == boost::none)
|
||||
{
|
||||
fail_msg_writer() << tr("Your original password was incorrect.");
|
||||
return true;
|
||||
}
|
||||
LOCK_IDLE_SCOPE();
|
||||
|
||||
if (args.size() < 2)
|
||||
{
|
||||
|
@ -984,7 +1020,7 @@ bool simple_wallet::finalize_multisig(const std::vector<std::string> &args)
|
|||
|
||||
try
|
||||
{
|
||||
if (!m_wallet->finalize_multisig(orig_pwd_container->password(), args))
|
||||
if (!m_wallet->finalize_multisig(pwd_container->password(), args))
|
||||
{
|
||||
fail_msg_writer() << tr("Failed to finalize multisig");
|
||||
return true;
|
||||
|
@ -1022,8 +1058,8 @@ bool simple_wallet::export_multisig(const std::vector<std::string> &args)
|
|||
fail_msg_writer() << tr("usage: export_multisig_info <filename>");
|
||||
return true;
|
||||
}
|
||||
if (m_wallet->ask_password() && !get_and_verify_password())
|
||||
return true;
|
||||
|
||||
SCOPED_WALLET_UNLOCK();
|
||||
|
||||
const std::string filename = args[0];
|
||||
if (m_wallet->confirm_export_overwrite() && !check_file_overwrite(filename))
|
||||
|
@ -1074,8 +1110,8 @@ bool simple_wallet::import_multisig(const std::vector<std::string> &args)
|
|||
fail_msg_writer() << tr("usage: import_multisig_info <filename1> [<filename2>...] - one for each other participant");
|
||||
return true;
|
||||
}
|
||||
if (m_wallet->ask_password() && !get_and_verify_password())
|
||||
return true;
|
||||
|
||||
SCOPED_WALLET_UNLOCK();
|
||||
|
||||
std::vector<cryptonote::blobdata> info;
|
||||
for (size_t n = 0; n < args.size(); ++n)
|
||||
|
@ -1091,11 +1127,11 @@ bool simple_wallet::import_multisig(const std::vector<std::string> &args)
|
|||
info.push_back(std::move(data));
|
||||
}
|
||||
|
||||
LOCK_IDLE_SCOPE();
|
||||
|
||||
// all read and parsed, actually import
|
||||
try
|
||||
{
|
||||
m_in_manual_refresh.store(true, std::memory_order_relaxed);
|
||||
epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){m_in_manual_refresh.store(false, std::memory_order_relaxed);});
|
||||
size_t n_outputs = m_wallet->import_multisig(info);
|
||||
// Clear line "Height xxx of xxx"
|
||||
std::cout << "\r \r";
|
||||
|
@ -1153,7 +1189,8 @@ bool simple_wallet::sign_multisig(const std::vector<std::string> &args)
|
|||
fail_msg_writer() << tr("usage: sign_multisig <filename>");
|
||||
return true;
|
||||
}
|
||||
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
|
||||
|
||||
SCOPED_WALLET_UNLOCK();
|
||||
|
||||
std::string filename = args[0];
|
||||
std::vector<crypto::hash> txids;
|
||||
|
@ -1226,7 +1263,8 @@ bool simple_wallet::submit_multisig(const std::vector<std::string> &args)
|
|||
fail_msg_writer() << tr("usage: submit_multisig <filename>");
|
||||
return true;
|
||||
}
|
||||
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
|
||||
|
||||
SCOPED_WALLET_UNLOCK();
|
||||
|
||||
if (!try_connect_to_daemon())
|
||||
return true;
|
||||
|
@ -1293,7 +1331,8 @@ bool simple_wallet::export_raw_multisig(const std::vector<std::string> &args)
|
|||
fail_msg_writer() << tr("usage: export_raw_multisig <filename>");
|
||||
return true;
|
||||
}
|
||||
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
|
||||
|
||||
SCOPED_WALLET_UNLOCK();
|
||||
|
||||
std::string filename = args[0];
|
||||
if (m_wallet->confirm_export_overwrite() && !check_file_overwrite(filename))
|
||||
|
@ -1915,6 +1954,14 @@ bool simple_wallet::set_ask_password(const std::vector<std::string> &args/* = st
|
|||
if (pwd_container)
|
||||
{
|
||||
parse_bool_and_use(args[1], [&](bool r) {
|
||||
const bool cur_r = m_wallet->ask_password();
|
||||
if (!m_wallet->watch_only())
|
||||
{
|
||||
if (cur_r && !r)
|
||||
m_wallet->decrypt_keys(pwd_container->password());
|
||||
else if (!cur_r && r)
|
||||
m_wallet->encrypt_keys(pwd_container->password());
|
||||
}
|
||||
m_wallet->ask_password(r);
|
||||
m_wallet->rewrite(m_wallet_file, pwd_container->password());
|
||||
});
|
||||
|
@ -3173,7 +3220,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
|
|||
m_wallet_file = m_generate_from_json;
|
||||
try
|
||||
{
|
||||
m_wallet = tools::wallet2::make_from_json(vm, m_wallet_file, password_prompter);
|
||||
m_wallet = tools::wallet2::make_from_json(vm, false, m_wallet_file, password_prompter);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
|
@ -3475,7 +3522,7 @@ boost::optional<tools::password_container> simple_wallet::get_and_verify_passwor
|
|||
boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
|
||||
const crypto::secret_key& recovery_key, bool recover, bool two_random, const std::string &old_language)
|
||||
{
|
||||
auto rc = tools::wallet2::make_new(vm, password_prompter);
|
||||
auto rc = tools::wallet2::make_new(vm, false, password_prompter);
|
||||
m_wallet = std::move(rc.first);
|
||||
if (!m_wallet)
|
||||
{
|
||||
|
@ -3530,7 +3577,10 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr
|
|||
recovery_val = m_wallet->generate(m_wallet_file, std::move(rc.second).password(), recovery_key, recover, two_random, create_address_file);
|
||||
message_writer(console_color_white, true) << tr("Generated new wallet: ")
|
||||
<< m_wallet->get_account().get_public_address_str(m_wallet->nettype());
|
||||
std::cout << tr("View key: ") << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_view_secret_key) << ENDL;
|
||||
PAUSE_READLINE();
|
||||
std::cout << tr("View key: ");
|
||||
print_secret_key(m_wallet->get_account().get_keys().m_view_secret_key);
|
||||
putchar('\n');
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
|
@ -3567,7 +3617,7 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr
|
|||
const cryptonote::account_public_address& address, const boost::optional<crypto::secret_key>& spendkey,
|
||||
const crypto::secret_key& viewkey)
|
||||
{
|
||||
auto rc = tools::wallet2::make_new(vm, password_prompter);
|
||||
auto rc = tools::wallet2::make_new(vm, false, password_prompter);
|
||||
m_wallet = std::move(rc.first);
|
||||
if (!m_wallet)
|
||||
{
|
||||
|
@ -3613,7 +3663,7 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr
|
|||
//----------------------------------------------------------------------------------------------------
|
||||
boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
|
||||
const std::string &device_name) {
|
||||
auto rc = tools::wallet2::make_new(vm, password_prompter);
|
||||
auto rc = tools::wallet2::make_new(vm, false, password_prompter);
|
||||
m_wallet = std::move(rc.first);
|
||||
if (!m_wallet)
|
||||
{
|
||||
|
@ -3649,7 +3699,7 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr
|
|||
boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
|
||||
const epee::wipeable_string &multisig_keys, const std::string &old_language)
|
||||
{
|
||||
auto rc = tools::wallet2::make_new(vm, password_prompter);
|
||||
auto rc = tools::wallet2::make_new(vm, false, password_prompter);
|
||||
m_wallet = std::move(rc.first);
|
||||
if (!m_wallet)
|
||||
{
|
||||
|
@ -3720,7 +3770,7 @@ bool simple_wallet::open_wallet(const boost::program_options::variables_map& vm)
|
|||
epee::wipeable_string password;
|
||||
try
|
||||
{
|
||||
auto rc = tools::wallet2::make_from_file(vm, m_wallet_file, password_prompter);
|
||||
auto rc = tools::wallet2::make_from_file(vm, false, m_wallet_file, password_prompter);
|
||||
m_wallet = std::move(rc.first);
|
||||
password = std::move(std::move(rc.second).password());
|
||||
if (!m_wallet)
|
||||
|
@ -3747,7 +3797,12 @@ bool simple_wallet::open_wallet(const boost::program_options::variables_map& vm)
|
|||
// NOTE: this is_deprecated() refers to the wallet file format before becoming JSON. It does not refer to the "old english" seed words form of "deprecated" used elsewhere.
|
||||
if (m_wallet->is_deprecated())
|
||||
{
|
||||
if (m_wallet->is_deterministic())
|
||||
bool is_deterministic;
|
||||
{
|
||||
SCOPED_WALLET_UNLOCK();
|
||||
is_deterministic = m_wallet->is_deterministic();
|
||||
}
|
||||
if (is_deterministic)
|
||||
{
|
||||
message_writer(console_color_green, false) << "\n" << tr("You had been using "
|
||||
"a deprecated version of the wallet. Please proceed to upgrade your wallet.\n");
|
||||
|
@ -3985,7 +4040,7 @@ bool simple_wallet::set_daemon(const std::vector<std::string>& args)
|
|||
daemon_url = args[0];
|
||||
}
|
||||
LOCK_IDLE_SCOPE();
|
||||
m_wallet->init(daemon_url);
|
||||
m_wallet->init(false, daemon_url);
|
||||
|
||||
if (args.size() == 2)
|
||||
{
|
||||
|
@ -4099,6 +4154,32 @@ void simple_wallet::on_skip_transaction(uint64_t height, const crypto::hash &txi
|
|||
{
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
boost::optional<epee::wipeable_string> simple_wallet::on_get_password(const char *reason)
|
||||
{
|
||||
// can't ask for password from a background thread
|
||||
if (!m_in_manual_refresh.load(std::memory_order_relaxed))
|
||||
{
|
||||
message_writer(console_color_red, false) << tr("Password needed - use the refresh command");
|
||||
m_cmd_binder.print_prompt();
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
#ifdef HAVE_READLINE
|
||||
rdln::suspend_readline pause_readline;
|
||||
#endif
|
||||
std::string msg = tr("Enter password");
|
||||
if (reason && *reason)
|
||||
msg += std::string(" (") + reason + ")";
|
||||
auto pwd_container = tools::password_container::prompt(false, msg.c_str());
|
||||
if (!pwd_container)
|
||||
{
|
||||
MERROR("Failed to read password");
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
return pwd_container->password();
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool simple_wallet::refresh_main(uint64_t start_height, bool reset, bool is_init)
|
||||
{
|
||||
if (!try_connect_to_daemon(is_init))
|
||||
|
@ -4574,11 +4655,10 @@ bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending
|
|||
bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::string> &args_)
|
||||
{
|
||||
// "transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> <amount> [<payment_id>]"
|
||||
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
|
||||
if (!try_connect_to_daemon())
|
||||
return true;
|
||||
|
||||
LOCK_IDLE_SCOPE();
|
||||
SCOPED_WALLET_UNLOCK();
|
||||
|
||||
std::vector<std::string> local_args = args_;
|
||||
|
||||
|
@ -4981,11 +5061,11 @@ bool simple_wallet::locked_sweep_all(const std::vector<std::string> &args_)
|
|||
|
||||
bool simple_wallet::sweep_unmixable(const std::vector<std::string> &args_)
|
||||
{
|
||||
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
|
||||
if (!try_connect_to_daemon())
|
||||
return true;
|
||||
|
||||
LOCK_IDLE_SCOPE();
|
||||
SCOPED_WALLET_UNLOCK();
|
||||
|
||||
try
|
||||
{
|
||||
// figure out what tx will be necessary
|
||||
|
@ -5098,7 +5178,6 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector<st
|
|||
return true;
|
||||
}
|
||||
|
||||
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
|
||||
if (!try_connect_to_daemon())
|
||||
return true;
|
||||
|
||||
|
@ -5262,7 +5341,7 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector<st
|
|||
}
|
||||
}
|
||||
|
||||
LOCK_IDLE_SCOPE();
|
||||
SCOPED_WALLET_UNLOCK();
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -5364,7 +5443,7 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector<st
|
|||
//----------------------------------------------------------------------------------------------------
|
||||
bool simple_wallet::sweep_single(const std::vector<std::string> &args_)
|
||||
{
|
||||
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
|
||||
SCOPED_WALLET_UNLOCK();
|
||||
if (!try_connect_to_daemon())
|
||||
return true;
|
||||
|
||||
|
@ -5790,7 +5869,8 @@ bool simple_wallet::sign_transfer(const std::vector<std::string> &args_)
|
|||
fail_msg_writer() << tr("usage: sign_transfer [export_raw]");
|
||||
return true;
|
||||
}
|
||||
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
|
||||
|
||||
SCOPED_WALLET_UNLOCK();
|
||||
const bool export_raw = args_.size() == 1;
|
||||
|
||||
std::vector<tools::wallet2::pending_tx> ptx;
|
||||
|
@ -5879,7 +5959,6 @@ bool simple_wallet::get_tx_key(const std::vector<std::string> &args_)
|
|||
fail_msg_writer() << tr("usage: get_tx_key <txid>");
|
||||
return true;
|
||||
}
|
||||
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
|
||||
|
||||
crypto::hash txid;
|
||||
if (!epee::string_tools::hex_to_pod(local_args[0], txid))
|
||||
|
@ -5888,7 +5967,7 @@ bool simple_wallet::get_tx_key(const std::vector<std::string> &args_)
|
|||
return true;
|
||||
}
|
||||
|
||||
LOCK_IDLE_SCOPE();
|
||||
SCOPED_WALLET_UNLOCK();
|
||||
|
||||
crypto::secret_key tx_key;
|
||||
std::vector<crypto::secret_key> additional_tx_keys;
|
||||
|
@ -5993,7 +6072,7 @@ bool simple_wallet::get_tx_proof(const std::vector<std::string> &args)
|
|||
return true;
|
||||
}
|
||||
|
||||
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
|
||||
SCOPED_WALLET_UNLOCK();
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -6208,7 +6287,7 @@ bool simple_wallet::get_spend_proof(const std::vector<std::string> &args)
|
|||
return true;
|
||||
}
|
||||
|
||||
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
|
||||
SCOPED_WALLET_UNLOCK();
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -6303,9 +6382,7 @@ bool simple_wallet::get_reserve_proof(const std::vector<std::string> &args)
|
|||
return true;
|
||||
}
|
||||
|
||||
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
|
||||
|
||||
LOCK_IDLE_SCOPE();
|
||||
SCOPED_WALLET_UNLOCK();
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -6558,6 +6635,9 @@ bool simple_wallet::show_transfers(const std::vector<std::string> &args_)
|
|||
if (pool) {
|
||||
try
|
||||
{
|
||||
m_in_manual_refresh.store(true, std::memory_order_relaxed);
|
||||
epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){m_in_manual_refresh.store(false, std::memory_order_relaxed);});
|
||||
|
||||
m_wallet->update_pool_state();
|
||||
std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>> payments;
|
||||
m_wallet->get_unconfirmed_payments(payments, m_current_subaddress_account, subaddr_indices);
|
||||
|
@ -7406,7 +7486,8 @@ bool simple_wallet::sign(const std::vector<std::string> &args)
|
|||
fail_msg_writer() << tr("This wallet is multisig and cannot sign");
|
||||
return true;
|
||||
}
|
||||
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
|
||||
|
||||
SCOPED_WALLET_UNLOCK();
|
||||
std::string filename = args[0];
|
||||
std::string data;
|
||||
bool r = epee::file_io_utils::load_file_to_string(filename, data);
|
||||
|
@ -7475,14 +7556,14 @@ bool simple_wallet::export_key_images(const std::vector<std::string> &args)
|
|||
fail_msg_writer() << tr("wallet is watch-only and cannot export key images");
|
||||
return true;
|
||||
}
|
||||
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
|
||||
|
||||
SCOPED_WALLET_UNLOCK();
|
||||
std::string filename = args[0];
|
||||
if (m_wallet->confirm_export_overwrite() && !check_file_overwrite(filename))
|
||||
return true;
|
||||
|
||||
try
|
||||
{
|
||||
LOCK_IDLE_SCOPE();
|
||||
if (!m_wallet->export_key_images(filename))
|
||||
{
|
||||
fail_msg_writer() << tr("failed to save file ") << filename;
|
||||
|
@ -7554,12 +7635,12 @@ bool simple_wallet::export_outputs(const std::vector<std::string> &args)
|
|||
fail_msg_writer() << tr("usage: export_outputs <filename>");
|
||||
return true;
|
||||
}
|
||||
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
|
||||
|
||||
SCOPED_WALLET_UNLOCK();
|
||||
std::string filename = args[0];
|
||||
if (m_wallet->confirm_export_overwrite() && !check_file_overwrite(filename))
|
||||
return true;
|
||||
|
||||
LOCK_IDLE_SCOPE();
|
||||
try
|
||||
{
|
||||
std::string data = m_wallet->export_outputs_to_str();
|
||||
|
@ -7605,7 +7686,7 @@ bool simple_wallet::import_outputs(const std::vector<std::string> &args)
|
|||
|
||||
try
|
||||
{
|
||||
LOCK_IDLE_SCOPE();
|
||||
SCOPED_WALLET_UNLOCK();
|
||||
size_t n_outputs = m_wallet->import_outputs_from_str(data);
|
||||
success_msg_writer() << boost::lexical_cast<std::string>(n_outputs) << " outputs imported";
|
||||
}
|
||||
|
|
|
@ -262,6 +262,7 @@ namespace cryptonote
|
|||
virtual void on_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index);
|
||||
virtual void on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx, const cryptonote::subaddress_index& subaddr_index);
|
||||
virtual void on_skip_transaction(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx);
|
||||
virtual boost::optional<epee::wipeable_string> on_get_password(const char *reason);
|
||||
//----------------------------------------------------------
|
||||
|
||||
friend class refresh_progress_reporter_t;
|
||||
|
|
|
@ -2032,7 +2032,8 @@ bool WalletImpl::isNewWallet() const
|
|||
|
||||
bool WalletImpl::doInit(const string &daemon_address, uint64_t upper_transaction_size_limit, bool ssl)
|
||||
{
|
||||
if (!m_wallet->init(daemon_address, m_daemon_login, upper_transaction_size_limit, ssl))
|
||||
// claim RPC so there's no in-memory encryption for now
|
||||
if (!m_wallet->init(true, daemon_address, m_daemon_login, upper_transaction_size_limit, ssl))
|
||||
return false;
|
||||
|
||||
// in case new wallet, this will force fast-refresh (pulling hashes instead of blocks)
|
||||
|
|
|
@ -89,6 +89,7 @@ using namespace cryptonote;
|
|||
|
||||
// arbitrary, used to generate different hashes from the same input
|
||||
#define CHACHA8_KEY_TAIL 0x8c
|
||||
#define CACHE_KEY_TAIL 0x8d
|
||||
|
||||
#define UNSIGNED_TX_PREFIX "Monero unsigned tx set\004"
|
||||
#define SIGNED_TX_PREFIX "Monero signed tx set\004"
|
||||
|
@ -121,8 +122,6 @@ using namespace cryptonote;
|
|||
|
||||
static const std::string MULTISIG_SIGNATURE_MAGIC = "SigMultisigPkV1";
|
||||
|
||||
std::atomic<unsigned int> tools::wallet2::key_ref::refs(0);
|
||||
|
||||
namespace
|
||||
{
|
||||
std::string get_default_ringdb_path()
|
||||
|
@ -197,7 +196,7 @@ std::string get_size_string(const cryptonote::blobdata &tx)
|
|||
return get_size_string(tx.size());
|
||||
}
|
||||
|
||||
std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variables_map& vm, const options& opts, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
|
||||
std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variables_map& vm, bool rpc, const options& opts, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
|
||||
{
|
||||
const bool testnet = command_line::get_arg(vm, opts.testnet);
|
||||
const bool stagenet = command_line::get_arg(vm, opts.stagenet);
|
||||
|
@ -238,7 +237,7 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
|
|||
daemon_address = std::string("http://") + daemon_host + ":" + std::to_string(daemon_port);
|
||||
|
||||
std::unique_ptr<tools::wallet2> wallet(new tools::wallet2(nettype, kdf_rounds));
|
||||
wallet->init(std::move(daemon_address), std::move(login));
|
||||
wallet->init(rpc, std::move(daemon_address), std::move(login));
|
||||
boost::filesystem::path ringdb_path = command_line::get_arg(vm, opts.shared_ringdb_dir);
|
||||
wallet->set_ring_database(ringdb_path.string());
|
||||
return wallet;
|
||||
|
@ -273,7 +272,7 @@ boost::optional<tools::password_container> get_password(const boost::program_opt
|
|||
return password_prompter(verify ? tr("Enter a new password for the wallet") : tr("Wallet password"), verify);
|
||||
}
|
||||
|
||||
std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, const boost::program_options::variables_map& vm, const options& opts, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
|
||||
std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, const boost::program_options::variables_map& vm, bool rpc, const options& opts, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
|
||||
{
|
||||
const bool testnet = command_line::get_arg(vm, opts.testnet);
|
||||
const bool stagenet = command_line::get_arg(vm, opts.stagenet);
|
||||
|
@ -411,7 +410,7 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file,
|
|||
THROW_WALLET_EXCEPTION_IF(deprecated_wallet, tools::error::wallet_internal_error,
|
||||
tools::wallet2::tr("Cannot generate deprecated wallets from JSON"));
|
||||
|
||||
wallet.reset(make_basic(vm, opts, password_prompter).release());
|
||||
wallet.reset(make_basic(vm, rpc, opts, password_prompter).release());
|
||||
wallet->set_refresh_from_block_height(field_scan_from_height);
|
||||
wallet->explicit_refresh_from_block_height(field_scan_from_height_found);
|
||||
|
||||
|
@ -648,6 +647,34 @@ const size_t MAX_SPLIT_ATTEMPTS = 30;
|
|||
constexpr const std::chrono::seconds wallet2::rpc_timeout;
|
||||
const char* wallet2::tr(const char* str) { return i18n_translate(str, "tools::wallet2"); }
|
||||
|
||||
wallet_keys_unlocker::wallet_keys_unlocker(wallet2 &w, const boost::optional<tools::password_container> &password):
|
||||
w(w),
|
||||
locked(password != boost::none)
|
||||
{
|
||||
if (!locked || w.is_rpc())
|
||||
return;
|
||||
const epee::wipeable_string pass = password->password();
|
||||
w.generate_chacha_key_from_password(pass, key);
|
||||
w.decrypt_keys(key);
|
||||
}
|
||||
|
||||
wallet_keys_unlocker::wallet_keys_unlocker(wallet2 &w, bool locked, const epee::wipeable_string &password):
|
||||
w(w),
|
||||
locked(locked)
|
||||
{
|
||||
if (!locked)
|
||||
return;
|
||||
w.generate_chacha_key_from_password(password, key);
|
||||
w.decrypt_keys(key);
|
||||
}
|
||||
|
||||
wallet_keys_unlocker::~wallet_keys_unlocker()
|
||||
{
|
||||
if (!locked)
|
||||
return;
|
||||
w.encrypt_keys(key);
|
||||
}
|
||||
|
||||
wallet2::wallet2(network_type nettype, uint64_t kdf_rounds):
|
||||
m_multisig_rescan_info(NULL),
|
||||
m_multisig_rescan_k(NULL),
|
||||
|
@ -693,7 +720,9 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds):
|
|||
m_key_on_device(false),
|
||||
m_ring_history_saved(false),
|
||||
m_ringdb(),
|
||||
m_last_block_reward(0)
|
||||
m_last_block_reward(0),
|
||||
m_encrypt_keys_after_refresh(boost::none),
|
||||
m_rpc(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -726,14 +755,14 @@ void wallet2::init_options(boost::program_options::options_description& desc_par
|
|||
command_line::add_arg(desc_params, opts.kdf_rounds);
|
||||
}
|
||||
|
||||
std::unique_ptr<wallet2> wallet2::make_from_json(const boost::program_options::variables_map& vm, const std::string& json_file, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
|
||||
std::unique_ptr<wallet2> wallet2::make_from_json(const boost::program_options::variables_map& vm, bool rpc, const std::string& json_file, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
|
||||
{
|
||||
const options opts{};
|
||||
return generate_from_json(json_file, vm, opts, password_prompter);
|
||||
return generate_from_json(json_file, vm, rpc, opts, password_prompter);
|
||||
}
|
||||
|
||||
std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_from_file(
|
||||
const boost::program_options::variables_map& vm, const std::string& wallet_file, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
|
||||
const boost::program_options::variables_map& vm, bool rpc, const std::string& wallet_file, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
|
||||
{
|
||||
const options opts{};
|
||||
auto pwd = get_password(vm, opts, password_prompter, false);
|
||||
|
@ -741,7 +770,7 @@ std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_from_file(
|
|||
{
|
||||
return {nullptr, password_container{}};
|
||||
}
|
||||
auto wallet = make_basic(vm, opts, password_prompter);
|
||||
auto wallet = make_basic(vm, rpc, opts, password_prompter);
|
||||
if (wallet)
|
||||
{
|
||||
wallet->load(wallet_file, pwd->password());
|
||||
|
@ -749,7 +778,7 @@ std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_from_file(
|
|||
return {std::move(wallet), std::move(*pwd)};
|
||||
}
|
||||
|
||||
std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_new(const boost::program_options::variables_map& vm, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter)
|
||||
std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_new(const boost::program_options::variables_map& vm, bool rpc, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter)
|
||||
{
|
||||
const options opts{};
|
||||
auto pwd = get_password(vm, opts, password_prompter, true);
|
||||
|
@ -757,18 +786,19 @@ std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_new(const
|
|||
{
|
||||
return {nullptr, password_container{}};
|
||||
}
|
||||
return {make_basic(vm, opts, password_prompter), std::move(*pwd)};
|
||||
return {make_basic(vm, rpc, opts, password_prompter), std::move(*pwd)};
|
||||
}
|
||||
|
||||
std::unique_ptr<wallet2> wallet2::make_dummy(const boost::program_options::variables_map& vm, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
|
||||
std::unique_ptr<wallet2> wallet2::make_dummy(const boost::program_options::variables_map& vm, bool rpc, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
|
||||
{
|
||||
const options opts{};
|
||||
return make_basic(vm, opts, password_prompter);
|
||||
return make_basic(vm, rpc, opts, password_prompter);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, uint64_t upper_transaction_size_limit, bool ssl)
|
||||
bool wallet2::init(bool rpc, std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, uint64_t upper_transaction_size_limit, bool ssl)
|
||||
{
|
||||
m_rpc = rpc;
|
||||
m_checkpoints.init_default_checkpoints(m_nettype);
|
||||
if(m_http_client.is_connected())
|
||||
m_http_client.disconnect();
|
||||
|
@ -785,8 +815,7 @@ bool wallet2::is_deterministic() const
|
|||
crypto::secret_key second;
|
||||
keccak((uint8_t *)&get_account().get_keys().m_spend_secret_key, sizeof(crypto::secret_key), (uint8_t *)&second, sizeof(crypto::secret_key));
|
||||
sc_reduce32((uint8_t *)&second);
|
||||
bool keys_deterministic = memcmp(second.data,get_account().get_keys().m_view_secret_key.data, sizeof(crypto::secret_key)) == 0;
|
||||
return keys_deterministic;
|
||||
return memcmp(second.data,get_account().get_keys().m_view_secret_key.data, sizeof(crypto::secret_key)) == 0;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool wallet2::get_seed(epee::wipeable_string& electrum_words, const epee::wipeable_string &passphrase) const
|
||||
|
@ -1103,9 +1132,25 @@ static uint64_t decodeRct(const rct::rctSig & rv, const crypto::key_derivation &
|
|||
}
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::scan_output(const cryptonote::transaction &tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, std::unordered_map<cryptonote::subaddress_index, uint64_t> &tx_money_got_in_outs, std::vector<size_t> &outs) const
|
||||
void wallet2::scan_output(const cryptonote::transaction &tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, std::unordered_map<cryptonote::subaddress_index, uint64_t> &tx_money_got_in_outs, std::vector<size_t> &outs)
|
||||
{
|
||||
THROW_WALLET_EXCEPTION_IF(i >= tx.vout.size(), error::wallet_internal_error, "Invalid vout index");
|
||||
|
||||
// if keys are encrypted, ask for password
|
||||
if (m_ask_password && !m_rpc && !m_watch_only && !m_multisig_rescan_k)
|
||||
{
|
||||
static critical_section password_lock;
|
||||
CRITICAL_REGION_LOCAL(password_lock);
|
||||
if (!m_encrypt_keys_after_refresh)
|
||||
{
|
||||
boost::optional<epee::wipeable_string> pwd = m_callback->on_get_password("output received");
|
||||
THROW_WALLET_EXCEPTION_IF(!pwd, error::password_needed, tr("Password is needed to compute key image for incoming monero"));
|
||||
THROW_WALLET_EXCEPTION_IF(!verify_password(*pwd), error::password_needed, tr("Invalid password: password is needed to compute key image for incoming monero"));
|
||||
decrypt_keys(*pwd);
|
||||
m_encrypt_keys_after_refresh = *pwd;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_multisig)
|
||||
{
|
||||
tx_scan_info.in_ephemeral.pub = boost::get<cryptonote::txout_to_key>(tx.vout[i].target).key;
|
||||
|
@ -2063,6 +2108,14 @@ void wallet2::update_pool_state(bool refreshed)
|
|||
{
|
||||
MDEBUG("update_pool_state start");
|
||||
|
||||
auto keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this]() {
|
||||
if (m_encrypt_keys_after_refresh)
|
||||
{
|
||||
encrypt_keys(*m_encrypt_keys_after_refresh);
|
||||
m_encrypt_keys_after_refresh = boost::none;
|
||||
}
|
||||
});
|
||||
|
||||
// get the pool state
|
||||
cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN::request req;
|
||||
cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN::response res;
|
||||
|
@ -2369,8 +2422,6 @@ bool wallet2::delete_address_book_row(std::size_t row_id) {
|
|||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blocks_fetched, bool& received_money)
|
||||
{
|
||||
key_ref kref(*this);
|
||||
|
||||
if(m_light_wallet) {
|
||||
|
||||
// MyMonero get_address_info needs to be called occasionally to trigger wallet sync.
|
||||
|
@ -2438,6 +2489,14 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
|
|||
// subsequent pulls in this refresh.
|
||||
start_height = 0;
|
||||
|
||||
auto keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this]() {
|
||||
if (m_encrypt_keys_after_refresh)
|
||||
{
|
||||
encrypt_keys(*m_encrypt_keys_after_refresh);
|
||||
m_encrypt_keys_after_refresh = boost::none;
|
||||
}
|
||||
});
|
||||
|
||||
bool first = true;
|
||||
while(m_run.load(std::memory_order_relaxed))
|
||||
{
|
||||
|
@ -2507,6 +2566,12 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
|
|||
throw std::runtime_error("proxy exception in refresh thread");
|
||||
}
|
||||
}
|
||||
catch (const tools::error::password_needed&)
|
||||
{
|
||||
blocks_fetched += added_blocks;
|
||||
waiter.wait(&tpool);
|
||||
throw;
|
||||
}
|
||||
catch (const std::exception&)
|
||||
{
|
||||
blocks_fetched += added_blocks;
|
||||
|
@ -2728,8 +2793,20 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
|
|||
std::string multisig_signers;
|
||||
cryptonote::account_base account = m_account;
|
||||
|
||||
crypto::chacha_key key;
|
||||
crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds);
|
||||
|
||||
if (m_ask_password && !m_rpc && !m_watch_only)
|
||||
{
|
||||
account.encrypt_viewkey(key);
|
||||
account.decrypt_keys(key);
|
||||
}
|
||||
|
||||
if (watch_only)
|
||||
account.forget_spend_key();
|
||||
|
||||
account.encrypt_keys(key);
|
||||
|
||||
bool r = epee::serialization::store_t_to_binary(account, account_data);
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to serialize wallet keys");
|
||||
wallet2::keys_file_data keys_file_data = boost::value_initialized<wallet2::keys_file_data>();
|
||||
|
@ -2846,6 +2923,9 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
|
|||
value2.SetUint(m_subaddress_lookahead_minor);
|
||||
json.AddMember("subaddress_lookahead_minor", value2, json.GetAllocator());
|
||||
|
||||
value2.SetUint(1);
|
||||
json.AddMember("encrypted_secret_keys", value2, json.GetAllocator());
|
||||
|
||||
// Serialize the JSON object
|
||||
rapidjson::StringBuffer buffer;
|
||||
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
|
||||
|
@ -2853,7 +2933,6 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
|
|||
account_data = buffer.GetString();
|
||||
|
||||
// Encrypt the entire JSON object.
|
||||
crypto::chacha_key key;
|
||||
crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds);
|
||||
std::string cipher;
|
||||
cipher.resize(account_data.size());
|
||||
|
@ -2871,6 +2950,35 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
|
|||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::setup_keys(const epee::wipeable_string &password)
|
||||
{
|
||||
crypto::chacha_key key;
|
||||
crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds);
|
||||
|
||||
// re-encrypt, but keep viewkey unencrypted
|
||||
if (m_ask_password && !m_rpc && !m_watch_only)
|
||||
{
|
||||
m_account.encrypt_keys(key);
|
||||
m_account.decrypt_viewkey(key);
|
||||
}
|
||||
|
||||
static_assert(HASH_SIZE == sizeof(crypto::chacha_key), "Mismatched sizes of hash and chacha key");
|
||||
tools::scrubbed_arr<char, HASH_SIZE+1> cache_key_data;
|
||||
memcpy(cache_key_data.data(), &key, HASH_SIZE);
|
||||
cache_key_data[HASH_SIZE] = CACHE_KEY_TAIL;
|
||||
cn_fast_hash(cache_key_data.data(), HASH_SIZE+1, (crypto::hash&)m_cache_key);
|
||||
get_ringdb_key();
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::change_password(const std::string &filename, const epee::wipeable_string &original_password, const epee::wipeable_string &new_password)
|
||||
{
|
||||
if (m_ask_password && !m_rpc && !m_watch_only)
|
||||
decrypt_keys(original_password);
|
||||
setup_keys(new_password);
|
||||
rewrite(filename, new_password);
|
||||
store();
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
/*!
|
||||
* \brief Load wallet information from wallet file.
|
||||
* \param keys_file_name Name of wallet file
|
||||
|
@ -2881,6 +2989,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
|
|||
rapidjson::Document json;
|
||||
wallet2::keys_file_data keys_file_data;
|
||||
std::string buf;
|
||||
bool encrypted_secret_keys = false;
|
||||
bool r = epee::file_io_utils::load_file_to_string(keys_file_name, buf);
|
||||
THROW_WALLET_EXCEPTION_IF(!r, error::file_read_error, keys_file_name);
|
||||
|
||||
|
@ -2926,6 +3035,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
|
|||
m_subaddress_lookahead_major = SUBADDRESS_LOOKAHEAD_MAJOR;
|
||||
m_subaddress_lookahead_minor = SUBADDRESS_LOOKAHEAD_MINOR;
|
||||
m_key_on_device = false;
|
||||
encrypted_secret_keys = false;
|
||||
}
|
||||
else if(json.IsObject())
|
||||
{
|
||||
|
@ -3055,6 +3165,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
|
|||
m_subaddress_lookahead_major = field_subaddress_lookahead_major;
|
||||
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, subaddress_lookahead_minor, uint32_t, Uint, false, SUBADDRESS_LOOKAHEAD_MINOR);
|
||||
m_subaddress_lookahead_minor = field_subaddress_lookahead_minor;
|
||||
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, encrypted_secret_keys, uint32_t, Uint, false, false);
|
||||
encrypted_secret_keys = field_encrypted_secret_keys;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -3071,12 +3183,39 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
|
|||
m_account.set_device(hwdev);
|
||||
LOG_PRINT_L0("Device inited...");
|
||||
}
|
||||
|
||||
if (r)
|
||||
{
|
||||
if (encrypted_secret_keys)
|
||||
{
|
||||
m_account.decrypt_keys(key);
|
||||
}
|
||||
else
|
||||
{
|
||||
// rewrite with encrypted keys, ignore errors
|
||||
if (m_ask_password && !m_rpc && !m_watch_only)
|
||||
encrypt_keys(key);
|
||||
bool saved_ret = store_keys(keys_file_name, password, m_watch_only);
|
||||
if (!saved_ret)
|
||||
{
|
||||
// just moan a bit, but not fatal
|
||||
MERROR("Error saving keys file with encrypted keys, not fatal");
|
||||
}
|
||||
if (m_ask_password && !m_rpc && !m_watch_only)
|
||||
decrypt_keys(key);
|
||||
m_keys_file_locker.reset();
|
||||
}
|
||||
}
|
||||
const cryptonote::account_keys& keys = m_account.get_keys();
|
||||
hw::device &hwdev = m_account.get_device();
|
||||
r = r && hwdev.verify_keys(keys.m_view_secret_key, keys.m_account_address.m_view_public_key);
|
||||
if(!m_watch_only && !m_multisig)
|
||||
r = r && hwdev.verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spend_public_key);
|
||||
THROW_WALLET_EXCEPTION_IF(!r, error::invalid_password);
|
||||
|
||||
if (r)
|
||||
setup_keys(password);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -3117,6 +3256,7 @@ bool wallet2::verify_password(const std::string& keys_file_name, const epee::wip
|
|||
rapidjson::Document json;
|
||||
wallet2::keys_file_data keys_file_data;
|
||||
std::string buf;
|
||||
bool encrypted_secret_keys = false;
|
||||
bool r = epee::file_io_utils::load_file_to_string(keys_file_name, buf);
|
||||
THROW_WALLET_EXCEPTION_IF(!r, error::file_read_error, keys_file_name);
|
||||
|
||||
|
@ -3140,19 +3280,50 @@ bool wallet2::verify_password(const std::string& keys_file_name, const epee::wip
|
|||
{
|
||||
account_data = std::string(json["key_data"].GetString(), json["key_data"].GetString() +
|
||||
json["key_data"].GetStringLength());
|
||||
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, encrypted_secret_keys, uint32_t, Uint, false, false);
|
||||
encrypted_secret_keys = field_encrypted_secret_keys;
|
||||
}
|
||||
|
||||
cryptonote::account_base account_data_check;
|
||||
|
||||
r = epee::serialization::load_t_from_binary(account_data_check, account_data);
|
||||
const cryptonote::account_keys& keys = account_data_check.get_keys();
|
||||
|
||||
if (encrypted_secret_keys)
|
||||
account_data_check.decrypt_keys(key);
|
||||
|
||||
const cryptonote::account_keys& keys = account_data_check.get_keys();
|
||||
r = r && hwdev.verify_keys(keys.m_view_secret_key, keys.m_account_address.m_view_public_key);
|
||||
if(!no_spend_key)
|
||||
r = r && hwdev.verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spend_public_key);
|
||||
return r;
|
||||
}
|
||||
|
||||
void wallet2::encrypt_keys(const crypto::chacha_key &key)
|
||||
{
|
||||
m_account.encrypt_keys(key);
|
||||
m_account.decrypt_viewkey(key);
|
||||
}
|
||||
|
||||
void wallet2::decrypt_keys(const crypto::chacha_key &key)
|
||||
{
|
||||
m_account.encrypt_viewkey(key);
|
||||
m_account.decrypt_keys(key);
|
||||
}
|
||||
|
||||
void wallet2::encrypt_keys(const epee::wipeable_string &password)
|
||||
{
|
||||
crypto::chacha_key key;
|
||||
crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds);
|
||||
encrypt_keys(key);
|
||||
}
|
||||
|
||||
void wallet2::decrypt_keys(const epee::wipeable_string &password)
|
||||
{
|
||||
crypto::chacha_key key;
|
||||
crypto::generate_chacha_key(password.data(), password.size(), key, m_kdf_rounds);
|
||||
decrypt_keys(key);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Generates a wallet or restores one.
|
||||
* \param wallet_ Name of wallet file
|
||||
|
@ -3227,6 +3398,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
|
|||
m_multisig_threshold = threshold;
|
||||
m_multisig_signers = multisig_signers;
|
||||
m_key_on_device = false;
|
||||
setup_keys(password);
|
||||
|
||||
if (!wallet_.empty())
|
||||
{
|
||||
|
@ -3281,6 +3453,7 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip
|
|||
m_multisig_threshold = 0;
|
||||
m_multisig_signers.clear();
|
||||
m_key_on_device = false;
|
||||
setup_keys(password);
|
||||
|
||||
// calculate a starting refresh height
|
||||
if(m_refresh_from_block_height == 0 && !recover){
|
||||
|
@ -3382,6 +3555,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
|
|||
m_multisig_threshold = 0;
|
||||
m_multisig_signers.clear();
|
||||
m_key_on_device = false;
|
||||
setup_keys(password);
|
||||
|
||||
if (!wallet_.empty())
|
||||
{
|
||||
|
@ -3435,6 +3609,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
|
|||
m_multisig_threshold = 0;
|
||||
m_multisig_signers.clear();
|
||||
m_key_on_device = false;
|
||||
setup_keys(password);
|
||||
|
||||
if (!wallet_.empty())
|
||||
{
|
||||
|
@ -3481,6 +3656,7 @@ void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& p
|
|||
m_multisig = false;
|
||||
m_multisig_threshold = 0;
|
||||
m_multisig_signers.clear();
|
||||
setup_keys(password);
|
||||
|
||||
if (!wallet_.empty()) {
|
||||
bool r = store_keys(m_keys_file, password, false);
|
||||
|
@ -3520,6 +3696,17 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
|
|||
|
||||
clear();
|
||||
|
||||
// decrypt keys
|
||||
epee::misc_utils::auto_scope_leave_caller keys_reencryptor;
|
||||
if (m_ask_password && !m_rpc && !m_watch_only)
|
||||
{
|
||||
crypto::chacha_key chacha_key;
|
||||
crypto::generate_chacha_key(password.data(), password.size(), chacha_key, m_kdf_rounds);
|
||||
m_account.encrypt_viewkey(chacha_key);
|
||||
m_account.decrypt_keys(chacha_key);
|
||||
keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this, chacha_key]() { m_account.encrypt_keys(chacha_key); m_account.decrypt_viewkey(chacha_key); });
|
||||
}
|
||||
|
||||
MINFO("Creating spend key...");
|
||||
std::vector<crypto::secret_key> multisig_keys;
|
||||
rct::key spend_pkey, spend_skey;
|
||||
|
@ -3580,6 +3767,9 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
|
|||
m_multisig_signers = std::vector<crypto::public_key>(spend_keys.size() + 1, crypto::null_pkey);
|
||||
}
|
||||
|
||||
// re-encrypt keys
|
||||
keys_reencryptor = epee::misc_utils::auto_scope_leave_caller();
|
||||
|
||||
if (!m_wallet_file.empty())
|
||||
{
|
||||
bool r = store_keys(m_keys_file, password, false);
|
||||
|
@ -3662,6 +3852,17 @@ bool wallet2::finalize_multisig(const epee::wipeable_string &password, std::unor
|
|||
{
|
||||
CHECK_AND_ASSERT_THROW_MES(!pkeys.empty(), "empty pkeys");
|
||||
|
||||
// keys are decrypted
|
||||
epee::misc_utils::auto_scope_leave_caller keys_reencryptor;
|
||||
if (m_ask_password && !m_rpc && !m_watch_only)
|
||||
{
|
||||
crypto::chacha_key chacha_key;
|
||||
crypto::generate_chacha_key(password.data(), password.size(), chacha_key, m_kdf_rounds);
|
||||
m_account.encrypt_viewkey(chacha_key);
|
||||
m_account.decrypt_keys(chacha_key);
|
||||
keys_reencryptor = epee::misc_utils::create_scope_leave_handler([&, this, chacha_key]() { m_account.encrypt_keys(chacha_key); m_account.decrypt_viewkey(chacha_key); });
|
||||
}
|
||||
|
||||
// add ours if not included
|
||||
crypto::public_key local_signer;
|
||||
CHECK_AND_ASSERT_THROW_MES(crypto::secret_key_to_public_key(get_account().get_keys().m_spend_secret_key, local_signer),
|
||||
|
@ -3684,6 +3885,9 @@ bool wallet2::finalize_multisig(const epee::wipeable_string &password, std::unor
|
|||
m_multisig_signers = signers;
|
||||
std::sort(m_multisig_signers.begin(), m_multisig_signers.end(), [](const crypto::public_key &e0, const crypto::public_key &e1){ return memcmp(&e0, &e1, sizeof(e0)); });
|
||||
|
||||
// keys are encrypted again
|
||||
keys_reencryptor = epee::misc_utils::auto_scope_leave_caller();
|
||||
|
||||
if (!m_wallet_file.empty())
|
||||
{
|
||||
bool r = store_keys(m_keys_file, password, false);
|
||||
|
@ -3995,6 +4199,11 @@ bool wallet2::generate_chacha_key_from_secret_keys(crypto::chacha_key &key) cons
|
|||
return hwdev.generate_chacha_key(m_account.get_keys(), key, m_kdf_rounds);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::generate_chacha_key_from_password(const epee::wipeable_string &pass, crypto::chacha_key &key) const
|
||||
{
|
||||
crypto::generate_chacha_key(pass.data(), pass.size(), key, m_kdf_rounds);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::load(const std::string& wallet_, const epee::wipeable_string& password)
|
||||
{
|
||||
clear();
|
||||
|
@ -4015,6 +4224,8 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
|
|||
LOG_PRINT_L0("Loaded wallet keys file, with public address: " << m_account.get_public_address_str(m_nettype));
|
||||
lock_keys_file();
|
||||
|
||||
wallet_keys_unlocker unlocker(*this, m_ask_password && !m_rpc && !m_watch_only, password);
|
||||
|
||||
//keys loaded ok!
|
||||
//try to load wallet file. but even if we failed, it is not big problem
|
||||
if(!boost::filesystem::exists(m_wallet_file, e) || e)
|
||||
|
@ -4036,11 +4247,9 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
|
|||
|
||||
r = ::serialization::parse_binary(buf, cache_file_data);
|
||||
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "internal error: failed to deserialize \"" + m_wallet_file + '\"');
|
||||
crypto::chacha_key key;
|
||||
generate_chacha_key_from_secret_keys(key);
|
||||
std::string cache_data;
|
||||
cache_data.resize(cache_file_data.cache_data.size());
|
||||
crypto::chacha20(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), key, cache_file_data.iv, &cache_data[0]);
|
||||
crypto::chacha20(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), m_cache_key, cache_file_data.iv, &cache_data[0]);
|
||||
|
||||
try {
|
||||
std::stringstream iss;
|
||||
|
@ -4048,11 +4257,13 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
|
|||
boost::archive::portable_binary_iarchive ar(iss);
|
||||
ar >> *this;
|
||||
}
|
||||
catch (...)
|
||||
catch(...)
|
||||
{
|
||||
crypto::chacha8(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), key, cache_file_data.iv, &cache_data[0]);
|
||||
try
|
||||
{
|
||||
// try with previous scheme: direct from keys
|
||||
crypto::chacha_key key;
|
||||
generate_chacha_key_from_secret_keys(key);
|
||||
crypto::chacha20(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), key, cache_file_data.iv, &cache_data[0]);
|
||||
try {
|
||||
std::stringstream iss;
|
||||
iss << cache_data;
|
||||
boost::archive::portable_binary_iarchive ar(iss);
|
||||
|
@ -4060,13 +4271,24 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
|
|||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_PRINT_L0("Failed to open portable binary, trying unportable");
|
||||
boost::filesystem::copy_file(m_wallet_file, m_wallet_file + ".unportable", boost::filesystem::copy_option::overwrite_if_exists);
|
||||
std::stringstream iss;
|
||||
iss.str("");
|
||||
iss << cache_data;
|
||||
boost::archive::binary_iarchive ar(iss);
|
||||
ar >> *this;
|
||||
crypto::chacha8(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), key, cache_file_data.iv, &cache_data[0]);
|
||||
try
|
||||
{
|
||||
std::stringstream iss;
|
||||
iss << cache_data;
|
||||
boost::archive::portable_binary_iarchive ar(iss);
|
||||
ar >> *this;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_PRINT_L0("Failed to open portable binary, trying unportable");
|
||||
boost::filesystem::copy_file(m_wallet_file, m_wallet_file + ".unportable", boost::filesystem::copy_option::overwrite_if_exists);
|
||||
std::stringstream iss;
|
||||
iss.str("");
|
||||
iss << cache_data;
|
||||
boost::archive::binary_iarchive ar(iss);
|
||||
ar >> *this;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4218,12 +4440,10 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas
|
|||
|
||||
wallet2::cache_file_data cache_file_data = boost::value_initialized<wallet2::cache_file_data>();
|
||||
cache_file_data.cache_data = oss.str();
|
||||
crypto::chacha_key key;
|
||||
generate_chacha_key_from_secret_keys(key);
|
||||
std::string cipher;
|
||||
cipher.resize(cache_file_data.cache_data.size());
|
||||
cache_file_data.iv = crypto::rand<crypto::chacha_iv>();
|
||||
crypto::chacha20(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), key, cache_file_data.iv, &cipher[0]);
|
||||
crypto::chacha20(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), m_cache_key, cache_file_data.iv, &cipher[0]);
|
||||
cache_file_data.cache_data = cipher;
|
||||
|
||||
const std::string new_file = same_file ? m_wallet_file + ".new" : path;
|
||||
|
@ -5861,12 +6081,6 @@ crypto::chacha_key wallet2::get_ringdb_key()
|
|||
return *m_ringdb_key;
|
||||
}
|
||||
|
||||
void wallet2::clear_ringdb_key()
|
||||
{
|
||||
MINFO("clearing ringdb key");
|
||||
m_ringdb_key = boost::none;
|
||||
}
|
||||
|
||||
bool wallet2::add_rings(const crypto::chacha_key &key, const cryptonote::transaction_prefix &tx)
|
||||
{
|
||||
if (!m_ringdb)
|
||||
|
@ -5877,7 +6091,6 @@ bool wallet2::add_rings(const crypto::chacha_key &key, const cryptonote::transac
|
|||
|
||||
bool wallet2::add_rings(const cryptonote::transaction_prefix &tx)
|
||||
{
|
||||
key_ref kref(*this);
|
||||
try { return add_rings(get_ringdb_key(), tx); }
|
||||
catch (const std::exception &e) { return false; }
|
||||
}
|
||||
|
@ -5886,7 +6099,6 @@ bool wallet2::remove_rings(const cryptonote::transaction_prefix &tx)
|
|||
{
|
||||
if (!m_ringdb)
|
||||
return false;
|
||||
key_ref kref(*this);
|
||||
try { return m_ringdb->remove_rings(get_ringdb_key(), tx); }
|
||||
catch (const std::exception &e) { return false; }
|
||||
}
|
||||
|
@ -5924,7 +6136,6 @@ bool wallet2::get_rings(const crypto::hash &txid, std::vector<std::pair<crypto::
|
|||
|
||||
bool wallet2::get_ring(const crypto::key_image &key_image, std::vector<uint64_t> &outs)
|
||||
{
|
||||
key_ref kref(*this);
|
||||
try { return get_ring(get_ringdb_key(), key_image, outs); }
|
||||
catch (const std::exception &e) { return false; }
|
||||
}
|
||||
|
@ -5934,7 +6145,6 @@ bool wallet2::set_ring(const crypto::key_image &key_image, const std::vector<uin
|
|||
if (!m_ringdb)
|
||||
return false;
|
||||
|
||||
key_ref kref(*this);
|
||||
try { return m_ringdb->set_ring(get_ringdb_key(), key_image, outs, relative); }
|
||||
catch (const std::exception &e) { return false; }
|
||||
}
|
||||
|
@ -5946,7 +6156,6 @@ bool wallet2::find_and_save_rings(bool force)
|
|||
if (!m_ringdb)
|
||||
return false;
|
||||
|
||||
key_ref kref(*this);
|
||||
COMMAND_RPC_GET_TRANSACTIONS::request req = AUTO_VAL_INIT(req);
|
||||
COMMAND_RPC_GET_TRANSACTIONS::response res = AUTO_VAL_INIT(res);
|
||||
|
||||
|
@ -10662,6 +10871,7 @@ size_t wallet2::import_multisig(std::vector<cryptonote::blobdata> blobs)
|
|||
m_multisig_rescan_info = &info;
|
||||
try
|
||||
{
|
||||
|
||||
refresh(false);
|
||||
}
|
||||
catch (...) {}
|
||||
|
|
|
@ -67,6 +67,19 @@ class Serialization_portability_wallet_Test;
|
|||
namespace tools
|
||||
{
|
||||
class ringdb;
|
||||
class wallet2;
|
||||
|
||||
class wallet_keys_unlocker
|
||||
{
|
||||
public:
|
||||
wallet_keys_unlocker(wallet2 &w, const boost::optional<tools::password_container> &password);
|
||||
wallet_keys_unlocker(wallet2 &w, bool locked, const epee::wipeable_string &password);
|
||||
~wallet_keys_unlocker();
|
||||
private:
|
||||
wallet2 &w;
|
||||
bool locked;
|
||||
crypto::chacha_key key;
|
||||
};
|
||||
|
||||
class i_wallet2_callback
|
||||
{
|
||||
|
@ -77,6 +90,7 @@ namespace tools
|
|||
virtual void on_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index) {}
|
||||
virtual void on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx, const cryptonote::subaddress_index& subaddr_index) {}
|
||||
virtual void on_skip_transaction(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx) {}
|
||||
virtual boost::optional<epee::wipeable_string> on_get_password(const char *reason) { return boost::none; }
|
||||
// Light wallet callbacks
|
||||
virtual void on_lw_new_block(uint64_t height) {}
|
||||
virtual void on_lw_money_received(uint64_t height, const crypto::hash &txid, uint64_t amount) {}
|
||||
|
@ -133,9 +147,11 @@ namespace tools
|
|||
std::deque<crypto::hash> m_blockchain;
|
||||
};
|
||||
|
||||
class wallet_keys_unlocker;
|
||||
class wallet2
|
||||
{
|
||||
friend class ::Serialization_portability_wallet_Test;
|
||||
friend class wallet_keys_unlocker;
|
||||
public:
|
||||
static constexpr const std::chrono::seconds rpc_timeout = std::chrono::minutes(3) + std::chrono::seconds(30);
|
||||
|
||||
|
@ -153,17 +169,17 @@ namespace tools
|
|||
static void init_options(boost::program_options::options_description& desc_params);
|
||||
|
||||
//! Uses stdin and stdout. Returns a wallet2 if no errors.
|
||||
static std::unique_ptr<wallet2> make_from_json(const boost::program_options::variables_map& vm, const std::string& json_file, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
|
||||
static std::unique_ptr<wallet2> make_from_json(const boost::program_options::variables_map& vm, bool rpc, const std::string& json_file, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
|
||||
|
||||
//! Uses stdin and stdout. Returns a wallet2 and password for `wallet_file` if no errors.
|
||||
static std::pair<std::unique_ptr<wallet2>, password_container>
|
||||
make_from_file(const boost::program_options::variables_map& vm, const std::string& wallet_file, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
|
||||
make_from_file(const boost::program_options::variables_map& vm, bool rpc, const std::string& wallet_file, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
|
||||
|
||||
//! Uses stdin and stdout. Returns a wallet2 and password for wallet with no file if no errors.
|
||||
static std::pair<std::unique_ptr<wallet2>, password_container> make_new(const boost::program_options::variables_map& vm, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
|
||||
static std::pair<std::unique_ptr<wallet2>, password_container> make_new(const boost::program_options::variables_map& vm, bool rpc, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
|
||||
|
||||
//! Just parses variables.
|
||||
static std::unique_ptr<wallet2> make_dummy(const boost::program_options::variables_map& vm, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
|
||||
static std::unique_ptr<wallet2> make_dummy(const boost::program_options::variables_map& vm, bool rpc, const std::function<boost::optional<password_container>(const char *, bool)> &password_prompter);
|
||||
|
||||
static bool verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds);
|
||||
|
||||
|
@ -477,16 +493,6 @@ namespace tools
|
|||
std::vector<is_out_data> additional;
|
||||
};
|
||||
|
||||
struct key_ref
|
||||
{
|
||||
key_ref(tools::wallet2 &w): wallet(w) { ++refs; }
|
||||
~key_ref() { if (!--refs) wallet.clear_ringdb_key(); }
|
||||
|
||||
private:
|
||||
tools::wallet2 &wallet;
|
||||
static std::atomic<unsigned int> refs;
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief Generates a wallet or restores one.
|
||||
* \param wallet_ Name of wallet file
|
||||
|
@ -613,6 +619,11 @@ namespace tools
|
|||
cryptonote::account_base& get_account(){return m_account;}
|
||||
const cryptonote::account_base& get_account()const{return m_account;}
|
||||
|
||||
void encrypt_keys(const crypto::chacha_key &key);
|
||||
void encrypt_keys(const epee::wipeable_string &password);
|
||||
void decrypt_keys(const crypto::chacha_key &key);
|
||||
void decrypt_keys(const epee::wipeable_string &password);
|
||||
|
||||
void set_refresh_from_block_height(uint64_t height) {m_refresh_from_block_height = height;}
|
||||
uint64_t get_refresh_from_block_height() const {return m_refresh_from_block_height;}
|
||||
|
||||
|
@ -625,7 +636,7 @@ namespace tools
|
|||
// into account the current median block size rather than
|
||||
// the minimum block size.
|
||||
bool deinit();
|
||||
bool init(std::string daemon_address = "http://localhost:8080",
|
||||
bool init(bool rpc, std::string daemon_address = "http://localhost:8080",
|
||||
boost::optional<epee::net_utils::http::login> daemon_login = boost::none, uint64_t upper_transaction_size_limit = 0, bool ssl = false);
|
||||
|
||||
void stop() { m_run.store(false, std::memory_order_relaxed); }
|
||||
|
@ -1077,6 +1088,8 @@ namespace tools
|
|||
uint64_t adjust_mixin(uint64_t mixin) const;
|
||||
uint32_t adjust_priority(uint32_t priority);
|
||||
|
||||
bool is_rpc() const { return m_rpc; }
|
||||
|
||||
// Light wallet specific functions
|
||||
// fetch unspent outs from lw node and store in m_transfers
|
||||
void light_wallet_get_unspent_outs();
|
||||
|
@ -1153,6 +1166,9 @@ namespace tools
|
|||
bool lock_keys_file();
|
||||
bool unlock_keys_file();
|
||||
bool is_keys_file_locked() const;
|
||||
|
||||
void change_password(const std::string &filename, const epee::wipeable_string &original_password, const epee::wipeable_string &new_password);
|
||||
|
||||
private:
|
||||
/*!
|
||||
* \brief Stores wallet information to wallet file.
|
||||
|
@ -1187,6 +1203,7 @@ namespace tools
|
|||
void generate_genesis(cryptonote::block& b) const;
|
||||
void check_genesis(const crypto::hash& genesis_hash) const; //throws
|
||||
bool generate_chacha_key_from_secret_keys(crypto::chacha_key &key) const;
|
||||
void generate_chacha_key_from_password(const epee::wipeable_string &pass, crypto::chacha_key &key) const;
|
||||
crypto::hash get_payment_id(const pending_tx &ptx) const;
|
||||
void check_acc_out_precomp(const cryptonote::tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, tx_scan_info_t &tx_scan_info) const;
|
||||
void check_acc_out_precomp(const cryptonote::tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, const is_out_data *is_out_data, tx_scan_info_t &tx_scan_info) const;
|
||||
|
@ -1204,7 +1221,7 @@ namespace tools
|
|||
crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const;
|
||||
bool should_pick_a_second_output(bool use_rct, size_t n_transfers, const std::vector<size_t> &unused_transfers_indices, const std::vector<size_t> &unused_dust_indices) const;
|
||||
std::vector<size_t> get_only_rct(const std::vector<size_t> &unused_dust_indices, const std::vector<size_t> &unused_transfers_indices) const;
|
||||
void scan_output(const cryptonote::transaction &tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, std::unordered_map<cryptonote::subaddress_index, uint64_t> &tx_money_got_in_outs, std::vector<size_t> &outs) const;
|
||||
void scan_output(const cryptonote::transaction &tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, std::unordered_map<cryptonote::subaddress_index, uint64_t> &tx_money_got_in_outs, std::vector<size_t> &outs);
|
||||
void trim_hashchain();
|
||||
crypto::key_image get_multisig_composite_key_image(size_t n) const;
|
||||
rct::multisig_kLRki get_multisig_composite_kLRki(size_t n, const crypto::public_key &ignore, std::unordered_set<rct::key> &used_L, std::unordered_set<rct::key> &new_used_L) const;
|
||||
|
@ -1216,8 +1233,7 @@ namespace tools
|
|||
bool remove_rings(const cryptonote::transaction_prefix &tx);
|
||||
bool get_ring(const crypto::chacha_key &key, const crypto::key_image &key_image, std::vector<uint64_t> &outs);
|
||||
crypto::chacha_key get_ringdb_key();
|
||||
void cache_ringdb_key();
|
||||
void clear_ringdb_key();
|
||||
void setup_keys(const epee::wipeable_string &password);
|
||||
|
||||
bool get_rct_distribution(uint64_t &start_height, std::vector<uint64_t> &distribution);
|
||||
|
||||
|
@ -1320,6 +1336,11 @@ namespace tools
|
|||
|
||||
uint64_t m_last_block_reward;
|
||||
std::unique_ptr<tools::file_locker> m_keys_file_locker;
|
||||
|
||||
crypto::chacha_key m_cache_key;
|
||||
boost::optional<epee::wipeable_string> m_encrypt_keys_after_refresh;
|
||||
|
||||
bool m_rpc;
|
||||
};
|
||||
}
|
||||
BOOST_CLASS_VERSION(tools::wallet2, 25)
|
||||
|
|
|
@ -53,6 +53,7 @@ namespace tools
|
|||
// wallet_not_initialized
|
||||
// multisig_export_needed
|
||||
// multisig_import_needed
|
||||
// password_needed
|
||||
// std::logic_error
|
||||
// wallet_logic_error *
|
||||
// file_exists
|
||||
|
@ -209,6 +210,14 @@ namespace tools
|
|||
}
|
||||
};
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
struct password_needed : public wallet_runtime_error
|
||||
{
|
||||
explicit password_needed(std::string&& loc, const std::string &msg = "Password needed")
|
||||
: wallet_runtime_error(std::move(loc), msg)
|
||||
{
|
||||
}
|
||||
};
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
const char* const file_error_messages[] = {
|
||||
"file already exists",
|
||||
"file not found",
|
||||
|
|
|
@ -163,7 +163,7 @@ namespace tools
|
|||
walvars = m_wallet;
|
||||
else
|
||||
{
|
||||
tmpwal = tools::wallet2::make_dummy(*m_vm, password_prompter);
|
||||
tmpwal = tools::wallet2::make_dummy(*m_vm, true, password_prompter);
|
||||
walvars = tmpwal.get();
|
||||
}
|
||||
boost::optional<epee::net_utils::http::login> http_login{};
|
||||
|
@ -2638,7 +2638,7 @@ namespace tools
|
|||
command_line::add_arg(desc, arg_password);
|
||||
po::store(po::parse_command_line(argc, argv, desc), vm2);
|
||||
}
|
||||
std::unique_ptr<tools::wallet2> wal = tools::wallet2::make_new(vm2, nullptr).first;
|
||||
std::unique_ptr<tools::wallet2> wal = tools::wallet2::make_new(vm2, true, nullptr).first;
|
||||
if (!wal)
|
||||
{
|
||||
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
|
||||
|
@ -2712,7 +2712,7 @@ namespace tools
|
|||
}
|
||||
std::unique_ptr<tools::wallet2> wal = nullptr;
|
||||
try {
|
||||
wal = tools::wallet2::make_from_file(vm2, wallet_file, nullptr).first;
|
||||
wal = tools::wallet2::make_from_file(vm2, true, wallet_file, nullptr).first;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
|
@ -3261,13 +3261,13 @@ int main(int argc, char** argv) {
|
|||
LOG_PRINT_L0(tools::wallet_rpc_server::tr("Loading wallet..."));
|
||||
if(!wallet_file.empty())
|
||||
{
|
||||
wal = tools::wallet2::make_from_file(*vm, wallet_file, password_prompt).first;
|
||||
wal = tools::wallet2::make_from_file(*vm, true, wallet_file, password_prompt).first;
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
wal = tools::wallet2::make_from_json(*vm, from_json, password_prompt);
|
||||
wal = tools::wallet2::make_from_json(*vm, true, from_json, password_prompt);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
|
|
|
@ -138,7 +138,7 @@ bool transactions_flow_test(std::string& working_folder,
|
|||
return false;
|
||||
}
|
||||
|
||||
w1.init(daemon_addr_a);
|
||||
w1.init(true, daemon_addr_a);
|
||||
|
||||
uint64_t blocks_fetched = 0;
|
||||
bool received_money;
|
||||
|
@ -149,7 +149,7 @@ bool transactions_flow_test(std::string& working_folder,
|
|||
return false;
|
||||
}
|
||||
|
||||
w2.init(daemon_addr_b);
|
||||
w2.init(true, daemon_addr_b);
|
||||
|
||||
MGINFO_GREEN("Using wallets: " << ENDL
|
||||
<< "Source: " << w1.get_account().get_public_address_str(MAINNET) << ENDL << "Path: " << working_folder + "/" + path_source_wallet << ENDL
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
set(unit_tests_sources
|
||||
account.cpp
|
||||
apply_permutation.cpp
|
||||
address_from_url.cpp
|
||||
ban.cpp
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
// Copyright (c) 2014-2018, The Monero Project
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are
|
||||
// permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
// of conditions and the following disclaimer in the documentation and/or other
|
||||
// materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||
// used to endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "cryptonote_basic/account.h"
|
||||
|
||||
TEST(account, encrypt_keys)
|
||||
{
|
||||
cryptonote::keypair recovery_key = cryptonote::keypair::generate(hw::get_device("default"));
|
||||
cryptonote::account_base account;
|
||||
crypto::secret_key key = account.generate(recovery_key.sec);
|
||||
const cryptonote::account_keys keys = account.get_keys();
|
||||
|
||||
ASSERT_EQ(account.get_keys().m_account_address, keys.m_account_address);
|
||||
ASSERT_EQ(account.get_keys().m_spend_secret_key, keys.m_spend_secret_key);
|
||||
ASSERT_EQ(account.get_keys().m_view_secret_key, keys.m_view_secret_key);
|
||||
ASSERT_EQ(account.get_keys().m_multisig_keys, keys.m_multisig_keys);
|
||||
|
||||
crypto::chacha_key chacha_key;
|
||||
crypto::generate_chacha_key(&recovery_key, sizeof(recovery_key), chacha_key, 1);
|
||||
|
||||
account.encrypt_keys(chacha_key);
|
||||
|
||||
ASSERT_EQ(account.get_keys().m_account_address, keys.m_account_address);
|
||||
ASSERT_NE(account.get_keys().m_spend_secret_key, keys.m_spend_secret_key);
|
||||
ASSERT_NE(account.get_keys().m_view_secret_key, keys.m_view_secret_key);
|
||||
|
||||
account.decrypt_viewkey(chacha_key);
|
||||
|
||||
ASSERT_EQ(account.get_keys().m_account_address, keys.m_account_address);
|
||||
ASSERT_NE(account.get_keys().m_spend_secret_key, keys.m_spend_secret_key);
|
||||
ASSERT_EQ(account.get_keys().m_view_secret_key, keys.m_view_secret_key);
|
||||
|
||||
account.encrypt_viewkey(chacha_key);
|
||||
|
||||
ASSERT_EQ(account.get_keys().m_account_address, keys.m_account_address);
|
||||
ASSERT_NE(account.get_keys().m_spend_secret_key, keys.m_spend_secret_key);
|
||||
ASSERT_NE(account.get_keys().m_view_secret_key, keys.m_view_secret_key);
|
||||
|
||||
account.decrypt_keys(chacha_key);
|
||||
|
||||
ASSERT_EQ(account.get_keys().m_account_address, keys.m_account_address);
|
||||
ASSERT_EQ(account.get_keys().m_spend_secret_key, keys.m_spend_secret_key);
|
||||
ASSERT_EQ(account.get_keys().m_view_secret_key, keys.m_view_secret_key);
|
||||
}
|
|
@ -61,10 +61,13 @@ static void make_wallet(unsigned int idx, tools::wallet2 &wallet)
|
|||
|
||||
try
|
||||
{
|
||||
wallet.init("");
|
||||
wallet.init(false, "");
|
||||
wallet.set_subaddress_lookahead(1, 1);
|
||||
wallet.generate("", "", spendkey, true, false);
|
||||
ASSERT_TRUE(test_addresses[idx].address == wallet.get_account().get_public_address_str(cryptonote::TESTNET));
|
||||
wallet.decrypt_keys("");
|
||||
ASSERT_TRUE(test_addresses[idx].spendkey == epee::string_tools::pod_to_hex(wallet.get_account().get_keys().m_spend_secret_key));
|
||||
wallet.encrypt_keys("");
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
|
@ -83,8 +86,12 @@ static void make_M_2_wallet(tools::wallet2 &wallet0, tools::wallet2 &wallet1, un
|
|||
std::vector<crypto::secret_key> sk0(1), sk1(1);
|
||||
std::vector<crypto::public_key> pk0(1), pk1(1);
|
||||
|
||||
wallet0.decrypt_keys("");
|
||||
std::string mi0 = wallet0.get_multisig_info();
|
||||
wallet0.encrypt_keys("");
|
||||
wallet1.decrypt_keys("");
|
||||
std::string mi1 = wallet1.get_multisig_info();
|
||||
wallet1.encrypt_keys("");
|
||||
|
||||
ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi1, sk0[0], pk0[0]));
|
||||
ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi0, sk1[0], pk1[0]));
|
||||
|
@ -118,9 +125,15 @@ static void make_M_3_wallet(tools::wallet2 &wallet0, tools::wallet2 &wallet1, to
|
|||
std::vector<crypto::secret_key> sk0(2), sk1(2), sk2(2);
|
||||
std::vector<crypto::public_key> pk0(2), pk1(2), pk2(2);
|
||||
|
||||
wallet0.decrypt_keys("");
|
||||
std::string mi0 = wallet0.get_multisig_info();
|
||||
wallet0.encrypt_keys("");
|
||||
wallet1.decrypt_keys("");
|
||||
std::string mi1 = wallet1.get_multisig_info();
|
||||
wallet1.encrypt_keys("");
|
||||
wallet2.decrypt_keys("");
|
||||
std::string mi2 = wallet2.get_multisig_info();
|
||||
wallet2.encrypt_keys("");
|
||||
|
||||
ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi1, sk0[0], pk0[0]));
|
||||
ASSERT_TRUE(tools::wallet2::verify_multisig_info(mi2, sk0[1], pk0[1]));
|
||||
|
|
|
@ -47,7 +47,7 @@ static crypto::chacha_key generate_chacha_key()
|
|||
{
|
||||
crypto::chacha_key chacha_key;
|
||||
uint64_t password = crypto::rand<uint64_t>();
|
||||
crypto::generate_chacha_key(std::string((const char*)&password, sizeof(password)), chacha_key);
|
||||
crypto::generate_chacha_key(std::string((const char*)&password, sizeof(password)), chacha_key, 1);
|
||||
return chacha_key;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue