diff --git a/src/mnemonics/electrum-words.cpp b/src/mnemonics/electrum-words.cpp index 718bbfd9..d0d90ed8 100644 --- a/src/mnemonics/electrum-words.cpp +++ b/src/mnemonics/electrum-words.cpp @@ -47,21 +47,25 @@ #include "mnemonics/electrum-words.h" #include #include +#include +#include namespace { - int num_words = 0; + int num_words = 0; + const int seed_length = 24; + std::map words_map; std::vector words_array; - bool is_old_style_mnemonics = false; + bool is_old_style_word_list = false; const std::string WORD_LISTS_DIRECTORY = "wordlists"; const std::string LANGUAGES_DIRECTORY = "languages"; const std::string OLD_WORD_FILE = "old-word-list"; /*! - * Tells if the module hasn't been initialized with a word list file. + * \brief Tells if the module hasn't been initialized with a word list file. * \return Whether the module hasn't been initialized with a word list file. */ bool is_uninitialized() @@ -70,7 +74,7 @@ namespace } /*! - * Create word list map and array data structres for use during inter-conversion between + * \brief Create word list map and array data structres for use during inter-conversion between * words and secret key. * \param word_file Path to the word list file from pwd. */ @@ -96,7 +100,7 @@ namespace } /*! - * Tells if all the words passed in wlist was present in current word list file. + * \brief Tells if all the words passed in wlist was present in current word list file. * \param wlist List of words to match. * \return Whether they were all present or not. */ @@ -111,17 +115,31 @@ namespace } return true; } + + /*! + * \brief Creates a checksum index in the word list array on the list of words. + * \param words Space separated list of words + * \return Checksum index + */ + uint32_t create_checksum_index(const std::string &words) + { + boost::crc_32_type result; + result.process_bytes(words.data(), words.length()); + return result.checksum() % seed_length; + } } /*! * \namespace crypto + * + * \brief crypto namespace. */ namespace crypto { /*! - * \namespace ElectrumWords + * \namespace crypto::ElectrumWords * - * \brief Mnemonic seed word generation and wallet restoration. + * \brief Mnemonic seed word generation and wallet restoration helper functions. */ namespace ElectrumWords { @@ -136,12 +154,12 @@ namespace crypto { // Use the old word list file if told to. create_data_structures(WORD_LISTS_DIRECTORY + '/' + OLD_WORD_FILE); - is_old_style_mnemonics = true; + is_old_style_word_list = true; } else { create_data_structures(WORD_LISTS_DIRECTORY + '/' + LANGUAGES_DIRECTORY + '/' + language); - is_old_style_mnemonics = false; + is_old_style_word_list = false; } if (num_words == 0) { @@ -150,19 +168,6 @@ namespace crypto } } - /*! - * \brief If the module is currenly using an old style word list. - * \return Whether it is currenly using an old style word list. - */ - bool get_is_old_style_mnemonics() - { - if (is_uninitialized()) - { - throw std::runtime_error("ElectrumWords hasn't been initialized with a word list yet."); - } - return is_old_style_mnemonics; - } - /*! * \brief Converts seed words to bytes (secret key). * \param words String containing the words separated by spaces. @@ -175,10 +180,25 @@ namespace crypto boost::split(wlist, words, boost::is_any_of(" ")); + // If it is seed with a checksum. + if (wlist.size() == seed_length + 1) + { + // The last word is the checksum. + std::string last_word = wlist.back(); + wlist.pop_back(); + + std::string checksum = wlist[create_checksum_index(boost::algorithm::join(wlist, " "))]; + + if (checksum != last_word) + { + // Checksum fail + return false; + } + } + // Try to find a word list file that contains all the words in the word list. std::vector languages; get_language_list(languages); - // Try to find a word list file that contains all the words in the word list. std::vector::iterator it; for (it = languages.begin(); it != languages.end(); it++) { @@ -232,8 +252,8 @@ namespace crypto /*! * \brief Converts bytes (secret key) to seed words. * \param src Secret key - * \param words Space separated words get copied here. - * \return Whether it was successful or not. Unsuccessful if wrong key size. + * \param words Space delimited concatenated words get written here. + * \return true if successful false if not. Unsuccessful if wrong key size. */ bool bytes_to_words(const crypto::secret_key& src, std::string& words) { @@ -241,9 +261,12 @@ namespace crypto { init("", true); } + + // To store the words for random access to add the checksum word later. + std::vector words_store; int n = num_words; - if (sizeof(src.data) % 4 != 0) return false; + if (sizeof(src.data) % 4 != 0 || sizeof(src.data) == 0) return false; // 8 bytes -> 3 words. 8 digits base 16 -> 3 digits base 1626 for (unsigned int i=0; i < sizeof(src.data)/4; i++, words += ' ') @@ -263,13 +286,20 @@ namespace crypto words += words_array[w2]; words += ' '; words += words_array[w3]; + + words_store.push_back(words_array[w1]); + words_store.push_back(words_array[w2]); + words_store.push_back(words_array[w3]); } + + words.pop_back(); + words += (' ' + words_store[create_checksum_index(words)]); return false; } /*! * \brief Gets a list of seed languages that are supported. - * \param languages The list gets added to this. + * \param languages The list of languages gets added to this vector. */ void get_language_list(std::vector &languages) { @@ -287,6 +317,31 @@ namespace crypto } } + /*! + * \brief Tells if the module is currenly using an old style word list. + * \return true if it is currenly using an old style word list false if not. + */ + bool get_is_old_style_word_list() + { + if (is_uninitialized()) + { + throw std::runtime_error("ElectrumWords hasn't been initialized with a word list yet."); + } + return is_old_style_word_list; + } + + /*! + * \brief Tells if the seed passed is an old style seed or not. + * \param seed The seed to check (a space delimited concatenated word list) + * \return true if the seed passed is a old style seed false if not. + */ + bool get_is_old_style_seed(const std::string &seed) + { + std::vector wlist; + boost::split(wlist, seed, boost::is_any_of(" ")); + return wlist.size() != (seed_length + 1); + } + } } diff --git a/src/mnemonics/electrum-words.h b/src/mnemonics/electrum-words.h index 687ac76c..ffe2fdef 100644 --- a/src/mnemonics/electrum-words.h +++ b/src/mnemonics/electrum-words.h @@ -43,18 +43,20 @@ /*! * \namespace crypto + * + * \brief crypto namespace. */ namespace crypto { /*! - * \namespace ElectrumWords + * \namespace crypto::ElectrumWords * - * \brief Mnemonic seed word generation and wallet restoration. + * \brief Mnemonic seed word generation and wallet restoration helper functions. */ namespace ElectrumWords { /*! - * \brief Called to initialize it work with a word list file. + * \brief Called to initialize it to work with a word list file. * \param language Language of the word list file. * \param old_word_list Whether it is to use the old style word list file. */ @@ -71,21 +73,28 @@ namespace crypto /*! * \brief Converts bytes (secret key) to seed words. * \param src Secret key - * \param words Space separated words get copied here. - * \return Whether it was successful or not. Unsuccessful if wrong key size. + * \param words Space delimited concatenated words get written here. + * \return true if successful false if not. Unsuccessful if wrong key size. */ bool bytes_to_words(const crypto::secret_key& src, std::string& words); /*! * \brief Gets a list of seed languages that are supported. - * \param languages The list gets added to this. + * \param languages The list of languages gets added to this vector. */ void get_language_list(std::vector &languages); /*! * \brief If the module is currenly using an old style word list. - * \return Whether it is currenly using an old style word list. + * \return true if it is currenly using an old style word list false if not. */ - bool get_is_old_style_mnemonics(); + bool get_is_old_style_word_list(); + + /*! + * \brief Tells if the seed passed is an old style seed or not. + * \param seed The seed to check (a space delimited concatenated word list) + * \return true if the seed passed is a old style seed false if not. + */ + bool get_is_old_style_seed(const std::string &seed); } } diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 9d6f23e6..ee2b1346 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -511,15 +511,19 @@ bool simple_wallet::new_wallet(const string &wallet_file, const std::string& pas // convert rng value to electrum-style word list std::string electrum_words; - // Ask for language if it is not a wallet restore or if the old version of the wallet - // had given the user an old style word list. - if (!m_restore_deterministic_wallet || crypto::ElectrumWords::get_is_old_style_mnemonics()) + bool is_deprecated_wallet = m_restore_deterministic_wallet && + (crypto::ElectrumWords::get_is_old_style_word_list() || + crypto::ElectrumWords::get_is_old_style_seed(m_electrum_seed)); + + // Ask for seed language if it is not a wallet restore or if it was a deprecated wallet + // that was earlier used before this restore. + if (!m_restore_deterministic_wallet || is_deprecated_wallet) { - if (crypto::ElectrumWords::get_is_old_style_mnemonics()) + if (is_deprecated_wallet) { // The user had used an older version of the wallet with old style mnemonics. - message_writer(epee::log_space::console_color_green, false) << "\nYou have been using " << - "a deprecated word list file. Please use the new seed that we provide.\n"; + message_writer(epee::log_space::console_color_green, false) << "\nYou had been using " << + "a deprecated version of the wallet. Please use the new seed that we provide.\n"; } std::string mnemonic_language = get_mnemonic_language(); try