diff --git a/common/protob/messages-management.proto b/common/protob/messages-management.proto index 0caa983201..4656ebd5d0 100644 --- a/common/protob/messages-management.proto +++ b/common/protob/messages-management.proto @@ -516,3 +516,12 @@ message UnlockedPathRequest { */ message ShowDeviceTutorial { } + +/** + * Request: Unlocks bootloader, !irreversible! + * @start + * @next Success + * @next Failure + */ +message UnlockBootloader { +} diff --git a/common/protob/messages.proto b/common/protob/messages.proto index dd8119141a..632c733c0c 100644 --- a/common/protob/messages.proto +++ b/common/protob/messages.proto @@ -121,6 +121,7 @@ enum MessageType { MessageType_UnlockPath = 93 [(bitcoin_only) = true, (wire_in) = true]; MessageType_UnlockedPathRequest = 94 [(bitcoin_only) = true, (wire_out) = true]; MessageType_ShowDeviceTutorial = 95 [(bitcoin_only) = true, (wire_in) = true]; + MessageType_UnlockBootloader = 96 [(bitcoin_only) = true, (wire_in) = true]; MessageType_SetU2FCounter = 63 [(wire_in) = true]; MessageType_GetNextU2FCounter = 80 [(wire_in) = true]; diff --git a/core/SConscript.bootloader b/core/SConscript.bootloader index 0eaf184e6d..bb6f685a4c 100644 --- a/core/SConscript.bootloader +++ b/core/SConscript.bootloader @@ -20,7 +20,7 @@ if TREZOR_MODEL in ('1', ): ) Return() -FEATURES_WANTED = ["input", "rgb_led", "consumption_mask", "usb"] +FEATURES_WANTED = ["input", "rgb_led", "consumption_mask", "usb", "optiga"] CCFLAGS_MOD = '' CPPPATH_MOD = [] diff --git a/core/SConscript.bootloader_emu b/core/SConscript.bootloader_emu index 0f8ea3eb85..025ea3f5a8 100644 --- a/core/SConscript.bootloader_emu +++ b/core/SConscript.bootloader_emu @@ -92,6 +92,20 @@ SOURCE_MOD += [ 'vendor/trezor-storage/flash_common.c', ] +if TREZOR_MODEL in ('1', ): + SOURCE_MOD += [ + 'embed/models/model_T1B1_layout.c', + ] +elif TREZOR_MODEL in ('T', ): + SOURCE_MOD += [ + 'embed/models/model_T2T1_layout.c', + ] +elif TREZOR_MODEL in ('R', ): + SOURCE_MOD += [ + 'embed/models/model_T2B1_layout.c', + ] + + SOURCE_NANOPB = [ 'vendor/nanopb/pb_common.c', 'vendor/nanopb/pb_decode.c', @@ -114,6 +128,7 @@ SOURCE_TREZORHAL = [ 'embed/trezorhal/unix/rng.c', 'embed/trezorhal/unix/usb.c', 'embed/trezorhal/unix/random_delays.c', + 'embed/trezorhal/unix/secret.c', ] SOURCE_UNIX = [ diff --git a/core/SConscript.prodtest b/core/SConscript.prodtest index a1be842ae3..e679bca129 100644 --- a/core/SConscript.prodtest +++ b/core/SConscript.prodtest @@ -17,7 +17,7 @@ if TREZOR_MODEL in ('DISC1', ): action=build_prodtest) Return() -FEATURES_WANTED = ["input", "sbu", "sd_card", "rdb_led", "usb"] +FEATURES_WANTED = ["input", "sbu", "sd_card", "rdb_led", "usb", "consumption_mask", "optiga"] CCFLAGS_MOD = '' CPPPATH_MOD = [] diff --git a/core/embed/bootloader/.changelog.d/+14e1ae91.added b/core/embed/bootloader/.changelog.d/+14e1ae91.added new file mode 100644 index 0000000000..d08b843e74 --- /dev/null +++ b/core/embed/bootloader/.changelog.d/+14e1ae91.added @@ -0,0 +1 @@ +Locked bootloader support: bootloader will disallow installation of unofficial firmware unless the Optiga pairing secret is erased. diff --git a/core/embed/bootloader/.changelog.d/+95c27be5.added b/core/embed/bootloader/.changelog.d/+95c27be5.added new file mode 100644 index 0000000000..8344dde438 --- /dev/null +++ b/core/embed/bootloader/.changelog.d/+95c27be5.added @@ -0,0 +1 @@ +Support unlocking the bootloader via `UnlockBootloader` message. diff --git a/core/embed/bootloader/bootui.c b/core/embed/bootloader/bootui.c index dea589ad4d..a2f13beeec 100644 --- a/core/embed/bootloader/bootui.c +++ b/core/embed/bootloader/bootui.c @@ -19,6 +19,8 @@ #include +#include TREZOR_BOARD + #include "bootui.h" #include "display.h" #ifdef TREZOR_EMULATOR @@ -233,6 +235,12 @@ void ui_screen_boot_empty(bool fading) { screen_boot_empty(fading); } // error UI void ui_screen_fail(void) { screen_install_fail(); } +#ifdef USE_OPTIGA +uint32_t ui_screen_unlock_bootloader_confirm(void) { + return screen_unlock_bootloader_confirm(); +} +#endif + // general functions void ui_fadein(void) { display_fade(0, BACKLIGHT_NORMAL, 1000); } diff --git a/core/embed/bootloader/bootui.h b/core/embed/bootloader/bootui.h index 053bf12ac9..ba318adcd9 100644 --- a/core/embed/bootloader/bootui.h +++ b/core/embed/bootloader/bootui.h @@ -23,6 +23,7 @@ #include "image.h" #include "secbool.h" #include "stdbool.h" +#include TREZOR_BOARD typedef enum { SCREEN_INTRO = 0, @@ -68,6 +69,10 @@ void ui_set_initial_setup(bool initial); void ui_screen_boot_empty(bool fading); +#ifdef USE_OPTIGA +uint32_t ui_screen_unlock_bootloader_confirm(void); +#endif + // clang-format off #define INPUT_CANCEL 0x01 // Cancel button #define INPUT_CONFIRM 0x02 // Confirm button diff --git a/core/embed/bootloader/main.c b/core/embed/bootloader/main.c index a33c67b785..b22b753610 100644 --- a/core/embed/bootloader/main.c +++ b/core/embed/bootloader/main.c @@ -24,8 +24,10 @@ #include "display.h" #include "flash.h" #include "image.h" +#include "messages.pb.h" #include "random_delays.h" #include "secbool.h" +#include "secret.h" #ifdef USE_DMA2D #include "dma2d.h" @@ -149,13 +151,13 @@ static usb_result_t bootloader_usb_loop(const vendor_header *const vhdr, continue; } switch (msg_id) { - case 0: // Initialize + case MessageType_MessageType_Initialize: process_msg_Initialize(USB_IFACE_NUM, msg_size, buf, vhdr, hdr); break; - case 1: // Ping + case MessageType_MessageType_Ping: process_msg_Ping(USB_IFACE_NUM, msg_size, buf); break; - case 5: // WipeDevice + case MessageType_MessageType_WipeDevice: response = ui_screen_wipe_confirm(); if (INPUT_CANCEL == response) { send_user_abort(USB_IFACE_NUM, "Wipe cancelled"); @@ -180,10 +182,10 @@ static usb_result_t bootloader_usb_loop(const vendor_header *const vhdr, return SHUTDOWN; } break; - case 6: // FirmwareErase + case MessageType_MessageType_FirmwareErase: process_msg_FirmwareErase(USB_IFACE_NUM, msg_size, buf); break; - case 7: // FirmwareUpload + case MessageType_MessageType_FirmwareUpload: r = process_msg_FirmwareUpload(USB_IFACE_NUM, msg_size, buf); if (r < 0 && r != UPLOAD_ERR_USER_ABORT) { // error, but not user abort ui_screen_fail(); @@ -210,9 +212,27 @@ static usb_result_t bootloader_usb_loop(const vendor_header *const vhdr, return CONTINUE; } break; - case 55: // GetFeatures + case MessageType_MessageType_GetFeatures: process_msg_GetFeatures(USB_IFACE_NUM, msg_size, buf, vhdr, hdr); break; +#ifdef USE_OPTIGA + case MessageType_MessageType_UnlockBootloader: + response = ui_screen_unlock_bootloader_confirm(); + if (INPUT_CANCEL == response) { + send_user_abort(USB_IFACE_NUM, "Bootloader unlock cancelled"); + hal_delay(100); + usb_stop(); + usb_deinit(); + return RETURN; + } + process_msg_AttestationDelete(USB_IFACE_NUM, msg_size, buf); + screen_unlock_bootloader_success(); + hal_delay(100); + usb_stop(); + usb_deinit(); + return SHUTDOWN; + break; +#endif default: process_msg_unknown(USB_IFACE_NUM, msg_size, buf); break; @@ -522,8 +542,20 @@ int bootloader_main(void) { &FIRMWARE_AREA), "Firmware is corrupted"); - // if all VTRUST flags are unset = ultimate trust => skip the procedure +#ifdef USE_OPTIGA + if (((vhdr.vtrust & VTRUST_SECRET) != 0) && (sectrue != secret_wiped())) { + display_clear(); + screen_fatal_error_rust( + "INSTALL RESTRICTED", + "Installation of custom firmware is currently restricted.", + "Please visit\ntrezor.io/bootloader"); + display_refresh(); + return 1; + } +#endif + + // if all VTRUST flags are unset = ultimate trust => skip the procedure if ((vhdr.vtrust & VTRUST_ALL) != VTRUST_ALL) { ui_fadeout(); ui_screen_boot(&vhdr, hdr); diff --git a/core/embed/bootloader/messages.c b/core/embed/bootloader/messages.c index 94d229bcc4..33c4a6a790 100644 --- a/core/embed/bootloader/messages.c +++ b/core/embed/bootloader/messages.c @@ -28,6 +28,7 @@ #include "flash.h" #include "image.h" #include "secbool.h" +#include "secret.h" #include "unit_variant.h" #include "usb.h" #include "version.h" @@ -572,6 +573,16 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size, &should_keep_seed, &is_newvendor); } +#ifdef USE_OPTIGA + if (sectrue != secret_wiped() && ((vhdr.vtrust & VTRUST_SECRET) != 0)) { + MSG_SEND_INIT(Failure); + MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); + MSG_SEND_ASSIGN_STRING(message, "Attestation present"); + MSG_SEND(Failure); + return UPLOAD_ERR_ATTESTATION_PRESENT; + } +#endif + uint32_t response = INPUT_CANCEL; if (sectrue == is_new) { // new installation - auto confirm @@ -722,3 +733,12 @@ void process_msg_unknown(uint8_t iface_num, uint32_t msg_size, uint8_t *buf) { MSG_SEND_ASSIGN_STRING(message, "Unexpected message"); MSG_SEND(Failure); } + +#ifdef USE_OPTIGA +void process_msg_AttestationDelete(uint8_t iface_num, uint32_t msg_size, + uint8_t *buf) { + secret_erase(); + MSG_SEND_INIT(Success); + MSG_SEND(Success); +} +#endif diff --git a/core/embed/bootloader/messages.h b/core/embed/bootloader/messages.h index dd4f3df5be..be8952941d 100644 --- a/core/embed/bootloader/messages.h +++ b/core/embed/bootloader/messages.h @@ -23,6 +23,7 @@ #include #include "image.h" #include "secbool.h" +#include TREZOR_BOARD #define USB_TIMEOUT 500 #define USB_PACKET_SIZE 64 @@ -40,6 +41,7 @@ enum { UPLOAD_ERR_USER_ABORT = -7, UPLOAD_ERR_FIRMWARE_TOO_BIG = -8, UPLOAD_ERR_INVALID_CHUNK_HASH = -9, + UPLOAD_ERR_ATTESTATION_PRESENT = -10, }; enum { @@ -66,6 +68,11 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size, int process_msg_WipeDevice(uint8_t iface_num, uint32_t msg_size, uint8_t *buf); void process_msg_unknown(uint8_t iface_num, uint32_t msg_size, uint8_t *buf); +#ifdef USE_OPTIGA +void process_msg_AttestationDelete(uint8_t iface_num, uint32_t msg_size, + uint8_t *buf); +#endif + secbool bootloader_WipeDevice(void); #endif diff --git a/core/embed/bootloader/protob/messages.pb.c b/core/embed/bootloader/protob/messages.pb.c index 762e72e1bd..087828cf70 100644 --- a/core/embed/bootloader/protob/messages.pb.c +++ b/core/embed/bootloader/protob/messages.pb.c @@ -39,6 +39,9 @@ PB_BIND(FirmwareRequest, FirmwareRequest, AUTO) PB_BIND(FirmwareUpload, FirmwareUpload, AUTO) +PB_BIND(UnlockBootloader, UnlockBootloader, AUTO) + + diff --git a/core/embed/bootloader/protob/messages.pb.h b/core/embed/bootloader/protob/messages.pb.h index 777f5e22e2..9b85a9f31c 100644 --- a/core/embed/bootloader/protob/messages.pb.h +++ b/core/embed/bootloader/protob/messages.pb.h @@ -15,13 +15,15 @@ typedef enum _MessageType { MessageType_MessageType_Ping = 1, MessageType_MessageType_Success = 2, MessageType_MessageType_Failure = 3, + MessageType_MessageType_WipeDevice = 5, MessageType_MessageType_FirmwareErase = 6, MessageType_MessageType_FirmwareUpload = 7, MessageType_MessageType_FirmwareRequest = 8, MessageType_MessageType_Features = 17, MessageType_MessageType_ButtonRequest = 26, MessageType_MessageType_ButtonAck = 27, - MessageType_MessageType_GetFeatures = 55 + MessageType_MessageType_GetFeatures = 55, + MessageType_MessageType_UnlockBootloader = 96 } MessageType; typedef enum _FailureType { @@ -48,6 +50,10 @@ typedef struct _Initialize { char dummy_field; } Initialize; +typedef struct _UnlockBootloader { + char dummy_field; +} UnlockBootloader; + typedef struct _ButtonRequest { bool has_code; ButtonRequestType code; @@ -129,8 +135,8 @@ typedef struct _Success { /* Helper constants for enums */ #define _MessageType_MIN MessageType_MessageType_Initialize -#define _MessageType_MAX MessageType_MessageType_GetFeatures -#define _MessageType_ARRAYSIZE ((MessageType)(MessageType_MessageType_GetFeatures+1)) +#define _MessageType_MAX MessageType_MessageType_UnlockBootloader +#define _MessageType_ARRAYSIZE ((MessageType)(MessageType_MessageType_UnlockBootloader+1)) #define _FailureType_MIN FailureType_Failure_UnexpectedMessage #define _FailureType_MAX FailureType_Failure_ProcessError @@ -157,6 +163,7 @@ extern "C" { #define FirmwareErase_init_default {false, 0} #define FirmwareRequest_init_default {0, 0} #define FirmwareUpload_init_default {{{NULL}, NULL}, false, {0, {0}}} +#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} @@ -168,6 +175,7 @@ extern "C" { #define FirmwareErase_init_zero {false, 0} #define FirmwareRequest_init_zero {0, 0} #define FirmwareUpload_init_zero {{{NULL}, NULL}, false, {0, {0}}} +#define UnlockBootloader_init_zero {0} /* Field tags (for use in manual encoding/decoding) */ #define ButtonRequest_code_tag 1 @@ -277,6 +285,11 @@ X(a, STATIC, OPTIONAL, BYTES, hash, 2) #define FirmwareUpload_CALLBACK pb_default_field_callback #define FirmwareUpload_DEFAULT NULL +#define UnlockBootloader_FIELDLIST(X, a) \ + +#define UnlockBootloader_CALLBACK NULL +#define UnlockBootloader_DEFAULT NULL + extern const pb_msgdesc_t Initialize_msg; extern const pb_msgdesc_t GetFeatures_msg; extern const pb_msgdesc_t Features_msg; @@ -288,6 +301,7 @@ extern const pb_msgdesc_t ButtonAck_msg; extern const pb_msgdesc_t FirmwareErase_msg; extern const pb_msgdesc_t FirmwareRequest_msg; extern const pb_msgdesc_t FirmwareUpload_msg; +extern const pb_msgdesc_t UnlockBootloader_msg; /* Defines for backwards compatibility with code written before nanopb-0.4.0 */ #define Initialize_fields &Initialize_msg @@ -301,6 +315,7 @@ extern const pb_msgdesc_t FirmwareUpload_msg; #define FirmwareErase_fields &FirmwareErase_msg #define FirmwareRequest_fields &FirmwareRequest_msg #define FirmwareUpload_fields &FirmwareUpload_msg +#define UnlockBootloader_fields &UnlockBootloader_msg /* Maximum encoded size of messages (where known) */ /* FirmwareUpload_size depends on runtime parameters */ @@ -314,6 +329,7 @@ extern const pb_msgdesc_t FirmwareUpload_msg; #define Initialize_size 0 #define Ping_size 258 #define Success_size 258 +#define UnlockBootloader_size 0 #ifdef __cplusplus } /* extern "C" */ diff --git a/core/embed/bootloader/protob/messages.proto b/core/embed/bootloader/protob/messages.proto index 23b22d6e4d..4149460d60 100644 --- a/core/embed/bootloader/protob/messages.proto +++ b/core/embed/bootloader/protob/messages.proto @@ -9,6 +9,7 @@ enum MessageType { MessageType_Ping = 1; MessageType_Success = 2; MessageType_Failure = 3; + MessageType_WipeDevice = 5; MessageType_FirmwareErase = 6; MessageType_FirmwareUpload = 7; MessageType_FirmwareRequest = 8; @@ -16,6 +17,7 @@ enum MessageType { MessageType_ButtonRequest = 26; MessageType_ButtonAck = 27; MessageType_GetFeatures = 55; + MessageType_UnlockBootloader = 96; } /** @@ -143,3 +145,12 @@ message FirmwareUpload { required bytes payload = 1; // firmware to be loaded into device optional bytes hash = 2; // hash of the payload } + +/** + * Request: Unlock bootloader, !irreversible! + * @start + * @next Success + * @next Failure + */ +message UnlockBootloader { +} diff --git a/core/embed/bootloader/version.h b/core/embed/bootloader/version.h index 79d0537d9b..4a109a2cab 100644 --- a/core/embed/bootloader/version.h +++ b/core/embed/bootloader/version.h @@ -11,4 +11,8 @@ #define FIX_VERSION_PATCH 0 #define FIX_VERSION_BUILD 0 +#ifdef TREZOR_MODEL_R +#define VERSION_MONOTONIC 2 +#else #define VERSION_MONOTONIC 1 +#endif diff --git a/core/embed/firmware/main.c b/core/embed/firmware/main.c index dca0931a19..7674d0fffb 100644 --- a/core/embed/firmware/main.c +++ b/core/embed/firmware/main.c @@ -165,10 +165,7 @@ int main(void) { #endif #if !defined TREZOR_MODEL_1 - // jump to unprivileged mode - // http://infocenter.arm.com/help/topic/com.arm.doc.dui0552a/CHDBIBGJ.html - __asm__ volatile("msr control, %0" ::"r"(0x1)); - __asm__ volatile("isb"); + drop_privileges(); #endif #ifdef USE_SECP256K1_ZKP diff --git a/core/embed/lib/image.h b/core/embed/lib/image.h index ce31fc87e5..b26d00c63f 100644 --- a/core/embed/lib/image.h +++ b/core/embed/lib/image.h @@ -56,6 +56,8 @@ typedef struct { #define VTRUST_RED 0x0010 #define VTRUST_CLICK 0x0020 #define VTRUST_STRING 0x0040 +#define VTRUST_SECRET \ + 0x0080 // inverse logic, if set, don't allow to run with secret present #define VTRUST_ALL (VTRUST_WAIT | VTRUST_RED | VTRUST_CLICK | VTRUST_STRING) typedef struct { diff --git a/core/embed/models/layout_common.h b/core/embed/models/layout_common.h index a7d4644650..57f12ff345 100644 --- a/core/embed/models/layout_common.h +++ b/core/embed/models/layout_common.h @@ -14,6 +14,7 @@ extern const flash_area_t STORAGE_AREAS[STORAGE_AREAS_COUNT]; extern const flash_area_t BOARDLOADER_AREA; +extern const flash_area_t SECRET_AREA; extern const flash_area_t BOOTLOADER_AREA; extern const flash_area_t FIRMWARE_AREA; extern const flash_area_t WIPE_AREA; diff --git a/core/embed/models/model_T2B1_layout.c b/core/embed/models/model_T2B1_layout.c deleted file mode 120000 index e68955d8d8..0000000000 --- a/core/embed/models/model_T2B1_layout.c +++ /dev/null @@ -1 +0,0 @@ -model_T2T1_layout.c \ No newline at end of file diff --git a/core/embed/models/model_T2B1_layout.c b/core/embed/models/model_T2B1_layout.c new file mode 100644 index 0000000000..478a89aa9c --- /dev/null +++ b/core/embed/models/model_T2B1_layout.c @@ -0,0 +1,87 @@ +#include "flash.h" +#include "model.h" + +const flash_area_t STORAGE_AREAS[STORAGE_AREAS_COUNT] = { + { + .num_subareas = 1, + .subarea[0] = + { + .first_sector = 4, + .num_sectors = 1, + }, + }, + { + .num_subareas = 1, + .subarea[0] = + { + .first_sector = 16, + .num_sectors = 1, + }, + }, +}; + +const flash_area_t BOARDLOADER_AREA = { + .num_subareas = 1, + .subarea[0] = + { + .first_sector = 0, + .num_sectors = 3, + }, +}; + +const flash_area_t SECRET_AREA = { + .num_subareas = 1, + .subarea[0] = + { + .first_sector = 12, + .num_sectors = 1, + }, +}; + +const flash_area_t BOOTLOADER_AREA = { + .num_subareas = 1, + .subarea[0] = + { + .first_sector = 5, + .num_sectors = 1, + }, +}; + +const flash_area_t FIRMWARE_AREA = { + .num_subareas = 2, + .subarea[0] = + { + .first_sector = 6, + .num_sectors = 6, + }, + .subarea[1] = + { + .first_sector = 17, + .num_sectors = 7, + }, +}; + +const flash_area_t WIPE_AREA = { + .num_subareas = 4, + .subarea[0] = + { + .first_sector = 4, + .num_sectors = 1, + }, + .subarea[1] = + { + .first_sector = 6, + .num_sectors = 6, + }, + .subarea[2] = + { + .first_sector = 13, + .num_sectors = 2, // sector 15 skipped due to bootloader MPU + // settings, sector 12 is secret + }, + .subarea[3] = + { + .first_sector = 16, + .num_sectors = 8, + }, +}; diff --git a/core/embed/rust/rust_ui.h b/core/embed/rust/rust_ui.h index 27bd69ad50..da3c566c18 100644 --- a/core/embed/rust/rust_ui.h +++ b/core/embed/rust/rust_ui.h @@ -25,4 +25,6 @@ uint32_t screen_install_fail(void); void screen_welcome_model(void); void screen_welcome(void); void screen_boot_empty(bool fading); +uint32_t screen_unlock_bootloader_confirm(void); +void screen_unlock_bootloader_success(void); void display_image(int16_t x, int16_t y, const uint8_t* data, uint32_t datalen); diff --git a/core/embed/rust/src/ui/display/mod.rs b/core/embed/rust/src/ui/display/mod.rs index a2e218f8a6..898245c481 100644 --- a/core/embed/rust/src/ui/display/mod.rs +++ b/core/embed/rust/src/ui/display/mod.rs @@ -858,9 +858,8 @@ pub fn marquee(area: Rect, text: &str, offset: i16, font: Font, fg: Color, bg: C pixeldata_dirty(); } -// Used on T1 only. -pub fn dotted_line(start: Point, width: i16, color: Color) { - for x in (start.x..width).step_by(2) { +pub fn dotted_line(start: Point, width: i16, color: Color, step: i16) { + for x in (start.x..width).step_by(step as usize) { display::bar(x, start.y, 1, 1, color.into()); } } diff --git a/core/embed/rust/src/ui/model_tr/bootloader/confirm.rs b/core/embed/rust/src/ui/model_tr/bootloader/confirm.rs index 36508cc51c..6b8aa10cdd 100644 --- a/core/embed/rust/src/ui/model_tr/bootloader/confirm.rs +++ b/core/embed/rust/src/ui/model_tr/bootloader/confirm.rs @@ -39,6 +39,7 @@ pub struct Confirm<'a> { buttons: ButtonController<&'static str>, /// Whether we are on the info screen (optional extra screen) showing_info_screen: bool, + two_btn_confirm: bool, } impl<'a> Confirm<'a> { @@ -48,8 +49,10 @@ impl<'a> Confirm<'a> { message: Label<&'a str>, alert: Option>, button_text: &'static str, + two_btn_confirm: bool, ) -> Self { - let btn_layout = Self::get_button_layout_general(false, button_text, false); + let btn_layout = + Self::get_button_layout_general(false, button_text, false, two_btn_confirm); Self { bg: Pad::with_background(bg_color).with_clear(), bg_color, @@ -61,6 +64,7 @@ impl<'a> Confirm<'a> { button_text, buttons: ButtonController::new(btn_layout), showing_info_screen: false, + two_btn_confirm, } } @@ -81,6 +85,7 @@ impl<'a> Confirm<'a> { self.showing_info_screen, self.button_text, self.has_info_screen(), + self.two_btn_confirm, ) } @@ -89,11 +94,14 @@ impl<'a> Confirm<'a> { showing_info_screen: bool, button_text: &'static str, has_info_screen: bool, + two_btn_confirm: bool, ) -> ButtonLayout<&'static str> { if showing_info_screen { ButtonLayout::arrow_none_none() } else if has_info_screen { ButtonLayout::cancel_armed_info(button_text) + } else if two_btn_confirm { + ButtonLayout::cancel_armed_none(button_text) } else { ButtonLayout::cancel_none_text(button_text) } @@ -166,6 +174,14 @@ impl<'a> Component for Confirm<'a> { } _ => None, } + } else if self.two_btn_confirm { + match msg { + Some(ButtonControllerMsg::Triggered(ButtonPos::Left)) => Some(ConfirmMsg::Cancel), + Some(ButtonControllerMsg::Triggered(ButtonPos::Middle)) => { + Some(ConfirmMsg::Confirm) + } + _ => None, + } } else { // There is just one main screen without info screen match msg { diff --git a/core/embed/rust/src/ui/model_tr/bootloader/mod.rs b/core/embed/rust/src/ui/model_tr/bootloader/mod.rs index 0e405926e8..3214001cf6 100644 --- a/core/embed/rust/src/ui/model_tr/bootloader/mod.rs +++ b/core/embed/rust/src/ui/model_tr/bootloader/mod.rs @@ -150,7 +150,7 @@ extern "C" fn screen_install_confirm( theme::TEXT_NORMAL, )); - let mut frame = Confirm::new(BLD_BG, title_str, message, alert, "INSTALL") + let mut frame = Confirm::new(BLD_BG, title_str, message, alert, "INSTALL", false) .with_info_screen("FW FINGERPRINT", fingerprint); run(&mut frame) } @@ -160,11 +160,32 @@ extern "C" fn screen_wipe_confirm() -> u32 { let message = Label::left_aligned("Seed and firmware will be erased!", theme::TEXT_NORMAL) .vertically_centered(); - let mut frame = Confirm::new(BLD_BG, "FACTORY RESET", message, None, "RESET"); + let mut frame = Confirm::new(BLD_BG, "FACTORY RESET", message, None, "RESET", false); run(&mut frame) } +#[no_mangle] +extern "C" fn screen_unlock_bootloader_confirm() -> u32 { + let message = Label::left_aligned("This action cannot be undone!", theme::TEXT_NORMAL) + .vertically_centered(); + + let mut frame = Confirm::new(BLD_BG, "UNLOCK BOOTLOADER?", message, None, "UNLOCK", true); + + run(&mut frame) +} + +#[no_mangle] +extern "C" fn screen_unlock_bootloader_success() { + let title = Label::centered("Bootloader unlocked", theme::TEXT_BOLD).vertically_centered(); + + let content = + Label::centered("Please reconnect the\ndevice", theme::TEXT_NORMAL).vertically_centered(); + + let mut frame = ResultScreen::new(BLD_FG, BLD_BG, ICON_SPINNER, title, content, true); + show(&mut frame); +} + #[no_mangle] extern "C" fn screen_menu(_bld_version: *const cty::c_char) -> u32 { run(&mut Menu::new()) diff --git a/core/embed/rust/src/ui/model_tr/component/button.rs b/core/embed/rust/src/ui/model_tr/component/button.rs index 0f8df14045..25bace9108 100644 --- a/core/embed/rust/src/ui/model_tr/component/button.rs +++ b/core/embed/rust/src/ui/model_tr/component/button.rs @@ -538,6 +538,15 @@ where ) } + /// Left cancel, armed text and blank on right. + pub fn cancel_armed_none(middle: T) -> Self { + Self::new( + Some(ButtonDetails::cancel_icon()), + Some(ButtonDetails::armed_text(middle)), + None, + ) + } + /// Left back arrow and middle armed text. pub fn arrow_armed_none(text: T) -> Self { Self::new( diff --git a/core/embed/rust/src/ui/model_tr/component/error.rs b/core/embed/rust/src/ui/model_tr/component/error.rs index 43cc3857bb..29624f40bd 100644 --- a/core/embed/rust/src/ui/model_tr/component/error.rs +++ b/core/embed/rust/src/ui/model_tr/component/error.rs @@ -1,6 +1,6 @@ use crate::ui::{ component::{Child, Component, Event, EventCtx, Label, Never, Pad}, - constant::screen, + constant::{screen, WIDTH}, display, geometry::{Alignment2D, Offset, Point, Rect}, }; @@ -11,7 +11,6 @@ use super::super::{ }; const FOOTER_AREA_HEIGHT: i16 = 20; -const MESSAGE_AREA_HEIGHT: i16 = 32; const DIVIDER_POSITION: i16 = 43; pub struct ErrorScreen { @@ -55,9 +54,15 @@ impl> Component for ErrorScreen { self.show_icons = false; } + let top_offset = if self.show_icons { + Offset::y(11) + } else { + Offset::y(8) + }; + let message_area = Rect::new( - title_area.bottom_left(), - title_area.bottom_right() + Offset::y(MESSAGE_AREA_HEIGHT), + title_area.top_left() + top_offset, + Point::new(title_area.bottom_right().x, DIVIDER_POSITION), ); self.message.place(message_area); @@ -84,12 +89,9 @@ impl> Component for ErrorScreen { } self.title.paint(); self.message.paint(); - // divider line - let bar = Rect::from_center_and_size( - Point::new(self.area.center().x, DIVIDER_POSITION), - Offset::new(self.area.width(), 1), - ); - display::rect_fill(bar, FG); + + // // divider line + display::dotted_line(Point::new(0, DIVIDER_POSITION), WIDTH, FG, 3); self.footer.paint(); } diff --git a/core/embed/trezorhal/secret.h b/core/embed/trezorhal/secret.h new file mode 100644 index 0000000000..a5a8be8efe --- /dev/null +++ b/core/embed/trezorhal/secret.h @@ -0,0 +1,18 @@ + +#include +#include "secbool.h" + +#define SECRET_HEADER_MAGIC "TRZS" +#define SECRET_HEADER_LEN 16 +#define SECRET_OPTIGA_KEY_OFFSET 16 +#define SECRET_OPTIGA_KEY_LEN 32 + +void secret_write(uint8_t* data, uint32_t offset, uint32_t len); + +secbool secret_read(uint8_t* data, uint32_t offset, uint32_t len); + +secbool secret_wiped(void); + +void secret_erase(void); + +void secret_write_header(void); diff --git a/core/embed/trezorhal/stm32f4/mpu.c b/core/embed/trezorhal/stm32f4/mpu.c index 75fa6916bc..c7c2676fa2 100644 --- a/core/embed/trezorhal/stm32f4/mpu.c +++ b/core/embed/trezorhal/stm32f4/mpu.c @@ -125,12 +125,13 @@ void mpu_config_firmware(void) { MPU->RASR = MPU_RASR_ENABLE_Msk | MPU_RASR_ATTR_FLASH | LL_MPU_REGION_SIZE_64KB | LL_MPU_REGION_FULL_ACCESS | MPU_RASR_XN_Msk; - // Storage#2 (0x08110000 - 0x0811FFFF, 64 KiB, read-write, execute never) + // Secret + Storage#2 (0x08100000 - 0x0811FFFF, 16 Kib + 64 KiB, read-write, + // execute never) MPU->RNR = MPU_REGION_NUMBER2; MPU->RBAR = FLASH_BASE + 0x110000; MPU->RASR = MPU_RASR_ENABLE_Msk | MPU_RASR_ATTR_FLASH | - LL_MPU_REGION_SIZE_64KB | LL_MPU_REGION_FULL_ACCESS | - MPU_RASR_XN_Msk; + LL_MPU_REGION_SIZE_128KB | LL_MPU_REGION_FULL_ACCESS | + MPU_RASR_XN_Msk | MPU_SUBREGION_DISABLE(0x0E); // Firmware (0x08040000 - 0x080FFFFF, 6 * 128 KiB = 1024 KiB except 2/8 at // start = 768 KiB, read-only) diff --git a/core/embed/trezorhal/stm32f4/platform.c b/core/embed/trezorhal/stm32f4/platform.c index e2dd41a95f..2624a7746e 100644 --- a/core/embed/trezorhal/stm32f4/platform.c +++ b/core/embed/trezorhal/stm32f4/platform.c @@ -195,6 +195,13 @@ void set_core_clock(clock_settings_t settings) { } #endif +void drop_privileges(void) { + // jump to unprivileged mode + // http://infocenter.arm.com/help/topic/com.arm.doc.dui0552a/CHDBIBGJ.html + __asm__ volatile("msr control, %0" ::"r"(0x1)); + __asm__ volatile("isb"); +} + // from util.s extern void shutdown_privileged(void); diff --git a/core/embed/trezorhal/stm32f4/platform.h b/core/embed/trezorhal/stm32f4/platform.h index 9af69c2dff..d41af2d43d 100644 --- a/core/embed/trezorhal/stm32f4/platform.h +++ b/core/embed/trezorhal/stm32f4/platform.h @@ -40,6 +40,7 @@ void jump_to_unprivileged(uint32_t address); void jump_to_with_flag(uint32_t address, uint32_t register_flag); void ensure_compatible_settings(void); void clear_otg_hs_memory(void); +void drop_privileges(void); extern uint32_t __stack_chk_guard; diff --git a/core/embed/trezorhal/stm32f4/secret.c b/core/embed/trezorhal/stm32f4/secret.c new file mode 100644 index 0000000000..d8d032dec6 --- /dev/null +++ b/core/embed/trezorhal/stm32f4/secret.c @@ -0,0 +1,59 @@ +#include "secret.h" +#include +#include "common.h" +#include "flash.h" +#include "model.h" + +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; +} + +void secret_write_header(void) { + uint8_t header[SECRET_HEADER_LEN] = {0}; + memcpy(header, SECRET_HEADER_MAGIC, 4); + secret_write(header, 0, SECRET_HEADER_LEN); +} + +void secret_write(uint8_t* data, uint32_t offset, uint32_t len) { + ensure(flash_unlock_write(), "secret write"); + for (int i = 0; i < len; i++) { + ensure(flash_area_write_byte(&SECRET_AREA, offset + i, data[i]), + "secret write"); + } + ensure(flash_lock_write(), "secret write"); +} + +secbool secret_read(uint8_t* data, uint32_t offset, uint32_t len) { + if (sectrue != verify_header()) { + return secfalse; + } + + memcpy(data, flash_area_get_address(&SECRET_AREA, offset, len), len); + + return sectrue; +} + +secbool secret_wiped(void) { + flash_area_get_address(&SECRET_AREA, 0, 1); + + flash_area_get_size(&SECRET_AREA); + + uint32_t size = flash_area_get_size(&SECRET_AREA); + + for (int i = 0; i < size; i += 4) { + uint32_t* addr = (uint32_t*)flash_area_get_address(&SECRET_AREA, i, 4); + if (*addr != 0xFFFFFFFF) { + return secfalse; + } + } + return sectrue; +} + +void secret_erase(void) { + ensure(flash_area_erase(&SECRET_AREA, NULL), "secret erase"); +} diff --git a/core/embed/trezorhal/unix/secret.c b/core/embed/trezorhal/unix/secret.c new file mode 100644 index 0000000000..24fee45f93 --- /dev/null +++ b/core/embed/trezorhal/unix/secret.c @@ -0,0 +1,9 @@ +#include "secret.h" + +void secret_write(uint8_t* data, uint32_t offset, uint32_t len) {} + +void secret_read(uint8_t* data, uint32_t offset, uint32_t len) {} + +secbool secret_wiped(void) { return secfalse; } + +void secret_erase(void) {} diff --git a/core/embed/vendorheader/D001/vendor_unsafe.json b/core/embed/vendorheader/D001/vendor_unsafe.json index 32bd4b1cc0..16a223e524 100644 --- a/core/embed/vendorheader/D001/vendor_unsafe.json +++ b/core/embed/vendorheader/D001/vendor_unsafe.json @@ -6,6 +6,7 @@ "version": [0, 1], "sig_m": 2, "trust": { + "allow_run_with_secret": false, "show_vendor_string": true, "require_user_click": true, "red_background": true, diff --git a/core/embed/vendorheader/T2B1/vendor_prodtest.json b/core/embed/vendorheader/T2B1/vendor_prodtest.json new file mode 100644 index 0000000000..abf6a0ac4e --- /dev/null +++ b/core/embed/vendorheader/T2B1/vendor_prodtest.json @@ -0,0 +1,19 @@ +{ + "header_len": 4608, + "text": "UNSAFE, FACTORY TEST ONLY", + "hw_model": "T2B1", + "expiry": 0, + "version": [0, 0], + "sig_m": 2, + "trust": { + "allow_run_with_secret": true, + "show_vendor_string": false, + "require_user_click": false, + "red_background": false, + "delay": 0 + }, + "pubkeys": [ + "0c0b31408416bd1e813e817599c3320bf775eb1e1843344a1108fdec775fee29", + "ed5daba70a99d5c816e6d8bec1c2ae1bb7be804ceddbbe42be772521ca561122" + ] +} diff --git a/core/embed/vendorheader/T2B1/vendor_prodtest.toif b/core/embed/vendorheader/T2B1/vendor_prodtest.toif new file mode 100644 index 0000000000..8c25f0ddd9 Binary files /dev/null and b/core/embed/vendorheader/T2B1/vendor_prodtest.toif differ diff --git a/core/embed/vendorheader/T2B1/vendor_qa_DO_NOT_SIGN.json b/core/embed/vendorheader/T2B1/vendor_qa_DO_NOT_SIGN.json index 77aa812863..325e759e89 100644 --- a/core/embed/vendorheader/T2B1/vendor_qa_DO_NOT_SIGN.json +++ b/core/embed/vendorheader/T2B1/vendor_qa_DO_NOT_SIGN.json @@ -6,6 +6,7 @@ "version": [0, 0], "sig_m": 2, "trust": { + "allow_run_with_secret": true, "show_vendor_string": false, "require_user_click": false, "red_background": false, diff --git a/core/embed/vendorheader/T2B1/vendor_satoshilabs.json b/core/embed/vendorheader/T2B1/vendor_satoshilabs.json index d4aeaa3898..7dc70d9013 100644 --- a/core/embed/vendorheader/T2B1/vendor_satoshilabs.json +++ b/core/embed/vendorheader/T2B1/vendor_satoshilabs.json @@ -6,6 +6,7 @@ "version": [0, 0], "sig_m": 2, "trust": { + "allow_run_with_secret": true, "show_vendor_string": false, "require_user_click": false, "red_background": false, diff --git a/core/embed/vendorheader/T2B1/vendor_unsafe.json b/core/embed/vendorheader/T2B1/vendor_unsafe.json index 41699ae41c..dc1159f268 100644 --- a/core/embed/vendorheader/T2B1/vendor_unsafe.json +++ b/core/embed/vendorheader/T2B1/vendor_unsafe.json @@ -6,6 +6,7 @@ "version": [0, 0], "sig_m": 2, "trust": { + "allow_run_with_secret": false, "show_vendor_string": true, "require_user_click": true, "red_background": true, diff --git a/core/embed/vendorheader/T2B1/vendorheader_prodtest_unsigned.bin b/core/embed/vendorheader/T2B1/vendorheader_prodtest_unsigned.bin new file mode 100644 index 0000000000..e003fbe676 Binary files /dev/null and b/core/embed/vendorheader/T2B1/vendorheader_prodtest_unsigned.bin differ diff --git a/core/embed/vendorheader/T2B1/vendorheader_qa_DO_NOT_SIGN_signed_dev.bin b/core/embed/vendorheader/T2B1/vendorheader_qa_DO_NOT_SIGN_signed_dev.bin index fac4d1b465..f0fcd329de 100644 Binary files a/core/embed/vendorheader/T2B1/vendorheader_qa_DO_NOT_SIGN_signed_dev.bin and b/core/embed/vendorheader/T2B1/vendorheader_qa_DO_NOT_SIGN_signed_dev.bin differ diff --git a/core/embed/vendorheader/T2B1/vendorheader_qa_DO_NOT_SIGN_unsigned.bin b/core/embed/vendorheader/T2B1/vendorheader_qa_DO_NOT_SIGN_unsigned.bin index 5c4a762e52..4e17570bca 100644 Binary files a/core/embed/vendorheader/T2B1/vendorheader_qa_DO_NOT_SIGN_unsigned.bin and b/core/embed/vendorheader/T2B1/vendorheader_qa_DO_NOT_SIGN_unsigned.bin differ diff --git a/core/embed/vendorheader/T2B1/vendorheader_satoshilabs_unsigned.bin b/core/embed/vendorheader/T2B1/vendorheader_satoshilabs_unsigned.bin index c59fed81a8..0c3622d046 100644 Binary files a/core/embed/vendorheader/T2B1/vendorheader_satoshilabs_unsigned.bin and b/core/embed/vendorheader/T2B1/vendorheader_satoshilabs_unsigned.bin differ diff --git a/core/embed/vendorheader/T2T1/vendor_prodtest.json b/core/embed/vendorheader/T2T1/vendor_prodtest.json index ca4d021e13..1d3a366862 100644 --- a/core/embed/vendorheader/T2T1/vendor_prodtest.json +++ b/core/embed/vendorheader/T2T1/vendor_prodtest.json @@ -6,6 +6,7 @@ "version": [0, 0], "sig_m": 2, "trust": { + "allow_run_with_secret": false, "show_vendor_string": false, "require_user_click": false, "red_background": false, diff --git a/core/embed/vendorheader/T2T1/vendor_qa_DO_NOT_SIGN.json b/core/embed/vendorheader/T2T1/vendor_qa_DO_NOT_SIGN.json index cbd85714be..6fab49ebb8 100644 --- a/core/embed/vendorheader/T2T1/vendor_qa_DO_NOT_SIGN.json +++ b/core/embed/vendorheader/T2T1/vendor_qa_DO_NOT_SIGN.json @@ -6,6 +6,7 @@ "version": [0, 0], "sig_m": 2, "trust": { + "allow_run_with_secret": false, "show_vendor_string": false, "require_user_click": false, "red_background": false, diff --git a/core/embed/vendorheader/T2T1/vendor_satoshilabs.json b/core/embed/vendorheader/T2T1/vendor_satoshilabs.json index 9e40a08d69..59dbf90cb0 100644 --- a/core/embed/vendorheader/T2T1/vendor_satoshilabs.json +++ b/core/embed/vendorheader/T2T1/vendor_satoshilabs.json @@ -6,6 +6,7 @@ "version": [0, 1], "sig_m": 2, "trust": { + "allow_run_with_secret": false, "show_vendor_string": false, "require_user_click": false, "red_background": false, diff --git a/core/embed/vendorheader/T2T1/vendor_unsafe.json b/core/embed/vendorheader/T2T1/vendor_unsafe.json index 54e8ce9db4..7d266f28dc 100644 --- a/core/embed/vendorheader/T2T1/vendor_unsafe.json +++ b/core/embed/vendorheader/T2T1/vendor_unsafe.json @@ -6,6 +6,7 @@ "version": [0, 1], "sig_m": 2, "trust": { + "allow_run_with_secret": false, "show_vendor_string": true, "require_user_click": true, "red_background": true, diff --git a/core/site_scons/boards/trezor_r_v10.py b/core/site_scons/boards/trezor_r_v10.py index d3ad100897..fa704d94b0 100644 --- a/core/site_scons/boards/trezor_r_v10.py +++ b/core/site_scons/boards/trezor_r_v10.py @@ -70,6 +70,7 @@ def configure( sources += ["embed/trezorhal/stm32f4/optiga_hal.c"] sources += ["embed/trezorhal/optiga/optiga_commands.c"] sources += ["embed/trezorhal/optiga/optiga_transport.c"] + sources += ["embed/trezorhal/stm32f4/secret.c"] env.get("ENV")["TREZOR_BOARD"] = board env.get("ENV")["MCU_TYPE"] = mcu diff --git a/core/src/trezor/enums/MessageType.py b/core/src/trezor/enums/MessageType.py index 75b9228f0d..205e648756 100644 --- a/core/src/trezor/enums/MessageType.py +++ b/core/src/trezor/enums/MessageType.py @@ -47,6 +47,7 @@ FirmwareHash = 89 UnlockPath = 93 UnlockedPathRequest = 94 ShowDeviceTutorial = 95 +UnlockBootloader = 96 FirmwareErase = 6 FirmwareUpload = 7 FirmwareRequest = 8 diff --git a/core/src/trezor/enums/__init__.py b/core/src/trezor/enums/__init__.py index e562b556f3..b2b3981f79 100644 --- a/core/src/trezor/enums/__init__.py +++ b/core/src/trezor/enums/__init__.py @@ -64,6 +64,7 @@ if TYPE_CHECKING: UnlockPath = 93 UnlockedPathRequest = 94 ShowDeviceTutorial = 95 + UnlockBootloader = 96 SetU2FCounter = 63 GetNextU2FCounter = 80 NextU2FCounter = 81 diff --git a/core/src/trezor/messages.py b/core/src/trezor/messages.py index f111e2375e..095c04e947 100644 --- a/core/src/trezor/messages.py +++ b/core/src/trezor/messages.py @@ -2628,6 +2628,12 @@ if TYPE_CHECKING: def is_type_of(cls, msg: Any) -> TypeGuard["ShowDeviceTutorial"]: return isinstance(msg, cls) + class UnlockBootloader(protobuf.MessageType): + + @classmethod + def is_type_of(cls, msg: Any) -> TypeGuard["UnlockBootloader"]: + return isinstance(msg, cls) + class DebugLinkDecision(protobuf.MessageType): button: "DebugButton | None" swipe: "DebugSwipeDirection | None" diff --git a/legacy/firmware/protob/Makefile b/legacy/firmware/protob/Makefile index 35cad8d0f3..9112964221 100644 --- a/legacy/firmware/protob/Makefile +++ b/legacy/firmware/protob/Makefile @@ -7,7 +7,8 @@ SKIPPED_MESSAGES := Binance Cardano DebugMonero Eos Monero Ontology Ripple SdPro DebugLinkLayout DebugLinkResetDebugEvents GetNonce \ TxAckInput TxAckOutput TxAckPrev TxAckPaymentRequest \ EthereumSignTypedData EthereumTypedDataStructRequest EthereumTypedDataStructAck \ - EthereumTypedDataValueRequest EthereumTypedDataValueAck ShowDeviceTutorial + EthereumTypedDataValueRequest EthereumTypedDataValueAck ShowDeviceTutorial \ + UnlockBootloader ifeq ($(BITCOIN_ONLY), 1) SKIPPED_MESSAGES += Ethereum NEM Stellar diff --git a/python/.changelog.d/+68d5cd56.added b/python/.changelog.d/+68d5cd56.added new file mode 100644 index 0000000000..3a61b253d2 --- /dev/null +++ b/python/.changelog.d/+68d5cd56.added @@ -0,0 +1 @@ +trezorctl: support unlocking bootloader via `trezorctl device unlock-bootloader`. diff --git a/python/src/trezorlib/cli/device.py b/python/src/trezorlib/cli/device.py index be38a7d876..c8f6e6104e 100644 --- a/python/src/trezorlib/cli/device.py +++ b/python/src/trezorlib/cli/device.py @@ -299,6 +299,13 @@ def tutorial(client: "TrezorClient") -> str: return device.show_device_tutorial(client) +@cli.command() +@with_client +def unlock_bootloader(client: "TrezorClient") -> str: + """Unlocks bootloader. Irreversible.""" + return device.unlock_bootloader(client) + + @cli.command() @click.argument("enable", type=ChoiceType({"on": True, "off": False}), required=False) @click.option( diff --git a/python/src/trezorlib/device.py b/python/src/trezorlib/device.py index 87fde1755e..4ed5cbbfa9 100644 --- a/python/src/trezorlib/device.py +++ b/python/src/trezorlib/device.py @@ -248,6 +248,12 @@ def show_device_tutorial(client: "TrezorClient") -> "MessageType": return client.call(messages.ShowDeviceTutorial()) +@session +@expect(messages.Success, field="message", ret_type=str) +def unlock_bootloader(client: "TrezorClient") -> "MessageType": + return client.call(messages.UnlockBootloader()) + + @expect(messages.Success, field="message", ret_type=str) @session def set_busy(client: "TrezorClient", expiry_ms: Optional[int]) -> "MessageType": diff --git a/python/src/trezorlib/firmware/vendor.py b/python/src/trezorlib/firmware/vendor.py index a9fd0ebcec..d4314d6d75 100644 --- a/python/src/trezorlib/firmware/vendor.py +++ b/python/src/trezorlib/firmware/vendor.py @@ -47,6 +47,7 @@ def _transform_vendor_trust(data: bytes) -> bytes: class VendorTrust(Struct): + allow_run_with_secret: bool show_vendor_string: bool require_user_click: bool red_background: bool @@ -56,7 +57,8 @@ class VendorTrust(Struct): SUBCON = c.Transformed( c.BitStruct( - "_reserved" / c.Default(c.BitsInteger(9), 0), + "_reserved" / c.Default(c.BitsInteger(8), 0), + "allow_run_with_secret" / c.Flag, "show_vendor_string" / c.Flag, "require_user_click" / c.Flag, "red_background" / c.Flag, diff --git a/python/src/trezorlib/messages.py b/python/src/trezorlib/messages.py index 2d6926b4c0..e1b90f79a5 100644 --- a/python/src/trezorlib/messages.py +++ b/python/src/trezorlib/messages.py @@ -72,6 +72,7 @@ class MessageType(IntEnum): UnlockPath = 93 UnlockedPathRequest = 94 ShowDeviceTutorial = 95 + UnlockBootloader = 96 SetU2FCounter = 63 GetNextU2FCounter = 80 NextU2FCounter = 81 @@ -3737,6 +3738,10 @@ class ShowDeviceTutorial(protobuf.MessageType): MESSAGE_WIRE_TYPE = 95 +class UnlockBootloader(protobuf.MessageType): + MESSAGE_WIRE_TYPE = 96 + + class DebugLinkDecision(protobuf.MessageType): MESSAGE_WIRE_TYPE = 100 FIELDS = {