mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-19 12:58:13 +00:00
commit
11a5825928
BIN
assets/dontcopy.png
Normal file
BIN
assets/dontcopy.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
BIN
assets/swipedown.png
Normal file
BIN
assets/swipedown.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
@ -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
|
||||||
|
@ -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
BIN
src/trezor/res/nocopy.toig
Normal file
Binary file not shown.
BIN
src/trezor/res/swipedown.toig
Normal file
BIN
src/trezor/res/swipedown.toig
Normal file
Binary file not shown.
@ -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:
|
||||||
|
@ -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)
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user