mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-18 04:18:10 +00:00
feat(tests): update, refactor and extend click tests
This commit is contained in:
parent
87c7e33198
commit
00b83d1dca
50
tests/click_tests/common.py
Normal file
50
tests/click_tests/common.py
Normal 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
|
@ -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"
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
299
tests/click_tests/test_passphrase_tt.py
Normal file
299
tests/click_tests/test_passphrase_tt.py
Normal 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)
|
274
tests/click_tests/test_pin.py
Normal file
274
tests/click_tests/test_pin.py
Normal 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)
|
@ -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)
|
||||
|
77
tests/click_tests/test_reset_bip39.py
Normal file
77
tests/click_tests/test_reset_bip39.py
Normal 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
|
@ -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)
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user