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/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',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -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',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
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 "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:
|
||||||
|
@ -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;
|
||||||
|
@ -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,
|
||||||
|
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_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
|
||||||
|
@ -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, ¤t_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
|
||||||
|
@ -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, ¤t_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);
|
||||||
|
@ -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, ¤t_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;
|
||||||
|
Loading…
Reference in New Issue
Block a user