feat(core): expose currently connected BLE host address to application
What changed, and why it matters
This commit is a straightforward feature addition: it exposes the Bluetooth Low Energy (BLE) address of the currently connected host (e.g., a phone or computer) to the Trezor device's application code. It does not change security policies, add authentication bypasses, or fix any vulnerability. It simply makes an already-recorded internal address available through a new API function.
No security action required. Review as normal feature code.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch refactors how the BLE connected address is stored, replacing separate connected_addr[6] and connected_addr_type fields with a single bt_le_addr_t struct. It then adds a new MicroPython/Rust binding trezorble.connected_addr() that returns (addr_bytes, addr_type) when connected, or None otherwise. The address data was already maintained by the BLE driver; this change only exposes it to higher-level application code. No security boundary is crossed and no trust decision is altered.
Changed components
core/embed/io/ble/stm32/ble.ccore/embed/rust/src/trezorhal/ble/micropython.rscore/embed/rust/src/trezorhal/ble/mod.rscore/mocks/generated/trezorble.pyiInspect captured patch +76 / −27
diff --git a/core/embed/io/ble/inc/io/ble.h b/core/embed/io/ble/inc/io/ble.h
index 2a51d3cf5..1c55fa48e 100644
--- a/core/embed/io/ble/inc/io/ble.h
+++ b/core/embed/io/ble/inc/io/ble.h
@@ -89,8 +89,7 @@ typedef struct {
bool reboot_on_resume;
uint8_t peer_count;
ble_mode_t mode_requested;
- uint8_t connected_addr[6];
- uint8_t connected_addr_type;
+ bt_le_addr_t connected_addr;
ble_adv_start_cmd_data_t adv_data;
} ble_wakeup_params_t;
@@ -119,6 +118,7 @@ typedef struct {
bool pairing_requested;
bool state_known;
uint8_t peer_count;
+ bt_le_addr_t connected_addr;
} ble_state_t;
// Initializes the BLE module
diff --git a/core/embed/io/ble/stm32/ble.c b/core/embed/io/ble/stm32/ble.c
index 8fdf974e5..222d72005 100644
--- a/core/embed/io/ble/stm32/ble.c
+++ b/core/embed/io/ble/stm32/ble.c
@@ -51,8 +51,7 @@ typedef struct {
ble_mode_t mode_requested;
ble_mode_t mode_current;
bool connected;
- uint8_t connected_addr[6];
- uint8_t connected_addr_type;
+ bt_le_addr_t connected_addr;
uint8_t peer_count;
bool initialized;
bool status_valid;
@@ -257,21 +256,22 @@ static void ble_process_rx_msg_status(const uint8_t *data, uint32_t len) {
}
}
- memcpy(drv->connected_addr, msg.connected_addr,
- sizeof(drv->connected_addr));
- drv->connected_addr_type = msg.connected_addr_type;
+ memcpy(drv->connected_addr.addr, msg.connected_addr,
+ sizeof(drv->connected_addr.addr));
+ drv->connected_addr.type = msg.connected_addr_type;
drv->connected = msg.connected;
} else {
- if (memcmp(drv->connected_addr, msg.connected_addr,
- sizeof(drv->connected_addr)) != 0 ||
- drv->connected_addr_type != msg.connected_addr_type) {
+ if (memcmp(drv->connected_addr.addr, msg.connected_addr,
+ sizeof(drv->connected_addr.addr)) != 0 ||
+ drv->connected_addr.type != msg.connected_addr_type) {
// address changed
- memcpy(drv->connected_addr, msg.connected_addr,
- sizeof(drv->connected_addr));
- drv->connected_addr_type = msg.connected_addr_type;
+ memcpy(drv->connected_addr.addr, msg.connected_addr,
+ sizeof(drv->connected_addr.addr));
+ drv->connected_addr.type = msg.connected_addr_type;
ble_event_t event = {.type = BLE_CONNECTION_CHANGED};
- memcpy(event.data, drv->connected_addr, sizeof(drv->connected_addr));
+ memcpy(event.data, drv->connected_addr.addr,
+ sizeof(drv->connected_addr.addr));
tsqueue_enqueue(&drv->event_queue, (uint8_t *)&event, sizeof(event),
NULL);
}
@@ -452,9 +452,9 @@ static void ble_process_rx_msg(const uint8_t *data, uint32_t len) {
}
static bool ble_connected_add_match(ble_driver_t *drv, const uint8_t *addr) {
- return (addr[0] == drv->connected_addr_type &&
- memcmp(&addr[1], drv->connected_addr, sizeof(drv->connected_addr)) ==
- 0);
+ return (addr[0] == drv->connected_addr.type &&
+ memcmp(&addr[1], drv->connected_addr.addr,
+ sizeof(drv->connected_addr.addr)) == 0);
}
static void ble_process_data(const uint8_t *data, uint32_t len) {
@@ -475,11 +475,13 @@ static void ble_process_data(const uint8_t *data, uint32_t len) {
if (!ble_connected_add_match(drv, data)) {
// inconsistent address of a connected device
- drv->connected_addr_type = data[0];
- memcpy(drv->connected_addr, &data[1], sizeof(drv->connected_addr));
+ drv->connected_addr.type = data[0];
+ memcpy(drv->connected_addr.addr, &data[1],
+ sizeof(drv->connected_addr.addr));
ble_event_t event = {.type = BLE_CONNECTION_CHANGED};
- memcpy(event.data, drv->connected_addr, sizeof(drv->connected_addr));
+ memcpy(event.data, drv->connected_addr.addr,
+ sizeof(drv->connected_addr.addr));
tsqueue_enqueue(&drv->event_queue, (uint8_t *)&event, sizeof(event), NULL);
}
@@ -652,8 +654,7 @@ void ble_suspend(ble_wakeup_params_t *wakeup_params) {
nrf_deinit();
} else {
irq_key_t key = irq_lock();
- wakeup_params->connected_addr_type = drv->connected_addr_type;
- memcpy(wakeup_params->connected_addr, drv->connected_addr,
+ memcpy(&wakeup_params->connected_addr, &drv->connected_addr,
sizeof(drv->connected_addr));
irq_unlock(key);
nrf_suspend();
@@ -678,9 +679,9 @@ bool ble_resume(const ble_wakeup_params_t *wakeup_params) {
irq_key_t key = irq_lock();
- drv->connected_addr_type = wakeup_params->connected_addr_type;
drv->peer_count = wakeup_params->peer_count;
- memcpy(drv->connected_addr, wakeup_params->connected_addr,
+
+ memcpy(&drv->connected_addr, &wakeup_params->connected_addr,
sizeof(drv->connected_addr));
memcpy(&drv->adv_cmd, &wakeup_params->adv_data, sizeof(drv->adv_cmd));
drv->mode_requested = wakeup_params->mode_requested;
@@ -775,8 +776,9 @@ bool ble_write(const uint8_t *data, uint16_t len) {
}
uint8_t tx_buf[BLE_DATA_SIZE];
- tx_buf[0] = drv->connected_addr_type;
- memcpy(&tx_buf[1], drv->connected_addr, sizeof(drv->connected_addr));
+ tx_buf[0] = drv->connected_addr.type;
+ memcpy(&tx_buf[1], drv->connected_addr.addr,
+ sizeof(drv->connected_addr.addr));
memcpy(&tx_buf[BLE_DATA_HEADER_SIZE], data,
MIN(len, BLE_DATA_SIZE - BLE_DATA_HEADER_SIZE));
@@ -1014,6 +1016,8 @@ void ble_get_state(ble_state_t *state) {
state->connectable = drv->mode_current == BLE_MODE_CONNECTABLE;
state->pairing_requested = drv->pairing_requested;
state->state_known = drv->status_valid;
+ memcpy(&state->connected_addr, &drv->connected_addr,
+ sizeof(drv->connected_addr));
irq_unlock(key);
}
diff --git a/core/embed/rust/librust_qstr.h b/core/embed/rust/librust_qstr.h
index 2b92a30c7..45b5266b5 100644
--- a/core/embed/rust/librust_qstr.h
+++ b/core/embed/rust/librust_qstr.h
@@ -273,6 +273,7 @@ static void _librust_qstrs(void) {
MP_QSTR_confirm_value;
MP_QSTR_confirm_value_intro;
MP_QSTR_confirm_with_info;
+ MP_QSTR_connected_addr;
MP_QSTR_connected_idx;
MP_QSTR_connection_flags;
MP_QSTR_continue_recovery_homepage;
diff --git a/core/embed/rust/src/trezorhal/ble/micropython.rs b/core/embed/rust/src/trezorhal/ble/micropython.rs
index 6401b2c7e..5ed48a016 100644
--- a/core/embed/rust/src/trezorhal/ble/micropython.rs
+++ b/core/embed/rust/src/trezorhal/ble/micropython.rs
@@ -142,6 +142,23 @@ extern "C" fn py_connection_flags() -> Obj {
unsafe { util::try_or_raise(block) }
}
+// Return (addr_bytes, addr_type) if connected, otherwise None.
+// addr_bytes: bytes of length 6
+// addr_type: integer as provided by bt_le_addr_t (e.g., 0=public, 1=random)
+extern "C" fn py_connected_addr() -> Obj {
+ let block = || {
+ if !is_connected() {
+ return Ok(Obj::const_none());
+ }
+ let a = connected_addr();
+ let addr_obj = Obj::try_from(&a.addr[..])?;
+ let type_obj = Obj::from(a.type_);
+ (addr_obj, type_obj).try_into()
+ };
+
+ unsafe { util::try_or_raise(block) }
+}
+
extern "C" fn py_iface_num(_self: Obj) -> Obj {
Obj::small_int(8) // FIXME SYSHANDLE_BLE_IFACE_0
}
@@ -317,6 +334,14 @@ pub static mp_module_trezorble: Module = obj_module! {
/// """
Qstr::MP_QSTR_connection_flags => obj_fn_0!(py_connection_flags).as_obj(),
+ /// def connected_addr() -> tuple[bytes, int] | None:
+ /// """
+ /// If connected, returns a tuple (addr_bytes, addr_type), otherwise None.
+ /// addr_bytes: bytes of length 6
+ /// addr_type: integer as provided by bt_le_addr_t (e.g., 0=public, 1=random)
+ /// """
+ Qstr::MP_QSTR_connected_addr => obj_fn_0!(py_connected_addr).as_obj(),
+
/// def allow_pairing(code: int):
/// """
/// Accept BLE pairing request. Code must match the one received with
diff --git a/core/embed/rust/src/trezorhal/ble/mod.rs b/core/embed/rust/src/trezorhal/ble/mod.rs
index 57f333f7c..b2035cdfc 100644
--- a/core/embed/rust/src/trezorhal/ble/mod.rs
+++ b/core/embed/rust/src/trezorhal/ble/mod.rs
@@ -5,7 +5,7 @@ mod micropython;
use crate::ui::event::BLEEvent;
use super::ffi;
-use crate::error::Error;
+use crate::{error::Error, trezorhal::ffi::bt_le_addr_t};
use core::{mem::size_of, ptr};
pub const ADV_NAME_LEN: usize = ffi::BLE_ADV_NAME_LEN as usize;
@@ -54,6 +54,12 @@ fn state() -> ffi::ble_state_t {
pairing: false,
pairing_requested: false,
state_known: false,
+ connected_addr: {
+ bt_le_addr_t {
+ type_: 0,
+ addr: [0; 6],
+ }
+ },
};
unsafe { ffi::ble_get_state(&mut state as _) };
state
@@ -180,6 +186,10 @@ pub fn is_pairing_requested() -> bool {
state().pairing_requested
}
+pub fn connected_addr() -> bt_le_addr_t {
+ state().connected_addr
+}
+
pub fn write(bytes: &[u8]) -> Result<(), Error> {
let len = bytes.len() as u16;
let success = unsafe { ffi::ble_write(bytes.as_ptr(), len) };
diff --git a/core/mocks/generated/trezorble.pyi b/core/mocks/generated/trezorble.pyi
index 89b442dbe..9642b7337 100644
--- a/core/mocks/generated/trezorble.pyi
+++ b/core/mocks/generated/trezorble.pyi
@@ -125,6 +125,15 @@ def connection_flags() -> list[str]:
"""
+# rust/src/trezorhal/ble/micropython.rs
+def connected_addr() -> tuple[bytes, int] | None:
+ """
+ If connected, returns a tuple (addr_bytes, addr_type), otherwise None.
+ addr_bytes: bytes of length 6
+ addr_type: integer as provided by bt_le_addr_t (e.g., 0=public, 1=random)
+ """
+
+
# rust/src/trezorhal/ble/micropython.rs
def allow_pairing(code: int):
"""
Why this scored 13/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.