diff --git a/core/SConscript.bootloader_emu b/core/SConscript.bootloader_emu index 789185f24f..009415fcf8 100644 --- a/core/SConscript.bootloader_emu +++ b/core/SConscript.bootloader_emu @@ -149,6 +149,7 @@ SOURCE_TREZORHAL = [ 'embed/trezorhal/unix/fault_handlers.c', 'embed/trezorhal/unix/flash.c', 'embed/trezorhal/unix/flash_otp.c', + 'embed/trezorhal/unix/monoctr.c', 'embed/trezorhal/unix/random_delays.c', 'embed/trezorhal/unix/rng.c', 'embed/trezorhal/unix/secret.c', diff --git a/core/embed/boardloader/main.c b/core/embed/boardloader/main.c index 321297d4d0..6444fff90d 100644 --- a/core/embed/boardloader/main.c +++ b/core/embed/boardloader/main.c @@ -54,6 +54,7 @@ #include "lowlevel.h" #include "model.h" +#include "monoctr.h" #include "version.h" #include "memzero.h" @@ -76,45 +77,17 @@ static const uint8_t * const BOARDLOADER_KEYS[] = { #endif }; -#ifdef STM32U5 -uint8_t get_bootloader_min_version(void) { - const uint8_t *counter_addr = - flash_area_get_address(&SECRET_AREA, SECRET_MONOTONIC_COUNTER_OFFSET, - SECRET_MONOTONIC_COUNTER_LEN); - - ensure((counter_addr != NULL) * sectrue, "counter_addr is NULL"); - - int counter = 0; - - for (int i = 0; i < SECRET_MONOTONIC_COUNTER_LEN / 16; i++) { - secbool not_cleared = sectrue; - for (int j = 0; j < 16; j++) { - if (counter_addr[i * 16 + j] != 0xFF) { - not_cleared = secfalse; - break; - } - } - - if (not_cleared != sectrue) { - counter++; - } else { - break; - } - } - - return counter; +static uint8_t get_bootloader_min_version(void) { + uint8_t version = 0; + ensure(monoctr_read(MONOCTR_BOOTLOADER_VERSION, &version), "monoctr read"); + return version; } -void write_bootloader_min_version(uint8_t version) { +static void write_bootloader_min_version(uint8_t version) { if (version > get_bootloader_min_version()) { - for (int i = 0; i < version; i++) { - uint32_t data[4] = {0}; - secret_write((uint8_t *)data, SECRET_MONOTONIC_COUNTER_OFFSET + i * 16, - 16); - } + ensure(monoctr_write(MONOCTR_BOOTLOADER_VERSION, version), "monoctr write"); } } -#endif struct BoardCapabilities capabilities __attribute__((section(".capabilities_section"))) = { @@ -197,11 +170,9 @@ static uint32_t check_sdcard(void) { } } -#ifdef STM32U5 if (hdr->monotonic < get_bootloader_min_version()) { return 0; } -#endif return hdr->codelen; } @@ -310,7 +281,6 @@ int main(void) { #if defined USE_SD_CARD sdcard_init(); -#ifdef STM32U5 // If the bootloader is being updated from SD card, we need to preserve the // monotonic counter from the old bootloader. This is in case that the old // bootloader did not have the chance yet to write its monotonic counter to @@ -327,7 +297,6 @@ int main(void) { check_image_contents(old_hdr, IMAGE_HEADER_SIZE, &BOOTLOADER_AREA))) { write_bootloader_min_version(old_hdr->monotonic); } -#endif if (check_sdcard()) { return copy_sdcard() == sectrue ? 0 : 3; @@ -348,14 +317,12 @@ int main(void) { ensure(check_image_contents(hdr, IMAGE_HEADER_SIZE, &BOOTLOADER_AREA), "invalid bootloader hash"); -#ifdef STM32U5 uint8_t bld_min_version = get_bootloader_min_version(); ensure((hdr->monotonic >= bld_min_version) * sectrue, "BOOTLOADER DOWNGRADED"); // Write the bootloader version to the secret area. // This includes the version of bootloader potentially updated from SD card. write_bootloader_min_version(hdr->monotonic); -#endif ensure_compatible_settings(); diff --git a/core/embed/bootloader/main.c b/core/embed/bootloader/main.c index 07adabd46d..4ec85145e8 100644 --- a/core/embed/bootloader/main.c +++ b/core/embed/bootloader/main.c @@ -69,6 +69,7 @@ #include "bootui.h" #include "messages.h" +#include "monoctr.h" #include "rust_ui.h" #include "unit_variant.h" @@ -265,24 +266,10 @@ static secbool check_vendor_header_lock(const vendor_header *const vhdr) { #if PRODUCTION && !defined STM32U5 static void check_bootloader_version(void) { - uint8_t bits[FLASH_OTP_BLOCK_SIZE]; - for (int i = 0; i < FLASH_OTP_BLOCK_SIZE * 8; i++) { - if (i < VERSION_MONOTONIC) { - bits[i / 8] &= ~(1 << (7 - (i % 8))); - } else { - bits[i / 8] |= (1 << (7 - (i % 8))); - } - } - ensure(flash_otp_write(FLASH_OTP_BLOCK_BOOTLOADER_VERSION, 0, bits, - FLASH_OTP_BLOCK_SIZE), - NULL); - - uint8_t bits2[FLASH_OTP_BLOCK_SIZE]; - ensure(flash_otp_read(FLASH_OTP_BLOCK_BOOTLOADER_VERSION, 0, bits2, - FLASH_OTP_BLOCK_SIZE), - NULL); - - ensure(sectrue * (0 == memcmp(bits, bits2, FLASH_OTP_BLOCK_SIZE)), + ensure(monoctr_write(MONOCTR_BOOTLOADER_VERSION, VERSION_MONOTONIC), NULL); + uint8_t val = 0; + ensure(monoctr_read(MONOCTR_BOOTLOADER_VERSION, &val), NULL); + ensure(sectrue * (val == VERSION_MONOTONIC), "Bootloader downgrade protection"); } diff --git a/core/embed/models/layout_common.h b/core/embed/models/layout_common.h index e95f868bf0..318bf5abd4 100644 --- a/core/embed/models/layout_common.h +++ b/core/embed/models/layout_common.h @@ -9,6 +9,7 @@ #define FLASH_OTP_BLOCK_VENDOR_HEADER_LOCK 2 #define FLASH_OTP_BLOCK_RANDOMNESS 3 #define FLASH_OTP_BLOCK_DEVICE_VARIANT 4 +#define FLASH_OTP_BLOCK_FIRMWARE_VERSION 5 #define STORAGE_AREAS_COUNT (2) diff --git a/core/embed/trezorhal/monoctr.h b/core/embed/trezorhal/monoctr.h new file mode 100644 index 0000000000..0d5e2bcfd1 --- /dev/null +++ b/core/embed/trezorhal/monoctr.h @@ -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 . + */ + +#ifndef TREZORHAL_MONOCTR +#define TREZORHAL_MONOCTR + +// Monoctr module provides monotonic counter functionality + +#define MONOCTR_MAX_VALUE 63 + +#include +#include "secbool.h" + +typedef enum { + MONOCTR_BOOTLOADER_VERSION = 0, + MONOCTR_FIRMWARE_VERSION = 1, +} monoctr_type_t; + +secbool monoctr_write(monoctr_type_t type, uint8_t value); + +secbool monoctr_read(monoctr_type_t type, uint8_t* value); + +#endif diff --git a/core/embed/trezorhal/secret.h b/core/embed/trezorhal/secret.h index f8fa453da0..99d5d120db 100644 --- a/core/embed/trezorhal/secret.h +++ b/core/embed/trezorhal/secret.h @@ -9,6 +9,7 @@ #define SECRET_MONOTONIC_COUNTER_OFFSET 48 #define SECRET_MONOTONIC_COUNTER_LEN 1024 +#define SECRET_MONOTONIC_COUNTER2_OFFSET (SECRET_MONOTONIC_COUNTER_LEN + 48) #define SECRET_BHK_OFFSET (1024 * 8) #define SECRET_BHK_LEN 32 diff --git a/core/embed/trezorhal/stm32f4/monoctr.c b/core/embed/trezorhal/stm32f4/monoctr.c new file mode 100644 index 0000000000..a30e13d907 --- /dev/null +++ b/core/embed/trezorhal/stm32f4/monoctr.c @@ -0,0 +1,119 @@ +/* + * 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 "monoctr.h" +#include "flash_otp.h" +#include "model.h" +#include "string.h" + +#if PRODUCTION +static int get_otp_block(monoctr_type_t type) { + switch (type) { + case MONOCTR_BOOTLOADER_VERSION: + return FLASH_OTP_BLOCK_BOOTLOADER_VERSION; + case MONOCTR_FIRMWARE_VERSION: + return FLASH_OTP_BLOCK_FIRMWARE_VERSION; + default: + return -1; + } +} +#endif + +secbool monoctr_write(monoctr_type_t type, uint8_t value) { +#if PRODUCTION + if (value > MONOCTR_MAX_VALUE) { + return secfalse; + } + + uint8_t bits[FLASH_OTP_BLOCK_SIZE]; + for (int i = 0; i < FLASH_OTP_BLOCK_SIZE * 8; i++) { + if (i < value) { + bits[i / 8] &= ~(1 << (7 - (i % 8))); + } else { + bits[i / 8] |= (1 << (7 - (i % 8))); + } + } + + int block = get_otp_block(type); + + if (block < 0) { + return secfalse; + } + + ensure(flash_otp_write(block, 0, bits, FLASH_OTP_BLOCK_SIZE), NULL); + +#endif + return sectrue; +} + +secbool monoctr_read(monoctr_type_t type, uint8_t* value) { +#if PRODUCTION + uint8_t bits[FLASH_OTP_BLOCK_SIZE]; + + int block = get_otp_block(type); + + if (block < 0) { + return secfalse; + } + + ensure(flash_otp_read(block, 0, bits, FLASH_OTP_BLOCK_SIZE), NULL); + + int result = 0; + + int i; + + // Iterate through each bit position in the bit field + for (i = 0; i < FLASH_OTP_BLOCK_SIZE * 8; i++) { + // Calculate the byte and bit index within the byte + int byteIndex = i / 8; + int bitIndex = 7 - (i % 8); + + // Check if the current bit is 0 + if ((bits[byteIndex] & (1 << bitIndex)) == 0) { + // If the bit is 0, increment the value + result++; + } else { + // Stop when we find the first 1 bit + break; + } + } + + for (; i < FLASH_OTP_BLOCK_SIZE * 8; i++) { + // Calculate the byte and bit index within the byte + int byteIndex = i / 8; + int bitIndex = 7 - (i % 8); + if ((bits[byteIndex] & (1 << bitIndex)) == 0) { + // If the bit is 0, return false - the monotonic counter is not valid + return secfalse; + } + } + + if (value != NULL) { + *value = result; + } else { + return secfalse; + } +#else + + *value = 0; + +#endif + + return sectrue; +} diff --git a/core/embed/trezorhal/stm32u5/monoctr.c b/core/embed/trezorhal/stm32u5/monoctr.c new file mode 100644 index 0000000000..e9db4eb273 --- /dev/null +++ b/core/embed/trezorhal/stm32u5/monoctr.c @@ -0,0 +1,111 @@ +/* + * 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 "monoctr.h" +#include "flash_area.h" +#include "model.h" +#include "secret.h" + +static int32_t get_offset(monoctr_type_t type) { + switch (type) { + case MONOCTR_BOOTLOADER_VERSION: + return SECRET_MONOTONIC_COUNTER_OFFSET; + case MONOCTR_FIRMWARE_VERSION: + return SECRET_MONOTONIC_COUNTER2_OFFSET; + default: + return -1; + } +} + +secbool monoctr_write(monoctr_type_t type, uint8_t value) { + if (value > MONOCTR_MAX_VALUE) { + return secfalse; + } + + int32_t offset = get_offset(type); + + if (offset < 0) { + return secfalse; + } + + for (int i = 0; i < value; i++) { + uint32_t data[4] = {0}; + secret_write((uint8_t *)data, offset + i * 16, 16); + } + + return sectrue; +} + +secbool monoctr_read(monoctr_type_t type, uint8_t *value) { + int32_t offset = get_offset(type); + + if (offset < 0) { + return secfalse; + } + + const uint8_t *counter_addr = flash_area_get_address( + &SECRET_AREA, offset, SECRET_MONOTONIC_COUNTER_LEN); + + if (counter_addr == NULL) { + return secfalse; + } + + int counter = 0; + + int i = 0; + + for (i = 0; i < SECRET_MONOTONIC_COUNTER_LEN / 16; i++) { + secbool not_cleared = sectrue; + for (int j = 0; j < 16; j++) { + if (counter_addr[i * 16 + j] != 0xFF) { + not_cleared = secfalse; + break; + } + } + + if (not_cleared != sectrue) { + counter++; + } else { + break; + } + } + + for (; i < SECRET_MONOTONIC_COUNTER_LEN / 16; i++) { + secbool not_cleared = sectrue; + for (int j = 0; j < 16; j++) { + if (counter_addr[i * 16 + j] != 0xFF) { + not_cleared = secfalse; + break; + } + } + + if (not_cleared != sectrue) { + // monotonic counter is not valid + return secfalse; + } + } + + if (value != NULL) { + *value = counter; + } else { + return secfalse; + } + + return sectrue; +} diff --git a/core/embed/trezorhal/unix/monoctr.c b/core/embed/trezorhal/unix/monoctr.c new file mode 100644 index 0000000000..e1ddf7a358 --- /dev/null +++ b/core/embed/trezorhal/unix/monoctr.c @@ -0,0 +1,109 @@ +/* + * 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 "monoctr.h" +#include "flash_otp.h" +#include "model.h" +#include "string.h" + +static int get_otp_block(monoctr_type_t type) { + switch (type) { + case MONOCTR_BOOTLOADER_VERSION: + return FLASH_OTP_BLOCK_BOOTLOADER_VERSION; + case MONOCTR_FIRMWARE_VERSION: + return FLASH_OTP_BLOCK_FIRMWARE_VERSION; + default: + return -1; + } +} + +secbool monoctr_write(monoctr_type_t type, uint8_t value) { + if (value > MONOCTR_MAX_VALUE) { + return secfalse; + } + + uint8_t bits[FLASH_OTP_BLOCK_SIZE]; + for (int i = 0; i < FLASH_OTP_BLOCK_SIZE * 8; i++) { + if (i < value) { + bits[i / 8] &= ~(1 << (7 - (i % 8))); + } else { + bits[i / 8] |= (1 << (7 - (i % 8))); + } + } + + 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; +} + +secbool monoctr_read(monoctr_type_t type, uint8_t* value) { + uint8_t bits[FLASH_OTP_BLOCK_SIZE]; + + int block = get_otp_block(type); + + if (block < 0) { + return secfalse; + } + + ensure(flash_otp_read(block, 0, bits, FLASH_OTP_BLOCK_SIZE), NULL); + + int result = 0; + + int i; + + // Iterate through each bit position in the bit field + for (i = 0; i < FLASH_OTP_BLOCK_SIZE * 8; i++) { + // Calculate the byte and bit index within the byte + int byteIndex = i / 8; + int bitIndex = 7 - (i % 8); + + // Check if the current bit is 0 + if ((bits[byteIndex] & (1 << bitIndex)) == 0) { + // If the bit is 0, increment the value + result++; + } else { + // Stop when we find the first 1 bit + break; + } + } + + for (; i < FLASH_OTP_BLOCK_SIZE * 8; i++) { + // Calculate the byte and bit index within the byte + int byteIndex = i / 8; + int bitIndex = 7 - (i % 8); + if ((bits[byteIndex] & (1 << bitIndex)) == 0) { + // If the bit is 0, return false - the monotonic counter is not valid + return secfalse; + } + } + + if (value != NULL) { + *value = result; + } else { + return secfalse; + } + + return sectrue; +} diff --git a/core/site_scons/models/stm32f4_common.py b/core/site_scons/models/stm32f4_common.py index 86032a51c1..3a53387f73 100644 --- a/core/site_scons/models/stm32f4_common.py +++ b/core/site_scons/models/stm32f4_common.py @@ -47,6 +47,7 @@ def stm32f4_common_files(env, defines, sources, paths): "embed/trezorhal/stm32f4/flash.c", "embed/trezorhal/stm32f4/flash_otp.c", "embed/trezorhal/stm32f4/lowlevel.c", + "embed/trezorhal/stm32f4/monoctr.c", "embed/trezorhal/stm32f4/mpu.c", "embed/trezorhal/stm32f4/platform.c", "embed/trezorhal/stm32f4/secret.c", diff --git a/core/site_scons/models/stm32u5_common.py b/core/site_scons/models/stm32u5_common.py index 2bbd7b31bb..ff08f108c7 100644 --- a/core/site_scons/models/stm32u5_common.py +++ b/core/site_scons/models/stm32u5_common.py @@ -57,6 +57,7 @@ def stm32u5_common_files(env, defines, sources, paths): "embed/trezorhal/stm32u5/flash_otp.c", "embed/trezorhal/stm32u5/lowlevel.c", "embed/trezorhal/stm32u5/hash_processor.c", + "embed/trezorhal/stm32u5/monoctr.c", "embed/trezorhal/stm32u5/mpu.c", "embed/trezorhal/stm32u5/platform.c", "embed/trezorhal/stm32u5/secret.c",