1
0
Fork 0
forked from lthn/blockchain

Merge branch 'secure_seed' into develop

This commit is contained in:
cryptozoidberg 2020-11-25 18:48:13 +01:00
commit 7faef885dd
No known key found for this signature in database
GPG key ID: 22DEB97A54C6FDEC
19 changed files with 286 additions and 85 deletions

View file

@ -60,18 +60,42 @@ namespace currency
return m_keys;
}
//-----------------------------------------------------------------
std::string account_base::get_seed_phrase() const
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
{
if (m_keys_seed_binary.empty())
return "";
std::string keys_seed_text = tools::mnemonic_encoding::binary2text(m_keys_seed_binary);
std::string timestamp_word = currency::get_word_from_timstamp(m_creation_timestamp);
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());
// floor creation time to WALLET_BRAIN_DATE_QUANTUM to make checksum calculation stable
uint64_t creation_timestamp_rounded = get_timstamp_from_word(timestamp_word);
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");
constexpr uint16_t checksum_max = tools::mnemonic_encoding::NUMWORDS >> 1; // maximum value of checksum
crypto::hash h = crypto::cn_fast_hash(m_keys_seed_binary.data(), m_keys_seed_binary.size());
std::string binary_for_check_sum((const char*)&m_keys_seed_binary[0], m_keys_seed_binary.size());
binary_for_check_sum.append(password);
crypto::hash h = crypto::cn_fast_hash(binary_for_check_sum.data(), binary_for_check_sum.size());
*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);
@ -104,7 +128,7 @@ namespace currency
return true;
}
//-----------------------------------------------------------------
bool account_base::restore_from_seed_phrase(const std::string& seed_phrase)
bool account_base::restore_from_seed_phrase(const std::string& seed_phrase, const std::string& seed_password)
{
//cut the last timestamp word from restore_dats
std::list<std::string> words;
@ -138,9 +162,20 @@ namespace currency
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);
CHECK_AND_ASSERT_MES(keys_seed_binary.size(), false, "text2binary failed to convert the given text"); // don't prints event incorrect seed into the log for security
std::vector<unsigned char> keys_seed_processed_binary = keys_seed_binary;
m_creation_timestamp = get_timstamp_from_word(timestamp_word);
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);
}
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
bool auditable_flag = false;
@ -150,7 +185,9 @@ namespace currency
auditable_flag = (auditable_flag_and_checksum & 1) != 0; // auditable flag is the lower 1 bit
uint16_t checksum = static_cast<uint16_t>(auditable_flag_and_checksum >> 1); // checksum -- everything else
constexpr uint16_t checksum_max = tools::mnemonic_encoding::NUMWORDS >> 1; // maximum value of checksum
crypto::hash h = crypto::cn_fast_hash(keys_seed_binary.data(), keys_seed_binary.size());
std::string binary_for_check_sum((const char*)&keys_seed_processed_binary[0], keys_seed_processed_binary.size());
binary_for_check_sum.append(seed_password);
crypto::hash h = crypto::cn_fast_hash(binary_for_check_sum.data(), binary_for_check_sum.size());
*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);
@ -158,10 +195,10 @@ namespace currency
CHECK_AND_ASSERT_MES(checksum == checksum_calculated, false, "seed phase has invalid checksum: " << checksum_calculated << ", while " << checksum << " is expected, check your words");
}
bool r = restore_keys(keys_seed_binary);
bool r = restore_keys(keys_seed_processed_binary);
CHECK_AND_ASSERT_MES(r, false, "restore_keys failed");
m_keys_seed_binary = keys_seed_binary;
m_keys_seed_binary = keys_seed_processed_binary;
if (auditable_flag)
m_keys.account_address.flags |= ACCOUNT_PUBLIC_ADDRESS_FLAG_AUDITABLE;
@ -169,6 +206,34 @@ namespace currency
return true;
}
//-----------------------------------------------------------------
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;
}
//-----------------------------------------------------------------
bool account_base::restore_from_tracking_seed(const std::string& tracking_seed)
{
set_null();

View file

@ -53,9 +53,9 @@ namespace currency
const account_public_address& get_public_address() const { return m_keys.account_address; };
std::string get_public_address_str() const;
std::string get_seed_phrase() const;
std::string get_seed_phrase(const std::string& seed_password) const;
std::string get_tracking_seed() const;
bool restore_from_seed_phrase(const std::string& seed_phrase);
bool restore_from_seed_phrase(const std::string& seed_phrase, const std::string& seed_password);
bool restore_from_tracking_seed(const std::string& tracking_seed);
uint64_t get_createtime() const { return m_creation_timestamp; }
@ -78,6 +78,7 @@ namespace currency
static std::string vector_of_chars_to_string(const std::vector<unsigned char>& v) { return std::string(v.begin(), v.end()); }
static std::vector<unsigned char> string_to_vector_of_chars(const std::string& v) { return std::vector<unsigned char>(v.begin(), v.end()); }
static bool is_seed_password_protected(const std::string& seed_phrase, bool& is_password_protected);
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(m_keys)

View file

@ -150,9 +150,10 @@
#define WALLET_FILE_MAX_KEYS_SIZE 10000 //
#define WALLET_BRAIN_DATE_OFFSET 1543622400
#define WALLET_BRAIN_DATE_QUANTUM 604800 //by last word we encode a number of week since launch of the project,
#define WALLET_BRAIN_DATE_QUANTUM 604800 //by last word we encode a number of week since launch of the project AND password flag,
//which let us to address tools::mnemonic_encoding::NUMWORDS weeks after project launch
//which is about 30 years
//which is about 15 years
#define WALLET_BRAIN_DATE_MAX_WEEKS_COUNT 800
#define OFFER_MAXIMUM_LIFE_TIME (60*60*24*30) // 30 days

View file

@ -1316,21 +1316,34 @@ namespace currency
return reward;
}
//---------------------------------------------------------------
std::string get_word_from_timstamp(uint64_t timestamp)
std::string get_word_from_timstamp(uint64_t timestamp, bool use_password)
{
uint64_t date_offset = timestamp > WALLET_BRAIN_DATE_OFFSET ? timestamp - WALLET_BRAIN_DATE_OFFSET : 0;
uint64_t weeks_count = date_offset / WALLET_BRAIN_DATE_QUANTUM;
CHECK_AND_ASSERT_THROW_MES(weeks_count < WALLET_BRAIN_DATE_MAX_WEEKS_COUNT, "SEED PHRASE need to be extended or refactored");
if (use_password)
weeks_count += WALLET_BRAIN_DATE_MAX_WEEKS_COUNT;
CHECK_AND_ASSERT_THROW_MES(weeks_count < std::numeric_limits<uint32_t>::max(), "internal error: unable to convert to uint32, val = " << weeks_count);
uint32_t weeks_count_32 = static_cast<uint32_t>(weeks_count);
return tools::mnemonic_encoding::word_by_num(weeks_count_32);
}
//---------------------------------------------------------------
uint64_t get_timstamp_from_word(std::string word)
uint64_t get_timstamp_from_word(std::string word, bool& password_used)
{
uint64_t count_of_weeks = tools::mnemonic_encoding::num_by_word(word);
if (count_of_weeks >= WALLET_BRAIN_DATE_MAX_WEEKS_COUNT)
{
count_of_weeks -= WALLET_BRAIN_DATE_MAX_WEEKS_COUNT;
password_used = true;
}
else {
password_used = false;
}
uint64_t timestamp = count_of_weeks * WALLET_BRAIN_DATE_QUANTUM + WALLET_BRAIN_DATE_OFFSET;
return timestamp;
}
//---------------------------------------------------------------

View file

@ -291,8 +291,8 @@ namespace currency
bool fill_tx_rpc_details(tx_rpc_extended_info& tei, const transaction& tx, const transaction_chain_entry* ptce, const crypto::hash& h, uint64_t timestamp, bool is_short = false);
bool fill_block_rpc_details(block_rpc_extended_info& pei_rpc, const block_extended_info& bei_chain, const crypto::hash& h);
void append_per_block_increments_for_tx(const transaction& tx, std::unordered_map<uint64_t, uint32_t>& gindices);
std::string get_word_from_timstamp(uint64_t timestamp);
uint64_t get_timstamp_from_word(std::string word);
std::string get_word_from_timstamp(uint64_t timestamp, bool use_password);
uint64_t get_timstamp_from_word(std::string word, bool& password_used);
template<class t_txin_v>
typename std::conditional<std::is_const<t_txin_v>::value, const std::vector<txin_etc_details_v>, std::vector<txin_etc_details_v> >::type& get_txin_etc_options(t_txin_v& in)

View file

@ -1676,7 +1676,7 @@ QString MainWindow::restore_wallet(const QString& param)
//return que_call2<view::restore_wallet_request>("restore_wallet", param, [this](const view::restore_wallet_request& owd, view::api_response& ar){
PREPARE_ARG_FROM_JSON(view::restore_wallet_request, owd);
PREPARE_RESPONSE(view::open_wallet_response, ar);
ar.error_code = m_backend.restore_wallet(epee::string_encoding::utf8_to_wstring(owd.path), owd.pass, owd.restore_key, ar.response_data);
ar.error_code = m_backend.restore_wallet(epee::string_encoding::utf8_to_wstring(owd.path), owd.pass, owd.seed_phrase, owd.seed_pass, ar.response_data);
return MAKE_RESPONSE(ar);
CATCH_ENTRY_FAIL_API_RESPONCE();
}
@ -1877,9 +1877,9 @@ QString MainWindow::get_smart_wallet_info(const QString& param)
{
TRY_ENTRY();
LOG_API_TIMING();
PREPARE_ARG_FROM_JSON(view::wallet_id_obj, wo);
PREPARE_ARG_FROM_JSON(view::request_get_smart_wallet_info, wo);
PREPARE_RESPONSE(view::get_restore_info_response, ar);
ar.error_code = m_backend.get_wallet_restore_info(wo.wallet_id, ar.response_data.restore_key);
ar.error_code = m_backend.get_wallet_restore_info(wo.wallet_id, ar.response_data.seed_phrase, wo.seed_password);
return MAKE_RESPONSE(ar);
CATCH_ENTRY_FAIL_API_RESPONCE();
}
@ -1973,14 +1973,26 @@ QString MainWindow::open_url_in_browser(const QString& param)
CATCH_ENTRY2(API_RETURN_CODE_INTERNAL_ERROR);
}
QString MainWindow::is_valid_restore_wallet_text(const QString& param)
{
TRY_ENTRY();
LOG_API_TIMING();
return m_backend.is_valid_brain_restore_data(param.toStdString()).c_str();
PREPARE_ARG_FROM_JSON(view::is_valid_restore_wallet_text_param, rwtp);
return m_backend.is_valid_brain_restore_data(rwtp.seed_phrase, rwtp.seed_password).c_str();
CATCH_ENTRY2(API_RETURN_CODE_INTERNAL_ERROR);
}
QString MainWindow::get_seed_phrase_info(const QString& param)
{
TRY_ENTRY();
LOG_API_TIMING();
PREPARE_ARG_FROM_JSON(view::is_valid_restore_wallet_text_param, rwtp);
PREPARE_RESPONSE(view::seed_phrase_info, ar);
ar.error_code = m_backend.get_seed_phrase_info(rwtp.seed_phrase, rwtp.seed_password, ar.response_data).c_str();
return MAKE_RESPONSE(ar);
CATCH_ENTRY_FAIL_API_RESPONCE();
}
void MainWindow::contextMenuEvent(QContextMenuEvent * event)
{
TRY_ENTRY();

View file

@ -137,6 +137,7 @@ public:
QString is_autostart_enabled();
QString toggle_autostart(const QString& param);
QString is_valid_restore_wallet_text(const QString& param);
QString get_seed_phrase_info(const QString& param);
QString print_text(const QString& param);
QString print_log(const QString& param);
QString set_clipboard(const QString& param);

View file

@ -46,7 +46,6 @@ namespace
const command_line::arg_descriptor<std::string> arg_password = {"password", "Wallet password", "", true};
const command_line::arg_descriptor<bool> arg_dont_refresh = { "no-refresh", "Do not refresh after load", false, true };
const command_line::arg_descriptor<bool> arg_dont_set_date = { "no-set-creation-date", "Do not set wallet creation date", false, false };
const command_line::arg_descriptor<bool> arg_print_brain_wallet = { "print-brain-wallet", "Print to conosole brain wallet", false, false };
const command_line::arg_descriptor<int> arg_daemon_port = {"daemon-port", "Use daemon instance at port <arg> instead of default", 0};
const command_line::arg_descriptor<uint32_t> arg_log_level = {"set-log", "", 0, true};
const command_line::arg_descriptor<bool> arg_do_pos_mining = { "do-pos-mining", "Do PoS mining", false, false };
@ -180,7 +179,6 @@ bool simple_wallet::help(const std::vector<std::string> &args/* = std::vector<st
simple_wallet::simple_wallet()
: m_daemon_port(0),
m_print_brain_wallet(false),
m_do_refresh_after_load(false),
m_do_not_set_date(false),
m_do_pos_mining(false),
@ -309,10 +307,6 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
m_do_refresh_after_load = false;
}
if (command_line::has_arg(vm, arg_print_brain_wallet))
{
m_print_brain_wallet = true;
}
if (!m_generate_new.empty())
{
@ -338,9 +332,26 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
fail_msg_writer() << "failed to read seed phrase";
return false;
}
bool looks_like_tracking_seed = restore_seed_container.password().find(':') != std::string::npos;
bool r = restore_wallet(m_restore_wallet, restore_seed_container.password(), pwd_container.password(), looks_like_tracking_seed);
tools::password_container seed_password_container;
if (!looks_like_tracking_seed)
{
bool is_password_protected = false;
bool sr = account_base::is_seed_password_protected(restore_seed_container.password(), is_password_protected);
if (!sr)
{
fail_msg_writer() << "failed to parse seed phrase";
return false;
}
if (is_password_protected && !seed_password_container.read_password("This seed is secured, to use it please enter the password:\n"))
{
fail_msg_writer() << "failed to read seed phrase";
return false;
}
}
bool r = restore_wallet(m_restore_wallet, restore_seed_container.password(), pwd_container.password(), looks_like_tracking_seed, seed_password_container.password());
CHECK_AND_ASSERT_MES(r, false, "wallet restoring failed");
}
else
@ -402,10 +413,6 @@ bool simple_wallet::new_wallet(const string &wallet_file, const std::string& pas
if (m_do_not_set_date)
m_wallet->reset_creation_time(0);
if (m_print_brain_wallet)
{
std::cout << "Seed phrase (keep it in secret): " << m_wallet->get_account().get_seed_phrase() << std::endl << std::flush;
}
}
catch (const std::exception& e)
@ -424,7 +431,7 @@ bool simple_wallet::new_wallet(const string &wallet_file, const std::string& pas
return true;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::restore_wallet(const std::string& wallet_file, const std::string& seed_or_tracking_seed, const std::string& password, bool tracking_wallet)
bool simple_wallet::restore_wallet(const std::string& wallet_file, const std::string& seed_or_tracking_seed, const std::string& password, bool tracking_wallet, const std::string& seed_password)
{
m_wallet_file = wallet_file;
@ -436,13 +443,13 @@ bool simple_wallet::restore_wallet(const std::string& wallet_file, const std::st
if (tracking_wallet)
{
// auditable watch-only aka tracking wallet
m_wallet->restore(epee::string_encoding::utf8_to_wstring(wallet_file), password, seed_or_tracking_seed, true);
m_wallet->restore(epee::string_encoding::utf8_to_wstring(wallet_file), password, seed_or_tracking_seed, true, "");
message_writer(epee::log_space::console_color_white, true) << "Tracking wallet restored: " << m_wallet->get_account().get_public_address_str();
}
else
{
// normal or auditable wallet
m_wallet->restore(epee::string_encoding::utf8_to_wstring(wallet_file), password, seed_or_tracking_seed, false);
m_wallet->restore(epee::string_encoding::utf8_to_wstring(wallet_file), password, seed_or_tracking_seed, false, seed_password);
message_writer(epee::log_space::console_color_white, true) << (m_wallet->is_auditable() ? "Auditable wallet" : "Wallet") << " restored: " << m_wallet->get_account().get_public_address_str();
std::cout << "view key: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().view_secret_key) << std::endl << std::flush;
if (m_wallet->is_auditable())
@ -456,7 +463,11 @@ bool simple_wallet::restore_wallet(const std::string& wallet_file, const std::st
fail_msg_writer() << "failed to restore wallet, check your " << (tracking_wallet ? "tracking seed!" : "seed phrase!") << ENDL << e.what();
return false;
}
catch (...)
{
fail_msg_writer() << "failed to restore wallet, check your " << (tracking_wallet ? "tracking seed!" : "seed phrase!") << ENDL;
return false;
}
m_wallet->init(m_daemon_address);
success_msg_writer() <<
@ -484,9 +495,6 @@ bool simple_wallet::open_wallet(const string &wallet_file, const std::string& pa
m_wallet->load(epee::string_encoding::utf8_to_wstring(m_wallet_file), password);
message_writer(epee::log_space::console_color_white, true) << "Opened" << (m_wallet->is_auditable() ? " auditable" : "") << (m_wallet->is_watch_only() ? " watch-only" : "") << " wallet: " << m_wallet->get_account().get_public_address_str();
if (m_print_brain_wallet)
std::cout << "Seed phrase (keep it in secret): " << m_wallet->get_account().get_seed_phrase() << std::endl << std::flush;
break;
}
catch (const tools::error::wallet_load_notice_wallet_restored& /*e*/)
@ -1361,9 +1369,24 @@ bool simple_wallet::print_address(const std::vector<std::string> &args/* = std::
//----------------------------------------------------------------------------------------------------
bool simple_wallet::show_seed(const std::vector<std::string> &args)
{
success_msg_writer() << "Here's your wallet's seed phrase. Write it down and keep in a safe place.";
success_msg_writer(true) << "Anyone who knows the following 26 words can access your wallet:";
std::cout << m_wallet->get_account().get_seed_phrase() << std::endl << std::flush;
success_msg_writer() << "Please enter a password to secure this seed. Securing your seed is HIGHLY recommended. Leave password blank to stay unsecured.";
success_msg_writer(true) << "Remember, restoring a wallet from Secured Seed can only be done if you know its password.";
tools::password_container seed_password_container1;
if (!seed_password_container1.read_password("Enter seed password: "))
{
return false;
}
tools::password_container seed_password_container2;
if (!seed_password_container2.read_password("Confirm seed password: "))
{
return false;
}
if(seed_password_container1.password() != seed_password_container2.password())
{
std::cout << "Error: password mismatch. Please make sure you entered the correct password and confirmed it" << std::endl << std::flush;
}
std::cout << m_wallet->get_account().get_seed_phrase(seed_password_container2.password()) << std::endl << std::flush;
return true;
}
//----------------------------------------------------------------------------------------------------
@ -1868,7 +1891,6 @@ int main(int argc, char* argv[])
command_line::add_arg(desc_params, arg_log_level);
command_line::add_arg(desc_params, arg_dont_refresh);
command_line::add_arg(desc_params, arg_dont_set_date);
command_line::add_arg(desc_params, arg_print_brain_wallet);
command_line::add_arg(desc_params, arg_do_pos_mining);
command_line::add_arg(desc_params, arg_pos_mining_reward_address);
command_line::add_arg(desc_params, arg_restore_wallet);

View file

@ -46,7 +46,7 @@ namespace currency
bool new_wallet(const std::string &wallet_file, const std::string& password, bool create_auditable_wallet);
bool open_wallet(const std::string &wallet_file, const std::string& password);
bool restore_wallet(const std::string& wallet_file, const std::string& seed_or_tracking_seed, const std::string& password, bool tracking_wallet);
bool restore_wallet(const std::string& wallet_file, const std::string& seed_or_tracking_seed, const std::string& password, bool tracking_wallet, const std::string& seed_password);
bool close_wallet();
bool help(const std::vector<std::string> &args = std::vector<std::string>());
@ -163,7 +163,6 @@ namespace currency
int m_daemon_port;
bool m_do_refresh_after_load;
bool m_do_not_set_date;
bool m_print_brain_wallet;
bool m_do_pos_mining;
bool m_offline_mode;
std::string m_restore_wallet;

View file

@ -400,13 +400,13 @@ namespace plain_wallet
return epee::serialization::store_t_to_json(err_result);
}
std::string restore(const std::string& seed, const std::string& path, const std::string& password)
std::string restore(const std::string& seed, const std::string& path, const std::string& password, const std::string& seed_password)
{
GET_INSTANCE_PTR(inst_ptr);
std::string full_path = get_wallets_folder() + path;
epee::json_rpc::response<view::open_wallet_response, epee::json_rpc::dummy_error> ok_response = AUTO_VAL_INIT(ok_response);
std::string rsp = inst_ptr->gwm.restore_wallet(epee::string_encoding::convert_to_unicode(full_path), password, seed, ok_response.result);
std::string rsp = inst_ptr->gwm.restore_wallet(epee::string_encoding::convert_to_unicode(full_path), password, seed, seed_password, ok_response.result);
if (rsp == API_RETURN_CODE_OK || rsp == API_RETURN_CODE_FILE_RESTORED)
{
if (rsp == API_RETURN_CODE_FILE_RESTORED)
@ -526,7 +526,7 @@ namespace plain_wallet
}
async_callback = [job_id, rwr]()
{
std::string res = restore(rwr.restore_key, rwr.path, rwr.pass);
std::string res = restore(rwr.seed_phrase, rwr.path, rwr.pass, rwr.seed_pass);
put_result(job_id, res);
};
}

View file

@ -27,7 +27,7 @@ namespace plain_wallet
std::string get_connectivity_status();
std::string open(const std::string& path, const std::string& password);
std::string restore(const std::string& seed, const std::string& path, const std::string& password);
std::string restore(const std::string& seed, const std::string& path, const std::string& password, const std::string& seed_password);
std::string generate(const std::string& path, const std::string& password);
std::string get_opened_wallets();

View file

@ -272,6 +272,19 @@ public:
END_KV_SERIALIZE_MAP()
};
struct request_get_smart_wallet_info
{
uint64_t wallet_id;
std::string seed_password;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(wallet_id)
KV_SERIALIZE(seed_password)
END_KV_SERIALIZE_MAP()
};
struct response_mining_estimate
{
uint64_t final_amount;
@ -429,16 +442,43 @@ public:
END_KV_SERIALIZE_MAP()
};
struct is_valid_restore_wallet_text_param
{
std::string seed_phrase;
std::string seed_password;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(seed_phrase)
KV_SERIALIZE(seed_password)
END_KV_SERIALIZE_MAP()
};
struct seed_phrase_info
{
bool syntax_correct;
bool require_password;
bool hash_sum_matched;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(syntax_correct)
KV_SERIALIZE(require_password)
KV_SERIALIZE(hash_sum_matched)
END_KV_SERIALIZE_MAP()
};
struct restore_wallet_request
{
std::string pass;
std::string seed_pass;
std::string path;
std::string restore_key;
std::string seed_phrase;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(pass)
KV_SERIALIZE(path)
KV_SERIALIZE(restore_key)
KV_SERIALIZE(seed_pass)
KV_SERIALIZE(seed_phrase)
END_KV_SERIALIZE_MAP()
};
@ -587,12 +627,10 @@ public:
struct get_restore_info_response
{
std::string restore_key;
std::string error_code;
std::string seed_phrase;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(restore_key)
KV_SERIALIZE(error_code)
KV_SERIALIZE(seed_phrase)
END_KV_SERIALIZE_MAP()
};

View file

@ -2347,7 +2347,7 @@ void wallet2::generate(const std::wstring& path, const std::string& pass, bool a
store();
}
//----------------------------------------------------------------------------------------------------
void wallet2::restore(const std::wstring& path, const std::string& pass, const std::string& seed_or_tracking_seed, bool tracking_wallet)
void wallet2::restore(const std::wstring& path, const std::string& pass, const std::string& seed_or_tracking_seed, bool tracking_wallet, const std::string& seed_password)
{
bool r = false;
clear();
@ -2363,7 +2363,7 @@ void wallet2::restore(const std::wstring& path, const std::string& pass, const s
}
else
{
r = m_account.restore_from_seed_phrase(seed_or_tracking_seed);
r = m_account.restore_from_seed_phrase(seed_or_tracking_seed, seed_password);
init_log_prefix();
THROW_IF_FALSE_WALLET_EX(r, error::wallet_wrong_seed_error, epee::string_encoding::convert_to_ansii(m_wallet_file));
}

View file

@ -470,7 +470,7 @@ namespace tools
void assign_account(const currency::account_base& acc);
void generate(const std::wstring& path, const std::string& password, bool auditable_wallet);
void restore(const std::wstring& path, const std::string& pass, const std::string& seed_or_tracking_seed, bool tracking_wallet);
void restore(const std::wstring& path, const std::string& pass, const std::string& seed_or_tracking_seed, bool tracking_wallet, const std::string& seed_password);
void load(const std::wstring& path, const std::string& password);
void store();
void store(const std::wstring& path);

View file

@ -196,7 +196,7 @@ namespace tools
res.path = epee::string_encoding::convert_to_ansii(m_wallet.get_wallet_path());
res.transfers_count = m_wallet.get_recent_transfers_total_count();
res.transfer_entries_count = m_wallet.get_transfer_entries_count();
res.seed = m_wallet.get_account().get_seed_phrase();
//res.seed = m_wallet.get_account().get_seed_phrase();
std::map<uint64_t, uint64_t> distribution;
m_wallet.get_utxo_distribution(distribution);
for (const auto& ent : distribution)

View file

@ -911,7 +911,7 @@ std::string wallets_manager::open_wallet(const std::wstring& path, const std::st
w->get_unconfirmed_transfers(owr.recent_history.history, exclude_mining_txs);
owr.wallet_local_bc_size = w->get_blockchain_current_size();
//workaround for missed fee
owr.seed = w->get_account().get_seed_phrase();
//owr.seed = w->get_account().get_seed_phrase();
break;
}
catch (const tools::error::file_not_found& /**/)
@ -1015,7 +1015,7 @@ std::string wallets_manager::generate_wallet(const std::wstring& path, const std
{
w->generate(path, password, false);
w->set_minimum_height(m_last_daemon_height);
owr.seed = w->get_account().get_seed_phrase();
//owr.seed = w->get_account().get_seed_phrase();
}
catch (const tools::error::file_exists&)
{
@ -1058,10 +1058,10 @@ std::string wallets_manager::is_pos_allowed()
else
return API_RETURN_CODE_FALSE;
}
std::string wallets_manager::is_valid_brain_restore_data(const std::string& seed_phrase)
std::string wallets_manager::is_valid_brain_restore_data(const std::string& seed_phrase, const std::string& seed_password)
{
currency::account_base acc;
if (acc.restore_from_seed_phrase(seed_phrase))
if (acc.restore_from_seed_phrase(seed_phrase, seed_password))
return API_RETURN_CODE_TRUE;
currency::account_public_address addr;
@ -1081,11 +1081,25 @@ void wallets_manager::subscribe_to_core_events(currency::i_core_event_handler* p
}
#endif
std::string wallets_manager::get_seed_phrase_info(const std::string& seed_phrase, const std::string& seed_password, view::seed_phrase_info& result)
{
//cut the last timestamp word from restore_dats
result.syntax_correct = currency::account_base::is_seed_password_protected(seed_phrase, result.require_password);
if (result.syntax_correct)
{
currency::account_base acc;
result.hash_sum_matched = acc.restore_from_seed_phrase(seed_phrase, seed_password);
}
return API_RETURN_CODE_OK;
}
void wallets_manager::get_gui_options(view::gui_options& opt)
{
opt = m_ui_opt;
}
std::string wallets_manager::restore_wallet(const std::wstring& path, const std::string& password, const std::string& restore_key, view::open_wallet_response& owr)
std::string wallets_manager::restore_wallet(const std::wstring& path, const std::string& password, const std::string& seed_phrase, const std::string& seed_password, view::open_wallet_response& owr)
{
std::shared_ptr<tools::wallet2> w(new tools::wallet2());
w->set_use_deffered_global_outputs(m_use_deffered_global_outputs);
@ -1108,9 +1122,9 @@ std::string wallets_manager::restore_wallet(const std::wstring& path, const std:
currency::account_base acc;
try
{
bool auditable_watch_only = restore_key.find(':') != std::string::npos;
w->restore(path, password, restore_key, auditable_watch_only);
owr.seed = w->get_account().get_seed_phrase();
bool auditable_watch_only = seed_phrase.find(':') != std::string::npos;
w->restore(path, password, seed_phrase, auditable_watch_only, seed_password);
//owr.seed = w->get_account().get_seed_phrase();
}
catch (const tools::error::file_exists&)
{
@ -1625,14 +1639,14 @@ std::string wallets_manager::get_mining_history(uint64_t wallet_id, tools::walle
wo.w->get()->get_mining_history(mh);
return API_RETURN_CODE_OK;
}
std::string wallets_manager::get_wallet_restore_info(uint64_t wallet_id, std::string& restore_key)
std::string wallets_manager::get_wallet_restore_info(uint64_t wallet_id, std::string& seed_phrase, const std::string& seed_password)
{
GET_WALLET_OPT_BY_ID(wallet_id, wo);
if (wo.wallet_state != view::wallet_status_info::wallet_state_ready || wo.long_refresh_in_progress)
return API_RETURN_CODE_CORE_BUSY;
restore_key = wo.w->get()->get_account().get_seed_phrase();
seed_phrase = wo.w->get()->get_account().get_seed_phrase(seed_password);
return API_RETURN_CODE_OK;
}

View file

@ -99,7 +99,7 @@ public:
bool get_opened_wallets(std::list<view::open_wallet_response>& result);
std::string open_wallet(const std::wstring& path, const std::string& password, uint64_t txs_to_return, view::open_wallet_response& owr, bool exclude_mining_txs = false);
std::string generate_wallet(const std::wstring& path, const std::string& password, view::open_wallet_response& owr);
std::string restore_wallet(const std::wstring& path, const std::string& password, const std::string& restore_key, view::open_wallet_response& owr);
std::string restore_wallet(const std::wstring& path, const std::string& password, const std::string& seed_phrase, const std::string& seed_password, view::open_wallet_response& owr);
std::string invoke(uint64_t wallet_id, std::string params);
std::string get_wallet_status(uint64_t wallet_id);
std::string run_wallet(uint64_t wallet_id);
@ -132,7 +132,7 @@ public:
std::string stop_pos_mining(uint64_t wallet_id);
std::string check_available_sources(uint64_t wallet_id, std::list<uint64_t>& amounts);
std::string get_mining_history(uint64_t wallet_id, tools::wallet_public::mining_history& wrpc);
std::string get_wallet_restore_info(uint64_t wallet_id, std::string& restore_key);
std::string get_wallet_restore_info(uint64_t wallet_id, std::string& seed_phrase, const std::string& seed_password);
std::string backup_wallet(uint64_t wallet_id, const std::wstring& path);
std::string reset_wallet_password(uint64_t wallet_id, const std::string& pass);
std::string is_wallet_password_valid(uint64_t wallet_id, const std::string& pass);
@ -149,7 +149,8 @@ public:
void toggle_pos_mining();
std::string transfer(size_t wallet_id, const view::transfer_params& tp, currency::transaction& res_tx);
std::string get_config_folder();
std::string is_valid_brain_restore_data(const std::string& seed_phrase);
std::string is_valid_brain_restore_data(const std::string& seed_phrase, const std::string& seed_password);
std::string get_seed_phrase_info(const std::string& seed_phrase, const std::string& seed_password, view::seed_phrase_info& result);
#ifndef MOBILE_WALLET_BUILD
void subscribe_to_core_events(currency::i_core_event_handler* pevents_handler);
//void unsubscribe_to_core_events();

View file

@ -842,7 +842,7 @@ namespace db_test
ptr = db_array[4];
ASSERT_EQ(ptr->v, "X");
ASSERT_TRUE(db_array.clear());
ASSERT_TRUE((db_array.clear()? true: false));
db_array.commit_transaction();

View file

@ -13,10 +13,10 @@ TEST(wallet_seed, store_restore_test)
{
currency::account_base acc;
acc.generate();
auto seed_phrase = acc.get_seed_phrase();
auto seed_phrase = acc.get_seed_phrase("");
currency::account_base acc2;
bool r = acc2.restore_from_seed_phrase(seed_phrase);
bool r = acc2.restore_from_seed_phrase(seed_phrase, "");
ASSERT_TRUE(r);
if (memcmp(&acc2.get_keys(), &acc.get_keys(), sizeof(currency::account_keys)))
@ -29,10 +29,10 @@ TEST(wallet_seed, store_restore_test)
{
currency::account_base acc;
acc.generate();
auto seed_phrase = acc.get_seed_phrase();
auto seed_phrase = acc.get_seed_phrase("");
currency::account_base acc2;
bool r = acc2.restore_from_seed_phrase(seed_phrase);
bool r = acc2.restore_from_seed_phrase(seed_phrase, "");
ASSERT_TRUE(r);
if (memcmp(&acc2.get_keys(), &acc.get_keys(), sizeof(currency::account_keys)))
@ -57,7 +57,7 @@ wallet_seed_entry wallet_seed_entries[] =
{
{
// legacy 24-word seed phrase -- invalid
"dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew",
"dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew god",
"",
"",
0,
@ -66,7 +66,7 @@ wallet_seed_entry wallet_seed_entries[] =
},
{
// old-style 25-word seed phrase -- valid
"dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew",
"dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew dew god",
"5e051454d7226b5734ebd64f754b57db4c655ecda00bd324f1b241d0b6381c0f",
"7dde5590fdf430568c00556ac2accf09da6cde9a29a4bc7d1cb6fd267130f006",
0,
@ -148,7 +148,41 @@ TEST(wallet_seed, basic_test)
bool r = false;
try
{
r = acc.restore_from_seed_phrase(wse.seed_phrase);
r = acc.restore_from_seed_phrase(wse.seed_phrase, "");
if (r)
{
for (size_t j = 0; j != 100; j++)
{
//generate random password
std::string pass = epee::string_tools::pod_to_hex(crypto::cn_fast_hash(&j, sizeof(j)));
if (j!= 0 && j < 64)
{
pass.resize(j);
}
//get secured seed
std::string secured_seed = acc.get_seed_phrase(pass);
//try to restore it without password(should fail)
currency::account_base acc2;
bool r_fail = acc2.restore_from_seed_phrase(secured_seed, "");
ASSERT_EQ(r_fail, false);
//try to restore it with wrong password
bool r_fake_pass = acc2.restore_from_seed_phrase(secured_seed, "fake_password");
if (r_fake_pass)
{
//accidentally checksumm matched(quite possible)
ASSERT_EQ(false, acc2.get_keys() == acc.get_keys());
}
//try to restore it from right password
currency::account_base acc3;
bool r_true_res = acc3.restore_from_seed_phrase(secured_seed, pass);
ASSERT_EQ(true, r_true_res);
ASSERT_EQ(true, acc3.get_keys() == acc.get_keys());
}
}
}
catch (...)
{