1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-07-01 04:12:37 +00:00

chore(tests): centralize all model-dependent input flows

[no changelog]
This commit is contained in:
grdddj 2023-01-16 15:09:36 +01:00
parent 723493aa30
commit e5b6d43a34
26 changed files with 2070 additions and 2466 deletions

View File

@ -15,8 +15,9 @@
# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
import json
import time
from pathlib import Path
from typing import TYPE_CHECKING, Generator, List, Optional
from typing import TYPE_CHECKING, Generator, Optional
import pytest
@ -24,7 +25,7 @@ from trezorlib import btc, tools
from trezorlib.messages import ButtonRequestType
if TYPE_CHECKING:
from trezorlib.debuglink import LayoutLines
from trezorlib.debuglink import LayoutContent
from trezorlib.debuglink import DebugLink, TrezorClientDebugLink as Client
from trezorlib.messages import ButtonRequest
from _pytest.mark.structures import MarkDecorator
@ -125,7 +126,23 @@ def generate_entropy(
def recovery_enter_shares(
debug: "DebugLink",
shares: List[str],
shares: list[str],
groups: bool = False,
click_info: bool = False,
) -> Generator[None, "ButtonRequest", None]:
if debug.model == "T":
yield from recovery_enter_shares_tt(
debug, shares, groups=groups, click_info=click_info
)
elif debug.model == "R":
yield from recovery_enter_shares_tr(debug, shares, groups=groups)
else:
raise ValueError(f"Unknown model: {debug.model}")
def recovery_enter_shares_tt(
debug: "DebugLink",
shares: list[str],
groups: bool = False,
click_info: bool = False,
) -> Generator[None, "ButtonRequest", None]:
@ -181,7 +198,7 @@ def recovery_enter_shares(
def recovery_enter_shares_tr(
debug: "DebugLink",
shares: List[str],
shares: list[str],
groups: bool = False,
) -> Generator[None, "ButtonRequest", None]:
"""Perform the recovery flow for a set of Shamir shares.
@ -236,7 +253,7 @@ def recovery_enter_shares_tr(
def click_through(
debug: "DebugLink", screens: int, code: ButtonRequestType = None
debug: "DebugLink", screens: int, code: Optional[ButtonRequestType] = None
) -> Generator[None, "ButtonRequest", None]:
"""Click through N dialog screens.
@ -259,6 +276,19 @@ def click_through(
def read_and_confirm_mnemonic(
debug: "DebugLink", choose_wrong: bool = False
) -> Generator[None, "ButtonRequest", Optional[str]]:
if debug.model == "T":
mnemonic = yield from read_and_confirm_mnemonic_tt(debug, choose_wrong)
elif debug.model == "R":
mnemonic = yield from read_and_confirm_mnemonic_tr(debug, choose_wrong)
else:
raise ValueError(f"Unknown model: {debug.model}")
return mnemonic
def read_and_confirm_mnemonic_tt(
debug: "DebugLink", choose_wrong: bool = False
) -> Generator[None, "ButtonRequest", Optional[str]]:
"""Read a given number of mnemonic words from Trezor T screen and correctly
answer confirmation questions. Return the full mnemonic.
@ -271,7 +301,7 @@ def read_and_confirm_mnemonic(
mnemonic = yield from read_and_confirm_mnemonic(client.debug)
"""
mnemonic: List[str] = []
mnemonic: list[str] = []
br = yield
assert br.pages is not None
for _ in range(br.pages - 1):
@ -294,19 +324,46 @@ def read_and_confirm_mnemonic(
return " ".join(mnemonic)
def read_and_confirm_mnemonic_tr(
debug: "DebugLink", choose_wrong: bool = False
) -> Generator[None, "ButtonRequest", Optional[str]]:
mnemonic: list[str] = []
br = yield
assert br.pages is not None
for _ in range(br.pages):
layout = debug.wait_layout()
words = ModelRLayout(layout).get_mnemonic_words()
mnemonic.extend(words)
debug.press_right()
yield # Select correct words...
debug.press_right()
for _ in range(3):
index = debug.read_reset_word_pos()
if choose_wrong:
debug.input(mnemonic[(index + 1) % len(mnemonic)])
return None
else:
debug.input(mnemonic[index])
return " ".join(mnemonic)
class ModelRLayout:
"""Layout shortcuts for Model R."""
def __init__(self, layout: "LayoutLines") -> None:
def __init__(self, layout: "LayoutContent") -> None:
self.layout = layout
def get_mnemonic_words(self) -> List[str]:
def get_mnemonic_words(self) -> list[str]:
"""Extract mnemonic words from the layout lines.
Example input: [..., '4 must', '5 during', '6 monitor', ...]
Example output: ['must', 'during', 'monitor']
"""
words: List[str] = []
words: list[str] = []
for line in self.layout.lines:
if " " in line:
number, word = line.split(" ", 1)
@ -335,31 +392,18 @@ class ModelRLayout:
return buttons.split("[Select(")[1].split(")]")[0]
def read_and_confirm_mnemonic_tr(
debug: "DebugLink", choose_wrong: bool = False
) -> Generator[None, "ButtonRequest", Optional[str]]:
mnemonic: List[str] = []
br = yield
assert br.pages is not None
for _ in range(br.pages):
layout = debug.wait_layout()
def click_info_button(debug: "DebugLink"):
"""Click Shamir backup info button and return back."""
debug.press_info()
yield # Info screen with text
debug.press_yes()
words = ModelRLayout(layout).get_mnemonic_words()
mnemonic.extend(words)
debug.press_right()
yield # Select correct words...
debug.press_right()
for _ in range(3):
index = debug.read_reset_word_pos()
if choose_wrong:
debug.input(mnemonic[(index + 1) % len(mnemonic)])
return None
else:
debug.input(mnemonic[index])
return " ".join(mnemonic)
def check_PIN_backoff_time(attempts: int, start: float) -> None:
"""Helper to assert the exponentially growing delay after incorrect PIN attempts"""
expected = (2**attempts) - 1
got = round(time.time() - start, 2)
assert got >= expected
def get_test_address(client: "Client") -> str:

View File

@ -20,6 +20,8 @@ from trezorlib import btc, messages, tools
from trezorlib.debuglink import TrezorClientDebugLink as Client
from trezorlib.exceptions import TrezorFailure
from ...input_flows import InputFlowShowAddressQRCode, InputFlowShowMultisigXPUBs
VECTORS = ( # path, script_type, address
(
"m/44h/0h/12h/0/0",
@ -48,22 +50,19 @@ VECTORS = ( # path, script_type, address
def test_show(
client: Client, path: str, script_type: messages.InputScriptType, address: str
):
def input_flow_tt():
def input_flow_t1():
yield
client.debug.press_no()
yield
client.debug.press_yes()
def input_flow_tr():
yield
client.debug.press_right()
client.debug.press_yes()
with client:
if client.features.model == "T":
client.set_input_flow(input_flow_tt)
elif client.features.model == "R":
client.set_input_flow(input_flow_tr)
# This is the only place where even T1 is using input flow
if client.features.model == "1":
client.set_input_flow(input_flow_t1)
else:
IF = InputFlowShowAddressQRCode(client)
client.set_input_flow(IF.get())
assert (
btc.get_address(
client,
@ -220,41 +219,10 @@ def test_show_multisig_xpubs(
)
for i in range(3):
def input_flow():
yield # show address
layout = client.debug.wait_layout() # TODO: do not need to *wait* now?
assert layout.get_title() == "MULTISIG 2 OF 3"
assert layout.get_content().replace(" ", "") == address
client.debug.press_no()
yield # show QR code
assert "Painter" in client.debug.wait_layout().text
# Three xpub pages with the same testing logic
for xpub_num in range(3):
expected_title = f"XPUB #{xpub_num + 1} " + (
"(yours)" if i == xpub_num else "(cosigner)"
)
client.debug.press_no()
yield # show XPUB#{xpub_num}
layout1 = client.debug.wait_layout()
assert layout1.get_title() == expected_title
client.debug.swipe_up()
layout2 = client.debug.wait_layout()
assert layout2.get_title() == expected_title
content = (layout1.get_content() + layout2.get_content()).replace(
" ", ""
)
assert content == xpubs[xpub_num]
client.debug.press_yes()
with client:
client.watch_layout()
client.set_input_flow(input_flow)
IF = InputFlowShowMultisigXPUBs(client, address, xpubs, i)
client.set_input_flow(IF.get())
btc.get_address(
client,
"Bitcoin",

View File

@ -14,22 +14,20 @@
# 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 Any
import pytest
from trezorlib import btc, messages
from trezorlib.debuglink import (
LayoutContent,
TrezorClientDebugLink as Client,
message_filters,
multipage_content,
)
from trezorlib.debuglink import TrezorClientDebugLink as Client, message_filters
from trezorlib.tools import parse_path
from ...input_flows import InputFlowSignMessagePagination
S = messages.InputScriptType
def case(id, *args, altcoin=False, skip_t1=False):
def case(id: str, *args: Any, altcoin: bool = False, skip_t1: bool = False):
marks = []
if altcoin:
marks.append(pytest.mark.altcoin)
@ -273,7 +271,14 @@ VECTORS = ( # case name, coin_name, path, script_type, address, message, signat
"coin_name, path, script_type, no_script_type, address, message, signature", VECTORS
)
def test_signmessage(
client, coin_name, path, script_type, no_script_type, address, message, signature
client: Client,
coin_name: str,
path: str,
script_type: messages.InputScriptType,
no_script_type: bool,
address: str,
message: str,
signature: str,
):
sig = btc.sign_message(
client,
@ -301,50 +306,9 @@ MESSAGE_LENGTHS = (
@pytest.mark.skip_t1
@pytest.mark.parametrize("message", MESSAGE_LENGTHS)
def test_signmessage_pagination(client: Client, message: str):
message_read = ""
def input_flow_tt():
# collect screen contents into `message_read`.
# Using a helper debuglink function to assemble the final text.
nonlocal message_read
layouts: list[LayoutContent] = []
# confirm address
br = yield
client.debug.wait_layout()
client.debug.press_yes()
br = yield
for i in range(br.pages):
layout = client.debug.wait_layout()
layouts.append(layout)
if i < br.pages - 1:
client.debug.swipe_up()
message_read = multipage_content(layouts)
client.debug.press_yes()
def input_flow_tr():
# confirm address
yield
client.debug.press_yes()
br = yield
# TODO: try load the message_read the same way as in model T
if br.pages is not None:
for i in range(br.pages):
if i < br.pages - 1:
client.debug.swipe_up()
client.debug.press_yes()
with client:
if client.features.model == "T":
client.set_input_flow(input_flow_tt)
elif client.features.model == "R":
client.set_input_flow(input_flow_tr)
client.debug.watch_layout(True)
IF = InputFlowSignMessagePagination(client)
client.set_input_flow(IF.get())
btc.sign_message(
client,
coin_name="Bitcoin",
@ -358,7 +322,7 @@ def test_signmessage_pagination(client: Client, message: str):
expected_message = (
("Confirm message: " + message).replace("\n", "").replace(" ", "")
)
message_read = message_read.replace(" ", "")
message_read = IF.message_read.replace(" ", "")
assert expected_message == message_read

View File

@ -23,6 +23,11 @@ from trezorlib.debuglink import TrezorClientDebugLink as Client
from trezorlib.exceptions import TrezorFailure
from trezorlib.tools import H_, parse_path
from ...input_flows import (
InputFlowLockTimeBlockHeight,
InputFlowLockTimeDatetime,
InputFlowSignTxHighFee,
)
from ...tx_cache import TxCache
from .signtx import (
assert_tx_matches,
@ -657,33 +662,13 @@ def test_fee_high_hardfail(client: Client):
client, safety_checks=messages.SafetyCheckLevel.PromptTemporarily
)
with client:
tt = client.features.model == "T"
finished = False
def input_flow():
nonlocal finished
for expected in (
B.ConfirmOutput,
(tt, B.ConfirmOutput),
B.FeeOverThreshold,
B.SignTx,
(tt, B.SignTx),
):
if isinstance(expected, tuple):
is_valid, expected = expected
if not is_valid:
continue
br = yield
assert br.code == expected
client.debug.press_yes()
finished = True
client.set_input_flow(input_flow)
IF = InputFlowSignTxHighFee(client)
client.set_input_flow(IF.get())
_, serialized_tx = btc.sign_tx(
client, "Testnet", [inp1], [out1], prev_txes=TX_CACHE_TESTNET
)
assert finished
assert IF.finished
# Transaction does not exist on the blockchain, not using assert_tx_matches()
assert (
@ -1486,47 +1471,9 @@ def test_lock_time_blockheight(client: Client):
script_type=messages.OutputScriptType.PAYTOADDRESS,
)
def input_flow_tt():
yield # confirm output
client.debug.wait_layout()
client.debug.press_yes()
yield # confirm output
client.debug.wait_layout()
client.debug.press_yes()
yield # confirm locktime
layout = client.debug.wait_layout()
assert "blockheight" in layout.text
assert "499999999" in layout.text
client.debug.press_yes()
yield # confirm transaction
client.debug.press_yes()
yield # confirm transaction
client.debug.press_yes()
def input_flow_tr():
yield # confirm output
client.debug.wait_layout()
client.debug.swipe_up()
client.debug.wait_layout()
client.debug.press_yes()
yield # confirm locktime
layout = client.debug.wait_layout()
assert "blockheight" in layout.text
assert "499999999" in layout.text
client.debug.press_yes()
yield # confirm transaction
client.debug.press_yes()
with client:
if client.debug.model == "T":
client.set_input_flow(input_flow_tt)
elif client.debug.model == "R":
client.set_input_flow(input_flow_tr)
client.watch_layout(True)
IF = InputFlowLockTimeBlockHeight(client, "499999999")
client.set_input_flow(IF.get())
btc.sign_tx(
client,
@ -1559,50 +1506,13 @@ def test_lock_time_datetime(client: Client, lock_time_str: str):
script_type=messages.OutputScriptType.PAYTOADDRESS,
)
def input_flow_tt():
yield # confirm output
client.debug.wait_layout()
client.debug.press_yes()
yield # confirm output
client.debug.wait_layout()
client.debug.press_yes()
yield # confirm locktime
layout = client.debug.wait_layout()
assert lock_time_str in layout.text
client.debug.press_yes()
yield # confirm transaction
client.debug.press_yes()
yield # confirm transaction
client.debug.press_yes()
def input_flow_tr():
yield # confirm output
client.debug.wait_layout()
client.debug.swipe_up()
client.debug.wait_layout()
client.debug.press_yes()
yield # confirm locktime
layout = client.debug.wait_layout()
assert lock_time_str in layout.text
client.debug.press_yes()
yield # confirm transaction
client.debug.press_yes()
lock_time_naive = datetime.strptime(lock_time_str, "%Y-%m-%d %H:%M:%S")
lock_time_utc = lock_time_naive.replace(tzinfo=timezone.utc)
lock_time_timestamp = int(lock_time_utc.timestamp())
with client:
if client.debug.model == "T":
client.set_input_flow(input_flow_tt)
elif client.debug.model == "R":
client.set_input_flow(input_flow_tr)
client.watch_layout(True)
IF = InputFlowLockTimeDatetime(client, lock_time_str)
client.set_input_flow(IF.get())
btc.sign_tx(
client,

View File

@ -23,6 +23,7 @@ from trezorlib.debuglink import TrezorClientDebugLink as Client
from trezorlib.exceptions import TrezorFailure
from trezorlib.tools import parse_path
from ...input_flows import InputFlowPaymentRequestDetails
from .payment_req import CoinPurchaseMemo, RefundMemo, TextMemo, make_payment_request
from .signtx import forge_prevtx
@ -194,35 +195,9 @@ def test_payment_request_details(client: Client):
)
]
def input_flow():
yield # request to see details
client.debug.wait_layout()
client.debug.press_info()
yield # confirm first output
layout = client.debug.wait_layout()
assert outputs[0].address[:16] in layout.text
client.debug.press_yes()
yield # confirm first output
client.debug.wait_layout()
client.debug.press_yes()
yield # confirm second output
layout = client.debug.wait_layout()
assert outputs[1].address[:16] in layout.text
client.debug.press_yes()
yield # confirm second output
client.debug.wait_layout()
client.debug.press_yes()
yield # confirm transaction
client.debug.press_yes()
yield # confirm transaction
client.debug.press_yes()
with client:
client.set_input_flow(input_flow)
client.watch_layout(True)
IF = InputFlowPaymentRequestDetails(client, outputs)
client.set_input_flow(IF.get())
_, serialized_tx = btc.sign_tx(
client,

View File

@ -21,8 +21,7 @@ from trezorlib.debuglink import TrezorClientDebugLink as Client
from trezorlib.tools import parse_path
from ...common import parametrize_using_common_fixtures
SHOW_MORE = (143, 167)
from ...input_flows import InputFlowEIP712Cancel, InputFlowEIP712ShowMore
pytestmark = [pytest.mark.altcoin, pytest.mark.ethereum]
@ -95,65 +94,12 @@ DATA = {
}
def _confirm_show_more(client: Client) -> None:
"""Model-specific, either clicks a screen or presses a button."""
if client.features.model == "T":
client.debug.click(SHOW_MORE)
elif client.features.model == "R":
client.debug.press_yes()
def input_flow_show_more(client: Client):
"""Clicks show_more button wherever possible"""
yield # confirm domain
client.debug.wait_layout()
_confirm_show_more(client)
# confirm domain properties
for _ in range(4):
yield
client.debug.press_yes()
yield # confirm message
client.debug.wait_layout()
_confirm_show_more(client)
yield # confirm message.from
client.debug.wait_layout()
_confirm_show_more(client)
# confirm message.from properties
for _ in range(2):
yield
client.debug.press_yes()
yield # confirm message.to
client.debug.wait_layout()
_confirm_show_more(client)
# confirm message.to properties
for _ in range(2):
yield
client.debug.press_yes()
yield # confirm message.contents
client.debug.press_yes()
yield # confirm final hash
client.debug.press_yes()
def input_flow_cancel(client: Client):
"""Clicks cancelling button"""
yield # confirm domain
client.debug.press_no()
@pytest.mark.skip_t1
def test_ethereum_sign_typed_data_show_more_button(client: Client):
with client:
client.watch_layout()
client.set_input_flow(input_flow_show_more(client))
IF = InputFlowEIP712ShowMore(client)
client.set_input_flow(IF.get())
ethereum.sign_typed_data(
client,
parse_path("m/44h/60h/0h/0/0"),
@ -169,7 +115,8 @@ def test_ethereum_sign_typed_data_cancel(client: Client):
with client, pytest.raises(exceptions.Cancelled):
client.watch_layout()
client.set_input_flow(input_flow_cancel(client))
IF = InputFlowEIP712Cancel(client)
client.set_input_flow(IF.get())
ethereum.sign_typed_data(
client,
parse_path("m/44h/60h/0h/0/0"),

View File

@ -22,10 +22,14 @@ from trezorlib.exceptions import TrezorFailure
from trezorlib.tools import parse_path
from ...common import parametrize_using_common_fixtures
from ...input_flows import (
InputFlowEthereumSignTxGoBack,
InputFlowEthereumSignTxScrollDown,
InputFlowEthereumSignTxSkip,
)
TO_ADDR = "0x1d1c328764a41bda0492b66baa30c4a339ff85ef"
SHOW_ALL = (143, 167)
GO_BACK = (16, 220)
pytestmark = [pytest.mark.altcoin, pytest.mark.ethereum]
@ -329,103 +333,19 @@ def test_sanity_checks_eip1559(client: Client):
def input_flow_skip(client: Client, cancel: bool = False):
yield # confirm address
client.debug.press_yes()
yield # confirm amount
client.debug.wait_layout()
client.debug.press_yes()
yield # confirm data
if cancel:
client.debug.press_no()
else:
client.debug.press_yes()
yield # gas price
client.debug.press_yes()
yield # maximum fee
client.debug.press_yes()
yield # hold to confirm
client.debug.press_yes()
return InputFlowEthereumSignTxSkip(client, cancel).get()
def input_flow_scroll_down(client: Client, cancel: bool = False):
if client.features.model == "R":
pytest.skip("Freezes")
yield # confirm address
client.debug.wait_layout()
client.debug.press_yes()
yield # confirm amount
client.debug.wait_layout()
if client.features.model == "R":
client.debug.swipe_up() # extra confirmation screen for model R
client.debug.press_yes()
# TODO: For model R, we do not have the "Show all" button, so users
# do not have the possibility to skip scrolling.
if client.features.model == "T":
yield # confirm data
client.debug.wait_layout()
client.debug.click(SHOW_ALL)
br = yield # paginated data
for i in range(br.pages):
client.debug.wait_layout()
if i < br.pages - 1:
client.debug.swipe_up()
client.debug.press_yes()
yield # confirm data
if cancel:
client.debug.press_no()
else:
client.debug.press_yes()
yield # gas price
client.debug.press_yes()
yield # maximum fee
client.debug.press_yes()
yield # hold to confirm
client.debug.press_yes()
return InputFlowEthereumSignTxScrollDown(client, cancel).get()
def input_flow_go_back(client: Client, cancel: bool = False):
if client.features.model == "R":
pytest.skip("Go back not supported for model R")
br = yield # confirm address
client.debug.wait_layout()
client.debug.press_yes()
br = yield # confirm amount
client.debug.wait_layout()
client.debug.press_yes()
br = yield # confirm data
client.debug.wait_layout()
client.debug.click(SHOW_ALL)
br = yield # paginated data
for i in range(br.pages):
client.debug.wait_layout()
if i == 2:
client.debug.click(GO_BACK)
yield # confirm data
client.debug.wait_layout()
if cancel:
client.debug.press_no()
else:
client.debug.press_yes()
yield # confirm address
client.debug.wait_layout()
client.debug.press_yes()
yield # confirm amount
client.debug.wait_layout()
client.debug.press_yes()
yield # hold to confirm
client.debug.wait_layout()
client.debug.press_yes()
return
elif i < br.pages - 1:
client.debug.swipe_up()
return InputFlowEthereumSignTxGoBack(client, cancel).get()
HEXDATA = "0123456789abcd000023456789abcd010003456789abcd020000456789abcd030000056789abcd040000006789abcd050000000789abcd060000000089abcd070000000009abcd080000000000abcd090000000001abcd0a0000000011abcd0b0000000111abcd0c0000001111abcd0d0000011111abcd0e0000111111abcd0f0000000002abcd100000000022abcd110000000222abcd120000002222abcd130000022222abcd140000222222abcd15"

View File

@ -14,18 +14,21 @@
# 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 Any, List
from typing import Any
import pytest
from trezorlib import device, exceptions, messages
from trezorlib.debuglink import TrezorClientDebugLink as Client
from ... import buttons
from ...common import MNEMONIC12
from ...input_flows import (
InputFlowBip39RecoveryDryRun,
InputFlowBip39RecoveryDryRunInvalid,
)
def do_recover_legacy(client: Client, mnemonic: List[str], **kwargs: Any):
def do_recover_legacy(client: Client, mnemonic: list[str], **kwargs: Any):
def input_callback(_):
word, pos = client.debug.read_recovery_word()
if pos != 0 and pos is not None:
@ -49,95 +52,17 @@ def do_recover_legacy(client: Client, mnemonic: List[str], **kwargs: Any):
return ret
def do_recover_core(client: Client, mnemonic: List[str], **kwargs: Any):
layout = client.debug.wait_layout
def input_flow():
yield
assert "check the recovery seed" in layout().get_content()
client.debug.click(buttons.OK)
yield
assert "select the number of words" in layout().get_content()
client.debug.click(buttons.OK)
yield
assert "SelectWordCount" in layout().text
# click the number
word_option_offset = 6
word_options = (12, 18, 20, 24, 33)
index = word_option_offset + word_options.index(len(mnemonic))
client.debug.click(buttons.grid34(index % 3, index // 3))
yield
assert "enter your recovery seed" in layout().get_content()
client.debug.click(buttons.OK)
yield
for word in mnemonic:
client.debug.wait_layout()
client.debug.input(word)
yield
client.debug.wait_layout()
client.debug.click(buttons.OK)
def do_recover_core(client: Client, mnemonic: list[str], **kwargs: Any):
with client:
client.watch_layout()
client.set_input_flow(input_flow)
IF = InputFlowBip39RecoveryDryRun(client, mnemonic)
client.set_input_flow(IF.get())
return device.recover(client, dry_run=True, show_tutorial=False, **kwargs)
def do_recover_r(client: Client, mnemonic: List[str], **kwargs: Any):
layout = client.debug.wait_layout
def input_flow():
yield
assert "check the recovery seed" in layout().text
client.debug.press_right()
yield
assert "select the number of words" in layout().text
client.debug.press_yes()
yield
yield
assert "NUMBER OF WORDS" in layout().text
word_options = (12, 18, 20, 24, 33)
index = word_options.index(len(mnemonic))
for _ in range(index):
client.debug.press_right()
client.debug.input(str(len(mnemonic)))
yield
assert "enter your recovery seed" in layout().text
client.debug.press_yes()
yield
client.debug.press_right()
yield
for word in mnemonic:
assert "WORD" in layout().text
client.debug.input(word)
client.debug.wait_layout()
client.debug.press_right()
yield
client.debug.press_yes()
yield
with client:
client.watch_layout()
client.set_input_flow(input_flow)
return device.recover(client, dry_run=True, show_tutorial=False, **kwargs)
def do_recover(client: Client, mnemonic: List[str]):
def do_recover(client: Client, mnemonic: list[str]):
if client.features.model == "1":
return do_recover_legacy(client, mnemonic)
elif client.features.model == "R":
return do_recover_r(client, mnemonic)
else:
return do_recover_core(client, mnemonic)
@ -165,93 +90,10 @@ def test_invalid_seed_t1(client: Client):
@pytest.mark.skip_t1
def test_invalid_seed_core(client: Client):
layout = client.debug.wait_layout
def input_flow_tt():
yield
assert "check the recovery seed" in layout().get_content()
client.debug.click(buttons.OK)
yield
assert "select the number of words" in layout().get_content()
client.debug.click(buttons.OK)
yield
assert "SelectWordCount" in layout().text
# select 12 words
client.debug.click(buttons.grid34(0, 2))
yield
assert "enter your recovery seed" in layout().get_content()
client.debug.click(buttons.OK)
yield
for _ in range(12):
assert layout().text == "< MnemonicKeyboard >"
client.debug.input("stick")
br = yield
assert br.code == messages.ButtonRequestType.Warning
assert "invalid recovery seed" in layout().get_content()
client.debug.click(buttons.OK)
yield
# retry screen
assert "select the number of words" in layout().get_content()
client.debug.click(buttons.CANCEL)
yield
assert "ABORT SEED CHECK" == layout().get_title()
client.debug.click(buttons.OK)
def input_flow_tr():
yield
assert "check the recovery seed" in layout().text
client.debug.press_right()
yield
assert "select the number of words" in layout().text
client.debug.press_yes()
yield
yield
assert "NUMBER OF WORDS" in layout().text
# select 12 words
client.debug.press_middle()
yield
assert "enter your recovery seed" in layout().text
client.debug.press_yes()
yield
assert "WORD ENTERING" in layout().text
client.debug.press_yes()
yield
for _ in range(12):
assert "WORD" in layout().text
client.debug.input("stick")
br = yield
assert br.code == messages.ButtonRequestType.Warning
assert "invalid recovery seed" in layout().text
client.debug.press_right()
yield
# retry screen
assert "select the number of words" in layout().text
client.debug.press_left()
yield
assert "abort" in layout().text
client.debug.press_right()
with client:
client.watch_layout()
if client.features.model == "T":
client.set_input_flow(input_flow_tt)
elif client.features.model == "R":
client.set_input_flow(input_flow_tr)
IF = InputFlowBip39RecoveryDryRunInvalid(client)
client.set_input_flow(IF.get())
with pytest.raises(exceptions.Cancelled):
return device.recover(client, dry_run=True, show_tutorial=False)

View File

@ -20,99 +20,16 @@ from trezorlib import device, exceptions, messages
from trezorlib.debuglink import TrezorClientDebugLink as Client
from ...common import MNEMONIC12
from ...input_flows import InputFlowBip39RecoveryNoPIN, InputFlowBip39RecoveryPIN
pytestmark = pytest.mark.skip_t1
@pytest.mark.setup_client(uninitialized=True)
def test_tt_pin_passphrase(client: Client):
layout = client.debug.wait_layout
mnemonic = MNEMONIC12.split(" ")
def input_flow_tt():
yield
assert "Do you really want to recover a wallet?" in layout().get_content()
client.debug.press_yes()
yield
assert layout().text == "< PinKeyboard >"
client.debug.input("654")
yield
assert layout().text == "< PinKeyboard >"
client.debug.input("654")
yield
assert "select the number of words" in layout().get_content()
client.debug.press_yes()
yield
assert "SelectWordCount" in layout().text
client.debug.input(str(len(mnemonic)))
yield
assert "enter your recovery seed" in layout().get_content()
client.debug.press_yes()
yield
for word in mnemonic:
assert layout().text == "< MnemonicKeyboard >"
client.debug.input(word)
yield
assert "You have successfully recovered your wallet." in layout().get_content()
client.debug.press_yes()
def input_flow_tr():
yield
assert "By continuing you agree" in layout().text
client.debug.press_right()
assert "trezor.io/tos" in layout().text
client.debug.press_yes()
yield
assert "ENTER" in layout().text
client.debug.input("654")
yield
assert "re-enter PIN to confirm" in layout().text
client.debug.press_right()
yield
assert "ENTER" in layout().text
client.debug.input("654")
yield
assert "select the number of words" in layout().text
client.debug.press_yes()
yield
yield
assert "NUMBER OF WORDS" in layout().text
client.debug.input(str(len(mnemonic)))
yield
assert "enter your recovery seed" in layout().text
client.debug.press_yes()
yield
assert "WORD ENTERING" in layout().text
client.debug.press_right()
yield
for word in mnemonic:
assert "WORD" in layout().text
client.debug.input(word)
yield
assert "You have finished recovering your wallet." in layout().text
client.debug.press_yes()
with client:
if client.features.model == "T":
client.set_input_flow(input_flow_tt)
elif client.features.model == "R":
client.set_input_flow(input_flow_tr)
IF = InputFlowBip39RecoveryPIN(client, MNEMONIC12.split(" "))
client.set_input_flow(IF.get())
client.watch_layout()
device.recover(
client,
@ -132,73 +49,9 @@ def test_tt_pin_passphrase(client: Client):
@pytest.mark.setup_client(uninitialized=True)
def test_tt_nopin_nopassphrase(client: Client):
layout = client.debug.wait_layout
mnemonic = MNEMONIC12.split(" ")
def input_flow_tt():
yield
assert "Do you really want to recover a wallet?" in layout().get_content()
client.debug.press_yes()
yield
assert "select the number of words" in layout().get_content()
client.debug.press_yes()
yield
assert "SelectWordCount" in layout().text
client.debug.input(str(len(mnemonic)))
yield
assert "enter your recovery seed" in layout().get_content()
client.debug.press_yes()
yield
for word in mnemonic:
assert layout().text == "< MnemonicKeyboard >"
client.debug.input(word)
yield
assert "You have successfully recovered your wallet." in layout().get_content()
client.debug.press_yes()
def input_flow_tr():
yield
assert "By continuing you agree" in layout().text
client.debug.press_right()
assert "trezor.io/tos" in layout().text
client.debug.press_yes()
yield
assert "select the number of words" in layout().text
client.debug.press_yes()
yield
yield
assert "NUMBER OF WORDS" in layout().text
client.debug.input(str(len(mnemonic)))
yield
assert "enter your recovery seed" in layout().text
client.debug.press_yes()
yield
assert "WORD ENTERING" in layout().text
client.debug.press_right()
yield
for word in mnemonic:
assert "WORD" in layout().text
client.debug.input(word)
yield
assert "You have finished recovering your wallet." in layout().text
client.debug.press_yes()
with client:
if client.features.model == "T":
client.set_input_flow(input_flow_tt)
elif client.features.model == "R":
client.set_input_flow(input_flow_tr)
IF = InputFlowBip39RecoveryNoPIN(client, MNEMONIC12.split(" "))
client.set_input_flow(IF.get())
client.watch_layout()
device.recover(
client,

View File

@ -19,11 +19,12 @@ import pytest
from trezorlib import device, exceptions, messages
from trezorlib.debuglink import TrezorClientDebugLink as Client
from ...common import (
MNEMONIC_SLIP39_ADVANCED_20,
MNEMONIC_SLIP39_ADVANCED_33,
recovery_enter_shares,
recovery_enter_shares_tr,
from ...common import MNEMONIC_SLIP39_ADVANCED_20, MNEMONIC_SLIP39_ADVANCED_33
from ...input_flows import (
InputFlowSlip39AdvancedRecovery,
InputFlowSlip39AdvancedRecoveryAbort,
InputFlowSlip39AdvancedRecoveryNoAbort,
InputFlowSlip39AdvancedRecoveryTwoSharesWarning,
)
pytestmark = pytest.mark.skip_t1
@ -46,27 +47,9 @@ VECTORS = (
def _test_secret(
client: Client, shares: list[str], secret: str, click_info: bool = False
):
debug = client.debug
def input_flow_tt():
yield # Confirm Recovery
debug.press_yes()
# Proceed with recovery
yield from recovery_enter_shares(
debug, shares, groups=True, click_info=click_info
)
def input_flow_tr():
yield # Confirm Recovery
debug.press_yes()
# Proceed with recovery
yield from recovery_enter_shares_tr(debug, shares, groups=True)
with client:
if client.features.model == "T":
client.set_input_flow(input_flow_tt)
elif client.features.model == "R":
client.set_input_flow(input_flow_tr)
IF = InputFlowSlip39AdvancedRecovery(client, shares, click_info=click_info)
client.set_input_flow(IF.get())
ret = device.recover(
client,
pin_protection=False,
@ -81,7 +64,7 @@ def _test_secret(
assert client.features.pin_protection is False
assert client.features.passphrase_protection is False
assert client.features.backup_type is messages.BackupType.Slip39_Advanced
assert debug.state().mnemonic_secret.hex() == secret
assert client.debug.state().mnemonic_secret.hex() == secret
@pytest.mark.parametrize("shares, secret", VECTORS)
@ -107,18 +90,9 @@ def test_extra_share_entered(client: Client):
@pytest.mark.setup_client(uninitialized=True)
def test_abort(client: Client):
debug = client.debug
def input_flow():
yield # Confirm Recovery
debug.press_yes()
yield # Homescreen - abort process
debug.press_no()
yield # Homescreen - confirm abort
debug.press_yes()
with client:
client.set_input_flow(input_flow)
IF = InputFlowSlip39AdvancedRecoveryAbort(client)
client.set_input_flow(IF.get())
with pytest.raises(exceptions.Cancelled):
device.recover(
client, pin_protection=False, label="label", show_tutorial=False
@ -129,35 +103,11 @@ def test_abort(client: Client):
@pytest.mark.setup_client(uninitialized=True)
def test_noabort(client: Client):
debug = client.debug
def input_flow_tt():
yield # Confirm Recovery
debug.press_yes()
yield # Homescreen - abort process
debug.press_no()
yield # Homescreen - go back to process
debug.press_no()
yield from recovery_enter_shares(
debug, EXTRA_GROUP_SHARE + MNEMONIC_SLIP39_ADVANCED_20, groups=True
)
def input_flow_tr():
yield # Confirm Recovery
debug.press_yes()
yield # Homescreen - abort process
debug.press_no()
yield # Homescreen - go back to process
debug.press_no()
yield from recovery_enter_shares_tr(
debug, EXTRA_GROUP_SHARE + MNEMONIC_SLIP39_ADVANCED_20, groups=True
)
with client:
if client.features.model == "T":
client.set_input_flow(input_flow_tt)
elif client.features.model == "R":
client.set_input_flow(input_flow_tr)
IF = InputFlowSlip39AdvancedRecoveryNoAbort(
client, EXTRA_GROUP_SHARE + MNEMONIC_SLIP39_ADVANCED_20
)
client.set_input_flow(IF.get())
device.recover(client, pin_protection=False, label="label", show_tutorial=False)
client.init_device()
assert client.features.initialized is True
@ -165,78 +115,17 @@ def test_noabort(client: Client):
@pytest.mark.setup_client(uninitialized=True)
def test_same_share(client: Client):
debug = client.debug
# we choose the second share from the fixture because
# the 1st is 1of1 and group threshold condition is reached first
first_share = MNEMONIC_SLIP39_ADVANCED_20[1].split(" ")
# second share is first 4 words of first
second_share = MNEMONIC_SLIP39_ADVANCED_20[1].split(" ")[:4]
def input_flow_tt():
yield # Confirm Recovery
debug.press_yes()
yield # Homescreen - start process
debug.press_yes()
yield # Enter number of words
debug.input(str(len(first_share)))
yield # Homescreen - proceed to share entry
debug.press_yes()
yield # Enter first share
for word in first_share:
debug.input(word)
yield # Continue to next share
debug.press_yes()
yield # Homescreen - next share
debug.press_yes()
yield # Enter next share
for word in second_share:
debug.input(word)
br = yield
assert br.code == messages.ButtonRequestType.Warning
client.cancel()
def input_flow_tr():
yield # Confirm Recovery
debug.press_yes()
yield # Homescreen - start process
debug.press_yes()
yield # Enter number of words
debug.input(str(len(first_share)))
yield # Homescreen - proceed to share entry
debug.press_yes()
yield # Enter first share
debug.press_yes()
yield # Enter first share
for word in first_share:
debug.input(word)
yield # Continue to next share
debug.press_yes()
yield # Homescreen - next share
debug.press_yes()
yield # Homescreen - next share
debug.press_yes()
yield # Enter next share
for word in second_share:
debug.input(word)
yield
br = yield
assert br.code == messages.ButtonRequestType.Warning
debug.press_right()
debug.press_yes()
yield
client.cancel()
with client:
if client.features.model == "T":
client.set_input_flow(input_flow_tt)
elif client.features.model == "R":
client.set_input_flow(input_flow_tr)
IF = InputFlowSlip39AdvancedRecoveryTwoSharesWarning(
client, first_share, second_share
)
client.set_input_flow(IF.get())
with pytest.raises(exceptions.Cancelled):
device.recover(
client, pin_protection=False, label="label", show_tutorial=False
@ -245,77 +134,16 @@ def test_same_share(client: Client):
@pytest.mark.setup_client(uninitialized=True)
def test_group_threshold_reached(client: Client):
debug = client.debug
# first share in the fixture is 1of1 so we choose that
first_share = MNEMONIC_SLIP39_ADVANCED_20[0].split(" ")
# second share is first 3 words of first
second_share = MNEMONIC_SLIP39_ADVANCED_20[0].split(" ")[:3]
def input_flow_tt():
yield # Confirm Recovery
debug.press_yes()
yield # Homescreen - start process
debug.press_yes()
yield # Enter number of words
debug.input(str(len(first_share)))
yield # Homescreen - proceed to share entry
debug.press_yes()
yield # Enter first share
for word in first_share:
debug.input(word)
yield # Continue to next share
debug.press_yes()
yield # Homescreen - next share
debug.press_yes()
yield # Enter next share
for word in second_share:
debug.input(word)
br = yield
assert br.code == messages.ButtonRequestType.Warning
client.cancel()
def input_flow_tr():
yield # Confirm Recovery
debug.press_yes()
yield # Homescreen - start process
debug.press_yes()
yield # Enter number of words
debug.input(str(len(first_share)))
yield # Homescreen - proceed to share entry
debug.press_yes()
yield # Enter first share
debug.press_yes()
yield # Enter first share
for word in first_share:
debug.input(word)
yield # Continue to next share
debug.press_yes()
yield # Homescreen - next share
debug.press_yes()
yield # Enter next share
debug.press_yes()
yield # Enter next share
for word in second_share:
debug.input(word)
br = yield
br = yield
assert br.code == messages.ButtonRequestType.Warning
debug.press_right()
debug.press_yes()
yield
client.cancel()
with client:
if client.features.model == "T":
client.set_input_flow(input_flow_tt)
elif client.features.model == "R":
client.set_input_flow(input_flow_tr)
IF = InputFlowSlip39AdvancedRecoveryTwoSharesWarning(
client, first_share, second_share
)
client.set_input_flow(IF.get())
with pytest.raises(exceptions.Cancelled):
device.recover(
client, pin_protection=False, label="label", show_tutorial=False

View File

@ -20,11 +20,8 @@ from trezorlib import device, messages
from trezorlib.debuglink import TrezorClientDebugLink as Client
from trezorlib.exceptions import TrezorFailure
from ...common import (
MNEMONIC_SLIP39_ADVANCED_20,
recovery_enter_shares,
recovery_enter_shares_tr,
)
from ...common import MNEMONIC_SLIP39_ADVANCED_20
from ...input_flows import InputFlowSlip39AdvancedRecoveryDryRun
pytestmark = pytest.mark.skip_t1
@ -43,29 +40,11 @@ EXTRA_GROUP_SHARE = [
@pytest.mark.setup_client(mnemonic=MNEMONIC_SLIP39_ADVANCED_20, passphrase=False)
def test_2of3_dryrun(client: Client):
debug = client.debug
def input_flow_tt():
yield # Confirm Dryrun
debug.press_yes()
# run recovery flow
yield from recovery_enter_shares(
debug, EXTRA_GROUP_SHARE + MNEMONIC_SLIP39_ADVANCED_20, groups=True
)
def input_flow_tr():
yield # Confirm Dryrun
debug.press_yes()
# run recovery flow
yield from recovery_enter_shares_tr(
debug, EXTRA_GROUP_SHARE + MNEMONIC_SLIP39_ADVANCED_20, groups=True
)
with client:
if client.features.model == "T":
client.set_input_flow(input_flow_tt)
elif client.features.model == "R":
client.set_input_flow(input_flow_tr)
IF = InputFlowSlip39AdvancedRecoveryDryRun(
client, EXTRA_GROUP_SHARE + MNEMONIC_SLIP39_ADVANCED_20
)
client.set_input_flow(IF.get())
ret = device.recover(
client,
passphrase_protection=False,
@ -84,32 +63,14 @@ def test_2of3_dryrun(client: Client):
@pytest.mark.setup_client(mnemonic=MNEMONIC_SLIP39_ADVANCED_20)
def test_2of3_invalid_seed_dryrun(client: Client):
debug = client.debug
def input_flow_tt():
yield # Confirm Dryrun
debug.press_yes()
# run recovery flow
yield from recovery_enter_shares(
debug, INVALID_SHARES_SLIP39_ADVANCED_20, groups=True
)
def input_flow_tr():
yield # Confirm Dryrun
debug.press_yes()
# run recovery flow
yield from recovery_enter_shares_tr(
debug, INVALID_SHARES_SLIP39_ADVANCED_20, groups=True
)
# test fails because of different seed on device
with client, pytest.raises(
TrezorFailure, match=r"The seed does not match the one in the device"
):
if client.features.model == "T":
client.set_input_flow(input_flow_tt)
elif client.features.model == "R":
client.set_input_flow(input_flow_tr)
IF = InputFlowSlip39AdvancedRecoveryDryRun(
client, INVALID_SHARES_SLIP39_ADVANCED_20
)
client.set_input_flow(IF.get())
device.recover(
client,
passphrase_protection=False,

View File

@ -22,8 +22,16 @@ from trezorlib.debuglink import TrezorClientDebugLink as Client
from ...common import (
MNEMONIC_SLIP39_BASIC_20_3of6,
MNEMONIC_SLIP39_BASIC_20_3of6_SECRET,
recovery_enter_shares,
recovery_enter_shares_tr,
)
from ...input_flows import (
InputFlowSlip39BasicRecovery,
InputFlowSlip39BasicRecoveryAbort,
InputFlowSlip39BasicRecoveryNoAbort,
InputFlowSlip39BasicRecoveryPIN,
InputFlowSlip39BasicRecoveryRetryFirst,
InputFlowSlip39BasicRecoveryRetrySecond,
InputFlowSlip39BasicRecoverySameShare,
InputFlowSlip39BasicRecoveryWrongNthWord,
)
pytestmark = pytest.mark.skip_t1
@ -50,25 +58,9 @@ VECTORS = (
@pytest.mark.setup_client(uninitialized=True)
@pytest.mark.parametrize("shares, secret", VECTORS)
def test_secret(client: Client, shares: list[str], secret: str):
debug = client.debug
def input_flow_tt():
yield # Confirm Recovery
debug.press_yes()
# run recovery flow
yield from recovery_enter_shares(debug, shares)
def input_flow_tr():
yield # Confirm Recovery
debug.press_yes()
# run recovery flow
yield from recovery_enter_shares_tr(debug, shares)
with client:
if client.features.model == "T":
client.set_input_flow(input_flow_tt)
elif client.features.model == "R":
client.set_input_flow(input_flow_tr)
IF = InputFlowSlip39BasicRecovery(client, shares)
client.set_input_flow(IF.get())
ret = device.recover(
client, pin_protection=False, label="label", show_tutorial=False
)
@ -80,40 +72,16 @@ def test_secret(client: Client, shares: list[str], secret: str):
assert client.features.backup_type is messages.BackupType.Slip39_Basic
# Check mnemonic
assert debug.state().mnemonic_secret.hex() == secret
assert client.debug.state().mnemonic_secret.hex() == secret
@pytest.mark.setup_client(uninitialized=True)
def test_recover_with_pin_passphrase(client: Client):
debug = client.debug
def input_flow_tt():
yield # Confirm Recovery
debug.press_yes()
yield # Enter PIN
debug.input("654")
yield # Enter PIN again
debug.input("654")
# Proceed with recovery
yield from recovery_enter_shares(debug, MNEMONIC_SLIP39_BASIC_20_3of6)
def input_flow_tr():
yield # Confirm Recovery
debug.press_yes()
yield # Enter PIN
debug.input("654")
yield # Please re-enter PIN to confirm
debug.press_yes()
yield # Enter PIN again
debug.input("654")
# Proceed with recovery
yield from recovery_enter_shares_tr(debug, MNEMONIC_SLIP39_BASIC_20_3of6)
with client:
if client.features.model == "T":
client.set_input_flow(input_flow_tt)
elif client.features.model == "R":
client.set_input_flow(input_flow_tr)
IF = InputFlowSlip39BasicRecoveryPIN(
client, MNEMONIC_SLIP39_BASIC_20_3of6, "654"
)
client.set_input_flow(IF.get())
ret = device.recover(
client,
pin_protection=True,
@ -122,7 +90,7 @@ def test_recover_with_pin_passphrase(client: Client):
show_tutorial=False,
)
# Workflow succesfully ended
# Workflow successfully ended
assert ret == messages.Success(message="Device recovered")
assert client.features.pin_protection is True
assert client.features.passphrase_protection is True
@ -131,18 +99,9 @@ def test_recover_with_pin_passphrase(client: Client):
@pytest.mark.setup_client(uninitialized=True)
def test_abort(client: Client):
debug = client.debug
def input_flow():
yield # Confirm Recovery
debug.press_yes()
yield # Homescreen - abort process
debug.press_no()
yield # Homescreen - confirm abort
debug.press_yes()
with client:
client.set_input_flow(input_flow)
IF = InputFlowSlip39BasicRecoveryAbort(client)
client.set_input_flow(IF.get())
with pytest.raises(exceptions.Cancelled):
device.recover(
client, pin_protection=False, label="label", show_tutorial=False
@ -153,31 +112,9 @@ def test_abort(client: Client):
@pytest.mark.setup_client(uninitialized=True)
def test_noabort(client: Client):
debug = client.debug
def input_flow_tt():
yield # Confirm Recovery
debug.press_yes()
yield # Homescreen - abort process
debug.press_no()
yield # Homescreen - go back to process
debug.press_no()
yield from recovery_enter_shares(debug, MNEMONIC_SLIP39_BASIC_20_3of6)
def input_flow_tr():
yield # Confirm Recovery
debug.press_yes()
yield # Homescreen - abort process
debug.press_no()
yield # Homescreen - go back to process
debug.press_no()
yield from recovery_enter_shares_tr(debug, MNEMONIC_SLIP39_BASIC_20_3of6)
with client:
if client.features.model == "T":
client.set_input_flow(input_flow_tt)
elif client.features.model == "R":
client.set_input_flow(input_flow_tr)
IF = InputFlowSlip39BasicRecoveryNoAbort(client, MNEMONIC_SLIP39_BASIC_20_3of6)
client.set_input_flow(IF.get())
device.recover(client, pin_protection=False, label="label", show_tutorial=False)
client.init_device()
assert client.features.initialized is True
@ -187,98 +124,9 @@ def test_noabort(client: Client):
def test_ask_word_number(client: Client):
if client.features.model == "R":
pytest.skip("Flow is not working correctly for TR")
debug = client.debug
def input_flow_retry_first_tt():
yield # Confirm Recovery
debug.press_yes()
yield # Homescreen - start process
debug.press_yes()
yield # Enter number of words
debug.input("20")
yield # Homescreen - proceed to share entry
debug.press_yes()
yield # Enter first share
for _ in range(20):
debug.input("slush")
br = yield # Invalid share
assert br.code == messages.ButtonRequestType.Warning
debug.press_yes()
yield # Homescreen - start process
debug.press_yes()
yield # Enter number of words
debug.input("33")
yield # Homescreen - proceed to share entry
debug.press_yes()
yield # Enter first share
for _ in range(33):
debug.input("slush")
br = yield # Invalid share
assert br.code == messages.ButtonRequestType.Warning
debug.press_yes()
yield # Homescreen
debug.press_no()
yield # Confirm abort
debug.press_yes()
def input_flow_retry_first_tr():
yield # Confirm Recovery
debug.press_yes()
yield # Homescreen - start process
debug.press_yes()
yield # Enter number of words
debug.input("20")
yield # Homescreen - proceed to share entry
debug.press_yes()
yield # Enter first share
debug.press_yes()
for _ in range(20):
debug.input("slush")
yield
# assert br.code == messages.ButtonRequestType.Warning
debug.press_yes()
yield # Homescreen - start process
debug.press_yes()
yield # Enter number of words
debug.input("33")
yield # Homescreen - proceed to share entry
debug.press_yes()
yield # Homescreen - proceed to share entry
debug.press_yes()
yield
for _ in range(33):
debug.input("slush")
yield
debug.press_yes()
yield
debug.press_no()
yield
debug.press_right()
yield
debug.press_right()
yield
debug.press_right()
yield
debug.press_yes()
with client:
if client.features.model == "T":
client.set_input_flow(input_flow_retry_first_tt)
elif client.features.model == "R":
client.set_input_flow(input_flow_retry_first_tr)
IF = InputFlowSlip39BasicRecoveryRetryFirst(client)
client.set_input_flow(IF.get())
with pytest.raises(exceptions.Cancelled):
device.recover(
client, pin_protection=False, label="label", show_tutorial=False
@ -286,85 +134,11 @@ def test_ask_word_number(client: Client):
client.init_device()
assert client.features.initialized is False
def input_flow_retry_second_tt():
yield # Confirm Recovery
debug.press_yes()
yield # Homescreen - start process
debug.press_yes()
yield # Enter number of words
debug.input("20")
yield # Homescreen - proceed to share entry
debug.press_yes()
yield # Enter first share
share = MNEMONIC_SLIP39_BASIC_20_3of6[0].split(" ")
for word in share:
debug.input(word)
yield # More shares needed
debug.press_yes()
yield # Enter another share
share = share[:3] + ["slush"] * 17
for word in share:
debug.input(word)
br = yield # Invalid share
assert br.code == messages.ButtonRequestType.Warning
debug.press_yes()
yield # Proceed to next share
share = MNEMONIC_SLIP39_BASIC_20_3of6[1].split(" ")
for word in share:
debug.input(word)
yield # More shares needed
debug.press_no()
yield # Confirm abort
debug.press_yes()
def input_flow_retry_second_tr():
yield # Confirm Recovery
debug.press_yes()
yield # Homescreen - start process
debug.press_yes()
yield # Enter number of words
debug.input("20")
yield # Homescreen - proceed to share entry
debug.press_yes()
yield # Enter first share
debug.press_yes()
yield # Enter first share
share = MNEMONIC_SLIP39_BASIC_20_3of6[0].split(" ")
for word in share:
debug.input(word)
yield # More shares needed
debug.press_yes()
yield # Enter another share
share = share[:3] + ["slush"] * 17
for word in share:
debug.input(word)
yield # Invalid share
# assert br.code == messages.ButtonRequestType.Warning
debug.press_yes()
yield # Proceed to next share
share = MNEMONIC_SLIP39_BASIC_20_3of6[1].split(" ")
for word in share:
debug.input(word)
yield # More shares needed
debug.press_no()
yield # Confirm abort
debug.press_yes()
with client:
if client.features.model == "T":
client.set_input_flow(input_flow_retry_second_tt)
elif client.features.model == "R":
client.set_input_flow(input_flow_retry_second_tr)
IF = InputFlowSlip39BasicRecoveryRetrySecond(
client, MNEMONIC_SLIP39_BASIC_20_3of6
)
client.set_input_flow(IF.get())
with pytest.raises(exceptions.Cancelled):
device.recover(
client, pin_protection=False, label="label", show_tutorial=False
@ -376,74 +150,10 @@ def test_ask_word_number(client: Client):
@pytest.mark.setup_client(uninitialized=True)
@pytest.mark.parametrize("nth_word", range(3))
def test_wrong_nth_word(client: Client, nth_word: int):
debug = client.debug
share = MNEMONIC_SLIP39_BASIC_20_3of6[0].split(" ")
def input_flow_tt():
yield # Confirm Recovery
debug.press_yes()
yield # Homescreen - start process
debug.press_yes()
yield # Enter number of words
debug.input(str(len(share)))
yield # Homescreen - proceed to share entry
debug.press_yes()
yield # Enter first share
for word in share:
debug.input(word)
yield # Continue to next share
debug.press_yes()
yield # Enter next share
for i, word in enumerate(share):
if i < nth_word:
debug.input(word)
else:
debug.input(share[-1])
break
br = yield
assert br.code == messages.ButtonRequestType.Warning
client.cancel()
def input_flow_tr():
yield # Confirm Recovery
debug.press_yes()
yield # Homescreen - start process
debug.press_yes()
yield # Enter number of words
debug.input(str(len(share)))
yield # Homescreen - proceed to share entry
debug.press_yes()
yield # Enter first share
debug.press_yes()
yield # Enter first share
for word in share:
debug.input(word)
yield # Continue to next share
debug.press_yes()
yield # Enter next share
debug.press_yes()
yield # Enter next share
for i, word in enumerate(share):
if i < nth_word:
debug.input(word)
else:
debug.input(share[-1])
break
yield
# assert br.code == messages.ButtonRequestType.Warning
client.cancel()
with client:
if client.features.model == "T":
client.set_input_flow(input_flow_tt)
elif client.features.model == "R":
client.set_input_flow(input_flow_tr)
IF = InputFlowSlip39BasicRecoveryWrongNthWord(client, share, nth_word)
client.set_input_flow(IF.get())
with pytest.raises(exceptions.Cancelled):
device.recover(
client, pin_protection=False, label="label", show_tutorial=False
@ -452,74 +162,12 @@ def test_wrong_nth_word(client: Client, nth_word: int):
@pytest.mark.setup_client(uninitialized=True)
def test_same_share(client: Client):
debug = client.debug
first_share = MNEMONIC_SLIP39_BASIC_20_3of6[0].split(" ")
# second share is first 4 words of first
second_share = MNEMONIC_SLIP39_BASIC_20_3of6[0].split(" ")[:4]
def input_flow_tt():
yield # Confirm Recovery
debug.press_yes()
yield # Homescreen - start process
debug.press_yes()
yield # Enter number of words
debug.input(str(len(first_share)))
yield # Homescreen - proceed to share entry
debug.press_yes()
yield # Enter first share
for word in first_share:
debug.input(word)
yield # Continue to next share
debug.press_yes()
yield # Enter next share
for word in second_share:
debug.input(word)
br = yield
assert br.code == messages.ButtonRequestType.Warning
# TODO: seems like the screenshots did not catch the WARNING
client.cancel()
def input_flow_tr():
yield # Confirm Recovery
debug.press_yes()
yield # Homescreen - start process
debug.press_yes()
yield # Enter number of words
debug.input(str(len(first_share)))
yield # Homescreen - proceed to share entry
debug.press_yes()
yield # Homescreen - proceed to share entry
debug.press_yes()
yield # Enter first share
for word in first_share:
debug.input(word)
yield # Continue to next share
debug.press_yes()
yield # Continue to next share
debug.press_yes()
yield # Enter next share
for word in second_share:
debug.input(word)
br = yield
br = yield
assert br.code == messages.ButtonRequestType.Warning
debug.press_right()
debug.press_yes()
yield
client.cancel()
with client:
if client.features.model == "T":
client.set_input_flow(input_flow_tt)
elif client.features.model == "R":
client.set_input_flow(input_flow_tr)
IF = InputFlowSlip39BasicRecoverySameShare(client, first_share, second_share)
client.set_input_flow(IF.get())
with pytest.raises(exceptions.Cancelled):
device.recover(
client, pin_protection=False, label="label", show_tutorial=False
@ -528,29 +176,9 @@ def test_same_share(client: Client):
@pytest.mark.setup_client(uninitialized=True)
def test_1of1(client: Client):
debug = client.debug
def input_flow_tt():
yield # Confirm Recovery
debug.press_yes()
# Proceed with recovery
yield from recovery_enter_shares(
debug, MNEMONIC_SLIP39_BASIC_20_1of1, groups=False
)
def input_flow_tr():
yield # Confirm Recovery
debug.press_yes()
# Proceed with recovery
yield from recovery_enter_shares_tr(
debug, MNEMONIC_SLIP39_BASIC_20_1of1, groups=False
)
with client:
if client.features.model == "T":
client.set_input_flow(input_flow_tt)
elif client.features.model == "R":
client.set_input_flow(input_flow_tr)
IF = InputFlowSlip39BasicRecovery(client, MNEMONIC_SLIP39_BASIC_20_1of1)
client.set_input_flow(IF.get())
ret = device.recover(
client,
pin_protection=False,

View File

@ -20,7 +20,7 @@ from trezorlib import device, messages
from trezorlib.debuglink import TrezorClientDebugLink as Client
from trezorlib.exceptions import TrezorFailure
from ...common import recovery_enter_shares, recovery_enter_shares_tr
from ...input_flows import InputFlowSlip39BasicRecovery
pytestmark = pytest.mark.skip_t1
@ -38,25 +38,9 @@ INVALID_SHARES_20_2of3 = [
@pytest.mark.setup_client(mnemonic=SHARES_20_2of3[0:2])
def test_2of3_dryrun(client: Client):
debug = client.debug
def input_flow_tt():
yield # Confirm Dryrun
debug.press_yes()
# run recovery flow
yield from recovery_enter_shares(debug, SHARES_20_2of3[1:3])
def input_flow_tr():
yield # Confirm Dryrun
debug.press_yes()
# run recovery flow
yield from recovery_enter_shares_tr(debug, SHARES_20_2of3[1:3])
with client:
if client.features.model == "T":
client.set_input_flow(input_flow_tt)
elif client.features.model == "R":
client.set_input_flow(input_flow_tr)
IF = InputFlowSlip39BasicRecovery(client, SHARES_20_2of3[1:3])
client.set_input_flow(IF.get())
ret = device.recover(
client,
passphrase_protection=False,
@ -75,28 +59,12 @@ def test_2of3_dryrun(client: Client):
@pytest.mark.setup_client(mnemonic=SHARES_20_2of3[0:2])
def test_2of3_invalid_seed_dryrun(client: Client):
debug = client.debug
def input_flow_tt():
yield # Confirm Dryrun
debug.press_yes()
# run recovery flow
yield from recovery_enter_shares(debug, INVALID_SHARES_20_2of3)
def input_flow_tr():
yield # Confirm Dryrun
debug.press_yes()
# run recovery flow
yield from recovery_enter_shares_tr(debug, INVALID_SHARES_20_2of3)
# test fails because of different seed on device
with client, pytest.raises(
TrezorFailure, match=r"The seed does not match the one in the device"
):
if client.features.model == "T":
client.set_input_flow(input_flow_tt)
elif client.features.model == "R":
client.set_input_flow(input_flow_tr)
IF = InputFlowSlip39BasicRecovery(client, INVALID_SHARES_20_2of3)
client.set_input_flow(IF.get())
device.recover(
client,
passphrase_protection=False,

View File

@ -24,41 +24,19 @@ from trezorlib import device, messages
from trezorlib.debuglink import TrezorClientDebugLink as Client
from trezorlib.messages import BackupType, ButtonRequestType as B
from ...common import (
EXTERNAL_ENTROPY,
click_through,
read_and_confirm_mnemonic,
read_and_confirm_mnemonic_tr,
from ...common import EXTERNAL_ENTROPY
from ...input_flows import (
InputFlowBip39Backup,
InputFlowResetSkipBackup,
InputFlowSlip39AdvancedBackup,
InputFlowSlip39BasicBackup,
)
def backup_flow_bip39(client: Client):
mnemonic = None
def input_flow():
nonlocal mnemonic
# 1. Confirm Reset
yield from click_through(client.debug, screens=1, code=B.ResetDevice)
# mnemonic phrases
if client.debug.model == "R":
client.debug.watch_layout(True)
mnemonic = yield from read_and_confirm_mnemonic_tr(client.debug)
else:
mnemonic = yield from read_and_confirm_mnemonic(client.debug)
# confirm recovery seed check
br = yield
assert br.code == B.Success
client.debug.press_yes()
# confirm success
br = yield
assert br.code == B.Success
client.debug.press_yes()
def backup_flow_bip39(client: Client) -> bytes:
with client:
IF = InputFlowBip39Backup(client)
client.set_input_flow(IF.get())
client.set_expected_responses(
[
*[
@ -71,74 +49,16 @@ def backup_flow_bip39(client: Client):
messages.Features,
]
)
client.set_input_flow(input_flow)
device.backup(client)
return mnemonic.encode()
assert IF.mnemonic is not None
return IF.mnemonic.encode()
def backup_flow_slip39_basic(client: Client):
mnemonics = []
def input_flow_tt():
# 1. Checklist
# 2. Number of shares (5)
# 3. Checklist
# 4. Threshold (3)
# 5. Checklist
# 6. Confirm show seeds
yield from click_through(client.debug, screens=6, code=B.ResetDevice)
# Mnemonic phrases
for _ in range(5):
# Phrase screen
mnemonic = yield from read_and_confirm_mnemonic(client.debug)
mnemonics.append(mnemonic)
yield # Confirm continue to next
client.debug.press_yes()
# Confirm backup
yield
client.debug.press_yes()
def input_flow_tr():
yield # Checklist
client.debug.press_yes()
yield # Number of shares info
client.debug.press_yes()
yield # Number of shares (5)
client.debug.input("5")
yield # Checklist
client.debug.press_yes()
yield # Threshold info
client.debug.press_yes()
yield # Threshold (3)
client.debug.input("3")
yield # Checklist
client.debug.press_yes()
yield # Confirm show seeds
client.debug.press_yes()
# Mnemonic phrases
for _ in range(5):
# Phrase screen
mnemonic = yield from read_and_confirm_mnemonic_tr(client.debug)
mnemonics.append(mnemonic)
br = yield # Confirm continue to next
assert br.code == B.Success
client.debug.press_yes()
br = yield # Confirm backup
assert br.code == B.Success
client.debug.press_yes()
with client:
if client.features.model == "T":
client.set_input_flow(input_flow_tt)
elif client.features.model == "R":
client.debug.watch_layout(True)
client.set_input_flow(input_flow_tr)
IF = InputFlowSlip39BasicBackup(client, False)
client.set_input_flow(IF.get())
client.set_expected_responses(
[messages.ButtonRequest(code=B.ResetDevice)] * 6 # intro screens
+ [
@ -154,89 +74,15 @@ def backup_flow_slip39_basic(client: Client):
)
device.backup(client)
groups = shamir.decode_mnemonics(mnemonics[:3])
groups = shamir.decode_mnemonics(IF.mnemonics[:3])
ems = shamir.recover_ems(groups)
return ems.ciphertext
def backup_flow_slip39_advanced(client: Client):
mnemonics = []
def input_flow_tt():
# 1. Confirm Reset
# 2. shares info
# 3. Set & Confirm number of groups
# 4. threshold info
# 5. Set & confirm group threshold value
# 6-15: for each of 5 groups:
# 1. Set & Confirm number of shares
# 2. Set & confirm share threshold value
# 16. Confirm show seeds
yield from click_through(client.debug, screens=16, code=B.ResetDevice)
# show & confirm shares for all groups
for _ in range(5):
for _ in range(5):
# mnemonic phrases
mnemonic = yield from read_and_confirm_mnemonic(client.debug)
mnemonics.append(mnemonic)
# Confirm continue to next share
br = yield
assert br.code == B.Success
client.debug.press_yes()
# safety warning
br = yield
assert br.code == B.Success
client.debug.press_yes()
def input_flow_tr():
yield # Checklist
client.debug.press_yes()
yield # Set and confirm group count
client.debug.input("5")
yield # Checklist
client.debug.press_yes()
yield # Set and confirm group threshold
client.debug.input("3")
yield # Checklist
client.debug.press_yes()
for _ in range(5): # for each of 5 groups
yield # Number of shares info
client.debug.press_yes()
yield # Number of shares (5)
client.debug.input("5")
yield # Threshold info
client.debug.press_yes()
yield # Threshold (3)
client.debug.input("3")
yield # Confirm show seeds
client.debug.press_yes()
# show & confirm shares for all groups
for _g in range(5):
for _h in range(5):
# mnemonic phrases
mnemonic = yield from read_and_confirm_mnemonic_tr(client.debug)
mnemonics.append(mnemonic)
# Confirm continue to next share
br = yield
assert br.code == B.Success
client.debug.press_yes()
# safety warning
br = yield
assert br.code == B.Success
client.debug.press_yes()
with client:
if client.features.model == "T":
client.set_input_flow(input_flow_tt)
elif client.features.model == "R":
client.debug.watch_layout(True)
client.set_input_flow(input_flow_tr)
IF = InputFlowSlip39AdvancedBackup(client, False)
client.set_input_flow(IF.get())
client.set_expected_responses(
[messages.ButtonRequest(code=B.ResetDevice)] * 6 # intro screens
+ [
@ -257,7 +103,7 @@ def backup_flow_slip39_advanced(client: Client):
)
device.backup(client)
mnemonics = mnemonics[0:3] + mnemonics[5:8] + mnemonics[10:13]
mnemonics = IF.mnemonics[0:3] + IF.mnemonics[5:8] + IF.mnemonics[10:13]
groups = shamir.decode_mnemonics(mnemonics)
ems = shamir.recover_ems(groups)
return ems.ciphertext
@ -309,19 +155,10 @@ def test_skip_backup_msg(client: Client, backup_type, backup_flow):
@pytest.mark.parametrize("backup_type, backup_flow", VECTORS)
@pytest.mark.setup_client(uninitialized=True)
def test_skip_backup_manual(client: Client, backup_type, backup_flow):
def reset_skip_input_flow():
yield # Confirm Recovery
client.debug.press_yes()
yield # Skip Backup
client.debug.press_no()
yield # Confirm skip backup
client.debug.press_no()
os_urandom = mock.Mock(return_value=EXTERNAL_ENTROPY)
with mock.patch("os.urandom", os_urandom), client:
client.set_input_flow(reset_skip_input_flow)
IF = InputFlowResetSkipBackup(client)
client.set_input_flow(IF.get())
client.set_expected_responses(
[
messages.ButtonRequest(code=B.ResetDevice),

View File

@ -24,12 +24,11 @@ from trezorlib.debuglink import TrezorClientDebugLink as Client
from trezorlib.exceptions import TrezorFailure
from trezorlib.messages import ButtonRequestType as B
from ...common import (
MNEMONIC12,
click_through,
generate_entropy,
read_and_confirm_mnemonic,
read_and_confirm_mnemonic_tr,
from ...common import MNEMONIC12, generate_entropy
from ...input_flows import (
InputFlowBip39ResetBackup,
InputFlowBip39ResetFailedCheck,
InputFlowBip39ResetPIN,
)
pytestmark = [pytest.mark.skip_t1]
@ -37,35 +36,11 @@ pytestmark = [pytest.mark.skip_t1]
EXTERNAL_ENTROPY = b"zlutoucky kun upel divoke ody" * 2
def reset_device(client: Client, strength):
mnemonic = None
def input_flow():
nonlocal mnemonic
# 1. Confirm Reset
# 2. Backup your seed
# 3. Confirm warning
yield from click_through(client.debug, screens=3, code=B.ResetDevice)
# mnemonic phrases
if client.debug.model == "R":
client.debug.watch_layout(True)
mnemonic = yield from read_and_confirm_mnemonic_tr(client.debug)
else:
mnemonic = yield from read_and_confirm_mnemonic(client.debug)
# confirm recovery seed check
br = yield
assert br.code == B.Success
client.debug.press_yes()
# confirm success
br = yield
assert br.code == B.Success
client.debug.press_yes()
def reset_device(client: Client, strength: int):
os_urandom = mock.Mock(return_value=EXTERNAL_ENTROPY)
with mock.patch("os.urandom", os_urandom), client:
IF = InputFlowBip39ResetBackup(client)
client.set_input_flow(IF.get())
client.set_expected_responses(
[
messages.ButtonRequest(code=B.ResetDevice),
@ -81,7 +56,6 @@ def reset_device(client: Client, strength):
messages.Features,
]
)
client.set_input_flow(input_flow)
# No PIN, no passphrase, don't display random
device.reset(
@ -101,7 +75,7 @@ def reset_device(client: Client, strength):
expected_mnemonic = Mnemonic("english").to_mnemonic(entropy)
# Compare that device generated proper mnemonic for given entropies
assert mnemonic == expected_mnemonic
assert IF.mnemonic == expected_mnemonic
# Check if device is properly initialized
resp = client.call_raw(messages.Initialize())
@ -128,64 +102,12 @@ def test_reset_device_192(client: Client):
@pytest.mark.setup_client(uninitialized=True)
def test_reset_device_pin(client: Client):
mnemonic = None
strength = 256 # 24 words
def input_flow():
nonlocal mnemonic
# Confirm Reset
br = yield
assert br.code == B.ResetDevice
client.debug.press_yes()
# Enter new PIN
yield
client.debug.input("654")
if client.debug.model == "R":
# Re-enter PIN
yield
client.debug.press_yes()
# Confirm PIN
yield
client.debug.input("654")
# Confirm entropy
br = yield
assert br.code == B.ResetDevice
client.debug.press_yes()
# Backup your seed
br = yield
assert br.code == B.ResetDevice
client.debug.press_yes()
# Confirm warning
br = yield
assert br.code == B.ResetDevice
client.debug.press_yes()
# mnemonic phrases
if client.debug.model == "R":
client.debug.watch_layout(True)
mnemonic = yield from read_and_confirm_mnemonic_tr(client.debug)
else:
mnemonic = yield from read_and_confirm_mnemonic(client.debug)
# confirm recovery seed check
br = yield
assert br.code == B.Success
client.debug.press_yes()
# confirm success
br = yield
assert br.code == B.Success
client.debug.press_yes()
os_urandom = mock.Mock(return_value=EXTERNAL_ENTROPY)
with mock.patch("os.urandom", os_urandom), client:
IF = InputFlowBip39ResetPIN(client)
client.set_input_flow(IF.get())
client.set_expected_responses(
[
messages.ButtonRequest(code=B.ResetDevice),
@ -204,7 +126,6 @@ def test_reset_device_pin(client: Client):
messages.Features,
]
)
client.set_input_flow(input_flow)
# PIN, passphrase, display random
device.reset(
@ -224,7 +145,7 @@ def test_reset_device_pin(client: Client):
expected_mnemonic = Mnemonic("english").to_mnemonic(entropy)
# Compare that device generated proper mnemonic for given entropies
assert mnemonic == expected_mnemonic
assert IF.mnemonic == expected_mnemonic
# Check if device is properly initialized
resp = client.call_raw(messages.Initialize())
@ -236,51 +157,12 @@ def test_reset_device_pin(client: Client):
@pytest.mark.setup_client(uninitialized=True)
def test_reset_failed_check(client: Client):
mnemonic = None
strength = 256 # 24 words
def input_flow():
nonlocal mnemonic
# 1. Confirm Reset
# 2. Backup your seed
# 3. Confirm warning
yield from click_through(client.debug, screens=3, code=B.ResetDevice)
# mnemonic phrases, wrong answer
if client.debug.model == "R":
client.debug.watch_layout(True)
mnemonic = yield from read_and_confirm_mnemonic_tr(
client.debug, choose_wrong=True
)
else:
mnemonic = yield from read_and_confirm_mnemonic(
client.debug, choose_wrong=True
)
# warning screen
br = yield
assert br.code == B.ResetDevice
client.debug.press_yes()
# mnemonic phrases
if client.debug.model == "R":
client.debug.watch_layout(True)
mnemonic = yield from read_and_confirm_mnemonic_tr(client.debug)
else:
mnemonic = yield from read_and_confirm_mnemonic(client.debug)
# confirm recovery seed check
br = yield
assert br.code == B.Success
client.debug.press_yes()
# confirm success
br = yield
assert br.code == B.Success
client.debug.press_yes()
os_urandom = mock.Mock(return_value=EXTERNAL_ENTROPY)
with mock.patch("os.urandom", os_urandom), client:
IF = InputFlowBip39ResetFailedCheck(client)
client.set_input_flow(IF.get())
client.set_expected_responses(
[
messages.ButtonRequest(code=B.ResetDevice),
@ -298,7 +180,6 @@ def test_reset_failed_check(client: Client):
messages.Features,
]
)
client.set_input_flow(input_flow)
# PIN, passphrase, display random
device.reset(
@ -318,7 +199,7 @@ def test_reset_failed_check(client: Client):
expected_mnemonic = Mnemonic("english").to_mnemonic(entropy)
# Compare that device generated proper mnemonic for given entropies
assert mnemonic == expected_mnemonic
assert IF.mnemonic == expected_mnemonic
# Check if device is properly initialized
resp = client.call_raw(messages.Initialize())

View File

@ -24,12 +24,8 @@ from trezorlib.debuglink import TrezorClientDebugLink as Client
from trezorlib.messages import BackupType, ButtonRequestType as B
from trezorlib.tools import parse_path
from ...common import (
EXTERNAL_ENTROPY,
click_through,
read_and_confirm_mnemonic,
read_and_confirm_mnemonic_tr,
)
from ...common import EXTERNAL_ENTROPY
from ...input_flows import InputFlowBip39RecoveryNoPIN, InputFlowBip39ResetBackup
@pytest.mark.skip_t1
@ -45,53 +41,10 @@ def test_reset_recovery(client: Client):
def reset(client: Client, strength: int = 128, skip_backup: bool = False) -> str:
mnemonic = None
def input_flow_tt():
nonlocal mnemonic
# 1. Confirm Reset
# 2. Backup your seed
# 3. Confirm warning
yield from click_through(client.debug, screens=3, code=B.ResetDevice)
# mnemonic phrases
mnemonic = yield from read_and_confirm_mnemonic(client.debug)
# confirm recovery seed check
br = yield
assert br.code == B.Success
client.debug.press_yes()
# confirm success
br = yield
assert br.code == B.Success
client.debug.press_yes()
def input_flow_tr():
nonlocal mnemonic
# 1. Confirm Reset
# 2. Backup your seed
# 3. Confirm warning
yield from click_through(client.debug, screens=3, code=B.ResetDevice)
# mnemonic phrases
client.debug.watch_layout(True)
mnemonic = yield from read_and_confirm_mnemonic_tr(client.debug)
# confirm recovery seed check
br = yield
assert br.code == B.Success
client.debug.press_yes()
# confirm success
br = yield
assert br.code == B.Success
client.debug.press_yes()
os_urandom = mock.Mock(return_value=EXTERNAL_ENTROPY)
with mock.patch("os.urandom", os_urandom), client:
IF = InputFlowBip39ResetBackup(client)
client.set_input_flow(IF.get())
client.set_expected_responses(
[
messages.ButtonRequest(code=B.ResetDevice),
@ -107,10 +60,6 @@ def reset(client: Client, strength: int = 128, skip_backup: bool = False) -> str
messages.Features,
]
)
if client.debug.model == "R":
client.set_input_flow(input_flow_tr)
elif client.debug.model == "T":
client.set_input_flow(input_flow_tt)
# No PIN, no passphrase, don't display random
device.reset(
@ -131,57 +80,16 @@ def reset(client: Client, strength: int = 128, skip_backup: bool = False) -> str
assert client.features.pin_protection is False
assert client.features.passphrase_protection is False
return mnemonic
assert IF.mnemonic is not None
return IF.mnemonic
def recover(client: Client, mnemonic: str):
debug = client.debug
words = mnemonic.split(" ")
def input_flow_tt():
yield # Confirm recovery
debug.press_yes()
yield # Homescreen
debug.press_yes()
yield # Enter word count
debug.input(str(len(words)))
yield # Homescreen
debug.press_yes()
yield # Enter words
for word in words:
debug.input(word)
yield # confirm success
debug.press_yes()
def input_flow_tr():
yield # Confirm recovery
debug.press_yes()
yield # Homescreen
debug.press_yes()
yield # Enter word count
debug.input(str(len(words)))
yield # Homescreen
debug.press_yes()
yield # Homescreen
debug.press_yes()
yield # Enter words
for word in words:
debug.input(word)
yield # confirm success
debug.press_yes()
yield
with client:
if client.debug.model == "R":
client.set_input_flow(input_flow_tr)
elif client.debug.model == "T":
client.set_input_flow(input_flow_tt)
IF = InputFlowBip39RecoveryNoPIN(client, words)
client.set_input_flow(IF.get())
client.watch_layout()
client.set_expected_responses(
[
messages.ButtonRequest(code=B.ProtectCall),

View File

@ -23,13 +23,10 @@ from trezorlib.debuglink import TrezorClientDebugLink as Client
from trezorlib.messages import BackupType, ButtonRequestType as B
from trezorlib.tools import parse_path
from ...common import (
EXTERNAL_ENTROPY,
click_through,
read_and_confirm_mnemonic,
read_and_confirm_mnemonic_tr,
recovery_enter_shares,
recovery_enter_shares_tr,
from ...common import EXTERNAL_ENTROPY
from ...input_flows import (
InputFlowSlip39AdvancedRecovery,
InputFlowSlip39AdvancedResetRecovery,
)
@ -63,85 +60,10 @@ def test_reset_recovery(client: Client):
def reset(client: Client, strength: int = 128) -> list[str]:
all_mnemonics: list[str] = []
def input_flow_tt():
# 1. Confirm Reset
# 2. Backup your seed
# 3. Confirm warning
# 4. shares info
# 5. Set & Confirm number of groups
# 6. threshold info
# 7. Set & confirm group threshold value
# 8-17: for each of 5 groups:
# 1. Set & Confirm number of shares
# 2. Set & confirm share threshold value
# 18. Confirm show seeds
yield from click_through(client.debug, screens=18, code=B.ResetDevice)
# show & confirm shares for all groups
for _g in range(5):
for _h in range(5):
# mnemonic phrases
mnemonic = yield from read_and_confirm_mnemonic(client.debug)
all_mnemonics.append(mnemonic)
# Confirm continue to next share
br = yield
assert br.code == B.Success
client.debug.press_yes()
# safety warning
br = yield
assert br.code == B.Success
client.debug.press_yes()
def input_flow_tr():
yield # Wallet backup
client.debug.press_yes()
yield # Wallet creation
client.debug.press_yes()
yield # Checklist
client.debug.press_yes()
yield # Set and confirm group count
client.debug.input("5")
yield # Checklist
client.debug.press_yes()
yield # Set and confirm group threshold
client.debug.input("3")
yield # Checklist
client.debug.press_yes()
for _ in range(5): # for each of 5 groups
yield # Number of shares info
client.debug.press_yes()
yield # Number of shares (5)
client.debug.input("5")
yield # Threshold info
client.debug.press_yes()
yield # Threshold (3)
client.debug.input("3")
yield # Confirm show seeds
client.debug.press_yes()
# show & confirm shares for all groups
for _g in range(5):
for _h in range(5):
# mnemonic phrases
mnemonic = yield from read_and_confirm_mnemonic_tr(client.debug)
all_mnemonics.append(mnemonic)
# Confirm continue to next share
br = yield
assert br.code == B.Success
client.debug.press_yes()
# safety warning
br = yield
assert br.code == B.Success
client.debug.press_yes()
os_urandom = mock.Mock(return_value=EXTERNAL_ENTROPY)
with mock.patch("os.urandom", os_urandom), client:
IF = InputFlowSlip39AdvancedResetRecovery(client, False)
client.set_input_flow(IF.get())
client.set_expected_responses(
[
messages.ButtonRequest(code=B.ResetDevice),
@ -176,11 +98,6 @@ def reset(client: Client, strength: int = 128) -> list[str]:
messages.Features,
]
)
if client.features.model == "T":
client.set_input_flow(input_flow_tt)
elif client.features.model == "R":
client.debug.watch_layout(True)
client.set_input_flow(input_flow_tr)
# No PIN, no passphrase, don't display random
device.reset(
@ -201,29 +118,13 @@ def reset(client: Client, strength: int = 128) -> list[str]:
assert client.features.pin_protection is False
assert client.features.passphrase_protection is False
return all_mnemonics
return IF.mnemonics
def recover(client: Client, shares: list[str]):
debug = client.debug
def input_flow_tt():
yield # Confirm Recovery
debug.press_yes()
# run recovery flow
yield from recovery_enter_shares(debug, shares, groups=True)
def input_flow_tr():
yield # Confirm Recovery
debug.press_yes()
# run recovery flow
yield from recovery_enter_shares_tr(debug, shares, groups=True)
with client:
if client.features.model == "T":
client.set_input_flow(input_flow_tt)
elif client.features.model == "R":
client.set_input_flow(input_flow_tr)
IF = InputFlowSlip39AdvancedRecovery(client, shares, False)
client.set_input_flow(IF.get())
ret = device.recover(
client, pin_protection=False, label="label", show_tutorial=False
)

View File

@ -24,12 +24,9 @@ from trezorlib.debuglink import TrezorClientDebugLink as Client
from trezorlib.messages import BackupType, ButtonRequestType as B
from trezorlib.tools import parse_path
from ...common import (
click_through,
read_and_confirm_mnemonic,
read_and_confirm_mnemonic_tr,
recovery_enter_shares,
recovery_enter_shares_tr,
from ...input_flows import (
InputFlowSlip39BasicRecovery,
InputFlowSlip39BasicResetRecovery,
)
EXTERNAL_ENTROPY = b"zlutoucky kun upel divoke ody" * 2
@ -54,72 +51,9 @@ def test_reset_recovery(client: Client):
def reset(client: Client, strength: int = 128) -> list[str]:
all_mnemonics: list[str] = []
def input_flow_tt():
# 1. Confirm Reset
# 2. Backup your seed
# 3. Confirm warning
# 4. shares info
# 5. Set & Confirm number of shares
# 6. threshold info
# 7. Set & confirm threshold value
# 8. Confirm show seeds
yield from click_through(client.debug, screens=8, code=B.ResetDevice)
# show & confirm shares
for _ in range(5):
# mnemonic phrases
mnemonic = yield from read_and_confirm_mnemonic(client.debug)
all_mnemonics.append(mnemonic)
# Confirm continue to next share
br = yield
assert br.code == B.Success
client.debug.press_yes()
# safety warning
br = yield
assert br.code == B.Success
client.debug.press_yes()
def input_flow_tr():
yield # Confirm Reset
client.debug.press_yes()
yield # Backup your seed
client.debug.press_yes()
yield # Checklist
client.debug.press_yes()
yield # Number of shares info
client.debug.press_yes()
yield # Number of shares (5)
client.debug.input("5")
yield # Checklist
client.debug.press_yes()
yield # Threshold info
client.debug.press_yes()
yield # Threshold (3)
client.debug.input("3")
yield # Checklist
client.debug.press_yes()
yield # Confirm show seeds
client.debug.press_yes()
# Mnemonic phrases
for _ in range(5):
# Phrase screen
mnemonic = yield from read_and_confirm_mnemonic_tr(client.debug)
all_mnemonics.append(mnemonic)
br = yield # Confirm continue to next
assert br.code == B.Success
client.debug.press_yes()
br = yield # Confirm backup
assert br.code == B.Success
client.debug.press_yes()
with client:
IF = InputFlowSlip39BasicResetRecovery(client)
client.set_input_flow(IF.get())
client.set_expected_responses(
[
messages.ButtonRequest(code=B.ResetDevice),
@ -144,11 +78,6 @@ def reset(client: Client, strength: int = 128) -> list[str]:
messages.Features,
]
)
if client.features.model == "T":
client.set_input_flow(input_flow_tt)
elif client.features.model == "R":
client.debug.watch_layout(True)
client.set_input_flow(input_flow_tr)
# No PIN, no passphrase, don't display random
device.reset(
@ -169,29 +98,13 @@ def reset(client: Client, strength: int = 128) -> list[str]:
assert client.features.pin_protection is False
assert client.features.passphrase_protection is False
return all_mnemonics
return IF.mnemonics
def recover(client: Client, shares: list[str]) -> None:
debug = client.debug
def input_flow_tt():
yield # Confirm Recovery
debug.press_yes()
# run recovery flow
yield from recovery_enter_shares(debug, shares)
def input_flow_tr():
yield # Confirm Recovery
debug.press_yes()
# run recovery flow
yield from recovery_enter_shares_tr(debug, shares)
def recover(client: Client, shares: list[str]):
with client:
if client.features.model == "T":
client.set_input_flow(input_flow_tt)
elif client.features.model == "R":
client.set_input_flow(input_flow_tr)
IF = InputFlowSlip39BasicRecovery(client, shares)
client.set_input_flow(IF.get())
ret = device.recover(
client, pin_protection=False, label="label", show_tutorial=False
)

View File

@ -24,12 +24,8 @@ from trezorlib.debuglink import TrezorClientDebugLink as Client
from trezorlib.exceptions import TrezorFailure
from trezorlib.messages import BackupType, ButtonRequestType as B
from ...common import (
click_through,
generate_entropy,
read_and_confirm_mnemonic,
read_and_confirm_mnemonic_tr,
)
from ...common import generate_entropy
from ...input_flows import InputFlowSlip39AdvancedResetRecovery
pytestmark = [pytest.mark.skip_t1]
@ -41,85 +37,11 @@ EXTERNAL_ENTROPY = b"zlutoucky kun upel divoke ody" * 2
def test_reset_device_slip39_advanced(client: Client):
strength = 128
member_threshold = 3
all_mnemonics = []
def input_flow_tt():
# 1. Confirm Reset
# 2. Backup your seed
# 3. Confirm warning
# 4. shares info
# 5. Set & Confirm number of groups
# 6. threshold info
# 7. Set & confirm group threshold value
# 8-17: for each of 5 groups:
# 1. Set & Confirm number of shares
# 2. Set & confirm share threshold value
# 18. Confirm show seeds
yield from click_through(client.debug, screens=18, code=B.ResetDevice)
# show & confirm shares for all groups
for _g in range(5):
for _h in range(5):
# mnemonic phrases
mnemonic = yield from read_and_confirm_mnemonic(client.debug)
all_mnemonics.append(mnemonic)
# Confirm continue to next share
br = yield
assert br.code == B.Success
client.debug.press_yes()
# safety warning
br = yield
assert br.code == B.Success
client.debug.press_yes()
def input_flow_tr():
yield # Wallet backup
client.debug.press_yes()
yield # Wallet creation
client.debug.press_yes()
yield # Checklist
client.debug.press_yes()
yield # Set and confirm group count
client.debug.input("5")
yield # Checklist
client.debug.press_yes()
yield # Set and confirm group threshold
client.debug.input("3")
yield # Checklist
client.debug.press_yes()
for _ in range(5): # for each of 5 groups
yield # Number of shares info
client.debug.press_yes()
yield # Number of shares (5)
client.debug.input("5")
yield # Threshold info
client.debug.press_yes()
yield # Threshold (3)
client.debug.input("3")
yield # Confirm show seeds
client.debug.press_yes()
# show & confirm shares for all groups
for _g in range(5):
for _h in range(5):
# mnemonic phrases
mnemonic = yield from read_and_confirm_mnemonic_tr(client.debug)
all_mnemonics.append(mnemonic)
# Confirm continue to next share
br = yield
assert br.code == B.Success
client.debug.press_yes()
# safety warning
br = yield
assert br.code == B.Success
client.debug.press_yes()
os_urandom = mock.Mock(return_value=EXTERNAL_ENTROPY)
with mock.patch("os.urandom", os_urandom), client:
IF = InputFlowSlip39AdvancedResetRecovery(client, False)
client.set_input_flow(IF.get())
client.set_expected_responses(
[
messages.ButtonRequest(code=B.ResetDevice),
@ -154,11 +76,6 @@ def test_reset_device_slip39_advanced(client: Client):
messages.Features,
]
)
if client.features.model == "T":
client.set_input_flow(input_flow_tt)
elif client.features.model == "R":
client.debug.watch_layout(True)
client.set_input_flow(input_flow_tr)
# No PIN, no passphrase, don't display random
device.reset(
@ -178,7 +95,7 @@ def test_reset_device_slip39_advanced(client: Client):
secret = generate_entropy(strength, internal_entropy, EXTERNAL_ENTROPY)
# validate that all combinations will result in the correct master secret
validate_mnemonics(all_mnemonics, member_threshold, secret)
validate_mnemonics(IF.mnemonics, member_threshold, secret)
# Check if device is properly initialized
assert client.features.initialized is True

View File

@ -25,86 +25,19 @@ from trezorlib.debuglink import TrezorClientDebugLink as Client
from trezorlib.exceptions import TrezorFailure
from trezorlib.messages import BackupType, ButtonRequestType as B
from ...common import (
EXTERNAL_ENTROPY,
click_through,
generate_entropy,
read_and_confirm_mnemonic,
read_and_confirm_mnemonic_tr,
)
from ...common import EXTERNAL_ENTROPY, generate_entropy
from ...input_flows import InputFlowSlip39BasicResetRecovery
pytestmark = [pytest.mark.skip_t1]
def reset_device(client: Client, strength):
def reset_device(client: Client, strength: int):
member_threshold = 3
all_mnemonics = []
def input_flow_tt():
# 1. Confirm Reset
# 2. Backup your seed
# 3. Confirm warning
# 4. shares info
# 5. Set & Confirm number of shares
# 6. threshold info
# 7. Set & confirm threshold value
# 8. Confirm show seeds
yield from click_through(client.debug, screens=8, code=B.ResetDevice)
# show & confirm shares
for _ in range(5):
# mnemonic phrases
mnemonic = yield from read_and_confirm_mnemonic(client.debug)
all_mnemonics.append(mnemonic)
# Confirm continue to next share
br = yield
assert br.code == B.Success
client.debug.press_yes()
# safety warning
br = yield
assert br.code == B.Success
client.debug.press_yes()
def input_flow_tr():
yield # Confirm Reset
client.debug.press_yes()
yield # Backup your seed
client.debug.press_yes()
yield # Checklist
client.debug.press_yes()
yield # Number of shares info
client.debug.press_yes()
yield # Number of shares (5)
client.debug.input("5")
yield # Checklist
client.debug.press_yes()
yield # Threshold info
client.debug.press_yes()
yield # Threshold (3)
client.debug.input("3")
yield # Checklist
client.debug.press_yes()
yield # Confirm show seeds
client.debug.press_yes()
# Mnemonic phrases
for _ in range(5):
# Phrase screen
mnemonic = yield from read_and_confirm_mnemonic_tr(client.debug)
all_mnemonics.append(mnemonic)
br = yield # Confirm continue to next
assert br.code == B.Success
client.debug.press_yes()
br = yield # Confirm backup
assert br.code == B.Success
client.debug.press_yes()
os_urandom = mock.Mock(return_value=EXTERNAL_ENTROPY)
with mock.patch("os.urandom", os_urandom), client:
IF = InputFlowSlip39BasicResetRecovery(client)
client.set_input_flow(IF.get())
client.set_expected_responses(
[
messages.ButtonRequest(code=B.ResetDevice),
@ -129,11 +62,6 @@ def reset_device(client: Client, strength):
messages.Features,
]
)
if client.features.model == "T":
client.set_input_flow(input_flow_tt)
elif client.features.model == "R":
client.debug.watch_layout(True)
client.set_input_flow(input_flow_tr)
# No PIN, no passphrase, don't display random
device.reset(
@ -153,7 +81,7 @@ def reset_device(client: Client, strength):
secret = generate_entropy(strength, internal_entropy, EXTERNAL_ENTROPY)
# validate that all combinations will result in the correct master secret
validate_mnemonics(all_mnemonics, member_threshold, secret)
validate_mnemonics(IF.mnemonics, member_threshold, secret)
# Check if device is properly initialized
assert client.features.initialized is True

View File

@ -27,41 +27,22 @@ from ..common import (
MNEMONIC12,
MNEMONIC_SLIP39_ADVANCED_20,
MNEMONIC_SLIP39_BASIC_20_3of6,
read_and_confirm_mnemonic,
read_and_confirm_mnemonic_tr,
)
def click_info_button(debug):
"""Click Shamir backup info button and return back."""
debug.press_info()
yield # Info screen with text
debug.press_yes()
from ..input_flows import (
InputFlowBip39Backup,
InputFlowSlip39AdvancedBackup,
InputFlowSlip39BasicBackup,
)
@pytest.mark.skip_t1 # TODO we want this for t1 too
@pytest.mark.setup_client(needs_backup=True, mnemonic=MNEMONIC12)
def test_backup_bip39(client: Client):
assert client.features.needs_backup is True
mnemonic = None
def input_flow():
nonlocal mnemonic
yield # Confirm Backup
client.debug.press_yes()
# Mnemonic phrases
if client.debug.model == "R":
client.debug.watch_layout(True)
mnemonic = yield from read_and_confirm_mnemonic_tr(client.debug)
else:
mnemonic = yield from read_and_confirm_mnemonic(client.debug)
yield # Confirm success
client.debug.press_yes()
yield # Backup is done
client.debug.press_yes()
with client:
client.set_input_flow(input_flow)
IF = InputFlowBip39Backup(client)
client.set_input_flow(IF.get())
client.set_expected_responses(
[
*[
@ -76,7 +57,7 @@ def test_backup_bip39(client: Client):
)
device.backup(client)
assert mnemonic == MNEMONIC12
assert IF.mnemonic == MNEMONIC12
client.init_device()
assert client.features.initialized is True
assert client.features.needs_backup is False
@ -95,76 +76,10 @@ def test_backup_slip39_basic(client: Client, click_info: bool):
pytest.skip("click_info not implemented on TR")
assert client.features.needs_backup is True
mnemonics = []
def input_flow_tt():
yield # Checklist
client.debug.press_yes()
if click_info:
yield from click_info_button(client.debug)
yield # Number of shares (5)
client.debug.press_yes()
yield # Checklist
client.debug.press_yes()
if click_info:
yield from click_info_button(client.debug)
yield # Threshold (3)
client.debug.press_yes()
yield # Checklist
client.debug.press_yes()
yield # Confirm show seeds
client.debug.press_yes()
# Mnemonic phrases
for _ in range(5):
# Phrase screen
mnemonic = yield from read_and_confirm_mnemonic(client.debug)
mnemonics.append(mnemonic)
br = yield # Confirm continue to next
assert br.code == B.Success
client.debug.press_yes()
br = yield # Confirm backup
assert br.code == B.Success
client.debug.press_yes()
def input_flow_tr():
yield # Checklist
client.debug.press_yes()
yield # Number of shares info
client.debug.press_yes()
yield # Number of shares (5)
client.debug.input("5")
yield # Checklist
client.debug.press_yes()
yield # Threshold info
client.debug.press_yes()
yield # Threshold (3)
client.debug.input("3")
yield # Checklist
client.debug.press_yes()
yield # Confirm show seeds
client.debug.press_yes()
# Mnemonic phrases
for _ in range(5):
# Phrase screen
mnemonic = yield from read_and_confirm_mnemonic_tr(client.debug)
mnemonics.append(mnemonic)
br = yield # Confirm continue to next
assert br.code == B.Success
client.debug.press_yes()
br = yield # Confirm backup
assert br.code == B.Success
client.debug.press_yes()
with client:
if client.features.model == "T":
client.set_input_flow(input_flow_tt)
elif client.features.model == "R":
client.debug.watch_layout(True)
client.set_input_flow(input_flow_tr)
IF = InputFlowSlip39BasicBackup(client, click_info)
client.set_input_flow(IF.get())
client.set_expected_responses(
[messages.ButtonRequest(code=B.ResetDevice)]
* (8 if click_info else 6) # intro screens (and optional info)
@ -189,7 +104,7 @@ def test_backup_slip39_basic(client: Client, click_info: bool):
assert client.features.backup_type is messages.BackupType.Slip39_Basic
expected_ms = shamir.combine_mnemonics(MNEMONIC_SLIP39_BASIC_20_3of6)
actual_ms = shamir.combine_mnemonics(mnemonics[:3])
actual_ms = shamir.combine_mnemonics(IF.mnemonics[:3])
assert expected_ms == actual_ms
@ -203,88 +118,10 @@ def test_backup_slip39_advanced(client: Client, click_info: bool):
pytest.skip("click_info not implemented on TR")
assert client.features.needs_backup is True
mnemonics = []
def input_flow_tt():
yield # Checklist
client.debug.press_yes()
if click_info:
yield from click_info_button(client.debug)
yield # Set and confirm group count
client.debug.press_yes()
yield # Checklist
client.debug.press_yes()
if click_info:
yield from click_info_button(client.debug)
yield # Set and confirm group threshold
client.debug.press_yes()
yield # Checklist
client.debug.press_yes()
for _ in range(5): # for each of 5 groups
if click_info:
yield from click_info_button(client.debug)
yield # Set & Confirm number of shares
client.debug.press_yes()
if click_info:
yield from click_info_button(client.debug)
yield # Set & confirm share threshold value
client.debug.press_yes()
yield # Confirm show seeds
client.debug.press_yes()
# Mnemonic phrases
for _ in range(5):
for _ in range(5):
# Phrase screen
mnemonic = yield from read_and_confirm_mnemonic(client.debug)
mnemonics.append(mnemonic)
yield # Confirm continue to next
client.debug.press_yes()
yield # Confirm backup
client.debug.press_yes()
def input_flow_tr():
yield # Checklist
client.debug.press_yes()
yield # Set and confirm group count
client.debug.input("5")
yield # Checklist
client.debug.press_yes()
yield # Set and confirm group threshold
client.debug.input("3")
yield # Checklist
client.debug.press_yes()
for _ in range(5): # for each of 5 groups
yield # Number of shares info
client.debug.press_yes()
yield # Number of shares (5)
client.debug.input("5")
yield # Threshold info
client.debug.press_yes()
yield # Threshold (3)
client.debug.input("3")
yield # Confirm show seeds
client.debug.press_yes()
# Mnemonic phrases
for _ in range(5):
for _ in range(5):
# Phrase screen
mnemonic = yield from read_and_confirm_mnemonic_tr(client.debug)
mnemonics.append(mnemonic)
yield # Confirm continue to next
client.debug.press_yes()
yield # Confirm backup
client.debug.press_yes()
with client:
if client.features.model == "T":
client.set_input_flow(input_flow_tt)
elif client.features.model == "R":
client.debug.watch_layout(True)
client.set_input_flow(input_flow_tr)
IF = InputFlowSlip39AdvancedBackup(client, click_info)
client.set_input_flow(IF.get())
client.set_expected_responses(
[messages.ButtonRequest(code=B.ResetDevice)]
* (8 if click_info else 6) # intro screens (and optional info)
@ -317,7 +154,7 @@ def test_backup_slip39_advanced(client: Client, click_info: bool):
expected_ms = shamir.combine_mnemonics(MNEMONIC_SLIP39_ADVANCED_20)
actual_ms = shamir.combine_mnemonics(
mnemonics[:3] + mnemonics[5:8] + mnemonics[10:13]
IF.mnemonics[:3] + IF.mnemonics[5:8] + IF.mnemonics[10:13]
)
assert expected_ms == actual_ms

View File

@ -21,6 +21,8 @@ from trezorlib.client import MAX_PIN_LENGTH, PASSPHRASE_TEST_PATH
from trezorlib.debuglink import TrezorClientDebugLink as Client
from trezorlib.exceptions import Cancelled, TrezorFailure
from ..input_flows import InputFlowNewCodeMismatch
PIN4 = "1234"
WIPE_CODE4 = "4321"
WIPE_CODE6 = "456789"
@ -95,24 +97,12 @@ def test_set_remove_wipe_code(client: Client):
def test_set_wipe_code_mismatch(client: Client):
# Let's set a wipe code.
def input_flow():
yield # do you want to set the wipe code?
client.debug.press_yes()
yield # enter new wipe code
client.debug.input(WIPE_CODE4)
yield # enter new wipe code again (but different)
client.debug.input(WIPE_CODE6)
# failed retry
yield # enter new wipe code
client.cancel()
with client, pytest.raises(Cancelled):
IF = InputFlowNewCodeMismatch(client, WIPE_CODE4, WIPE_CODE6)
client.set_input_flow(IF.get())
client.set_expected_responses(
[messages.ButtonRequest()] * 4 + [messages.Failure()]
)
client.set_input_flow(input_flow)
device.change_wipe_code(client)

View File

@ -21,6 +21,12 @@ from trezorlib.client import MAX_PIN_LENGTH, PASSPHRASE_TEST_PATH
from trezorlib.debuglink import TrezorClientDebugLink as Client
from trezorlib.exceptions import Cancelled, TrezorFailure
from ..input_flows import (
InputFlowCodeChangeFail,
InputFlowNewCodeMismatch,
InputFlowWrongPIN,
)
PIN4 = "1234"
PIN60 = "789456" * 10
PIN_MAX = "".join(chr((i % 10) + ord("0")) for i in range(MAX_PIN_LENGTH))
@ -28,7 +34,7 @@ PIN_MAX = "".join(chr((i % 10) + ord("0")) for i in range(MAX_PIN_LENGTH))
pytestmark = pytest.mark.skip_t1
def _check_pin(client: Client, pin):
def _check_pin(client: Client, pin: str):
client.lock()
assert client.features.pin_protection is True
assert client.features.unlocked is False
@ -116,22 +122,10 @@ def test_set_failed(client: Client):
# Check that there's no PIN protection
_check_no_pin(client)
# Let's set new PIN
def input_flow():
yield # do you want to set pin?
client.debug.press_yes()
yield # enter new pin
client.debug.input(PIN4)
yield # enter new pin again (but different)
client.debug.input(PIN60)
# failed retry
yield # enter new pin
client.cancel()
with client, pytest.raises(Cancelled):
IF = InputFlowNewCodeMismatch(client, PIN4, PIN60)
client.set_input_flow(IF.get())
client.set_expected_responses([messages.ButtonRequest] * 4 + [messages.Failure])
client.set_input_flow(input_flow)
device.change_pin(client)
@ -148,24 +142,10 @@ def test_change_failed(client: Client):
# Check current PIN value
_check_pin(client, PIN4)
# Let's set new PIN
def input_flow():
yield # do you want to change pin?
client.debug.press_yes()
yield # enter current pin
client.debug.input(PIN4)
yield # enter new pin
client.debug.input("457891")
yield # enter new pin again (but different)
client.debug.input("381847")
# failed retry
yield # enter current pin again
client.cancel()
with client, pytest.raises(Cancelled):
IF = InputFlowCodeChangeFail(client, PIN4, "457891", "381847")
client.set_input_flow(IF.get())
client.set_expected_responses([messages.ButtonRequest] * 5 + [messages.Failure])
client.set_input_flow(input_flow)
device.change_pin(client)
@ -182,18 +162,10 @@ def test_change_invalid_current(client: Client):
# Check current PIN value
_check_pin(client, PIN4)
# Let's set new PIN
def input_flow():
yield # do you want to change pin?
client.debug.press_yes()
yield # enter wrong current pin
client.debug.input(PIN60)
yield
client.debug.press_no()
with client, pytest.raises(TrezorFailure):
IF = InputFlowWrongPIN(client, PIN60)
client.set_input_flow(IF.get())
client.set_expected_responses([messages.ButtonRequest] * 3 + [messages.Failure])
client.set_input_flow(input_flow)
device.change_pin(client)

View File

@ -22,7 +22,8 @@ from trezorlib import messages
from trezorlib.debuglink import TrezorClientDebugLink as Client
from trezorlib.exceptions import PinException
from ..common import get_test_address
from ..common import check_PIN_backoff_time, get_test_address
from ..input_flows import InputFlowPINBackoff
PIN4 = "1234"
BAD_PIN = "5678"
@ -79,13 +80,6 @@ def test_incorrect_pin_t2(client: Client):
get_test_address(client)
def _check_backoff_time(attempts: int, start: float) -> None:
"""Helper to assert the exponentially growing delay after incorrect PIN attempts"""
expected = (2**attempts) - 1
got = round(time.time() - start, 2)
assert got >= expected
@pytest.mark.skip_t2
@pytest.mark.skip_tr
def test_exponential_backoff_t1(client: Client):
@ -94,21 +88,12 @@ def test_exponential_backoff_t1(client: Client):
with client, pytest.raises(PinException):
client.use_pin_sequence([BAD_PIN])
get_test_address(client)
_check_backoff_time(attempt, start)
check_PIN_backoff_time(attempt, start)
@pytest.mark.skip_t1
def test_exponential_backoff_t2(client: Client):
def input_flow():
"""Inputting some bad PINs and finally the correct one"""
yield # PIN entry
for attempt in range(3):
start = time.time()
client.debug.input(BAD_PIN)
yield # PIN entry
_check_backoff_time(attempt, start)
client.debug.input(PIN4)
with client:
client.set_input_flow(input_flow)
IF = InputFlowPINBackoff(client, BAD_PIN, PIN4)
client.set_input_flow(IF.get())
get_test_address(client)

1738
tests/input_flows.py Normal file

File diff suppressed because it is too large Load Diff

View File

@ -3,6 +3,7 @@ from trezorlib.debuglink import TrezorClientDebugLink as Client
from ..common import MNEMONIC12
from ..emulators import EmulatorWrapper
from ..input_flows import InputFlowSetupDevicePINWIpeCode
from ..upgrade_tests import core_only, legacy_only
PIN = "1234"
@ -26,24 +27,12 @@ def setup_device_core(client: Client, pin: str, wipe_code: str) -> None:
client, MNEMONIC12, pin, passphrase_protection=False, label="WIPECODE"
)
def input_flow():
yield # do you want to set/change the wipe_code?
client.debug.press_yes()
if pin is not None:
yield # enter current pin
client.debug.input(pin)
yield # enter new wipe code
client.debug.input(wipe_code)
yield # enter new wipe code again
client.debug.input(wipe_code)
yield # success
client.debug.press_yes()
with client:
client.set_expected_responses(
[messages.ButtonRequest()] * 5 + [messages.Success, messages.Features]
)
client.set_input_flow(input_flow)
IF = InputFlowSetupDevicePINWIpeCode(client, pin, wipe_code)
client.set_input_flow(IF.get())
device.change_wipe_code(client)