1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-12-18 20:38:10 +00:00

feat(tests): update, refactor and extend click tests

This commit is contained in:
grdddj 2023-05-04 14:16:40 +02:00 committed by Martin Milata
parent 87c7e33198
commit 00b83d1dca
11 changed files with 989 additions and 351 deletions

View File

@ -0,0 +1,50 @@
from __future__ import annotations
from enum import Enum
from typing import TYPE_CHECKING
from .. import buttons
if TYPE_CHECKING:
from trezorlib.debuglink import DebugLink, LayoutContent
# Passphrases and addresses for both models
class CommonPass:
RANDOM_25 = "Y@14lw%p)JN@f54MYvys@zj'g"
RANDOM_25_ADDRESS = "mnkoxeaMzLgfCxUdDSZWrGactyJJerQVW6"
SHORT = "abc123ABC_<>"
SHORT_ADDRESS = "mtHHfh6uHtJiACwp7kzJZ97yueT6sEdQiG"
WITH_SPACE = "abc 123"
WITH_SPACE_ADDRESS = "mvqzZUb9NaUc62Buk9WCP4L7hunsXFyamT"
EMPTY_ADDRESS = "mvbu1Gdy8SUjTenqerxUaZyYjmveZvt33q"
class PassphraseCategory(Enum):
MENU = "MENU"
DIGITS = "123"
LOWERCASE = "abc"
UPPERCASE = "ABC"
SPECIAL = "#$!"
def get_char_category(char: str) -> PassphraseCategory:
"""What is the category of a character"""
if char.isdigit():
return PassphraseCategory.DIGITS
if char.islower():
return PassphraseCategory.LOWERCASE
if char.isupper():
return PassphraseCategory.UPPERCASE
return PassphraseCategory.SPECIAL
def go_next(debug: "DebugLink", wait: bool = False) -> "LayoutContent" | None:
return debug.click(buttons.OK, wait=wait) # type: ignore
def go_back(debug: "DebugLink", wait: bool = False) -> "LayoutContent" | None:
return debug.click(buttons.CANCEL, wait=wait) # type: ignore

View File

@ -13,35 +13,28 @@ def enter_word(
for coords in buttons.type_word(typed_word, is_slip39=is_slip39):
debug.click(coords)
# For BIP39 - double-click on CONFIRM WORD is needed in case the word
# is not already typed as a whole
if not is_slip39 and typed_word != word:
debug.click(buttons.CONFIRM_WORD)
return debug.click(buttons.CONFIRM_WORD, wait=True)
def confirm_recovery(debug: "DebugLink", legacy_ui: bool = False) -> None:
layout = debug.wait_layout()
if legacy_ui:
assert layout.text.startswith("Recovery mode")
else:
assert layout.get_title().startswith("WALLET RECOVERY")
if not legacy_ui:
layout = debug.wait_layout()
assert layout.title().startswith("WALLET RECOVERY")
debug.click(buttons.OK, wait=True)
def select_number_of_words(
debug: "DebugLink", num_of_words: int = 20, legacy_ui: bool = False
) -> None:
layout = debug.read_layout()
# select number of words
assert "Select number of words" in layout.get_content()
if not legacy_ui:
assert "select the number of words" in debug.read_layout().text_content()
layout = debug.click(buttons.OK, wait=True)
if legacy_ui:
assert layout.text == "WordSelector"
assert layout.json_str == "WordSelector"
else:
# Two title options
assert layout.get_title() in ("SEED CHECK", "WALLET RECOVERY")
assert layout.title() in ("SEED CHECK", "WALLET RECOVERY")
# click the number
word_option_offset = 6
@ -51,7 +44,12 @@ def select_number_of_words(
) # raises if num of words is invalid
coords = buttons.grid34(index % 3, index // 3)
layout = debug.click(coords, wait=True)
assert "Enter any share" in layout.get_content()
if not legacy_ui:
if num_of_words in (20, 33):
assert "Enter any share" in layout.text_content()
else:
assert "enter your recovery seed" in layout.text_content()
def enter_share(
@ -60,9 +58,9 @@ def enter_share(
layout = debug.click(buttons.OK, wait=True)
if legacy_ui:
assert layout.text == "Slip39Keyboard"
assert layout.json_str == "Slip39Keyboard"
else:
assert layout.text == "< MnemonicKeyboard >"
assert layout.main_component() == "MnemonicKeyboard"
for word in share.split(" "):
layout = enter_word(debug, word, is_slip39=True)
@ -75,14 +73,26 @@ def enter_shares(debug: "DebugLink", shares: list[str]) -> None:
expected_text = "Enter any share"
remaining = len(shares)
for share in shares:
assert expected_text in layout.get_content()
assert expected_text in layout.text_content()
layout = enter_share(debug, share)
remaining -= 1
expected_text = f"{remaining} more share"
assert "You have successfully recovered your wallet" in layout.get_content()
assert "You have finished recovering your wallet" in layout.text_content()
def enter_seed(debug: "DebugLink", seed_words: list[str]) -> None:
assert "enter" in debug.read_layout().text_content()
layout = debug.click(buttons.OK, wait=True)
assert layout.main_component() == "MnemonicKeyboard"
for word in seed_words:
layout = enter_word(debug, word, is_slip39=False)
assert "You have finished recovering your wallet" in layout.text_content()
def finalize(debug: "DebugLink") -> None:
layout = debug.click(buttons.OK, wait=True)
assert layout.text.startswith("< Homescreen ")
assert layout.main_component() == "Homescreen"

View File

@ -1,6 +1,6 @@
from typing import TYPE_CHECKING
from shamir_mnemonic import shamir
from shamir_mnemonic import shamir # type: ignore
from trezorlib import messages
@ -10,61 +10,83 @@ if TYPE_CHECKING:
from trezorlib.debuglink import DebugLink
def confirm_wait(debug: "DebugLink", title: str) -> None:
layout = debug.wait_layout()
assert title.upper() in layout.get_title()
def confirm_new_wallet(debug: "DebugLink") -> None:
assert debug.wait_layout().title().startswith("WALLET CREATION")
debug.click(buttons.OK, wait=True)
def confirm_read(debug: "DebugLink", title: str) -> None:
def confirm_read(debug: "DebugLink", title: str, hold: bool = False) -> None:
layout = debug.read_layout()
if title == "Caution":
assert "OK, I UNDERSTAND" in layout.text
# TODO: could look into button texts
assert "OK, I UNDERSTAND" in layout.json_str
elif title == "Success":
# TODO: improve this
assert any(
text in layout.get_content() for text in ("success", "finished", "done")
text in layout.text_content()
for text in (
"success",
"finished",
"done",
"has been created",
"Keep it safe",
)
)
elif title == "Checklist":
assert "number of shares" in layout.text_content().lower()
else:
assert title.upper() in layout.get_title()
assert title.upper() in layout.title()
debug.click(buttons.OK, wait=True)
def set_selection(debug: "DebugLink", button: tuple[int, int], diff: int) -> None:
layout = debug.read_layout()
assert "NumberInputDialog" in layout.text
assert "NumberInputDialog" in debug.read_layout().all_components()
for _ in range(diff):
debug.click(button, wait=False)
debug.click(button)
debug.click(buttons.OK, wait=True)
def read_words(debug: "DebugLink", is_advanced: bool = False) -> list[str]:
def read_words(
debug: "DebugLink", backup_type: messages.BackupType, do_htc: bool = True
) -> list[str]:
words: list[str] = []
layout = debug.read_layout()
if is_advanced:
assert layout.get_title().startswith("GROUP")
if backup_type == messages.BackupType.Slip39_Advanced:
assert layout.title().startswith("GROUP")
elif backup_type == messages.BackupType.Slip39_Basic:
assert layout.title().startswith("RECOVERY SHARE #")
else:
assert layout.get_title().startswith("RECOVERY SHARE #")
assert layout.title() == "RECOVERY SEED"
# Swiping through all the page and loading the words
for _ in range(layout.get_page_count() - 1):
words.extend(layout.get_seed_words())
for _ in range(layout.page_count() - 1):
words.extend(layout.seed_words())
layout = debug.input(swipe=messages.DebugSwipeDirection.UP, wait=True)
words.extend(layout.get_seed_words())
assert layout is not None
words.extend(layout.seed_words())
debug.press_yes()
# There is hold-to-confirm button
if do_htc:
debug.click_hold(buttons.OK, hold_ms=1500)
else:
# It would take a very long time to test 16-of-16 with doing 1500 ms HTC after
# each word set
debug.press_yes()
return words
def confirm_words(debug: "DebugLink", words: list[str]) -> None:
layout = debug.wait_layout()
assert "Select word" in layout.text
assert "Select word" in layout.text_content()
for _ in range(3):
# "Select word 3 of 20"
# ^
word_pos = int(layout.get_content().split()[2])
word_pos = int(layout.text_content().split()[2])
# Unifying both the buttons and words to lowercase
btn_texts = [text.lower() for text in layout.get_button_texts()]
btn_texts = [text.lower() for text in layout.button_contents()]
wanted_word = words[word_pos - 1].lower()
button_pos = btn_texts.index(wanted_word)
layout = debug.click(buttons.RESET_WORD_CHECK[button_pos], wait=True)

View File

@ -31,6 +31,7 @@ from . import recovery
if TYPE_CHECKING:
from ..device_handler import BackgroundDeviceHandler
from trezorlib.debuglink import DebugLink, LayoutContent
TX_CACHE_MAINNET = TxCache("Bitcoin")
TX_CACHE_TESTNET = TxCache("Testnet")
@ -48,26 +49,24 @@ TXHASH_d5f65e = bytes.fromhex(
PIN4 = "1234"
WORDS_20 = buttons.grid34(2, 2)
CENTER_BUTTON = buttons.grid35(1, 2)
def set_autolock_delay(device_handler: "BackgroundDeviceHandler", delay_ms: int):
debug = device_handler.debuglink()
device_handler.run(device.apply_settings, auto_lock_delay_ms=delay_ms)
device_handler.run(device.apply_settings, auto_lock_delay_ms=delay_ms) # type: ignore
assert debug.wait_layout().main_component() == "PinKeyboard"
layout = debug.wait_layout()
assert layout.text == "< PinKeyboard >"
debug.input("1234")
layout = debug.wait_layout()
assert (
f"auto-lock your device after {delay_ms // 1000} seconds"
in layout.get_content()
in debug.wait_layout().text_content()
)
debug.click(buttons.OK)
layout = debug.wait_layout()
assert layout.text.startswith("< Homescreen")
layout = debug.click(buttons.OK, wait=True)
assert layout.main_component() == "Homescreen"
assert device_handler.result() == "Settings applied"
@ -92,17 +91,16 @@ def test_autolock_interrupts_signing(device_handler: "BackgroundDeviceHandler"):
script_type=messages.OutputScriptType.PAYTOADDRESS,
)
device_handler.run(
btc.sign_tx, "Bitcoin", [inp1], [out1], prev_txes=TX_CACHE_MAINNET
device_handler.run(btc.sign_tx, "Bitcoin", [inp1], [out1], prev_txes=TX_CACHE_MAINNET) # type: ignore
assert (
"1MJ2tj2ThBE62zXbBYA5ZaN3fdve5CPAz1"
in debug.wait_layout().text_content().replace(" ", "")
)
layout = debug.wait_layout()
assert "1MJ2tj2ThBE62zXbBYA5ZaN3fdve5CPAz1" in layout.get_content().replace(" ", "")
debug.click(buttons.OK, wait=True)
layout = debug.click(buttons.OK, wait=True)
assert "Total amount: 0.0039 BTC" in layout.get_content()
assert "Total amount: 0.0039 BTC" in layout.text_content()
# wait for autolock to kick in
time.sleep(10.1)
@ -135,13 +133,15 @@ def test_autolock_does_not_interrupt_signing(device_handler: "BackgroundDeviceHa
btc.sign_tx, "Bitcoin", [inp1], [out1], prev_txes=TX_CACHE_MAINNET
)
layout = debug.wait_layout()
assert "1MJ2tj2ThBE62zXbBYA5ZaN3fdve5CPAz1" in layout.get_content().replace(" ", "")
assert (
"1MJ2tj2ThBE62zXbBYA5ZaN3fdve5CPAz1"
in debug.wait_layout().text_content().replace(" ", "")
)
debug.click(buttons.OK, wait=True)
layout = debug.click(buttons.OK, wait=True)
assert "Total amount: 0.0039 BTC" in layout.get_content()
assert "Total amount: 0.0039 BTC" in layout.text_content()
def sleepy_filter(msg: MessageType) -> MessageType:
time.sleep(10.1)
@ -166,18 +166,18 @@ def test_autolock_passphrase_keyboard(device_handler: "BackgroundDeviceHandler")
debug = device_handler.debuglink()
# get address
device_handler.run(common.get_test_address)
device_handler.run(common.get_test_address) # type: ignore
assert debug.wait_layout().main_component() == "PassphraseKeyboard"
# enter passphrase - slowly
layout = debug.wait_layout()
assert layout.text == "< PassphraseKeyboard >"
CENTER_BUTTON = buttons.grid35(1, 2)
# keep clicking for long enough to trigger the autolock if it incorrectly ignored key presses
for _ in range(math.ceil(11 / 1.5)):
# click at "j"
debug.click(CENTER_BUTTON)
time.sleep(1.5)
# Confirm the passphrase
debug.click(buttons.OK, wait=True)
assert device_handler.result() == "mnF4yRWJXmzRB6EuBzuVigqeqTqirQupxJ"
@ -188,13 +188,11 @@ def test_autolock_interrupts_passphrase(device_handler: "BackgroundDeviceHandler
debug = device_handler.debuglink()
# get address
device_handler.run(common.get_test_address)
device_handler.run(common.get_test_address) # type: ignore
assert debug.wait_layout().main_component() == "PassphraseKeyboard"
# enter passphrase - slowly
layout = debug.wait_layout()
assert layout.text == "< PassphraseKeyboard >"
CENTER_BUTTON = buttons.grid35(1, 2)
# autolock must activate even if we pressed some buttons
for _ in range(math.ceil(6 / 1.5)):
debug.click(CENTER_BUTTON)
@ -202,41 +200,51 @@ def test_autolock_interrupts_passphrase(device_handler: "BackgroundDeviceHandler
# wait for autolock to kick in
time.sleep(10.1)
layout = debug.wait_layout()
assert layout.text.startswith("< Lockscreen")
assert debug.wait_layout().main_component() == "Lockscreen"
with pytest.raises(exceptions.Cancelled):
device_handler.result()
def unlock_dry_run(debug: "DebugLink") -> "LayoutContent":
assert (
"Do you really want to check the recovery seed?"
in debug.wait_layout().text_content()
)
layout = debug.click(buttons.OK, wait=True)
assert layout.main_component() == "PinKeyboard"
layout = debug.input(PIN4, wait=True)
assert layout is not None
return layout
@pytest.mark.setup_client(pin=PIN4)
def test_dryrun_locks_at_number_of_words(device_handler: "BackgroundDeviceHandler"):
set_autolock_delay(device_handler, 10_000)
debug = device_handler.debuglink()
device_handler.run(device.recover, dry_run=True)
device_handler.run(device.recover, dry_run=True) # type: ignore
# unlock
layout = debug.wait_layout()
assert "Do you really want to check the recovery seed?" in layout.get_content()
layout = debug.click(buttons.OK, wait=True)
assert layout.text == "< PinKeyboard >"
layout = debug.input(PIN4, wait=True)
assert "Select number of words " in layout.get_content()
layout = unlock_dry_run(debug)
assert "select the number of words " in layout.text_content()
# wait for autolock to trigger
time.sleep(10.1)
layout = debug.wait_layout()
assert layout.text.startswith("< Lockscreen")
assert debug.wait_layout().main_component() == "Lockscreen"
with pytest.raises(exceptions.Cancelled):
device_handler.result()
# unlock
debug.wait_layout(
wait_for_external_change=True
) # lockscreen triggered automatically
layout = debug.click(buttons.OK, wait=True)
assert layout.text == "< PinKeyboard >"
assert layout.main_component() == "PinKeyboard"
layout = debug.input(PIN4, wait=True)
assert layout is not None
# we are back at homescreen
assert "Select number of words" in layout.get_content()
assert "select the number of words" in layout.text_content()
@pytest.mark.setup_client(pin=PIN4)
@ -244,24 +252,18 @@ def test_dryrun_locks_at_word_entry(device_handler: "BackgroundDeviceHandler"):
set_autolock_delay(device_handler, 10_000)
debug = device_handler.debuglink()
device_handler.run(device.recover, dry_run=True)
device_handler.run(device.recover, dry_run=True) # type: ignore
# unlock
layout = debug.wait_layout()
assert "Do you really want to check the recovery seed?" in layout.get_content()
layout = debug.click(buttons.OK, wait=True)
assert layout.text == "< PinKeyboard >"
layout = debug.input(PIN4, wait=True)
unlock_dry_run(debug)
# select 20 words
recovery.select_number_of_words(debug, 20)
layout = debug.click(buttons.OK, wait=True)
assert layout.main_component() == "MnemonicKeyboard"
# make sure keyboard locks
assert layout.text == "< MnemonicKeyboard >"
time.sleep(10.1)
layout = debug.wait_layout()
assert layout.text.startswith("< Lockscreen")
assert debug.wait_layout().main_component() == "Lockscreen"
with pytest.raises(exceptions.Cancelled):
device_handler.result()
@ -271,27 +273,24 @@ def test_dryrun_enter_word_slowly(device_handler: "BackgroundDeviceHandler"):
set_autolock_delay(device_handler, 10_000)
debug = device_handler.debuglink()
device_handler.run(device.recover, dry_run=True)
device_handler.run(device.recover, dry_run=True) # type: ignore
# unlock
layout = debug.wait_layout()
assert "Do you really want to check the recovery seed?" in layout.get_content()
layout = debug.click(buttons.OK, wait=True)
assert layout.text == "< PinKeyboard >"
layout = debug.input(PIN4, wait=True)
unlock_dry_run(debug)
# select 20 words
recovery.select_number_of_words(debug, 20)
layout = debug.click(buttons.OK, wait=True)
assert layout.main_component() == "MnemonicKeyboard"
# type the word OCEAN slowly
assert layout.text == "< MnemonicKeyboard >"
for coords in buttons.type_word("ocea", is_slip39=True):
time.sleep(9)
debug.click(coords)
layout = debug.click(buttons.CONFIRM_WORD, wait=True)
# should not have locked, even though we took 9 seconds to type each letter
assert layout.text == "< MnemonicKeyboard >"
assert layout.main_component() == "MnemonicKeyboard"
device_handler.kill_task()

View File

@ -32,36 +32,40 @@ PIN4 = "1234"
def test_hold_to_lock(device_handler: "BackgroundDeviceHandler"):
debug = device_handler.debuglink()
short_duration = 1000
lock_duration = 3500
def hold(duration: int, wait: bool = True) -> None:
debug.input(x=13, y=37, hold_ms=duration, wait=wait)
time.sleep(duration / 1000 + 0.5)
assert device_handler.features().unlocked is False
# unlock with message
device_handler.run(common.get_test_address)
layout = debug.wait_layout()
assert layout.text == "< PinKeyboard >"
assert debug.wait_layout().main_component() == "PinKeyboard"
debug.input("1234", wait=True)
assert device_handler.result()
assert device_handler.features().unlocked is True
# short touch
hold(1000, wait=False)
hold(short_duration)
time.sleep(0.5) # so that the homescreen appears again (hacky)
assert device_handler.features().unlocked is True
# lock
hold(3500)
hold(lock_duration)
assert device_handler.features().unlocked is False
# unlock by touching
layout = debug.click(buttons.INFO, wait=True)
assert layout.text == "< PinKeyboard >"
assert layout.main_component() == "PinKeyboard"
debug.input("1234", wait=True)
assert device_handler.features().unlocked is True
# lock
hold(3500)
hold(lock_duration)
assert device_handler.features().unlocked is False

View File

@ -0,0 +1,299 @@
# This file is part of the Trezor project.
#
# Copyright (C) 2012-2023 SatoshiLabs and contributors
#
# This library is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License version 3
# as published by the Free Software Foundation.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the License along with this library.
# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
import time
from contextlib import contextmanager
from typing import TYPE_CHECKING, Generator, Optional
import pytest
from .. import buttons
from ..common import get_test_address
from .common import CommonPass, PassphraseCategory, get_char_category
if TYPE_CHECKING:
from ..device_handler import BackgroundDeviceHandler
from trezorlib.debuglink import DebugLink
pytestmark = pytest.mark.skip_t1
# TODO: it is not possible to cancel the passphrase entry on TT
# NOTE: the prompt (underscoring) is not there when a space is entered
TT_CATEGORIES = [
PassphraseCategory.DIGITS,
PassphraseCategory.LOWERCASE,
PassphraseCategory.UPPERCASE,
PassphraseCategory.SPECIAL,
]
# TODO: better read this from the trace
TT_CATEGORY = PassphraseCategory.LOWERCASE
TT_COORDS_PREV: buttons.Coords = (0, 0)
# Testing the maximum length is really 50
# TODO: show some UI message when length reaches 50?
# (it currently disabled typing and greys out the buttons)
DA_50 = 25 * "da"
DA_50_ADDRESS = "mg5L2i8HZKUvceK1sfmGHhE4gichFSsdvm"
assert len(DA_50) == 50
DA_49 = DA_50[:-1]
DA_49_ADDRESS = "mxrB75ydMS3ZzqmYKK28fj4bNMEx7dDw6e"
assert len(DA_49) == 49
assert DA_49_ADDRESS != DA_50_ADDRESS
DA_51 = DA_50 + "d"
DA_51_ADDRESS = DA_50_ADDRESS
assert len(DA_51) == 51
assert DA_51_ADDRESS == DA_50_ADDRESS
@contextmanager
def prepare_passphrase_dialogue(
device_handler: "BackgroundDeviceHandler", address: Optional[str] = None
) -> Generator["DebugLink", None, None]:
debug = device_handler.debuglink()
device_handler.run(get_test_address) # type: ignore
assert debug.wait_layout().main_component() == "PassphraseKeyboard"
# Resetting the category as it could have been changed by previous tests
global TT_CATEGORY
TT_CATEGORY = PassphraseCategory.LOWERCASE # type: ignore
yield debug
result = device_handler.result()
if address is not None:
assert result == address
def go_to_category(debug: "DebugLink", category: PassphraseCategory) -> None:
"""Go to a specific category"""
global TT_CATEGORY
global TT_COORDS_PREV
# Already there
if TT_CATEGORY == category:
return
current_index = TT_CATEGORIES.index(TT_CATEGORY)
target_index = TT_CATEGORIES.index(category)
if target_index > current_index:
for _ in range(target_index - current_index):
debug.swipe_left(wait=True)
else:
for _ in range(current_index - target_index):
debug.swipe_right(wait=True)
TT_CATEGORY = category # type: ignore
# Category changed, reset coordinates
TT_COORDS_PREV = (0, 0) # type: ignore
def press_char(debug: "DebugLink", char: str) -> None:
"""Press a character"""
global TT_COORDS_PREV
# Space and couple others are a special case
if char in " *#":
char_category = PassphraseCategory.LOWERCASE
else:
char_category = get_char_category(char)
go_to_category(debug, char_category)
coords, amount = buttons.passphrase(char)
# If the button is the same as for the previous char,
# waiting a second before pressing it again.
# (not for a space)
if coords == TT_COORDS_PREV and char != " ":
time.sleep(1.1)
TT_COORDS_PREV = coords # type: ignore
for _ in range(amount):
debug.click(coords, wait=True)
def input_passphrase(debug: "DebugLink", passphrase: str, check: bool = True) -> None:
"""Input a passphrase with validation it got added"""
if check:
before = debug.read_layout().passphrase()
for char in passphrase:
press_char(debug, char)
if check:
after = debug.read_layout().passphrase()
assert after == before + passphrase
def enter_passphrase(debug: "DebugLink") -> None:
"""Enter a passphrase"""
coords = buttons.pin_passphrase_grid(11)
debug.click(coords, wait=True)
def delete_char(debug: "DebugLink") -> None:
"""Deletes the last char"""
coords = buttons.pin_passphrase_grid(9)
debug.click(coords, wait=True)
VECTORS = ( # passphrase, address
(CommonPass.SHORT, CommonPass.SHORT_ADDRESS),
(CommonPass.WITH_SPACE, CommonPass.WITH_SPACE_ADDRESS),
(CommonPass.RANDOM_25, CommonPass.RANDOM_25_ADDRESS),
(DA_49, DA_49_ADDRESS),
(DA_50, DA_50_ADDRESS),
)
@pytest.mark.parametrize("passphrase, address", VECTORS)
@pytest.mark.setup_client(passphrase=True)
def test_passphrase_input(
device_handler: "BackgroundDeviceHandler", passphrase: str, address: str
):
with prepare_passphrase_dialogue(device_handler, address) as debug:
input_passphrase(debug, passphrase)
enter_passphrase(debug)
@pytest.mark.setup_client(passphrase=True)
def test_passphrase_input_over_50_chars(device_handler: "BackgroundDeviceHandler"):
with prepare_passphrase_dialogue(device_handler, DA_51_ADDRESS) as debug: # type: ignore
input_passphrase(debug, DA_51, check=False)
assert debug.read_layout().passphrase() == DA_50
enter_passphrase(debug)
@pytest.mark.setup_client(passphrase=True)
def test_passphrase_delete(device_handler: "BackgroundDeviceHandler"):
with prepare_passphrase_dialogue(device_handler, CommonPass.SHORT_ADDRESS) as debug:
input_passphrase(debug, CommonPass.SHORT[:8])
for _ in range(4):
delete_char(debug)
debug.wait_layout()
input_passphrase(debug, CommonPass.SHORT[8 - 4 :])
enter_passphrase(debug)
@pytest.mark.setup_client(passphrase=True)
def test_passphrase_delete_all(
device_handler: "BackgroundDeviceHandler",
):
with prepare_passphrase_dialogue(device_handler, CommonPass.EMPTY_ADDRESS) as debug:
passphrase = "trezor"
input_passphrase(debug, passphrase)
for _ in range(len(passphrase)):
delete_char(debug)
enter_passphrase(debug)
@pytest.mark.setup_client(passphrase=True)
def test_passphrase_loop_all_characters(device_handler: "BackgroundDeviceHandler"):
with prepare_passphrase_dialogue(device_handler, CommonPass.EMPTY_ADDRESS) as debug:
for category in (
PassphraseCategory.DIGITS,
PassphraseCategory.LOWERCASE,
PassphraseCategory.UPPERCASE,
PassphraseCategory.SPECIAL,
):
go_to_category(debug, category)
debug.wait_layout()
enter_passphrase(debug)
coords = buttons.pin_passphrase_grid(11)
debug.click(coords)
@pytest.mark.setup_client(passphrase=True)
def test_passphrase_click_same_button_many_times(
device_handler: "BackgroundDeviceHandler",
):
with prepare_passphrase_dialogue(device_handler) as debug:
a_coords, _ = buttons.passphrase("a")
for _ in range(10):
debug.click(a_coords)
enter_passphrase(debug)
@pytest.mark.setup_client(passphrase=True)
def test_passphrase_prompt_disappears(
device_handler: "BackgroundDeviceHandler",
):
with prepare_passphrase_dialogue(device_handler) as debug:
input_passphrase(debug, "a")
# Wait a second for the prompt to disappear
time.sleep(1.1)
enter_passphrase(debug)
@pytest.mark.setup_client(passphrase=True)
def test_passphrase_long_spaces_deletion(
device_handler: "BackgroundDeviceHandler",
):
with prepare_passphrase_dialogue(device_handler) as debug:
input_passphrase(
debug,
"a"
+ " " * 7
+ "b"
+ " " * 7
+ "c"
+ " " * 7
+ "d"
+ " " * 7
+ "e"
+ " " * 7
+ "f",
)
for _ in range(12):
delete_char(debug)
enter_passphrase(debug)
@pytest.mark.setup_client(passphrase=True)
def test_passphrase_dollar_sign_deletion(
device_handler: "BackgroundDeviceHandler",
):
# Checks that dollar signs will not leave one pixel on the top after deleting
# (was a bug previously)
with prepare_passphrase_dialogue(device_handler, CommonPass.EMPTY_ADDRESS) as debug:
passphrase = "$$ I want $$"
input_passphrase(debug, passphrase)
for _ in range(len(passphrase)):
delete_char(debug)
enter_passphrase(debug)
@pytest.mark.setup_client(passphrase=True)
def test_cycle_through_last_character(
device_handler: "BackgroundDeviceHandler",
):
# Checks that we can cycle through the last (50th) passphrase character
# (was a bug previously)
with prepare_passphrase_dialogue(device_handler) as debug:
passphrase = DA_49 + "i" # for i we need to cycle through "ghi" three times
input_passphrase(debug, passphrase)
enter_passphrase(debug)

View File

@ -0,0 +1,274 @@
# This file is part of the Trezor project.
#
# Copyright (C) 2012-2023 SatoshiLabs and contributors
#
# This library is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License version 3
# as published by the Free Software Foundation.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the License along with this library.
# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
from contextlib import contextmanager
from enum import Enum
from typing import TYPE_CHECKING, Generator
import pytest
from trezorlib import device, exceptions
from .. import buttons
from .common import go_back, go_next
if TYPE_CHECKING:
from ..device_handler import BackgroundDeviceHandler
from trezorlib.debuglink import DebugLink
pytestmark = pytest.mark.skip_t1
PIN_CANCELLED = pytest.raises(exceptions.TrezorFailure, match="PIN entry cancelled")
PIN_INVALID = pytest.raises(exceptions.TrezorFailure, match="PIN invalid")
PIN4 = "1234"
PIN24 = "875163065288639289952973"
PIN50 = "31415926535897932384626433832795028841971693993751"
PIN60 = PIN50 + "9" * 10
TR_PIN_ACTIONS = [
"DELETE",
"SHOW",
"ENTER",
"0",
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
]
class Situation(Enum):
PIN_INPUT = 1
PIN_SETUP = 2
PIN_CHANGE = 3
WIPE_CODE_SETUP = 4
@contextmanager
def prepare(
device_handler: "BackgroundDeviceHandler",
situation: Situation = Situation.PIN_INPUT,
old_pin: str = "",
) -> Generator["DebugLink", None, None]:
debug = device_handler.debuglink()
# So that the digit order is the same. Needed for UI tests.
# Even though it should be done in conftest::client fixture (used by device_handler),
# without reseeding "again", the results are still random.
debug.reseed(0)
# Setup according to the wanted situation
if situation == Situation.PIN_INPUT:
# Any action triggering the PIN dialogue
device_handler.run(device.apply_settings, auto_lock_delay_ms=300_000) # type: ignore
elif situation == Situation.PIN_SETUP:
# Set new PIN
device_handler.run(device.change_pin) # type: ignore
assert "enable PIN protection" in debug.wait_layout().text_content()
layout = go_next(debug, wait=True)
assert "access this device" in layout.text_content()
go_next(debug)
elif situation == Situation.PIN_CHANGE:
# Change PIN
device_handler.run(device.change_pin) # type: ignore
_input_see_confirm(debug, old_pin)
assert "change your PIN" in debug.read_layout().text_content()
go_next(debug, wait=True)
_input_see_confirm(debug, old_pin)
elif situation == Situation.WIPE_CODE_SETUP:
# Set wipe code
device_handler.run(device.change_wipe_code) # type: ignore
if old_pin:
_input_see_confirm(debug, old_pin)
assert "enable wipe code" in debug.wait_layout().text_content()
layout = go_next(debug, wait=True)
assert "erase all data" in layout.text_content()
go_next(debug)
if old_pin:
debug.wait_layout()
_input_see_confirm(debug, old_pin)
debug.wait_layout()
_assert_pin_entry(debug)
yield debug
go_next(debug)
device_handler.result()
def _assert_pin_entry(debug: "DebugLink") -> None:
assert debug.read_layout().main_component() == "PinKeyboard"
def _input_pin(debug: "DebugLink", pin: str, check: bool = False) -> None:
"""Input the PIN"""
before = debug.read_layout().pin()
digits_order = debug.read_layout().tt_pin_digits_order()
for digit in pin:
digit_index = digits_order.index(digit)
coords = buttons.pin_passphrase_index(digit_index)
debug.click(coords, wait=True)
if check:
after = debug.read_layout().pin()
assert before + pin == after
def _see_pin(debug: "DebugLink") -> None:
"""Navigate to "SHOW" and press it"""
debug.click(buttons.TOP_ROW, wait=True)
def _delete_pin(debug: "DebugLink", digits_to_delete: int, check: bool = True) -> None:
"""Navigate to "DELETE" and press it how many times requested"""
if check:
before = debug.read_layout().pin()
for _ in range(digits_to_delete):
debug.click(buttons.pin_passphrase_grid(9), wait=True)
if check:
after = debug.read_layout().pin()
assert before[:-digits_to_delete] == after
def _cancel_pin(debug: "DebugLink") -> None:
"""Navigate to "CANCEL" and press it"""
# It is the same button as DELETE
_delete_pin(debug, 1, check=False)
def _confirm_pin(debug: "DebugLink") -> None:
"""Navigate to "ENTER" and press it"""
debug.click(buttons.pin_passphrase_grid(11), wait=True)
def _input_see_confirm(debug: "DebugLink", pin: str) -> None:
_input_pin(debug, pin)
_see_pin(debug)
_confirm_pin(debug)
def _enter_two_times(debug: "DebugLink", pin1: str, pin2: str) -> None:
_input_see_confirm(debug, pin1)
# Please re-enter
debug.click(buttons.OK, wait=True)
_input_see_confirm(debug, pin2)
@pytest.mark.setup_client(pin=PIN4)
def test_pin_short(device_handler: "BackgroundDeviceHandler"):
with prepare(device_handler) as debug:
_input_see_confirm(debug, PIN4)
@pytest.mark.setup_client(pin=PIN24)
def test_pin_long(device_handler: "BackgroundDeviceHandler"):
with prepare(device_handler) as debug:
_input_see_confirm(debug, PIN24)
@pytest.mark.setup_client(pin=PIN24)
def test_pin_long_delete(device_handler: "BackgroundDeviceHandler"):
with prepare(device_handler) as debug:
_input_pin(debug, PIN24)
_see_pin(debug)
_delete_pin(debug, 10)
_see_pin(debug)
_input_see_confirm(debug, PIN24[-10:])
@pytest.mark.setup_client(pin=PIN60[:50])
def test_pin_longer_than_max(device_handler: "BackgroundDeviceHandler"):
with prepare(device_handler) as debug:
_input_pin(debug, PIN60, check=False)
# What is over 50 digits was not entered
# TODO: do some UI change when limit is reached?
assert debug.read_layout().pin() == PIN60[:50]
_see_pin(debug)
_confirm_pin(debug)
@pytest.mark.setup_client(pin=PIN4)
def test_pin_incorrect(device_handler: "BackgroundDeviceHandler"):
with prepare(device_handler) as debug:
_input_see_confirm(debug, "1235")
# debug.wait_layout()
_input_see_confirm(debug, PIN4)
@pytest.mark.setup_client(pin=PIN4)
def test_pin_cancel(device_handler: "BackgroundDeviceHandler"):
with PIN_CANCELLED, prepare(device_handler) as debug:
_input_pin(debug, PIN4)
_see_pin(debug)
_delete_pin(debug, len(PIN4))
_see_pin(debug)
_cancel_pin(debug)
@pytest.mark.setup_client()
def test_pin_setup(device_handler: "BackgroundDeviceHandler"):
with prepare(device_handler, Situation.PIN_SETUP) as debug:
_enter_two_times(debug, PIN4, PIN4)
@pytest.mark.setup_client()
def test_pin_setup_mismatch(device_handler: "BackgroundDeviceHandler"):
with PIN_CANCELLED, prepare(device_handler, Situation.PIN_SETUP) as debug:
_enter_two_times(debug, "1", "2")
go_next(debug)
_cancel_pin(debug)
@pytest.mark.setup_client(pin="1")
def test_pin_change(device_handler: "BackgroundDeviceHandler"):
with prepare(device_handler, Situation.PIN_CHANGE, old_pin="1") as debug:
_enter_two_times(debug, "2", "2")
@pytest.mark.setup_client(pin="1")
def test_wipe_code_setup(device_handler: "BackgroundDeviceHandler"):
with prepare(device_handler, Situation.WIPE_CODE_SETUP, old_pin="1") as debug:
_enter_two_times(debug, "2", "2")
@pytest.mark.setup_client(pin="1")
def test_wipe_code_same_as_pin(device_handler: "BackgroundDeviceHandler"):
with prepare(device_handler, Situation.WIPE_CODE_SETUP, old_pin="1") as debug:
_input_see_confirm(debug, "1")
# Try again
go_next(debug, wait=True)
_enter_two_times(debug, "2", "2")
@pytest.mark.setup_client()
def test_pin_same_as_wipe_code(device_handler: "BackgroundDeviceHandler"):
with prepare(device_handler, Situation.WIPE_CODE_SETUP) as debug:
_enter_two_times(debug, "1", "1")
with PIN_INVALID, prepare(device_handler, Situation.PIN_SETUP) as debug:
_enter_two_times(debug, "1", "1")
go_back(debug)

View File

@ -14,35 +14,56 @@
# You should have received a copy of the License along with this library.
# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
from typing import TYPE_CHECKING
from contextlib import contextmanager
from typing import TYPE_CHECKING, Generator
import pytest
from trezorlib import device, messages
from ..common import MNEMONIC_SLIP39_BASIC_20_3of6
from ..common import MNEMONIC12, MNEMONIC_SLIP39_BASIC_20_3of6
from . import recovery
if TYPE_CHECKING:
from ..device_handler import BackgroundDeviceHandler
from trezorlib.debuglink import DebugLink
@pytest.mark.skip_t1
@pytest.mark.setup_client(uninitialized=True)
def test_recovery(device_handler: "BackgroundDeviceHandler"):
pytestmark = [pytest.mark.skip_t1]
@contextmanager
def prepare_recovery_and_evaluate(
device_handler: "BackgroundDeviceHandler",
) -> Generator["DebugLink", None, None]:
features = device_handler.features()
debug = device_handler.debuglink()
assert features.initialized is False
device_handler.run(device.recover, pin_protection=False)
device_handler.run(device.recover, pin_protection=False) # type: ignore
recovery.confirm_recovery(debug)
recovery.select_number_of_words(debug)
recovery.enter_shares(debug, MNEMONIC_SLIP39_BASIC_20_3of6)
recovery.finalize(debug)
yield debug
assert isinstance(device_handler.result(), messages.Success)
features = device_handler.features()
assert features.initialized is True
assert features.recovery_mode is False
@pytest.mark.setup_client(uninitialized=True)
def test_recovery_slip39_basic(device_handler: "BackgroundDeviceHandler"):
with prepare_recovery_and_evaluate(device_handler) as debug:
recovery.confirm_recovery(debug)
recovery.select_number_of_words(debug)
recovery.enter_shares(debug, MNEMONIC_SLIP39_BASIC_20_3of6)
recovery.finalize(debug)
@pytest.mark.setup_client(uninitialized=True)
def test_recovery_bip39(device_handler: "BackgroundDeviceHandler"):
with prepare_recovery_and_evaluate(device_handler) as debug:
recovery.confirm_recovery(debug)
recovery.select_number_of_words(debug, num_of_words=12)
recovery.enter_seed(debug, MNEMONIC12.split())
recovery.finalize(debug)

View File

@ -0,0 +1,77 @@
# This file is part of the Trezor project.
#
# Copyright (C) 2012-2019 SatoshiLabs and contributors
#
# This library is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License version 3
# as published by the Free Software Foundation.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the License along with this library.
# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
from typing import TYPE_CHECKING
import pytest
from trezorlib import device, messages
from ..common import WITH_MOCK_URANDOM
from . import reset
if TYPE_CHECKING:
from ..device_handler import BackgroundDeviceHandler
pytestmark = [pytest.mark.skip_t1]
@pytest.mark.setup_client(uninitialized=True)
@WITH_MOCK_URANDOM
def test_reset_bip39(device_handler: "BackgroundDeviceHandler"):
features = device_handler.features()
debug = device_handler.debuglink()
assert features.initialized is False
device_handler.run(
device.reset,
strength=128,
backup_type=messages.BackupType.Bip39,
pin_protection=False,
)
# confirm new wallet
reset.confirm_new_wallet(debug)
# confirm back up
reset.confirm_read(debug, "Success")
# confirm backup warning (hold-to-confirm on TR)
reset.confirm_read(debug, "Caution", hold=True)
# read words
words = reset.read_words(debug, messages.BackupType.Bip39)
# confirm words
reset.confirm_words(debug, words)
# confirm backup done
reset.confirm_read(debug, "Success")
# Your backup is done
debug.press_yes()
# TODO: some validation of the generated secret?
assert device_handler.result() == "Initialized"
features = device_handler.features()
assert features.initialized is True
assert features.needs_backup is False
assert features.pin_protection is False
assert features.passphrase_protection is False
assert features.backup_type is messages.BackupType.Bip39

View File

@ -15,30 +15,37 @@
# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
from typing import TYPE_CHECKING
from unittest import mock
import pytest
from trezorlib import device, messages
from .. import buttons
from ..common import generate_entropy
from ..common import EXTERNAL_ENTROPY, WITH_MOCK_URANDOM, generate_entropy
from . import reset
if TYPE_CHECKING:
from ..device_handler import BackgroundDeviceHandler
EXTERNAL_ENTROPY = b"zlutoucky kun upel divoke ody" * 2
with_mock_urandom = mock.patch("os.urandom", mock.Mock(return_value=EXTERNAL_ENTROPY))
pytestmark = [pytest.mark.skip_t1]
@pytest.mark.skip_t1
@pytest.mark.setup_client(uninitialized=True)
@with_mock_urandom
def test_reset_slip39_advanced_2of2groups_2of2shares(
@pytest.mark.parametrize(
"group_count, group_threshold, share_count, share_threshold",
[
pytest.param(2, 2, 2, 2, id="2of2"),
pytest.param(16, 16, 16, 16, id="16of16", marks=pytest.mark.slow),
],
)
@WITH_MOCK_URANDOM
def test_reset_slip39_advanced(
device_handler: "BackgroundDeviceHandler",
group_count: int,
group_threshold: int,
share_count: int,
share_threshold: int,
):
features = device_handler.features()
debug = device_handler.debuglink()
@ -52,7 +59,7 @@ def test_reset_slip39_advanced_2of2groups_2of2shares(
)
# confirm new wallet
reset.confirm_wait(debug, "Wallet creation")
reset.confirm_new_wallet(debug)
# confirm back up
reset.confirm_read(debug, "Success")
@ -60,119 +67,54 @@ def test_reset_slip39_advanced_2of2groups_2of2shares(
# confirm checklist
reset.confirm_read(debug, "Checklist")
# set num of groups
reset.set_selection(debug, buttons.RESET_MINUS, 3)
# set num of groups - default is 5
if group_count < 5:
reset.set_selection(debug, buttons.RESET_MINUS, 5 - group_count)
else:
reset.set_selection(debug, buttons.RESET_PLUS, group_count - 5)
# confirm checklist
reset.confirm_read(debug, "Checklist")
# set group threshold
reset.set_selection(debug, buttons.RESET_MINUS, 0)
# TODO: could make it general as well
if group_count == 2 and group_threshold == 2:
reset.set_selection(debug, buttons.RESET_PLUS, 0)
elif group_count == 16 and group_threshold == 16:
reset.set_selection(debug, buttons.RESET_PLUS, 11)
else:
raise RuntimeError("not a supported combination")
# confirm checklist
reset.confirm_read(debug, "Checklist")
# set share num and threshold for groups
for _ in range(2):
# set num of shares
reset.set_selection(debug, buttons.RESET_MINUS, 3)
for _ in range(group_count):
# set num of shares - default is 5
if share_count < 5:
reset.set_selection(debug, buttons.RESET_MINUS, 5 - share_count)
else:
reset.set_selection(debug, buttons.RESET_PLUS, share_count - 5)
# set share threshold
reset.set_selection(debug, buttons.RESET_MINUS, 0)
# TODO: could make it general as well
if share_count == 2 and share_threshold == 2:
reset.set_selection(debug, buttons.RESET_PLUS, 0)
elif share_count == 16 and share_threshold == 16:
reset.set_selection(debug, buttons.RESET_PLUS, 11)
else:
raise RuntimeError("not a supported combination")
# confirm backup warning
reset.confirm_read(debug, "Caution")
# confirm backup warning (hold-to-confirm on TR)
reset.confirm_read(debug, "Caution", hold=True)
all_words: list[str] = []
for _ in range(2):
for _ in range(2):
for _ in range(group_count):
for _ in range(share_count):
# read words
words = reset.read_words(debug, True)
# confirm words
reset.confirm_words(debug, words)
# confirm share checked
reset.confirm_read(debug, "Success")
all_words.append(" ".join(words))
# confirm backup done
reset.confirm_read(debug, "Success")
# generate secret locally
internal_entropy = debug.state().reset_entropy
assert internal_entropy is not None
secret = generate_entropy(128, internal_entropy, EXTERNAL_ENTROPY)
# validate that all combinations will result in the correct master secret
reset.validate_mnemonics(all_words, secret)
assert device_handler.result() == "Initialized"
features = device_handler.features()
assert features.initialized is True
assert features.needs_backup is False
assert features.pin_protection is False
assert features.passphrase_protection is False
assert features.backup_type is messages.BackupType.Slip39_Advanced
@pytest.mark.skip_t1
@pytest.mark.setup_client(uninitialized=True)
@pytest.mark.slow
@with_mock_urandom
def test_reset_slip39_advanced_16of16groups_16of16shares(
device_handler: "BackgroundDeviceHandler",
):
features = device_handler.features()
debug = device_handler.debuglink()
assert features.initialized is False
device_handler.run(
device.reset,
backup_type=messages.BackupType.Slip39_Advanced,
pin_protection=False,
)
# confirm new wallet
reset.confirm_wait(debug, "Wallet creation")
# confirm back up
reset.confirm_read(debug, "Success")
# confirm checklist
reset.confirm_read(debug, "Checklist")
# set num of groups
reset.set_selection(debug, buttons.RESET_PLUS, 11)
# confirm checklist
reset.confirm_read(debug, "Checklist")
# set group threshold
reset.set_selection(debug, buttons.RESET_PLUS, 11)
# confirm checklist
reset.confirm_read(debug, "Checklist")
# set share num and threshold for groups
for _ in range(16):
# set num of shares
reset.set_selection(debug, buttons.RESET_PLUS, 11)
# set share threshold
reset.set_selection(debug, buttons.RESET_PLUS, 11)
# confirm backup warning
reset.confirm_read(debug, "Caution")
all_words: list[str] = []
for _ in range(16):
for _ in range(16):
# read words
words = reset.read_words(debug, True)
words = reset.read_words(
debug, messages.BackupType.Slip39_Advanced, do_htc=False
)
# confirm words
reset.confirm_words(debug, words)

View File

@ -15,68 +15,83 @@
# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
from typing import TYPE_CHECKING
from unittest import mock
import pytest
from trezorlib import device, messages
from .. import buttons
from ..common import generate_entropy
from ..common import EXTERNAL_ENTROPY, WITH_MOCK_URANDOM, generate_entropy
from . import reset
if TYPE_CHECKING:
from ..device_handler import BackgroundDeviceHandler
EXTERNAL_ENTROPY = b"zlutoucky kun upel divoke ody" * 2
pytestmark = [pytest.mark.skip_t1]
@pytest.mark.skip_t1
@pytest.mark.parametrize(
"num_of_shares, threshold",
[
pytest.param(1, 1, id="1of1"),
pytest.param(16, 16, id="16of16"),
],
)
@pytest.mark.setup_client(uninitialized=True)
def test_reset_slip39_basic_1of1(device_handler: "BackgroundDeviceHandler"):
@WITH_MOCK_URANDOM
def test_reset_slip39_basic(
device_handler: "BackgroundDeviceHandler", num_of_shares: int, threshold: int
):
features = device_handler.features()
debug = device_handler.debuglink()
assert features.initialized is False
os_urandom = mock.Mock(return_value=EXTERNAL_ENTROPY)
with mock.patch("os.urandom", os_urandom), device_handler:
device_handler.run(
device.reset,
strength=128,
backup_type=messages.BackupType.Slip39_Basic,
pin_protection=False,
)
device_handler.run(
device.reset,
strength=128,
backup_type=messages.BackupType.Slip39_Basic,
pin_protection=False,
)
# confirm new wallet
reset.confirm_wait(debug, "Wallet creation")
# confirm new wallet
reset.confirm_new_wallet(debug)
# confirm back up
reset.confirm_read(debug, "Success")
# confirm back up
reset.confirm_read(debug, "Success")
# confirm checklist
reset.confirm_read(debug, "Checklist")
# confirm checklist
reset.confirm_read(debug, "Checklist")
# set num of shares
# default is 5 so we press RESET_MINUS 4 times
reset.set_selection(debug, buttons.RESET_MINUS, 4)
# set num of shares - default is 5
if num_of_shares < 5:
reset.set_selection(debug, buttons.RESET_MINUS, 5 - num_of_shares)
else:
reset.set_selection(debug, buttons.RESET_PLUS, num_of_shares - 5)
# confirm checklist
reset.confirm_read(debug, "Checklist")
# confirm checklist
reset.confirm_read(debug, "Checklist")
# set threshold
# threshold will default to 1
reset.set_selection(debug, buttons.RESET_MINUS, 0)
# set threshold
# TODO: could make it general as well
if num_of_shares == 1 and threshold == 1:
reset.set_selection(debug, buttons.RESET_PLUS, 0)
elif num_of_shares == 16 and threshold == 16:
reset.set_selection(debug, buttons.RESET_PLUS, 11)
else:
raise RuntimeError("not a supported combination")
# confirm checklist
reset.confirm_read(debug, "Checklist")
# confirm checklist
reset.confirm_read(debug, "Checklist")
# confirm backup warning
reset.confirm_read(debug, "Caution")
# confirm backup warning (hold-to-confirm on TR)
reset.confirm_read(debug, "Caution", hold=True)
all_words: list[str] = []
for _ in range(num_of_shares):
# read words
words = reset.read_words(debug)
words = reset.read_words(debug, messages.BackupType.Slip39_Basic)
# confirm words
reset.confirm_words(debug, words)
@ -84,98 +99,23 @@ def test_reset_slip39_basic_1of1(device_handler: "BackgroundDeviceHandler"):
# confirm share checked
reset.confirm_read(debug, "Success")
# confirm backup done
reset.confirm_read(debug, "Success")
all_words.append(" ".join(words))
# generate secret locally
internal_entropy = debug.state().reset_entropy
assert internal_entropy is not None
secret = generate_entropy(128, internal_entropy, EXTERNAL_ENTROPY)
# confirm backup done
reset.confirm_read(debug, "Success")
# validate that all combinations will result in the correct master secret
validate = [" ".join(words)]
reset.validate_mnemonics(validate, secret)
# generate secret locally
internal_entropy = debug.state().reset_entropy
assert internal_entropy is not None
secret = generate_entropy(128, internal_entropy, EXTERNAL_ENTROPY)
assert device_handler.result() == "Initialized"
features = device_handler.features()
assert features.initialized is True
assert features.needs_backup is False
assert features.pin_protection is False
assert features.passphrase_protection is False
assert features.backup_type is messages.BackupType.Slip39_Basic
# validate that all combinations will result in the correct master secret
reset.validate_mnemonics(all_words, secret)
@pytest.mark.skip_t1
@pytest.mark.setup_client(uninitialized=True)
def test_reset_slip39_basic_16of16(device_handler: "BackgroundDeviceHandler"):
assert device_handler.result() == "Initialized"
features = device_handler.features()
debug = device_handler.debuglink()
assert features.initialized is False
os_urandom = mock.Mock(return_value=EXTERNAL_ENTROPY)
with mock.patch("os.urandom", os_urandom), device_handler:
device_handler.run(
device.reset,
strength=128,
backup_type=messages.BackupType.Slip39_Basic,
pin_protection=False,
)
# confirm new wallet
reset.confirm_wait(debug, "Wallet creation")
# confirm back up
reset.confirm_read(debug, "Success")
# confirm checklist
reset.confirm_read(debug, "Checklist")
# set num of shares
# default is 5 so we add 11
reset.set_selection(debug, buttons.RESET_PLUS, 11)
# confirm checklist
reset.confirm_read(debug, "Checklist")
# set threshold
# default is 5 so we add 11
reset.set_selection(debug, buttons.RESET_PLUS, 11)
# confirm checklist
reset.confirm_read(debug, "Checklist")
# confirm backup warning
reset.confirm_read(debug, "Caution")
all_words: list[str] = []
for _ in range(16):
# read words
words = reset.read_words(debug)
# confirm words
reset.confirm_words(debug, words)
# confirm share checked
reset.confirm_read(debug, "Success")
all_words.append(" ".join(words))
# confirm backup done
reset.confirm_read(debug, "Success")
# generate secret locally
internal_entropy = debug.state().reset_entropy
assert internal_entropy is not None
secret = generate_entropy(128, internal_entropy, EXTERNAL_ENTROPY)
# validate that all combinations will result in the correct master secret
reset.validate_mnemonics(all_words, secret)
assert device_handler.result() == "Initialized"
features = device_handler.features()
assert features.initialized is True
assert features.needs_backup is False
assert features.pin_protection is False
assert features.passphrase_protection is False
assert features.backup_type is messages.BackupType.Slip39_Basic
assert features.initialized is True
assert features.needs_backup is False
assert features.pin_protection is False
assert features.passphrase_protection is False
assert features.backup_type is messages.BackupType.Slip39_Basic