feat(core): add experimental_features setting

The setting is off by default. When it is enabled protobuf messages
marked UNSTABLE are rejected after decoding.
pull/1280/head
Martin Milata 4 years ago committed by Tomas Susanka
parent 0376670404
commit 830592f2d9

@ -95,6 +95,7 @@ def get_features() -> Features:
f.safety_checks = safety_checks.read_setting()
f.auto_lock_delay_ms = storage.device.get_autolock_delay_ms()
f.display_rotation = storage.device.get_rotation()
f.experimental_features = storage.device.get_experimental_features()
return f
@ -264,4 +265,6 @@ def boot() -> None:
wire.register(MessageType.DoPreauthorized, handle_DoPreauthorized)
wire.register(MessageType.CancelAuthorization, handle_CancelAuthorization)
wire.experimental_enabled = storage.device.get_experimental_features()
workflow.idle_timer.set(storage.device.get_autolock_delay_ms(), lock_device)

@ -25,6 +25,8 @@ class Homescreen(HomescreenBase):
ui.header_warning("NEEDS BACKUP!")
elif storage.device.is_initialized() and not config.has_pin():
ui.header_warning("PIN NOT SET!")
elif storage.device.get_experimental_features():
ui.header_warning("EXPERIMENTAL MODE!")
else:
ui.display.bar(0, 0, ui.WIDTH, ui.HEIGHT, ui.BG)

@ -45,6 +45,7 @@ async def apply_settings(ctx: wire.Context, msg: ApplySettings):
and msg.display_rotation is None
and msg.auto_lock_delay_ms is None
and msg.safety_checks is None
and msg.experimental_features is None
):
raise wire.ProcessError("No setting provided")
@ -91,6 +92,11 @@ async def apply_settings(ctx: wire.Context, msg: ApplySettings):
storage.device.set_rotation(msg.display_rotation)
ui.display.orientation(storage.device.get_rotation())
if msg.experimental_features is not None:
await require_confirm_experimental_features(ctx, msg.experimental_features)
storage.device.set_experimental_features(msg.experimental_features)
wire.experimental_enabled = msg.experimental_features
return Success(message="Settings applied")
@ -182,3 +188,12 @@ async def require_confirm_safety_checks(ctx, level: EnumTypeSafetyCheckLevel) ->
await require_confirm(ctx, text, ButtonRequestType.ProtectCall)
else:
raise ValueError # enum value out of range
async def require_confirm_experimental_features(ctx, enable: bool) -> None:
if enable:
text = Text("Experimental mode", ui.ICON_CONFIG)
text.normal("Enable experimental", "features?")
text.br_half()
text.bold("Only for development", "and beta testing!")
await require_confirm(ctx, text, ButtonRequestType.ProtectCall)

@ -142,6 +142,7 @@ class UnicodeType:
class MessageType:
WIRE_TYPE = 2
UNSTABLE = False
# Type id for the wire codec.
# Technically, not every protobuf message has this.
@ -198,7 +199,10 @@ if False:
def load_message(
reader: Reader, msg_type: Type[LoadedMessageType], field_cache: FieldCache = None
reader: Reader,
msg_type: Type[LoadedMessageType],
field_cache: FieldCache = None,
experimental_enabled: bool = True,
) -> LoadedMessageType:
if field_cache is None:
field_cache = {}
@ -207,6 +211,9 @@ def load_message(
fields = msg_type.get_fields()
field_cache[msg_type] = fields
if msg_type.UNSTABLE and not experimental_enabled:
raise ValueError # experimental messages not enabled
# we need to avoid calling __init__, which enforces required arguments
msg = object.__new__(msg_type) # type: LoadedMessageType
# pre-seed the object with defaults
@ -263,7 +270,9 @@ def load_message(
reader.readinto(fvalue)
fvalue = bytes(fvalue).decode()
elif issubclass(ftype, MessageType):
fvalue = load_message(LimitedReader(reader, ivalue), ftype, field_cache)
fvalue = load_message(
LimitedReader(reader, ivalue), ftype, field_cache, experimental_enabled
)
else:
raise TypeError # field type is unknown

@ -36,6 +36,7 @@ _SLIP39_ITERATION_EXPONENT = const(0x11) # int
_SD_SALT_AUTH_KEY = const(0x12) # bytes
INITIALIZED = const(0x13) # bool (0x01 or empty)
_SAFETY_CHECK_LEVEL = const(0x14) # int
_EXPERIMENTAL_FEATURES = const(0x15) # bool (0x01 or empty)
_DEFAULT_BACKUP_TYPE = BackupType.Bip39
@ -304,3 +305,11 @@ def set_safety_check_level(level: StorageSafetyCheckLevel) -> None:
if level not in (SAFETY_CHECK_LEVEL_STRICT, SAFETY_CHECK_LEVEL_PROMPT):
raise ValueError
common.set_uint8(_NAMESPACE, _SAFETY_CHECK_LEVEL, level)
def get_experimental_features() -> bool:
return common.get_bool(_NAMESPACE, _EXPERIMENTAL_FEATURES)
def set_experimental_features(enabled: bool) -> None:
common.set_true_or_delete(_NAMESPACE, _EXPERIMENTAL_FEATURES, enabled)

@ -73,6 +73,9 @@ workflow_handlers = {} # type: Dict[int, Handler]
# to be dynamically imported when such message arrives.
workflow_packages = {} # type: Dict[int, Tuple[str, str]]
# If set to False protobuf messages marked with "unstable" option are rejected.
experimental_enabled = False # type: bool
def add(wire_type: int, pkgname: str, modname: str) -> None:
"""Shortcut for registering a dynamically-imported Protobuf workflow."""
@ -123,7 +126,9 @@ def _wrap_protobuf_load(
field_cache: protobuf.FieldCache = None,
) -> protobuf.LoadedMessageType:
try:
return protobuf.load_message(reader, expected_type, field_cache)
return protobuf.load_message(
reader, expected_type, field_cache, experimental_enabled
)
except Exception as e:
if e.args:
raise DataError("Failed to decode message: {}".format(e.args[0]))

@ -20,7 +20,7 @@ import pytest
from trezorlib import debuglink, log
from trezorlib.debuglink import TrezorClientDebugLink
from trezorlib.device import wipe as wipe_device
from trezorlib.device import apply_settings, wipe as wipe_device
from trezorlib.transport import enumerate_devices, get_transport
from . import ui_tests
@ -132,6 +132,9 @@ def client(request):
no_backup=setup_params["no_backup"],
)
if client.features.model == "T":
apply_settings(client, experimental_features=True)
if use_passphrase and isinstance(setup_params["passphrase"], str):
client.use_passphrase(setup_params["passphrase"])

@ -219,3 +219,41 @@ class TestMsgApplysettings:
with client:
client.set_expected_responses([messages.ButtonRequest, messages.Address])
get_bad_address()
@pytest.mark.skip_t1
def test_experimental_features(self, client):
def experimental_call():
btc.authorize_coinjoin(
client,
coordinator="www.example.com",
max_total_fee=10010,
fee_per_anonymity=5000000, # 0.005 %
n=parse_path("m/84'/1'/0'"),
coin_name="Testnet",
script_type=messages.InputScriptType.SPENDWITNESS,
)
assert client.features.experimental_features is None
# unlock
with client:
_set_expected_responses(client)
device.apply_settings(client, label="new label")
assert client.features.experimental_features
with client:
client.set_expected_responses(
[messages.ButtonRequest, messages.ButtonRequest, messages.Success]
)
experimental_call()
with client:
client.set_expected_responses([messages.Success, messages.Features])
device.apply_settings(client, experimental_features=False)
assert not client.features.experimental_features
with pytest.raises(exceptions.TrezorFailure, match="DataError"), client:
client.set_expected_responses([messages.Failure])
experimental_call()

@ -38,33 +38,34 @@
"cardano-test_sign_tx.py::test_cardano_sign_tx_failed[parameters7-result7]": "612dad8ab8762162a186ec9279d7de0bdfc589c52b4e4f4eba0545a00f21c3f0",
"cardano-test_sign_tx.py::test_cardano_sign_tx_failed[parameters8-result8]": "612dad8ab8762162a186ec9279d7de0bdfc589c52b4e4f4eba0545a00f21c3f0",
"cardano-test_sign_tx.py::test_cardano_sign_tx_failed[parameters9-result9]": "612dad8ab8762162a186ec9279d7de0bdfc589c52b4e4f4eba0545a00f21c3f0",
"test_autolock.py::test_apply_auto_lock_delay": "1997527e85989f3ca5719f93cd76bcfb8f125fb96ef3025073b13fd4de7a5fa2",
"test_autolock.py::test_apply_auto_lock_delay_valid[10]": "d03aca645ebd8a44c8a1bb24a275e31441245c3211f7c82365b2f432370b05bc",
"test_autolock.py::test_apply_auto_lock_delay_valid[123]": "7588a745923a732cd8715ab76f95b7abb9c2caf043075ec1a73e31b2453743a1",
"test_autolock.py::test_apply_auto_lock_delay_valid[3601]": "904323c7f7a27393c4a192680340d571a2a4899f0300da4d0e439f55f32768e8",
"test_autolock.py::test_apply_auto_lock_delay_valid[536870]": "ff8b1d62a60d581504779cb7bb3dcf7882e960914bac4981d52fa714880c3d1e",
"test_autolock.py::test_apply_auto_lock_delay_valid[60]": "02f813f809bec7b303998fe288f02bfa4cd1a30990c0dc071ad51ff86f2739e6",
"test_autolock.py::test_apply_auto_lock_delay_valid[7227]": "41bca947f7834baa9968cbb164b70005aee4dbf23586187ab3bef336ff42a422",
"test_autolock.py::test_autolock_cancels_ui": "eedc6196565bf6d53bc9c3f8984acc2bb91d2d71e57f3a28f8afbe35b02fb4dc",
"test_autolock.py::test_autolock_default_value": "4e564ee7f060684b4a0e8c7439fc867dc221f59c35b68f84d7a641d9e466d3e6",
"test_autolock.py::test_apply_auto_lock_delay": "38c720e0d29b7487060f2a0f8d256a5e5b4f735511e049c43f6ea62e560603ae",
"test_autolock.py::test_apply_auto_lock_delay_valid[10]": "a751228f82166c107a8e8872919e2b010ef3079763adc473066e7a3ada36f864",
"test_autolock.py::test_apply_auto_lock_delay_valid[123]": "caf130bf5fa66fa5ac17432689046c1b6cd8b6a495bac3abef3c414d89b81e3f",
"test_autolock.py::test_apply_auto_lock_delay_valid[3601]": "b2a9a7f3e50afb04fb174627a07b939284aa0acc8b3b53af56f75a35ff1b32c9",
"test_autolock.py::test_apply_auto_lock_delay_valid[536870]": "ca2b4707227cc15089f4d6ba710706d2d5c270f19a4668c09f04f175143b202e",
"test_autolock.py::test_apply_auto_lock_delay_valid[60]": "af8d06c92fc5f9aad5685bf56e59b26ec44418a6174ff73db69803f23785802a",
"test_autolock.py::test_apply_auto_lock_delay_valid[7227]": "437cc6b0780d482a835c23f0935683c40177ae4b0ff31da4fc99eba603545ffe",
"test_autolock.py::test_autolock_cancels_ui": "bb4776bfea145528544554b11bdf13ae99f63a371e8eb0885b0a9bd5b608e027",
"test_autolock.py::test_autolock_default_value": "b9f4cd94638f5f8f4c02026b0ccaee89b42406ab63ce7fcef5c9164467de939b",
"test_basic.py-test_device_id_different": "bc6acd0386b9d009e6550519917d6e08632b3badde0b0cf04c95abe5f773038a",
"test_basic.py-test_device_id_same": "5a80508a71a9ef64f94762b07636f90e464832f0f4a3102af8fa1a8c69e94586",
"test_basic.py-test_features": "5a80508a71a9ef64f94762b07636f90e464832f0f4a3102af8fa1a8c69e94586",
"test_basic.py-test_ping": "5a80508a71a9ef64f94762b07636f90e464832f0f4a3102af8fa1a8c69e94586",
"test_msg_applysettings.py-test_apply_homescreen_toif": "d129829fe8c6c30125151b134826b6a11eb96cca2158c1cad951290ac314a5cc",
"test_msg_applysettings.py-test_apply_settings": "2cc8bf660f3be815d19a4bf1265936162a58386fbe632ca4be01541245b79134",
"test_msg_applysettings.py-test_apply_settings_passphrase": "5c1ed9a0be3d14475102d447da0b5d51bbb6dfaaeceff5ea9179064609db7870",
"test_msg_applysettings.py-test_apply_homescreen_toif": "408bdb69368ebdf1d299c6d43c1571f86cb1a0f1f606c5badd2f05ce7731f121",
"test_msg_applysettings.py-test_apply_settings": "8f9f6013bb8a44fda279e9c7d091328fd7ccb39222a02bee701918528355083a",
"test_msg_applysettings.py-test_apply_settings_passphrase": "40de0143b32b5d06ece43d47be27bb91499f0c2417754ddb8e9e03ff41a7f6d4",
"test_msg_applysettings.py-test_apply_settings_passphrase_on_device": "3e6527e227bdde54f51bc9c417b176d0d87fdb6c40c4761368f50eb201b4beed",
"test_msg_applysettings.py-test_apply_settings_rotation": "e4a544dfc55550e340a3879d23ffab7b8ccc6362060ec941c3a7d545675d3cbd",
"test_msg_applysettings.py-test_apply_settings_rotation": "6f0fa323dd2c82d01994273c023d3ed5e43d43c9c562664a10266f4a7f7ba4cc",
"test_msg_applysettings.py-test_experimental_features": "3127d41bd8615097295b917110ece9dd364986809288c7f958ff71d52106e346",
"test_msg_applysettings.py-test_safety_checks": "4eb00e8d3bce08e800f3524f9a03960865c9725a08df0ebe57853602cd84b6a5",
"test_msg_authorize_coinjoin.py::test_cancel_authorization": "d8a608beb6165f5667cc44dcff6bdc17ebb4638ddd3bd09e7f0e1e75d1e21135",
"test_msg_authorize_coinjoin.py::test_no_anonymity": "fd09da284b650e893990b95047b63a35b6b695fc5301d595f17a6d2cf9d90bcb",
"test_msg_authorize_coinjoin.py::test_sign_tx": "2838d4062333c241b6bbef7e680ec8a5764fe7bcaa41419e4141e146d3586a5d",
"test_msg_authorize_coinjoin.py::test_sign_tx": "5d53448397ff5cf4f951e2ac7f37c34e2ca9be99a3d3d2d31499397e10e4b157",
"test_msg_authorize_coinjoin.py::test_unfair_fee": "62314e936de46a6caaf02c8eb20f6f471be6e79ca0c5450cad6f67f9cb823f2b",
"test_msg_authorize_coinjoin.py::test_wrong_coordinator": "d8a608beb6165f5667cc44dcff6bdc17ebb4638ddd3bd09e7f0e1e75d1e21135",
"test_msg_backup_device.py::test_backup_bip39": "42325cccfc0bd54db180a01a076437ec981022338307339bb5e0f6463d842e23",
"test_msg_backup_device.py::test_backup_slip39_advanced": "9a01aa5ecdafa52571ed2149575f86ffea6c2984a2541ea8e5f9a7c37cf4c9fe",
"test_msg_backup_device.py::test_backup_slip39_basic": "57d841257b10c4f67cf76487c8f0bc95947a93a8e0b8c03d7a11894da3c233da",
"test_msg_backup_device.py::test_backup_bip39": "e9398e4a6ac419c06345e27654463a4576b5d3e468afd043e8e265a5c5367185",
"test_msg_backup_device.py::test_backup_slip39_advanced": "c5d95cd68074b8f46a797269c37e9cb8f7d389de50d4c36021bdc6d0a0e950ff",
"test_msg_backup_device.py::test_backup_slip39_basic": "6ff28aa2bdc45643c7205da2e0828a850053a5e59bdd322c6abdd809a5a4711c",
"test_msg_backup_device.py::test_interrupt_backup_fails": "8dc5c385fec6dd871a141e2efd83f767a5f3da85b2857c8ac27e054f9fa4b384",
"test_msg_backup_device.py::test_no_backup_fails": "93039a9472cfc9058563bd56e4a3dbe2e41af64744a61f6ee3255a04bd3a9366",
"test_msg_backup_device.py::test_no_backup_show_entropy_fails": "14fcdd2ded299ca099a35966cc9f21204b31de8d6bab9ec91cb64537bd70440c",
@ -74,15 +75,15 @@
"test_msg_binance_sign_tx.py::test_binance_sign_message[message0-expected_response0]": "d41ee5e01a50f0f96fd7881db1750fab31cfe62c25b4eabbc092cc3daa039c7f",
"test_msg_binance_sign_tx.py::test_binance_sign_message[message1-expected_response1]": "7b8bbe5ba7d7b07c95065608fb1cf9aeafcb3f9671835a6e5e5a6997ff4ff99b",
"test_msg_binance_sign_tx.py::test_binance_sign_message[message2-expected_response2]": "813ad1b802dee1ace4dfa378edd840dbcea57c1a1b8eed67134def024c40a6e9",
"test_msg_change_wipe_code_t2.py::test_set_pin_to_wipe_code": "b9c44290bfef91baa76a43fcf72e41092c716c79e5c21ccc368a817e8e52739f",
"test_msg_change_wipe_code_t2.py::test_set_remove_wipe_code": "d280ed129a2ea4781af9e35542aa31ecf63da75fc6812ed3bd05107809f836a4",
"test_msg_change_wipe_code_t2.py::test_set_wipe_code_mismatch": "c128b6317db1219a88cea645aabead10eae7ba478f344af97b0b09b5cd4b1404",
"test_msg_change_wipe_code_t2.py::test_set_wipe_code_to_pin": "3c4072fa7e3def16258b2ed9c4fadf74a634b691889d78bbb771df616ea547af",
"test_msg_changepin_t2.py::test_change_failed": "6f759f27c33ba5bb43731f7532e3ec5b21a5c06c5b8a64e4d1d69e760b0003f0",
"test_msg_changepin_t2.py::test_change_pin": "5e3251031e1b221b34ad47c643c6e15af3480c22a0e44bfd89df08643267c76f",
"test_msg_changepin_t2.py::test_remove_pin": "b0eb22819b27451cf5807fd31894fc4674b9b5eb6f177eebcd3d0c33701efc4e",
"test_msg_changepin_t2.py::test_set_failed": "8c0844d3ec6e9d22a8e85e87c491e360291c26dc6ac0b6643e6292c3f6cb03a3",
"test_msg_changepin_t2.py::test_set_pin": "7957e51d5fd6ac93d4b508bcb7b170304c6548ab859cc445bb076074fd0a62d1",
"test_msg_change_wipe_code_t2.py::test_set_pin_to_wipe_code": "ff7af3e6c6e280d380200c2d919c5a474f1e875af9a5440cb8511e20b911c4ac",
"test_msg_change_wipe_code_t2.py::test_set_remove_wipe_code": "9cb01ba3996dfcddfadc9e7e187455ed12ce51049ea1e0754cb7ce68bc03d410",
"test_msg_change_wipe_code_t2.py::test_set_wipe_code_mismatch": "3f29784c14941de5fe0595780e09bd68830e3d95c981cc99e4ebd1418f875748",
"test_msg_change_wipe_code_t2.py::test_set_wipe_code_to_pin": "65d64d32e4fcc694e95e675bc2b4fbbf2d967cfc8f8fc852aeadbf58acefacf9",
"test_msg_changepin_t2.py::test_change_failed": "63c1d90ce9bd9d7fdd627ce8b70b60518b373e235dcdb6713a1b5bc020e4466d",
"test_msg_changepin_t2.py::test_change_pin": "4cdff56add70b77cd901654fcdef6098cd38214567060c870865ee697efa6df5",
"test_msg_changepin_t2.py::test_remove_pin": "64701aa15082e4d3f9639799c9d12c129dd60d1aed5f7203bfac2fd3665d1d19",
"test_msg_changepin_t2.py::test_set_failed": "6ad935b038f00177fea7e7221204ca13b188ff2eb2e699b367592d1567c5bcd6",
"test_msg_changepin_t2.py::test_set_pin": "d7a7eeff208c7080d3606a2f66170de841e429e856fff4c0e3dafa449eedd86f",
"test_msg_cipherkeyvalue.py-test_decrypt": "166d85b1bf11aeaeb5b93ef5d047b6f8910c28b8fce1d853e6912d89d7bfca2f",
"test_msg_cipherkeyvalue.py-test_decrypt_badlen": "5a80508a71a9ef64f94762b07636f90e464832f0f4a3102af8fa1a8c69e94586",
"test_msg_cipherkeyvalue.py-test_encrypt": "3a37e4004c87bc6df6a8fa7c93b6fe3e3524986914709fda2f9c99ba0ff69775",
@ -413,6 +414,6 @@
"test_reset_backup.py::test_skip_backup_msg[2-backup_flow_slip39_advanced]": "cd6c1248d9ee4d6416c57026a96190a84ac8608af04fd42c9c8c6b7275226aba",
"test_sdcard.py::test_sd_format": "6bb7486932a5d38cdbb9b1368ee92aca3fad384115c744feadfade80c1605dd8",
"test_sdcard.py::test_sd_no_format": "f47e897caee95cf98c1b4506732825f853c4b8afcdc2713e38e3b4055973c9ac",
"test_sdcard.py::test_sd_protect_unlock": "9b98ad83499e38acaa9d73b0ef3261abde6e3b4b46194c32f323f28a79705077",
"test_sdcard.py::test_sd_protect_unlock": "52a0a4b847ceab2ef5bc9b22898e14df4e4b703227f4eda9807947702da28af8",
"test_u2f_counter.py::test_u2f_counter": "7d96a4d262b9d8a2c1158ac1e5f0f7b2c3ed5f2ba9d6235a014320313f9488fe"
}

Loading…
Cancel
Save