diff --git a/core/src/apps/binance/get_address.py b/core/src/apps/binance/get_address.py index a9797ab7e..f7f54be20 100644 --- a/core/src/apps/binance/get_address.py +++ b/core/src/apps/binance/get_address.py @@ -3,7 +3,6 @@ from trezor.ui.layouts import show_address from apps.common import paths from apps.common.keychain import Keychain, auto_keychain -from apps.common.layout import address_n_to_str from .helpers import address_from_public_key @@ -18,7 +17,7 @@ async def get_address(ctx, msg: BinanceGetAddress, keychain: Keychain): pubkey = node.public_key() address = address_from_public_key(pubkey, HRP) if msg.show_display: - title = address_n_to_str(msg.address_n) + title = paths.address_n_to_str(msg.address_n) await show_address(ctx, address=address, address_qr=address, title=title) return BinanceAddress(address=address) diff --git a/core/src/apps/bitcoin/get_address.py b/core/src/apps/bitcoin/get_address.py index 50d535242..5a954c72a 100644 --- a/core/src/apps/bitcoin/get_address.py +++ b/core/src/apps/bitcoin/get_address.py @@ -3,8 +3,7 @@ from trezor.enums import InputScriptType from trezor.messages import Address from trezor.ui.layouts import show_address -from apps.common.layout import address_n_to_str -from apps.common.paths import validate_path +from apps.common.paths import address_n_to_str, validate_path from . import addresses from .keychain import validate_path_against_script_type, with_keychain diff --git a/core/src/apps/cardano/get_address.py b/core/src/apps/cardano/get_address.py index e1b8c064f..2c1bb30fc 100644 --- a/core/src/apps/cardano/get_address.py +++ b/core/src/apps/cardano/get_address.py @@ -3,7 +3,6 @@ from trezor.messages import CardanoAddress from trezor.ui.layouts import show_address from apps.common import paths -from apps.common.layout import address_n_to_str from . import seed from .address import derive_human_readable_address, validate_address_parameters @@ -71,7 +70,7 @@ async def _display_address( if not protocol_magics.is_mainnet(protocol_magic): network_name = protocol_magics.to_ui_string(protocol_magic) - address_n = address_n_to_str(address_parameters.address_n) + address_n = paths.address_n_to_str(address_parameters.address_n) await show_address( ctx, address=address, diff --git a/core/src/apps/cardano/helpers/paths.py b/core/src/apps/cardano/helpers/paths.py index 453cec653..5f69854e4 100644 --- a/core/src/apps/cardano/helpers/paths.py +++ b/core/src/apps/cardano/helpers/paths.py @@ -1,7 +1,6 @@ from micropython import const -from apps.common import HARDENED -from apps.common.paths import PathSchema +from apps.common.paths import HARDENED, PathSchema SLIP44_ID = 1815 diff --git a/core/src/apps/cardano/layout.py b/core/src/apps/cardano/layout.py index e6b76b054..94f7534a5 100644 --- a/core/src/apps/cardano/layout.py +++ b/core/src/apps/cardano/layout.py @@ -8,7 +8,7 @@ from trezor.ui.layouts import ( confirm_properties, ) -from apps.common.layout import address_n_to_str +from apps.common.paths import address_n_to_str from . import seed from .address import ( diff --git a/core/src/apps/common/__init__.py b/core/src/apps/common/__init__.py index 784ea372c..e69de29bb 100644 --- a/core/src/apps/common/__init__.py +++ b/core/src/apps/common/__init__.py @@ -1,17 +0,0 @@ -from micropython import const - -from trezor import workflow -from trezor.enums import ButtonRequestType -from trezor.messages import ButtonAck, ButtonRequest - -if False: - from trezor import wire - -HARDENED = const(0x8000_0000) - - -async def button_request( - ctx: wire.GenericContext, code: ButtonRequestType = ButtonRequestType.Other -) -> None: - workflow.close_others() - await ctx.call(ButtonRequest(code=code), ButtonAck) diff --git a/core/src/apps/common/confirm.py b/core/src/apps/common/confirm.py index 987f762d6..f1b7d2fb2 100644 --- a/core/src/apps/common/confirm.py +++ b/core/src/apps/common/confirm.py @@ -7,8 +7,7 @@ from trezor.ui.components.tt.confirm import ( HoldToConfirm, InfoConfirm, ) - -from . import button_request +from trezor.ui.layouts.common import button_request if __debug__: from trezor.ui.components.tt.scroll import Paginated diff --git a/core/src/apps/common/layout.py b/core/src/apps/common/layout.py deleted file mode 100644 index 17f546bb7..000000000 --- a/core/src/apps/common/layout.py +++ /dev/null @@ -1,23 +0,0 @@ -from trezor.utils import chunks - -from apps.common import HARDENED - -if False: - from typing import Iterable, Iterator - - -def split_address(address: str) -> Iterator[str]: - return chunks(address, 17) - - -def address_n_to_str(address_n: Iterable[int]) -> str: - def path_item(i: int) -> str: - if i & HARDENED: - return str(i ^ HARDENED) + "'" - else: - return str(i) - - if not address_n: - return "m" - - return "m/" + "/".join([path_item(i) for i in address_n]) diff --git a/core/src/apps/common/mnemonic.py b/core/src/apps/common/mnemonic.py index 1ab467827..dcd570b07 100644 --- a/core/src/apps/common/mnemonic.py +++ b/core/src/apps/common/mnemonic.py @@ -3,7 +3,7 @@ from trezor import ui, utils, workflow from trezor.enums import BackupType -def get() -> tuple[bytes | None, int]: +def get() -> tuple[bytes | None, BackupType]: return get_secret(), get_type() @@ -54,13 +54,12 @@ def get_seed(passphrase: str = "", progress_bar: bool = True) -> bytes: def _start_progress() -> None: - from trezor.ui.components.tt.text import Text + from trezor.ui.layouts import draw_simple_text # Because we are drawing to the screen manually, without a layout, we # should make sure that no other layout is running. workflow.close_others() - t = Text("Please wait", ui.ICON_CONFIG) - ui.draw_simple(t) + draw_simple_text("Please wait") def _render_progress(progress: int, total: int) -> None: diff --git a/core/src/apps/common/passphrase.py b/core/src/apps/common/passphrase.py index 27faa698c..1accd899c 100644 --- a/core/src/apps/common/passphrase.py +++ b/core/src/apps/common/passphrase.py @@ -2,9 +2,6 @@ from micropython import const import storage.device from trezor import wire, workflow -from trezor.enums import ButtonRequestType - -from . import button_request _MAX_PASSPHRASE_LEN = const(50) @@ -23,7 +20,9 @@ async def get(ctx: wire.Context) -> str: async def _request_from_user(ctx: wire.Context) -> str: workflow.close_others() # request exclusive UI access if storage.device.get_passphrase_always_on_device(): - passphrase = await _request_on_device(ctx) + from trezor.ui.layouts import request_passphrase_on_device + + passphrase = await request_passphrase_on_device(ctx, _MAX_PASSPHRASE_LEN) else: passphrase = await _request_on_host(ctx) if len(passphrase.encode()) > _MAX_PASSPHRASE_LEN: @@ -36,19 +35,17 @@ async def _request_from_user(ctx: wire.Context) -> str: async def _request_on_host(ctx: wire.Context) -> str: from trezor.messages import PassphraseAck, PassphraseRequest - from trezor.ui import ICON_CONFIG - from trezor.ui.components.tt.text import Text - - from .confirm import require_confirm _entry_dialog() request = PassphraseRequest() ack = await ctx.call(request, PassphraseAck) if ack.on_device: + from trezor.ui.layouts import request_passphrase_on_device + if ack.passphrase is not None: raise wire.DataError("Passphrase provided when it should not be") - return await _request_on_device(ctx) + return await request_passphrase_on_device(ctx, _MAX_PASSPHRASE_LEN) if ack.passphrase is None: raise wire.DataError( @@ -57,41 +54,33 @@ async def _request_on_host(ctx: wire.Context) -> str: # non-empty passphrase if ack.passphrase: - text = Text("Hidden wallet", ICON_CONFIG) - text.normal("Access hidden wallet?") - text.br() - text.normal("Next screen will show") - text.normal("the passphrase!") - await require_confirm(ctx, text, ButtonRequestType.Other) - - text = Text("Hidden wallet", ICON_CONFIG, break_words=True) - text.normal("Use this passphrase?") - text.br() - text.mono(ack.passphrase) - await require_confirm(ctx, text, ButtonRequestType.Other) - - return ack.passphrase - - -async def _request_on_device(ctx: wire.Context) -> str: - from trezor.ui.components.tt.passphrase import CANCELLED, PassphraseKeyboard - - await button_request(ctx, code=ButtonRequestType.PassphraseEntry) - - keyboard = PassphraseKeyboard("Enter passphrase", _MAX_PASSPHRASE_LEN) - passphrase = await ctx.wait(keyboard) - if passphrase is CANCELLED: - raise wire.ActionCancelled("Passphrase entry cancelled") + from trezor import ui + from trezor.ui.layouts import confirm_action, confirm_blob + + await confirm_action( + ctx, + "passphrase_host1", + title="Hidden wallet", + description="Access hidden wallet?\n\nNext screen will show\nthe passphrase!", + icon=ui.ICON_CONFIG, + ) - assert isinstance(passphrase, str) + await confirm_blob( + ctx, + "passphrase_host2", + title="Hidden wallet", + description="Use this passphrase?\n", + data=ack.passphrase, + icon=ui.ICON_CONFIG, + icon_color=ui.ORANGE_ICON, + ) - return passphrase + return ack.passphrase def _entry_dialog() -> None: - from trezor.ui import ICON_CONFIG, draw_simple - from trezor.ui.components.tt.text import Text + from trezor.ui.layouts import draw_simple_text - text = Text("Passphrase entry", ICON_CONFIG) - text.normal("Please type your", "passphrase on the", "connected host.") - draw_simple(text) + draw_simple_text( + "Passphrase entry", "Please type your\npassphrase on the\nconnected host." + ) diff --git a/core/src/apps/common/paths.py b/core/src/apps/common/paths.py index b9bd3a0da..93fc1d3a4 100644 --- a/core/src/apps/common/paths.py +++ b/core/src/apps/common/paths.py @@ -1,4 +1,6 @@ -from . import HARDENED +from micropython import const + +HARDENED = const(0x8000_0000) if False: from typing import ( @@ -325,7 +327,6 @@ async def validate_path( async def show_path_warning(ctx: wire.Context, path: Bip32Path) -> None: from trezor.ui.layouts import confirm_path_warning - from .layout import address_n_to_str await confirm_path_warning(ctx, address_n_to_str(path)) @@ -336,3 +337,16 @@ def is_hardened(i: int) -> bool: def path_is_hardened(address_n: Bip32Path) -> bool: return all(is_hardened(n) for n in address_n) + + +def address_n_to_str(address_n: Iterable[int]) -> str: + def path_item(i: int) -> str: + if i & HARDENED: + return str(i ^ HARDENED) + "'" + else: + return str(i) + + if not address_n: + return "m" + + return "m/" + "/".join(path_item(i) for i in address_n) diff --git a/core/src/apps/common/request_pin.py b/core/src/apps/common/request_pin.py index e01b8723d..af11bcb62 100644 --- a/core/src/apps/common/request_pin.py +++ b/core/src/apps/common/request_pin.py @@ -2,11 +2,8 @@ import utime import storage.cache import storage.sd_salt -from trezor import config, ui, wire -from trezor.enums import ButtonRequestType -from trezor.ui.popup import Popup +from trezor import config, wire -from . import button_request from .sdcard import SdCardUnavailable, request_sd_salt if False: @@ -24,25 +21,9 @@ async def request_pin( attempts_remaining: int | None = None, allow_cancel: bool = True, ) -> str: - from trezor.ui.components.tt.pin import CANCELLED, PinDialog + from trezor.ui.layouts import request_pin_on_device - await button_request(ctx, code=ButtonRequestType.PinEntry) - - if attempts_remaining is None: - subprompt = None - elif attempts_remaining == 1: - subprompt = "This is your last attempt" - else: - subprompt = "%s attempts remaining" % attempts_remaining - - dialog = PinDialog(prompt, subprompt, allow_cancel) - - while True: - pin = await ctx.wait(dialog) - if pin is CANCELLED: - raise wire.PinCancelled - assert isinstance(pin, str) - return pin + return await request_pin_on_device(ctx, prompt, attempts_remaining, allow_cancel) async def request_pin_confirm(ctx: wire.Context, *args: Any, **kwargs: Any) -> str: @@ -55,14 +36,12 @@ async def request_pin_confirm(ctx: wire.Context, *args: Any, **kwargs: Any) -> s async def pin_mismatch() -> None: - from trezor.ui.components.tt.text import Text + from trezor.ui.layouts import show_popup - text = Text("PIN mismatch", ui.ICON_WRONG, ui.RED) - text.normal("The PINs you entered", "do not match.") - text.normal("") - text.normal("Please try again.") - popup = Popup(text, 3000) # show for 3 seconds - await popup + await show_popup( + title="PIN mismatch", + description="The PINs you entered\ndo not match.\n\nPlease try again.", + ) async def request_pin_and_sd_salt( @@ -109,7 +88,11 @@ async def verify_user_pin( return if config.has_pin(): - pin = await request_pin(ctx, prompt, config.get_pin_rem(), allow_cancel) + from trezor.ui.layouts import request_pin_on_device + + pin = await request_pin_on_device( + ctx, prompt, config.get_pin_rem(), allow_cancel + ) config.ensure_not_wipe_code(pin) else: pin = "" @@ -125,7 +108,7 @@ async def verify_user_pin( raise RuntimeError while retry: - pin = await request_pin( + pin = await request_pin_on_device( ctx, "Wrong PIN, enter again", config.get_pin_rem(), allow_cancel ) if config.unlock(pin, salt): diff --git a/core/src/apps/ethereum/get_address.py b/core/src/apps/ethereum/get_address.py index 57a8cb860..b03dd36b9 100644 --- a/core/src/apps/ethereum/get_address.py +++ b/core/src/apps/ethereum/get_address.py @@ -4,7 +4,6 @@ from trezor.messages import EthereumAddress from trezor.ui.layouts import show_address from apps.common import paths -from apps.common.layout import address_n_to_str from . import networks from .address import address_from_bytes @@ -27,7 +26,7 @@ async def get_address(ctx, msg, keychain): address = address_from_bytes(address_bytes, network) if msg.show_display: - title = address_n_to_str(msg.address_n) + title = paths.address_n_to_str(msg.address_n) await show_address(ctx, address=address, address_qr=address, title=title) return EthereumAddress(address=address) diff --git a/core/src/apps/ethereum/keychain.py b/core/src/apps/ethereum/keychain.py index 545419848..5693fa119 100644 --- a/core/src/apps/ethereum/keychain.py +++ b/core/src/apps/ethereum/keychain.py @@ -1,6 +1,6 @@ from trezor import wire -from apps.common import HARDENED, paths +from apps.common import paths from apps.common.keychain import get_keychain from . import CURVE, networks @@ -37,10 +37,10 @@ def _schemas_from_address_n( if slip44_hardened not in networks.all_slip44_ids_hardened(): return () - if not slip44_hardened & HARDENED: + if not slip44_hardened & paths.HARDENED: return () - slip44_id = slip44_hardened - HARDENED + slip44_id = slip44_hardened - paths.HARDENED schemas = [paths.PathSchema.parse(pattern, slip44_id) for pattern in patterns] return [s.copy() for s in schemas] diff --git a/core/src/apps/ethereum/networks.py b/core/src/apps/ethereum/networks.py index 0bbecc193..8bc959e45 100644 --- a/core/src/apps/ethereum/networks.py +++ b/core/src/apps/ethereum/networks.py @@ -3,7 +3,7 @@ from micropython import const -from apps.common import HARDENED +from apps.common.paths import HARDENED SLIP44_WANCHAIN = const(5718350) SLIP44_ETHEREUM = const(60) diff --git a/core/src/apps/ethereum/networks.py.mako b/core/src/apps/ethereum/networks.py.mako index c78632083..78da0cbdd 100644 --- a/core/src/apps/ethereum/networks.py.mako +++ b/core/src/apps/ethereum/networks.py.mako @@ -3,7 +3,7 @@ from micropython import const -from apps.common import HARDENED +from apps.common.paths import HARDENED SLIP44_WANCHAIN = const(5718350) SLIP44_ETHEREUM = const(60) diff --git a/core/src/apps/lisk/get_address.py b/core/src/apps/lisk/get_address.py index 7526b654f..abe000d2f 100644 --- a/core/src/apps/lisk/get_address.py +++ b/core/src/apps/lisk/get_address.py @@ -3,7 +3,6 @@ from trezor.ui.layouts import show_address from apps.common import paths from apps.common.keychain import auto_keychain -from apps.common.layout import address_n_to_str from .helpers import get_address_from_public_key @@ -18,7 +17,7 @@ async def get_address(ctx, msg, keychain): address = get_address_from_public_key(pubkey) if msg.show_display: - title = address_n_to_str(msg.address_n) + title = paths.address_n_to_str(msg.address_n) await show_address(ctx, address=address, address_qr=address, title=title) return LiskAddress(address=address) diff --git a/core/src/apps/management/recovery_device/layout.py b/core/src/apps/management/recovery_device/layout.py index 06f14909c..f0a097b52 100644 --- a/core/src/apps/management/recovery_device/layout.py +++ b/core/src/apps/management/recovery_device/layout.py @@ -6,8 +6,8 @@ from trezor.ui.components.tt.scroll import Paginated from trezor.ui.components.tt.text import Text from trezor.ui.components.tt.word_select import WordSelector from trezor.ui.layouts import confirm_action, show_success, show_warning +from trezor.ui.layouts.common import button_request -from apps.common import button_request from apps.common.confirm import confirm, info_confirm, require_confirm from .. import backup_types diff --git a/core/src/apps/misc/get_ecdh_session_key.py b/core/src/apps/misc/get_ecdh_session_key.py index be6dbfb61..7bcef6ea9 100644 --- a/core/src/apps/misc/get_ecdh_session_key.py +++ b/core/src/apps/misc/get_ecdh_session_key.py @@ -5,9 +5,8 @@ from trezor.crypto.hashlib import sha256 from trezor.messages import ECDHSessionKey from trezor.ui.layouts import confirm_address -from apps.common import HARDENED from apps.common.keychain import get_keychain -from apps.common.paths import AlwaysMatchingSchema +from apps.common.paths import HARDENED, AlwaysMatchingSchema from .sign_identity import serialize_identity, serialize_identity_without_proto diff --git a/core/src/apps/misc/sign_identity.py b/core/src/apps/misc/sign_identity.py index c4c2b4017..279bfdf6b 100644 --- a/core/src/apps/misc/sign_identity.py +++ b/core/src/apps/misc/sign_identity.py @@ -5,9 +5,9 @@ from trezor.crypto.hashlib import sha256 from trezor.messages import SignedIdentity from trezor.ui.layouts import confirm_sign_identity -from apps.common import HARDENED, coininfo +from apps.common import coininfo from apps.common.keychain import get_keychain -from apps.common.paths import AlwaysMatchingSchema +from apps.common.paths import HARDENED, AlwaysMatchingSchema if False: from trezor.messages import IdentityType, SignIdentity diff --git a/core/src/apps/monero/get_address.py b/core/src/apps/monero/get_address.py index 70cbe35c9..195b51aaf 100644 --- a/core/src/apps/monero/get_address.py +++ b/core/src/apps/monero/get_address.py @@ -3,7 +3,6 @@ from trezor.ui.layouts import show_address from apps.common import paths from apps.common.keychain import auto_keychain -from apps.common.layout import address_n_to_str from apps.monero import misc from apps.monero.xmr import addresses, crypto, monero from apps.monero.xmr.networks import net_version @@ -41,7 +40,7 @@ async def get_address(ctx, msg, keychain): ) if msg.show_display: - title = address_n_to_str(msg.address_n) + title = paths.address_n_to_str(msg.address_n) await show_address( ctx, address=addr.decode(), diff --git a/core/src/apps/nem/get_address.py b/core/src/apps/nem/get_address.py index fccdb516d..0fc884825 100644 --- a/core/src/apps/nem/get_address.py +++ b/core/src/apps/nem/get_address.py @@ -2,8 +2,7 @@ from trezor.messages import NEMAddress from trezor.ui.layouts import show_address from apps.common.keychain import with_slip44_keychain -from apps.common.layout import address_n_to_str -from apps.common.paths import validate_path +from apps.common.paths import address_n_to_str, validate_path from . import CURVE, PATTERNS, SLIP44_ID from .helpers import check_path, get_network_str diff --git a/core/src/apps/nem/helpers.py b/core/src/apps/nem/helpers.py index fe55cf5ca..ef563f10d 100644 --- a/core/src/apps/nem/helpers.py +++ b/core/src/apps/nem/helpers.py @@ -1,6 +1,6 @@ from micropython import const -from apps.common import HARDENED, paths +from apps.common import paths from . import SLIP44_ID @@ -45,7 +45,7 @@ def check_path(path: paths.Bip32Path, network: int) -> bool: if len(path) < 2: return False - coin_type = path[1] - HARDENED + coin_type = path[1] - paths.HARDENED if network == NEM_NETWORK_TESTNET: return coin_type == 1 diff --git a/core/src/apps/ripple/get_address.py b/core/src/apps/ripple/get_address.py index 967189964..35898704e 100644 --- a/core/src/apps/ripple/get_address.py +++ b/core/src/apps/ripple/get_address.py @@ -3,7 +3,6 @@ from trezor.ui.layouts import show_address from apps.common import paths from apps.common.keychain import auto_keychain -from apps.common.layout import address_n_to_str from .helpers import address_from_public_key @@ -17,7 +16,7 @@ async def get_address(ctx, msg: RippleGetAddress, keychain): address = address_from_public_key(pubkey) if msg.show_display: - title = address_n_to_str(msg.address_n) + title = paths.address_n_to_str(msg.address_n) await show_address(ctx, address=address, address_qr=address, title=title) return RippleAddress(address=address) diff --git a/core/src/apps/stellar/get_address.py b/core/src/apps/stellar/get_address.py index 537303bb5..d40cb9e0d 100644 --- a/core/src/apps/stellar/get_address.py +++ b/core/src/apps/stellar/get_address.py @@ -3,7 +3,6 @@ from trezor.ui.layouts import show_address from apps.common import paths, seed from apps.common.keychain import auto_keychain -from apps.common.layout import address_n_to_str from . import helpers @@ -17,7 +16,7 @@ async def get_address(ctx, msg: StellarGetAddress, keychain): address = helpers.address_from_public_key(pubkey) if msg.show_display: - title = address_n_to_str(msg.address_n) + title = paths.address_n_to_str(msg.address_n) await show_address( ctx, address=address, address_qr=address.upper(), title=title ) diff --git a/core/src/apps/tezos/get_address.py b/core/src/apps/tezos/get_address.py index ac676c1e6..4b16cab54 100644 --- a/core/src/apps/tezos/get_address.py +++ b/core/src/apps/tezos/get_address.py @@ -4,7 +4,6 @@ from trezor.ui.layouts import show_address from apps.common import paths, seed from apps.common.keychain import with_slip44_keychain -from apps.common.layout import address_n_to_str from . import CURVE, PATTERNS, SLIP44_ID, helpers @@ -22,7 +21,7 @@ async def get_address(ctx, msg, keychain): ) if msg.show_display: - title = address_n_to_str(msg.address_n) + title = paths.address_n_to_str(msg.address_n) await show_address(ctx, address=address, address_qr=address, title=title) return TezosAddress(address=address) diff --git a/core/src/apps/webauthn/credential.py b/core/src/apps/webauthn/credential.py index 6c91b8ca0..fb3d5f7f7 100644 --- a/core/src/apps/webauthn/credential.py +++ b/core/src/apps/webauthn/credential.py @@ -7,7 +7,8 @@ from trezor import log, utils from trezor.crypto import bip32, chacha20poly1305, der, hashlib, hmac, random from trezor.crypto.curve import ed25519, nist256p1 -from apps.common import HARDENED, cbor, seed +from apps.common import cbor, seed +from apps.common.paths import HARDENED from . import common diff --git a/core/src/trezor/ui/layouts/common.py b/core/src/trezor/ui/layouts/common.py index e5dd48352..edbd8095e 100644 --- a/core/src/trezor/ui/layouts/common.py +++ b/core/src/trezor/ui/layouts/common.py @@ -14,17 +14,25 @@ if __debug__: from ..components.tt.scroll import Paginated +async def button_request( + ctx: wire.GenericContext, + br_type: str, + code: ButtonRequestType = ButtonRequestType.Other, +) -> None: + log.debug(__name__, "ButtonRequest.type={}".format(br_type)) + workflow.close_others() + await ctx.call(ButtonRequest(code=code), ButtonAck) + + async def interact( ctx: wire.GenericContext, layout: LayoutType, - brtype: str, - brcode: ButtonRequestType = ButtonRequestType.Other, + br_type: str, + br_code: ButtonRequestType = ButtonRequestType.Other, ) -> Any: - log.debug(__name__, "ButtonRequest.type={}".format(brtype)) if layout.__class__.__name__ == "Paginated": assert isinstance(layout, Paginated) - return await layout.interact(ctx, code=brcode) + return await layout.interact(ctx, code=br_code) else: - workflow.close_others() - await ctx.call(ButtonRequest(code=brcode), ButtonAck) + await button_request(ctx, br_type, br_code) return await ctx.wait(layout) diff --git a/core/src/trezor/ui/layouts/tt.py b/core/src/trezor/ui/layouts/tt.py index cdfb8f815..7adf96bb4 100644 --- a/core/src/trezor/ui/layouts/tt.py +++ b/core/src/trezor/ui/layouts/tt.py @@ -12,6 +12,7 @@ from trezor.utils import chunks, chunks_intersperse from ..components.common import break_path_to_lines from ..components.common.confirm import is_confirmed, raise_if_cancelled from ..components.common.webauthn import ConfirmInfo +from ..components.tt import passphrase, pin from ..components.tt.button import ButtonCancel, ButtonDefault from ..components.tt.confirm import Confirm, ConfirmPageable, HoldToConfirm, Pageable from ..components.tt.scroll import ( @@ -30,7 +31,7 @@ from ..constants.tt import ( QR_Y, TEXT_MAX_LINES, ) -from .common import interact +from .common import button_request, interact if False: from typing import ( @@ -78,6 +79,9 @@ __all__ = ( "show_popup", "confirm_webauthn", "confirm_webauthn_reset", + "draw_simple_text", + "request_passphrase_on_device", + "request_pin_on_device", ) @@ -1055,3 +1059,47 @@ async def confirm_webauthn_reset() -> bool: text.normal("Do you really want to") text.bold("erase all credentials?") return is_confirmed(await Confirm(text)) + + +def draw_simple_text(title: str, description: str = "") -> None: + text = Text(title, ui.ICON_CONFIG, new_lines=False) + text.normal(description) + ui.draw_simple(text) + + +async def request_passphrase_on_device(ctx: wire.GenericContext, max_len: int) -> str: + await button_request( + ctx, "passphrase_device", code=ButtonRequestType.PassphraseEntry + ) + + keyboard = passphrase.PassphraseKeyboard("Enter passphrase", max_len) + result = await ctx.wait(keyboard) + if result is passphrase.CANCELLED: + raise wire.ActionCancelled("Passphrase entry cancelled") + + assert isinstance(result, str) + return result + + +async def request_pin_on_device( + ctx: wire.GenericContext, + prompt: str, + attempts_remaining: int, + allow_cancel: bool, +) -> str: + await button_request(ctx, "pin_device", code=ButtonRequestType.PinEntry) + + if attempts_remaining is None: + subprompt = None + elif attempts_remaining == 1: + subprompt = "This is your last attempt" + else: + subprompt = "%s attempts remaining" % attempts_remaining + + dialog = pin.PinDialog(prompt, subprompt, allow_cancel) + while True: + result = await ctx.wait(dialog) + if result is pin.CANCELLED: + raise wire.PinCancelled + assert isinstance(result, str) + return result diff --git a/core/tests/common.py b/core/tests/common.py index d9c38ee02..44512d3eb 100644 --- a/core/tests/common.py +++ b/core/tests/common.py @@ -7,7 +7,7 @@ from ubinascii import hexlify, unhexlify # noqa: F401 import unittest # noqa: F401 from trezor import utils # noqa: F401 -from apps.common import HARDENED +from apps.common.paths import HARDENED def H_(x: int) -> int: diff --git a/core/tests/test_apps.cardano.address.py b/core/tests/test_apps.cardano.address.py index 574c692d2..30410df43 100644 --- a/core/tests/test_apps.cardano.address.py +++ b/core/tests/test_apps.cardano.address.py @@ -5,7 +5,8 @@ from trezor.enums import CardanoAddressType from trezor.messages import CardanoAddressParametersType from trezor.messages import CardanoBlockchainPointerType -from apps.common import HARDENED, seed +from apps.common import seed +from apps.common.paths import HARDENED if not utils.BITCOIN_ONLY: from apps.cardano.address import derive_human_readable_address, validate_address_parameters diff --git a/core/tests/test_apps.cardano.keychain.py b/core/tests/test_apps.cardano.keychain.py index f7d41ee5d..5e2645f3f 100644 --- a/core/tests/test_apps.cardano.keychain.py +++ b/core/tests/test_apps.cardano.keychain.py @@ -2,7 +2,8 @@ from common import * from trezor import wire from trezor.crypto import bip32, slip39 -from apps.common import HARDENED, seed +from apps.common import seed +from apps.common.paths import HARDENED if not utils.BITCOIN_ONLY: from apps.cardano.seed import Keychain diff --git a/core/tests/test_apps.cardano.sign_tx.py b/core/tests/test_apps.cardano.sign_tx.py index 756745992..5f8c842c5 100644 --- a/core/tests/test_apps.cardano.sign_tx.py +++ b/core/tests/test_apps.cardano.sign_tx.py @@ -1,5 +1,5 @@ from common import * -from apps.common import HARDENED +from apps.common.paths import HARDENED from trezor.messages import CardanoTxInputType if not utils.BITCOIN_ONLY: diff --git a/core/tests/test_apps.cardano.staking_use_cases.py b/core/tests/test_apps.cardano.staking_use_cases.py index ac3304324..50b7c3799 100644 --- a/core/tests/test_apps.cardano.staking_use_cases.py +++ b/core/tests/test_apps.cardano.staking_use_cases.py @@ -2,7 +2,7 @@ from ubinascii import unhexlify from common import * -from apps.common import HARDENED +from apps.common.paths import HARDENED from trezor.crypto import bip32 from trezor.enums import CardanoAddressType from trezor.messages import CardanoAddressParametersType diff --git a/core/tests/test_apps.common.paths.py b/core/tests/test_apps.common.paths.py index 4b8401cc3..dde5149dd 100644 --- a/core/tests/test_apps.common.paths.py +++ b/core/tests/test_apps.common.paths.py @@ -1,6 +1,5 @@ from common import * from trezor.utils import ensure -from apps.common.layout import address_n_to_str from apps.common.paths import * diff --git a/core/tests/test_apps.nem.address.py b/core/tests/test_apps.nem.address.py index 029205608..d8ede1531 100644 --- a/core/tests/test_apps.nem.address.py +++ b/core/tests/test_apps.nem.address.py @@ -1,5 +1,5 @@ from common import * -from apps.common import HARDENED +from apps.common.paths import HARDENED if not utils.BITCOIN_ONLY: from trezor.crypto import nem diff --git a/tests/ui_tests/fixtures.json b/tests/ui_tests/fixtures.json index 9d607dc5f..e108ac9c5 100644 --- a/tests/ui_tests/fixtures.json +++ b/tests/ui_tests/fixtures.json @@ -347,7 +347,7 @@ "test_msg_loaddevice.py-test_load_device_2": "dc13c8486d8a59c5062e19139d8b3cea4ece1a3bc93592be7dc226f83ba54477", "test_msg_loaddevice.py-test_load_device_slip39_advanced": "1c6db0d592b1d22b3c9fce3ddab8a9fd138f11d83e5d4e64431a02bf4ffed605", "test_msg_loaddevice.py-test_load_device_slip39_basic": "1c6db0d592b1d22b3c9fce3ddab8a9fd138f11d83e5d4e64431a02bf4ffed605", -"test_msg_loaddevice.py-test_load_device_utf": "ad7c162c76a13a161166aba78c461ad5525a9a5da846e8d99854248d521e6979", +"test_msg_loaddevice.py-test_load_device_utf": "b51beeb07c60e8c803b5bf81e4470db785dc46006374b15df21a7810d659b2b6", "test_msg_monero_getaddress.py-test_monero_getaddress": "0ba9bba02378d62c8f9f76aac5181ab94b25dcbed0f58d006c1f7d4eb1ecd03e", "test_msg_monero_getwatchkey.py-test_monero_getwatchkey": "d77fa4d4322e145c41f1ce07526ff59f8b58d8854aeffaa5266e14cd572350e7", "test_msg_nem_getaddress.py-test_nem_getaddress": "d3ac0e389d139c79abeff96cd05b9c9f94c3372086096d5eb85759ab3a37b0b8", @@ -714,7 +714,7 @@ "test_session_id_and_passphrase.py::test_multiple_sessions": "5a80508a71a9ef64f94762b07636f90e464832f0f4a3102af8fa1a8c69e94586", "test_session_id_and_passphrase.py::test_passphrase_ack_mismatch": "612dad8ab8762162a186ec9279d7de0bdfc589c52b4e4f4eba0545a00f21c3f0", "test_session_id_and_passphrase.py::test_passphrase_always_on_device": "524d90c68c6f091c2145f571a884a8a168e8af101513966f5e0fbb3859035129", -"test_session_id_and_passphrase.py::test_passphrase_length": "a8858d89288f2160148860fee8938d261c4960732db3b2c874b9899395b9ddc6", +"test_session_id_and_passphrase.py::test_passphrase_length": "6c9063e65e113d7c67624392b1bedae600ae02d4576375a11b399b3c3dd15987", "test_session_id_and_passphrase.py::test_passphrase_missing": "3a92115b6bfb2d53f2445a67c9c5df6b6b5ff97769de98e3fac9e1bf424c5669", "test_session_id_and_passphrase.py::test_passphrase_on_device": "c9ca2c9cf6dd416dad4de311266690ec2266b551d74f9d3619301305b3dbe81e", "test_session_id_and_passphrase.py::test_session_enable_passphrase": "b27321ed372b8ade7c4941a80f1f945851046b039a1b43c43a6953106bd1619e",