def test_recovery_device(self): self.client.set_mnemonic(self.mnemonic12) with self.client: self.client.set_expected_responses( [proto.ButtonRequest()] + [proto.WordRequest()] * 24 + [proto.Success(), proto.Features()] ) device.recover( self.client, 12, False, False, "label", "english", self.client.mnemonic_callback, ) # This must fail, because device is already initialized with pytest.raises(RuntimeError): device.recover( self.client, 12, False, False, "label", "english", self.client.mnemonic_callback, )
def test_already_initialized(self): self.setup_mnemonic_nopin_nopassphrase() with pytest.raises(RuntimeError): device.recover( self.client, 12, False, False, "label", "english", self.client.mnemonic_callback, )
def test_wrong_nth_word(client, nth_word): debug = client.debug 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 code = yield assert 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_same_share(client): debug = client.debug first_share = SHARES_20_3of6[0].split(" ") # second share is first 4 words of first second_share = SHARES_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: time.sleep(1) debug.input(word) yield # Continue to next share debug.press_yes() yield # Enter next share for word in second_share: time.sleep(1) debug.input(word) code = yield assert 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_2of3_invalid_seed_dryrun(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_2of3_GROUPS, 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) device.recover( client, passphrase_protection=False, pin_protection=False, label="label", language="english", dry_run=True, )
def test_invalid_seed_core(client): def input_flow(): yield layout = client.debug.wait_layout() assert "check the recovery seed" in layout.text 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): client.debug.input("stick") code = yield layout = client.debug.wait_layout() assert 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.set_input_flow(input_flow) with pytest.raises(exceptions.Cancelled): return device.recover(client, dry_run=True)
def recover(client, shares): debug = client.debug def input_flow(): yield # Confirm Recovery debug.press_yes() # run recovery flow yield from recovery_enter_shares(debug, shares, groups=True) 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 do_recover_legacy(client, mnemonic, **kwargs): def input_callback(_): word, pos = client.debug.read_recovery_word() if pos != 0: word = mnemonic[pos - 1] mnemonic[pos - 1] = None assert word is not None return word ret = device.recover(client, dry_run=True, word_count=len(mnemonic), type=messages.RecoveryDeviceType.ScrambledWords, input_callback=input_callback, **kwargs) # if the call succeeded, check that all words have been used assert all(m is None for m in mnemonic) return ret
def test_secret(client, shares, secret): debug = client.debug def input_flow(): yield # Confirm Recovery debug.press_yes() # run recovery flow yield from enter_all_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 # Check mnemonic assert debug.read_mnemonic_secret().hex() == secret
def test_recover_no_pin_no_passphrase(client): debug = client.debug def input_flow(): yield # Confirm Recovery debug.press_yes() # Proceed with recovery yield from enter_all_shares(debug, SHARES_20_2of3_2of3_GROUPS) 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
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
def do_recover_core(client, mnemonic, **kwargs): 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" # 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 layout = client.debug.wait_layout() assert "Enter recovery seed" in layout.text 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) with client: client.watch_layout() client.set_input_flow(input_flow) return device.recover(client, dry_run=True, **kwargs)
def test_secret(client, shares, secret): debug = client.debug def input_flow(): yield # Confirm Recovery debug.press_yes() # Proceed with recovery yield from recovery_enter_shares(debug, shares, groups=True) 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 test_2of3_dryrun(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) ret = device.recover( client, passphrase_protection=False, pin_protection=False, label="label", language="english", 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_1of1(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) 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_Basic
def test_recover_with_pin_passphrase(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 enter_all_shares(debug, SHARES_20_3of6) with client: client.set_input_flow(input_flow) ret = device.recover(client, pin_protection=True, passphrase_protection=True, label="label") # Workflow succesfully ended assert ret == messages.Success(message="Device recovered") assert client.features.pin_protection is True assert client.features.passphrase_protection is True
def test_recover_with_pin_passphrase(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) ret = device.recover( client, pin_protection=True, passphrase_protection=True, label="label" ) # Workflow succesfully ended assert ret == messages.Success(message="Device recovered") assert client.features.pin_protection is True assert client.features.passphrase_protection is True assert client.features.backup_type is messages.BackupType.Slip39_Basic
def test_already_initialized(client): with pytest.raises(RuntimeError): device.recover(client) with pytest.raises(exceptions.TrezorFailure, match="Already initialized"): client.call(messages.RecoveryDevice())
def test_already_initialized(self): self.setup_mnemonic_nopin_nopassphrase() with pytest.raises(Exception): device.recover(self.client, 12, False, False, "label", "english")
def test_ask_word_number(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) 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) with pytest.raises(exceptions.Cancelled): device.recover(client, pin_protection=False, label="label") client.init_device() assert client.features.initialized is False
def test_already_initialized(self, client): with pytest.raises(RuntimeError): device.recover(client, 12, False, False, "label", "english", client.mnemonic_callback)
def recover_device(hw_device_id: str, hw_device_transport_id: Any, hw_client: Any, word_count: int, passphrase_enabled: bool, pin_enabled: bool, hw_label: str, input_type: Literal["scrambled_words", "matrix"], parent_window: Optional[QWidget] = None) -> Optional[str]: mnem = Mnemonic('english') type = { "scrambled_words": messages.RecoveryDeviceType.ScrambledWords }.get(input_type, messages.RecoveryDeviceType.Matrix) def ask_for_word(_type): nonlocal mnem if _type == 0: msg = "Enter one word of mnemonic: " word = ask_for_word_callback(msg, mnem.wordlist, parent_window) if not word: raise exceptions.Cancelled return word elif _type in (1, 2): # _type # 1: matrix has three columns # 2: matrix has two columns element = hw_common.ask_for_martix_element_callback( "<span>Select the matrix element that corresponds<br>" "to the part of the word displayed on<br>" "the device screen</span>", columns=3 if _type == 1 else 2, parent_window=parent_window) if element: return element else: raise exceptions.Cancelled else: raise exceptions.Cancelled client = None try: if not hw_client: client = open_session(hw_device_id, hw_device_transport_id) else: client = hw_client if client: if client.features.initialized: device.wipe(client) hw_device_id = client.features.device_id device.recover(client, word_count, passphrase_enabled, pin_enabled, hw_label, input_callback=ask_for_word, type=type) return hw_device_id else: raise Exception('Couldn\'t connect to Trezor device.') except exceptions.Cancelled: raise CancelException except exceptions.TrezorFailure as e: if e.failure.message == 'Device not initialized': raise HwNotInitialized(e.failure.message) else: raise finally: if client and hw_client != client: client.close()