fix(legacy): Abort recovery for invalid words.

pull/2455/head
Andrew Kozlik 2 years ago committed by matejcik
parent fa5e7feda6
commit f8ffaecd1c

@ -0,0 +1 @@
Fix potential security issues in recovery workflow.

@ -37,12 +37,12 @@
/* number of words expected in the new seed */ /* number of words expected in the new seed */
static uint32_t word_count; static uint32_t word_count;
/* recovery mode: /* recovery mode */
* 0: not recovering static enum {
* 1: recover by scrambled plain text words RECOVERY_NONE = 0, // recovery not in progress
* 2: recover by matrix entry RECOVERY_SCRAMBLED = 1, // standard recovery by scrambled plain text words
*/ RECOVERY_MATRIX = 2, // advanced recovery by matrix entry
static int awaiting_word = 0; } recovery_mode = RECOVERY_NONE;
/* True if we should not write anything back to config /* True if we should not write anything back to config
* (can be used for testing seed for correctness). * (can be used for testing seed for correctness).
@ -145,7 +145,7 @@ static void format_number(char *dest, int number) {
static void recovery_request(void) { static void recovery_request(void) {
WordRequest resp = {0}; WordRequest resp = {0};
memzero(&resp, sizeof(WordRequest)); memzero(&resp, sizeof(WordRequest));
if (awaiting_word == 1) { if (recovery_mode == RECOVERY_SCRAMBLED) {
resp.type = WordRequestType_WordRequestType_Plain; resp.type = WordRequestType_WordRequestType_Plain;
} else if (word_index % 4 == 3) { } else if (word_index % 4 == 3) {
resp.type = WordRequestType_WordRequestType_Matrix6; resp.type = WordRequestType_WordRequestType_Matrix6;
@ -217,7 +217,9 @@ static void recovery_done(void) {
fsm_sendFailure(FailureType_Failure_DataError, fsm_sendFailure(FailureType_Failure_DataError,
_("Invalid seed, are words in correct order?")); _("Invalid seed, are words in correct order?"));
} }
awaiting_word = 0; memzero(words, sizeof(words));
word_pincode = 0;
recovery_mode = RECOVERY_NONE;
layoutHome(); layoutHome();
} }
@ -476,6 +478,9 @@ void recovery_init(uint32_t _word_count, bool passphrase_protection,
bool _dry_run) { bool _dry_run) {
if (_word_count != 12 && _word_count != 18 && _word_count != 24) return; if (_word_count != 12 && _word_count != 18 && _word_count != 24) return;
recovery_mode = RECOVERY_NONE;
word_pincode = 0;
word_index = 0;
word_count = _word_count; word_count = _word_count;
enforce_wordlist = _enforce_wordlist; enforce_wordlist = _enforce_wordlist;
dry_run = _dry_run; dry_run = _dry_run;
@ -504,10 +509,9 @@ void recovery_init(uint32_t _word_count, bool passphrase_protection,
config_setU2FCounter(u2f_counter); config_setU2FCounter(u2f_counter);
} }
// Prefer matrix recovery if the host supports it.
if ((type & RecoveryDeviceType_RecoveryDeviceType_Matrix) != 0) { if ((type & RecoveryDeviceType_RecoveryDeviceType_Matrix) != 0) {
awaiting_word = 2; recovery_mode = RECOVERY_MATRIX;
word_index = 0;
word_pincode = 0;
next_matrix(); next_matrix();
} else { } else {
for (uint32_t i = 0; i < word_count; i++) { for (uint32_t i = 0; i < word_count; i++) {
@ -517,8 +521,7 @@ void recovery_init(uint32_t _word_count, bool passphrase_protection,
word_order[i] = 0; word_order[i] = 0;
} }
random_permute(word_order, 24); random_permute(word_order, 24);
awaiting_word = 1; recovery_mode = RECOVERY_SCRAMBLED;
word_index = 0;
next_word(); next_word();
} }
} }
@ -527,29 +530,18 @@ static void recovery_scrambledword(const char *word) {
int index = -1; int index = -1;
if (enforce_wordlist) { // check if word is valid if (enforce_wordlist) { // check if word is valid
index = mnemonic_find_word(word); index = mnemonic_find_word(word);
} if (index < 0) { // not found
if (word_pos == 0) { // fake word
if (strcmp(word, fake_word) != 0) {
if (!dry_run) { if (!dry_run) {
session_clear(true); session_clear(true);
} }
fsm_sendFailure(FailureType_Failure_ProcessError, fsm_sendFailure(FailureType_Failure_DataError,
_("Wrong word retyped")); _("Word not found in a wordlist"));
layoutHome(); recovery_abort();
return; return;
} }
} else { // real word }
if (enforce_wordlist) {
if (index < 0) { // not found if (word_pos != 0) { // ignore fake words
if (!dry_run) {
session_clear(true);
}
fsm_sendFailure(FailureType_Failure_DataError,
_("Word not found in a wordlist"));
layoutHome();
return;
}
}
strlcpy(words[word_pos - 1], word, sizeof(words[word_pos - 1])); strlcpy(words[word_pos - 1], word, sizeof(words[word_pos - 1]));
} }
@ -565,11 +557,11 @@ static void recovery_scrambledword(const char *word) {
* for scrambled recovery. * for scrambled recovery.
*/ */
void recovery_word(const char *word) { void recovery_word(const char *word) {
switch (awaiting_word) { switch (recovery_mode) {
case 2: case RECOVERY_MATRIX:
recovery_digit(word[0]); recovery_digit(word[0]);
break; break;
case 1: case RECOVERY_SCRAMBLED:
recovery_scrambledword(word); recovery_scrambledword(word);
break; break;
default: default:
@ -582,9 +574,11 @@ void recovery_word(const char *word) {
/* Abort recovery. /* Abort recovery.
*/ */
void recovery_abort(void) { void recovery_abort(void) {
if (awaiting_word) { memzero(words, sizeof(words));
word_pincode = 0;
if (recovery_mode != RECOVERY_NONE) {
layoutHome(); layoutHome();
awaiting_word = 0; recovery_mode = RECOVERY_NONE;
} }
} }

Loading…
Cancel
Save