def test_change_failed(client: Client): assert client.features.pin_protection is True # 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) device.change_pin(client) # Check that there's still old PIN protection client.init_device() assert client.features.pin_protection is True _check_pin(client, PIN4)
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) with client: client.set_input_flow(input_flow) ret = device.recover(client, pin_protection=False, passphrase_protection=False, label="label") # Workflow succesfully ended assert ret == messages.Success(message="Device recovered") assert client.features.initialized is True 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
def backup_flow_bip39(client: Client): mnemonic = None def input_flow(): nonlocal mnemonic # 1. Confirm Reset yield from click_through(client.debug, screens=1, code=B.ResetDevice) # mnemonic phrases 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() 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) device.backup(client) return mnemonic.encode()
def setup_device_core(client: Client, pin: str, wipe_code: str) -> None: device.wipe(client) debuglink.load_device( client, MNEMONIC12, pin, passphrase_protection=False, label="WIPECODE" ) def input_flow(): yield # do you want to set/change the wipe_code? client.debug.press_yes() if pin is not None: yield # enter current pin client.debug.input(pin) yield # enter new wipe code client.debug.input(wipe_code) yield # enter new wipe code again client.debug.input(wipe_code) yield # success client.debug.press_yes() with client: client.set_expected_responses( [messages.ButtonRequest()] * 5 + [messages.Success, messages.Features] ) client.set_input_flow(input_flow) device.change_wipe_code(client)
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) ret = device.recover( client, passphrase_protection=False, pin_protection=False, label="label", language="en-US", dry_run=True, ) # Dry run was successful assert ret == messages.Success( message="The seed is valid and matches the one in the device")
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) with pytest.raises(exceptions.Cancelled): device.recover(client, pin_protection=False, label="label")
def test_signtx_data_pagination(client: Client, flow): with client: client.watch_layout() client.set_input_flow(flow(client)) 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), ) 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), )
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, ]) device.backup(client) assert mnemonic == MNEMONIC12 client.init_device() assert client.features.initialized is True assert client.features.needs_backup is False assert client.features.unfinished_backup is False assert client.features.no_backup is False assert client.features.backup_type is messages.BackupType.Bip39
def test_set_failed(client: Client): assert client.features.pin_protection is False # 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) device.change_pin(client) # Check that there's still no PIN protection now client.init_device() assert client.features.pin_protection is False _check_no_pin(client)
def test_change_invalid_current(client: Client): assert client.features.pin_protection is True # 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) device.change_pin(client) # Check that there's still old PIN protection client.init_device() assert client.features.pin_protection is True _check_pin(client, PIN4)
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, ]) device.backup(client) client.init_device() assert client.features.initialized is True assert client.features.needs_backup is False assert client.features.unfinished_backup is False assert client.features.no_backup is False 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]) assert expected_ms == actual_ms
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) # No PIN, no passphrase, don't display random device.reset( client, display_random=False, strength=strength, passphrase_protection=False, pin_protection=False, label="test", language="en-US", backup_type=BackupType.Bip39, ) # Check if device is properly initialized assert client.features.initialized is True assert client.features.needs_backup is False assert client.features.pin_protection is False assert client.features.passphrase_protection is False return mnemonic
def test_cancel_message_via_cancel(client: Client, message): def input_flow(): yield client.cancel() with client, pytest.raises(Cancelled): client.set_expected_responses([m.ButtonRequest(), m.Failure()]) client.set_input_flow(input_flow) client.call(message)
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)) ethereum.sign_typed_data( client, parse_path("m/44h/60h/0h/0/0"), DATA, metamask_v4_compat=True, )
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)) ethereum.sign_typed_data( client, parse_path("m/44h/60h/0h/0/0"), DATA, metamask_v4_compat=True, )
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, ]) device.backup(client) mnemonics = mnemonics[0:3] + mnemonics[5:8] + mnemonics[10:13] groups = shamir.decode_mnemonics(mnemonics) ems = shamir.recover_ems(groups) return ems.ciphertext
def test_sd_no_format(client: Client): def input_flow(): yield # enable SD protection? client.debug.press_yes() yield # format SD card client.debug.press_no() with pytest.raises(TrezorFailure) as e, client: client.set_input_flow(input_flow) device.sd_protect(client, Op.ENABLE) assert e.value.code == messages.FailureType.ProcessError
def test_tt_pin_passphrase(client: Client): layout = client.debug.wait_layout mnemonic = MNEMONIC12.split(" ") def input_flow(): yield assert "Do you really want to recover a wallet?" in layout().text client.debug.press_yes() yield assert layout().text == "PinDialog" client.debug.input("654") yield assert layout().text == "PinDialog" client.debug.input("654") yield assert "Select number of words" in layout().text client.debug.press_yes() yield assert "WordSelector" in layout().text client.debug.input(str(len(mnemonic))) yield assert "Enter recovery seed" in layout().text client.debug.press_yes() yield for word in mnemonic: assert layout().text == "Bip39Keyboard" client.debug.input(word) yield assert "You have successfully recovered your wallet." in layout().text client.debug.press_yes() with client: client.set_input_flow(input_flow) client.watch_layout() device.recover( client, pin_protection=True, passphrase_protection=True, label="hello" ) assert client.debug.state().mnemonic_secret.decode() == MNEMONIC12 assert client.features.pin_protection is True assert client.features.passphrase_protection is True assert client.features.backup_type is messages.BackupType.Bip39 assert client.features.label == "hello"
def test_invalid_seed_core(client: Client): def input_flow(): yield layout = client.debug.wait_layout() assert "check the recovery seed" in layout.text.replace("\n", " ") client.debug.click(buttons.OK) yield layout = client.debug.wait_layout() assert "Select number of words" in layout.text client.debug.click(buttons.OK) yield layout = client.debug.wait_layout() assert layout.text == "WordSelector" # select 12 words client.debug.click(buttons.grid34(0, 2)) yield layout = client.debug.wait_layout() assert "Enter recovery seed" in layout.text client.debug.click(buttons.OK) yield for _ in range(12): layout = client.debug.wait_layout() assert layout.text == "Bip39Keyboard" client.debug.input("stick") br = yield layout = client.debug.wait_layout() assert br.code == messages.ButtonRequestType.Warning assert "invalid recovery seed" in layout.text client.debug.click(buttons.OK) yield # retry screen layout = client.debug.wait_layout() assert "Select number of words" in layout.text client.debug.click(buttons.CANCEL) yield layout = client.debug.wait_layout() assert "abort" in layout.text client.debug.click(buttons.OK) with client: client.watch_layout() client.set_input_flow(input_flow) with pytest.raises(exceptions.Cancelled): return device.recover(client, dry_run=True)
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) get_test_address(client)
def test_payment_request_details(client: Client): # Test that payment request details are shown when requested. outputs[0].payment_req_index = 0 outputs[1].payment_req_index = 0 outputs[2].payment_req_index = None nonce = misc.get_nonce(client) payment_reqs = [ make_payment_request( client, recipient_name="trezor.io", outputs=outputs[:2], memos=[TextMemo("Invoice #87654321.")], nonce=nonce, ) ] 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 second output layout = client.debug.wait_layout() assert outputs[1].address[:16] in layout.text client.debug.press_yes() yield # confirm transaction client.debug.press_yes() with client: client.set_input_flow(input_flow) client.watch_layout(True) _, serialized_tx = btc.sign_tx( client, "Testnet", inputs, outputs, prev_txes=PREV_TXES, payment_reqs=payment_reqs, ) assert serialized_tx.hex() == SERIALIZED_TX
def test_show(client: Client, path, script_type, address): def input_flow(): yield client.debug.press_no() yield client.debug.press_yes() with client: client.set_input_flow(input_flow) assert (btc.get_address( client, "Bitcoin", tools.parse_path(path), script_type=script_type, show_display=True, ) == address)
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, ]) device.reset( client, pin_protection=False, passphrase_protection=False, backup_type=backup_type, ) assert client.features.initialized is True assert client.features.needs_backup is True assert client.features.unfinished_backup is False assert client.features.no_backup is False assert client.features.backup_type is backup_type secret = backup_flow(client) client.init_device() assert client.features.initialized is True assert client.features.needs_backup is False assert client.features.unfinished_backup is False assert client.features.backup_type is backup_type assert secret is not None state = client.debug.state() assert state.mnemonic_type is backup_type assert state.mnemonic_secret == secret
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) with pytest.raises(exceptions.Cancelled): device.recover(client, pin_protection=False, label="label") client.init_device() assert client.features.initialized is False
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) device.recover(client, pin_protection=False, label="label") client.init_device() assert client.features.initialized is True
def recover(client: Client, shares): debug = client.debug def input_flow(): yield # Confirm Recovery debug.press_yes() # run recovery flow yield from recovery_enter_shares(debug, shares) with client: client.set_input_flow(input_flow) ret = device.recover(client, pin_protection=False, label="label") # Workflow successfully ended assert ret == messages.Success(message="Device recovered") assert client.features.pin_protection is False assert client.features.passphrase_protection is False
def test_signmessage_pagination(client: Client, message): message_read = "" def input_flow(): # collect screen contents into `message_read`. # Join lines that are separated by a single "-" string, space-separate lines otherwise. nonlocal message_read # confirm address br = yield layout = client.debug.wait_layout() client.debug.press_yes() # start assuming there was a word break; this avoids prepending space at start word_break = True br = yield for i in range(br.pages): layout = client.debug.wait_layout() for line in layout.lines[1:]: if line == "-": # next line will be attached without space word_break = True elif word_break: # attach without space, reset word_break message_read += line word_break = False else: # attach with space message_read += " " + line if i < br.pages - 1: client.debug.swipe_up() client.debug.press_yes() with client: client.set_input_flow(input_flow) client.debug.watch_layout(True) btc.sign_message( client, coin_name="Bitcoin", n=parse_path("m/44h/0h/0h/0/0"), message=message, ) assert "Confirm message: " + message.replace("\n", " ") == message_read
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, ]) device.backup(client) groups = shamir.decode_mnemonics(mnemonics[:3]) ems = shamir.recover_ems(groups) return ems.ciphertext
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) with client: client.set_input_flow(input_flow) ret = device.recover(client, pin_protection=False, label="label") # Workflow succesfully ended assert ret == messages.Success(message="Device recovered") assert client.features.pin_protection is False assert client.features.passphrase_protection is False assert client.features.backup_type is messages.BackupType.Slip39_Basic # Check mnemonic assert debug.state().mnemonic_secret.hex() == secret
def recover(client: Client, mnemonic): debug = client.debug 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, ]) ret = device.recover(client, pin_protection=False, label="label") # Workflow successfully ended assert ret == messages.Success(message="Device recovered") assert client.features.pin_protection is False assert client.features.passphrase_protection is False
if __name__ == "__main__": wirelink = get_device() client = TrezorClientDebugLink(wirelink) client.open() i = 0 last_pin = None while True: # set private field device.apply_settings(client, use_passphrase=True) assert client.features.passphrase_protection is True device.apply_settings(client, use_passphrase=False) assert client.features.passphrase_protection is False # set public field label = "".join(random.choices(string.ascii_uppercase + string.digits, k=17)) device.apply_settings(client, label=label) assert client.features.label == label # change PIN new_pin = "".join(random.choices(string.digits, k=random.randint(6, 10))) client.set_input_flow(pin_input_flow(client, last_pin, new_pin)) device.change_pin(client) client.set_input_flow(None) last_pin = new_pin print("iteration %d" % i) i = i + 1