mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-03-12 14:16:06 +00:00
refactor(core): complete bootloader refactoring
This commit is contained in:
parent
29ed889375
commit
3b05b7603a
@ -105,11 +105,29 @@ SOURCE_BOOTLOADER = [
|
|||||||
'embed/projects/bootloader/header.S',
|
'embed/projects/bootloader/header.S',
|
||||||
'embed/projects/bootloader/bootui.c',
|
'embed/projects/bootloader/bootui.c',
|
||||||
'embed/projects/bootloader/main.c',
|
'embed/projects/bootloader/main.c',
|
||||||
'embed/projects/bootloader/messages.c',
|
'embed/projects/bootloader/poll.c',
|
||||||
'embed/projects/bootloader/protob/messages.pb.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/version_check.c',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
if 'optiga' in FEATURES_AVAILABLE:
|
||||||
|
SOURCE_BOOTLOADER += [
|
||||||
|
'embed/projects/bootloader/workflow/wf_unlock_bootloader.c',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
env.Replace(
|
env.Replace(
|
||||||
CAT='cat',
|
CAT='cat',
|
||||||
|
@ -99,7 +99,7 @@ SOURCE_BOOTLOADER = [
|
|||||||
'embed/projects/bootloader_ci/bootui.c',
|
'embed/projects/bootloader_ci/bootui.c',
|
||||||
'embed/projects/bootloader_ci/main.c',
|
'embed/projects/bootloader_ci/main.c',
|
||||||
'embed/projects/bootloader_ci/messages.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',
|
'embed/projects/bootloader_ci/version_check.c',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -99,10 +99,23 @@ SOURCE_NANOPB = [
|
|||||||
SOURCE_BOOTLOADER = [
|
SOURCE_BOOTLOADER = [
|
||||||
'embed/projects/bootloader/bootui.c',
|
'embed/projects/bootloader/bootui.c',
|
||||||
'embed/projects/bootloader/main.c',
|
'embed/projects/bootloader/main.c',
|
||||||
'embed/projects/bootloader/messages.c',
|
'embed/projects/bootloader/poll.c',
|
||||||
'embed/projects/bootloader/emulator.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/version_check.c',
|
||||||
'embed/projects/bootloader/protob/messages.pb.c',
|
'embed/projects/bootloader/emulator.c',
|
||||||
]
|
]
|
||||||
|
|
||||||
SOURCE_TREZORHAL = [
|
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)
|
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(
|
env.Replace(
|
||||||
CP='cp',
|
CP='cp',
|
||||||
AS='as',
|
AS='as',
|
||||||
|
1
core/embed/projects/bootloader/.changelog.d/4572.changed
Normal file
1
core/embed/projects/bootloader/.changelog.d/4572.changed
Normal file
@ -0,0 +1 @@
|
|||||||
|
Major code clean-up and refactoring.
|
35
core/embed/projects/bootloader/antiglitch.c
Normal file
35
core/embed/projects/bootloader/antiglitch.c
Normal 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;
|
||||||
|
}
|
30
core/embed/projects/bootloader/antiglitch.h
Normal file
30
core/embed/projects/bootloader/antiglitch.h
Normal 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);
|
@ -22,35 +22,13 @@
|
|||||||
#include <io/display.h>
|
#include <io/display.h>
|
||||||
#include <io/display_utils.h>
|
#include <io/display_utils.h>
|
||||||
#include <rtl/mini_printf.h>
|
#include <rtl/mini_printf.h>
|
||||||
|
|
||||||
#include "bootui.h"
|
#include "bootui.h"
|
||||||
#include "rust_ui.h"
|
#include "rust_ui.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
||||||
#define BACKLIGHT_NORMAL 150
|
#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)
|
#define TOIF_LENGTH(ptr) ((*(uint32_t *)((ptr) + 8)) + 12)
|
||||||
|
|
||||||
// common shared functions
|
// common shared functions
|
||||||
@ -139,13 +117,15 @@ uint32_t ui_screen_menu(secbool firmware_present) {
|
|||||||
return screen_menu(firmware_present);
|
return screen_menu(firmware_present);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ui_screen_connect(void) { screen_connect(initial_setup); }
|
||||||
|
|
||||||
// install UI
|
// install UI
|
||||||
|
|
||||||
uint32_t ui_screen_install_confirm(const vendor_header *const vhdr,
|
ui_result_t ui_screen_install_confirm(const vendor_header *const vhdr,
|
||||||
const image_header *const hdr,
|
const image_header *const hdr,
|
||||||
secbool should_keep_seed,
|
secbool should_keep_seed,
|
||||||
secbool is_newvendor, secbool is_newinstall,
|
secbool is_newvendor,
|
||||||
int version_cmp) {
|
secbool is_newinstall, int version_cmp) {
|
||||||
uint8_t fingerprint[32];
|
uint8_t fingerprint[32];
|
||||||
char ver_str[64];
|
char ver_str[64];
|
||||||
get_image_fingerprint(hdr, fingerprint);
|
get_image_fingerprint(hdr, fingerprint);
|
||||||
@ -171,7 +151,7 @@ void ui_screen_install_progress_upload(int pos) {
|
|||||||
|
|
||||||
// wipe UI
|
// 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); }
|
void ui_screen_wipe(void) { screen_wipe_progress(0, true); }
|
||||||
|
|
||||||
|
@ -17,23 +17,32 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __BOOTUI_H__
|
#pragma once
|
||||||
#define __BOOTUI_H__
|
|
||||||
|
|
||||||
#include <trezor_types.h>
|
#include <trezor_types.h>
|
||||||
|
|
||||||
#include <util/image.h>
|
#include <util/image.h>
|
||||||
|
|
||||||
|
// todo: use bindgen to tie this to rust
|
||||||
typedef enum {
|
typedef enum {
|
||||||
SCREEN_INTRO = 0,
|
UI_RESULT_CANCEL = 1,
|
||||||
SCREEN_MENU = 1,
|
UI_RESULT_CONFIRM = 2,
|
||||||
SCREEN_WIPE_CONFIRM = 2,
|
} ui_result_t;
|
||||||
SCREEN_FINGER_PRINT = 3,
|
|
||||||
SCREEN_WAIT_FOR_HOST = 4,
|
|
||||||
SCREEN_WELCOME = 5,
|
|
||||||
} screen_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
|
// Shows vendor image, vendor string and firmware version
|
||||||
// and optional message to the user (see `wait` argument)
|
// 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_menu(secbool firmware_present);
|
||||||
|
|
||||||
uint32_t ui_screen_install_confirm(const vendor_header* const vhdr,
|
void ui_screen_connect(void);
|
||||||
const image_header* const hdr,
|
|
||||||
secbool shold_keep_seed,
|
ui_result_t ui_screen_install_confirm(const vendor_header* const vhdr,
|
||||||
secbool is_newvendor, secbool is_newinstall,
|
const image_header* const hdr,
|
||||||
int version_cmp);
|
secbool shold_keep_seed,
|
||||||
|
secbool is_newvendor,
|
||||||
|
secbool is_newinstall, int version_cmp);
|
||||||
void ui_screen_install_start();
|
void ui_screen_install_start();
|
||||||
void ui_screen_install_progress_erase(int pos, int len);
|
void ui_screen_install_progress_erase(int pos, int len);
|
||||||
void ui_screen_install_progress_upload(int pos);
|
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(void);
|
||||||
void ui_screen_wipe_progress(int pos, int len);
|
void ui_screen_wipe_progress(int pos, int len);
|
||||||
|
|
||||||
@ -84,12 +95,3 @@ void ui_screen_boot_stage_1(bool fading);
|
|||||||
#ifdef USE_OPTIGA
|
#ifdef USE_OPTIGA
|
||||||
uint32_t ui_screen_unlock_bootloader_confirm(void);
|
uint32_t ui_screen_unlock_bootloader_confirm(void);
|
||||||
#endif
|
#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
|
|
||||||
|
@ -8,11 +8,9 @@
|
|||||||
#include <io/display.h>
|
#include <io/display.h>
|
||||||
#include <sys/bootargs.h>
|
#include <sys/bootargs.h>
|
||||||
#include <sys/bootutils.h>
|
#include <sys/bootutils.h>
|
||||||
#include <sys/systick.h>
|
|
||||||
#include <util/flash.h>
|
#include <util/flash.h>
|
||||||
#include <util/flash_otp.h>
|
#include <util/flash_otp.h>
|
||||||
#include "bootui.h"
|
#include "bootui.h"
|
||||||
#include "rust_ui.h"
|
|
||||||
|
|
||||||
#ifdef USE_OPTIGA
|
#ifdef USE_OPTIGA
|
||||||
#include <sec/secret.h>
|
#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,
|
(void)!flash_otp_write(FLASH_OTP_BLOCK_DEVICE_VARIANT, 0, otp_data,
|
||||||
sizeof(otp_data));
|
sizeof(otp_data));
|
||||||
|
|
||||||
bootloader_main();
|
int exit_code = bootloader_main();
|
||||||
hal_delay(3000);
|
|
||||||
jump_to_next_stage(0);
|
|
||||||
|
|
||||||
|
char msg[64];
|
||||||
|
snprintf(msg, sizeof(msg), "Exit code: %d", exit_code);
|
||||||
|
|
||||||
|
error_shutdown_ex("BOOTLOADER ERROR", msg, "UNEXPECTED EXIT");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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>
|
#include <trezor_types.h>
|
||||||
|
|
||||||
#undef FIRMWARE_START
|
#undef FIRMWARE_START
|
||||||
|
|
||||||
extern uint8_t *FIRMWARE_START;
|
extern uint8_t *FIRMWARE_START;
|
||||||
|
|
||||||
#endif
|
|
||||||
|
@ -20,27 +20,20 @@
|
|||||||
#include <trezor_model.h>
|
#include <trezor_model.h>
|
||||||
#include <trezor_rtl.h>
|
#include <trezor_rtl.h>
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#include <gfx/gfx_bitblt.h>
|
|
||||||
#include <io/display.h>
|
#include <io/display.h>
|
||||||
#include <io/display_utils.h>
|
#include <io/display_utils.h>
|
||||||
#include <sec/monoctr.h>
|
|
||||||
#include <sec/random_delays.h>
|
#include <sec/random_delays.h>
|
||||||
#include <sec/secret.h>
|
#include <sec/secret.h>
|
||||||
#include <sys/bootargs.h>
|
#include <sys/bootargs.h>
|
||||||
#include <sys/bootutils.h>
|
#include <sys/bootutils.h>
|
||||||
#include <sys/mpu.h>
|
|
||||||
#include <sys/system.h>
|
#include <sys/system.h>
|
||||||
#include <sys/systick.h>
|
#include <sys/systick.h>
|
||||||
#include <sys/systimer.h>
|
#include <sys/types.h>
|
||||||
#include <util/flash.h>
|
|
||||||
#include <util/flash_otp.h>
|
#include <util/flash_otp.h>
|
||||||
#include <util/flash_utils.h>
|
#include <util/flash_utils.h>
|
||||||
#include <util/image.h>
|
#include <util/image.h>
|
||||||
#include <util/rsod.h>
|
#include <util/rsod.h>
|
||||||
#include <util/unit_properties.h>
|
#include <util/unit_properties.h>
|
||||||
#include "messages.pb.h"
|
|
||||||
|
|
||||||
#ifdef USE_PVD
|
#ifdef USE_PVD
|
||||||
#include <sys/pvd.h>
|
#include <sys/pvd.h>
|
||||||
@ -67,27 +60,16 @@
|
|||||||
#include <sys/tamper.h>
|
#include <sys/tamper.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <io/usb.h>
|
#include "antiglitch.h"
|
||||||
#include "version.h"
|
|
||||||
|
|
||||||
#include "bootui.h"
|
#include "bootui.h"
|
||||||
#include "messages.h"
|
|
||||||
#include "rust_ui.h"
|
|
||||||
#include "version_check.h"
|
#include "version_check.h"
|
||||||
|
#include "workflow/workflow.h"
|
||||||
|
|
||||||
#ifdef TREZOR_EMULATOR
|
#ifdef TREZOR_EMULATOR
|
||||||
#include "SDL.h"
|
#include "SDL.h"
|
||||||
#include "emulator.h"
|
#include "emulator.h"
|
||||||
#endif
|
#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);
|
void failed_jump_to_firmware(void);
|
||||||
|
|
||||||
CONFIDENTIAL volatile secbool dont_optimize_out_true = sectrue;
|
CONFIDENTIAL volatile secbool dont_optimize_out_true = sectrue;
|
||||||
@ -154,158 +136,6 @@ static void drivers_deinit(void) {
|
|||||||
display_deinit(DISPLAY_JUMP_BEHAVIOR);
|
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) {
|
static secbool check_vendor_header_lock(const vendor_header *const vhdr) {
|
||||||
uint8_t lock[FLASH_OTP_BLOCK_SIZE];
|
uint8_t lock[FLASH_OTP_BLOCK_SIZE];
|
||||||
ensure(flash_otp_read(FLASH_OTP_BLOCK_VENDOR_HEADER_LOCK, 0, lock,
|
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));
|
IMAGE_CODE_ALIGN(FIRMWARE_START + vhdr.hdrlen + IMAGE_HEADER_SIZE));
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_RESET_TO_BOOT
|
|
||||||
__attribute__((noreturn)) void jump_to_fw_through_reset(void) {
|
__attribute__((noreturn)) void jump_to_fw_through_reset(void) {
|
||||||
display_fade(display_get_backlight(), 0, 200);
|
display_fade(display_get_backlight(), 0, 200);
|
||||||
|
|
||||||
reboot_device();
|
reboot_device();
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef TREZOR_EMULATOR
|
#ifndef TREZOR_EMULATOR
|
||||||
int main(void) {
|
int main(void) {
|
||||||
@ -551,143 +378,47 @@ int bootloader_main(void) {
|
|||||||
// ... or there is no valid firmware
|
// ... or there is no valid firmware
|
||||||
if (touched || stay_in_bootloader == sectrue || firmware_present != sectrue ||
|
if (touched || stay_in_bootloader == sectrue || firmware_present != sectrue ||
|
||||||
auto_upgrade == sectrue) {
|
auto_upgrade == sectrue) {
|
||||||
screen_t screen;
|
workflow_result_t result;
|
||||||
ui_set_initial_setup(true);
|
|
||||||
|
jump_reset();
|
||||||
if (header_present == sectrue) {
|
if (header_present == sectrue) {
|
||||||
if (auto_upgrade == sectrue) {
|
if (auto_upgrade == sectrue) {
|
||||||
screen = SCREEN_WAIT_FOR_HOST;
|
result = workflow_auto_update(&vhdr, hdr);
|
||||||
} else {
|
} else {
|
||||||
ui_set_initial_setup(false);
|
result = workflow_bootloader(&vhdr, hdr, firmware_present);
|
||||||
screen = SCREEN_INTRO;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
screen = SCREEN_WELCOME;
|
result = workflow_empty_device();
|
||||||
|
|
||||||
#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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
while (true) {
|
switch (result) {
|
||||||
volatile secbool continue_to_firmware = secfalse;
|
case WF_OK_FIRMWARE_INSTALLED:
|
||||||
volatile secbool continue_to_firmware_backup = secfalse;
|
firmware_present = sectrue;
|
||||||
uint32_t ui_result = 0;
|
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) {
|
ensure(dont_optimize_out_true *
|
||||||
case SCREEN_WELCOME:
|
(firmware_present == firmware_present_backup),
|
||||||
|
NULL);
|
||||||
ui_screen_welcome();
|
jump_to_fw_through_reset();
|
||||||
|
break;
|
||||||
// and start the usb loop
|
case WF_OK_DEVICE_WIPED:
|
||||||
switch (bootloader_usb_loop(NULL, NULL)) {
|
case WF_OK_BOOTLOADER_UNLOCKED:
|
||||||
case CONTINUE_TO_FIRMWARE:
|
case WF_ERROR:
|
||||||
continue_to_firmware = sectrue;
|
reboot_or_halt_after_rsod();
|
||||||
continue_to_firmware_backup = sectrue;
|
return 0;
|
||||||
break;
|
case WF_ERROR_FATAL:
|
||||||
case RETURN_TO_MENU:
|
default: {
|
||||||
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) {
|
|
||||||
// erase storage if we saw flips randomly flip, most likely due to
|
// erase storage if we saw flips randomly flip, most likely due to
|
||||||
// glitch
|
// glitch
|
||||||
|
|
||||||
#ifdef USE_STORAGE_HWKEY
|
#ifdef USE_STORAGE_HWKEY
|
||||||
secret_bhk_regenerate();
|
secret_bhk_regenerate();
|
||||||
#endif
|
#endif
|
||||||
ensure(erase_storage(NULL), NULL);
|
ensure(erase_storage(NULL), NULL);
|
||||||
}
|
error_shutdown("Bootloader fatal error");
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -695,16 +426,9 @@ int bootloader_main(void) {
|
|||||||
ensure(dont_optimize_out_true * (firmware_present == firmware_present_backup),
|
ensure(dont_optimize_out_true * (firmware_present == firmware_present_backup),
|
||||||
NULL);
|
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) {
|
if (sectrue == firmware_present) {
|
||||||
firmware_jump_fn = real_jump_to_firmware;
|
firmware_jump_fn = real_jump_to_firmware;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
firmware_jump_fn();
|
firmware_jump_fn();
|
||||||
|
|
||||||
|
@ -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, ¤t_vhdr)) {
|
|
||||||
is_new = sectrue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const image_header *current_hdr = NULL;
|
|
||||||
|
|
||||||
if (is_new == secfalse) {
|
|
||||||
current_hdr = read_image_header(
|
|
||||||
(const uint8_t *)FIRMWARE_START + current_vhdr.hdrlen,
|
|
||||||
FIRMWARE_IMAGE_MAGIC, FIRMWARE_MAXSIZE);
|
|
||||||
|
|
||||||
if (current_hdr !=
|
|
||||||
(const image_header *)(FIRMWARE_START + current_vhdr.hdrlen)) {
|
|
||||||
is_new = sectrue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
secbool should_keep_seed = secfalse;
|
|
||||||
secbool is_newvendor = secfalse;
|
|
||||||
secbool is_upgrade = secfalse;
|
|
||||||
if (is_new == secfalse) {
|
|
||||||
detect_installation(¤t_vhdr, current_hdr, &vhdr, &hdr, &is_new,
|
|
||||||
&should_keep_seed, &is_newvendor, &is_upgrade);
|
|
||||||
}
|
|
||||||
|
|
||||||
secbool is_ilu = secfalse; // interaction-less update
|
|
||||||
|
|
||||||
if (bootargs_get_command() == BOOT_COMMAND_INSTALL_UPGRADE) {
|
|
||||||
IMAGE_HASH_CTX ctx;
|
|
||||||
uint8_t hash[IMAGE_HASH_DIGEST_LENGTH];
|
|
||||||
IMAGE_HASH_INIT(&ctx);
|
|
||||||
IMAGE_HASH_UPDATE(&ctx, CHUNK_BUFFER_PTR,
|
|
||||||
vhdr.hdrlen + received_hdr->hdrlen);
|
|
||||||
IMAGE_HASH_FINAL(&ctx, hash);
|
|
||||||
|
|
||||||
// the firmware must be the same as confirmed by the user
|
|
||||||
boot_args_t args = {0};
|
|
||||||
bootargs_get_args(&args);
|
|
||||||
|
|
||||||
if (memcmp(args.hash, hash, sizeof(hash)) != 0) {
|
|
||||||
MSG_SEND_INIT(Failure);
|
|
||||||
MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError);
|
|
||||||
MSG_SEND_ASSIGN_STRING(message, "Firmware mismatch");
|
|
||||||
MSG_SEND(Failure);
|
|
||||||
return UPLOAD_ERR_FIRMWARE_MISMATCH;
|
|
||||||
}
|
|
||||||
|
|
||||||
// the firmware must be from the same vendor
|
|
||||||
// the firmware must be newer
|
|
||||||
if (is_upgrade != sectrue || is_newvendor != secfalse) {
|
|
||||||
MSG_SEND_INIT(Failure);
|
|
||||||
MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError);
|
|
||||||
MSG_SEND_ASSIGN_STRING(message, "Not a firmware upgrade");
|
|
||||||
MSG_SEND(Failure);
|
|
||||||
return UPLOAD_ERR_NOT_FIRMWARE_UPGRADE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((vhdr.vtrust & VTRUST_NO_WARNING) != VTRUST_NO_WARNING) {
|
|
||||||
MSG_SEND_INIT(Failure);
|
|
||||||
MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError);
|
|
||||||
MSG_SEND_ASSIGN_STRING(message, "Not a full-trust image");
|
|
||||||
MSG_SEND(Failure);
|
|
||||||
return UPLOAD_ERR_NOT_FULLTRUST_IMAGE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// upload the firmware without confirmation
|
|
||||||
is_ilu = sectrue;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined USE_OPTIGA
|
|
||||||
if (secfalse != secret_optiga_present() &&
|
|
||||||
((vhdr.vtrust & VTRUST_SECRET_MASK) != VTRUST_SECRET_ALLOW)) {
|
|
||||||
MSG_SEND_INIT(Failure);
|
|
||||||
MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError);
|
|
||||||
MSG_SEND_ASSIGN_STRING(message, "Install restricted");
|
|
||||||
MSG_SEND(Failure);
|
|
||||||
return UPLOAD_ERR_BOOTLOADER_LOCKED;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
uint32_t response = INPUT_CANCEL;
|
|
||||||
if (((vhdr.vtrust & VTRUST_NO_WARNING) == VTRUST_NO_WARNING) &&
|
|
||||||
(sectrue == is_new || sectrue == is_ilu)) {
|
|
||||||
// new installation or interaction less updated - auto confirm
|
|
||||||
// only allowed for full-trust images
|
|
||||||
response = INPUT_CONFIRM;
|
|
||||||
} else {
|
|
||||||
if (sectrue != is_new) {
|
|
||||||
int version_cmp = version_compare(hdr.version, current_hdr->version);
|
|
||||||
response = ui_screen_install_confirm(
|
|
||||||
&vhdr, &hdr, should_keep_seed, is_newvendor, is_new, version_cmp);
|
|
||||||
} else {
|
|
||||||
response = ui_screen_install_confirm(&vhdr, &hdr, sectrue,
|
|
||||||
is_newvendor, is_new, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (INPUT_CANCEL == response) {
|
|
||||||
send_user_abort(iface_num, "Firmware install cancelled");
|
|
||||||
return UPLOAD_ERR_USER_ABORT;
|
|
||||||
}
|
|
||||||
|
|
||||||
ui_screen_install_start();
|
|
||||||
|
|
||||||
// if firmware is not upgrade, erase storage
|
|
||||||
if (sectrue != should_keep_seed) {
|
|
||||||
#ifdef USE_STORAGE_HWKEY
|
|
||||||
secret_bhk_regenerate();
|
|
||||||
#endif
|
|
||||||
ensure(erase_storage(NULL), NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
headers_offset = IMAGE_HEADER_SIZE + vhdr.hdrlen;
|
|
||||||
read_offset = IMAGE_INIT_CHUNK_SIZE;
|
|
||||||
|
|
||||||
// request the rest of the first chunk
|
|
||||||
MSG_SEND_INIT(FirmwareRequest);
|
|
||||||
uint32_t chunk_limit = (firmware_remaining > IMAGE_CHUNK_SIZE)
|
|
||||||
? IMAGE_CHUNK_SIZE
|
|
||||||
: firmware_remaining;
|
|
||||||
chunk_requested = chunk_limit - read_offset;
|
|
||||||
MSG_SEND_ASSIGN_REQUIRED_VALUE(offset, read_offset);
|
|
||||||
MSG_SEND_ASSIGN_REQUIRED_VALUE(length, chunk_requested);
|
|
||||||
MSG_SEND(FirmwareRequest);
|
|
||||||
|
|
||||||
firmware_remaining -= read_offset;
|
|
||||||
return (int)firmware_remaining;
|
|
||||||
} else {
|
|
||||||
// first block with the headers parsed -> the first chunk is now complete
|
|
||||||
read_offset = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// should not happen, but double-check
|
|
||||||
if (flash_area_get_address(&FIRMWARE_AREA, firmware_block * IMAGE_CHUNK_SIZE,
|
|
||||||
0) == NULL) {
|
|
||||||
MSG_SEND_INIT(Failure);
|
|
||||||
MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError);
|
|
||||||
MSG_SEND_ASSIGN_STRING(message, "Firmware too big");
|
|
||||||
MSG_SEND(Failure);
|
|
||||||
return UPLOAD_ERR_FIRMWARE_TOO_BIG;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sectrue != check_single_hash(hdr.hashes + firmware_block * 32,
|
|
||||||
CHUNK_BUFFER_PTR + headers_offset,
|
|
||||||
chunk_size - headers_offset)) {
|
|
||||||
if (firmware_upload_chunk_retry > 0) {
|
|
||||||
--firmware_upload_chunk_retry;
|
|
||||||
MSG_SEND_INIT(FirmwareRequest);
|
|
||||||
MSG_SEND_ASSIGN_REQUIRED_VALUE(offset, firmware_block * IMAGE_CHUNK_SIZE);
|
|
||||||
MSG_SEND_ASSIGN_REQUIRED_VALUE(length, chunk_requested);
|
|
||||||
MSG_SEND(FirmwareRequest);
|
|
||||||
return (int)firmware_remaining;
|
|
||||||
}
|
|
||||||
|
|
||||||
MSG_SEND_INIT(Failure);
|
|
||||||
MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError);
|
|
||||||
MSG_SEND_ASSIGN_STRING(message, "Invalid chunk hash");
|
|
||||||
MSG_SEND(Failure);
|
|
||||||
return UPLOAD_ERR_INVALID_CHUNK_HASH;
|
|
||||||
}
|
|
||||||
|
|
||||||
// buffer with the received data
|
|
||||||
const uint32_t *src = (const uint32_t *)CHUNK_BUFFER_PTR;
|
|
||||||
// number of received bytes
|
|
||||||
uint32_t bytes_remaining = chunk_size;
|
|
||||||
// offset into the FIRMWARE_AREA part of the flash
|
|
||||||
uint32_t write_offset = firmware_block * IMAGE_CHUNK_SIZE;
|
|
||||||
|
|
||||||
ensure((chunk_size % FLASH_BLOCK_SIZE == 0) * sectrue, NULL);
|
|
||||||
|
|
||||||
while (bytes_remaining > 0) {
|
|
||||||
// erase flash before writing
|
|
||||||
uint32_t bytes_erased = 0;
|
|
||||||
|
|
||||||
if (write_offset >= erase_offset) {
|
|
||||||
// erase the next flash section
|
|
||||||
ensure(
|
|
||||||
flash_area_erase_partial(&FIRMWARE_AREA, erase_offset, &bytes_erased),
|
|
||||||
NULL);
|
|
||||||
erase_offset += bytes_erased;
|
|
||||||
} else {
|
|
||||||
// some erased space left from the previous round => use it
|
|
||||||
bytes_erased = erase_offset - write_offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
// write the received data
|
|
||||||
uint32_t bytes_to_write = MIN(bytes_erased, bytes_remaining);
|
|
||||||
ensure(flash_unlock_write(), NULL);
|
|
||||||
ensure(flash_area_write_data(&FIRMWARE_AREA, write_offset, src,
|
|
||||||
bytes_to_write),
|
|
||||||
NULL);
|
|
||||||
ensure(flash_lock_write(), NULL);
|
|
||||||
|
|
||||||
write_offset += bytes_to_write;
|
|
||||||
src += bytes_to_write / sizeof(uint32_t);
|
|
||||||
|
|
||||||
bytes_remaining -= bytes_to_write;
|
|
||||||
}
|
|
||||||
|
|
||||||
firmware_remaining -= chunk_requested;
|
|
||||||
|
|
||||||
if (firmware_remaining == 0) {
|
|
||||||
// erase the rest (unused part) of the FIRMWARE_AREA
|
|
||||||
uint32_t bytes_erased = 0;
|
|
||||||
do {
|
|
||||||
ensure(
|
|
||||||
flash_area_erase_partial(&FIRMWARE_AREA, erase_offset, &bytes_erased),
|
|
||||||
NULL);
|
|
||||||
erase_offset += bytes_erased;
|
|
||||||
} while (bytes_erased > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
headers_offset = 0;
|
|
||||||
firmware_block++;
|
|
||||||
firmware_upload_chunk_retry = FIRMWARE_UPLOAD_CHUNK_RETRY_COUNT;
|
|
||||||
|
|
||||||
if (firmware_remaining > 0) {
|
|
||||||
chunk_requested = (firmware_remaining > IMAGE_CHUNK_SIZE)
|
|
||||||
? IMAGE_CHUNK_SIZE
|
|
||||||
: firmware_remaining;
|
|
||||||
MSG_SEND_INIT(FirmwareRequest);
|
|
||||||
MSG_SEND_ASSIGN_REQUIRED_VALUE(offset, firmware_block * IMAGE_CHUNK_SIZE);
|
|
||||||
MSG_SEND_ASSIGN_REQUIRED_VALUE(length, chunk_requested);
|
|
||||||
MSG_SEND(FirmwareRequest);
|
|
||||||
} else {
|
|
||||||
MSG_SEND_INIT(Success);
|
|
||||||
MSG_SEND(Success);
|
|
||||||
}
|
|
||||||
return (int)firmware_remaining;
|
|
||||||
}
|
|
||||||
|
|
||||||
secbool bootloader_WipeDevice(void) {
|
|
||||||
return erase_device(ui_screen_wipe_progress);
|
|
||||||
}
|
|
||||||
|
|
||||||
int process_msg_WipeDevice(uint8_t iface_num, uint32_t msg_size, uint8_t *buf) {
|
|
||||||
secbool wipe_result = bootloader_WipeDevice();
|
|
||||||
if (sectrue != wipe_result) {
|
|
||||||
MSG_SEND_INIT(Failure);
|
|
||||||
MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_ProcessError);
|
|
||||||
MSG_SEND_ASSIGN_STRING(message, "Could not erase flash");
|
|
||||||
MSG_SEND(Failure);
|
|
||||||
return WIPE_ERR_CANNOT_ERASE;
|
|
||||||
} else {
|
|
||||||
MSG_SEND_INIT(Success);
|
|
||||||
MSG_SEND(Success);
|
|
||||||
return WIPE_OK;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void process_msg_unknown(uint8_t iface_num, uint32_t msg_size, uint8_t *buf) {
|
|
||||||
// consume remaining message
|
|
||||||
int remaining_chunks = 0;
|
|
||||||
|
|
||||||
if (msg_size > (USB_PACKET_SIZE - MSG_HEADER1_LEN)) {
|
|
||||||
// calculate how many blocks need to be read to drain the message (rounded
|
|
||||||
// up to not leave any behind)
|
|
||||||
remaining_chunks = (msg_size - (USB_PACKET_SIZE - MSG_HEADER1_LEN) +
|
|
||||||
((USB_PACKET_SIZE - MSG_HEADER2_LEN) - 1)) /
|
|
||||||
(USB_PACKET_SIZE - MSG_HEADER2_LEN);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < remaining_chunks; i++) {
|
|
||||||
// read next packet (with retry)
|
|
||||||
_usb_webusb_read_retry(iface_num, buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
MSG_SEND_INIT(Failure);
|
|
||||||
MSG_SEND_ASSIGN_VALUE(code, FailureType_Failure_UnexpectedMessage);
|
|
||||||
MSG_SEND_ASSIGN_STRING(message, "Unexpected message");
|
|
||||||
MSG_SEND(Failure);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined USE_OPTIGA
|
|
||||||
void process_msg_UnlockBootloader(uint8_t iface_num, uint32_t msg_size,
|
|
||||||
uint8_t *buf) {
|
|
||||||
secret_optiga_erase();
|
|
||||||
MSG_SEND_INIT(Success);
|
|
||||||
MSG_SEND(Success);
|
|
||||||
}
|
|
||||||
#endif
|
|
@ -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
|
|
62
core/embed/projects/bootloader/poll.c
Normal file
62
core/embed/projects/bootloader/poll.c
Normal 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;
|
||||||
|
}
|
39
core/embed/projects/bootloader/poll.h
Normal file
39
core/embed/projects/bootloader/poll.h
Normal 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);
|
245
core/embed/projects/bootloader/protob/protob.c
Normal file
245
core/embed/projects/bootloader/protob/protob.c
Normal 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);
|
||||||
|
}
|
68
core/embed/projects/bootloader/protob/protob.h
Normal file
68
core/embed/projects/bootloader/protob/protob.h
Normal 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);
|
248
core/embed/projects/bootloader/wire/codec_v1.c
Normal file
248
core/embed/projects/bootloader/wire/codec_v1.c
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
54
core/embed/projects/bootloader/wire/codec_v1.h
Normal file
54
core/embed/projects/bootloader/wire/codec_v1.h
Normal 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);
|
113
core/embed/projects/bootloader/wire/wire_iface_usb.c
Normal file
113
core/embed/projects/bootloader/wire/wire_iface_usb.c
Normal 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();
|
||||||
|
}
|
26
core/embed/projects/bootloader/wire/wire_iface_usb.h
Normal file
26
core/embed/projects/bootloader/wire/wire_iface_usb.h
Normal 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);
|
37
core/embed/projects/bootloader/workflow/wf_auto_update.c
Normal file
37
core/embed/projects/bootloader/workflow/wf_auto_update.c
Normal 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;
|
||||||
|
}
|
99
core/embed/projects/bootloader/workflow/wf_bootloader.c
Normal file
99
core/embed/projects/bootloader/workflow/wf_bootloader.c
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
57
core/embed/projects/bootloader/workflow/wf_empty_device.c
Normal file
57
core/embed/projects/bootloader/workflow/wf_empty_device.c
Normal 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;
|
||||||
|
}
|
561
core/embed/projects/bootloader/workflow/wf_firmware_update.c
Normal file
561
core/embed/projects/bootloader/workflow/wf_firmware_update.c
Normal 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, ¤t_vhdr)) {
|
||||||
|
is_new = sectrue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const image_header *current_hdr = NULL;
|
||||||
|
|
||||||
|
if (is_new == secfalse) {
|
||||||
|
current_hdr = read_image_header(
|
||||||
|
(const uint8_t *)FIRMWARE_START + current_vhdr.hdrlen,
|
||||||
|
FIRMWARE_IMAGE_MAGIC, FIRMWARE_MAXSIZE);
|
||||||
|
|
||||||
|
if (current_hdr !=
|
||||||
|
(const image_header *)(void *)(FIRMWARE_START +
|
||||||
|
current_vhdr.hdrlen)) {
|
||||||
|
is_new = sectrue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
secbool should_keep_seed = secfalse;
|
||||||
|
secbool is_newvendor = secfalse;
|
||||||
|
secbool is_upgrade = secfalse;
|
||||||
|
if (is_new == secfalse) {
|
||||||
|
detect_installation(¤t_vhdr, current_hdr, &vhdr, &hdr, &is_new,
|
||||||
|
&should_keep_seed, &is_newvendor, &is_upgrade);
|
||||||
|
}
|
||||||
|
|
||||||
|
secbool is_ilu = secfalse; // interaction-less update
|
||||||
|
|
||||||
|
if (bootargs_get_command() == BOOT_COMMAND_INSTALL_UPGRADE) {
|
||||||
|
IMAGE_HASH_CTX ctx;
|
||||||
|
uint8_t hash[IMAGE_HASH_DIGEST_LENGTH];
|
||||||
|
IMAGE_HASH_INIT(&ctx);
|
||||||
|
IMAGE_HASH_UPDATE(&ctx, (uint8_t *)chunk_buffer,
|
||||||
|
vhdr.hdrlen + received_hdr->hdrlen);
|
||||||
|
IMAGE_HASH_FINAL(&ctx, hash);
|
||||||
|
|
||||||
|
// the firmware must be the same as confirmed by the user
|
||||||
|
boot_args_t args = {0};
|
||||||
|
bootargs_get_args(&args);
|
||||||
|
|
||||||
|
if (memcmp(args.hash, hash, sizeof(hash)) != 0) {
|
||||||
|
send_msg_failure(iface, FailureType_Failure_ProcessError,
|
||||||
|
"Firmware mismatch");
|
||||||
|
return UPLOAD_ERR_FIRMWARE_MISMATCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the firmware must be from the same vendor
|
||||||
|
// the firmware must be newer
|
||||||
|
if (is_upgrade != sectrue || is_newvendor != secfalse) {
|
||||||
|
send_msg_failure(iface, FailureType_Failure_ProcessError,
|
||||||
|
"Not a firmware upgrade");
|
||||||
|
return UPLOAD_ERR_NOT_FIRMWARE_UPGRADE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((vhdr.vtrust & VTRUST_NO_WARNING) != VTRUST_NO_WARNING) {
|
||||||
|
send_msg_failure(iface, FailureType_Failure_ProcessError,
|
||||||
|
"Not a full-trust image");
|
||||||
|
return UPLOAD_ERR_NOT_FULLTRUST_IMAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// upload the firmware without confirmation
|
||||||
|
is_ilu = sectrue;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined USE_OPTIGA
|
||||||
|
if (secfalse != secret_optiga_present() &&
|
||||||
|
((vhdr.vtrust & VTRUST_SECRET_MASK) != VTRUST_SECRET_ALLOW)) {
|
||||||
|
send_msg_failure(iface, FailureType_Failure_ProcessError,
|
||||||
|
"Install restricted");
|
||||||
|
return UPLOAD_ERR_BOOTLOADER_LOCKED;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ui_result_t response = UI_RESULT_CANCEL;
|
||||||
|
if (((vhdr.vtrust & VTRUST_NO_WARNING) == VTRUST_NO_WARNING) &&
|
||||||
|
(sectrue == is_new || sectrue == is_ilu)) {
|
||||||
|
// new installation or interaction less updated - auto confirm
|
||||||
|
// only allowed for full-trust images
|
||||||
|
response = UI_RESULT_CONFIRM;
|
||||||
|
} else {
|
||||||
|
if (sectrue != is_new) {
|
||||||
|
int version_cmp = version_compare(hdr.version, current_hdr->version);
|
||||||
|
response = ui_screen_install_confirm(
|
||||||
|
&vhdr, &hdr, should_keep_seed, is_newvendor, is_new, version_cmp);
|
||||||
|
} else {
|
||||||
|
response = ui_screen_install_confirm(&vhdr, &hdr, sectrue,
|
||||||
|
is_newvendor, is_new, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UI_RESULT_CONFIRM != response) {
|
||||||
|
send_user_abort(iface, "Firmware install cancelled");
|
||||||
|
return UPLOAD_ERR_USER_ABORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
ui_screen_install_start();
|
||||||
|
|
||||||
|
// if firmware is not upgrade, erase storage
|
||||||
|
if (sectrue != should_keep_seed) {
|
||||||
|
#ifdef USE_STORAGE_HWKEY
|
||||||
|
secret_bhk_regenerate();
|
||||||
|
#endif
|
||||||
|
ensure(erase_storage(NULL), NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->headers_offset = IMAGE_HEADER_SIZE + vhdr.hdrlen;
|
||||||
|
ctx->read_offset = IMAGE_INIT_CHUNK_SIZE;
|
||||||
|
|
||||||
|
// request the rest of the first chunk
|
||||||
|
uint32_t chunk_limit = (ctx->firmware_remaining > IMAGE_CHUNK_SIZE)
|
||||||
|
? IMAGE_CHUNK_SIZE
|
||||||
|
: ctx->firmware_remaining;
|
||||||
|
ctx->chunk_requested = chunk_limit - ctx->read_offset;
|
||||||
|
|
||||||
|
if (sectrue != send_msg_request_firmware(iface, ctx->read_offset,
|
||||||
|
ctx->chunk_requested)) {
|
||||||
|
return UPLOAD_ERR_COMMUNICATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->firmware_remaining -= ctx->read_offset;
|
||||||
|
if (ctx->firmware_remaining > 0) {
|
||||||
|
return UPLOAD_IN_PROGRESS;
|
||||||
|
}
|
||||||
|
return UPLOAD_OK;
|
||||||
|
} else {
|
||||||
|
// first block with the headers parsed -> the first chunk is now complete
|
||||||
|
ctx->read_offset = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// should not happen, but double-check
|
||||||
|
if (flash_area_get_address(
|
||||||
|
&FIRMWARE_AREA, ctx->firmware_block * IMAGE_CHUNK_SIZE, 0) == NULL) {
|
||||||
|
send_msg_failure(iface, FailureType_Failure_ProcessError,
|
||||||
|
"Firmware too big");
|
||||||
|
return UPLOAD_ERR_FIRMWARE_TOO_BIG;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sectrue !=
|
||||||
|
check_single_hash(hdr.hashes + ctx->firmware_block * 32,
|
||||||
|
(uint8_t *)chunk_buffer + ctx->headers_offset,
|
||||||
|
ctx->chunk_size - ctx->headers_offset)) {
|
||||||
|
if (ctx->firmware_upload_chunk_retry > 0) {
|
||||||
|
--ctx->firmware_upload_chunk_retry;
|
||||||
|
|
||||||
|
// clear chunk buffer
|
||||||
|
memset((uint8_t *)&chunk_buffer, 0xFF, IMAGE_CHUNK_SIZE);
|
||||||
|
ctx->chunk_size = 0;
|
||||||
|
|
||||||
|
if (sectrue != send_msg_request_firmware(
|
||||||
|
iface, ctx->firmware_block * IMAGE_CHUNK_SIZE,
|
||||||
|
ctx->chunk_requested)) {
|
||||||
|
return UPLOAD_ERR_COMMUNICATION;
|
||||||
|
}
|
||||||
|
if (ctx->firmware_remaining > 0) {
|
||||||
|
return UPLOAD_IN_PROGRESS;
|
||||||
|
}
|
||||||
|
return UPLOAD_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
send_msg_failure(iface, FailureType_Failure_ProcessError,
|
||||||
|
"Invalid chunk hash");
|
||||||
|
return UPLOAD_ERR_INVALID_CHUNK_HASH;
|
||||||
|
}
|
||||||
|
|
||||||
|
// buffer with the received data
|
||||||
|
const uint32_t *src = (const uint32_t *)chunk_buffer;
|
||||||
|
// number of received bytes
|
||||||
|
uint32_t bytes_remaining = ctx->chunk_size;
|
||||||
|
// offset into the FIRMWARE_AREA part of the flash
|
||||||
|
uint32_t write_offset = ctx->firmware_block * IMAGE_CHUNK_SIZE;
|
||||||
|
|
||||||
|
ensure((ctx->chunk_size % FLASH_BLOCK_SIZE == 0) * sectrue, NULL);
|
||||||
|
|
||||||
|
while (bytes_remaining > 0) {
|
||||||
|
// erase flash before writing
|
||||||
|
uint32_t bytes_erased = 0;
|
||||||
|
|
||||||
|
if (write_offset >= ctx->erase_offset) {
|
||||||
|
// erase the next flash section
|
||||||
|
ensure(flash_area_erase_partial(&FIRMWARE_AREA, ctx->erase_offset,
|
||||||
|
&bytes_erased),
|
||||||
|
NULL);
|
||||||
|
ctx->erase_offset += bytes_erased;
|
||||||
|
} else {
|
||||||
|
// some erased space left from the previous round => use it
|
||||||
|
bytes_erased = ctx->erase_offset - write_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// write the received data
|
||||||
|
uint32_t bytes_to_write = MIN(bytes_erased, bytes_remaining);
|
||||||
|
ensure(flash_unlock_write(), NULL);
|
||||||
|
ensure(flash_area_write_data(&FIRMWARE_AREA, write_offset, src,
|
||||||
|
bytes_to_write),
|
||||||
|
NULL);
|
||||||
|
ensure(flash_lock_write(), NULL);
|
||||||
|
|
||||||
|
write_offset += bytes_to_write;
|
||||||
|
src += bytes_to_write / sizeof(uint32_t);
|
||||||
|
|
||||||
|
bytes_remaining -= bytes_to_write;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->firmware_remaining -= ctx->chunk_requested;
|
||||||
|
|
||||||
|
if (ctx->firmware_remaining == 0) {
|
||||||
|
// erase the rest (unused part) of the FIRMWARE_AREA
|
||||||
|
uint32_t bytes_erased = 0;
|
||||||
|
do {
|
||||||
|
ensure(flash_area_erase_partial(&FIRMWARE_AREA, ctx->erase_offset,
|
||||||
|
&bytes_erased),
|
||||||
|
NULL);
|
||||||
|
ctx->erase_offset += bytes_erased;
|
||||||
|
} while (bytes_erased > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->headers_offset = 0;
|
||||||
|
ctx->firmware_block++;
|
||||||
|
ctx->firmware_upload_chunk_retry = FIRMWARE_UPLOAD_CHUNK_RETRY_COUNT;
|
||||||
|
|
||||||
|
if (ctx->firmware_remaining > 0) {
|
||||||
|
ctx->chunk_requested = (ctx->firmware_remaining > IMAGE_CHUNK_SIZE)
|
||||||
|
? IMAGE_CHUNK_SIZE
|
||||||
|
: ctx->firmware_remaining;
|
||||||
|
|
||||||
|
// clear chunk buffer
|
||||||
|
ctx->chunk_size = 0;
|
||||||
|
memset((uint8_t *)&chunk_buffer, 0xFF, IMAGE_CHUNK_SIZE);
|
||||||
|
if (sectrue !=
|
||||||
|
send_msg_request_firmware(iface, ctx->firmware_block * IMAGE_CHUNK_SIZE,
|
||||||
|
ctx->chunk_requested)) {
|
||||||
|
return UPLOAD_ERR_COMMUNICATION;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
send_msg_success(iface, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx->firmware_remaining > 0) {
|
||||||
|
return UPLOAD_IN_PROGRESS;
|
||||||
|
}
|
||||||
|
return UPLOAD_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
workflow_result_t workflow_firmware_update(protob_io_t *iface) {
|
||||||
|
firmware_update_ctx_t ctx = {
|
||||||
|
.firmware_upload_chunk_retry = FIRMWARE_UPLOAD_CHUNK_RETRY_COUNT,
|
||||||
|
};
|
||||||
|
|
||||||
|
FirmwareErase msg;
|
||||||
|
secbool res = recv_msg_firmware_erase(iface, &msg);
|
||||||
|
|
||||||
|
if (res != sectrue) {
|
||||||
|
return WF_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.firmware_remaining = msg.has_length ? msg.length : 0;
|
||||||
|
if ((ctx.firmware_remaining > 0) &&
|
||||||
|
((ctx.firmware_remaining % sizeof(uint32_t)) == 0) &&
|
||||||
|
(ctx.firmware_remaining <= FIRMWARE_MAXSIZE)) {
|
||||||
|
// clear chunk buffer
|
||||||
|
memset((uint8_t *)&chunk_buffer, 0xFF, IMAGE_CHUNK_SIZE);
|
||||||
|
ctx.chunk_size = 0;
|
||||||
|
|
||||||
|
// request new firmware
|
||||||
|
ctx.chunk_requested = (ctx.firmware_remaining > IMAGE_INIT_CHUNK_SIZE)
|
||||||
|
? IMAGE_INIT_CHUNK_SIZE
|
||||||
|
: ctx.firmware_remaining;
|
||||||
|
if (sectrue != send_msg_request_firmware(iface, 0, ctx.chunk_requested)) {
|
||||||
|
ui_screen_fail();
|
||||||
|
return WF_ERROR;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// invalid firmware size
|
||||||
|
send_msg_failure(iface, FailureType_Failure_ProcessError,
|
||||||
|
"Wrong firmware size");
|
||||||
|
return WF_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
upload_status_t s = UPLOAD_IN_PROGRESS;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
uint16_t ifaces[1] = {protob_get_iface_flag(iface) | MODE_READ};
|
||||||
|
poll_event_t e = {0};
|
||||||
|
uint8_t i = poll_events(ifaces, 1, &e, 100);
|
||||||
|
|
||||||
|
if (e.type == EVENT_NONE || i != protob_get_iface_flag(iface)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t msg_id = 0;
|
||||||
|
|
||||||
|
if (sectrue != protob_get_msg_header(iface, &msg_id)) {
|
||||||
|
// invalid header -> discard
|
||||||
|
return WF_ERROR;
|
||||||
|
}
|
||||||
|
s = process_msg_FirmwareUpload(iface, &ctx);
|
||||||
|
|
||||||
|
if (s < 0 && s != UPLOAD_ERR_USER_ABORT) { // error, but not user abort
|
||||||
|
if (s == UPLOAD_ERR_BOOTLOADER_LOCKED) {
|
||||||
|
// This function does not return
|
||||||
|
show_install_restricted_screen();
|
||||||
|
} else {
|
||||||
|
ui_screen_fail();
|
||||||
|
}
|
||||||
|
return WF_ERROR;
|
||||||
|
} else if (s == UPLOAD_ERR_USER_ABORT) {
|
||||||
|
systick_delay_ms(100);
|
||||||
|
return WF_CANCELLED;
|
||||||
|
} else if (s == UPLOAD_OK) { // last chunk received
|
||||||
|
ui_screen_install_progress_upload(1000);
|
||||||
|
ui_screen_done(4, sectrue);
|
||||||
|
ui_screen_done(3, secfalse);
|
||||||
|
systick_delay_ms(1000);
|
||||||
|
ui_screen_done(2, secfalse);
|
||||||
|
systick_delay_ms(1000);
|
||||||
|
ui_screen_done(1, secfalse);
|
||||||
|
systick_delay_ms(1000);
|
||||||
|
return WF_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
33
core/embed/projects/bootloader/workflow/wf_get_features.c
Normal file
33
core/embed/projects/bootloader/workflow/wf_get_features.c
Normal 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;
|
||||||
|
}
|
123
core/embed/projects/bootloader/workflow/wf_host_control.c
Normal file
123
core/embed/projects/bootloader/workflow/wf_host_control.c
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
33
core/embed/projects/bootloader/workflow/wf_initialize.c
Normal file
33
core/embed/projects/bootloader/workflow/wf_initialize.c
Normal 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;
|
||||||
|
}
|
33
core/embed/projects/bootloader/workflow/wf_ping.c
Normal file
33
core/embed/projects/bootloader/workflow/wf_ping.c
Normal 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;
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
55
core/embed/projects/bootloader/workflow/wf_wipe_device.c
Normal file
55
core/embed/projects/bootloader/workflow/wf_wipe_device.c
Normal 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;
|
||||||
|
}
|
68
core/embed/projects/bootloader/workflow/workflow.h
Normal file
68
core/embed/projects/bootloader/workflow/workflow.h
Normal 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);
|
@ -23,7 +23,7 @@
|
|||||||
#include <pb.h>
|
#include <pb.h>
|
||||||
#include <pb_decode.h>
|
#include <pb_decode.h>
|
||||||
#include <pb_encode.h>
|
#include <pb_encode.h>
|
||||||
#include "messages.pb.h"
|
#include "pb/messages.pb.h"
|
||||||
|
|
||||||
#include <io/usb.h>
|
#include <io/usb.h>
|
||||||
#include <util/flash.h>
|
#include <util/flash.h>
|
||||||
|
@ -102,7 +102,6 @@ def configure(
|
|||||||
"USE_TAMPER=1",
|
"USE_TAMPER=1",
|
||||||
"USE_FLASH_BURST=1",
|
"USE_FLASH_BURST=1",
|
||||||
"USE_OEM_KEYS_CHECK=1",
|
"USE_OEM_KEYS_CHECK=1",
|
||||||
"USE_RESET_TO_BOOT=1",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
env.get("ENV")["LINKER_SCRIPT"] = linker_script
|
env.get("ENV")["LINKER_SCRIPT"] = linker_script
|
||||||
|
@ -106,7 +106,6 @@ def configure(
|
|||||||
("USE_STORAGE_HWKEY", "1"),
|
("USE_STORAGE_HWKEY", "1"),
|
||||||
("USE_TAMPER", "1"),
|
("USE_TAMPER", "1"),
|
||||||
("USE_FLASH_BURST", "1"),
|
("USE_FLASH_BURST", "1"),
|
||||||
("USE_RESET_TO_BOOT", "1"),
|
|
||||||
("USE_OEM_KEYS_CHECK", "1"),
|
("USE_OEM_KEYS_CHECK", "1"),
|
||||||
("USE_PVD", "1"),
|
("USE_PVD", "1"),
|
||||||
]
|
]
|
||||||
|
@ -139,7 +139,6 @@ def configure(
|
|||||||
("USE_STORAGE_HWKEY", "1"),
|
("USE_STORAGE_HWKEY", "1"),
|
||||||
("USE_TAMPER", "1"),
|
("USE_TAMPER", "1"),
|
||||||
("USE_FLASH_BURST", "1"),
|
("USE_FLASH_BURST", "1"),
|
||||||
("USE_RESET_TO_BOOT", "1"),
|
|
||||||
("USE_OEM_KEYS_CHECK", "1"),
|
("USE_OEM_KEYS_CHECK", "1"),
|
||||||
("USE_PVD", "1"),
|
("USE_PVD", "1"),
|
||||||
]
|
]
|
||||||
|
@ -223,7 +223,6 @@ def configure(
|
|||||||
("USE_TAMPER", "1"),
|
("USE_TAMPER", "1"),
|
||||||
("USE_FLASH_BURST", "1"),
|
("USE_FLASH_BURST", "1"),
|
||||||
("USE_OEM_KEYS_CHECK", "1"),
|
("USE_OEM_KEYS_CHECK", "1"),
|
||||||
("USE_RESET_TO_BOOT", "1"),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
sources += [
|
sources += [
|
||||||
|
@ -228,7 +228,6 @@ def configure(
|
|||||||
("USE_TAMPER", "1"),
|
("USE_TAMPER", "1"),
|
||||||
("USE_FLASH_BURST", "1"),
|
("USE_FLASH_BURST", "1"),
|
||||||
("USE_OEM_KEYS_CHECK", "1"),
|
("USE_OEM_KEYS_CHECK", "1"),
|
||||||
("USE_RESET_TO_BOOT", "1"),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
sources += [
|
sources += [
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
^\./core/embed/projects/bootloader/protob/
|
^\./core/embed/projects/bootloader/protob/pb/
|
||||||
^\./crypto/aes/
|
^\./crypto/aes/
|
||||||
^\./crypto/chacha20poly1305/
|
^\./crypto/chacha20poly1305/
|
||||||
^\./crypto/ed25519-donna/
|
^\./crypto/ed25519-donna/
|
||||||
|
Loading…
Reference in New Issue
Block a user