feat(core): implement secret handling in bootloader

pull/3215/head
tychovrahe 10 months ago committed by matejcik
parent 58bb702498
commit e8281385f6

@ -516,3 +516,12 @@ message UnlockedPathRequest {
*/
message ShowDeviceTutorial {
}
/**
* Request: Unlocks bootloader, !irreversible!
* @start
* @next Success
* @next Failure
*/
message UnlockBootloader {
}

@ -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];

@ -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 = []

@ -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 = [

@ -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 = []

@ -0,0 +1 @@
Locked bootloader support: bootloader will disallow installation of unofficial firmware unless the Optiga pairing secret is erased.

@ -0,0 +1 @@
Support unlocking the bootloader via `UnlockBootloader` message.

@ -19,6 +19,8 @@
#include <string.h>
#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); }

@ -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

@ -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);

@ -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

@ -23,6 +23,7 @@
#include <stdint.h>
#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

@ -39,6 +39,9 @@ PB_BIND(FirmwareRequest, FirmwareRequest, AUTO)
PB_BIND(FirmwareUpload, FirmwareUpload, AUTO)
PB_BIND(UnlockBootloader, UnlockBootloader, AUTO)

@ -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" */

@ -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 {
}

@ -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

@ -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

@ -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 {

@ -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;

@ -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,
},
};

@ -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);

@ -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());
}
}

@ -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<Label<&'a str>>,
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 {

@ -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())

@ -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(

@ -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<T> {
@ -55,9 +54,15 @@ impl<T: AsRef<str>> Component for ErrorScreen<T> {
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<T: AsRef<str>> Component for ErrorScreen<T> {
}
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();
}

@ -0,0 +1,18 @@
#include <stdint.h>
#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);

@ -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)

@ -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);

@ -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;

@ -0,0 +1,59 @@
#include "secret.h"
#include <string.h>
#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");
}

@ -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) {}

@ -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,

@ -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"
]
}

@ -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,

@ -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,

@ -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,

@ -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,

@ -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,

@ -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,

@ -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,

@ -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

@ -47,6 +47,7 @@ FirmwareHash = 89
UnlockPath = 93
UnlockedPathRequest = 94
ShowDeviceTutorial = 95
UnlockBootloader = 96
FirmwareErase = 6
FirmwareUpload = 7
FirmwareRequest = 8

@ -64,6 +64,7 @@ if TYPE_CHECKING:
UnlockPath = 93
UnlockedPathRequest = 94
ShowDeviceTutorial = 95
UnlockBootloader = 96
SetU2FCounter = 63
GetNextU2FCounter = 80
NextU2FCounter = 81

@ -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"

@ -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

@ -0,0 +1 @@
trezorctl: support unlocking bootloader via `trezorctl device unlock-bootloader`.

@ -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(

@ -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":

@ -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,

@ -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 = {

Loading…
Cancel
Save