mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-21 23:18:13 +00:00
core: add slip39 keyboard
This commit is contained in:
parent
132519123e
commit
49d6a35249
@ -88,6 +88,7 @@ SOURCE_MOD += [
|
||||
'vendor/trezor-crypto/sha2.c',
|
||||
'vendor/trezor-crypto/sha3.c',
|
||||
'vendor/trezor-crypto/shamir.c',
|
||||
'vendor/trezor-crypto/slip39.c',
|
||||
]
|
||||
|
||||
# libsecp256k1-zkp
|
||||
|
@ -86,6 +86,7 @@ SOURCE_MOD += [
|
||||
'vendor/trezor-crypto/sha2.c',
|
||||
'vendor/trezor-crypto/sha3.c',
|
||||
'vendor/trezor-crypto/shamir.c',
|
||||
'vendor/trezor-crypto/slip39.c',
|
||||
]
|
||||
|
||||
# libsecp256k1-zkp
|
||||
|
122
core/embed/extmod/modtrezorcrypto/modtrezorcrypto-slip39.h
Normal file
122
core/embed/extmod/modtrezorcrypto/modtrezorcrypto-slip39.h
Normal file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* This file is part of the TREZOR project, https://trezor.io/
|
||||
*
|
||||
* Copyright (c) SatoshiLabs
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "py/obj.h"
|
||||
#include "py/runtime.h"
|
||||
|
||||
#include "slip39.h"
|
||||
|
||||
/// package: trezorcrypto.slip39
|
||||
|
||||
/// def compute_mask(prefix: int) -> int:
|
||||
/// """
|
||||
/// Calculates which buttons still can be pressed after some already were.
|
||||
/// Returns a 9-bit bitmask, where each bit specifies which buttons
|
||||
/// can be further pressed (there are still words in this combination).
|
||||
/// LSB denotes first button.
|
||||
///
|
||||
/// Example: 110000110 - second, third, eighth and ninth button still can be
|
||||
/// pressed.
|
||||
/// """
|
||||
STATIC mp_obj_t mod_trezorcrypto_slip39_compute_mask(mp_obj_t _prefix) {
|
||||
uint16_t prefix = mp_obj_get_int(_prefix);
|
||||
|
||||
if (prefix < 1 || prefix > 9999) {
|
||||
mp_raise_ValueError(
|
||||
"Invalid button prefix (range between 1 and 9999 is allowed)");
|
||||
}
|
||||
return mp_obj_new_int_from_uint(compute_mask(prefix));
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorcrypto_slip39_compute_mask_obj,
|
||||
mod_trezorcrypto_slip39_compute_mask);
|
||||
|
||||
/// def button_sequence_to_word(prefix: int) -> str:
|
||||
/// """
|
||||
/// Finds the first word that fits the given button prefix.
|
||||
/// """
|
||||
STATIC mp_obj_t
|
||||
mod_trezorcrypto_slip39_button_sequence_to_word(mp_obj_t _prefix) {
|
||||
uint16_t prefix = mp_obj_get_int(_prefix);
|
||||
|
||||
if (prefix < 1 || prefix > 9999) {
|
||||
mp_raise_ValueError(
|
||||
"Invalid button prefix (range between 1 and 9999 is allowed)");
|
||||
}
|
||||
const char *word = button_sequence_to_word(prefix);
|
||||
return mp_obj_new_str_copy(&mp_type_str, (const uint8_t *)word, strlen(word));
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(
|
||||
mod_trezorcrypto_slip39_button_sequence_to_word_obj,
|
||||
mod_trezorcrypto_slip39_button_sequence_to_word);
|
||||
|
||||
/// def word_index(word: str) -> int:
|
||||
/// """
|
||||
/// Finds index of given word.
|
||||
/// Raises ValueError if not found.
|
||||
/// """
|
||||
STATIC mp_obj_t mod_trezorcrypto_slip39_word_index(mp_obj_t _word) {
|
||||
mp_buffer_info_t word;
|
||||
|
||||
mp_get_buffer_raise(_word, &word, MP_BUFFER_READ);
|
||||
|
||||
uint16_t result = 0;
|
||||
if (word_index(&result, word.buf, word.len) == false) {
|
||||
mp_raise_ValueError("Invalid mnemonic word");
|
||||
}
|
||||
return mp_obj_new_int_from_uint(result);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorcrypto_slip39_word_index_obj,
|
||||
mod_trezorcrypto_slip39_word_index);
|
||||
|
||||
/// def get_word(index: int) -> str:
|
||||
/// """
|
||||
/// Returns word on position 'index'.
|
||||
/// """
|
||||
STATIC mp_obj_t mod_trezorcrypto_slip39_get_word(mp_obj_t _index) {
|
||||
uint16_t index = mp_obj_get_int(_index);
|
||||
|
||||
if (index > 1023) {
|
||||
mp_raise_ValueError(
|
||||
"Invalid wordlist index (range between 0 and 1024 is allowed)");
|
||||
}
|
||||
|
||||
const char *word = get_word(index);
|
||||
return mp_obj_new_str_copy(&mp_type_str, (const uint8_t *)word, strlen(word));
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorcrypto_slip39_get_word_obj,
|
||||
mod_trezorcrypto_slip39_get_word);
|
||||
|
||||
STATIC const mp_rom_map_elem_t mod_trezorcrypto_slip39_globals_table[] = {
|
||||
{MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_slip39)},
|
||||
{MP_ROM_QSTR(MP_QSTR_compute_mask),
|
||||
MP_ROM_PTR(&mod_trezorcrypto_slip39_compute_mask_obj)},
|
||||
{MP_ROM_QSTR(MP_QSTR_button_sequence_to_word),
|
||||
MP_ROM_PTR(&mod_trezorcrypto_slip39_button_sequence_to_word_obj)},
|
||||
{MP_ROM_QSTR(MP_QSTR_word_index),
|
||||
MP_ROM_PTR(&mod_trezorcrypto_slip39_word_index_obj)},
|
||||
{MP_ROM_QSTR(MP_QSTR_get_word),
|
||||
MP_ROM_PTR(&mod_trezorcrypto_slip39_get_word_obj)},
|
||||
};
|
||||
STATIC MP_DEFINE_CONST_DICT(mod_trezorcrypto_slip39_globals,
|
||||
mod_trezorcrypto_slip39_globals_table);
|
||||
|
||||
STATIC const mp_obj_module_t mod_trezorcrypto_slip39_module = {
|
||||
.base = {&mp_type_module},
|
||||
.globals = (mp_obj_dict_t *)&mod_trezorcrypto_slip39_globals,
|
||||
};
|
@ -51,6 +51,7 @@
|
||||
#include "modtrezorcrypto-sha3-512.h"
|
||||
#include "modtrezorcrypto-sha512.h"
|
||||
#include "modtrezorcrypto-shamir.h"
|
||||
#include "modtrezorcrypto-slip39.h"
|
||||
|
||||
STATIC const mp_rom_map_elem_t mp_module_trezorcrypto_globals_table[] = {
|
||||
{MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_trezorcrypto)},
|
||||
@ -91,6 +92,7 @@ STATIC const mp_rom_map_elem_t mp_module_trezorcrypto_globals_table[] = {
|
||||
{MP_ROM_QSTR(MP_QSTR_sha3_512),
|
||||
MP_ROM_PTR(&mod_trezorcrypto_Sha3_512_type)},
|
||||
{MP_ROM_QSTR(MP_QSTR_shamir), MP_ROM_PTR(&mod_trezorcrypto_shamir_module)},
|
||||
{MP_ROM_QSTR(MP_QSTR_slip39), MP_ROM_PTR(&mod_trezorcrypto_slip39_module)},
|
||||
};
|
||||
STATIC MP_DEFINE_CONST_DICT(mp_module_trezorcrypto_globals,
|
||||
mp_module_trezorcrypto_globals_table);
|
||||
|
@ -90,7 +90,7 @@ class Prompt(ui.Control):
|
||||
self.repaint = False
|
||||
|
||||
|
||||
class MnemonicKeyboard(ui.Layout):
|
||||
class Bip39Keyboard(ui.Layout):
|
||||
def __init__(self, prompt):
|
||||
self.prompt = Prompt(prompt)
|
||||
|
205
core/src/trezor/ui/mnemonic_slip39.py
Normal file
205
core/src/trezor/ui/mnemonic_slip39.py
Normal file
@ -0,0 +1,205 @@
|
||||
from trezor import io, loop, res, ui
|
||||
from trezor.crypto import slip39
|
||||
from trezor.ui import display
|
||||
from trezor.ui.button import Button, ButtonClear, ButtonMono, ButtonMonoConfirm
|
||||
|
||||
|
||||
def check_mask(mask: int, index: int) -> bool:
|
||||
return bool((1 << (index - 1)) & mask)
|
||||
|
||||
|
||||
# TODO: ask UX if we want to finish sooner than 4 words
|
||||
# example: 'hairy'
|
||||
def _is_final(content: str) -> bool:
|
||||
return len(content) > 3
|
||||
|
||||
|
||||
class KeyButton(Button):
|
||||
def __init__(self, area, content, keyboard, index):
|
||||
self.keyboard = keyboard
|
||||
self.index = index
|
||||
super().__init__(area, content)
|
||||
|
||||
def on_click(self):
|
||||
self.keyboard.on_key_click(self)
|
||||
|
||||
|
||||
class InputButton(Button):
|
||||
def __init__(self, area, content, word):
|
||||
super().__init__(area, content)
|
||||
self.word = word
|
||||
self.pending_button = None
|
||||
self.pending_index = None
|
||||
self.icon = None # rendered icon
|
||||
self.disable()
|
||||
|
||||
def edit(self, content, word, pending_button, pending_index):
|
||||
self.word = word
|
||||
self.content = content
|
||||
self.pending_button = pending_button
|
||||
self.pending_index = pending_index
|
||||
self.repaint = True
|
||||
if word:
|
||||
self.enable()
|
||||
self.normal_style = ButtonMonoConfirm.normal
|
||||
self.active_style = ButtonMonoConfirm.active
|
||||
self.icon = ui.ICON_CONFIRM
|
||||
else: # disabled button
|
||||
self.disabled_style = ButtonMono.normal
|
||||
self.disable()
|
||||
self.icon = None
|
||||
|
||||
def render_content(self, s, ax, ay, aw, ah):
|
||||
text_style = s.text_style
|
||||
fg_color = s.fg_color
|
||||
bg_color = s.bg_color
|
||||
|
||||
tx = ax + 24 # x-offset of the content
|
||||
ty = ay + ah // 2 + 8 # y-offset of the content
|
||||
|
||||
if not _is_final(self.content):
|
||||
to_display = len(self.content) * "*"
|
||||
if self.pending_button:
|
||||
to_display = (
|
||||
to_display[:-1] + self.pending_button.content[self.pending_index]
|
||||
)
|
||||
else:
|
||||
to_display = self.word
|
||||
|
||||
display.text(tx, ty, to_display, text_style, fg_color, bg_color)
|
||||
|
||||
if self.pending_button and not _is_final(self.content):
|
||||
width = display.text_width(to_display, text_style)
|
||||
pw = display.text_width(self.content[-1:], text_style)
|
||||
px = tx + width - pw
|
||||
display.bar(px, ty + 2, pw + 1, 3, fg_color)
|
||||
|
||||
if self.icon:
|
||||
ix = ax + aw - 16 * 2
|
||||
iy = ty - 16
|
||||
display.icon(ix, iy, res.load(self.icon), fg_color, bg_color)
|
||||
|
||||
|
||||
class Prompt(ui.Control):
|
||||
def __init__(self, prompt):
|
||||
self.prompt = prompt
|
||||
self.repaint = True
|
||||
|
||||
def on_render(self):
|
||||
if self.repaint:
|
||||
display.bar(0, 8, ui.WIDTH, 60, ui.BG)
|
||||
display.text(20, 40, self.prompt, ui.BOLD, ui.GREY, ui.BG)
|
||||
self.repaint = False
|
||||
|
||||
|
||||
class Slip39Keyboard(ui.Layout):
|
||||
def __init__(self, prompt):
|
||||
self.prompt = Prompt(prompt)
|
||||
|
||||
icon_back = res.load(ui.ICON_BACK)
|
||||
self.back = Button(ui.grid(0, n_x=4, n_y=4), icon_back, ButtonClear)
|
||||
self.back.on_click = self.on_back_click
|
||||
|
||||
self.input = InputButton(ui.grid(1, n_x=4, n_y=4, cells_x=3), "", "")
|
||||
self.input.on_click = self.on_input_click
|
||||
|
||||
self.keys = [
|
||||
KeyButton(ui.grid(i + 3, n_y=4), k, self, i + 1)
|
||||
for i, k in enumerate(
|
||||
("ab", "cd", "ef", "ghij", "klm", "nopq", "rs", "tuv", "wxyz")
|
||||
)
|
||||
]
|
||||
self.pending_button = None
|
||||
self.pending_index = 0
|
||||
self.button_sequence = ""
|
||||
|
||||
def dispatch(self, event: int, x: int, y: int):
|
||||
for btn in self.keys:
|
||||
btn.dispatch(event, x, y)
|
||||
if self.input.content:
|
||||
self.input.dispatch(event, x, y)
|
||||
self.back.dispatch(event, x, y)
|
||||
else:
|
||||
self.prompt.dispatch(event, x, y)
|
||||
|
||||
def on_back_click(self):
|
||||
# Backspace was clicked, let's delete the last character of input.
|
||||
self.button_sequence = self.button_sequence[:-1]
|
||||
self.edit()
|
||||
|
||||
def on_input_click(self):
|
||||
# Input button was clicked. If the content matches the suggested word,
|
||||
# let's confirm it, otherwise just auto-complete.
|
||||
result = self.input.word
|
||||
if _is_final(self.input.content):
|
||||
self.button_sequence = ""
|
||||
self.edit()
|
||||
self.on_confirm(result)
|
||||
|
||||
def on_key_click(self, btn: KeyButton):
|
||||
# Key button was clicked. If this button is pending, let's cycle the
|
||||
# pending character in input. If not, let's just append the first
|
||||
# character.
|
||||
if self.pending_button is btn:
|
||||
index = (self.pending_index + 1) % len(btn.content)
|
||||
else:
|
||||
index = 0
|
||||
self.button_sequence += str(btn.index)
|
||||
self.edit(btn, index)
|
||||
|
||||
def on_timeout(self):
|
||||
# Timeout occurred. Let's redraw to draw asterisks.
|
||||
self.edit()
|
||||
|
||||
def on_confirm(self, word):
|
||||
# Word was confirmed by the user.
|
||||
raise ui.Result(word)
|
||||
|
||||
def edit(self, button: KeyButton = None, index: int = 0):
|
||||
self.pending_button = button
|
||||
self.pending_index = index
|
||||
|
||||
# find the completions
|
||||
mask = 0
|
||||
word = ""
|
||||
if _is_final(self.button_sequence):
|
||||
word = slip39.button_sequence_to_word(self.button_sequence)
|
||||
else:
|
||||
mask = slip39.compute_mask(self.button_sequence)
|
||||
|
||||
# modify the input state
|
||||
self.input.edit(
|
||||
self.button_sequence, word, self.pending_button, self.pending_index
|
||||
)
|
||||
|
||||
# enable or disable key buttons
|
||||
for btn in self.keys:
|
||||
if (not _is_final(self.button_sequence) and btn is button) or check_mask(
|
||||
mask, btn.index
|
||||
):
|
||||
btn.enable()
|
||||
else:
|
||||
btn.disable()
|
||||
|
||||
# invalidate the prompt if we display it next frame
|
||||
if not self.input.content:
|
||||
self.prompt.repaint = True
|
||||
|
||||
async def handle_input(self):
|
||||
touch = loop.wait(io.TOUCH)
|
||||
timeout = loop.sleep(1000 * 1000 * 1)
|
||||
spawn_touch = loop.spawn(touch)
|
||||
spawn_timeout = loop.spawn(touch, timeout)
|
||||
|
||||
while True:
|
||||
if self.pending_button is not None:
|
||||
spawn = spawn_timeout
|
||||
else:
|
||||
spawn = spawn_touch
|
||||
result = await spawn
|
||||
|
||||
if touch in spawn.finished:
|
||||
event, x, y = result
|
||||
self.dispatch(event, x, y)
|
||||
else:
|
||||
self.on_timeout()
|
@ -66,6 +66,7 @@ SRCS += memzero.c
|
||||
SRCS += shamir.c
|
||||
SRCS += hmac_drbg.c
|
||||
SRCS += rfc6979.c
|
||||
SRCS += slip39.c
|
||||
|
||||
OBJS = $(SRCS:.c=.o)
|
||||
|
||||
|
136
crypto/slip39.c
Normal file
136
crypto/slip39.c
Normal file
@ -0,0 +1,136 @@
|
||||
/**
|
||||
* This file is part of the TREZOR project, https://trezor.io/
|
||||
*
|
||||
* Copyright (c) SatoshiLabs
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
||||
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "slip39.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "slip39_wordlist.h"
|
||||
|
||||
/**
|
||||
* Returns word on position `index`.
|
||||
*/
|
||||
const char* get_word(uint16_t index) { return wordlist[index]; }
|
||||
|
||||
/**
|
||||
* Finds index of given word, if found.
|
||||
* Returns true on success and stores result in `index`.
|
||||
*/
|
||||
bool word_index(uint16_t* index, const char* word, uint8_t word_length) {
|
||||
uint16_t lo = 0;
|
||||
uint16_t hi = WORDS_COUNT;
|
||||
uint16_t mid = 0;
|
||||
|
||||
while ((hi - lo) > 1) {
|
||||
mid = (hi + lo) / 2;
|
||||
if (strncmp(wordlist[mid], word, word_length) > 0) {
|
||||
hi = mid;
|
||||
} else {
|
||||
lo = mid;
|
||||
}
|
||||
}
|
||||
if (strncmp(wordlist[lo], word, word_length) != 0) {
|
||||
return false;
|
||||
}
|
||||
*index = lo;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates which buttons still can be pressed after some already were.
|
||||
* Returns a 9-bit bitmask, where each bit specifies which buttons
|
||||
* can be further pressed (there are still words in this combination).
|
||||
* LSB denotes first button.
|
||||
*
|
||||
* Example: 110000110 - second, third, eighth and ninth button still can be
|
||||
* pressed.
|
||||
*/
|
||||
uint16_t compute_mask(uint16_t prefix) { return find(prefix, false); }
|
||||
|
||||
/**
|
||||
* Converts sequence to word index.
|
||||
*/
|
||||
const char* button_sequence_to_word(uint16_t prefix) {
|
||||
return wordlist[find(prefix, true)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes mask if find_index is false.
|
||||
* Finds the first word index that suits the prefix otherwise.
|
||||
*/
|
||||
uint16_t find(uint16_t prefix, bool find_index) {
|
||||
uint16_t min = prefix;
|
||||
uint16_t max = 0;
|
||||
uint16_t for_max = 0;
|
||||
uint8_t multiplier = 0;
|
||||
uint8_t divider = 0;
|
||||
uint16_t bitmap = 0;
|
||||
uint8_t digit = 0;
|
||||
uint16_t i = 0;
|
||||
|
||||
max = prefix + 1;
|
||||
while (min < 1000) {
|
||||
min = min * 10;
|
||||
max = max * 10;
|
||||
multiplier++;
|
||||
}
|
||||
|
||||
// Four char prefix -> the mask is zero
|
||||
if (!multiplier && !find_index) {
|
||||
return 0;
|
||||
}
|
||||
for_max = min - (min % 1000) + 1000;
|
||||
|
||||
// We can't use binary search because the numbers are not sorted.
|
||||
// They are sorted using the words' alphabet (so we can use the index).
|
||||
// Example: axle (1953), beam (1315)
|
||||
// The first digit is sorted so we can loop only upto `for_max`.
|
||||
while (words_button_seq[i] < for_max) {
|
||||
if (words_button_seq[i] >= min && words_button_seq[i] < max) {
|
||||
if (find_index) {
|
||||
return i;
|
||||
}
|
||||
|
||||
switch (multiplier) {
|
||||
case 1:
|
||||
divider = 1;
|
||||
break;
|
||||
case 2:
|
||||
divider = 10;
|
||||
break;
|
||||
case 3:
|
||||
divider = 100;
|
||||
break;
|
||||
default:
|
||||
divider = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
digit = (words_button_seq[i] / divider) % 10;
|
||||
bitmap |= 1 << (digit - 1);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
return bitmap;
|
||||
}
|
36
crypto/slip39.h
Normal file
36
crypto/slip39.h
Normal file
@ -0,0 +1,36 @@
|
||||
/**
|
||||
* This file is part of the TREZOR project, https://trezor.io/
|
||||
*
|
||||
* Copyright (c) SatoshiLabs
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
||||
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
const char* get_word(uint16_t index);
|
||||
|
||||
bool word_index(uint16_t* index, const char* word, uint8_t word_length);
|
||||
|
||||
uint16_t compute_mask(uint16_t prefix);
|
||||
|
||||
const char* button_sequence_to_word(uint16_t prefix);
|
||||
|
||||
uint16_t find(uint16_t prefix, bool find_index);
|
1238
crypto/slip39_wordlist.h
Normal file
1238
crypto/slip39_wordlist.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -67,6 +67,7 @@
|
||||
#include "sha2.h"
|
||||
#include "sha3.h"
|
||||
#include "shamir.h"
|
||||
#include "slip39.h"
|
||||
|
||||
#if VALGRIND
|
||||
/*
|
||||
@ -5096,6 +5097,96 @@ START_TEST(test_mnemonic_to_entropy) {
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_slip39_get_word) {
|
||||
static const struct {
|
||||
const int index;
|
||||
const char *expected_word;
|
||||
} vectors[] = {{573, "member"},
|
||||
{0, "academic"},
|
||||
{1023, "zero"},
|
||||
{245, "drove"},
|
||||
{781, "satoshi"}};
|
||||
for (size_t i = 0; i < (sizeof(vectors) / sizeof(*vectors)); i++) {
|
||||
const char *a = get_word(vectors[i].index);
|
||||
ck_assert_str_eq(a, vectors[i].expected_word);
|
||||
}
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_slip39_word_index) {
|
||||
uint16_t index;
|
||||
static const struct {
|
||||
const char *word;
|
||||
bool expected_result;
|
||||
uint16_t expected_index;
|
||||
} vectors[] = {{"academic", true, 0},
|
||||
{"zero", true, 1023},
|
||||
{"drove", true, 245},
|
||||
{"satoshi", true, 781},
|
||||
{"member", true, 573},
|
||||
// 9999 value is never checked since the word is not in list
|
||||
{"fakeword", false, 9999}};
|
||||
for (size_t i = 0; i < (sizeof(vectors) / sizeof(*vectors)); i++) {
|
||||
bool result = word_index(&index, vectors[i].word, sizeof(vectors[i].word));
|
||||
ck_assert_int_eq(result, vectors[i].expected_result);
|
||||
if (result) {
|
||||
ck_assert_int_eq(index, vectors[i].expected_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_slip39_compute_mask) {
|
||||
static const struct {
|
||||
const uint16_t prefix;
|
||||
const uint16_t expected_mask;
|
||||
} vectors[] = {{
|
||||
12,
|
||||
0xFD // 011111101
|
||||
},
|
||||
{
|
||||
21,
|
||||
0xF8 // 011111000
|
||||
},
|
||||
{
|
||||
75,
|
||||
0xAD // 010101101
|
||||
},
|
||||
{
|
||||
4,
|
||||
0x1F7 // 111110111
|
||||
},
|
||||
{
|
||||
738,
|
||||
0x6D // 001101101
|
||||
},
|
||||
{
|
||||
9,
|
||||
0x6D // 001101101
|
||||
}};
|
||||
for (size_t i = 0; i < (sizeof(vectors) / sizeof(*vectors)); i++) {
|
||||
uint16_t mask = compute_mask(vectors[i].prefix);
|
||||
ck_assert_int_eq(mask, vectors[i].expected_mask);
|
||||
}
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_slip39_sequence_to_word) {
|
||||
static const struct {
|
||||
const uint16_t prefix;
|
||||
const char *expected_word;
|
||||
} vectors[] = {{7945, "swimming"},
|
||||
{646, "photo"},
|
||||
{5, "kernel"},
|
||||
{34, "either"},
|
||||
{62, "ocean"}};
|
||||
for (size_t i = 0; i < (sizeof(vectors) / sizeof(*vectors)); i++) {
|
||||
const char *word = button_sequence_to_word(vectors[i].prefix);
|
||||
ck_assert_str_eq(word, vectors[i].expected_word);
|
||||
}
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_shamir) {
|
||||
#define SHAMIR_MAX_COUNT 16
|
||||
static const struct {
|
||||
@ -8676,6 +8767,13 @@ Suite *test_suite(void) {
|
||||
tcase_add_test(tc, test_mnemonic_to_entropy);
|
||||
suite_add_tcase(s, tc);
|
||||
|
||||
tc = tcase_create("slip39");
|
||||
tcase_add_test(tc, test_slip39_get_word);
|
||||
tcase_add_test(tc, test_slip39_word_index);
|
||||
tcase_add_test(tc, test_slip39_compute_mask);
|
||||
tcase_add_test(tc, test_slip39_sequence_to_word);
|
||||
suite_add_tcase(s, tc);
|
||||
|
||||
tc = tcase_create("shamir");
|
||||
tcase_add_test(tc, test_shamir);
|
||||
suite_add_tcase(s, tc);
|
||||
|
Loading…
Reference in New Issue
Block a user