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",