diff --git a/firmware/fsm.c b/firmware/fsm.c index c3adea394..7369fee32 100644 --- a/firmware/fsm.c +++ b/firmware/fsm.c @@ -974,7 +974,8 @@ void fsm_msgRecoveryDevice(RecoveryDevice *msg) msg->has_pin_protection && msg->pin_protection, msg->has_language ? msg->language : 0, msg->has_label ? msg->label : 0, - msg->has_enforce_wordlist ? msg->enforce_wordlist : false, + msg->has_enforce_wordlist && msg->enforce_wordlist, + msg->has_type ? msg->type : 0, msg->has_u2f_counter ? msg->u2f_counter : 0 ); } diff --git a/firmware/recovery-table.h b/firmware/recovery-table.h new file mode 100644 index 000000000..ebf40753f --- /dev/null +++ b/firmware/recovery-table.h @@ -0,0 +1,112 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2016 Jochen Hoenicke + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +/* DO NOT EDIT: This file is automatically generated by + * cd ../gen/wordlist + * perl build-recoverytable.pl recovery_english.txt + */ + +static const uint16_t word_table1[82] = +{ + 8192, 8200, 8208, 8217, 8225, 8234, 8243, 8250, 8259, + 12361, 12367, 8280, 8285, 8292, 8297, 8302, 8311, 8318, + 8325, 12429, 12437, 8347, 8356, 8365, 8373, 8382, 8391, + 8400, 8409, 8412, 8417, 8426, 8432, 8439, 8447, 8455, + 8463, 8472, 8480, 8487, 8495, 4408, 4416, 8521, 8530, + 8539, 8548, 8557, 8564, 8573, 8582, 8589, 8597, 8601, + 8609, 8618, 8627, 8634, 4545, 8645, 12750, 12759, 8672, + 8681, 8690, 8695, 8703, 8712, 8721, 8730, 8738, 8746, + 8751, 8757, 8766, 8775, 8782, 4690, 4699, 8804, 8813, + 630, +}; + +static const uint16_t word_table2[631] = +{ + 12288, 12293, 12297, 12298, 12302, 12304, 12306, 12307, 12312, + 12313, 12316, 8225, 8226, 8229, 8233, 8234, 12334, 12337, + 12342, 12345, 8253, 12354, 12357, 12361, 12365, 8274, 12376, + 12378, 12380, 12381, 12385, 12386, 12390, 12394, 12396, 12400, + 8305, 12407, 12410, 8318, 8321, 8327, 12424, 12428, 12433, + 12439, 12443, 12448, 12451, 12456, 12459, 12463, 12465, 12468, + 12471, 12476, 12479, 12482, 12485, 12489, 12494, 12498, 12502, + 12507, 12509, 12515, 12521, 12522, 12527, 12530, 12532, 12535, + 12538, 12541, 12544, 12545, 12546, 12547, 12549, 16647, 16651, + 12559, 16658, 16662, 12569, 12574, 12579, 12582, 12583, 12584, + 12585, 12586, 12588, 16686, 16689, 12599, 12605, 12609, 12611, + 12612, 12615, 12616, 12617, 12618, 12620, 12621, 12626, 12629, + 12635, 12641, 12645, 12650, 12655, 16757, 16761, 12669, 12673, + 12679, 12684, 16782, 16786, 12695, 12699, 12703, 12707, 12713, + 12715, 12716, 12717, 12719, 12723, 12725, 8630, 12727, 12728, + 12730, 12732, 12733, 12734, 12735, 12736, 12737, 12738, 12740, + 12746, 12747, 12750, 12751, 12753, 12755, 12758, 12763, 12764, + 12770, 12772, 12775, 12779, 12782, 12786, 12788, 16886, 16891, + 12798, 12801, 12802, 12804, 12805, 12807, 12808, 12811, 12812, + 12813, 12814, 12815, 12820, 12822, 12827, 12830, 12832, 8741, + 8742, 12839, 12841, 8751, 8757, 12857, 12859, 12864, 12866, + 12870, 12874, 12876, 12879, 12884, 12887, 12891, 8799, 8802, + 8808, 8812, 8814, 12914, 12916, 12921, 12925, 12929, 12935, + 8841, 12939, 12942, 12943, 12945, 12947, 12950, 12953, 12955, + 12959, 12961, 12964, 12967, 12973, 12977, 12980, 12985, 12988, + 12993, 12998, 13001, 13005, 13008, 13012, 17112, 17116, 13023, + 13027, 13029, 13031, 13033, 13038, 8943, 13045, 13047, 13049, + 13051, 13056, 13058, 13060, 8966, 8972, 13069, 13070, 13071, + 13072, 13073, 13075, 13076, 13080, 13082, 13087, 13088, 13090, + 13093, 13096, 17194, 17197, 13105, 13107, 13110, 13113, 9018, + 9024, 13121, 13123, 13126, 13128, 13132, 13136, 13140, 13142, + 13145, 13147, 13149, 13151, 13154, 13156, 13160, 13164, 13167, + 13172, 13173, 13174, 13177, 13180, 13183, 9088, 9089, 9091, + 9094, 9095, 13194, 13195, 13196, 13198, 13202, 13206, 13210, + 13213, 13216, 13219, 13223, 13228, 9138, 9144, 9148, 9152, + 13253, 13254, 13255, 13256, 13259, 9164, 9165, 13265, 13266, + 13268, 13270, 13271, 13275, 9180, 13280, 13285, 13288, 13292, + 13295, 13300, 13304, 13309, 13314, 13318, 13322, 13326, 13330, + 13335, 13340, 13344, 9253, 9259, 13356, 13360, 13363, 13366, + 13372, 13373, 13379, 13382, 13387, 13389, 13393, 13394, 13396, + 13398, 13400, 13402, 13406, 13408, 13410, 13412, 13414, 13415, + 13419, 13421, 13424, 13427, 13432, 13436, 13440, 13444, 13448, + 13452, 13457, 9362, 13461, 13463, 13465, 13468, 13470, 13473, + 13475, 13477, 13480, 9387, 13485, 13489, 13491, 13492, 13496, + 9402, 9406, 13503, 13506, 9414, 9417, 9418, 9422, 9423, + 9424, 9427, 9428, 9433, 13534, 13540, 9447, 9448, 9449, + 9453, 9456, 9458, 13557, 13563, 13568, 13574, 13579, 13582, + 13586, 13591, 9500, 13600, 13604, 13609, 13614, 13619, 13624, + 13627, 13632, 13638, 13643, 13645, 17747, 17750, 17755, 17759, + 17764, 13672, 13674, 13677, 13679, 13681, 13685, 9592, 13689, + 13692, 13693, 13696, 13697, 13698, 13701, 13703, 13706, 13708, + 13711, 13713, 13715, 13718, 13721, 13723, 13728, 13729, 13732, + 13735, 13736, 13740, 13744, 13747, 13748, 13752, 13753, 13759, + 13762, 13763, 13765, 9670, 13767, 13773, 13778, 13783, 13788, + 13793, 13798, 13801, 13805, 13810, 13815, 13818, 13822, 13824, + 13828, 13831, 13835, 13839, 13843, 13847, 13853, 13856, 13862, + 13866, 13869, 17970, 17972, 13881, 13883, 13884, 13885, 13889, + 13895, 13899, 13904, 13906, 13911, 13915, 13919, 9827, 9832, + 13933, 13934, 13937, 13939, 13944, 13947, 13949, 13954, 13958, + 13963, 13964, 13969, 13970, 13975, 13978, 9883, 18078, 18084, + 13992, 13997, 14000, 14006, 14011, 14014, 14015, 14018, 14020, + 14024, 14026, 14029, 14032, 14038, 14040, 14044, 14046, 14050, + 9955, 14055, 14059, 14064, 14068, 14071, 14075, 14078, 14080, + 14085, 14088, 14091, 14093, 14095, 14099, 14101, 14104, 14108, + 14111, 14114, 14117, 14119, 14122, 14125, 14128, 18228, 18233, + 14142, 14145, 14151, 14153, 14159, 14160, 14165, 10072, 10078, + 10080, 14178, 14182, 14187, 14191, 10100, 10106, 10108, 10114, + 14211, 14217, 14223, 14228, 14234, 14239, 14245, 14250, 14253, + 14257, 14260, 14264, 14267, 14273, 14278, 14281, 14285, 14291, + 14293, 18394, 18399, 14305, 14310, 14315, 10224, 6134, 6140, + 2048, +}; diff --git a/firmware/recovery.c b/firmware/recovery.c index 088c1f63d..17e68385f 100644 --- a/firmware/recovery.c +++ b/firmware/recovery.c @@ -2,6 +2,7 @@ * This file is part of the TREZOR project. * * Copyright (C) 2014 Pavol Rusnak + * Copyright (C) 2016 Jochen Hoenicke * * This library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -17,6 +18,7 @@ * along with this library. If not, see . */ +#include #include "recovery.h" #include "fsm.h" #include "storage.h" @@ -26,16 +28,326 @@ #include "messages.h" #include "rng.h" #include "bip39.h" +#include "oled.h" +#include "usb.h" +#include "types.pb.h" +#include "recovery-table.h" +/* number of words expected in the new seed */ static uint32_t word_count; -static bool awaiting_word = false; + +/* recovery mode: + * 0: not recovering + * 1: recover by scrambled plain text words + * 2: recover by matrix entry + */ +static int awaiting_word = 0; + +/* True if we should check that seed corresponds to bip39. + */ static bool enforce_wordlist; + +/* For scrambled recovery Trezor may ask for faked words if + * seed is short. This contains the fake word. + */ static char fake_word[12]; + +/* Word position in the seed that we are currently asking for. + * This is 0 if we ask for a fake word. Only for scrambled recovery. + */ static uint32_t word_pos; + +/* Scrambled recovery: How many words has the user already entered. + * Matrix recovery: How many digits has the user already entered. + */ static uint32_t word_index; + +/* Order in which we ask for the words. It holds that + * word_order[word_index] == word_pos. Only for scrambled recovery. + */ static char word_order[24]; + +/* The recovered seed. This is filled during the recovery process. + */ static char words[24][12]; +/* The "pincode" of the current word. This is basically the "pin" + * that the user would have entered for the current word if the words + * were displayed in alphabetical order. Note that it is base 9, not + * base 10. Only for matrix recovery. + */ +static uint16_t word_pincode; + +/* The pinmatrix currently displayed on screen. + * Only for matrix recovery. + */ +static uint8_t word_matrix[9]; + +#define MASK_IDX(x) ((x) & 0xfff) +#define TABLE1(x) MASK_IDX(word_table1[x]) +#define TABLE2(x) MASK_IDX(word_table2[x]) + +/* Helper function to format a two digit number. + * Parameter dest is buffer containing the string. It should already + * start with "##th". The number is written in place. + * Parameter number gives the number that we should format. + */ +static void format_number(char *dest, int number) { + if (number < 10) { + dest[0] = ' '; + } else { + dest[0] = '0' + number / 10; + } + dest[1] = '0' + number % 10; + if (number == 1 || number == 21) { + dest[2] = 's'; dest[3] = 't'; + } else if (number == 2 || number == 22) { + dest[2] = 'n'; dest[3] = 'd'; + } else if (number == 3 || number == 23) { + dest[2] = 'r'; dest[3] = 'd'; + } +} + +/* Send a request for a new word/matrix code to the PC. + */ +static void recovery_request(void) { + WordRequest resp; + memset(&resp, 0, sizeof(WordRequest)); + resp.has_type = true; + resp.type = awaiting_word == 1 ? WordRequestType_WordRequestType_Plain + : (word_index % 4 == 3) ? WordRequestType_WordRequestType_Matrix6 + : WordRequestType_WordRequestType_Matrix9; + msg_write(MessageType_MessageType_WordRequest, &resp); +} + +/* Called when the last word was entered. + * Check mnemonic and send success/failure. + */ +static void recovery_done(void) { + uint32_t i; + strlcpy(storage.mnemonic, words[0], sizeof(storage.mnemonic)); + for (i = 1; i < word_count; i++) { + strlcat(storage.mnemonic, " ", sizeof(storage.mnemonic)); + strlcat(storage.mnemonic, words[i], sizeof(storage.mnemonic)); + } + if (!enforce_wordlist || mnemonic_check(storage.mnemonic)) { + storage.has_mnemonic = true; + if (!enforce_wordlist) { + // not enforcing => mark storage as imported + storage.has_imported = true; + storage.imported = true; + } + storage_commit(); + fsm_sendSuccess("Device recovered"); + } else { + storage_reset(); + fsm_sendFailure(FailureType_Failure_SyntaxError, "Invalid mnemonic, are words in correct order?"); + } + awaiting_word = 0; + layoutHome(); +} + +/* Helper function for matrix recovery: + * Formats a string describing the word range from first to last where + * prefixlen gives the number of characters in first and last that are + * significant, i.e. the word before first or the word after last differ + * exactly at the prefixlen-th character. + * + * Invariants: + * memcmp("first - 1", first, prefixlen) != 0 + * memcmp(last, "last + 1", prefixlen) != 0 + * first[prefixlen-2] == last[prefixlen-2] except for range WI-Z. + */ +static void add_choice(char choice[12], int prefixlen, const char *first, const char *last) { + // assert prefixlen < 4 + int i; + char *dest = choice; + for (i = 0; i < prefixlen; i++) { + *dest++ = toupper((int) first[i]); + } + if (first[0] != last[0]) { + /* special case WI-Z; also used for T-Z, etc. */ + *dest++ = '-'; + *dest++ = toupper((int) last[0]); + } else if (last[prefixlen-1] == first[prefixlen-1]) { + /* single prefix */ + } else if (prefixlen < 3) { + /* AB-AC, etc. */ + *dest++ = '-'; + for (i = 0; i < prefixlen; i++) { + *dest++ = toupper((int) last[i]); + } + } else { + /* RE[A-M] etc. */ + /* remove last and replace with space */ + dest[-1] = ' '; + if (first[prefixlen - 1]) { + /* handle special case: CAN[-D] */ + *dest++ = toupper((int)first[prefixlen - 1]); + } + *dest++ = '-'; + *dest++ = toupper((int) last[prefixlen - 1]); + } + *dest++ = 0; +} + +/* Helper function for matrix recovery: + * Display the recovery matrix given in choices. If twoColumn is set + * use 2x3 layout, otherwise 3x3 layout. Also generates a random + * scrambling and stores it in word_matrix. + */ +static void display_choices(bool twoColumn, char choices[9][12], int num) +{ + int i; + int nColumns = twoColumn ? 2 : 3; + int displayedChoices = nColumns * 3; + int row, col; + for (i = 0; i < displayedChoices; i++) { + word_matrix[i] = i; + } + /* scramble matrix */ + random_permute((char*)word_matrix, displayedChoices); + + if (word_index % 4 == 0) { + char desc[] = "##th word"; + int nr = (word_index / 4) + 1; + format_number(desc, nr); + layoutDialogSwipe(&bmp_icon_info, NULL, NULL, NULL, "Please enter the", (nr < 10 ? desc + 1 : desc), "of your mnemonic", NULL, NULL, NULL); + } else { + oledBox(0, 18, 127, 63, false); + } + + for (row = 0; row < 3; row ++) { + int y = 55 - row * 11; + for (col = 0; col < nColumns; col++) { + int x = twoColumn ? 64 * col + 32 : 42 * col + 22; + int choice = word_matrix[nColumns*row + col]; + const char *text = choice < num ? choices[choice] : "-"; + oledDrawString(x - oledStringWidth(text)/2, y, text); + } + } + oledRefresh(); + + /* avoid picking out of range numbers */ + for (i = 0; i < displayedChoices; i++) { + if (word_matrix[i] > num) + word_matrix[i] = 0; + } + /* two column layout: middle column = right column */ + if (twoColumn) { + static const uint8_t twolayout[9] = { 0, 1, 1, 2, 3, 3, 4, 5, 5 }; + for (i = 8; i >= 2; i--) { + word_matrix[i] = word_matrix[twolayout[i]]; + } + } +} + +/* Helper function for matrix recovery: + * Generates a new matrix and requests the next pin. + */ +static void next_matrix(void) { + const char * const *wl = mnemonic_wordlist(); + char word_choices[9][12]; + uint32_t i, idx, first, num; + bool last = (word_index % 4) == 3; + + switch (word_index % 4) { + case 3: + idx = TABLE1(word_pincode / 9) + word_pincode % 9; + first = word_table2[idx] & 0xfff; + num = (word_table2[idx+1] & 0xfff) - first; + for (i = 0; i < num; i++) { + strlcpy(word_choices[i], wl[first + i], sizeof(word_choices[i])); + } + break; + + case 2: + idx = TABLE1(word_pincode); + num = TABLE1(word_pincode + 1) - idx; + for (i = 0; i < num; i++) { + add_choice(word_choices[i], (word_table2[idx + i] >> 12), + wl[TABLE2(idx + i)], + wl[TABLE2(idx + i + 1) - 1]); + } + break; + + case 1: + idx = word_pincode * 9; + num = 9; + for (i = 0; i < num; i++) { + add_choice(word_choices[i], (word_table1[idx + i] >> 12), + wl[TABLE2(TABLE1(idx + i))], + wl[TABLE2(TABLE1(idx + i + 1)) - 1]); + } + break; + + case 0: + num = 9; + for (i = 0; i < num; i++) { + add_choice(word_choices[i], 1, + wl[TABLE2(TABLE1(9*i))], + wl[TABLE2(TABLE1(9*(i+1)))-1]); + } + break; + } + display_choices(last, word_choices, num); + + recovery_request(); +} + +/* Function called when a digit was entered by user. + * digit: ascii code of the entered digit ('1'-'9') or + * '\x08' for backspace. + */ +static void recovery_digit(const char digit) { + if (digit == 8) { + /* backspace: undo */ + if ((word_index % 4) == 0) { + /* undo complete word */ + if (word_index > 0) + word_index -= 4; + } else { + word_index--; + word_pincode /= 9; + } + next_matrix(); + return; + } + + if (digit < '1' || digit > '9') { + recovery_request(); + return; + } + + int choice = word_matrix[digit - '1']; + if ((word_index % 4) == 3) { + /* received final word */ + int y = 54 - ((digit - '1')/3)*11; + int x = 64 * (((digit - '1') % 3) > 0); + oledInvert(x, y, x + 63, y + 9); + oledRefresh(); + usbSleep(250); + + int idx = TABLE2(TABLE1(word_pincode / 9) + (word_pincode % 9)) + choice; + uint32_t widx = word_index / 4; + + word_pincode = 0; + strlcpy(words[widx], mnemonic_wordlist()[idx], sizeof(words[widx])); + if (widx + 1 == word_count) { + recovery_done(); + return; + } + /* next word */ + } else { + word_pincode = word_pincode * 9 + choice; + } + word_index++; + next_matrix(); +} + +/* Helper function for scrambled recovery: + * Ask the user for the next word. + */ void next_word(void) { word_pos = word_order[word_index]; if (word_pos == 0) { @@ -45,29 +357,13 @@ void next_word(void) { } else { fake_word[0] = 0; char desc[] = "##th word"; - if (word_pos < 10) { - desc[0] = ' '; - } else { - desc[0] = '0' + word_pos / 10; - } - desc[1] = '0' + word_pos % 10; - if (word_pos == 1 || word_pos == 21) { - desc[2] = 's'; desc[3] = 't'; - } else - if (word_pos == 2 || word_pos == 22) { - desc[2] = 'n'; desc[3] = 'd'; - } else - if (word_pos == 3 || word_pos == 23) { - desc[2] = 'r'; desc[3] = 'd'; - } + format_number(desc, word_pos); layoutDialogSwipe(&bmp_icon_info, NULL, NULL, NULL, "Please enter the", NULL, (word_pos < 10 ? desc + 1 : desc), NULL, "of your mnemonic", NULL); } - WordRequest resp; - memset(&resp, 0, sizeof(WordRequest)); - msg_write(MessageType_MessageType_WordRequest, &resp); + recovery_request(); } -void recovery_init(uint32_t _word_count, bool passphrase_protection, bool pin_protection, const char *language, const char *label, bool _enforce_wordlist, uint32_t u2f_counter) +void recovery_init(uint32_t _word_count, bool passphrase_protection, bool pin_protection, const char *language, const char *label, bool _enforce_wordlist, uint32_t type, uint32_t u2f_counter) { if (_word_count != 12 && _word_count != 18 && _word_count != 24) return; @@ -86,27 +382,27 @@ void recovery_init(uint32_t _word_count, bool passphrase_protection, bool pin_pr storage_setLabel(label); storage_setU2FCounter(u2f_counter); - uint32_t i; - for (i = 0; i < word_count; i++) { - word_order[i] = i + 1; - } - for (i = word_count; i < 24; i++) { - word_order[i] = 0; + if ((type & RecoveryDeviceType_RecoveryDeviceType_Matrix) != 0) { + awaiting_word = 2; + word_index = 0; + next_matrix(); + } else { + uint32_t i; + for (i = 0; i < word_count; i++) { + word_order[i] = i + 1; + } + for (i = word_count; i < 24; i++) { + word_order[i] = 0; + } + random_permute(word_order, 24); + awaiting_word = 1; + word_index = 0; + next_word(); } - random_permute(word_order, 24); - awaiting_word = true; - word_index = 0; - next_word(); } -void recovery_word(const char *word) +static void recovery_scrambledword(const char *word) { - if (!awaiting_word) { - fsm_sendFailure(FailureType_Failure_UnexpectedMessage, "Not in Recovery mode"); - layoutHome(); - return; - } - if (word_pos == 0) { // fake word if (strcmp(word, fake_word) != 0) { storage_reset(); @@ -136,33 +432,38 @@ void recovery_word(const char *word) } if (word_index + 1 == 24) { // last one - uint32_t i; - strlcpy(storage.mnemonic, words[0], sizeof(storage.mnemonic)); - for (i = 1; i < word_count; i++) { - strlcat(storage.mnemonic, " ", sizeof(storage.mnemonic)); - strlcat(storage.mnemonic, words[i], sizeof(storage.mnemonic)); - } - if (!enforce_wordlist || mnemonic_check(storage.mnemonic)) { - storage.has_mnemonic = true; - storage_commit(); - fsm_sendSuccess("Device recovered"); - } else { - storage_reset(); - fsm_sendFailure(FailureType_Failure_SyntaxError, "Invalid mnemonic, are words in correct order?"); - } - awaiting_word = false; - layoutHome(); + recovery_done(); } else { word_index++; next_word(); } } +/* Function called when a word was entered by user. Used + * for scrambled recovery. + */ +void recovery_word(const char *word) +{ + switch (awaiting_word) { + case 2: + recovery_digit(word[0]); + break; + case 1: + recovery_scrambledword(word); + break; + default: + fsm_sendFailure(FailureType_Failure_UnexpectedMessage, "Not in Recovery mode"); + break; + } +} + +/* Abort recovery. + */ void recovery_abort(void) { if (awaiting_word) { layoutHome(); - awaiting_word = false; + awaiting_word = 0; } } diff --git a/firmware/recovery.h b/firmware/recovery.h index e1de844c4..ac463d28e 100644 --- a/firmware/recovery.h +++ b/firmware/recovery.h @@ -23,7 +23,7 @@ #include #include -void recovery_init(uint32_t _word_count, bool passphrase_protection, bool pin_protection, const char *language, const char *label, bool _enforce_wordlist, uint32_t u2f_counter); +void recovery_init(uint32_t _word_count, bool passphrase_protection, bool pin_protection, const char *language, const char *label, bool _enforce_wordlist, uint32_t type, uint32_t u2f_counter); void recovery_word(const char *word); void recovery_abort(void); const char *recovery_get_fake_word(void); diff --git a/gen/wordlist/build-recovery-table.pl b/gen/wordlist/build-recovery-table.pl new file mode 100644 index 000000000..c67325f53 --- /dev/null +++ b/gen/wordlist/build-recovery-table.pl @@ -0,0 +1,110 @@ +#!/usr/bin/perl +print <<'EOF'; +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2016 Jochen Hoenicke + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +EOF + +my @arr1; +my @arr2; +my $x = 0; +my $l = "00"; +my @words; +while (<>) { + $_ =~ /([1-9]{2})[1-9] ([1-6]):(.*)/; + my $n = $1; + my $c = $2; + my @nw = split(",", $3); + die if $c != @nw; + die if $c > 6; + push @words, @nw; + if ($n ne $l) { + $len = @arr2; + die if $len - $arr1[-1] > 9; + push @arr1, $len; + } + push @arr2, $x; + $x += $c; + $l = $n; +} +$len = @arr2; +push @arr1, $len; +push @arr2, $x; + +sub computerange($$$) { + my ($i1, $i2, $entries) = @_; + $prev = $i1 == 0 ? "_" : $words[$i1 - 1]; + $first = $words[$i1]; + $last = $words[$i2-1]; + $next = $i2 == @words ? "_" : $words[$i2]; + my $j; + for ($j = 0; $j < 5; $j++) { + last if substr($first, 0, $j+1) ne substr($last, 0, $j+1); + last if substr($prev, 0, $j) ne substr($first, 0, $j) + && substr($next, 0, $j) ne substr($last, 0, $j); + } + $prefix = substr($first, 0, $j); + $range = ""; + $rng = 0; + if (substr($prev, 0, $j) eq substr($first, 0, $j) + || substr($last, 0, $j) eq substr($next, 0, $j)) { + $range = "[".substr($first, $j, 1) . "-". substr($last, $j, 1)."]"; + $rng++; + if ($j <= 1) { + $range = substr($first,0, $j+1)."-".substr($last,0,$j+1); + $prefix= ""; + } + } + if (substr($prev, 0, $j+1) eq substr($first, 0, $j+1) + || substr($last, 0, $j+1) eq substr($next, 0, $j+1)) { + $j = 0; $rng = 2; + } + #printf STDERR " # %1d: %9s - %9s = \U$prefix$range\E\n", $entries, $first, $last; + return $j + $rng; +} + +print << 'EOF'; +/* DO NOT EDIT: This file is automatically generated by + * cd ../gen/wordlist + * perl build-recoverytable.pl recovery_english.txt + */ + +EOF + +$len = @arr1; +print "static const uint16_t word_table1[$len] =\n"; +print "{"; +for ($i = 0; $i< @arr1; $i++) { + print "\n " if ($i % 9 == 0); + $prefixlen = computerange($arr2[$arr1[$i]], $arr2[$arr1[$i+1]], $arr1[$i+1]-$arr1[$i]); + $prefixlen = 0 if ($i == @arr1 - 1); + printf(" %5d,", $arr1[$i] + 4096 * $prefixlen); +} +print "\n};\n\n"; + +$len = @arr2; +print "static const uint16_t word_table2[$len] =\n"; +print "{"; +for ($i = 0; $i< @arr2; $i++) { + print "\n " if ($i % 9 == 0); + $prefixlen = computerange($arr2[$i], $arr2[$i+1], $arr2[$i+1]-$arr2[$i]); + $prefixlen = 0 if ($i == @arr2 - 1); + printf(" %5d,", $arr2[$i] + 4096 * $prefixlen); +} +print "\n};\n"; diff --git a/gen/wordlist/recovery_english.txt b/gen/wordlist/recovery_english.txt new file mode 100644 index 000000000..ab85f326b --- /dev/null +++ b/gen/wordlist/recovery_english.txt @@ -0,0 +1,630 @@ +111 5:abandon,ability,able,about,above +112 4:absent,absorb,abstract,absurd +113 1:abuse +114 4:access,accident,account,accuse +115 2:achieve,acid +116 2:acoustic,acquire +117 1:across +118 5:act,action,actor,actress,actual +121 1:adapt +122 3:add,addict,address +123 5:adjust,admit,adult,advance,advice +124 1:aerobic +125 3:affair,afford,afraid +126 4:again,age,agent,agree +127 1:ahead +128 4:aim,air,airport,aisle +131 3:alarm,album,alcohol +132 5:alert,alien,all,alley,allow +133 3:almost,alone,alpha +134 4:already,also,alter,always +135 5:amateur,amazing,among,amount,amused +136 3:analyst,anchor,ancient +137 4:anger,angle,angry,animal +138 4:ankle,announce,annual,another +139 5:answer,antenna,antique,anxiety,any +141 6:apart,apology,appear,apple,approve,april +142 2:arch,arctic +143 2:area,arena +144 1:argue +145 4:arm,armed,armor,army +146 1:around +147 4:arrange,arrest,arrive,arrow +148 4:art,artefact,artist,artwork +151 2:ask,aspect +152 4:assault,asset,assist,assume +153 1:asthma +154 6:athlete,atom,attack,attend,attitude,attract +155 3:auction,audit,august +156 4:aunt,author,auto,autumn +157 3:average,avocado,avoid +158 6:awake,aware,away,awesome,awful,awkward +159 1:axis +161 4:baby,bachelor,bacon,badge +162 5:bag,balance,balcony,ball,bamboo +163 6:banana,banner,bar,barely,bargain,barrel +164 4:base,basic,basket,battle +165 5:beach,bean,beauty,because,become +166 3:beef,before,begin +167 5:behave,behind,believe,below,belt +168 3:bench,benefit,best +169 4:betray,better,between,beyond +171 2:bicycle,bid +172 3:bike,bind,biology +173 3:bird,birth,bitter +174 5:black,blade,blame,blanket,blast +175 3:bleak,bless,blind +176 3:blood,blossom,blouse +177 3:blue,blur,blush +181 4:board,boat,body,boil +182 5:bomb,bone,bonus,book,boost +183 4:border,boring,borrow,boss +184 4:bottom,bounce,box,boy +185 5:bracket,brain,brand,brass,brave +186 2:bread,breeze +187 6:brick,bridge,brief,bright,bring,brisk +188 6:broccoli,broken,bronze,broom,brother,brown +189 1:brush +191 5:bubble,buddy,budget,buffalo,build +192 3:bulb,bulk,bullet +193 2:bundle,bunker +194 3:burden,burger,burst +195 3:bus,business,busy +196 3:butter,buyer,buzz +211 3:cabbage,cabin,cable +212 1:cactus +213 1:cage +214 1:cake +215 2:call,calm +216 2:camera,camp +221 4:can,canal,cancel,candy +222 4:cannon,canoe,canvas,canyon +223 3:capable,capital,captain +224 4:car,carbon,card,cargo +225 3:carpet,carry,cart +226 5:case,cash,casino,castle,casual +227 5:cat,catalog,catch,category,cattle +228 3:caught,cause,caution +229 1:cave +231 1:ceiling +232 1:celery +233 1:cement +234 2:census,century +235 2:cereal,certain +241 3:chair,chalk,champion +243 6:change,chaos,chapter,charge,chase,chat +245 6:cheap,check,cheese,chef,cherry,chest +246 4:chicken,chief,child,chimney +247 2:choice,choose +248 1:chronic +249 3:chuckle,chunk,churn +251 1:cigar +252 1:cinnamon +253 1:circle +254 2:citizen,city +255 1:civil +261 5:claim,clap,clarify,claw,clay +262 3:clean,clerk,clever +263 6:click,client,cliff,climb,clinic,clip +264 6:clock,clog,close,cloth,cloud,clown +265 4:club,clump,cluster,clutch +271 5:coach,coast,coconut,code,coffee +272 5:coil,coin,collect,color,column +273 6:combine,come,comfort,comic,common,company +274 4:concert,conduct,confirm,congress +275 4:connect,consider,control,convince +276 4:cook,cool,copper,copy +277 6:coral,core,corn,correct,cost,cotton +278 5:couch,country,couple,course,cousin +279 2:cover,coyote +281 4:crack,cradle,craft,cram +283 5:crane,crash,crater,crawl,crazy +285 4:cream,credit,creek,crew +286 4:cricket,crime,crisp,critic +287 4:crop,cross,crouch,crowd +288 6:crucial,cruel,cruise,crumble,crunch,crush +289 2:cry,crystal +291 1:cube +292 1:culture +293 2:cup,cupboard +294 4:curious,current,curtain,curve +295 2:cushion,custom +296 1:cute +299 1:cycle +311 1:dad +312 2:damage,damp +313 2:dance,danger +314 1:daring +315 1:dash +316 1:daughter +317 1:dawn +318 1:day +321 1:deal +322 2:debate,debris +323 6:decade,december,decide,decline,decorate,decrease +324 1:deer +325 3:defense,define,defy +326 1:degree +327 2:delay,deliver +328 2:demand,demise +331 3:denial,dentist,deny +332 5:depart,depend,deposit,depth,deputy +333 1:derive +334 6:describe,desert,design,desk,despair,destroy +335 2:detail,detect +336 3:develop,device,devote +341 4:diagram,dial,diamond,diary +342 3:dice,diesel,diet +343 4:differ,digital,dignity,dilemma +344 2:dinner,dinosaur +345 2:direct,dirt +346 5:disagree,discover,disease,dish,dismiss +347 3:disorder,display,distance +348 3:divert,divide,divorce +349 1:dizzy +351 2:doctor,document +352 1:dog +353 2:doll,dolphin +354 1:domain +355 3:donate,donkey,donor +356 1:door +357 1:dose +358 1:double +359 1:dove +361 5:draft,dragon,drama,drastic,draw +362 2:dream,dress +363 5:drift,drill,drink,drip,drive +364 3:drop,drum,dry +365 2:duck,dumb +366 5:dune,during,dust,dutch,duty +368 1:dwarf +369 1:dynamic +371 2:eager,eagle +372 6:early,earn,earth,easily,east,easy +373 6:echo,ecology,economy,edge,edit,educate +374 4:effort,egg,eight,either +375 2:elbow,elder +376 5:electric,elegant,element,elephant,elevator +377 2:elite,else +378 4:embark,embody,embrace,emerge +379 4:emotion,employ,empower,empty +381 2:enable,enact +382 3:end,endless,endorse +383 5:enemy,energy,enforce,engage,engine +384 3:enhance,enjoy,enlist +385 4:enough,enrich,enroll,ensure +386 4:enter,entire,entry,envelope +387 3:episode,equal,equip +388 6:era,erase,erode,erosion,error,erupt +389 4:escape,essay,essence,estate +391 2:eternal,ethics +392 4:evidence,evil,evoke,evolve +393 2:exact,example +394 5:excess,exchange,excite,exclude,excuse +395 4:execute,exercise,exhaust,exhibit +396 4:exile,exist,exit,exotic +397 6:expand,expect,expire,explain,expose,express +398 2:extend,extra +399 2:eye,eyebrow +411 3:fabric,face,faculty +412 1:fade +413 2:faint,faith +414 2:fall,false +415 3:fame,family,famous +416 3:fan,fancy,fantasy +417 2:farm,fashion +418 4:fat,fatal,father,fatigue +419 2:fault,favorite +421 3:feature,february,federal +422 3:fee,feed,feel +423 6:female,fence,festival,fetch,fever,few +431 4:fiber,fiction,field,figure +432 3:file,film,filter +433 5:final,find,fine,finger,finish +434 3:fire,firm,first +435 5:fiscal,fish,fit,fitness,fix +441 5:flag,flame,flash,flat,flavor +442 3:flee,flight,flip +443 4:float,flock,floor,flower +444 3:fluid,flush,fly +445 4:foam,focus,fog,foil +446 4:fold,follow,food,foot +447 4:force,forest,forget,fork +448 3:fortune,forum,forward +449 4:fossil,foster,found,fox +451 2:fragile,frame +452 2:frequent,fresh +453 2:friend,fringe +454 5:frog,front,frost,frown,frozen +455 1:fruit +458 6:fuel,fun,funny,furnace,fury,future +461 2:gadget,gain +462 2:galaxy,gallery +463 2:game,gap +464 5:garage,garbage,garden,garlic,garment +465 2:gas,gasp +466 2:gate,gather +467 2:gauge,gaze +471 6:general,genius,genre,gentle,genuine,gesture +472 1:ghost +473 1:giant +474 1:gift +475 1:giggle +476 1:ginger +477 2:giraffe,girl +478 1:give +481 4:glad,glance,glare,glass +482 2:glide,glimpse +483 5:globe,gloom,glory,glove,glow +484 1:glue +486 2:goat,goddess +487 3:gold,good,goose +488 3:gorilla,gospel,gossip +489 2:govern,gown +491 3:grab,grace,grain +492 4:grant,grape,grass,gravity +493 2:great,green +494 3:grid,grief,grit +495 3:grocery,group,grow +496 1:grunt +497 6:guard,guess,guide,guilt,guitar,gun +498 1:gym +511 2:habit,hair +512 3:half,hammer,hamster +513 2:hand,happy +514 4:harbor,hard,harsh,harvest +515 4:hat,have,hawk,hazard +516 4:head,health,heart,heavy +517 2:hedgehog,height +518 3:hello,helmet,help +519 2:hen,hero +521 2:hidden,high +522 2:hill,hint +523 3:hip,hire,history +524 2:hobby,hockey +525 4:hold,hole,holiday,hollow +526 4:home,honey,hood,hope +527 3:horn,horror,horse +528 5:hospital,host,hotel,hour,hover +531 1:hub +532 1:huge +533 3:human,humble,humor +534 3:hundred,hungry,hunt +535 3:hurdle,hurry,hurt +536 1:husband +539 1:hybrid +541 2:ice,icon +542 3:idea,identify,idle +543 1:ignore +544 3:ill,illegal,illness +545 1:image +546 1:imitate +547 2:immense,immune +548 4:impact,impose,improve,impulse +551 4:inch,include,income,increase +552 4:index,indicate,indoor,industry +553 3:infant,inflict,inform +554 3:inhale,inherit,initial +555 3:inject,injury,inmate +556 4:inner,innocent,input,inquiry +557 5:insane,insect,inside,inspire,install +558 6:intact,interest,into,invest,invite,involve +559 6:iron,island,isolate,issue,item,ivory +561 4:jacket,jaguar,jar,jazz +562 4:jealous,jeans,jelly,jewel +563 5:job,join,joke,journey,joy +564 1:judge +565 1:juice +566 1:jump +567 3:jungle,junior,junk +568 1:just +571 1:kangaroo +572 4:keen,keep,ketchup,key +573 1:kick +574 2:kid,kidney +575 2:kind,kingdom +576 1:kiss +577 4:kit,kitchen,kite,kitten +578 1:kiwi +579 4:knee,knife,knock,know +581 5:lab,label,labor,ladder,lady +582 3:lake,lamp,language +583 4:laptop,large,later,latin +584 3:laugh,laundry,lava +585 5:law,lawn,lawsuit,layer,lazy +586 4:leader,leaf,learn,leave +587 5:lecture,left,leg,legal,legend +588 5:leisure,lemon,lend,length,lens +589 4:leopard,lesson,letter,level +591 4:liar,liberty,library,license +592 4:life,lift,light,like +593 4:limb,limit,link,lion +594 5:liquid,list,little,live,lizard +595 5:load,loan,lobster,local,lock +596 4:logic,lonely,long,loop +597 5:lottery,loud,lounge,love,loyal +598 6:lucky,luggage,lumber,lunar,lunch,luxury +599 1:lyrics +611 4:machine,mad,magic,magnet +612 3:maid,mail,main +613 3:major,make,mammal +614 6:man,manage,mandate,mango,mansion,manual +615 1:maple +616 6:marble,march,margin,marine,market,marriage +617 3:mask,mass,master +618 5:match,material,math,matrix,matter +619 2:maximum,maze +621 4:meadow,mean,measure,meat +622 1:mechanic +623 2:medal,media +624 2:melody,melt +625 2:member,memory +626 2:mention,menu +627 4:mercy,merge,merit,merry +628 2:mesh,message +629 2:metal,method +631 2:middle,midnight +632 2:milk,million +633 1:mimic +634 4:mind,minimum,minor,minute +635 2:miracle,mirror +636 3:misery,miss,mistake +637 3:mix,mixed,mixture +641 5:mobile,model,modify,mom,moment +642 4:monitor,monkey,monster,month +643 4:moon,moral,more,morning +644 4:mosquito,mother,motion,motor +645 4:mountain,mouse,move,movie +646 4:much,muffin,mule,multiply +647 5:muscle,museum,mushroom,music,must +648 1:mutual +649 3:myself,mystery,myth +651 2:naive,name +652 2:napkin,narrow +653 3:nasty,nation,nature +654 2:near,neck +655 3:need,negative,neglect +656 2:neither,nephew +657 2:nerve,nest +658 3:net,network,neutral +659 3:never,news,next +661 2:nice,night +662 4:noble,noise,nominee,noodle +663 2:normal,north +664 1:nose +665 4:notable,note,nothing,notice +666 2:novel,now +669 4:nuclear,number,nurse,nut +671 1:oak +672 3:obey,object,oblige +673 4:obscure,observe,obtain,obvious +675 3:occur,ocean,october +676 1:odor +677 4:off,offer,office,often +678 1:oil +679 1:okay +681 3:old,olive,olympic +682 1:omit +683 5:once,one,onion,online,only +684 5:open,opera,opinion,oppose,option +691 6:orange,orbit,orchard,order,ordinary,organ +692 3:orient,original,orphan +693 1:ostrich +694 1:other +695 4:outdoor,outer,output,outside +696 3:oval,oven,over +697 2:own,owner +698 3:oxygen,oyster,ozone +711 6:pact,paddle,page,pair,palace,palm +712 5:panda,panel,panic,panther,paper +713 6:parade,parent,park,parrot,party,pass +714 5:patch,path,patient,patrol,pattern +715 3:pause,pave,payment +716 4:peace,peanut,pear,peasant +717 5:pelican,pen,penalty,pencil,people +718 5:pepper,perfect,permit,person,pet +719 4:phone,photo,phrase,physical +721 4:piano,picnic,picture,piece +722 5:pig,pigeon,pill,pilot,pink +723 5:pioneer,pipe,pistol,pitch,pizza +724 5:place,planet,plastic,plate,play +725 5:please,pledge,pluck,plug,plunge +726 3:poem,poet,point +727 5:polar,pole,police,pond,pony +728 6:pool,popular,portion,position,possible,post +729 5:potato,pottery,poverty,powder,power +731 2:practice,praise +732 6:predict,prefer,prepare,present,pretty,prevent +733 3:price,pride,primary +735 5:print,priority,prison,private,prize +736 4:problem,process,produce,profit +737 5:program,project,promote,proof,property +739 4:prosper,protect,proud,provide +741 2:public,pudding +742 3:pull,pulp,pulse +743 2:pumpkin,punch +744 2:pupil,puppy +745 4:purchase,purity,purpose,purse +746 3:push,put,puzzle +749 1:pyramid +751 3:quality,quantum,quarter +752 1:question +753 3:quick,quit,quiz +754 1:quote +761 1:rabbit +762 3:raccoon,race,rack +763 2:radar,radio +764 3:rail,rain,raise +765 2:rally,ramp +766 3:ranch,random,range +767 2:rapid,rare +768 2:rate,rather +769 3:raven,raw,razor +771 3:ready,real,reason +772 2:rebel,rebuild +773 5:recall,receive,recipe,record,recycle +774 1:reduce +775 3:reflect,reform,refuse +776 3:region,regret,regular +777 1:reject +778 4:relax,release,relief,rely +779 4:remain,remember,remind,remove +781 3:render,renew,rent +782 1:reopen +783 4:repair,repeat,replace,report +784 1:require +785 6:rescue,resemble,resist,resource,response,result +786 3:retire,retreat,return +787 1:reunion +788 2:reveal,review +789 1:reward +791 1:rhythm +792 6:rib,ribbon,rice,rich,ride,ridge +793 5:rifle,right,rigid,ring,riot +794 5:ripple,risk,ritual,rival,river +795 5:road,roast,robot,robust,rocket +796 5:romance,roof,rookie,room,rose +797 5:rotate,rough,round,route,royal +798 3:rubber,rude,rug +799 4:rule,run,runway,rural +811 5:sad,saddle,sadness,safe,sail +812 5:salad,salmon,salon,salt,salute +813 3:same,sample,sand +814 4:satisfy,satoshi,sauce,sausage +815 2:save,say +816 4:scale,scan,scare,scatter +817 3:scene,scheme,school +818 4:science,scissors,scorpion,scout +819 4:scrap,screen,script,scrub +821 4:sea,search,season,seat +822 4:second,secret,section,security +823 6:seed,seek,segment,select,sell,seminar +824 3:senior,sense,sentence +825 6:series,service,session,settle,setup,seven +831 4:shadow,shaft,shallow,share +832 3:shed,shell,sheriff +833 5:shield,shift,shine,ship,shiver +834 2:shock,shoe +836 5:shoot,shop,short,shoulder,shove +837 2:shrimp,shrug +838 1:shuffle +839 1:shy +841 4:sibling,sick,side,siege +842 6:sight,sign,silent,silk,silly,silver +843 4:similar,simple,since,sing +844 5:siren,sister,situate,six,size +845 2:skate,sketch +846 5:ski,skill,skin,skirt,skull +847 4:slab,slam,sleep,slender +848 4:slice,slide,slight,slim +849 4:slogan,slot,slow,slush +851 5:small,smart,smile,smoke,smooth +852 5:snack,snake,snap,sniff,snow +853 1:soap +854 3:soccer,social,sock +855 2:soda,soft +856 5:solar,soldier,solid,solution,solve +857 3:someone,song,soon +858 2:sorry,sort +859 5:soul,sound,soup,source,south +861 4:space,spare,spatial,spawn +862 5:speak,special,speed,spell,spend +863 1:sphere +864 5:spice,spider,spike,spin,spirit +865 1:split +866 5:spoil,sponsor,spoon,sport,spot +867 3:spray,spread,spring +868 1:spy +869 3:square,squeeze,squirrel +871 6:stable,stadium,staff,stage,stairs,stamp +872 4:stand,start,state,stay +873 5:steak,steel,stem,step,stereo +874 3:stick,still,sting +875 6:stock,stomach,stone,stool,story,stove +876 5:strategy,street,strike,strong,struggle +877 3:student,stuff,stumble +878 1:style +881 3:subject,submit,subway +882 2:success,such +883 4:sudden,suffer,sugar,suggest +884 2:suit,summer +885 3:sun,sunny,sunset +886 3:super,supply,supreme +887 6:sure,surface,surge,surprise,surround,survey +888 2:suspect,sustain +891 4:swallow,swamp,swap,swarm +892 2:swear,sweet +893 4:swift,swim,swing,switch +894 1:sword +898 4:symbol,symptom,syrup,system +911 4:table,tackle,tag,tail, +912 5:talent,talk,tank,tape,target +913 4:task,taste,tattoo,taxi +914 3:teach,team,tell +915 4:ten,tenant,tennis,tent +916 3:term,test,text +921 2:thank,that +922 5:theme,then,theory,there,they +923 3:thing,this,thought +924 3:three,thrive,throw +925 2:thumb,thunder +926 2:ticket,tide +927 4:tiger,tilt,timber,time +928 2:tiny,tip +929 3:tired,tissue,title +931 4:toast,tobacco,today,toddler +932 3:toe,together,toilet +933 3:token,tomato,tomorrow +934 3:tone,tongue,tonight +935 2:tool,tooth +936 3:top,topic,topple +937 3:torch,tornado,tortoise +938 3:toss,total,tourist +939 4:toward,tower,town,toy +941 5:track,trade,traffic,tragic,train +942 5:transfer,trap,trash,travel,tray +943 3:treat,tree,trend +944 6:trial,tribe,trick,trigger,trim,trip +945 2:trophy,trouble +946 6:truck,true,truly,trumpet,trust,truth +947 1:try +951 5:tube,tuition,tumble,tuna,tunnel +952 3:turkey,turn,turtle +953 6:twelve,twenty,twice,twin,twist,two +954 2:type,typical +961 2:ugly,umbrella +962 4:unable,unaware,uncle,uncover +963 5:under,undo,unfair,unfold,unhappy +964 4:uniform,unique,unit,universe +965 5:unknown,unlock,until,unusual,unveil +966 6:update,upgrade,uphold,upon,upper,upset +967 2:urban,urge +968 6:usage,use,used,useful,useless,usual +969 1:utility +971 6:vacant,vacuum,vague,valid,valley,valve +972 6:van,vanish,vapor,various,vast,vault +973 5:vehicle,velvet,vendor,venture,venue +974 6:verb,verify,version,very,vessel,veteran +975 5:viable,vibrant,vicious,victory,video +976 6:view,village,vintage,violin,virtual,virus +977 5:visa,visit,visual,vital,vivid +978 3:vocal,voice,void +979 4:volcano,volume,vote,voyage +981 3:wage,wagon,wait +982 4:walk,wall,walnut,want +983 3:warfare,warm,warrior +984 6:wash,wasp,waste,water,wave,way +985 5:wealth,weapon,wear,weasel,weather +986 3:web,wedding,weekend +987 4:weird,welcome,west,wet +988 6:whale,what,wheat,wheel,when,where +989 2:whip,whisper +991 5:wide,width,wife,wild,will +992 5:win,window,wine,wing,wink +993 2:winner,winter +994 5:wire,wisdom,wise,wish,witness +995 5:wolf,woman,wonder,wood,wool +996 5:word,work,world,worry,worth +997 6:wrap,wreck,wrestle,wrist,write,wrong +998 6:yard,year,yellow,you,young,youth +999 4:zebra,zero,zone,zoo