@ -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,11 +815,10 @@ 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 ( std: : string& electrum_words , const epee : : wipeable_string & passphrase ) const
bool wallet2 : : get_seed ( epee: : wipeable_ string& electrum_words , const epee : : wipeable_string & passphrase ) const
{
bool keys_deterministic = is_deterministic ( ) ;
if ( ! keys_deterministic )
@ -815,7 +844,7 @@ bool wallet2::get_seed(std::string& electrum_words, const epee::wipeable_string
return true ;
}
//----------------------------------------------------------------------------------------------------
bool wallet2 : : get_multisig_seed ( std: : string& seed , const epee : : wipeable_string & passphrase , bool raw ) const
bool wallet2 : : get_multisig_seed ( epee: : wipeable_ string& seed , const epee : : wipeable_string & passphrase , bool raw ) const
{
bool ready ;
uint32_t threshold , total ;
@ -838,7 +867,7 @@ bool wallet2::get_multisig_seed(std::string& seed, const epee::wipeable_string &
crypto : : secret_key skey ;
crypto : : public_key pkey ;
const account_keys & keys = get_account ( ) . get_keys ( ) ;
std: : string data ;
epee: : wipeable_ string data ;
data . append ( ( const char * ) & threshold , sizeof ( uint32_t ) ) ;
data . append ( ( const char * ) & total , sizeof ( uint32_t ) ) ;
skey = keys . m_spend_secret_key ;
@ -864,7 +893,7 @@ bool wallet2::get_multisig_seed(std::string& seed, const epee::wipeable_string &
if ( raw )
{
seed = epee : : string_tools: : buff_to_hex_nodelimer ( data ) ;
seed = epee : : to_hex: : wipeable_string ( { ( const unsigned char * ) data . data ( ) , data . size ( ) } ) ;
}
else
{
@ -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 " ) ;
epee : : mlocked < 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
@ -3161,7 +3332,7 @@ bool wallet2::verify_password(const std::string& keys_file_name, const epee::wip
* \ param create_address_file Whether to create an address file
*/
void wallet2 : : generate ( const std : : string & wallet_ , const epee : : wipeable_string & password ,
const std: : string& multisig_data , bool create_address_file )
const epee: : wipeable_ string& multisig_data , bool create_address_file )
{
clear ( ) ;
prepare_file_names ( wallet_ ) ;
@ -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 ( . . . ) { }
@ -10671,14 +10881,14 @@ size_t wallet2::import_multisig(std::vector<cryptonote::blobdata> blobs)
return n_outputs ;
}
//----------------------------------------------------------------------------------------------------
std : : string wallet2 : : encrypt ( const std : : string & plaintext , const crypto : : secret_key & skey , bool authenticated ) const
std : : string wallet2 : : encrypt ( const char * plaintext , size_t len , const crypto : : secret_key & skey , bool authenticated ) const
{
crypto : : chacha_key key ;
crypto : : generate_chacha_key ( & skey , sizeof ( skey ) , key , m_kdf_rounds ) ;
std : : string ciphertext ;
crypto : : chacha_iv iv = crypto : : rand < crypto : : chacha_iv > ( ) ;
ciphertext . resize ( plaintext. size ( ) + sizeof ( iv ) + ( authenticated ? sizeof ( crypto : : signature ) : 0 ) ) ;
crypto : : chacha20 ( plaintext .data ( ) , plaintext . size ( ) , key , iv , & ciphertext [ sizeof ( iv ) ] ) ;
ciphertext . resize ( len + sizeof ( iv ) + ( authenticated ? sizeof ( crypto : : signature ) : 0 ) ) ;
crypto : : chacha20 ( plaintext , len , key , iv , & ciphertext [ sizeof ( iv ) ] ) ;
memcpy ( & ciphertext [ 0 ] , & iv , sizeof ( iv ) ) ;
if ( authenticated )
{
@ -10692,12 +10902,28 @@ std::string wallet2::encrypt(const std::string &plaintext, const crypto::secret_
return ciphertext ;
}
//----------------------------------------------------------------------------------------------------
std : : string wallet2 : : encrypt ( const epee : : span < char > & plaintext , const crypto : : secret_key & skey , bool authenticated ) const
{
return encrypt ( plaintext . data ( ) , plaintext . size ( ) , skey , authenticated ) ;
}
//----------------------------------------------------------------------------------------------------
std : : string wallet2 : : encrypt ( const std : : string & plaintext , const crypto : : secret_key & skey , bool authenticated ) const
{
return encrypt ( plaintext . data ( ) , plaintext . size ( ) , skey , authenticated ) ;
}
//----------------------------------------------------------------------------------------------------
std : : string wallet2 : : encrypt ( const epee : : wipeable_string & plaintext , const crypto : : secret_key & skey , bool authenticated ) const
{
return encrypt ( plaintext . data ( ) , plaintext . size ( ) , skey , authenticated ) ;
}
//----------------------------------------------------------------------------------------------------
std : : string wallet2 : : encrypt_with_view_secret_key ( const std : : string & plaintext , bool authenticated ) const
{
return encrypt ( plaintext , get_account ( ) . get_keys ( ) . m_view_secret_key , authenticated ) ;
}
//----------------------------------------------------------------------------------------------------
std : : string wallet2 : : decrypt ( const std : : string & ciphertext , const crypto : : secret_key & skey , bool authenticated ) const
template < typename T >
T wallet2 : : decrypt ( const std : : string & ciphertext , const crypto : : secret_key & skey , bool authenticated ) const
{
const size_t prefix_size = sizeof ( chacha_iv ) + ( authenticated ? sizeof ( crypto : : signature ) : 0 ) ;
THROW_WALLET_EXCEPTION_IF ( ciphertext . size ( ) < prefix_size ,
@ -10706,8 +10932,6 @@ std::string wallet2::decrypt(const std::string &ciphertext, const crypto::secret
crypto : : chacha_key key ;
crypto : : generate_chacha_key ( & skey , sizeof ( skey ) , key , m_kdf_rounds ) ;
const crypto : : chacha_iv & iv = * ( const crypto : : chacha_iv * ) & ciphertext [ 0 ] ;
std : : string plaintext ;
plaintext . resize ( ciphertext . size ( ) - prefix_size ) ;
if ( authenticated )
{
crypto : : hash hash ;
@ -10718,10 +10942,14 @@ std::string wallet2::decrypt(const std::string &ciphertext, const crypto::secret
THROW_WALLET_EXCEPTION_IF ( ! crypto : : check_signature ( hash , pkey , signature ) ,
error : : wallet_internal_error , " Failed to authenticate ciphertext " ) ;
}
crypto : : chacha20 ( ciphertext . data ( ) + sizeof ( iv ) , ciphertext . size ( ) - prefix_size , key , iv , & plaintext [ 0 ] ) ;
return plaintext ;
std : : unique_ptr < char [ ] > buffer { new char [ ciphertext . size ( ) - prefix_size ] } ;
auto wiper = epee : : misc_utils : : create_scope_leave_handler ( [ & ] ( ) { memwipe ( buffer . get ( ) , ciphertext . size ( ) - prefix_size ) ; } ) ;
crypto : : chacha20 ( ciphertext . data ( ) + sizeof ( iv ) , ciphertext . size ( ) - prefix_size , key , iv , buffer . get ( ) ) ;
return T ( buffer . get ( ) , ciphertext . size ( ) - prefix_size ) ;
}
//----------------------------------------------------------------------------------------------------
template epee : : wipeable_string wallet2 : : decrypt ( const std : : string & ciphertext , const crypto : : secret_key & skey , bool authenticated ) const ;
//----------------------------------------------------------------------------------------------------
std : : string wallet2 : : decrypt_with_view_secret_key ( const std : : string & ciphertext , bool authenticated ) const
{
return decrypt ( ciphertext , get_account ( ) . get_keys ( ) . m_view_secret_key , authenticated ) ;