From b685820fb35360ec139ac45878a7a3c75d425c52 Mon Sep 17 00:00:00 2001 From: tychovrahe Date: Tue, 27 Aug 2024 15:25:58 +0200 Subject: [PATCH] feat(core): implement firmware downgrade protection --- core/SConscript.bootloader | 1 + core/SConscript.bootloader_emu | 1 + core/embed/bootloader/.changelog.d/4133.added | 1 + core/embed/bootloader/main.c | 39 +++++++-------- core/embed/bootloader/messages.c | 13 +++++ core/embed/bootloader/messages.h | 1 + core/embed/bootloader/version_check.c | 48 +++++++++++++++++++ core/embed/bootloader/version_check.h | 40 ++++++++++++++++ core/embed/trezorhal/monoctr.h | 4 ++ core/embed/trezorhal/stm32f4/monoctr.c | 26 +++++++--- core/embed/trezorhal/stm32u5/monoctr.c | 14 ++++++ core/embed/trezorhal/unix/monoctr.c | 26 +++++++--- 12 files changed, 183 insertions(+), 31 deletions(-) create mode 100644 core/embed/bootloader/.changelog.d/4133.added create mode 100644 core/embed/bootloader/version_check.c create mode 100644 core/embed/bootloader/version_check.h diff --git a/core/SConscript.bootloader b/core/SConscript.bootloader index dd0060af83..e5226cd313 100644 --- a/core/SConscript.bootloader +++ b/core/SConscript.bootloader @@ -168,6 +168,7 @@ SOURCE_BOOTLOADER = [ 'embed/bootloader/main.c', 'embed/bootloader/messages.c', 'embed/bootloader/protob/messages.pb.c', + 'embed/bootloader/version_check.c', ] diff --git a/core/SConscript.bootloader_emu b/core/SConscript.bootloader_emu index 009415fcf8..9d0627cbcc 100644 --- a/core/SConscript.bootloader_emu +++ b/core/SConscript.bootloader_emu @@ -140,6 +140,7 @@ SOURCE_BOOTLOADER = [ 'embed/bootloader/main.c', 'embed/bootloader/messages.c', 'embed/bootloader/emulator.c', + 'embed/bootloader/version_check.c', 'embed/bootloader/protob/messages.pb.c', ] diff --git a/core/embed/bootloader/.changelog.d/4133.added b/core/embed/bootloader/.changelog.d/4133.added new file mode 100644 index 0000000000..b808dcad60 --- /dev/null +++ b/core/embed/bootloader/.changelog.d/4133.added @@ -0,0 +1 @@ +Added firmware downgrade protection diff --git a/core/embed/bootloader/main.c b/core/embed/bootloader/main.c index 23d0ee9133..d93ec0cf4a 100644 --- a/core/embed/bootloader/main.c +++ b/core/embed/bootloader/main.c @@ -72,6 +72,7 @@ #include "monoctr.h" #include "rust_ui.h" #include "unit_variant.h" +#include "version_check.h" #ifdef TREZOR_EMULATOR #include "emulator.h" @@ -261,22 +262,6 @@ static secbool check_vendor_header_lock(const vendor_header *const vhdr) { return sectrue * (0 == memcmp(lock, hash, 32)); } -// protection against bootloader downgrade - -#if PRODUCTION && !defined STM32U5 - -static void check_bootloader_version(void) { - ensure( - monoctr_write(MONOCTR_BOOTLOADER_VERSION, BOOTLOADER_MONOTONIC_VERSION), - NULL); - uint8_t val = 0; - ensure(monoctr_read(MONOCTR_BOOTLOADER_VERSION, &val), NULL); - ensure(sectrue * (val == BOOTLOADER_MONOTONIC_VERSION), - "Bootloader downgrade protection"); -} - -#endif - void failed_jump_to_firmware(void) { error_shutdown("(glitch)"); } void real_jump_to_firmware(void) { @@ -304,6 +289,10 @@ void real_jump_to_firmware(void) { ensure(check_image_header_sig(hdr, vhdr.vsig_m, vhdr.vsig_n, vhdr.vpub), "Firmware is corrupted"); + ensure(check_firmware_min_version(hdr->version), + "Firmware downgrade protection"); + ensure_firmware_min_version(hdr->version); + ensure(check_image_contents(hdr, IMAGE_HEADER_SIZE + vhdr.hdrlen, &FIRMWARE_AREA), "Firmware is corrupted"); @@ -422,6 +411,8 @@ int bootloader_main(void) { volatile secbool vhdr_lock_ok = secfalse; volatile secbool img_hdr_ok = secfalse; volatile secbool model_ok = secfalse; + volatile secbool signatures_ok = secfalse; + volatile secbool version_ok = secfalse; volatile secbool header_present = secfalse; volatile secbool firmware_present = secfalse; volatile secbool firmware_present_backup = secfalse; @@ -448,12 +439,22 @@ int bootloader_main(void) { if (sectrue == img_hdr_ok) { model_ok = check_image_model(hdr); } + if (sectrue == model_ok) { - header_present = + signatures_ok = check_image_header_sig(hdr, vhdr.vsig_m, vhdr.vsig_n, vhdr.vpub); } + if (sectrue == signatures_ok) { + version_ok = check_firmware_min_version(hdr->monotonic); + } + + if (sectrue == version_ok) { + header_present = version_ok; + } + if (sectrue == header_present) { + ensure_firmware_min_version(hdr->monotonic); firmware_present = check_image_contents( hdr, IMAGE_HEADER_SIZE + vhdr.hdrlen, &FIRMWARE_AREA); firmware_present_backup = firmware_present; @@ -477,12 +478,12 @@ int bootloader_main(void) { #if PRODUCTION && !defined STM32U5 // for STM32U5, this check is moved to boardloader - check_bootloader_version(); + ensure_bootloader_min_version(); #endif switch (bootargs_get_command()) { case BOOT_COMMAND_STOP_AND_WAIT: - // firmare requested to stay in bootloader + // firmware requested to stay in bootloader stay_in_bootloader = sectrue; break; case BOOT_COMMAND_INSTALL_UPGRADE: diff --git a/core/embed/bootloader/messages.c b/core/embed/bootloader/messages.c index 568cafdb26..5006afb856 100644 --- a/core/embed/bootloader/messages.c +++ b/core/embed/bootloader/messages.c @@ -37,6 +37,7 @@ #include "bootui.h" #include "messages.h" #include "rust_ui.h" +#include "version_check.h" #include "memzero.h" #include "model.h" @@ -472,6 +473,10 @@ static void detect_installation(const vendor_header *current_vhdr, *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)) { @@ -574,6 +579,14 @@ int process_msg_FirmwareUpload(uint8_t iface_num, uint32_t msg_size, 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)); size_t headers_end = IMAGE_HEADER_SIZE + vhdr.hdrlen; diff --git a/core/embed/bootloader/messages.h b/core/embed/bootloader/messages.h index d1ff9a2f08..ad8e3f4211 100644 --- a/core/embed/bootloader/messages.h +++ b/core/embed/bootloader/messages.h @@ -39,6 +39,7 @@ enum { 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, diff --git a/core/embed/bootloader/version_check.c b/core/embed/bootloader/version_check.c new file mode 100644 index 0000000000..ae7d225154 --- /dev/null +++ b/core/embed/bootloader/version_check.c @@ -0,0 +1,48 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "version_check.h" + +#include "error_handling.h" +#include "model_version.h" +#include "monoctr.h" + +void ensure_bootloader_min_version(void) { + monoctr_write(MONOCTR_BOOTLOADER_VERSION, BOOTLOADER_MONOTONIC_VERSION); + uint8_t val = 0; + ensure(monoctr_read(MONOCTR_BOOTLOADER_VERSION, &val), NULL); + ensure(sectrue * (val == BOOTLOADER_MONOTONIC_VERSION), + "Bootloader downgrade protection"); +} + +secbool check_firmware_min_version(uint8_t check_version) { + uint8_t min_version = 0; + ensure(monoctr_read(MONOCTR_FIRMWARE_VERSION, &min_version), "monoctr read"); + + return (check_version >= min_version) * sectrue; +} + +void ensure_firmware_min_version(uint8_t version) { + monoctr_write(MONOCTR_FIRMWARE_VERSION, version); + uint8_t val = 0; + ensure(monoctr_read(MONOCTR_FIRMWARE_VERSION, &val), NULL); + ensure(sectrue * (val == version), "Firmware downgrade protection"); +} diff --git a/core/embed/bootloader/version_check.h b/core/embed/bootloader/version_check.h new file mode 100644 index 0000000000..a7011808b5 --- /dev/null +++ b/core/embed/bootloader/version_check.h @@ -0,0 +1,40 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include "secbool.h" + +// Protection against bootloader downgrade + +// Ensures bootloader version is stored in monotonic counter +// If the version cannot be written, the function will shutdown the device +void ensure_bootloader_min_version(void); + +// Protection against firmware downgrade + +// This functions checks if the firmware version is at least the minimum +// required version, returns sectrue if check_version is higher or equal to the +// stored version +secbool check_firmware_min_version(uint8_t check_version); + +// Ensures firmware version is stored in monotonic counter +// If the version cannot be written, the function will shutdown the device +void ensure_firmware_min_version(uint8_t version); diff --git a/core/embed/trezorhal/monoctr.h b/core/embed/trezorhal/monoctr.h index 0d5e2bcfd1..296b573dbf 100644 --- a/core/embed/trezorhal/monoctr.h +++ b/core/embed/trezorhal/monoctr.h @@ -32,8 +32,12 @@ typedef enum { MONOCTR_FIRMWARE_VERSION = 1, } monoctr_type_t; +// Write a new value to the monotonic counter +// Returns sectrue on success, when value is lower than the current value +// the write fails and returns secfalse secbool monoctr_write(monoctr_type_t type, uint8_t value); +// Read the current value of the monotonic counter secbool monoctr_read(monoctr_type_t type, uint8_t* value); #endif diff --git a/core/embed/trezorhal/stm32f4/monoctr.c b/core/embed/trezorhal/stm32f4/monoctr.c index a30e13d907..70b58faa9d 100644 --- a/core/embed/trezorhal/stm32f4/monoctr.c +++ b/core/embed/trezorhal/stm32f4/monoctr.c @@ -41,6 +41,26 @@ secbool monoctr_write(monoctr_type_t type, uint8_t value) { return secfalse; } + int block = get_otp_block(type); + + if (block < 0) { + return secfalse; + } + + uint8_t current_value = 0; + + if (sectrue != monoctr_read(type, ¤t_value)) { + return secfalse; + } + + if (value < current_value) { + return secfalse; + } + + if (value == current_value) { + return sectrue; + } + uint8_t bits[FLASH_OTP_BLOCK_SIZE]; for (int i = 0; i < FLASH_OTP_BLOCK_SIZE * 8; i++) { if (i < value) { @@ -50,12 +70,6 @@ secbool monoctr_write(monoctr_type_t type, uint8_t value) { } } - int block = get_otp_block(type); - - if (block < 0) { - return secfalse; - } - ensure(flash_otp_write(block, 0, bits, FLASH_OTP_BLOCK_SIZE), NULL); #endif diff --git a/core/embed/trezorhal/stm32u5/monoctr.c b/core/embed/trezorhal/stm32u5/monoctr.c index e9db4eb273..6504371fa2 100644 --- a/core/embed/trezorhal/stm32u5/monoctr.c +++ b/core/embed/trezorhal/stm32u5/monoctr.c @@ -44,6 +44,20 @@ secbool monoctr_write(monoctr_type_t type, uint8_t value) { return secfalse; } + uint8_t current_value = 0; + + if (sectrue != monoctr_read(type, ¤t_value)) { + return secfalse; + } + + if (value < current_value) { + return secfalse; + } + + if (value == current_value) { + return sectrue; + } + for (int i = 0; i < value; i++) { uint32_t data[4] = {0}; secret_write((uint8_t *)data, offset + i * 16, 16); diff --git a/core/embed/trezorhal/unix/monoctr.c b/core/embed/trezorhal/unix/monoctr.c index e1ddf7a358..a8a140db60 100644 --- a/core/embed/trezorhal/unix/monoctr.c +++ b/core/embed/trezorhal/unix/monoctr.c @@ -38,6 +38,26 @@ secbool monoctr_write(monoctr_type_t type, uint8_t value) { return secfalse; } + int block = get_otp_block(type); + + if (block < 0) { + return secfalse; + } + + uint8_t current_value = 0; + + if (sectrue != monoctr_read(type, ¤t_value)) { + return secfalse; + } + + if (value < current_value) { + return secfalse; + } + + if (value == current_value) { + return sectrue; + } + uint8_t bits[FLASH_OTP_BLOCK_SIZE]; for (int i = 0; i < FLASH_OTP_BLOCK_SIZE * 8; i++) { if (i < value) { @@ -47,12 +67,6 @@ secbool monoctr_write(monoctr_type_t type, uint8_t value) { } } - int block = get_otp_block(type); - - if (block < 0) { - return secfalse; - } - ensure(flash_otp_write(block, 0, bits, FLASH_OTP_BLOCK_SIZE), NULL); return sectrue;