1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-11-12 18:49:07 +00:00

New Matrix-based recovery

This commit is contained in:
Jochen Hoenicke 2016-11-23 12:56:42 +01:00 committed by Pavol Rusnak
parent 14aa486fa6
commit acfdb714ff
No known key found for this signature in database
GPG Key ID: 91F3B339B9A02A3D
6 changed files with 1210 additions and 56 deletions

View File

@ -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
);
}

112
firmware/recovery-table.h Normal file
View File

@ -0,0 +1,112 @@
/*
* This file is part of the TREZOR project.
*
* Copyright (C) 2016 Jochen Hoenicke <hoenicke@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
/* 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,
};

View File

@ -2,6 +2,7 @@
* This file is part of the TREZOR project.
*
* Copyright (C) 2014 Pavol Rusnak <stick@satoshilabs.com>
* Copyright (C) 2016 Jochen Hoenicke <hoenicke@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <ctype.h>
#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;
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();
}
for (i = word_count; i < 24; i++) {
word_order[i] = 0;
}
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;
}
}

View File

@ -23,7 +23,7 @@
#include <stdint.h>
#include <stdbool.h>
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);

View File

@ -0,0 +1,110 @@
#!/usr/bin/perl
print <<'EOF';
/*
* This file is part of the TREZOR project.
*
* Copyright (C) 2016 Jochen Hoenicke <hoenicke@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
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";

View File

@ -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