mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-02-16 17:42:02 +00:00
crypto: refactor bip39 api
This commit is contained in:
parent
e05c6f8983
commit
78041d261b
@ -24,54 +24,41 @@
|
|||||||
|
|
||||||
/// 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) {
|
|
||||||
mp_buffer_info_t pfx;
|
|
||||||
mp_get_buffer_raise(prefix, &pfx, MP_BUFFER_READ);
|
|
||||||
if (pfx.len == 0) {
|
|
||||||
return mp_const_none;
|
|
||||||
}
|
|
||||||
for (const char *const *w = mnemonic_wordlist(); *w != 0; w++) {
|
|
||||||
if (strncmp(*w, pfx.buf, pfx.len) == 0) {
|
|
||||||
return mp_obj_new_str(*w, strlen(*w));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return mp_const_none;
|
|
||||||
}
|
|
||||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorcrypto_bip39_find_word_obj,
|
|
||||||
mod_trezorcrypto_bip39_find_word);
|
|
||||||
|
|
||||||
/// def complete_word(prefix: str) -> int:
|
|
||||||
/// """
|
|
||||||
/// Return possible 1-letter suffixes for given word prefix.
|
|
||||||
/// Result is a bitmask, with 'a' on the lowest bit, 'b' on the second
|
|
||||||
/// lowest, etc.
|
|
||||||
/// """
|
|
||||||
STATIC mp_obj_t mod_trezorcrypto_bip39_complete_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_obj_new_int(0xFFFFFFFF); // all letters
|
return mp_const_none;
|
||||||
}
|
}
|
||||||
uint32_t res = 0;
|
const char *word = mnemonic_complete_word(pfx.buf, pfx.len);
|
||||||
uint8_t bit;
|
if (word) {
|
||||||
const char *word;
|
return mp_obj_new_str(word, strlen(word));
|
||||||
const char *const *wlist;
|
} else {
|
||||||
for (wlist = mnemonic_wordlist(); *wlist != 0; wlist++) {
|
return mp_const_none;
|
||||||
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_obj,
|
||||||
mod_trezorcrypto_bip39_complete_word);
|
mod_trezorcrypto_bip39_complete_word);
|
||||||
|
|
||||||
|
/// def word_completion_mask(prefix: str) -> int:
|
||||||
|
/// """
|
||||||
|
/// Return possible 1-letter suffixes for given word prefix.
|
||||||
|
/// Result is a bitmask, with 'a' on the lowest bit, 'b' on the second
|
||||||
|
/// lowest, etc.
|
||||||
|
/// """
|
||||||
|
STATIC mp_obj_t mod_trezorcrypto_bip39_word_completion_mask(mp_obj_t prefix) {
|
||||||
|
mp_buffer_info_t pfx;
|
||||||
|
mp_get_buffer_raise(prefix, &pfx, MP_BUFFER_READ);
|
||||||
|
return mp_obj_new_int(mnemonic_word_completion_mask(pfx.buf, pfx.len));
|
||||||
|
}
|
||||||
|
STATIC MP_DEFINE_CONST_FUN_OBJ_1(
|
||||||
|
mod_trezorcrypto_bip39_word_completion_mask_obj,
|
||||||
|
mod_trezorcrypto_bip39_word_completion_mask);
|
||||||
|
|
||||||
/// def generate(strength: int) -> str:
|
/// def generate(strength: int) -> str:
|
||||||
/// """
|
/// """
|
||||||
/// Generate a mnemonic of given strength (128, 160, 192, 224 and 256 bits).
|
/// Generate a mnemonic of given strength (128, 160, 192, 224 and 256 bits).
|
||||||
@ -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…
Reference in New Issue
Block a user