diff --git a/src/currency_core/account.cpp b/src/currency_core/account.cpp index ada25025..59934dd4 100644 --- a/src/currency_core/account.cpp +++ b/src/currency_core/account.cpp @@ -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 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 = m_keys_seed_binary; + 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(&h) = creation_timestamp_rounded; h = crypto::cn_fast_hash(&h, sizeof h); uint64_t h_64 = *reinterpret_cast(&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 words; @@ -138,9 +162,20 @@ namespace currency auditable_flag_and_checksum = tools::mnemonic_encoding::num_by_word(auditable_flag_and_checksum_word); std::vector 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 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(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 = keys_seed_processed_binary; + 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(&h) = m_creation_timestamp; h = crypto::cn_fast_hash(&h, sizeof h); uint64_t h_64 = *reinterpret_cast(&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; diff --git a/src/currency_core/account.h b/src/currency_core/account.h index 0d9abbdd..a99c84c1 100644 --- a/src/currency_core/account.h +++ b/src/currency_core/account.h @@ -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; } diff --git a/src/currency_core/currency_config.h b/src/currency_core/currency_config.h index 4e06493b..c6b106b4 100644 --- a/src/currency_core/currency_config.h +++ b/src/currency_core/currency_config.h @@ -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 diff --git a/src/currency_core/currency_format_utils.cpp b/src/currency_core/currency_format_utils.cpp index 740c7afc..622d40fd 100644 --- a/src/currency_core/currency_format_utils.cpp +++ b/src/currency_core/currency_format_utils.cpp @@ -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::max(), "internal error: unable to convert to uint32, val = " << weeks_count); uint32_t weeks_count_32 = static_cast(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; } //--------------------------------------------------------------- diff --git a/src/currency_core/currency_format_utils.h b/src/currency_core/currency_format_utils.h index b53931e3..22642fb8 100644 --- a/src/currency_core/currency_format_utils.h +++ b/src/currency_core/currency_format_utils.h @@ -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& 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 typename std::conditional::value, const std::vector, std::vector >::type& get_txin_etc_options(t_txin_v& in)