refactor(core): move unit_properties to sec layer
What changed, and why it matters
This commit is a pure code reorganization: it moves the unit_properties module from the util directory to the sec (security) directory and updates all include paths and build files accordingly. The actual logic for reading device properties from one-time-programmable memory, serial numbers, and SD hotswap settings is copied unchanged. There is no functional change and no security fix or vulnerability introduced.
No action required. Treat as routine refactoring. If reviewing for security architecture, verify that the new sec/ location is covered by the expected privilege/compilation context, but the commit itself does not alter behavior.
Security signals we found
No functional code changes
No changes to OTP read logic or trust boundaries
Include path and build path migration only
No changelog entry, consistent with internal refactoring
Evidence from the diff
The patch relocates unit_properties from core/embed/util/ to core/embed/sec/. Header includes change from
Changed components
core/embed/sec/unit_propertiescore/embed/util/unit_properties (removed)core/embed/projects/bootloadercore/embed/projects/kernelcore/embed/projects/prodtestcore/embed/projects/secmoncore/embed/projects/unixcore/embed/sys/smcallcore/embed/sys/syscallcore/embed/upymod/modtrezorutilscore/site_scons build scriptsInspect captured patch +458 / −458
diff --git a/core/embed/io/ble/stm32/ble.c b/core/embed/io/ble/stm32/ble.c
index f23a2b6d..fb734c3e 100644
--- a/core/embed/io/ble/stm32/ble.c
+++ b/core/embed/io/ble/stm32/ble.c
@@ -28,12 +28,12 @@
#include <io/ble.h>
#include <io/nrf.h>
#include <sec/backup_ram.h>
+#include <sec/unit_properties.h>
#include <sys/irq.h>
#include <sys/sysevent_source.h>
#include <sys/systick.h>
#include <sys/systimer.h>
#include <util/tsqueue.h>
-#include <util/unit_properties.h>
#ifdef USE_POWER_MANAGER
#include <io/power_manager.h>
diff --git a/core/embed/io/power_manager/fuel_gauge/battery_model.c b/core/embed/io/power_manager/fuel_gauge/battery_model.c
index a03b0724..b9bfa584 100644
--- a/core/embed/io/power_manager/fuel_gauge/battery_model.c
+++ b/core/embed/io/power_manager/fuel_gauge/battery_model.c
@@ -20,7 +20,7 @@
#include <math.h>
-#include <util/unit_properties.h>
+#include <sec/unit_properties.h>
#include "battery_model.h"
diff --git a/core/embed/projects/bootloader/main.c b/core/embed/projects/bootloader/main.c
index 5bf68ea4..63388c05 100644
--- a/core/embed/projects/bootloader/main.c
+++ b/core/embed/projects/bootloader/main.c
@@ -26,6 +26,7 @@
#include <io/usb_config.h>
#include <sec/random_delays.h>
#include <sec/secret.h>
+#include <sec/unit_properties.h>
#include <sys/bootargs.h>
#include <sys/bootutils.h>
#include <sys/system.h>
@@ -35,7 +36,6 @@
#include <util/image.h>
#include <util/rsod.h>
#include <util/rsod_special.h>
-#include <util/unit_properties.h>
#ifdef USE_BOOT_UCB
#include <util/boot_ucb.h>
diff --git a/core/embed/projects/bootloader/protob/protob.c b/core/embed/projects/bootloader/protob/protob.c
index 787872c0..aa43abd4 100644
--- a/core/embed/projects/bootloader/protob/protob.c
+++ b/core/embed/projects/bootloader/protob/protob.c
@@ -23,8 +23,8 @@
#include <pb.h>
#include <pb_decode.h>
+#include <sec/unit_properties.h>
#include <util/image.h>
-#include <util/unit_properties.h>
#if LOCKABLE_BOOTLOADER
#include <sec/secret.h>
diff --git a/core/embed/projects/kernel/main.c b/core/embed/projects/kernel/main.c
index ad285a07..0bc995a7 100644
--- a/core/embed/projects/kernel/main.c
+++ b/core/embed/projects/kernel/main.c
@@ -25,6 +25,7 @@
#include <sec/random_delays.h>
#include <sec/secret.h>
#include <sec/secure_aes.h>
+#include <sec/unit_properties.h>
#include <sys/bootutils.h>
#include <sys/coreapp.h>
#include <sys/mpu.h>
@@ -36,7 +37,6 @@
#include <util/boot_image.h>
#include <util/option_bytes.h>
#include <util/rsod.h>
-#include <util/unit_properties.h>
#ifdef USE_BUTTON
#include <io/button.h>
diff --git a/core/embed/projects/prodtest/cmd/common.c b/core/embed/projects/prodtest/cmd/common.c
index 35696510..455fa2ee 100644
--- a/core/embed/projects/prodtest/cmd/common.c
+++ b/core/embed/projects/prodtest/cmd/common.c
@@ -17,9 +17,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <sec/unit_properties.h>
#include <trezor_model.h>
#include <trezor_rtl.h>
-#include <util/unit_properties.h>
#include "common.h"
diff --git a/core/embed/projects/prodtest/main.c b/core/embed/projects/prodtest/main.c
index abfaa703..dc960767 100644
--- a/core/embed/projects/prodtest/main.c
+++ b/core/embed/projects/prodtest/main.c
@@ -26,12 +26,12 @@
#include <io/usb.h>
#include <io/usb_config.h>
#include <rtl/cli.h>
+#include <sec/unit_properties.h>
#include <sys/system.h>
#include <sys/systick.h>
#include <util/board_capabilities.h>
#include <util/flash_otp.h>
#include <util/rsod.h>
-#include <util/unit_properties.h>
#include "commands.h"
#include "rust_types.h"
diff --git a/core/embed/projects/secmon/main.c b/core/embed/projects/secmon/main.c
index afbe4e8f..7a9ebf53 100644
--- a/core/embed/projects/secmon/main.c
+++ b/core/embed/projects/secmon/main.c
@@ -22,6 +22,7 @@
#include <sec/random_delays.h>
#include <sec/secure_aes.h>
+#include <sec/unit_properties.h>
#include <sys/bootutils.h>
#include <sys/system.h>
#include <sys/systick.h>
@@ -30,7 +31,6 @@
#include <util/boot_image.h>
#include <util/flash.h>
#include <util/option_bytes.h>
-#include <util/unit_properties.h>
#ifdef USE_BACKUP_RAM
#include <sec/backup_ram.h>
diff --git a/core/embed/projects/unix/main.c b/core/embed/projects/unix/main.c
index 929ea8a7..2d9c5cdd 100644
--- a/core/embed/projects/unix/main.c
+++ b/core/embed/projects/unix/main.c
@@ -39,12 +39,12 @@
#include <io/display.h>
#include <io/usb_config.h>
#include <sec/secret.h>
+#include <sec/unit_properties.h>
#include <sys/system.h>
#include <sys/systimer.h>
#include <util/flash.h>
#include <util/flash_otp.h>
#include <util/rsod.h>
-#include <util/unit_properties.h>
#include "extmod/misc.h"
#include "extmod/vfs_posix.h"
#include "genhdr/mpversion.h"
diff --git a/core/embed/sec/unit_properties/inc/sec/unit_properties.h b/core/embed/sec/unit_properties/inc/sec/unit_properties.h
new file mode 100644
index 00000000..2ca44559
--- /dev/null
+++ b/core/embed/sec/unit_properties/inc/sec/unit_properties.h
@@ -0,0 +1,132 @@
+/*
+ * 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 <trezor_types.h>
+
+#define MAX_DEVICE_SN_SIZE 31
+
+#ifdef SECURE_MODE
+
+/**
+ * @brief Initializes module and detects the unit properties
+ *
+ * @return true if the properties are successfully detected
+ * @return false otherwise
+ */
+bool unit_properties_init(void);
+
+#endif // SECURE_MODE
+
+typedef struct {
+ /**
+ * Production lock status indicator.
+ * When set to true, the unit properties are locked and cannot be modified.
+ * This indicates the device is in production mode and configuration is
+ * finalized.
+ */
+ bool locked;
+
+ /**
+ * Unit color identifier.
+ * This field contains a hardware-specific color code that is opaque to the
+ * firmware. The value is interpreted and displayed by Trezor Suite for user
+ * identification purposes.
+ */
+ uint8_t color;
+ /** Validity flag for the color field - set to true when color contains a
+ * valid value */
+ bool color_is_valid;
+
+ /**
+ * Unit packaging type identifier.
+ * This field contains a packaging-specific code that is opaque to the
+ * firmware. The value is used by Trezor Suite to determine the device's
+ * packaging variant.
+ */
+ uint8_t packaging;
+ /** Validity flag for the packaging field - set to true when packaging
+ * contains a valid value */
+ bool packaging_is_valid;
+
+ /**
+ * Bitcoin-only firmware restriction flag.
+ * When set to true, indicates this unit is configured to run Bitcoin-only
+ * firmware, restricting functionality to Bitcoin-related operations only.
+ */
+ bool btconly;
+ /** Validity flag for the btconly field - set to true when btconly contains a
+ * valid value */
+ bool btconly_is_valid;
+
+ /**
+ * SD card hotswap capability flag.
+ * When set to true, indicates the unit supports hot-swapping of SD cards
+ * without requiring a system restart or power cycle.
+ */
+ bool sd_hotswap_enabled;
+
+ /**
+ * Type of the battery used in this unit
+ *
+ * Interpretation is model-specific.
+ */
+ uint8_t battery_type;
+ /** Validity flag for the battery_type field - set to true when battery_type
+ * contains a valid value */
+ bool battery_type_is_valid;
+
+ /** Device production date */
+ struct {
+ uint16_t year;
+ uint8_t month;
+ uint8_t day;
+ } production_date;
+
+} unit_properties_t;
+
+/**
+ * @brief Gets a copy of unit properties structure
+ *
+ * Properties are detected just once during the initialization.
+ *
+ * @param props Pointer to the structure to fill with unit properties
+ */
+void unit_properties_get(unit_properties_t* props);
+
+/**
+ * @brief Gets a pointer to the static unit properties structure
+ *
+ * @return const unit_properties_t* Pointer to the static unit properties
+ * structure
+ */
+const unit_properties_t* unit_properties(void);
+
+/**
+ * @brief Gets the device serial number
+ *
+ * @param device_sn Buffer to store the device serial number
+ * @param max_device_sn_size Maximum size of the device_sn buffer
+ * @param device_sn_size Pointer to store the actual size of the serial number
+ * @return true if the serial number was successfully retrieved
+ * @return false otherwise
+ */
+bool unit_properties_get_sn(uint8_t* device_sn, size_t max_device_sn_size,
+ size_t* device_sn_size);
diff --git a/core/embed/sec/unit_properties/stm32/unit_properties.c b/core/embed/sec/unit_properties/stm32/unit_properties.c
new file mode 100644
index 00000000..617ecd06
--- /dev/null
+++ b/core/embed/sec/unit_properties/stm32/unit_properties.c
@@ -0,0 +1,212 @@
+/*
+ * 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 <sec/unit_properties.h>
+
+#ifdef SECURE_MODE
+
+#include <trezor_bsp.h>
+#include <trezor_model.h>
+#include <trezor_rtl.h>
+
+#include <util/flash_otp.h>
+
+// Unit properties driver structure
+typedef struct {
+ // Set to true if the unit properties are valid
+ bool initialized;
+ // Cached unit properties data
+ unit_properties_t cache;
+
+} unit_properties_driver_t;
+
+// Unit properties driver instance
+static unit_properties_driver_t g_unit_properties_driver = {
+ .initialized = false,
+};
+
+// Parse two digit number from the string.
+//
+// Returns -1 if the string is not a valid two-digit number.
+static inline int parse_two_digits(const char* str) {
+ if (str[0] < '0' || str[0] > '9' || str[1] < '0' || str[1] > '9') {
+ return -1;
+ }
+ return (str[0] - '0') * 10 + (str[1] - '0');
+}
+
+// Reads the production date from the OTP block.
+//
+// Returns `false` in case of and flash read error.
+static bool get_production_date(int* year, int* month, int* day) {
+ *year = -1;
+
+ uint8_t otp_data[FLASH_OTP_BLOCK_SIZE];
+
+ // Batch block contains a string with the build date.
+ // Expecting format {MODEL_IDENTIFIER}-YYMMDD.
+ // https://docs.trezor.io/trezor-firmware/core/misc/memory.html?highlight=otp#otp
+
+ if (sectrue != flash_otp_read(FLASH_OTP_BLOCK_BATCH, 0, otp_data,
+ FLASH_OTP_BLOCK_SIZE)) {
+ return false;
+ }
+
+ if (otp_data[0] != 0xFF) {
+ char* str = (char*)otp_data;
+
+ // Last 7 characters are the date "-YYMMDD"
+ int i = strnlen(str, FLASH_OTP_BLOCK_SIZE) - 7;
+
+ if (i >= 0 && str[i] == '-') {
+ *year = parse_two_digits(&str[i + 1]);
+ *month = parse_two_digits(&str[i + 3]);
+ *day = parse_two_digits(&str[i + 5]);
+ }
+ }
+
+ return true;
+}
+
+// Reads and parses the unit properties from the OTP block.
+//
+// Returns `false` in case of and flash read error.
+static bool detect_properties(unit_properties_t* props) {
+ uint8_t otp_data[FLASH_OTP_BLOCK_SIZE];
+
+ props->locked =
+ sectrue == flash_otp_is_locked(FLASH_OTP_BLOCK_DEVICE_VARIANT);
+
+ if (sectrue != flash_otp_read(FLASH_OTP_BLOCK_DEVICE_VARIANT, 0, otp_data,
+ FLASH_OTP_BLOCK_SIZE)) {
+ return false;
+ }
+
+ if (sectrue == flash_otp_is_locked(FLASH_OTP_BLOCK_DEVICE_VARIANT_REWORK)) {
+ uint8_t otp_rework_data[FLASH_OTP_BLOCK_SIZE];
+ if (sectrue != flash_otp_read(FLASH_OTP_BLOCK_DEVICE_VARIANT_REWORK, 0,
+ otp_rework_data, FLASH_OTP_BLOCK_SIZE)) {
+ return false;
+ }
+ if (otp_rework_data[0] != 0xFF) {
+ memcpy(otp_data, otp_rework_data, sizeof(otp_rework_data));
+ }
+ }
+
+ switch (otp_data[0]) {
+ case 0xFF:
+ // OTP block was not written yet, keep the defaults
+ break;
+
+ case 0x01:
+ // The fields were gradually added to the OTP block over time.
+ // Unused trailing bytes were always set to 0x00.
+ props->color = otp_data[1];
+ props->color_is_valid = true;
+ props->btconly = otp_data[2] == 1;
+ props->btconly_is_valid = true;
+ props->packaging = otp_data[3];
+ props->packaging_is_valid = true;
+ props->battery_type = otp_data[4];
+ props->battery_type_is_valid = true;
+ break;
+
+ default:
+ // Unknown variant, be conservative and keep the defaults
+ break;
+ }
+
+ int production_year = 0, production_month = 0, production_day = 0;
+ get_production_date(&production_year, &production_month, &production_day);
+ props->production_date.year = 2000 + production_year;
+ props->production_date.month = production_month;
+ props->production_date.day = production_day;
+
+ props->sd_hotswap_enabled = true;
+#ifdef TREZOR_MODEL_T2T1
+ // Early produced TTs have a HW bug that prevents hotswapping of the SD card,
+ // lets check the build data and decide based on that.
+
+ if (production_year <= 18) {
+ props->sd_hotswap_enabled = false;
+ }
+#endif
+
+ return true;
+}
+
+bool unit_properties_init(void) {
+ unit_properties_driver_t* drv = &g_unit_properties_driver;
+
+ if (drv->initialized) {
+ return true;
+ }
+
+ memset(drv, 0, sizeof(unit_properties_driver_t));
+
+ if (!detect_properties(&drv->cache)) {
+ return false;
+ }
+
+ drv->initialized = true;
+
+ return true;
+}
+
+void unit_properties_get(unit_properties_t* props) {
+ unit_properties_driver_t* drv = &g_unit_properties_driver;
+
+ ensure(sectrue * drv->initialized, "Unit properties not initialized");
+
+ *props = drv->cache;
+}
+
+bool unit_properties_get_sn(uint8_t* device_sn, size_t max_device_sn_size,
+ size_t* device_sn_size) {
+ uint8_t block[FLASH_OTP_BLOCK_SIZE] = {0};
+ // The OTP block should contain a null-terminated string when set.
+ if (sectrue !=
+ flash_otp_read(FLASH_OTP_BLOCK_DEVICE_SN, 0, block, sizeof(block)) ||
+ block[0] == 0xFF) {
+ return false;
+ }
+
+ size_t len = strnlen((char*)block, sizeof(block));
+ if (len > max_device_sn_size) {
+ return false;
+ }
+
+ memcpy(device_sn, block, len);
+ *device_sn_size = len;
+ return true;
+}
+
+#endif // SECURE_MODE
+
+const unit_properties_t* unit_properties(void) {
+ static bool cache_initialized = false;
+ static unit_properties_t cache = {0};
+
+ if (!cache_initialized) {
+ unit_properties_get(&cache);
+ cache_initialized = true;
+ }
+
+ return &cache;
+}
diff --git a/core/embed/sec/unit_properties/unix/unit_properties.c b/core/embed/sec/unit_properties/unix/unit_properties.c
new file mode 100644
index 00000000..22ce5ce3
--- /dev/null
+++ b/core/embed/sec/unit_properties/unix/unit_properties.c
@@ -0,0 +1,92 @@
+/*
+ * 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 <trezor_model.h>
+#include <trezor_rtl.h>
+
+#include <sec/unit_properties.h>
+#include <util/flash_otp.h>
+
+// Unit properties driver structure
+typedef struct {
+ // Set to true if the unit properties are valid
+ bool initialized;
+ // Cached unit properties data
+ unit_properties_t cache;
+
+} unit_properties_driver_t;
+
+// Unit properties driver instance
+static unit_properties_driver_t g_unit_properties_driver = {
+ .initialized = false,
+};
+
+bool unit_properties_init(void) {
+ unit_properties_driver_t* drv = &g_unit_properties_driver;
+
+ if (drv->initialized) {
+ return true;
+ }
+
+ memset(drv, 0, sizeof(unit_properties_driver_t));
+
+ drv->cache.sd_hotswap_enabled = true;
+#ifdef TREZOR_MODEL_T2T1
+ drv->cache.sd_hotswap_enabled = false;
+#endif
+
+ // Properties detection is not fully implemented for emulator.
+ // Default values are used.
+
+ drv->initialized = true;
+
+ return true;
+}
+
+void unit_properties_get(unit_properties_t* props) {
+ unit_properties_driver_t* drv = &g_unit_properties_driver;
+
+ ensure(sectrue * drv->initialized, "Unit properties not initialized");
+
+ *props = drv->cache;
+}
+
+const unit_properties_t* unit_properties(void) {
+ static bool cache_initialized = false;
+ static unit_properties_t cache = {0};
+
+ if (!cache_initialized) {
+ unit_properties_get(&cache);
+ cache_initialized = true;
+ }
+
+ return &cache;
+}
+
+bool unit_properties_get_sn(uint8_t* device_sn, size_t max_device_sn_size,
+ size_t* device_sn_size) {
+ uint8_t sn[] = "12345678901234";
+ if (max_device_sn_size < sizeof(sn) - 1) {
+ return false;
+ }
+
+ memcpy(device_sn, sn, sizeof(sn) - 1);
+ *device_sn_size = sizeof(sn) - 1;
+ return true;
+}
diff --git a/core/embed/sys/smcall/stm32/smcall_dispatch.c b/core/embed/sys/smcall/stm32/smcall_dispatch.c
index 44794627..465bb8d0 100644
--- a/core/embed/sys/smcall/stm32/smcall_dispatch.c
+++ b/core/embed/sys/smcall/stm32/smcall_dispatch.c
@@ -25,13 +25,13 @@
#include <sec/rng_strong.h>
#include <sec/secret.h>
#include <sec/secret_keys.h>
+#include <sec/unit_properties.h>
#include <sys/bootargs.h>
#include <sys/bootutils.h>
#include <sys/irq.h>
#include <sys/system.h>
#include <util/board_capabilities.h>
#include <util/fwutils.h>
-#include <util/unit_properties.h>
#ifdef USE_BACKUP_RAM
#include <sec/backup_ram.h>
diff --git a/core/embed/sys/smcall/stm32/smcall_stubs.c b/core/embed/sys/smcall/stm32/smcall_stubs.c
index e95b9ac4..de892bec 100644
--- a/core/embed/sys/smcall/stm32/smcall_stubs.c
+++ b/core/embed/sys/smcall/stm32/smcall_stubs.c
@@ -127,7 +127,7 @@ void resume_secure_drivers(void) {
// unit_properties.h
// =============================================================================
-#include <util/unit_properties.h>
+#include <sec/unit_properties.h>
void unit_properties_get(unit_properties_t *props) {
smcall_invoke1((uint32_t)props, SMCALL_UNIT_PROPERTIES_GET);
diff --git a/core/embed/sys/smcall/stm32/smcall_verifiers.h b/core/embed/sys/smcall/stm32/smcall_verifiers.h
index 489937ce..284c8453 100644
--- a/core/embed/sys/smcall/stm32/smcall_verifiers.h
+++ b/core/embed/sys/smcall/stm32/smcall_verifiers.h
@@ -45,7 +45,7 @@ void reboot_and_upgrade__verified(const uint8_t hash[32]);
void reboot_with_rsod__verified(const systask_postmortem_t *pminfo);
// ---------------------------------------------------------------------
-#include <util/unit_properties.h>
+#include <sec/unit_properties.h>
void unit_properties_get__verified(unit_properties_t *props);
diff --git a/core/embed/sys/syscall/stm32/syscall_dispatch.c b/core/embed/sys/syscall/stm32/syscall_dispatch.c
index 3095c5ea..b62b3f3f 100644
--- a/core/embed/sys/syscall/stm32/syscall_dispatch.c
+++ b/core/embed/sys/syscall/stm32/syscall_dispatch.c
@@ -30,6 +30,7 @@
#include <sec/rng_strong.h>
#include <sec/secret.h>
#include <sec/secret_keys.h>
+#include <sec/unit_properties.h>
#include <sys/bootutils.h>
#include <sys/irq.h>
#include <sys/sysevent.h>
@@ -38,7 +39,6 @@
#include <sys/systick.h>
#include <util/fwutils.h>
#include <util/translations.h>
-#include <util/unit_properties.h>
#ifdef USE_BLE
#include <io/ble.h>
diff --git a/core/embed/sys/syscall/stm32/syscall_stubs.c b/core/embed/sys/syscall/stm32/syscall_stubs.c
index fbf1a269..4a3d7f0f 100644
--- a/core/embed/sys/syscall/stm32/syscall_stubs.c
+++ b/core/embed/sys/syscall/stm32/syscall_stubs.c
@@ -293,7 +293,7 @@ secbool __wur sdcard_write_blocks(const uint32_t *src, uint32_t block_num,
// unit_properties.h
// =============================================================================
-#include <util/unit_properties.h>
+#include <sec/unit_properties.h>
void unit_properties_get(unit_properties_t *props) {
syscall_invoke1((uint32_t)props, SYSCALL_UNIT_PROPERTIES_GET);
diff --git a/core/embed/sys/syscall/stm32/syscall_verifiers.h b/core/embed/sys/syscall/stm32/syscall_verifiers.h
index 6282d39a..21977c4d 100644
--- a/core/embed/sys/syscall/stm32/syscall_verifiers.h
+++ b/core/embed/sys/syscall/stm32/syscall_verifiers.h
@@ -118,7 +118,7 @@ secbool __wur sdcard_write_blocks__verified(const uint32_t *src,
#endif // USE_SD_CARD
// ---------------------------------------------------------------------
-#include <util/unit_properties.h>
+#include <sec/unit_properties.h>
void unit_properties_get__verified(unit_properties_t *props);
diff --git a/core/embed/upymod/modtrezorutils/modtrezorutils.c b/core/embed/upymod/modtrezorutils/modtrezorutils.c
index 789eb317..9686abb1 100644
--- a/core/embed/upymod/modtrezorutils/modtrezorutils.c
+++ b/core/embed/upymod/modtrezorutils/modtrezorutils.c
@@ -39,10 +39,10 @@
#include <io/notify.h>
#include <sec/secret_keys.h>
+#include <sec/unit_properties.h>
#include <sys/bootutils.h>
#include <util/fwutils.h>
#include <util/scm_revision.h>
-#include <util/unit_properties.h>
#include "blake2s.h"
#include "memzero.h"
diff --git a/core/embed/util/unit_properties/inc/util/unit_properties.h b/core/embed/util/unit_properties/inc/util/unit_properties.h
deleted file mode 100644
index 2ca44559..00000000
--- a/core/embed/util/unit_properties/inc/util/unit_properties.h
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * 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 <trezor_types.h>
-
-#define MAX_DEVICE_SN_SIZE 31
-
-#ifdef SECURE_MODE
-
-/**
- * @brief Initializes module and detects the unit properties
- *
- * @return true if the properties are successfully detected
- * @return false otherwise
- */
-bool unit_properties_init(void);
-
-#endif // SECURE_MODE
-
-typedef struct {
- /**
- * Production lock status indicator.
- * When set to true, the unit properties are locked and cannot be modified.
- * This indicates the device is in production mode and configuration is
- * finalized.
- */
- bool locked;
-
- /**
- * Unit color identifier.
- * This field contains a hardware-specific color code that is opaque to the
- * firmware. The value is interpreted and displayed by Trezor Suite for user
- * identification purposes.
- */
- uint8_t color;
- /** Validity flag for the color field - set to true when color contains a
- * valid value */
- bool color_is_valid;
-
- /**
- * Unit packaging type identifier.
- * This field contains a packaging-specific code that is opaque to the
- * firmware. The value is used by Trezor Suite to determine the device's
- * packaging variant.
- */
- uint8_t packaging;
- /** Validity flag for the packaging field - set to true when packaging
- * contains a valid value */
- bool packaging_is_valid;
-
- /**
- * Bitcoin-only firmware restriction flag.
- * When set to true, indicates this unit is configured to run Bitcoin-only
- * firmware, restricting functionality to Bitcoin-related operations only.
- */
- bool btconly;
- /** Validity flag for the btconly field - set to true when btconly contains a
- * valid value */
- bool btconly_is_valid;
-
- /**
- * SD card hotswap capability flag.
- * When set to true, indicates the unit supports hot-swapping of SD cards
- * without requiring a system restart or power cycle.
- */
- bool sd_hotswap_enabled;
-
- /**
- * Type of the battery used in this unit
- *
- * Interpretation is model-specific.
- */
- uint8_t battery_type;
- /** Validity flag for the battery_type field - set to true when battery_type
- * contains a valid value */
- bool battery_type_is_valid;
-
- /** Device production date */
- struct {
- uint16_t year;
- uint8_t month;
- uint8_t day;
- } production_date;
-
-} unit_properties_t;
-
-/**
- * @brief Gets a copy of unit properties structure
- *
- * Properties are detected just once during the initialization.
- *
- * @param props Pointer to the structure to fill with unit properties
- */
-void unit_properties_get(unit_properties_t* props);
-
-/**
- * @brief Gets a pointer to the static unit properties structure
- *
- * @return const unit_properties_t* Pointer to the static unit properties
- * structure
- */
-const unit_properties_t* unit_properties(void);
-
-/**
- * @brief Gets the device serial number
- *
- * @param device_sn Buffer to store the device serial number
- * @param max_device_sn_size Maximum size of the device_sn buffer
- * @param device_sn_size Pointer to store the actual size of the serial number
- * @return true if the serial number was successfully retrieved
- * @return false otherwise
- */
-bool unit_properties_get_sn(uint8_t* device_sn, size_t max_device_sn_size,
- size_t* device_sn_size);
diff --git a/core/embed/util/unit_properties/stm32/unit_properties.c b/core/embed/util/unit_properties/stm32/unit_properties.c
deleted file mode 100644
index 08248c59..00000000
--- a/core/embed/util/unit_properties/stm32/unit_properties.c
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * 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 <util/unit_properties.h>
-
-#ifdef SECURE_MODE
-
-#include <trezor_bsp.h>
-#include <trezor_model.h>
-#include <trezor_rtl.h>
-
-#include <util/flash_otp.h>
-
-// Unit properties driver structure
-typedef struct {
- // Set to true if the unit properties are valid
- bool initialized;
- // Cached unit properties data
- unit_properties_t cache;
-
-} unit_properties_driver_t;
-
-// Unit properties driver instance
-static unit_properties_driver_t g_unit_properties_driver = {
- .initialized = false,
-};
-
-// Parse two digit number from the string.
-//
-// Returns -1 if the string is not a valid two-digit number.
-static inline int parse_two_digits(const char* str) {
- if (str[0] < '0' || str[0] > '9' || str[1] < '0' || str[1] > '9') {
- return -1;
- }
- return (str[0] - '0') * 10 + (str[1] - '0');
-}
-
-// Reads the production date from the OTP block.
-//
-// Returns `false` in case of and flash read error.
-static bool get_production_date(int* year, int* month, int* day) {
- *year = -1;
-
- uint8_t otp_data[FLASH_OTP_BLOCK_SIZE];
-
- // Batch block contains a string with the build date.
- // Expecting format {MODEL_IDENTIFIER}-YYMMDD.
- // https://docs.trezor.io/trezor-firmware/core/misc/memory.html?highlight=otp#otp
-
- if (sectrue != flash_otp_read(FLASH_OTP_BLOCK_BATCH, 0, otp_data,
- FLASH_OTP_BLOCK_SIZE)) {
- return false;
- }
-
- if (otp_data[0] != 0xFF) {
- char* str = (char*)otp_data;
-
- // Last 7 characters are the date "-YYMMDD"
- int i = strnlen(str, FLASH_OTP_BLOCK_SIZE) - 7;
-
- if (i >= 0 && str[i] == '-') {
- *year = parse_two_digits(&str[i + 1]);
- *month = parse_two_digits(&str[i + 3]);
- *day = parse_two_digits(&str[i + 5]);
- }
- }
-
- return true;
-}
-
-// Reads and parses the unit properties from the OTP block.
-//
-// Returns `false` in case of and flash read error.
-static bool detect_properties(unit_properties_t* props) {
- uint8_t otp_data[FLASH_OTP_BLOCK_SIZE];
-
- props->locked =
- sectrue == flash_otp_is_locked(FLASH_OTP_BLOCK_DEVICE_VARIANT);
-
- if (sectrue != flash_otp_read(FLASH_OTP_BLOCK_DEVICE_VARIANT, 0, otp_data,
- FLASH_OTP_BLOCK_SIZE)) {
- return false;
- }
-
- if (sectrue == flash_otp_is_locked(FLASH_OTP_BLOCK_DEVICE_VARIANT_REWORK)) {
- uint8_t otp_rework_data[FLASH_OTP_BLOCK_SIZE];
- if (sectrue != flash_otp_read(FLASH_OTP_BLOCK_DEVICE_VARIANT_REWORK, 0,
- otp_rework_data, FLASH_OTP_BLOCK_SIZE)) {
- return false;
- }
- if (otp_rework_data[0] != 0xFF) {
- memcpy(otp_data, otp_rework_data, sizeof(otp_rework_data));
- }
- }
-
- switch (otp_data[0]) {
- case 0xFF:
- // OTP block was not written yet, keep the defaults
- break;
-
- case 0x01:
- // The fields were gradually added to the OTP block over time.
- // Unused trailing bytes were always set to 0x00.
- props->color = otp_data[1];
- props->color_is_valid = true;
- props->btconly = otp_data[2] == 1;
- props->btconly_is_valid = true;
- props->packaging = otp_data[3];
- props->packaging_is_valid = true;
- props->battery_type = otp_data[4];
- props->battery_type_is_valid = true;
- break;
-
- default:
- // Unknown variant, be conservative and keep the defaults
- break;
- }
-
- int production_year = 0, production_month = 0, production_day = 0;
- get_production_date(&production_year, &production_month, &production_day);
- props->production_date.year = 2000 + production_year;
- props->production_date.month = production_month;
- props->production_date.day = production_day;
-
- props->sd_hotswap_enabled = true;
-#ifdef TREZOR_MODEL_T2T1
- // Early produced TTs have a HW bug that prevents hotswapping of the SD card,
- // lets check the build data and decide based on that.
-
- if (production_year <= 18) {
- props->sd_hotswap_enabled = false;
- }
-#endif
-
- return true;
-}
-
-bool unit_properties_init(void) {
- unit_properties_driver_t* drv = &g_unit_properties_driver;
-
- if (drv->initialized) {
- return true;
- }
-
- memset(drv, 0, sizeof(unit_properties_driver_t));
-
- if (!detect_properties(&drv->cache)) {
- return false;
- }
-
- drv->initialized = true;
-
- return true;
-}
-
-void unit_properties_get(unit_properties_t* props) {
- unit_properties_driver_t* drv = &g_unit_properties_driver;
-
- ensure(sectrue * drv->initialized, "Unit properties not initialized");
-
- *props = drv->cache;
-}
-
-bool unit_properties_get_sn(uint8_t* device_sn, size_t max_device_sn_size,
- size_t* device_sn_size) {
- uint8_t block[FLASH_OTP_BLOCK_SIZE] = {0};
- // The OTP block should contain a null-terminated string when set.
- if (sectrue !=
- flash_otp_read(FLASH_OTP_BLOCK_DEVICE_SN, 0, block, sizeof(block)) ||
- block[0] == 0xFF) {
- return false;
- }
-
- size_t len = strnlen((char*)block, sizeof(block));
- if (len > max_device_sn_size) {
- return false;
- }
-
- memcpy(device_sn, block, len);
- *device_sn_size = len;
- return true;
-}
-
-#endif // SECURE_MODE
-
-const unit_properties_t* unit_properties(void) {
- static bool cache_initialized = false;
- static unit_properties_t cache = {0};
-
- if (!cache_initialized) {
- unit_properties_get(&cache);
- cache_initialized = true;
- }
-
- return &cache;
-}
diff --git a/core/embed/util/unit_properties/unix/unit_properties.c b/core/embed/util/unit_properties/unix/unit_properties.c
deleted file mode 100644
index 6224c08f..00000000
--- a/core/embed/util/unit_properties/unix/unit_properties.c
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * 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 <trezor_model.h>
-#include <trezor_rtl.h>
-
-#include <util/flash_otp.h>
-#include <util/unit_properties.h>
-
-// Unit properties driver structure
-typedef struct {
- // Set to true if the unit properties are valid
- bool initialized;
- // Cached unit properties data
- unit_properties_t cache;
-
-} unit_properties_driver_t;
-
-// Unit properties driver instance
-static unit_properties_driver_t g_unit_properties_driver = {
- .initialized = false,
-};
-
-bool unit_properties_init(void) {
- unit_properties_driver_t* drv = &g_unit_properties_driver;
-
- if (drv->initialized) {
- return true;
- }
-
- memset(drv, 0, sizeof(unit_properties_driver_t));
-
- drv->cache.sd_hotswap_enabled = true;
-#ifdef TREZOR_MODEL_T2T1
- drv->cache.sd_hotswap_enabled = false;
-#endif
-
- // Properties detection is not fully implemented for emulator.
- // Default values are used.
-
- drv->initialized = true;
-
- return true;
-}
-
-void unit_properties_get(unit_properties_t* props) {
- unit_properties_driver_t* drv = &g_unit_properties_driver;
-
- ensure(sectrue * drv->initialized, "Unit properties not initialized");
-
- *props = drv->cache;
-}
-
-const unit_properties_t* unit_properties(void) {
- static bool cache_initialized = false;
- static unit_properties_t cache = {0};
-
- if (!cache_initialized) {
- unit_properties_get(&cache);
- cache_initialized = true;
- }
-
- return &cache;
-}
-
-bool unit_properties_get_sn(uint8_t* device_sn, size_t max_device_sn_size,
- size_t* device_sn_size) {
- uint8_t sn[] = "12345678901234";
- if (max_device_sn_size < sizeof(sn) - 1) {
- return false;
- }
-
- memcpy(device_sn, sn, sizeof(sn) - 1);
- *device_sn_size = sizeof(sn) - 1;
- return true;
-}
diff --git a/core/site_scons/models/stm32f4_common.py b/core/site_scons/models/stm32f4_common.py
index 54d0e4ec..6e8193a2 100644
--- a/core/site_scons/models/stm32f4_common.py
+++ b/core/site_scons/models/stm32f4_common.py
@@ -25,6 +25,7 @@ def stm32f4_common_files(env, features_wanted, defines, sources, paths):
"embed/sys/pvd/inc",
"embed/sys/rng/inc",
"embed/sec/secret/inc",
+ "embed/sec/unit_properties/inc",
"embed/sys/stack/inc",
"embed/sys/startup/inc",
"embed/sys/syscall/inc",
@@ -36,7 +37,6 @@ def stm32f4_common_files(env, features_wanted, defines, sources, paths):
"embed/util/fwutils/inc",
"embed/util/option_bytes/inc",
"embed/util/tsqueue/inc",
- "embed/util/unit_properties/inc",
"vendor/micropython/lib/cmsis/inc",
"vendor/micropython/lib/stm32lib/STM32F4xx_HAL_Driver/Inc",
"vendor/micropython/lib/stm32lib/CMSIS/STM32F4xx/Include",
@@ -75,6 +75,7 @@ def stm32f4_common_files(env, features_wanted, defines, sources, paths):
"embed/sec/secret/secret_keys_common.c",
"embed/sec/storage/stm32f4/storage_salt.c",
"embed/sec/time_estimate/stm32/time_estimate.c",
+ "embed/sec/unit_properties/stm32/unit_properties.c",
"embed/sys/irq/stm32/irq.c",
"embed/sys/linker/linker_utils.c",
"embed/sys/mpu/stm32f4/mpu.c",
@@ -107,7 +108,6 @@ def stm32f4_common_files(env, features_wanted, defines, sources, paths):
"embed/util/fwutils/fwutils.c",
"embed/util/option_bytes/stm32f4/option_bytes.c",
"embed/util/tsqueue/tsqueue.c",
- "embed/util/unit_properties/stm32/unit_properties.c",
]
if "dbg_console" in features_wanted:
diff --git a/core/site_scons/models/stm32u5_common.py b/core/site_scons/models/stm32u5_common.py
index 85672265..3b4adb93 100644
--- a/core/site_scons/models/stm32u5_common.py
+++ b/core/site_scons/models/stm32u5_common.py
@@ -20,6 +20,7 @@ def stm32u5_common_files(env, features_wanted, defines, sources, paths):
"embed/sec/secure_aes/inc",
"embed/sec/tamper/inc",
"embed/sec/time_estimate/inc",
+ "embed/sec/unit_properties/inc",
"embed/sys/bsp/stm32u5",
"embed/sys/inc",
"embed/sys/irq/inc",
@@ -39,7 +40,6 @@ def stm32u5_common_files(env, features_wanted, defines, sources, paths):
"embed/util/fwutils/inc",
"embed/util/option_bytes/inc",
"embed/util/tsqueue/inc",
- "embed/util/unit_properties/inc",
"vendor/stm32u5xx_hal_driver/Inc",
"vendor/cmsis_device_u5/Include",
"vendor/cmsis_5/CMSIS/Core/Include",
@@ -101,6 +101,7 @@ def stm32u5_common_files(env, features_wanted, defines, sources, paths):
"embed/sec/storage/stm32u5/storage_salt.c",
"embed/sec/tamper/stm32u5/tamper.c",
"embed/sec/time_estimate/stm32/time_estimate.c",
+ "embed/sec/unit_properties/stm32/unit_properties.c",
"embed/sys/irq/stm32/irq.c",
"embed/sys/linker/linker_utils.c",
"embed/sys/mpu/stm32u5/mpu.c",
@@ -136,7 +137,6 @@ def stm32u5_common_files(env, features_wanted, defines, sources, paths):
"embed/util/fwutils/fwutils.c",
"embed/util/option_bytes/stm32u5/option_bytes.c",
"embed/util/tsqueue/tsqueue.c",
- "embed/util/unit_properties/stm32/unit_properties.c",
]
if "dbg_console" in features_wanted:
diff --git a/core/site_scons/models/unix_common.py b/core/site_scons/models/unix_common.py
index 32ff222d..9450bb5a 100644
--- a/core/site_scons/models/unix_common.py
+++ b/core/site_scons/models/unix_common.py
@@ -19,6 +19,7 @@ def unix_common_files(env, features_wanted, defines, sources, paths):
"embed/sec/rng/inc",
"embed/sec/monoctr/inc",
"embed/sec/secret/inc",
+ "embed/sec/unit_properties/inc",
"embed/sys/irq/inc",
"embed/sys/mpu/inc",
"embed/sys/rng/inc",
@@ -29,7 +30,6 @@ def unix_common_files(env, features_wanted, defines, sources, paths):
"embed/util/cpuid/inc",
"embed/util/flash/inc",
"embed/util/fwutils/inc",
- "embed/util/unit_properties/inc",
]
sources += [
@@ -44,6 +44,7 @@ def unix_common_files(env, features_wanted, defines, sources, paths):
"embed/sec/monoctr/unix/monoctr.c",
"embed/sec/rng/rng_strong.c",
"embed/sec/time_estimate/unix/time_estimate.c",
+ "embed/sec/unit_properties/unix/unit_properties.c",
"embed/sys/mpu/unix/mpu.c",
"embed/sys/rng/unix/rng.c",
"embed/sys/startup/unix/bootutils.c",
@@ -58,7 +59,6 @@ def unix_common_files(env, features_wanted, defines, sources, paths):
"embed/util/flash/unix/flash.c",
"embed/util/flash/unix/flash_otp.c",
"embed/util/fwutils/fwutils.c",
- "embed/util/unit_properties/unix/unit_properties.c",
]
if "usb" in features_wanted:
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.