From 3b05b7603aa738ad71b5d50743db024b1cc8c0f0 Mon Sep 17 00:00:00 2001 From: tychovrahe Date: Fri, 31 Jan 2025 17:11:40 +0100 Subject: [PATCH] refactor(core): complete bootloader refactoring --- core/SConscript.bootloader | 22 +- core/SConscript.bootloader_ci | 2 +- core/SConscript.bootloader_emu | 24 +- .../bootloader/.changelog.d/4572.changed | 1 + core/embed/projects/bootloader/antiglitch.c | 35 + core/embed/projects/bootloader/antiglitch.h | 30 + core/embed/projects/bootloader/bootui.c | 38 +- core/embed/projects/bootloader/bootui.h | 52 +- core/embed/projects/bootloader/emulator.c | 10 +- core/embed/projects/bootloader/emulator.h | 24 +- core/embed/projects/bootloader/main.c | 336 +------ core/embed/projects/bootloader/messages.c | 884 ------------------ core/embed/projects/bootloader/messages.h | 83 -- core/embed/projects/bootloader/poll.c | 62 ++ core/embed/projects/bootloader/poll.h | 39 + .../bootloader/protob/{ => pb}/.gitignore | 0 .../bootloader/protob/{ => pb}/Makefile | 0 .../protob/{ => pb}/messages.options | 0 .../bootloader/protob/{ => pb}/messages.pb.c | 0 .../bootloader/protob/{ => pb}/messages.pb.h | 0 .../bootloader/protob/{ => pb}/messages.proto | 0 .../embed/projects/bootloader/protob/protob.c | 245 +++++ .../embed/projects/bootloader/protob/protob.h | 68 ++ .../embed/projects/bootloader/wire/codec_v1.c | 248 +++++ .../embed/projects/bootloader/wire/codec_v1.h | 54 ++ .../projects/bootloader/wire/wire_iface_usb.c | 113 +++ .../projects/bootloader/wire/wire_iface_usb.h | 26 + .../bootloader/workflow/wf_auto_update.c | 37 + .../bootloader/workflow/wf_bootloader.c | 99 ++ .../bootloader/workflow/wf_empty_device.c | 57 ++ .../bootloader/workflow/wf_firmware_update.c | 561 +++++++++++ .../bootloader/workflow/wf_get_features.c | 33 + .../bootloader/workflow/wf_host_control.c | 123 +++ .../bootloader/workflow/wf_initialize.c | 33 + .../projects/bootloader/workflow/wf_ping.c | 33 + .../workflow/wf_unlock_bootloader.c | 41 + .../bootloader/workflow/wf_wipe_device.c | 55 ++ .../projects/bootloader/workflow/workflow.h | 68 ++ core/embed/projects/bootloader_ci/messages.c | 2 +- core/site_scons/models/D002/discovery2.py | 1 - .../models/T3B1/trezor_t3b1_revB.py | 1 - .../models/T3T1/trezor_t3t1_revE.py | 1 - .../models/T3W1/trezor_t3w1_revA.py | 1 - .../models/T3W1/trezor_t3w1_revB.py | 1 - tools/style.c.exclude | 2 +- 45 files changed, 2196 insertions(+), 1349 deletions(-) create mode 100644 core/embed/projects/bootloader/.changelog.d/4572.changed create mode 100644 core/embed/projects/bootloader/antiglitch.c create mode 100644 core/embed/projects/bootloader/antiglitch.h delete mode 100644 core/embed/projects/bootloader/messages.c delete mode 100644 core/embed/projects/bootloader/messages.h create mode 100644 core/embed/projects/bootloader/poll.c create mode 100644 core/embed/projects/bootloader/poll.h rename core/embed/projects/bootloader/protob/{ => pb}/.gitignore (100%) rename core/embed/projects/bootloader/protob/{ => pb}/Makefile (100%) rename core/embed/projects/bootloader/protob/{ => pb}/messages.options (100%) rename core/embed/projects/bootloader/protob/{ => pb}/messages.pb.c (100%) rename core/embed/projects/bootloader/protob/{ => pb}/messages.pb.h (100%) rename core/embed/projects/bootloader/protob/{ => pb}/messages.proto (100%) create mode 100644 core/embed/projects/bootloader/protob/protob.c create mode 100644 core/embed/projects/bootloader/protob/protob.h create mode 100644 core/embed/projects/bootloader/wire/codec_v1.c create mode 100644 core/embed/projects/bootloader/wire/codec_v1.h create mode 100644 core/embed/projects/bootloader/wire/wire_iface_usb.c create mode 100644 core/embed/projects/bootloader/wire/wire_iface_usb.h create mode 100644 core/embed/projects/bootloader/workflow/wf_auto_update.c create mode 100644 core/embed/projects/bootloader/workflow/wf_bootloader.c create mode 100644 core/embed/projects/bootloader/workflow/wf_empty_device.c create mode 100644 core/embed/projects/bootloader/workflow/wf_firmware_update.c create mode 100644 core/embed/projects/bootloader/workflow/wf_get_features.c create mode 100644 core/embed/projects/bootloader/workflow/wf_host_control.c create mode 100644 core/embed/projects/bootloader/workflow/wf_initialize.c create mode 100644 core/embed/projects/bootloader/workflow/wf_ping.c create mode 100644 core/embed/projects/bootloader/workflow/wf_unlock_bootloader.c create mode 100644 core/embed/projects/bootloader/workflow/wf_wipe_device.c create mode 100644 core/embed/projects/bootloader/workflow/workflow.h diff --git a/core/SConscript.bootloader b/core/SConscript.bootloader index 54c95fbcf5..6b9116e773 100644 --- a/core/SConscript.bootloader +++ b/core/SConscript.bootloader @@ -105,11 +105,29 @@ SOURCE_BOOTLOADER = [ 'embed/projects/bootloader/header.S', 'embed/projects/bootloader/bootui.c', 'embed/projects/bootloader/main.c', - 'embed/projects/bootloader/messages.c', - 'embed/projects/bootloader/protob/messages.pb.c', + 'embed/projects/bootloader/poll.c', + 'embed/projects/bootloader/antiglitch.c', + 'embed/projects/bootloader/workflow/wf_firmware_update.c', + 'embed/projects/bootloader/workflow/wf_wipe_device.c', + 'embed/projects/bootloader/workflow/wf_get_features.c', + 'embed/projects/bootloader/workflow/wf_initialize.c', + 'embed/projects/bootloader/workflow/wf_ping.c', + 'embed/projects/bootloader/workflow/wf_bootloader.c', + 'embed/projects/bootloader/workflow/wf_empty_device.c', + 'embed/projects/bootloader/workflow/wf_auto_update.c', + 'embed/projects/bootloader/workflow/wf_host_control.c', + 'embed/projects/bootloader/wire/codec_v1.c', + 'embed/projects/bootloader/wire/wire_iface_usb.c', + 'embed/projects/bootloader/protob/protob.c', + 'embed/projects/bootloader/protob/pb/messages.pb.c', 'embed/projects/bootloader/version_check.c', ] +if 'optiga' in FEATURES_AVAILABLE: + SOURCE_BOOTLOADER += [ + 'embed/projects/bootloader/workflow/wf_unlock_bootloader.c', + ] + env.Replace( CAT='cat', diff --git a/core/SConscript.bootloader_ci b/core/SConscript.bootloader_ci index 6de6dbe4e7..3a56880ab4 100644 --- a/core/SConscript.bootloader_ci +++ b/core/SConscript.bootloader_ci @@ -99,7 +99,7 @@ SOURCE_BOOTLOADER = [ 'embed/projects/bootloader_ci/bootui.c', 'embed/projects/bootloader_ci/main.c', 'embed/projects/bootloader_ci/messages.c', - 'embed/projects/bootloader_ci/protob/messages.pb.c', + 'embed/projects/bootloader_ci/protob/pb/messages.pb.c', 'embed/projects/bootloader_ci/version_check.c', ] diff --git a/core/SConscript.bootloader_emu b/core/SConscript.bootloader_emu index 3a727a3925..c338d3b0d8 100644 --- a/core/SConscript.bootloader_emu +++ b/core/SConscript.bootloader_emu @@ -99,10 +99,23 @@ SOURCE_NANOPB = [ SOURCE_BOOTLOADER = [ 'embed/projects/bootloader/bootui.c', 'embed/projects/bootloader/main.c', - 'embed/projects/bootloader/messages.c', - 'embed/projects/bootloader/emulator.c', + 'embed/projects/bootloader/poll.c', + 'embed/projects/bootloader/antiglitch.c', + 'embed/projects/bootloader/workflow/wf_firmware_update.c', + 'embed/projects/bootloader/workflow/wf_wipe_device.c', + 'embed/projects/bootloader/workflow/wf_get_features.c', + 'embed/projects/bootloader/workflow/wf_initialize.c', + 'embed/projects/bootloader/workflow/wf_ping.c', + 'embed/projects/bootloader/workflow/wf_bootloader.c', + 'embed/projects/bootloader/workflow/wf_empty_device.c', + 'embed/projects/bootloader/workflow/wf_auto_update.c', + 'embed/projects/bootloader/workflow/wf_host_control.c', + 'embed/projects/bootloader/wire/codec_v1.c', + 'embed/projects/bootloader/wire/wire_iface_usb.c', + 'embed/projects/bootloader/protob/protob.c', + 'embed/projects/bootloader/protob/pb/messages.pb.c', 'embed/projects/bootloader/version_check.c', - 'embed/projects/bootloader/protob/messages.pb.c', + 'embed/projects/bootloader/emulator.c', ] SOURCE_TREZORHAL = [ @@ -137,6 +150,11 @@ env = Environment( FEATURES_AVAILABLE = models.configure_board(TREZOR_MODEL, HW_REVISION, FEATURES_WANTED, env, CPPDEFINES_HAL, SOURCE_UNIX, PATH_HAL) +if 'optiga' in FEATURES_AVAILABLE: + SOURCE_BOOTLOADER += [ + 'embed/projects/bootloader/workflow/wf_unlock_bootloader.c', + ] + env.Replace( CP='cp', AS='as', diff --git a/core/embed/projects/bootloader/.changelog.d/4572.changed b/core/embed/projects/bootloader/.changelog.d/4572.changed new file mode 100644 index 0000000000..1fc7107384 --- /dev/null +++ b/core/embed/projects/bootloader/.changelog.d/4572.changed @@ -0,0 +1 @@ +Major code clean-up and refactoring. diff --git a/core/embed/projects/bootloader/antiglitch.c b/core/embed/projects/bootloader/antiglitch.c new file mode 100644 index 0000000000..fa06bb2cb4 --- /dev/null +++ b/core/embed/projects/bootloader/antiglitch.c @@ -0,0 +1,35 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +// anti-glitch +static volatile secbool continue_to_firmware = secfalse; +static volatile secbool continue_to_firmware_backup = secfalse; + +secbool jump_is_allowed_1(void) { return continue_to_firmware; } +secbool jump_is_allowed_2(void) { return continue_to_firmware_backup; } + +void jump_allow_1(void) { continue_to_firmware = sectrue; } +void jump_allow_2(void) { continue_to_firmware_backup = sectrue; } + +void jump_reset(void) { + continue_to_firmware_backup = secfalse; + continue_to_firmware = secfalse; +} diff --git a/core/embed/projects/bootloader/antiglitch.h b/core/embed/projects/bootloader/antiglitch.h new file mode 100644 index 0000000000..60cf8a076f --- /dev/null +++ b/core/embed/projects/bootloader/antiglitch.h @@ -0,0 +1,30 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + +secbool jump_is_allowed_1(void); +secbool jump_is_allowed_2(void); + +void jump_allow_1(void); +void jump_allow_2(void); + +void jump_reset(void); diff --git a/core/embed/projects/bootloader/bootui.c b/core/embed/projects/bootloader/bootui.c index c2aa9b24b9..51ac3da056 100644 --- a/core/embed/projects/bootloader/bootui.c +++ b/core/embed/projects/bootloader/bootui.c @@ -22,35 +22,13 @@ #include #include #include + #include "bootui.h" #include "rust_ui.h" #include "version.h" #define BACKLIGHT_NORMAL 150 -#define COLOR_BL_BG COLOR_WHITE // background -#define COLOR_BL_FG COLOR_BLACK // foreground - -#ifdef RGB16 -#define COLOR_BL_FAIL RGB16(0xFF, 0x00, 0x00) // red -#define COLOR_BL_DONE RGB16(0x00, 0xAE, 0x0B) // green -#define COLOR_BL_PROCESS RGB16(0x4A, 0x90, 0xE2) // blue -#define COLOR_BL_GRAY RGB16(0x99, 0x99, 0x99) // gray -#else -#define COLOR_BL_FAIL COLOR_BL_FG -#define COLOR_BL_DONE COLOR_BL_FG -#define COLOR_BL_PROCESS COLOR_BL_FG -#define COLOR_BL_GRAY COLOR_BL_FG -#endif - -#if !defined TREZOR_MODEL_T2B1 && !defined TREZOR_MODEL_T3B1 -#define BOOT_WAIT_HEIGHT 25 -#define BOOT_WAIT_Y_TOP (DISPLAY_RESY - BOOT_WAIT_HEIGHT) -#else -#define BOOT_WAIT_HEIGHT 12 -#define BOOT_WAIT_Y_TOP (DISPLAY_RESY - BOOT_WAIT_HEIGHT) -#endif - #define TOIF_LENGTH(ptr) ((*(uint32_t *)((ptr) + 8)) + 12) // common shared functions @@ -139,13 +117,15 @@ uint32_t ui_screen_menu(secbool firmware_present) { return screen_menu(firmware_present); } +void ui_screen_connect(void) { screen_connect(initial_setup); } + // install UI -uint32_t ui_screen_install_confirm(const vendor_header *const vhdr, - const image_header *const hdr, - secbool should_keep_seed, - secbool is_newvendor, secbool is_newinstall, - int version_cmp) { +ui_result_t ui_screen_install_confirm(const vendor_header *const vhdr, + const image_header *const hdr, + secbool should_keep_seed, + secbool is_newvendor, + secbool is_newinstall, int version_cmp) { uint8_t fingerprint[32]; char ver_str[64]; get_image_fingerprint(hdr, fingerprint); @@ -171,7 +151,7 @@ void ui_screen_install_progress_upload(int pos) { // wipe UI -uint32_t ui_screen_wipe_confirm(void) { return screen_wipe_confirm(); } +ui_result_t ui_screen_wipe_confirm(void) { return screen_wipe_confirm(); } void ui_screen_wipe(void) { screen_wipe_progress(0, true); } diff --git a/core/embed/projects/bootloader/bootui.h b/core/embed/projects/bootloader/bootui.h index b3fc7e082a..8dd4c556d8 100644 --- a/core/embed/projects/bootloader/bootui.h +++ b/core/embed/projects/bootloader/bootui.h @@ -17,23 +17,32 @@ * along with this program. If not, see . */ -#ifndef __BOOTUI_H__ -#define __BOOTUI_H__ +#pragma once #include #include +// todo: use bindgen to tie this to rust typedef enum { - SCREEN_INTRO = 0, - SCREEN_MENU = 1, - SCREEN_WIPE_CONFIRM = 2, - SCREEN_FINGER_PRINT = 3, - SCREEN_WAIT_FOR_HOST = 4, - SCREEN_WELCOME = 5, -} screen_t; + UI_RESULT_CANCEL = 1, + UI_RESULT_CONFIRM = 2, +} ui_result_t; -// Displays a warning screeen before jumping to the untrusted firmware +// todo: use bindgen to tie this to rust +typedef enum { + MENU_EXIT = 0xAABBCCDD, + MENU_REBOOT = 0x11223344, + MENU_WIPE = 0x55667788, +} menu_result_t; + +// todo: use bindgen to tie this to rust +typedef enum { + INTRO_MENU = 1, + INTRO_HOST = 2, +} intro_result_t; + +// Displays a warning screen before jumping to the untrusted firmware // // Shows vendor image, vendor string and firmware version // and optional message to the user (see `wait` argument) @@ -58,16 +67,18 @@ uint32_t ui_screen_intro(const vendor_header* const vhdr, uint32_t ui_screen_menu(secbool firmware_present); -uint32_t ui_screen_install_confirm(const vendor_header* const vhdr, - const image_header* const hdr, - secbool shold_keep_seed, - secbool is_newvendor, secbool is_newinstall, - int version_cmp); +void ui_screen_connect(void); + +ui_result_t ui_screen_install_confirm(const vendor_header* const vhdr, + const image_header* const hdr, + secbool shold_keep_seed, + secbool is_newvendor, + secbool is_newinstall, int version_cmp); void ui_screen_install_start(); void ui_screen_install_progress_erase(int pos, int len); void ui_screen_install_progress_upload(int pos); -uint32_t ui_screen_wipe_confirm(void); +ui_result_t ui_screen_wipe_confirm(void); void ui_screen_wipe(void); void ui_screen_wipe_progress(int pos, int len); @@ -84,12 +95,3 @@ void ui_screen_boot_stage_1(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 -#define INPUT_LONG_CONFIRM 0x04 // Long Confirm button -#define INPUT_INFO 0x08 // Info icon -// clang-format on - -#endif diff --git a/core/embed/projects/bootloader/emulator.c b/core/embed/projects/bootloader/emulator.c index 8f46770f5a..0c9bbb8df4 100644 --- a/core/embed/projects/bootloader/emulator.c +++ b/core/embed/projects/bootloader/emulator.c @@ -8,11 +8,9 @@ #include #include #include -#include #include #include #include "bootui.h" -#include "rust_ui.h" #ifdef USE_OPTIGA #include @@ -188,10 +186,12 @@ int main(int argc, char **argv) { (void)!flash_otp_write(FLASH_OTP_BLOCK_DEVICE_VARIANT, 0, otp_data, sizeof(otp_data)); - bootloader_main(); - hal_delay(3000); - jump_to_next_stage(0); + int exit_code = bootloader_main(); + char msg[64]; + snprintf(msg, sizeof(msg), "Exit code: %d", exit_code); + + error_shutdown_ex("BOOTLOADER ERROR", msg, "UNEXPECTED EXIT"); return 0; } diff --git a/core/embed/projects/bootloader/emulator.h b/core/embed/projects/bootloader/emulator.h index 721490e912..e3d362cde4 100644 --- a/core/embed/projects/bootloader/emulator.h +++ b/core/embed/projects/bootloader/emulator.h @@ -1,10 +1,26 @@ -#ifndef __EMULATOR_H__ -#define __EMULATOR_H__ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once #include #undef FIRMWARE_START extern uint8_t *FIRMWARE_START; - -#endif diff --git a/core/embed/projects/bootloader/main.c b/core/embed/projects/bootloader/main.c index 3f9c896e58..b34924182e 100644 --- a/core/embed/projects/bootloader/main.c +++ b/core/embed/projects/bootloader/main.c @@ -20,27 +20,20 @@ #include #include -#include - -#include #include #include -#include #include #include #include #include -#include #include #include -#include -#include +#include #include #include #include #include #include -#include "messages.pb.h" #ifdef USE_PVD #include @@ -67,27 +60,16 @@ #include #endif -#include -#include "version.h" - +#include "antiglitch.h" #include "bootui.h" -#include "messages.h" -#include "rust_ui.h" #include "version_check.h" +#include "workflow/workflow.h" #ifdef TREZOR_EMULATOR #include "SDL.h" #include "emulator.h" #endif -#define USB_IFACE_NUM 0 - -typedef enum { - SHUTDOWN = 0, - CONTINUE_TO_FIRMWARE = 0xAABBCCDD, - RETURN_TO_MENU = 0x55667788, -} usb_result_t; - void failed_jump_to_firmware(void); CONFIDENTIAL volatile secbool dont_optimize_out_true = sectrue; @@ -154,158 +136,6 @@ static void drivers_deinit(void) { display_deinit(DISPLAY_JUMP_BEHAVIOR); } -static void usb_init_all(secbool usb21_landing) { - usb_dev_info_t dev_info = { - .device_class = 0x00, - .device_subclass = 0x00, - .device_protocol = 0x00, - .vendor_id = 0x1209, - .product_id = 0x53C0, - .release_num = 0x0200, - .manufacturer = MODEL_USB_MANUFACTURER, - .product = MODEL_USB_PRODUCT, - .serial_number = "000000000000000000000000", - .interface = "TREZOR Interface", - .usb21_enabled = sectrue, - .usb21_landing = usb21_landing, - }; - - static uint8_t rx_buffer[USB_PACKET_SIZE]; - - static const usb_webusb_info_t webusb_info = { - .iface_num = USB_IFACE_NUM, -#ifdef TREZOR_EMULATOR - .emu_port = 21324, -#else - .ep_in = 0x01, - .ep_out = 0x01, -#endif - .subclass = 0, - .protocol = 0, - .max_packet_len = sizeof(rx_buffer), - .rx_buffer = rx_buffer, - .polling_interval = 1, - }; - - ensure(usb_init(&dev_info), NULL); - - ensure(usb_webusb_add(&webusb_info), NULL); - - ensure(usb_start(), NULL); -} - -static usb_result_t bootloader_usb_loop(const vendor_header *const vhdr, - const image_header *const hdr) { - // if both are NULL, we don't have a firmware installed - // let's show a webusb landing page in this case - usb_init_all((vhdr == NULL && hdr == NULL) ? sectrue : secfalse); - - uint8_t buf[USB_PACKET_SIZE]; - - for (;;) { -#ifdef TREZOR_EMULATOR - // Ensures that SDL events are processed. This prevents the emulator from - // freezing when the user interacts with the window. - SDL_PumpEvents(); -#endif - int r = usb_webusb_read_blocking(USB_IFACE_NUM, buf, USB_PACKET_SIZE, - USB_TIMEOUT); - if (r != USB_PACKET_SIZE) { - continue; - } - uint16_t msg_id; - uint32_t msg_size; - uint32_t response; - if (sectrue != msg_parse_header(buf, &msg_id, &msg_size)) { - // invalid header -> discard - continue; - } - switch (msg_id) { - case MessageType_MessageType_Initialize: - process_msg_Initialize(USB_IFACE_NUM, msg_size, buf, vhdr, hdr); - break; - case MessageType_MessageType_Ping: - process_msg_Ping(USB_IFACE_NUM, msg_size, buf); - break; - case MessageType_MessageType_WipeDevice: - response = ui_screen_wipe_confirm(); - if (INPUT_CANCEL == response) { - send_user_abort(USB_IFACE_NUM, "Wipe cancelled"); - hal_delay(100); - usb_deinit(); - return RETURN_TO_MENU; - } - ui_screen_wipe(); - r = process_msg_WipeDevice(USB_IFACE_NUM, msg_size, buf); - if (r < 0) { // error - screen_wipe_fail(); - hal_delay(100); - usb_deinit(); - return SHUTDOWN; - } else { // success - screen_wipe_success(); - hal_delay(100); - usb_deinit(); - return SHUTDOWN; - } - break; - case MessageType_MessageType_FirmwareErase: - process_msg_FirmwareErase(USB_IFACE_NUM, msg_size, buf); - break; - 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 - if (r == UPLOAD_ERR_BOOTLOADER_LOCKED) { - // This function does not return - show_install_restricted_screen(); - } else { - ui_screen_fail(); - } - usb_deinit(); - return SHUTDOWN; - } else if (r == UPLOAD_ERR_USER_ABORT) { - hal_delay(100); - usb_deinit(); - return RETURN_TO_MENU; - } else if (r == 0) { // last chunk received - ui_screen_install_progress_upload(1000); - ui_screen_done(4, sectrue); - ui_screen_done(3, secfalse); - hal_delay(1000); - ui_screen_done(2, secfalse); - hal_delay(1000); - ui_screen_done(1, secfalse); - hal_delay(1000); - usb_deinit(); - return CONTINUE_TO_FIRMWARE; - } - break; - case MessageType_MessageType_GetFeatures: - process_msg_GetFeatures(USB_IFACE_NUM, msg_size, buf, vhdr, hdr); - break; -#if defined 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_deinit(); - return RETURN_TO_MENU; - } - process_msg_UnlockBootloader(USB_IFACE_NUM, msg_size, buf); - screen_unlock_bootloader_success(); - hal_delay(100); - usb_deinit(); - return SHUTDOWN; - break; -#endif - default: - process_msg_unknown(USB_IFACE_NUM, msg_size, buf); - break; - } - } -} - static secbool check_vendor_header_lock(const vendor_header *const vhdr) { uint8_t lock[FLASH_OTP_BLOCK_SIZE]; ensure(flash_otp_read(FLASH_OTP_BLOCK_VENDOR_HEADER_LOCK, 0, lock, @@ -400,13 +230,10 @@ void real_jump_to_firmware(void) { IMAGE_CODE_ALIGN(FIRMWARE_START + vhdr.hdrlen + IMAGE_HEADER_SIZE)); } -#ifdef USE_RESET_TO_BOOT __attribute__((noreturn)) void jump_to_fw_through_reset(void) { display_fade(display_get_backlight(), 0, 200); - reboot_device(); } -#endif #ifndef TREZOR_EMULATOR int main(void) { @@ -551,143 +378,47 @@ int bootloader_main(void) { // ... or there is no valid firmware if (touched || stay_in_bootloader == sectrue || firmware_present != sectrue || auto_upgrade == sectrue) { - screen_t screen; - ui_set_initial_setup(true); + workflow_result_t result; + + jump_reset(); if (header_present == sectrue) { if (auto_upgrade == sectrue) { - screen = SCREEN_WAIT_FOR_HOST; + result = workflow_auto_update(&vhdr, hdr); } else { - ui_set_initial_setup(false); - screen = SCREEN_INTRO; + result = workflow_bootloader(&vhdr, hdr, firmware_present); } - } else { - screen = SCREEN_WELCOME; - -#ifdef USE_STORAGE_HWKEY - secret_bhk_regenerate(); -#endif - ensure(erase_storage(NULL), NULL); - - // keep the model screen up for a while -#ifndef USE_BACKLIGHT - hal_delay(1500); -#else - // backlight fading takes some time so the explicit delay here is - // shorter - hal_delay(1000); -#endif + result = workflow_empty_device(); } - while (true) { - volatile secbool continue_to_firmware = secfalse; - volatile secbool continue_to_firmware_backup = secfalse; - uint32_t ui_result = 0; + switch (result) { + case WF_OK_FIRMWARE_INSTALLED: + firmware_present = sectrue; + firmware_present_backup = sectrue; + case WF_OK_REBOOT_SELECTED: + ensure(dont_optimize_out_true * + (jump_is_allowed_1() == jump_is_allowed_2()), + NULL); - switch (screen) { - case SCREEN_WELCOME: - - ui_screen_welcome(); - - // and start the usb loop - switch (bootloader_usb_loop(NULL, NULL)) { - case CONTINUE_TO_FIRMWARE: - continue_to_firmware = sectrue; - continue_to_firmware_backup = sectrue; - break; - case RETURN_TO_MENU: - break; - default: - case SHUTDOWN: - return 1; - break; - } - break; - - case SCREEN_INTRO: - ui_result = ui_screen_intro(&vhdr, hdr, firmware_present); - if (ui_result == 1) { - screen = SCREEN_MENU; - } - if (ui_result == 2) { - screen = SCREEN_WAIT_FOR_HOST; - } - break; - case SCREEN_MENU: - ui_result = ui_screen_menu(firmware_present); - if (ui_result == 0xAABBCCDD) { // exit menu - screen = SCREEN_INTRO; - } - if (ui_result == 0x11223344) { // reboot -#ifndef USE_HASH_PROCESSOR - ui_screen_boot_stage_1(true); -#endif - continue_to_firmware = firmware_present; - continue_to_firmware_backup = firmware_present_backup; - } - if (ui_result == 0x55667788) { // wipe - screen = SCREEN_WIPE_CONFIRM; - } - break; - case SCREEN_WIPE_CONFIRM: - ui_result = screen_wipe_confirm(); - if (ui_result == INPUT_CANCEL) { - // canceled - screen = SCREEN_MENU; - } - if (ui_result == INPUT_CONFIRM) { - ui_screen_wipe(); - secbool r = bootloader_WipeDevice(); - if (r != sectrue) { // error - screen_wipe_fail(); - return 1; - } else { // success - screen_wipe_success(); - return 1; - } - } - break; - case SCREEN_WAIT_FOR_HOST: - screen_connect(auto_upgrade == sectrue); - switch (bootloader_usb_loop(&vhdr, hdr)) { - case CONTINUE_TO_FIRMWARE: - continue_to_firmware = sectrue; - continue_to_firmware_backup = sectrue; - break; - case RETURN_TO_MENU: - screen = SCREEN_INTRO; - break; - case SHUTDOWN: - return 1; - break; - default: - break; - } - break; - default: - break; - } - - if (continue_to_firmware != continue_to_firmware_backup) { + ensure(dont_optimize_out_true * + (firmware_present == firmware_present_backup), + NULL); + jump_to_fw_through_reset(); + break; + case WF_OK_DEVICE_WIPED: + case WF_OK_BOOTLOADER_UNLOCKED: + case WF_ERROR: + reboot_or_halt_after_rsod(); + return 0; + case WF_ERROR_FATAL: + default: { // erase storage if we saw flips randomly flip, most likely due to // glitch - #ifdef USE_STORAGE_HWKEY secret_bhk_regenerate(); #endif ensure(erase_storage(NULL), NULL); - } - ensure(dont_optimize_out_true * - (continue_to_firmware == continue_to_firmware_backup), - NULL); - if (sectrue == continue_to_firmware) { -#ifdef USE_RESET_TO_BOOT - firmware_jump_fn = jump_to_fw_through_reset; -#else - ui_screen_boot_stage_1(true); - firmware_jump_fn = real_jump_to_firmware; -#endif - break; + error_shutdown("Bootloader fatal error"); } } } @@ -695,16 +426,9 @@ int bootloader_main(void) { ensure(dont_optimize_out_true * (firmware_present == firmware_present_backup), NULL); -#ifdef USE_RESET_TO_BOOT - if (sectrue == firmware_present && - firmware_jump_fn != jump_to_fw_through_reset) { - firmware_jump_fn = real_jump_to_firmware; - } -#else if (sectrue == firmware_present) { firmware_jump_fn = real_jump_to_firmware; } -#endif firmware_jump_fn(); diff --git a/core/embed/projects/bootloader/messages.c b/core/embed/projects/bootloader/messages.c deleted file mode 100644 index f1371721d8..0000000000 --- a/core/embed/projects/bootloader/messages.c +++ /dev/null @@ -1,884 +0,0 @@ -/* - * This file is part of the Trezor project, https://trezor.io/ - * - * Copyright (c) SatoshiLabs - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include - -#include -#include -#include -#include "messages.pb.h" - -#include -#include -#include -#include -#include -#include -#include -#include "version.h" - -#include "bootui.h" -#include "messages.h" -#include "rust_ui.h" -#include "version_check.h" - -#include "memzero.h" - -#ifdef TREZOR_EMULATOR -#include "emulator.h" -#endif - -#if USE_OPTIGA -#include -#endif - -#define MSG_HEADER1_LEN 9 -#define MSG_HEADER2_LEN 1 - -secbool msg_parse_header(const uint8_t *buf, uint16_t *msg_id, - uint32_t *msg_size) { - if (buf[0] != '?' || buf[1] != '#' || buf[2] != '#') { - return secfalse; - } - *msg_id = (buf[3] << 8) + buf[4]; - *msg_size = (buf[5] << 24) + (buf[6] << 16) + (buf[7] << 8) + buf[8]; - return sectrue; -} - -typedef struct { - uint8_t iface_num; - uint8_t packet_index; - uint8_t packet_pos; - uint8_t buf[USB_PACKET_SIZE]; -} usb_write_state; - -/* we don't use secbool/sectrue/secfalse here as it is a nanopb api */ -static bool _usb_write(pb_ostream_t *stream, const pb_byte_t *buf, - size_t count) { - usb_write_state *state = (usb_write_state *)(stream->state); - - size_t written = 0; - // while we have data left - while (written < count) { - size_t remaining = count - written; - // if all remaining data fit into our packet - if (state->packet_pos + remaining <= USB_PACKET_SIZE) { - // append data from buf to state->buf - memcpy(state->buf + state->packet_pos, buf + written, remaining); - // advance position - state->packet_pos += remaining; - // and return - return true; - } else { - // append data that fits - memcpy(state->buf + state->packet_pos, buf + written, - USB_PACKET_SIZE - state->packet_pos); - written += USB_PACKET_SIZE - state->packet_pos; - // send packet - int r = usb_webusb_write_blocking(state->iface_num, state->buf, - USB_PACKET_SIZE, USB_TIMEOUT); - ensure(sectrue * (r == USB_PACKET_SIZE), NULL); - // prepare new packet - state->packet_index++; - memzero(state->buf, USB_PACKET_SIZE); - state->buf[0] = '?'; - state->packet_pos = MSG_HEADER2_LEN; - } - } - - return true; -} - -static void _usb_write_flush(usb_write_state *state) { - // if packet is not filled up completely - if (state->packet_pos < USB_PACKET_SIZE) { - // pad it with zeroes - memzero(state->buf + state->packet_pos, - USB_PACKET_SIZE - state->packet_pos); - } - // send packet - int r = usb_webusb_write_blocking(state->iface_num, state->buf, - USB_PACKET_SIZE, USB_TIMEOUT); - ensure(sectrue * (r == USB_PACKET_SIZE), NULL); -} - -static secbool _send_msg(uint8_t iface_num, uint16_t msg_id, - const pb_msgdesc_t *fields, const void *msg) { - // determine message size by serializing it into a dummy stream - pb_ostream_t sizestream = {.callback = NULL, - .state = NULL, - .max_size = SIZE_MAX, - .bytes_written = 0, - .errmsg = NULL}; - if (false == pb_encode(&sizestream, fields, msg)) { - return secfalse; - } - const uint32_t msg_size = sizestream.bytes_written; - - usb_write_state state = { - .iface_num = iface_num, - .packet_index = 0, - .packet_pos = MSG_HEADER1_LEN, - .buf = - { - '?', - '#', - '#', - (msg_id >> 8) & 0xFF, - msg_id & 0xFF, - (msg_size >> 24) & 0xFF, - (msg_size >> 16) & 0xFF, - (msg_size >> 8) & 0xFF, - msg_size & 0xFF, - }, - }; - - pb_ostream_t stream = {.callback = &_usb_write, - .state = &state, - .max_size = SIZE_MAX, - .bytes_written = 0, - .errmsg = NULL}; - - if (false == pb_encode(&stream, fields, msg)) { - return secfalse; - } - - _usb_write_flush(&state); - - return sectrue; -} - -#define MSG_SEND_INIT(TYPE) TYPE msg_send = TYPE##_init_default -#define MSG_SEND_ASSIGN_REQUIRED_VALUE(FIELD, VALUE) \ - { msg_send.FIELD = VALUE; } -#define MSG_SEND_ASSIGN_VALUE(FIELD, VALUE) \ - { \ - msg_send.has_##FIELD = true; \ - msg_send.FIELD = VALUE; \ - } -#define MSG_SEND_ASSIGN_STRING(FIELD, VALUE) \ - { \ - msg_send.has_##FIELD = true; \ - memzero(msg_send.FIELD, sizeof(msg_send.FIELD)); \ - strncpy(msg_send.FIELD, VALUE, sizeof(msg_send.FIELD) - 1); \ - } -#define MSG_SEND_ASSIGN_STRING_LEN(FIELD, VALUE, LEN) \ - { \ - msg_send.has_##FIELD = true; \ - memzero(msg_send.FIELD, sizeof(msg_send.FIELD)); \ - strncpy(msg_send.FIELD, VALUE, MIN(LEN, sizeof(msg_send.FIELD) - 1)); \ - } -#define MSG_SEND_ASSIGN_BYTES(FIELD, VALUE, LEN) \ - { \ - msg_send.has_##FIELD = true; \ - memzero(msg_send.FIELD.bytes, sizeof(msg_send.FIELD.bytes)); \ - memcpy(msg_send.FIELD.bytes, VALUE, \ - MIN(LEN, sizeof(msg_send.FIELD.bytes))); \ - msg_send.FIELD.size = MIN(LEN, sizeof(msg_send.FIELD.bytes)); \ - } -#define MSG_SEND(TYPE) \ - _send_msg(iface_num, MessageType_MessageType_##TYPE, TYPE##_fields, &msg_send) - -typedef struct { - uint8_t iface_num; - uint8_t packet_index; - uint8_t packet_pos; - uint8_t *buf; -} usb_read_state; - -static void _usb_webusb_read_retry(uint8_t iface_num, uint8_t *buf) { - for (int retry = 0;; retry++) { - int r = - usb_webusb_read_blocking(iface_num, buf, USB_PACKET_SIZE, USB_TIMEOUT); - if (r != USB_PACKET_SIZE) { // reading failed - if (r == 0 && retry < 10) { - // only timeout => let's try again - continue; - } else { - // error - error_shutdown_ex("USB ERROR", - "Error reading from USB. Try different USB cable.", - NULL); - } - } - return; // success - } -} - -/* we don't use secbool/sectrue/secfalse here as it is a nanopb api */ -static bool _usb_read(pb_istream_t *stream, uint8_t *buf, size_t count) { - usb_read_state *state = (usb_read_state *)(stream->state); - - size_t read = 0; - // while we have data left - while (read < count) { - size_t remaining = count - read; - // if all remaining data fit into our packet - if (state->packet_pos + remaining <= USB_PACKET_SIZE) { - // append data from buf to state->buf - memcpy(buf + read, state->buf + state->packet_pos, remaining); - // advance position - state->packet_pos += remaining; - // and return - return true; - } else { - // append data that fits - memcpy(buf + read, state->buf + state->packet_pos, - USB_PACKET_SIZE - state->packet_pos); - read += USB_PACKET_SIZE - state->packet_pos; - // read next packet (with retry) - _usb_webusb_read_retry(state->iface_num, state->buf); - // prepare next packet - state->packet_index++; - state->packet_pos = MSG_HEADER2_LEN; - } - } - - return true; -} - -static void _usb_read_flush(usb_read_state *state) { (void)state; } - -static secbool _recv_msg(uint8_t iface_num, uint32_t msg_size, uint8_t *buf, - const pb_msgdesc_t *fields, void *msg) { - usb_read_state state = {.iface_num = iface_num, - .packet_index = 0, - .packet_pos = MSG_HEADER1_LEN, - .buf = buf}; - - pb_istream_t stream = {.callback = &_usb_read, - .state = &state, - .bytes_left = msg_size, - .errmsg = NULL}; - - if (false == pb_decode_noinit(&stream, fields, msg)) { - return secfalse; - } - - _usb_read_flush(&state); - - return sectrue; -} - -#define MSG_RECV_INIT(TYPE) TYPE msg_recv = TYPE##_init_default -#define MSG_RECV_CALLBACK(FIELD, CALLBACK, ARGUMENT) \ - { \ - msg_recv.FIELD.funcs.decode = &CALLBACK; \ - msg_recv.FIELD.arg = (void *)ARGUMENT; \ - } -#define MSG_RECV(TYPE) \ - _recv_msg(iface_num, msg_size, buf, TYPE##_fields, &msg_recv) - -void send_user_abort(uint8_t iface_num, const char *msg) { - MSG_SEND_INIT(Failure); - MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ActionCancelled); - MSG_SEND_ASSIGN_STRING(message, msg); - MSG_SEND(Failure); -} - -static void send_msg_features(uint8_t iface_num, - const vendor_header *const vhdr, - const image_header *const hdr) { - MSG_SEND_INIT(Features); - MSG_SEND_ASSIGN_STRING(vendor, "trezor.io"); - MSG_SEND_ASSIGN_REQUIRED_VALUE(major_version, VERSION_MAJOR); - MSG_SEND_ASSIGN_REQUIRED_VALUE(minor_version, VERSION_MINOR); - MSG_SEND_ASSIGN_REQUIRED_VALUE(patch_version, VERSION_PATCH); - MSG_SEND_ASSIGN_VALUE(bootloader_mode, true); - MSG_SEND_ASSIGN_STRING(model, MODEL_NAME); - MSG_SEND_ASSIGN_STRING(internal_model, MODEL_INTERNAL_NAME); - if (vhdr && hdr) { - MSG_SEND_ASSIGN_VALUE(firmware_present, true); - MSG_SEND_ASSIGN_VALUE(fw_major, (hdr->version & 0xFF)); - MSG_SEND_ASSIGN_VALUE(fw_minor, ((hdr->version >> 8) & 0xFF)); - MSG_SEND_ASSIGN_VALUE(fw_patch, ((hdr->version >> 16) & 0xFF)); - MSG_SEND_ASSIGN_STRING_LEN(fw_vendor, vhdr->vstr, vhdr->vstr_len); - } else { - MSG_SEND_ASSIGN_VALUE(firmware_present, false); - } - if (unit_properties()->color_is_valid) { - MSG_SEND_ASSIGN_VALUE(unit_color, unit_properties()->color); - } - if (unit_properties()->packaging_is_valid) { - MSG_SEND_ASSIGN_VALUE(unit_packaging, unit_properties()->packaging); - } - if (unit_properties()->btconly_is_valid) { - MSG_SEND_ASSIGN_VALUE(unit_btconly, unit_properties()->btconly); - } - -#if USE_OPTIGA - MSG_SEND_ASSIGN_VALUE(bootloader_locked, - (secret_bootloader_locked() == sectrue)); -#endif - MSG_SEND(Features); -} - -void process_msg_Initialize(uint8_t iface_num, uint32_t msg_size, uint8_t *buf, - const vendor_header *const vhdr, - const image_header *const hdr) { - MSG_RECV_INIT(Initialize); - MSG_RECV(Initialize); - send_msg_features(iface_num, vhdr, hdr); -} - -void process_msg_GetFeatures(uint8_t iface_num, uint32_t msg_size, uint8_t *buf, - const vendor_header *const vhdr, - const image_header *const hdr) { - MSG_RECV_INIT(GetFeatures); - MSG_RECV(GetFeatures); - send_msg_features(iface_num, vhdr, hdr); -} - -void process_msg_Ping(uint8_t iface_num, uint32_t msg_size, uint8_t *buf) { - MSG_RECV_INIT(Ping); - MSG_RECV(Ping); - - MSG_SEND_INIT(Success); - MSG_SEND_ASSIGN_STRING(message, msg_recv.message); - MSG_SEND(Success); -} - -static uint32_t firmware_remaining; -static uint32_t firmware_block; -static uint32_t chunk_requested; -static uint32_t erase_offset; - -void process_msg_FirmwareErase(uint8_t iface_num, uint32_t msg_size, - uint8_t *buf) { - firmware_remaining = 0; - firmware_block = 0; - chunk_requested = 0; - erase_offset = 0; - - MSG_RECV_INIT(FirmwareErase); - MSG_RECV(FirmwareErase); - - firmware_remaining = msg_recv.has_length ? msg_recv.length : 0; - if ((firmware_remaining > 0) && - ((firmware_remaining % sizeof(uint32_t)) == 0) && - (firmware_remaining <= FIRMWARE_MAXSIZE)) { - // request new firmware - chunk_requested = (firmware_remaining > IMAGE_INIT_CHUNK_SIZE) - ? IMAGE_INIT_CHUNK_SIZE - : firmware_remaining; - MSG_SEND_INIT(FirmwareRequest); - MSG_SEND_ASSIGN_REQUIRED_VALUE(offset, 0); - MSG_SEND_ASSIGN_REQUIRED_VALUE(length, chunk_requested); - MSG_SEND(FirmwareRequest); - } else { - // invalid firmware size - MSG_SEND_INIT(Failure); - MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); - MSG_SEND_ASSIGN_STRING(message, "Wrong firmware size"); - MSG_SEND(Failure); - } -} - -static uint32_t chunk_size = 0; - -#ifndef TREZOR_EMULATOR -__attribute__((section(".buf"))) -#endif -uint32_t chunk_buffer[IMAGE_CHUNK_SIZE / 4]; - -#define CHUNK_BUFFER_PTR ((const uint8_t *const)&chunk_buffer) - -/* we don't use secbool/sectrue/secfalse here as it is a nanopb api */ -static bool _read_payload(pb_istream_t *stream, const pb_field_t *field, - void **arg) { -#define BUFSIZE 32768 - - size_t offset = (size_t)(*arg); - - if (stream->bytes_left > IMAGE_CHUNK_SIZE) { - chunk_size = 0; - return false; - } - - if (offset == 0) { - // clear chunk buffer - memset((uint8_t *)&chunk_buffer, 0xFF, IMAGE_CHUNK_SIZE); - } - - uint32_t chunk_written = offset; - chunk_size = offset + stream->bytes_left; - - while (stream->bytes_left) { - // update loader but skip first block - if (firmware_block > 0) { - ui_screen_install_progress_upload( - 1000 * (firmware_block * IMAGE_CHUNK_SIZE + chunk_written) / - (firmware_block * IMAGE_CHUNK_SIZE + firmware_remaining)); - } - // read data - if (!pb_read( - stream, (pb_byte_t *)(CHUNK_BUFFER_PTR + chunk_written), - (stream->bytes_left > BUFSIZE) ? BUFSIZE : stream->bytes_left)) { - chunk_size = 0; - return false; - } - chunk_written += BUFSIZE; - } - - return true; -} - -static int version_compare(uint32_t vera, uint32_t verb) { - /* Explicit casts so that we control how compiler does the unsigned shift - * and correctly then promote uint8_t to int without possibility of - * having implementation-defined right shift on negative int - * in case compiler promoted the wrong unsinged int - */ - int a, b; - a = (uint8_t)vera & 0xFF; - b = (uint8_t)verb & 0xFF; - if (a != b) return a - b; - a = (uint8_t)(vera >> 8) & 0xFF; - b = (uint8_t)(verb >> 8) & 0xFF; - if (a != b) return a - b; - a = (uint8_t)(vera >> 16) & 0xFF; - b = (uint8_t)(verb >> 16) & 0xFF; - if (a != b) return a - b; - a = (uint8_t)(vera >> 24) & 0xFF; - b = (uint8_t)(verb >> 24) & 0xFF; - return a - b; -} - -static void detect_installation(const vendor_header *current_vhdr, - const image_header *current_hdr, - const vendor_header *const new_vhdr, - const image_header *const new_hdr, - secbool *is_new, secbool *keep_seed, - secbool *is_newvendor, secbool *is_upgrade) { - *is_new = secfalse; - *keep_seed = secfalse; - *is_newvendor = secfalse; - *is_upgrade = secfalse; - if (sectrue != check_vendor_header_keys(current_vhdr)) { - *is_new = sectrue; - return; - } - if (sectrue != check_image_model(current_hdr)) { - *is_new = sectrue; - return; - } - if (sectrue != check_firmware_min_version(current_hdr->monotonic)) { - *is_new = sectrue; - return; - } - if (sectrue != check_image_header_sig(current_hdr, current_vhdr->vsig_m, - current_vhdr->vsig_n, - current_vhdr->vpub)) { - *is_new = sectrue; - return; - } - uint8_t hash1[32], hash2[32]; - vendor_header_hash(new_vhdr, hash1); - vendor_header_hash(current_vhdr, hash2); - if (0 != memcmp(hash1, hash2, 32)) { - *is_newvendor = sectrue; - return; - } - if (version_compare(new_hdr->version, current_hdr->fix_version) < 0) { - return; - } - if (version_compare(new_hdr->version, current_hdr->version) > 0) { - *is_upgrade = sectrue; - } - - *keep_seed = sectrue; -} - -static int firmware_upload_chunk_retry = FIRMWARE_UPLOAD_CHUNK_RETRY_COUNT; -static size_t headers_offset = 0; -static size_t read_offset = 0; - -int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size, - uint8_t *buf) { - MSG_RECV_INIT(FirmwareUpload); - MSG_RECV_CALLBACK(payload, _read_payload, read_offset); - const secbool r = MSG_RECV(FirmwareUpload); - - if (sectrue != r || chunk_size != (chunk_requested + read_offset)) { - MSG_SEND_INIT(Failure); - MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); - MSG_SEND_ASSIGN_STRING(message, "Invalid chunk size"); - MSG_SEND(Failure); - return UPLOAD_ERR_INVALID_CHUNK_SIZE; - } - - static image_header hdr; - - if (firmware_block == 0) { - if (headers_offset == 0) { - // first block and headers are not yet parsed - vendor_header vhdr; - - if (sectrue != read_vendor_header(CHUNK_BUFFER_PTR, &vhdr)) { - MSG_SEND_INIT(Failure); - MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); - MSG_SEND_ASSIGN_STRING(message, "Invalid vendor header"); - MSG_SEND(Failure); - return UPLOAD_ERR_INVALID_VENDOR_HEADER; - } - - if (sectrue != check_vendor_header_model(&vhdr)) { - MSG_SEND_INIT(Failure); - MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); - MSG_SEND_ASSIGN_STRING(message, "Wrong model"); - MSG_SEND(Failure); - return UPLOAD_ERR_INVALID_VENDOR_HEADER_MODEL; - } - - if (sectrue != check_vendor_header_keys(&vhdr)) { - MSG_SEND_INIT(Failure); - MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); - MSG_SEND_ASSIGN_STRING(message, "Invalid vendor header signature"); - MSG_SEND(Failure); - return UPLOAD_ERR_INVALID_VENDOR_HEADER_SIG; - } - - const image_header *received_hdr = - read_image_header(CHUNK_BUFFER_PTR + vhdr.hdrlen, - FIRMWARE_IMAGE_MAGIC, FIRMWARE_MAXSIZE); - - if (received_hdr != - (const image_header *)(CHUNK_BUFFER_PTR + vhdr.hdrlen)) { - MSG_SEND_INIT(Failure); - MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); - MSG_SEND_ASSIGN_STRING(message, "Invalid firmware header"); - MSG_SEND(Failure); - return UPLOAD_ERR_INVALID_IMAGE_HEADER; - } - - if (sectrue != check_image_model(received_hdr)) { - MSG_SEND_INIT(Failure); - MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); - MSG_SEND_ASSIGN_STRING(message, "Wrong firmware model"); - MSG_SEND(Failure); - return UPLOAD_ERR_INVALID_IMAGE_MODEL; - } - - if (sectrue != check_image_header_sig(received_hdr, vhdr.vsig_m, - vhdr.vsig_n, vhdr.vpub)) { - MSG_SEND_INIT(Failure); - MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); - MSG_SEND_ASSIGN_STRING(message, "Invalid firmware signature"); - MSG_SEND(Failure); - return UPLOAD_ERR_INVALID_IMAGE_HEADER_SIG; - } - - if (sectrue != check_firmware_min_version(received_hdr->monotonic)) { - MSG_SEND_INIT(Failure); - MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); - MSG_SEND_ASSIGN_STRING(message, "Cannot downgrade to this version"); - MSG_SEND(Failure); - return UPLOAD_ERR_INVALID_IMAGE_HEADER_VERSION; - } - - memcpy(&hdr, received_hdr, sizeof(hdr)); - - vendor_header current_vhdr; - - secbool is_new = secfalse; - - if (sectrue != - read_vendor_header((const uint8_t *)FIRMWARE_START, ¤t_vhdr)) { - is_new = sectrue; - } - - const image_header *current_hdr = NULL; - - if (is_new == secfalse) { - current_hdr = read_image_header( - (const uint8_t *)FIRMWARE_START + current_vhdr.hdrlen, - FIRMWARE_IMAGE_MAGIC, FIRMWARE_MAXSIZE); - - if (current_hdr != - (const image_header *)(FIRMWARE_START + current_vhdr.hdrlen)) { - is_new = sectrue; - } - } - - secbool should_keep_seed = secfalse; - secbool is_newvendor = secfalse; - secbool is_upgrade = secfalse; - if (is_new == secfalse) { - detect_installation(¤t_vhdr, current_hdr, &vhdr, &hdr, &is_new, - &should_keep_seed, &is_newvendor, &is_upgrade); - } - - secbool is_ilu = secfalse; // interaction-less update - - if (bootargs_get_command() == BOOT_COMMAND_INSTALL_UPGRADE) { - IMAGE_HASH_CTX ctx; - uint8_t hash[IMAGE_HASH_DIGEST_LENGTH]; - IMAGE_HASH_INIT(&ctx); - IMAGE_HASH_UPDATE(&ctx, CHUNK_BUFFER_PTR, - vhdr.hdrlen + received_hdr->hdrlen); - IMAGE_HASH_FINAL(&ctx, hash); - - // the firmware must be the same as confirmed by the user - boot_args_t args = {0}; - bootargs_get_args(&args); - - if (memcmp(args.hash, hash, sizeof(hash)) != 0) { - MSG_SEND_INIT(Failure); - MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); - MSG_SEND_ASSIGN_STRING(message, "Firmware mismatch"); - MSG_SEND(Failure); - return UPLOAD_ERR_FIRMWARE_MISMATCH; - } - - // the firmware must be from the same vendor - // the firmware must be newer - if (is_upgrade != sectrue || is_newvendor != secfalse) { - MSG_SEND_INIT(Failure); - MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); - MSG_SEND_ASSIGN_STRING(message, "Not a firmware upgrade"); - MSG_SEND(Failure); - return UPLOAD_ERR_NOT_FIRMWARE_UPGRADE; - } - - if ((vhdr.vtrust & VTRUST_NO_WARNING) != VTRUST_NO_WARNING) { - MSG_SEND_INIT(Failure); - MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); - MSG_SEND_ASSIGN_STRING(message, "Not a full-trust image"); - MSG_SEND(Failure); - return UPLOAD_ERR_NOT_FULLTRUST_IMAGE; - } - - // upload the firmware without confirmation - is_ilu = sectrue; - } - -#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); - MSG_SEND_ASSIGN_STRING(message, "Install restricted"); - MSG_SEND(Failure); - return UPLOAD_ERR_BOOTLOADER_LOCKED; - } -#endif - - uint32_t response = INPUT_CANCEL; - if (((vhdr.vtrust & VTRUST_NO_WARNING) == VTRUST_NO_WARNING) && - (sectrue == is_new || sectrue == is_ilu)) { - // new installation or interaction less updated - auto confirm - // only allowed for full-trust images - response = INPUT_CONFIRM; - } else { - if (sectrue != is_new) { - int version_cmp = version_compare(hdr.version, current_hdr->version); - response = ui_screen_install_confirm( - &vhdr, &hdr, should_keep_seed, is_newvendor, is_new, version_cmp); - } else { - response = ui_screen_install_confirm(&vhdr, &hdr, sectrue, - is_newvendor, is_new, 0); - } - } - - if (INPUT_CANCEL == response) { - send_user_abort(iface_num, "Firmware install cancelled"); - return UPLOAD_ERR_USER_ABORT; - } - - ui_screen_install_start(); - - // if firmware is not upgrade, erase storage - if (sectrue != should_keep_seed) { -#ifdef USE_STORAGE_HWKEY - secret_bhk_regenerate(); -#endif - ensure(erase_storage(NULL), NULL); - } - - headers_offset = IMAGE_HEADER_SIZE + vhdr.hdrlen; - read_offset = IMAGE_INIT_CHUNK_SIZE; - - // request the rest of the first chunk - MSG_SEND_INIT(FirmwareRequest); - uint32_t chunk_limit = (firmware_remaining > IMAGE_CHUNK_SIZE) - ? IMAGE_CHUNK_SIZE - : firmware_remaining; - chunk_requested = chunk_limit - read_offset; - MSG_SEND_ASSIGN_REQUIRED_VALUE(offset, read_offset); - MSG_SEND_ASSIGN_REQUIRED_VALUE(length, chunk_requested); - MSG_SEND(FirmwareRequest); - - firmware_remaining -= read_offset; - return (int)firmware_remaining; - } else { - // first block with the headers parsed -> the first chunk is now complete - read_offset = 0; - } - } - - // should not happen, but double-check - if (flash_area_get_address(&FIRMWARE_AREA, firmware_block * IMAGE_CHUNK_SIZE, - 0) == NULL) { - MSG_SEND_INIT(Failure); - MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); - MSG_SEND_ASSIGN_STRING(message, "Firmware too big"); - MSG_SEND(Failure); - return UPLOAD_ERR_FIRMWARE_TOO_BIG; - } - - if (sectrue != check_single_hash(hdr.hashes + firmware_block * 32, - CHUNK_BUFFER_PTR + headers_offset, - chunk_size - headers_offset)) { - if (firmware_upload_chunk_retry > 0) { - --firmware_upload_chunk_retry; - MSG_SEND_INIT(FirmwareRequest); - MSG_SEND_ASSIGN_REQUIRED_VALUE(offset, firmware_block * IMAGE_CHUNK_SIZE); - MSG_SEND_ASSIGN_REQUIRED_VALUE(length, chunk_requested); - MSG_SEND(FirmwareRequest); - return (int)firmware_remaining; - } - - MSG_SEND_INIT(Failure); - MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); - MSG_SEND_ASSIGN_STRING(message, "Invalid chunk hash"); - MSG_SEND(Failure); - return UPLOAD_ERR_INVALID_CHUNK_HASH; - } - - // buffer with the received data - const uint32_t *src = (const uint32_t *)CHUNK_BUFFER_PTR; - // number of received bytes - uint32_t bytes_remaining = chunk_size; - // offset into the FIRMWARE_AREA part of the flash - uint32_t write_offset = firmware_block * IMAGE_CHUNK_SIZE; - - ensure((chunk_size % FLASH_BLOCK_SIZE == 0) * sectrue, NULL); - - while (bytes_remaining > 0) { - // erase flash before writing - uint32_t bytes_erased = 0; - - if (write_offset >= erase_offset) { - // erase the next flash section - ensure( - flash_area_erase_partial(&FIRMWARE_AREA, erase_offset, &bytes_erased), - NULL); - erase_offset += bytes_erased; - } else { - // some erased space left from the previous round => use it - bytes_erased = erase_offset - write_offset; - } - - // write the received data - uint32_t bytes_to_write = MIN(bytes_erased, bytes_remaining); - ensure(flash_unlock_write(), NULL); - ensure(flash_area_write_data(&FIRMWARE_AREA, write_offset, src, - bytes_to_write), - NULL); - ensure(flash_lock_write(), NULL); - - write_offset += bytes_to_write; - src += bytes_to_write / sizeof(uint32_t); - - bytes_remaining -= bytes_to_write; - } - - firmware_remaining -= chunk_requested; - - if (firmware_remaining == 0) { - // erase the rest (unused part) of the FIRMWARE_AREA - uint32_t bytes_erased = 0; - do { - ensure( - flash_area_erase_partial(&FIRMWARE_AREA, erase_offset, &bytes_erased), - NULL); - erase_offset += bytes_erased; - } while (bytes_erased > 0); - } - - headers_offset = 0; - firmware_block++; - firmware_upload_chunk_retry = FIRMWARE_UPLOAD_CHUNK_RETRY_COUNT; - - if (firmware_remaining > 0) { - chunk_requested = (firmware_remaining > IMAGE_CHUNK_SIZE) - ? IMAGE_CHUNK_SIZE - : firmware_remaining; - MSG_SEND_INIT(FirmwareRequest); - MSG_SEND_ASSIGN_REQUIRED_VALUE(offset, firmware_block * IMAGE_CHUNK_SIZE); - MSG_SEND_ASSIGN_REQUIRED_VALUE(length, chunk_requested); - MSG_SEND(FirmwareRequest); - } else { - MSG_SEND_INIT(Success); - MSG_SEND(Success); - } - return (int)firmware_remaining; -} - -secbool bootloader_WipeDevice(void) { - return erase_device(ui_screen_wipe_progress); -} - -int process_msg_WipeDevice(uint8_t iface_num, uint32_t msg_size, uint8_t *buf) { - secbool wipe_result = bootloader_WipeDevice(); - if (sectrue != wipe_result) { - MSG_SEND_INIT(Failure); - MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError); - MSG_SEND_ASSIGN_STRING(message, "Could not erase flash"); - MSG_SEND(Failure); - return WIPE_ERR_CANNOT_ERASE; - } else { - MSG_SEND_INIT(Success); - MSG_SEND(Success); - return WIPE_OK; - } -} - -void process_msg_unknown(uint8_t iface_num, uint32_t msg_size, uint8_t *buf) { - // consume remaining message - int remaining_chunks = 0; - - if (msg_size > (USB_PACKET_SIZE - MSG_HEADER1_LEN)) { - // calculate how many blocks need to be read to drain the message (rounded - // up to not leave any behind) - remaining_chunks = (msg_size - (USB_PACKET_SIZE - MSG_HEADER1_LEN) + - ((USB_PACKET_SIZE - MSG_HEADER2_LEN) - 1)) / - (USB_PACKET_SIZE - MSG_HEADER2_LEN); - } - - for (int i = 0; i < remaining_chunks; i++) { - // read next packet (with retry) - _usb_webusb_read_retry(iface_num, buf); - } - - MSG_SEND_INIT(Failure); - MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_UnexpectedMessage); - MSG_SEND_ASSIGN_STRING(message, "Unexpected message"); - MSG_SEND(Failure); -} - -#if defined USE_OPTIGA -void process_msg_UnlockBootloader(uint8_t iface_num, uint32_t msg_size, - uint8_t *buf) { - secret_optiga_erase(); - MSG_SEND_INIT(Success); - MSG_SEND(Success); -} -#endif diff --git a/core/embed/projects/bootloader/messages.h b/core/embed/projects/bootloader/messages.h deleted file mode 100644 index 5623ff3b5f..0000000000 --- a/core/embed/projects/bootloader/messages.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * This file is part of the Trezor project, https://trezor.io/ - * - * Copyright (c) SatoshiLabs - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef __MESSAGES_H__ -#define __MESSAGES_H__ - -#include - -#include - -#define USB_TIMEOUT 500 -#define USB_PACKET_SIZE 64 - -#define FIRMWARE_UPLOAD_CHUNK_RETRY_COUNT 2 - -enum { - UPLOAD_OK = 0, - UPLOAD_ERR_INVALID_CHUNK_SIZE = -1, - UPLOAD_ERR_INVALID_VENDOR_HEADER = -2, - UPLOAD_ERR_INVALID_VENDOR_HEADER_SIG = -3, - UPLOAD_ERR_INVALID_VENDOR_HEADER_MODEL = -15, - UPLOAD_ERR_INVALID_IMAGE_HEADER = -4, - UPLOAD_ERR_INVALID_IMAGE_MODEL = -5, - UPLOAD_ERR_INVALID_IMAGE_HEADER_SIG = -6, - UPLOAD_ERR_INVALID_IMAGE_HEADER_VERSION = -16, - UPLOAD_ERR_USER_ABORT = -7, - UPLOAD_ERR_FIRMWARE_TOO_BIG = -8, - UPLOAD_ERR_INVALID_CHUNK_HASH = -9, - UPLOAD_ERR_BOOTLOADER_LOCKED = -10, - UPLOAD_ERR_FIRMWARE_MISMATCH = -11, - UPLOAD_ERR_NOT_FIRMWARE_UPGRADE = -12, - UPLOAD_ERR_NOT_FULLTRUST_IMAGE = -13, - UPLOAD_ERR_INVALID_CHUNK_PADDING = -14, -}; - -enum { - WIPE_OK = 0, - WIPE_ERR_CANNOT_ERASE = -1, -}; - -secbool msg_parse_header(const uint8_t *buf, uint16_t *msg_id, - uint32_t *msg_size); - -void send_user_abort(uint8_t iface_num, const char *msg); - -void process_msg_Initialize(uint8_t iface_num, uint32_t msg_size, uint8_t *buf, - const vendor_header *const vhdr, - const image_header *const hdr); -void process_msg_GetFeatures(uint8_t iface_num, uint32_t msg_size, uint8_t *buf, - const vendor_header *const vhdr, - const image_header *const hdr); -void process_msg_Ping(uint8_t iface_num, uint32_t msg_size, uint8_t *buf); -void process_msg_FirmwareErase(uint8_t iface_num, uint32_t msg_size, - uint8_t *buf); -int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size, - uint8_t *buf); -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_UnlockBootloader(uint8_t iface_num, uint32_t msg_size, - uint8_t *buf); -#endif - -secbool bootloader_WipeDevice(void); - -#endif diff --git a/core/embed/projects/bootloader/poll.c b/core/embed/projects/bootloader/poll.c new file mode 100644 index 0000000000..6d6bc70e73 --- /dev/null +++ b/core/embed/projects/bootloader/poll.c @@ -0,0 +1,62 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "poll.h" + +#include +#include + +#ifdef TREZOR_EMULATOR +#include "SDL.h" +#endif + +uint8_t poll_events(const uint16_t* ifaces, size_t ifaces_num, + poll_event_t* event, uint32_t timeout_ms) { + uint32_t deadline = ticks_timeout(timeout_ms); + + while (!ticks_expired(deadline)) { +#ifdef TREZOR_EMULATOR + // Ensures that SDL events are processed. This prevents the emulator from + // freezing when the user interacts with the window. + SDL_PumpEvents(); +#endif + + for (size_t i = 0; i < ifaces_num; i++) { + uint8_t iface_num = ifaces[i] & 0xFF; + if (iface_num < IFACE_USB_MAX) { + if ((ifaces[i] & MODE_READ) == MODE_READ) { + // check if USB can read + if (sectrue == usb_webusb_can_read(iface_num)) { + event->type = EVENT_USB_CAN_READ; + return iface_num; + } + } + } + } + +#ifndef TREZOR_EMULATOR + __WFI(); +#endif + } + + event->type = EVENT_NONE; + return 0; +} diff --git a/core/embed/projects/bootloader/poll.h b/core/embed/projects/bootloader/poll.h new file mode 100644 index 0000000000..61d7d4e79c --- /dev/null +++ b/core/embed/projects/bootloader/poll.h @@ -0,0 +1,39 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + +#define IFACE_USB_MAX (15) // 0-15 reserved for USB + +#define MODE_READ 0x0000 +#define MODE_WRITE 0x0100 + +typedef enum { + EVENT_NONE = 0, + EVENT_USB_CAN_READ = 0x01, +} poll_event_type_t; + +typedef struct { + poll_event_type_t type; +} poll_event_t; + +uint8_t poll_events(const uint16_t* ifaces, size_t ifaces_num, + poll_event_t* event, uint32_t timeout_ms); diff --git a/core/embed/projects/bootloader/protob/.gitignore b/core/embed/projects/bootloader/protob/pb/.gitignore similarity index 100% rename from core/embed/projects/bootloader/protob/.gitignore rename to core/embed/projects/bootloader/protob/pb/.gitignore diff --git a/core/embed/projects/bootloader/protob/Makefile b/core/embed/projects/bootloader/protob/pb/Makefile similarity index 100% rename from core/embed/projects/bootloader/protob/Makefile rename to core/embed/projects/bootloader/protob/pb/Makefile diff --git a/core/embed/projects/bootloader/protob/messages.options b/core/embed/projects/bootloader/protob/pb/messages.options similarity index 100% rename from core/embed/projects/bootloader/protob/messages.options rename to core/embed/projects/bootloader/protob/pb/messages.options diff --git a/core/embed/projects/bootloader/protob/messages.pb.c b/core/embed/projects/bootloader/protob/pb/messages.pb.c similarity index 100% rename from core/embed/projects/bootloader/protob/messages.pb.c rename to core/embed/projects/bootloader/protob/pb/messages.pb.c diff --git a/core/embed/projects/bootloader/protob/messages.pb.h b/core/embed/projects/bootloader/protob/pb/messages.pb.h similarity index 100% rename from core/embed/projects/bootloader/protob/messages.pb.h rename to core/embed/projects/bootloader/protob/pb/messages.pb.h diff --git a/core/embed/projects/bootloader/protob/messages.proto b/core/embed/projects/bootloader/protob/pb/messages.proto similarity index 100% rename from core/embed/projects/bootloader/protob/messages.proto rename to core/embed/projects/bootloader/protob/pb/messages.proto diff --git a/core/embed/projects/bootloader/protob/protob.c b/core/embed/projects/bootloader/protob/protob.c new file mode 100644 index 0000000000..c0ee01d89d --- /dev/null +++ b/core/embed/projects/bootloader/protob/protob.c @@ -0,0 +1,245 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include +#include + +#include +#include + +#if USE_OPTIGA +#include +#endif + +#include "memzero.h" +#include "pb/messages.pb.h" +#include "protob.h" +#include "version.h" +#include "wire/codec_v1.h" + +#define MSG_SEND_INIT(TYPE) TYPE msg_send = TYPE##_init_default +#define MSG_SEND_ASSIGN_REQUIRED_VALUE(FIELD, VALUE) \ + { msg_send.FIELD = VALUE; } +#define MSG_SEND_ASSIGN_VALUE(FIELD, VALUE) \ + { \ + msg_send.has_##FIELD = true; \ + msg_send.FIELD = VALUE; \ + } +#define MSG_SEND_ASSIGN_STRING(FIELD, VALUE) \ + { \ + msg_send.has_##FIELD = true; \ + memzero(msg_send.FIELD, sizeof(msg_send.FIELD)); \ + strncpy(msg_send.FIELD, VALUE, sizeof(msg_send.FIELD) - 1); \ + } +#define MSG_SEND_ASSIGN_STRING_LEN(FIELD, VALUE, LEN) \ + { \ + msg_send.has_##FIELD = true; \ + memzero(msg_send.FIELD, sizeof(msg_send.FIELD)); \ + strncpy(msg_send.FIELD, VALUE, MIN(LEN, sizeof(msg_send.FIELD) - 1)); \ + } +#define MSG_SEND_ASSIGN_BYTES(FIELD, VALUE, LEN) \ + { \ + msg_send.has_##FIELD = true; \ + memzero(msg_send.FIELD.bytes, sizeof(msg_send.FIELD.bytes)); \ + memcpy(msg_send.FIELD.bytes, VALUE, \ + MIN(LEN, sizeof(msg_send.FIELD.bytes))); \ + msg_send.FIELD.size = MIN(LEN, sizeof(msg_send.FIELD.bytes)); \ + } +#define MSG_SEND(TYPE) \ + codec_send_msg(iface->wire, MessageType_MessageType_##TYPE, TYPE##_fields, \ + &msg_send) + +#define MSG_RECV_INIT(TYPE) TYPE msg_recv = TYPE##_init_default +#define MSG_RECV_CALLBACK(FIELD, CALLBACK, ARGUMENT) \ + { \ + msg_recv.FIELD.funcs.decode = &CALLBACK; \ + msg_recv.FIELD.arg = (void *)ARGUMENT; \ + } +#define MSG_RECV(TYPE) \ + codec_recv_message(iface->wire, iface->msg_size, iface->buf, TYPE##_fields, \ + &msg_recv) + +secbool send_user_abort(protob_io_t *iface, const char *msg) { + MSG_SEND_INIT(Failure); + MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ActionCancelled); + MSG_SEND_ASSIGN_STRING(message, msg); + return MSG_SEND(Failure); +} + +secbool send_msg_failure(protob_io_t *iface, FailureType type, + const char *msg) { + MSG_SEND_INIT(Failure); + MSG_SEND_ASSIGN_VALUE(code, type); + MSG_SEND_ASSIGN_STRING(message, msg); + return MSG_SEND(Failure); +} + +secbool send_msg_success(protob_io_t *iface, const char *msg) { + MSG_SEND_INIT(Success); + if (msg != NULL) { + MSG_SEND_ASSIGN_STRING(message, msg); + } + return MSG_SEND(Success); +} + +secbool send_msg_features(protob_io_t *iface, const vendor_header *const vhdr, + const image_header *const hdr) { + MSG_SEND_INIT(Features); + MSG_SEND_ASSIGN_STRING(vendor, "trezor.io"); + MSG_SEND_ASSIGN_REQUIRED_VALUE(major_version, VERSION_MAJOR); + MSG_SEND_ASSIGN_REQUIRED_VALUE(minor_version, VERSION_MINOR); + MSG_SEND_ASSIGN_REQUIRED_VALUE(patch_version, VERSION_PATCH); + MSG_SEND_ASSIGN_VALUE(bootloader_mode, true); + MSG_SEND_ASSIGN_STRING(model, MODEL_NAME); + MSG_SEND_ASSIGN_STRING(internal_model, MODEL_INTERNAL_NAME); + if (vhdr && hdr) { + MSG_SEND_ASSIGN_VALUE(firmware_present, true); + MSG_SEND_ASSIGN_VALUE(fw_major, (hdr->version & 0xFF)); + MSG_SEND_ASSIGN_VALUE(fw_minor, ((hdr->version >> 8) & 0xFF)); + MSG_SEND_ASSIGN_VALUE(fw_patch, ((hdr->version >> 16) & 0xFF)); + MSG_SEND_ASSIGN_STRING_LEN(fw_vendor, vhdr->vstr, vhdr->vstr_len); + } else { + MSG_SEND_ASSIGN_VALUE(firmware_present, false); + } + if (unit_properties()->color_is_valid) { + MSG_SEND_ASSIGN_VALUE(unit_color, unit_properties()->color); + } + if (unit_properties()->packaging_is_valid) { + MSG_SEND_ASSIGN_VALUE(unit_packaging, unit_properties()->packaging); + } + if (unit_properties()->btconly_is_valid) { + MSG_SEND_ASSIGN_VALUE(unit_btconly, unit_properties()->btconly); + } + +#if USE_OPTIGA + MSG_SEND_ASSIGN_VALUE(bootloader_locked, + (secret_bootloader_locked() == sectrue)); +#endif + return MSG_SEND(Features); +} + +secbool recv_msg_initialize(protob_io_t *iface, Initialize *msg) { + MSG_RECV_INIT(Initialize); + secbool result = MSG_RECV(Initialize); + memcpy(msg, &msg_recv, sizeof(Initialize)); + return result; +} + +secbool recv_msg_get_features(protob_io_t *iface, GetFeatures *msg) { + MSG_RECV_INIT(GetFeatures); + secbool result = MSG_RECV(GetFeatures); + memcpy(msg, &msg_recv, sizeof(GetFeatures)); + return result; +} + +secbool recv_msg_ping(protob_io_t *iface, Ping *msg) { + MSG_RECV_INIT(Ping); + secbool result = MSG_RECV(Ping); + memcpy(msg, &msg_recv, sizeof(Ping)); + return result; +} + +secbool recv_msg_firmware_erase(protob_io_t *iface, FirmwareErase *msg) { + MSG_RECV_INIT(FirmwareErase); + secbool result = MSG_RECV(FirmwareErase); + memcpy(msg, &msg_recv, sizeof(FirmwareErase)); + return result; +} + +secbool send_msg_request_firmware(protob_io_t *iface, uint32_t offset, + uint32_t length) { + MSG_SEND_INIT(FirmwareRequest); + MSG_SEND_ASSIGN_REQUIRED_VALUE(offset, offset); + MSG_SEND_ASSIGN_REQUIRED_VALUE(length, length); + return MSG_SEND(FirmwareRequest); +} + +typedef struct { + void (*cb)(size_t len, void *ctx); + void *ctx; + uint8_t *buffer; + size_t buffer_size; +} payload_ctx_t; + +/* we don't use secbool/sectrue/secfalse here as it is a nanopb api */ +static bool read_payload(pb_istream_t *stream, const pb_field_t *field, + void **arg) { + payload_ctx_t *payload_ctx = (payload_ctx_t *)*arg; +#define BUFSIZE 32768 + + if (stream->bytes_left > payload_ctx->buffer_size) { + return false; + } + + uint32_t bytes_written = 0; + + while (stream->bytes_left) { + uint32_t received = + stream->bytes_left > BUFSIZE ? BUFSIZE : stream->bytes_left; + + // notify of received data + payload_ctx->cb(received, payload_ctx->ctx); + + // read data + if (!pb_read(stream, (pb_byte_t *)(payload_ctx->buffer + bytes_written), + (received))) { + return false; + } + bytes_written += received; + } + + return true; +} + +secbool recv_msg_firmware_upload(protob_io_t *iface, FirmwareUpload *msg, + void *ctx, + void (*data_cb)(size_t len, void *ctx), + uint8_t *buffer, size_t buffer_size) { + payload_ctx_t payload_ctx = { + .cb = data_cb, .ctx = ctx, .buffer = buffer, .buffer_size = buffer_size}; + + MSG_RECV_INIT(FirmwareUpload); + MSG_RECV_CALLBACK(payload, read_payload, &payload_ctx); + secbool result = MSG_RECV(FirmwareUpload); + memcpy(msg, &msg_recv, sizeof(FirmwareUpload)); + return result; +} + +void recv_msg_unknown(protob_io_t *iface) { + codec_flush(iface->wire, iface->msg_size, iface->buf); + send_msg_failure(iface, FailureType_Failure_UnexpectedMessage, + "Unexpected message"); +} + +void protob_init(protob_io_t *iface, wire_iface_t *wire) { + memset(iface, 0, sizeof(protob_io_t)); + iface->wire = wire; +} + +uint32_t protob_get_iface_flag(protob_io_t *iface) { + return iface->wire->poll_iface_id; +} + +secbool protob_get_msg_header(protob_io_t *iface, uint16_t *msg_id) { + iface->wire->read(iface->buf, iface->wire->rx_packet_size); + return codec_parse_header(iface->buf, msg_id, &iface->msg_size); +} diff --git a/core/embed/projects/bootloader/protob/protob.h b/core/embed/projects/bootloader/protob/protob.h new file mode 100644 index 0000000000..bcf4687ac8 --- /dev/null +++ b/core/embed/projects/bootloader/protob/protob.h @@ -0,0 +1,68 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + +#include + +#include "pb/messages.pb.h" + +#include "wire/codec_v1.h" + +typedef struct { + wire_iface_t *wire; + uint8_t buf[MAX_PACKET_SIZE]; + size_t msg_size; + +} protob_io_t; + +secbool send_user_abort(protob_io_t *iface, const char *msg); + +secbool send_msg_features(protob_io_t *iface, const vendor_header *const vhdr, + const image_header *const hdr); + +secbool send_msg_failure(protob_io_t *iface, FailureType type, const char *msg); + +secbool send_msg_success(protob_io_t *iface, const char *msg); + +secbool send_msg_request_firmware(protob_io_t *iface, uint32_t offset, + uint32_t length); + +secbool recv_msg_initialize(protob_io_t *iface, Initialize *msg); + +secbool recv_msg_get_features(protob_io_t *iface, GetFeatures *msg); + +secbool recv_msg_ping(protob_io_t *iface, Ping *msg); + +secbool recv_msg_firmware_erase(protob_io_t *iface, FirmwareErase *msg); + +secbool recv_msg_firmware_upload(protob_io_t *iface, FirmwareUpload *msg, + void *ctx, + void (*data_cb)(size_t len, void *ctx), + uint8_t *buffer, size_t buffer_size); + +void recv_msg_unknown(protob_io_t *iface); + +void protob_init(protob_io_t *iface, wire_iface_t *wire); + +uint32_t protob_get_iface_flag(protob_io_t *iface); + +secbool protob_get_msg_header(protob_io_t *iface, uint16_t *msg_id); diff --git a/core/embed/projects/bootloader/wire/codec_v1.c b/core/embed/projects/bootloader/wire/codec_v1.c new file mode 100644 index 0000000000..d7dcf773f5 --- /dev/null +++ b/core/embed/projects/bootloader/wire/codec_v1.c @@ -0,0 +1,248 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include +#include +#include +#include "memzero.h" + +#include "codec_v1.h" + +#define MSG_HEADER1_LEN 9 +#define MSG_HEADER2_LEN 1 + +typedef struct { + wire_iface_t *iface; + + uint8_t packet_index; + uint8_t packet_pos; + uint8_t buf[MAX_PACKET_SIZE]; + +} packet_write_state_t; + +typedef struct { + wire_iface_t *iface; + uint8_t packet_index; + uint8_t packet_pos; + uint8_t *buf; +} packet_read_state_t; + +secbool codec_parse_header(const uint8_t *buf, uint16_t *msg_id, + size_t *msg_size) { + if (buf[0] != '?' || buf[1] != '#' || buf[2] != '#') { + return secfalse; + } + *msg_id = (buf[3] << 8) + buf[4]; + *msg_size = (buf[5] << 24) + (buf[6] << 16) + (buf[7] << 8) + buf[8]; + return sectrue; +} + +/* we don't use secbool/sectrue/secfalse here as it is a nanopb api */ +static bool write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count) { + packet_write_state_t *state = (packet_write_state_t *)(stream->state); + + size_t tx_len = state->iface->tx_packet_size; + uint8_t *tx_buf = state->buf; + + size_t written = 0; + // while we have data left + while (written < count) { + size_t remaining = count - written; + // if all remaining data fit into our packet + if (state->packet_pos + remaining <= tx_len) { + // append data from buf to state->buf + memcpy(tx_buf + state->packet_pos, buf + written, remaining); + // advance position + state->packet_pos += remaining; + // and return + return true; + } else { + // append data that fits + memcpy(tx_buf + state->packet_pos, buf + written, + tx_len - state->packet_pos); + written += tx_len - state->packet_pos; + // send packet + bool ok = state->iface->write(tx_buf, tx_len); + ensure(sectrue * ok, NULL); + // prepare new packet + state->packet_index++; + memzero(tx_buf, tx_len); + tx_buf[0] = '?'; + state->packet_pos = MSG_HEADER2_LEN; + } + } + + return true; +} + +static void write_flush(packet_write_state_t *state) { + size_t packet_size = state->iface->tx_packet_size; + + // if packet is not filled up completely + if (state->packet_pos < packet_size) { + // pad it with zeroes + memzero(state->buf + state->packet_pos, packet_size - state->packet_pos); + } + // send packet + bool ok = state->iface->write(state->buf, packet_size); + ensure(sectrue * (ok), NULL); +} + +secbool codec_send_msg(wire_iface_t *iface, uint16_t msg_id, + const pb_msgdesc_t *fields, const void *msg) { + // determine message size by serializing it into a dummy stream + pb_ostream_t sizestream = {.callback = NULL, + .state = NULL, + .max_size = SIZE_MAX, + .bytes_written = 0, + .errmsg = NULL}; + if (false == pb_encode(&sizestream, fields, msg)) { + return secfalse; + } + const uint32_t msg_size = sizestream.bytes_written; + + packet_write_state_t state = { + .iface = iface, + .packet_index = 0, + .packet_pos = MSG_HEADER1_LEN, + .buf = + { + '?', + '#', + '#', + (msg_id >> 8) & 0xFF, + msg_id & 0xFF, + (msg_size >> 24) & 0xFF, + (msg_size >> 16) & 0xFF, + (msg_size >> 8) & 0xFF, + msg_size & 0xFF, + }, + }; + + pb_ostream_t stream = {.callback = &write, + .state = &state, + .max_size = SIZE_MAX, + .bytes_written = 0, + .errmsg = NULL}; + + if (false == pb_encode(&stream, fields, msg)) { + return secfalse; + } + + write_flush(&state); + + return sectrue; +} + +static void read_retry(wire_iface_t *iface, uint8_t *buf) { + size_t packet_size = iface->rx_packet_size; + + for (int retry = 0;; retry++) { + int r = iface->read(buf, packet_size); + if (r != packet_size) { // reading failed + if (r == 0 && retry < 10) { + // only timeout => let's try again + continue; + } else { + iface->error(); + } + } + return; // success + } +} + +/* we don't use secbool/sectrue/secfalse here as it is a nanopb api */ +static bool read(pb_istream_t *stream, uint8_t *buf, size_t count) { + packet_read_state_t *state = (packet_read_state_t *)(stream->state); + + size_t packet_size = state->iface->rx_packet_size; + + size_t read = 0; + // while we have data left + while (read < count) { + size_t remaining = count - read; + // if all remaining data fit into our packet + if (state->packet_pos + remaining <= packet_size) { + // append data from buf to state->buf + memcpy(buf + read, state->buf + state->packet_pos, remaining); + // advance position + state->packet_pos += remaining; + // and return + return true; + } else { + // append data that fits + memcpy(buf + read, state->buf + state->packet_pos, + packet_size - state->packet_pos); + read += packet_size - state->packet_pos; + // read next packet (with retry) + read_retry(state->iface, state->buf); + // prepare next packet + state->packet_index++; + state->packet_pos = MSG_HEADER2_LEN; + } + } + + return true; +} + +static void read_flush(packet_read_state_t *state) { (void)state; } + +secbool codec_recv_message(wire_iface_t *iface, uint32_t msg_size, uint8_t *buf, + const pb_msgdesc_t *fields, void *msg) { + packet_read_state_t state = {.iface = iface, + .packet_index = 0, + .packet_pos = MSG_HEADER1_LEN, + .buf = buf}; + + pb_istream_t stream = {.callback = &read, + .state = &state, + .bytes_left = msg_size, + .errmsg = NULL}; + + if (false == pb_decode_noinit(&stream, fields, msg)) { + return secfalse; + } + + read_flush(&state); + + return sectrue; +} + +void codec_flush(wire_iface_t *iface, uint32_t msg_size, uint8_t *buf) { + // consume remaining message + int remaining_chunks = 0; + + size_t packet_size = iface->rx_packet_size; + + if (msg_size > (packet_size - MSG_HEADER1_LEN)) { + // calculate how many blocks need to be read to drain the message (rounded + // up to not leave any behind) + remaining_chunks = (msg_size - (packet_size - MSG_HEADER1_LEN) + + ((packet_size - MSG_HEADER2_LEN) - 1)) / + (packet_size - MSG_HEADER2_LEN); + } + + for (int i = 0; i < remaining_chunks; i++) { + // read next packet (with retry) + read_retry(iface, buf); + } +} diff --git a/core/embed/projects/bootloader/wire/codec_v1.h b/core/embed/projects/bootloader/wire/codec_v1.h new file mode 100644 index 0000000000..82450250ac --- /dev/null +++ b/core/embed/projects/bootloader/wire/codec_v1.h @@ -0,0 +1,54 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + +#include + +#define MAX_PACKET_SIZE 256 + +typedef struct { + // identifier of the interface used for polling communication events + uint8_t poll_iface_id; + // size of TX packet + size_t tx_packet_size; + // size of RX packet + size_t rx_packet_size; + + // write function pointer + bool (*write)(uint8_t *data, size_t size); + // read function pointer + int (*read)(uint8_t *buffer, size_t buffer_size); + + // RSOD function pointer + void (*error)(void); +} wire_iface_t; + +secbool codec_parse_header(const uint8_t *buf, uint16_t *msg_id, + size_t *msg_size); + +secbool codec_send_msg(wire_iface_t *iface, uint16_t msg_id, + const pb_msgdesc_t *fields, const void *msg); + +secbool codec_recv_message(wire_iface_t *iface, uint32_t msg_size, uint8_t *buf, + const pb_msgdesc_t *fields, void *msg); + +void codec_flush(wire_iface_t *iface, uint32_t msg_size, uint8_t *buf); diff --git a/core/embed/projects/bootloader/wire/wire_iface_usb.c b/core/embed/projects/bootloader/wire/wire_iface_usb.c new file mode 100644 index 0000000000..4ebb691cef --- /dev/null +++ b/core/embed/projects/bootloader/wire/wire_iface_usb.c @@ -0,0 +1,113 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "wire_iface_usb.h" + +#include + +#define USB_TIMEOUT 500 +#define USB_PACKET_SIZE 64 +#define USB_IFACE_NUM 0 + +static bool usb_write(uint8_t* data, size_t size) { + if (size != USB_PACKET_SIZE) { + return false; + } + + int r = usb_webusb_write_blocking(USB_IFACE_NUM, data, size, USB_TIMEOUT); + + return r == size; +} + +static int usb_read(uint8_t* buffer, size_t buffer_size) { + if (buffer_size != USB_PACKET_SIZE) { + return -1; + } + + int r = usb_webusb_read_blocking(USB_IFACE_NUM, buffer, USB_PACKET_SIZE, + USB_TIMEOUT); + + return r; +} + +static void usb_error(void) { + error_shutdown_ex("USB ERROR", + "Error reading from USB. Try different USB cable.", NULL); +} + +static void usb_init_all(secbool usb21_landing) { + usb_dev_info_t dev_info = { + .device_class = 0x00, + .device_subclass = 0x00, + .device_protocol = 0x00, + .vendor_id = 0x1209, + .product_id = 0x53C0, + .release_num = 0x0200, + .manufacturer = MODEL_USB_MANUFACTURER, + .product = MODEL_USB_PRODUCT, + .serial_number = "000000000000000000000000", + .interface = "TREZOR Interface", + .usb21_enabled = sectrue, + .usb21_landing = usb21_landing, + }; + + static uint8_t rx_buffer[USB_PACKET_SIZE]; + + static const usb_webusb_info_t webusb_info = { + .iface_num = USB_IFACE_NUM, +#ifdef TREZOR_EMULATOR + .emu_port = 21324, +#else + .ep_in = 0x01, + .ep_out = 0x01, +#endif + .subclass = 0, + .protocol = 0, + .max_packet_len = sizeof(rx_buffer), + .rx_buffer = rx_buffer, + .polling_interval = 1, + }; + + ensure(usb_init(&dev_info), NULL); + + ensure(usb_webusb_add(&webusb_info), NULL); + + ensure(usb_start(), NULL); +} + +void usb_iface_init(wire_iface_t* iface, secbool usb21_landing) { + usb_init_all(usb21_landing); + + memset(iface, 0, sizeof(wire_iface_t)); + + iface->poll_iface_id = USB_IFACE_NUM; + iface->tx_packet_size = USB_PACKET_SIZE; + iface->rx_packet_size = USB_PACKET_SIZE; + iface->write = &usb_write; + iface->read = &usb_read; + iface->error = &usb_error; +} + +void usb_iface_deinit(wire_iface_t* iface) { + memset(iface, 0, sizeof(wire_iface_t)); + usb_deinit(); +} diff --git a/core/embed/projects/bootloader/wire/wire_iface_usb.h b/core/embed/projects/bootloader/wire/wire_iface_usb.h new file mode 100644 index 0000000000..6b2057071c --- /dev/null +++ b/core/embed/projects/bootloader/wire/wire_iface_usb.h @@ -0,0 +1,26 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "codec_v1.h" + +void usb_iface_init(wire_iface_t* iface, secbool usb21_landing); + +void usb_iface_deinit(wire_iface_t* iface); diff --git a/core/embed/projects/bootloader/workflow/wf_auto_update.c b/core/embed/projects/bootloader/workflow/wf_auto_update.c new file mode 100644 index 0000000000..d5a4e64260 --- /dev/null +++ b/core/embed/projects/bootloader/workflow/wf_auto_update.c @@ -0,0 +1,37 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include + +#include "bootui.h" +#include "workflow.h" + +workflow_result_t workflow_auto_update(const vendor_header *const vhdr, + const image_header *const hdr) { + ui_set_initial_setup(true); + + workflow_result_t res = WF_CANCELLED; + while (res == WF_CANCELLED) { + res = workflow_host_control(vhdr, hdr, ui_screen_connect); + } + return res; +} diff --git a/core/embed/projects/bootloader/workflow/wf_bootloader.c b/core/embed/projects/bootloader/workflow/wf_bootloader.c new file mode 100644 index 0000000000..f2191b73fa --- /dev/null +++ b/core/embed/projects/bootloader/workflow/wf_bootloader.c @@ -0,0 +1,99 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include +#include + +#include "antiglitch.h" +#include "bootui.h" +#include "workflow.h" + +typedef enum { + SCREEN_INTRO, + SCREEN_MENU, + SCREEN_WAIT_FOR_HOST, +} screen_t; + +workflow_result_t workflow_bootloader(const vendor_header *const vhdr, + const image_header *const hdr, + secbool firmware_present) { + ui_set_initial_setup(false); + + screen_t screen = SCREEN_INTRO; + + while (true) { + switch (screen) { + case SCREEN_INTRO: + intro_result_t ui_result = ui_screen_intro(vhdr, hdr, firmware_present); + if (ui_result == INTRO_MENU) { + screen = SCREEN_MENU; + } + if (ui_result == INTRO_HOST) { + screen = SCREEN_WAIT_FOR_HOST; + } + break; + case SCREEN_MENU: + menu_result_t menu_result = ui_screen_menu(firmware_present); + if (menu_result == MENU_EXIT) { // exit menu + screen = SCREEN_INTRO; + } + if (menu_result == MENU_REBOOT) { // reboot +#ifndef USE_HASH_PROCESSOR + ui_screen_boot_stage_1(true); +#endif + jump_allow_1(); + jump_allow_2(); + return WF_OK_REBOOT_SELECTED; + } + if (menu_result == MENU_WIPE) { // wipe + workflow_result_t r = workflow_wipe_device(NULL); + if (r == WF_ERROR) { + return r; + } + if (r == WF_OK) { + return WF_OK_DEVICE_WIPED; + } + + if (r == WF_CANCELLED) { + screen = SCREEN_MENU; + continue; + } + return WF_ERROR_FATAL; + } + break; + case SCREEN_WAIT_FOR_HOST: + workflow_result_t res = + workflow_host_control(vhdr, hdr, ui_screen_connect); + switch (res) { + case WF_CANCELLED: + screen = SCREEN_INTRO; + continue; + default: + return res; + } + break; + default: + return WF_ERROR_FATAL; + break; + } + } +} diff --git a/core/embed/projects/bootloader/workflow/wf_empty_device.c b/core/embed/projects/bootloader/workflow/wf_empty_device.c new file mode 100644 index 0000000000..136e6f5617 --- /dev/null +++ b/core/embed/projects/bootloader/workflow/wf_empty_device.c @@ -0,0 +1,57 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include +#include +#include +#include + +#ifdef USE_STORAGE_HWKEY +#include +#endif + +#include "bootui.h" +#include "workflow.h" + +workflow_result_t workflow_empty_device(void) { + ui_set_initial_setup(true); + +#ifdef USE_STORAGE_HWKEY + secret_bhk_regenerate(); +#endif + ensure(erase_storage(NULL), NULL); + + // keep the model screen up for a while +#ifndef USE_BACKLIGHT + systick_delay_ms(1500); +#else + // backlight fading takes some time so the explicit delay here is + // shorter + systick_delay_ms(1000); +#endif + + workflow_result_t res = WF_CANCELLED; + while (res == WF_CANCELLED) { + res = workflow_host_control(NULL, NULL, ui_screen_welcome); + } + return res; +} diff --git a/core/embed/projects/bootloader/workflow/wf_firmware_update.c b/core/embed/projects/bootloader/workflow/wf_firmware_update.c new file mode 100644 index 0000000000..b60dbcf9ce --- /dev/null +++ b/core/embed/projects/bootloader/workflow/wf_firmware_update.c @@ -0,0 +1,561 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include +#include +#include +#include + +#if USE_OPTIGA +#include +#endif + +#include + +#include "bootui.h" +#include "protob/protob.h" +#include "version_check.h" +#include "workflow.h" + +#ifdef TREZOR_EMULATOR +#include "emulator.h" +#endif + +typedef enum { + UPLOAD_OK = 0, + UPLOAD_IN_PROGRESS = 1, + UPLOAD_ERR_INVALID_CHUNK_SIZE = -1, + UPLOAD_ERR_INVALID_VENDOR_HEADER = -2, + UPLOAD_ERR_INVALID_VENDOR_HEADER_SIG = -3, + UPLOAD_ERR_INVALID_VENDOR_HEADER_MODEL = -15, + UPLOAD_ERR_INVALID_IMAGE_HEADER = -4, + UPLOAD_ERR_INVALID_IMAGE_MODEL = -5, + UPLOAD_ERR_INVALID_IMAGE_HEADER_SIG = -6, + UPLOAD_ERR_INVALID_IMAGE_HEADER_VERSION = -16, + UPLOAD_ERR_USER_ABORT = -7, + UPLOAD_ERR_FIRMWARE_TOO_BIG = -8, + UPLOAD_ERR_INVALID_CHUNK_HASH = -9, + UPLOAD_ERR_BOOTLOADER_LOCKED = -10, + UPLOAD_ERR_FIRMWARE_MISMATCH = -11, + UPLOAD_ERR_NOT_FIRMWARE_UPGRADE = -12, + UPLOAD_ERR_NOT_FULLTRUST_IMAGE = -13, + UPLOAD_ERR_INVALID_CHUNK_PADDING = -14, + UPLOAD_ERR_COMMUNICATION = -17, +} upload_status_t; + +#define FIRMWARE_UPLOAD_CHUNK_RETRY_COUNT 2 + +#ifndef TREZOR_EMULATOR +__attribute__((section(".buf"))) +#endif +uint32_t chunk_buffer[IMAGE_CHUNK_SIZE / 4]; + +typedef struct { + uint32_t firmware_remaining; + uint32_t firmware_block; + uint32_t chunk_requested; + uint32_t erase_offset; + int32_t firmware_upload_chunk_retry; + size_t headers_offset; + size_t read_offset; + uint32_t chunk_size; +} firmware_update_ctx_t; + +static int version_compare(uint32_t vera, uint32_t verb) { + /* Explicit casts so that we control how compiler does the unsigned shift + * and correctly then promote uint8_t to int without possibility of + * having implementation-defined right shift on negative int + * in case compiler promoted the wrong unsigned int + */ + int a, b; + a = (uint8_t)vera & 0xFF; + b = (uint8_t)verb & 0xFF; + if (a != b) return a - b; + a = (uint8_t)(vera >> 8) & 0xFF; + b = (uint8_t)(verb >> 8) & 0xFF; + if (a != b) return a - b; + a = (uint8_t)(vera >> 16) & 0xFF; + b = (uint8_t)(verb >> 16) & 0xFF; + if (a != b) return a - b; + a = (uint8_t)(vera >> 24) & 0xFF; + b = (uint8_t)(verb >> 24) & 0xFF; + return a - b; +} + +static void detect_installation(const vendor_header *current_vhdr, + const image_header *current_hdr, + const vendor_header *const new_vhdr, + const image_header *const new_hdr, + secbool *is_new, secbool *keep_seed, + secbool *is_newvendor, secbool *is_upgrade) { + *is_new = secfalse; + *keep_seed = secfalse; + *is_newvendor = secfalse; + *is_upgrade = secfalse; + if (sectrue != check_vendor_header_keys(current_vhdr)) { + *is_new = sectrue; + return; + } + if (sectrue != check_image_model(current_hdr)) { + *is_new = sectrue; + return; + } + if (sectrue != check_firmware_min_version(current_hdr->monotonic)) { + *is_new = sectrue; + return; + } + if (sectrue != check_image_header_sig(current_hdr, current_vhdr->vsig_m, + current_vhdr->vsig_n, + current_vhdr->vpub)) { + *is_new = sectrue; + return; + } + uint8_t hash1[32], hash2[32]; + vendor_header_hash(new_vhdr, hash1); + vendor_header_hash(current_vhdr, hash2); + if (0 != memcmp(hash1, hash2, 32)) { + *is_newvendor = sectrue; + return; + } + if (version_compare(new_hdr->version, current_hdr->fix_version) < 0) { + return; + } + if (version_compare(new_hdr->version, current_hdr->version) > 0) { + *is_upgrade = sectrue; + } + + *keep_seed = sectrue; +} + +static void fw_data_received(size_t len, void *ctx) { + firmware_update_ctx_t *context = (firmware_update_ctx_t *)ctx; + + context->chunk_size += len; + // update loader but skip first block + if (context->firmware_block > 0) { + ui_screen_install_progress_upload( + 1000 * + (context->firmware_block * IMAGE_CHUNK_SIZE + context->chunk_size) / + (context->firmware_block * IMAGE_CHUNK_SIZE + + context->firmware_remaining)); + } +} + +static upload_status_t process_msg_FirmwareUpload(protob_io_t *iface, + firmware_update_ctx_t *ctx) { + FirmwareUpload msg; + + const secbool r = + recv_msg_firmware_upload(iface, &msg, ctx, fw_data_received, + &((uint8_t *)chunk_buffer)[ctx->read_offset], + sizeof(chunk_buffer) - ctx->read_offset); + + if (sectrue != r || + ctx->chunk_size != (ctx->chunk_requested + ctx->read_offset)) { + send_msg_failure(iface, FailureType_Failure_ProcessError, + "Invalid chunk size"); + return UPLOAD_ERR_INVALID_CHUNK_SIZE; + } + + static image_header hdr; + + if (ctx->firmware_block == 0) { + if (ctx->headers_offset == 0) { + // first block and headers are not yet parsed + vendor_header vhdr; + + if (sectrue != read_vendor_header((uint8_t *)chunk_buffer, &vhdr)) { + send_msg_failure(iface, FailureType_Failure_ProcessError, + "Invalid vendor header"); + return UPLOAD_ERR_INVALID_VENDOR_HEADER; + } + + if (sectrue != check_vendor_header_model(&vhdr)) { + send_msg_failure(iface, FailureType_Failure_ProcessError, + "Wrong model"); + return UPLOAD_ERR_INVALID_VENDOR_HEADER_MODEL; + } + + if (sectrue != check_vendor_header_keys(&vhdr)) { + send_msg_failure(iface, FailureType_Failure_ProcessError, + "Invalid vendor header signature"); + return UPLOAD_ERR_INVALID_VENDOR_HEADER_SIG; + } + + const image_header *received_hdr = + read_image_header((uint8_t *)chunk_buffer + vhdr.hdrlen, + FIRMWARE_IMAGE_MAGIC, FIRMWARE_MAXSIZE); + + if (received_hdr != + (const image_header *)((uint8_t *)chunk_buffer + vhdr.hdrlen)) { + send_msg_failure(iface, FailureType_Failure_ProcessError, + "Invalid firmware header"); + return UPLOAD_ERR_INVALID_IMAGE_HEADER; + } + + if (sectrue != check_image_model(received_hdr)) { + send_msg_failure(iface, FailureType_Failure_ProcessError, + "Wrong firmware model"); + return UPLOAD_ERR_INVALID_IMAGE_MODEL; + } + + if (sectrue != check_image_header_sig(received_hdr, vhdr.vsig_m, + vhdr.vsig_n, vhdr.vpub)) { + send_msg_failure(iface, FailureType_Failure_ProcessError, + "Invalid firmware signature"); + return UPLOAD_ERR_INVALID_IMAGE_HEADER_SIG; + } + + if (sectrue != check_firmware_min_version(received_hdr->monotonic)) { + send_msg_failure(iface, FailureType_Failure_ProcessError, + "Firmware downgrade protection"); + return UPLOAD_ERR_INVALID_IMAGE_HEADER_VERSION; + } + + memcpy(&hdr, received_hdr, sizeof(hdr)); + + vendor_header current_vhdr; + + secbool is_new = secfalse; + + if (sectrue != + read_vendor_header((const uint8_t *)FIRMWARE_START, ¤t_vhdr)) { + is_new = sectrue; + } + + const image_header *current_hdr = NULL; + + if (is_new == secfalse) { + current_hdr = read_image_header( + (const uint8_t *)FIRMWARE_START + current_vhdr.hdrlen, + FIRMWARE_IMAGE_MAGIC, FIRMWARE_MAXSIZE); + + if (current_hdr != + (const image_header *)(void *)(FIRMWARE_START + + current_vhdr.hdrlen)) { + is_new = sectrue; + } + } + + secbool should_keep_seed = secfalse; + secbool is_newvendor = secfalse; + secbool is_upgrade = secfalse; + if (is_new == secfalse) { + detect_installation(¤t_vhdr, current_hdr, &vhdr, &hdr, &is_new, + &should_keep_seed, &is_newvendor, &is_upgrade); + } + + secbool is_ilu = secfalse; // interaction-less update + + if (bootargs_get_command() == BOOT_COMMAND_INSTALL_UPGRADE) { + IMAGE_HASH_CTX ctx; + uint8_t hash[IMAGE_HASH_DIGEST_LENGTH]; + IMAGE_HASH_INIT(&ctx); + IMAGE_HASH_UPDATE(&ctx, (uint8_t *)chunk_buffer, + vhdr.hdrlen + received_hdr->hdrlen); + IMAGE_HASH_FINAL(&ctx, hash); + + // the firmware must be the same as confirmed by the user + boot_args_t args = {0}; + bootargs_get_args(&args); + + if (memcmp(args.hash, hash, sizeof(hash)) != 0) { + send_msg_failure(iface, FailureType_Failure_ProcessError, + "Firmware mismatch"); + return UPLOAD_ERR_FIRMWARE_MISMATCH; + } + + // the firmware must be from the same vendor + // the firmware must be newer + if (is_upgrade != sectrue || is_newvendor != secfalse) { + send_msg_failure(iface, FailureType_Failure_ProcessError, + "Not a firmware upgrade"); + return UPLOAD_ERR_NOT_FIRMWARE_UPGRADE; + } + + if ((vhdr.vtrust & VTRUST_NO_WARNING) != VTRUST_NO_WARNING) { + send_msg_failure(iface, FailureType_Failure_ProcessError, + "Not a full-trust image"); + return UPLOAD_ERR_NOT_FULLTRUST_IMAGE; + } + + // upload the firmware without confirmation + is_ilu = sectrue; + } + +#if defined USE_OPTIGA + if (secfalse != secret_optiga_present() && + ((vhdr.vtrust & VTRUST_SECRET_MASK) != VTRUST_SECRET_ALLOW)) { + send_msg_failure(iface, FailureType_Failure_ProcessError, + "Install restricted"); + return UPLOAD_ERR_BOOTLOADER_LOCKED; + } +#endif + + ui_result_t response = UI_RESULT_CANCEL; + if (((vhdr.vtrust & VTRUST_NO_WARNING) == VTRUST_NO_WARNING) && + (sectrue == is_new || sectrue == is_ilu)) { + // new installation or interaction less updated - auto confirm + // only allowed for full-trust images + response = UI_RESULT_CONFIRM; + } else { + if (sectrue != is_new) { + int version_cmp = version_compare(hdr.version, current_hdr->version); + response = ui_screen_install_confirm( + &vhdr, &hdr, should_keep_seed, is_newvendor, is_new, version_cmp); + } else { + response = ui_screen_install_confirm(&vhdr, &hdr, sectrue, + is_newvendor, is_new, 0); + } + } + + if (UI_RESULT_CONFIRM != response) { + send_user_abort(iface, "Firmware install cancelled"); + return UPLOAD_ERR_USER_ABORT; + } + + ui_screen_install_start(); + + // if firmware is not upgrade, erase storage + if (sectrue != should_keep_seed) { +#ifdef USE_STORAGE_HWKEY + secret_bhk_regenerate(); +#endif + ensure(erase_storage(NULL), NULL); + } + + ctx->headers_offset = IMAGE_HEADER_SIZE + vhdr.hdrlen; + ctx->read_offset = IMAGE_INIT_CHUNK_SIZE; + + // request the rest of the first chunk + uint32_t chunk_limit = (ctx->firmware_remaining > IMAGE_CHUNK_SIZE) + ? IMAGE_CHUNK_SIZE + : ctx->firmware_remaining; + ctx->chunk_requested = chunk_limit - ctx->read_offset; + + if (sectrue != send_msg_request_firmware(iface, ctx->read_offset, + ctx->chunk_requested)) { + return UPLOAD_ERR_COMMUNICATION; + } + + ctx->firmware_remaining -= ctx->read_offset; + if (ctx->firmware_remaining > 0) { + return UPLOAD_IN_PROGRESS; + } + return UPLOAD_OK; + } else { + // first block with the headers parsed -> the first chunk is now complete + ctx->read_offset = 0; + } + } + + // should not happen, but double-check + if (flash_area_get_address( + &FIRMWARE_AREA, ctx->firmware_block * IMAGE_CHUNK_SIZE, 0) == NULL) { + send_msg_failure(iface, FailureType_Failure_ProcessError, + "Firmware too big"); + return UPLOAD_ERR_FIRMWARE_TOO_BIG; + } + + if (sectrue != + check_single_hash(hdr.hashes + ctx->firmware_block * 32, + (uint8_t *)chunk_buffer + ctx->headers_offset, + ctx->chunk_size - ctx->headers_offset)) { + if (ctx->firmware_upload_chunk_retry > 0) { + --ctx->firmware_upload_chunk_retry; + + // clear chunk buffer + memset((uint8_t *)&chunk_buffer, 0xFF, IMAGE_CHUNK_SIZE); + ctx->chunk_size = 0; + + if (sectrue != send_msg_request_firmware( + iface, ctx->firmware_block * IMAGE_CHUNK_SIZE, + ctx->chunk_requested)) { + return UPLOAD_ERR_COMMUNICATION; + } + if (ctx->firmware_remaining > 0) { + return UPLOAD_IN_PROGRESS; + } + return UPLOAD_OK; + } + + send_msg_failure(iface, FailureType_Failure_ProcessError, + "Invalid chunk hash"); + return UPLOAD_ERR_INVALID_CHUNK_HASH; + } + + // buffer with the received data + const uint32_t *src = (const uint32_t *)chunk_buffer; + // number of received bytes + uint32_t bytes_remaining = ctx->chunk_size; + // offset into the FIRMWARE_AREA part of the flash + uint32_t write_offset = ctx->firmware_block * IMAGE_CHUNK_SIZE; + + ensure((ctx->chunk_size % FLASH_BLOCK_SIZE == 0) * sectrue, NULL); + + while (bytes_remaining > 0) { + // erase flash before writing + uint32_t bytes_erased = 0; + + if (write_offset >= ctx->erase_offset) { + // erase the next flash section + ensure(flash_area_erase_partial(&FIRMWARE_AREA, ctx->erase_offset, + &bytes_erased), + NULL); + ctx->erase_offset += bytes_erased; + } else { + // some erased space left from the previous round => use it + bytes_erased = ctx->erase_offset - write_offset; + } + + // write the received data + uint32_t bytes_to_write = MIN(bytes_erased, bytes_remaining); + ensure(flash_unlock_write(), NULL); + ensure(flash_area_write_data(&FIRMWARE_AREA, write_offset, src, + bytes_to_write), + NULL); + ensure(flash_lock_write(), NULL); + + write_offset += bytes_to_write; + src += bytes_to_write / sizeof(uint32_t); + + bytes_remaining -= bytes_to_write; + } + + ctx->firmware_remaining -= ctx->chunk_requested; + + if (ctx->firmware_remaining == 0) { + // erase the rest (unused part) of the FIRMWARE_AREA + uint32_t bytes_erased = 0; + do { + ensure(flash_area_erase_partial(&FIRMWARE_AREA, ctx->erase_offset, + &bytes_erased), + NULL); + ctx->erase_offset += bytes_erased; + } while (bytes_erased > 0); + } + + ctx->headers_offset = 0; + ctx->firmware_block++; + ctx->firmware_upload_chunk_retry = FIRMWARE_UPLOAD_CHUNK_RETRY_COUNT; + + if (ctx->firmware_remaining > 0) { + ctx->chunk_requested = (ctx->firmware_remaining > IMAGE_CHUNK_SIZE) + ? IMAGE_CHUNK_SIZE + : ctx->firmware_remaining; + + // clear chunk buffer + ctx->chunk_size = 0; + memset((uint8_t *)&chunk_buffer, 0xFF, IMAGE_CHUNK_SIZE); + if (sectrue != + send_msg_request_firmware(iface, ctx->firmware_block * IMAGE_CHUNK_SIZE, + ctx->chunk_requested)) { + return UPLOAD_ERR_COMMUNICATION; + } + } else { + send_msg_success(iface, NULL); + } + + if (ctx->firmware_remaining > 0) { + return UPLOAD_IN_PROGRESS; + } + return UPLOAD_OK; +} + +workflow_result_t workflow_firmware_update(protob_io_t *iface) { + firmware_update_ctx_t ctx = { + .firmware_upload_chunk_retry = FIRMWARE_UPLOAD_CHUNK_RETRY_COUNT, + }; + + FirmwareErase msg; + secbool res = recv_msg_firmware_erase(iface, &msg); + + if (res != sectrue) { + return WF_ERROR; + } + + ctx.firmware_remaining = msg.has_length ? msg.length : 0; + if ((ctx.firmware_remaining > 0) && + ((ctx.firmware_remaining % sizeof(uint32_t)) == 0) && + (ctx.firmware_remaining <= FIRMWARE_MAXSIZE)) { + // clear chunk buffer + memset((uint8_t *)&chunk_buffer, 0xFF, IMAGE_CHUNK_SIZE); + ctx.chunk_size = 0; + + // request new firmware + ctx.chunk_requested = (ctx.firmware_remaining > IMAGE_INIT_CHUNK_SIZE) + ? IMAGE_INIT_CHUNK_SIZE + : ctx.firmware_remaining; + if (sectrue != send_msg_request_firmware(iface, 0, ctx.chunk_requested)) { + ui_screen_fail(); + return WF_ERROR; + } + } else { + // invalid firmware size + send_msg_failure(iface, FailureType_Failure_ProcessError, + "Wrong firmware size"); + return WF_ERROR; + } + + upload_status_t s = UPLOAD_IN_PROGRESS; + + while (true) { + uint16_t ifaces[1] = {protob_get_iface_flag(iface) | MODE_READ}; + poll_event_t e = {0}; + uint8_t i = poll_events(ifaces, 1, &e, 100); + + if (e.type == EVENT_NONE || i != protob_get_iface_flag(iface)) { + continue; + } + + uint16_t msg_id = 0; + + if (sectrue != protob_get_msg_header(iface, &msg_id)) { + // invalid header -> discard + return WF_ERROR; + } + s = process_msg_FirmwareUpload(iface, &ctx); + + if (s < 0 && s != UPLOAD_ERR_USER_ABORT) { // error, but not user abort + if (s == UPLOAD_ERR_BOOTLOADER_LOCKED) { + // This function does not return + show_install_restricted_screen(); + } else { + ui_screen_fail(); + } + return WF_ERROR; + } else if (s == UPLOAD_ERR_USER_ABORT) { + systick_delay_ms(100); + return WF_CANCELLED; + } else if (s == UPLOAD_OK) { // last chunk received + ui_screen_install_progress_upload(1000); + ui_screen_done(4, sectrue); + ui_screen_done(3, secfalse); + systick_delay_ms(1000); + ui_screen_done(2, secfalse); + systick_delay_ms(1000); + ui_screen_done(1, secfalse); + systick_delay_ms(1000); + return WF_OK; + } + } +} diff --git a/core/embed/projects/bootloader/workflow/wf_get_features.c b/core/embed/projects/bootloader/workflow/wf_get_features.c new file mode 100644 index 0000000000..5f65eed4bf --- /dev/null +++ b/core/embed/projects/bootloader/workflow/wf_get_features.c @@ -0,0 +1,33 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "protob.h" +#include "workflow.h" + +workflow_result_t workflow_get_features(protob_io_t *iface, + const vendor_header *const vhdr, + const image_header *const hdr) { + GetFeatures msg_recv; + recv_msg_get_features(iface, &msg_recv); + send_msg_features(iface, vhdr, hdr); + return WF_OK; +} diff --git a/core/embed/projects/bootloader/workflow/wf_host_control.c b/core/embed/projects/bootloader/workflow/wf_host_control.c new file mode 100644 index 0000000000..5ac20be205 --- /dev/null +++ b/core/embed/projects/bootloader/workflow/wf_host_control.c @@ -0,0 +1,123 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include +#include +#include + +#include "antiglitch.h" +#include "poll.h" +#include "protob/protob.h" +#include "wire/wire_iface_usb.h" +#include "workflow.h" + +workflow_result_t workflow_host_control(const vendor_header *const vhdr, + const image_header *const hdr, + void (*redraw_wait_screen)(void)) { + wire_iface_t usb_iface = {0}; + protob_io_t protob_usb_iface = {0}; + + redraw_wait_screen(); + + // if both are NULL, we don't have a firmware installed + // let's show a webusb landing page in this case + usb_iface_init(&usb_iface, + (vhdr == NULL && hdr == NULL) ? sectrue : secfalse); + + protob_init(&protob_usb_iface, &usb_iface); + + workflow_result_t result = WF_ERROR_FATAL; + + for (;;) { + uint16_t ifaces[1] = {protob_get_iface_flag(&protob_usb_iface) | MODE_READ}; + poll_event_t e = {0}; + + uint8_t i = poll_events(ifaces, 1, &e, 100); + + uint16_t msg_id = 0; + protob_io_t *active_iface = NULL; + + switch (e.type) { + case EVENT_USB_CAN_READ: + if (i == protob_get_iface_flag(&protob_usb_iface) && + sectrue == protob_get_msg_header(&protob_usb_iface, &msg_id)) { + active_iface = &protob_usb_iface; + } else { + continue; + } + break; + case EVENT_NONE: + default: + continue; + } + + switch (msg_id) { + case MessageType_MessageType_Initialize: + workflow_initialize(active_iface, vhdr, hdr); + // whatever the result, we stay here and continue + continue; + case MessageType_MessageType_Ping: + workflow_ping(active_iface); + // whatever the result, we stay here and continue + continue; + case MessageType_MessageType_GetFeatures: + workflow_get_features(active_iface, vhdr, hdr); + // whatever the result, we stay here and continue + continue; + case MessageType_MessageType_WipeDevice: + result = workflow_wipe_device(active_iface); + if (result == WF_OK) { + systick_delay_ms(100); + usb_iface_deinit(&usb_iface); + return WF_OK_DEVICE_WIPED; + } + break; + case MessageType_MessageType_FirmwareErase: + result = workflow_firmware_update(active_iface); + if (result == WF_OK) { + jump_allow_1(); + jump_allow_2(); + systick_delay_ms(100); + usb_iface_deinit(&usb_iface); + return WF_OK_FIRMWARE_INSTALLED; + } + break; +#if defined USE_OPTIGA + case MessageType_MessageType_UnlockBootloader: + result = workflow_unlock_bootloader(active_iface); + if (result == WF_OK) { + systick_delay_ms(100); + usb_iface_deinit(&usb_iface); + return WF_OK_BOOTLOADER_UNLOCKED; + } + break; +#endif + default: + recv_msg_unknown(active_iface); + continue; + } + + systick_delay_ms(100); + usb_iface_deinit(&usb_iface); + return result; + } +} diff --git a/core/embed/projects/bootloader/workflow/wf_initialize.c b/core/embed/projects/bootloader/workflow/wf_initialize.c new file mode 100644 index 0000000000..c378cf1f72 --- /dev/null +++ b/core/embed/projects/bootloader/workflow/wf_initialize.c @@ -0,0 +1,33 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "protob.h" +#include "workflow.h" + +workflow_result_t workflow_initialize(protob_io_t *iface, + const vendor_header *const vhdr, + const image_header *const hdr) { + Initialize msg_recv; + recv_msg_initialize(iface, &msg_recv); + send_msg_features(iface, vhdr, hdr); + return WF_OK; +} diff --git a/core/embed/projects/bootloader/workflow/wf_ping.c b/core/embed/projects/bootloader/workflow/wf_ping.c new file mode 100644 index 0000000000..60b5140e8f --- /dev/null +++ b/core/embed/projects/bootloader/workflow/wf_ping.c @@ -0,0 +1,33 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "protob.h" +#include "workflow.h" + +workflow_result_t workflow_ping(protob_io_t *iface) { + Ping msg_recv; + if (sectrue != recv_msg_ping(iface, &msg_recv)) { + return WF_ERROR; + } + send_msg_success(iface, msg_recv.message); + return WF_OK; +} diff --git a/core/embed/projects/bootloader/workflow/wf_unlock_bootloader.c b/core/embed/projects/bootloader/workflow/wf_unlock_bootloader.c new file mode 100644 index 0000000000..d6e01e5cb0 --- /dev/null +++ b/core/embed/projects/bootloader/workflow/wf_unlock_bootloader.c @@ -0,0 +1,41 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include + +#include "bootui.h" +#include "protob.h" +#include "rust_ui.h" +#include "workflow.h" + +workflow_result_t workflow_unlock_bootloader(protob_io_t *iface) { + ui_result_t response = ui_screen_unlock_bootloader_confirm(); + if (UI_RESULT_CONFIRM != response) { + send_user_abort(iface, "Bootloader unlock cancelled"); + return WF_CANCELLED; + } + secret_optiga_erase(); + send_msg_success(iface, NULL); + + screen_unlock_bootloader_success(); + return WF_OK; +} diff --git a/core/embed/projects/bootloader/workflow/wf_wipe_device.c b/core/embed/projects/bootloader/workflow/wf_wipe_device.c new file mode 100644 index 0000000000..51b483d06e --- /dev/null +++ b/core/embed/projects/bootloader/workflow/wf_wipe_device.c @@ -0,0 +1,55 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include + +#include "bootui.h" +#include "protob.h" +#include "rust_ui.h" +#include "workflow.h" + +workflow_result_t workflow_wipe_device(protob_io_t *iface) { + ui_result_t response = ui_screen_wipe_confirm(); + if (UI_RESULT_CONFIRM != response) { + if (iface != NULL) { + send_user_abort(iface, "Wipe cancelled"); + } + return WF_CANCELLED; + } + ui_screen_wipe(); + secbool wipe_result = erase_device(ui_screen_wipe_progress); + + if (sectrue != wipe_result) { + if (iface != NULL) { + send_msg_failure(iface, FailureType_Failure_ProcessError, + "Could not erase flash"); + } + screen_wipe_fail(); + return WF_ERROR; + } + + if (iface != NULL) { + send_msg_success(iface, NULL); + } + screen_wipe_success(); + return WF_OK; +} diff --git a/core/embed/projects/bootloader/workflow/workflow.h b/core/embed/projects/bootloader/workflow/workflow.h new file mode 100644 index 0000000000..2a45dac4b1 --- /dev/null +++ b/core/embed/projects/bootloader/workflow/workflow.h @@ -0,0 +1,68 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + +#include + +#include "protob/protob.h" + +typedef enum { + WF_ERROR_FATAL = 0, + WF_ERROR = 0x11223344, + WF_OK = 0x7ABBCCDD, + WF_OK_REBOOT_SELECTED = 0x68A4DABF, + WF_OK_FIRMWARE_INSTALLED = 0x04D9D07F, + WF_OK_DEVICE_WIPED = 0x30DC3841, + WF_OK_BOOTLOADER_UNLOCKED = 0x23FCBD03, + WF_CANCELLED = 0x55667788, +} workflow_result_t; + +workflow_result_t workflow_firmware_update(protob_io_t *iface); + +workflow_result_t workflow_wipe_device(protob_io_t *iface); + +#ifdef USE_OPTIGA +workflow_result_t workflow_unlock_bootloader(protob_io_t *iface); +#endif + +workflow_result_t workflow_ping(protob_io_t *iface); + +workflow_result_t workflow_initialize(protob_io_t *iface, + const vendor_header *const vhdr, + const image_header *const hdr); + +workflow_result_t workflow_get_features(protob_io_t *iface, + const vendor_header *const vhdr, + const image_header *const hdr); + +workflow_result_t workflow_bootloader(const vendor_header *const vhdr, + const image_header *const hdr, + secbool firmware_present); + +workflow_result_t workflow_empty_device(void); + +workflow_result_t workflow_host_control(const vendor_header *const vhdr, + const image_header *const hdr, + void (*redraw_wait_screen)(void)); + +workflow_result_t workflow_auto_update(const vendor_header *const vhdr, + const image_header *const hdr); diff --git a/core/embed/projects/bootloader_ci/messages.c b/core/embed/projects/bootloader_ci/messages.c index 9e3251f7a4..67f91cf77f 100644 --- a/core/embed/projects/bootloader_ci/messages.c +++ b/core/embed/projects/bootloader_ci/messages.c @@ -23,7 +23,7 @@ #include #include #include -#include "messages.pb.h" +#include "pb/messages.pb.h" #include #include diff --git a/core/site_scons/models/D002/discovery2.py b/core/site_scons/models/D002/discovery2.py index 1a1bc8ea33..f3499e8df8 100644 --- a/core/site_scons/models/D002/discovery2.py +++ b/core/site_scons/models/D002/discovery2.py @@ -102,7 +102,6 @@ def configure( "USE_TAMPER=1", "USE_FLASH_BURST=1", "USE_OEM_KEYS_CHECK=1", - "USE_RESET_TO_BOOT=1", ] env.get("ENV")["LINKER_SCRIPT"] = linker_script diff --git a/core/site_scons/models/T3B1/trezor_t3b1_revB.py b/core/site_scons/models/T3B1/trezor_t3b1_revB.py index f307a852b3..fd8bc66bb8 100644 --- a/core/site_scons/models/T3B1/trezor_t3b1_revB.py +++ b/core/site_scons/models/T3B1/trezor_t3b1_revB.py @@ -106,7 +106,6 @@ def configure( ("USE_STORAGE_HWKEY", "1"), ("USE_TAMPER", "1"), ("USE_FLASH_BURST", "1"), - ("USE_RESET_TO_BOOT", "1"), ("USE_OEM_KEYS_CHECK", "1"), ("USE_PVD", "1"), ] diff --git a/core/site_scons/models/T3T1/trezor_t3t1_revE.py b/core/site_scons/models/T3T1/trezor_t3t1_revE.py index 6ed6184ad0..a2042f34d0 100644 --- a/core/site_scons/models/T3T1/trezor_t3t1_revE.py +++ b/core/site_scons/models/T3T1/trezor_t3t1_revE.py @@ -139,7 +139,6 @@ def configure( ("USE_STORAGE_HWKEY", "1"), ("USE_TAMPER", "1"), ("USE_FLASH_BURST", "1"), - ("USE_RESET_TO_BOOT", "1"), ("USE_OEM_KEYS_CHECK", "1"), ("USE_PVD", "1"), ] diff --git a/core/site_scons/models/T3W1/trezor_t3w1_revA.py b/core/site_scons/models/T3W1/trezor_t3w1_revA.py index 92e0e8190a..8f94eca4bb 100644 --- a/core/site_scons/models/T3W1/trezor_t3w1_revA.py +++ b/core/site_scons/models/T3W1/trezor_t3w1_revA.py @@ -223,7 +223,6 @@ def configure( ("USE_TAMPER", "1"), ("USE_FLASH_BURST", "1"), ("USE_OEM_KEYS_CHECK", "1"), - ("USE_RESET_TO_BOOT", "1"), ] sources += [ diff --git a/core/site_scons/models/T3W1/trezor_t3w1_revB.py b/core/site_scons/models/T3W1/trezor_t3w1_revB.py index cbc89f833e..6d81b310fd 100644 --- a/core/site_scons/models/T3W1/trezor_t3w1_revB.py +++ b/core/site_scons/models/T3W1/trezor_t3w1_revB.py @@ -228,7 +228,6 @@ def configure( ("USE_TAMPER", "1"), ("USE_FLASH_BURST", "1"), ("USE_OEM_KEYS_CHECK", "1"), - ("USE_RESET_TO_BOOT", "1"), ] sources += [ diff --git a/tools/style.c.exclude b/tools/style.c.exclude index 8d9ca1e4ca..11fed97082 100644 --- a/tools/style.c.exclude +++ b/tools/style.c.exclude @@ -1,4 +1,4 @@ -^\./core/embed/projects/bootloader/protob/ +^\./core/embed/projects/bootloader/protob/pb/ ^\./crypto/aes/ ^\./crypto/chacha20poly1305/ ^\./crypto/ed25519-donna/