feat(core): expose `ble_get_bond_list()` to MicroPython
What changed, and why it matters
This commit adds a new read-only function that lets the device's MicroPython code ask the Bluetooth chip for the list of currently paired/bonded devices. It only exposes information that was already stored inside the device; it does not change pairings, bypass authentication, or alter any security behavior. By itself, this is a feature addition with very low security risk.
No immediate action required. Treat as routine feature exposure. If this function is later used in UI workflows, ensure the returned bond list is handled with the same trust assumptions as other local state and that any screen displaying bonded devices does not mislead users into trusting unverified addresses.
Security signals we found
New read-only BLE API surface added to MicroPython
No input parameters accepted from callers, reducing injection/validation risk
No changes to pairing, bonding, or cryptographic code paths
Data returned is local BLE bond metadata (address and type), not secrets or keys
Evidence from the diff
The patch exposes ble_get_bond_list() from the Trezor HAL to MicroPython as trezorble.get_bonds(). It adds FFI allowlisting for BLE_MAX_BONDS, ble_get_bond_list(), and bt_le_addr_t, plus a Rust wrapper that copies the bond array into a MicroPython list of (6-byte address, address-type) tuples. The function is read-only and returns data already present in the BLE bond store. No authentication, pairing, or cryptographic operations are modified.
Changed components
core/embed/rust/src/trezorhal/ble/micropython.rscore/embed/rust/src/trezorhal/ble/mod.rscore/embed/rust/build.rscore/embed/rust/librust_qstr.hcore/mocks/generated/trezorble.pyiInspect captured patch +46 / −0
diff --git a/core/embed/rust/build.rs b/core/embed/rust/build.rs
index a66e44ac9..21f7068ae 100644
--- a/core/embed/rust/build.rs
+++ b/core/embed/rust/build.rs
@@ -410,6 +410,7 @@ fn generate_trezorhal_bindings() {
.allowlist_type("usb_event_t")
.allowlist_function("usb_get_state")
// ble
+ .allowlist_var("BLE_MAX_BONDS")
.allowlist_var("BLE_PAIRING_CODE_LEN")
.allowlist_var("BLE_RX_PACKET_SIZE")
.allowlist_var("BLE_TX_PACKET_SIZE")
@@ -422,9 +423,11 @@ fn generate_trezorhal_bindings() {
.allowlist_function("ble_read")
.allowlist_function("ble_set_name")
.allowlist_function("ble_unpair")
+ .allowlist_function("ble_get_bond_list")
.allowlist_type("ble_command_t")
.allowlist_type("ble_state_t")
.allowlist_type("ble_event_t")
+ .allowlist_type("bt_le_addr_t")
// touch
.allowlist_function("touch_get_event")
// button
diff --git a/core/embed/rust/librust_qstr.h b/core/embed/rust/librust_qstr.h
index 0b595f3bb..0fd3c2b02 100644
--- a/core/embed/rust/librust_qstr.h
+++ b/core/embed/rust/librust_qstr.h
@@ -327,6 +327,7 @@ static void _librust_qstrs(void) {
MP_QSTR_flow_get_address;
MP_QSTR_flow_get_pubkey;
MP_QSTR_get;
+ MP_QSTR_get_bonds;
MP_QSTR_get_language;
MP_QSTR_get_transition_out;
MP_QSTR_haptic_feedback;
diff --git a/core/embed/rust/src/trezorhal/ble/micropython.rs b/core/embed/rust/src/trezorhal/ble/micropython.rs
index 5ed48a016..2989cc8a0 100644
--- a/core/embed/rust/src/trezorhal/ble/micropython.rs
+++ b/core/embed/rust/src/trezorhal/ble/micropython.rs
@@ -159,6 +159,21 @@ extern "C" fn py_connected_addr() -> Obj {
unsafe { util::try_or_raise(block) }
}
+extern "C" fn py_get_bonds() -> Obj {
+ let block = || {
+ get_bonds(|bonds| {
+ let mut result = List::with_capacity(bonds.len())?;
+ for bond in bonds {
+ let addr = Obj::try_from(&bond.addr[..])?;
+ let addr_type = Obj::from(bond.type_);
+ result.append(Obj::try_from((addr, addr_type))?)?;
+ }
+ Ok(result.leak().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
}
@@ -334,6 +349,14 @@ pub static mp_module_trezorble: Module = obj_module! {
/// """
Qstr::MP_QSTR_connection_flags => obj_fn_0!(py_connection_flags).as_obj(),
+ /// def get_bonds() -> list[tuple[bytes, int], ...]:
+ /// """
+ /// Returns a list of (addr_bytes, addr_type) tuples, representing the current bonds.
+ /// 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_get_bonds => obj_fn_0!(py_get_bonds).as_obj(),
+
/// def connected_addr() -> tuple[bytes, int] | None:
/// """
/// If connected, returns a tuple (addr_bytes, addr_type), otherwise None.
diff --git a/core/embed/rust/src/trezorhal/ble/mod.rs b/core/embed/rust/src/trezorhal/ble/mod.rs
index acea77b73..6d2897790 100644
--- a/core/embed/rust/src/trezorhal/ble/mod.rs
+++ b/core/embed/rust/src/trezorhal/ble/mod.rs
@@ -9,6 +9,7 @@ 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;
+pub const BLE_MAX_BONDS: usize = ffi::BLE_MAX_BONDS as usize;
pub const PAIRING_CODE_LEN: usize = ffi::BLE_PAIRING_CODE_LEN as usize;
pub const RX_PACKET_SIZE: usize = ffi::BLE_RX_PACKET_SIZE as usize;
pub const TX_PACKET_SIZE: usize = ffi::BLE_TX_PACKET_SIZE as usize;
@@ -194,6 +195,15 @@ pub fn connected_addr() -> bt_le_addr_t {
state().connected_addr
}
+pub fn get_bonds<F, T>(f: F) -> T
+where
+ F: Fn(&[bt_le_addr_t]) -> T,
+{
+ let mut bonds = [bt_le_addr_t::zero(); BLE_MAX_BONDS];
+ let size = unsafe { ffi::ble_get_bond_list(bonds.as_mut_ptr(), bonds.len()) };
+ f(&bonds[..size.into()])
+}
+
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 9642b7337..136bb0adf 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 get_bonds() -> list[tuple[bytes, int], ...]:
+ """
+ Returns a list of (addr_bytes, addr_type) tuples, representing the current bonds.
+ 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 connected_addr() -> tuple[bytes, int] | None:
"""
Why this scored 19/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.