1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-12-19 12:58:13 +00:00

Merge pull request #98 from trezor/reset

Reset device
This commit is contained in:
Jan Pochyla 2018-01-22 17:58:38 +01:00 committed by GitHub
commit 11a5825928
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 98 additions and 70 deletions

BIN
assets/dontcopy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
assets/swipedown.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -12,8 +12,8 @@ def dispatch_LoadDevice(*args, **kwargs):
@unimport @unimport
def dispatch_ResetDevice(*args, **kwargs): def dispatch_ResetDevice(*args, **kwargs):
from .reset_device import layout_reset_device from .reset_device import reset_device
return layout_reset_device(*args, **kwargs) return reset_device(*args, **kwargs)
@unimport @unimport

View File

@ -1,6 +1,5 @@
from micropython import const from micropython import const
from trezor import wire, ui from trezor import config, ui, wire
from trezor.ui.container import Container
from trezor.utils import unimport, chunks from trezor.utils import unimport, chunks
from ubinascii import hexlify from ubinascii import hexlify
@ -10,16 +9,15 @@ if __debug__:
@unimport @unimport
async def layout_reset_device(ctx, msg): async def reset_device(ctx, msg):
from trezor import config
from trezor.ui.text import Text from trezor.ui.text import Text
from trezor.crypto import hashlib, random, bip39 from trezor.crypto import hashlib, random, bip39
from trezor.ui.keyboard import MnemonicKeyboard
from trezor.messages.EntropyRequest import EntropyRequest from trezor.messages.EntropyRequest import EntropyRequest
from trezor.messages.Success import Success from trezor.messages.Success import Success
from trezor.messages import FailureType from trezor.messages import FailureType
from trezor.messages import ButtonRequestType from trezor.messages import ButtonRequestType
from trezor.messages.wire_types import EntropyAck from trezor.messages.wire_types import EntropyAck
from apps.management.change_pin import request_pin_confirm from apps.management.change_pin import request_pin_confirm
from apps.common.confirm import require_confirm from apps.common.confirm import require_confirm
from apps.common import storage from apps.common import storage
@ -37,11 +35,13 @@ async def layout_reset_device(ctx, msg):
internal_entropy = random.bytes(32) internal_entropy = random.bytes(32)
# display internal entropy
if msg.display_random: if msg.display_random:
entropy_lines = chunks(hexlify(internal_entropy).decode(), 16) entropy_lines = chunks(hexlify(internal_entropy).decode(), 16)
entropy_content = Text('Internal entropy', ui.ICON_RESET, ui.MONO, *entropy_lines) entropy_content = Text('Internal entropy', ui.ICON_RESET, ui.MONO, *entropy_lines)
await require_confirm(ctx, entropy_content, ButtonRequestType.ResetDevice) await require_confirm(ctx, entropy_content, ButtonRequestType.ResetDevice)
# request new PIN
if msg.pin_protection: if msg.pin_protection:
curpin = '' curpin = ''
newpin = await request_pin_confirm(ctx) newpin = await request_pin_confirm(ctx)
@ -49,6 +49,7 @@ async def layout_reset_device(ctx, msg):
curpin = '' curpin = ''
newpin = '' newpin = ''
# request external entropy and compute mnemonic
external_entropy_ack = await ctx.call(EntropyRequest(), EntropyAck) external_entropy_ack = await ctx.call(EntropyRequest(), EntropyAck)
ehash = hashlib.sha256() ehash = hashlib.sha256()
ehash.update(internal_entropy) ehash.update(internal_entropy)
@ -56,46 +57,62 @@ async def layout_reset_device(ctx, msg):
entropy = ehash.digest() entropy = ehash.digest()
mnemonic = bip39.from_data(entropy[:msg.strength // 8]) mnemonic = bip39.from_data(entropy[:msg.strength // 8])
await show_mnemonic_by_word(ctx, mnemonic) # mnemonic safety warning
warning_content = Text(
'Backup your seed', ui.ICON_NOCOPY, ui.NORMAL,
'Never make a digital',
'copy of your recovery',
'seed and never upload',
'it online!')
await require_confirm(
ctx,
warning_content,
ButtonRequestType.ResetDevice,
confirm='I understand',
cancel=None)
# ask to write down mnemonic
await show_mnemonic(mnemonic)
# ask for random word to check correctness
words = mnemonic.split()
index = random.uniform(len(words))
res = await MnemonicKeyboard('Type %s. word' % (index + 1))
if res != words[index]:
content = Text(
'Wrong entry!', ui.ICON_CLEAR,
'You have entered',
'wrong seed word.',
'Please, reconnect',
'the device and try again.', icon_color=ui.RED)
ui.display.clear()
await content
raise wire.FailureError(FailureType.DataError, 'Wrong entry')
# write into storage
if curpin != newpin: if curpin != newpin:
config.change_pin(curpin, newpin) config.change_pin(curpin, newpin)
storage.load_settings(label=msg.label, storage.load_settings(
use_passphrase=msg.passphrase_protection) label=msg.label, use_passphrase=msg.passphrase_protection)
storage.load_mnemonic(mnemonic) storage.load_mnemonic(mnemonic)
# show success message
content = Text(
'Backup is done!', ui.ICON_CONFIRM,
'Never make a digital',
'copy of your recovery',
'seed and never upload',
'it online!', icon_color=ui.GREEN)
await require_confirm(
ctx,
content,
ButtonRequestType.ResetDevice,
confirm='Finish setup',
cancel=None)
return Success(message='Initialized') return Success(message='Initialized')
async def show_mnemonic_by_word(ctx, mnemonic):
from trezor.ui.text import Text
from trezor.messages.ButtonRequestType import ConfirmWord
from apps.common.confirm import confirm
words = mnemonic.split()
if __debug__:
global current_word
for index, word in enumerate(words):
if __debug__:
current_word = word
await confirm(ctx,
Text('Recovery seed setup', ui.ICON_RESET,
ui.NORMAL, 'Write down seed word', '',
ui.BOLD, '%d. %s' % (index + 1, word)),
ConfirmWord, confirm='Next', cancel=None)
for index, word in enumerate(words):
if __debug__:
current_word = word
await confirm(ctx,
Text('Recovery seed setup', ui.ICON_RESET,
ui.NORMAL, 'Confirm seed word', '',
ui.BOLD, '%d. %s' % (index + 1, word)),
ConfirmWord, confirm='Next', cancel=None)
async def show_mnemonic(mnemonic): async def show_mnemonic(mnemonic):
from trezor.ui.scroll import paginate from trezor.ui.scroll import paginate
@ -108,26 +125,23 @@ async def show_mnemonic(mnemonic):
async def show_mnemonic_page(page, page_count, mnemonic): async def show_mnemonic_page(page, page_count, mnemonic):
from trezor.ui.button import Button from trezor.ui.button import Button
from trezor.ui.scroll import render_scrollbar, animate_swipe from trezor.ui.text import Text
from trezor.ui.scroll import Scrollpage, animate_swipe
lines = ['%d. %s' % (wi + 1, word) for wi, word in mnemonic[page]]
scroll_page = Scrollpage(
Text('Recovery seed setup', ui.ICON_RESET, ui.MONO, lines),
page,
page_count)
ui.display.clear() ui.display.clear()
ui.header('Write down your seed', ui.ICON_RESET, ui.BG, ui.LIGHT_GREEN) scroll_page.render()
render_scrollbar(page, page_count)
for pi, (wi, word) in enumerate(mnemonic[page]):
top = pi * 35 + 68
pos = wi + 1
offset = 0
if pos > 9:
offset += 12
ui.display.text(10, top, '%d.' % pos, ui.BOLD, ui.LIGHT_GREEN, ui.BG)
ui.display.text(30 + offset, top, '%s' % word, ui.BOLD, ui.FG, ui.BG)
if page + 1 == page_count: if page + 1 == page_count:
await Button( await Button(
(0, 240 - 48, 240, 48), ui.grid(4, n_x=1),
'Finish', "I'm done",
normal_style=ui.BTN_CONFIRM, normal_style=ui.BTN_CONFIRM,
active_style=ui.BTN_CONFIRM_ACTIVE) active_style=ui.BTN_CONFIRM_ACTIVE)
ui.display.clear()
else: else:
await animate_swipe() await animate_swipe()

BIN
src/trezor/res/nocopy.toig Normal file

Binary file not shown.

Binary file not shown.

View File

@ -16,10 +16,10 @@ class ConfirmDialog(Widget):
def __init__(self, content, confirm=DEFAULT_CONFIRM, cancel=DEFAULT_CANCEL): def __init__(self, content, confirm=DEFAULT_CONFIRM, cancel=DEFAULT_CANCEL):
self.content = content self.content = content
if cancel is not None: if cancel is not None:
self.confirm = Button(ui.grid(8, n_x=2), confirm, self.confirm = Button(ui.grid(9, n_x=2), confirm,
normal_style=ui.BTN_CONFIRM, normal_style=ui.BTN_CONFIRM,
active_style=ui.BTN_CONFIRM_ACTIVE) active_style=ui.BTN_CONFIRM_ACTIVE)
self.cancel = Button(ui.grid(9, n_x=2), cancel, self.cancel = Button(ui.grid(8, n_x=2), cancel,
normal_style=ui.BTN_CANCEL, normal_style=ui.BTN_CANCEL,
active_style=ui.BTN_CANCEL_ACTIVE) active_style=ui.BTN_CANCEL_ACTIVE)
else: else:

View File

@ -1,5 +1,5 @@
from micropython import const from micropython import const
from trezor import loop, ui from trezor import loop, ui, res
from .swipe import Swipe, SWIPE_UP, SWIPE_DOWN, SWIPE_VERTICAL from .swipe import Swipe, SWIPE_UP, SWIPE_DOWN, SWIPE_VERTICAL
@ -37,17 +37,15 @@ async def animate_swipe():
sleep = loop.sleep(time_delay) sleep = loop.sleep(time_delay)
for t in ui.pulse(draw_delay): for t in ui.pulse(draw_delay):
fg = ui.blend(ui.GREY, ui.DARK_GREY, t) fg = ui.blend(ui.GREY, ui.DARK_GREY, t)
ui.display.bar_radius(102, 214, 36, 4, fg, ui.BG, 2) ui.display.icon(110, 210, res.load(ui.ICON_SWIPE), fg, ui.BG)
ui.display.bar_radius(106, 222, 28, 4, fg, ui.BG, 2)
ui.display.bar_radius(110, 230, 20, 4, fg, ui.BG, 2)
await sleep await sleep
def render_scrollbar(page, page_count): def render_scrollbar(page, page_count):
bbox = const(220) bbox = const(220)
size = const(10) size = const(8)
padding = 18 padding = 14
if page_count * padding > bbox: if page_count * padding > bbox:
padding = bbox // page_count padding = bbox // page_count
@ -62,11 +60,16 @@ def render_scrollbar(page, page_count):
size, ui.FG, ui.BG, 4) size, ui.FG, ui.BG, 4)
class Scrollbar(ui.Widget): class Scrollpage(ui.Widget):
def __init__(self, page, page_count): def __init__(self, content, page, page_count):
self.content = content
self.page = page self.page = page
self.page_count = page_count self.page_count = page_count
def render(self): def render(self):
self.content.render()
render_scrollbar(self.page, self.page_count) render_scrollbar(self.page, self.page_count)
async def __iter__(self):
return await loop.wait(super().__iter__(), self.content)

View File

@ -55,6 +55,8 @@ ICON_LOCK = 'trezor/res/lock.toig'
ICON_SEND = 'trezor/res/send.toig' ICON_SEND = 'trezor/res/send.toig'
ICON_CLICK = 'trezor/res/click.toig' ICON_CLICK = 'trezor/res/click.toig'
ICON_BACK = 'trezor/res/left.toig' ICON_BACK = 'trezor/res/left.toig'
ICON_NOCOPY = 'trezor/res/nocopy.toig'
ICON_SWIPE = 'trezor/res/swipedown.toig'
# buttons # buttons
BTN_DEFAULT = { BTN_DEFAULT = {
@ -153,5 +155,5 @@ LDR_DEFAULT_ACTIVE = {
'bg-color': BG, 'bg-color': BG,
'fg-color': GREEN, 'fg-color': GREEN,
'icon': ICON_SEND, 'icon': ICON_SEND,
'icon-fg-color': GREEN, 'icon-fg-color': WHITE,
} }

View File

@ -22,11 +22,20 @@ class Text(ui.Widget):
bg = ui.BG bg = ui.BG
ui.header(self.header_text, self.header_icon, ui.TITLE_GREY, ui.BG, self.icon_color) ui.header(self.header_text, self.header_icon, ui.TITLE_GREY, ui.BG, self.icon_color)
for item in self.content: def process(eitem):
if isinstance(item, str): nonlocal offset_y
ui.display.text(offset_x, offset_y, item, style, fg, bg) nonlocal style
nonlocal fg
if isinstance(eitem, str):
ui.display.text(offset_x, offset_y, eitem, style, fg, bg)
offset_y += TEXT_LINE_HEIGHT offset_y += TEXT_LINE_HEIGHT
elif item == ui.MONO or item == ui.NORMAL or item == ui.BOLD: elif isinstance(eitem, (tuple, list, dict, set)):
style = item for i in eitem:
process(i)
elif eitem == ui.MONO or eitem == ui.NORMAL or eitem == ui.BOLD:
style = eitem
else: else:
fg = item fg = eitem
for item in self.content:
process(item)