fix(core/ble): end pairing mode when a bonded device connects
What changed, and why it matters
This commit fixes a Bluetooth pairing behavior in Trezor hardware wallets. Previously, if the device was left in 'pairing mode' (which advertises to and accepts new devices), a connection from an already-bonded/trusted device could keep it stuck in pairing mode instead of exiting it. The fix detects bonded connections and ends pairing mode when one connects. Staying in pairing mode longer than intended could let a nearby attacker pair with or interact with the device while the user thinks it is only reconnecting to a known device.
Treat as a low-to-moderate hardening fix. Review whether pairing-mode timeout or user confirmation already limits the window, and confirm the bonded flag is cleared reliably on disconnect. No urgent user action is indicated by the commit alone.
Security signals we found
Behavioral fix around pairing-mode lifecycle
New bonded-connection flag derived from BT_SECURITY_L4
Core now exits pairing mode on bonded reconnect
No changelog entry provided
No explicit security advisory language in commit
Evidence from the diff
The patch adds a bonded_connection flag to the BLE status message shared between the Nordic BLE firmware and the STM32 core. The Nordic side sets this flag when security_changed reports BT_SECURITY_L4 (LE Secure Connections with bonding). The STM32 core uses the flag in two ways: (1) it no longer transitions to BLE_MODE_PAIRING merely because a bonded device is connected while pairing is allowed, and (2) if a bonded device connects while pairing mode was explicitly requested, it calls ble_pairing_end() to leave pairing mode. This prevents a bonded reconnect from keeping the device in an advertising/pairing state.
Changed components
core/embed/io/ble/stm32/ble.ccore/embed/io/ble/stm32/ble_comm_defs.hnordic/trezor/trezor-ble/src/ble/ble_internal.hnordic/trezor/trezor-ble/src/ble/ble_management.cnordic/trezor/trezor-ble/src/ble/connection.cInspect captured patch +31 / −3
diff --git a/core/embed/io/ble/stm32/ble.c b/core/embed/io/ble/stm32/ble.c
index 222d72005..0d0739b39 100644
--- a/core/embed/io/ble/stm32/ble.c
+++ b/core/embed/io/ble/stm32/ble.c
@@ -279,7 +279,7 @@ static void ble_process_rx_msg_status(const uint8_t *data, uint32_t len) {
ble_mode_t prev_mode = drv->mode_current;
if ((msg.advertising && !msg.advertising_whitelist) ||
- (msg.connected && drv->pairing_allowed)) {
+ (msg.connected && drv->pairing_allowed && !msg.flags.bonded_connection)) {
drv->mode_current = BLE_MODE_PAIRING;
} else if (msg.advertising) {
drv->mode_current = BLE_MODE_CONNECTABLE;
@@ -298,6 +298,12 @@ static void ble_process_rx_msg_status(const uint8_t *data, uint32_t len) {
drv->busy_flag = msg.busy_flag;
drv->peer_count = msg.peer_count;
+ if (msg.connected && msg.flags.bonded_connection &&
+ drv->mode_requested == BLE_MODE_PAIRING) {
+ // bonded device connected in pairing mode - end pairing
+ ble_pairing_end(drv);
+ }
+
if (prev_mode == BLE_MODE_PAIRING && drv->mode_current != BLE_MODE_PAIRING) {
if (drv->mode_requested == BLE_MODE_PAIRING) {
// unexpected pairing end - restart pairing
diff --git a/core/embed/io/ble/stm32/ble_comm_defs.h b/core/embed/io/ble/stm32/ble_comm_defs.h
index b1e7c277c..278169056 100644
--- a/core/embed/io/ble/stm32/ble_comm_defs.h
+++ b/core/embed/io/ble/stm32/ble_comm_defs.h
@@ -31,7 +31,10 @@ typedef struct {
uint8_t peer_count;
uint8_t busy_flag;
- uint8_t reserved;
+ struct {
+ bool bonded_connection : 1;
+ uint8_t reserved : 7;
+ } flags;
uint8_t sd_version_number;
uint16_t sd_company_id;
diff --git a/nordic/trezor/trezor-ble/src/ble/ble_internal.h b/nordic/trezor/trezor-ble/src/ble/ble_internal.h
index 1e0b23eea..7625907c5 100644
--- a/nordic/trezor/trezor-ble/src/ble/ble_internal.h
+++ b/nordic/trezor/trezor-ble/src/ble/ble_internal.h
@@ -61,7 +61,10 @@ typedef struct {
uint8_t peer_count;
uint8_t busy_flag;
- uint8_t reserved;
+ struct {
+ bool bonded_connection : 1;
+ uint8_t reserved : 7;
+ } flags;
uint8_t sd_version_number;
uint16_t sd_company_id;
@@ -177,6 +180,8 @@ void connection_disconnect(void);
bool connection_is_connected(void);
// Get current connection
struct bt_conn *connection_get_current(void);
+// Is current connection bonded
+bool connection_is_bonded(void);
// Pairing functions
// Initialization
diff --git a/nordic/trezor/trezor-ble/src/ble/ble_management.c b/nordic/trezor/trezor-ble/src/ble/ble_management.c
index 91cbdef52..beea4f545 100644
--- a/nordic/trezor/trezor-ble/src/ble/ble_management.c
+++ b/nordic/trezor/trezor-ble/src/ble/ble_management.c
@@ -60,6 +60,8 @@ void ble_management_send_status_event(void) {
msg.app_version = 0;
msg.bld_version = 0;
msg.busy_flag = ble_get_busy_flag();
+ msg.flags.bonded_connection = connection_is_bonded();
+ msg.flags.reserved = 0;
if (connected) {
memcpy(msg.connected_addr, bt_conn_get_dst(conn)->a.val, BT_ADDR_SIZE);
diff --git a/nordic/trezor/trezor-ble/src/ble/connection.c b/nordic/trezor/trezor-ble/src/ble/connection.c
index 8debe85f2..65f9046df 100644
--- a/nordic/trezor/trezor-ble/src/ble/connection.c
+++ b/nordic/trezor/trezor-ble/src/ble/connection.c
@@ -34,6 +34,7 @@ LOG_MODULE_REGISTER(LOG_MODULE_NAME);
static struct bt_conn *current_conn = NULL;
static struct bt_conn *next_conn = NULL;
+static bool bonded_connection = false;
static void show_params(struct bt_conn *conn) {
struct bt_conn_info info;
@@ -102,6 +103,8 @@ void connected(struct bt_conn *conn, uint8_t err) {
void disconnected(struct bt_conn *conn, uint8_t reason) {
char addr[BT_ADDR_LE_STR_LEN];
+ bonded_connection = false;
+
advertising_stop();
pairing_reset();
@@ -132,7 +135,14 @@ static void security_changed(struct bt_conn *conn, bt_security_t level,
if (!err) {
LOG_INF("Security changed: %s level %u", addr, level);
+
+ if (level == BT_SECURITY_L4) {
+ bonded_connection = true;
+ } else {
+ bonded_connection = false;
+ }
} else {
+ bonded_connection = false;
LOG_WRN("Security failed: %s level %u err %d", addr, level, err);
}
}
@@ -174,3 +184,5 @@ void connection_resume(void) {
bt_conn_le_param_update(conn, param);
}
}
+
+bool connection_is_bonded(void) { return bonded_connection; }
Why this scored 46/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.