crypto: refactor bip39 api

pull/561/head
Pavol Rusnak 5 years ago
parent e05c6f8983
commit 78041d261b
No known key found for this signature in database
GPG Key ID: 91F3B339B9A02A3D

@ -24,53 +24,40 @@
/// package: trezorcrypto.bip39 /// package: trezorcrypto.bip39
/// def find_word(prefix: str) -> Optional[str]: /// def complete_word(prefix: str) -> Optional[str]:
/// """ /// """
/// Return the first word from the wordlist starting with prefix. /// Return the first word from the wordlist starting with prefix.
/// """ /// """
STATIC mp_obj_t mod_trezorcrypto_bip39_find_word(mp_obj_t prefix) { STATIC mp_obj_t mod_trezorcrypto_bip39_complete_word(mp_obj_t prefix) {
mp_buffer_info_t pfx; mp_buffer_info_t pfx;
mp_get_buffer_raise(prefix, &pfx, MP_BUFFER_READ); mp_get_buffer_raise(prefix, &pfx, MP_BUFFER_READ);
if (pfx.len == 0) { if (pfx.len == 0) {
return mp_const_none; return mp_const_none;
} }
for (const char *const *w = mnemonic_wordlist(); *w != 0; w++) { const char *word = mnemonic_complete_word(pfx.buf, pfx.len);
if (strncmp(*w, pfx.buf, pfx.len) == 0) { if (word) {
return mp_obj_new_str(*w, strlen(*w)); return mp_obj_new_str(word, strlen(word));
} } else {
return mp_const_none;
} }
return mp_const_none;
} }
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorcrypto_bip39_find_word_obj, STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorcrypto_bip39_complete_word_obj,
mod_trezorcrypto_bip39_find_word); mod_trezorcrypto_bip39_complete_word);
/// def complete_word(prefix: str) -> int: /// def word_completion_mask(prefix: str) -> int:
/// """ /// """
/// Return possible 1-letter suffixes for given word prefix. /// Return possible 1-letter suffixes for given word prefix.
/// Result is a bitmask, with 'a' on the lowest bit, 'b' on the second /// Result is a bitmask, with 'a' on the lowest bit, 'b' on the second
/// lowest, etc. /// lowest, etc.
/// """ /// """
STATIC mp_obj_t mod_trezorcrypto_bip39_complete_word(mp_obj_t prefix) { STATIC mp_obj_t mod_trezorcrypto_bip39_word_completion_mask(mp_obj_t prefix) {
mp_buffer_info_t pfx; mp_buffer_info_t pfx;
mp_get_buffer_raise(prefix, &pfx, MP_BUFFER_READ); mp_get_buffer_raise(prefix, &pfx, MP_BUFFER_READ);
if (pfx.len == 0) { return mp_obj_new_int(mnemonic_word_completion_mask(pfx.buf, pfx.len));
return mp_obj_new_int(0xFFFFFFFF); // all letters
}
uint32_t res = 0;
uint8_t bit;
const char *word;
const char *const *wlist;
for (wlist = mnemonic_wordlist(); *wlist != 0; wlist++) {
word = *wlist;
if (strncmp(word, pfx.buf, pfx.len) == 0 && strlen(word) > pfx.len) {
bit = word[pfx.len] - 'a';
res |= 1 << bit;
}
}
return mp_obj_new_int(res);
} }
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorcrypto_bip39_complete_word_obj, STATIC MP_DEFINE_CONST_FUN_OBJ_1(
mod_trezorcrypto_bip39_complete_word); mod_trezorcrypto_bip39_word_completion_mask_obj,
mod_trezorcrypto_bip39_word_completion_mask);
/// def generate(strength: int) -> str: /// def generate(strength: int) -> str:
/// """ /// """
@ -167,10 +154,10 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_trezorcrypto_bip39_seed_obj, 2,
STATIC const mp_rom_map_elem_t mod_trezorcrypto_bip39_globals_table[] = { STATIC const mp_rom_map_elem_t mod_trezorcrypto_bip39_globals_table[] = {
{MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_bip39)}, {MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_bip39)},
{MP_ROM_QSTR(MP_QSTR_find_word),
MP_ROM_PTR(&mod_trezorcrypto_bip39_find_word_obj)},
{MP_ROM_QSTR(MP_QSTR_complete_word), {MP_ROM_QSTR(MP_QSTR_complete_word),
MP_ROM_PTR(&mod_trezorcrypto_bip39_complete_word_obj)}, MP_ROM_PTR(&mod_trezorcrypto_bip39_complete_word_obj)},
{MP_ROM_QSTR(MP_QSTR_word_completion_mask),
MP_ROM_PTR(&mod_trezorcrypto_bip39_word_completion_mask_obj)},
{MP_ROM_QSTR(MP_QSTR_generate), {MP_ROM_QSTR(MP_QSTR_generate),
MP_ROM_PTR(&mod_trezorcrypto_bip39_generate_obj)}, MP_ROM_PTR(&mod_trezorcrypto_bip39_generate_obj)},
{MP_ROM_QSTR(MP_QSTR_from_data), {MP_ROM_QSTR(MP_QSTR_from_data),

@ -2,14 +2,14 @@ from typing import *
# extmod/modtrezorcrypto/modtrezorcrypto-bip39.h # extmod/modtrezorcrypto/modtrezorcrypto-bip39.h
def find_word(prefix: str) -> Optional[str]: def complete_word(prefix: str) -> Optional[str]:
""" """
Return the first word from the wordlist starting with prefix. Return the first word from the wordlist starting with prefix.
""" """
# extmod/modtrezorcrypto/modtrezorcrypto-bip39.h # extmod/modtrezorcrypto/modtrezorcrypto-bip39.h
def complete_word(prefix: str) -> int: def word_completion_mask(prefix: str) -> int:
""" """
Return possible 1-letter suffixes for given word prefix. Return possible 1-letter suffixes for given word prefix.
Result is a bitmask, with 'a' on the lowest bit, 'b' on the second Result is a bitmask, with 'a' on the lowest bit, 'b' on the second

@ -172,8 +172,8 @@ class Bip39Keyboard(ui.Layout):
# find the completions # find the completions
pending = button is not None pending = button is not None
word = bip39.find_word(text) or "" word = bip39.complete_word(text) or ""
mask = bip39.complete_word(text) mask = bip39.word_completion_mask(text)
# modify the input state # modify the input state
self.input.edit(text, word, pending) self.input.edit(text, word, pending)

@ -228,4 +228,54 @@ void mnemonic_to_seed(const char *mnemonic, const char *passphrase,
#endif #endif
} }
const char *const *mnemonic_wordlist(void) { return wordlist; } // binary search for finding the word in the wordlist
int mnemonic_find_word(const char *word) {
int lo = 0, hi = BIP39_WORDS - 1;
while (lo <= hi) {
int mid = lo + (hi - lo) / 2;
int cmp = strcmp(word, wordlist[mid]);
if (cmp == 0) {
return mid;
}
if (cmp > 0) {
lo = mid + 1;
} else {
hi = mid - 1;
}
}
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;
}
}
return NULL;
}
const char *mnemonic_get_word(int index) {
if (index >= 0 && index < BIP39_WORDS) {
return wordlist[index];
} else {
return NULL;
}
}
uint32_t mnemonic_word_completion_mask(const char *prefix, int len) {
if (len <= 0) {
return 0x3ffffff; // all letters (bits 1-26 set)
}
uint32_t res = 0;
for (const char *const *w = wordlist; *w != 0; w++) {
const char *word = *w;
if (strncmp(word, prefix, len) == 0 && word[len] >= 'a' &&
word[len] <= 'z') {
res |= 1 << (word[len] - 'a');
}
}
return res;
}

@ -24,8 +24,10 @@
#ifndef __BIP39_H__ #ifndef __BIP39_H__
#define __BIP39_H__ #define __BIP39_H__
#include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#define BIP39_WORDS 2048
#define BIP39_PBKDF2_ROUNDS 2048 #define BIP39_PBKDF2_ROUNDS 2048
const char *mnemonic_generate(int strength); // strength in bits const char *mnemonic_generate(int strength); // strength in bits
@ -42,6 +44,9 @@ void mnemonic_to_seed(const char *mnemonic, const char *passphrase,
void (*progress_callback)(uint32_t current, void (*progress_callback)(uint32_t current,
uint32_t total)); uint32_t total));
const char *const *mnemonic_wordlist(void); int mnemonic_find_word(const char *word);
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);
#endif #endif

@ -5097,6 +5097,17 @@ START_TEST(test_mnemonic_to_entropy) {
} }
END_TEST END_TEST
START_TEST(test_mnemonic_find_word) {
ck_assert_int_eq(-1, mnemonic_find_word("aaaa"));
ck_assert_int_eq(-1, mnemonic_find_word("zzzz"));
for (int i = 0; i < BIP39_WORDS; i++) {
const char *word = mnemonic_get_word(i);
int index = mnemonic_find_word(word);
ck_assert_int_eq(i, index);
}
}
END_TEST
START_TEST(test_slip39_get_word) { START_TEST(test_slip39_get_word) {
static const struct { static const struct {
const int index; const int index;
@ -8765,6 +8776,7 @@ Suite *test_suite(void) {
tcase_add_test(tc, test_mnemonic); tcase_add_test(tc, test_mnemonic);
tcase_add_test(tc, test_mnemonic_check); tcase_add_test(tc, test_mnemonic_check);
tcase_add_test(tc, test_mnemonic_to_entropy); tcase_add_test(tc, test_mnemonic_to_entropy);
tcase_add_test(tc, test_mnemonic_find_word);
suite_add_tcase(s, tc); suite_add_tcase(s, tc);
tc = tcase_create("slip39"); tc = tcase_create("slip39");

@ -323,7 +323,6 @@ static void display_choices(bool twoColumn, char choices[9][12], int num) {
* Generates a new matrix and requests the next pin. * Generates a new matrix and requests the next pin.
*/ */
static void next_matrix(void) { static void next_matrix(void) {
const char *const *wl = mnemonic_wordlist();
char word_choices[9][12]; char word_choices[9][12];
uint32_t idx, num; uint32_t idx, num;
bool last = (word_index % 4) == 3; bool last = (word_index % 4) == 3;
@ -342,7 +341,8 @@ static void next_matrix(void) {
const uint32_t first = TABLE2(idx); const uint32_t first = TABLE2(idx);
num = TABLE2(idx + 1) - first; num = TABLE2(idx + 1) - first;
for (uint32_t i = 0; i < num; i++) { for (uint32_t i = 0; i < num; i++) {
strlcpy(word_choices[i], wl[first + i], sizeof(word_choices[i])); strlcpy(word_choices[i], mnemonic_get_word(first + i),
sizeof(word_choices[i]));
} }
break; break;
@ -354,7 +354,8 @@ static void next_matrix(void) {
num = TABLE1(word_pincode + 1) - idx; num = TABLE1(word_pincode + 1) - idx;
for (uint32_t i = 0; i < num; i++) { 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] >> 12),
wl[TABLE2(idx + i)], wl[TABLE2(idx + i + 1) - 1]); mnemonic_get_word(TABLE2(idx + i)),
mnemonic_get_word(TABLE2(idx + i + 1) - 1));
} }
break; break;
@ -366,8 +367,8 @@ static void next_matrix(void) {
num = 9; num = 9;
for (uint32_t i = 0; i < num; i++) { 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] >> 12),
wl[TABLE2(TABLE1(idx + i))], mnemonic_get_word(TABLE2(TABLE1(idx + i))),
wl[TABLE2(TABLE1(idx + i + 1)) - 1]); mnemonic_get_word(TABLE2(TABLE1(idx + i + 1)) - 1));
} }
break; break;
@ -376,8 +377,8 @@ static void next_matrix(void) {
/* num: the number of choices. */ /* num: the number of choices. */
num = 9; num = 9;
for (uint32_t i = 0; i < num; i++) { for (uint32_t i = 0; i < num; i++) {
add_choice(word_choices[i], 1, wl[TABLE2(TABLE1(9 * i))], add_choice(word_choices[i], 1, mnemonic_get_word(TABLE2(TABLE1(9 * i))),
wl[TABLE2(TABLE1(9 * (i + 1))) - 1]); mnemonic_get_word(TABLE2(TABLE1(9 * (i + 1))) - 1));
} }
break; break;
} }
@ -427,7 +428,7 @@ static void recovery_digit(const char digit) {
uint32_t widx = word_index / 4; uint32_t widx = word_index / 4;
word_pincode = 0; word_pincode = 0;
strlcpy(words[widx], mnemonic_wordlist()[idx], sizeof(words[widx])); strlcpy(words[widx], mnemonic_get_word(idx), sizeof(words[widx]));
if (widx + 1 == word_count) { if (widx + 1 == word_count) {
recovery_done(); recovery_done();
return; return;
@ -449,8 +450,8 @@ void next_word(void) {
oledDrawStringCenter(OLED_WIDTH / 2, 8, _("Please enter"), FONT_STANDARD); oledDrawStringCenter(OLED_WIDTH / 2, 8, _("Please enter"), FONT_STANDARD);
word_pos = word_order[word_index]; word_pos = word_order[word_index];
if (word_pos == 0) { if (word_pos == 0) {
const char *const *wl = mnemonic_wordlist(); strlcpy(fake_word, mnemonic_get_word(random_uniform(BIP39_WORDS)),
strlcpy(fake_word, wl[random_uniform(2048)], sizeof(fake_word)); sizeof(fake_word));
oledDrawStringCenter(OLED_WIDTH / 2, 24, fake_word, oledDrawStringCenter(OLED_WIDTH / 2, 24, fake_word,
FONT_FIXED | FONT_DOUBLE); FONT_FIXED | FONT_DOUBLE);
} else { } else {
@ -521,6 +522,10 @@ void recovery_init(uint32_t _word_count, bool passphrase_protection,
} }
static void recovery_scrambledword(const char *word) { static void recovery_scrambledword(const char *word) {
int index = -1;
if (enforce_wordlist) { // check if word is valid
index = mnemonic_find_word(word);
}
if (word_pos == 0) { // fake word if (word_pos == 0) { // fake word
if (strcmp(word, fake_word) != 0) { if (strcmp(word, fake_word) != 0) {
if (!dry_run) { if (!dry_run) {
@ -531,18 +536,9 @@ static void recovery_scrambledword(const char *word) {
layoutHome(); layoutHome();
return; return;
} }
} else { // real word } else { // real word
if (enforce_wordlist) { // check if word is valid if (enforce_wordlist) {
const char *const *wl = mnemonic_wordlist(); if (index < 0) { // not found
bool found = false;
while (*wl) {
if (strcmp(word, *wl) == 0) {
found = true;
break;
}
wl++;
}
if (!found) {
if (!dry_run) { if (!dry_run) {
session_clear(true); session_clear(true);
} }

Loading…
Cancel
Save