diff --git a/core/embed/bootloader/.changelog.d/4133.changed b/core/embed/bootloader/.changelog.d/4133.changed new file mode 100644 index 0000000000..cc3f2171f8 --- /dev/null +++ b/core/embed/bootloader/.changelog.d/4133.changed @@ -0,0 +1 @@ +[T3B1, T3T1] Added bootloader unlock mechanism to U5 models diff --git a/core/embed/bootloader/main.c b/core/embed/bootloader/main.c index fbba58170e..07adabd46d 100644 --- a/core/embed/bootloader/main.c +++ b/core/embed/bootloader/main.c @@ -220,7 +220,7 @@ static usb_result_t bootloader_usb_loop(const vendor_header *const vhdr, case MessageType_MessageType_GetFeatures: process_msg_GetFeatures(USB_IFACE_NUM, msg_size, buf, vhdr, hdr); break; -#if defined USE_OPTIGA && !defined STM32U5 +#if defined USE_OPTIGA case MessageType_MessageType_UnlockBootloader: response = ui_screen_unlock_bootloader_confirm(); if (INPUT_CANCEL == response) { diff --git a/core/embed/bootloader/messages.c b/core/embed/bootloader/messages.c index 7cee213aba..568cafdb26 100644 --- a/core/embed/bootloader/messages.c +++ b/core/embed/bootloader/messages.c @@ -662,8 +662,8 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size, is_ilu = sectrue; } -#if defined USE_OPTIGA && !defined STM32U5 - if (sectrue != secret_wiped() && +#if defined USE_OPTIGA + if (secfalse != secret_optiga_present() && ((vhdr.vtrust & VTRUST_SECRET_MASK) != VTRUST_SECRET_ALLOW)) { MSG_SEND_INIT(Failure); MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); @@ -868,10 +868,10 @@ void process_msg_unknown(uint8_t iface_num, uint32_t msg_size, uint8_t *buf) { MSG_SEND(Failure); } -#if defined USE_OPTIGA && !defined STM32U5 +#if defined USE_OPTIGA void process_msg_UnlockBootloader(uint8_t iface_num, uint32_t msg_size, uint8_t *buf) { - secret_erase(); + secret_optiga_erase(); MSG_SEND_INIT(Success); MSG_SEND(Success); } diff --git a/core/embed/rust/src/ui/model_mercury/bootloader/mod.rs b/core/embed/rust/src/ui/model_mercury/bootloader/mod.rs index 5f849615a0..70d886400c 100644 --- a/core/embed/rust/src/ui/model_mercury/bootloader/mod.rs +++ b/core/embed/rust/src/ui/model_mercury/bootloader/mod.rs @@ -315,11 +315,32 @@ impl UIFeaturesBootloader for ModelMercuryFeatures { } fn screen_unlock_bootloader_confirm() -> u32 { - unimplemented!(); + let title = + Label::left_aligned("UNLOCK BOOTLOADER".into(), TEXT_BOLD).vertically_centered(); + let msg = Label::centered("This action cannot be undone!".into(), TEXT_NORMAL); + + let right = Button::with_text("UNLOCK".into()) + .styled(button_confirm()) + .with_text_align(Alignment::Center); + let left = Button::with_text("CANCEL".into()) + .styled(button_bld()) + .with_text_align(Alignment::Center); + + let mut frame = Confirm::new(BLD_BG, left, right, ConfirmTitle::Text(title), msg); + + run(&mut frame) } fn screen_unlock_bootloader_success() { - unimplemented!(); + let mut frame = ResultScreen::new( + &RESULT_FW_INSTALL, + Icon::new(CHECK40), + "Bootloader unlocked".into(), + Label::centered(RECONNECT_MESSAGE.into(), RESULT_FW_INSTALL.title_style()) + .vertically_centered(), + true, + ); + show(&mut frame, true); } fn screen_menu(firmware_present: secbool) -> u32 { diff --git a/core/embed/rust/src/ui/model_mercury/component/error.rs b/core/embed/rust/src/ui/model_mercury/component/error.rs index c6280ae355..9df9741851 100644 --- a/core/embed/rust/src/ui/model_mercury/component/error.rs +++ b/core/embed/rust/src/ui/model_mercury/component/error.rs @@ -17,7 +17,7 @@ use super::{ const ICON_TOP: i16 = 23; const TITLE_AREA_START: i16 = 70; -const MESSAGE_AREA_START: i16 = 116; +const MESSAGE_AREA_START: i16 = 90; #[cfg(feature = "bootloader")] const STYLE: &ResultStyle = &crate::ui::model_mercury::theme::bootloader::RESULT_WIPE; @@ -34,7 +34,7 @@ pub struct ErrorScreen<'a> { impl<'a> ErrorScreen<'a> { pub fn new(title: TString<'a>, message: TString<'a>, footer: TString<'a>) -> Self { let title = Label::centered(title, STYLE.title_style()); - let message = Label::centered(message, STYLE.message_style()); + let message = Label::centered(message, STYLE.message_style()).vertically_centered(); let footer = ResultFooter::new( Label::centered(footer, STYLE.title_style()).vertically_centered(), STYLE, diff --git a/core/embed/trezorhal/secret.h b/core/embed/trezorhal/secret.h index e22ff9ad71..f8fa453da0 100644 --- a/core/embed/trezorhal/secret.h +++ b/core/embed/trezorhal/secret.h @@ -52,6 +52,12 @@ secbool secret_optiga_set(const uint8_t secret[SECRET_OPTIGA_KEY_LEN]); // was made unavailable by calling secret_optiga_hide secbool secret_optiga_get(uint8_t dest[SECRET_OPTIGA_KEY_LEN]); +// Checks if the optiga pairing secret is present in the secret storage +secbool secret_optiga_present(void); + +// Erases optiga pairing secret from the secret storage +void secret_optiga_erase(void); + // Regenerates the BHK and writes it to the secret storage void secret_bhk_regenerate(void); diff --git a/core/embed/trezorhal/stm32f4/secret.c b/core/embed/trezorhal/stm32f4/secret.c index e6c5cef2b4..45abb31e98 100644 --- a/core/embed/trezorhal/stm32f4/secret.c +++ b/core/embed/trezorhal/stm32f4/secret.c @@ -98,6 +98,12 @@ secbool secret_optiga_get(uint8_t dest[SECRET_OPTIGA_KEY_LEN]) { return secret_read(dest, SECRET_OPTIGA_KEY_OFFSET, SECRET_OPTIGA_KEY_LEN); } +secbool secret_optiga_present(void) { + return (sectrue != secret_wiped()) * sectrue; +} + +void secret_optiga_erase(void) { secret_erase(); } + void secret_prepare_fw(secbool allow_run_with_secret, secbool _trust_all) { #ifdef USE_OPTIGA if (sectrue != allow_run_with_secret && sectrue != secret_wiped()) { diff --git a/core/embed/trezorhal/stm32u5/secret.c b/core/embed/trezorhal/stm32u5/secret.c index bfa107a965..3700c5e914 100644 --- a/core/embed/trezorhal/stm32u5/secret.c +++ b/core/embed/trezorhal/stm32u5/secret.c @@ -102,7 +102,9 @@ static secbool secret_present(uint32_t offset, uint32_t len) { int secret_empty_bytes = 0; for (int i = 0; i < len; i++) { - if (secret[i] == 0xFF) { + // 0xFF being the default value of the flash memory (before any write) + // 0x00 being the value of the flash memory after manual erase + if (secret[i] == 0xFF || secret[i] == 0x00) { secret_empty_bytes++; } } @@ -164,7 +166,7 @@ void secret_bhk_regenerate(void) { // This functions only works when software has access to the secret storage, // i.e. in bootloader. Access to secret storage is restricted by calling // secret_hide. -static secbool secret_optiga_present(void) { +secbool secret_optiga_present(void) { return secret_present(SECRET_OPTIGA_KEY_OFFSET, SECRET_OPTIGA_KEY_LEN); } @@ -235,6 +237,11 @@ static void secret_optiga_uncache(void) { } #endif +void secret_optiga_erase(void) { + uint8_t value[SECRET_OPTIGA_KEY_LEN] = {0}; + secret_write(value, SECRET_OPTIGA_KEY_OFFSET, SECRET_OPTIGA_KEY_LEN); +} + void secret_erase(void) { ensure(flash_area_erase(&SECRET_AREA, NULL), "secret erase"); } @@ -263,6 +270,9 @@ void secret_prepare_fw(secbool allow_run_with_secret, secbool trust_all) { secret_disable_access(); } } else { + if (secfalse != secret_optiga_present()) { + show_install_restricted_screen(); + } secret_disable_access(); } #else