43 #include <unordered_map> 48 #include <boost/crc.hpp> 66 #undef ELECTRONEUM_DEFAULT_LOG_CATEGORY 67 #define ELECTRONEUM_DEFAULT_LOG_CATEGORY "mnemonic" 71 namespace ElectrumWords
79 uint32_t create_checksum_index(
const std::vector<epee::wipeable_string> &word_list,
81 bool checksum_test(std::vector<epee::wipeable_string> seed,
const Language::Base *language);
92 bool find_seed_language(
const std::vector<epee::wipeable_string> &seed,
93 bool has_checksum, std::vector<uint32_t> &matched_indices,
Language::Base **language)
96 std::vector<Language::Base*> language_instances({
113 std::vector<epee::wipeable_string>::const_iterator it2;
114 matched_indices.reserve(seed.size());
117 for (std::vector<Language::Base*>::iterator it1 = language_instances.begin();
118 it1 != language_instances.end(); it1++)
120 const std::unordered_map<epee::wipeable_string, uint32_t, Language::WordHash, Language::WordEqual> &word_map = (*it1)->get_word_map();
121 const std::unordered_map<epee::wipeable_string, uint32_t, Language::WordHash, Language::WordEqual> &trimmed_word_map = (*it1)->get_trimmed_word_map();
123 bool full_match =
true;
127 for (it2 = seed.begin(); it2 != seed.end(); it2++)
133 if (trimmed_word_map.count(trimmed_word) == 0)
138 matched_indices.push_back(trimmed_word_map.at(trimmed_word));
142 if (word_map.count(*it2) == 0)
147 matched_indices.push_back(word_map.at(*it2));
155 if (!checksum_test(seed, *it1))
164 MINFO(
"Full match for language " << (*language)->get_english_language_name());
168 memwipe(matched_indices.data(), matched_indices.size() *
sizeof(matched_indices[0]));
169 matched_indices.clear();
177 *language = fallback;
178 MINFO(
"Fallback match for language " << (*language)->get_english_language_name());
182 MINFO(
"No match found");
183 memwipe(matched_indices.data(), matched_indices.size() *
sizeof(matched_indices[0]));
193 uint32_t create_checksum_index(
const std::vector<epee::wipeable_string> &word_list,
201 for (std::vector<epee::wipeable_string>::const_iterator it = word_list.begin(); it != word_list.end(); it++)
204 auto it2 = trimmed_word_map.find(word);
205 if (it2 == trimmed_word_map.end())
207 trimmed_words += it2->first;
209 boost::crc_32_type result;
210 result.process_bytes(trimmed_words.
data(), trimmed_words.
length());
211 return result.checksum() % word_list.size();
220 bool checksum_test(std::vector<epee::wipeable_string> seed,
const Language::Base *language)
230 auto idx = create_checksum_index(seed, language);
238 MINFO(
"Checksum is " << (ret ?
"valid" :
"invalid"));
255 namespace ElectrumWords
269 std::vector<epee::wipeable_string> seed;
275 MERROR(
"Invalid seed: not a multiple of 4");
279 bool has_checksum =
true;
283 const size_t expected = len * 8 * 3 / 32;
284 if (seed.size() != expected/2 && seed.size() != expected &&
285 seed.size() != expected + 1)
287 MERROR(
"Invalid seed: unexpected number of words");
292 has_checksum = seed.size() == (expected + 1);
295 std::vector<uint32_t> matched_indices;
298 if (!find_seed_language(seed, has_checksum, matched_indices, &language))
300 MERROR(
"Invalid seed: language not found");
303 language_name = language->get_language_name();
304 uint32_t word_list_length = language->get_word_list().size();
308 if (!checksum_test(seed, language))
311 MERROR(
"Invalid seed: invalid checksum");
317 for (
unsigned int i=0; i < seed.size() / 3; i++)
320 w[1] = matched_indices[i*3];
321 w[2] = matched_indices[i*3 + 1];
322 w[3] = matched_indices[i*3 + 2];
324 w[0]= w[1] + word_list_length * (((word_list_length - w[1]) + w[2]) % word_list_length) +
325 word_list_length * word_list_length * (((word_list_length - w[2]) + w[3]) % word_list_length);
327 if (!(w[0]% word_list_length == w[1]))
330 MERROR(
"Invalid seed: mumble mumble");
335 dst.
append((
const char*)&w[0], 4);
339 if (len > 0 && duplicate)
341 const size_t expected = len * 3 / 32;
342 if (seed.size() == expected/2)
364 MERROR(
"Invalid seed: failed to convert words to bytes");
367 if (s.
size() !=
sizeof(dst))
369 MERROR(
"Invalid seed: wrong output size");
387 if (len % 4 != 0 || len == 0)
return false;
393 if (language_name == l->get_language_name() || language_name == l->get_english_language_name())
400 const std::vector<std::string> &word_list = language->
get_word_list();
402 std::vector<epee::wipeable_string> words_store;
404 uint32_t word_list_length = word_list.size();
406 for (
unsigned int i=0; i < len/4; i++, words.
push_back(
' '))
412 w[1] = w[0] % word_list_length;
413 w[2] = ((w[0] / word_list_length) + w[1]) % word_list_length;
414 w[3] = (((w[0] / word_list_length) / word_list_length) + w[2]) % word_list_length;
416 words += word_list[w[1]];
418 words += word_list[w[2]];
420 words += word_list[w[3]];
422 words_store.push_back(word_list[w[1]]);
423 words_store.push_back(word_list[w[2]]);
424 words_store.push_back(word_list[w[3]]);
429 words += words_store[create_checksum_index(words_store, language)];
436 return bytes_to_words(src.data,
sizeof(src), words, language_name);
441 static const std::vector<const Language::Base*> language_instances({
455 return language_instances;
464 const std::vector<const Language::Base*> language_instances =
get_language_list();
465 for (std::vector<const Language::Base*>::const_iterator it = language_instances.begin();
466 it != language_instances.end(); it++)
468 languages.push_back(english ? (*it)->get_english_language_name() : (*it)->get_language_name());
479 std::vector<epee::wipeable_string> word_list;
480 seed.
split(word_list);
486 const std::vector<const Language::Base*> language_instances =
get_language_list();
487 for (std::vector<const Language::Base*>::const_iterator it = language_instances.begin();
488 it != language_instances.end(); it++)
490 if ((*it)->get_language_name() ==
name)
491 return (*it)->get_english_language_name();
493 return "<language not found>";
Portuguese word list and map.
const std::vector< std::string > & get_word_list() const
Returns a pointer to the word list.
A singleton helper class based on template.
French word list and map.
size_t size() const noexcept
std::vector< const Language::Base * > get_language_list()
New Dutch word list and map.
auto_scope_leave_caller create_scope_leave_handler(t_scope_leave_handler f)
bool words_to_bytes(const epee::wipeable_string &words, epee::wipeable_string &dst, size_t len, bool duplicate, std::string &language_name)
Converts seed words to bytes (secret key).
A base language class which all languages have to inherit from for Polymorphism.
void append(const char *ptr, size_t len)
const std::unordered_map< epee::wipeable_string, uint32_t, WordHash, WordEqual > & get_trimmed_word_map() const
Returns a pointer to the trimmed word map.
Language Base class for Polymorphism.
Russian word list and map.
New Esperanto word list and map.
size_t length() const noexcept
void split(std::vector< wipeable_string > &fields) const
const std::unordered_map< epee::wipeable_string, uint32_t, WordHash, WordEqual > & get_word_map() const
Returns a pointer to the word map.
bool bytes_to_words(const char *src, size_t len, epee::wipeable_string &words, const std::string &language_name)
Converts bytes (secret key) to seed words.
Italian word list and map.
German word list and map.
Japanese word list and map.
Older version of English word list and map.
Spanish word list and map.
std::string get_english_name_for(const std::string &name)
Returns the name of a language in English.
New English word list and map.
New Lojban word list and map.
Mnemonic seed generation and wallet restoration from them.
const std::string & get_english_language_name() const
Returns the name of the language in English.
bool get_is_old_style_seed(const epee::wipeable_string &seed)
Tells if the seed passed is an old style seed or not.
void * memwipe(void *src, size_t n)
const char * data() const noexcept
uint32_t get_unique_prefix_length() const
Returns the number of unique starting characters to be used for matching.
Simplified Chinese word list and map.
T utf8prefix(const T &s, size_t count)
Returns a string made of (at most) the first count characters in s. Assumes well formedness. No check is made for this.