Implement ElectrumWords::get_invalid_word

This commit is contained in:
b4n6-b4n6 2024-12-14 19:12:42 +07:00
parent a1dc85c537
commit bfbc84e2a0
4 changed files with 122 additions and 21 deletions

View file

@ -70,6 +70,7 @@ namespace crypto
namespace ElectrumWords namespace ElectrumWords
{ {
std::vector<const Language::Base*> get_language_list(); std::vector<const Language::Base*> get_language_list();
std::vector<const Language::Base*> get_language_list_2();
} }
} }
@ -89,31 +90,16 @@ namespace
* \return true if all the words were present in some language false if not. * \return true if all the words were present in some language false if not.
*/ */
bool find_seed_language(const std::vector<epee::wipeable_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) bool has_checksum, std::vector<uint32_t> &matched_indices, const Language::Base **language)
{ {
// If there's a new language added, add an instance of it here. std::vector<const Language::Base*> language_instances = crypto::ElectrumWords::get_language_list_2();
std::vector<Language::Base*> language_instances({ const Language::Base *fallback = NULL;
Language::Singleton<Language::Chinese_Simplified>::instance(),
Language::Singleton<Language::English>::instance(),
Language::Singleton<Language::Dutch>::instance(),
Language::Singleton<Language::French>::instance(),
Language::Singleton<Language::Spanish>::instance(),
Language::Singleton<Language::German>::instance(),
Language::Singleton<Language::Italian>::instance(),
Language::Singleton<Language::Portuguese>::instance(),
Language::Singleton<Language::Japanese>::instance(),
Language::Singleton<Language::Russian>::instance(),
Language::Singleton<Language::Esperanto>::instance(),
Language::Singleton<Language::Lojban>::instance(),
Language::Singleton<Language::EnglishOld>::instance()
});
Language::Base *fallback = NULL;
std::vector<epee::wipeable_string>::const_iterator it2; std::vector<epee::wipeable_string>::const_iterator it2;
matched_indices.reserve(seed.size()); matched_indices.reserve(seed.size());
// Iterate through all the languages and find a match // Iterate through all the languages and find a match
for (std::vector<Language::Base*>::iterator it1 = language_instances.begin(); for (std::vector<const Language::Base*>::const_iterator it1 = language_instances.begin();
it1 != language_instances.end(); it1++) it1 != language_instances.end(); it1++)
{ {
const std::unordered_map<epee::wipeable_string, uint32_t, Language::WordHash, Language::WordEqual> &word_map = (*it1)->get_word_map(); const std::unordered_map<epee::wipeable_string, uint32_t, Language::WordHash, Language::WordEqual> &word_map = (*it1)->get_word_map();
@ -292,7 +278,7 @@ namespace crypto
std::vector<uint32_t> matched_indices; 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]));}); auto wiper = epee::misc_utils::create_scope_leave_handler([&](){memwipe(matched_indices.data(), matched_indices.size() * sizeof(matched_indices[0]));});
Language::Base *language; const Language::Base *language;
if (!find_seed_language(seed, has_checksum, matched_indices, &language)) if (!find_seed_language(seed, has_checksum, matched_indices, &language))
{ {
MERROR("Invalid seed: language not found"); MERROR("Invalid seed: language not found");
@ -371,6 +357,43 @@ namespace crypto
return true; return true;
} }
std::string get_invalid_word(const epee::wipeable_string &words)
{
std::vector<const Language::Base*> language_instances = crypto::ElectrumWords::get_language_list_2();
std::vector<epee::wipeable_string> seed;
words.split(seed);
for (
std::vector<epee::wipeable_string>::const_iterator seed_i = seed.begin();
seed_i != seed.end();
seed_i++
)
{
bool has_any = false;
for (
std::vector<const Language::Base*>::const_iterator lang_i = language_instances.begin();
lang_i != language_instances.end() && !has_any;
lang_i++
)
{
auto &word_map = (*lang_i)->get_word_map();
if (word_map.count(*seed_i) != 0)
{
has_any = true;
break;
}
}
if (!has_any) {
return std::string(seed_i->data(), seed_i->size());
}
}
return "";
}
/*! /*!
* \brief Converts bytes (secret key) to seed words. * \brief Converts bytes (secret key) to seed words.
* \param src Secret key * \param src Secret key
@ -453,6 +476,26 @@ namespace crypto
return language_instances; return language_instances;
} }
std::vector<const Language::Base*> get_language_list_2()
{
static const std::vector<const Language::Base*> language_instances_2({
Language::Singleton<Language::German>::instance(),
Language::Singleton<Language::English>::instance(),
Language::Singleton<Language::Spanish>::instance(),
Language::Singleton<Language::French>::instance(),
Language::Singleton<Language::Italian>::instance(),
Language::Singleton<Language::Dutch>::instance(),
Language::Singleton<Language::Portuguese>::instance(),
Language::Singleton<Language::Russian>::instance(),
Language::Singleton<Language::Japanese>::instance(),
Language::Singleton<Language::Chinese_Simplified>::instance(),
Language::Singleton<Language::Esperanto>::instance(),
Language::Singleton<Language::Lojban>::instance(),
Language::Singleton<Language::EnglishOld>::instance()
});
return language_instances_2;
}
/*! /*!
* \brief Gets a list of seed languages that are supported. * \brief Gets a list of seed languages that are supported.
* \param languages The vector is set to the list of languages. * \param languages The vector is set to the list of languages.

View file

@ -83,6 +83,8 @@ namespace crypto
bool words_to_bytes(const epee::wipeable_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 &language_name);
std::string get_invalid_word(const epee::wipeable_string &words);
/*! /*!
* \brief Converts bytes to seed words. * \brief Converts bytes to seed words.
* \param src Secret data * \param src Secret data

View file

@ -749,7 +749,12 @@ bool WalletImpl::recover(const std::string &path, const std::string &password, c
crypto::secret_key recovery_key; crypto::secret_key recovery_key;
std::string old_language; std::string old_language;
if (!crypto::ElectrumWords::words_to_bytes(seed, recovery_key, old_language)) { if (!crypto::ElectrumWords::words_to_bytes(seed, recovery_key, old_language)) {
setStatusError(tr("Electrum-style word list failed verification")); std::string invalid_word = crypto::ElectrumWords::get_invalid_word(seed);
if (invalid_word != "") {
setStatusError((boost::format(tr("Electrum-style word list failed verification: %s")) % ("'" + invalid_word + "'")).str());
} else {
setStatusError(tr("Electrum-style word list failed verification"));
}
return false; return false;
} }
if (!seed_offset.empty()) if (!seed_offset.empty())

View file

@ -255,3 +255,54 @@ TEST(mnemonics, partial_word_tolerance)
ASSERT_EQ(true, res); ASSERT_EQ(true, res);
ASSERT_STREQ(language_name_1.c_str(), "English"); ASSERT_STREQ(language_name_1.c_str(), "English");
} }
TEST(mnemonics, get_invalid_word)
{
ASSERT_TRUE(
crypto::ElectrumWords::get_invalid_word(
"crim bam scamp gna limi woma wron tuit birth mundane donuts square cohesive dolphin titans narrate fue saved wrap aloof magic mirr toget upda wra"
) == "crim"
);
ASSERT_TRUE(
crypto::ElectrumWords::get_invalid_word(
"criminal bamboo scamper gnaw limits womanly wrong tuition birth mundane donuts square cohesive dolphin titans narrate fue saved wrap aloof magically mirror together update wrap"
) == "fue"
);
ASSERT_TRUE(
crypto::ElectrumWords::get_invalid_word(
"a a a a a a a a a a a a a a a a a a a a a a a a a"
) == "a"
);
ASSERT_TRUE(
crypto::ElectrumWords::get_invalid_word(
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandonnn abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon"
) == "abandonnn"
);
ASSERT_TRUE(
crypto::ElectrumWords::get_invalid_word(
"abando abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandonnn abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon"
) == "abando"
);
ASSERT_TRUE(
crypto::ElectrumWords::get_invalid_word(
"criminal bamboo scamper gnaw limits womanly wrong tuition birth mundane donuts square cohesive dolphin titans narrate fuel saved wrap aloof magically mirror together update wrap"
) == ""
);
ASSERT_TRUE(
crypto::ElectrumWords::get_invalid_word(
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon"
) == ""
);
ASSERT_TRUE(
crypto::ElectrumWords::get_invalid_word(
""
) == ""
);
}