From f3e10f2343d52fb8dce0089fdfaa66a89f893baf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric?= Date: Fri, 10 Aug 2018 13:33:17 +0200 Subject: [PATCH] add on-device seed converion (24 bip32 words to 25 electrum words) --- src/monero_api.h | 3 +- src/monero_dispatch.c | 3 + src/monero_init.c | 1 + src/monero_key.c | 176 +- src/monero_types.h | 27 +- src/monero_ux_nanos.c | 60 +- .../ledger/monero/dictionaries/esperanto.py | 3252 ++++++++--------- tools/python/src/ledger/monero/seedconv.py | 271 +- 8 files changed, 2064 insertions(+), 1729 deletions(-) diff --git a/src/monero_api.h b/src/monero_api.h index 9ff171c..cfd2027 100644 --- a/src/monero_api.h +++ b/src/monero_api.h @@ -25,6 +25,7 @@ int monero_dispatch(void); int monero_apdu_put_key(void); int monero_apdu_get_key(void); +int monero_apdu_manage_seedwords() ; int monero_apdu_verify_key(void); int monero_apdu_get_chacha8_prekey(void); int monero_apdu_sc_add(void); @@ -117,7 +118,7 @@ void monero_get_subaddress_spend_public_key(unsigned char *x,unsigned char *inde void monero_get_subaddress(unsigned char *C, unsigned char *D, unsigned char *index); void monero_get_subaddress_secret_key(unsigned char *sub_s, unsigned char *s, unsigned char *index); - +void monero_clear_words(); /* ----------------------------------------------------------------------- */ /* --- CRYPTO ---- */ /* ----------------------------------------------------------------------- */ diff --git a/src/monero_dispatch.c b/src/monero_dispatch.c index 839e516..8e44e79 100644 --- a/src/monero_dispatch.c +++ b/src/monero_dispatch.c @@ -71,6 +71,9 @@ int monero_dispatch() { case INS_GET_KEY: sw = monero_apdu_get_key(); break; + case INS_MANAGE_SEEDWORDS: + sw = monero_apdu_manage_seedwords(); + break; /* --- PROVISIONING--- */ case INS_VERIFY_KEY: diff --git a/src/monero_init.c b/src/monero_init.c index 2ad2242..b543b3b 100644 --- a/src/monero_init.c +++ b/src/monero_init.c @@ -83,6 +83,7 @@ void monero_init_private_key() { unsigned int path[5]; unsigned char seed[32]; + // m/44'/128'/0'/0/0 path[0] = 0x8000002C; path[1] = 0x80000080; path[2] = 0x80000000; diff --git a/src/monero_key.c b/src/monero_key.c index c5211a5..72aca4c 100644 --- a/src/monero_key.c +++ b/src/monero_key.c @@ -19,6 +19,152 @@ #include "monero_api.h" #include "monero_vars.h" +/* ----------------------------------------------------------------------- */ +/* --- --- */ +/* ----------------------------------------------------------------------- */ +static const unsigned int crcTable[256] = { + 0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535, + 0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD, + 0xE7B82D07,0x90BF1D91,0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D, + 0x6DDDE4EB,0xF4D4B551,0x83D385C7,0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC, + 0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5,0x3B6E20C8,0x4C69105E,0xD56041E4, + 0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,0x35B5A8FA,0x42B2986C, + 0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59,0x26D930AC, + 0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F, + 0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB, + 0xB6662D3D,0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F, + 0x9FBFE4A5,0xE8B8D433,0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB, + 0x086D3D2D,0x91646C97,0xE6635C01,0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E, + 0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,0x65B0D9C6,0x12B7E950,0x8BBEB8EA, + 0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65,0x4DB26158,0x3AB551CE, + 0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,0x4369E96A, + 0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9, + 0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409, + 0xCE61E49F,0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81, + 0xB7BD5C3B,0xC0BA6CAD,0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739, + 0x9DD277AF,0x04DB2615,0x73DC1683,0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8, + 0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1,0xF00F9344,0x8708A3D2,0x1E01F268, + 0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,0xFED41B76,0x89D32BE0, + 0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5,0xD6D6A3E8, + 0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B, + 0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF, + 0x4669BE79,0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703, + 0x220216B9,0x5505262F,0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7, + 0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D,0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A, + 0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,0x95BF4A82,0xE2B87A14,0x7BB12BAE, + 0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21,0x86D3D2D4,0xF1D4E242, + 0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,0x88085AE6, + 0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45, + 0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D, + 0x3E6E77DB,0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5, + 0x47B2CF7F,0x30B5FFE9,0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605, + 0xCDD70693,0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94, + 0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D }; + +static unsigned long monero_crc32( unsigned long inCrc32, const void *buf, + size_t bufLen ) +{ + + unsigned long crc32; + unsigned char *byteBuf; + size_t i; + + /** accumulate crc32 for buffer **/ + crc32 = inCrc32 ^ 0xFFFFFFFF; + byteBuf = (unsigned char*) buf; + for (i=0; i < bufLen; i++) { + crc32 = (crc32 >> 8) ^ crcTable[ (crc32 ^ byteBuf[i]) & 0xFF ]; + } + return( crc32 ^ 0xFFFFFFFF ); +} + + +void monero_clear_words() { + for (int i = 0; i<25; i++) { + monero_nvm_write(N_monero_pstate->words[i], NULL,WORDS_MAX_LENGTH); + } +} +/** + * n : word to write + * idx: index of word to copy + * w_start: first wordd index in list + * word_list + * len : word_list length + */ + +static void monero_set_word(unsigned int n, unsigned int idx, unsigned int w_start, unsigned char* word_list, int len) { + while (w_start < idx) { + len -= 1 + word_list[0]; + if (len < 0) { + monero_clear_words(); + THROW(SW_WRONG_DATA+1); + return; + } + word_list += 1 + word_list[0]; + w_start++; + } + + if ((w_start != idx) || (word_list[0] > (len-1)) || (word_list[0] > 19)) { + THROW(SW_WRONG_DATA+2); + return; + } + len = word_list[0]; + word_list++; + monero_nvm_write(N_monero_pstate->words[n], word_list, len); +} + +#define word_list_length 1626 +#define seed G_monero_vstate.b + +int monero_apdu_manage_seedwords() { + unsigned int w_start, w_end; + unsigned short wc[4]; + switch (G_monero_vstate.io_p1) { + //SETUP + case 1: + w_start = monero_io_fetch_u32(); + w_end = w_start+monero_io_fetch_u32(); + if ((w_start >= word_list_length) || (w_end > word_list_length) || (w_start > w_end)) { + THROW(SW_WRONG_DATA); + return SW_WRONG_DATA; + } + for (int i = 0; i<8; i++) { + unsigned int val = (seed[i*4+0]<<0) | (seed[i*4+1]<<8) | (seed[i*4+2]<<16) | (seed[i*4+3]<<24); + wc[0] = val % word_list_length; + wc[1] = ((val / word_list_length) + wc[0]) % word_list_length; + wc[2] = (((val / word_list_length) / word_list_length) + wc[1]) % word_list_length; + + for (int wi = 0; wi < 3; wi++) { + if ((wc[wi] >= w_start) && (wc[wi] < w_end)) { + monero_set_word(i*3+wi, wc[wi], w_start, G_monero_vstate.io_buffer+G_monero_vstate.io_offset, MONERO_IO_BUFFER_LENGTH-G_monero_vstate.io_offset); + } + } + } + + monero_io_discard(1); + if (G_monero_vstate.io_p2) { + for (int i = 0; i<24; i++) { + for (int j = 0; jwords[i][j]; + } + } + w_start = monero_crc32(0, G_monero_vstate.io_buffer, G_monero_vstate.io_p2*24)%24; + monero_nvm_write(N_monero_pstate->words[24], N_monero_pstate->words[w_start], WORDS_MAX_LENGTH); + } + + break; + + //CLEAR + case 2: + monero_io_discard(0); + monero_clear_words(); + break; + } + + return SW_OK; +} +#undef seed +#undef word_list_length /* ----------------------------------------------------------------------- */ /* --- --- */ @@ -70,9 +216,8 @@ int monero_apdu_put_key() { /* --- --- */ /* ----------------------------------------------------------------------- */ int monero_apdu_get_key() { - unsigned char x[32]; - monero_io_discard(0); + monero_io_discard(1); switch (G_monero_vstate.io_p1) { //get pub case 1: @@ -90,7 +235,30 @@ int monero_apdu_get_key() { //view key ui_export_viewkey_display(); return 0; + + #if DEBUG_HWDEVICE + //get info + case 3:{ + unsigned int path[5]; + unsigned char seed[32]; + // m/44'/128'/0'/0/0 + path[0] = 0x8000002C; + path[1] = 0x80000080; + path[2] = 0x80000000; + path[3] = 0x00000000; + path[4] = 0x00000000; + + os_perso_derive_node_bip32(CX_CURVE_SECP256K1, path, 5 , seed, G_monero_vstate.a); + monero_io_insert(seed, 32); + + monero_io_insert(G_monero_vstate.b, 32); + monero_io_insert(G_monero_vstate.a, 32); + + break; + } + #endif + default: THROW(SW_WRONG_P1P2); return SW_WRONG_P1P2; @@ -442,7 +610,3 @@ int monero_apdu_get_subaddress_secret_key(/*const crypto::secret_key& sec, const monero_io_insert_encrypt(sub_sec,32); return SW_OK; } - - - - diff --git a/src/monero_types.h b/src/monero_types.h index 7f20e41..cdea47e 100644 --- a/src/monero_types.h +++ b/src/monero_types.h @@ -69,6 +69,10 @@ struct monero_nv_state_s { /* spend key */ unsigned char b[32]; + /*words*/ + #define WORDS_MAX_LENGTH 20 + char words[26][20]; + } ; typedef struct monero_nv_state_s monero_nv_state_t; @@ -145,14 +149,20 @@ struct monero_v_state_s { /* ------------------------------------------ */ /* --- UI/UX --- */ /* ------------------------------------------ */ - /* menu 0: 95-chars + "" + null */ - char ux_menu[112]; - // address to display: 95-chars + null - char ux_address[96]; - // xmr to display: max pow(2,64) unit, aka 20-chars + '0' + dot + null - char ux_amount[23]; - -} ; + union { + struct { + /* menu 0: 95-chars + "" + null */ + char ux_menu[112]; + // address to display: 95-chars + null + char ux_address[96]; + // xmr to display: max pow(2,64) unit, aka 20-chars + '0' + dot + null + char ux_amount[23]; + }; + struct { + char words[340]; + }; + }; +}; typedef struct monero_v_state_s monero_v_state_t; @@ -181,6 +191,7 @@ typedef struct monero_v_state_s monero_v_state_t; #define INS_PUT_KEY 0x22 #define INS_GET_CHACHA8_PREKEY 0x24 #define INS_VERIFY_KEY 0x26 +#define INS_MANAGE_SEEDWORDS 0x28 #define INS_SECRET_KEY_TO_PUBLIC_KEY 0x30 #define INS_GEN_KEY_DERIVATION 0x32 diff --git a/src/monero_ux_nanos.c b/src/monero_ux_nanos.c index c05a385..8c5e8f9 100644 --- a/src/monero_ux_nanos.c +++ b/src/monero_ux_nanos.c @@ -40,7 +40,7 @@ const ux_menu_entry_t ui_menu_main[]; void ui_menu_main_display(unsigned int value) ; const bagl_element_t* ui_menu_main_preprocessor(const ux_menu_entry_t* entry, bagl_element_t* element); - +void ui_menu_settings_display(unsigned int value); /* ------------------------------- Helpers UX ------------------------------- */ void ui_CCID_reset(void) { @@ -100,6 +100,54 @@ void ui_menu_fee_validation_display(unsigned int value) { UX_MENU_DISPLAY(0, ui_menu_fee_validation, ui_menu_fee_validation_preprocessor); } + +/* -------------------------------------- 25 WORDS --------------------------------------- */ +void ui_menu_words_clear(unsigned int value); +void ui_menu_words_back(unsigned int value); +#define WORDS N_monero_pstate->words +const ux_menu_entry_t ui_menu_words[] = { + {NULL, ui_menu_words_back, 0, NULL, "", "", 0, 0}, + {NULL, ui_menu_words_back, 2, NULL, "", "", 0, 0}, + {NULL, ui_menu_words_back, 4, NULL, "", "", 0, 0}, + {NULL, ui_menu_words_back, 6, NULL, "", "", 0, 0}, + {NULL, ui_menu_words_back, 8, NULL, "", "", 0, 0}, + {NULL, ui_menu_words_back, 10, NULL, "", "", 0, 0}, + {NULL, ui_menu_words_back, 12, NULL, "", "", 0, 0}, + {NULL, ui_menu_words_back, 14, NULL, "", "", 0, 0}, + {NULL, ui_menu_words_back, 16, NULL, "", "", 0, 0}, + {NULL, ui_menu_words_back, 18, NULL, "", "", 0, 0}, + {NULL, ui_menu_words_back, 20, NULL, "", "", 0, 0}, + {NULL, ui_menu_words_back, 22, NULL, "", "", 0, 0}, + {NULL, ui_menu_words_back, 24, NULL, "", "", 0, 0}, + {NULL, ui_menu_words_clear, -1, NULL, "CLEAR WORDS", "(NO WIPE)", 0, 0}, + UX_MENU_END +}; + +const bagl_element_t* ui_menu_words_preprocessor(const ux_menu_entry_t* entry, bagl_element_t* element) { + if ((entry->userid >= 0) && (entry->userid <25)) { + + if(element->component.userid==0x21) { + element->text = N_monero_pstate->words[entry->userid]; + } + + if ((element->component.userid==0x22)&&(entry->userid<24)) { + element->text = N_monero_pstate->words[entry->userid+1]; + } + } + + return element; +} + +void ui_menu_words_display(unsigned int value) { + UX_MENU_DISPLAY(0, ui_menu_words, ui_menu_words_preprocessor); +} +void ui_menu_words_clear(unsigned int value) { + monero_clear_words(); + ui_menu_main_display(0); +} +void ui_menu_words_back(unsigned int value) { + ui_menu_settings_display(1); +} /* ----------------------------- USER DEST/AMOUNT VALIDATION ----------------------------- */ void ui_menu_validation_action(unsigned int value); @@ -348,13 +396,17 @@ void ui_menu_reset_action(unsigned int value) { /* ------------------------------- SETTINGS UX ------------------------------- */ const ux_menu_entry_t ui_menu_settings[] = { - {NULL, ui_menu_network_display, 0, NULL, "Change Network", NULL, 0, 0}, - {ui_menu_reset, NULL, 0, NULL, "Reset", NULL, 0, 0}, - {NULL, ui_menu_main_display, 2, &C_badge_back, "Back", NULL, 61, 40}, + {NULL, ui_menu_network_display, 0, NULL, "Change Network", NULL, 0, 0}, + {NULL, ui_menu_words_display, 0, NULL, "Show 25 words", NULL, 0, 0}, + {ui_menu_reset, NULL, 0, NULL, "Reset", NULL, 0, 0}, + {NULL, ui_menu_main_display, 2, &C_badge_back, "Back", NULL, 61, 40}, UX_MENU_END }; +void ui_menu_settings_display(unsigned int value) { + UX_MENU_DISPLAY(value, ui_menu_settings, NULL); +} /* --------------------------------- INFO UX --------------------------------- */ diff --git a/tools/python/src/ledger/monero/dictionaries/esperanto.py b/tools/python/src/ledger/monero/dictionaries/esperanto.py index a082660..c4e36a9 100644 --- a/tools/python/src/ledger/monero/dictionaries/esperanto.py +++ b/tools/python/src/ledger/monero/dictionaries/esperanto.py @@ -3,1631 +3,1631 @@ 'english_language_name': u"Esperanto", 'prefix_length': 4, 'words' : [ - "abako", - "abdiki", - "abelo", - "abituriento", - "ablativo", - "abnorma", - "abonantoj", - "abrikoto", - "absoluta", - "abunda", - "acetono", - "acida", - "adapti", - "adekvata", - "adheri", - "adicii", - "adjektivo", - "administri", - "adolesko", - "adreso", - "adstringa", - "adulto", - "advokato", - "adzo", - "aeroplano", - "aferulo", - "afgana", - "afiksi", - "aflaba", - "aforismo", - "afranki", - "aftozo", - "afusto", - "agavo", - "agento", - "agiti", - "aglo", - "agmaniero", - "agnoski", - "agordo", - "agrabla", - "agtipo", - "agutio", - "aikido", - "ailanto", - "aina", - "ajatolo", - "ajgenvaloro", - "ajlobulbo", - "ajnlitera", - "ajuto", - "ajzi", - "akademio", - "akcepti", - "akeo", - "akiri", - "aklamado", - "akmeo", - "akno", - "akompani", - "akrobato", - "akselo", - "aktiva", - "akurata", - "akvofalo", - "alarmo", - "albumo", - "alcedo", - "aldoni", - "aleo", - "alfabeto", - "algo", - "alhasti", - "aligatoro", - "alkoholo", - "almozo", - "alnomo", - "alojo", - "alpinisto", - "alrigardi", - "alskribi", - "alta", - "alumeto", - "alveni", - "alzaca", - "amaso", - "ambasado", - "amdeklaro", - "amebo", - "amfibio", - "amhara", - "amiko", - "amkanto", - "amletero", - "amnestio", - "amoranto", - "amplekso", - "amrakonto", - "amsterdama", - "amuzi", - "ananaso", - "androido", - "anekdoto", - "anfrakto", - "angulo", - "anheli", - "animo", - "anjono", - "ankro", - "anonci", - "anpriskribo", - "ansero", - "antikva", - "anuitato", - "aorto", - "aparta", - "aperti", - "apika", - "aplikado", - "apneo", - "apogi", - "aprobi", - "apsido", - "apterigo", - "apudesto", - "araneo", - "arbo", - "ardeco", - "aresti", - "argilo", - "aristokrato", - "arko", - "arlekeno", - "armi", - "arniko", - "aromo", - "arpio", - "arsenalo", - "artisto", - "aruba", - "arvorto", - "asaio", - "asbesto", - "ascendi", - "asekuri", - "asfalto", - "asisti", - "askalono", - "asocio", - "aspekti", - "astro", - "asulo", - "atakonto", - "atendi", - "atingi", - "atleto", - "atmosfero", - "atomo", - "atropino", - "atuto", - "avataro", - "aventuro", - "aviadilo", - "avokado", - "azaleo", - "azbuko", - "azenino", - "azilpetanto", - "azoto", - "azteka", - "babili", - "bacilo", - "badmintono", - "bagatelo", - "bahama", - "bajoneto", - "baki", - "balai", - "bambuo", - "bani", - "baobabo", - "bapti", - "baro", - "bastono", - "batilo", - "bavara", - "bazalto", - "beata", - "bebofono", - "bedo", - "begonio", - "behaviorismo", - "bejlo", - "bekero", - "belarto", - "bemolo", - "benko", - "bereto", - "besto", - "betulo", - "bevelo", - "bezoni", - "biaso", - "biblioteko", - "biciklo", - "bidaro", - "bieno", - "bifsteko", - "bigamiulo", - "bijekcio", - "bikino", - "bildo", - "bimetalismo", - "bindi", - "biografio", - "birdo", - "biskvito", - "bitlibro", - "bivako", - "bizara", - "bjalistoka", - "blanka", - "bleki", - "blinda", - "blovi", - "blua", - "boato", - "bobsledo", - "bocvanano", - "bodisatvo", - "bofratino", - "bogefratoj", - "bohema", - "boji", - "bokalo", - "boli", - "bombono", - "bona", - "bopatrino", - "bordo", - "bosko", - "botelo", - "bovido", - "brakpleno", - "bretaro", - "brikmuro", - "broso", - "brulema", - "bubalo", - "buctrapi", - "budo", - "bufedo", - "bugio", - "bujabeso", - "buklo", - "buldozo", - "bumerango", - "bunta", - "burokrataro", - "busbileto", - "butero", - "buzuko", - "caro", - "cebo", - "ceceo", - "cedro", - "cefalo", - "cejana", - "cekumo", - "celebri", - "cemento", - "cent", - "cepo", - "certa", - "cetera", - "cezio", - "ciano", - "cibeto", - "cico", - "cidro", - "cifero", - "cigaredo", - "ciklo", - "cilindro", - "cimbalo", - "cinamo", - "cipreso", - "cirkonstanco", - "cisterno", - "citrono", - "ciumi", - "civilizado", - "colo", - "congo", - "cunamo", - "cvana", - "dabi", - "daco", - "dadaismo", - "dafodilo", - "dago", - "daimio", - "dajmono", - "daktilo", - "dalio", - "damo", - "danki", - "darmo", - "datumoj", - "dazipo", - "deadmoni", - "debeto", - "decidi", - "dedukti", - "deerigi", - "defendi", - "degeli", - "dehaki", - "deirpunkto", - "deklaracio", - "delikata", - "demandi", - "dento", - "dependi", - "derivi", - "desegni", - "detrui", - "devi", - "deziri", - "dialogo", - "dicentro", - "didaktika", - "dieto", - "diferenci", - "digesti", - "diino", - "dikfingro", - "diligenta", - "dimensio", - "dinamo", - "diodo", - "diplomo", - "direkte", - "diskuti", - "diurno", - "diversa", - "dizajno", - "dobrogitaro", - "docento", - "dogano", - "dojeno", - "doktoro", - "dolori", - "domego", - "donaci", - "dopado", - "dormi", - "dosierujo", - "dotita", - "dozeno", - "drato", - "dresi", - "drinki", - "droni", - "druido", - "duaranga", - "dubi", - "ducent", - "dudek", - "duelo", - "dufoje", - "dugongo", - "duhufa", - "duilo", - "dujare", - "dukato", - "duloka", - "dumtempe", - "dungi", - "duobla", - "dupiedulo", - "dura", - "dusenca", - "dutaga", - "duuma", - "duvalvuloj", - "duzo", - "ebena", - "eblecoj", - "ebono", - "ebria", - "eburo", - "ecaro", - "ecigi", - "ecoj", - "edelvejso", - "editoro", - "edro", - "eduki", - "edzino", - "efektiva", - "efiki", - "efloreski", - "egala", - "egeco", - "egiptologo", - "eglefino", - "egoista", - "egreto", - "ejakuli", - "ejlo", - "ekarto", - "ekbruligi", - "ekceli", - "ekde", - "ekesti", - "ekfirmao", - "ekgliti", - "ekhavi", - "ekipi", - "ekkapti", - "eklezio", - "ekmalsati", - "ekonomio", - "ekpluvi", - "ekrano", - "ekster", - "ektiri", - "ekumeno", - "ekvilibro", - "ekzemplo", - "elasta", - "elbalai", - "elcento", - "eldoni", - "elektro", - "elfari", - "elgliti", - "elhaki", - "elipso", - "elkovi", - "ellasi", - "elmeti", - "elnutri", - "elokventa", - "elparoli", - "elrevigi", - "elstari", - "elteni", - "eluzita", - "elvoki", - "elzasa", - "emajlo", - "embaraso", - "emerito", - "emfazo", - "eminenta", - "emocio", - "empiria", - "emulsio", - "enarkivigi", - "enboteligi", - "enciklopedio", - "endorfino", - "energio", - "enfermi", - "engluti", - "enhavo", - "enigmo", - "enjekcio", - "enketi", - "enlanda", - "enmeti", - "enorma", - "enplanti", - "enradiki", - "enspezo", - "entrepreni", - "enui", - "envolvi", - "enzimo", - "eono", - "eosto", - "epitafo", - "epoko", - "epriskribebla", - "epsilono", - "erari", - "erbio", - "erco", - "erekti", - "ergonomia", - "erikejo", - "ermito", - "erotika", - "erpilo", - "erupcio", - "esameno", - "escepti", - "esenco", - "eskapi", - "esotera", - "esperi", - "estonto", - "etapo", - "etendi", - "etfingro", - "etikedo", - "etlitero", - "etmakleristo", - "etnika", - "etoso", - "etradio", - "etskala", - "etullernejo", - "evakui", - "evento", - "eviti", - "evolui", - "ezoko", - "fabriko", - "facila", - "fadeno", - "fagoto", - "fajro", - "fakto", - "fali", - "familio", - "fanatiko", - "farbo", - "fasko", - "fatala", - "favora", - "fazeolo", - "febro", - "federacio", - "feino", - "fekunda", - "felo", - "femuro", - "fenestro", - "fermi", - "festi", - "fetora", - "fezo", - "fiasko", - "fibro", - "fidela", - "fiera", - "fifama", - "figuro", - "fiherbo", - "fiinsekto", - "fiksa", - "filmo", - "fimensa", - "finalo", - "fiolo", - "fiparoli", - "firmao", - "fisko", - "fitingo", - "fiuzanto", - "fivorto", - "fiziko", - "fjordo", - "flago", - "flegi", - "flirti", - "floro", - "flugi", - "fobio", - "foceno", - "foirejo", - "fojfoje", - "fokuso", - "folio", - "fomenti", - "fonto", - "formulo", - "fosforo", - "fotografi", - "fratino", - "fremda", - "friti", - "frosto", - "frua", - "ftizo", - "fuelo", - "fugo", - "fuksia", - "fulmilo", - "fumanto", - "fundamento", - "fuorto", - "furioza", - "fusilo", - "futbalo", - "fuzio", - "gabardino", - "gado", - "gaela", - "gafo", - "gagato", - "gaja", - "gaki", - "galanta", - "gamao", - "ganto", - "gapulo", - "gardi", - "gasto", - "gavio", - "gazeto", - "geamantoj", - "gebani", - "geedzeco", - "gefratoj", - "geheno", - "gejsero", - "geko", - "gelateno", - "gemisto", - "geniulo", - "geografio", - "gepardo", - "geranio", - "gestolingvo", - "geto", - "geumo", - "gibono", - "giganta", - "gildo", - "gimnastiko", - "ginekologo", - "gipsi", - "girlando", - "gistfungo", - "gitaro", - "glazuro", - "glebo", - "gliti", - "globo", - "gluti", - "gnafalio", - "gnejso", - "gnomo", - "gnuo", - "gobio", - "godetio", - "goeleto", - "gojo", - "golfludejo", - "gombo", - "gondolo", - "gorilo", - "gospelo", - "gotika", - "granda", - "greno", - "griza", - "groto", - "grupo", - "guano", - "gubernatoro", - "gudrotuko", - "gufo", - "gujavo", - "guldeno", - "gumi", - "gupio", - "guruo", - "gusto", - "guto", - "guvernistino", - "gvardio", - "gverilo", - "gvidanto", - "habitato", - "hadito", - "hafnio", - "hagiografio", - "haitiano", - "hajlo", - "hakbloko", - "halti", - "hamstro", - "hangaro", - "hapalo", - "haro", - "hasta", - "hati", - "havebla", - "hazardo", - "hebrea", - "hedero", - "hegemonio", - "hejmo", - "hektaro", - "helpi", - "hemisfero", - "heni", - "hepato", - "herbo", - "hesa", - "heterogena", - "heziti", - "hiacinto", - "hibrida", - "hidrogeno", - "hieroglifo", - "higieno", - "hihii", - "hilumo", - "himno", - "hindino", - "hiperteksto", - "hirundo", - "historio", - "hobio", - "hojli", - "hokeo", - "hologramo", - "homido", - "honesta", - "hopi", - "horizonto", - "hospitalo", - "hotelo", - "huadi", - "hubo", - "hufumo", - "hugenoto", - "hukero", - "huligano", - "humana", - "hundo", - "huoj", - "hupilo", - "hurai", - "husaro", - "hutuo", - "huzo", - "iafoje", - "iagrade", - "iamaniere", - "iarelate", - "iaspeca", - "ibekso", - "ibiso", - "idaro", - "ideala", - "idiomo", - "idolo", - "iele", - "igluo", - "ignori", - "iguamo", - "igvano", - "ikono", - "iksodo", - "ikto", - "iliaflanke", - "ilkomputilo", - "ilobreto", - "ilremedo", - "ilumini", - "imagi", - "imitado", - "imperio", - "imuna", - "incidento", - "industrio", - "inerta", - "infano", - "ingenra", - "inhali", - "iniciati", - "injekti", - "inklino", - "inokuli", - "insekto", - "inteligenta", - "inundi", - "inviti", - "ioma", - "ionosfero", - "iperito", - "ipomeo", - "irana", - "irejo", - "irigacio", - "ironio", - "isato", - "islamo", - "istempo", - "itinero", - "itrio", - "iuloke", - "iumaniere", - "iutempe", - "izolita", - "jado", - "jaguaro", - "jakto", - "jama", - "januaro", - "japano", - "jarringo", - "jazo", - "jenoj", - "jesulo", - "jetavio", - "jezuito", - "jodli", - "joviala", - "juano", - "jubileo", - "judismo", - "jufto", - "juki", - "julio", - "juneca", - "jupo", - "juristo", - "juste", - "juvelo", - "kabineto", - "kadrato", - "kafo", - "kahelo", - "kajako", - "kakao", - "kalkuli", - "kampo", - "kanti", - "kapitalo", - "karaktero", - "kaserolo", - "katapulto", - "kaverna", - "kazino", - "kebabo", - "kefiro", - "keglo", - "kejlo", - "kekso", - "kelka", - "kemio", - "kerno", - "kesto", - "kiamaniere", - "kibuco", - "kidnapi", - "kielo", - "kikero", - "kilogramo", - "kimono", - "kinejo", - "kiosko", - "kirurgo", - "kisi", - "kitelo", - "kivio", - "klavaro", - "klerulo", - "klini", - "klopodi", - "klubo", - "knabo", - "knedi", - "koalo", - "kobalto", - "kodigi", - "kofro", - "kohera", - "koincidi", - "kojoto", - "kokoso", - "koloro", - "komenci", - "kontrakto", - "kopio", - "korekte", - "kosti", - "kotono", - "kovri", - "krajono", - "kredi", - "krii", - "krom", - "kruco", - "ksantino", - "ksenono", - "ksilofono", - "ksosa", - "kubuto", - "kudri", - "kuglo", - "kuiri", - "kuko", - "kulero", - "kumuluso", - "kuneco", - "kupro", - "kuri", - "kuseno", - "kutimo", - "kuvo", - "kuzino", - "kvalito", - "kverko", - "kvin", - "kvoto", - "labori", - "laculo", - "ladbotelo", - "lafo", - "laguno", - "laikino", - "laktobovino", - "lampolumo", - "landkarto", - "laosa", - "lapono", - "larmoguto", - "lastjare", - "latitudo", - "lavejo", - "lazanjo", - "leciono", - "ledosako", - "leganto", - "lekcio", - "lemura", - "lentuga", - "leopardo", - "leporo", - "lerni", - "lesivo", - "letero", - "levilo", - "lezi", - "liano", - "libera", - "liceo", - "lieno", - "lifto", - "ligilo", - "likvoro", - "lila", - "limono", - "lingvo", - "lipo", - "lirika", - "listo", - "literatura", - "liveri", - "lobio", - "logika", - "lojala", - "lokalo", - "longa", - "lordo", - "lotado", - "loza", - "luanto", - "lubriki", - "lucida", - "ludema", - "luigi", - "lukso", - "luli", - "lumbilda", - "lunde", - "lupago", - "lustro", - "lutilo", - "luzerno", - "maato", - "maceri", - "madono", - "mafiano", - "magazeno", - "mahometano", - "maizo", - "majstro", - "maketo", - "malgranda", - "mamo", - "mandareno", - "maorio", - "mapigi", - "marini", - "masko", - "mateno", - "mazuto", - "meandro", - "meblo", - "mecenato", - "medialo", - "mefito", - "megafono", - "mejlo", - "mekanika", - "melodia", - "membro", - "mendi", - "mergi", - "mespilo", - "metoda", - "mevo", - "mezuri", - "miaflanke", - "micelio", - "mielo", - "migdalo", - "mikrofilmo", - "militi", - "mimiko", - "mineralo", - "miopa", - "miri", - "mistera", - "mitralo", - "mizeri", - "mjelo", - "mnemoniko", - "mobilizi", - "mocio", - "moderna", - "mohajro", - "mokadi", - "molaro", - "momento", - "monero", - "mopso", - "mordi", - "moskito", - "motoro", - "movimento", - "mozaiko", - "mueli", - "mukozo", - "muldi", - "mumio", - "munti", - "muro", - "muskolo", - "mutacio", - "muzikisto", - "nabo", - "nacio", - "nadlo", - "nafto", - "naiva", - "najbaro", - "nanometro", - "napo", - "narciso", - "naski", - "naturo", - "navigi", - "naztruo", - "neatendite", - "nebulo", - "necesa", - "nedankinde", - "neebla", - "nefari", - "negoco", - "nehavi", - "neimagebla", - "nektaro", - "nelonga", - "nematura", - "nenia", - "neordinara", - "nepra", - "nervuro", - "nesto", - "nete", - "neulo", - "nevino", - "nifo", - "nigra", - "nihilisto", - "nikotino", - "nilono", - "nimfeo", - "nitrogeno", - "nivelo", - "nobla", - "nocio", - "nodozo", - "nokto", - "nomkarto", - "norda", - "nostalgio", - "notbloko", - "novico", - "nuanco", - "nuboza", - "nuda", - "nugato", - "nuklea", - "nuligi", - "numero", - "nuntempe", - "nupto", - "nura", - "nutri", - "oazo", - "obei", - "objekto", - "oblikva", - "obolo", - "observi", - "obtuza", - "obuso", - "oceano", - "odekolono", - "odori", - "oferti", - "oficiala", - "ofsajdo", - "ofte", - "ogivo", - "ogro", - "ojstredoj", - "okaze", - "okcidenta", - "okro", - "oksido", - "oktobro", - "okulo", - "oldulo", - "oleo", - "olivo", - "omaro", - "ombro", - "omego", - "omikrono", - "omleto", - "omnibuso", - "onagro", - "ondo", - "oneco", - "onidire", - "onklino", - "onlajna", - "onomatopeo", - "ontologio", - "opaka", - "operacii", - "opinii", - "oportuna", - "opresi", - "optimisto", - "oratoro", - "orbito", - "ordinara", - "orelo", - "orfino", - "organizi", - "orienta", - "orkestro", - "orlo", - "orminejo", - "ornami", - "ortangulo", - "orumi", - "oscedi", - "osmozo", - "ostocerbo", - "ovalo", - "ovingo", - "ovoblanko", - "ovri", - "ovulado", - "ozono", - "pacama", - "padeli", - "pafilo", - "pagigi", - "pajlo", - "paketo", - "palaco", - "pampelmo", - "pantalono", - "papero", - "paroli", - "pasejo", - "patro", - "pavimo", - "peco", - "pedalo", - "peklita", - "pelikano", - "pensiono", - "peplomo", - "pesilo", - "petanto", - "pezoforto", - "piano", - "picejo", - "piede", - "pigmento", - "pikema", - "pilkoludo", - "pimento", - "pinglo", - "pioniro", - "pipromento", - "pirato", - "pistolo", - "pitoreska", - "piulo", - "pivoti", - "pizango", - "planko", - "plektita", - "plibonigi", - "ploradi", - "plurlingva", - "pobo", - "podio", - "poeto", - "pogranda", - "pohora", - "pokalo", - "politekniko", - "pomarbo", - "ponevosto", - "populara", - "porcelana", - "postkompreno", - "poteto", - "poviga", - "pozitiva", - "prapatroj", - "precize", - "pridemandi", - "probable", - "pruntanto", - "psalmo", - "psikologio", - "psoriazo", - "pterido", - "publiko", - "pudro", - "pufo", - "pugnobato", - "pulovero", - "pumpi", - "punkto", - "pupo", - "pureo", - "puso", - "putrema", - "puzlo", - "rabate", - "racionala", - "radiko", - "rafinado", - "raguo", - "rajto", - "rakonti", - "ralio", - "rampi", - "rando", - "rapida", - "rastruma", - "ratifiki", - "raviolo", - "razeno", - "reakcio", - "rebildo", - "recepto", - "redakti", - "reenigi", - "reformi", - "regiono", - "rehavi", - "reinspekti", - "rejesi", - "reklamo", - "relativa", - "rememori", - "renkonti", - "reorganizado", - "reprezenti", - "respondi", - "retumilo", - "reuzebla", - "revidi", - "rezulti", - "rialo", - "ribeli", - "ricevi", - "ridiga", - "rifuginto", - "rigardi", - "rikolti", - "rilati", - "rimarki", - "rinocero", - "ripozi", - "riski", - "ritmo", - "rivero", - "rizokampo", - "roboto", - "rododendro", - "rojo", - "rokmuziko", - "rolvorto", - "romantika", - "ronroni", - "rosino", - "rotondo", - "rovero", - "rozeto", - "rubando", - "rudimenta", - "rufa", - "rugbeo", - "ruino", - "ruleto", - "rumoro", - "runo", - "rupio", - "rura", - "rustimuna", - "ruzulo", - "sabato", - "sadismo", - "safario", - "sagaca", - "sakfluto", - "salti", - "samtage", - "sandalo", - "sapejo", - "sarongo", - "satelito", - "savano", - "sbiro", - "sciado", - "seanco", - "sebo", - "sedativo", - "segligno", - "sekretario", - "selektiva", - "semajno", - "senpeza", - "separeo", - "servilo", - "sesangulo", - "setli", - "seurigi", - "severa", - "sezono", - "sfagno", - "sfero", - "sfinkso", - "siatempe", - "siblado", - "sidejo", - "siesto", - "sifono", - "signalo", - "siklo", - "silenti", - "simpla", - "sinjoro", - "siropo", - "sistemo", - "situacio", - "siverto", - "sizifa", - "skatolo", - "skemo", - "skianto", - "sklavo", - "skorpio", - "skribisto", - "skulpti", - "skvamo", - "slango", - "sledeto", - "sliparo", - "smeraldo", - "smirgi", - "smokingo", - "smuto", - "snoba", - "snufegi", - "sobra", - "sociano", - "sodakvo", - "sofo", - "soifi", - "sojlo", - "soklo", - "soldato", - "somero", - "sonilo", - "sopiri", - "sorto", - "soulo", - "soveto", - "sparkado", - "speciala", - "spiri", - "splito", - "sporto", - "sprita", - "spuro", - "stabila", - "stelfiguro", - "stimulo", - "stomako", - "strato", - "studanto", - "subgrupo", - "suden", - "suferanta", - "sugesti", - "suito", - "sukero", - "sulko", - "sume", - "sunlumo", - "super", - "surskribeto", - "suspekti", - "suturo", - "svati", - "svenfali", - "svingi", - "svopo", - "tabako", - "taglumo", - "tajloro", - "taksimetro", - "talento", - "tamen", - "tanko", - "taoismo", - "tapioko", - "tarifo", - "tasko", - "tatui", - "taverno", - "teatro", - "tedlaboro", - "tegmento", - "tehoro", - "teknika", - "telefono", - "tempo", - "tenisejo", - "teorie", - "teraso", - "testudo", - "tetablo", - "teujo", - "tezo", - "tialo", - "tibio", - "tielnomata", - "tifono", - "tigro", - "tikli", - "timida", - "tinkturo", - "tiom", - "tiparo", - "tirkesto", - "titolo", - "tiutempe", - "tizano", - "tobogano", - "tofeo", - "togo", - "toksa", - "tolerema", - "tombolo", - "tondri", - "topografio", - "tordeti", - "tosti", - "totalo", - "traduko", - "tredi", - "triangulo", - "tropika", - "trumpeto", - "tualeto", - "tubisto", - "tufgrebo", - "tuja", - "tukano", - "tulipo", - "tumulto", - "tunelo", - "turisto", - "tusi", - "tutmonda", - "tvisto", - "udono", - "uesto", - "ukazo", - "ukelelo", - "ulcero", - "ulmo", - "ultimato", - "ululi", - "umbiliko", - "unco", - "ungego", - "uniformo", - "unkti", - "unukolora", - "uragano", - "urbano", - "uretro", - "urino", - "ursido", - "uskleco", - "usonigi", - "utero", - "utila", - "utopia", - "uverturo", - "uzadi", - "uzeblo", - "uzino", - "uzkutimo", - "uzofini", - "uzurpi", - "uzvaloro", - "vadejo", - "vafleto", - "vagono", - "vahabismo", - "vajco", - "vakcino", - "valoro", - "vampiro", - "vangharoj", - "vaporo", - "varma", - "vasta", - "vato", - "vazaro", - "veaspekta", - "vedismo", - "vegetalo", - "vehiklo", - "vejno", - "vekita", - "velstango", - "vemieno", - "vendi", - "vepro", - "verando", - "vespero", - "veturi", - "veziko", - "viando", - "vibri", - "vico", - "videbla", - "vifio", - "vigla", - "viktimo", - "vila", - "vimeno", - "vintro", - "violo", - "vippuno", - "virtuala", - "viskoza", - "vitro", - "viveca", - "viziti", - "vobli", - "vodko", - "vojeto", - "vokegi", - "volbo", - "vomema", - "vono", - "vortaro", - "vosto", - "voti", - "vrako", - "vringi", - "vualo", - "vulkano", - "vundo", - "vuvuzelo", - "zamenhofa", - "zapi", - "zebro", - "zefiro", - "zeloto", - "zenismo", - "zeolito", - "zepelino", - "zeto", - "zigzagi", - "zinko", - "zipo", - "zirkonio", - "zodiako", - "zoeto", - "zombio", - "zono", - "zoologio", - "zorgi", - "zukino", - "zumilo", + u"abako", + u"abdiki", + u"abelo", + u"abituriento", + u"ablativo", + u"abnorma", + u"abonantoj", + u"abrikoto", + u"absoluta", + u"abunda", + u"acetono", + u"acida", + u"adapti", + u"adekvata", + u"adheri", + u"adicii", + u"adjektivo", + u"administri", + u"adolesko", + u"adreso", + u"adstringa", + u"adulto", + u"advokato", + u"adzo", + u"aeroplano", + u"aferulo", + u"afgana", + u"afiksi", + u"aflaba", + u"aforismo", + u"afranki", + u"aftozo", + u"afusto", + u"agavo", + u"agento", + u"agiti", + u"aglo", + u"agmaniero", + u"agnoski", + u"agordo", + u"agrabla", + u"agtipo", + u"agutio", + u"aikido", + u"ailanto", + u"aina", + u"ajatolo", + u"ajgenvaloro", + u"ajlobulbo", + u"ajnlitera", + u"ajuto", + u"ajzi", + u"akademio", + u"akcepti", + u"akeo", + u"akiri", + u"aklamado", + u"akmeo", + u"akno", + u"akompani", + u"akrobato", + u"akselo", + u"aktiva", + u"akurata", + u"akvofalo", + u"alarmo", + u"albumo", + u"alcedo", + u"aldoni", + u"aleo", + u"alfabeto", + u"algo", + u"alhasti", + u"aligatoro", + u"alkoholo", + u"almozo", + u"alnomo", + u"alojo", + u"alpinisto", + u"alrigardi", + u"alskribi", + u"alta", + u"alumeto", + u"alveni", + u"alzaca", + u"amaso", + u"ambasado", + u"amdeklaro", + u"amebo", + u"amfibio", + u"amhara", + u"amiko", + u"amkanto", + u"amletero", + u"amnestio", + u"amoranto", + u"amplekso", + u"amrakonto", + u"amsterdama", + u"amuzi", + u"ananaso", + u"androido", + u"anekdoto", + u"anfrakto", + u"angulo", + u"anheli", + u"animo", + u"anjono", + u"ankro", + u"anonci", + u"anpriskribo", + u"ansero", + u"antikva", + u"anuitato", + u"aorto", + u"aparta", + u"aperti", + u"apika", + u"aplikado", + u"apneo", + u"apogi", + u"aprobi", + u"apsido", + u"apterigo", + u"apudesto", + u"araneo", + u"arbo", + u"ardeco", + u"aresti", + u"argilo", + u"aristokrato", + u"arko", + u"arlekeno", + u"armi", + u"arniko", + u"aromo", + u"arpio", + u"arsenalo", + u"artisto", + u"aruba", + u"arvorto", + u"asaio", + u"asbesto", + u"ascendi", + u"asekuri", + u"asfalto", + u"asisti", + u"askalono", + u"asocio", + u"aspekti", + u"astro", + u"asulo", + u"atakonto", + u"atendi", + u"atingi", + u"atleto", + u"atmosfero", + u"atomo", + u"atropino", + u"atuto", + u"avataro", + u"aventuro", + u"aviadilo", + u"avokado", + u"azaleo", + u"azbuko", + u"azenino", + u"azilpetanto", + u"azoto", + u"azteka", + u"babili", + u"bacilo", + u"badmintono", + u"bagatelo", + u"bahama", + u"bajoneto", + u"baki", + u"balai", + u"bambuo", + u"bani", + u"baobabo", + u"bapti", + u"baro", + u"bastono", + u"batilo", + u"bavara", + u"bazalto", + u"beata", + u"bebofono", + u"bedo", + u"begonio", + u"behaviorismo", + u"bejlo", + u"bekero", + u"belarto", + u"bemolo", + u"benko", + u"bereto", + u"besto", + u"betulo", + u"bevelo", + u"bezoni", + u"biaso", + u"biblioteko", + u"biciklo", + u"bidaro", + u"bieno", + u"bifsteko", + u"bigamiulo", + u"bijekcio", + u"bikino", + u"bildo", + u"bimetalismo", + u"bindi", + u"biografio", + u"birdo", + u"biskvito", + u"bitlibro", + u"bivako", + u"bizara", + u"bjalistoka", + u"blanka", + u"bleki", + u"blinda", + u"blovi", + u"blua", + u"boato", + u"bobsledo", + u"bocvanano", + u"bodisatvo", + u"bofratino", + u"bogefratoj", + u"bohema", + u"boji", + u"bokalo", + u"boli", + u"bombono", + u"bona", + u"bopatrino", + u"bordo", + u"bosko", + u"botelo", + u"bovido", + u"brakpleno", + u"bretaro", + u"brikmuro", + u"broso", + u"brulema", + u"bubalo", + u"buctrapi", + u"budo", + u"bufedo", + u"bugio", + u"bujabeso", + u"buklo", + u"buldozo", + u"bumerango", + u"bunta", + u"burokrataro", + u"busbileto", + u"butero", + u"buzuko", + u"caro", + u"cebo", + u"ceceo", + u"cedro", + u"cefalo", + u"cejana", + u"cekumo", + u"celebri", + u"cemento", + u"cent", + u"cepo", + u"certa", + u"cetera", + u"cezio", + u"ciano", + u"cibeto", + u"cico", + u"cidro", + u"cifero", + u"cigaredo", + u"ciklo", + u"cilindro", + u"cimbalo", + u"cinamo", + u"cipreso", + u"cirkonstanco", + u"cisterno", + u"citrono", + u"ciumi", + u"civilizado", + u"colo", + u"congo", + u"cunamo", + u"cvana", + u"dabi", + u"daco", + u"dadaismo", + u"dafodilo", + u"dago", + u"daimio", + u"dajmono", + u"daktilo", + u"dalio", + u"damo", + u"danki", + u"darmo", + u"datumoj", + u"dazipo", + u"deadmoni", + u"debeto", + u"decidi", + u"dedukti", + u"deerigi", + u"defendi", + u"degeli", + u"dehaki", + u"deirpunkto", + u"deklaracio", + u"delikata", + u"demandi", + u"dento", + u"dependi", + u"derivi", + u"desegni", + u"detrui", + u"devi", + u"deziri", + u"dialogo", + u"dicentro", + u"didaktika", + u"dieto", + u"diferenci", + u"digesti", + u"diino", + u"dikfingro", + u"diligenta", + u"dimensio", + u"dinamo", + u"diodo", + u"diplomo", + u"direkte", + u"diskuti", + u"diurno", + u"diversa", + u"dizajno", + u"dobrogitaro", + u"docento", + u"dogano", + u"dojeno", + u"doktoro", + u"dolori", + u"domego", + u"donaci", + u"dopado", + u"dormi", + u"dosierujo", + u"dotita", + u"dozeno", + u"drato", + u"dresi", + u"drinki", + u"droni", + u"druido", + u"duaranga", + u"dubi", + u"ducent", + u"dudek", + u"duelo", + u"dufoje", + u"dugongo", + u"duhufa", + u"duilo", + u"dujare", + u"dukato", + u"duloka", + u"dumtempe", + u"dungi", + u"duobla", + u"dupiedulo", + u"dura", + u"dusenca", + u"dutaga", + u"duuma", + u"duvalvuloj", + u"duzo", + u"ebena", + u"eblecoj", + u"ebono", + u"ebria", + u"eburo", + u"ecaro", + u"ecigi", + u"ecoj", + u"edelvejso", + u"editoro", + u"edro", + u"eduki", + u"edzino", + u"efektiva", + u"efiki", + u"efloreski", + u"egala", + u"egeco", + u"egiptologo", + u"eglefino", + u"egoista", + u"egreto", + u"ejakuli", + u"ejlo", + u"ekarto", + u"ekbruligi", + u"ekceli", + u"ekde", + u"ekesti", + u"ekfirmao", + u"ekgliti", + u"ekhavi", + u"ekipi", + u"ekkapti", + u"eklezio", + u"ekmalsati", + u"ekonomio", + u"ekpluvi", + u"ekrano", + u"ekster", + u"ektiri", + u"ekumeno", + u"ekvilibro", + u"ekzemplo", + u"elasta", + u"elbalai", + u"elcento", + u"eldoni", + u"elektro", + u"elfari", + u"elgliti", + u"elhaki", + u"elipso", + u"elkovi", + u"ellasi", + u"elmeti", + u"elnutri", + u"elokventa", + u"elparoli", + u"elrevigi", + u"elstari", + u"elteni", + u"eluzita", + u"elvoki", + u"elzasa", + u"emajlo", + u"embaraso", + u"emerito", + u"emfazo", + u"eminenta", + u"emocio", + u"empiria", + u"emulsio", + u"enarkivigi", + u"enboteligi", + u"enciklopedio", + u"endorfino", + u"energio", + u"enfermi", + u"engluti", + u"enhavo", + u"enigmo", + u"enjekcio", + u"enketi", + u"enlanda", + u"enmeti", + u"enorma", + u"enplanti", + u"enradiki", + u"enspezo", + u"entrepreni", + u"enui", + u"envolvi", + u"enzimo", + u"eono", + u"eosto", + u"epitafo", + u"epoko", + u"epriskribebla", + u"epsilono", + u"erari", + u"erbio", + u"erco", + u"erekti", + u"ergonomia", + u"erikejo", + u"ermito", + u"erotika", + u"erpilo", + u"erupcio", + u"esameno", + u"escepti", + u"esenco", + u"eskapi", + u"esotera", + u"esperi", + u"estonto", + u"etapo", + u"etendi", + u"etfingro", + u"etikedo", + u"etlitero", + u"etmakleristo", + u"etnika", + u"etoso", + u"etradio", + u"etskala", + u"etullernejo", + u"evakui", + u"evento", + u"eviti", + u"evolui", + u"ezoko", + u"fabriko", + u"facila", + u"fadeno", + u"fagoto", + u"fajro", + u"fakto", + u"fali", + u"familio", + u"fanatiko", + u"farbo", + u"fasko", + u"fatala", + u"favora", + u"fazeolo", + u"febro", + u"federacio", + u"feino", + u"fekunda", + u"felo", + u"femuro", + u"fenestro", + u"fermi", + u"festi", + u"fetora", + u"fezo", + u"fiasko", + u"fibro", + u"fidela", + u"fiera", + u"fifama", + u"figuro", + u"fiherbo", + u"fiinsekto", + u"fiksa", + u"filmo", + u"fimensa", + u"finalo", + u"fiolo", + u"fiparoli", + u"firmao", + u"fisko", + u"fitingo", + u"fiuzanto", + u"fivorto", + u"fiziko", + u"fjordo", + u"flago", + u"flegi", + u"flirti", + u"floro", + u"flugi", + u"fobio", + u"foceno", + u"foirejo", + u"fojfoje", + u"fokuso", + u"folio", + u"fomenti", + u"fonto", + u"formulo", + u"fosforo", + u"fotografi", + u"fratino", + u"fremda", + u"friti", + u"frosto", + u"frua", + u"ftizo", + u"fuelo", + u"fugo", + u"fuksia", + u"fulmilo", + u"fumanto", + u"fundamento", + u"fuorto", + u"furioza", + u"fusilo", + u"futbalo", + u"fuzio", + u"gabardino", + u"gado", + u"gaela", + u"gafo", + u"gagato", + u"gaja", + u"gaki", + u"galanta", + u"gamao", + u"ganto", + u"gapulo", + u"gardi", + u"gasto", + u"gavio", + u"gazeto", + u"geamantoj", + u"gebani", + u"geedzeco", + u"gefratoj", + u"geheno", + u"gejsero", + u"geko", + u"gelateno", + u"gemisto", + u"geniulo", + u"geografio", + u"gepardo", + u"geranio", + u"gestolingvo", + u"geto", + u"geumo", + u"gibono", + u"giganta", + u"gildo", + u"gimnastiko", + u"ginekologo", + u"gipsi", + u"girlando", + u"gistfungo", + u"gitaro", + u"glazuro", + u"glebo", + u"gliti", + u"globo", + u"gluti", + u"gnafalio", + u"gnejso", + u"gnomo", + u"gnuo", + u"gobio", + u"godetio", + u"goeleto", + u"gojo", + u"golfludejo", + u"gombo", + u"gondolo", + u"gorilo", + u"gospelo", + u"gotika", + u"granda", + u"greno", + u"griza", + u"groto", + u"grupo", + u"guano", + u"gubernatoro", + u"gudrotuko", + u"gufo", + u"gujavo", + u"guldeno", + u"gumi", + u"gupio", + u"guruo", + u"gusto", + u"guto", + u"guvernistino", + u"gvardio", + u"gverilo", + u"gvidanto", + u"habitato", + u"hadito", + u"hafnio", + u"hagiografio", + u"haitiano", + u"hajlo", + u"hakbloko", + u"halti", + u"hamstro", + u"hangaro", + u"hapalo", + u"haro", + u"hasta", + u"hati", + u"havebla", + u"hazardo", + u"hebrea", + u"hedero", + u"hegemonio", + u"hejmo", + u"hektaro", + u"helpi", + u"hemisfero", + u"heni", + u"hepato", + u"herbo", + u"hesa", + u"heterogena", + u"heziti", + u"hiacinto", + u"hibrida", + u"hidrogeno", + u"hieroglifo", + u"higieno", + u"hihii", + u"hilumo", + u"himno", + u"hindino", + u"hiperteksto", + u"hirundo", + u"historio", + u"hobio", + u"hojli", + u"hokeo", + u"hologramo", + u"homido", + u"honesta", + u"hopi", + u"horizonto", + u"hospitalo", + u"hotelo", + u"huadi", + u"hubo", + u"hufumo", + u"hugenoto", + u"hukero", + u"huligano", + u"humana", + u"hundo", + u"huoj", + u"hupilo", + u"hurai", + u"husaro", + u"hutuo", + u"huzo", + u"iafoje", + u"iagrade", + u"iamaniere", + u"iarelate", + u"iaspeca", + u"ibekso", + u"ibiso", + u"idaro", + u"ideala", + u"idiomo", + u"idolo", + u"iele", + u"igluo", + u"ignori", + u"iguamo", + u"igvano", + u"ikono", + u"iksodo", + u"ikto", + u"iliaflanke", + u"ilkomputilo", + u"ilobreto", + u"ilremedo", + u"ilumini", + u"imagi", + u"imitado", + u"imperio", + u"imuna", + u"incidento", + u"industrio", + u"inerta", + u"infano", + u"ingenra", + u"inhali", + u"iniciati", + u"injekti", + u"inklino", + u"inokuli", + u"insekto", + u"inteligenta", + u"inundi", + u"inviti", + u"ioma", + u"ionosfero", + u"iperito", + u"ipomeo", + u"irana", + u"irejo", + u"irigacio", + u"ironio", + u"isato", + u"islamo", + u"istempo", + u"itinero", + u"itrio", + u"iuloke", + u"iumaniere", + u"iutempe", + u"izolita", + u"jado", + u"jaguaro", + u"jakto", + u"jama", + u"januaro", + u"japano", + u"jarringo", + u"jazo", + u"jenoj", + u"jesulo", + u"jetavio", + u"jezuito", + u"jodli", + u"joviala", + u"juano", + u"jubileo", + u"judismo", + u"jufto", + u"juki", + u"julio", + u"juneca", + u"jupo", + u"juristo", + u"juste", + u"juvelo", + u"kabineto", + u"kadrato", + u"kafo", + u"kahelo", + u"kajako", + u"kakao", + u"kalkuli", + u"kampo", + u"kanti", + u"kapitalo", + u"karaktero", + u"kaserolo", + u"katapulto", + u"kaverna", + u"kazino", + u"kebabo", + u"kefiro", + u"keglo", + u"kejlo", + u"kekso", + u"kelka", + u"kemio", + u"kerno", + u"kesto", + u"kiamaniere", + u"kibuco", + u"kidnapi", + u"kielo", + u"kikero", + u"kilogramo", + u"kimono", + u"kinejo", + u"kiosko", + u"kirurgo", + u"kisi", + u"kitelo", + u"kivio", + u"klavaro", + u"klerulo", + u"klini", + u"klopodi", + u"klubo", + u"knabo", + u"knedi", + u"koalo", + u"kobalto", + u"kodigi", + u"kofro", + u"kohera", + u"koincidi", + u"kojoto", + u"kokoso", + u"koloro", + u"komenci", + u"kontrakto", + u"kopio", + u"korekte", + u"kosti", + u"kotono", + u"kovri", + u"krajono", + u"kredi", + u"krii", + u"krom", + u"kruco", + u"ksantino", + u"ksenono", + u"ksilofono", + u"ksosa", + u"kubuto", + u"kudri", + u"kuglo", + u"kuiri", + u"kuko", + u"kulero", + u"kumuluso", + u"kuneco", + u"kupro", + u"kuri", + u"kuseno", + u"kutimo", + u"kuvo", + u"kuzino", + u"kvalito", + u"kverko", + u"kvin", + u"kvoto", + u"labori", + u"laculo", + u"ladbotelo", + u"lafo", + u"laguno", + u"laikino", + u"laktobovino", + u"lampolumo", + u"landkarto", + u"laosa", + u"lapono", + u"larmoguto", + u"lastjare", + u"latitudo", + u"lavejo", + u"lazanjo", + u"leciono", + u"ledosako", + u"leganto", + u"lekcio", + u"lemura", + u"lentuga", + u"leopardo", + u"leporo", + u"lerni", + u"lesivo", + u"letero", + u"levilo", + u"lezi", + u"liano", + u"libera", + u"liceo", + u"lieno", + u"lifto", + u"ligilo", + u"likvoro", + u"lila", + u"limono", + u"lingvo", + u"lipo", + u"lirika", + u"listo", + u"literatura", + u"liveri", + u"lobio", + u"logika", + u"lojala", + u"lokalo", + u"longa", + u"lordo", + u"lotado", + u"loza", + u"luanto", + u"lubriki", + u"lucida", + u"ludema", + u"luigi", + u"lukso", + u"luli", + u"lumbilda", + u"lunde", + u"lupago", + u"lustro", + u"lutilo", + u"luzerno", + u"maato", + u"maceri", + u"madono", + u"mafiano", + u"magazeno", + u"mahometano", + u"maizo", + u"majstro", + u"maketo", + u"malgranda", + u"mamo", + u"mandareno", + u"maorio", + u"mapigi", + u"marini", + u"masko", + u"mateno", + u"mazuto", + u"meandro", + u"meblo", + u"mecenato", + u"medialo", + u"mefito", + u"megafono", + u"mejlo", + u"mekanika", + u"melodia", + u"membro", + u"mendi", + u"mergi", + u"mespilo", + u"metoda", + u"mevo", + u"mezuri", + u"miaflanke", + u"micelio", + u"mielo", + u"migdalo", + u"mikrofilmo", + u"militi", + u"mimiko", + u"mineralo", + u"miopa", + u"miri", + u"mistera", + u"mitralo", + u"mizeri", + u"mjelo", + u"mnemoniko", + u"mobilizi", + u"mocio", + u"moderna", + u"mohajro", + u"mokadi", + u"molaro", + u"momento", + u"monero", + u"mopso", + u"mordi", + u"moskito", + u"motoro", + u"movimento", + u"mozaiko", + u"mueli", + u"mukozo", + u"muldi", + u"mumio", + u"munti", + u"muro", + u"muskolo", + u"mutacio", + u"muzikisto", + u"nabo", + u"nacio", + u"nadlo", + u"nafto", + u"naiva", + u"najbaro", + u"nanometro", + u"napo", + u"narciso", + u"naski", + u"naturo", + u"navigi", + u"naztruo", + u"neatendite", + u"nebulo", + u"necesa", + u"nedankinde", + u"neebla", + u"nefari", + u"negoco", + u"nehavi", + u"neimagebla", + u"nektaro", + u"nelonga", + u"nematura", + u"nenia", + u"neordinara", + u"nepra", + u"nervuro", + u"nesto", + u"nete", + u"neulo", + u"nevino", + u"nifo", + u"nigra", + u"nihilisto", + u"nikotino", + u"nilono", + u"nimfeo", + u"nitrogeno", + u"nivelo", + u"nobla", + u"nocio", + u"nodozo", + u"nokto", + u"nomkarto", + u"norda", + u"nostalgio", + u"notbloko", + u"novico", + u"nuanco", + u"nuboza", + u"nuda", + u"nugato", + u"nuklea", + u"nuligi", + u"numero", + u"nuntempe", + u"nupto", + u"nura", + u"nutri", + u"oazo", + u"obei", + u"objekto", + u"oblikva", + u"obolo", + u"observi", + u"obtuza", + u"obuso", + u"oceano", + u"odekolono", + u"odori", + u"oferti", + u"oficiala", + u"ofsajdo", + u"ofte", + u"ogivo", + u"ogro", + u"ojstredoj", + u"okaze", + u"okcidenta", + u"okro", + u"oksido", + u"oktobro", + u"okulo", + u"oldulo", + u"oleo", + u"olivo", + u"omaro", + u"ombro", + u"omego", + u"omikrono", + u"omleto", + u"omnibuso", + u"onagro", + u"ondo", + u"oneco", + u"onidire", + u"onklino", + u"onlajna", + u"onomatopeo", + u"ontologio", + u"opaka", + u"operacii", + u"opinii", + u"oportuna", + u"opresi", + u"optimisto", + u"oratoro", + u"orbito", + u"ordinara", + u"orelo", + u"orfino", + u"organizi", + u"orienta", + u"orkestro", + u"orlo", + u"orminejo", + u"ornami", + u"ortangulo", + u"orumi", + u"oscedi", + u"osmozo", + u"ostocerbo", + u"ovalo", + u"ovingo", + u"ovoblanko", + u"ovri", + u"ovulado", + u"ozono", + u"pacama", + u"padeli", + u"pafilo", + u"pagigi", + u"pajlo", + u"paketo", + u"palaco", + u"pampelmo", + u"pantalono", + u"papero", + u"paroli", + u"pasejo", + u"patro", + u"pavimo", + u"peco", + u"pedalo", + u"peklita", + u"pelikano", + u"pensiono", + u"peplomo", + u"pesilo", + u"petanto", + u"pezoforto", + u"piano", + u"picejo", + u"piede", + u"pigmento", + u"pikema", + u"pilkoludo", + u"pimento", + u"pinglo", + u"pioniro", + u"pipromento", + u"pirato", + u"pistolo", + u"pitoreska", + u"piulo", + u"pivoti", + u"pizango", + u"planko", + u"plektita", + u"plibonigi", + u"ploradi", + u"plurlingva", + u"pobo", + u"podio", + u"poeto", + u"pogranda", + u"pohora", + u"pokalo", + u"politekniko", + u"pomarbo", + u"ponevosto", + u"populara", + u"porcelana", + u"postkompreno", + u"poteto", + u"poviga", + u"pozitiva", + u"prapatroj", + u"precize", + u"pridemandi", + u"probable", + u"pruntanto", + u"psalmo", + u"psikologio", + u"psoriazo", + u"pterido", + u"publiko", + u"pudro", + u"pufo", + u"pugnobato", + u"pulovero", + u"pumpi", + u"punkto", + u"pupo", + u"pureo", + u"puso", + u"putrema", + u"puzlo", + u"rabate", + u"racionala", + u"radiko", + u"rafinado", + u"raguo", + u"rajto", + u"rakonti", + u"ralio", + u"rampi", + u"rando", + u"rapida", + u"rastruma", + u"ratifiki", + u"raviolo", + u"razeno", + u"reakcio", + u"rebildo", + u"recepto", + u"redakti", + u"reenigi", + u"reformi", + u"regiono", + u"rehavi", + u"reinspekti", + u"rejesi", + u"reklamo", + u"relativa", + u"rememori", + u"renkonti", + u"reorganizado", + u"reprezenti", + u"respondi", + u"retumilo", + u"reuzebla", + u"revidi", + u"rezulti", + u"rialo", + u"ribeli", + u"ricevi", + u"ridiga", + u"rifuginto", + u"rigardi", + u"rikolti", + u"rilati", + u"rimarki", + u"rinocero", + u"ripozi", + u"riski", + u"ritmo", + u"rivero", + u"rizokampo", + u"roboto", + u"rododendro", + u"rojo", + u"rokmuziko", + u"rolvorto", + u"romantika", + u"ronroni", + u"rosino", + u"rotondo", + u"rovero", + u"rozeto", + u"rubando", + u"rudimenta", + u"rufa", + u"rugbeo", + u"ruino", + u"ruleto", + u"rumoro", + u"runo", + u"rupio", + u"rura", + u"rustimuna", + u"ruzulo", + u"sabato", + u"sadismo", + u"safario", + u"sagaca", + u"sakfluto", + u"salti", + u"samtage", + u"sandalo", + u"sapejo", + u"sarongo", + u"satelito", + u"savano", + u"sbiro", + u"sciado", + u"seanco", + u"sebo", + u"sedativo", + u"segligno", + u"sekretario", + u"selektiva", + u"semajno", + u"senpeza", + u"separeo", + u"servilo", + u"sesangulo", + u"setli", + u"seurigi", + u"severa", + u"sezono", + u"sfagno", + u"sfero", + u"sfinkso", + u"siatempe", + u"siblado", + u"sidejo", + u"siesto", + u"sifono", + u"signalo", + u"siklo", + u"silenti", + u"simpla", + u"sinjoro", + u"siropo", + u"sistemo", + u"situacio", + u"siverto", + u"sizifa", + u"skatolo", + u"skemo", + u"skianto", + u"sklavo", + u"skorpio", + u"skribisto", + u"skulpti", + u"skvamo", + u"slango", + u"sledeto", + u"sliparo", + u"smeraldo", + u"smirgi", + u"smokingo", + u"smuto", + u"snoba", + u"snufegi", + u"sobra", + u"sociano", + u"sodakvo", + u"sofo", + u"soifi", + u"sojlo", + u"soklo", + u"soldato", + u"somero", + u"sonilo", + u"sopiri", + u"sorto", + u"soulo", + u"soveto", + u"sparkado", + u"speciala", + u"spiri", + u"splito", + u"sporto", + u"sprita", + u"spuro", + u"stabila", + u"stelfiguro", + u"stimulo", + u"stomako", + u"strato", + u"studanto", + u"subgrupo", + u"suden", + u"suferanta", + u"sugesti", + u"suito", + u"sukero", + u"sulko", + u"sume", + u"sunlumo", + u"super", + u"surskribeto", + u"suspekti", + u"suturo", + u"svati", + u"svenfali", + u"svingi", + u"svopo", + u"tabako", + u"taglumo", + u"tajloro", + u"taksimetro", + u"talento", + u"tamen", + u"tanko", + u"taoismo", + u"tapioko", + u"tarifo", + u"tasko", + u"tatui", + u"taverno", + u"teatro", + u"tedlaboro", + u"tegmento", + u"tehoro", + u"teknika", + u"telefono", + u"tempo", + u"tenisejo", + u"teorie", + u"teraso", + u"testudo", + u"tetablo", + u"teujo", + u"tezo", + u"tialo", + u"tibio", + u"tielnomata", + u"tifono", + u"tigro", + u"tikli", + u"timida", + u"tinkturo", + u"tiom", + u"tiparo", + u"tirkesto", + u"titolo", + u"tiutempe", + u"tizano", + u"tobogano", + u"tofeo", + u"togo", + u"toksa", + u"tolerema", + u"tombolo", + u"tondri", + u"topografio", + u"tordeti", + u"tosti", + u"totalo", + u"traduko", + u"tredi", + u"triangulo", + u"tropika", + u"trumpeto", + u"tualeto", + u"tubisto", + u"tufgrebo", + u"tuja", + u"tukano", + u"tulipo", + u"tumulto", + u"tunelo", + u"turisto", + u"tusi", + u"tutmonda", + u"tvisto", + u"udono", + u"uesto", + u"ukazo", + u"ukelelo", + u"ulcero", + u"ulmo", + u"ultimato", + u"ululi", + u"umbiliko", + u"unco", + u"ungego", + u"uniformo", + u"unkti", + u"unukolora", + u"uragano", + u"urbano", + u"uretro", + u"urino", + u"ursido", + u"uskleco", + u"usonigi", + u"utero", + u"utila", + u"utopia", + u"uverturo", + u"uzadi", + u"uzeblo", + u"uzino", + u"uzkutimo", + u"uzofini", + u"uzurpi", + u"uzvaloro", + u"vadejo", + u"vafleto", + u"vagono", + u"vahabismo", + u"vajco", + u"vakcino", + u"valoro", + u"vampiro", + u"vangharoj", + u"vaporo", + u"varma", + u"vasta", + u"vato", + u"vazaro", + u"veaspekta", + u"vedismo", + u"vegetalo", + u"vehiklo", + u"vejno", + u"vekita", + u"velstango", + u"vemieno", + u"vendi", + u"vepro", + u"verando", + u"vespero", + u"veturi", + u"veziko", + u"viando", + u"vibri", + u"vico", + u"videbla", + u"vifio", + u"vigla", + u"viktimo", + u"vila", + u"vimeno", + u"vintro", + u"violo", + u"vippuno", + u"virtuala", + u"viskoza", + u"vitro", + u"viveca", + u"viziti", + u"vobli", + u"vodko", + u"vojeto", + u"vokegi", + u"volbo", + u"vomema", + u"vono", + u"vortaro", + u"vosto", + u"voti", + u"vrako", + u"vringi", + u"vualo", + u"vulkano", + u"vundo", + u"vuvuzelo", + u"zamenhofa", + u"zapi", + u"zebro", + u"zefiro", + u"zeloto", + u"zenismo", + u"zeolito", + u"zepelino", + u"zeto", + u"zigzagi", + u"zinko", + u"zipo", + u"zirkonio", + u"zodiako", + u"zoeto", + u"zombio", + u"zono", + u"zoologio", + u"zorgi", + u"zukino", + u"zumilo", ] } \ No newline at end of file diff --git a/tools/python/src/ledger/monero/seedconv.py b/tools/python/src/ledger/monero/seedconv.py index 8c0d367..7c79aa6 100644 --- a/tools/python/src/ledger/monero/seedconv.py +++ b/tools/python/src/ledger/monero/seedconv.py @@ -1,13 +1,13 @@ # Copyright 2018 Cedric Mesnil , Ledger SAS # - # Licensed under the Apache License, Version 2.0 (the "License"); + # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software - # distributed under the License is distributed on an "AS IS" BASIS, + # distributed under the License is distributed on an 'AS IS' BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. @@ -17,68 +17,111 @@ import unicodedata import readline import binascii - +from struct import pack import hashlib import hmac from ecpy import curves from Cryptodome.Hash import keccak +from ledgerblue.comm import getDongle + from .dictionaries.languages import monero_langs + +# ========================================================================================= +# MISC +# ========================================================================================= + MAJOR = 0 MINOR = 8 +def usage(): + print(''' +Usage: + python -m ledger.monero.seedconv online|offline + + online: Seed will be avalaible on the NanoS screen. It is possible to clear it directly from the device. + offline: Seed is computed offline (NanoS is not required) from your 24 BIPS32 words + ''') -def printerr(*args): - # print(args) +def banner(): + print(''' +============================================================= +Monero Seed Converter v%s.%s. Copyright (c) Ledger SAS 20018. +Licensed under the Apache License, Version 2.0 +============================================================= + + '''%(MAJOR,MINOR)) + +def printdbg(*args): + print(*args) return +def error(msg): + print ('Ooops: %s'%msg) + print ('Aborting, sorry.') + sys.exit(1) + def NKFDbytes(str): return unicodedata.normalize('NFKD', str).encode() - def retrieve_language(): - print("* Select Language") + print('* Select Language') i = 0 - dflt = 0 + dflt = -1 for l in monero_langs: - print(" %2d : %s (%s)"%(i, l['language_name'], l['english_language_name'])) + if len(l['words']) != 1626: + print('Wrong dictionary length : %d. 1626 expected. Dictionary %s (%s) skipped.' + %(len(l['words']), l['language_name'], l['english_language_name'])) + print(' %2d : %s (%s)'%(i, l['language_name'], l['english_language_name'])) if l['english_language_name'] == 'English': dflt = i i += 1 - n = input("Enter the number corresponding to the language of your choice (%d): "%dflt) + n = input('Enter the number corresponding to the language of your choice (%d): '%dflt) if len(n) == 0: n = dflt return monero_langs[int(n)] + +# ========================================================================================= +# OFFLINE +# ========================================================================================= + def retrieve_credentials(): print() - w = input("* Enter your NanoS 12/18/24 words: ") + print("WARNING: Be sure to use a TRUSTED and SAFE computer.") + w = input('* Continue? (yes/no): ') + if w != "yes": + print ('Aborting.') + sys.exit(1) + + print() + w = input('* Enter your NanoS 12/18/24 words: ') w = w.split() if len(w) not in (12,24,18): - error("Your mnemonic does not contain 12, 18 or 24 words, but %d"%len(w)) + error('Your mnemonic does not contain 12, 18 or 24 words, but %d'%len(w)) w = ' '.join(w) print() - p = input("* Enter your NanoS passphrase (may be empty): ") + p = input('* Enter your NanoS passphrase (may be empty): ') return w,p -def mnemonic_to_seed(mnemonic, passphrase=""): - seed = hashlib.pbkdf2_hmac('sha512', NKFDbytes(mnemonic), NKFDbytes(u"mnemonic"+passphrase), 2048) +def mnemonic_to_seed(mnemonic, passphrase=''): + seed = hashlib.pbkdf2_hmac('sha512', NKFDbytes(mnemonic), NKFDbytes(u'mnemonic'+passphrase), 2048) return seed def seed_to_master_key(seed): - """ return (mkey, mchain) """ - I = hmac.new(NKFDbytes(u"Bitcoin seed"), seed, 'sha512' ).digest() + ''' return (mkey, mchain) ''' + I = hmac.new(NKFDbytes(u'Bitcoin seed'), seed, 'sha512' ).digest() return I[0:32], I[32:] def master_key_to_child_key(key,path): - G = curves.Curve.get_curve("secp256k1").generator - O = curves.Curve.get_curve("secp256k1").order + G = curves.Curve.get_curve('secp256k1').generator + O = curves.Curve.get_curve('secp256k1').order path = path.split('/') kpar = key[0] cpar = key[1] @@ -86,10 +129,10 @@ def master_key_to_child_key(key,path): for child in path[1:]: hardened = child[len(child)-1]=="'" - printerr("\nprocess %s"%child) - printerr("kpar %s"%binascii.hexlify(kpar)) - printerr("cpar %s"%binascii.hexlify(cpar)) - printerr("hardened %d"%hardened) + printdbg('\nprocess %s'%child) + printdbg('kpar %s'%binascii.hexlify(kpar)) + printdbg('cpar %s'%binascii.hexlify(cpar)) + printdbg('hardened %d'%hardened) if hardened: @@ -101,16 +144,16 @@ def master_key_to_child_key(key,path): k = int.from_bytes(kpar,'big') kG = k*G Wpar = kG.x.to_bytes(32,'big') - if kG.y == 0: - Wpar = b"\x02" + Wpar + if kG.y&1 == 0: + Wpar = b'\x02' + Wpar else: - Wpar = b"\x03" + Wpar + Wpar = b'\x03' + Wpar data = Wpar+int(child).to_bytes(4,'big') - printerr("hmac key: %s"%binascii.hexlify(cpar)) - printerr("hmac input: %s"%binascii.hexlify(data)) + printdbg('hmac key: %s'%binascii.hexlify(cpar)) + printdbg('hmac input: %s'%binascii.hexlify(data)) I = hmac.new(cpar, data, 'sha512').digest() - printerr("hmac output: %s"%binascii.hexlify(I)) + printdbg('hmac output: %s'%binascii.hexlify(I)) Il, Ir = I[:32], I[32:] Il = int.from_bytes(Il,'big') @@ -118,19 +161,20 @@ def master_key_to_child_key(key,path): Il = (Il +kpar) % O Il = Il.to_bytes(32,'big') kpar,cpar = Il,Ir - printerr("ki %s"%binascii.hexlify(kpar)) - printerr("ci %s"%binascii.hexlify(cpar)) + printdbg('ki %s'%binascii.hexlify(kpar)) + printdbg('ci %s'%binascii.hexlify(cpar)) + printdbg() return kpar,cpar def monero_seed_to_monero_keys(seed): l = 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed - printerr('monero_seed_to_monero_keys: seed %s'%seed) + printdbg('monero_seed_to_monero_keys: seed %s'%seed.hex()) kh = keccak.new(digest_bits=256) kh.update(seed) b = kh.digest() - printerr('monero_seed_to_monero_keys: b %s'%b) + printdbg('monero_seed_to_monero_keys: b %s'%b.hex()) ble = int.from_bytes(b,'little')%l b = ble.to_bytes(32, 'little') @@ -139,7 +183,7 @@ def monero_seed_to_monero_keys(seed): a = kh.digest() ale = int.from_bytes(a,'little')%l a = ale.to_bytes(32, 'little') - printerr('monero_seed_to_monero_keys: a %s'%a) + printdbg('monero_seed_to_monero_keys: a %s'%a.hex()) return a,b @@ -183,68 +227,127 @@ def convert_mnemonic(language, ledger_mnemonic, passphrase): monero_view_key, monero_spend_key = monero_seed_to_monero_keys(monero_seed) monero_words = spendkey_to_words(monero_spend_key, language) - printerr("seed: %s"%binascii.hexlify(s)) - printerr("Km : %s"%binascii.hexlify(mkey[0])) - printerr("Cm: %s"%binascii.hexlify(mkey[0])) - printerr("monero_seed: %s"%binascii.hexlify(monero_seed)) - printerr("monero view keys: %s"%binascii.hexlify(monero_view_key)) - printerr("monero spend keys: %s"%binascii.hexlify(monero_spend_key)) - printerr("monero words: %d %s"%(len(monero_words), ' '.join(monero_words))) - - return monero_words, monero_view_key, monero_spend_key - - - -def error(msg): - print ("Ooops: %s"%msg) - print ("Aborting, sorry.") - sys.exit(1) - -def usage(): - print(""" -Usage: - python -m ledger.monero.seedconv - - """) - + printdbg('seed: %s'%binascii.hexlify(s)) + printdbg('Km : %s'%binascii.hexlify(mkey[0])) + printdbg('Cm: %s'%binascii.hexlify(mkey[0])) + printdbg('monero_seed: %s'%binascii.hexlify(monero_seed)) + printdbg('monero view keys: %s'%binascii.hexlify(monero_view_key)) + printdbg('monero spend keys: %s'%binascii.hexlify(monero_spend_key)) + printdbg('monero words: %d %s'%(len(monero_words), ' '.join(monero_words))) + + return monero_words, monero_view_key, monero_spend_key,monero_seed + + + + +def get_offline_seed(lang): + mnemonic, passphrase = retrieve_credentials() + electrum_words, monero_view_key, monero_spend_key, monero_seed = convert_mnemonic(lang, mnemonic, passphrase) + print(u''' + * Result: + --------------------------------------------------------------------------------------------------- + | Monero Electrum words : {0:<70} | + | {1:<70} | + | {2:<70} | + | | + | seed : {3:<70} | + | Spend key : {4:<70} | + | View key : {5:<70} | + --------------------------------------------------------------------------------------------------- + ''' .format (' '.join(electrum_words[0:8]),' '.join(electrum_words[8:16]),' '.join(electrum_words[16:]), + binascii.hexlify(monero_seed).decode(), + binascii.hexlify(monero_spend_key).decode(), + binascii.hexlify(monero_view_key).decode())) + + +# ========================================================================================= +# ONLINE +# ========================================================================================= + +def send_dict_chunk(dongle, p2, chunk,start,cnt): + header = pack('>4B', 0x00, 0x28, 0x01, p2) + data = pack('>BII', 0,start,cnt) + chunk + apdu = header+pack('>B',len(data))+data + print('.', end='') + dongle.exchange(apdu) + print('.', end='') + +def get_online_seed(lang): + if lang['english_language_name'] not in ("English", "Esperanto", "French", "Italian", "Lobjan", "Portuguese"): + error("%s not supported online"%lang['english_language_name']) + + print("Open device...") + dongle = getDongle(False) + + print("Erase old key words...") + dongle.exchange(pack('>6B', 0x00, 0x28, 0x02, 0x00, 0x01, 0x00)) + + print("Load dictionnary", end='') + start = 0 + cnt = 0 + chunk = b'' + for w in lang['words']: + w = w.encode('ascii') + if 1+8+len(chunk)+1+len(w) > 254: + + send_dict_chunk(dongle, 0, chunk, start, cnt) + start += cnt + cnt = 0; + chunk = b'' + chunk += pack('>b', len(w))+w + cnt += 1; + send_dict_chunk(dongle,lang['prefix_length'], chunk, start, cnt) + print() + print("Done.") -def banner(): - print(""" -============================================================= -Monero Seed Converter v%s.%s. Copyright (c) Ledger SAS 20018. -Licensed under the Apache License, Version 2.0 -============================================================= -WARNING: Be sure to use a TRUSTED and SAFE computer. +def clear_online_seed(lang): + print("Open device...") + dongle = getDongle(False) - """%(MAJOR,MINOR)) + print("Erase old key words...") + dongle.exchange(pack('>6B', 0x00, 0x28, 0x02, 0x00, 0x01, 0x00)) + +# ========================================================================================= +# MAIN +# ========================================================================================= def test(): # Ledger: abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about - # Monero: tiger building listen sack payment debut cunning howls sieve fight ledge rally assorted toyed slower jetting dizzy industrial fully baffles awakened island bowling cube slower + # Monero: tavern judge beyond bifocals deepest mural onward dummy eagle diode gained vacation rally cause firm idled + # jerseys moat vigilant upload bobsled jobs cunning doing jobs # seed DB9E57474BE8B64118B6ACF6ECEBD13F8F7C326B3BC1B19F4546573D6BAC9DCF # spend 3B094CA7218F175E91FA2402B4AE239A2FE8262792A3E718533A1A357A1E4109 # view 0F3FE25D0C6D4C94DDE0C0BCC214B233E9C72927F813728B0F01F28F9D5E1201 monero_view_key, monero_spend_key = monero_seed_to_monero_keys(binascii.unhexlify(u'DB9E57474BE8B64118B6ACF6ECEBD13F8F7C326B3BC1B19F4546573D6BAC9DCF')) - print("monero view keys: %s"%binascii.hexlify(monero_view_key)) - print("monero spend keys: %s"%binascii.hexlify(monero_spend_key)) + print('monero view keys: %s'%binascii.hexlify(monero_view_key)) + print('monero spend keys: %s'%binascii.hexlify(monero_spend_key)) sys.exit(1); +def max_wlen(): + for l in monero_langs: + print(' %s %d'%(l['english_language_name'], len(l['words']))) + wlen = 0 + for w in l['words']: + if len(w) > wlen: + wlen = len(w.encode()) + print(' wlen = %d'%wlen) + sys.exit(1) + + banner() +if len(sys.argv) != 2 or sys.argv[1] not in ("-h","--help","online","offline"): + print("Invalid argument") + usage() + sys.exit(1) + lang = retrieve_language() -mnemonic, passphrase = retrieve_credentials() -electrum_words, monero_view_key, monero_spend_key = convert_mnemonic(lang, mnemonic, passphrase) -print(u""" -* Result: - --------------------------------------------------------------------------------------------------- - | Monero Electrum words : {0:<70} | - | {1:<70} | - | {2:<70} | - | | - | Spend key : {3:<70} | - | View key : {4:<70} | - --------------------------------------------------------------------------------------------------- -""" .format (' '.join(electrum_words[0:8]),' '.join(electrum_words[8:16]),' '.join(electrum_words[16:]), - binascii.hexlify(monero_spend_key).decode(), - binascii.hexlify(monero_view_key).decode())) + + +if sys.argv[1] == "online": + get_online_seed(lang) +elif sys.argv[1] == "offline": + get_offline_seed(lang) +else: + usage()