2018-12-27 18:50:45 +03:00
// Copyright (c) 2014-2018 Zano Project
// Copyright (c) 2014-2018 The Louisdor Project
// Copyright (c) 2012-2013 The Cryptonote developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
# include <boost/archive/binary_oarchive.hpp>
# include <boost/archive/binary_iarchive.hpp>
# include <fstream>
# include "include_base_utils.h"
# include "account.h"
# include "warnings.h"
# include "crypto/crypto.h"
# include "currency_core/currency_format_utils.h"
# include "common/mnemonic-encoding.h"
using namespace std ;
2020-05-02 23:59:37 +03:00
//DISABLE_VS_WARNINGS(4244 4345)
2018-12-27 18:50:45 +03:00
namespace currency
{
//-----------------------------------------------------------------
account_base : : account_base ( )
{
set_null ( ) ;
}
//-----------------------------------------------------------------
void account_base : : set_null ( )
{
2019-12-26 18:08:57 +03:00
// fill sensitive data with random bytes
2020-04-22 23:30:03 +03:00
crypto : : generate_random_bytes ( sizeof m_keys . spend_secret_key , & m_keys . spend_secret_key ) ;
crypto : : generate_random_bytes ( sizeof m_keys . view_secret_key , & m_keys . view_secret_key ) ;
2020-05-02 23:59:37 +03:00
if ( m_keys_seed_binary . size ( ) )
crypto : : generate_random_bytes ( m_keys_seed_binary . size ( ) , & m_keys_seed_binary [ 0 ] ) ;
2019-12-26 18:08:57 +03:00
// clear
2018-12-27 18:50:45 +03:00
m_keys = account_keys ( ) ;
2019-12-26 18:08:57 +03:00
m_creation_timestamp = 0 ;
2020-05-02 23:59:37 +03:00
m_keys_seed_binary . clear ( ) ;
2018-12-27 18:50:45 +03:00
}
//-----------------------------------------------------------------
2020-05-02 23:59:37 +03:00
void account_base : : generate ( bool auditable /* = false */ )
2019-02-18 19:40:12 +01:00
{
2020-05-02 23:59:37 +03:00
if ( auditable )
m_keys . account_address . flags = ACCOUNT_PUBLIC_ADDRESS_FLAG_AUDITABLE ;
crypto : : generate_seed_keys ( m_keys . account_address . spend_public_key , m_keys . spend_secret_key , m_keys_seed_binary , BRAINWALLET_DEFAULT_SEED_SIZE ) ;
crypto : : dependent_key ( m_keys . spend_secret_key , m_keys . view_secret_key ) ;
2020-04-22 23:30:03 +03:00
if ( ! crypto : : secret_key_to_public_key ( m_keys . view_secret_key , m_keys . account_address . view_public_key ) )
2018-12-27 18:50:45 +03:00
throw std : : runtime_error ( " Failed to create public view key " ) ;
m_creation_timestamp = time ( NULL ) ;
}
//-----------------------------------------------------------------
const account_keys & account_base : : get_keys ( ) const
{
return m_keys ;
}
//-----------------------------------------------------------------
2020-11-17 01:30:51 +01:00
void crypt_with_pass ( const void * scr_data , std : : size_t src_length , void * dst_data , const std : : string & password )
{
crypto : : chacha8_key key = AUTO_VAL_INIT ( key ) ;
crypto : : generate_chacha8_key ( password , key ) ;
crypto : : hash pass_hash = crypto : : cn_fast_hash ( password . data ( ) , password . size ( ) ) ;
crypto : : chacha8_iv iv = AUTO_VAL_INIT ( iv ) ;
CHECK_AND_ASSERT_THROW_MES ( sizeof ( pass_hash ) > = sizeof ( iv ) , " Invalid configuration: hash size is less than keys_file_data.iv " ) ;
iv = * ( ( crypto : : chacha8_iv * ) & pass_hash ) ;
crypto : : chacha8 ( scr_data , src_length , key , iv , ( char * ) dst_data ) ;
}
std : : string account_base : : get_seed_phrase ( const std : : string & password ) const
2018-12-27 18:50:45 +03:00
{
2020-05-02 23:59:37 +03:00
if ( m_keys_seed_binary . empty ( ) )
2019-12-26 18:08:57 +03:00
return " " ;
2020-11-17 01:30:51 +01:00
std : : vector < unsigned char > processed_seed_binary = m_keys_seed_binary ;
if ( ! password . empty ( ) )
{
//encrypt seed phrase binary data
crypt_with_pass ( & m_keys_seed_binary [ 0 ] , m_keys_seed_binary . size ( ) , & processed_seed_binary [ 0 ] , password ) ;
}
std : : string keys_seed_text = tools : : mnemonic_encoding : : binary2text ( processed_seed_binary ) ;
std : : string timestamp_word = currency : : get_word_from_timstamp ( m_creation_timestamp , ! password . empty ( ) ) ;
2020-05-02 23:59:37 +03:00
// floor creation time to WALLET_BRAIN_DATE_QUANTUM to make checksum calculation stable
2020-11-17 01:30:51 +01:00
bool self_check_is_password_used = false ;
uint64_t creation_timestamp_rounded = get_timstamp_from_word ( timestamp_word , self_check_is_password_used ) ;
CHECK_AND_ASSERT_THROW_MES ( self_check_is_password_used = = ! password . empty ( ) , " Account seed phrase internal error: password flag encoded wrong " ) ;
2020-05-02 23:59:37 +03:00
constexpr uint16_t checksum_max = tools : : mnemonic_encoding : : NUMWORDS > > 1 ; // maximum value of checksum
2020-11-18 00:23:57 +01:00
std : : string binary_for_check_sum ( ( const char * ) & m_keys_seed_binary [ 0 ] , m_keys_seed_binary . size ( ) ) ;
2020-11-17 01:30:51 +01:00
binary_for_check_sum . append ( password ) ;
crypto : : hash h = crypto : : cn_fast_hash ( binary_for_check_sum . data ( ) , binary_for_check_sum . size ( ) ) ;
2020-05-02 23:59:37 +03:00
* reinterpret_cast < uint64_t * > ( & h ) = creation_timestamp_rounded ;
h = crypto : : cn_fast_hash ( & h , sizeof h ) ;
uint64_t h_64 = * reinterpret_cast < uint64_t * > ( & h ) ;
uint16_t checksum = h_64 % ( checksum_max + 1 ) ;
uint8_t auditable_flag = 0 ;
if ( m_keys . account_address . flags & ACCOUNT_PUBLIC_ADDRESS_FLAG_AUDITABLE )
auditable_flag = 1 ;
2020-07-19 00:31:55 +02:00
uint32_t auditable_flag_and_checksum = ( auditable_flag & 1 ) | ( checksum < < 1 ) ;
2020-05-02 23:59:37 +03:00
std : : string auditable_flag_and_checksum_word = tools : : mnemonic_encoding : : word_by_num ( auditable_flag_and_checksum ) ;
return keys_seed_text + " " + timestamp_word + " " + auditable_flag_and_checksum_word ;
2018-12-27 18:50:45 +03:00
}
//-----------------------------------------------------------------
2020-06-29 22:55:25 +03:00
std : : string account_base : : get_tracking_seed ( ) const
2020-06-01 15:32:00 +03:00
{
return get_public_address_str ( ) + " : " +
epee : : string_tools : : pod_to_hex ( m_keys . view_secret_key ) +
( m_creation_timestamp ? " : " : " " ) + ( m_creation_timestamp ? epee : : string_tools : : num_to_string_fast ( m_creation_timestamp ) : " " ) ;
}
//-----------------------------------------------------------------
2020-05-02 23:59:37 +03:00
bool account_base : : restore_keys ( const std : : vector < unsigned char > & keys_seed_binary )
2018-12-27 18:50:45 +03:00
{
2020-05-02 23:59:37 +03:00
CHECK_AND_ASSERT_MES ( keys_seed_binary . size ( ) = = BRAINWALLET_DEFAULT_SEED_SIZE , false , " wrong restore data size: " < < keys_seed_binary . size ( ) ) ;
crypto : : keys_from_default ( keys_seed_binary . data ( ) , m_keys . account_address . spend_public_key , m_keys . spend_secret_key , keys_seed_binary . size ( ) ) ;
2020-04-22 23:30:03 +03:00
crypto : : dependent_key ( m_keys . spend_secret_key , m_keys . view_secret_key ) ;
bool r = crypto : : secret_key_to_public_key ( m_keys . view_secret_key , m_keys . account_address . view_public_key ) ;
2018-12-27 18:50:45 +03:00
CHECK_AND_ASSERT_MES ( r , false , " failed to secret_key_to_public_key for view key " ) ;
return true ;
}
//-----------------------------------------------------------------
2020-11-17 01:30:51 +01:00
bool account_base : : restore_from_seed_phrase ( const std : : string & seed_phrase , const std : : string & seed_password )
2018-12-27 18:50:45 +03:00
{
2019-02-18 19:40:12 +01:00
//cut the last timestamp word from restore_dats
std : : list < std : : string > words ;
2020-05-02 23:59:37 +03:00
boost : : split ( words , seed_phrase , boost : : is_space ( ) ) ;
2018-12-27 18:50:45 +03:00
2020-05-02 23:59:37 +03:00
std : : string keys_seed_text , timestamp_word , auditable_flag_and_checksum_word ;
if ( words . size ( ) = = SEED_PHRASE_V1_WORDS_COUNT )
{
// 24 seed words + one timestamp word = 25 total
timestamp_word = words . back ( ) ;
words . erase ( - - words . end ( ) ) ;
keys_seed_text = boost : : algorithm : : join ( words , " " ) ;
}
else if ( words . size ( ) = = SEED_PHRASE_V2_WORDS_COUNT )
{
// 24 seed words + one timestamp word + one flags & checksum = 26 total
auditable_flag_and_checksum_word = words . back ( ) ;
words . erase ( - - words . end ( ) ) ;
timestamp_word = words . back ( ) ;
words . erase ( - - words . end ( ) ) ;
keys_seed_text = boost : : algorithm : : join ( words , " " ) ;
}
else
{
LOG_ERROR ( " Invalid seed words count: " < < words . size ( ) ) ;
return false ;
}
2020-05-07 15:02:35 +03:00
uint64_t auditable_flag_and_checksum = UINT64_MAX ;
if ( ! auditable_flag_and_checksum_word . empty ( ) )
2020-05-02 23:59:37 +03:00
auditable_flag_and_checksum = tools : : mnemonic_encoding : : num_by_word ( auditable_flag_and_checksum_word ) ;
std : : vector < unsigned char > keys_seed_binary = tools : : mnemonic_encoding : : text2binary ( keys_seed_text ) ;
2020-11-17 01:30:51 +01:00
std : : vector < unsigned char > keys_seed_processed_binary = keys_seed_binary ;
bool has_password = false ;
m_creation_timestamp = get_timstamp_from_word ( timestamp_word , has_password ) ;
//double check is password setting from timestamp word match with passed parameters
CHECK_AND_ASSERT_MES ( has_password ! = seed_password . empty ( ) , false , " Seed phrase password wrong interpretation " ) ;
if ( has_password )
{
CHECK_AND_ASSERT_MES ( ! seed_password . empty ( ) , false , " Seed phrase password wrong interpretation: internal error " ) ;
crypt_with_pass ( & keys_seed_binary [ 0 ] , keys_seed_binary . size ( ) , & keys_seed_processed_binary [ 0 ] , seed_password ) ;
}
2018-12-27 18:50:45 +03:00
2020-11-17 01:30:51 +01:00
CHECK_AND_ASSERT_MES ( keys_seed_processed_binary . size ( ) , false , " text2binary failed to convert the given text " ) ; // don't prints event incorrect seed into the log for security
2020-05-02 23:59:37 +03:00
2020-05-07 15:02:35 +03:00
bool auditable_flag = false ;
// check the checksum if checksum word provided
if ( auditable_flag_and_checksum ! = UINT64_MAX )
{
auditable_flag = ( auditable_flag_and_checksum & 1 ) ! = 0 ; // auditable flag is the lower 1 bit
2020-07-19 00:31:55 +02:00
uint16_t checksum = static_cast < uint16_t > ( auditable_flag_and_checksum > > 1 ) ; // checksum -- everything else
2020-05-07 15:02:35 +03:00
constexpr uint16_t checksum_max = tools : : mnemonic_encoding : : NUMWORDS > > 1 ; // maximum value of checksum
2020-11-18 00:23:57 +01:00
std : : string binary_for_check_sum ( ( const char * ) & keys_seed_processed_binary [ 0 ] , keys_seed_processed_binary . size ( ) ) ;
2020-11-17 01:30:51 +01:00
binary_for_check_sum . append ( seed_password ) ;
crypto : : hash h = crypto : : cn_fast_hash ( binary_for_check_sum . data ( ) , binary_for_check_sum . size ( ) ) ;
2020-05-07 15:02:35 +03:00
* reinterpret_cast < uint64_t * > ( & h ) = m_creation_timestamp ;
h = crypto : : cn_fast_hash ( & h , sizeof h ) ;
uint64_t h_64 = * reinterpret_cast < uint64_t * > ( & h ) ;
uint16_t checksum_calculated = h_64 % ( checksum_max + 1 ) ;
CHECK_AND_ASSERT_MES ( checksum = = checksum_calculated , false , " seed phase has invalid checksum: " < < checksum_calculated < < " , while " < < checksum < < " is expected, check your words " ) ;
}
2020-05-02 23:59:37 +03:00
2020-11-17 01:30:51 +01:00
bool r = restore_keys ( keys_seed_processed_binary ) ;
2020-05-02 23:59:37 +03:00
CHECK_AND_ASSERT_MES ( r , false , " restore_keys failed " ) ;
2020-11-17 01:30:51 +01:00
m_keys_seed_binary = keys_seed_processed_binary ;
2020-05-02 23:59:37 +03:00
if ( auditable_flag )
m_keys . account_address . flags | = ACCOUNT_PUBLIC_ADDRESS_FLAG_AUDITABLE ;
2019-02-18 19:40:12 +01:00
return true ;
2018-12-27 18:50:45 +03:00
}
//-----------------------------------------------------------------
2020-11-21 23:05:09 +01:00
bool account_base : : is_seed_password_protected ( const std : : string & seed_phrase , bool & is_password_protected )
{
//cut the last timestamp word from restore_dats
std : : list < std : : string > words ;
boost : : split ( words , seed_phrase , boost : : is_space ( ) ) ;
std : : string timestamp_word ;
if ( words . size ( ) = = SEED_PHRASE_V1_WORDS_COUNT )
{
// 24 seed words + one timestamp word = 25 total
timestamp_word = words . back ( ) ;
}
else if ( words . size ( ) = = SEED_PHRASE_V2_WORDS_COUNT )
{
// 24 seed words + one timestamp word + one flags & checksum = 26 total
words . erase ( - - words . end ( ) ) ;
timestamp_word = words . back ( ) ;
}
else
{
return false ;
}
get_timstamp_from_word ( timestamp_word , is_password_protected ) ;
return true ;
}
//-----------------------------------------------------------------
2020-06-29 22:55:25 +03:00
bool account_base : : restore_from_tracking_seed ( const std : : string & tracking_seed )
2020-05-27 17:20:25 +03:00
{
set_null ( ) ;
2020-06-29 22:55:25 +03:00
bool r = parse_tracking_seed ( tracking_seed , m_keys . account_address , m_keys . view_secret_key , m_creation_timestamp ) ;
2020-05-27 17:20:25 +03:00
return r ;
}
//-----------------------------------------------------------------
2019-02-25 17:31:21 +03:00
std : : string account_base : : get_public_address_str ( ) const
2018-12-27 18:50:45 +03:00
{
//TODO: change this code into base 58
2020-04-22 23:30:03 +03:00
return get_account_address_as_str ( m_keys . account_address ) ;
2018-12-27 18:50:45 +03:00
}
//-----------------------------------------------------------------
2019-04-08 14:16:11 +03:00
void account_base : : make_account_watch_only ( )
{
2019-12-26 18:08:57 +03:00
// keep only:
// timestamp
2020-05-27 17:20:25 +03:00
// view pub & spend pub + flags (public address)
2019-12-26 18:08:57 +03:00
// view sec
// store to local tmp
uint64_t local_ts = m_creation_timestamp ;
2020-04-22 23:30:03 +03:00
account_public_address local_addr = m_keys . account_address ;
crypto : : secret_key local_view_sec = m_keys . view_secret_key ;
2019-12-26 18:08:57 +03:00
// clear
set_null ( ) ;
// restore
m_creation_timestamp = local_ts ;
2020-04-22 23:30:03 +03:00
m_keys . account_address = local_addr ;
m_keys . view_secret_key = local_view_sec ;
2019-04-08 14:16:11 +03:00
}
//-----------------------------------------------------------------
2018-12-27 18:50:45 +03:00
std : : string transform_addr_to_str ( const account_public_address & addr )
{
return get_account_address_as_str ( addr ) ;
}
2019-04-08 14:16:11 +03:00
//-----------------------------------------------------------------
2018-12-27 18:50:45 +03:00
account_public_address transform_str_to_addr ( const std : : string & str )
{
account_public_address ad = AUTO_VAL_INIT ( ad ) ;
2020-02-18 15:50:24 +03:00
if ( ! get_account_address_from_str ( ad , str ) )
{
LOG_ERROR ( " cannot parse address from string: " < < str ) ;
}
2018-12-27 18:50:45 +03:00
return ad ;
}
}