diff --git a/common/protob/messages-management.proto b/common/protob/messages-management.proto index e3fd7bb67..cfe1c2cce 100644 --- a/common/protob/messages-management.proto +++ b/common/protob/messages-management.proto @@ -129,6 +129,7 @@ message Features { optional bool unit_btconly = 46; // unit/device is intended as bitcoin only optional uint32 homescreen_width = 47; // homescreen width in pixels optional uint32 homescreen_height = 48; // homescreen height in pixels + optional bool bootloader_locked = 49; // bootloader is locked } /** diff --git a/core/SConscript.bootloader_emu b/core/SConscript.bootloader_emu index 95139230f..710ab376f 100644 --- a/core/SConscript.bootloader_emu +++ b/core/SConscript.bootloader_emu @@ -131,6 +131,9 @@ SOURCE_TREZORHAL = [ ] if TREZOR_MODEL in ('R', ): + CPPDEFINES_MOD += [ + ('USE_OPTIGA', '1'), + ] SOURCE_TREZORHAL += [ 'embed/trezorhal/unix/secret.c', ] diff --git a/core/embed/bootloader/messages.c b/core/embed/bootloader/messages.c index c6860b58b..9226b1b80 100644 --- a/core/embed/bootloader/messages.c +++ b/core/embed/bootloader/messages.c @@ -44,6 +44,10 @@ #include "emulator.h" #endif +#if USE_OPTIGA +#include "secret.h" +#endif + #define MSG_HEADER1_LEN 9 #define MSG_HEADER2_LEN 1 @@ -311,6 +315,11 @@ static void send_msg_features(uint8_t iface_num, MSG_SEND_ASSIGN_VALUE(unit_color, unit_variant_get_color()); MSG_SEND_ASSIGN_VALUE(unit_btconly, unit_variant_get_btconly()); } + +#if USE_OPTIGA + MSG_SEND_ASSIGN_VALUE(bootloader_locked, + (secret_bootloader_locked() == sectrue)); +#endif MSG_SEND(Features); } diff --git a/core/embed/bootloader/protob/messages.pb.h b/core/embed/bootloader/protob/messages.pb.h index 9b85a9f31..563cebcaf 100644 --- a/core/embed/bootloader/protob/messages.pb.h +++ b/core/embed/bootloader/protob/messages.pb.h @@ -103,6 +103,8 @@ typedef struct _Features { uint32_t unit_color; bool has_unit_btconly; bool unit_btconly; + bool has_bootloader_locked; + bool bootloader_locked; } Features; typedef struct _FirmwareErase { @@ -154,7 +156,7 @@ extern "C" { /* Initializer values for message structs */ #define Initialize_init_default {0} #define GetFeatures_init_default {0} -#define Features_init_default {false, "", 0, 0, 0, false, 0, false, "", false, "", false, "", false, 0, false, {0, {0}}, false, 0, false, "", false, 0, false, 0, false, 0, false, "", false, "", false, 0, false, 0} +#define Features_init_default {false, "", 0, 0, 0, false, 0, false, "", false, "", false, "", false, 0, false, {0, {0}}, false, 0, false, "", false, 0, false, 0, false, 0, false, "", false, "", false, 0, false, 0, false, 0} #define Ping_init_default {false, ""} #define Success_init_default {false, ""} #define Failure_init_default {false, _FailureType_MIN, false, ""} @@ -166,7 +168,7 @@ extern "C" { #define UnlockBootloader_init_default {0} #define Initialize_init_zero {0} #define GetFeatures_init_zero {0} -#define Features_init_zero {false, "", 0, 0, 0, false, 0, false, "", false, "", false, "", false, 0, false, {0, {0}}, false, 0, false, "", false, 0, false, 0, false, 0, false, "", false, "", false, 0, false, 0} +#define Features_init_zero {false, "", 0, 0, 0, false, 0, false, "", false, "", false, "", false, 0, false, {0, {0}}, false, 0, false, "", false, 0, false, 0, false, 0, false, "", false, "", false, 0, false, 0, false, 0} #define Ping_init_zero {false, ""} #define Success_init_zero {false, ""} #define Failure_init_zero {false, _FailureType_MIN, false, ""} @@ -200,6 +202,7 @@ extern "C" { #define Features_internal_model_tag 44 #define Features_unit_color_tag 45 #define Features_unit_btconly_tag 46 +#define Features_bootloader_locked_tag 49 #define FirmwareErase_length_tag 1 #define FirmwareRequest_offset_tag 1 #define FirmwareRequest_length_tag 2 @@ -238,7 +241,8 @@ X(a, STATIC, OPTIONAL, UINT32, fw_patch, 24) \ X(a, STATIC, OPTIONAL, STRING, fw_vendor, 25) \ X(a, STATIC, OPTIONAL, STRING, internal_model, 44) \ X(a, STATIC, OPTIONAL, UINT32, unit_color, 45) \ -X(a, STATIC, OPTIONAL, BOOL, unit_btconly, 46) +X(a, STATIC, OPTIONAL, BOOL, unit_btconly, 46) \ +X(a, STATIC, OPTIONAL, BOOL, bootloader_locked, 49) #define Features_CALLBACK NULL #define Features_DEFAULT NULL @@ -322,7 +326,7 @@ extern const pb_msgdesc_t UnlockBootloader_msg; #define ButtonAck_size 0 #define ButtonRequest_size 2 #define Failure_size 260 -#define Features_size 487 +#define Features_size 490 #define FirmwareErase_size 6 #define FirmwareRequest_size 12 #define GetFeatures_size 0 diff --git a/core/embed/bootloader/protob/messages.proto b/core/embed/bootloader/protob/messages.proto index 4149460d6..bfe08db48 100644 --- a/core/embed/bootloader/protob/messages.proto +++ b/core/embed/bootloader/protob/messages.proto @@ -61,6 +61,7 @@ message Features { optional string internal_model = 44; // internal model name optional uint32 unit_color = 45; // color of the unit/device optional bool unit_btconly = 46; // unit/device is intended as bitcoin only + optional bool bootloader_locked = 49; // bootloader is locked } /** diff --git a/core/embed/extmod/modtrezorutils/modtrezorutils.c b/core/embed/extmod/modtrezorutils/modtrezorutils.c index 95a0b0067..1bfa738b4 100644 --- a/core/embed/extmod/modtrezorutils/modtrezorutils.c +++ b/core/embed/extmod/modtrezorutils/modtrezorutils.c @@ -43,6 +43,10 @@ #include "image.h" #endif +#if USE_OPTIGA && !defined(TREZOR_EMULATOR) +#include "secret.h" +#endif + static void ui_progress(mp_obj_t ui_wait_callback, uint32_t current, uint32_t total) { if (mp_obj_is_callable(ui_wait_callback)) { @@ -254,6 +258,26 @@ STATIC mp_obj_t mod_trezorutils_reboot_to_bootloader() { STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorutils_reboot_to_bootloader_obj, mod_trezorutils_reboot_to_bootloader); +/// def bootloader_locked() -> bool | None: +/// """ +/// Returns True/False if the the bootloader is locked/unlocked and None if +/// the feature is not supported. +/// """ +STATIC mp_obj_t mod_trezorutils_bootloader_locked() { +#if USE_OPTIGA +#ifdef TREZOR_EMULATOR + return mp_const_true; +#else + return (secret_bootloader_locked() == sectrue) ? mp_const_true + : mp_const_false; +#endif +#else + return mp_const_none; +#endif +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorutils_bootloader_locked_obj, + mod_trezorutils_bootloader_locked); + STATIC mp_obj_str_t mod_trezorutils_revision_obj = { {&mp_type_bytes}, 0, sizeof(SCM_REVISION) - 1, (const byte *)SCM_REVISION}; @@ -280,6 +304,8 @@ STATIC const mp_rom_map_elem_t mp_module_trezorutils_globals_table[] = { MP_ROM_PTR(&mod_trezorutils_firmware_vendor_obj)}, {MP_ROM_QSTR(MP_QSTR_reboot_to_bootloader), MP_ROM_PTR(&mod_trezorutils_reboot_to_bootloader_obj)}, + {MP_ROM_QSTR(MP_QSTR_bootloader_locked), + MP_ROM_PTR(&mod_trezorutils_bootloader_locked_obj)}, {MP_ROM_QSTR(MP_QSTR_unit_color), MP_ROM_PTR(&mod_trezorutils_unit_color_obj)}, {MP_ROM_QSTR(MP_QSTR_unit_btconly), diff --git a/core/embed/trezorhal/secret.h b/core/embed/trezorhal/secret.h index a5a8be8ef..336680147 100644 --- a/core/embed/trezorhal/secret.h +++ b/core/embed/trezorhal/secret.h @@ -7,6 +7,8 @@ #define SECRET_OPTIGA_KEY_OFFSET 16 #define SECRET_OPTIGA_KEY_LEN 32 +secbool secret_bootloader_locked(void); + void secret_write(uint8_t* data, uint32_t offset, uint32_t len); secbool secret_read(uint8_t* data, uint32_t offset, uint32_t len); diff --git a/core/embed/trezorhal/stm32f4/secret.c b/core/embed/trezorhal/stm32f4/secret.c index d8d032dec..2232f889a 100644 --- a/core/embed/trezorhal/stm32f4/secret.c +++ b/core/embed/trezorhal/stm32f4/secret.c @@ -4,13 +4,28 @@ #include "flash.h" #include "model.h" +static secbool bootloader_locked_set = secfalse; +static secbool bootloader_locked = secfalse; + static secbool verify_header(void) { uint8_t header[SECRET_HEADER_LEN] = {0}; memcpy(header, flash_area_get_address(&SECRET_AREA, 0, SECRET_HEADER_LEN), SECRET_HEADER_LEN); - return memcmp(header, SECRET_HEADER_MAGIC, 4) == 0 ? sectrue : secfalse; + bootloader_locked = + memcmp(header, SECRET_HEADER_MAGIC, 4) == 0 ? sectrue : secfalse; + bootloader_locked_set = sectrue; + return bootloader_locked; +} + +secbool secret_bootloader_locked(void) { + if (bootloader_locked_set != sectrue) { + // Set bootloader_locked. + verify_header(); + } + + return bootloader_locked; } void secret_write_header(void) { diff --git a/core/mocks/generated/trezorutils.pyi b/core/mocks/generated/trezorutils.pyi index a2685f073..9fa4ab13b 100644 --- a/core/mocks/generated/trezorutils.pyi +++ b/core/mocks/generated/trezorutils.pyi @@ -79,6 +79,14 @@ def reboot_to_bootloader() -> None: """ Reboots to bootloader. """ + + +# extmod/modtrezorutils/modtrezorutils.c +def bootloader_locked() -> bool | None: + """ + Returns True/False if the the bootloader is locked/unlocked and None if + the feature is not supported. + """ SCM_REVISION: bytes VERSION_MAJOR: int VERSION_MINOR: int diff --git a/core/src/apps/base.py b/core/src/apps/base.py index eee7f0ead..32d3b917f 100644 --- a/core/src/apps/base.py +++ b/core/src/apps/base.py @@ -66,6 +66,7 @@ def get_features() -> Features: homescreen_height=HEIGHT, unit_color=utils.unit_color(), unit_btconly=utils.unit_btconly(), + bootloader_locked=utils.bootloader_locked(), ) if utils.INTERNAL_MODEL in ("T1B1", "T2B1"): diff --git a/core/src/apps/management/authenticate_device.py b/core/src/apps/management/authenticate_device.py index b98428dfd..32bac8ec7 100644 --- a/core/src/apps/management/authenticate_device.py +++ b/core/src/apps/management/authenticate_device.py @@ -11,10 +11,13 @@ async def authenticate_device(msg: AuthenticateDevice) -> AuthenticityProof: from trezor.crypto.hashlib import sha256 from trezor.messages import AuthenticityProof from trezor.ui.layouts import confirm_action - from trezor.utils import BufferReader + from trezor.utils import BufferReader, bootloader_locked from apps.common.writers import write_compact_size + if not bootloader_locked(): + raise wire.ProcessError("Cannot authenticate since bootloader is unlocked.") + await confirm_action( "authenticate_device", "Authenticate device", diff --git a/core/src/trezor/messages.py b/core/src/trezor/messages.py index c0c9ee854..efe61ba80 100644 --- a/core/src/trezor/messages.py +++ b/core/src/trezor/messages.py @@ -2128,6 +2128,7 @@ if TYPE_CHECKING: unit_btconly: "bool | None" homescreen_width: "int | None" homescreen_height: "int | None" + bootloader_locked: "bool | None" def __init__( self, @@ -2177,6 +2178,7 @@ if TYPE_CHECKING: unit_btconly: "bool | None" = None, homescreen_width: "int | None" = None, homescreen_height: "int | None" = None, + bootloader_locked: "bool | None" = None, ) -> None: pass diff --git a/core/src/trezor/utils.py b/core/src/trezor/utils.py index 8b31a8da0..63ca9e6db 100644 --- a/core/src/trezor/utils.py +++ b/core/src/trezor/utils.py @@ -12,6 +12,7 @@ from trezorutils import ( # noqa: F401 VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, + bootloader_locked, consteq, firmware_hash, firmware_vendor, diff --git a/python/src/trezorlib/messages.py b/python/src/trezorlib/messages.py index 9e7bdcbd7..adabfc2be 100644 --- a/python/src/trezorlib/messages.py +++ b/python/src/trezorlib/messages.py @@ -3190,6 +3190,7 @@ class Features(protobuf.MessageType): 46: protobuf.Field("unit_btconly", "bool", repeated=False, required=False, default=None), 47: protobuf.Field("homescreen_width", "uint32", repeated=False, required=False, default=None), 48: protobuf.Field("homescreen_height", "uint32", repeated=False, required=False, default=None), + 49: protobuf.Field("bootloader_locked", "bool", repeated=False, required=False, default=None), } def __init__( @@ -3241,6 +3242,7 @@ class Features(protobuf.MessageType): unit_btconly: Optional["bool"] = None, homescreen_width: Optional["int"] = None, homescreen_height: Optional["int"] = None, + bootloader_locked: Optional["bool"] = None, ) -> None: self.capabilities: Sequence["Capability"] = capabilities if capabilities is not None else [] self.major_version = major_version @@ -3288,6 +3290,7 @@ class Features(protobuf.MessageType): self.unit_btconly = unit_btconly self.homescreen_width = homescreen_width self.homescreen_height = homescreen_height + self.bootloader_locked = bootloader_locked class LockDevice(protobuf.MessageType):