feat(core/ble): expose nRF authenticate to coreapp
What changed, and why it matters
This commit exposes an existing Bluetooth security pairing/authentication function so it can be called by the main Trezor application (coreapp). It does not appear to fix a vulnerability; rather, it is a feature that lets the coreapp request authentication with the nRF Bluetooth chip. The change removes a compile-time restriction that previously limited the function to secure mode, adds a new system call, and adds a secure-monitor call for validating a pairing secret. Without additional context, this looks like a normal feature enabling secure Bluetooth pairing, not a security patch.
Treat as a feature commit, not a security fix. Review the new syscall and secure-monitor call for correct privilege separation, ensure the __verified wrapper always validates buffer bounds and permissions, and verify that nrf_authenticate() and secret_validate_nrf_pairing() cannot be invoked in a way that bypasses pairing policy or leaks the pairing secret. No immediate patching action is indicated by the diff alone.
Security signals we found
Removal of SECURE_MODE compile-time guard around nrf_authenticate()
Addition of SYSCALL_NRF_AUTHENTICATE syscall number and dispatch
Addition of SMCALL_SECRET_VALIDATE_NRF_PAIRING secure-monitor call
Memory-access probing wrapper (probe_read_access) for message and MAC buffers
secret_validate_nrf_pairing moved from SECURE_MODE to KERNEL_MODE
Evidence from the diff
The commit ‘feat(core/ble): expose nRF authenticate to coreapp’ makes nrf_authenticate() available outside SECURE_MODE by removing the #ifdef SECURE_MODE guard in core/embed/io/nrf/stm32u5/nrf.c. It adds SYSCALL_NRF_AUTHENTICATE to the syscall table and dispatches it to nrf_authenticate(). It also moves secret_validate_nrf_pairing() from secure-only to kernel mode, exposes it via a new SMCALL_SECRET_VALIDATE_NRF_PAIRING secure-monitor call, and adds a __verified wrapper that probes caller memory read access before invoking the secret validation. The change is architectural: it wires up an existing secure-world capability so the coreapp can trigger nRF authentication and validate pairing secrets.
Changed components
core/embed/io/nrf/stm32u5/nrf.ccore/embed/sec/secret/inc/sec/secret_keys.hcore/embed/sys/smcall/stm32/smcall_dispatch.ccore/embed/sys/smcall/stm32/smcall_numbers.hcore/embed/sys/smcall/stm32/smcall_stubs.ccore/embed/sys/smcall/stm32/smcall_verifiers.ccore/embed/sys/smcall/stm32/smcall_verifiers.hcore/embed/sys/syscall/inc/sys/syscall_numbers.hcore/embed/sys/syscall/stm32/syscall_dispatch.ccore/embed/sys/syscall/stm32/syscall_stubs.cInspect captured patch +70 / −9
diff --git a/core/embed/io/nrf/stm32u5/nrf.c b/core/embed/io/nrf/stm32u5/nrf.c
index 60fb2132c..bcb368d66 100644
--- a/core/embed/io/nrf/stm32u5/nrf.c
+++ b/core/embed/io/nrf/stm32u5/nrf.c
@@ -545,7 +545,6 @@ bool nrf_system_off(void) {
return true;
}
-#ifdef SECURE_MODE
bool nrf_authenticate(void) {
nrf_driver_t *drv = &g_nrf_driver;
if (!drv->initialized) {
@@ -586,6 +585,5 @@ bool nrf_authenticate(void) {
return false;
}
-#endif
#endif
diff --git a/core/embed/sec/secret/inc/sec/secret_keys.h b/core/embed/sec/secret/inc/sec/secret_keys.h
index 99c51a646..dafed935f 100644
--- a/core/embed/sec/secret/inc/sec/secret_keys.h
+++ b/core/embed/sec/secret/inc/sec/secret_keys.h
@@ -61,9 +61,6 @@ secbool secret_key_tropic_masking(uint8_t dest[ECDSA_PRIVATE_KEY_SIZE]);
#define NRF_PAIRING_SECRET_SIZE 32
secbool secret_key_nrf_pairing(uint8_t dest[NRF_PAIRING_SECRET_SIZE]);
-secbool secret_validate_nrf_pairing(const uint8_t *message, size_t msg_len,
- const uint8_t *mac, size_t mac_len);
-
#endif
#define SECRET_KEY_STORAGE_SALT_SIZE 32
@@ -72,3 +69,11 @@ secbool secret_key_storage_salt(uint16_t fw_type,
uint8_t dest[SECRET_KEY_STORAGE_SALT_SIZE]);
#endif // SECURE_MODE
+
+#ifdef KERNEL_MODE
+#ifdef USE_NRF
+secbool secret_validate_nrf_pairing(const uint8_t *message, size_t msg_len,
+ const uint8_t *mac, size_t mac_len);
+
+#endif
+#endif
diff --git a/core/embed/sys/smcall/stm32/smcall_dispatch.c b/core/embed/sys/smcall/stm32/smcall_dispatch.c
index 740d087a3..ae89b85ec 100644
--- a/core/embed/sys/smcall/stm32/smcall_dispatch.c
+++ b/core/embed/sys/smcall/stm32/smcall_dispatch.c
@@ -23,6 +23,7 @@
#include <sec/random_delays.h>
#include <sec/rng.h>
+#include <sec/secret.h>
#include <sys/bootargs.h>
#include <sys/bootutils.h>
#include <sys/irq.h>
@@ -43,10 +44,6 @@
#include <sys/suspend_io.h>
#endif
-#ifdef LOCKABLE_BOOTLOADER
-#include <sec/secret.h>
-#endif
-
#include <util/boot_image.h>
#include "smcall_numbers.h"
@@ -135,6 +132,18 @@ __attribute((no_stack_protector)) void smcall_handler(uint32_t *args,
} break;
#endif
+#ifdef USE_NRF
+ case SMCALL_SECRET_VALIDATE_NRF_PAIRING: {
+ const uint8_t *message = (const uint8_t *)args[0];
+ size_t message_len = args[1];
+ const uint8_t *mac = (const uint8_t *)args[2];
+ size_t mac_len = args[3];
+ args[0] = secret_validate_nrf_pairing__verified(message, message_len, mac,
+ mac_len);
+ } break;
+
+#endif // USE_NRF
+
case SMCALL_WAIT_RANDOM: {
wait_random();
} break;
diff --git a/core/embed/sys/smcall/stm32/smcall_numbers.h b/core/embed/sys/smcall/stm32/smcall_numbers.h
index aa167403d..077d9e375 100644
--- a/core/embed/sys/smcall/stm32/smcall_numbers.h
+++ b/core/embed/sys/smcall/stm32/smcall_numbers.h
@@ -52,6 +52,7 @@ typedef enum {
SMCALL_UNIT_PROPERTIES_GET,
SMCALL_SECRET_BOOTLOADER_LOCKED,
+ SMCALL_SECRET_VALIDATE_NRF_PAIRING,
SMCALL_WAIT_RANDOM,
SMCALL_RANDOM_DELAYS_REFRESH_RDI,
diff --git a/core/embed/sys/smcall/stm32/smcall_stubs.c b/core/embed/sys/smcall/stm32/smcall_stubs.c
index 28bdc1674..fc5d1c999 100644
--- a/core/embed/sys/smcall/stm32/smcall_stubs.c
+++ b/core/embed/sys/smcall/stm32/smcall_stubs.c
@@ -362,4 +362,16 @@ bool backup_ram_write(uint16_t key, backup_ram_item_type_t type,
#endif // USE_BACKUP_RAM
+#ifdef USE_NRF
+
+#include <sec/secret.h>
+
+secbool secret_validate_nrf_pairing(const uint8_t *message, size_t msg_len,
+ const uint8_t *mac, size_t mac_len) {
+ return (secbool)smcall_invoke4((uint32_t)message, msg_len, (uint32_t)mac,
+ mac_len, SMCALL_SECRET_VALIDATE_NRF_PAIRING);
+}
+
+#endif
+
#endif // defined(KERNEL) && defined(USE_SECMON_LAYOUT)
diff --git a/core/embed/sys/smcall/stm32/smcall_verifiers.c b/core/embed/sys/smcall/stm32/smcall_verifiers.c
index ff1fc96de..ecd4f6b62 100644
--- a/core/embed/sys/smcall/stm32/smcall_verifiers.c
+++ b/core/embed/sys/smcall/stm32/smcall_verifiers.c
@@ -477,4 +477,25 @@ access_violation:
#endif // USE_BACKUP_RAM
+#ifdef USE_NRF
+secbool secret_validate_nrf_pairing__verified(const uint8_t *message,
+ size_t msg_len,
+ const uint8_t *mac,
+ size_t mac_len) {
+ if (!probe_read_access(message, msg_len)) {
+ goto access_violation;
+ }
+ if (!probe_read_access(mac, mac_len)) {
+ goto access_violation;
+ }
+
+ return secret_validate_nrf_pairing(message, msg_len, mac, mac_len);
+
+access_violation:
+ apptask_access_violation();
+ return false;
+}
+
+#endif
+
#endif // SECMON
diff --git a/core/embed/sys/smcall/stm32/smcall_verifiers.h b/core/embed/sys/smcall/stm32/smcall_verifiers.h
index a3f62723b..34e9bf6c5 100644
--- a/core/embed/sys/smcall/stm32/smcall_verifiers.h
+++ b/core/embed/sys/smcall/stm32/smcall_verifiers.h
@@ -134,4 +134,10 @@ bool backup_ram_write__verified(uint16_t key, backup_ram_item_type_t type,
#endif // USE_BACKUP_RAM
+#ifdef USE_NRF
+secbool secret_validate_nrf_pairing__verified(const uint8_t *message,
+ size_t msg_len,
+ const uint8_t *mac,
+ size_t mac_len);
+#endif
#endif // SECMON
diff --git a/core/embed/sys/syscall/inc/sys/syscall_numbers.h b/core/embed/sys/syscall/inc/sys/syscall_numbers.h
index 6967d77cd..86c7e6212 100644
--- a/core/embed/sys/syscall/inc/sys/syscall_numbers.h
+++ b/core/embed/sys/syscall/inc/sys/syscall_numbers.h
@@ -148,6 +148,7 @@ typedef enum {
SYSCALL_NRF_UPDATE_REQUIRED,
SYSCALL_NRF_UPDATE,
SYSCALL_NRF_GET_VERSION,
+ SYSCALL_NRF_AUTHENTICATE,
SYSCALL_POWER_MANAGER_SUSPEND,
SYSCALL_POWER_MANAGER_HIBERNATE,
diff --git a/core/embed/sys/syscall/stm32/syscall_dispatch.c b/core/embed/sys/syscall/stm32/syscall_dispatch.c
index 27f32efb7..4b0225274 100644
--- a/core/embed/sys/syscall/stm32/syscall_dispatch.c
+++ b/core/embed/sys/syscall/stm32/syscall_dispatch.c
@@ -764,6 +764,10 @@ __attribute((no_stack_protector)) void syscall_handler(uint32_t *args,
args[0] = nrf_get_version();
} break;
+ case SYSCALL_NRF_AUTHENTICATE: {
+ args[0] = nrf_authenticate();
+ } break;
+
#endif
#ifdef USE_POWER_MANAGER
diff --git a/core/embed/sys/syscall/stm32/syscall_stubs.c b/core/embed/sys/syscall/stm32/syscall_stubs.c
index f999bd0b6..514b38865 100644
--- a/core/embed/sys/syscall/stm32/syscall_stubs.c
+++ b/core/embed/sys/syscall/stm32/syscall_stubs.c
@@ -727,6 +727,10 @@ uint32_t nrf_get_version(void) {
return syscall_invoke0(SYSCALL_NRF_GET_VERSION);
}
+bool nrf_authenticate(void) {
+ return (bool)syscall_invoke0(SYSCALL_NRF_AUTHENTICATE);
+}
+
#endif
// =============================================================================
Why this scored 36/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.