def test_recovery_device(client: Client):
    assert client.features.pin_protection is False
    assert client.features.passphrase_protection is False
    client.use_mnemonic(MNEMONIC12)
    with client:
        client.set_expected_responses(
            [messages.ButtonRequest]
            + [messages.WordRequest] * 24
            + [messages.Success, messages.Features]
        )

        device.recover(
            client, 12, False, False, "label", "en-US", client.mnemonic_callback
        )

    with pytest.raises(TrezorFailure):
        # This must fail, because device is already initialized
        # Using direct call because `device.recover` has its own check
        client.call(
            messages.RecoveryDevice(
                word_count=12,
                passphrase_protection=False,
                pin_protection=False,
                label="label",
                language="en-US",
            )
        )
Example #2
0
def test_softlock_instability(client: Client):
    def load_device():
        debuglink.load_device(
            client,
            mnemonic=MNEMONIC12,
            pin="1234",
            passphrase_protection=False,
            label="test",
        )

    # start from a clean slate:
    resp = client.debug.reseed(0)
    if isinstance(resp, messages.Failure) and not isinstance(
            client.transport, udp.UdpTransport):
        pytest.xfail("reseed only supported on emulator")
    device.wipe(client)
    entropy_after_wipe = misc.get_entropy(client, 16)

    # configure and wipe the device
    load_device()
    client.debug.reseed(0)
    device.wipe(client)
    assert misc.get_entropy(client, 16) == entropy_after_wipe

    load_device()
    # the device has PIN -> lock it
    client.call(messages.LockDevice())
    client.debug.reseed(0)
    # wipe_device should succeed with no need to unlock
    device.wipe(client)
    # the device is now trying to run the lockscreen, which attempts to unlock.
    # If the device actually called config.unlock(), it would use additional randomness.
    # That is undesirable. Assert that the returned entropy is still the same.
    assert misc.get_entropy(client, 16) == entropy_after_wipe
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:
        client.set_expected_responses(
            [messages.ButtonRequest]
            + [messages.EntropyRequest]
            + [messages.ButtonRequest] * 24
            + [messages.Success, messages.Features]
        )
        device.reset(client, False, 128, True, False, "label", "en-US")

    with pytest.raises(TrezorFailure):
        # This must fail, because device is already initialized
        # Using direct call because `device.reset` has its own check
        client.call(
            messages.ResetDevice(
                display_random=False,
                strength=128,
                passphrase_protection=True,
                pin_protection=False,
                label="label",
                language="en-US",
            )
        )
Example #4
0
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_bad_parameters(client: Client, field_name, field_value):
    msg = messages.RecoveryDevice(
        dry_run=True,
        word_count=12,
        enforce_wordlist=True,
        type=messages.RecoveryDeviceType.ScrambledWords,
    )
    setattr(msg, field_name, field_value)
    with pytest.raises(exceptions.TrezorFailure,
                       match="Forbidden field set in dry-run"):
        client.call(msg)
Example #6
0
def test_passphrase_always_on_device(client: Client):
    # Let's start the communication by calling Initialize.
    session_id = _init_session(client)

    # Force passphrase entry on Trezor.
    response = client.call(messages.ApplySettings(passphrase_always_on_device=True))
    assert isinstance(response, messages.Success)

    # Since we enabled the always_on_device setting, Trezor will send ButtonRequests and ask for it on the device.
    response = client.call_raw(XPUB_REQUEST)
    assert isinstance(response, messages.ButtonRequest)
    client.debug.input("")  # Input empty passphrase.
    response = client.call_raw(messages.ButtonAck())
    assert isinstance(response, messages.PublicKey)
    assert response.xpub == XPUB_PASSPHRASE_NONE

    # Passphrase will not be prompted. The session id stays the same and the passphrase is cached.
    _init_session(client, session_id=session_id)
    response = client.call_raw(XPUB_REQUEST)
    assert isinstance(response, messages.PublicKey)
    assert response.xpub == XPUB_PASSPHRASE_NONE

    # In case we want to add a new passphrase we need to send session_id = None.
    _init_session(client)
    response = client.call_raw(XPUB_REQUEST)
    assert isinstance(response, messages.ButtonRequest)
    client.debug.input("A")  # Input non-empty passphrase.
    response = client.call_raw(messages.ButtonAck())
    assert isinstance(response, messages.PublicKey)
    assert response.xpub == XPUB_PASSPHRASES["A"]
Example #7
0
def test_passphrase_on_device(client: Client):
    _init_session(client)

    # try to get xpub with passphrase on host:
    response = client.call_raw(XPUB_REQUEST)
    assert isinstance(response, messages.PassphraseRequest)
    # using `client.call` to auto-skip subsequent ButtonRequests for "show passphrase"
    response = client.call(messages.PassphraseAck(passphrase="A", on_device=False))

    assert isinstance(response, messages.PublicKey)
    assert response.xpub == XPUB_PASSPHRASES["A"]

    # try to get xpub again, passphrase should be cached
    response = client.call_raw(XPUB_REQUEST)
    assert isinstance(response, messages.PublicKey)
    assert response.xpub == XPUB_PASSPHRASES["A"]

    # make a new session
    _init_session(client)

    # try to get xpub with passphrase on device:
    response = client.call_raw(XPUB_REQUEST)
    assert isinstance(response, messages.PassphraseRequest)
    response = client.call_raw(messages.PassphraseAck(on_device=True))
    # no "show passphrase" here
    assert isinstance(response, messages.ButtonRequest)
    client.debug.input("A")
    response = client.call_raw(messages.ButtonAck())
    assert isinstance(response, messages.PublicKey)
    assert response.xpub == XPUB_PASSPHRASES["A"]

    # try to get xpub again, passphrase should be cached
    response = client.call_raw(XPUB_REQUEST)
    assert isinstance(response, messages.PublicKey)
    assert response.xpub == XPUB_PASSPHRASES["A"]
Example #8
0
def _init_session(client: Client, session_id=None, derive_cardano=False):
    """Call Initialize, check and return the session ID."""
    response = client.call(
        messages.Initialize(session_id=session_id, derive_cardano=derive_cardano)
    )
    assert isinstance(response, messages.Features)
    assert len(response.session_id) == 32
    return response.session_id
Example #9
0
def _get_xpub_cardano(client: Client, passphrase):
    msg = messages.CardanoGetPublicKey(
        address_n=parse_path("m/44h/1815h/0h/0/0"),
        derivation_type=messages.CardanoDerivationType.ICARUS,
    )
    response = client.call_raw(msg)
    if passphrase is not None:
        assert isinstance(response, messages.PassphraseRequest)
        response = client.call(messages.PassphraseAck(passphrase=passphrase))
    assert isinstance(response, messages.CardanoPublicKey)
    return response.xpub
Example #10
0
def _get_xpub(client: Client, passphrase=None):
    """Get XPUB and check that the appropriate passphrase flow has happened."""
    if passphrase is not None:
        expected_responses = [
            messages.PassphraseRequest,
            messages.ButtonRequest,
            messages.ButtonRequest,
            messages.PublicKey,
        ]
    else:
        expected_responses = [messages.PublicKey]

    with client:
        client.use_passphrase(passphrase or "")
        client.set_expected_responses(expected_responses)
        result = client.call(XPUB_REQUEST)
        return result.xpub
Example #11
0
def test_session_enable_passphrase(client: Client):
    # Let's start the communication by calling Initialize.
    session_id = _init_session(client)

    # Trezor will not prompt for passphrase because it is turned off.
    assert _get_xpub(client, passphrase=None) == XPUB_PASSPHRASE_NONE

    # Turn on passphrase.
    # Emit the call explicitly to avoid ClearSession done by the library function
    response = client.call(messages.ApplySettings(use_passphrase=True))
    assert isinstance(response, messages.Success)

    # The session id is unchanged, therefore we do not prompt for the passphrase.
    new_session_id = _init_session(client, session_id=session_id)
    assert session_id == new_session_id
    assert _get_xpub(client, passphrase=None) == XPUB_PASSPHRASE_NONE

    # We clear the session id now, so the passphrase should be asked.
    new_session_id = _init_session(client)
    assert session_id != new_session_id
    assert _get_xpub(client, passphrase="A") == XPUB_PASSPHRASES["A"]
Example #12
0
def test_ping(client: Client):
    ping = client.call(messages.Ping(message="ahoj!"))
    assert ping == messages.Success(message="ahoj!")
Example #13
0
def test_features(client: Client):
    f0 = client.features
    # client erases session_id from its features
    f0.session_id = client.session_id
    f1 = client.call(messages.Initialize(session_id=f0.session_id))
    assert f0 == f1
def test_sign_tx(client: Client):
    # NOTE: FAKE input tx

    commitment_data = b"\x0fwww.example.com" + (1).to_bytes(ROUND_ID_LEN, "big")

    with client:
        client.use_pin_sequence([PIN])
        btc.authorize_coinjoin(
            client,
            coordinator="www.example.com",
            max_rounds=2,
            max_coordinator_fee_rate=50_000_000,  # 0.5 %
            max_fee_per_kvbyte=3500,
            n=parse_path("m/84h/1h/0h"),
            coin_name="Testnet",
            script_type=messages.InputScriptType.SPENDWITNESS,
        )

    client.call(messages.LockDevice())

    with client:
        client.set_expected_responses(
            [messages.PreauthorizedRequest, messages.OwnershipProof]
        )
        btc.get_ownership_proof(
            client,
            "Testnet",
            parse_path("m/84h/1h/0h/1/0"),
            script_type=messages.InputScriptType.SPENDWITNESS,
            user_confirmation=True,
            commitment_data=commitment_data,
            preauthorized=True,
        )

    with client:
        client.set_expected_responses(
            [messages.PreauthorizedRequest, messages.OwnershipProof]
        )
        btc.get_ownership_proof(
            client,
            "Testnet",
            parse_path("m/84h/1h/0h/1/5"),
            script_type=messages.InputScriptType.SPENDWITNESS,
            user_confirmation=True,
            commitment_data=commitment_data,
            preauthorized=True,
        )

    inputs = [
        messages.TxInputType(
            # seed "alcohol woman abuse must during monitor noble actual mixed trade anger aisle"
            # 84'/1'/0'/0/0
            # tb1qnspxpr2xj9s2jt6qlhuvdnxw6q55jvygcf89r2
            amount=100_000,
            prev_hash=TXHASH_e5b7e2,
            prev_index=0,
            script_type=messages.InputScriptType.EXTERNAL,
            script_pubkey=bytes.fromhex("00149c02608d469160a92f40fdf8c6ccced029493088"),
            ownership_proof=bytearray.fromhex(
                "534c001901016b2055d8190244b2ed2d46513c40658a574d3bc2deb6969c0535bb818b44d2c40002483045022100a6c7d59b453efa7b4abc9bc724a94c5655ae986d5924dc29d28bcc2b859cbace022047d2bc4422a47f7b044bd6cdfbf63fe1a0ecbf11393f4c0bf8565f867a5ced16012103505f0d82bbdd251511591b34f36ad5eea37d3220c2b81a1189084431ddb3aa3d"
            ),
            commitment_data=commitment_data,
        ),
        messages.TxInputType(
            address_n=parse_path("m/84h/1h/0h/1/0"),
            amount=7_289_000,
            prev_hash=FAKE_TXHASH_f982c0,
            prev_index=1,
            script_type=messages.InputScriptType.SPENDWITNESS,
        ),
    ]
Example #15
0
def test_already_initialized(client: Client):
    with pytest.raises(RuntimeError):
        device.recover(client)

    with pytest.raises(exceptions.TrezorFailure, match="Already initialized"):
        client.call(messages.RecoveryDevice())