mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-01-19 03:40:59 +00:00
feat(core): implement firmware downgrade protection
This commit is contained in:
parent
395a4af9be
commit
b685820fb3
@ -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',
|
||||
]
|
||||
|
||||
|
||||
|
@ -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',
|
||||
]
|
||||
|
||||
|
1
core/embed/bootloader/.changelog.d/4133.added
Normal file
1
core/embed/bootloader/.changelog.d/4133.added
Normal file
@ -0,0 +1 @@
|
||||
Added firmware downgrade protection
|
@ -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:
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
48
core/embed/bootloader/version_check.c
Normal file
48
core/embed/bootloader/version_check.c
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#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");
|
||||
}
|
40
core/embed/bootloader/version_check.h
Normal file
40
core/embed/bootloader/version_check.h
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#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);
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user