diff --git a/components/bt/common/btc/core/btc_manage.c b/components/bt/common/btc/core/btc_manage.c index 4b63f1efbab..9ead68c8767 100644 --- a/components/bt/common/btc/core/btc_manage.c +++ b/components/bt/common/btc/core/btc_manage.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -51,15 +51,17 @@ int btc_profile_cb_set(btc_pid_t profile_id, void *cb) void *btc_profile_cb_get(btc_pid_t profile_id) { #if BTC_DYNAMIC_MEMORY == TRUE - void *p = btc_profile_cb_tab; + void **p = btc_profile_cb_tab; if (p == NULL) { return NULL; } +#else + void **p = btc_profile_cb_tab; #endif if (profile_id < 0 || profile_id >= BTC_PID_NUM) { return NULL; } - return btc_profile_cb_tab[profile_id]; + return p[profile_id]; } diff --git a/components/bt/common/btc/core/btc_task.c b/components/bt/common/btc/core/btc_task.c index d81e4347fa4..6ba8f17c66f 100644 --- a/components/bt/common/btc/core/btc_task.c +++ b/components/bt/common/btc/core/btc_task.c @@ -286,6 +286,8 @@ static void btc_thread_handler(void *arg) btc_msg_t *msg = (btc_msg_t *)arg; BTC_TRACE_DEBUG("%s msg %u %u %u %p\n", __func__, msg->sig, msg->pid, msg->act, msg->arg); + /* msg->pid is validated at btc_transfer_context() entry; any message that + * reaches this handler is guaranteed to carry a valid pid. */ switch (msg->sig) { case BTC_SIG_API_CALL: profile_tab[msg->pid].btc_call(msg); @@ -325,7 +327,12 @@ bt_status_t btc_transfer_context(btc_msg_t *msg, void *arg, int arg_len, btc_arg btc_msg_t* lmsg; bt_status_t ret; // arg XOR arg_len - if ((msg == NULL) || ((arg == NULL) == !(arg_len == 0))) { + if ((msg == NULL) || ((arg == NULL) == !(arg_len == 0)) || + (msg->pid >= BTC_PID_NUM)) { + /* Reject invalid pid here, before any deep_copy runs, so the caller's + * arg is not yet duplicated into lmsg and there is nothing to free. + * This keeps the trust boundary at the single public entry point and + * makes the downstream handler unable to encounter an invalid pid. */ BTC_TRACE_WARNING("%s Invalid parameters\n", __func__); return BT_STATUS_PARM_INVALID; } @@ -558,22 +565,31 @@ bt_status_t btc_init(void) void btc_deinit(void) { - osi_thread_t *thread = btc_thread; - if (!thread) { + if (!btc_thread) { return; } - osi_thread_free(thread); - btc_thread = NULL; + /* Reverse order of btc_init(): + * 1) BLE GAP deinit must run BEFORE btc_deinit_mem(), otherwise under + * BTC_DYNAMIC_MEMORY the gl_bta_adv_data macro expands to + * *(NULL) and btc_cleanup_adv_data() early-returns, leaking the + * inner adv-data fields (p_manu / p_proprietary / p_services...). + * 2) BT classic GAP deinit follows. + * 3) Then release the dynamic-memory pool. + * 4) Finally tear down the BTC worker thread. + */ +#if (BLE_INCLUDED == TRUE) + btc_gap_ble_deinit(); +#endif ///BLE_INCLUDED == TRUE #if BTC_GAP_BT_INCLUDED btc_gap_bt_deinit(); #endif #if BTC_DYNAMIC_MEMORY btc_deinit_mem(); #endif -#if (BLE_INCLUDED == TRUE) - btc_gap_ble_deinit(); -#endif ///BLE_INCLUDED == TRUE + + osi_thread_free(btc_thread); + btc_thread = NULL; } int get_btc_work_queue_size(void) diff --git a/components/bt/common/btc/profile/esp/blufi/bluedroid_host/esp_blufi.c b/components/bt/common/btc/profile/esp/blufi/bluedroid_host/esp_blufi.c index 744c0df83ad..a6b24eb43db 100644 --- a/components/bt/common/btc/profile/esp/blufi/bluedroid_host/esp_blufi.c +++ b/components/bt/common/btc/profile/esp/blufi/bluedroid_host/esp_blufi.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -32,11 +32,15 @@ #include "esp_bt_device.h" #include "esp_err.h" #include "esp_blufi.h" +#include #if (BLUFI_INCLUDED == TRUE) static uint8_t server_if; static uint16_t conn_id; + +/* Forward declaration used by the GATTS event handler. */ +esp_err_t esp_blufi_close(esp_gatt_if_t gatts_if, uint16_t conn_id); static uint8_t blufi_service_uuid128[32] = { /* LSB <--------------------------------------------------------------------------------> MSB */ //first uuid, 16bit, [12],[13] is the value @@ -70,12 +74,166 @@ static esp_ble_adv_params_t blufi_adv_params = { .adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY, }; +#ifdef CONFIG_BT_BLUFI_BLE_SMP_ENABLE +static char *esp_auth_req_to_str(esp_ble_auth_req_t auth_req) +{ + char *auth_str = NULL; + switch(auth_req) { + case ESP_LE_AUTH_NO_BOND: + auth_str = "ESP_LE_AUTH_NO_BOND"; + break; + case ESP_LE_AUTH_BOND: + auth_str = "ESP_LE_AUTH_BOND"; + break; + case ESP_LE_AUTH_REQ_MITM: + auth_str = "ESP_LE_AUTH_REQ_MITM"; + break; + case ESP_LE_AUTH_REQ_BOND_MITM: + auth_str = "ESP_LE_AUTH_REQ_BOND_MITM"; + break; + case ESP_LE_AUTH_REQ_SC_ONLY: + auth_str = "ESP_LE_AUTH_REQ_SC_ONLY"; + break; + case ESP_LE_AUTH_REQ_SC_BOND: + auth_str = "ESP_LE_AUTH_REQ_SC_BOND"; + break; + case ESP_LE_AUTH_REQ_SC_MITM: + auth_str = "ESP_LE_AUTH_REQ_SC_MITM"; + break; + case ESP_LE_AUTH_REQ_SC_MITM_BOND: + auth_str = "ESP_LE_AUTH_REQ_SC_MITM_BOND"; + break; + default: + auth_str = "INVALID BLE AUTH REQ"; + break; + } + + return auth_str; +} + +static char *esp_key_type_to_str(esp_ble_key_type_t key_type) +{ + char *key_str = NULL; + switch(key_type) { + case ESP_LE_KEY_NONE: + key_str = "ESP_LE_KEY_NONE"; + break; + case ESP_LE_KEY_PENC: + key_str = "ESP_LE_KEY_PENC"; + break; + case ESP_LE_KEY_PID: + key_str = "ESP_LE_KEY_PID"; + break; + case ESP_LE_KEY_PCSRK: + key_str = "ESP_LE_KEY_PCSRK"; + break; + case ESP_LE_KEY_PLK: + key_str = "ESP_LE_KEY_PLK"; + break; + case ESP_LE_KEY_LLK: + key_str = "ESP_LE_KEY_LLK"; + break; + case ESP_LE_KEY_LENC: + key_str = "ESP_LE_KEY_LENC"; + break; + case ESP_LE_KEY_LID: + key_str = "ESP_LE_KEY_LID"; + break; + case ESP_LE_KEY_LCSRK: + key_str = "ESP_LE_KEY_LCSRK"; + break; + default: + key_str = "INVALID BLE KEY TYPE"; + break; + } + return key_str; +} +#endif + void esp_blufi_gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { + BLUFI_TRACE_DEBUG("GAP_EVT, event %d", event); switch (event) { case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: + if (param->adv_data_cmpl.status != ESP_BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("Advertising data set failed, status %x", param->adv_data_cmpl.status); + break; + } esp_ble_gap_start_advertising(&blufi_adv_params); break; + case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: + //advertising start complete event to indicate advertising start successfully or failed + if (param->adv_start_cmpl.status != ESP_BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("Advertising start failed, status %x", param->adv_start_cmpl.status); + break; + } + BLUFI_TRACE_API("Advertising start successfully"); + break; +#ifdef CONFIG_BT_BLUFI_BLE_SMP_ENABLE + case ESP_GAP_BLE_PASSKEY_REQ_EVT: /* passkey request event */ + BLUFI_TRACE_API("Passkey request"); + break; + case ESP_GAP_BLE_OOB_REQ_EVT: { + BLUFI_TRACE_API("OOB request"); + uint8_t tk[16] = {1}; //If you paired with OOB, both devices need to use the same tk + esp_ble_oob_req_reply(param->ble_security.ble_req.bd_addr, tk, sizeof(tk)); + break; + } + case ESP_GAP_BLE_LOCAL_IR_EVT: /* BLE local IR event */ + BLUFI_TRACE_API("Local identity root"); + break; + case ESP_GAP_BLE_LOCAL_ER_EVT: /* BLE local ER event */ + BLUFI_TRACE_API("Local encryption root"); + break; + case ESP_GAP_BLE_NC_REQ_EVT: + /* The app will receive this evt when the IO has DisplayYesNO capability and the peer device IO also has DisplayYesNo capability. + show the passkey number to the user to confirm it with the number displayed by peer device. */ + /* + * Security note: + * Auto-accepting Numeric Comparison would bypass the user confirmation step and + * effectively weaken MITM protection. Reject by default; applications that want + * MITM protection must implement explicit user confirmation logic. + */ + esp_ble_confirm_reply(param->ble_security.ble_req.bd_addr, false); + BLUFI_TRACE_WARNING("Numeric Comparison request rejected by default, passkey %" PRIu32, + param->ble_security.key_notif.passkey); + break; + case ESP_GAP_BLE_SEC_REQ_EVT: + /* send the positive(true) security response to the peer device to accept the security request. + If not accept the security request, should send the security response with negative(false) accept value*/ + esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true); + break; + case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: ///the app will receive this evt when the IO has Output capability and the peer device IO has Input capability. + ///show the passkey number to the user to input it in the peer device. + BLUFI_TRACE_WARNING("Passkey notify, passkey %06" PRIu32, param->ble_security.key_notif.passkey); + break; + case ESP_GAP_BLE_KEY_EVT: + //shows the ble key info share with peer device to the user. + BLUFI_TRACE_API("Key exchanged, key_type %s", esp_key_type_to_str(param->ble_security.ble_key.key_type)); + break; + case ESP_GAP_BLE_AUTH_CMPL_EVT: { + esp_bd_addr_t bd_addr; + memcpy(bd_addr, param->ble_security.auth_cmpl.bd_addr, sizeof(esp_bd_addr_t)); + BLUFI_TRACE_API("Authentication complete, addr_type %u, addr "ESP_BD_ADDR_STR"", + param->ble_security.auth_cmpl.addr_type, ESP_BD_ADDR_HEX(bd_addr)); + if(!param->ble_security.auth_cmpl.success) { + BLUFI_TRACE_WARNING("Pairing failed, reason 0x%x",param->ble_security.auth_cmpl.fail_reason); + } else { + BLUFI_TRACE_WARNING("Pairing successfully, auth_mode %s",esp_auth_req_to_str(param->ble_security.auth_cmpl.auth_mode)); + } + break; + } + case ESP_GAP_BLE_REMOVE_BOND_DEV_COMPLETE_EVT: { + BLUFI_TRACE_DEBUG("Bond device remove, status %d, device "ESP_BD_ADDR_STR"", + param->remove_bond_dev_cmpl.status, ESP_BD_ADDR_HEX(param->remove_bond_dev_cmpl.bd_addr)); + break; + } + case ESP_GAP_BLE_SET_LOCAL_PRIVACY_COMPLETE_EVT: + if (param->local_privacy_cmpl.status != ESP_BT_STATUS_SUCCESS){ + BLUFI_TRACE_WARNING("Local privacy config failed, status %x", param->local_privacy_cmpl.status); + } + break; +#endif // CONFIG_BT_BLUFI_BLE_SMP_ENABLE default: break; } @@ -149,7 +307,7 @@ static void blufi_profile_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data) break; } case BTA_GATTS_READ_EVT: - memset(&rsp, 0, sizeof(tBTA_GATTS_API_RSP)); + memset(&rsp, 0, sizeof(rsp)); rsp.attr_value.handle = p_data->req_data.p_data->read_req.handle; rsp.attr_value.len = 1; rsp.attr_value.value[0] = 0x00; @@ -176,7 +334,9 @@ static void blufi_profile_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data) status = GATT_INVALID_OFFSET; break; } - blufi_env.prepare_buf = osi_malloc(BLUFI_PREPARE_BUF_MAX_SIZE); + /* Use calloc so any unwritten gaps are deterministic (0) if peer sends + * out-of-order/overlapping fragments and later executes the write. */ + blufi_env.prepare_buf = osi_calloc(BLUFI_PREPARE_BUF_MAX_SIZE); blufi_env.prepare_len = 0; if (blufi_env.prepare_buf == NULL) { BLUFI_TRACE_ERROR("Blufi prep no mem\n"); @@ -209,7 +369,14 @@ static void blufi_profile_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data) memcpy(blufi_env.prepare_buf + p_data->req_data.p_data->write_req.offset, p_data->req_data.p_data->write_req.value, p_data->req_data.p_data->write_req.len); - blufi_env.prepare_len += p_data->req_data.p_data->write_req.len; + /* Maintain the maximum written extent rather than summing lengths. + * Summation breaks on retransmissions/overlaps and can exceed the actual + * written range, causing parsing of unwritten bytes. */ + const uint16_t end = + (uint16_t)(p_data->req_data.p_data->write_req.offset + p_data->req_data.p_data->write_req.len); + if (end > blufi_env.prepare_len) { + blufi_env.prepare_len = end; + } return; } else { @@ -251,10 +418,16 @@ static void blufi_profile_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data) break; case BTA_GATTS_CREATE_EVT: blufi_env.handle_srvc = p_data->create.service_id; - + #if CONFIG_BT_BLUFI_BLE_SMP_ENABLE + BLUFI_TRACE_WARNING("BLE SMP support in BLUFI is ENABLED!"); + #endif // CONFIG_BT_BLUFI_BLE_SMP_ENABLE //add the first blufi characteristic --> write characteristic BTA_GATTS_AddCharacteristic(blufi_env.handle_srvc, &blufi_char_uuid_p2e, - (GATT_PERM_WRITE), + #if CONFIG_BT_BLUFI_BLE_SMP_ENABLE + GATT_PERM_WRITE_ENC_MITM, + #else + GATT_PERM_WRITE, + #endif (GATT_CHAR_PROP_BIT_WRITE), NULL, NULL); break; @@ -264,7 +437,11 @@ static void blufi_profile_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data) blufi_env.handle_char_p2e = p_data->add_result.attr_id; BTA_GATTS_AddCharacteristic(blufi_env.handle_srvc, &blufi_char_uuid_e2p, + #if CONFIG_BT_BLUFI_BLE_SMP_ENABLE + (GATT_PERM_READ_ENC_MITM), + #else (GATT_PERM_READ), + #endif (GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY), NULL, NULL); break; @@ -272,7 +449,11 @@ static void blufi_profile_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data) blufi_env.handle_char_e2p = p_data->add_result.attr_id; BTA_GATTS_AddCharDescriptor (blufi_env.handle_srvc, + #if CONFIG_BT_BLUFI_BLE_SMP_ENABLE + (GATT_PERM_READ_ENC_MITM | GATT_PERM_WRITE_ENC_MITM), + #else (GATT_PERM_READ | GATT_PERM_WRITE), + #endif &blufi_descr_uuid_e2p, NULL, NULL); break; @@ -301,6 +482,13 @@ static void blufi_profile_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data) btc_msg_t msg; esp_blufi_cb_param_t param; + if (blufi_env.is_connected) { + BLUFI_TRACE_WARNING("BLUFI already connected; rejecting new connection from "BT_BD_ADDR_STR", connect_id=%d", + BT_BD_ADDR_HEX(p_data->conn.remote_bda), p_data->conn.conn_id); + (void)esp_blufi_close(p_data->conn.server_if, BTC_GATT_GET_CONN_ID(p_data->conn.conn_id)); + break; + } + //set the connection flag to true BLUFI_TRACE_API("\ndevice is connected "BT_BD_ADDR_STR", server_if=%d,reason=0x%x,connect_id=%d\n", BT_BD_ADDR_HEX(p_data->conn.remote_bda), p_data->conn.server_if, @@ -325,6 +513,18 @@ static void blufi_profile_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data) btc_msg_t msg; esp_blufi_cb_param_t param; + /* Ignore disconnects that do not belong to the active BLUFI connection. + * Even though we reject concurrent connections, the stack may still + * deliver disconnect events for transient/previous connections; blindly + * resetting the global env would corrupt the active session. */ + if (!blufi_env.is_connected || blufi_env.conn_id != p_data->conn.conn_id) { + BLUFI_TRACE_WARNING("Ignoring BLUFI disconnect for non-active conn_id=%d (active=%d, is_connected=%d) from " + BT_BD_ADDR_STR, + p_data->conn.conn_id, blufi_env.conn_id, blufi_env.is_connected, + BT_BD_ADDR_HEX(p_data->conn.remote_bda)); + break; + } + blufi_env.is_connected = false; //set the connection flag to true BLUFI_TRACE_API("\ndevice is disconnected "BT_BD_ADDR_STR", server_if=%d,reason=0x%x,connect_id=%d\n", @@ -342,6 +542,12 @@ static void blufi_profile_cb(tBTA_GATTS_EVT event, tBTA_GATTS *p_data) blufi_env.aggr_buf = NULL; } + if (blufi_env.prepare_buf) { + osi_free(blufi_env.prepare_buf); + blufi_env.prepare_buf = NULL; + blufi_env.prepare_len = 0; + } + msg.sig = BTC_SIG_API_CB; msg.pid = BTC_PID_BLUFI; msg.act = ESP_BLUFI_EVENT_BLE_DISCONNECT; @@ -373,6 +579,18 @@ void esp_blufi_send_notify(void *arg) void esp_blufi_deinit(void) { + if (blufi_env.prepare_buf) { + osi_free(blufi_env.prepare_buf); + blufi_env.prepare_buf = NULL; + blufi_env.prepare_len = 0; + } + if (blufi_env.aggr_buf) { + osi_free(blufi_env.aggr_buf); + blufi_env.aggr_buf = NULL; + } + blufi_env.offset = 0; + blufi_env.total_len = 0; + BTA_GATTS_StopService(blufi_env.handle_srvc); BTA_GATTS_DeleteService(blufi_env.handle_srvc); /* register the BLUFI profile to the BTA_GATTS module*/ @@ -398,6 +616,16 @@ void esp_blufi_adv_stop(void) esp_ble_gap_stop_advertising(); } + +esp_err_t esp_blufi_start_security_request(esp_blufi_bd_addr_t remote_bda) +{ + #ifdef CONFIG_BT_BLUFI_BLE_SMP_ENABLE + return esp_ble_set_encryption(remote_bda, ESP_BLE_SEC_ENCRYPT_MITM); + #else + return ESP_ERR_INVALID_STATE; + #endif // CONFIG_BT_BLUFI_BLE_SMP_ENABLE +} + void esp_blufi_send_encap(void *arg) { struct blufi_hdr *hdr = (struct blufi_hdr *)arg; @@ -423,6 +651,8 @@ esp_err_t esp_blufi_close(esp_gatt_if_t gatts_if, uint16_t conn_id) ESP_BLE_HOST_STATUS_CHECK(ESP_BLE_HOST_STATUS_ENABLED); btc_msg_t msg; btc_ble_gatts_args_t arg; + memset(&msg, 0, sizeof(msg)); + memset(&arg, 0, sizeof(arg)); msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTS; msg.act = BTC_GATTS_ACT_CLOSE; diff --git a/components/bt/common/btc/profile/esp/blufi/blufi_prf.c b/components/bt/common/btc/profile/esp/blufi/blufi_prf.c index 304b162555d..d1fc67ea33e 100644 --- a/components/bt/common/btc/profile/esp/blufi/blufi_prf.c +++ b/components/bt/common/btc/profile/esp/blufi/blufi_prf.c @@ -5,10 +5,10 @@ */ #include +#include #include #include -#include #include "osi/allocator.h" @@ -34,6 +34,22 @@ tBLUFI_ENV *blufi_env_ptr; // static functions declare static void btc_blufi_send_ack(uint8_t seq); +static inline void btc_blufi_env_free_buffers(void) +{ + if (blufi_env.aggr_buf) { + osi_free(blufi_env.aggr_buf); + blufi_env.aggr_buf = NULL; + } + blufi_env.total_len = 0; + blufi_env.offset = 0; + + if (blufi_env.prepare_buf) { + osi_free(blufi_env.prepare_buf); + blufi_env.prepare_buf = NULL; + } + blufi_env.prepare_len = 0; +} + inline void btc_blufi_cb_to_app(esp_blufi_cb_event_t event, esp_blufi_cb_param_t *param) { esp_blufi_event_cb_t btc_blufi_cb = (esp_blufi_event_cb_t)btc_profile_cb_get(BTC_PID_BLUFI); @@ -52,6 +68,7 @@ static uint8_t btc_blufi_profile_init(void) return ESP_BLUFI_ERROR; } + btc_blufi_env_free_buffers(); memset(&blufi_env, 0x0, sizeof(blufi_env)); blufi_env.cbs = store_p; /* if set callback prior, restore the point */ blufi_env.frag_size = BLUFI_FRAG_DATA_DEFAULT_LEN; @@ -138,6 +155,7 @@ void btc_blufi_recv_handler(uint8_t *data, int len) blufi_env.aggr_buf = NULL; \ } \ blufi_env.offset = 0; \ + blufi_env.total_len = 0; \ } while (0) // first step, decrypt @@ -170,11 +188,14 @@ void btc_blufi_recv_handler(uint8_t *data, int len) } if (BLUFI_FC_IS_FRAG(hdr->fc)) { - if(hdr->data_len<2) { - BTC_TRACE_ERROR("%s: Invalid fragment data length: %d", __func__, hdr->data_len); - btc_blufi_report_error(ESP_BLUFI_DATA_FORMAT_ERROR); - return; + if (hdr->data_len < 2) { + BTC_TRACE_ERROR("%s: Invalid fragment data length: %d", __func__, hdr->data_len); + BLUFI_RESET_AGGR_BUF(); + btc_blufi_report_error(ESP_BLUFI_DATA_FORMAT_ERROR); + return; } + uint16_t frag_payload = (uint16_t)(hdr->data_len - 2); + if (blufi_env.offset == 0) { /* blufi_env.aggr_buf should be NULL if blufi_env.offset is 0. @@ -183,17 +204,16 @@ void btc_blufi_recv_handler(uint8_t *data, int len) */ if (blufi_env.aggr_buf) { BTC_TRACE_ERROR("%s msg error, blufi_env.aggr_buf is not freed\n", __func__); - osi_free(blufi_env.aggr_buf); - blufi_env.aggr_buf = NULL; + BLUFI_RESET_AGGR_BUF(); btc_blufi_report_error(ESP_BLUFI_MSG_STATE_ERROR); return; } - if (hdr->data_len < 2) { - BTC_TRACE_ERROR("%s frag header too short: data_len=%d\n", __func__, hdr->data_len); + blufi_env.total_len = hdr->data[0] | (((uint16_t) hdr->data[1]) << 8); + if (blufi_env.total_len == 0) { + BTC_TRACE_ERROR("%s zero total_len\n", __func__); btc_blufi_report_error(ESP_BLUFI_DATA_FORMAT_ERROR); return; } - blufi_env.total_len = hdr->data[0] | (((uint16_t) hdr->data[1]) << 8); blufi_env.aggr_buf = osi_malloc(blufi_env.total_len); if (blufi_env.aggr_buf == NULL) { BTC_TRACE_ERROR("%s no mem, len %d\n", __func__, blufi_env.total_len); @@ -201,15 +221,15 @@ void btc_blufi_recv_handler(uint8_t *data, int len) return; } } - if (blufi_env.offset + hdr->data_len - 2 <= blufi_env.total_len){ - memcpy(blufi_env.aggr_buf + blufi_env.offset, hdr->data + 2, hdr->data_len - 2); - blufi_env.offset += (hdr->data_len - 2); - } else { - BTC_TRACE_ERROR("%s payload is longer than packet length, len %d \n", __func__, blufi_env.total_len); + + if ((uint32_t)blufi_env.offset + frag_payload > blufi_env.total_len) { + BTC_TRACE_ERROR("%s payload is longer than packet length, len %d\n", __func__, blufi_env.total_len); BLUFI_RESET_AGGR_BUF(); btc_blufi_report_error(ESP_BLUFI_DATA_FORMAT_ERROR); return; } + memcpy(blufi_env.aggr_buf + blufi_env.offset, hdr->data + 2, frag_payload); + blufi_env.offset += frag_payload; } else { if (blufi_env.offset > 0) { /* if previous pkt is frag */ @@ -255,6 +275,16 @@ void btc_blufi_send_encap(uint8_t type, uint8_t *data, int total_data_len) return; } + if (total_data_len <= 0 || data == NULL) { + BTC_TRACE_ERROR("%s invalid data or length %d\n", __func__, total_data_len); + return; + } + if (total_data_len > UINT16_MAX) { + BTC_TRACE_ERROR("%s total_data_len too large %d\n", __func__, total_data_len); + btc_blufi_report_error(ESP_BLUFI_DATA_FORMAT_ERROR); + return; + } + while (remain_len > 0) { if (remain_len > blufi_env.frag_size) { hdr = osi_malloc(sizeof(struct blufi_hdr) + 2 + blufi_env.frag_size + 2); @@ -264,8 +294,8 @@ void btc_blufi_send_encap(uint8_t type, uint8_t *data, int total_data_len) } hdr->fc = 0x0; hdr->data_len = blufi_env.frag_size + 2; - hdr->data[0] = remain_len & 0xff; - hdr->data[1] = (remain_len >> 8) & 0xff; + hdr->data[0] = (uint8_t)(remain_len & 0xff); + hdr->data[1] = (uint8_t)((remain_len >> 8) & 0xff); memcpy(hdr->data + 2, &data[total_data_len - remain_len], blufi_env.frag_size); //copy first, easy for check sum hdr->fc |= BLUFI_FC_FRAG; } else { @@ -325,15 +355,99 @@ void btc_blufi_send_encap(uint8_t type, uint8_t *data, int total_data_len) } } +/* IEEE 802.11 SSID / passphrase upper bounds for BLUFI encoding */ +#define BLUFI_WIFI_SSID_MAX 32 +#define BLUFI_WIFI_PASS_MAX 64 +#define BLUFI_WIFI_CONN_REP_MAX 512 + +static size_t blufi_extra_field_len(int len, size_t max_len) +{ + if (len <= 0) { + return 0; + } + return (size_t)len > max_len ? max_len : (size_t)len; +} + +static bool btc_blufi_wifi_conn_report_add(size_t *acc, size_t add) +{ + if (*acc > SIZE_MAX - add) { + return false; + } + *acc += add; + return *acc <= BLUFI_WIFI_CONN_REP_MAX; +} + +static size_t btc_blufi_wifi_conn_report_need(esp_blufi_extra_info_t *info) +{ + size_t n = 3; + + if (info == NULL) { + return n; + } + if (info->sta_bssid_set && !btc_blufi_wifi_conn_report_add(&n, 8)) { + return 0; + } + if (info->sta_ssid) { + size_t sl = blufi_extra_field_len(info->sta_ssid_len, BLUFI_WIFI_SSID_MAX); + if (!btc_blufi_wifi_conn_report_add(&n, 2 + sl)) { + return 0; + } + } + if (info->sta_passwd) { + size_t pl = blufi_extra_field_len(info->sta_passwd_len, BLUFI_WIFI_PASS_MAX); + if (!btc_blufi_wifi_conn_report_add(&n, 2 + pl)) { + return 0; + } + } + if (info->softap_ssid) { + size_t sl = blufi_extra_field_len(info->softap_ssid_len, BLUFI_WIFI_SSID_MAX); + if (!btc_blufi_wifi_conn_report_add(&n, 2 + sl)) { + return 0; + } + } + if (info->softap_passwd) { + size_t pl = blufi_extra_field_len(info->softap_passwd_len, BLUFI_WIFI_PASS_MAX); + if (!btc_blufi_wifi_conn_report_add(&n, 2 + pl)) { + return 0; + } + } + if (info->softap_authmode_set && !btc_blufi_wifi_conn_report_add(&n, 3)) { + return 0; + } + if (info->softap_max_conn_num_set && !btc_blufi_wifi_conn_report_add(&n, 3)) { + return 0; + } + if (info->softap_channel_set && !btc_blufi_wifi_conn_report_add(&n, 3)) { + return 0; + } + if (info->sta_max_conn_retry_set && !btc_blufi_wifi_conn_report_add(&n, 3)) { + return 0; + } + if (info->sta_conn_end_reason_set && !btc_blufi_wifi_conn_report_add(&n, 3)) { + return 0; + } + if (info->sta_conn_rssi_set && !btc_blufi_wifi_conn_report_add(&n, 3)) { + return 0; + } + return n; +} + static void btc_blufi_wifi_conn_report(uint8_t opmode, uint8_t sta_conn_state, uint8_t softap_conn_num, esp_blufi_extra_info_t *info, int info_len) { uint8_t type; uint8_t *data; - int data_len; uint8_t *p; + size_t buf_len; - data_len = info_len + 3; - p = data = osi_malloc(data_len); + (void)info_len; + + buf_len = btc_blufi_wifi_conn_report_need(info); + if (buf_len == 0 || buf_len > BLUFI_WIFI_CONN_REP_MAX) { + BTC_TRACE_ERROR("%s invalid buffer size %u\n", __func__, (unsigned)buf_len); + return; + } + + p = data = osi_malloc(buf_len); if (data == NULL) { BTC_TRACE_ERROR("%s no mem\n", __func__); return; @@ -352,28 +466,32 @@ static void btc_blufi_wifi_conn_report(uint8_t opmode, uint8_t sta_conn_state, u p += 6; } if (info->sta_ssid) { + size_t sl = blufi_extra_field_len(info->sta_ssid_len, BLUFI_WIFI_SSID_MAX); *p++ = BLUFI_TYPE_DATA_SUBTYPE_STA_SSID; - *p++ = info->sta_ssid_len; - memcpy(p, info->sta_ssid, info->sta_ssid_len); - p += info->sta_ssid_len; + *p++ = (uint8_t)sl; + memcpy(p, info->sta_ssid, sl); + p += sl; } if (info->sta_passwd) { + size_t pl = blufi_extra_field_len(info->sta_passwd_len, BLUFI_WIFI_PASS_MAX); *p++ = BLUFI_TYPE_DATA_SUBTYPE_STA_PASSWD; - *p++ = info->sta_passwd_len; - memcpy(p, info->sta_passwd, info->sta_passwd_len); - p += info->sta_passwd_len; + *p++ = (uint8_t)pl; + memcpy(p, info->sta_passwd, pl); + p += pl; } if (info->softap_ssid) { + size_t sl = blufi_extra_field_len(info->softap_ssid_len, BLUFI_WIFI_SSID_MAX); *p++ = BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_SSID; - *p++ = info->softap_ssid_len; - memcpy(p, info->softap_ssid, info->softap_ssid_len); - p += info->softap_ssid_len; + *p++ = (uint8_t)sl; + memcpy(p, info->softap_ssid, sl); + p += sl; } if (info->softap_passwd) { + size_t pl = blufi_extra_field_len(info->softap_passwd_len, BLUFI_WIFI_PASS_MAX); *p++ = BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_PASSWD; - *p++ = info->softap_passwd_len; - memcpy(p, info->softap_passwd, info->softap_passwd_len); - p += info->softap_passwd_len; + *p++ = (uint8_t)pl; + memcpy(p, info->softap_passwd, pl); + p += pl; } if (info->softap_authmode_set) { *p++ = BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_AUTH_MODE; @@ -403,14 +521,16 @@ static void btc_blufi_wifi_conn_report(uint8_t opmode, uint8_t sta_conn_state, u if (info->sta_conn_rssi_set) { *p++ = BLUFI_TYPE_DATA_SUBTYPE_STA_CONN_RSSI; *p++ = 1; - *p++ = info->sta_conn_rssi; + *p++ = (uint8_t)info->sta_conn_rssi; } } - if (p - data > data_len) { - BTC_TRACE_ERROR("%s len error %d %d\n", __func__, (int)(p - data), data_len); + if ((size_t)(p - data) != buf_len) { + BTC_TRACE_ERROR("%s len mismatch %d %u\n", __func__, (int)(p - data), (unsigned)buf_len); + osi_free(data); + return; } - btc_blufi_send_encap(type, data, data_len); + btc_blufi_send_encap(type, data, (int)buf_len); osi_free(data); } @@ -420,30 +540,48 @@ void btc_blufi_send_wifi_list(uint16_t apCount, esp_blufi_ap_record_t *list) uint8_t *data; int data_len; uint8_t *p; - // malloc size: (len + RSSI + ssid buffer) * apCount; - uint32_t malloc_size = (1 + 1 + sizeof(list->ssid)) * apCount; + uint32_t entry_sz; + uint32_t malloc_size; + + if (list == NULL || apCount == 0) { + return; + } + entry_sz = 1u + 1u + (uint32_t)sizeof(list[0].ssid); + + /* The encoded WiFi list is later wrapped by btc_blufi_send_encap(), + * which rejects total_data_len > UINT16_MAX (single BLUFI frame limit). + * Cap apCount against that real ceiling here instead of the previous + * (uint16_t)(UINT32_MAX / entry_sz) check, which the narrowing cast + * silently truncated to 18439 entries (~645 KB) and let the buffer + * be allocated only to be discarded later by send_encap. + * The apCount value returned by WiFi is very small in practice, but + * we still want to fail fast and avoid a pointless large allocation. */ + if ((uint32_t)apCount > (UINT16_MAX / entry_sz)) { + BTC_TRACE_ERROR("%s apCount %u exceeds BLUFI single-frame limit\n", + __func__, (unsigned)apCount); + return; + } + malloc_size = (uint32_t)apCount * entry_sz; p = data = osi_malloc(malloc_size); if (data == NULL) { BTC_TRACE_ERROR("malloc error\n"); return; } type = BLUFI_BUILD_TYPE(BLUFI_TYPE_DATA, BLUFI_TYPE_DATA_SUBTYPE_WIFI_LIST); - for (int i = 0; i < apCount; ++i) - { - uint32_t len = strlen((const char *)list[i].ssid); - data_len = (p - data); - //current_len + ssid + rssi + total_len_value - if((data_len + len + 1 + 1) > malloc_size) { + for (int i = 0; i < apCount; ++i) { + size_t len = strnlen((const char *)list[i].ssid, sizeof(list[i].ssid)); + data_len = (int)(p - data); + if ((uint32_t)data_len + len + 1u + 1u > malloc_size) { BTC_TRACE_ERROR("%s len error", __func__); osi_free(data); return; } - *p++ = len + 1; // length of ssid + rssi - *p++ = list[i].rssi; + *p++ = (uint8_t)(len + 1); /* length of ssid + rssi */ + *p++ = (uint8_t)list[i].rssi; memcpy(p, list[i].ssid, len); - p = p + len; + p += len; } - data_len = (p - data); + data_len = (int)(p - data); btc_blufi_send_encap(type, data, data_len); osi_free(data); } @@ -506,76 +644,97 @@ void btc_blufi_cb_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) switch (msg->act) { case ESP_BLUFI_EVENT_RECV_STA_SSID: + dst->sta_ssid.ssid = NULL; dst->sta_ssid.ssid = osi_malloc(src->sta_ssid.ssid_len); if (dst->sta_ssid.ssid == NULL) { BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act); + break; } memcpy(dst->sta_ssid.ssid, src->sta_ssid.ssid, src->sta_ssid.ssid_len); break; case ESP_BLUFI_EVENT_RECV_STA_PASSWD: + dst->sta_passwd.passwd = NULL; dst->sta_passwd.passwd = osi_malloc(src->sta_passwd.passwd_len); if (dst->sta_passwd.passwd == NULL) { BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act); + break; } memcpy(dst->sta_passwd.passwd, src->sta_passwd.passwd, src->sta_passwd.passwd_len); break; case ESP_BLUFI_EVENT_RECV_SOFTAP_SSID: + dst->softap_ssid.ssid = NULL; dst->softap_ssid.ssid = osi_malloc(src->softap_ssid.ssid_len); if (dst->softap_ssid.ssid == NULL) { BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act); + break; } memcpy(dst->softap_ssid.ssid, src->softap_ssid.ssid, src->softap_ssid.ssid_len); break; case ESP_BLUFI_EVENT_RECV_SOFTAP_PASSWD: + dst->softap_passwd.passwd = NULL; dst->softap_passwd.passwd = osi_malloc(src->softap_passwd.passwd_len); if (dst->softap_passwd.passwd == NULL) { BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act); + break; } memcpy(dst->softap_passwd.passwd, src->softap_passwd.passwd, src->softap_passwd.passwd_len); break; case ESP_BLUFI_EVENT_RECV_USERNAME: + dst->username.name = NULL; dst->username.name = osi_malloc(src->username.name_len); if (dst->username.name == NULL) { BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act); + break; } memcpy(dst->username.name, src->username.name, src->username.name_len); break; case ESP_BLUFI_EVENT_RECV_CA_CERT: + dst->ca.cert = NULL; dst->ca.cert = osi_malloc(src->ca.cert_len); if (dst->ca.cert == NULL) { BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act); + break; } memcpy(dst->ca.cert, src->ca.cert, src->ca.cert_len); break; case ESP_BLUFI_EVENT_RECV_CLIENT_CERT: + dst->client_cert.cert = NULL; dst->client_cert.cert = osi_malloc(src->client_cert.cert_len); if (dst->client_cert.cert == NULL) { BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act); + break; } memcpy(dst->client_cert.cert, src->client_cert.cert, src->client_cert.cert_len); break; case ESP_BLUFI_EVENT_RECV_SERVER_CERT: + dst->server_cert.cert = NULL; dst->server_cert.cert = osi_malloc(src->server_cert.cert_len); if (dst->server_cert.cert == NULL) { BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act); + break; } memcpy(dst->server_cert.cert, src->server_cert.cert, src->server_cert.cert_len); break; case ESP_BLUFI_EVENT_RECV_CLIENT_PRIV_KEY: + dst->client_pkey.pkey = NULL; dst->client_pkey.pkey = osi_malloc(src->client_pkey.pkey_len); if (dst->client_pkey.pkey == NULL) { BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act); + break; } memcpy(dst->client_pkey.pkey, src->client_pkey.pkey, src->client_pkey.pkey_len); break; case ESP_BLUFI_EVENT_RECV_SERVER_PRIV_KEY: + dst->server_pkey.pkey = NULL; dst->server_pkey.pkey = osi_malloc(src->server_pkey.pkey_len); if (dst->server_pkey.pkey == NULL) { BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act); + break; } memcpy(dst->server_pkey.pkey, src->server_pkey.pkey, src->server_pkey.pkey_len); break; case ESP_BLUFI_EVENT_RECV_CUSTOM_DATA: + dst->custom_data.data = NULL; dst->custom_data.data = osi_malloc(src->custom_data.data_len); if (dst->custom_data.data == NULL) { BTC_TRACE_ERROR("%s %d no mem\n", __func__, msg->act); @@ -831,8 +990,8 @@ void btc_blufi_call_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) } case BTC_BLUFI_ACT_SEND_WIFI_LIST:{ esp_blufi_ap_record_t *list = src->wifi_list.list; - src->wifi_list.list = NULL; - if (list == NULL || src->wifi_list.apCount <= 0) { + dst->wifi_list.list = NULL; + if (list == NULL || src->wifi_list.apCount == 0) { break; } dst->wifi_list.list = (esp_blufi_ap_record_t *)osi_malloc(sizeof(esp_blufi_ap_record_t) * src->wifi_list.apCount); diff --git a/components/bt/common/btc/profile/esp/blufi/blufi_protocol.c b/components/bt/common/btc/profile/esp/blufi/blufi_protocol.c index 0b0c08981e0..53a570258cc 100644 --- a/components/bt/common/btc/profile/esp/blufi/blufi_protocol.c +++ b/components/bt/common/btc/profile/esp/blufi/blufi_protocol.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -13,6 +13,7 @@ #include "btc_blufi_prf.h" #include "btc/btc_task.h" #include "btc/btc_manage.h" +#include "osi/allocator.h" #include "blufi_int.h" @@ -27,6 +28,14 @@ void btc_blufi_protocol_handler(uint8_t type, uint8_t *data, int len) int output_len = 0; bool need_free = false; +#define BLUFI_REQUIRE_LEN_AT_LEAST(_n) \ + if (len < (_n)) { \ + BTC_TRACE_ERROR("%s pkt %02x len too short: %d\n", \ + __func__, type, len); \ + btc_blufi_report_error(ESP_BLUFI_DATA_FORMAT_ERROR); \ + break; \ + } + switch (BLUFI_GET_TYPE(type)) { case BLUFI_TYPE_CTRL: switch (BLUFI_GET_SUBTYPE(type)) { @@ -34,9 +43,11 @@ void btc_blufi_protocol_handler(uint8_t type, uint8_t *data, int len) /* TODO: check sequence */ break; case BLUFI_TYPE_CTRL_SUBTYPE_SET_SEC_MODE: + BLUFI_REQUIRE_LEN_AT_LEAST(1); blufi_env.sec_mode = data[0]; break; case BLUFI_TYPE_CTRL_SUBTYPE_SET_WIFI_OPMODE: + BLUFI_REQUIRE_LEN_AT_LEAST(1); msg.sig = BTC_SIG_API_CB; msg.pid = BTC_PID_BLUFI; msg.act = ESP_BLUFI_EVENT_SET_WIFI_OPMODE; @@ -109,11 +120,16 @@ void btc_blufi_protocol_handler(uint8_t type, uint8_t *data, int len) btc_blufi_send_encap(BLUFI_BUILD_TYPE(BLUFI_TYPE_DATA, BLUFI_TYPE_DATA_SUBTYPE_NEG), output_data, output_len); } + if (need_free && output_data) { + osi_free(output_data); + output_data = NULL; + } break; case BLUFI_TYPE_DATA_SUBTYPE_STA_BSSID: if (len < 6) { BTC_TRACE_ERROR("%s STA_BSSID data too short: %d\n", __func__, len); - return; + btc_blufi_report_error(ESP_BLUFI_DATA_FORMAT_ERROR); + break; } msg.sig = BTC_SIG_API_CB; msg.pid = BTC_PID_BLUFI; @@ -159,6 +175,7 @@ void btc_blufi_protocol_handler(uint8_t type, uint8_t *data, int len) btc_transfer_context(&msg, ¶m, sizeof(esp_blufi_cb_param_t), btc_blufi_cb_deep_copy, btc_blufi_cb_deep_free); break; case BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_MAX_CONN_NUM: + BLUFI_REQUIRE_LEN_AT_LEAST(1); msg.sig = BTC_SIG_API_CB; msg.pid = BTC_PID_BLUFI; msg.act = ESP_BLUFI_EVENT_RECV_SOFTAP_MAX_CONN_NUM; @@ -167,6 +184,7 @@ void btc_blufi_protocol_handler(uint8_t type, uint8_t *data, int len) btc_transfer_context(&msg, ¶m, sizeof(esp_blufi_cb_param_t), NULL, NULL); break; case BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_AUTH_MODE: + BLUFI_REQUIRE_LEN_AT_LEAST(1); msg.sig = BTC_SIG_API_CB; msg.pid = BTC_PID_BLUFI; msg.act = ESP_BLUFI_EVENT_RECV_SOFTAP_AUTH_MODE; @@ -175,6 +193,7 @@ void btc_blufi_protocol_handler(uint8_t type, uint8_t *data, int len) btc_transfer_context(&msg, ¶m, sizeof(esp_blufi_cb_param_t), NULL, NULL); break; case BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_CHANNEL: + BLUFI_REQUIRE_LEN_AT_LEAST(1); msg.sig = BTC_SIG_API_CB; msg.pid = BTC_PID_BLUFI; msg.act = ESP_BLUFI_EVENT_RECV_SOFTAP_CHANNEL; @@ -252,6 +271,8 @@ void btc_blufi_protocol_handler(uint8_t type, uint8_t *data, int len) default: break; } + +#undef BLUFI_REQUIRE_LEN_AT_LEAST } #endif ///BLUFI_INCLUDED == TRUE diff --git a/components/bt/common/btc/profile/esp/blufi/include/esp_blufi.h b/components/bt/common/btc/profile/esp/blufi/include/esp_blufi.h index 8dc9a674ba8..5e800db9b10 100644 --- a/components/bt/common/btc/profile/esp/blufi/include/esp_blufi.h +++ b/components/bt/common/btc/profile/esp/blufi/include/esp_blufi.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -18,6 +18,7 @@ #ifdef CONFIG_BT_BLUEDROID_ENABLED #include "esp_gap_ble_api.h" +#include "esp_gatt_defs.h" #endif #ifdef __cplusplus @@ -61,7 +62,7 @@ void esp_blufi_btc_deinit(void); * - other : failed * */ -esp_err_t esp_blufi_close(uint8_t gatts_if, uint16_t conn_id); +esp_err_t esp_blufi_close(esp_gatt_if_t gatts_if, uint16_t conn_id); void esp_blufi_gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param); #endif @@ -90,6 +91,22 @@ void esp_blufi_adv_start_with_name(const char *name); void esp_blufi_send_encap(void *arg); +/* + * @brief Initiate BLE security request with the connected peer device. + * + * This function triggers the BLE Security Manager Protocol (SMP) procedure + * to establish a secure, encrypted connection with the specified remote device. + * It should be called after a BLE connection is established. + * + * @param[in] remote_bda Bluetooth device address of the connected peer. + * + * @return + * - ESP_OK: Security request initiated successfully + * - ESP_FAIL: Security request failed + * - ESP_ERR_INVALID_STATE: BluFi BLE SMP is not enabled + */ +esp_err_t esp_blufi_start_security_request(esp_blufi_bd_addr_t remote_bda); + #ifdef CONFIG_BT_NIMBLE_ENABLED /** * @brief Handle gap event for BluFi. diff --git a/components/bt/common/btc/profile/esp/include/btc_blufi_prf.h b/components/bt/common/btc/profile/esp/include/btc_blufi_prf.h index 1174f01ee0b..9112d75aa78 100644 --- a/components/bt/common/btc/profile/esp/include/btc_blufi_prf.h +++ b/components/bt/common/btc/profile/esp/include/btc_blufi_prf.h @@ -30,7 +30,7 @@ extern "C" { #define BT_BD_ADDR_HEX(addr) addr[0], addr[1], addr[2], addr[3], addr[4], addr[5] #define GATT_UUID_CHAR_CLIENT_CONFIG 0x2902 /* Client Characteristic Configuration */ -//define the blufi serivce uuid +//define the blufi service uuid #define BLUFI_SERVICE_UUID 0xFFFF //define the blufi Char uuid (PHONE to ESP32) #define BLUFI_CHAR_P2E_UUID 0xFF01 @@ -98,6 +98,7 @@ void btc_blufi_cb_handler(btc_msg_t *msg); void btc_blufi_call_handler(btc_msg_t *msg); void btc_blufi_set_callbacks(esp_blufi_callbacks_t *callbacks); +void btc_blufi_report_error(esp_blufi_error_state_t state); void btc_blufi_recv_handler(uint8_t *data, int len); void btc_blufi_send_notify(uint8_t *pkt, int pkt_len); void btc_blufi_call_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src); diff --git a/components/bt/common/hci_log/bt_hci_log.c b/components/bt/common/hci_log/bt_hci_log.c index fef3760b3f2..5840adab856 100644 --- a/components/bt/common/hci_log/bt_hci_log.c +++ b/components/bt/common/hci_log/bt_hci_log.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -13,9 +13,14 @@ #include "esp_timer.h" #if (BT_HCI_LOG_INCLUDED == TRUE) + +static const char *const TAG = "bt_hci_log"; + #define BT_HCI_LOG_PRINT_TAG (1) #define BT_HCI_LOG_DATA_BUF_SIZE (1024 * HCI_LOG_DATA_BUFFER_SIZE) #define BT_HCI_LOG_ADV_BUF_SIZE (1024 * HCI_LOG_ADV_BUFFER_SIZE) +/* Max payload per HCI log line; larger inputs are dropped (not logged). */ +#define BT_HCI_LOG_RECORD_PAYLOAD_MAX (2048U) typedef struct { osi_mutex_t mutex_lock; @@ -64,8 +69,14 @@ esp_err_t bt_hci_log_init(void) g_bt_hci_log_adv_ctl.buf_size = BT_HCI_LOG_ADV_BUF_SIZE; g_bt_hci_log_adv_ctl.p_hci_log_buffer = g_bt_hci_log_adv_buffer; - osi_mutex_new((osi_mutex_t *)&g_bt_hci_log_data_ctl.mutex_lock); - osi_mutex_new((osi_mutex_t *)&g_bt_hci_log_adv_ctl.mutex_lock); + if (osi_mutex_new((osi_mutex_t *)&g_bt_hci_log_data_ctl.mutex_lock) != 0) { + bt_hci_log_deinit(); + return ESP_FAIL; + } + if (osi_mutex_new((osi_mutex_t *)&g_bt_hci_log_adv_ctl.mutex_lock) != 0) { + bt_hci_log_deinit(); + return ESP_FAIL; + } return ESP_OK; } @@ -191,9 +202,16 @@ void bt_hci_log_record_string(bt_hci_log_t *p_hci_log_ctl, char *string) g_hci_log_buffer = p_hci_log_ctl->p_hci_log_buffer; - while (*string != '\0') { - g_hci_log_buffer[p_hci_log_ctl->log_record_in] = *string; - ++string; + if (string == NULL) { + return; + } + + /* Avoid unbounded memory scan if string is not NUL-terminated. */ + const size_t max_len = 256; + size_t len = strnlen(string, max_len); + + for (size_t i = 0; i < len; i++) { + g_hci_log_buffer[p_hci_log_ctl->log_record_in] = (uint8_t)string[i]; if (++p_hci_log_ctl->log_record_in >= p_hci_log_ctl->buf_size) { p_hci_log_ctl->log_record_in = 0; @@ -211,6 +229,7 @@ esp_err_t IRAM_ATTR bt_hci_log_record_data(bt_hci_log_t *p_hci_log_ctl, char *st uint8_t *g_hci_log_buffer; int64_t ts; uint8_t *temp_buf; + uint16_t record_len; if (!p_hci_log_ctl->p_hci_log_buffer) { return ESP_FAIL; @@ -221,21 +240,34 @@ esp_err_t IRAM_ATTR bt_hci_log_record_data(bt_hci_log_t *p_hci_log_ctl, char *st if (!g_hci_log_buffer) { return ESP_FAIL; } + if (p_hci_log_ctl->mutex_lock == NULL) { + return ESP_FAIL; + } + + if ((data_len == 0) || (data == NULL)) { + return ESP_ERR_INVALID_ARG; + } ts = esp_timer_get_time(); - temp_buf = (uint8_t *)malloc(data_len + 8); + if (data_len > BT_HCI_LOG_RECORD_PAYLOAD_MAX) { + ESP_LOGW(TAG, "HCI log record dropped: data_len=%u (max %u)", + (unsigned)data_len, (unsigned)BT_HCI_LOG_RECORD_PAYLOAD_MAX); + return ESP_FAIL; + } + + record_len = (uint16_t)((uint32_t)data_len + 8U); + + temp_buf = (uint8_t *)malloc((size_t)record_len); if (!temp_buf) { return ESP_ERR_NO_MEM; } - memset(temp_buf, 0x0, data_len + 8); + memset(temp_buf, 0x0, (size_t)record_len); memcpy(temp_buf, &ts, 8); memcpy(temp_buf + 8, data, data_len); - data_len += 8; - mutex_lock = p_hci_log_ctl->mutex_lock; osi_mutex_lock(&mutex_lock, OSI_MUTEX_MAX_TIMEOUT); @@ -267,7 +299,7 @@ esp_err_t IRAM_ATTR bt_hci_log_record_data(bt_hci_log_t *p_hci_log_ctl, char *st bt_hci_log_record_string(p_hci_log_ctl, str); } - bt_hci_log_record_hex(p_hci_log_ctl, temp_buf, data_len); + bt_hci_log_record_hex(p_hci_log_ctl, temp_buf, record_len); g_hci_log_buffer[p_hci_log_ctl->log_record_in] = '\n'; @@ -296,6 +328,9 @@ void bt_hci_log_data_show(bt_hci_log_t *p_hci_log_ctl) if (!p_hci_log_ctl->p_hci_log_buffer) { return; } + if (p_hci_log_ctl->mutex_lock == NULL) { + return; + } osi_mutex_t mutex_lock = p_hci_log_ctl->mutex_lock; diff --git a/components/bt/common/include/esp_assume.h b/components/bt/common/include/esp_assume.h new file mode 100644 index 00000000000..7ef41004445 --- /dev/null +++ b/components/bt/common/include/esp_assume.h @@ -0,0 +1,18 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include + +#if !defined(ESP_ASSUME_NONNULL) +#if defined(CONFIG_COMPILER_OPTIMIZATION_DEBUG) || defined(CONFIG_BT_NIMBLE_DEBUG) || !defined(NDEBUG) +/* Debug: fail fast if upstream contract is broken */ +#define ESP_ASSUME_NONNULL(p) do { assert((p) != NULL); } while (0) +#else +/* Release: contract only (no extra code size) */ +#define ESP_ASSUME_NONNULL(p) do { (void)(p); } while (0) +#endif +#endif diff --git a/components/bt/common/osi/alarm.c b/components/bt/common/osi/alarm.c index 464bbc2c897..6fe46625e74 100644 --- a/components/bt/common/osi/alarm.c +++ b/components/bt/common/osi/alarm.c @@ -323,9 +323,20 @@ bool osi_alarm_is_active(osi_alarm_t *alarm) { assert(alarm != NULL); - if (alarm->alarm_hdl != NULL) { - return esp_timer_is_active(alarm->alarm_hdl); + assert(alarm_mutex != NULL); + + bool active = false; + osi_mutex_lock(&alarm_mutex, OSI_MUTEX_MAX_TIMEOUT); + if (alarm_state != ALARM_STATE_OPEN) { + active = false; + goto end; } - return false; + if (alarm->alarm_hdl != NULL) { + active = esp_timer_is_active(alarm->alarm_hdl); + } + +end: + osi_mutex_unlock(&alarm_mutex); + return active; } diff --git a/components/bt/common/osi/config.c b/components/bt/common/osi/config.c index fb6727a7a3d..38c981bc7b8 100644 --- a/components/bt/common/osi/config.c +++ b/components/bt/common/osi/config.c @@ -16,6 +16,7 @@ #include #include "bt_common.h" +#include "esp_assume.h" #include "osi/allocator.h" #include "osi/config.h" #include "osi/list.h" @@ -40,7 +41,7 @@ struct config_t { // Empty definition; this type is aliased to list_node_t. struct config_section_iter_t {}; -static void config_parse(nvs_handle_t fp, config_t *config); +static bool config_parse(nvs_handle_t fp, config_t *config); static section_t *section_new(const char *name); static void section_free(void *ptr); @@ -94,8 +95,12 @@ config_t *config_new(const char *filename) return NULL; } - config_parse(fp, config); + bool ok = config_parse(fp, config); nvs_close(fp); + if (!ok) { + config_free(config); + return NULL; + } return config; } @@ -136,7 +141,7 @@ bool config_has_key_in_section(config_t *config, const char *key, char *key_valu entry_t *entry = list_node(node); OSI_TRACE_DEBUG("entry->key = %s, entry->value = %s", entry->key, entry->value); if (!strcmp(entry->key, key) && !strcmp(entry->value, key_value)) { - OSI_TRACE_DEBUG("%s, the irk aready in the flash.", __func__); + OSI_TRACE_DEBUG("%s, the irk already in the flash.", __func__); return true; } } @@ -221,6 +226,10 @@ void config_set_string(config_t *config, const char *section, const char *key, c section_t *sec = section_find(config, section); if (!sec) { sec = section_new(section); + if (sec == NULL) { + OSI_TRACE_ERROR("%s unable to allocate section\n", __func__); + return; + } if (insert_back) { list_append(config->sections, sec); } else { @@ -231,13 +240,22 @@ void config_set_string(config_t *config, const char *section, const char *key, c for (const list_node_t *node = list_begin(sec->entries); node != list_end(sec->entries); node = list_next(node)) { entry_t *entry = list_node(node); if (!strcmp(entry->key, key)) { + char *new_value = osi_strdup(value); + if (new_value == NULL) { + OSI_TRACE_ERROR("%s unable to allocate entry value\n", __func__); + return; + } osi_free(entry->value); - entry->value = osi_strdup(value); + entry->value = new_value; return; } } entry_t *entry = entry_new(key, value); + if (entry == NULL) { + OSI_TRACE_ERROR("%s unable to allocate entry\n", __func__); + return; + } list_append(sec->entries, entry); } @@ -327,32 +345,57 @@ const char *config_section_name(const config_section_node_t *node) return section->name; } -static int get_config_size(const config_t *config) +static bool get_config_size(const config_t *config, size_t *out_size) { assert(config != NULL); + assert(out_size != NULL); - int w_len = 0, total_size = 0; + size_t total_size = 0; for (const list_node_t *node = list_begin(config->sections); node != list_end(config->sections); node = list_next(node)) { const section_t *section = (const section_t *)list_node(node); - w_len = strlen(section->name) + strlen("[]\n");// format "[section->name]\n" + /* CONTRACT / PRECONDITION: + * - section->name is guaranteed non-NULL. + * - Upstream guard: section_new() stores osi_strdup(name) from config_set_string(). + */ + ESP_ASSUME_NONNULL(section->name); + size_t w_len = strlen(section->name) + strlen("[]\n"); // format "[section->name]\n" + if (total_size > SIZE_MAX - w_len) { + return false; + } total_size += w_len; for (const list_node_t *enode = list_begin(section->entries); enode != list_end(section->entries); enode = list_next(enode)) { const entry_t *entry = (const entry_t *)list_node(enode); - w_len = strlen(entry->key) + strlen(entry->value) + strlen(" = \n");// format "entry->key = entry->value\n" + /* CONTRACT / PRECONDITION: + * - entry->key/value are guaranteed non-NULL. + * - Upstream guard: entry_new() stores osi_strdup(key/value) from config_set_string(). + */ + ESP_ASSUME_NONNULL(entry->key); + ESP_ASSUME_NONNULL(entry->value); + w_len = strlen(entry->key) + strlen(entry->value) + strlen(" = \n"); // format "entry->key = entry->value\n" + if (total_size > SIZE_MAX - w_len) { + return false; + } total_size += w_len; } // Only add a separating newline if there are more sections. if (list_next(node) != list_end(config->sections)) { - total_size ++; //'\n' + if (total_size == SIZE_MAX) { + return false; + } + total_size++; // '\n' } else { break; } } - total_size ++; //'\0' - return total_size; + if (total_size == SIZE_MAX) { + return false; + } + total_size++; // '\0' + *out_size = total_size; + return true; } static int get_config_size_from_flash(nvs_handle_t fp) @@ -364,7 +407,7 @@ static int get_config_size_from_flash(nvs_handle_t fp) char *keyname = osi_calloc(keyname_bufsz); if (!keyname){ OSI_TRACE_ERROR("%s, malloc error\n", __func__); - return 0; + return -1; } size_t length = CONFIG_FILE_DEFAULE_LENGTH; size_t total_length = 0; @@ -378,7 +421,7 @@ static int get_config_size_from_flash(nvs_handle_t fp) if (err != ESP_OK) { OSI_TRACE_ERROR("%s, error %d\n", __func__, err); osi_free(keyname); - return 0; + return -2; } total_length += length; while (length == CONFIG_FILE_MAX_SIZE) { @@ -392,7 +435,7 @@ static int get_config_size_from_flash(nvs_handle_t fp) if (err != ESP_OK) { OSI_TRACE_ERROR("%s, error %d\n", __func__, err); osi_free(keyname); - return 0; + return -3; } total_length += length; } @@ -409,12 +452,21 @@ bool config_save(const config_t *config, const char *filename) esp_err_t err; int err_code = 0; nvs_handle_t fp; - char *line = osi_calloc(1024); + char *buf = NULL; + bool nvs_opened = false; const size_t keyname_bufsz = sizeof(CONFIG_KEY) + 5 + 1; // including log10(sizeof(i)) char *keyname = osi_calloc(keyname_bufsz); - int config_size = get_config_size(config); - char *buf = osi_calloc(config_size); - if (!line || !buf || !keyname) { + if (!keyname) { + err_code |= 0x01; + goto error; + } + size_t config_size = 0; + if (!get_config_size(config, &config_size) || config_size == 0) { + err_code |= 0x01; + goto error; + } + buf = osi_calloc(config_size); + if (!buf) { err_code |= 0x01; goto error; } @@ -428,63 +480,67 @@ bool config_save(const config_t *config, const char *filename) err_code |= 0x02; goto error; } + nvs_opened = true; - int w_cnt, w_cnt_total = 0; + int w_cnt; + size_t w_cnt_total = 0; for (const list_node_t *node = list_begin(config->sections); node != list_end(config->sections); node = list_next(node)) { const section_t *section = (const section_t *)list_node(node); - w_cnt = snprintf(line, 1024, "[%s]\n", section->name); - if(w_cnt < 0) { - OSI_TRACE_ERROR("snprintf error w_cnt %d.",w_cnt); - err_code |= 0x10; - goto error; - } - if(w_cnt_total + w_cnt > config_size) { - OSI_TRACE_ERROR("%s, memcpy size (w_cnt + w_cnt_total = %d) is larger than buffer size (config_size = %d).", __func__, (w_cnt + w_cnt_total), config_size); + size_t remaining = (w_cnt_total < config_size) ? (config_size - w_cnt_total) : 0; + if (remaining == 0) { err_code |= 0x20; goto error; } - OSI_TRACE_DEBUG("section name: %s, w_cnt + w_cnt_total = %d\n", section->name, w_cnt + w_cnt_total); - memcpy(buf + w_cnt_total, line, w_cnt); - w_cnt_total += w_cnt; + w_cnt = snprintf(buf + w_cnt_total, remaining, "[%s]\n", section->name); + if (w_cnt < 0 || (size_t)w_cnt >= remaining) { + err_code |= 0x20; + goto error; + } + OSI_TRACE_DEBUG("section name: %s, w_cnt + w_cnt_total = %d\n", section->name, (int)(w_cnt + w_cnt_total)); + w_cnt_total += (size_t)w_cnt; for (const list_node_t *enode = list_begin(section->entries); enode != list_end(section->entries); enode = list_next(enode)) { const entry_t *entry = (const entry_t *)list_node(enode); OSI_TRACE_DEBUG("(key, val): (%s, %s)\n", entry->key, entry->value); - w_cnt = snprintf(line, 1024, "%s = %s\n", entry->key, entry->value); - if(w_cnt < 0) { - OSI_TRACE_ERROR("snprintf error w_cnt %d.",w_cnt); - err_code |= 0x10; - goto error; - } - if(w_cnt_total + w_cnt > config_size) { - OSI_TRACE_ERROR("%s, memcpy size (w_cnt + w_cnt_total = %d) is larger than buffer size.(config_size = %d)", __func__, (w_cnt + w_cnt_total), config_size); + remaining = (w_cnt_total < config_size) ? (config_size - w_cnt_total) : 0; + if (remaining == 0) { err_code |= 0x20; goto error; } - OSI_TRACE_DEBUG("%s, w_cnt + w_cnt_total = %d", __func__, w_cnt + w_cnt_total); - memcpy(buf + w_cnt_total, line, w_cnt); - w_cnt_total += w_cnt; + w_cnt = snprintf(buf + w_cnt_total, remaining, "%s = %s\n", entry->key, entry->value); + if (w_cnt < 0 || (size_t)w_cnt >= remaining) { + err_code |= 0x20; + goto error; + } + OSI_TRACE_DEBUG("%s, w_cnt + w_cnt_total = %d", __func__, (int)(w_cnt + w_cnt_total)); + w_cnt_total += (size_t)w_cnt; } // Only add a separating newline if there are more sections. if (list_next(node) != list_end(config->sections)) { - buf[w_cnt_total] = '\n'; - w_cnt_total += 1; + if (w_cnt_total + 1 >= config_size) { + err_code |= 0x20; + goto error; + } + buf[w_cnt_total++] = '\n'; } else { break; } } + if (w_cnt_total >= config_size) { + err_code |= 0x20; + goto error; + } buf[w_cnt_total] = '\0'; if (w_cnt_total < CONFIG_FILE_MAX_SIZE) { snprintf(keyname, keyname_bufsz, "%s%d", CONFIG_KEY, 0); err = nvs_set_blob(fp, keyname, buf, w_cnt_total); if (err != ESP_OK) { - nvs_close(fp); err_code |= 0x04; goto error; } }else { - int count = (w_cnt_total / CONFIG_FILE_MAX_SIZE); + int count = (int)(w_cnt_total / CONFIG_FILE_MAX_SIZE); assert(count <= 0xFF); for (uint8_t i = 0; i <= count; i++) { @@ -497,7 +553,6 @@ bool config_save(const config_t *config, const char *filename) OSI_TRACE_DEBUG("save keyname = %s, i = %d, %d\n", keyname, i, CONFIG_FILE_MAX_SIZE); } if (err != ESP_OK) { - nvs_close(fp); err_code |= 0x04; goto error; } @@ -506,24 +561,23 @@ bool config_save(const config_t *config, const char *filename) err = nvs_commit(fp); if (err != ESP_OK) { - nvs_close(fp); err_code |= 0x08; goto error; } nvs_close(fp); - osi_free(line); + nvs_opened = false; osi_free(buf); osi_free(keyname); return true; error: + if (nvs_opened) { + nvs_close(fp); + } if (buf) { osi_free(buf); } - if (line) { - osi_free(line); - } if (keyname) { osi_free(keyname); } @@ -552,7 +606,7 @@ static char *trim(char *str) return str; } -static void config_parse(nvs_handle_t fp, config_t *config) +static bool config_parse(nvs_handle_t fp, config_t *config) { assert(fp != 0); assert(config != NULL); @@ -570,9 +624,14 @@ static void config_parse(nvs_handle_t fp, config_t *config) int buf_size = get_config_size_from_flash(fp); char *buf = NULL; - if(buf_size == 0) { //First use nvs + if (buf_size < 0) { + err_code |= 0x04; goto error; } + + if(buf_size == 0) { //First use nvs + goto ok; + } buf = osi_calloc(buf_size); if (!line || !section || !buf || !keyname) { err_code |= 0x01; @@ -581,7 +640,7 @@ static void config_parse(nvs_handle_t fp, config_t *config) snprintf(keyname, keyname_bufsz, "%s%d", CONFIG_KEY, 0); err = nvs_get_blob(fp, keyname, buf, &length); if (err == ESP_ERR_NVS_NOT_FOUND) { - goto error; + goto ok; } if (err != ESP_OK) { err_code |= 0x02; @@ -644,6 +703,21 @@ static void config_parse(nvs_handle_t fp, config_t *config) } } +ok: + if (buf) { + osi_free(buf); + } + if (line) { + osi_free(line); + } + if (section) { + osi_free(section); + } + if (keyname) { + osi_free(keyname); + } + return true; + error: if (buf) { osi_free(buf); @@ -660,6 +734,7 @@ error: if (err_code) { OSI_TRACE_ERROR("%s returned with err code: %d\n", __func__, err_code); } + return false; } static section_t *section_new(const char *name) @@ -671,6 +746,10 @@ static section_t *section_new(const char *name) section->name = osi_strdup(name); section->entries = list_new(entry_free); + if (section->name == NULL || section->entries == NULL) { + section_free(section); + return NULL; + } return section; } @@ -707,6 +786,10 @@ static entry_t *entry_new(const char *key, const char *value) entry->key = osi_strdup(key); entry->value = osi_strdup(value); + if (entry->key == NULL || entry->value == NULL) { + entry_free(entry); + return NULL; + } return entry; } diff --git a/components/bt/common/osi/fixed_queue.c b/components/bt/common/osi/fixed_queue.c index 0f1ba9b7c22..ae7ca135047 100644 --- a/components/bt/common/osi/fixed_queue.c +++ b/components/bt/common/osi/fixed_queue.c @@ -22,6 +22,7 @@ #include "osi/osi.h" #include "osi/mutex.h" #include "osi/semaphore.h" +#include "esp_assume.h" typedef struct fixed_queue_t { @@ -43,6 +44,12 @@ fixed_queue_t *fixed_queue_new(size_t capacity) } osi_mutex_new(&ret->lock); + /* CONTRACT / PRECONDITION: + * - ret->lock is guaranteed non-NULL after successful osi_mutex_new(). + * - Rationale: treat mutex allocation failure as fatal in debug builds; + * release builds do not add extra code size. + */ + ESP_ASSUME_NONNULL(ret->lock); ret->capacity = capacity; ret->list = list_new(NULL); diff --git a/components/bt/common/osi/hash_map.c b/components/bt/common/osi/hash_map.c index 59942bfd41b..df73eff7eb3 100644 --- a/components/bt/common/osi/hash_map.c +++ b/components/bt/common/osi/hash_map.c @@ -138,26 +138,37 @@ bool hash_map_set(hash_map_t *hash_map, const void *key, void *data) } list_t *hash_bucket_list = hash_map->bucket[hash_key].list; - hash_map_entry_t *hash_map_entry = find_bucket_entry_(hash_bucket_list, key); + /* Allocate the new entry FIRST, before touching any existing entry. + * This preserves the atomicity of hash_map_set: on failure the map + * stays exactly as it was, and the caller's previously-stored + * key/data remain valid. */ + hash_map_entry_t *new_entry = osi_calloc(sizeof(hash_map_entry_t)); + if (new_entry == NULL) { + return false; + } + new_entry->key = key; + new_entry->data = data; + new_entry->hash_map = hash_map; - if (hash_map_entry) { - // Calls hash_map callback to delete the hash_map_entry. - bool rc = list_remove(hash_bucket_list, hash_map_entry); + hash_map_entry_t *old_entry = find_bucket_entry_(hash_bucket_list, key); + + if (!list_append(hash_bucket_list, new_entry)) { + osi_free(new_entry); + return false; + } + + /* Only after the new entry has been successfully inserted do we + * destroy the old one (which frees the previous key/data via + * bucket_free_). For a brand-new key, bump hash_size instead. */ + if (old_entry) { + bool rc = list_remove(hash_bucket_list, old_entry); assert(rc == true); (void)rc; } else { hash_map->hash_size++; } - hash_map_entry = osi_calloc(sizeof(hash_map_entry_t)); - if (hash_map_entry == NULL) { - return false; - } - hash_map_entry->key = key; - hash_map_entry->data = data; - hash_map_entry->hash_map = hash_map; - - return list_append(hash_bucket_list, hash_map_entry); + return true; } bool hash_map_erase(hash_map_t *hash_map, const void *key) diff --git a/components/bt/common/osi/list.c b/components/bt/common/osi/list.c index c951d2126fc..f7ffc32d898 100644 --- a/components/bt/common/osi/list.c +++ b/components/bt/common/osi/list.c @@ -181,8 +181,9 @@ bool list_remove(list_t *list, void *data) } if (list->head->data == data) { + const bool was_tail = (list->tail == list->head); list_node_t *next = list_free_node(list, list->head); - if (list->tail == list->head) { + if (was_tail) { list->tail = next; } list->head = next; @@ -191,8 +192,9 @@ bool list_remove(list_t *list, void *data) for (list_node_t *prev = list->head, *node = list->head->next; node; prev = node, node = node->next) if (node->data == data) { + const bool was_tail = (list->tail == node); prev->next = list_free_node(list, node); - if (list->tail == node) { + if (was_tail) { list->tail = prev; } return true; @@ -211,8 +213,9 @@ bool list_delete(list_t *list, void *data) } if (list->head->data == data) { + const bool was_tail = (list->tail == list->head); list_node_t *next = list_delete_node(list, list->head); - if (list->tail == list->head) { + if (was_tail) { list->tail = next; } list->head = next; @@ -221,8 +224,9 @@ bool list_delete(list_t *list, void *data) for (list_node_t *prev = list->head, *node = list->head->next; node; prev = node, node = node->next) if (node->data == data) { + const bool was_tail = (list->tail == node); prev->next = list_delete_node(list, node); - if (list->tail == node) { + if (was_tail) { list->tail = prev; } return true; diff --git a/components/bt/common/osi/mutex.c b/components/bt/common/osi/mutex.c index 5a4de81ee5b..eaa2b8a6dca 100644 --- a/components/bt/common/osi/mutex.c +++ b/components/bt/common/osi/mutex.c @@ -16,6 +16,8 @@ * ******************************************************************************/ +#include + #include "osi/mutex.h" @@ -90,15 +92,21 @@ int osi_mutex_global_init(void) void osi_mutex_global_deinit(void) { + if (gl_mutex == NULL) { + return; + } vSemaphoreDelete(gl_mutex); + gl_mutex = NULL; } void osi_mutex_global_lock(void) { + assert(gl_mutex != NULL); xSemaphoreTakeRecursive(gl_mutex, portMAX_DELAY); } void osi_mutex_global_unlock(void) { + assert(gl_mutex != NULL); xSemaphoreGiveRecursive(gl_mutex); } diff --git a/components/bt/host/bluedroid/Kconfig.in b/components/bt/host/bluedroid/Kconfig.in index 7a0801f57b7..9b63e24e76a 100644 --- a/components/bt/host/bluedroid/Kconfig.in +++ b/components/bt/host/bluedroid/Kconfig.in @@ -222,6 +222,16 @@ config BT_BLE_BLUFI_ENABLE help This option can be close when the app does not require blufi function. +config BT_BLUFI_BLE_SMP_ENABLE + bool "Enable BLE SMP support for BluFi" + depends on BT_BLE_BLUFI_ENABLE && BT_BLE_SMP_ENABLE + default n + help + Enable BLE Security Manager Protocol (SMP) for BluFi. + When enabled, BluFi will support BLE pairing and encryption + before Wi-Fi provisioning, providing a more secure provisioning process. + This feature is only supported with the Bluedroid host. + config BT_GATT_MAX_SR_PROFILES int "Max GATT Server Profiles" depends on BT_GATTS_ENABLE && BT_BLUEDROID_ENABLED diff --git a/components/bt/host/bluedroid/api/esp_bluedroid_hci.c b/components/bt/host/bluedroid/api/esp_bluedroid_hci.c index 4ddc169a97c..5476f43409d 100644 --- a/components/bt/host/bluedroid/api/esp_bluedroid_hci.c +++ b/components/bt/host/bluedroid/api/esp_bluedroid_hci.c @@ -68,7 +68,9 @@ bool hci_host_check_send_available(void) void hci_host_send_packet(uint8_t *data, uint16_t len) { #if (BT_HCI_LOG_INCLUDED == TRUE) - bt_hci_log_record_hci_data(data[0], &data[1], len - 1); + if (data != NULL && len > 1) { + bt_hci_log_record_hci_data(data[0], &data[1], (uint16_t)(len - 1)); + } #endif #if CONFIG_BT_BLE_LOG_SPI_OUT_HCI_ENABLED ble_log_spi_out_hci_write(BLE_LOG_SPI_OUT_SOURCE_HCI_DOWNSTREAM, data, len); diff --git a/components/bt/host/bluedroid/api/esp_bt_main.c b/components/bt/host/bluedroid/api/esp_bt_main.c index 61c07db7a6f..a91b322a5b4 100644 --- a/components/bt/host/bluedroid/api/esp_bt_main.c +++ b/components/bt/host/bluedroid/api/esp_bt_main.c @@ -208,7 +208,9 @@ esp_err_t esp_bluedroid_init_with_cfg(esp_bluedroid_config_t *cfg) } #if (BT_HCI_LOG_INCLUDED == TRUE) - bt_hci_log_init(); + if (bt_hci_log_init() != ESP_OK) { + LOG_WARN("HCI log facility unavailable, continuing without HCI log\n"); + } #endif // (BT_HCI_LOG_INCLUDED == TRUE) s_bt_host_state = ESP_BLUEDROID_STATUS_INITIALIZED; diff --git a/components/bt/host/bluedroid/btc/core/btc_config.c b/components/bt/host/bluedroid/btc/core/btc_config.c index c7edd3f6691..c55b80023d5 100644 --- a/components/bt/host/bluedroid/btc/core/btc_config.c +++ b/components/bt/host/bluedroid/btc/core/btc_config.c @@ -92,7 +92,7 @@ bool btc_config_init(void) config = config_new(CONFIG_FILE_PATH); if (!config) { - BTC_TRACE_WARNING("%s unable to load config file; starting unconfigured.\n", __func__); + BTC_TRACE_WARNING("%s unable to load/parse config; starting unconfigured without overwriting NVS.\n", __func__); config = config_new_empty(); if (!config) { BTC_TRACE_ERROR("%s unable to allocate a config object.\n", __func__); @@ -345,14 +345,17 @@ int btc_config_clear(void) { assert(config != NULL); - config_free(config); + btc_config_lock(); + config_free(config); config = config_new_empty(); if (config == NULL) { - return false; + btc_config_unlock(); + return -1; } - int ret = config_save(config, CONFIG_FILE_PATH); - return ret; + bool ret = config_save(config, CONFIG_FILE_PATH); + btc_config_unlock(); + return ret ? 0 : -1; } void btc_config_lock(void) diff --git a/components/bt/host/bluedroid/btc/profile/std/gap/btc_gap_ble.c b/components/bt/host/bluedroid/btc/profile/std/gap/btc_gap_ble.c index b886849626b..057a1248b60 100644 --- a/components/bt/host/bluedroid/btc/profile/std/gap/btc_gap_ble.c +++ b/components/bt/host/bluedroid/btc/profile/std/gap/btc_gap_ble.c @@ -593,7 +593,7 @@ static void btc_gap_ble_adv_pkt_handler(void *arg) } } - if (pkt_queue_length(p_env->adv_rpt_queue) != 0) { + if (p_env->adv_rpt_ready && pkt_queue_length(p_env->adv_rpt_queue) != 0) { osi_thread_post_event(p_env->adv_rpt_ready, OSI_THREAD_MAX_TIMEOUT); } } @@ -628,7 +628,9 @@ static void btc_process_adv_rpt_pkt(tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH *p_ } while (0); pkt_queue_enqueue(p_env->adv_rpt_queue, linked_pkt); - osi_thread_post_event(p_env->adv_rpt_ready, OSI_THREAD_MAX_TIMEOUT); + if (p_env->adv_rpt_ready) { + osi_thread_post_event(p_env->adv_rpt_ready, OSI_THREAD_MAX_TIMEOUT); + } } static void btc_search_callback(tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH *p_data) @@ -3412,15 +3414,34 @@ void btc_gap_ble_deinit(void) #if (BLE_42_SCAN_EN == TRUE) btc_gap_ble_env_t *p_env = &btc_gap_ble_env; - osi_event_delete(p_env->adv_rpt_ready); + struct osi_event *adv_evt = p_env->adv_rpt_ready; p_env->adv_rpt_ready = NULL; + if (adv_evt) { + osi_event_delete(adv_evt); + } - pkt_queue_destroy(p_env->adv_rpt_queue, NULL); - p_env->adv_rpt_queue = NULL; + if (p_env->adv_rpt_queue) { + pkt_queue_destroy(p_env->adv_rpt_queue, NULL); + p_env->adv_rpt_queue = NULL; + } #endif // #if (BLE_42_SCAN_EN == TRUE) #if (BLE_42_ADV_EN == TRUE) + /* Under BTC_DYNAMIC_MEMORY, gl_bta_adv_data is a macro that dereferences + * gl_bta_adv_data_ptr. Guard against the case where the pointer is NULL + * (e.g. btc_init_mem() failed midway, or btc_deinit() is invoked twice) + * to avoid &(*NULL) UB before reaching btc_cleanup_adv_data()'s NULL + * check. */ +#if (BTC_DYNAMIC_MEMORY == TRUE) + if (gl_bta_adv_data_ptr) { + btc_cleanup_adv_data(&gl_bta_adv_data); + } + if (gl_bta_scan_rsp_data_ptr) { + btc_cleanup_adv_data(&gl_bta_scan_rsp_data); + } +#else btc_cleanup_adv_data(&gl_bta_adv_data); btc_cleanup_adv_data(&gl_bta_scan_rsp_data); +#endif // #if (BTC_DYNAMIC_MEMORY == TRUE) #endif // #if (BLE_42_ADV_EN == TRUE) #endif // #if (BLE_42_FEATURE_SUPPORT == TRUE) } diff --git a/components/bt/host/bluedroid/stack/btu/btu_task.c b/components/bt/host/bluedroid/stack/btu/btu_task.c index 2fe59a92601..27d3ca79fa5 100644 --- a/components/bt/host/bluedroid/stack/btu/btu_task.c +++ b/components/bt/host/bluedroid/stack/btu/btu_task.c @@ -461,11 +461,10 @@ void btu_start_timer(TIMER_LIST_ENT *p_tle, UINT16 type, UINT32 timeout_sec) return; } } - osi_mutex_unlock(&btu_general_alarm_lock); - alarm = hash_map_get(btu_general_alarm_hash_map, p_tle); if (alarm == NULL) { HCI_TRACE_ERROR("%s Unable to create alarm", __func__); + osi_mutex_unlock(&btu_general_alarm_lock); return; } osi_alarm_cancel(alarm); @@ -475,6 +474,7 @@ void btu_start_timer(TIMER_LIST_ENT *p_tle, UINT16 type, UINT32 timeout_sec) p_tle->ticks = timeout_sec; p_tle->in_use = TRUE; osi_alarm_set(alarm, (period_ms_t)((period_ms_t)timeout_sec * 1000)); + osi_mutex_unlock(&btu_general_alarm_lock); } @@ -491,18 +491,21 @@ void btu_stop_timer(TIMER_LIST_ENT *p_tle) { assert(p_tle != NULL); + osi_mutex_lock(&btu_general_alarm_lock, OSI_MUTEX_MAX_TIMEOUT); if (p_tle->in_use == FALSE) { + osi_mutex_unlock(&btu_general_alarm_lock); return; } - p_tle->in_use = FALSE; - - // Get the alarm for the timer list entry. osi_alarm_t *alarm = hash_map_get(btu_general_alarm_hash_map, p_tle); if (alarm == NULL) { HCI_TRACE_WARNING("%s Unable to find expected alarm in hashmap", __func__); + p_tle->in_use = FALSE; + osi_mutex_unlock(&btu_general_alarm_lock); return; } osi_alarm_cancel(alarm); + p_tle->in_use = FALSE; + osi_mutex_unlock(&btu_general_alarm_lock); } /******************************************************************************* @@ -518,16 +521,18 @@ void btu_free_timer(TIMER_LIST_ENT *p_tle) { assert(p_tle != NULL); - p_tle->in_use = FALSE; - - // Get the alarm for the timer list entry. + osi_mutex_lock(&btu_general_alarm_lock, OSI_MUTEX_MAX_TIMEOUT); osi_alarm_t *alarm = hash_map_get(btu_general_alarm_hash_map, p_tle); if (alarm == NULL) { HCI_TRACE_DEBUG("%s Unable to find expected alarm in hashmap", __func__); + p_tle->in_use = FALSE; + osi_mutex_unlock(&btu_general_alarm_lock); return; } osi_alarm_cancel(alarm); hash_map_erase(btu_general_alarm_hash_map, p_tle); + p_tle->in_use = FALSE; + osi_mutex_unlock(&btu_general_alarm_lock); } #if defined(QUICK_TIMER_TICKS_PER_SEC) && (QUICK_TIMER_TICKS_PER_SEC > 0) @@ -586,11 +591,10 @@ void btu_start_quick_timer(TIMER_LIST_ENT *p_tle, UINT16 type, UINT32 timeout_ti return; } } - osi_mutex_unlock(&btu_l2cap_alarm_lock); - alarm = hash_map_get(btu_l2cap_alarm_hash_map, p_tle); if (alarm == NULL) { HCI_TRACE_ERROR("%s Unable to create alarm", __func__); + osi_mutex_unlock(&btu_l2cap_alarm_lock); return; } osi_alarm_cancel(alarm); @@ -600,6 +604,7 @@ void btu_start_quick_timer(TIMER_LIST_ENT *p_tle, UINT16 type, UINT32 timeout_ti p_tle->in_use = TRUE; // The quick timer ticks are 100ms long. osi_alarm_set(alarm, (period_ms_t)(timeout_ticks * 100)); + osi_mutex_unlock(&btu_l2cap_alarm_lock); } /******************************************************************************* @@ -615,34 +620,39 @@ void btu_stop_quick_timer(TIMER_LIST_ENT *p_tle) { assert(p_tle != NULL); + osi_mutex_lock(&btu_l2cap_alarm_lock, OSI_MUTEX_MAX_TIMEOUT); if (p_tle->in_use == FALSE) { + osi_mutex_unlock(&btu_l2cap_alarm_lock); return; } - p_tle->in_use = FALSE; - - // Get the alarm for the timer list entry. osi_alarm_t *alarm = hash_map_get(btu_l2cap_alarm_hash_map, p_tle); if (alarm == NULL) { HCI_TRACE_WARNING("%s Unable to find expected alarm in hashmap", __func__); + p_tle->in_use = FALSE; + osi_mutex_unlock(&btu_l2cap_alarm_lock); return; } osi_alarm_cancel(alarm); + p_tle->in_use = FALSE; + osi_mutex_unlock(&btu_l2cap_alarm_lock); } void btu_free_quick_timer(TIMER_LIST_ENT *p_tle) { assert(p_tle != NULL); - p_tle->in_use = FALSE; - - // Get the alarm for the timer list entry. + osi_mutex_lock(&btu_l2cap_alarm_lock, OSI_MUTEX_MAX_TIMEOUT); osi_alarm_t *alarm = hash_map_get(btu_l2cap_alarm_hash_map, p_tle); if (alarm == NULL) { HCI_TRACE_DEBUG("%s Unable to find expected alarm in hashmap", __func__); + p_tle->in_use = FALSE; + osi_mutex_unlock(&btu_l2cap_alarm_lock); return; } osi_alarm_cancel(alarm); hash_map_erase(btu_l2cap_alarm_hash_map, p_tle); + p_tle->in_use = FALSE; + osi_mutex_unlock(&btu_l2cap_alarm_lock); } #endif /* defined(QUICK_TIMER_TICKS_PER_SEC) && (QUICK_TIMER_TICKS_PER_SEC > 0) */ @@ -682,11 +692,10 @@ void btu_start_timer_oneshot(TIMER_LIST_ENT *p_tle, UINT16 type, UINT32 timeout_ return; } } - osi_mutex_unlock(&btu_oneshot_alarm_lock); - alarm = hash_map_get(btu_oneshot_alarm_hash_map, p_tle); if (alarm == NULL) { HCI_TRACE_ERROR("%s Unable to create alarm", __func__); + osi_mutex_unlock(&btu_oneshot_alarm_lock); return; } osi_alarm_cancel(alarm); @@ -696,24 +705,28 @@ void btu_start_timer_oneshot(TIMER_LIST_ENT *p_tle, UINT16 type, UINT32 timeout_ // NOTE: This value is in seconds but stored in a ticks field. p_tle->ticks = timeout_sec; osi_alarm_set(alarm, (period_ms_t)(timeout_sec * 1000)); + osi_mutex_unlock(&btu_oneshot_alarm_lock); } void btu_stop_timer_oneshot(TIMER_LIST_ENT *p_tle) { assert(p_tle != NULL); + osi_mutex_lock(&btu_oneshot_alarm_lock, OSI_MUTEX_MAX_TIMEOUT); if (p_tle->in_use == FALSE) { + osi_mutex_unlock(&btu_oneshot_alarm_lock); return; } - p_tle->in_use = FALSE; - - // Get the alarm for the timer list entry. osi_alarm_t *alarm = hash_map_get(btu_oneshot_alarm_hash_map, p_tle); if (alarm == NULL) { HCI_TRACE_WARNING("%s Unable to find expected alarm in hashmap", __func__); + p_tle->in_use = FALSE; + osi_mutex_unlock(&btu_oneshot_alarm_lock); return; } osi_alarm_cancel(alarm); + p_tle->in_use = FALSE; + osi_mutex_unlock(&btu_oneshot_alarm_lock); } #if (defined(HCILP_INCLUDED) && HCILP_INCLUDED == TRUE) diff --git a/docs/en/api-guides/ble/blufi.rst b/docs/en/api-guides/ble/blufi.rst index 862cfda39e2..f83b22bacae 100644 --- a/docs/en/api-guides/ble/blufi.rst +++ b/docs/en/api-guides/ble/blufi.rst @@ -12,6 +12,16 @@ Fragmenting, data encryption, and checksum verification in the BluFi layer are t You can customize symmetric encryption, asymmetric encryption, and checksum support customization. Here we use the DH algorithm for key negotiation, 128-AES algorithm for data encryption, and CRC16 algorithm for checksum verification. +.. note:: + + **BluFi is currently in maintenance mode, and no new features are planned.** + + For new projects or when adding Wi-Fi provisioning, we recommend using the network_provisioning component + + (`network_provisioning `_) + + for a modern, secure, and actively maintained solution. + The BluFi Flow ---------------- @@ -481,15 +491,36 @@ The data to be encrypted and decrypted must be in the same length. The IV8 is an This function is used to compute CheckSum and return a value of CheckSum. BluFi uses the returned value to compare the CheckSum of the frame. -5. Implementing Stronger Security +5. BLE SMP Encryption for Blufi -The default encryption/decryption logic in this example is intended for demonstration purposes only. +Before Wi-Fi provisioning, you can use BLE SMP pairing to establish a secure connection, making the provisioning process safer. -If you require a higher level of security, it is recommended to implement your own encryption, decryption, authentication, and checksum algorithms by customizing the security callbacks in the BluFi framework. +This feature can be enabled or disabled via the configuration option: .. code-block:: c - esp_err_t esp_blufi_register_callbacks(esp_blufi_callbacks_t *callbacks) + CONFIG_EXAMPLE_BLUFI_BLE_SMP_ENABLE + +If this option is enabled, the ESP32 device will issue a pairing request once it is connected. Only after a successful pairing can the device proceed with provisioning. + +Currently, BLE SMP pairing is supported **only on the Bluedroid host**. + +6. Implementing Stronger Security + +The default encryption and decryption logic in this example is intended for demonstration purposes only. + +If you require a higher level of security, you may consider one of the following approaches: + +1. **Custom Security Callbacks** – Implement your own encryption, decryption, authentication, and checksum algorithms by customizing the security callbacks in the Blufi framework: + + .. code-block:: c + + esp_err_t esp_blufi_register_callbacks(esp_blufi_callbacks_t *callbacks); + +2. **Network Provisioning Component (recommended)** – Alternatively, you can use the network_provisioning component for a secure, ready-to-use provisioning solution: + + `network_provisioning `_ + GATT Related Instructions ---------------------------- diff --git a/docs/zh_CN/api-guides/ble/blufi.rst b/docs/zh_CN/api-guides/ble/blufi.rst index a8c697721b0..05993f64a48 100644 --- a/docs/zh_CN/api-guides/ble/blufi.rst +++ b/docs/zh_CN/api-guides/ble/blufi.rst @@ -12,6 +12,16 @@ BluFi 流程的关键部分包括数据的分片、加密以及校验和验证 用户可按需自定义用于对称加密、非对称加密以及校验的算法。此处,我们采用 DH 算法进行密钥协商,128-AES 算法用于数据加密,CRC16 算法用于校验和验证。 +.. note:: + + **BluFi 目前处于维护模式,不再规划新功能。** + + 对于新项目或新增 Wi-Fi 配网的场景,建议使用 network_provisioning 组件 + + (`network_provisioning `_) + + 来实现现代化、安全且有持续维护的解决方案。 + BluFi 流程 ----------- @@ -481,15 +491,36 @@ BluFi 会在调用完 Negotiate_data_handler 后,发送 Negotiate_data_handler 该函数用来进行校验,返回值为校验的值。BluFi 会使用该函数返回值与帧的校验值进行比较。 -5. 实现更强的安全性 +5. BLE SMP Encryption for Blufi -本示例中默认的加密/解密逻辑仅用于演示目的。 +在 Wi-Fi 配网之前,可以使用 BLE SMP 配对建立安全连接,使配网过程更安全。 -如果需要更高等级的安全性,建议通过自定义 BluFi 框架中的安全回调函数,实现您自己的加密、解密、认证以及校验算法。 +此功能可通过配置选项启用或禁用: .. code-block:: c - esp_err_t esp_blufi_register_callbacks(esp_blufi_callbacks_t *callbacks) + CONFIG_EXAMPLE_BLUFI_BLE_SMP_ENABLE + +如果启用该选项,ESP32 设备在连接成功后会发起配对请求。仅在配对成功后,设备才可继续进行 Wi-Fi 配网。 + +目前 BLE SMP 配对 **仅支持 Bluedroid 主机**。 + +6. 实现更强的安全性 + +示例中的默认加密/解密逻辑仅用于演示目的。 + +如果需要更高等级的安全性,可以考虑以下方法: + +1. **自定义安全回调** – 通过自定义 BluFi 框架中的安全回调函数,实现自己的加密、解密、认证以及校验算法: + + .. code-block:: c + + esp_err_t esp_blufi_register_callbacks(esp_blufi_callbacks_t *callbacks); + +2. **Network Provisioning 组件(推荐使用)** – 或者可以使用 ESP-IDF 提供的 network_provisioning 组件,实现安全、可直接使用的配网解决方案: + + `network_provisioning `_ + GATT 相关说明 diff --git a/examples/bluetooth/bluedroid/ble/ble_compatibility_test/main/ble_compatibility_test.c b/examples/bluetooth/bluedroid/ble/ble_compatibility_test/main/ble_compatibility_test.c index c65072ab130..7c7ca41f850 100644 --- a/examples/bluetooth/bluedroid/ble/ble_compatibility_test/main/ble_compatibility_test.c +++ b/examples/bluetooth/bluedroid/ble/ble_compatibility_test/main/ble_compatibility_test.c @@ -695,7 +695,7 @@ void app_main(void) /* set the security iocap & auth_req & key size & init key response key parameters to the stack*/ esp_ble_auth_req_t auth_req = ESP_LE_AUTH_REQ_SC_MITM_BOND; //bonding with peer device after authentication - esp_ble_io_cap_t iocap = ESP_IO_CAP_OUT; //set the IO capability to No output No input + esp_ble_io_cap_t iocap = ESP_IO_CAP_OUT; //set the IO capability to DisplayOnly uint8_t key_size = 16; //the key size should be 7~16 bytes uint8_t init_key = ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK; uint8_t rsp_key = ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK; diff --git a/examples/bluetooth/blufi/README.md b/examples/bluetooth/blufi/README.md index 49be5e30f8d..30ab590c15f 100644 --- a/examples/bluetooth/blufi/README.md +++ b/examples/bluetooth/blufi/README.md @@ -82,3 +82,9 @@ I (1198) BLUFI_EXAMPLE: BLUFI init finish ## Troubleshooting For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon. + +## Note +BluFi is currently in maintenance mode, and no new features are planned. + +For new projects or when adding network_provisioning, we recommend using the [network_provisioning](https://github.com/espressif/idf-extra-components/tree/master/network_provisioning). + diff --git a/examples/bluetooth/blufi/main/Kconfig.projbuild b/examples/bluetooth/blufi/main/Kconfig.projbuild index b8fe9f1957d..fc1a20695b0 100644 --- a/examples/bluetooth/blufi/main/Kconfig.projbuild +++ b/examples/bluetooth/blufi/main/Kconfig.projbuild @@ -33,4 +33,23 @@ menu "Example Configuration" bool "WAPI PSK" endchoice + config EXAMPLE_BLUFI_BLE_SMP_ENABLE + bool "Enable BLE SMP support in BLUFI" + depends on BT_BLUEDROID_ENABLED + select BT_BLUFI_BLE_SMP_ENABLE + select BT_BLE_SMP_ENABLE + default n + help + Enable BLE Security Manager Protocol (SMP) for BLUFI. + Currently, this feature is only supported with the Bluedroid host. + + If enabled: + - BLUFI will configure SMP security parameters such as + IO capabilities, authentication mode, and key size. + - After a BLE connection is established, BLUFI will + proactively initiate a security request. + - Only after the BLE pairing is successfully completed, + BLUFI can proceed with Wi-Fi provisioning. + - If the user rejects pairing or inputs an incorrect passkey, + BLUFI will not start Wi-Fi provisioning. endmenu diff --git a/examples/bluetooth/blufi/main/blufi_example_main.c b/examples/bluetooth/blufi/main/blufi_example_main.c index 76b05ae857a..ba6f08d9a1b 100644 --- a/examples/bluetooth/blufi/main/blufi_example_main.c +++ b/examples/bluetooth/blufi/main/blufi_example_main.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -319,6 +319,14 @@ static void example_event_callback(esp_blufi_cb_event_t event, esp_blufi_cb_para ble_is_connected = true; esp_blufi_adv_stop(); blufi_security_init(); + #ifdef CONFIG_EXAMPLE_BLUFI_BLE_SMP_ENABLE + // Try to initiate BLE security request after connection established. + BLUFI_INFO("Try to initiate BLE security request\n"); + esp_err_t ret = esp_blufi_start_security_request(param->connect.remote_bda); + if (ret != ESP_OK) { + BLUFI_ERROR("Failed to start security request: %s\n", esp_err_to_name(ret)); + } + #endif // CONFIG_EXAMPLE_BLUFI_BLE_SMP_ENABLE break; case ESP_BLUFI_EVENT_BLE_DISCONNECT: BLUFI_INFO("BLUFI ble disconnect\n"); diff --git a/examples/bluetooth/blufi/main/blufi_init.c b/examples/bluetooth/blufi/main/blufi_init.c index 7e9e9117e80..2edb5c6dd00 100644 --- a/examples/bluetooth/blufi/main/blufi_init.c +++ b/examples/bluetooth/blufi/main/blufi_init.c @@ -16,6 +16,7 @@ #ifdef CONFIG_BT_BLUEDROID_ENABLED #include "esp_bt_main.h" #include "esp_bt_device.h" +#include "esp_gap_ble_api.h" #endif #ifdef CONFIG_BT_NIMBLE_ENABLED @@ -56,6 +57,37 @@ esp_err_t esp_blufi_host_init(void) } +#ifdef CONFIG_EXAMPLE_BLUFI_BLE_SMP_ENABLE + +void esp_blufi_set_ble_security_params(void) +{ + /* set the security iocap & auth_req & key size & init key response key parameters to the stack*/ + esp_ble_auth_req_t auth_req = ESP_LE_AUTH_REQ_SC_MITM; // Secure Connections with MITM protection (no bonding) + esp_ble_io_cap_t iocap = ESP_IO_CAP_OUT; // IO capability: DisplayOnly + uint8_t key_size = 16; //the key size should be 7~16 bytes + uint8_t init_key = ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK; + uint8_t rsp_key = ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK; + //set static passkey + uint32_t passkey = 123456; + uint8_t auth_option = ESP_BLE_ONLY_ACCEPT_SPECIFIED_AUTH_ENABLE; + uint8_t oob_support = ESP_BLE_OOB_DISABLE; + BLUFI_INFO("BLE SMP passkey: %06" PRIu32 " (WARNING: Change this default value for production or don't use static passkey!)\n", passkey); + esp_ble_gap_set_security_param(ESP_BLE_SM_SET_STATIC_PASSKEY, &passkey, sizeof(uint32_t)); + esp_ble_gap_set_security_param(ESP_BLE_SM_AUTHEN_REQ_MODE, &auth_req, sizeof(uint8_t)); + esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, sizeof(uint8_t)); + esp_ble_gap_set_security_param(ESP_BLE_SM_MAX_KEY_SIZE, &key_size, sizeof(uint8_t)); + esp_ble_gap_set_security_param(ESP_BLE_SM_ONLY_ACCEPT_SPECIFIED_SEC_AUTH, &auth_option, sizeof(uint8_t)); + esp_ble_gap_set_security_param(ESP_BLE_SM_OOB_SUPPORT, &oob_support, sizeof(uint8_t)); + /* If your BLE device acts as a Slave, the init_key means you hope which types of key of the master should distribute to you, + and the response key means which key you can distribute to the master; + If your BLE device acts as a master, the response key means you hope which types of key of the slave should distribute to you, + and the init key means which key you can distribute to the slave. */ + esp_ble_gap_set_security_param(ESP_BLE_SM_SET_INIT_KEY, &init_key, sizeof(uint8_t)); + esp_ble_gap_set_security_param(ESP_BLE_SM_SET_RSP_KEY, &rsp_key, sizeof(uint8_t)); +} + +#endif // #if CONFIG_EXAMPLE_BLUFI_BLE_SMP_ENABLE + esp_err_t esp_blufi_host_deinit(void) { int ret; @@ -112,8 +144,11 @@ esp_err_t esp_blufi_host_and_cb_init(esp_blufi_callbacks_t *example_callbacks) return ret; } - return ESP_OK; + #ifdef CONFIG_EXAMPLE_BLUFI_BLE_SMP_ENABLE + esp_blufi_set_ble_security_params(); + #endif // CONFIG_EXAMPLE_BLUFI_BLE_SMP_ENABLE + return ESP_OK; } #endif /* CONFIG_BT_BLUEDROID_ENABLED */