refactor(core): refactor io/ble interface
What changed, and why it matters
This commit is a code cleanup that rewrites how the Trezor firmware talks to its Bluetooth chip. Instead of sending one big generic 'command' packet that could describe many different actions, the code now uses a separate, specific function for each Bluetooth action (such as turn on, pair, disconnect, erase bonds). The change also tightens the security boundary between the app and the kernel by validating the pairing name and pairing code directly, rather than trusting a caller-built command structure. There is no direct evidence in the commit that this fixes an active security bug, but the old design made it easier for a caller to request unintended operations by crafting command data, and the new design removes that entire class of mistakes.
Treat this as a hardening/refactoring change rather than an urgent vulnerability fix. Reviewers should verify that all former BLE_SWITCH_ON callers now also call ble_set_name where needed, that ble_enter_pairing_mode rejects names longer than BLE_ADV_NAME_LEN, and that the new syscall verifiers correctly handle name_len == 0 and pairing_code pointer validation. No immediate user action is required.
Security signals we found
Removal of a generic command-dispatch union/enum that mixed control flow with caller-supplied data
Introduction of typed BLE API functions with explicit, per-call parameter validation
Syscall verifier now checks pairing name length and pairing code buffer directly rather than a caller-built command struct
Rust bindings no longer construct raw ble_command_t objects or compute data_len
No changelog entry and no CVE/advisory text in the commit message
Evidence from the diff
The patch refactors the BLE HAL from a single ble_issue_command(ble_command_t *) dispatch interface into a set of typed functions: ble_switch_on/off, ble_enter_pairing_mode(name, len), ble_disconnect, ble_erase_bonds, ble_allow_pairing(code), ble_reject_pairing, ble_keep_connection, and ble_set_static_mac. The ble_command_type_t enum, ble_command_t struct, and ble_command_data_t union are removed. Callers in bootloader, prodtest, and Rust firmware are updated to use the new API. The syscall layer is updated so SYSCALL_BLE_ISSUE_COMMAND is replaced by individual syscall numbers, and the kernel-side verifiers now probe the pairing name and pairing-code buffers directly instead of probing a whole caller-supplied command object. This reduces the trusted input surface and prevents a caller from smuggling mismatched cmd_type/data_len/data combinations across the syscall boundary.
Changed components
core/embed/io/ble HAL (stm32 and unix implementations)core/embed/sys/syscall BLE syscall numbers, stubs, dispatch, and verifierscore/embed/projects/bootloader BLE wire interface and workflowscore/embed/projects/prodtest BLE commandscore/embed/rust/trezorhal BLE moduleInspect captured patch +380 / −270
diff --git a/core/embed/io/ble/inc/io/ble.h b/core/embed/io/ble/inc/io/ble.h
index 1b18053d..49a13a6c 100644
--- a/core/embed/io/ble/inc/io/ble.h
+++ b/core/embed/io/ble/inc/io/ble.h
@@ -33,18 +33,6 @@
#define BLE_MAX_BONDS 8
-typedef enum {
- BLE_SWITCH_OFF = 0, // Turn off BLE advertising, disconnect
- BLE_SWITCH_ON = 1, // Turn on BLE advertising
- BLE_PAIRING_MODE = 2, // Enter pairing mode
- BLE_DISCONNECT = 3, // Disconnect from the connected device
- BLE_ERASE_BONDS = 4, // Erase all bonding information
- BLE_ALLOW_PAIRING = 5, // Accept pairing request
- BLE_REJECT_PAIRING = 6, // Reject pairing request
- BLE_KEEP_CONNECTION =
- 7, // Keep connection to the connected device, but do not advertise
-} ble_command_type_t;
-
typedef enum {
BLE_MODE_OFF,
BLE_MODE_KEEP_CONNECTION,
@@ -76,23 +64,6 @@ typedef struct {
uint8_t addr[6];
} bt_le_addr_t;
-typedef struct {
- uint8_t name[BLE_ADV_NAME_LEN];
- bool static_mac;
-} ble_adv_start_cmd_data_t;
-
-typedef union {
- uint8_t raw[32];
- ble_adv_start_cmd_data_t adv_start;
- uint8_t pairing_code[BLE_PAIRING_CODE_LEN];
-} ble_command_data_t;
-
-typedef struct {
- ble_command_type_t cmd_type;
- uint8_t data_len;
- ble_command_data_t data;
-} ble_command_t;
-
typedef struct {
bool accept_msgs;
bool reboot_on_resume;
@@ -100,9 +71,10 @@ typedef struct {
uint8_t peer_count;
ble_mode_t mode_requested;
bt_le_addr_t connected_addr;
- ble_adv_start_cmd_data_t adv_data;
bool restart_adv_on_disconnect;
bool next_adv_with_disconnect;
+ uint8_t name[BLE_ADV_NAME_LEN];
+ bool static_mac;
} ble_wakeup_params_t;
typedef enum {
@@ -164,12 +136,50 @@ void ble_start(void);
// Flushes any queued messages
void ble_stop(void);
-// Issues a command to the BLE module
+// Turns off BLE advertising and disconnects from devices
+//
+// Returns `true` if the command was successfully executed.
+bool ble_switch_off(void);
+
+// Turns on BLE advertising
//
-// Sends a specific command to the BLE module for execution.
+// Returns `true` if the command was successfully executed.
+bool ble_switch_on(void);
+
+// Enters pairing mode
+//
+// Returns `true` if the command was successfully executed.
+bool ble_enter_pairing_mode(const uint8_t *name, size_t name_len);
+
+// Disconnects from the currently connected device
+//
+// Returns `true` if the command was successfully executed.
+bool ble_disconnect(void);
+
+// Erases all bonding information
+//
+// Returns `true` if the command was successfully executed.
+bool ble_erase_bonds(void);
+
+// Accepts a pairing request with the provided pairing code
+//
+// Returns `true` if the command was successfully executed.
+bool ble_allow_pairing(const uint8_t *pairing_code);
+
+// Rejects a pairing request
+//
+// Returns `true` if the command was successfully executed.
+bool ble_reject_pairing(void);
+
+// Keeps connection to the connected device but stops advertising
+//
+// Returns `true` if the command was successfully executed.
+bool ble_keep_connection(void);
+
+// Set static ble MAC
//
-// Returns `true` if the command was successfully issued.
-bool ble_issue_command(ble_command_t *command);
+// Returns `true` if the command was successfully executed.
+bool ble_set_static_mac(bool static_mac);
// Sets the BLE advertising name, but does not affect advertising
void ble_set_name(const uint8_t *name, size_t len);
diff --git a/core/embed/io/ble/stm32/ble.c b/core/embed/io/ble/stm32/ble.c
index 7578096d..3df7d326 100644
--- a/core/embed/io/ble/stm32/ble.c
+++ b/core/embed/io/ble/stm32/ble.c
@@ -40,8 +40,6 @@
#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
@@ -81,7 +79,8 @@ typedef struct {
tsqueue_entry_t ts_queue_entries[TX_QUEUE_LEN];
tsqueue_t tx_queue;
- ble_adv_start_cmd_data_t adv_cmd;
+ uint8_t adv_name[BLE_ADV_NAME_LEN];
+ bool static_mac;
bt_le_addr_t mac;
bool mac_ready;
bool high_speed;
@@ -134,13 +133,13 @@ static bool ble_send_advertising_on(ble_driver_t *drv, bool whitelist) {
.flags.user_disconnect = drv->next_adv_with_disconnect ? 1 : 0,
.flags.reserved = 0,
.color = props.color,
- .static_addr = drv->adv_cmd.static_mac,
+ .static_addr = drv->static_mac,
.device_code = MODEL_BLE_CODE,
};
drv->next_adv_with_disconnect = false;
- memcpy(data.name, drv->adv_cmd.name, BLE_ADV_NAME_LEN);
+ memcpy(data.name, drv->adv_name, BLE_ADV_NAME_LEN);
return nrf_send_msg(NRF_SERVICE_BLE_MANAGER, (uint8_t *)&data, sizeof(data),
NULL, NULL) >= 0;
@@ -717,8 +716,8 @@ void ble_suspend(ble_wakeup_params_t *wakeup_params) {
wakeup_params->high_speed = drv->high_speed;
wakeup_params->next_adv_with_disconnect = drv->next_adv_with_disconnect;
wakeup_params->restart_adv_on_disconnect = drv->restart_adv_on_disconnect;
- memcpy(&wakeup_params->adv_data, &drv->adv_cmd, sizeof(drv->adv_cmd));
-
+ wakeup_params->static_mac = drv->static_mac;
+ memcpy(wakeup_params->name, drv->adv_name, sizeof(drv->adv_name));
ble_deinit_common(drv);
if (!connected) {
@@ -755,10 +754,11 @@ bool ble_resume(const ble_wakeup_params_t *wakeup_params) {
drv->peer_count = wakeup_params->peer_count;
drv->high_speed = wakeup_params->high_speed;
+ drv->static_mac = wakeup_params->static_mac;
memcpy(&drv->connected_addr, &wakeup_params->connected_addr,
sizeof(drv->connected_addr));
- memcpy(&drv->adv_cmd, &wakeup_params->adv_data, sizeof(drv->adv_cmd));
+ memcpy(drv->adv_name, wakeup_params->name, sizeof(drv->adv_name));
drv->mode_requested = wakeup_params->mode_requested;
drv->next_adv_with_disconnect = wakeup_params->next_adv_with_disconnect;
drv->restart_adv_on_disconnect = wakeup_params->restart_adv_on_disconnect;
@@ -920,7 +920,7 @@ uint32_t ble_read(uint8_t *data, uint16_t max_len) {
return BLE_RX_PACKET_SIZE;
}
-bool ble_issue_command(ble_command_t *command) {
+bool ble_switch_off(void) {
ble_driver_t *drv = &g_ble_driver;
if (!drv->initialized) {
@@ -929,62 +929,15 @@ bool ble_issue_command(ble_command_t *command) {
irq_key_t key = irq_lock();
- bool result = false;
-
- switch (command->cmd_type) {
- case BLE_SWITCH_OFF:
- drv->restart_adv_on_disconnect = false;
- drv->mode_requested = BLE_MODE_OFF;
- result = true;
- break;
- case BLE_SWITCH_ON:
- drv->restart_adv_on_disconnect = true;
- memcpy(&drv->adv_cmd, &command->data.adv_start, sizeof(drv->adv_cmd));
- if (drv->connected) {
- drv->mode_requested = BLE_MODE_KEEP_CONNECTION;
- } else {
- drv->mode_requested = BLE_MODE_CONNECTABLE;
- }
- result = true;
- break;
- case BLE_PAIRING_MODE:
- drv->restart_adv_on_disconnect = true;
- irq_unlock(key);
- 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:
- result = ble_send_erase_bonds(drv);
- break;
- case BLE_ALLOW_PAIRING:
- result = ble_send_pairing_accept(drv, command->data.pairing_code);
- break;
- case BLE_REJECT_PAIRING:
- result = ble_send_pairing_reject(drv);
- break;
- case BLE_KEEP_CONNECTION:
- drv->restart_adv_on_disconnect = false;
- if (drv->connected) {
- drv->mode_requested = BLE_MODE_KEEP_CONNECTION;
- } else {
- drv->mode_requested = BLE_MODE_OFF;
- }
- break;
- default:
- break;
- }
+ drv->restart_adv_on_disconnect = false;
+ drv->mode_requested = BLE_MODE_OFF;
irq_unlock(key);
- return result;
+ return true;
}
-bool ble_get_event(ble_event_t *event) {
+bool ble_switch_on(void) {
ble_driver_t *drv = &g_ble_driver;
if (!drv->initialized) {
@@ -993,29 +946,35 @@ bool ble_get_event(ble_event_t *event) {
irq_key_t key = irq_lock();
- bool result = tsqueue_dequeue(&drv->event_queue, (uint8_t *)event,
- sizeof(*event), NULL, NULL);
+ drv->restart_adv_on_disconnect = true;
+ if (drv->connected) {
+ drv->mode_requested = BLE_MODE_KEEP_CONNECTION;
+ } else {
+ drv->mode_requested = BLE_MODE_CONNECTABLE;
+ }
irq_unlock(key);
- return result;
+ return true;
}
-static void ble_event_flush(void) {
+bool ble_set_static_mac(bool static_mac) {
ble_driver_t *drv = &g_ble_driver;
if (!drv->initialized) {
- return;
+ return false;
}
irq_key_t key = irq_lock();
- tsqueue_reset(&drv->event_queue);
+ drv->static_mac = static_mac;
irq_unlock(key);
+
+ return true;
}
-void ble_set_name(const uint8_t *name, size_t len) {
+static void ble_event_flush(void) {
ble_driver_t *drv = &g_ble_driver;
if (!drv->initialized) {
@@ -1024,31 +983,33 @@ void ble_set_name(const uint8_t *name, size_t len) {
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);
- }
+ tsqueue_reset(&drv->event_queue);
irq_unlock(key);
}
-static bool ble_start_pairing(ble_command_t *command) {
+bool ble_enter_pairing_mode(const uint8_t *name, size_t name_len) {
+ if (name == NULL || name_len == 0 || name_len > BLE_ADV_NAME_LEN) {
+ return false;
+ }
+
ble_driver_t *drv = &g_ble_driver;
if (!drv->initialized) {
return false;
}
- uint16_t retry_cnt = 0;
- irq_key_t key;
+ irq_key_t key = irq_lock();
+ memset(drv->adv_name, 0, sizeof(drv->adv_name));
+ memcpy(drv->adv_name, name, name_len);
+ drv->restart_adv_on_disconnect = true;
bool connected = drv->connected;
+
+ irq_unlock(key);
+
+ uint16_t retry_cnt = 0;
+
while (connected) {
retry_cnt++;
if (retry_cnt > 10) {
@@ -1069,7 +1030,6 @@ static bool ble_start_pairing(ble_command_t *command) {
key = irq_lock();
- memcpy(&drv->adv_cmd, &command->data.adv_start, sizeof(drv->adv_cmd));
drv->mode_requested = BLE_MODE_PAIRING;
irq_unlock(key);
@@ -1077,6 +1037,137 @@ static bool ble_start_pairing(ble_command_t *command) {
return true;
}
+bool ble_disconnect(void) {
+ ble_driver_t *drv = &g_ble_driver;
+
+ if (!drv->initialized) {
+ return false;
+ }
+
+ irq_key_t key = irq_lock();
+
+ if (drv->connected && drv->restart_adv_on_disconnect) {
+ drv->next_adv_with_disconnect = true;
+ }
+
+ bool result = ble_send_disconnect(drv);
+
+ irq_unlock(key);
+
+ return result;
+}
+
+bool ble_erase_bonds(void) {
+ ble_driver_t *drv = &g_ble_driver;
+
+ if (!drv->initialized) {
+ return false;
+ }
+
+ irq_key_t key = irq_lock();
+ bool result = ble_send_erase_bonds(drv);
+ irq_unlock(key);
+
+ return result;
+}
+
+bool ble_allow_pairing(const uint8_t *pairing_code) {
+ if (pairing_code == NULL) {
+ return false;
+ }
+
+ ble_driver_t *drv = &g_ble_driver;
+
+ if (!drv->initialized) {
+ return false;
+ }
+
+ irq_key_t key = irq_lock();
+ bool result = ble_send_pairing_accept(drv, (uint8_t *)pairing_code);
+ irq_unlock(key);
+
+ return result;
+}
+
+bool ble_reject_pairing(void) {
+ ble_driver_t *drv = &g_ble_driver;
+
+ if (!drv->initialized) {
+ return false;
+ }
+
+ irq_key_t key = irq_lock();
+ bool result = ble_send_pairing_reject(drv);
+ irq_unlock(key);
+
+ return result;
+}
+
+bool ble_keep_connection(void) {
+ ble_driver_t *drv = &g_ble_driver;
+
+ if (!drv->initialized) {
+ return false;
+ }
+
+ irq_key_t key = irq_lock();
+
+ drv->restart_adv_on_disconnect = false;
+ if (drv->connected) {
+ drv->mode_requested = BLE_MODE_KEEP_CONNECTION;
+ } else {
+ drv->mode_requested = BLE_MODE_OFF;
+ }
+
+ irq_unlock(key);
+
+ return true;
+}
+
+void ble_set_name(const uint8_t *name, size_t len) {
+ ble_driver_t *drv = &g_ble_driver;
+
+ if (!drv->initialized) {
+ return;
+ }
+
+ if (len > BLE_ADV_NAME_LEN) {
+ return;
+ }
+
+ irq_key_t key = irq_lock();
+
+ memset(drv->adv_name, 0, sizeof(drv->adv_name));
+ memcpy(drv->adv_name, name, MIN(len, sizeof(drv->adv_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);
+}
+
+bool ble_get_event(ble_event_t *event) {
+ ble_driver_t *drv = &g_ble_driver;
+
+ if (!drv->initialized) {
+ return false;
+ }
+
+ irq_key_t key = irq_lock();
+
+ bool result = tsqueue_dequeue(&drv->event_queue, (uint8_t *)event,
+ sizeof(*event), NULL, NULL);
+
+ irq_unlock(key);
+
+ return result;
+}
+
void ble_get_state(ble_state_t *state) {
const ble_driver_t *drv = &g_ble_driver;
@@ -1162,7 +1253,7 @@ uint8_t ble_get_bond_list(bt_le_addr_t *bonds, size_t count) {
void ble_get_advertising_name(char *name, size_t max_len) {
ble_driver_t *drv = &g_ble_driver;
- if (max_len < sizeof(drv->adv_cmd.name)) {
+ if (max_len < sizeof(drv->adv_name)) {
memset(name, 0, max_len);
return;
}
@@ -1172,7 +1263,7 @@ void ble_get_advertising_name(char *name, size_t max_len) {
return;
}
- memcpy(name, drv->adv_cmd.name, sizeof(drv->adv_cmd.name));
+ memcpy(name, drv->adv_name, sizeof(drv->adv_name));
}
bool ble_unpair(const bt_le_addr_t *addr) {
diff --git a/core/embed/io/ble/unix/ble.c b/core/embed/io/ble/unix/ble.c
index 257fa71e..69acdebe 100644
--- a/core/embed/io/ble/unix/ble.c
+++ b/core/embed/io/ble/unix/ble.c
@@ -9,7 +9,23 @@ void ble_start(void) {}
void ble_stop(void) {}
-bool ble_issue_command(ble_command_t *command) { return true; }
+bool ble_switch_off(void) { return true; }
+
+bool ble_switch_on(void) { return true; }
+
+bool ble_enter_pairing_mode(const uint8_t *name, size_t name_len) {
+ return true;
+}
+
+bool ble_disconnect(void) { return true; }
+
+bool ble_erase_bonds(void) { return true; }
+
+bool ble_allow_pairing(const uint8_t *pairing_code) { return true; }
+
+bool ble_reject_pairing(void) { return true; }
+
+bool ble_keep_connection(void) { return true; }
void ble_set_name(const uint8_t *name, size_t len) {}
diff --git a/core/embed/projects/bootloader/main.c b/core/embed/projects/bootloader/main.c
index 33b1a998..a2fb4203 100644
--- a/core/embed/projects/bootloader/main.c
+++ b/core/embed/projects/bootloader/main.c
@@ -188,10 +188,7 @@ static secbool boot_sequence(void) {
}
} while (!ticks_expired(timeout));
- ble_command_t stop_cmd = {
- .data_len = BLE_SWITCH_OFF,
- };
- ble_issue_command(&stop_cmd);
+ ble_switch_off();
#endif
}
diff --git a/core/embed/projects/bootloader/wire/wire_iface_ble.c b/core/embed/projects/bootloader/wire/wire_iface_ble.c
index 7b7618bc..4cdeadcd 100644
--- a/core/embed/projects/bootloader/wire/wire_iface_ble.c
+++ b/core/embed/projects/bootloader/wire/wire_iface_ble.c
@@ -113,15 +113,8 @@ wire_iface_t* ble_iface_init(void) {
if (!state.connectable && !state.pairing) {
if (state.peer_count > 0) {
- ble_command_t cmd = {
- .cmd_type = BLE_SWITCH_ON,
- .data = {.adv_start =
- {
- .name = MODEL_FULL_NAME,
- .static_mac = false,
- }},
- };
- ble_issue_command(&cmd);
+ ble_set_name((const uint8_t*)MODEL_FULL_NAME, sizeof(MODEL_FULL_NAME));
+ ble_switch_on();
}
}
@@ -137,11 +130,7 @@ void ble_iface_deinit(void) {
return;
}
- ble_command_t cmd = {
- .cmd_type = BLE_KEEP_CONNECTION,
- };
- ble_issue_command(&cmd);
-
+ ble_keep_connection();
ble_stop();
memset(iface, 0, sizeof(wire_iface_t));
@@ -150,23 +139,15 @@ 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_reject_pairing();
ble_set_name((const uint8_t*)MODEL_FULL_NAME, sizeof(MODEL_FULL_NAME));
ble_get_state(&state);
if (state.peer_count > 0) {
- ble_command_t cmd = {.cmd_type = BLE_SWITCH_ON};
- memcpy(cmd.data.adv_start.name, MODEL_FULL_NAME,
- MIN(sizeof(MODEL_FULL_NAME), BLE_ADV_NAME_LEN));
- ble_issue_command(&cmd);
+ ble_switch_on();
} else {
- ble_command_t cmd = {.cmd_type = BLE_SWITCH_OFF};
- ble_issue_command(&cmd);
+ ble_switch_off();
}
}
@@ -187,16 +168,8 @@ bool ble_iface_start_pairing(void) {
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());
-
- ble_command_t cmd = {
- .cmd_type = BLE_PAIRING_MODE,
- .data = {.adv_start =
- {
- .static_mac = false,
- }},
- };
- memcpy(cmd.data.adv_start.name, adv_name, BLE_ADV_NAME_LEN);
- if (!ble_issue_command(&cmd)) {
+ if (!ble_enter_pairing_mode((const uint8_t*)adv_name,
+ strnlen(adv_name, BLE_ADV_NAME_LEN))) {
return false;
}
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 b6c7b00d..e1dec1f6 100644
--- a/core/embed/projects/bootloader/workflow/wf_ble_pairing_request.c
+++ b/core/embed/projects/bootloader/workflow/wf_ble_pairing_request.c
@@ -86,12 +86,7 @@ workflow_result_t workflow_ble_pairing_request(const fw_info_t *fw) {
return WF_OK_PAIRING_FAILED;
}
- ble_command_t cmd = {
- .cmd_type = BLE_ALLOW_PAIRING,
- .data_len = sizeof(pairing_code),
- };
- memcpy(cmd.data.raw, pairing_code, sizeof(pairing_code));
- ble_issue_command(&cmd);
+ ble_allow_pairing(pairing_code);
bool skip_finalization = false;
@@ -118,8 +113,7 @@ workflow_result_t workflow_ble_pairing_request(const fw_info_t *fw) {
return WF_OK_PAIRING_FAILED;
}
if (r == PAIRING_FINALIZATION_CANCEL) {
- ble_command_t disconnect = {.cmd_type = BLE_DISCONNECT};
- ble_issue_command(&disconnect);
+ ble_disconnect();
ble_iface_end_pairing();
return WF_OK_PAIRING_FAILED;
}
@@ -172,12 +166,7 @@ workflow_result_t workflow_wireless_setup(const fw_info_t *fw,
return WF_OK_PAIRING_FAILED;
}
- ble_command_t cmd = {
- .cmd_type = BLE_ALLOW_PAIRING,
- .data_len = sizeof(pairing_code),
- };
- memcpy(cmd.data.raw, pairing_code, sizeof(pairing_code));
- ble_issue_command(&cmd);
+ ble_allow_pairing(pairing_code);
bool skip_finalization = false;
@@ -204,8 +193,7 @@ workflow_result_t workflow_wireless_setup(const fw_info_t *fw,
return WF_OK_PAIRING_FAILED;
}
if (r == PAIRING_FINALIZATION_CANCEL) {
- ble_command_t disconnect = {.cmd_type = BLE_DISCONNECT};
- ble_issue_command(&disconnect);
+ ble_disconnect();
ble_iface_end_pairing();
return WF_OK_PAIRING_FAILED;
}
diff --git a/core/embed/projects/bootloader/workflow/wf_bootloader.c b/core/embed/projects/bootloader/workflow/wf_bootloader.c
index 38e08404..39177b6c 100644
--- a/core/embed/projects/bootloader/workflow/wf_bootloader.c
+++ b/core/embed/projects/bootloader/workflow/wf_bootloader.c
@@ -64,9 +64,7 @@ workflow_result_t workflow_menu(const fw_info_t* fw, protob_ios_t* ios) {
workflow_ifaces_resume(ios);
if (ios == NULL) {
// in case we were not in connected-mode, stop advertising
- ble_command_t cmd = {0};
- cmd.cmd_type = BLE_KEEP_CONNECTION;
- ble_issue_command(&cmd);
+ ble_keep_connection();
}
continue;
}
diff --git a/core/embed/projects/bootloader/workflow/wf_wipe_device.c b/core/embed/projects/bootloader/workflow/wf_wipe_device.c
index 4553bf45..cde4b86d 100644
--- a/core/embed/projects/bootloader/workflow/wf_wipe_device.c
+++ b/core/embed/projects/bootloader/workflow/wf_wipe_device.c
@@ -60,9 +60,7 @@ static bool wipe_bonds(protob_io_t* iface) {
return false;
}
- ble_command_t ble_command = {0};
- ble_command.cmd_type = BLE_ERASE_BONDS;
- if (!ble_issue_command(&ble_command)) {
+ if (!ble_erase_bonds()) {
send_error_conditionally(iface, "Could not issue BLE command");
screen_wipe_fail();
return false;
diff --git a/core/embed/projects/prodtest/cmd/prodtest_ble.c b/core/embed/projects/prodtest/cmd/prodtest_ble.c
index 987d0401..a5f1af4f 100644
--- a/core/embed/projects/prodtest/cmd/prodtest_ble.c
+++ b/core/embed/projects/prodtest/cmd/prodtest_ble.c
@@ -32,17 +32,13 @@
void ble_timer_cb(void* context) {
ble_event_t e = {0};
- ble_command_t cmd = {0};
bool event_received = ble_get_event(&e);
if (event_received) {
switch (e.type) {
case BLE_PAIRING_REQUEST:
- cmd.cmd_type = BLE_ALLOW_PAIRING;
- memcpy(cmd.data.raw, e.data, BLE_PAIRING_CODE_LEN);
- cmd.data_len = BLE_PAIRING_CODE_LEN;
- ble_issue_command(&cmd);
+ ble_allow_pairing(e.data);
default:
break;
}
@@ -85,13 +81,8 @@ static void prodtest_ble_adv_start(cli_t* cli) {
uint16_t name_len =
strlen(name) > BLE_ADV_NAME_LEN ? BLE_ADV_NAME_LEN : strlen(name);
- ble_command_t cmd = {0};
- cmd.cmd_type = BLE_PAIRING_MODE;
- cmd.data_len = sizeof(cmd.data.adv_start);
- cmd.data.adv_start.static_mac = true;
- memcpy(cmd.data.adv_start.name, name, name_len);
-
- if (!ble_issue_command(&cmd)) {
+ ble_set_static_mac(true);
+ if (!ble_enter_pairing_mode((const uint8_t*)name, name_len)) {
cli_error(cli, CLI_ERROR, "Could not start advertising.");
return;
}
@@ -128,11 +119,7 @@ static void prodtest_ble_adv_stop(cli_t* cli) {
return;
}
- ble_command_t cmd = {0};
- cmd.cmd_type = BLE_SWITCH_OFF;
- cmd.data_len = 0;
-
- if (!ble_issue_command(&cmd)) {
+ if (!ble_switch_off()) {
cli_error(cli, CLI_ERROR, "Could not stop advertising.");
return;
}
@@ -182,11 +169,8 @@ static void prodtest_ble_info(cli_t* cli) {
}
bool prodtest_ble_erase_bonds(cli_t* cli) {
- ble_command_t cmd = {0};
- cmd.cmd_type = BLE_ERASE_BONDS;
-
ble_state_t state = {0};
- ble_issue_command(&cmd);
+ ble_erase_bonds();
uint32_t timeout = ticks_timeout(100);
diff --git a/core/embed/rust/build.rs b/core/embed/rust/build.rs
index 6780045b..2dd9de27 100644
--- a/core/embed/rust/build.rs
+++ b/core/embed/rust/build.rs
@@ -423,7 +423,14 @@ fn generate_trezorhal_bindings() {
.allowlist_var("BLE_ADV_NAME_LEN")
.allowlist_function("ble_get_state")
.allowlist_function("ble_get_event")
- .allowlist_function("ble_issue_command")
+ .allowlist_function("ble_switch_on")
+ .allowlist_function("ble_switch_off")
+ .allowlist_function("ble_enter_pairing_mode")
+ .allowlist_function("ble_disconnect")
+ .allowlist_function("ble_set_name")
+ .allowlist_function("ble_erase_bonds")
+ .allowlist_function("ble_allow_pairing")
+ .allowlist_function("ble_reject_pairing")
.allowlist_function("ble_start")
.allowlist_function("ble_write")
.allowlist_function("ble_read")
diff --git a/core/embed/rust/src/trezorhal/ble/mod.rs b/core/embed/rust/src/trezorhal/ble/mod.rs
index 50f2d18d..3c73c577 100644
--- a/core/embed/rust/src/trezorhal/ble/mod.rs
+++ b/core/embed/rust/src/trezorhal/ble/mod.rs
@@ -6,7 +6,7 @@ use crate::ui::event::BLEEvent;
use super::ffi;
use crate::{error::Error, trezorhal::ffi::bt_le_addr_t};
-use core::{mem::size_of, ptr};
+use core::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;
@@ -26,6 +26,14 @@ fn prefix_utf8_bytes(text: &str, max_len: usize) -> &[u8] {
&text.as_bytes()[..i]
}
+pub fn res_to_result(res: bool) -> Result<(), Error> {
+ if res {
+ Ok(())
+ } else {
+ Err(COMMAND_FAILED)
+ }
+}
+
#[cfg(feature = "ui")]
pub fn ble_parse_event(event: ffi::ble_event_t) -> BLEEvent {
match event.type_ {
@@ -75,79 +83,43 @@ fn state() -> ffi::ble_state_t {
state
}
-fn issue_command(
- cmd_type: ffi::ble_command_type_t,
- cmd_data: ffi::ble_command_data_t,
-) -> Result<(), Error> {
- let data_len = match cmd_type {
- ffi::ble_command_type_t_BLE_ALLOW_PAIRING => PAIRING_CODE_LEN,
- ffi::ble_command_type_t_BLE_PAIRING_MODE | ffi::ble_command_type_t_BLE_SWITCH_ON => {
- size_of::<ffi::ble_adv_start_cmd_data_t>()
- }
- _ => 0,
- };
- let mut cmd = ffi::ble_command_t {
- cmd_type,
- data_len: unwrap!(data_len.try_into()),
- data: cmd_data,
- };
- if unsafe { ffi::ble_issue_command(&mut cmd as _) } {
- Ok(())
- } else {
- Err(COMMAND_FAILED)
- }
-}
-
-fn data_advname(name: &str) -> ffi::ble_command_data_t {
- let mut data = ffi::ble_command_data_t {
- adv_start: ffi::ble_adv_start_cmd_data_t {
- name: [0u8; ADV_NAME_LEN],
- static_mac: false,
- },
- };
- let bytes = prefix_utf8_bytes(name, ADV_NAME_LEN);
- unsafe {
- data.adv_start.name[..bytes.len()].copy_from_slice(bytes);
- }
- data
-}
-
-fn data_code(mut code: u32) -> ffi::ble_command_data_t {
- let mut pairing_code: [u8; PAIRING_CODE_LEN] = [0; PAIRING_CODE_LEN];
- for i in (0..PAIRING_CODE_LEN).rev() {
- let digit = b'0' + ((code % 10) as u8);
- code /= 10;
- pairing_code[i] = digit;
- }
- ffi::ble_command_data_t { pairing_code }
-}
-
-const fn data_none() -> ffi::ble_command_data_t {
- ffi::ble_command_data_t { raw: [0; 32] }
-}
-
pub fn pairing_mode(name: &str) -> Result<(), Error> {
- issue_command(ffi::ble_command_type_t_BLE_PAIRING_MODE, data_advname(name))
+ let res = unsafe { ffi::ble_enter_pairing_mode(name.as_ptr(), name.len()) };
+ res_to_result(res)
}
pub fn switch_on(name: &str) -> Result<(), Error> {
- issue_command(ffi::ble_command_type_t_BLE_SWITCH_ON, data_advname(name))
+ unsafe { ffi::ble_set_name(name.as_ptr(), name.len()) };
+ let res = unsafe { ffi::ble_switch_on() };
+ res_to_result(res)
}
pub fn switch_off() -> Result<(), Error> {
- issue_command(ffi::ble_command_type_t_BLE_SWITCH_OFF, data_none())
+ let res = unsafe { ffi::ble_switch_off() };
+ res_to_result(res)
}
pub fn allow_pairing(code: u32) -> Result<(), Error> {
- issue_command(ffi::ble_command_type_t_BLE_ALLOW_PAIRING, data_code(code))
+ let mut tmp_code = code;
+ let mut pairing_code: [u8; PAIRING_CODE_LEN] = [0; PAIRING_CODE_LEN];
+ for i in (0..PAIRING_CODE_LEN).rev() {
+ let digit = b'0' + ((tmp_code % 10) as u8);
+ tmp_code /= 10;
+ pairing_code[i] = digit;
+ }
+
+ let res = unsafe { ffi::ble_allow_pairing(pairing_code.as_ptr()) };
+ res_to_result(res)
}
pub fn reject_pairing() -> Result<(), Error> {
- issue_command(ffi::ble_command_type_t_BLE_REJECT_PAIRING, data_none())
+ let res = unsafe { ffi::ble_reject_pairing() };
+ res_to_result(res)
}
pub fn erase_bonds() -> Result<(), Error> {
- issue_command(ffi::ble_command_type_t_BLE_ERASE_BONDS, data_none())
+ let res = unsafe { ffi::ble_erase_bonds() };
+ res_to_result(res)
}
pub fn unpair(addr: Option<&bt_le_addr_t>) -> Result<(), Error> {
@@ -163,7 +135,8 @@ pub fn unpair(addr: Option<&bt_le_addr_t>) -> Result<(), Error> {
}
pub fn disconnect() -> Result<(), Error> {
- issue_command(ffi::ble_command_type_t_BLE_DISCONNECT, data_none())
+ let res = unsafe { ffi::ble_disconnect() };
+ res_to_result(res)
}
pub fn set_name(name: &str) {
diff --git a/core/embed/sys/syscall/inc/sys/syscall_numbers.h b/core/embed/sys/syscall/inc/sys/syscall_numbers.h
index c8507993..e1c2e7f0 100644
--- a/core/embed/sys/syscall/inc/sys/syscall_numbers.h
+++ b/core/embed/sys/syscall/inc/sys/syscall_numbers.h
@@ -120,7 +120,13 @@ typedef enum {
SYSCALL_FIRMWARE_HASH_CONTINUE,
SYSCALL_BLE_START,
- SYSCALL_BLE_ISSUE_COMMAND,
+ SYSCALL_BLE_SWITCH_ON,
+ SYSCALL_BLE_SWITCH_OFF,
+ SYSCALL_BLE_ENTER_PAIRING_MODE,
+ SYSCALL_BLE_DISCONNECT,
+ SYSCALL_BLE_ERASE_BONDS,
+ SYSCALL_BLE_ALLOW_PAIRING,
+ SYSCALL_BLE_REJECT_PAIRING,
SYSCALL_BLE_GET_EVENT,
SYSCALL_BLE_GET_STATE,
SYSCALL_BLE_CAN_WRITE,
diff --git a/core/embed/sys/syscall/stm32/syscall_dispatch.c b/core/embed/sys/syscall/stm32/syscall_dispatch.c
index cfaa7a00..d479a1ef 100644
--- a/core/embed/sys/syscall/stm32/syscall_dispatch.c
+++ b/core/embed/sys/syscall/stm32/syscall_dispatch.c
@@ -18,6 +18,8 @@
*/
#ifdef KERNEL
+#include <stdint.h>
+#include "embed/io/ble/inc/io/ble.h"
#include <trezor_rtl.h>
@@ -598,9 +600,35 @@ __attribute((no_stack_protector)) void syscall_handler(uint32_t *args,
ble_start();
} break;
- case SYSCALL_BLE_ISSUE_COMMAND: {
- ble_command_t *command = (ble_command_t *)args[0];
- args[0] = ble_issue_command__verified(command);
+ case SYSCALL_BLE_SWITCH_ON: {
+ args[0] = ble_switch_on();
+ } break;
+
+ case SYSCALL_BLE_SWITCH_OFF: {
+ args[0] = ble_switch_off();
+ } break;
+
+ case SYSCALL_BLE_ENTER_PAIRING_MODE: {
+ const uint8_t *name = (const uint8_t *)args[0];
+ size_t name_len = (size_t)args[1];
+ args[0] = ble_enter_pairing_mode__verified(name, name_len);
+ } break;
+
+ case SYSCALL_BLE_DISCONNECT: {
+ args[0] = ble_disconnect();
+ } break;
+
+ case SYSCALL_BLE_ERASE_BONDS: {
+ args[0] = ble_erase_bonds();
+ } break;
+
+ case SYSCALL_BLE_ALLOW_PAIRING: {
+ const uint8_t *code = (const uint8_t *)args[0];
+ args[0] = ble_allow_pairing__verified(code);
+ } break;
+
+ case SYSCALL_BLE_REJECT_PAIRING: {
+ args[0] = ble_reject_pairing();
} break;
case SYSCALL_BLE_GET_STATE: {
diff --git a/core/embed/sys/syscall/stm32/syscall_stubs.c b/core/embed/sys/syscall/stm32/syscall_stubs.c
index ba23291e..2f78d660 100644
--- a/core/embed/sys/syscall/stm32/syscall_stubs.c
+++ b/core/embed/sys/syscall/stm32/syscall_stubs.c
@@ -593,8 +593,34 @@ int firmware_hash_continue(uint8_t *hash, size_t hash_len) {
void ble_start(void) { syscall_invoke0(SYSCALL_BLE_START); }
-bool ble_issue_command(ble_command_t *command) {
- return (bool)syscall_invoke1((uint32_t)command, SYSCALL_BLE_ISSUE_COMMAND);
+bool ble_switch_off(void) {
+ return (bool)syscall_invoke0(SYSCALL_BLE_SWITCH_OFF);
+}
+
+bool ble_switch_on(void) {
+ return (bool)syscall_invoke0(SYSCALL_BLE_SWITCH_ON);
+}
+
+bool ble_enter_pairing_mode(const uint8_t *name, size_t name_len) {
+ return (bool)syscall_invoke2((uint32_t)name, name_len,
+ SYSCALL_BLE_ENTER_PAIRING_MODE);
+}
+
+bool ble_disconnect(void) {
+ return (bool)syscall_invoke0(SYSCALL_BLE_DISCONNECT);
+}
+
+bool ble_erase_bonds(void) {
+ return (bool)syscall_invoke0(SYSCALL_BLE_ERASE_BONDS);
+}
+
+bool ble_allow_pairing(const uint8_t *pairing_code) {
+ return (bool)syscall_invoke1((uint32_t)pairing_code,
+ SYSCALL_BLE_ALLOW_PAIRING);
+}
+
+bool ble_reject_pairing(void) {
+ return (bool)syscall_invoke0(SYSCALL_BLE_REJECT_PAIRING);
}
bool ble_get_event(ble_event_t *event) {
diff --git a/core/embed/sys/syscall/stm32/syscall_verifiers.c b/core/embed/sys/syscall/stm32/syscall_verifiers.c
index 1d44228a..af8e3932 100644
--- a/core/embed/sys/syscall/stm32/syscall_verifiers.c
+++ b/core/embed/sys/syscall/stm32/syscall_verifiers.c
@@ -723,12 +723,25 @@ access_violation:
// ---------------------------------------------------------------------
#ifdef USE_BLE
-bool ble_issue_command__verified(ble_command_t *command) {
- if (!probe_read_access(command, sizeof(*command))) {
+
+bool ble_enter_pairing_mode__verified(const uint8_t *name, size_t name_len) {
+ if (!probe_read_access(name, name_len)) {
+ goto access_violation;
+ }
+
+ return ble_enter_pairing_mode(name, name_len);
+
+access_violation:
+ apptask_access_violation();
+ return false;
+}
+
+bool ble_allow_pairing__verified(const uint8_t *pairing_code) {
+ if (!probe_read_access(pairing_code, BLE_PAIRING_CODE_LEN)) {
goto access_violation;
}
- return ble_issue_command(command);
+ return ble_allow_pairing(pairing_code);
access_violation:
apptask_access_violation();
diff --git a/core/embed/sys/syscall/stm32/syscall_verifiers.h b/core/embed/sys/syscall/stm32/syscall_verifiers.h
index fa1e87ec..0c458c23 100644
--- a/core/embed/sys/syscall/stm32/syscall_verifiers.h
+++ b/core/embed/sys/syscall/stm32/syscall_verifiers.h
@@ -187,7 +187,9 @@ secbool firmware_get_vendor__verified(char *buff, size_t buff_size);
#include <io/ble.h>
-bool ble_issue_command__verified(ble_command_t *state);
+bool ble_enter_pairing_mode__verified(const uint8_t *name, size_t name_len);
+
+bool ble_allow_pairing__verified(const uint8_t *pairing_code);
void ble_get_state__verified(ble_state_t *state);
Why this scored 28/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.