From 23ce8a46f3e122e9d297751dc24b63d3f7ad7d02 Mon Sep 17 00:00:00 2001 From: grdddj Date: Mon, 13 Sep 2021 18:02:46 +0200 Subject: [PATCH] feat(tests): PIN and protection_levels tests for TT --- tests/device_tests/test_pin.py | 111 ++++ tests/device_tests/test_protect_call.py | 67 --- tests/device_tests/test_protection_levels.py | 571 ++++++++++++------- tests/ui_tests/fixtures.json | 17 + 4 files changed, 482 insertions(+), 284 deletions(-) create mode 100644 tests/device_tests/test_pin.py delete mode 100644 tests/device_tests/test_protect_call.py diff --git a/tests/device_tests/test_pin.py b/tests/device_tests/test_pin.py new file mode 100644 index 000000000..064c9a903 --- /dev/null +++ b/tests/device_tests/test_pin.py @@ -0,0 +1,111 @@ +# This file is part of the Trezor project. +# +# Copyright (C) 2012-2019 SatoshiLabs and contributors +# +# This library is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# as published by the Free Software Foundation. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the License along with this library. +# If not, see . + +import time + +import pytest + +from trezorlib import messages +from trezorlib.exceptions import PinException + +from ..common import get_test_address + +PIN4 = "1234" +BAD_PIN = "5678" + +pytestmark = pytest.mark.setup_client(pin=PIN4) + + +@pytest.mark.setup_client(pin=None) +def test_no_protection(client): + with client: + client.set_expected_responses([messages.Address]) + get_test_address(client) + + +def test_correct_pin(client): + with client: + client.use_pin_sequence([PIN4]) + # Expected responses differ between T1 and TT + is_t1 = client.features.model == "1" + client.set_expected_responses( + [ + (is_t1, messages.PinMatrixRequest), + ( + not is_t1, + messages.ButtonRequest(code=messages.ButtonRequestType.PinEntry), + ), + messages.Address, + ] + ) + # client.set_expected_responses([messages.ButtonRequest, messages.Address]) + get_test_address(client) + + +@pytest.mark.skip_t2 +def test_incorrect_pin_t1(client): + with pytest.raises(PinException): + client.use_pin_sequence([BAD_PIN]) + get_test_address(client) + + +@pytest.mark.skip_t1 +def test_incorrect_pin_t2(client): + with client: + # After first incorrect attempt, TT will not raise an error, but instead ask for another attempt + client.use_pin_sequence([BAD_PIN, PIN4]) + client.set_expected_responses( + [ + messages.ButtonRequest(code=messages.ButtonRequestType.PinEntry), + messages.ButtonRequest(code=messages.ButtonRequestType.PinEntry), + messages.Address, + ] + ) + 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): + for attempt in range(3): + start = time.time() + with client, pytest.raises(PinException): + client.use_pin_sequence([BAD_PIN]) + get_test_address(client) + _check_backoff_time(attempt, start) + + +@pytest.mark.skip_t1 +def test_exponential_backoff_t2(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) diff --git a/tests/device_tests/test_protect_call.py b/tests/device_tests/test_protect_call.py deleted file mode 100644 index 43ba42326..000000000 --- a/tests/device_tests/test_protect_call.py +++ /dev/null @@ -1,67 +0,0 @@ -# This file is part of the Trezor project. -# -# Copyright (C) 2012-2019 SatoshiLabs and contributors -# -# This library is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License version 3 -# as published by the Free Software Foundation. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the License along with this library. -# If not, see . - -import time - -import pytest - -from trezorlib import btc, messages -from trezorlib.exceptions import PinException - -# FIXME TODO Add passphrase tests - - -@pytest.mark.skip_t2 -class TestProtectCall: - def _some_protected_call(self, client): - # This method perform any call which have protection in the device - res = btc.get_address(client, "Testnet", [0]) - assert res == "mndoQDWatQhfeQbprzZxD43mZ75Z94D6vz" - - def test_no_protection(self, client): - with client: - client.set_expected_responses([messages.Address]) - self._some_protected_call(client) - - @pytest.mark.setup_client(pin="1234") - def test_pin(self, client): - with client: - client.use_pin_sequence(["1234"]) - client.set_expected_responses([messages.PinMatrixRequest, messages.Address]) - self._some_protected_call(client) - - @pytest.mark.setup_client(pin="1234") - def test_incorrect_pin(self, client): - with pytest.raises(PinException): - client.use_pin_sequence(["5678"]) - self._some_protected_call(client) - - @pytest.mark.setup_client(pin="1234", passphrase=True) - def test_exponential_backoff_with_reboot(self, client): - def test_backoff(attempts, start): - if attempts <= 1: - expected = 0 - else: - expected = (2 ** (attempts - 1)) - 1 - got = round(time.time() - start, 2) - assert got >= expected - - for attempt in range(1, 4): - start = time.time() - with client, pytest.raises(PinException): - client.use_pin_sequence(["5678"]) - self._some_protected_call(client) - test_backoff(attempt, start) diff --git a/tests/device_tests/test_protection_levels.py b/tests/device_tests/test_protection_levels.py index 961ec28f8..ac2f3de5e 100644 --- a/tests/device_tests/test_protection_levels.py +++ b/tests/device_tests/test_protection_levels.py @@ -18,8 +18,9 @@ import pytest from trezorlib import btc, device, messages, misc from trezorlib.exceptions import TrezorFailure +from trezorlib.tools import parse_path -from ..common import MNEMONIC12 +from ..common import MNEMONIC12, get_test_address from ..tx_cache import TxCache from .signtx import request_finished, request_input, request_meta, request_output @@ -32,239 +33,375 @@ TXHASH_d5f65e = bytes.fromhex( PIN4 = "1234" +pytestmark = pytest.mark.setup_client(pin=PIN4, passphrase=True) + + +def _pin_request(client): + """Get appropriate PIN request for each model""" + if client.features.model == "1": + return messages.PinMatrixRequest + else: + return messages.ButtonRequest(code=B.PinEntry) + + +def _assert_protection(client, pin: bool = True, passphrase: bool = True) -> None: + """Make sure PIN and passphrase protection have expected values""" + with client: + client.use_pin_sequence([PIN4]) + client.ensure_unlocked() + assert client.features.pin_protection is pin + assert client.features.passphrase_protection is passphrase + client.clear_session() + + +def test_initialize(client): + _assert_protection(client) + with client: + client.set_expected_responses([messages.Features]) + client.init_device() + + +def test_apply_settings(client): + _assert_protection(client) + with client: + client.use_pin_sequence([PIN4]) + client.set_expected_responses( + [ + _pin_request(client), + messages.ButtonRequest, + messages.Success, + messages.Features, + ] + ) # TrezorClient reinitializes device + device.apply_settings(client, label="nazdar") + + @pytest.mark.skip_t2 -class TestProtectionLevels: - @pytest.mark.setup_client(pin=PIN4, passphrase=True) - def test_initialize(self, client): - with client: - client.set_expected_responses([messages.Features]) - client.init_device() - - @pytest.mark.setup_client(pin=PIN4, passphrase=True) - def test_apply_settings(self, client): - with client: - client.use_pin_sequence([PIN4]) - client.set_expected_responses( - [ - messages.PinMatrixRequest, - messages.ButtonRequest, - messages.Success, - messages.Features, - ] - ) # TrezorClient reinitializes device - device.apply_settings(client, label="nazdar") - - @pytest.mark.setup_client(pin=PIN4, passphrase=True) - def test_change_pin(self, client): - with client: - client.use_pin_sequence([PIN4, PIN4, PIN4]) - client.set_expected_responses( - [ - messages.ButtonRequest, - messages.PinMatrixRequest, - messages.PinMatrixRequest, - messages.PinMatrixRequest, - messages.Success, - messages.Features, - ] - ) - device.change_pin(client) - - def test_ping(self, client): - with client: - client.set_expected_responses([messages.ButtonRequest, messages.Success]) - client.ping("msg", True) - - @pytest.mark.setup_client(pin=PIN4, passphrase=True) - def test_get_entropy(self, client): - with client: - client.set_expected_responses([messages.ButtonRequest, messages.Entropy]) - misc.get_entropy(client, 10) - - @pytest.mark.setup_client(pin=PIN4, passphrase=True) - def test_get_public_key(self, client): - with client: - client.use_pin_sequence([PIN4]) - client.set_expected_responses( - [ - messages.PinMatrixRequest, - messages.PassphraseRequest, - messages.PublicKey, - ] - ) - btc.get_public_node(client, []) - - @pytest.mark.setup_client(pin=PIN4, passphrase=True) - def test_get_address(self, client): - with client: - client.use_pin_sequence([PIN4]) - client.set_expected_responses( - [ - messages.PinMatrixRequest, - messages.PassphraseRequest, - messages.Address, - ] - ) - btc.get_address(client, "Bitcoin", []) +def test_change_pin_t1(client): + _assert_protection(client) + with client: + client.use_pin_sequence([PIN4, PIN4, PIN4]) + client.set_expected_responses( + [ + messages.ButtonRequest, + _pin_request(client), + _pin_request(client), + _pin_request(client), + messages.Success, + messages.Features, + ] + ) + device.change_pin(client) + + +@pytest.mark.skip_t1 +def test_change_pin_t2(client): + _assert_protection(client) + with client: + client.use_pin_sequence([PIN4, PIN4, PIN4, PIN4]) + client.set_expected_responses( + [ + _pin_request(client), + messages.ButtonRequest, + _pin_request(client), + _pin_request(client), + _pin_request(client), + messages.ButtonRequest, + messages.Success, + messages.Features, + ] + ) + device.change_pin(client) - @pytest.mark.setup_client(pin=PIN4, passphrase=True) - def test_wipe_device(self, client): - with client: - client.set_expected_responses( - [messages.ButtonRequest, messages.Success, messages.Features] - ) - device.wipe(client) - - @pytest.mark.setup_client(uninitialized=True) - def test_reset_device(self, client): - with 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", - ) - ) - @pytest.mark.setup_client(uninitialized=True) - def test_recovery_device(self, client): - client.use_mnemonic(MNEMONIC12) - with client: - client.set_expected_responses( - [messages.ButtonRequest] - + [messages.WordRequest] * 24 - + [messages.Success, messages.Features] - ) +@pytest.mark.setup_client(pin=None, passphrase=False) +def test_ping(client): + _assert_protection(client, pin=False, passphrase=False) + with client: + client.set_expected_responses([messages.ButtonRequest, messages.Success]) + client.ping("msg", True) - 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.reset` has its own check - client.call( - messages.RecoveryDevice( - word_count=12, - passphrase_protection=False, - pin_protection=False, - label="label", - language="en-US", - ) - ) +@pytest.mark.skip_t2 +def test_get_entropy_t1(client): + _assert_protection(client) + with client: + client.set_expected_responses( + [ + messages.ButtonRequest(code=B.ProtectCall), + messages.Entropy, + ] + ) + misc.get_entropy(client, 10) + + +@pytest.mark.skip_t1 +def test_get_entropy_t2(client): + _assert_protection(client) + with client: + client.use_pin_sequence([PIN4]) + client.set_expected_responses( + [ + _pin_request(client), + messages.ButtonRequest(code=B.ProtectCall), + messages.Entropy, + ] + ) + misc.get_entropy(client, 10) + + +def test_get_public_key(client): + _assert_protection(client) + with client: + client.use_pin_sequence([PIN4]) + client.set_expected_responses( + [ + _pin_request(client), + messages.PassphraseRequest, + messages.PublicKey, + ] + ) + btc.get_public_node(client, []) + + +def test_get_address(client): + _assert_protection(client) + with client: + client.use_pin_sequence([PIN4]) + client.set_expected_responses( + [ + _pin_request(client), + messages.PassphraseRequest, + messages.Address, + ] + ) + get_test_address(client) - @pytest.mark.setup_client(pin=PIN4, passphrase=True) - def test_sign_message(self, client): - with client: - client.use_pin_sequence([PIN4]) - client.set_expected_responses( - [ - messages.ButtonRequest, - messages.PinMatrixRequest, - messages.PassphraseRequest, - messages.MessageSignature, - ] - ) - btc.sign_message(client, "Bitcoin", [], "testing message") - @pytest.mark.setup_client(pin=PIN4, passphrase=True) - def test_verify_message(self, client): - with client: - client.set_expected_responses( - [messages.ButtonRequest, messages.ButtonRequest, messages.Success] - ) - btc.verify_message( - client, - "Bitcoin", - "14LmW5k4ssUrtbAB4255zdqv3b4w1TuX9e", - bytes.fromhex( - "209e23edf0e4e47ff1dec27f32cd78c50e74ef018ee8a6adf35ae17c7a9b0dd96f48b493fd7dbab03efb6f439c6383c9523b3bbc5f1a7d158a6af90ab154e9be80" - ), - "This is an example of a signed message.", +def test_wipe_device(client): + _assert_protection(client) + with client: + client.set_expected_responses( + [messages.ButtonRequest, messages.Success, messages.Features] + ) + device.wipe(client) + + +@pytest.mark.setup_client(uninitialized=True) +@pytest.mark.skip_t2 +def test_reset_device(client): + assert client.features.pin_protection is False + assert client.features.passphrase_protection is False + with 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", ) + ) - @pytest.mark.setup_client(pin=PIN4, passphrase=True) - def test_signtx(self, client): - inp1 = messages.TxInputType( - address_n=[0], # 14LmW5k4ssUrtbAB4255zdqv3b4w1TuX9e - prev_hash=TXHASH_d5f65e, - prev_index=0, - amount=390000, + +@pytest.mark.setup_client(uninitialized=True) +@pytest.mark.skip_t2 +def test_recovery_device(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] ) - out1 = messages.TxOutputType( - address="1MJ2tj2ThBE62zXbBYA5ZaN3fdve5CPAz1", - amount=390000 - 10000, - script_type=messages.OutputScriptType.PAYTOADDRESS, + device.recover( + client, 12, False, False, "label", "en-US", client.mnemonic_callback ) - with client: - - client.use_pin_sequence([PIN4]) - client.set_expected_responses( - [ - messages.PinMatrixRequest, - messages.PassphraseRequest, - request_input(0), - request_output(0), - messages.ButtonRequest(code=B.ConfirmOutput), - messages.ButtonRequest(code=B.SignTx), - request_input(0), - request_meta(TXHASH_d5f65e), - request_input(0, TXHASH_d5f65e), - request_input(1, TXHASH_d5f65e), - request_output(0, TXHASH_d5f65e), - request_input(0), - request_output(0), - request_output(0), - request_finished(), - ] + 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", ) - btc.sign_tx(client, "Bitcoin", [inp1], [out1], prev_txes=TxCache("Bitcoin")) + ) + - # def test_firmware_erase(self): - # pass +@pytest.mark.skip_t2 +def test_sign_message_t1(client): + _assert_protection(client) + with client: + client.use_pin_sequence([PIN4]) + client.set_expected_responses( + [ + messages.ButtonRequest, + _pin_request(client), + messages.PassphraseRequest, + messages.MessageSignature, + ] + ) + btc.sign_message( + client, "Bitcoin", parse_path("44h/0h/0h/0/0"), "testing message" + ) - # def test_firmware_upload(self): - # pass - @pytest.mark.setup_client(pin=PIN4) - def test_unlocked(self, client): - assert client.features.unlocked is False +@pytest.mark.skip_t1 +def test_sign_message_t2(client): + _assert_protection(client) + with client: + client.use_pin_sequence([PIN4]) + client.set_expected_responses( + [ + _pin_request(client), + messages.PassphraseRequest, + messages.ButtonRequest, + messages.MessageSignature, + ] + ) + btc.sign_message( + client, "Bitcoin", parse_path("44h/0h/0h/0/0"), "testing message" + ) - with client: - client.use_pin_sequence([PIN4]) - client.set_expected_responses([messages.PinMatrixRequest, messages.Address]) - btc.get_address(client, "Testnet", [0]) - client.init_device() - assert client.features.unlocked is True - with client: - client.set_expected_responses([messages.Address]) - btc.get_address(client, "Testnet", [0]) - - @pytest.mark.setup_client(passphrase=True) - def test_passphrase_cached(self, client): - with client: - client.set_expected_responses( - [messages.PassphraseRequest, messages.Address] - ) - btc.get_address(client, "Testnet", [0]) +@pytest.mark.skip_t2 +def test_verify_message_t1(client): + _assert_protection(client) + with client: + client.set_expected_responses( + [ + messages.ButtonRequest, + messages.ButtonRequest, + messages.Success, + ] + ) + btc.verify_message( + client, + "Bitcoin", + "14LmW5k4ssUrtbAB4255zdqv3b4w1TuX9e", + bytes.fromhex( + "209e23edf0e4e47ff1dec27f32cd78c50e74ef018ee8a6adf35ae17c7a9b0dd96f48b493fd7dbab03efb6f439c6383c9523b3bbc5f1a7d158a6af90ab154e9be80" + ), + "This is an example of a signed message.", + ) + + +@pytest.mark.skip_t1 +def test_verify_message_t2(client): + _assert_protection(client) + with client: + client.use_pin_sequence([PIN4]) + client.set_expected_responses( + [ + _pin_request(client), + messages.ButtonRequest, + messages.ButtonRequest, + messages.Success, + ] + ) + btc.verify_message( + client, + "Bitcoin", + "14LmW5k4ssUrtbAB4255zdqv3b4w1TuX9e", + bytes.fromhex( + "209e23edf0e4e47ff1dec27f32cd78c50e74ef018ee8a6adf35ae17c7a9b0dd96f48b493fd7dbab03efb6f439c6383c9523b3bbc5f1a7d158a6af90ab154e9be80" + ), + "This is an example of a signed message.", + ) + + +def test_signtx(client): + # tx: d5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882 + # input 0: 0.0039 BTC + + inp1 = messages.TxInputType( + address_n=parse_path("44h/0h/0h/0/0"), + amount=390000, + prev_hash=TXHASH_d5f65e, + prev_index=0, + ) + + out1 = messages.TxOutputType( + address="1MJ2tj2ThBE62zXbBYA5ZaN3fdve5CPAz1", + amount=390000 - 10000, + script_type=messages.OutputScriptType.PAYTOADDRESS, + ) + + _assert_protection(client) + with client: + client.use_pin_sequence([PIN4]) + client.set_expected_responses( + [ + _pin_request(client), + messages.PassphraseRequest, + request_input(0), + request_output(0), + messages.ButtonRequest(code=B.ConfirmOutput), + messages.ButtonRequest(code=B.SignTx), + request_input(0), + request_meta(TXHASH_d5f65e), + request_input(0, TXHASH_d5f65e), + request_input(1, TXHASH_d5f65e), + request_output(0, TXHASH_d5f65e), + request_input(0), + request_output(0), + request_output(0), + request_finished(), + ] + ) + btc.sign_tx(client, "Bitcoin", [inp1], [out1], prev_txes=TxCache("Bitcoin")) + + +# def test_firmware_erase(): +# pass + +# def test_firmware_upload(): +# pass + + +@pytest.mark.setup_client(pin=PIN4, passphrase=False) +def test_unlocked(client): + assert client.features.unlocked is False + + _assert_protection(client, passphrase=False) + with client: + client.use_pin_sequence([PIN4]) + client.set_expected_responses([_pin_request(client), messages.Address]) + get_test_address(client) + + client.init_device() + assert client.features.unlocked is True + with client: + client.set_expected_responses([messages.Address]) + get_test_address(client) + + +@pytest.mark.setup_client(pin=None, passphrase=True) +def test_passphrase_cached(client): + _assert_protection(client, pin=False) + with client: + client.set_expected_responses([messages.PassphraseRequest, messages.Address]) + get_test_address(client) - with client: - client.set_expected_responses([messages.Address]) - btc.get_address(client, "Testnet", [0]) + with client: + client.set_expected_responses([messages.Address]) + get_test_address(client) diff --git a/tests/ui_tests/fixtures.json b/tests/ui_tests/fixtures.json index 4cbd88ab0..9b83e5583 100644 --- a/tests/ui_tests/fixtures.json +++ b/tests/ui_tests/fixtures.json @@ -708,6 +708,23 @@ "test_passphrase_slip39_advanced.py::test_256bit_passphrase": "0d854e06e58ec8e27f04d8604002f1dcb8fee790bdac07cbabb94e3ed357abe3", "test_passphrase_slip39_basic.py::test_2of5_passphrase": "54fe7196c39e3f70734be72fc45a121670e891852354057b4d8ab094ced4b493", "test_passphrase_slip39_basic.py::test_3of6_passphrase": "54fe7196c39e3f70734be72fc45a121670e891852354057b4d8ab094ced4b493", +"test_pin.py::test_correct_pin": "d81ff1a197803cfda9180cad05dad5c1912a064da957d036c1311fa3eeef4b70", +"test_pin.py::test_exponential_backoff_t2": "d4e7ed263d72c37c6f6d05c2e4aa4d8245267594511df704efe8a32f3c5c30ca", +"test_pin.py::test_incorrect_pin_t2": "3d549b537340e13140cd77f362f323ca116a79e58fb0784d69aadad7b5c13063", +"test_pin.py::test_no_protection": "c09de07fbbf1e047442180e2facb5482d06a1a428891b875b7dd93c9e4704ae1", +"test_protection_levels.py::test_apply_settings": "2451a804df4a867fef29adf4b71445352a3bface95a797f65bb87cf58c1ef34f", +"test_protection_levels.py::test_change_pin_t2": "d414bdebe6ea6b0f754aec1cdde61133b87fd27cf791ab1bdfdb61866a400d6c", +"test_protection_levels.py::test_get_address": "f0ac110de788b3112e04dc2ef131fca011a8dea1c309df37adeb23066729e273", +"test_protection_levels.py::test_get_entropy_t2": "539ac09590d3252f0ccb2eb836c703a16be5a219c8bd46add57be8319a336ae9", +"test_protection_levels.py::test_get_public_key": "f0ac110de788b3112e04dc2ef131fca011a8dea1c309df37adeb23066729e273", +"test_protection_levels.py::test_initialize": "59e518cba8589979f0af46e2acb211d37c96312f1d1a63a899d138ebb2f3ca29", +"test_protection_levels.py::test_passphrase_cached": "7fe34cc300a6f3547eaf72ab4339b758469f1e2722244d2a14d06e55ab1a3716", +"test_protection_levels.py::test_ping": "5551c263e8e09c8ae683f4dec3dd9d7ecc05ebbd3f2556604b27479c8f1fbc82", +"test_protection_levels.py::test_sign_message_t2": "bdcc7268caf112c3ba1708d9387fcca8384b330ba5a1e500df87e5fa3b0a4320", +"test_protection_levels.py::test_signtx": "f13f1687e062bd407dc781ae93ebb6619eee5ea3d37ee031ffdc98d5bd7aea33", +"test_protection_levels.py::test_unlocked": "f2be4c8c13c6a201770966438ffa9bcfe0eb031683920f93a55fa92921a28b51", +"test_protection_levels.py::test_verify_message_t2": "c258650c3697a46c61b67306b89cec1d67900095aeed05d4a691600a2cc12c2f", +"test_protection_levels.py::test_wipe_device": "a30d958dda50b06e8bfc1c861c0ff2c0eb4acd0656bdf1dcd6474660882e3cd5", "test_reset_backup.py::test_skip_backup_manual[BackupType.Bip39-backup_flow_bip39]": "cad035eb013b620849f10638ca8559f1734bcc9a2242873b64cf98267d037d14", "test_reset_backup.py::test_skip_backup_manual[BackupType.Slip39_Advanced-backup_flow_slip39-799d9907": "cfdd178988740145a245f90cd6c66e425779ddf239f77a48fa4c1eec24e1f407", "test_reset_backup.py::test_skip_backup_manual[BackupType.Slip39_Basic-backup_flow_slip39_basic]": "107b2d3f9d0ccc506752e261f35ecd8e67f04751644783dd353659440802f9a9",