diff --git a/src/apps/management/__init__.py b/src/apps/management/__init__.py index d8cbe7634..4486abed6 100644 --- a/src/apps/management/__init__.py +++ b/src/apps/management/__init__.py @@ -5,19 +5,23 @@ from trezor.utils import unimport_func @unimport_func def dispatch_LoadDevice(mtype, mbuf): from trezor.messages.LoadDevice import LoadDevice - message = LoadDevice.loads(mbuf) - from .layout_load_device import layout_load_device return layout_load_device(message) +@unimport_func +def dispatch_ResetDevice(mtype, mbuf): + from trezor.messages.ResetDevice import ResetDevice + message = ResetDevice.loads(mbuf) + from .layout_reset_device import layout_reset_device + return layout_reset_device(message) + + @unimport_func def dispatch_WipeDevice(mtype, mbuf): from trezor.messages.WipeDevice import WipeDevice - message = WipeDevice.loads(mbuf) - from .layout_wipe_device import layout_wipe_device return layout_wipe_device(message) @@ -25,5 +29,7 @@ def dispatch_WipeDevice(mtype, mbuf): def boot(): LoadDevice = 13 register(LoadDevice, dispatch_LoadDevice) + ResetDevice = 14 + register(ResetDevice, dispatch_ResetDevice) WipeDevice = 5 register(WipeDevice, dispatch_WipeDevice) diff --git a/src/apps/management/layout_reset_device.py b/src/apps/management/layout_reset_device.py new file mode 100644 index 000000000..54fb99dd0 --- /dev/null +++ b/src/apps/management/layout_reset_device.py @@ -0,0 +1,78 @@ +from trezor import wire, loop, res, ui +from trezor.ui.swipe import Swipe, SWIPE_UP, SWIPE_DOWN +from trezor.crypto import hashlib, random, bip39 +from trezor.utils import unimport_gen + + +def generate_mnemonic(strength, display_random): + from trezor.messages.EntropyRequest import EntropyRequest + from trezor.messages.EntropyAck import EntropyAck + + ack = yield from wire.call(EntropyRequest(), EntropyAck) + + ctx = hashlib.sha256() + ctx.update(random.bytes(32)) + ctx.update(ack.entropy) + entropy = ctx.digest() + + # TODO: handle strength + # TODO: handle display_random + + return bip39.from_data(entropy) + + +def request_new_pin(): + from trezor.workflows.pin import request_pin + + pin = yield from request_pin() + pin_again = yield from request_pin('Enter PIN again') + + if pin == pin_again: + return pin + else: + raise Exception() # TODO: wrong PIN should be handled in unified way + + +@unimport_gen +def layout_reset_device(m): + + # TODO: Failure if not empty + + mnemonic = yield from generate_mnemonic(m.strength, m.display_random) + + if m.pin_protection: + pin = yield from request_new_pin() + else: + pin = None + + mnemonic_words = mnemonic.split() + words_per_page = const(4) + + def render(page): + ui.clear() + ui.display.text(10, 30, 'Write down your seed', ui.BOLD, ui.LIGHT_GREEN, ui.BLACK) + for i in range(0, words_per_page): + index = i + page * words_per_page + word = mnemonic_words[index] + top = 74 + i * 30 + ui.display.text(10, top, '%d.' % (index + 1), ui.BOLD, ui.LIGHT_GREEN, ui.BLACK) + ui.display.text(40, top, '%s' % word, ui.BOLD, ui.WHITE, ui.BLACK) + + def paginate(): + count = len(mnemonic_words) // words_per_page + page = 0 + while True: + render(page) + degrees = yield from Swipe().wait() + if degrees == SWIPE_UP: + page = max(page - 1, 0) + elif degrees == SWIPE_DOWN: + page = min(page + 1, count) + + def animate_arrow(): + def func(foreground): + ui.display.icon(105, 200, res.load('apps/seed/res/small-arrow.toig'), foreground, ui.BLACK) + yield from ui.animate_pulse(func, ui.WHITE, ui.BLACK, speed=190000) + + yield loop.Wait([paginate(), + animate_arrow()]) diff --git a/src/trezor/workflows/request_pin.py b/src/trezor/workflows/request_pin.py index e163e72f6..0363056c0 100644 --- a/src/trezor/workflows/request_pin.py +++ b/src/trezor/workflows/request_pin.py @@ -1,20 +1,59 @@ +from trezor import ui from trezor import wire +from trezor import config from trezor.utils import unimport_gen +MGMT_APP = const(1) -@unimport_gen -def request_pin(): +PASSPHRASE_PROTECT = (1) # 0 | 1 +PIN_PROTECT = const(2) # 0 | 1 +PIN = const(4) # str + + +def prompt_pin(*args, **kwargs): from trezor.ui.pin import PinMatrix from trezor.ui.confirm import ConfirmDialog, CONFIRMED + + ui.clear() + + matrix = PinMatrix(*args, **kwargs) + dialog = ConfirmDialog(matrix) + result = yield from dialog.wait() + + return matrix.pin if result == CONFIRMED else None + + +def request_pin(*args, **kwargs): from trezor.messages.ButtonRequest import ButtonRequest from trezor.messages.ButtonRequestType import ProtectCall from trezor.messages.ButtonAck import ButtonAck - matrix = PinMatrix() - dialog = ConfirmDialog(matrix) - dialog.render() - ack = yield from wire.call(ButtonRequest(code=ProtectCall), ButtonAck) - res = yield from dialog.wait() + pin = yield from prompt_pin(*args, **kwargs) - return matrix.pin if res == CONFIRMED else None + return pin + + +def change_pin(): + pass + + +@unimport_gen +def protect_with_pin(): + from trezor.messages.Failure import Failure + from trezor.messages.FailureType import PinInvalid + from trezor.messages.FailureType import ActionCancelled + + pin_protect = config.get(MGMT_APP, PIN_PROTECT) + if not pin_protect: + return + + entered_pin = yield from request_pin() + if entered_pin is None: + yield from wire.write(Failure(code=ActionCancelled, message='Cancelled')) + raise Exception('Cancelled') + + stored_pin = config.get(MGMT_APP, PIN) + if stored_pin != entered_pin: + yield from wire.write(Failure(code=PinInvalid, message='PIN invalid')) + raise Exception('PIN invalid')