1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-03-12 06:06:07 +00:00

refactor(core): complete bootloader refactoring

This commit is contained in:
tychovrahe 2025-01-31 17:11:40 +01:00 committed by M1nd3r
parent 29ed889375
commit 3b05b7603a
45 changed files with 2196 additions and 1349 deletions

View File

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

View File

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

View File

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

View File

@ -0,0 +1 @@
Major code clean-up and refactoring.

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <trezor_rtl.h>
// 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;
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <trezor_types.h>
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);

View File

@ -22,35 +22,13 @@
#include <io/display.h>
#include <io/display_utils.h>
#include <rtl/mini_printf.h>
#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); }

View File

@ -17,23 +17,32 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __BOOTUI_H__
#define __BOOTUI_H__
#pragma once
#include <trezor_types.h>
#include <util/image.h>
// 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

View File

@ -8,11 +8,9 @@
#include <io/display.h>
#include <sys/bootargs.h>
#include <sys/bootutils.h>
#include <sys/systick.h>
#include <util/flash.h>
#include <util/flash_otp.h>
#include "bootui.h"
#include "rust_ui.h"
#ifdef USE_OPTIGA
#include <sec/secret.h>
@ -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;
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <trezor_types.h>
#undef FIRMWARE_START
extern uint8_t *FIRMWARE_START;
#endif

View File

@ -20,27 +20,20 @@
#include <trezor_model.h>
#include <trezor_rtl.h>
#include <sys/types.h>
#include <gfx/gfx_bitblt.h>
#include <io/display.h>
#include <io/display_utils.h>
#include <sec/monoctr.h>
#include <sec/random_delays.h>
#include <sec/secret.h>
#include <sys/bootargs.h>
#include <sys/bootutils.h>
#include <sys/mpu.h>
#include <sys/system.h>
#include <sys/systick.h>
#include <sys/systimer.h>
#include <util/flash.h>
#include <sys/types.h>
#include <util/flash_otp.h>
#include <util/flash_utils.h>
#include <util/image.h>
#include <util/rsod.h>
#include <util/unit_properties.h>
#include "messages.pb.h"
#ifdef USE_PVD
#include <sys/pvd.h>
@ -67,27 +60,16 @@
#include <sys/tamper.h>
#endif
#include <io/usb.h>
#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();

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <trezor_model.h>
#include <trezor_rtl.h>
#include <pb.h>
#include <pb_decode.h>
#include <pb_encode.h>
#include "messages.pb.h"
#include <io/usb.h>
#include <sec/secret.h>
#include <sys/bootargs.h>
#include <util/flash.h>
#include <util/flash_utils.h>
#include <util/image.h>
#include <util/unit_properties.h>
#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 <sec/secret.h>
#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, &current_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(&current_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

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef __MESSAGES_H__
#define __MESSAGES_H__
#include <trezor_types.h>
#include <util/image.h>
#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

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <trezor_bsp.h>
#include "poll.h"
#include <io/usb.h>
#include <sys/systick.h>
#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;
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <trezor_types.h>
#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);

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <trezor_model.h>
#include <trezor_rtl.h>
#include <pb.h>
#include <pb_decode.h>
#include <util/image.h>
#include <util/unit_properties.h>
#if USE_OPTIGA
#include <sec/secret.h>
#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);
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <trezor_types.h>
#include <util/image.h>
#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);

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <trezor_model.h>
#include <trezor_rtl.h>
#include <pb.h>
#include <pb_decode.h>
#include <pb_encode.h>
#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);
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <trezor_types.h>
#include <pb.h>
#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);

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <trezor_model.h>
#include <trezor_rtl.h>
#include "wire_iface_usb.h"
#include <io/usb.h>
#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();
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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);

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <trezor_model.h>
#include <trezor_rtl.h>
#include <util/image.h>
#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;
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <trezor_model.h>
#include <trezor_rtl.h>
#include <sys/types.h>
#include <util/image.h>
#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;
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <trezor_model.h>
#include <trezor_rtl.h>
#include <sys/systick.h>
#include <sys/types.h>
#include <util/flash_utils.h>
#include <util/image.h>
#ifdef USE_STORAGE_HWKEY
#include <sec/secret.h>
#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;
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <trezor_model.h>
#include <trezor_rtl.h>
#include <sys/bootargs.h>
#include <sys/systick.h>
#include <util/flash.h>
#include <util/flash_utils.h>
#if USE_OPTIGA
#include <sec/secret.h>
#endif
#include <poll.h>
#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, &current_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(&current_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;
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <trezor_model.h>
#include <trezor_rtl.h>
#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;
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <trezor_model.h>
#include <trezor_rtl.h>
#include <sys/systick.h>
#include <sys/types.h>
#include <util/image.h>
#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;
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <trezor_model.h>
#include <trezor_rtl.h>
#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;
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <trezor_model.h>
#include <trezor_rtl.h>
#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;
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <trezor_model.h>
#include <trezor_rtl.h>
#include <sec/secret.h>
#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;
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <trezor_model.h>
#include <trezor_rtl.h>
#include <util/flash_utils.h>
#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;
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <trezor_types.h>
#include <util/image.h>
#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);

View File

@ -23,7 +23,7 @@
#include <pb.h>
#include <pb_decode.h>
#include <pb_encode.h>
#include "messages.pb.h"
#include "pb/messages.pb.h"
#include <io/usb.h>
#include <util/flash.h>

View File

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

View File

@ -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"),
]

View File

@ -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"),
]

View File

@ -223,7 +223,6 @@ def configure(
("USE_TAMPER", "1"),
("USE_FLASH_BURST", "1"),
("USE_OEM_KEYS_CHECK", "1"),
("USE_RESET_TO_BOOT", "1"),
]
sources += [

View File

@ -228,7 +228,6 @@ def configure(
("USE_TAMPER", "1"),
("USE_FLASH_BURST", "1"),
("USE_OEM_KEYS_CHECK", "1"),
("USE_RESET_TO_BOOT", "1"),
]
sources += [

View File

@ -1,4 +1,4 @@
^\./core/embed/projects/bootloader/protob/
^\./core/embed/projects/bootloader/protob/pb/
^\./crypto/aes/
^\./crypto/chacha20poly1305/
^\./crypto/ed25519-donna/