chore(tests): update device tests

pull/3011/head
grdddj 1 year ago committed by Martin Milata
parent facbd6b1cf
commit f809a37f7d

@ -584,13 +584,14 @@ def test_send_btg_external_presigned(client: Client):
script_type=messages.OutputScriptType.PAYTOADDRESS,
)
with client:
tt = client.features.model == "T"
client.set_expected_responses(
[
request_input(0),
request_input(1),
request_output(0),
messages.ButtonRequest(code=B.ConfirmOutput),
messages.ButtonRequest(code=B.ConfirmOutput),
(tt, messages.ButtonRequest(code=B.ConfirmOutput)),
messages.ButtonRequest(code=B.SignTx),
request_input(0),
request_meta(FAKE_TXHASH_6f0398),

@ -20,6 +20,12 @@ from trezorlib import btc, messages, tools
from trezorlib.debuglink import TrezorClientDebugLink as Client
from trezorlib.exceptions import Cancelled, TrezorFailure
from ...input_flows import (
InputFlowShowAddressQRCode,
InputFlowShowAddressQRCodeCancel,
InputFlowShowMultisigXPUBs,
)
VECTORS = ( # path, script_type, address
(
"m/44h/0h/12h/0/0",
@ -43,22 +49,21 @@ VECTORS = ( # path, script_type, address
),
)
CORNER_BUTTON = (215, 25)
@pytest.mark.skip_t2
@pytest.mark.parametrize("path, script_type, address", VECTORS)
def test_show_t1(
client: Client, path: str, script_type: messages.InputScriptType, address: str
):
def input_flow():
def input_flow_t1():
yield
client.debug.press_no()
yield
client.debug.press_yes()
with client:
client.set_input_flow(input_flow)
# This is the only place where even T1 is using input flow
client.set_input_flow(input_flow_t1)
assert (
btc.get_address(
client,
@ -76,22 +81,9 @@ def test_show_t1(
def test_show_tt(
client: Client, path: str, script_type: messages.InputScriptType, address: str
):
def input_flow():
yield
client.debug.click(CORNER_BUTTON, wait=True)
# synchronize; TODO get rid of this once we have single-global-layout
client.debug.synchronize_at("HorizontalPage")
client.debug.swipe_left(wait=True)
client.debug.swipe_right(wait=True)
client.debug.swipe_left(wait=True)
client.debug.click(CORNER_BUTTON, wait=True)
client.debug.press_no(wait=True)
client.debug.press_no(wait=True)
client.debug.press_yes()
with client:
client.set_input_flow(input_flow)
IF = InputFlowShowAddressQRCode(client)
client.set_input_flow(IF.get())
assert (
btc.get_address(
client,
@ -109,19 +101,9 @@ def test_show_tt(
def test_show_cancel(
client: Client, path: str, script_type: messages.InputScriptType, address: str
):
def input_flow():
yield
client.debug.click(CORNER_BUTTON, wait=True)
# synchronize; TODO get rid of this once we have single-global-layout
client.debug.synchronize_at("HorizontalPage")
client.debug.swipe_left(wait=True)
client.debug.click(CORNER_BUTTON, wait=True)
client.debug.press_no(wait=True)
client.debug.press_yes()
with client, pytest.raises(Cancelled):
client.set_input_flow(input_flow)
IF = InputFlowShowAddressQRCodeCancel(client)
client.set_input_flow(IF.get())
btc.get_address(
client,
"Bitcoin",
@ -270,40 +252,9 @@ def test_show_multisig_xpubs(
)
for i in range(3):
def input_flow():
yield # show address
layout = client.debug.wait_layout()
assert "RECEIVE ADDRESS (MULTISIG)" in layout.get_title()
assert layout.get_content().replace(" ", "") == address
client.debug.click(CORNER_BUTTON)
assert "Qr" in client.debug.wait_layout().text
layout = client.debug.swipe_left(wait=True)
# address details
assert "Multisig 2 of 3" in layout.text
# Three xpub pages with the same testing logic
for xpub_num in range(3):
expected_title = f"MULTISIG XPUB #{xpub_num + 1} " + (
"(YOURS)" if i == xpub_num else "(COSIGNER)"
)
layout = client.debug.swipe_left(wait=True)
assert expected_title in layout.get_title()
content = layout.get_content().replace(" ", "")
assert xpubs[xpub_num] in content
client.debug.click(CORNER_BUTTON, wait=True)
# show address
client.debug.press_no(wait=True)
# address mismatch
client.debug.press_no(wait=True)
# show address
client.debug.press_yes()
with client:
client.set_input_flow(input_flow)
IF = InputFlowShowMultisigXPUBs(client, address, xpubs, i)
client.set_input_flow(IF.get())
client.debug.synchronize_at("Homescreen")
client.watch_layout()
btc.get_address(

@ -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,34 +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():
# 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()
with client:
client.set_input_flow(input_flow)
client.debug.watch_layout(True)
IF = InputFlowSignMessagePagination(client)
client.set_input_flow(IF.get())
btc.sign_message(
client,
coin_name="Bitcoin",
@ -340,7 +320,7 @@ def test_signmessage_pagination(client: Client, message: str):
expected_message = (
("Confirm message: " + message).replace("\n", "").replace(" ", "")
)
message_read = message_read.replace(" ", "").replace("...", "")
message_read = IF.message_read.replace(" ", "").replace("...", "")
assert expected_message == message_read

@ -23,6 +23,11 @@ from trezorlib.debuglink import TrezorClientDebugLink as Client
from trezorlib.exceptions import Cancelled, 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,
@ -655,27 +660,13 @@ def test_fee_high_hardfail(client: Client):
client, safety_checks=messages.SafetyCheckLevel.PromptTemporarily
)
with client:
finished = False
def input_flow():
nonlocal finished
for expected in (
B.ConfirmOutput,
B.ConfirmOutput,
B.FeeOverThreshold,
B.SignTx,
):
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 (
@ -1471,28 +1462,9 @@ def test_lock_time_blockheight(client: Client):
script_type=messages.OutputScriptType.PAYTOADDRESS,
)
def input_flow():
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()
with client:
client.set_input_flow(input_flow)
client.watch_layout(True)
IF = InputFlowLockTimeBlockHeight(client, "499999999")
client.set_input_flow(IF.get())
btc.sign_tx(
client,
@ -1508,7 +1480,7 @@ def test_lock_time_blockheight(client: Client):
@pytest.mark.parametrize(
"lock_time_str", ("1985-11-05 00:53:20", "2048-08-16 22:14:00")
)
def test_lock_time_datetime(client: Client, lock_time_str):
def test_lock_time_datetime(client: Client, lock_time_str: str):
# input tx: 0dac366fd8a67b2a89fbb0d31086e7acded7a5bbf9ef9daa935bc873229ef5b5
inp1 = messages.TxInputType(
@ -1525,30 +1497,13 @@ def test_lock_time_datetime(client: Client, lock_time_str):
script_type=messages.OutputScriptType.PAYTOADDRESS,
)
def input_flow():
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()
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:
client.set_input_flow(input_flow)
client.watch_layout(True)
IF = InputFlowLockTimeDatetime(client, lock_time_str)
client.set_input_flow(IF.get())
btc.sign_tx(
client,
@ -1591,7 +1546,7 @@ def test_information(client: Client):
client.debug.press_info()
layout = client.debug.wait_layout()
content = layout.get_content().lower()
content = layout.text_content().lower()
assert "sending from" in content
assert "legacy #6" in content
assert "fee rate" in content
@ -1647,7 +1602,7 @@ def test_information_mixed(client: Client):
client.debug.press_info()
layout = client.debug.wait_layout()
content = layout.get_content().lower()
content = layout.text_content().lower()
assert "sending from" in content
assert "multiple accounts" in content
assert "fee rate" in content

@ -216,19 +216,20 @@ def test_p2wpkh_in_p2sh_presigned(client: Client):
)
with client:
tt = client.features.model == "T"
client.set_expected_responses(
[
request_input(0),
request_input(1),
request_output(0),
messages.ButtonRequest(code=B.ConfirmOutput),
messages.ButtonRequest(code=B.ConfirmOutput),
(tt, messages.ButtonRequest(code=B.ConfirmOutput)),
request_output(1),
messages.ButtonRequest(code=B.ConfirmOutput),
messages.ButtonRequest(code=B.ConfirmOutput),
(tt, messages.ButtonRequest(code=B.ConfirmOutput)),
request_output(2),
messages.ButtonRequest(code=B.ConfirmOutput),
messages.ButtonRequest(code=B.ConfirmOutput),
(tt, messages.ButtonRequest(code=B.ConfirmOutput)),
messages.ButtonRequest(code=B.SignTx),
request_input(0),
request_meta(TXHASH_20912f),
@ -267,19 +268,20 @@ def test_p2wpkh_in_p2sh_presigned(client: Client):
# Test corrupted script hash in scriptsig.
inp1.script_sig[10] ^= 1
with client:
tt = client.features.model == "T"
client.set_expected_responses(
[
request_input(0),
request_input(1),
request_output(0),
messages.ButtonRequest(code=B.ConfirmOutput),
messages.ButtonRequest(code=B.ConfirmOutput),
(tt, messages.ButtonRequest(code=B.ConfirmOutput)),
request_output(1),
messages.ButtonRequest(code=B.ConfirmOutput),
messages.ButtonRequest(code=B.ConfirmOutput),
(tt, messages.ButtonRequest(code=B.ConfirmOutput)),
request_output(2),
messages.ButtonRequest(code=B.ConfirmOutput),
messages.ButtonRequest(code=B.ConfirmOutput),
(tt, messages.ButtonRequest(code=B.ConfirmOutput)),
messages.ButtonRequest(code=B.SignTx),
request_input(0),
request_meta(TXHASH_20912f),
@ -399,13 +401,14 @@ def test_p2wsh_external_presigned(client: Client):
)
with client:
tt = client.features.model == "T"
client.set_expected_responses(
[
request_input(0),
request_input(1),
request_output(0),
messages.ButtonRequest(code=B.ConfirmOutput),
messages.ButtonRequest(code=B.ConfirmOutput),
(tt, messages.ButtonRequest(code=B.ConfirmOutput)),
messages.ButtonRequest(code=B.SignTx),
request_input(0),
request_meta(TXHASH_ec16dc),
@ -444,13 +447,14 @@ def test_p2wsh_external_presigned(client: Client):
# Test corrupted signature in witness.
inp2.witness[10] ^= 1
with client:
tt = client.features.model == "T"
client.set_expected_responses(
[
request_input(0),
request_input(1),
request_output(0),
messages.ButtonRequest(code=B.ConfirmOutput),
messages.ButtonRequest(code=B.ConfirmOutput),
(tt, messages.ButtonRequest(code=B.ConfirmOutput)),
messages.ButtonRequest(code=B.SignTx),
request_input(0),
request_meta(TXHASH_ec16dc),
@ -509,13 +513,14 @@ def test_p2tr_external_presigned(client: Client):
script_type=messages.OutputScriptType.PAYTOTAPROOT,
)
with client:
tt = client.features.model == "T"
client.set_expected_responses(
[
request_input(0),
request_input(1),
request_output(0),
messages.ButtonRequest(code=B.ConfirmOutput),
messages.ButtonRequest(code=B.ConfirmOutput),
(tt, messages.ButtonRequest(code=B.ConfirmOutput)),
request_output(1),
messages.ButtonRequest(code=B.SignTx),
request_input(1),
@ -541,13 +546,14 @@ def test_p2tr_external_presigned(client: Client):
# Test corrupted signature in witness.
inp2.witness[10] ^= 1
with client:
tt = client.features.model == "T"
client.set_expected_responses(
[
request_input(0),
request_input(1),
request_output(0),
messages.ButtonRequest(code=B.ConfirmOutput),
messages.ButtonRequest(code=B.ConfirmOutput),
(tt, messages.ButtonRequest(code=B.ConfirmOutput)),
request_output(1),
messages.ButtonRequest(code=B.SignTx),
request_input(1),

@ -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
@ -191,35 +192,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,

@ -115,7 +115,7 @@ def test_p2pkh_fee_bump(client: Client):
orig_index=1,
)
tt = client.features.model == "T"
new_model = client.features.model in ("T",)
with client:
client.set_expected_responses(
@ -133,7 +133,7 @@ def test_p2pkh_fee_bump(client: Client):
request_meta(TXHASH_beafc7),
request_input(0, TXHASH_beafc7),
request_output(0, TXHASH_beafc7),
(tt, request_orig_input(0, TXHASH_50f6f1)),
(new_model, request_orig_input(0, TXHASH_50f6f1)),
request_orig_input(0, TXHASH_50f6f1),
request_orig_output(0, TXHASH_50f6f1),
request_orig_output(1, TXHASH_50f6f1),

@ -260,13 +260,14 @@ def test_external_presigned(client: Client):
)
with client:
tt = client.features.model == "T"
client.set_expected_responses(
[
request_input(0),
request_input(1),
request_output(0),
messages.ButtonRequest(code=B.ConfirmOutput),
messages.ButtonRequest(code=B.ConfirmOutput),
(tt, messages.ButtonRequest(code=B.ConfirmOutput)),
messages.ButtonRequest(code=B.SignTx),
request_input(0),
request_meta(TXHASH_e38206),

@ -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]
@ -94,63 +93,12 @@ DATA = {
}
def input_flow_show_more(client: Client):
"""Clicks show_more button wherever possible"""
yield # confirm address
client.debug.press_yes()
yield # confirm domain
client.debug.wait_layout()
client.debug.click(SHOW_MORE)
# confirm domain properties
for _ in range(4):
yield
client.debug.press_yes()
yield # confirm message
client.debug.wait_layout()
client.debug.click(SHOW_MORE)
yield # confirm message.from
client.debug.wait_layout()
client.debug.click(SHOW_MORE)
# confirm message.from properties
for _ in range(2):
yield
client.debug.press_yes()
yield # confirm message.to
client.debug.wait_layout()
client.debug.click(SHOW_MORE)
# 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 address
client.debug.press_yes()
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"),
@ -163,7 +111,8 @@ def test_ethereum_sign_typed_data_show_more_button(client: Client):
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"),

@ -22,11 +22,15 @@ from trezorlib.exceptions import TrezorFailure
from trezorlib.tools import parse_path, unharden
from ...common import parametrize_using_common_fixtures
from ...input_flows import (
InputFlowEthereumSignTxGoBack,
InputFlowEthereumSignTxScrollDown,
InputFlowEthereumSignTxSkip,
)
from .common import encode_network
TO_ADDR = "0x1d1c328764a41bda0492b66baa30c4a339ff85ef"
SHOW_ALL = (143, 167)
GO_BACK = (16, 220)
pytestmark = [pytest.mark.altcoin, pytest.mark.ethereum]
@ -141,13 +145,14 @@ def test_data_streaming(client: Client):
"""
with client:
tt = client.features.model == "T"
not_t1 = client.features.model != "1"
client.set_expected_responses(
[
messages.ButtonRequest(code=messages.ButtonRequestType.SignTx),
messages.ButtonRequest(code=messages.ButtonRequestType.SignTx),
messages.ButtonRequest(code=messages.ButtonRequestType.SignTx),
(tt, messages.ButtonRequest(code=messages.ButtonRequestType.Other)),
(tt, messages.ButtonRequest(code=messages.ButtonRequestType.SignTx)),
(not_t1, messages.ButtonRequest(code=messages.ButtonRequestType.Other)),
messages.ButtonRequest(code=messages.ButtonRequestType.SignTx),
message_filters.EthereumTxRequest(
data_length=1_024,
signature_r=None,
@ -343,90 +348,15 @@ 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):
yield # confirm address
client.debug.wait_layout()
client.debug.press_yes()
yield # confirm amount
client.debug.wait_layout()
client.debug.press_yes()
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):
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"
@ -437,9 +367,7 @@ HEXDATA = "0123456789abcd000023456789abcd010003456789abcd020000456789abcd0300000
)
@pytest.mark.skip_t1
def test_signtx_data_pagination(client: Client, flow):
with client:
client.watch_layout()
client.set_input_flow(flow(client))
def _sign_tx_call():
ethereum.sign_tx(
client,
n=parse_path("m/44h/60h/0h/0/0"),
@ -453,18 +381,12 @@ def test_signtx_data_pagination(client: Client, flow):
data=bytes.fromhex(HEXDATA),
)
with client:
client.watch_layout()
client.set_input_flow(flow(client))
_sign_tx_call()
with client, pytest.raises(exceptions.Cancelled):
client.watch_layout()
client.set_input_flow(flow(client, cancel=True))
ethereum.sign_tx(
client,
n=parse_path("m/44h/60h/0h/0/0"),
nonce=0x0,
gas_price=0x14,
gas_limit=0x14,
to="0x1d1c328764a41bda0492b66baa30c4a339ff85ef",
chain_id=1,
value=0xA,
tx_type=None,
data=bytes.fromhex(HEXDATA),
)
_sign_tx_call()

@ -14,19 +14,24 @@
# 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 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, **kwargs):
def do_recover_legacy(client: Client, mnemonic: list[str], **kwargs: Any):
def input_callback(_):
word, pos = client.debug.read_recovery_word()
if pos != 0:
if pos != 0 and pos is not None:
word = mnemonic[pos - 1]
mnemonic[pos - 1] = None
assert word is not None
@ -46,46 +51,15 @@ def do_recover_legacy(client: Client, mnemonic, **kwargs):
return ret
def do_recover_core(client: Client, mnemonic, **kwargs):
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 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 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, **kwargs)
def do_recover(client: Client, mnemonic):
def do_recover(client: Client, mnemonic: list[str]):
if client.features.model == "1":
return do_recover_legacy(client, mnemonic)
else:
@ -114,48 +88,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():
yield
assert "check the recovery seed" in layout().get_content()
client.debug.click(buttons.OK)
yield
assert "Select 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 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 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)
with client:
client.watch_layout()
client.set_input_flow(input_flow)
IF = InputFlowBip39RecoveryDryRunInvalid(client)
client.set_input_flow(IF.get())
with pytest.raises(exceptions.Cancelled):
return device.recover(client, dry_run=True)
@ -166,7 +102,13 @@ def test_uninitialized(client: Client):
do_recover(client, ["all"] * 12)
DRY_RUN_ALLOWED_FIELDS = ("dry_run", "word_count", "enforce_wordlist", "type")
DRY_RUN_ALLOWED_FIELDS = (
"dry_run",
"word_count",
"enforce_wordlist",
"type",
"show_tutorial",
)
def _make_bad_params():
@ -190,7 +132,7 @@ def _make_bad_params():
@pytest.mark.parametrize("field_name, field_value", _make_bad_params())
def test_bad_parameters(client: Client, field_name, field_value):
def test_bad_parameters(client: Client, field_name: str, field_value: Any):
msg = messages.RecoveryDevice(
dry_run=True,
word_count=12,

@ -205,7 +205,13 @@ def test_pin_fail(client: Client):
def test_already_initialized(client: Client):
with pytest.raises(RuntimeError):
device.recover(
client, 12, False, False, "label", "en-US", client.mnemonic_callback
client,
12,
False,
False,
"label",
"en-US",
client.mnemonic_callback,
)
ret = client.call_raw(

@ -20,54 +20,22 @@ 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():
yield
assert "recover wallet" in layout().text.lower()
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 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 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()
with client:
client.set_input_flow(input_flow)
IF = InputFlowBip39RecoveryPIN(client, MNEMONIC12.split(" "))
client.set_input_flow(IF.get())
client.watch_layout()
device.recover(
client, pin_protection=True, passphrase_protection=True, label="hello"
client,
pin_protection=True,
passphrase_protection=True,
label="hello",
)
assert client.debug.state().mnemonic_secret.decode() == MNEMONIC12
@ -80,40 +48,15 @@ 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():
yield
assert "recover wallet" in layout().text.lower()
client.debug.press_yes()
yield
assert "Select 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 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()
with client:
client.set_input_flow(input_flow)
IF = InputFlowBip39RecoveryNoPIN(client, MNEMONIC12.split(" "))
client.set_input_flow(IF.get())
client.watch_layout()
device.recover(
client, pin_protection=False, passphrase_protection=False, label="hello"
client,
pin_protection=False,
passphrase_protection=False,
label="hello",
)
assert client.debug.state().mnemonic_secret.decode() == MNEMONIC12

@ -19,10 +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,
from ...common import MNEMONIC_SLIP39_ADVANCED_20, MNEMONIC_SLIP39_ADVANCED_33
from ...input_flows import (
InputFlowSlip39AdvancedRecovery,
InputFlowSlip39AdvancedRecoveryAbort,
InputFlowSlip39AdvancedRecoveryNoAbort,
InputFlowSlip39AdvancedRecoveryTwoSharesWarning,
)
pytestmark = pytest.mark.skip_t1
@ -42,21 +44,17 @@ VECTORS = (
# To allow reusing functionality for multiple tests
def _test_secret(client: Client, shares, secret, click_info=False):
debug = client.debug
def input_flow():
yield # Confirm Recovery
debug.press_yes()
# Proceed with recovery
yield from recovery_enter_shares(
debug, shares, groups=True, click_info=click_info
)
def _test_secret(
client: Client, shares: list[str], secret: str, click_info: bool = False
):
with client:
client.set_input_flow(input_flow)
IF = InputFlowSlip39AdvancedRecovery(client, shares, click_info=click_info)
client.set_input_flow(IF.get())
ret = device.recover(
client, pin_protection=False, passphrase_protection=False, label="label"
client,
pin_protection=False,
passphrase_protection=False,
label="label",
)
# Workflow succesfully ended
@ -65,18 +63,18 @@ def _test_secret(client: Client, shares, secret, click_info=False):
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)
@pytest.mark.setup_client(uninitialized=True)
def test_secret(client: Client, shares, secret):
def test_secret(client: Client, shares: list[str], secret: str):
_test_secret(client, shares, secret)
@pytest.mark.parametrize("shares, secret", VECTORS)
@pytest.mark.setup_client(uninitialized=True)
def test_secret_click_info_button(client: Client, shares, secret):
def test_secret_click_info_button(client: Client, shares: list[str], secret: str):
_test_secret(client, shares, secret, click_info=True)
@ -91,18 +89,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")
client.init_device()
@ -111,21 +100,11 @@ def test_abort(client: Client):
@pytest.mark.setup_client(uninitialized=True)
def test_noabort(client: Client):
debug = client.debug
def input_flow():
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
)
with client:
client.set_input_flow(input_flow)
IF = InputFlowSlip39AdvancedRecoveryNoAbort(
client, EXTRA_GROUP_SHARE + MNEMONIC_SLIP39_ADVANCED_20
)
client.set_input_flow(IF.get())
device.recover(client, pin_protection=False, label="label")
client.init_device()
assert client.features.initialized is True
@ -133,80 +112,32 @@ 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():
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()
with client:
client.set_input_flow(input_flow)
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")
@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():
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()
with client:
client.set_input_flow(input_flow)
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")

@ -20,7 +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
from ...common import MNEMONIC_SLIP39_ADVANCED_20
from ...input_flows import InputFlowSlip39AdvancedRecoveryDryRun
pytestmark = pytest.mark.skip_t1
@ -39,18 +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():
yield # Confirm Dryrun
debug.press_yes()
# run recovery flow
yield from recovery_enter_shares(
debug, EXTRA_GROUP_SHARE + MNEMONIC_SLIP39_ADVANCED_20, groups=True
)
with client:
client.set_input_flow(input_flow)
IF = InputFlowSlip39AdvancedRecoveryDryRun(
client, EXTRA_GROUP_SHARE + MNEMONIC_SLIP39_ADVANCED_20
)
client.set_input_flow(IF.get())
ret = device.recover(
client,
passphrase_protection=False,
@ -68,21 +62,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():
yield # Confirm Dryrun
debug.press_yes()
# run recovery flow
yield from recovery_enter_shares(
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"
):
client.set_input_flow(input_flow)
IF = InputFlowSlip39AdvancedRecoveryDryRun(
client, INVALID_SHARES_SLIP39_ADVANCED_20
)
client.set_input_flow(IF.get())
device.recover(
client,
passphrase_protection=False,

@ -22,7 +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,
)
from ...input_flows import (
InputFlowSlip39BasicRecovery,
InputFlowSlip39BasicRecoveryAbort,
InputFlowSlip39BasicRecoveryNoAbort,
InputFlowSlip39BasicRecoveryPIN,
InputFlowSlip39BasicRecoveryRetryFirst,
InputFlowSlip39BasicRecoveryRetrySecond,
InputFlowSlip39BasicRecoverySameShare,
InputFlowSlip39BasicRecoveryWrongNthWord,
)
pytestmark = pytest.mark.skip_t1
@ -48,17 +57,10 @@ VECTORS = (
@pytest.mark.setup_client(uninitialized=True)
@pytest.mark.parametrize("shares, secret", VECTORS)
def test_secret(client: Client, shares, secret):
debug = client.debug
def input_flow():
yield # Confirm Recovery
debug.press_yes()
# run recovery flow
yield from recovery_enter_shares(debug, shares)
def test_secret(client: Client, shares: list[str], secret: str):
with client:
client.set_input_flow(input_flow)
IF = InputFlowSlip39BasicRecovery(client, shares)
client.set_input_flow(IF.get())
ret = device.recover(client, pin_protection=False, label="label")
# Workflow succesfully ended
@ -68,30 +70,24 @@ def test_secret(client: Client, shares, secret):
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():
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)
with client:
client.set_input_flow(input_flow)
IF = InputFlowSlip39BasicRecoveryPIN(
client, MNEMONIC_SLIP39_BASIC_20_3of6, "654"
)
client.set_input_flow(IF.get())
ret = device.recover(
client, pin_protection=True, passphrase_protection=True, label="label"
client,
pin_protection=True,
passphrase_protection=True,
label="label",
)
# 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
@ -100,18 +96,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")
client.init_device()
@ -120,19 +107,9 @@ def test_abort(client: Client):
@pytest.mark.setup_client(uninitialized=True)
def test_noabort(client: Client):
debug = client.debug
def input_flow():
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)
with client:
client.set_input_flow(input_flow)
IF = InputFlowSlip39BasicRecoveryNoAbort(client, MNEMONIC_SLIP39_BASIC_20_3of6)
client.set_input_flow(IF.get())
device.recover(client, pin_protection=False, label="label")
client.init_device()
assert client.features.initialized is True
@ -140,89 +117,19 @@ def test_noabort(client: Client):
@pytest.mark.setup_client(uninitialized=True)
def test_ask_word_number(client: Client):
debug = client.debug
def input_flow_retry_first():
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()
with client:
client.set_input_flow(input_flow_retry_first)
IF = InputFlowSlip39BasicRecoveryRetryFirst(client)
client.set_input_flow(IF.get())
with pytest.raises(exceptions.Cancelled):
device.recover(client, pin_protection=False, label="label")
client.init_device()
assert client.features.initialized is False
def input_flow_retry_second():
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()
with client:
client.set_input_flow(input_flow_retry_second)
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")
client.init_device()
@ -231,100 +138,40 @@ 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):
debug = client.debug
def test_wrong_nth_word(client: Client, nth_word: int):
share = MNEMONIC_SLIP39_BASIC_20_3of6[0].split(" ")
def input_flow():
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()
with client:
client.set_input_flow(input_flow)
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")
@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():
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
client.cancel()
with client:
client.set_input_flow(input_flow)
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")
@pytest.mark.setup_client(uninitialized=True)
def test_1of1(client: Client):
debug = client.debug
def input_flow():
yield # Confirm Recovery
debug.press_yes()
# Proceed with recovery
yield from recovery_enter_shares(
debug, MNEMONIC_SLIP39_BASIC_20_1of1, groups=False
)
with client:
client.set_input_flow(input_flow)
IF = InputFlowSlip39BasicRecovery(client, MNEMONIC_SLIP39_BASIC_20_1of1)
client.set_input_flow(IF.get())
ret = device.recover(
client, pin_protection=False, passphrase_protection=False, label="label"
client,
pin_protection=False,
passphrase_protection=False,
label="label",
)
# Workflow succesfully ended
# Workflow successfully ended
assert ret == messages.Success(message="Device recovered")
assert client.features.initialized is True
assert client.features.pin_protection is False

@ -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
from ...input_flows import InputFlowSlip39BasicRecovery
pytestmark = pytest.mark.skip_t1
@ -38,16 +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():
yield # Confirm Dryrun
debug.press_yes()
# run recovery flow
yield from recovery_enter_shares(debug, SHARES_20_2of3[1:3])
with client:
client.set_input_flow(input_flow)
IF = InputFlowSlip39BasicRecovery(client, SHARES_20_2of3[1:3])
client.set_input_flow(IF.get())
ret = device.recover(
client,
passphrase_protection=False,
@ -65,19 +58,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():
yield # Confirm Dryrun
debug.press_yes()
# run recovery flow
yield from recovery_enter_shares(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"
):
client.set_input_flow(input_flow)
IF = InputFlowSlip39BasicRecovery(client, INVALID_SHARES_20_2of3)
client.set_input_flow(IF.get())
device.recover(
client,
passphrase_protection=False,

@ -15,158 +15,50 @@
# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
from unittest import mock
import pytest
from shamir_mnemonic import shamir
from trezorlib import device, messages
from trezorlib import device
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
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)
from trezorlib.messages import BackupType
# mnemonic phrases
mnemonic = yield from read_and_confirm_mnemonic(client.debug)
from ...common import WITH_MOCK_URANDOM
from ...input_flows import (
InputFlowBip39Backup,
InputFlowResetSkipBackup,
InputFlowSlip39AdvancedBackup,
InputFlowSlip39BasicBackup,
)
# 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:
client.set_expected_responses(
[
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.Success),
messages.Success,
messages.Features,
]
)
client.set_input_flow(input_flow)
IF = InputFlowBip39Backup(client)
client.set_input_flow(IF.get())
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():
# 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()
with client:
client.set_input_flow(input_flow)
client.set_expected_responses(
[messages.ButtonRequest(code=B.ResetDevice)] * 6 # intro screens
+ [
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
]
* 5 # individual shares
+ [
messages.ButtonRequest(code=B.Success),
messages.Success,
messages.Features,
]
)
IF = InputFlowSlip39BasicBackup(client, False)
client.set_input_flow(IF.get())
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():
# 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()
with client:
client.set_input_flow(input_flow)
client.set_expected_responses(
[messages.ButtonRequest(code=B.ResetDevice)] * 6 # intro screens
+ [
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
]
* 5 # group thresholds
+ [
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
]
* 25 # individual shares
+ [
messages.ButtonRequest(code=B.Success),
messages.Success,
messages.Features,
]
)
IF = InputFlowSlip39AdvancedBackup(client, False)
client.set_input_flow(IF.get())
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
@ -183,9 +75,7 @@ VECTORS = [
@pytest.mark.parametrize("backup_type, backup_flow", VECTORS)
@pytest.mark.setup_client(uninitialized=True)
def test_skip_backup_msg(client: Client, backup_type, backup_flow):
os_urandom = mock.Mock(return_value=EXTERNAL_ENTROPY)
with mock.patch("os.urandom", os_urandom), client:
with WITH_MOCK_URANDOM, client:
device.reset(
client,
skip_backup=True,
@ -218,29 +108,9 @@ 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)
client.set_expected_responses(
[
messages.ButtonRequest(code=B.ResetDevice),
messages.EntropyRequest(),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.Success,
messages.Features,
]
)
with WITH_MOCK_URANDOM, client:
IF = InputFlowResetSkipBackup(client)
client.set_input_flow(IF.get())
device.reset(
client,
pin_protection=False,

@ -20,11 +20,10 @@ from mnemonic import Mnemonic
from trezorlib import messages
from trezorlib.debuglink import TrezorClientDebugLink as Client
from ...common import generate_entropy
from ...common import EXTERNAL_ENTROPY, generate_entropy
pytestmark = pytest.mark.skip_t2
EXTERNAL_ENTROPY = b"zlutoucky kun upel divoke ody" * 2
STRENGTH = 128

@ -21,15 +21,13 @@ from trezorlib import device, messages
from trezorlib.debuglink import TrezorClientDebugLink as Client
from trezorlib.tools import parse_path
from ...common import generate_entropy
from ...common import EXTERNAL_ENTROPY, generate_entropy
pytestmark = pytest.mark.skip_t2
def reset_device(client: Client, strength):
def reset_device(client: Client, strength: int):
# No PIN, no passphrase
external_entropy = b"zlutoucky kun upel divoke ody" * 2
ret = client.call_raw(
messages.ResetDevice(
display_random=False,
@ -48,10 +46,10 @@ def reset_device(client: Client, strength):
# Provide entropy
assert isinstance(ret, messages.EntropyRequest)
internal_entropy = client.debug.state().reset_entropy
ret = client.call_raw(messages.EntropyAck(entropy=external_entropy))
ret = client.call_raw(messages.EntropyAck(entropy=EXTERNAL_ENTROPY))
# Generate mnemonic locally
entropy = generate_entropy(strength, internal_entropy, external_entropy)
entropy = generate_entropy(strength, internal_entropy, EXTERNAL_ENTROPY)
expected_mnemonic = Mnemonic("english").to_mnemonic(entropy)
mnemonic = []
@ -104,7 +102,6 @@ def test_reset_device_192(client: Client):
@pytest.mark.setup_client(uninitialized=True)
def test_reset_device_256_pin(client: Client):
external_entropy = b"zlutoucky kun upel divoke ody" * 2
strength = 256
ret = client.call_raw(
@ -147,10 +144,10 @@ def test_reset_device_256_pin(client: Client):
# Provide entropy
assert isinstance(ret, messages.EntropyRequest)
internal_entropy = client.debug.state().reset_entropy
ret = client.call_raw(messages.EntropyAck(entropy=external_entropy))
ret = client.call_raw(messages.EntropyAck(entropy=EXTERNAL_ENTROPY))
# Generate mnemonic locally
entropy = generate_entropy(strength, internal_entropy, external_entropy)
entropy = generate_entropy(strength, internal_entropy, EXTERNAL_ENTROPY)
expected_mnemonic = Mnemonic("english").to_mnemonic(entropy)
mnemonic = []
@ -194,7 +191,6 @@ def test_reset_device_256_pin(client: Client):
@pytest.mark.setup_client(uninitialized=True)
def test_failed_pin(client: Client):
# external_entropy = b'zlutoucky kun upel divoke ody' * 2
strength = 128
ret = client.call_raw(

@ -14,67 +14,27 @@
# 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 unittest import mock
import pytest
from mnemonic import Mnemonic
from trezorlib import device, messages
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,
from ...common import EXTERNAL_ENTROPY, MNEMONIC12, WITH_MOCK_URANDOM, generate_entropy
from ...input_flows import (
InputFlowBip39ResetBackup,
InputFlowBip39ResetFailedCheck,
InputFlowBip39ResetPIN,
)
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
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:
client.set_expected_responses(
[
messages.ButtonRequest(code=B.ResetDevice),
messages.EntropyRequest(),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.Success),
messages.Success,
messages.Features,
]
)
client.set_input_flow(input_flow)
def reset_device(client: Client, strength: int):
with WITH_MOCK_URANDOM, client:
IF = InputFlowBip39ResetBackup(client)
client.set_input_flow(IF.get())
# No PIN, no passphrase, don't display random
device.reset(
@ -93,7 +53,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())
@ -120,72 +80,11 @@ 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")
# 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
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:
client.set_expected_responses(
[
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.PinEntry),
messages.ButtonRequest(code=B.PinEntry),
messages.ButtonRequest(code=B.ResetDevice),
messages.EntropyRequest(),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.Success),
messages.Success,
messages.Features,
]
)
client.set_input_flow(input_flow)
with WITH_MOCK_URANDOM, client:
IF = InputFlowBip39ResetPIN(client)
client.set_input_flow(IF.get())
# PIN, passphrase, display random
device.reset(
@ -204,7 +103,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())
@ -216,55 +115,11 @@ 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
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
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:
client.set_expected_responses(
[
messages.ButtonRequest(code=B.ResetDevice),
messages.EntropyRequest(),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.Success),
messages.Success,
messages.Features,
]
)
client.set_input_flow(input_flow)
with WITH_MOCK_URANDOM, client:
IF = InputFlowBip39ResetFailedCheck(client)
client.set_input_flow(IF.get())
# PIN, passphrase, display random
device.reset(
@ -283,7 +138,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())
@ -296,7 +151,6 @@ def test_reset_failed_check(client: Client):
@pytest.mark.setup_client(uninitialized=True)
def test_failed_pin(client: Client):
# external_entropy = b'zlutoucky kun upel divoke ody' * 2
strength = 128
ret = client.call_raw(
messages.ResetDevice(strength=strength, pin_protection=True, label="test")
@ -312,11 +166,21 @@ def test_failed_pin(client: Client):
client.debug.input("654")
ret = client.call_raw(messages.ButtonAck())
# Re-enter PIN
assert isinstance(ret, messages.ButtonRequest)
client.debug.press_yes()
ret = client.call_raw(messages.ButtonAck())
# Enter PIN for second time
assert isinstance(ret, messages.ButtonRequest)
client.debug.input("456")
ret = client.call_raw(messages.ButtonAck())
# PIN mismatch
assert isinstance(ret, messages.ButtonRequest)
client.debug.press_yes()
ret = client.call_raw(messages.ButtonAck())
assert isinstance(ret, messages.ButtonRequest)

@ -14,17 +14,15 @@
# 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 unittest import mock
import pytest
from trezorlib import btc, device, messages
from trezorlib.debuglink import TrezorClientDebugLink as Client
from trezorlib.messages import BackupType, ButtonRequestType as B
from trezorlib.messages import BackupType
from trezorlib.tools import parse_path
from ...common import EXTERNAL_ENTROPY, click_through, read_and_confirm_mnemonic
from ...common import WITH_MOCK_URANDOM
from ...input_flows import InputFlowBip39RecoveryNoPIN, InputFlowBip39ResetBackup
@pytest.mark.skip_t1
@ -39,46 +37,10 @@ def test_reset_recovery(client: Client):
assert address_before == address_after
def reset(client: Client, strength=128, skip_backup=False):
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
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:
client.set_expected_responses(
[
messages.ButtonRequest(code=B.ResetDevice),
messages.EntropyRequest(),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.Success),
messages.Success,
messages.Features,
]
)
client.set_input_flow(input_flow)
def reset(client: Client, strength: int = 128, skip_backup: bool = False) -> str:
with WITH_MOCK_URANDOM, client:
IF = InputFlowBip39ResetBackup(client)
client.set_input_flow(IF.get())
# No PIN, no passphrase, don't display random
device.reset(
@ -98,45 +60,16 @@ def reset(client: Client, strength=128, skip_backup=False):
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):
debug = client.debug
def recover(client: Client, mnemonic: str):
words = mnemonic.split(" ")
def input_flow():
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()
with client:
client.set_input_flow(input_flow)
client.set_expected_responses(
[
messages.ButtonRequest(code=B.ProtectCall),
messages.ButtonRequest(code=B.RecoveryHomepage),
messages.ButtonRequest(code=B.MnemonicWordCount),
messages.ButtonRequest(code=B.RecoveryHomepage),
messages.ButtonRequest(code=B.MnemonicInput),
messages.ButtonRequest(code=B.Success),
messages.Success,
messages.Features,
]
)
IF = InputFlowBip39RecoveryNoPIN(client, words)
client.set_input_flow(IF.get())
client.watch_layout()
ret = device.recover(client, pin_protection=False, label="label")
# Workflow successfully ended

@ -14,20 +14,17 @@
# 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 unittest import mock
import pytest
from trezorlib import btc, device, messages
from trezorlib.debuglink import TrezorClientDebugLink as Client
from trezorlib.messages import BackupType, ButtonRequestType as B
from trezorlib.messages import BackupType
from trezorlib.tools import parse_path
from ...common import (
EXTERNAL_ENTROPY,
click_through,
read_and_confirm_mnemonic,
recovery_enter_shares,
from ...common import WITH_MOCK_URANDOM
from ...input_flows import (
InputFlowSlip39AdvancedRecovery,
InputFlowSlip39AdvancedResetRecovery,
)
@ -60,77 +57,10 @@ def test_reset_recovery(client: Client):
assert address_before == address_after
def reset(client: Client, strength=128):
all_mnemonics = []
def input_flow():
# 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()
os_urandom = mock.Mock(return_value=EXTERNAL_ENTROPY)
with mock.patch("os.urandom", os_urandom), client:
client.set_expected_responses(
[
messages.ButtonRequest(code=B.ResetDevice),
messages.EntropyRequest(),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice), # group #1 counts
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice), # group #2 counts
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice), # group #3 counts
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice), # group #4 counts
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice), # group #5 counts
messages.ButtonRequest(code=B.ResetDevice),
]
+ [
# individual mnemonic
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
]
* (5 * 5) # groups * shares
+ [
messages.ButtonRequest(code=B.Success),
messages.Success,
messages.Features,
]
)
client.set_input_flow(input_flow)
def reset(client: Client, strength: int = 128) -> list[str]:
with WITH_MOCK_URANDOM, client:
IF = InputFlowSlip39AdvancedResetRecovery(client, False)
client.set_input_flow(IF.get())
# No PIN, no passphrase, don't display random
device.reset(
@ -150,20 +80,13 @@ def reset(client: Client, strength=128):
assert client.features.pin_protection is False
assert client.features.passphrase_protection is False
return all_mnemonics
def recover(client: Client, shares):
debug = client.debug
return IF.mnemonics
def input_flow():
yield # Confirm Recovery
debug.press_yes()
# run recovery flow
yield from recovery_enter_shares(debug, shares, groups=True)
def recover(client: Client, shares: list[str]):
with client:
client.set_input_flow(input_flow)
IF = InputFlowSlip39AdvancedRecovery(client, shares, False)
client.set_input_flow(IF.get())
ret = device.recover(client, pin_protection=False, label="label")
# Workflow successfully ended

@ -15,24 +15,24 @@
# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
import itertools
from unittest import mock
import pytest
from trezorlib import btc, device, messages
from trezorlib.debuglink import TrezorClientDebugLink as Client
from trezorlib.messages import BackupType, ButtonRequestType as B
from trezorlib.messages import BackupType
from trezorlib.tools import parse_path
from ...common import click_through, read_and_confirm_mnemonic, recovery_enter_shares
EXTERNAL_ENTROPY = b"zlutoucky kun upel divoke ody" * 2
MOCK_OS_URANDOM = mock.Mock(return_value=EXTERNAL_ENTROPY)
from ...common import WITH_MOCK_URANDOM
from ...input_flows import (
InputFlowSlip39BasicRecovery,
InputFlowSlip39BasicResetRecovery,
)
@pytest.mark.skip_t1
@pytest.mark.setup_client(uninitialized=True)
@mock.patch("os.urandom", MOCK_OS_URANDOM)
@WITH_MOCK_URANDOM
def test_reset_recovery(client: Client):
mnemonics = reset(client)
address_before = btc.get_address(client, "Bitcoin", parse_path("m/44h/0h/0h/0/0"))
@ -47,62 +47,10 @@ def test_reset_recovery(client: Client):
assert address_before == address_after
def reset(client: Client, strength=128):
all_mnemonics = []
def input_flow():
# 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 reset(client: Client, strength: int = 128) -> list[str]:
with client:
client.set_expected_responses(
[
messages.ButtonRequest(code=B.ResetDevice),
messages.EntropyRequest(),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
]
+ [
# individual mnemonic
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
]
* 5 # number of shares
+ [
messages.ButtonRequest(code=B.Success),
messages.Success,
messages.Features,
]
)
client.set_input_flow(input_flow)
IF = InputFlowSlip39BasicResetRecovery(client)
client.set_input_flow(IF.get())
# No PIN, no passphrase, don't display random
device.reset(
@ -122,20 +70,13 @@ def reset(client: Client, strength=128):
assert client.features.pin_protection is False
assert client.features.passphrase_protection is False
return all_mnemonics
def recover(client: Client, shares):
debug = client.debug
return IF.mnemonics
def input_flow():
yield # Confirm Recovery
debug.press_yes()
# run recovery flow
yield from recovery_enter_shares(debug, shares)
def recover(client: Client, shares: list[str]):
with client:
client.set_input_flow(input_flow)
IF = InputFlowSlip39BasicRecovery(client, shares)
client.set_input_flow(IF.get())
ret = device.recover(client, pin_protection=False, label="label")
# Workflow successfully ended

@ -14,98 +14,29 @@
# 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 unittest import mock
import pytest
from shamir_mnemonic import shamir
from trezorlib import device, messages
from trezorlib import device
from trezorlib.debuglink import TrezorClientDebugLink as Client
from trezorlib.exceptions import TrezorFailure
from trezorlib.messages import BackupType, ButtonRequestType as B
from trezorlib.messages import BackupType
from ...common import click_through, generate_entropy, read_and_confirm_mnemonic
from ...common import EXTERNAL_ENTROPY, WITH_MOCK_URANDOM, generate_entropy
from ...input_flows import InputFlowSlip39AdvancedResetRecovery
pytestmark = [pytest.mark.skip_t1]
EXTERNAL_ENTROPY = b"zlutoucky kun upel divoke ody" * 2
# TODO: test with different options
@pytest.mark.setup_client(uninitialized=True)
def test_reset_device_slip39_advanced(client: Client):
strength = 128
member_threshold = 3
all_mnemonics = []
def input_flow():
# 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()
os_urandom = mock.Mock(return_value=EXTERNAL_ENTROPY)
with mock.patch("os.urandom", os_urandom), client:
client.set_expected_responses(
[
messages.ButtonRequest(code=B.ResetDevice),
messages.EntropyRequest(),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice), # group #1 counts
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice), # group #2 counts
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice), # group #3 counts
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice), # group #4 counts
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice), # group #5 counts
messages.ButtonRequest(code=B.ResetDevice),
]
+ [
# individual mnemonic
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
]
* (5 * 5) # groups * shares
+ [
messages.ButtonRequest(code=B.Success),
messages.Success,
messages.Features,
]
)
client.set_input_flow(input_flow)
with WITH_MOCK_URANDOM, client:
IF = InputFlowSlip39AdvancedResetRecovery(client, False)
client.set_input_flow(IF.get())
# No PIN, no passphrase, don't display random
device.reset(
@ -124,7 +55,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
@ -138,7 +69,9 @@ def test_reset_device_slip39_advanced(client: Client):
device.backup(client)
def validate_mnemonics(mnemonics, threshold, expected_ems):
def validate_mnemonics(
mnemonics: list[list[str]], threshold: int, expected_ems: bytes
) -> None:
# 3of5 shares 3of5 groups
# TODO: test all possible group+share combinations?
test_combination = mnemonics[0:3] + mnemonics[5:8] + mnemonics[10:13]

@ -15,84 +15,27 @@
# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
from itertools import combinations
from unittest import mock
import pytest
from shamir_mnemonic import MnemonicError, shamir
from trezorlib import device, messages
from trezorlib import device
from trezorlib.debuglink import TrezorClientDebugLink as Client
from trezorlib.exceptions import TrezorFailure
from trezorlib.messages import BackupType, ButtonRequestType as B
from trezorlib.messages import BackupType
from ...common import (
EXTERNAL_ENTROPY,
click_through,
generate_entropy,
read_and_confirm_mnemonic,
)
from ...common import EXTERNAL_ENTROPY, WITH_MOCK_URANDOM, 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():
# 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()
os_urandom = mock.Mock(return_value=EXTERNAL_ENTROPY)
with mock.patch("os.urandom", os_urandom), client:
client.set_expected_responses(
[
messages.ButtonRequest(code=B.ResetDevice),
messages.EntropyRequest(),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
]
+ [
# individual mnemonic
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
]
* 5 # number of shares
+ [
messages.ButtonRequest(code=B.Success),
messages.Success,
messages.Features,
]
)
client.set_input_flow(input_flow)
with WITH_MOCK_URANDOM, client:
IF = InputFlowSlip39BasicResetRecovery(client)
client.set_input_flow(IF.get())
# No PIN, no passphrase, don't display random
device.reset(
@ -111,7 +54,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

@ -21,55 +21,30 @@ import shamir_mnemonic as shamir
from trezorlib import device, messages
from trezorlib.debuglink import TrezorClientDebugLink as Client
from trezorlib.exceptions import TrezorFailure
from trezorlib.messages import ButtonRequestType as B
from ..common import (
MNEMONIC12,
MNEMONIC_SLIP39_ADVANCED_20,
MNEMONIC_SLIP39_BASIC_20_3of6,
read_and_confirm_mnemonic,
)
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
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)
client.set_expected_responses(
[
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
messages.ButtonRequest(code=B.Success),
messages.Success,
messages.Features,
]
)
IF = InputFlowBip39Backup(client)
client.set_input_flow(IF.get())
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
@ -85,53 +60,10 @@ def test_backup_bip39(client: Client):
)
def test_backup_slip39_basic(client: Client, click_info: bool):
assert client.features.needs_backup is True
mnemonics = []
def input_flow():
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)
yield # Confirm continue to next
client.debug.press_yes()
yield # Confirm backup
client.debug.press_yes()
with client:
client.set_input_flow(input_flow)
client.set_expected_responses(
[messages.ButtonRequest(code=B.ResetDevice)]
* (8 if click_info else 6) # intro screens (and optional info)
+ [
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
]
* 5 # individual shares
+ [
messages.ButtonRequest(code=B.Success),
messages.Success,
messages.Features,
]
)
IF = InputFlowSlip39BasicBackup(client, click_info)
client.set_input_flow(IF.get())
device.backup(client)
client.init_device()
@ -142,7 +74,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
@ -153,70 +85,10 @@ def test_backup_slip39_basic(client: Client, click_info: bool):
)
def test_backup_slip39_advanced(client: Client, click_info: bool):
assert client.features.needs_backup is True
mnemonics = []
def input_flow():
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()
with client:
client.set_input_flow(input_flow)
client.set_expected_responses(
[messages.ButtonRequest(code=B.ResetDevice)]
* (8 if click_info else 6) # intro screens (and optional info)
+ [
(click_info, messages.ButtonRequest(code=B.ResetDevice)),
messages.ButtonRequest(code=B.ResetDevice),
(click_info, messages.ButtonRequest(code=B.ResetDevice)),
messages.ButtonRequest(code=B.ResetDevice),
]
* 5 # group thresholds (and optional info)
+ [
messages.ButtonRequest(code=B.ResetDevice),
messages.ButtonRequest(code=B.Success),
]
* 25 # individual shares
+ [
messages.ButtonRequest(code=B.Success),
messages.Success,
messages.Features,
]
)
IF = InputFlowSlip39AdvancedBackup(client, click_info)
client.set_input_flow(IF.get())
device.backup(client)
client.init_device()
@ -228,7 +100,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

@ -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"
@ -29,7 +31,7 @@ WIPE_CODE_MAX = "".join(chr((i % 10) + ord("0")) for i in range(MAX_PIN_LENGTH))
pytestmark = pytest.mark.skip_t1
def _check_wipe_code(client: Client, pin, wipe_code):
def _check_wipe_code(client: Client, pin: str, wipe_code: str):
client.init_device()
assert client.features.wipe_code_protection is True
@ -37,13 +39,13 @@ def _check_wipe_code(client: Client, pin, wipe_code):
with client, pytest.raises(TrezorFailure):
client.use_pin_sequence([pin, wipe_code, wipe_code])
client.set_expected_responses(
[messages.ButtonRequest()] * 5
[messages.ButtonRequest()] * 6
+ [messages.Failure(code=messages.FailureType.PinInvalid)]
)
device.change_pin(client)
def _ensure_unlocked(client: Client, pin):
def _ensure_unlocked(client: Client, pin: str):
with client:
client.use_pin_sequence([pin])
btc.get_address(client, "Testnet", PASSPHRASE_TEST_PATH)
@ -61,7 +63,7 @@ def test_set_remove_wipe_code(client: Client):
with client:
client.set_expected_responses(
[messages.ButtonRequest()] * 5 + [messages.Success, messages.Features]
[messages.ButtonRequest()] * 7 + [messages.Success, messages.Features]
)
client.use_pin_sequence([PIN4, WIPE_CODE_MAX, WIPE_CODE_MAX])
device.change_wipe_code(client)
@ -73,7 +75,7 @@ def test_set_remove_wipe_code(client: Client):
# Test change wipe code.
with client:
client.set_expected_responses(
[messages.ButtonRequest()] * 5 + [messages.Success, messages.Features]
[messages.ButtonRequest()] * 6 + [messages.Success, messages.Features]
)
client.use_pin_sequence([PIN4, WIPE_CODE6, WIPE_CODE6])
device.change_wipe_code(client)
@ -95,24 +97,9 @@ 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):
client.set_expected_responses(
[messages.ButtonRequest()] * 4 + [messages.Failure()]
)
client.set_input_flow(input_flow)
IF = InputFlowNewCodeMismatch(client, WIPE_CODE4, WIPE_CODE6)
client.set_input_flow(IF.get())
device.change_wipe_code(client)
@ -127,7 +114,7 @@ def test_set_wipe_code_to_pin(client: Client):
with client:
client.set_expected_responses(
[messages.ButtonRequest()] * 6 + [messages.Success, messages.Features]
[messages.ButtonRequest()] * 9 + [messages.Success, messages.Features]
)
client.use_pin_sequence([PIN4, PIN4, WIPE_CODE4, WIPE_CODE4])
device.change_wipe_code(client)
@ -141,7 +128,7 @@ def test_set_pin_to_wipe_code(client: Client):
# Set wipe code.
with client:
client.set_expected_responses(
[messages.ButtonRequest()] * 4 + [messages.Success, messages.Features]
[messages.ButtonRequest()] * 6 + [messages.Success, messages.Features]
)
client.use_pin_sequence([WIPE_CODE4, WIPE_CODE4])
device.change_wipe_code(client)
@ -149,7 +136,7 @@ def test_set_pin_to_wipe_code(client: Client):
# Try to set the PIN to the current wipe code value.
with client, pytest.raises(TrezorFailure):
client.set_expected_responses(
[messages.ButtonRequest()] * 4
[messages.ButtonRequest()] * 6
+ [messages.Failure(code=messages.FailureType.PinInvalid)]
)
client.use_pin_sequence([WIPE_CODE4, WIPE_CODE4])

@ -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
@ -58,7 +64,7 @@ def test_set_pin(client: Client):
with client:
client.use_pin_sequence([PIN_MAX, PIN_MAX])
client.set_expected_responses(
[messages.ButtonRequest] * 4 + [messages.Success, messages.Features]
[messages.ButtonRequest] * 6 + [messages.Success, messages.Features]
)
device.change_pin(client)
@ -78,7 +84,7 @@ def test_change_pin(client: Client):
with client:
client.use_pin_sequence([PIN4, PIN_MAX, PIN_MAX])
client.set_expected_responses(
[messages.ButtonRequest] * 5 + [messages.Success, messages.Features]
[messages.ButtonRequest] * 6 + [messages.Success, messages.Features]
)
device.change_pin(client)
@ -116,22 +122,9 @@ 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):
client.set_expected_responses([messages.ButtonRequest] * 4 + [messages.Failure])
client.set_input_flow(input_flow)
IF = InputFlowNewCodeMismatch(client, PIN4, PIN60)
client.set_input_flow(IF.get())
device.change_pin(client)
@ -148,24 +141,9 @@ 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):
client.set_expected_responses([messages.ButtonRequest] * 5 + [messages.Failure])
client.set_input_flow(input_flow)
IF = InputFlowCodeChangeFail(client, PIN4, "457891", "381847")
client.set_input_flow(IF.get())
device.change_pin(client)
@ -182,18 +160,9 @@ 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):
client.set_expected_responses([messages.ButtonRequest] * 3 + [messages.Failure])
client.set_input_flow(input_flow)
IF = InputFlowWrongPIN(client, PIN60)
client.set_input_flow(IF.get())
device.change_pin(client)

@ -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"
@ -78,13 +79,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
def test_exponential_backoff_t1(client: Client):
for attempt in range(3):
@ -92,21 +86,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)

@ -14,8 +14,6 @@
# 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 unittest import mock
import pytest
from trezorlib import btc, device, messages, misc
@ -23,7 +21,7 @@ from trezorlib.debuglink import TrezorClientDebugLink as Client
from trezorlib.exceptions import TrezorFailure
from trezorlib.tools import parse_path
from ..common import EXTERNAL_ENTROPY, MNEMONIC12, get_test_address
from ..common import MNEMONIC12, WITH_MOCK_URANDOM, get_test_address
from ..tx_cache import TxCache
from .bitcoin.signtx import (
request_finished,
@ -141,6 +139,7 @@ def test_change_pin_t2(client: Client):
messages.ButtonRequest,
_pin_request(client),
_pin_request(client),
messages.ButtonRequest,
_pin_request(client),
messages.ButtonRequest,
messages.Success,
@ -214,8 +213,7 @@ def test_wipe_device(client: Client):
def test_reset_device(client: Client):
assert client.features.pin_protection is False
assert client.features.passphrase_protection is False
os_urandom = mock.Mock(return_value=EXTERNAL_ENTROPY)
with mock.patch("os.urandom", os_urandom), client:
with WITH_MOCK_URANDOM, client:
client.set_expected_responses(
[messages.ButtonRequest]
+ [messages.EntropyRequest]
@ -253,7 +251,13 @@ def test_recovery_device(client: Client):
)
device.recover(
client, 12, False, False, "label", "en-US", client.mnemonic_callback
client,
12,
False,
False,
"label",
"en-US",
client.mnemonic_callback,
)
with pytest.raises(TrezorFailure):

@ -53,19 +53,19 @@ def test_sd_protect_unlock(client: Client):
def input_flow_enable_sd_protect():
yield # Enter PIN to unlock device
assert "< PinKeyboard >" == layout().text
assert layout().main_component() == "PinKeyboard"
client.debug.input("1234")
yield # do you really want to enable SD protection
assert "SD card protection" in layout().get_content()
assert "SD card protection" in layout().text_content()
client.debug.press_yes()
yield # enter current PIN
assert "< PinKeyboard >" == layout().text
assert layout().main_component() == "PinKeyboard"
client.debug.input("1234")
yield # you have successfully enabled SD protection
assert "You have successfully enabled SD protection." in layout().get_content()
assert "You have successfully enabled SD protection." in layout().text_content()
client.debug.press_yes()
with client:
@ -75,23 +75,27 @@ def test_sd_protect_unlock(client: Client):
def input_flow_change_pin():
yield # do you really want to change PIN?
assert "PIN SETTINGS" == layout().get_title()
assert "PIN SETTINGS" == layout().title()
client.debug.press_yes()
yield # enter current PIN
assert "< PinKeyboard >" == layout().text
assert layout().main_component() == "PinKeyboard"
client.debug.input("1234")
yield # enter new PIN
assert "< PinKeyboard >" == layout().text
assert layout().main_component() == "PinKeyboard"
client.debug.input("1234")
yield # re-enter to confirm
assert "re-enter to confirm" in layout().text_content()
client.debug.press_yes()
yield # enter new PIN again
assert "< PinKeyboard >" == layout().text
assert layout().main_component() == "PinKeyboard"
client.debug.input("1234")
yield # Pin change successful
assert "You have successfully changed your PIN." in layout().get_content()
assert "PIN changed" in layout().text_content()
client.debug.press_yes()
with client:
@ -103,15 +107,15 @@ def test_sd_protect_unlock(client: Client):
def input_flow_change_pin_format():
yield # do you really want to change PIN?
assert "PIN SETTINGS" == layout().get_title()
assert "PIN SETTINGS" == layout().title()
client.debug.press_yes()
yield # enter current PIN
assert "< PinKeyboard >" == layout().text
assert layout().main_component() == "PinKeyboard"
client.debug.input("1234")
yield # SD card problem
assert "Wrong SD card" in layout().get_content()
assert "Wrong SD card" in layout().text_content()
client.debug.press_no() # close
with client, pytest.raises(TrezorFailure) as e:

@ -397,7 +397,7 @@ def test_hide_passphrase_from_host(client: Client):
layout = client.debug.wait_layout()
assert (
"Passphrase provided by host will be used but will not be displayed due to the device settings."
in layout.get_content()
in layout.text_content()
)
client.debug.press_yes()
@ -426,13 +426,14 @@ def test_hide_passphrase_from_host(client: Client):
def input_flow():
yield
layout = client.debug.wait_layout()
assert "Next screen will show the passphrase." in layout.get_content()
assert "Next screen will show the passphrase" in layout.text_content()
client.debug.press_yes()
yield
layout = client.debug.wait_layout()
assert "confirm passphrase" in layout.get_title().lower()
assert passphrase in layout.get_content()
assert "confirm passphrase" in layout.title().lower()
assert passphrase in layout.text_content()
client.debug.press_yes()
client.watch_layout()

Loading…
Cancel
Save