legacy/firmware: implement dual-button ime support for seed recovery

pull/712/head
exeabyss 5 years ago
parent c2de7ac291
commit 035b94df45

@ -246,15 +246,50 @@ int mnemonic_find_word(const char *word) {
return -1;
}
const char *mnemonic_complete_word(const char *prefix, int len) {
// we need to perform linear search,
// because we want to return the first match
for (const char *const *w = wordlist; *w != 0; w++) {
if (strncmp(*w, prefix, len) == 0) {
return *w;
int mnemonic_find_words(const char *prefix, int len, int *end) {
int lo = 0, hi = BIP39_WORDS - 1, mid;
while (lo <= hi) {
mid = lo + (hi - lo) / 2;
int cmp = strncmp(prefix, wordlist[mid], len);
if (cmp == 0) {
break;
}
if (cmp > 0) {
lo = mid + 1;
} else {
hi = mid - 1;
}
}
if (lo <= hi) {
if (end != NULL) {
int tmp = mid;
while (tmp < BIP39_WORDS - 1) {
if (strncmp(prefix, wordlist[tmp + 1], len) == 0) {
++tmp;
} else {
break;
}
}
*end = tmp + 1;
}
int tmp = mid;
while (tmp > 0) {
if (strncmp(prefix, wordlist[tmp - 1], len) == 0) {
--tmp;
} else {
break;
}
}
return tmp;
} else {
*end = -1;
return -1;
}
return NULL;
}
const char *mnemonic_complete_word(const char *prefix, int len) {
int index = mnemonic_find_words(prefix, len, NULL);
return index >= 0 ? wordlist[index] : NULL;
}
const char *mnemonic_get_word(int index) {

@ -45,6 +45,7 @@ void mnemonic_to_seed(const char *mnemonic, const char *passphrase,
uint32_t total));
int mnemonic_find_word(const char *word);
int mnemonic_find_words(const char *prefix, int len, int *end);
const char *mnemonic_complete_word(const char *prefix, int len);
const char *mnemonic_get_word(int index);
uint32_t mnemonic_word_completion_mask(const char *prefix, int len);

@ -19,6 +19,7 @@ OBJS += fsm.o
OBJS += coins.o
OBJS += coin_info.o
OBJS += transaction.o
OBJS += input.o
OBJS += protect.o
OBJS += layout2.o
OBJS += recovery.o

@ -0,0 +1,217 @@
/*
* This file is part of the Trezor project, https://trezor.io/
*
* Copyright (C) 2014 Pavol Rusnak <stick@satoshilabs.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/>.
*/
#include "input.h"
#include "buttons.h"
#include "config.h"
#include "gettext.h"
#include "layout2.h"
#include "memory.h"
#include "oled.h"
#include "rng.h"
#include "usb.h"
#define CARET_SHOW 80
#define CARET_CYCLE (CARET_SHOW * 2)
void buttonCheckRepeat(bool *yes, bool *no, bool *confirm) {
*yes = false;
*no = false;
*confirm = false;
const int Threshold0 = 20;
const int Thresholds[] = {Threshold0, 80, 20, 18, 16, 14, 12, 10, 8, 6, 4};
const int MaxThresholdLevel = sizeof(Thresholds) / sizeof(Thresholds[0]) - 1;
static int yesthreshold = Threshold0;
static int nothreshold = Threshold0;
static int yeslevel = 0;
static int nolevel = 0;
static bool both = false;
usbSleep(5);
buttonUpdate();
if (both) {
if (!button.YesDown && !button.NoDown) {
both = false;
yeslevel = 0;
nolevel = 0;
yesthreshold = Thresholds[0];
nothreshold = Thresholds[0];
}
} else if ((button.YesDown && button.NoDown) ||
(button.YesUp && button.NoDown) ||
(button.YesDown && button.NoUp) || (button.YesUp && button.NoUp)) {
if (!yeslevel && !nolevel) {
both = true;
*confirm = true;
}
} else {
if (button.YesUp) {
if (!yeslevel) *yes = true;
yeslevel = 0;
yesthreshold = Thresholds[0];
} else if (button.YesDown >= yesthreshold) {
if (yeslevel < MaxThresholdLevel) ++yeslevel;
yesthreshold += Thresholds[yeslevel];
*yes = true;
}
if (button.NoUp) {
if (!nolevel) *no = true;
nolevel = 0;
nothreshold = Thresholds[0];
} else if (button.NoDown >= nothreshold) {
if (nolevel < MaxThresholdLevel) ++nolevel;
nothreshold += Thresholds[nolevel];
*no = true;
}
}
}
void buttonWaitForYesUp(void) {
buttonUpdate();
for (;;) {
usbSleep(5);
buttonUpdate();
if (button.YesUp) break;
}
}
void buttonWaitForIdle(void) {
buttonUpdate();
for (;;) {
usbSleep(5);
buttonUpdate();
if (!button.YesDown && !button.YesUp && !button.NoDown && !button.NoUp)
break;
}
}
void requestOnDeviceTextInput(void) {
layoutDialog(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL,
_("Do you like to use"), _("on-device text input?"), NULL, NULL,
NULL, NULL);
buttonUpdate();
for (;;) {
usbSleep(5);
buttonUpdate();
if (button.YesUp || button.NoUp) break;
}
layoutSwipe();
session_setUseOnDeviceTextInput(button.YesUp);
}
int findCharIndex(const char entries[], char needle, int numtotal,
int startindex, bool forward) {
if (numtotal <= 1 || entries[startindex] == needle) return startindex;
int step = forward ? 1 : -1;
int index = (startindex + step + numtotal) % numtotal;
while (index != startindex) {
if (entries[index] == needle) return index;
index += step;
}
return startindex;
}
int inputTextScroll(char *text, int *textcharindex, int maxtextcharindex,
const char entries[], int textwidth, int entryindex,
int numtotal, int numscreen, int horizontalpadding,
const int groups[], int numgroup, int numskipingroups,
int *caret) {
for (;; *caret = (*caret + 1) % CARET_CYCLE) {
bool yes, no, confirm;
buttonCheckRepeat(&yes, &no, &confirm);
if (confirm) {
buttonWaitForIdle();
if (entries[entryindex] == CHAR_BCKSPC) {
if (*textcharindex > 0) {
--(*textcharindex);
text[*textcharindex] = 0;
}
} else if (entries[entryindex] == CHAR_DONE) {
return INPUT_DONE;
} else {
if (*textcharindex < maxtextcharindex) {
text[*textcharindex] = entries[entryindex];
++(*textcharindex);
}
return entryindex;
}
entryindex = random32() % numtotal;
} else {
if (yes) entryindex = (entryindex + 1) % numtotal;
if (no) entryindex = (entryindex - 1 + numtotal) % numtotal;
}
layoutScrollInput(text, textwidth, numtotal, numscreen, entryindex, entries,
horizontalpadding, numgroup, groups, numskipingroups,
*caret < CARET_SHOW);
}
}
bool inputText(char *text, int maxtextlen, const char characters[],
int numcharacters, char groupseparator, int width,
bool requiredone, bool allowempty) {
#define MAX_NUM_CHARACTERS_GROUPS 32
int charactersGroups[MAX_NUM_CHARACTERS_GROUPS];
charactersGroups[0] = 0;
int numcharactersgroups = 1;
for (int i = 0; i < numcharacters; ++i) {
if (characters[i] == groupseparator) {
charactersGroups[numcharactersgroups] = i + 1;
++numcharactersgroups;
if (numcharactersgroups >= MAX_NUM_CHARACTERS_GROUPS) {
break;
}
}
}
usbSleep(5);
buttonUpdate();
int charindex = strlen(text);
int caret = 0;
for (;;) {
int entryindex = random32() % numcharacters;
if (charindex >= maxtextlen)
entryindex = findCharIndex(characters, CHAR_DONE, numcharacters,
entryindex, entryindex < numcharacters / 2);
entryindex = inputTextScroll(
text, &charindex, maxtextlen, characters, width, entryindex,
numcharacters, 9, 9, charactersGroups, numcharactersgroups, 2, &caret);
if ((!requiredone || entryindex == INPUT_DONE) &&
(allowempty || charindex > 0)) {
return entryindex == INPUT_DONE;
}
}
}

@ -0,0 +1,35 @@
/*
* This file is part of the Trezor project, https://trezor.io/
*
* Copyright (C) 2014 Pavol Rusnak <stick@satoshilabs.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/>.
*/
#ifndef __INPUT_H__
#define __INPUT_H__
#include <stdbool.h>
#define INPUT_DONE -1
void buttonWaitForYesUp(void);
void requestOnDeviceTextInput(void);
bool inputText(char *text, int maxtextlen, const char characters[],
int numcharacters, char groupseparator, int width,
bool requiredone, bool allowempty);
#endif

@ -23,6 +23,7 @@
#include "debug.h"
#include "fsm.h"
#include "gettext.h"
#include "input.h"
#include "layout2.h"
#include "memory.h"
#include "memzero.h"
@ -36,172 +37,15 @@
#define MAX_WRONG_PINS 15
#define BACKSPACE '\x08'
#define SPACE '\x09'
#define DONE '\x06'
#define INPUT_DONE -1
#define CHAR_AND_SPACE_WIDTH (5 + 1)
#define NUM_PASSPHRASE_LINES 3
#define PASSPHRASE_WIDTH \
((MAX_PASSPHRASE_LEN + 1) / NUM_PASSPHRASE_LINES * CHAR_AND_SPACE_WIDTH)
#define PIN_WIDTH (MAX_PIN_LEN * CHAR_AND_SPACE_WIDTH)
#define CARET_SHOW 80
#define CARET_CYCLE (CARET_SHOW * 2)
bool protectAbortedByCancel = false;
bool protectAbortedByInitialize = false;
void buttonCheckRepeat(bool *yes, bool *no, bool *confirm) {
*yes = false;
*no = false;
*confirm = false;
const int Threshold0 = 20;
const int Thresholds[] = {Threshold0, 80, 20, 18, 16, 14, 12, 10, 8, 6, 4};
const int MaxThresholdLevel = sizeof(Thresholds) / sizeof(Thresholds[0]) - 1;
static int yesthreshold = Threshold0;
static int nothreshold = Threshold0;
static int yeslevel = 0;
static int nolevel = 0;
static bool both = false;
usbSleep(5);
buttonUpdate();
if (both) {
if (!button.YesDown && !button.NoDown) {
both = false;
yeslevel = 0;
nolevel = 0;
yesthreshold = Thresholds[0];
nothreshold = Thresholds[0];
}
} else if ((button.YesDown && button.NoDown) ||
(button.YesUp && button.NoDown) ||
(button.YesDown && button.NoUp) || (button.YesUp && button.NoUp)) {
if (!yeslevel && !nolevel) {
both = true;
*confirm = true;
}
} else {
if (button.YesUp) {
if (!yeslevel) *yes = true;
yeslevel = 0;
yesthreshold = Thresholds[0];
} else if (button.YesDown >= yesthreshold) {
if (yeslevel < MaxThresholdLevel) yeslevel++;
yesthreshold += Thresholds[yeslevel];
*yes = true;
}
if (button.NoUp) {
if (!nolevel) *no = true;
nolevel = 0;
nothreshold = Thresholds[0];
} else if (button.NoDown >= nothreshold) {
if (nolevel < MaxThresholdLevel) nolevel++;
nothreshold += Thresholds[nolevel];
*no = true;
}
}
}
void buttonWaitForYesUp(void) {
buttonUpdate();
for (;;) {
usbSleep(5);
buttonUpdate();
if (button.YesUp) break;
}
}
void buttonWaitForIdle(void) {
buttonUpdate();
for (;;) {
usbSleep(5);
buttonUpdate();
if (!button.YesDown && !button.YesUp && !button.NoDown && !button.NoUp)
break;
}
}
void requestOnDeviceTextInput(void) {
layoutDialog(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL,
_("Do you like to use"), _("on-device text input?"), NULL, NULL,
NULL, NULL);
buttonUpdate();
for (;;) {
usbSleep(5);
buttonUpdate();
if (button.YesUp || button.NoUp) break;
}
layoutSwipe();
session_setUseOnDeviceTextInput(button.YesUp);
}
int findCharIndex(const char entries[], char needle, int numtotal,
int startindex, bool forward) {
int index = startindex;
int step = forward ? 1 : -1;
while (index >= 0 && index < numtotal) {
if (entries[index] == needle) return index;
index += step;
}
return startindex;
}
int inputTextScroll(char *text, int *textcharindex, int maxtextcharindex,
const char entries[], int textwidth, int entryindex,
int numtotal, int numscreen, int horizontalpadding,
const int groups[], int numgroup, int numskipingroups,
int *caret) {
for (;; *caret = (*caret + 1) % CARET_CYCLE) {
bool yes, no, confirm;
buttonCheckRepeat(&yes, &no, &confirm);
if (confirm) {
buttonWaitForIdle();
if (entries[entryindex] == BACKSPACE) {
if (*textcharindex > 0) {
--(*textcharindex);
text[*textcharindex] = 0;
}
} else if (entries[entryindex] == DONE) {
return INPUT_DONE;
} else {
if (*textcharindex < maxtextcharindex) {
text[*textcharindex] = entries[entryindex];
++(*textcharindex);
}
return entryindex;
}
entryindex = random32() % numtotal;
} else {
if (yes) entryindex = (entryindex + 1) % numtotal;
if (no) entryindex = (entryindex - 1 + numtotal) % numtotal;
}
layoutScrollInput(text, textwidth, numtotal, numscreen, entryindex, entries,
horizontalpadding, numgroup, groups, numskipingroups,
*caret < CARET_SHOW);
}
}
bool protectButton(ButtonRequestType type, bool confirm_only) {
ButtonRequest resp = {0};
bool result = false;
@ -358,33 +202,17 @@ secbool protectPinUiCallback(uint32_t wait, uint32_t progress,
return secfalse;
}
void userEnterPin(char pin[]) {
const char Entries[] = {
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', BACKSPACE, DONE,
void inputPin(char pin[]) {
const char Characters[] = {
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', CHAR_BCKSPC, CHAR_DONE,
};
const int EntriesGroups[] = {0, 12};
int numentries = sizeof(Entries) / sizeof(Entries[0]);
int numentriesgroups = sizeof(EntriesGroups) / sizeof(EntriesGroups[0]);
usbSleep(5);
buttonUpdate();
int pincharindex = strlen(pin);
int caret = 0;
for (;;) {
int entryindex = random32() % numentries;
if (pincharindex >= MAX_PIN_LEN)
entryindex = findCharIndex(Entries, DONE, numentries, entryindex, true);
entryindex = inputTextScroll(pin, &pincharindex, MAX_PIN_LEN, Entries,
PIN_WIDTH, entryindex, numentries, 9, 9,
EntriesGroups, numentriesgroups, 2, &caret);
if (entryindex == INPUT_DONE && pincharindex > 0) return;
}
inputText(pin, MAX_PIN_LEN, Characters,
sizeof(Characters) / sizeof(Characters[0]), CHAR_DONE, PIN_WIDTH,
true, false);
}
bool userCheckPin(char pin[]) {
bool confirmPin(char pin[]) {
layoutCheckInput(pin, PIN_WIDTH, true, true, "Confirm PIN:", NULL, NULL);
buttonUpdate();
@ -407,10 +235,10 @@ void requestPinDevice(const char *line1, const char *line2, const char *line3,
layoutSwipe();
for (;;) {
userEnterPin(pin);
inputPin(pin);
layoutSwipe();
if (userCheckPin(pin)) break;
if (confirmPin(pin)) break;
oledSwipeRight();
}
@ -579,51 +407,40 @@ bool protectPassphraseComputer(void) {
return result;
}
void userEnterPassphrase(char *passphrase) {
const char Entries[] = {
'a', 'b', 'c', 'd', 'e', 'f', 'g',
'h', 'i', BACKSPACE, DONE, 'j', 'k', 'l',
'm', 'n', 'o', 'p', 'q', 'r', BACKSPACE,
DONE, 's', 't', 'u', 'v', 'w', 'x',
'y', 'z', SPACE, BACKSPACE, DONE, 'A', 'B',
'C', 'D', 'E', 'F', 'G', 'H', 'I',
BACKSPACE, DONE, 'J', 'K', 'L', 'M', 'N',
'O', 'P', 'Q', 'R', BACKSPACE, DONE, 'S',
'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
SPACE, BACKSPACE, DONE, '1', '2', '3', '4',
'5', '6', '7', '8', '9', '0', BACKSPACE,
DONE, '!', '@', '#', '$', '\x25', '^',
'&', '*', '(', ')', BACKSPACE, DONE, '`',
'-', '=', '[', ']', '\\', ';', '\'',
',', '.', '/', BACKSPACE, DONE, '~', '_',
'+', '{', '}', '|', ':', '\'', '<',
'>', '?', BACKSPACE, DONE};
const int EntriesGroups[] = {0, 11, 22, 33, 44, 55, 66, 78, 90, 103, 116};
int numentries = sizeof(Entries) / sizeof(Entries[0]);
int numentriesgroups = sizeof(EntriesGroups) / sizeof(EntriesGroups[0]);
usbSleep(5);
buttonUpdate();
int passphrasecharindex = strlen(passphrase);
int caret = 0;
for (;;) {
int entryindex = random32() % numentries;
if (passphrasecharindex >= MAX_PASSPHRASE_LEN)
entryindex = findCharIndex(Entries, DONE, numentries, entryindex,
entryindex < numentries / 2);
entryindex =
inputTextScroll(passphrase, &passphrasecharindex, MAX_PASSPHRASE_LEN,
Entries, PASSPHRASE_WIDTH, entryindex, numentries, 9, 9,
EntriesGroups, numentriesgroups, 2, &caret);
if (entryindex == INPUT_DONE) return;
}
void inputPassphrase(char *passphrase) {
const char Characters[] = {
'a', 'b', 'c', 'd', 'e',
'f', 'g', 'h', 'i', CHAR_BCKSPC,
CHAR_DONE, 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r',
CHAR_BCKSPC, CHAR_DONE, 's', 't', 'u',
'v', 'w', 'x', 'y', 'z',
CHAR_SPACE, CHAR_BCKSPC, CHAR_DONE, 'A', 'B',
'C', 'D', 'E', 'F', 'G',
'H', 'I', CHAR_BCKSPC, CHAR_DONE, 'J',
'K', 'L', 'M', 'N', 'O',
'P', 'Q', 'R', CHAR_BCKSPC, CHAR_DONE,
'S', 'T', 'U', 'V', 'W',
'X', 'Y', 'Z', CHAR_SPACE, CHAR_BCKSPC,
CHAR_DONE, '1', '2', '3', '4',
'5', '6', '7', '8', '9',
'0', CHAR_BCKSPC, CHAR_DONE, '!', '@',
'#', '$', '\x25', '^', '&',
'*', '(', ')', CHAR_BCKSPC, CHAR_DONE,
'`', '-', '=', '[', ']',
'\\', ';', '\'', ',', '.',
'/', CHAR_BCKSPC, CHAR_DONE, '~', '_',
'+', '{', '}', '|', ':',
'\'', '<', '>', '?', CHAR_BCKSPC,
CHAR_DONE};
inputText(passphrase, MAX_PASSPHRASE_LEN, Characters,
sizeof(Characters) / sizeof(Characters[0]), CHAR_DONE,
PASSPHRASE_WIDTH, true, true);
}
bool userCheckPassphrase(const char *passphrase, bool enable_edit,
bool enable_done) {
bool confirmPassphrase(const char *passphrase, bool enable_edit,
bool enable_done) {
layoutCheckInput(passphrase, PASSPHRASE_WIDTH, enable_edit, enable_done,
"Confirm passphrase:", "Passphrases mismatched:",
"Passphrase confirmed:");
@ -669,9 +486,9 @@ bool protectPassphraseDevice(void) {
layoutSwipe();
for (;;) {
userEnterPassphrase(passphrase);
inputPassphrase(passphrase);
if (userCheckPassphrase(passphrase, true, true)) break;
if (confirmPassphrase(passphrase, true, true)) break;
oledSwipeRight();
}
@ -688,21 +505,21 @@ bool protectPassphraseDevice(void) {
layoutSwipe();
for (;;) {
userEnterPassphrase(passphrase2);
inputPassphrase(passphrase2);
if (strcmp(passphrase, passphrase2) == 0) break;
userCheckPassphrase(passphrase2, true, false);
confirmPassphrase(passphrase2, true, false);
oledSwipeRight();
}
memzero(passphrase2, sizeof(passphrase2));
}
userCheckPassphrase(passphrase, false, true);
confirmPassphrase(passphrase, false, true);
for (int i = 0; i < MAX_PASSPHRASE_LEN + 1 && passphrase[i]; ++i)
if (passphrase[i] == SPACE) passphrase[i] = ' ';
if (passphrase[i] == CHAR_SPACE) passphrase[i] = ' ';
session_cachePassphrase(passphrase);
memzero(passphrase, sizeof(passphrase));

@ -21,9 +21,12 @@
#include "recovery.h"
#include <ctype.h>
#include "bip39.h"
#include "bip39_english.h"
#include "buttons.h"
#include "config.h"
#include "fsm.h"
#include "gettext.h"
#include "input.h"
#include "layout2.h"
#include "memzero.h"
#include "messages.h"
@ -34,6 +37,10 @@
#include "rng.h"
#include "usb.h"
#define MAX_WORD_LEN 11
#define WORD_SIZE (MAX_WORD_LEN + 1)
#define WORD_WIDTH (MAX_WORD_LEN * CHAR_AND_SPACE_WIDTH)
/* number of words expected in the new seed */
static uint32_t word_count;
@ -56,7 +63,7 @@ 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];
static char fake_word[WORD_SIZE];
/* 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.
@ -75,7 +82,7 @@ static char word_order[24];
/* The recovered seed. This is filled during the recovery process.
*/
static char words[24][12];
static char words[24][WORD_SIZE];
/* The "pincode" of the current word. This is basically the "pin"
* that the user would have entered for the current word if the words
@ -231,7 +238,7 @@ static void recovery_done(void) {
* 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,
static void add_choice(char choice[WORD_SIZE], int prefixlen, const char *first,
const char *last) {
// assert 1 <= prefixlen <= 4
char *dest = choice;
@ -269,7 +276,8 @@ static void add_choice(char choice[12], int prefixlen, const char *first,
* 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) {
static void display_choices(bool twoColumn, char choices[9][WORD_SIZE],
int num) {
const int nColumns = twoColumn ? 2 : 3;
const int displayedChoices = nColumns * 3;
for (int i = 0; i < displayedChoices; i++) {
@ -323,7 +331,7 @@ static void display_choices(bool twoColumn, char choices[9][12], int num) {
* Generates a new matrix and requests the next pin.
*/
static void next_matrix(void) {
char word_choices[9][12] = {0};
char word_choices[9][WORD_SIZE] = {0};
uint32_t idx = 0, num = 0;
bool last = (word_index % 4) == 3;
@ -353,7 +361,7 @@ static void next_matrix(void) {
idx = TABLE1(word_pincode);
num = TABLE1(word_pincode + 1) - idx;
for (uint32_t i = 0; i < num; i++) {
add_choice(word_choices[i], (word_table2[idx + i] >> 12),
add_choice(word_choices[i], (word_table2[idx + i] >> WORD_SIZE),
mnemonic_get_word(TABLE2(idx + i)),
mnemonic_get_word(TABLE2(idx + i + 1) - 1));
}
@ -366,7 +374,7 @@ static void next_matrix(void) {
idx = word_pincode * 9;
num = 9;
for (uint32_t i = 0; i < num; i++) {
add_choice(word_choices[i], (word_table1[idx + i] >> 12),
add_choice(word_choices[i], (word_table1[idx + i] >> WORD_SIZE),
mnemonic_get_word(TABLE2(TABLE1(idx + i))),
mnemonic_get_word(TABLE2(TABLE1(idx + i + 1)) - 1));
}
@ -469,6 +477,63 @@ void next_word(void) {
recovery_request();
}
bool confirmWord(const char *word, bool enable_edit, bool enable_done) {
layoutCheckInput(word, WORD_WIDTH, enable_edit, enable_done,
"Confirm word:", "Word not found:", "Word confirmed:");
buttonUpdate();
for (;;) {
usbSleep(5);
buttonUpdate();
if (enable_done && button.YesUp) return true;
if (enable_edit && button.NoUp) return false;
}
}
void input_seed(void) {
const char Characters[] = {
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', CHAR_BCKSPC,
CHAR_DONE, 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
CHAR_BCKSPC, CHAR_DONE, 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
CHAR_BCKSPC, CHAR_DONE};
char prefix[5];
for (word_index = 0; word_index < word_count; ++word_index) {
memzero(prefix, sizeof(prefix));
char desc[] = "##th word";
int nr = word_index + 1;
format_number(desc, nr);
layoutDialogSwipe(&bmp_icon_info, NULL, "Next", NULL, _("Please enter the"),
(nr < 10 ? desc + 1 : desc), _("of your mnemonic"),
_("on the next screen."), NULL, NULL);
for (;;) {
usbSleep(5);
buttonUpdate();
if (button.YesUp) break;
}
for (;;) {
int begin, end;
for (;;) {
bool done = inputText(prefix, 4, Characters,
sizeof(Characters) / sizeof(Characters[0]),
CHAR_DONE, WORD_WIDTH, false, false);
begin = mnemonic_find_words(prefix, strlen(prefix), &end);
if ((done && begin >= 0) || end == begin + 1) {
break;
} else if (begin < 0) {
confirmWord(prefix, true, false);
}
}
if (confirmWord(wordlist[begin], true, true)) {
strcpy(words[word_index], wordlist[begin]);
break;
}
}
}
recovery_done();
}
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,
@ -502,9 +567,13 @@ void recovery_init(uint32_t _word_count, bool passphrase_protection,
config_setU2FCounter(u2f_counter);
}
if ((type & RecoveryDeviceType_RecoveryDeviceType_Matrix) != 0) {
requestOnDeviceTextInput();
word_index = 0;
if (session_isUseOnDeviceTextInput()) {
input_seed();
} else if ((type & RecoveryDeviceType_RecoveryDeviceType_Matrix) != 0) {
awaiting_word = 2;
word_index = 0;
word_pincode = 0;
next_matrix();
} else {
@ -516,7 +585,6 @@ void recovery_init(uint32_t _word_count, bool passphrase_protection,
}
random_permute(word_order, 24);
awaiting_word = 1;
word_index = 0;
next_word();
}
}

@ -15,6 +15,12 @@
#define FONT_DOUBLE 0x80
#define CHAR_BCKSPC '\x08' // Backspace
#define CHAR_SPACE '\x09'
#define CHAR_DONE '\x06'
#define CHAR_AND_SPACE_WIDTH (5 + 1)
extern const uint8_t *const font_data[FONTS][128];
int fontCharWidth(uint8_t font, uint8_t c);

Loading…
Cancel
Save