1
0
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:
tychovrahe 2024-08-27 15:25:58 +02:00 committed by TychoVrahe
parent 395a4af9be
commit b685820fb3
12 changed files with 183 additions and 31 deletions

View File

@ -168,6 +168,7 @@ SOURCE_BOOTLOADER = [
'embed/bootloader/main.c', 'embed/bootloader/main.c',
'embed/bootloader/messages.c', 'embed/bootloader/messages.c',
'embed/bootloader/protob/messages.pb.c', 'embed/bootloader/protob/messages.pb.c',
'embed/bootloader/version_check.c',
] ]

View File

@ -140,6 +140,7 @@ SOURCE_BOOTLOADER = [
'embed/bootloader/main.c', 'embed/bootloader/main.c',
'embed/bootloader/messages.c', 'embed/bootloader/messages.c',
'embed/bootloader/emulator.c', 'embed/bootloader/emulator.c',
'embed/bootloader/version_check.c',
'embed/bootloader/protob/messages.pb.c', 'embed/bootloader/protob/messages.pb.c',
] ]

View File

@ -0,0 +1 @@
Added firmware downgrade protection

View File

@ -72,6 +72,7 @@
#include "monoctr.h" #include "monoctr.h"
#include "rust_ui.h" #include "rust_ui.h"
#include "unit_variant.h" #include "unit_variant.h"
#include "version_check.h"
#ifdef TREZOR_EMULATOR #ifdef TREZOR_EMULATOR
#include "emulator.h" #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)); 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 failed_jump_to_firmware(void) { error_shutdown("(glitch)"); }
void real_jump_to_firmware(void) { 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), ensure(check_image_header_sig(hdr, vhdr.vsig_m, vhdr.vsig_n, vhdr.vpub),
"Firmware is corrupted"); "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, ensure(check_image_contents(hdr, IMAGE_HEADER_SIZE + vhdr.hdrlen,
&FIRMWARE_AREA), &FIRMWARE_AREA),
"Firmware is corrupted"); "Firmware is corrupted");
@ -422,6 +411,8 @@ int bootloader_main(void) {
volatile secbool vhdr_lock_ok = secfalse; volatile secbool vhdr_lock_ok = secfalse;
volatile secbool img_hdr_ok = secfalse; volatile secbool img_hdr_ok = secfalse;
volatile secbool model_ok = secfalse; volatile secbool model_ok = secfalse;
volatile secbool signatures_ok = secfalse;
volatile secbool version_ok = secfalse;
volatile secbool header_present = secfalse; volatile secbool header_present = secfalse;
volatile secbool firmware_present = secfalse; volatile secbool firmware_present = secfalse;
volatile secbool firmware_present_backup = secfalse; volatile secbool firmware_present_backup = secfalse;
@ -448,12 +439,22 @@ int bootloader_main(void) {
if (sectrue == img_hdr_ok) { if (sectrue == img_hdr_ok) {
model_ok = check_image_model(hdr); model_ok = check_image_model(hdr);
} }
if (sectrue == model_ok) { if (sectrue == model_ok) {
header_present = signatures_ok =
check_image_header_sig(hdr, vhdr.vsig_m, vhdr.vsig_n, vhdr.vpub); 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) { if (sectrue == header_present) {
ensure_firmware_min_version(hdr->monotonic);
firmware_present = check_image_contents( firmware_present = check_image_contents(
hdr, IMAGE_HEADER_SIZE + vhdr.hdrlen, &FIRMWARE_AREA); hdr, IMAGE_HEADER_SIZE + vhdr.hdrlen, &FIRMWARE_AREA);
firmware_present_backup = firmware_present; firmware_present_backup = firmware_present;
@ -477,12 +478,12 @@ int bootloader_main(void) {
#if PRODUCTION && !defined STM32U5 #if PRODUCTION && !defined STM32U5
// for STM32U5, this check is moved to boardloader // for STM32U5, this check is moved to boardloader
check_bootloader_version(); ensure_bootloader_min_version();
#endif #endif
switch (bootargs_get_command()) { switch (bootargs_get_command()) {
case BOOT_COMMAND_STOP_AND_WAIT: case BOOT_COMMAND_STOP_AND_WAIT:
// firmare requested to stay in bootloader // firmware requested to stay in bootloader
stay_in_bootloader = sectrue; stay_in_bootloader = sectrue;
break; break;
case BOOT_COMMAND_INSTALL_UPGRADE: case BOOT_COMMAND_INSTALL_UPGRADE:

View File

@ -37,6 +37,7 @@
#include "bootui.h" #include "bootui.h"
#include "messages.h" #include "messages.h"
#include "rust_ui.h" #include "rust_ui.h"
#include "version_check.h"
#include "memzero.h" #include "memzero.h"
#include "model.h" #include "model.h"
@ -472,6 +473,10 @@ static void detect_installation(const vendor_header *current_vhdr,
*is_new = sectrue; *is_new = sectrue;
return; 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, if (sectrue != check_image_header_sig(current_hdr, current_vhdr->vsig_m,
current_vhdr->vsig_n, current_vhdr->vsig_n,
current_vhdr->vpub)) { 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; 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)); memcpy(&hdr, received_hdr, sizeof(hdr));
size_t headers_end = IMAGE_HEADER_SIZE + vhdr.hdrlen; size_t headers_end = IMAGE_HEADER_SIZE + vhdr.hdrlen;

View File

@ -39,6 +39,7 @@ enum {
UPLOAD_ERR_INVALID_IMAGE_HEADER = -4, UPLOAD_ERR_INVALID_IMAGE_HEADER = -4,
UPLOAD_ERR_INVALID_IMAGE_MODEL = -5, UPLOAD_ERR_INVALID_IMAGE_MODEL = -5,
UPLOAD_ERR_INVALID_IMAGE_HEADER_SIG = -6, UPLOAD_ERR_INVALID_IMAGE_HEADER_SIG = -6,
UPLOAD_ERR_INVALID_IMAGE_HEADER_VERSION = -16,
UPLOAD_ERR_USER_ABORT = -7, UPLOAD_ERR_USER_ABORT = -7,
UPLOAD_ERR_FIRMWARE_TOO_BIG = -8, UPLOAD_ERR_FIRMWARE_TOO_BIG = -8,
UPLOAD_ERR_INVALID_CHUNK_HASH = -9, UPLOAD_ERR_INVALID_CHUNK_HASH = -9,

View 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");
}

View 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);

View File

@ -32,8 +32,12 @@ typedef enum {
MONOCTR_FIRMWARE_VERSION = 1, MONOCTR_FIRMWARE_VERSION = 1,
} monoctr_type_t; } 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); 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); secbool monoctr_read(monoctr_type_t type, uint8_t* value);
#endif #endif

View File

@ -41,6 +41,26 @@ secbool monoctr_write(monoctr_type_t type, uint8_t value) {
return secfalse; return secfalse;
} }
int block = get_otp_block(type);
if (block < 0) {
return secfalse;
}
uint8_t current_value = 0;
if (sectrue != monoctr_read(type, &current_value)) {
return secfalse;
}
if (value < current_value) {
return secfalse;
}
if (value == current_value) {
return sectrue;
}
uint8_t bits[FLASH_OTP_BLOCK_SIZE]; uint8_t bits[FLASH_OTP_BLOCK_SIZE];
for (int i = 0; i < FLASH_OTP_BLOCK_SIZE * 8; i++) { for (int i = 0; i < FLASH_OTP_BLOCK_SIZE * 8; i++) {
if (i < value) { 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); ensure(flash_otp_write(block, 0, bits, FLASH_OTP_BLOCK_SIZE), NULL);
#endif #endif

View File

@ -44,6 +44,20 @@ secbool monoctr_write(monoctr_type_t type, uint8_t value) {
return secfalse; return secfalse;
} }
uint8_t current_value = 0;
if (sectrue != monoctr_read(type, &current_value)) {
return secfalse;
}
if (value < current_value) {
return secfalse;
}
if (value == current_value) {
return sectrue;
}
for (int i = 0; i < value; i++) { for (int i = 0; i < value; i++) {
uint32_t data[4] = {0}; uint32_t data[4] = {0};
secret_write((uint8_t *)data, offset + i * 16, 16); secret_write((uint8_t *)data, offset + i * 16, 16);

View File

@ -38,6 +38,26 @@ secbool monoctr_write(monoctr_type_t type, uint8_t value) {
return secfalse; return secfalse;
} }
int block = get_otp_block(type);
if (block < 0) {
return secfalse;
}
uint8_t current_value = 0;
if (sectrue != monoctr_read(type, &current_value)) {
return secfalse;
}
if (value < current_value) {
return secfalse;
}
if (value == current_value) {
return sectrue;
}
uint8_t bits[FLASH_OTP_BLOCK_SIZE]; uint8_t bits[FLASH_OTP_BLOCK_SIZE];
for (int i = 0; i < FLASH_OTP_BLOCK_SIZE * 8; i++) { for (int i = 0; i < FLASH_OTP_BLOCK_SIZE * 8; i++) {
if (i < value) { 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); ensure(flash_otp_write(block, 0, bits, FLASH_OTP_BLOCK_SIZE), NULL);
return sectrue; return sectrue;