After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.3 KiB |
@ -0,0 +1,30 @@
|
||||
from trezor import wire, ui, loop
|
||||
from trezor.utils import unimport
|
||||
|
||||
# used to confirm/cancel the dialogs from outside of this module (i.e.
|
||||
# through debug link)
|
||||
if __debug__:
|
||||
signal = loop.signal()
|
||||
|
||||
|
||||
@ui.layout
|
||||
@unimport
|
||||
async def request_words(ctx, content, code=None, *args, **kwargs):
|
||||
from trezor.ui.word_select import WordSelector
|
||||
from trezor.messages.ButtonRequest import ButtonRequest
|
||||
from trezor.messages.ButtonRequestType import Other
|
||||
from trezor.messages.wire_types import ButtonAck
|
||||
|
||||
ui.display.clear()
|
||||
dialog = WordSelector(content, *args, **kwargs)
|
||||
dialog.render()
|
||||
|
||||
if code is None:
|
||||
code = Other
|
||||
await ctx.call(ButtonRequest(code=code), ButtonAck)
|
||||
|
||||
if __debug__:
|
||||
waiter = loop.wait(signal, dialog)
|
||||
else:
|
||||
waiter = dialog
|
||||
return await waiter
|
@ -1,23 +1,55 @@
|
||||
from trezor import ui, wire
|
||||
from trezor.utils import unimport
|
||||
|
||||
|
||||
def nth(n):
|
||||
if 4 <= n % 100 <= 20:
|
||||
sfx = 'th'
|
||||
else:
|
||||
sfx = {1: 'st', 2: 'nd', 3: 'rd'}.get(n % 10, 'th')
|
||||
return str(n) + sfx
|
||||
async def recovery_device(ctx, msg):
|
||||
'''
|
||||
Recover BIP39 seed into empty device.
|
||||
|
||||
1. Ask for the number of words in recovered seed.
|
||||
2. Let user type in the mnemonic words one by one.
|
||||
3. Optionally check the seed validity.
|
||||
4. Optionally ask for the PIN, with confirmation.
|
||||
5. Save into storage.
|
||||
'''
|
||||
from trezor import config
|
||||
from trezor.crypto import bip39
|
||||
from trezor.messages.FailureType import UnexpectedMessage, ProcessError
|
||||
from trezor.messages.Success import Success
|
||||
from trezor.ui.text import Text
|
||||
from apps.common import storage
|
||||
from apps.common.request_pin import request_pin
|
||||
from apps.common.request_words import request_words
|
||||
|
||||
@unimport
|
||||
async def layout_recovery_device(ctx, msg):
|
||||
if storage.is_initialized():
|
||||
raise wire.FailureError(UnexpectedMessage, 'Already initialized')
|
||||
|
||||
msg = 'Please enter ' + nth(msg.word_count) + ' word'
|
||||
wordcount = await request_words(ctx,
|
||||
Text('Device recovery', ui.ICON_RECOVERY,
|
||||
'Number of words?'))
|
||||
mnemonic = await request_mnemonic(wordcount, 'Type %s. word')
|
||||
|
||||
ui.display.clear()
|
||||
ui.header('Recovery device', ui.ICON_RECOVERY, ui.BG, ui.LIGHT_GREEN)
|
||||
ui.display.text(10, 74, msg, ui.BOLD, ui.FG, ui.BG)
|
||||
ui.display.text(10, 104, 'of your mnemonic.', ui.BOLD, ui.FG, ui.BG)
|
||||
if msg.enforce_wordlist and not bip39.check(mnemonic):
|
||||
raise wire.FailureError(ProcessError, 'Mnemonic is not valid')
|
||||
|
||||
# TODO
|
||||
if msg.pin_protection:
|
||||
curpin = ''
|
||||
newpin = await request_pin(ctx)
|
||||
config.change_pin(curpin, newpin)
|
||||
|
||||
storage.load_settings(label=msg.label,
|
||||
use_passphrase=msg.passphrase_protection)
|
||||
storage.load_mnemonic(mnemonic)
|
||||
return Success()
|
||||
|
||||
|
||||
async def request_mnemonic(count: int, prompt: str) -> str:
|
||||
from trezor.ui.keyboard import MnemonicKeyboard
|
||||
|
||||
words = []
|
||||
board = MnemonicKeyboard()
|
||||
for i in range(0, count):
|
||||
board.prompt = prompt % (i + 1)
|
||||
word = await board
|
||||
words.append(word)
|
||||
|
||||
return ' '.join(words)
|
||||
|
@ -0,0 +1,47 @@
|
||||
from micropython import const
|
||||
from trezor import loop
|
||||
from trezor import ui
|
||||
from trezor.ui import Widget
|
||||
from trezor.ui.button import Button, BTN_CLICKED
|
||||
|
||||
_W12 = const(12)
|
||||
_W15 = const(15)
|
||||
_W18 = const(18)
|
||||
_W24 = const(24)
|
||||
|
||||
|
||||
class WordSelector(Widget):
|
||||
|
||||
def __init__(self, content):
|
||||
self.content = content
|
||||
self.w12 = Button(ui.grid(8, n_y=4, n_x=4, cells_x=2), str(_W12),
|
||||
normal_style=ui.BTN_KEY,
|
||||
active_style=ui.BTN_KEY_ACTIVE)
|
||||
self.w15 = Button(ui.grid(10, n_y=4, n_x=4, cells_x=2), str(_W15),
|
||||
normal_style=ui.BTN_KEY,
|
||||
active_style=ui.BTN_KEY_ACTIVE)
|
||||
self.w18 = Button(ui.grid(12, n_y=4, n_x=4, cells_x=2), str(_W18),
|
||||
normal_style=ui.BTN_KEY,
|
||||
active_style=ui.BTN_KEY_ACTIVE)
|
||||
self.w24 = Button(ui.grid(14, n_y=4, n_x=4, cells_x=2), str(_W24),
|
||||
normal_style=ui.BTN_KEY,
|
||||
active_style=ui.BTN_KEY_ACTIVE)
|
||||
|
||||
def render(self):
|
||||
self.w12.render()
|
||||
self.w15.render()
|
||||
self.w18.render()
|
||||
self.w24.render()
|
||||
|
||||
def touch(self, event, pos):
|
||||
if self.w12.touch(event, pos) == BTN_CLICKED:
|
||||
return _W12
|
||||
if self.w15.touch(event, pos) == BTN_CLICKED:
|
||||
return _W15
|
||||
if self.w18.touch(event, pos) == BTN_CLICKED:
|
||||
return _W18
|
||||
if self.w24.touch(event, pos) == BTN_CLICKED:
|
||||
return _W24
|
||||
|
||||
async def __iter__(self):
|
||||
return await loop.wait(super().__iter__(), self.content)
|