From 08b7fe4e92c7706fbc6a107e8bc7cf96d66e3bd5 Mon Sep 17 00:00:00 2001 From: b4n6-b4n6 Date: Fri, 30 Aug 2024 20:40:01 +0700 Subject: [PATCH] Implement ElectrumWords::get_invalid_word --- src/mnemonics/electrum-words.cpp | 53 ++++++++++++++++++++++++++++++++ src/mnemonics/electrum-words.h | 2 ++ src/wallet/api/wallet.cpp | 7 ++++- tests/unit_tests/mnemonics.cpp | 51 ++++++++++++++++++++++++++++++ 4 files changed, 112 insertions(+), 1 deletion(-) diff --git a/src/mnemonics/electrum-words.cpp b/src/mnemonics/electrum-words.cpp index 171dd750c8..3e4d4d5dcf 100644 --- a/src/mnemonics/electrum-words.cpp +++ b/src/mnemonics/electrum-words.cpp @@ -371,6 +371,59 @@ namespace crypto return true; } + std::string get_invalid_word(const epee::wipeable_string &words) + { + // If there's a new language added, add an instance of it here. + std::vector language_instances({ + Language::Singleton::instance(), + Language::Singleton::instance(), + Language::Singleton::instance(), + Language::Singleton::instance(), + Language::Singleton::instance(), + Language::Singleton::instance(), + Language::Singleton::instance(), + Language::Singleton::instance(), + Language::Singleton::instance(), + Language::Singleton::instance(), + Language::Singleton::instance(), + Language::Singleton::instance(), + Language::Singleton::instance() + }); + + std::vector seed; + words.split(seed); + + for ( + std::vector::const_iterator seed_i = seed.begin(); + seed_i != seed.end(); + seed_i++ + ) + { + bool has_any = false; + + for ( + std::vector::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. * \param src Secret key diff --git a/src/mnemonics/electrum-words.h b/src/mnemonics/electrum-words.h index 374ebef57e..8231ffe5d5 100644 --- a/src/mnemonics/electrum-words.h +++ b/src/mnemonics/electrum-words.h @@ -83,6 +83,8 @@ namespace crypto bool words_to_bytes(const epee::wipeable_string &words, crypto::secret_key& dst, std::string &language_name); + std::string get_invalid_word(const epee::wipeable_string &words); + /*! * \brief Converts bytes to seed words. * \param src Secret data diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index c8257919dd..13c703b4b8 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -749,7 +749,12 @@ bool WalletImpl::recover(const std::string &path, const std::string &password, c crypto::secret_key recovery_key; std::string 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("Invalid word %s")) % ("'" + invalid_word + "'")).str()); + } else { + setStatusError(tr("Electrum-style word list failed verification")); + } return false; } if (!seed_offset.empty()) diff --git a/tests/unit_tests/mnemonics.cpp b/tests/unit_tests/mnemonics.cpp index 8e8886f551..60e67cae43 100644 --- a/tests/unit_tests/mnemonics.cpp +++ b/tests/unit_tests/mnemonics.cpp @@ -255,3 +255,54 @@ TEST(mnemonics, partial_word_tolerance) ASSERT_EQ(true, res); 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( + "ㇴ" + ) == "ㇴ" + ); +}