Electroneum
Loading...
Searching...
No Matches
mnemonics.cpp
Go to the documentation of this file.
1// Copyrights(c) 2017-2021, The Electroneum Project
2// Copyrights(c) 2014-2019, The Monero Project
3//
4// All rights reserved.
5//
6// Redistribution and use in source and binary forms, with or without modification, are
7// permitted provided that the following conditions are met:
8//
9// 1. Redistributions of source code must retain the above copyright notice, this list of
10// conditions and the following disclaimer.
11//
12// 2. Redistributions in binary form must reproduce the above copyright notice, this list
13// of conditions and the following disclaimer in the documentation and/or other
14// materials provided with the distribution.
15//
16// 3. Neither the name of the copyright holder nor the names of its contributors may be
17// used to endorse or promote products derived from this software without specific
18// prior written permission.
19//
20// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
21// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
22// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
23// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
28// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30#include "gtest/gtest.h"
31#include "wipeable_string.h"
34#include "crypto/crypto.h"
35#include <stdlib.h>
36#include <vector>
37#include <time.h>
38#include <iostream>
39#include <boost/algorithm/string.hpp>
41#include "mnemonics/english.h"
42#include "mnemonics/spanish.h"
44#include "mnemonics/japanese.h"
45#include "mnemonics/german.h"
46#include "mnemonics/italian.h"
47#include "mnemonics/russian.h"
48#include "mnemonics/french.h"
49#include "mnemonics/dutch.h"
50#include "mnemonics/esperanto.h"
51#include "mnemonics/lojban.h"
54#include "mnemonics/singleton.h"
55#include <boost/algorithm/string.hpp>
56
57namespace
58{
64 void compare_vectors(const std::vector<std::string> &expected, const std::vector<std::string> &present)
65 {
66 std::vector<std::string>::const_iterator it1, it2;
67 for (it1 = expected.begin(), it2 = present.begin(); it1 != expected.end() && it2 != present.end();
68 it1++, it2++)
69 {
70 ASSERT_STREQ(it1->c_str(), it2->c_str());
71 }
72 }
73
78 void test_language(const Language::Base &language)
79 {
80 epee::wipeable_string w_seed = "", w_return_seed = "";
81 std::string seed, return_seed;
82 // Generate a random seed without checksum
83 crypto::secret_key randkey;
84 for (size_t ii = 0; ii < sizeof(randkey); ++ii)
85 {
86 randkey.data[ii] = rand();
87 }
89 seed = std::string(w_seed.data(), w_seed.size());
90 // remove the checksum word
91 const char *space = strrchr(seed.c_str(), ' ');
92 ASSERT_TRUE(space != NULL);
93 seed = std::string(seed.c_str(), space-seed.c_str());
94
95 std::cout << "Test seed without checksum:\n";
96 std::cout << seed << std::endl;
97
99 std::string language_name;
100 bool res;
101 std::vector<std::string> seed_vector, return_seed_vector;
102 std::string checksum_word;
103
104 // Convert it to secret key
105 res = crypto::ElectrumWords::words_to_bytes(seed, key, language_name);
106 ASSERT_EQ(true, res);
107 std::cout << "Detected language: " << language_name << std::endl;
108 ASSERT_STREQ(language.get_language_name().c_str(), language_name.c_str());
109
110 // Convert the secret key back to seed
112 return_seed = std::string(w_return_seed.data(), w_return_seed.size());
113 ASSERT_EQ(true, res);
114 std::cout << "Returned seed:\n";
115 std::cout << return_seed << std::endl;
116 boost::split(seed_vector, seed, boost::is_any_of(" "));
117 boost::split(return_seed_vector, return_seed, boost::is_any_of(" "));
118
119 // Extract the checksum word
120 checksum_word = return_seed_vector.back();
121 return_seed_vector.pop_back();
122 ASSERT_EQ(seed_vector.size(), return_seed_vector.size());
123 // Ensure that the rest of it is same
124 compare_vectors(seed_vector, return_seed_vector);
125
126 // Append the checksum word to repeat the entire process with a seed with checksum
127 seed += (" " + checksum_word);
128 std::cout << "Test seed with checksum:\n";
129 std::cout << seed << std::endl;
130 res = crypto::ElectrumWords::words_to_bytes(seed, key, language_name);
131 ASSERT_EQ(true, res);
132 std::cout << "Detected language: " << language_name << std::endl;
133 ASSERT_STREQ(language.get_language_name().c_str(), language_name.c_str());
134
135 w_return_seed = "";
137 return_seed = std::string(w_return_seed.data(), w_return_seed.size());
138 ASSERT_EQ(true, res);
139 std::cout << "Returned seed:\n";
140 std::cout << return_seed << std::endl;
141
142 seed_vector.clear();
143 return_seed_vector.clear();
144 boost::split(seed_vector, seed, boost::is_any_of(" "));
145 boost::split(return_seed_vector, return_seed, boost::is_any_of(" "));
146 ASSERT_EQ(seed_vector.size(), return_seed_vector.size());
147 compare_vectors(seed_vector, return_seed_vector);
148 }
149}
150
151TEST(mnemonics, consistency)
152{
153 try {
154 std::vector<std::string> language_list;
156 }
157 catch(const std::exception &e)
158 {
159 std::cout << "Error initializing mnemonics: " << e.what() << std::endl;
160 ASSERT_TRUE(false);
161 }
162}
163
164TEST(mnemonics, all_languages)
165{
166 srand(time(NULL));
167 std::vector<Language::Base*> languages({
180 });
181
182 for (std::vector<Language::Base*>::iterator it = languages.begin(); it != languages.end(); it++)
183 {
184 try {
185 test_language(*(*it));
186 }
187 catch (const std::exception &e) {
188 std::cout << "Error testing " << (*it)->get_language_name() << " language: " << e.what() << std::endl;
189 ASSERT_TRUE(false);
190 }
191 }
192}
193
194TEST(mnemonics, language_detection_with_bad_checksum)
195{
197 std::string language_name;
198 bool res;
199
200 // This Portuguese (4-prefix) seed has all its words with 3-prefix that's also present in English
201 const std::string base_seed = "cinzento luxuriante leonardo gnostico digressao cupula fifa broxar iniquo louvor ovario dorsal ideologo besuntar decurso rosto susto lemure unheiro pagodeiro nitroglicerina eclusa mazurca bigorna";
202 const std::string real_checksum = "gnostico";
203
204 res = crypto::ElectrumWords::words_to_bytes(base_seed, key, language_name);
205 ASSERT_EQ(true, res);
206 ASSERT_STREQ(language_name.c_str(), "Português");
207
208 res = crypto::ElectrumWords::words_to_bytes(base_seed + " " + real_checksum, key, language_name);
209 ASSERT_EQ(true, res);
210 ASSERT_STREQ(language_name.c_str(), "Português");
211}
212
226
227TEST(mnemonics, case_tolerance)
228{
229 bool res;
230 //
231 crypto::secret_key key_1;
232 std::string language_name_1;
233 const std::string seed_1 = "Neubau umarmen Abart umarmen Turban feilen Brett Bargeld Episode Milchkuh Substanz Jahr Armband Maibaum Tand Grünalge Tabak erziehen Federboa Lobrede Tenor Leuchter Curry Diskurs Tenor";
234 res = crypto::ElectrumWords::words_to_bytes(seed_1, key_1, language_name_1);
235 ASSERT_EQ(true, res);
236 ASSERT_STREQ(language_name_1.c_str(), "Deutsch");
237 //
238 crypto::secret_key key_2;
239 std::string language_name_2;
240 // neubau is capitalized in the word list, but the language detection code should be able to detect it as Deutsch
241 std::string seed_2 = "neubau umarmen Abart umarmen Turban feilen Brett Bargeld Episode Milchkuh Substanz Jahr Armband Maibaum Tand Grünalge Tabak erziehen Federboa Lobrede Tenor Leuchter Curry Diskurs tenor";
242 boost::algorithm::to_lower(seed_2);
243 res = crypto::ElectrumWords::words_to_bytes(seed_2, key_2, language_name_2);
244 ASSERT_EQ(true, res);
245 ASSERT_STREQ(language_name_2.c_str(), "Deutsch");
246 //
247 ASSERT_TRUE(key_1 == key_2);
248}
249
250TEST(mnemonics, partial_word_tolerance)
251{
252 bool res;
253 //
254 crypto::secret_key key_1;
255 std::string language_name_1;
256 const std::string seed_1 = "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";
257 res = crypto::ElectrumWords::words_to_bytes(seed_1, key_1, language_name_1);
258 ASSERT_EQ(true, res);
259 ASSERT_STREQ(language_name_1.c_str(), "English");
260}
time_t time
Simplified Chinese word list and map.
A base language class which all languages have to inherit from for Polymorphism.
const std::string & get_language_name() const
Returns the name of the language.
static T * instance()
Definition singleton.h:57
const char * data() const noexcept
size_t size() const noexcept
New Dutch word list and map.
Mnemonic seed generation and wallet restoration from them.
New English word list and map.
Older version of English word list and map.
New Esperanto word list and map.
#define ASSERT_EQ(val1, val2)
Definition gtest.h:1956
#define ASSERT_STREQ(s1, s2)
Definition gtest.h:2004
#define TEST(test_case_name, test_name)
Definition gtest.h:2187
#define ASSERT_TRUE(condition)
Definition gtest.h:1865
French word list and map.
German word list and map.
const char * res
const char * key
Italian word list and map.
Japanese word list and map.
Language Base class for Polymorphism.
New Lojban 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....
std::vector< const Language::Base * > get_language_list()
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).
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.
epee::mlocked< tools::scrubbed< ec_scalar > > secret_key
Definition crypto.h:82
void rand(size_t N, uint8_t *bytes)
Definition crypto.h:209
Portuguese word list and map.
Russian word list and map.
A singleton helper class based on template.
Spanish word list and map.