What changed, and why it matters
This commit fixes Bluetooth Low Energy (BLE) pairing behavior in the Trezor hardware wallet firmware. It changes how the device advertises itself, handles pairing requests, disconnects existing connections before pairing, and resets the advertising name after pairing ends. The changes are framed as a functional bug fix rather than a security fix, but they touch on sensitive pairing state management that could affect whether an attacker could trick the device into pairing unexpectedly or stay connected when it should not.
Treat this as a functional fix with potential security side effects. Review the BLE state machine for race conditions around pairing_allowed/requested transitions, verify that the new disconnect-before-pairing logic cannot be abused to disconnect a legitimate peer and force pairing with an attacker, and inspect the updated `trezor-ble.bin` blob for corresponding changes. No CVE or advisory is indicated by the commit metadata.
Security signals we found
BLE pairing state machine changed to disconnect existing connections before entering pairing mode
Pairing flags (`pairing_allowed`, `pairing_requested`) now cleared consistently via `ble_pairing_end`
Advertising name reset to model name after pairing completes or fails
Switch-off command now disconnects active BLE connections
New syscall verifier added for `ble_set_name` to validate caller memory access
Precompiled BLE coprocessor binary updated without source diff
Evidence from the diff
The patch refactors BLE state transitions in the STM32 BLE driver. Key changes include: (1) moving the disconnect-before-pairing logic from the bootloader’s ble_iface_start_pairing into the driver’s ble_start_pairing, with a retry loop and event flush; (2) adding ble_set_name to update the advertising name without restarting advertising; (3) renaming Python-facing functions from stop_advertising/connectable_mode to switch_off/switch_on and adding set_name; (4) making BLE_SWITCH_OFF actually disconnect an active connection; (5) adjusting mode transitions so pairing mode is entered when pairing_allowed is set while connected, and clearing pairing flags when pairing ends; (6) updating the bootloader pairing workflows to reject pairing and reset the name via ble_iface_end_pairing; (7) adding a syscall for ble_set_name with a memory-access verifier. A precompiled binary blob trezor-ble.bin is also updated, but its contents are not shown in the diff.
Changed components
core/embed/io/ble/stm32/ble.ccore/embed/io/ble/unix/ble.ccore/embed/io/ble/inc/io/ble.hcore/embed/projects/bootloader/wire/wire_iface_ble.ccore/embed/projects/bootloader/workflow/wf_ble_pairing_request.ccore/embed/rust/src/trezorhal/ble/mod.rscore/embed/rust/src/trezorhal/ble/micropython.rscore/embed/sys/syscall/stm32/syscall_dispatch.ccore/embed/sys/syscall/stm32/syscall_stubs.ccore/embed/sys/syscall/stm32/syscall_verifiers.ccore/embed/sys/syscall/stm32/syscall_verifiers.hcore/embed/models/T3W1/trezor-ble.bincore/src/apps/management/ble/pair_new_device.pyInspect captured patch +208 / −68
diff --git a/core/embed/io/ble/inc/io/ble.h b/core/embed/io/ble/inc/io/ble.h
index 0e8994a8..72069355 100644
--- a/core/embed/io/ble/inc/io/ble.h
+++ b/core/embed/io/ble/inc/io/ble.h
@@ -140,6 +140,9 @@ void ble_stop(void);
// Returns `true` if the command was successfully issued.
bool ble_issue_command(ble_command_t *command);
+// Sets the BLE advertising name, but does not affect advertising
+void ble_set_name(const uint8_t *name, size_t len);
+
// Reads an event from the BLE module
//
// Retrieves the next event from the BLE module's event queue.
@@ -148,9 +151,6 @@ bool ble_issue_command(ble_command_t *command);
// available.
bool ble_get_event(ble_event_t *event);
-// Flushes the BLE event queue
-void ble_event_flush(void);
-
// Retrieves the current state of the BLE module
//
// Obtains the current operational state of the BLE module.
diff --git a/core/embed/io/ble/stm32/ble.c b/core/embed/io/ble/stm32/ble.c
index 382498e4..286f2420 100644
--- a/core/embed/io/ble/stm32/ble.c
+++ b/core/embed/io/ble/stm32/ble.c
@@ -34,6 +34,8 @@
#include "ble_comm_defs.h"
+static bool ble_start_pairing(ble_command_t *command);
+
// changing value of TX_QUEUE_LEN is not allowed
// as it might result in order of messages being changed
#define TX_QUEUE_LEN 1
@@ -83,6 +85,12 @@ static ble_driver_t g_ble_driver = {0};
static const syshandle_vmt_t ble_handle_vmt;
static const syshandle_vmt_t ble_iface_handle_vmt;
+static void ble_pairing_end(ble_driver_t *drv) {
+ drv->pairing_allowed = false;
+ drv->pairing_requested = false;
+ drv->mode_requested = BLE_MODE_KEEP_CONNECTION;
+}
+
static bool ble_send_state_request(ble_driver_t *drv) {
(void)drv;
uint8_t cmd = INTERNAL_CMD_SEND_STATE;
@@ -144,7 +152,7 @@ static bool ble_send_pairing_reject(ble_driver_t *drv) {
nrf_send_msg(NRF_SERVICE_BLE_MANAGER, &cmd, sizeof(cmd), NULL, NULL);
if (result) {
- drv->pairing_requested = false;
+ ble_pairing_end(drv);
}
return result;
@@ -161,7 +169,7 @@ static bool ble_send_pairing_accept(ble_driver_t *drv, uint8_t *code) {
sizeof(data), NULL, NULL);
if (result) {
- drv->pairing_requested = false;
+ ble_pairing_end(drv);
}
return result;
@@ -184,6 +192,11 @@ static void ble_process_rx_msg_status(const uint8_t *data, uint32_t len) {
event_status_msg_t msg = {0};
memcpy(&msg, data, MIN(sizeof(event_status_msg_t), len));
+ if (!drv->status_valid && msg.connected &&
+ drv->mode_requested == BLE_MODE_OFF) {
+ drv->mode_requested = BLE_MODE_KEEP_CONNECTION;
+ }
+
if (drv->connected != msg.connected) {
if (msg.connected) {
// new connection
@@ -199,10 +212,12 @@ static void ble_process_rx_msg_status(const uint8_t *data, uint32_t len) {
drv->pairing_allowed = false;
}
- if (msg.peer_count > 1) {
- drv->mode_requested = BLE_MODE_CONNECTABLE;
- } else {
- drv->mode_requested = BLE_MODE_KEEP_CONNECTION;
+ if (drv->mode_current != BLE_MODE_PAIRING) {
+ if (msg.peer_count > 1) {
+ drv->mode_requested = BLE_MODE_CONNECTABLE;
+ } else {
+ drv->mode_requested = BLE_MODE_KEEP_CONNECTION;
+ }
}
} else {
// connection lost
@@ -239,7 +254,9 @@ static void ble_process_rx_msg_status(const uint8_t *data, uint32_t len) {
}
}
- if (msg.advertising && !msg.advertising_whitelist) {
+ ble_mode_t prev_mode = drv->mode_current;
+ if ((msg.advertising && !msg.advertising_whitelist) ||
+ (msg.connected && drv->pairing_allowed)) {
drv->mode_current = BLE_MODE_PAIRING;
} else if (msg.advertising) {
drv->mode_current = BLE_MODE_CONNECTABLE;
@@ -249,6 +266,26 @@ static void ble_process_rx_msg_status(const uint8_t *data, uint32_t len) {
drv->mode_current = BLE_MODE_OFF;
}
+ if (drv->mode_current == BLE_MODE_KEEP_CONNECTION && drv->peer_count > 1) {
+ drv->mode_requested = BLE_MODE_CONNECTABLE;
+ }
+
+ drv->busy_flag = msg.busy_flag;
+ drv->peer_count = msg.peer_count;
+
+ if (prev_mode == BLE_MODE_PAIRING && drv->mode_current != BLE_MODE_PAIRING) {
+ // pairing mode ended
+ ble_pairing_end(drv);
+ }
+
+ if (drv->mode_requested == BLE_MODE_KEEP_CONNECTION && !drv->connected) {
+ if (drv->peer_count > 0) {
+ drv->mode_requested = BLE_MODE_CONNECTABLE;
+ } else {
+ drv->mode_requested = BLE_MODE_OFF;
+ }
+ }
+
if (msg.peer_count > 1 && drv->peer_count <= 1) {
// new bond
if (msg.connected && drv->mode_requested == BLE_MODE_KEEP_CONNECTION) {
@@ -256,9 +293,6 @@ 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;
-
drv->status_valid = true;
}
@@ -299,8 +333,7 @@ static void ble_process_rx_msg_pairing_cancelled(const uint8_t *data,
ble_event_t event = {.type = BLE_PAIRING_CANCELLED, .data_len = 0};
tsqueue_enqueue(&drv->event_queue, (uint8_t *)&event, sizeof(event), NULL);
- drv->pairing_requested = false;
- drv->pairing_allowed = false;
+ ble_pairing_end(drv);
}
static void ble_process_rx_msg_pairing_completed(const uint8_t *data,
@@ -312,8 +345,8 @@ static void ble_process_rx_msg_pairing_completed(const uint8_t *data,
ble_event_t event = {.type = BLE_PAIRING_COMPLETED, .data_len = 0};
tsqueue_enqueue(&drv->event_queue, (uint8_t *)&event, sizeof(event), NULL);
- drv->pairing_requested = false;
drv->pairing_allowed = false;
+ drv->pairing_requested = false;
}
static void ble_process_rx_msg_mac(const uint8_t *data, uint32_t len) {
@@ -432,9 +465,9 @@ static void ble_loop(void *context) {
if (drv->mode_current != drv->mode_requested) {
if (drv->mode_requested == BLE_MODE_OFF) {
ble_send_advertising_off(drv);
- // if (drv->connected) {
- // nrf_send_disconnect();
- // }
+ if (drv->connected) {
+ ble_send_disconnect(drv);
+ }
} else if (drv->mode_requested == BLE_MODE_KEEP_CONNECTION) {
ble_send_advertising_off(drv);
} else if (drv->mode_requested == BLE_MODE_CONNECTABLE) {
@@ -764,10 +797,9 @@ bool ble_issue_command(ble_command_t *command) {
result = true;
break;
case BLE_PAIRING_MODE:
- memcpy(&drv->adv_cmd, &command->data.adv_start, sizeof(drv->adv_cmd));
- drv->mode_requested = BLE_MODE_PAIRING;
- result = true;
- break;
+ irq_unlock(key);
+ result = ble_start_pairing(command);
+ return result;
case BLE_DISCONNECT:
result = ble_send_disconnect(drv);
break;
@@ -809,7 +841,7 @@ bool ble_get_event(ble_event_t *event) {
return result;
}
-void ble_event_flush(void) {
+static void ble_event_flush(void) {
ble_driver_t *drv = &g_ble_driver;
if (!drv->initialized) {
@@ -823,6 +855,68 @@ void ble_event_flush(void) {
irq_unlock(key);
}
+void ble_set_name(const uint8_t *name, size_t len) {
+ ble_driver_t *drv = &g_ble_driver;
+
+ if (!drv->initialized) {
+ return;
+ }
+
+ irq_key_t key = irq_lock();
+
+ memset(drv->adv_cmd.name, 0, sizeof(drv->adv_cmd.name));
+ memcpy(drv->adv_cmd.name, name, MIN(len, sizeof(drv->adv_cmd.name)));
+
+ if (drv->mode_requested == BLE_MODE_CONNECTABLE) {
+ ble_send_advertising_on(drv, true);
+ }
+
+ if (drv->mode_requested == BLE_MODE_PAIRING) {
+ ble_send_advertising_on(drv, false);
+ }
+
+ irq_unlock(key);
+}
+
+static bool ble_start_pairing(ble_command_t *command) {
+ ble_driver_t *drv = &g_ble_driver;
+
+ if (!drv->initialized) {
+ return false;
+ }
+
+ uint16_t retry_cnt = 0;
+ irq_key_t key;
+
+ bool connected = drv->connected;
+ while (connected) {
+ retry_cnt++;
+ if (retry_cnt > 10) {
+ // too many retries, give up
+ return false;
+ }
+
+ ble_send_disconnect(drv);
+
+ systick_delay_ms(20); // wait for disconnect to complete
+
+ key = irq_lock();
+ connected = drv->connected;
+ irq_unlock(key);
+ }
+
+ ble_event_flush();
+
+ key = irq_lock();
+
+ memcpy(&drv->adv_cmd, &command->data.adv_start, sizeof(drv->adv_cmd));
+ drv->mode_requested = BLE_MODE_PAIRING;
+
+ irq_unlock(key);
+
+ return true;
+}
+
void ble_get_state(ble_state_t *state) {
const ble_driver_t *drv = &g_ble_driver;
diff --git a/core/embed/io/ble/unix/ble.c b/core/embed/io/ble/unix/ble.c
index cd1777fa..5e04c893 100644
--- a/core/embed/io/ble/unix/ble.c
+++ b/core/embed/io/ble/unix/ble.c
@@ -11,6 +11,8 @@ void ble_stop(void) {}
bool ble_issue_command(ble_command_t *command) { return true; }
+void ble_set_name(const uint8_t *name, size_t len) {}
+
bool ble_get_event(ble_event_t *event) { return false; }
void ble_get_state(ble_state_t *state) {
diff --git a/core/embed/models/T3W1/trezor-ble.bin b/core/embed/models/T3W1/trezor-ble.bin
index b80399ea..58ffa093 100644
Binary files a/core/embed/models/T3W1/trezor-ble.bin and b/core/embed/models/T3W1/trezor-ble.bin differ
diff --git a/core/embed/projects/bootloader/wire/wire_iface_ble.c b/core/embed/projects/bootloader/wire/wire_iface_ble.c
index 03833d4f..ddb2c96a 100644
--- a/core/embed/projects/bootloader/wire/wire_iface_ble.c
+++ b/core/embed/projects/bootloader/wire/wire_iface_ble.c
@@ -149,6 +149,13 @@ void ble_iface_deinit(void) {
void ble_iface_end_pairing(void) {
ble_state_t state = {0};
+ ble_command_t reject_cmd = {
+ .cmd_type = BLE_REJECT_PAIRING,
+ };
+ ble_issue_command(&reject_cmd);
+
+ ble_set_name((const uint8_t*)MODEL_FULL_NAME, sizeof(MODEL_FULL_NAME));
+
ble_get_state(&state);
if (state.peer_count > 0) {
@@ -176,22 +183,6 @@ bool ble_iface_start_pairing(void) {
uint16_t retry_cnt = 0;
- while (state.connected && retry_cnt < 10) {
- ble_command_t cmd_disconnect = {
- .cmd_type = BLE_DISCONNECT,
- };
- ble_issue_command(&cmd_disconnect);
- systick_delay_ms(20);
- ble_get_state(&state);
- retry_cnt++;
- }
-
- if (state.connected) {
- return false;
- }
-
- ble_event_flush();
-
char adv_name[BLE_ADV_NAME_LEN];
mini_snprintf(adv_name, sizeof(adv_name), "%s (%c%c%c)", MODEL_FULL_NAME,
get_random_char(), get_random_char(), get_random_char());
@@ -204,7 +195,9 @@ bool ble_iface_start_pairing(void) {
}},
};
memcpy(cmd.data.adv_start.name, adv_name, BLE_ADV_NAME_LEN);
- ble_issue_command(&cmd);
+ if (!ble_issue_command(&cmd)) {
+ return false;
+ }
retry_cnt = 0;
ble_get_state(&state);
diff --git a/core/embed/projects/bootloader/workflow/wf_ble_pairing_request.c b/core/embed/projects/bootloader/workflow/wf_ble_pairing_request.c
index c8003866..ee370a1d 100644
--- a/core/embed/projects/bootloader/workflow/wf_ble_pairing_request.c
+++ b/core/embed/projects/bootloader/workflow/wf_ble_pairing_request.c
@@ -72,10 +72,7 @@ workflow_result_t workflow_ble_pairing_request(const vendor_header *const vhdr,
uint8_t pairing_code[BLE_PAIRING_CODE_LEN] = {0};
if (result != CONFIRM || !encode_pairing_code(code, pairing_code)) {
- ble_command_t cmd = {
- .cmd_type = BLE_REJECT_PAIRING,
- };
- ble_issue_command(&cmd);
+ ble_iface_end_pairing();
return WF_OK_PAIRING_FAILED;
}
@@ -118,6 +115,7 @@ workflow_result_t workflow_ble_pairing_request(const vendor_header *const vhdr,
}
}
+ ble_set_name((const uint8_t *)MODEL_FULL_NAME, sizeof(MODEL_FULL_NAME));
return WF_OK_PAIRING_COMPLETED;
}
@@ -151,10 +149,7 @@ workflow_result_t workflow_wireless_setup(const vendor_header *const vhdr,
uint8_t pairing_code[BLE_PAIRING_CODE_LEN] = {0};
if (result != CONFIRM || !encode_pairing_code(code, pairing_code)) {
- ble_command_t cmd = {
- .cmd_type = BLE_REJECT_PAIRING,
- };
- ble_issue_command(&cmd);
+ ble_iface_end_pairing();
return WF_OK_PAIRING_FAILED;
}
@@ -197,6 +192,7 @@ workflow_result_t workflow_wireless_setup(const vendor_header *const vhdr,
}
}
+ ble_set_name((const uint8_t *)MODEL_FULL_NAME, sizeof(MODEL_FULL_NAME));
return WF_OK_PAIRING_COMPLETED;
}
diff --git a/core/embed/rust/build.rs b/core/embed/rust/build.rs
index f3d95cce..ba88866c 100644
--- a/core/embed/rust/build.rs
+++ b/core/embed/rust/build.rs
@@ -424,6 +424,7 @@ fn generate_trezorhal_bindings() {
.allowlist_function("ble_start")
.allowlist_function("ble_write")
.allowlist_function("ble_read")
+ .allowlist_function("ble_set_name")
.allowlist_type("ble_command_t")
.allowlist_type("ble_state_t")
.allowlist_type("ble_event_t")
diff --git a/core/embed/rust/librust_qstr.h b/core/embed/rust/librust_qstr.h
index 4d16c1ca..d4d5c77f 100644
--- a/core/embed/rust/librust_qstr.h
+++ b/core/embed/rust/librust_qstr.h
@@ -720,6 +720,7 @@ static void _librust_qstrs(void) {
MP_QSTR_send__transaction_signed;
MP_QSTR_send__you_are_contributing;
MP_QSTR_set_brightness;
+ MP_QSTR_set_name;
MP_QSTR_setting__adjust;
MP_QSTR_setting__apply;
MP_QSTR_share_words__words_in_order;
@@ -759,7 +760,6 @@ static void _librust_qstrs(void) {
MP_QSTR_skip_first_paint;
MP_QSTR_start_advertising;
MP_QSTR_start_comm;
- MP_QSTR_stop_advertising;
MP_QSTR_storage_msg__processing;
MP_QSTR_storage_msg__starting;
MP_QSTR_storage_msg__verifying_pin;
@@ -771,6 +771,7 @@ static void _librust_qstrs(void) {
MP_QSTR_summary_br_name;
MP_QSTR_summary_items;
MP_QSTR_summary_title;
+ MP_QSTR_switch_off;
MP_QSTR_text;
MP_QSTR_text_check;
MP_QSTR_text_confirm;
diff --git a/core/embed/rust/src/trezorhal/ble/micropython.rs b/core/embed/rust/src/trezorhal/ble/micropython.rs
index 9081467c..a2d35ace 100644
--- a/core/embed/rust/src/trezorhal/ble/micropython.rs
+++ b/core/embed/rust/src/trezorhal/ble/micropython.rs
@@ -43,7 +43,7 @@ extern "C" fn py_start_advertising(whitelist: Obj, name: Obj) -> Obj {
let name = name.as_deref().unwrap_or(model::FULL_NAME);
if whitelist {
- connectable_mode(name)?;
+ switch_on(name)?;
} else {
pairing_mode(name)?;
};
@@ -52,9 +52,21 @@ extern "C" fn py_start_advertising(whitelist: Obj, name: Obj) -> Obj {
unsafe { util::try_or_raise(block) }
}
-extern "C" fn py_stop_advertising() -> Obj {
+extern "C" fn py_set_name(name: Obj) -> Obj {
let block = || {
- stop_advertising()?;
+ let name = name.try_into_option::<StrBuffer>()?;
+ let name = name.as_deref().unwrap_or(model::FULL_NAME);
+
+ set_name(name);
+
+ Ok(Obj::const_none())
+ };
+ unsafe { util::try_or_raise(block) }
+}
+
+extern "C" fn py_switch_off() -> Obj {
+ let block = || {
+ switch_off()?;
Ok(Obj::const_none())
};
unsafe { util::try_or_raise(block) }
@@ -237,12 +249,18 @@ pub static mp_module_trezorble: Module = obj_module! {
/// """
Qstr::MP_QSTR_start_advertising => obj_fn_2!(py_start_advertising).as_obj(),
- /// def stop_advertising():
+ /// def set_name(name: str | None):
+ /// """
+ /// Set advertising name.
+ /// """
+ Qstr::MP_QSTR_set_name => obj_fn_1!(py_set_name).as_obj(),
+
+ /// def switch_off():
/// """
- /// Stop advertising.
+ /// Stop advertising and disconnect any connected devices.
/// Raises exception if BLE driver reports an error.
/// """
- Qstr::MP_QSTR_stop_advertising => obj_fn_0!(py_stop_advertising).as_obj(),
+ Qstr::MP_QSTR_switch_off => obj_fn_0!(py_switch_off).as_obj(),
/// def disconnect():
/// """
diff --git a/core/embed/rust/src/trezorhal/ble/mod.rs b/core/embed/rust/src/trezorhal/ble/mod.rs
index cf509641..da13bce7 100644
--- a/core/embed/rust/src/trezorhal/ble/mod.rs
+++ b/core/embed/rust/src/trezorhal/ble/mod.rs
@@ -115,11 +115,11 @@ pub fn pairing_mode(name: &str) -> Result<(), Error> {
issue_command(ffi::ble_command_type_t_BLE_PAIRING_MODE, data_advname(name))
}
-pub fn connectable_mode(name: &str) -> Result<(), Error> {
+pub fn switch_on(name: &str) -> Result<(), Error> {
issue_command(ffi::ble_command_type_t_BLE_SWITCH_ON, data_advname(name))
}
-pub fn stop_advertising() -> Result<(), Error> {
+pub fn switch_off() -> Result<(), Error> {
issue_command(ffi::ble_command_type_t_BLE_SWITCH_OFF, data_none())
}
@@ -143,6 +143,11 @@ pub fn disconnect() -> Result<(), Error> {
issue_command(ffi::ble_command_type_t_BLE_DISCONNECT, data_none())
}
+pub fn set_name(name: &str) {
+ let bytes = prefix_utf8_bytes(name, ADV_NAME_LEN);
+ unsafe { ffi::ble_set_name(bytes.as_ptr(), bytes.len()) }
+}
+
pub fn start_comm() {
unsafe { ffi::ble_start() }
}
diff --git a/core/embed/sys/syscall/inc/sys/syscall_numbers.h b/core/embed/sys/syscall/inc/sys/syscall_numbers.h
index 63003abe..dfb8bf22 100644
--- a/core/embed/sys/syscall/inc/sys/syscall_numbers.h
+++ b/core/embed/sys/syscall/inc/sys/syscall_numbers.h
@@ -141,6 +141,7 @@ typedef enum {
SYSCALL_BLE_WRITE,
SYSCALL_BLE_CAN_READ,
SYSCALL_BLE_READ,
+ SYSCALL_BLE_SET_NAME,
SYSCALL_NRF_UPDATE_REQUIRED,
SYSCALL_NRF_UPDATE,
diff --git a/core/embed/sys/syscall/stm32/syscall_dispatch.c b/core/embed/sys/syscall/stm32/syscall_dispatch.c
index e39dae1e..361a9800 100644
--- a/core/embed/sys/syscall/stm32/syscall_dispatch.c
+++ b/core/embed/sys/syscall/stm32/syscall_dispatch.c
@@ -738,6 +738,12 @@ __attribute((no_stack_protector)) void syscall_handler(uint32_t *args,
size_t len = args[1];
args[0] = ble_read__verified(data, len);
} break;
+
+ case SYSCALL_BLE_SET_NAME: {
+ const uint8_t *name = (const uint8_t *)args[0];
+ size_t len = args[1];
+ ble_set_name__verified(name, len);
+ } break;
#endif
#ifdef USE_NRF
diff --git a/core/embed/sys/syscall/stm32/syscall_stubs.c b/core/embed/sys/syscall/stm32/syscall_stubs.c
index e8b0dacc..58a89d74 100644
--- a/core/embed/sys/syscall/stm32/syscall_stubs.c
+++ b/core/embed/sys/syscall/stm32/syscall_stubs.c
@@ -695,6 +695,10 @@ uint32_t ble_read(uint8_t *data, uint16_t len) {
return (uint32_t)syscall_invoke2((uint32_t)data, len, SYSCALL_BLE_READ);
}
+void ble_set_name(const uint8_t *name, size_t len) {
+ syscall_invoke2((uint32_t)name, len, SYSCALL_BLE_SET_NAME);
+}
+
#endif
#ifdef USE_NRF
diff --git a/core/embed/sys/syscall/stm32/syscall_verifiers.c b/core/embed/sys/syscall/stm32/syscall_verifiers.c
index c47d7198..7113b39b 100644
--- a/core/embed/sys/syscall/stm32/syscall_verifiers.c
+++ b/core/embed/sys/syscall/stm32/syscall_verifiers.c
@@ -843,6 +843,20 @@ access_violation:
apptask_access_violation();
return 0;
}
+
+void ble_set_name__verified(const uint8_t *name, size_t len) {
+ if (!probe_read_access(name, len)) {
+ goto access_violation;
+ }
+
+ ble_set_name(name, len);
+
+ return;
+
+access_violation:
+ apptask_access_violation();
+}
+
#endif
// ---------------------------------------------------------------------
diff --git a/core/embed/sys/syscall/stm32/syscall_verifiers.h b/core/embed/sys/syscall/stm32/syscall_verifiers.h
index d02364cf..03b83ea8 100644
--- a/core/embed/sys/syscall/stm32/syscall_verifiers.h
+++ b/core/embed/sys/syscall/stm32/syscall_verifiers.h
@@ -213,6 +213,8 @@ bool ble_write__verified(const uint8_t *data, size_t len);
secbool ble_read__verified(uint8_t *data, size_t len);
+void ble_set_name__verified(const uint8_t *name, size_t len);
+
#endif
// ---------------------------------------------------------------------
diff --git a/core/mocks/generated/trezorble.pyi b/core/mocks/generated/trezorble.pyi
index 6ba0055c..02f26bb8 100644
--- a/core/mocks/generated/trezorble.pyi
+++ b/core/mocks/generated/trezorble.pyi
@@ -61,9 +61,16 @@ def start_advertising(whitelist: bool, name: str | None):
# rust/src/trezorhal/ble/micropython.rs
-def stop_advertising():
+def set_name(name: str | None):
"""
- Stop advertising.
+ Set advertising name.
+ """
+
+
+# rust/src/trezorhal/ble/micropython.rs
+def switch_off():
+ """
+ Stop advertising and disconnect any connected devices.
Raises exception if BLE driver reports an error.
"""
diff --git a/core/src/apps/management/ble/pair_new_device.py b/core/src/apps/management/ble/pair_new_device.py
index 0b0611b6..38979578 100644
--- a/core/src/apps/management/ble/pair_new_device.py
+++ b/core/src/apps/management/ble/pair_new_device.py
@@ -7,13 +7,6 @@ from trezor.ui.layouts import CONFIRMED, interact
from trezor.wire import ActionCancelled
-def _end_pairing() -> None:
- if ble.peer_count() > 0:
- ble.start_advertising(True, storage_device.get_label())
- else:
- ble.stop_advertising()
-
-
def _default_ble_name() -> str:
"""Return model name and three random letters.
@@ -34,6 +27,7 @@ def _default_ble_name() -> str:
async def pair_new_device() -> None:
label = storage_device.get_label() or _default_ble_name()
ble.start_advertising(False, label)
+ result = None
try:
code = await interact(
trezorui_api.show_pairing_device_name(
@@ -60,4 +54,6 @@ async def pair_new_device() -> None:
if result is CONFIRMED:
ble.allow_pairing(code)
finally:
- _end_pairing()
+ if result is not CONFIRMED:
+ ble.reject_pairing()
+ ble.set_name(storage_device.get_label())
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.