diff options
author | moneromooo-monero <moneromooo-monero@users.noreply.github.com> | 2018-07-07 00:03:15 +0100 |
---|---|---|
committer | moneromooo-monero <moneromooo-monero@users.noreply.github.com> | 2018-08-16 09:17:52 +0000 |
commit | ea37614efe518ff8f363ddf2465301687e04d977 (patch) | |
tree | 17a975260d2943c18f3a19c51bb6bc88dd26b98c /src/mnemonics/electrum-words.cpp | |
parent | Merge pull request #4191 (diff) | |
download | monero-ea37614efe518ff8f363ddf2465301687e04d977.tar.xz |
wallet: wipe seed from memory where appropriate
Diffstat (limited to 'src/mnemonics/electrum-words.cpp')
-rw-r--r-- | src/mnemonics/electrum-words.cpp | 120 |
1 files changed, 62 insertions, 58 deletions
diff --git a/src/mnemonics/electrum-words.cpp b/src/mnemonics/electrum-words.cpp index 19a9c26bb..290f2cb93 100644 --- a/src/mnemonics/electrum-words.cpp +++ b/src/mnemonics/electrum-words.cpp @@ -43,6 +43,8 @@ #include <vector> #include <unordered_map> #include <boost/algorithm/string.hpp> +#include "wipeable_string.h" +#include "misc_language.h" #include "crypto/crypto.h" // for declaration of crypto::secret_key #include <fstream> #include "mnemonics/electrum-words.h" @@ -80,9 +82,9 @@ namespace crypto namespace { - uint32_t create_checksum_index(const std::vector<std::string> &word_list, + uint32_t create_checksum_index(const std::vector<epee::wipeable_string> &word_list, uint32_t unique_prefix_length); - bool checksum_test(std::vector<std::string> seed, uint32_t unique_prefix_length); + bool checksum_test(std::vector<epee::wipeable_string> seed, uint32_t unique_prefix_length); /*! * \brief Finds the word list that contains the seed words and puts the indices @@ -93,7 +95,7 @@ namespace * \param language Language instance pointer to write to after it is found. * \return true if all the words were present in some language false if not. */ - bool find_seed_language(const std::vector<std::string> &seed, + bool find_seed_language(const std::vector<epee::wipeable_string> &seed, bool has_checksum, std::vector<uint32_t> &matched_indices, Language::Base **language) { // If there's a new language added, add an instance of it here. @@ -114,17 +116,19 @@ namespace }); Language::Base *fallback = NULL; + std::vector<epee::wipeable_string>::const_iterator it2; + matched_indices.reserve(seed.size()); + // Iterate through all the languages and find a match for (std::vector<Language::Base*>::iterator it1 = language_instances.begin(); it1 != language_instances.end(); it1++) { - const std::unordered_map<std::string, uint32_t> &word_map = (*it1)->get_word_map(); - const std::unordered_map<std::string, uint32_t> &trimmed_word_map = (*it1)->get_trimmed_word_map(); + const std::unordered_map<epee::wipeable_string, uint32_t> &word_map = (*it1)->get_word_map(); + const std::unordered_map<epee::wipeable_string, uint32_t> &trimmed_word_map = (*it1)->get_trimmed_word_map(); // To iterate through seed words - std::vector<std::string>::const_iterator it2; bool full_match = true; - std::string trimmed_word; + epee::wipeable_string trimmed_word; // Iterate through all the words and see if they're all present for (it2 = seed.begin(); it2 != seed.end(); it2++) { @@ -167,6 +171,7 @@ namespace return true; } // Some didn't match. Clear the index array. + memwipe(matched_indices.data(), matched_indices.size() * sizeof(matched_indices[0])); matched_indices.clear(); } @@ -181,6 +186,7 @@ namespace } MINFO("No match found"); + memwipe(matched_indices.data(), matched_indices.size() * sizeof(matched_indices[0])); return false; } @@ -190,12 +196,12 @@ namespace * \param unique_prefix_length the prefix length of each word to use for checksum * \return Checksum index */ - uint32_t create_checksum_index(const std::vector<std::string> &word_list, + uint32_t create_checksum_index(const std::vector<epee::wipeable_string> &word_list, uint32_t unique_prefix_length) { - std::string trimmed_words = ""; + epee::wipeable_string trimmed_words = ""; - for (std::vector<std::string>::const_iterator it = word_list.begin(); it != word_list.end(); it++) + for (std::vector<epee::wipeable_string>::const_iterator it = word_list.begin(); it != word_list.end(); it++) { if (it->length() > unique_prefix_length) { @@ -217,22 +223,22 @@ namespace * \param unique_prefix_length the prefix length of each word to use for checksum * \return True if the test passed false if not. */ - bool checksum_test(std::vector<std::string> seed, uint32_t unique_prefix_length) + bool checksum_test(std::vector<epee::wipeable_string> seed, uint32_t unique_prefix_length) { if (seed.empty()) return false; // The last word is the checksum. - std::string last_word = seed.back(); + epee::wipeable_string last_word = seed.back(); seed.pop_back(); - std::string checksum = seed[create_checksum_index(seed, unique_prefix_length)]; + epee::wipeable_string checksum = seed[create_checksum_index(seed, unique_prefix_length)]; - std::string trimmed_checksum = checksum.length() > unique_prefix_length ? Language::utf8prefix(checksum, unique_prefix_length) : + epee::wipeable_string trimmed_checksum = checksum.length() > unique_prefix_length ? Language::utf8prefix(checksum, unique_prefix_length) : checksum; - std::string trimmed_last_word = last_word.length() > unique_prefix_length ? Language::utf8prefix(last_word, unique_prefix_length) : + epee::wipeable_string trimmed_last_word = last_word.length() > unique_prefix_length ? Language::utf8prefix(last_word, unique_prefix_length) : last_word; bool ret = trimmed_checksum == trimmed_last_word; - MINFO("Checksum is %s" << (ret ? "valid" : "invalid")); + MINFO("Checksum is " << (ret ? "valid" : "invalid")); return ret; } } @@ -260,13 +266,12 @@ namespace crypto * \param language_name Language of the seed as found gets written here. * \return false if not a multiple of 3 words, or if word is not in the words list */ - bool words_to_bytes(std::string words, std::string& dst, size_t len, bool duplicate, + bool words_to_bytes(const epee::wipeable_string &words, epee::wipeable_string& dst, size_t len, bool duplicate, std::string &language_name) { - std::vector<std::string> seed; + std::vector<epee::wipeable_string> seed; - boost::algorithm::trim(words); - boost::split(seed, words, boost::is_any_of(" "), boost::token_compress_on); + words.split(seed); if (len % 4) { @@ -291,6 +296,7 @@ namespace crypto } std::vector<uint32_t> matched_indices; + auto wiper = epee::misc_utils::create_scope_leave_handler([&](){memwipe(matched_indices.data(), matched_indices.size() * sizeof(matched_indices[0]));}); Language::Base *language; if (!find_seed_language(seed, has_checksum, matched_indices, &language)) { @@ -313,33 +319,33 @@ namespace crypto for (unsigned int i=0; i < seed.size() / 3; i++) { - uint32_t val; - uint32_t w1, w2, w3; - w1 = matched_indices[i*3]; - w2 = matched_indices[i*3 + 1]; - w3 = matched_indices[i*3 + 2]; + uint32_t w[4]; + w[1] = matched_indices[i*3]; + w[2] = matched_indices[i*3 + 1]; + w[3] = matched_indices[i*3 + 2]; - val = w1 + word_list_length * (((word_list_length - w1) + w2) % word_list_length) + - word_list_length * word_list_length * (((word_list_length - w2) + w3) % word_list_length); + w[0]= w[1] + word_list_length * (((word_list_length - w[1]) + w[2]) % word_list_length) + + word_list_length * word_list_length * (((word_list_length - w[2]) + w[3]) % word_list_length); - if (!(val % word_list_length == w1)) + if (!(w[0]% word_list_length == w[1])) { + memwipe(w, sizeof(w)); MERROR("Invalid seed: mumble mumble"); return false; } - dst.append((const char*)&val, 4); // copy 4 bytes to position + dst.append((const char*)&w[0], 4); // copy 4 bytes to position + memwipe(w, sizeof(w)); } if (len > 0 && duplicate) { const size_t expected = len * 3 / 32; - std::string wlist_copy = words; if (seed.size() == expected/2) { - dst.append(dst); // if electrum 12-word seed, duplicate - wlist_copy += ' '; - wlist_copy += words; + dst += ' '; // if electrum 12-word seed, duplicate + dst += dst; // if electrum 12-word seed, duplicate + dst.pop_back(); // trailing space } } @@ -353,10 +359,10 @@ namespace crypto * \param language_name Language of the seed as found gets written here. * \return false if not a multiple of 3 words, or if word is not in the words list */ - bool words_to_bytes(std::string words, crypto::secret_key& dst, + bool words_to_bytes(const epee::wipeable_string &words, crypto::secret_key& dst, std::string &language_name) { - std::string s; + epee::wipeable_string s; if (!words_to_bytes(words, s, sizeof(dst), true, language_name)) { MERROR("Invalid seed: failed to convert words to bytes"); @@ -378,7 +384,7 @@ namespace crypto * \param language_name Seed language name * \return true if successful false if not. Unsuccessful if wrong key size. */ - bool bytes_to_words(const char *src, size_t len, std::string& words, + bool bytes_to_words(const char *src, size_t len, epee::wipeable_string& words, const std::string &language_name) { @@ -397,39 +403,38 @@ namespace crypto } const std::vector<std::string> &word_list = language->get_word_list(); // To store the words for random access to add the checksum word later. - std::vector<std::string> words_store; + std::vector<epee::wipeable_string> words_store; uint32_t word_list_length = word_list.size(); // 4 bytes -> 3 words. 8 digits base 16 -> 3 digits base 1626 - for (unsigned int i=0; i < len/4; i++, words += ' ') + for (unsigned int i=0; i < len/4; i++, words.push_back(' ')) { - uint32_t w1, w2, w3; - - uint32_t val; + uint32_t w[4]; - memcpy(&val, src + (i * 4), 4); + memcpy(&w[0], src + (i * 4), 4); - w1 = val % word_list_length; - w2 = ((val / word_list_length) + w1) % word_list_length; - w3 = (((val / word_list_length) / word_list_length) + w2) % word_list_length; + w[1] = w[0] % word_list_length; + w[2] = ((w[0] / word_list_length) + w[1]) % word_list_length; + w[3] = (((w[0] / word_list_length) / word_list_length) + w[2]) % word_list_length; - words += word_list[w1]; + words += word_list[w[1]]; words += ' '; - words += word_list[w2]; + words += word_list[w[2]]; words += ' '; - words += word_list[w3]; + words += word_list[w[3]]; + + words_store.push_back(word_list[w[1]]); + words_store.push_back(word_list[w[2]]); + words_store.push_back(word_list[w[3]]); - words_store.push_back(word_list[w1]); - words_store.push_back(word_list[w2]); - words_store.push_back(word_list[w3]); + memwipe(w, sizeof(w)); } - words.pop_back(); - words += (' ' + words_store[create_checksum_index(words_store, language->get_unique_prefix_length())]); + words += words_store[create_checksum_index(words_store, language->get_unique_prefix_length())]; return true; } - bool bytes_to_words(const crypto::secret_key& src, std::string& words, + bool bytes_to_words(const crypto::secret_key& src, epee::wipeable_string& words, const std::string &language_name) { return bytes_to_words(src.data, sizeof(src), words, language_name); @@ -473,11 +478,10 @@ namespace crypto * \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(std::string seed) + bool get_is_old_style_seed(const epee::wipeable_string &seed) { - std::vector<std::string> word_list; - boost::algorithm::trim(seed); - boost::split(word_list, seed, boost::is_any_of(" "), boost::token_compress_on); + std::vector<epee::wipeable_string> word_list; + seed.split(word_list); return word_list.size() != (seed_length + 1); } |