feat(core/ble): send user disconnect flag in advertisement data
What changed, and why it matters
This commit is a feature addition, not a security fix. It changes how the Trezor hardware wallet's Bluetooth Low Energy (BLE) advertising data is structured so that a new flag can be broadcast when the user intentionally disconnected. This lets a paired computer or phone know not to automatically reconnect. There is no indication in the commit or supplied references that this resolves a vulnerability or security issue.
No security action required. Treat as normal feature code review; verify the bitfield layout is consistent across host and Nordic firmware and that reserved bits are zeroed.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch refactors the cmd_advertising_on_t command structure on both the STM32 host and Nordic BLE coprocessor, replacing a single whitelist byte with a bitfield flags containing whitelist, user_disconnect, and reserved bits. When the host issues BLE_DISCONNECT while connected and restart_adv_on_disconnect is enabled, it sets next_adv_with_disconnect, which is forwarded to the Nordic side and ORed into manufacturer advertisement data as ADV_FLAG_USER_DISCONNECT (0x08). This is purely an informational flag in the BLE advertisement payload.
Changed components
core/embed/io/ble/stm32/ble.ccore/embed/io/ble/stm32/ble_comm_defs.hnordic/trezor/trezor-ble/src/ble/advertising.cnordic/trezor/trezor-ble/src/ble/ble_internal.hnordic/trezor/trezor-ble/src/ble/ble_management.cInspect captured patch +35 / −10
diff --git a/core/embed/io/ble/stm32/ble.c b/core/embed/io/ble/stm32/ble.c
index 5986ffa7f..8fdf974e5 100644
--- a/core/embed/io/ble/stm32/ble.c
+++ b/core/embed/io/ble/stm32/ble.c
@@ -59,6 +59,8 @@ typedef struct {
bool accept_msgs;
bool reboot_on_resume;
bool restart_adv_on_disconnect;
+ bool next_adv_with_disconnect; // next advertising will be started with
+ // forced disconnect flag
uint8_t busy_flag;
bool pairing_allowed;
bool pairing_requested;
@@ -115,12 +117,16 @@ static bool ble_send_advertising_on(ble_driver_t *drv, bool whitelist) {
cmd_advertising_on_t data = {
.cmd_id = INTERNAL_CMD_ADVERTISING_ON,
- .whitelist = whitelist ? 1 : 0,
+ .flags.whitelist = whitelist ? 1 : 0,
+ .flags.user_disconnect = drv->next_adv_with_disconnect ? 1 : 0,
+ .flags.reserved = 0,
.color = props.color,
.static_addr = drv->adv_cmd.static_mac,
.device_code = MODEL_BLE_CODE,
};
+ drv->next_adv_with_disconnect = false;
+
memcpy(data.name, drv->adv_cmd.name, BLE_ADV_NAME_LEN);
return nrf_send_msg(NRF_SERVICE_BLE_MANAGER, (uint8_t *)&data, sizeof(data),
@@ -868,6 +874,9 @@ bool ble_issue_command(ble_command_t *command) {
result = ble_start_pairing(command);
return result;
case BLE_DISCONNECT:
+ if (drv->connected && drv->restart_adv_on_disconnect) {
+ drv->next_adv_with_disconnect = true;
+ }
result = ble_send_disconnect(drv);
break;
case BLE_ERASE_BONDS:
diff --git a/core/embed/io/ble/stm32/ble_comm_defs.h b/core/embed/io/ble/stm32/ble_comm_defs.h
index e4ad11dbd..b1e7c277c 100644
--- a/core/embed/io/ble/stm32/ble_comm_defs.h
+++ b/core/embed/io/ble/stm32/ble_comm_defs.h
@@ -72,7 +72,11 @@ typedef enum {
typedef struct {
uint8_t cmd_id;
- uint8_t whitelist;
+ struct {
+ uint8_t whitelist : 1;
+ uint8_t user_disconnect : 1;
+ uint8_t reserved : 6;
+ } flags;
uint8_t color;
uint8_t static_addr;
uint8_t device_code;
diff --git a/nordic/trezor/trezor-ble/src/ble/advertising.c b/nordic/trezor/trezor-ble/src/ble/advertising.c
index dcc55f53f..6cedb820c 100644
--- a/nordic/trezor/trezor-ble/src/ble/advertising.c
+++ b/nordic/trezor/trezor-ble/src/ble/advertising.c
@@ -35,6 +35,7 @@ LOG_MODULE_REGISTER(LOG_MODULE_NAME);
#define ADV_FLAG_PAIRING 0x01
#define ADV_FLAG_BOND_MEM_FULL 0x02
#define ADV_FLAG_DEV_CONNECTED 0x04
+#define ADV_FLAG_USER_DISCONNECT 0x08
bool advertising = false;
bool advertising_wl = false;
@@ -65,8 +66,9 @@ void advertising_setup_wl(void) {
bt_foreach_bond(BT_ID_DEFAULT, add_to_whitelist, NULL);
}
-void advertising_start(bool wl, uint8_t color, uint8_t device_code,
- bool static_addr, char *name, int name_len) {
+void advertising_start(bool wl, bool user_disconnect, uint8_t color,
+ uint8_t device_code, bool static_addr, char *name,
+ int name_len) {
if (advertising) {
LOG_WRN("Restarting advertising");
bt_le_adv_stop();
@@ -88,6 +90,9 @@ void advertising_start(bool wl, uint8_t color, uint8_t device_code,
if (connection_is_connected()) {
manufacturer_data[2] |= ADV_FLAG_DEV_CONNECTED;
}
+ if (user_disconnect) {
+ manufacturer_data[2] |= ADV_FLAG_USER_DISCONNECT;
+ }
manufacturer_data[3] = color;
manufacturer_data[4] = device_code;
diff --git a/nordic/trezor/trezor-ble/src/ble/ble_internal.h b/nordic/trezor/trezor-ble/src/ble/ble_internal.h
index 117d56645..1e0b23eea 100644
--- a/nordic/trezor/trezor-ble/src/ble/ble_internal.h
+++ b/nordic/trezor/trezor-ble/src/ble/ble_internal.h
@@ -103,7 +103,12 @@ typedef enum {
typedef struct {
uint8_t cmd_id;
- uint8_t whitelist;
+ struct {
+ uint8_t whitelist : 1;
+ uint8_t user_disconnect : 1; // If set, the device should not try to
+ // reconnect after disconnecting
+ uint8_t reserved : 6;
+ } flags;
uint8_t color;
uint8_t static_addr;
uint8_t device_code;
@@ -153,8 +158,9 @@ size_t bonds_get_all(bt_addr_le_t *addr, size_t max_count);
// Initialization
void advertising_init(void);
// Start advertising, with or without whitelist
-void advertising_start(bool wl, uint8_t color, uint8_t device_code,
- bool static_addr, char *name, int name_len);
+void advertising_start(bool wl, bool user_disconnect, uint8_t color,
+ uint8_t device_code, bool static_addr, char *name,
+ int name_len);
// Check if advertising is active
bool advertising_is_advertising(void);
// Check if advertising is active with whitelist
diff --git a/nordic/trezor/trezor-ble/src/ble/ble_management.c b/nordic/trezor/trezor-ble/src/ble/ble_management.c
index eb838196a..91cbdef52 100644
--- a/nordic/trezor/trezor-ble/src/ble/ble_management.c
+++ b/nordic/trezor/trezor-ble/src/ble/ble_management.c
@@ -155,12 +155,13 @@ static void process_command(uint8_t *data, uint16_t len) {
int name_len = strnlen(cmd->name, BLE_ADV_NAME_LEN);
- if (cmd->whitelist != 0) {
+ if (cmd->flags.whitelist != 0) {
pairing_num_comp_reply(false, NULL);
}
- advertising_start(cmd->whitelist != 0, cmd->color, cmd->device_code,
- cmd->static_addr, (char *)cmd->name, name_len);
+ advertising_start(cmd->flags.whitelist != 0, cmd->flags.user_disconnect,
+ cmd->color, cmd->device_code, cmd->static_addr,
+ (char *)cmd->name, name_len);
} break;
case INTERNAL_CMD_ADVERTISING_OFF:
advertising_stop();
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.