feat(core): use tropic chip entropy in RNG
What changed, and why it matters
This commit refactors how Trezor firmware generates random numbers. It adds a new 'strong' randomness path that mixes the device's hardware RNG with entropy from external secure chips (Optiga and/or Tropic), and routes all randomness through a common internal API. It is a feature/cleanup change, not a disclosed security fix, but it touches critical cryptography code and changes how secure chips are used.
Treat as a high-risk feature commit requiring focused review: verify that XOR-mixing of entropy sources is safe even if one source fails silently or is weak, confirm that Tropic's `lt_port_random_bytes` callback cannot be invoked re-entrantly from `rng_fill_buffer_strong`, ensure that removal of `optiga_random_buffer` syscall does not break any remaining callers, and run RNG/statistical tests on both `rng_fill_buffer` and `rng_fill_buffer_strong` outputs.
Security signals we found
Refactoring of critical RNG code across bootloader, kernel, secure monitor, firmware, and Unix emulator builds
New external entropy source (Tropic chip) mixed into RNG output via XOR
Removal of direct Optiga random-buffer syscall in favor of generic RNG syscalls
Changes to storage key initialization and production-test secret generation
Potential dependency/callback between Tropic secure chip and hardware RNG
Evidence from the diff
The change introduces rng_fill_buffer() (hardware RNG only) and rng_fill_buffer_strong() (hardware RNG XORed with Optiga and/or Tropic chip randomness) in core/embed/sec/rng/. It removes the old per-module random_buffer/random32 wrappers and the SMCALL/SYSCALL_OPTIGA_RANDOM_BUFFER paths, replacing them with SMCALL/SYSCALL_RNG_FILL_BUFFER and _STRONG. The MicroPython trezorcrypto.random.bytes() binding now uses the new API, and storage.c initialization uses rng_fill_buffer_strong for cached keys. A potential concern is that rng_fill_buffer_strong XORs multiple entropy sources sequentially into the same buffer; if any source is weak or returns predictable data, the XOR still depends on the other sources being strong. The Tropic lt_port_random_bytes callback now calls rng_fill_buffer, which could create a dependency loop if rng_fill_buffer_strong later calls back into Tropic, but the current code uses rng_fill_buffer (non-strong) there, so no direct recursion is present.
Changed components
core/embed/sec/rngcore/embed/sec/tropiccore/embed/sys/smcallcore/embed/sys/syscallcore/embed/upymod/modtrezorcryptocore/embed/io/nrf/stm32u5core/embed/projects/prodteststorage/storage.clegacy/sec/rng.hInspect captured patch +384 / −154
diff --git a/core/SConscript.bootloader b/core/SConscript.bootloader
index effcf23e..7f22a36e 100644
--- a/core/SConscript.bootloader
+++ b/core/SConscript.bootloader
@@ -107,7 +107,6 @@ CPPPATH_MOD += [
]
SOURCE_MOD += [
- 'embed/upymod/modtrezorcrypto/rand.c',
'embed/gfx/bitblt/gfx_bitblt.c',
'embed/gfx/bitblt/gfx_bitblt_mono8.c',
'embed/gfx/bitblt/gfx_bitblt_rgb565.c',
diff --git a/core/SConscript.bootloader_ci b/core/SConscript.bootloader_ci
index d057e0ac..32d39dbd 100644
--- a/core/SConscript.bootloader_ci
+++ b/core/SConscript.bootloader_ci
@@ -87,7 +87,6 @@ CPPPATH_MOD += [
'vendor/micropython/lib/uzlib',
]
SOURCE_MOD += [
- 'embed/upymod/modtrezorcrypto/rand.c',
'embed/gfx/bitblt/gfx_bitblt.c',
'embed/gfx/bitblt/gfx_bitblt_mono8.c',
'embed/gfx/bitblt/gfx_bitblt_rgb565.c',
diff --git a/core/SConscript.bootloader_emu b/core/SConscript.bootloader_emu
index 6a33b6ce..ab4560d1 100644
--- a/core/SConscript.bootloader_emu
+++ b/core/SConscript.bootloader_emu
@@ -77,7 +77,6 @@ CPPPATH_MOD += [
]
SOURCE_MOD += [
- 'embed/upymod/modtrezorcrypto/rand.c',
'embed/gfx/bitblt/gfx_bitblt.c',
'embed/gfx/bitblt/gfx_bitblt_mono8.c',
'embed/gfx/bitblt/gfx_bitblt_rgb565.c',
diff --git a/core/SConscript.firmware b/core/SConscript.firmware
index 67a8d4e1..deb9bf0b 100644
--- a/core/SConscript.firmware
+++ b/core/SConscript.firmware
@@ -124,7 +124,6 @@ SOURCE_MOD += [
'embed/upymod/trezorobj.c',
'embed/upymod/modtrezorcrypto/crc.c',
'embed/upymod/modtrezorcrypto/modtrezorcrypto.c',
- 'embed/upymod/modtrezorcrypto/rand.c',
]
SOURCE_MOD_CRYPTO += [
'vendor/trezor-crypto/address.c',
diff --git a/core/SConscript.kernel b/core/SConscript.kernel
index 8761d328..cec1b269 100644
--- a/core/SConscript.kernel
+++ b/core/SConscript.kernel
@@ -215,7 +215,6 @@ SOURCE_MOD += [
'embed/util/rsod/rsod.c',
'embed/rtl/error_handling.c',
'embed/rtl/mini_printf.c',
- 'embed/upymod/modtrezorcrypto/rand.c',
'vendor/micropython/lib/uzlib/adler32.c',
'vendor/micropython/lib/uzlib/crc32.c',
'vendor/micropython/lib/uzlib/tinflate.c',
diff --git a/core/SConscript.secmon b/core/SConscript.secmon
index 50c067cf..45fa7aa3 100644
--- a/core/SConscript.secmon
+++ b/core/SConscript.secmon
@@ -210,7 +210,6 @@ SOURCE_MOD += [
'embed/util/image/image.c',
'embed/rtl/error_handling.c',
'embed/rtl/mini_printf.c',
- 'embed/upymod/modtrezorcrypto/rand.c',
'vendor/micropython/lib/uzlib/adler32.c',
'vendor/micropython/lib/uzlib/crc32.c',
'vendor/micropython/lib/uzlib/tinflate.c',
diff --git a/core/embed/io/nrf/stm32u5/nrf.c b/core/embed/io/nrf/stm32u5/nrf.c
index dec39608..05a5fb6e 100644
--- a/core/embed/io/nrf/stm32u5/nrf.c
+++ b/core/embed/io/nrf/stm32u5/nrf.c
@@ -567,9 +567,7 @@ bool nrf_authenticate(void) {
uint8_t data[1 + sizeof(challenge)] = {MGMT_CMD_AUTH_CHALLENGE};
// generate random challenge
- for (int i = 0; i < ARRAY_LENGTH(challenge); i++) {
- challenge[i] = rng_get();
- }
+ rng_fill_buffer(challenge, sizeof(challenge));
memcpy(data + 1, challenge, sizeof(challenge));
diff --git a/core/embed/projects/prodtest/cmd/prodtest_secrets.c b/core/embed/projects/prodtest/cmd/prodtest_secrets.c
index 0f33fc07..6cef290a 100644
--- a/core/embed/projects/prodtest/cmd/prodtest_secrets.c
+++ b/core/embed/projects/prodtest/cmd/prodtest_secrets.c
@@ -22,6 +22,7 @@
#include <string.h>
#include <rtl/cli.h>
+#include <sec/rng.h>
#include <sec/secret.h>
#include <sec/secret_keys.h>
@@ -48,32 +49,6 @@
#include <../vendor/mldsa-native/mldsa/sign.h>
-secbool generate_random_secret(uint8_t* secret, size_t length) {
- random_buffer(secret, length);
-
- uint8_t buffer[length];
-#ifdef USE_OPTIGA
- if (!optiga_random_buffer(buffer, length)) {
- return secfalse;
- }
- for (size_t i = 0; i < length; i++) {
- secret[i] ^= buffer[i];
- }
-#endif
-
-#ifdef USE_TROPIC
- if (LT_OK != lt_random_value_get(tropic_get_handle(), buffer, length)) {
- return secfalse;
- }
- for (size_t i = 0; i < length; i++) {
- secret[i] ^= buffer[i];
- }
-#endif
-
- memzero(buffer, sizeof(buffer));
- return sectrue;
-}
-
secbool set_random_secret(uint8_t slot, size_t length) {
uint8_t secret[length];
uint8_t secret_read[length];
@@ -87,7 +62,7 @@ secbool set_random_secret(uint8_t slot, size_t length) {
goto cleanup;
}
- if (generate_random_secret(secret, sizeof(secret)) != sectrue) {
+ if (!rng_fill_buffer_strong(secret, sizeof(secret))) {
goto cleanup;
}
diff --git a/core/embed/sec/rng/inc/sec/rng.h b/core/embed/sec/rng/inc/sec/rng.h
index 4b5dfb85..893ae813 100644
--- a/core/embed/sec/rng/inc/sec/rng.h
+++ b/core/embed/sec/rng/inc/sec/rng.h
@@ -17,19 +17,54 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef TREZORHAL_RNG_H
-#define TREZORHAL_RNG_H
+#pragma once
#include <trezor_types.h>
-#ifdef KERNEL_MODE
+#ifdef SECURE_MODE
+/**
+ * @brief Initializes the hardware random number generator.
+ *
+ */
void rng_init(void);
-uint32_t rng_read(const uint32_t previous, const uint32_t compare_previous);
-
-#endif // KERNEL_MODE
+#endif
+/**
+ * @brief Gets 32 bits of random data using from the hardware RNG.
+ *
+ * @return uint32_t Random data.
+ */
uint32_t rng_get(void);
-#endif
+/**
+ * @brief Fills a buffer with random bytes using the hardware RNG
+ *
+ * This function uses only single source of entropy - the hardware RNG
+ * available on the microcontroller. It is fast but less suitable for
+ * generating critical secrets.
+ *
+ * @param buffer Buffer to fill with random bytes.
+ * @param buffer_size Size of the buffer in bytes.
+ */
+void rng_fill_buffer(void* buffer, size_t buffer_size);
+
+/**
+ * @brief Fills a buffer with random bytes using the hardware RNG and
+ * combines it with other entropy sources (e.g., Optiga, Tropic) if
+ * available.
+ *
+ * This function is suitable for generating critical secrets since it
+ * combines multiple sources of entropy, but it is slower than
+ * `rng_fill_buffer()` since it may use external chips on I2C/SPI.
+ *
+ * The function requires that Optiga and/or Tropic to be initialized
+ * if they are enabled by USE_OPTIGA/USE_TROPIC.
+ *
+ * @param buffer Buffer to fill with random bytes.
+ * @param buffer_size Size of the buffer in bytes.
+ *
+ * @return True on success, false on failure.
+ */
+bool __wur rng_fill_buffer_strong(void* buffer, size_t buffer_size);
diff --git a/core/embed/sec/rng/rng_common.c b/core/embed/sec/rng/rng_common.c
new file mode 100644
index 00000000..dfec14fb
--- /dev/null
+++ b/core/embed/sec/rng/rng_common.c
@@ -0,0 +1,108 @@
+/*
+ * 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_rtl.h>
+
+#include <sec/rng.h>
+
+#if SECURE_MODE
+
+#ifdef USE_OPTIGA
+#include <sec/optiga.h>
+#endif
+
+#ifdef USE_TROPIC
+#include <sec/tropic.h>
+#endif
+
+#include "memzero.h"
+#include "rand.h"
+
+void rng_fill_buffer(void* buffer, size_t buffer_size) {
+ uint32_t* dst = (uint32_t*)buffer;
+ size_t remaining = buffer_size;
+
+ while (remaining >= sizeof(uint32_t)) {
+ *dst++ = rng_get();
+ remaining -= sizeof(uint32_t);
+ }
+
+ if (remaining > 0) {
+ uint32_t r = rng_get();
+ memcpy(dst, &r, remaining);
+ }
+}
+
+bool rng_fill_buffer_strong(void* buffer, size_t buffer_size) {
+ rng_fill_buffer(buffer, buffer_size);
+
+ uint8_t* dst = (uint8_t*)buffer;
+ size_t remaining = buffer_size;
+
+ uint8_t block[32];
+
+ while (remaining > 0) {
+ size_t block_size = MIN(remaining, sizeof(block));
+#ifdef USE_OPTIGA
+ if (!optiga_random_buffer(block, block_size)) {
+ return false;
+ }
+
+ for (size_t i = 0; i < block_size; i++) {
+ dst[i] ^= block[i];
+ }
+#endif
+#ifdef USE_TROPIC
+ if (!tropic_random_buffer(block, block_size)) {
+ return false;
+ }
+
+ for (size_t i = 0; i < block_size; i++) {
+ dst[i] ^= block[i];
+ }
+#endif
+ dst += block_size;
+ remaining -= block_size;
+ }
+
+ memzero(block, sizeof(block));
+ return true;
+}
+
+#endif // SECURE_MODE
+
+#ifndef SECURE_MODE
+uint32_t rng_get(void) {
+ uint32_t temp = 0;
+ // Note: In non-secure mode we use rng_fill_buffer() since rng_get() is not
+ // available as a smcall/syscall.
+ rng_fill_buffer(&temp, sizeof(temp));
+ return temp;
+}
+#endif // !SECURE_MODE
+
+#ifndef USE_INSECURE_PRNG
+// Re-implementation of random32() function declared in crypto/rand.h
+// to use MCU TRNG instead of crypto library PRNG.
+uint32_t random32(void) { return rng_get(); }
+#endif
+
+// Re-implementation of weak random_buffer() function defined in crypto/rand.c
+// to be the same as rng_fill_buffer() function.
+void random_buffer(uint8_t* buf, size_t len) { rng_fill_buffer(buf, len); }
diff --git a/core/embed/sec/rng/stm32/rng.c b/core/embed/sec/rng/stm32/rng.c
index d1d0f855..b53acb4d 100644
--- a/core/embed/sec/rng/stm32/rng.c
+++ b/core/embed/sec/rng/stm32/rng.c
@@ -17,15 +17,17 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#pragma GCC optimize( \
+ "no-stack-protector") // applies to all functions in this file
+
#include <trezor_bsp.h>
#include <trezor_rtl.h>
#include <sec/rng.h>
-#if SECURE_MODE
+#include "rand.h"
-#pragma GCC optimize( \
- "no-stack-protector") // applies to all functions in this file
+#if SECURE_MODE
void rng_init(void) {
// enable TRNG peripheral clock
@@ -35,7 +37,17 @@ void rng_init(void) {
RNG->CR = RNG_CR_RNGEN; // enable TRNG
}
-uint32_t rng_read(const uint32_t previous, const uint32_t compare_previous) {
+#ifdef USE_INSECURE_PRNG
+
+uint32_t rng_get(void) {
+ // Uses PRNG implemented in crypto/rand.c
+ return random32();
+}
+
+#else
+
+static uint32_t rng_read(const uint32_t previous,
+ const uint32_t compare_previous) {
uint32_t temp = previous;
do {
while ((RNG->SR & (RNG_SR_SECS | RNG_SR_CECS | RNG_SR_DRDY)) != RNG_SR_DRDY)
@@ -60,4 +72,6 @@ uint32_t rng_get(void) {
return current;
}
+#endif // USE_INSECURE_PRNG
+
#endif // SECURE_MODE
diff --git a/core/embed/sec/rng/unix/rng.c b/core/embed/sec/rng/unix/rng.c
index c204a475..18876dfb 100644
--- a/core/embed/sec/rng/unix/rng.c
+++ b/core/embed/sec/rng/unix/rng.c
@@ -21,6 +21,17 @@
#include <sec/rng.h>
+#include "rand.h"
+
+#ifdef USE_INSECURE_PRNG
+
+uint32_t rng_get(void) {
+ // Uses PRNG implemented in crypto/rand.c
+ return random32();
+}
+
+#else
+
uint32_t rng_get(void) {
static FILE *frand = NULL;
if (!frand) {
@@ -32,3 +43,5 @@ uint32_t rng_get(void) {
"fread failed");
return r;
}
+
+#endif // USE_INSECURE_PRNG
diff --git a/core/embed/sec/tropic/inc/sec/tropic.h b/core/embed/sec/tropic/inc/sec/tropic.h
index 8587f4ad..ea92fef1 100644
--- a/core/embed/sec/tropic/inc/sec/tropic.h
+++ b/core/embed/sec/tropic/inc/sec/tropic.h
@@ -69,3 +69,5 @@ bool tropic_data_multi_size(uint16_t first_slot, size_t* data_length);
bool tropic_data_multi_read(uint16_t first_slot, uint16_t slot_count,
uint8_t* data, size_t max_data_length,
size_t* data_length);
+
+bool tropic_random_buffer(void* buffer, size_t length);
diff --git a/core/embed/sec/tropic/stm32/tropic01.c b/core/embed/sec/tropic/stm32/tropic01.c
index d48c1c8c..cb675cbf 100644
--- a/core/embed/sec/tropic/stm32/tropic01.c
+++ b/core/embed/sec/tropic/stm32/tropic01.c
@@ -27,8 +27,6 @@
#include <sec/tropic.h>
#include <sys/systick.h>
-#include "rand.h"
-
typedef struct {
bool initialized;
SPI_HandleTypeDef spi;
@@ -187,7 +185,8 @@ lt_ret_t lt_port_delay(lt_handle_t *h, uint32_t ms) {
lt_ret_t lt_port_random_bytes(lt_l2_state_t *s2, void *buff, size_t count) {
(void)s2;
- random_buffer((uint8_t *)buff, count);
+
+ rng_fill_buffer((uint8_t *)buff, count);
return LT_OK;
}
diff --git a/core/embed/sec/tropic/tropic.c b/core/embed/sec/tropic/tropic.c
index 3d22fa26..09c5316a 100644
--- a/core/embed/sec/tropic/tropic.c
+++ b/core/embed/sec/tropic/tropic.c
@@ -236,6 +236,20 @@ static bool tropic_get_tropic_pubkey(lt_handle_t *handle,
}
#endif // !PRODUCTION
+bool tropic_random_buffer(void *buffer, size_t length) {
+ tropic_driver_t *drv = &g_tropic_driver;
+
+ if (!drv->initialized) {
+ return false;
+ }
+
+ if (LT_OK != lt_random_value_get(&drv->handle, buffer, length)) {
+ return false;
+ }
+
+ return true;
+}
+
#endif // SECURE_MODE
bool tropic_data_multi_size(uint16_t first_slot, size_t *data_length) {
diff --git a/core/embed/sys/smcall/stm32/smcall_dispatch.c b/core/embed/sys/smcall/stm32/smcall_dispatch.c
index f48c0ec2..60a79561 100644
--- a/core/embed/sys/smcall/stm32/smcall_dispatch.c
+++ b/core/embed/sys/smcall/stm32/smcall_dispatch.c
@@ -183,12 +183,6 @@ __attribute((no_stack_protector)) void smcall_handler(uint32_t *args,
args[0] = optiga_read_sec__verified(sec);
} break;
- case SMCALL_OPTIGA_RANDOM_BUFFER: {
- uint8_t *dest = (uint8_t *)args[0];
- size_t size = args[1];
- args[0] = optiga_random_buffer__verified(dest, size);
- } break;
-
#if PYOPT == 0
case SMCALL_OPTIGA_SET_SEC_MAX: {
optiga_set_sec_max();
@@ -300,8 +294,16 @@ __attribute((no_stack_protector)) void smcall_handler(uint32_t *args,
args[0] = storage_next_counter__verified(key, count);
} break;
- case SMCALL_RNG_GET: {
- args[0] = rng_get();
+ case SMCALL_RNG_FILL_BUFFER: {
+ uint8_t *buffer = (uint8_t *)args[0];
+ size_t buffer_size = args[1];
+ rng_fill_buffer__verified(buffer, buffer_size);
+ } break;
+
+ case SMCALL_RNG_FILL_BUFFER_STRONG: {
+ uint8_t *buffer = (uint8_t *)args[0];
+ size_t buffer_size = args[1];
+ args[0] = rng_fill_buffer_strong__verified(buffer, buffer_size);
} break;
case SMCALL_FIRMWARE_GET_VENDOR: {
diff --git a/core/embed/sys/smcall/stm32/smcall_numbers.h b/core/embed/sys/smcall/stm32/smcall_numbers.h
index 0efb2f3c..d42e7a0e 100644
--- a/core/embed/sys/smcall/stm32/smcall_numbers.h
+++ b/core/embed/sys/smcall/stm32/smcall_numbers.h
@@ -61,7 +61,6 @@ typedef enum {
SMCALL_OPTIGA_CERT_SIZE,
SMCALL_OPTIGA_READ_CERT,
SMCALL_OPTIGA_READ_SEC,
- SMCALL_OPTIGA_RANDOM_BUFFER,
SMCALL_OPTIGA_SET_SEC_MAX,
SMCALL_STORAGE_SETUP,
@@ -83,7 +82,8 @@ typedef enum {
SMCALL_STORAGE_SET_COUNTER,
SMCALL_STORAGE_NEXT_COUNTER,
- SMCALL_RNG_GET,
+ SMCALL_RNG_FILL_BUFFER,
+ SMCALL_RNG_FILL_BUFFER_STRONG,
SMCALL_FIRMWARE_GET_VENDOR,
SMCALL_FIRMWARE_HASH_START,
diff --git a/core/embed/sys/smcall/stm32/smcall_stubs.c b/core/embed/sys/smcall/stm32/smcall_stubs.c
index 47264157..1c280e4b 100644
--- a/core/embed/sys/smcall/stm32/smcall_stubs.c
+++ b/core/embed/sys/smcall/stm32/smcall_stubs.c
@@ -188,11 +188,6 @@ bool optiga_read_sec(uint8_t *sec) {
return (bool)smcall_invoke1((uint32_t)sec, SMCALL_OPTIGA_READ_SEC);
}
-bool optiga_random_buffer(uint8_t *dest, size_t size) {
- return (bool)smcall_invoke2((uint32_t)dest, size,
- SMCALL_OPTIGA_RANDOM_BUFFER);
-}
-
#if PYOPT == 0
void optiga_set_sec_max(void) { smcall_invoke0(SMCALL_OPTIGA_SET_SEC_MAX); }
@@ -294,7 +289,14 @@ secbool storage_next_counter(const uint16_t key, uint32_t *count) {
#include <sec/rng.h>
-uint32_t rng_get(void) { return smcall_invoke0(SMCALL_RNG_GET); }
+void rng_fill_buffer(void *buffer, size_t buffer_size) {
+ smcall_invoke2((uint32_t)buffer, buffer_size, SMCALL_RNG_FILL_BUFFER);
+}
+
+bool rng_fill_buffer_strong(void *buffer, size_t buffer_size) {
+ return (bool)smcall_invoke2((uint32_t)buffer, buffer_size,
+ SMCALL_RNG_FILL_BUFFER_STRONG);
+}
// =============================================================================
// fwutils.h
diff --git a/core/embed/sys/smcall/stm32/smcall_verifiers.c b/core/embed/sys/smcall/stm32/smcall_verifiers.c
index d4dbcdae..ba7329c3 100644
--- a/core/embed/sys/smcall/stm32/smcall_verifiers.c
+++ b/core/embed/sys/smcall/stm32/smcall_verifiers.c
@@ -195,18 +195,6 @@ access_violation:
return false;
}
-bool __wur optiga_random_buffer__verified(uint8_t *dest, size_t size) {
- if (!probe_write_access(dest, size)) {
- goto access_violation;
- }
-
- return optiga_random_buffer(dest, size);
-
-access_violation:
- apptask_access_violation();
- return false;
-}
-
#endif // USE_OPTIGA
// ---------------------------------------------------------------------
@@ -365,6 +353,34 @@ access_violation:
// ---------------------------------------------------------------------
+#include <sec/rng.h>
+
+void rng_fill_buffer__verified(void *buffer, size_t buffer_size) {
+ if (!probe_write_access(buffer, buffer_size)) {
+ goto access_violation;
+ }
+
+ rng_fill_buffer(buffer, buffer_size);
+ return;
+
+access_violation:
+ apptask_access_violation();
+}
+
+bool rng_fill_buffer_strong__verified(void *buffer, size_t buffer_size) {
+ if (!probe_write_access(buffer, buffer_size)) {
+ goto access_violation;
+ }
+
+ return rng_fill_buffer_strong(buffer, buffer_size);
+
+access_violation:
+ apptask_access_violation();
+ return false;
+}
+
+// ---------------------------------------------------------------------
+
int firmware_hash_start__verified(const uint8_t *challenge,
size_t challenge_len) {
if (!probe_read_access(challenge, challenge_len)) {
diff --git a/core/embed/sys/smcall/stm32/smcall_verifiers.h b/core/embed/sys/smcall/stm32/smcall_verifiers.h
index 432a9404..c9184139 100644
--- a/core/embed/sys/smcall/stm32/smcall_verifiers.h
+++ b/core/embed/sys/smcall/stm32/smcall_verifiers.h
@@ -65,8 +65,6 @@ bool __wur optiga_read_cert__verified(uint8_t index, uint8_t *cert,
bool __wur optiga_read_sec__verified(uint8_t *sec);
-bool __wur optiga_random_buffer__verified(uint8_t *dest, size_t size);
-
#endif // USE_OPTIGA
// ---------------------------------------------------------------------
@@ -97,6 +95,13 @@ secbool storage_set__verified(const uint16_t key, const void *val,
secbool storage_next_counter__verified(const uint16_t key, uint32_t *count);
+// ---------------------------------------------------------------------
+#include <sec/rng.h>
+
+void rng_fill_buffer__verified(void *buffer, size_t buffer_size);
+
+bool rng_fill_buffer_strong__verified(void *buffer, size_t buffer_size);
+
// ---------------------------------------------------------------------
#include <util/fwutils.h>
diff --git a/core/embed/sys/syscall/inc/sys/syscall_numbers.h b/core/embed/sys/syscall/inc/sys/syscall_numbers.h
index 7a483d19..1d5e7509 100644
--- a/core/embed/sys/syscall/inc/sys/syscall_numbers.h
+++ b/core/embed/sys/syscall/inc/sys/syscall_numbers.h
@@ -104,7 +104,6 @@ typedef enum {
SYSCALL_OPTIGA_CERT_SIZE,
SYSCALL_OPTIGA_READ_CERT,
SYSCALL_OPTIGA_READ_SEC,
- SYSCALL_OPTIGA_RANDOM_BUFFER,
SYSCALL_OPTIGA_SET_SEC_MAX,
SYSCALL_TRANSLATIONS_WRITE,
@@ -112,7 +111,8 @@ typedef enum {
SYSCALL_TRANSLATIONS_ERASE,
SYSCALL_TRANSLATIONS_AREA_BYTESIZE,
- SYSCALL_RNG_GET,
+ SYSCALL_RNG_FILL_BUFFER,
+ SYSCALL_RNG_FILL_BUFFER_STRONG,
SYSCALL_FIRMWARE_GET_VENDOR,
SYSCALL_FIRMWARE_HASH_START,
diff --git a/core/embed/sys/syscall/stm32/syscall_dispatch.c b/core/embed/sys/syscall/stm32/syscall_dispatch.c
index 34949c00..e033a71b 100644
--- a/core/embed/sys/syscall/stm32/syscall_dispatch.c
+++ b/core/embed/sys/syscall/stm32/syscall_dispatch.c
@@ -423,12 +423,6 @@ __attribute((no_stack_protector)) void syscall_handler(uint32_t *args,
args[0] = optiga_read_sec__verified(sec);
} break;
- case SYSCALL_OPTIGA_RANDOM_BUFFER: {
- uint8_t *dest = (uint8_t *)args[0];
- size_t size = args[1];
- args[0] = optiga_random_buffer__verified(dest, size);
- } break;
-
#if PYOPT == 0
case SYSCALL_OPTIGA_SET_SEC_MAX: {
optiga_set_sec_max();
@@ -561,8 +555,16 @@ __attribute((no_stack_protector)) void syscall_handler(uint32_t *args,
args[0] = translations_area_bytesize();
} break;
- case SYSCALL_RNG_GET: {
- args[0] = rng_get();
+ case SYSCALL_RNG_FILL_BUFFER: {
+ void *buffer = (void *)args[0];
+ size_t buffer_size = (size_t)args[1];
+ rng_fill_buffer__verified(buffer, buffer_size);
+ } break;
+
+ case SYSCALL_RNG_FILL_BUFFER_STRONG: {
+ void *buffer = (void *)args[0];
+ size_t buffer_size = (size_t)args[1];
+ args[0] = rng_fill_buffer_strong__verified(buffer, buffer_size);
} break;
case SYSCALL_FIRMWARE_GET_VENDOR: {
diff --git a/core/embed/sys/syscall/stm32/syscall_stubs.c b/core/embed/sys/syscall/stm32/syscall_stubs.c
index 1a463ca0..ba378b37 100644
--- a/core/embed/sys/syscall/stm32/syscall_stubs.c
+++ b/core/embed/sys/syscall/stm32/syscall_stubs.c
@@ -413,11 +413,6 @@ bool optiga_read_sec(uint8_t *sec) {
return (bool)syscall_invoke1((uint32_t)sec, SYSCALL_OPTIGA_READ_SEC);
}
-bool optiga_random_buffer(uint8_t *dest, size_t size) {
- return (bool)syscall_invoke2((uint32_t)dest, size,
- SYSCALL_OPTIGA_RANDOM_BUFFER);
-}
-
#if PYOPT == 0
void optiga_set_sec_max(void) { syscall_invoke0(SYSCALL_OPTIGA_SET_SEC_MAX); }
@@ -551,7 +546,14 @@ uint32_t translations_area_bytesize(void) {
#include <sec/rng.h>
-uint32_t rng_get(void) { return syscall_invoke0(SYSCALL_RNG_GET); }
+void rng_fill_buffer(void *buffer, size_t buffer_size) {
+ syscall_invoke2((uint32_t)buffer, buffer_size, SYSCALL_RNG_FILL_BUFFER);
+}
+
+bool rng_fill_buffer_strong(void *buffer, size_t buffer_size) {
+ return (bool)syscall_invoke2((uint32_t)buffer, buffer_size,
+ SYSCALL_RNG_FILL_BUFFER_STRONG);
+}
// =============================================================================
// fwutils.h
diff --git a/core/embed/sys/syscall/stm32/syscall_verifiers.c b/core/embed/sys/syscall/stm32/syscall_verifiers.c
index 971721fa..53e82cb6 100644
--- a/core/embed/sys/syscall/stm32/syscall_verifiers.c
+++ b/core/embed/sys/syscall/stm32/syscall_verifiers.c
@@ -457,18 +457,6 @@ access_violation:
return false;
}
-bool __wur optiga_random_buffer__verified(uint8_t *dest, size_t size) {
- if (!probe_write_access(dest, size)) {
- goto access_violation;
- }
-
- return optiga_random_buffer(dest, size);
-
-access_violation:
- apptask_access_violation();
- return false;
-}
-
#endif // USE_OPTIGA
// ---------------------------------------------------------------------
@@ -624,6 +612,32 @@ access_violation:
// ---------------------------------------------------------------------
+void rng_fill_buffer__verified(void *buffer, size_t buffer_size) {
+ if (!probe_write_access(buffer, buffer_size)) {
+ goto access_violation;
+ }
+
+ rng_fill_buffer(buffer, buffer_size);
+ return;
+
+access_violation:
+ apptask_access_violation();
+}
+
+bool rng_fill_buffer_strong__verified(void *buffer, size_t buffer_size) {
+ if (!probe_write_access(buffer, buffer_size)) {
+ goto access_violation;
+ }
+
+ return rng_fill_buffer_strong(buffer, buffer_size);
+
+access_violation:
+ apptask_access_violation();
+ return false;
+}
+
+// ---------------------------------------------------------------------
+
bool translations_write__verified(const uint8_t *data, uint32_t offset,
uint32_t len) {
if (!probe_read_access(data, len)) {
diff --git a/core/embed/sys/syscall/stm32/syscall_verifiers.h b/core/embed/sys/syscall/stm32/syscall_verifiers.h
index 7e67dc99..36a691ba 100644
--- a/core/embed/sys/syscall/stm32/syscall_verifiers.h
+++ b/core/embed/sys/syscall/stm32/syscall_verifiers.h
@@ -123,8 +123,6 @@ bool __wur optiga_read_cert__verified(uint8_t index, uint8_t *cert,
bool __wur optiga_read_sec__verified(uint8_t *sec);
-bool __wur optiga_random_buffer__verified(uint8_t *dest, size_t size);
-
#endif // USE_OPTIGA
// ---------------------------------------------------------------------
@@ -155,6 +153,13 @@ secbool storage_set__verified(const uint16_t key, const void *val,
secbool storage_next_counter__verified(const uint16_t key, uint32_t *count);
+// ---------------------------------------------------------------------
+#include <sec/rng.h>
+
+void rng_fill_buffer__verified(void *buffer, size_t buffer_size);
+
+bool rng_fill_buffer_strong__verified(void *buffer, size_t buffer_size);
+
// ---------------------------------------------------------------------
#include <util/translations.h>
diff --git a/core/embed/upymod/modtrezorcrypto/modtrezorcrypto-random.h b/core/embed/upymod/modtrezorcrypto/modtrezorcrypto-random.h
index 1c42b60c..4f3d1e6e 100644
--- a/core/embed/upymod/modtrezorcrypto/modtrezorcrypto-random.h
+++ b/core/embed/upymod/modtrezorcrypto/modtrezorcrypto-random.h
@@ -21,12 +21,9 @@
#include "embed/upymod/trezorobj.h"
+#include <sec/rng.h>
#include "rand.h"
-#if USE_OPTIGA
-#include <sec/optiga.h>
-#endif
-
/// package: trezorcrypto.random
/// def uniform(n: int) -> int:
@@ -57,19 +54,14 @@ STATIC mp_obj_t mod_trezorcrypto_random_bytes(size_t n_args,
}
vstr_t vstr = {0};
vstr_init_len(&vstr, len);
-#if USE_OPTIGA
if (n_args > 1 && mp_obj_is_true(args[1])) {
- if (!optiga_random_buffer((uint8_t *)vstr.buf, len)) {
+ if (!rng_fill_buffer_strong((uint8_t *)vstr.buf, len)) {
vstr_clear(&vstr);
mp_raise_msg(&mp_type_RuntimeError,
- MP_ERROR_TEXT("Failed to get randomness from Optiga."));
+ MP_ERROR_TEXT("Failed to get strong randomness."));
}
-
- random_xor((uint8_t *)vstr.buf, len);
- } else
-#endif
- {
- random_buffer((uint8_t *)vstr.buf, len);
+ } else {
+ rng_fill_buffer((uint8_t *)vstr.buf, len);
}
return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
}
diff --git a/core/embed/upymod/modtrezorcrypto/rand.c b/core/embed/upymod/modtrezorcrypto/rand.c
deleted file mode 100644
index adc44c74..00000000
--- a/core/embed/upymod/modtrezorcrypto/rand.c
+++ /dev/null
@@ -1,23 +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 "rand.h"
-#include <sec/rng.h>
-
-uint32_t random32(void) { return rng_get(); }
diff --git a/core/site_scons/models/stm32f4_common.py b/core/site_scons/models/stm32f4_common.py
index 675ee66e..3c614624 100644
--- a/core/site_scons/models/stm32f4_common.py
+++ b/core/site_scons/models/stm32f4_common.py
@@ -68,6 +68,7 @@ def stm32f4_common_files(env, features_wanted, defines, sources, paths):
"embed/sec/monoctr/stm32f4/monoctr.c",
"embed/sec/random_delays/stm32/random_delays.c",
"embed/sec/rng/stm32/rng.c",
+ "embed/sec/rng/rng_common.c",
"embed/sec/secret/stm32f4/secret.c",
"embed/sec/secret/stm32f4/secret_keys.c",
"embed/sec/storage/stm32f4/storage_salt.c",
diff --git a/core/site_scons/models/stm32u5_common.py b/core/site_scons/models/stm32u5_common.py
index c34b1c5b..485d826b 100644
--- a/core/site_scons/models/stm32u5_common.py
+++ b/core/site_scons/models/stm32u5_common.py
@@ -87,6 +87,7 @@ def stm32u5_common_files(env, features_wanted, defines, sources, paths):
"embed/sec/monoctr/stm32u5/monoctr.c",
"embed/sec/random_delays/stm32/random_delays.c",
"embed/sec/rng/stm32/rng.c",
+ "embed/sec/rng/rng_common.c",
"embed/sec/secret/stm32u5/secret.c",
"embed/sec/secret/stm32u5/secret_keys.c",
"embed/sec/secure_aes/stm32u5/secure_aes.c",
diff --git a/core/site_scons/models/unix_common.py b/core/site_scons/models/unix_common.py
index 707843d6..8efd7005 100644
--- a/core/site_scons/models/unix_common.py
+++ b/core/site_scons/models/unix_common.py
@@ -40,6 +40,7 @@ def unix_common_files(env, features_wanted, defines, sources, paths):
"embed/sec/storage/unix/storage_salt.c",
"embed/sec/monoctr/unix/monoctr.c",
"embed/sec/rng/unix/rng.c",
+ "embed/sec/rng/rng_common.c",
"embed/sec/time_estimate/unix/time_estimate.c",
"embed/sys/dbg/dbg_console.c",
"embed/sys/dbg/unix/dbg_console_backend.c",
diff --git a/legacy/sec/rng.h b/legacy/sec/rng.h
new file mode 100644
index 00000000..3485f640
--- /dev/null
+++ b/legacy/sec/rng.h
@@ -0,0 +1,31 @@
+/*
+ * 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 <stdbool.h>
+
+#include "rand.h"
+
+// Minimal implementation of sec/rng.h from core/embed
+
+static inline bool rng_fill_buffer_strong(void* buffer, size_t buffer_size) {
+ random_buffer((uint8_t*)buffer, buffer_size);
+ return true;
+}
diff --git a/storage/storage.c b/storage/storage.c
index b9e14a86..a7c1a129 100644
--- a/storage/storage.c
+++ b/storage/storage.c
@@ -20,6 +20,7 @@
#include <assert.h>
#include <string.h>
+#include <sec/rng.h>
#include <sys/mpu.h>
#include "chacha20poly1305/rfc7539.h"
@@ -779,14 +780,9 @@ static void init_wiped_storage(void) {
return;
}
-#if USE_OPTIGA
- ensure(optiga_random_buffer(cached_keys, sizeof(cached_keys)) ? sectrue
- : secfalse,
- "optiga_random_buffer failed");
- random_xor(cached_keys, sizeof(cached_keys));
-#else
- random_buffer(cached_keys, sizeof(cached_keys));
-#endif
+ ensure(rng_fill_buffer_strong(cached_keys, sizeof(cached_keys)) ? sectrue
+ : secfalse,
+ "rng_fill_buffer_strong failed");
unlocked = sectrue;
uint32_t version = NORCOW_VERSION;
ensure(auth_init(), "set_storage_auth_tag failed");
diff --git a/storage/tests/c/sec/rng.h b/storage/tests/c/sec/rng.h
new file mode 100644
index 00000000..3e4e3e63
--- /dev/null
+++ b/storage/tests/c/sec/rng.h
@@ -0,0 +1,31 @@
+/*
+ * 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 <stdbool.h>
+
+#include "rand.h"
+
+// Minimal implementation of rng.h for the storage tests
+
+static inline bool rng_fill_buffer_strong(void* buffer, size_t buffer_size) {
+ random_buffer((uint8_t*)buffer, buffer_size);
+ return true;
+}
Why this scored 34/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.