fix(core): show label prefix during BLE pairing
What changed, and why it matters
This commit fixes a small user-interface bug in Trezor's Bluetooth pairing flow. Previously, if the device name was too long for the Bluetooth advertising packet, the user saw the full name on the Trezor screen while the truncated name was actually broadcast. Now the code returns the truncated name and displays that same shortened name to the user, so what they see matches what nearby devices see.
No security action required; treat as a normal UI/UX fix. Reviewers may optionally verify that the truncated label behavior is acceptable to users with long device names.
Security signals we found
UI consistency fix for Bluetooth pairing display
Return value now reflects truncated advertisement name
No memory-safety, cryptographic, or authorization changes observed
Evidence from the diff
The patch changes the Rust BLE HAL so that start_advertising(), pairing_mode(), switch_on(), and set_name() return the actual UTF-8 prefix used for the BLE advertisement (truncated to ADV_NAME_LEN). The Python pairing flow then uses this returned truncated label for on-screen prompts and restores it afterwards. This prevents a mismatch between the displayed label and the advertised label, and avoids a bug where the original full label was restored after pairing even though pairing used a truncated version.
Changed components
core/embed/rust/src/trezorhal/ble/micropython.rscore/embed/rust/src/trezorhal/ble/mod.rscore/mocks/generated/trezorble.pyicore/src/apps/management/ble/pair_new_device.pyInspect captured patch +23 / −20
diff --git a/core/embed/rust/src/trezorhal/ble/micropython.rs b/core/embed/rust/src/trezorhal/ble/micropython.rs
index 5349d6c3..4d4933f5 100644
--- a/core/embed/rust/src/trezorhal/ble/micropython.rs
+++ b/core/embed/rust/src/trezorhal/ble/micropython.rs
@@ -65,12 +65,12 @@ extern "C" fn py_start_advertising(whitelist: Obj, name: Obj) -> Obj {
let name = name.try_into_option::<StrBuffer>()?;
let name = name.as_deref().unwrap_or(model::FULL_NAME);
- if whitelist {
- switch_on(name)?;
+ let adv_name = if whitelist {
+ switch_on(name)?
} else {
- pairing_mode(name)?;
+ pairing_mode(name)?
};
- Ok(Obj::const_none())
+ adv_name.try_into()
};
unsafe { util::try_or_raise(block) }
}
@@ -339,7 +339,7 @@ pub static mp_module_trezorble: Module = obj_module! {
/// """
Qstr::MP_QSTR_start_comm => obj_fn_0!(py_start_comm).as_obj(),
- /// def start_advertising(whitelist: bool, name: str | None):
+ /// def start_advertising(whitelist: bool, name: str | None) -> str:
/// """
/// Start advertising.
/// Raises exception if BLE driver reports an error.
diff --git a/core/embed/rust/src/trezorhal/ble/mod.rs b/core/embed/rust/src/trezorhal/ble/mod.rs
index dc250d06..aa6a250a 100644
--- a/core/embed/rust/src/trezorhal/ble/mod.rs
+++ b/core/embed/rust/src/trezorhal/ble/mod.rs
@@ -18,12 +18,12 @@ const COMMAND_FAILED: Error = Error::RuntimeError(c"BLE command failed");
const WRITE_FAILED: Error = Error::RuntimeError(c"BLE write failed");
// NOTE: replace with floor_char_boundary when stable
-fn prefix_utf8_bytes(text: &str, max_len: usize) -> &[u8] {
+fn prefix_utf8_bytes(text: &str, max_len: usize) -> &str {
let mut i = text.len().min(max_len);
while !text.is_char_boundary(i) {
i -= 1;
}
- &text.as_bytes()[..i]
+ &text[..i]
}
pub fn res_to_result(res: bool) -> Result<(), Error> {
@@ -83,16 +83,18 @@ fn state() -> ffi::ble_state_t {
state
}
-pub fn pairing_mode(name: &str) -> Result<(), Error> {
- let name = prefix_utf8_bytes(name, ADV_NAME_LEN);
- let res = unsafe { ffi::ble_enter_pairing_mode(name.as_ptr(), name.len()) };
- res_to_result(res)
+pub fn pairing_mode(name: &str) -> Result<&str, Error> {
+ let adv_name = prefix_utf8_bytes(name, ADV_NAME_LEN);
+ let res = unsafe { ffi::ble_enter_pairing_mode(adv_name.as_ptr(), adv_name.len()) };
+ res_to_result(res)?;
+ Ok(adv_name)
}
-pub fn switch_on(name: &str) -> Result<(), Error> {
- set_name(name);
+pub fn switch_on(name: &str) -> Result<&str, Error> {
+ let adv_name = set_name(name);
let res = unsafe { ffi::ble_switch_on() };
- res_to_result(res)
+ res_to_result(res)?;
+ Ok(adv_name)
}
pub fn switch_off() -> Result<(), Error> {
@@ -148,9 +150,10 @@ pub fn disconnect() -> Result<(), Error> {
res_to_result(res)
}
-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 set_name(name: &str) -> &str {
+ let adv_name = prefix_utf8_bytes(name, ADV_NAME_LEN);
+ unsafe { ffi::ble_set_name(adv_name.as_ptr(), adv_name.len()) }
+ adv_name
}
pub fn set_high_speed(enable: bool) {
diff --git a/core/mocks/generated/trezorble.pyi b/core/mocks/generated/trezorble.pyi
index c91dab08..19e26a04 100644
--- a/core/mocks/generated/trezorble.pyi
+++ b/core/mocks/generated/trezorble.pyi
@@ -55,7 +55,7 @@ def start_comm():
# rust/src/trezorhal/ble/micropython.rs
-def start_advertising(whitelist: bool, name: str | None):
+def start_advertising(whitelist: bool, name: str | None) -> str:
"""
Start advertising.
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 d4f3da1a..995599ac 100644
--- a/core/src/apps/management/ble/pair_new_device.py
+++ b/core/src/apps/management/ble/pair_new_device.py
@@ -36,7 +36,7 @@ async def pair_new_device() -> None:
from trezor import TR
label = storage_device.get_label() or _default_ble_name()
- ble.start_advertising(False, label)
+ label = ble.start_advertising(False, label)
result = None
try:
code = await interact(
@@ -65,4 +65,4 @@ async def pair_new_device() -> None:
finally:
if result is not CONFIRMED:
ble.reject_pairing()
- ble.set_name(storage_device.get_label())
+ ble.set_name(label)
Why this scored 17/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.