Skip to content
This repository has been archived by the owner on May 28, 2019. It is now read-only.

Commit

Permalink
src/apps/debug: simplify debuglink, add more decision/state fields
Browse files Browse the repository at this point in the history
- move data exported over debuglink into apps.debug
- move debug signals into apps.debug
- make pin/mnemonic dialogs testable
- streamline code style of apps.management.reset_device
- check __debug__ when debug app starts
  • Loading branch information
jpochyla committed Mar 20, 2018
1 parent f9c51af commit 01bc12e
Show file tree
Hide file tree
Showing 9 changed files with 108 additions and 103 deletions.
20 changes: 3 additions & 17 deletions src/apps/common/confirm.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
from trezor import loop, ui, wire
from trezor import ui, wire
from trezor.messages import ButtonRequestType, FailureType, wire_types
from trezor.messages.ButtonRequest import ButtonRequest
from trezor.ui.confirm import CONFIRMED, ConfirmDialog, HoldToConfirmDialog
from apps.common import cache

# used to confirm/cancel the dialogs from outside of this module (i.e.
# through debug link)
if __debug__:
signal = cache.memory.setdefault('confirm_signal', loop.signal())


@ui.layout
Expand All @@ -18,11 +12,7 @@ async def confirm(ctx, content, code=None, *args, **kwargs):

dialog = ConfirmDialog(content, *args, **kwargs)

if __debug__:
waiter = ctx.wait(signal, dialog)
else:
waiter = ctx.wait(dialog)
return await waiter == CONFIRMED
return await ctx.wait(dialog) == CONFIRMED


@ui.layout
Expand All @@ -33,11 +23,7 @@ async def hold_to_confirm(ctx, content, code=None, *args, **kwargs):

dialog = HoldToConfirmDialog(content, 'Hold to confirm', *args, **kwargs)

if __debug__:
waiter = ctx.wait(signal, dialog)
else:
waiter = ctx.wait(dialog)
return await waiter == CONFIRMED
return await ctx.wait(dialog) == CONFIRMED


async def require_confirm(*args, **kwargs):
Expand Down
16 changes: 12 additions & 4 deletions src/apps/common/request_pin.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
from trezor import res, ui
from trezor import loop, res, ui
from trezor.messages import PinMatrixRequestType
from trezor.ui.confirm import CONFIRMED, ConfirmDialog
from trezor.ui.pin import PinMatrix

if __debug__:
from apps.debug import input_signal


class PinCancelled(Exception):
pass
Expand Down Expand Up @@ -42,13 +45,18 @@ def onchange():
matrix.onchange()

while True:
result = await dialog
if __debug__:
result = await loop.wait(dialog, input_signal)
if isinstance(result, str):
return result
else:
result = await dialog
if result == CONFIRMED:
return matrix.pin
elif result != CONFIRMED and matrix.pin:
elif matrix.pin: # reset
matrix.change('')
continue
else:
else: # cancel
raise PinCancelled()


Expand Down
81 changes: 27 additions & 54 deletions src/apps/debug/__init__.py
Original file line number Diff line number Diff line change
@@ -1,69 +1,42 @@
import micropython
import gc
from uctypes import bytes_at, bytearray_at

from trezor import loop
from trezor.wire import register, protobuf_workflow
from trezor.messages.wire_types import \
DebugLinkDecision, DebugLinkGetState, DebugLinkStop, \
DebugLinkMemoryRead, DebugLinkMemoryWrite, DebugLinkFlashErase
from trezor.messages.DebugLinkMemory import DebugLinkMemory
from trezor import loop, utils
from trezor.messages import wire_types
from trezor.messages.DebugLinkState import DebugLinkState
from trezor.ui.confirm import CONFIRMED, CANCELLED

from apps.common.confirm import signal
from trezor.ui import confirm, swipe
from trezor.wire import register, protobuf_workflow
from apps.common import storage
from apps.management import reset_device

if not __debug__:
utils.halt("debug mode inactive")

reset_internal_entropy = None
reset_current_words = None
reset_word_index = None

confirm_signal = loop.signal()
swipe_signal = loop.signal()
input_signal = loop.signal()


async def dispatch_DebugLinkDecision(ctx, msg):
signal.send(CONFIRMED if msg.yes_no else CANCELLED)
if msg.yes_no is not None:
confirm_signal.send(confirm.CONFIRMED if msg.yes_no else confirm.CANCELLED)
if msg.up_down is not None:
swipe_signal.send(swipe.SWIPE_DOWN if msg.up_down else swipe.SWIPE_UP)
if msg.input is not None:
input_signal.send(msg.input)


async def dispatch_DebugLinkGetState(ctx, msg):
m = DebugLinkState()
m.mnemonic = storage.get_mnemonic()
m.passphrase_protection = storage.has_passphrase()
m.reset_entropy = reset_device.internal_entropy
m.reset_word = reset_device.current_word
m.reset_word_pos = reset_word_index
m.reset_entropy = reset_internal_entropy
if reset_current_words:
m.reset_word = ' '.join(reset_current_words)
return m


async def dispatch_DebugLinkStop(ctx, msg):
pass


async def dispatch_DebugLinkMemoryRead(ctx, msg):
m = DebugLinkMemory()
m.memory = bytes_at(msg.address, msg.length)
return m


async def dispatch_DebugLinkMemoryWrite(ctx, msg):
l = len(msg.memory)
data = bytearray_at(msg.address, l)
data[0:l] = msg.memory


async def dispatch_DebugLinkFlashErase(ctx, msg):
# TODO: erase(msg.sector)
pass


async def memory_stats(interval):
sleep = loop.sleep(interval * 1000 * 1000)
while True:
micropython.mem_info()
gc.collect()
await sleep


def boot():
register(DebugLinkDecision, protobuf_workflow, dispatch_DebugLinkDecision)
register(DebugLinkGetState, protobuf_workflow, dispatch_DebugLinkGetState)
register(DebugLinkStop, protobuf_workflow, dispatch_DebugLinkStop)
register(DebugLinkMemoryRead, protobuf_workflow, dispatch_DebugLinkMemoryRead)
register(DebugLinkMemoryWrite, protobuf_workflow, dispatch_DebugLinkMemoryWrite)
register(DebugLinkFlashErase, protobuf_workflow, dispatch_DebugLinkFlashErase)

# loop.schedule(memory_stats(10))
register(wire_types.DebugLinkDecision, protobuf_workflow, dispatch_DebugLinkDecision)
register(wire_types.DebugLinkGetState, protobuf_workflow, dispatch_DebugLinkGetState)
42 changes: 21 additions & 21 deletions src/apps/management/reset_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,10 @@
from apps.management.change_pin import request_pin_confirm

if __debug__:
internal_entropy = None
current_word = None
from apps import debug


async def reset_device(ctx, msg):
if __debug__:
global internal_entropy

# validate parameters and device state
if msg.strength not in (128, 192, 256):
raise wire.FailureError(
Expand All @@ -36,31 +32,29 @@ async def reset_device(ctx, msg):
FailureType.UnexpectedMessage,
'Already initialized')

# request new PIN
if msg.pin_protection:
# request new PIN
newpin = await request_pin_confirm(ctx)
else:
# new PIN is empty
newpin = ''

# generate and display internal entropy
internal_entropy = random.bytes(32)
internal_ent = random.bytes(32)
if __debug__:
debug.reset_internal_entropy = internal_ent
if msg.display_random:
await show_entropy(ctx, internal_entropy)
await show_entropy(ctx, internal_ent)

# request external entropy and compute mnemonic
ack = await ctx.call(EntropyRequest(), wire_types.EntropyAck)
mnemonic = generate_mnemonic(
msg.strength, internal_entropy, ack.entropy)
ent_ack = await ctx.call(EntropyRequest(), wire_types.EntropyAck)
mnemonic = generate_mnemonic(msg.strength, internal_ent, ent_ack.entropy)

if msg.skip_backup:
# let user backup the mnemonic later
pass
else:
# warn user about mnemonic safety
if not msg.skip_backup:
# require confirmation of the mnemonic safety
await show_warning(ctx)

# show mnemonic and require confirmation of a random word
while True:
# show mnemonic and require confirmation of a random word
await show_mnemonic(ctx, mnemonic)
if await check_mnemonic(ctx, mnemonic):
break
Expand All @@ -77,11 +71,11 @@ async def reset_device(ctx, msg):
storage.load_mnemonic(
mnemonic=mnemonic, needs_backup=msg.skip_backup)

# show success message
# show success message. if we skipped backup, it's possible that homescreen
# is still running, uninterrupted. restart it to pick up new label.
if not msg.skip_backup:
await show_success(ctx)
else:
# trigger reload of homescreen
workflow.restartdefault()

return Success(message='Initialized')
Expand Down Expand Up @@ -142,7 +136,7 @@ async def show_success(ctx):
cancel=None)


async def show_entropy(ctx, entropy: int):
async def show_entropy(ctx, entropy: bytes):
estr = hexlify(entropy).decode()
lines = chunks(estr, 16)
content = Text('Internal entropy', ui.ICON_RESET, ui.MONO, *lines)
Expand All @@ -165,6 +159,9 @@ async def show_mnemonic(ctx, mnemonic: str):

@ui.layout
async def show_mnemonic_page(page: int, page_count: int, pages: list):
if __debug__:
debug.reset_current_words = [word for _, word in pages[page]]

lines = ['%2d. %s' % (wi + 1, word) for wi, word in pages[page]]
content = Text('Recovery seed', ui.ICON_RESET, ui.MONO, *lines)
content = Scrollpage(content, page, page_count)
Expand Down Expand Up @@ -194,6 +191,9 @@ async def check_mnemonic(ctx, mnemonic: str) -> bool:

@ui.layout
async def check_word(ctx, words: list, index: int):
if __debug__:
debug.reset_word_index = index

keyboard = MnemonicKeyboard('Type the %s word:' % format_ordinal(index + 1))
result = await ctx.wait(keyboard)
return result == words[index]
8 changes: 7 additions & 1 deletion src/trezor/messages/DebugLinkDecision.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,20 @@

class DebugLinkDecision(p.MessageType):
FIELDS = {
1: ('yes_no', p.BoolType, 0), # required
1: ('yes_no', p.BoolType, 0),
2: ('up_down', p.BoolType, 0),
3: ('input', p.UnicodeType, 0),
}
MESSAGE_WIRE_TYPE = 100

def __init__(
self,
yes_no: bool = None,
up_down: bool = None,
input: str = None,
**kwargs,
):
self.yes_no = yes_no
self.up_down = up_down
self.input = input
p.MessageType.__init__(self, **kwargs)
3 changes: 3 additions & 0 deletions src/trezor/messages/DebugLinkState.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class DebugLinkState(p.MessageType):
8: ('reset_entropy', p.BytesType, 0),
9: ('recovery_fake_word', p.UnicodeType, 0),
10: ('recovery_word_pos', p.UVarintType, 0),
11: ('reset_word_pos', p.UVarintType, 0),
}
MESSAGE_WIRE_TYPE = 102

Expand All @@ -30,6 +31,7 @@ def __init__(
reset_entropy: bytes = None,
recovery_fake_word: str = None,
recovery_word_pos: int = None,
reset_word_pos: int = None,
**kwargs,
):
self.layout = layout
Expand All @@ -42,4 +44,5 @@ def __init__(
self.reset_entropy = reset_entropy
self.recovery_fake_word = recovery_fake_word
self.recovery_word_pos = recovery_word_pos
self.reset_word_pos = reset_word_pos
p.MessageType.__init__(self, **kwargs)
21 changes: 17 additions & 4 deletions src/trezor/ui/confirm.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,22 @@
from trezor.ui.button import BTN_ACTIVE, BTN_CLICKED, BTN_STARTED, Button
from trezor.ui.loader import Loader

if __debug__:
from apps.debug import confirm_signal

CONFIRMED = const(1)
CANCELLED = const(2)
DEFAULT_CONFIRM = res.load(ui.ICON_CONFIRM)
DEFAULT_CANCEL = res.load(ui.ICON_CANCEL)


class ConfirmDialog(Widget):

def __init__(self, content, confirm=DEFAULT_CONFIRM, cancel=DEFAULT_CANCEL, confirm_style=ui.BTN_CONFIRM, cancel_style=ui.BTN_CANCEL):
def __init__(self,
content,
confirm=DEFAULT_CONFIRM,
cancel=DEFAULT_CANCEL,
confirm_style=ui.BTN_CONFIRM,
cancel_style=ui.BTN_CANCEL):
self.content = content
if cancel is not None:
self.confirm = Button(
Expand All @@ -37,7 +44,10 @@ def touch(self, event, pos):
return CANCELLED

async def __iter__(self):
return await loop.wait(super().__iter__(), self.content)
if __debug__:
return await loop.wait(super().__iter__(), self.content, confirm_signal)
else:
return await loop.wait(super().__iter__(), self.content)


_STARTED = const(-1)
Expand Down Expand Up @@ -81,5 +91,8 @@ async def __iter__(self):
else:
content_loop = self.content
confirm_loop = super().__iter__() # default loop (render on touch)
result = await loop.wait(content_loop, confirm_loop)
if __debug__:
result = await loop.wait(content_loop, confirm_loop, confirm_signal)
else:
result = await loop.wait(content_loop, confirm_loop)
return result
9 changes: 9 additions & 0 deletions src/trezor/ui/mnemonic.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
from trezor.ui import display
from trezor.ui.button import BTN_CLICKED, ICON, Button

if __debug__:
from apps.debug import input_signal

MNEMONIC_KEYS = ('abc', 'def', 'ghi', 'jkl', 'mno', 'pqr', 'stu', 'vwx', 'yz')


Expand Down Expand Up @@ -145,6 +148,12 @@ def edit(self, content, button=None, index=0):
btn.disable()

async def __iter__(self):
if __debug__:
return await loop.wait(self.edit_loop(), input_signal)
else:
return await self.edit_loop()

async def edit_loop(self):
timeout = loop.sleep(1000 * 1000 * 1)
touch = loop.select(io.TOUCH)
wait_timeout = loop.wait(touch, timeout)
Expand Down
Loading

0 comments on commit 01bc12e

Please sign in to comment.