ESP-ADF wifi_service子模块esp_wifi_setting配网之blufi_config详解
版本信息: v2.7-65-gcf908721
本章节分析的源码位于
/components/wifi_service/blufi_config/blufi_config.c
文件、/components/wifi_service/blufi_config/blufi_security.c
文件和/components/wifi_service/include/blufi_config.h
文件
模块概览
Blufi Config在ESP-ADF的wifi_service组件架构中处于配网接口实现层,下图展示了其在整体架构中的位置:
从图中可以看出,blufi_config是esp_wifi_setting抽象接口的一种具体实现,与其他配网方式(smart_config、airkiss_config和softap_config)并列。配网完成后,获取的WiFi凭证会被保存到wifi_ssid_manager模块中进行管理。
ESP-ADF 的 blufi_config 模块是基于蓝牙 BLE 技术实现的 WiFi 配网方式,它同样遵循 ESP-ADF 中的 esp_wifi_setting
接口规范,实现了通过蓝牙向 ESP 设备传输 WiFi 配置信息的功能。
BLUFI (Bluetooth Low Energy + WiFi) 是乐鑫推出的一种通过蓝牙配置 WiFi 的方案,它的优势是可以在无需外部显示设备的情况下,通过移动设备的蓝牙连接向 ESP 设备传输 WiFi 的 SSID 和密码等配置信息。
与SmartConfig和AirKiss配网相比,BLUFI配网具有以下特点:
- 使用蓝牙而非WiFi进行初始通信,不依赖设备处于同一WiFi网络
- 安全性更高,采用标准加密算法保护传输数据
- 可靠性更好,通信过程有确认机制
- 支持双向通信,可以获取配网状态和发送自定义数据
- 耗电相对较低,特别是使用BLE技术
文件结构
blufi_config 模块主要由以下文件组成:
/components/wifi_service/include/blufi_config.h
- 对外接口定义/components/wifi_service/blufi_config/blufi_config.c
- 核心实现代码/components/wifi_service/blufi_config/blufi_security.c
- 安全加密相关代码
关键数据结构
BLUFI配置结构体
typedef struct wifi_blufi_config {
uint8_t ble_server_if; // BLE 服务接口号
uint16_t ble_conn_id; // BLE 连接 ID
wifi_config_t sta_config; // WiFi 站点配置
bool sta_connected_flag; // WiFi 站点连接状态标志
bool ble_connected_flag; // BLE 连接状态标志
void *user_data; // 用户自定义数据
int user_data_length; // 用户自定义数据长度
} wifi_blufi_config_t;
蓝牙广播数据
static uint8_t blufi_service_uuid128[32] = {
/* LSB <--------------------------------------------------------------------------------> MSB */
// BLUFI服务的128位UUID值,用于唯一标识BLE服务类型
// 实际上这里只使用了16字节,对应于乐鑫为BLUFI定义的标准UUID
// 注意数组中存储顺序是从最低有效字节(LSB)到最高有效字节(MSB)
0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00,
};
static esp_ble_adv_data_t blufi_adv_data = {
.set_scan_rsp = false, // 是否在扫描响应中设置此广播数据,false表示在广播包中
.include_name = true, // 是否在广播数据中包含设备名称
.include_txpower = true, // 是否在广播数据中包含发射功率
.min_interval = 0x0006, // 最小连接间隔,实际值 = min_interval * 1.25ms,这里约7.5ms
.max_interval = 0x0010, // 最大连接间隔,实际值 = max_interval * 1.25ms,这里约20ms
.appearance = 0x00, // 设备外观特征值,0x00表示未指定类型
.manufacturer_len = 0, // 制造商特定数据长度
.p_manufacturer_data = NULL, // 制造商特定数据,这里未使用
.service_data_len = 0, // 服务数据长度
.p_service_data = NULL, // 服务数据,这里未使用
.service_uuid_len = 16, // 服务UUID长度,使用16字节UUID
.p_service_uuid = blufi_service_uuid128, // 指向服务UUID数组的指针,用于标识BLUFI服务
.flag = 0x6, // 广播标志,0x6表示LE通用可发现模式且仅支持BR/EDR
};
蓝牙广播参数
static esp_ble_adv_params_t blufi_adv_params = {
.adv_int_min = 0x100, // 广播间隔最小值,实际值 = adv_int_min * 0.625ms,这里约160ms
.adv_int_max = 0x100, // 广播间隔最大值,设置为与最小值相同以确保固定间隔
.adv_type = ADV_TYPE_IND, // 广播类型,指定为可连接的不定向广播(最常用类型)
.own_addr_type = BLE_ADDR_TYPE_PUBLIC, // 地址类型,使用公共地址(固定的MAC地址)
.channel_map = ADV_CHNL_ALL, // 使用所有广播信道(37、3839信道)
.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY, // 广播过滤策略,允许任何设备扫描和连接
};
BLUFI回调函数结构体
/**
* @brief 定义BLUFI协议的全部回调函数集
*
* 该结构体定义了BLUFI协议需要的全部回调函数,包括:
* 1. 事件处理函数 - 处理BLUFI协议不同状态的事件
* 2. 密钥协商处理函数 - 负责DH密钥交换
* 3. 加密/解密函数 - 提供数据加密保护
* 4. 校验和函数 - 确保数据完整性
*/
static esp_blufi_callbacks_t wifi_ble_callbacks = {
.event_cb = wifi_ble_event_callback, // BLUFI事件回调函数,处理各种BLUFI事件
.negotiate_data_handler = blufi_dh_negotiate_data_handler, // DH密钥协商数据处理函数
.encrypt_func = blufi_aes_encrypt, // 数据加密函数,使用AES-128-CFB加密
.decrypt_func = blufi_aes_decrypt, // 数据解密函数,使用AES-128-CFB解密
.checksum_func = blufi_crc_checksum, // 校验和计算函数,使用CRC-16确保数据完整性
};
关键函数分析
创建和初始化
/**
* @brief 创建BLUFI配网实例
*
* 该函数完成以下主要操作:
* 1. 创廾sp_wifi_setting接口实例
* 2. 分配并初始化BLUFI配置结构体
* 3. 初始化蓝牙协议栈和回调函数
* 4. 注册配网启动和停止函数
*
* @param info 可选参数,本函数未使用该参数,可传入NULL
* @return esp_wifi_setting_handle_t 配网接口句柄,失败时返回NULL
**/
esp_wifi_setting_handle_t blufi_config_create(void *info)
{
// 1. 创建WiFi配置设置处理句柄,并命名为"blufi_config"
bc_setting_handle = esp_wifi_setting_create("blufi_config");
AUDIO_MEM_CHECK(TAG, bc_setting_handle, return NULL); // 内存检查,失败则返回NULL
// 2. 分配并初始化BLUFI配置结构体
wifi_blufi_config_t *cfg = audio_calloc(1, sizeof(wifi_blufi_config_t));
AUDIO_MEM_CHECK(TAG, cfg, { // 内存分配失败处理
audio_free(bc_setting_handle);
return NULL;
});
// 3. 将配置结构体设置到WiFi配置处理句柄中
esp_wifi_setting_set_data(bc_setting_handle, cfg);
// 4. 初始化BLUFI协议栈和回调函数
// 将已定义的回调函数注册到BLUFI协议栈,并初始化蓝牙主机和GAP协议
ESP_ERROR_CHECK(esp_blufi_host_and_cb_init(&wifi_ble_callbacks));
// 5. 注册配网模块的启动和停止函数
esp_wifi_setting_register_function(bc_setting_handle, _ble_config_start, _ble_config_stop, NULL);
return bc_setting_handle; // 返回创建的处理句柄
}
该函数是BLUFI配网模块的入口点,主要功能包括:
- 创建WiFi配置句柄
- 分配并初始化BLUFI配置结构体内存
- 初始化蓝牙主机和回调函数
- 注册配网模块的启动和停止函数
BLUFI蓝牙协议栈初始化
esp_blufi_host_and_cb_init
函数是初始化BLUFI蓝牙协议栈的关键函数,它会根据不同的蓝牙协议栈选择(经典Bluedroid或轻量级NimBLE)进行相应初始化。
Bluedroid协议栈实现(ESP-IDF默认)
/**
* @brief 初始化BLUFI蓝牙协议栈和回调函数
*
* 该函数完成三个主要初始化工作:
* 1. 初始化蓝牙主机(host)
* 2. 注册BLUFI协议回调函数
* 3. 注册通用访问配置(GAP)回调函数
*
* @param example_callbacks BLUFI回调函数结构体指针
* @return esp_err_t 成功返回ESP_OK,失败返回错误码
*/
esp_err_t esp_blufi_host_and_cb_init(esp_blufi_callbacks_t *example_callbacks)
{
// 1. 初始化蓝牙主机
ESP_ERROR_CHECK(esp_blufi_host_init());
// 2. 注册BLUFI应用层回调函数
ESP_ERROR_CHECK(esp_blufi_register_callbacks(example_callbacks));
// 3. 注册GAP层回调函数并初始化BLUFI配置文件
ESP_ERROR_CHECK(esp_blufi_gap_register_callback());
return ESP_OK;
}
蓝牙主机初始化函数esp_blufi_host_init
是BLUFI协议栈初始化的核心。ESP-IDF支持两种不同的蓝牙协议栈实现:Bluedroid(默认,资源占用较大)和NimBLE(轻量级替代方案)。以下是基于NimBLE的实现:
/**
* @brief 初始化BLUFI蓝牙主机(NimBLE协议栈实现)
*
* 该函数完成NimBLE蓝牙协议栈的初始化工作,包括:
* 1. 初始化NimBLE主机控制接口(HCI)
* 2. 配置回调函数(重置、同步、GATT服务注册等)
* 3. 初始化GATT服务器
* 4. 设置设备名称
* 5. 初始化BLUFI BTC(蓝牙控制器)接口
* 6. 启动NimBLE主机任务
*
* @return esp_err_t 成功返回ESP_OK
*/
esp_err_t esp_blufi_host_init(void)
{
// 初始化NimBLE HCI接口
ESP_ERROR_CHECK(esp_nimble_hci_init());
nimble_port_init();
// 配置NimBLE主机回调函数
ble_hs_cfg.reset_cb = blufi_on_reset; // 主机重置回调
ble_hs_cfg.sync_cb = blufi_on_sync; // 主机同步回调
ble_hs_cfg.gatts_register_cb = esp_blufi_gatt_svr_register_cb; // GATT服务注册回调
ble_hs_cfg.store_status_cb = ble_store_util_status_rr; // 存储状态回调
ble_hs_cfg.sm_io_cap = 4; // 安全管理器I/O能力(无输入、无输出)
ble_hs_cfg.sm_sc = 0; // 禁用安全连接(Secure Connections)
// 初始化GATT服务器
int rc;
rc = esp_blufi_gatt_svr_init();
assert(rc == 0); // 断言初始化成功
// 设置设备名称(用于广播)
rc = ble_svc_gap_device_name_set(BLUFI_DEVICE_NAME);
assert(rc == 0); // 断言设置成功
// 初始化BLUFI BTC接口
esp_blufi_btc_init();
// 初始化NimBLE主机任务
nimble_port_freertos_init(bleprph_host_task);
return ESP_OK;
}
GAP注册回调过程(esp_blufi_gap_register_callback
):
esp_err_t esp_blufi_gap_register_callback(void)
{
int rc;
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0))
// ESP-IDF 4.3及以上版本使用新的回调处理函数
rc = esp_ble_gap_register_callback(esp_blufi_gap_event_handler);
#else
// 较早版本使用原始的回调处理函数
rc = esp_ble_gap_register_callback(blufi_config_gap_event_handler);
#endif
if(rc){
return rc;
}
// 初始化BLUFI配置文件
return esp_blufi_profile_init();
}
配网启动
/**
* @brief 启动BLUFI配网过程
*
* 该函数的主要功能是启动蓝牙广播,使设备可被手机扫描到。
* 根据ESP-IDF版本不同,使用不同的API启动蓝牙广播:
* - ESP-IDF 4.3及以上版本使用简化API直接调用esp_blufi_adv_start()
* - 早期版本需要手动设置设备名称、配置广播数据并启动广播
*
* @param self WiFi配置接口句柄,由blufi_config_create创建
* @return esp_err_t 成功返回ESP_OK,失败返回错误码
**/
esp_err_t _ble_config_start(esp_wifi_setting_handle_t self)
{
ESP_LOGI(TAG, "blufi_config_start"); // 记录开始配网的日志
// 根据ESP-IDF版本使用不同的API启动蓝牙广播
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0))
// ESP-IDF 4.3及以上版本使用新的简化API
esp_blufi_adv_start();
#else
// ESP-IDF 4.3以下版本需要手动设置设备名称、配置广播数据并启动广播
esp_ble_gap_set_device_name(BLUFI_DEVICE_NAME); // 设置蓝牙设备名称
esp_ble_gap_config_adv_data(&blufi_adv_data); // 配置蓝牙广播数据
esp_ble_gap_start_advertising(&blufi_adv_params); // 开始蓝牙广播
#endif
ESP_LOGI(TAG, "BLUFI VERSION %04x", esp_blufi_get_version()); // 记录BLUFI协议版本
return ESP_OK;
}
配网停止
/**
* @brief 停止BLUFI配网过程
*
* 该函数停止蓝牙广播,一般在以下情况下调用:
* 1. 当配网完成后,无需继续广播
* 2. 当配网失败需要停止当前广播时
* 3. 当进入低功耗模式需要关闭蓝牙时
*
* 根据ESP-IDF版本不同,使用不同的API停止蓝牙广播。
*
* @param self WiFi配置接口句柄,由blufi_config_create创建
* @return esp_err_t 成功返回ESP_OK,失败返回错误码
**/
static esp_err_t _ble_config_stop(esp_wifi_setting_handle_t self)
{
// 根据ESP-IDF版本使用不同的API停止蓝牙广播
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0))
// ESP-IDF 4.3及以上版本使用新的简化API
esp_blufi_adv_stop();
#else
// ESP-IDF 4.3以下版本使用原始的蓝牙API
esp_ble_gap_stop_advertising();
#endif
ESP_LOGI(TAG, "blufi_config_stop"); // 记录停止配网的日志
return ESP_OK;
}
该函数停止BLUFI配网流程,主要是停止蓝牙广播。
4. BLUFI事件处理
/**
* @brief BLUFI事件回调函数,处理所有BLUFI协议事件
*
* 该函数是BLUFI配网过程的核心,负责处理从手机端发来的各种指令和配置数据,
* 并完成相应的WiFi配置和连接操作。
*
* @param event BLUFI事件类型
* @param param 事件参数,根据事件类型有不同的数据结构
*/
static void wifi_ble_event_callback(esp_blufi_cb_event_t event, esp_blufi_cb_param_t *param)
{
// 获取BLUFI模块的配置数据
wifi_blufi_config_t *cfg = esp_wifi_setting_get_data(bc_setting_handle);
switch (event) {
case ESP_BLUFI_EVENT_INIT_FINISH:
// BLUFI协议栈初始化完成
ESP_LOGI(TAG, "BLUFI init finish, func:%s, line:%d ", __func__, __LINE__);
break;
case ESP_BLUFI_EVENT_DEINIT_FINISH:
// BLUFI协议栈取消初始化完成
ESP_LOGI(TAG, "BLUFI deinit finish, func:%s, line:%d ", __func__, __LINE__);
break;
case ESP_BLUFI_EVENT_BLE_CONNECT:
// 蓝牙设备已连接(手机与ESP设备连接成功)
ESP_LOGI(TAG, "BLUFI ble connect, func:%s, line:%d ", __func__, __LINE__);
cfg->ble_connected_flag = true; // 设置蓝牙连接标志
// 停止蓝牙广播,因为已经建立了连接
esp_blufi_adv_stop(); // 或者esp_ble_gap_stop_advertising()
// 初始化安全模块,准备密钥交换
blufi_security_init();
break;
case ESP_BLUFI_EVENT_BLE_DISCONNECT:
// 蓝牙连接断开
ESP_LOGI(TAG, "BLUFI ble disconnect, func:%s, line:%d ", __func__, __LINE__);
cfg->ble_connected_flag = false; // 清除蓝牙连接标志
// 清理安全模块资源
blufi_security_deinit();
break;
case ESP_BLUFI_EVENT_SET_WIFI_OPMODE:
// 设置WiFi工作模式(站点/AP/混合模式)
ESP_LOGI(TAG, "BLUFI Set WIFI opmode %d", param->wifi_mode.op_mode);
// 调用ESP-IDF API设置WiFi模式
ESP_ERROR_CHECK(esp_wifi_set_mode(param->wifi_mode.op_mode));
break;
case ESP_BLUFI_EVENT_REQ_CONNECT_TO_AP:
// 手机请求设备连接到WiFi接入点
ESP_LOGI(TAG, "BLUFI requset wifi connect to AP");
esp_wifi_disconnect(); // 先断开当前连接(如果有)
esp_wifi_connect(); // 连接到新配置的AP
_ble_config_stop(NULL); // 停止配网过程,因为已收到连接请求
break;
case ESP_BLUFI_EVENT_REQ_DISCONNECT_FROM_AP:
// 手机请求设备断开WiFi连接
ESP_LOGI(TAG, "BLUFI requset wifi disconnect from AP");
esp_wifi_disconnect();
break;
case ESP_BLUFI_EVENT_REPORT_ERROR:
// 报告错误状态
ESP_LOGE(TAG, "BLUFI report error, error code %d", param->report_error.state);
esp_blufi_send_error_info(param->report_error.state);
break;
case ESP_BLUFI_EVENT_GET_WIFI_STATUS:
// 手机端请求获取WiFi状态
{
wifi_mode_t mode;
esp_blufi_extra_info_t info = {0};
esp_wifi_get_mode(&mode); // 获取当前WiFi模式
if (cfg->sta_connected_flag) {
// 已经连接到AP,发送成功状态和SSID信息
memset(&info, 0, sizeof(esp_blufi_extra_info_t));
info.sta_ssid = cfg->sta_config.sta.ssid;
info.sta_ssid_len = strlen((char *)cfg->sta_config.sta.ssid);
esp_blufi_send_wifi_conn_report(mode, ESP_BLUFI_STA_CONN_SUCCESS, 0, &info);
} else {
// 未连接到AP,发送失败状态
esp_blufi_send_wifi_conn_report(mode, ESP_BLUFI_STA_CONN_FAIL, 0, NULL);
}
ESP_LOGI(TAG, "BLUFI get wifi status from AP");
}
break;
case ESP_BLUFI_EVENT_RECV_SLAVE_DISCONNECT_BLE:
// 收到指令,要求断开蓝牙连接
esp_blufi_disconnect(); // 执行断开指令
ESP_LOGI(TAG, "BLUFI close a gatt connection, func:%s, line:%d ", __func__, __LINE__);
break;
case ESP_BLUFI_EVENT_RECV_STA_BSSID:
// 收到WiFi AP的BSSID(MAC地址)
memcpy(cfg->sta_config.sta.bssid, param->sta_bssid.bssid, 6);
cfg->sta_config.sta.bssid_set = 1;
esp_wifi_set_config(WIFI_IF_STA, &cfg->sta_config);
ESP_LOGI(TAG, "Recv STA BSSID %s\n", cfg->sta_config.sta.ssid);
break;
case ESP_BLUFI_EVENT_RECV_STA_SSID:
// 收到WiFi AP的SSID(网络名称)
strncpy((char *)cfg->sta_config.sta.ssid, (char *)param->sta_ssid.ssid, param->sta_ssid.ssid_len);
cfg->sta_config.sta.ssid[param->sta_ssid.ssid_len] = '\0';
esp_wifi_set_config(WIFI_IF_STA, &cfg->sta_config);
ESP_LOGI(TAG, "Recv STA SSID %s\n", cfg->sta_config.sta.ssid);
break;
case ESP_BLUFI_EVENT_RECV_STA_PASSWD:
// 收到WiFi AP的密码
strncpy((char *)cfg->sta_config.sta.password, (char *)param->sta_passwd.passwd, param->sta_passwd.passwd_len);
cfg->sta_config.sta.password[param->sta_passwd.passwd_len] = '\0';
esp_wifi_set_config(WIFI_IF_STA, &cfg->sta_config);
ESP_LOGI(TAG, "Recv STA PASSWORD %s\n", cfg->sta_config.sta.password);
break;
case ESP_BLUFI_EVENT_GET_WIFI_LIST:
// 手机端请求获取附近的WiFi列表
{
wifi_scan_config_t scanConf = {
.ssid = NULL,
.bssid = NULL,
.channel = 0,
.show_hidden = false
};
// 启动WiFi扫描
esp_wifi_scan_start(&scanConf, true);
}
break;
case ESP_BLUFI_EVENT_RECV_CUSTOM_DATA:
// 收到自定义数据(用于扩展使用)
ESP_LOGI(TAG, "Recv Custom Data %" PRIu32 "\n", param->custom_data.data_len);
esp_log_buffer_hex("Custom Data", param->custom_data.data, param->custom_data.data_len);
break;
default:
break;
}
}
BLUFI事件交互时序图
下图展示BLUFI配网过程中的主要事件交互顺序,清晰地描述了ESP设备、手机APP和WiFi接入点之间的通信流程:
如上图所示,BLUFI配网的完整交互流程可以分为以下几个核心阶段:
- 初始化阶段:初始化BLUFI协议栈并启动蓝牙广播
- 蓝牙连接阶段:手机连接到ESP设备并初始化安全模块
- 安全协商阶段:建立加密通道,保护数据传输
- WiFi配置阶段:接收并保存WiFi配置信息(SSID、密码等)
- WiFi连接阶段:连接到指定WiFi接入点
- 状态报告阶段:向手机报告WiFi连接状态
- 完成阶段:可能交换自定义数据或断开蓝牙连接
这个交互流程清晰地展示了各类ESP_BLUFI_EVENT事件的触发顺序和处理逻辑,以及wifi_ble_event_callback
函数在整个配网过程中的关键作用。
5. 扩展功能
/**
* @brief 设置WiFi站点模式连接状态标志
*
* 此函数用于更新BLUFI配置结构体中的站点连接状态标志。
* 当WiFi连接或断开时,应用程序应调用此函数更新状态,
* 这样当手机APP查询WiFi状态时,可以返回正确的连接状态。
*
* @param handle BLUFI配网模块句柄
* @param flag true表示已连接,false表示未连接
* @return esp_err_t 成功返回ESP_OK,失败返回ESP_FAIL
*/
esp_err_t blufi_set_sta_connected_flag(esp_wifi_setting_handle_t handle, bool flag)
{
AUDIO_NULL_CHECK(TAG, handle, return ESP_FAIL);
wifi_blufi_config_t *blufi_cfg = esp_wifi_setting_get_data(handle);
blufi_cfg->sta_connected_flag = flag;
return ESP_OK;
}
/**
* @brief 设置BLUFI自定义数据
*
* 此函数允许应用程序存储自定义数据到BLUFI配置结构体中,
* 之后可以通过blufi_send_customized_data函数将此数据发送给手机APP。
* 常用于在配网成功后向手机传递额外信息,如设备ID、IP地址等。
*
* @param handle BLUFI配网模块句柄
* @param data 自定义数据指针
* @param data_len 数据长度
* @return esp_err_t 成功返回ESP_OK,失败返回ESP_FAIL
*/
esp_err_t blufi_set_customized_data(esp_wifi_setting_handle_t handle, char *data, int data_len)
{
AUDIO_NULL_CHECK(TAG, handle, return ESP_FAIL);
AUDIO_NULL_CHECK(TAG, data, return ESP_FAIL);
wifi_blufi_config_t *blufi_cfg = esp_wifi_setting_get_data(handle);
blufi_cfg->user_data = audio_calloc(1, data_len + 1);
blufi_cfg->user_data_length = data_len;
AUDIO_MEM_CHECK(TAG, blufi_cfg->user_data, return ESP_FAIL);
memcpy(blufi_cfg->user_data, data, data_len);
ESP_LOGI(TAG, "Set blufi customized data: %s, length: %d", data, data_len);
return ESP_OK;
}
/**
* @brief 发送自定义数据到手机APP
*
* 此函数将之前通过blufi_set_customized_data设置的自定义数据
* 通过BLUFI协议发送给已连接的手机APP。
* 只有在蓝牙已连接状态下才能发送数据。
*
* 应用场景:
* - 配网成功后发送设备信息(如IP地址、设备ID等)
* - 传输设备状态或配置参数
* - 实现自定义控制协议
*
* @param handle BLUFI配网模块句柄
* @return esp_err_t 成功返回ESP_OK,失败返回ESP_FAIL或ESP_ERR_INVALID_STATE
*/
esp_err_t blufi_send_customized_data(esp_wifi_setting_handle_t handle)
{
AUDIO_NULL_CHECK(TAG, handle, return ESP_FAIL);
wifi_blufi_config_t *blufi_cfg = esp_wifi_setting_get_data(handle);
// 检查蓝牙是否已连接,未连接状态无法发送数据
if (!blufi_cfg->ble_connected_flag) {
ESP_LOGE(TAG, "BLE is not connected, cannot send customized data");
return ESP_ERR_INVALID_STATE;
}
// 检查是否有自定义数据可发送
if (blufi_cfg->user_data == NULL || blufi_cfg->user_data_length <= 0) {
ESP_LOGE(TAG, "No customized data to send");
return ESP_ERR_INVALID_STATE;
}
// 通过BLUFI协议发送自定义数据
esp_err_t ret = esp_blufi_send_custom_data(blufi_cfg->user_data, blufi_cfg->user_data_length);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to send customized data, err=0x%x", ret);
return ret;
}
ESP_LOGI(TAG, "Send customized data success, length: %d", blufi_cfg->user_data_length);
return ESP_OK;
}
这些函数提供了扩展功能:
blufi_set_sta_connected_flag
:设置WiFi连接状态标志blufi_set_customized_data
:设置自定义数据blufi_send_customized_data
:发送自定义数据到蓝牙客户端
工作流程
BLUFI配网的工作流程如下:
安全机制
BLUFI配网模块实现了多层安全保护:
- DH密钥交换:使用Diffie-Hellman密钥交换算法安全地在设备和手机间建立共享密钥
- AES-128加密:使用AES-128算法加密传输的数据
- CRC校验和:确保数据完整性
这些安全机制由blufi_security.c
文件实现,主要包括:
blufi_dh_negotiate_data_handler
:处理DH密钥交换blufi_aes_encrypt
:AES加密函数blufi_aes_decrypt
:AES解密函数blufi_crc_checksum
:CRC校验和计算
适配不同版本
代码包含了对不同ESP-IDF版本的适配:
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0))
esp_blufi_adv_start();
#else
esp_ble_gap_set_device_name(BLUFI_DEVICE_NAME);
esp_ble_gap_config_adv_data(&blufi_adv_data);
esp_ble_gap_start_advertising(&blufi_adv_params);
#endif
同时也支持不同的蓝牙协议栈实现:
- BLUEDROID:ESP-IDF默认的蓝牙协议栈
- NimBLE:内存占用更小的轻量级蓝牙协议栈
使用示例
以下是使用BLUFI配网模块的简单示例:
#include "esp_wifi_setting.h"
#include "blufi_config.h"
#include "esp_log.h"
static const char *TAG = "BLUFI_EXAMPLE";
void app_main()
{
// 初始化ESP-ADF音频内存管理
audio_mem_init();
// 初始化ESP-IDF WiFi
esp_netif_init();
esp_event_loop_create_default();
esp_netif_create_default_wifi_sta();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
esp_wifi_init(&cfg);
// 创建WiFi服务
wifi_service_config_t wifi_service_config = {
.extern_stack = true,
.evt_cb = wifi_service_cb,
.cb_ctx = NULL,
.setting_timeout_s = 60,
};
wifi_service_handle_t wifi_service = wifi_service_create(&wifi_service_config);
// 创建BLUFI配网
esp_wifi_setting_handle_t blufi_handle = blufi_config_create(NULL);
// 注册BLUFI配网到WiFi服务
wifi_service_register_setting_handle(wifi_service, blufi_handle, "blufi");
// 启动WiFi服务
wifi_service_set_sta_info(wifi_service, WIFI_CONFIG_RECONNECT);
wifi_service_connect(wifi_service);
// 设置并发送自定义数据(可选)
char *custom_data = "Hello from ESP32";
blufi_set_customized_data(blufi_handle, custom_data, strlen(custom_data));
// WiFi连接成功后发送自定义数据
esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED, wifi_connected_handler, blufi_handle);
}
static void wifi_connected_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
{
esp_wifi_setting_handle_t blufi_handle = (esp_wifi_setting_handle_t)arg;
// 设置WiFi连接标志
blufi_set_sta_connected_flag(blufi_handle, true);
// 发送自定义数据
blufi_send_customized_data(blufi_handle);
}
总结
ESP-ADF的BLUFI配网模块提供了一种安全、可靠的通过蓝牙配置WiFi的方法,特别适用于没有屏幕或按键的智能音箱、智能开关等设备。该模块具有以下优势:
- 安全性强:使用DH密钥交换和AES加密保护配网数据
- 可靠性高:BLE通信稳定,有完整的错误处理机制
- 用户体验好:无需切换WiFi网络,直接通过蓝牙配置
- 扩展性强:支持自定义数据传输,可用于设备注册、身份认证等
与其他配网方式相比,BLUFI是一种更现代、更安全的选择,特别适合需要高安全性的IoT设备。
blufi_security.c 安全机制详解
为了确保WiFi配网过程中的数据安全,BLUFI采用了多层安全机制,主要通过blufi_security.c
文件来实现。下面将详细分析该文件中的关键组件和工作原理。
安全结构体
/**
* @brief BLUFI安全机制结构体
*
* 该结构体包含了BLUFI安全机制所需的各种密钥和加密上下文数据,
* 用于实现DH密钥交换和AES加密通信。
*/
struct blufi_security {
#define DH_SELF_PUB_KEY_LEN 128 /* 自身公钥的长度(字节) */
#define DH_SELF_PUB_KEY_BIT_LEN (DH_SELF_PUB_KEY_LEN * 8) /* 公钥长度(比特) */
uint8_t self_public_key[DH_SELF_PUB_KEY_LEN]; /* 存储本地生成的DH公钥 */
#define SHARE_KEY_LEN 128 /* 共享密钥长度(字节) */
#define SHARE_KEY_BIT_LEN (SHARE_KEY_LEN * 8) /* 共享密钥长度(比特) */
uint8_t share_key[SHARE_KEY_LEN]; /* DH协商后生成的共享密钥,用于加密数据 */
size_t share_len; /* 共享密钥的实际长度 */
#define PSK_LEN 16 /* 预共享密钥长度,用于AES-128加密 */
uint8_t psk[PSK_LEN]; /* 由共享密钥生成的AES密钥 */
uint8_t *dh_param; /* DH参数,包含素数p和生成元g */
int dh_param_len; /* DH参数的长度 */
uint8_t iv[16]; /* AES-CFB模式的初始化向量(IV) */
mbedtls_dhm_context dhm; /* mbedTLS的DH密钥协商上下文 */
mbedtls_aes_context aes; /* mbedTLS的AES加密上下文 */
};
这个结构体是BLUFI安全实现的核心,包含以下重要字段:
self_public_key
:设备端的DH公钥,长度为128字节share_key
:通过DH算法生成的共享密钥,长度为128字节psk
:从share_key派生的预共享密钥,用于AES加密,长度为16字节dh_param
:临时存储DH参数的缓冲区iv
:初始化向量,用于AES-CFB模式加密dhm
:mbedtls提供的DH上下文aes
:mbedtls提供的AES上下文
随机数生成函数
/**
* @brief 安全随机数生成函数
*
* 该函数为mbedTLS库提供安全的随机数生成服务,用于DH密钥生成和其他密码学操作。
* 它遵循了mbedTLS的rng_callback函数原型,作为密码学函数的随机数源。
*
* 安全性说明:
* - 使用ESP-IDF的esp_random()函数,它是硬件生成的真随机数,比伪随机数更安全
* - 在密码学中,随机数质量直接影响密钥的安全性,善意的攻击者可能通过预测伪随机数序列来破解密钥
*
* @param rng_state 随机数生成器状态(本函数未使用)
* @param output 输出缓冲区,存储生成的随机字节
* @param len 需要生成的随机字节数
*
* @return ESP_OK 表示随机数生成成功
*/
static int myrand(void *rng_state, unsigned char *output, size_t len)
{
size_t i;
for (i = 0; i < len; ++i) {
output[i] = esp_random(); // 使用ESP-IDF提供的硬件随机数生成器
}
return ESP_OK;
}
该函数是一个自定义随机数生成器,用于DH密钥生成过程。它使用ESP-IDF的esp_random()
函数生成安全随机数,这对于密码学应用至关重要。
DH密钥协商处理
/**
* @brief DH密钥协商数据处理函数,实现安全的Diffie-Hellman密钥交换
*
* @param data 输入的协商数据
* @param len 输入数据的长度
* @param output_data 输出数据的指针,用于存储要发送给对方的数据
* @param output_len 输出数据的长度
* @param need_free 指示输出数据是否需要释放
*/
void blufi_dh_negotiate_data_handler(uint8_t *data, int len, uint8_t **output_data, int *output_len, bool *need_free)
{
int ret;
uint8_t type = data[0]; // 第一个字节定义了协商数据的类型
// 检查安全上下文是否初始化
if (blufi_sec == NULL) {
ESP_LOGE(BLUFI_SECURITY_TAG, "BLUFI Security is not initialized");
return;
}
// 根据数据类型进行处理
switch (type) {
case SEC_TYPE_DH_PARAM_LEN:
// 处理DH参数长度信息
// 从数据中提取参数长度并分配内存缓冲区
blufi_sec->dh_param_len = ((data[1] << 8) | data[2]); // 组合第2、3字节为16位长度值
// 释放旧的参数缓冲区(如果存在)
if (blufi_sec->dh_param) {
audio_free(blufi_sec->dh_param);
blufi_sec->dh_param = NULL;
}
// 分配新的参数缓冲区
blufi_sec->dh_param = (uint8_t *)audio_calloc(1, blufi_sec->dh_param_len);
if (blufi_sec->dh_param == NULL) {
ESP_LOGE(BLUFI_SECURITY_TAG, "%s, Malloc failed", __func__);
return;
}
break;
case SEC_TYPE_DH_PARAM_DATA:
// 处理DH参数数据,包含素数p和生成器g
if (blufi_sec->dh_param == NULL) {
ESP_LOGE(BLUFI_SECURITY_TAG, "%s, Blufi_sec->dh_param == NULL", __func__);
return;
}
// 复制DH参数数据
uint8_t *param = blufi_sec->dh_param;
memcpy(blufi_sec->dh_param, &data[1], blufi_sec->dh_param_len);
// 读取参数到DH上下文中
ret = mbedtls_dhm_read_params(&blufi_sec->dhm, ¶m, ¶m[blufi_sec->dh_param_len]);
if (ret) {
ESP_LOGE(BLUFI_SECURITY_TAG, "%s Read param failed %d", __func__, ret);
return;
}
// 释放临时参数缓冲区
audio_free(blufi_sec->dh_param);
blufi_sec->dh_param = NULL;
// 根据ESP-IDF版本选择适合的API生成DH公钥
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0))
const int dhm_len = mbedtls_dhm_get_len(&blufi_sec->dhm);
ret = mbedtls_dhm_make_public(&blufi_sec->dhm, dhm_len, blufi_sec->self_public_key, dhm_len, myrand, NULL);
#else
ret = mbedtls_dhm_make_public(&blufi_sec->dhm, (int) mbedtls_mpi_size( &blufi_sec->dhm.P ), blufi_sec->self_public_key, blufi_sec->dhm.len, myrand, NULL);
#endif
if (ret) {
ESP_LOGE(BLUFI_SECURITY_TAG, "%s Make public failed %d", __func__, ret);
return;
}
// 使用DH算法计算共享密钥
mbedtls_dhm_calc_secret(&blufi_sec->dhm,
blufi_sec->share_key,
SHARE_KEY_BIT_LEN,
&blufi_sec->share_len,
NULL, NULL);
// 使用MD5生成AES加密的密钥
mbedtls_md5(blufi_sec->share_key, blufi_sec->share_len, blufi_sec->psk);
// 初始化AES加密上下文,使用生成的PSK作为密钥
mbedtls_aes_setkey_enc(&blufi_sec->aes, blufi_sec->psk, 128);
// 设置输出数据为自己的公钥,返回给对方
*output_data = &blufi_sec->self_public_key[0];
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0))
*output_len = dhm_len;
#endif
*need_free = false; // 指示这里返回的数据不需要释放,因为使用的是结构体内的内存
break;
case SEC_TYPE_DH_P: // 其他DH参数类型,当前未实现处理
break;
case SEC_TYPE_DH_G:
break;
case SEC_TYPE_DH_PUBLIC:
break;
}
}
DH密钥协商时序图
下图展示了BLUFI安全机制中的Diffie-Hellman密钥交换完整流程,直观描述了手机APP与ESP设备之间如何建立安全的通信通道:
上图展示了BLUFI安全机制中的Diffie-Hellman密钥协商流程,它包含以下关键步骤:
- 参数准备:手机APP发送DH参数长度和参数数据(包括素数p和生成元g)
- 公钥交换:
- ESP设备生成私钥a和公钥A = g^a mod p
- 设备将公钥A发送给手机APP
- 手机APP有自己的私钥b和公钥B = g^b mod p(这一步在手机端完成)
- 共享密钥计算:
- ESP设备使用私钥a和收到的手机公钥B计算共享密钥s = B^a mod p
- 手机APP使用私钥b和ESP公钥A计算共享密钥s = A^b mod p
- 数学原理保证两边计算的s相同,而中间人无法预算出密钥
- AES加密密钥生成:
- 对共享密钥s进行MD5计算,生成128位AES密钥(PSK)
- 初始化AES上下文以准备加密通信
- 建立安全通道:
- 使用协商好的AES密钥加密所有后续通信数据(WiFi配置等)
- 确保全部数据传输过程的机密性和完整性
这个函数是BLUFI安全协商的核心,处理DH密钥交换过程中的各种数据类型:
SEC_TYPE_DH_PARAM_LEN
:处理DH参数长度信息,分配相应大小的内存SEC_TYPE_DH_PARAM_DATA
:处理实际的DH参数数据,主要包括:- 读取DH参数(素数p和生成器g)
- 生成本地的DH公钥
- 计算共享密钥
- 使用MD5从共享密钥派生PSK
- 初始化AES加密上下文
AES加密与解密
/**
* @brief 对数据进行AES-128-CFB模式加密
*
* 该函数使用AES-128-CFB模式对数据进行加密,以保证传输过程中的数据安全。
* 主要特点如下:
* 1. 使用基于mbedTLS的AES加密实现
* 2. 采用CFB模式,支持任意长度数据的加密
* 3. 每次加密可使用不同的iv8实现动态IV,增强安全性
* 4. 支持原地加密,即加密结果替换原始数据
*
* @param iv8 初始化向量的首字节值,用于增强安全性,每次加密应使用不同值
* @param crypt_data 要加密的数据缓冲区,加密结果也存放在此缓冲区
* @param crypt_len 要加密的数据长度,字节单位
* @return int 成功返回加密后的数据长度,失败返回ESP_FAIL
**/
int blufi_aes_encrypt(uint8_t iv8, uint8_t *crypt_data, int crypt_len)
{
int ret;
size_t iv_offset = 0; // CFB模式的IV偏移量,初始为0
uint8_t iv0[16]; // 初始化向量缓冲区,AES-128需要128位(16字节)的IV
// 复制默认IV到临时缓冲区
memcpy(iv0, blufi_sec->iv, sizeof(blufi_sec->iv));
/* 设置iv8作为IV的第一个字节,从而使每次加密使用不同的IV */
iv0[0] = iv8;
// 使用mbedTLS的AES-CFB128模式进行加密
// 参数说明:
// 1. &blufi_sec->aes - AES上下文
// 2. MBEDTLS_AES_ENCRYPT - 指定加密操作
// 3. crypt_len - 要加密的数据长度
// 4. &iv_offset - IV偏移量的指针
// 5. iv0 - 初始化向量
// 6. crypt_data - 输入数据
// 7. crypt_data - 输出数据(与输入相同,原地加密)
ret = mbedtls_aes_crypt_cfb128(&blufi_sec->aes, MBEDTLS_AES_ENCRYPT, crypt_len, &iv_offset, iv0, crypt_data, crypt_data);
if (ret) {
return ESP_FAIL; // 加密失败时返回错误码
}
return crypt_len; // 成功时返回加密后的数据长度
}
/**
* @brief 对数据进行AES-128-CFB模式解密
*
* 该函数与blufi_aes_encrypt对应,用于解密通过BLUFI协议收到的加密数据。
* 该函数具有的特点:
* 1. 使用与加密相同的AES-128-CFB模式
* 2. 要求iv8参数必须与加密时使用的相同,否则无法正确解密
* 3. 同样支持原地解密,减少内存使用
*
* @param iv8 初始化向量的首字节值,必须与加密时使用的相同
* @param crypt_data 要解密的数据缓冲区,解密结果也存放在此缓冲区
* @param crypt_len 要解密的数据长度,字节单位
* @return int 成功返回解密后的数据长度,失败返回ESP_FAIL
**/
int blufi_aes_decrypt(uint8_t iv8, uint8_t *crypt_data, int crypt_len)
{
int ret;
size_t iv_offset = 0; // CFB模式的IV偏移量,初始为0
uint8_t iv0[16]; // 初始化向量缓冲区
// 复制默认IV到临时缓冲区
memcpy(iv0, blufi_sec->iv, sizeof(blufi_sec->iv));
/* 设置iv8作为IV的第一个字节,必须与加密时使用的相同 */
iv0[0] = iv8;
// 使用mbedTLS的AES-CFB128模式进行解密,参数与加密函数类似
// 不同的是使用MBEDTLS_AES_DECRYPT指定解密操作
ret = mbedtls_aes_crypt_cfb128(&blufi_sec->aes, MBEDTLS_AES_DECRYPT, crypt_len, &iv_offset, iv0, crypt_data, crypt_data);
if (ret) {
return ESP_FAIL; // 解密失败时返回错误码
}
return crypt_len; // 成功时返回解密后的数据长度
}
这两个函数分别实现了数据的加密和解密:
- 使用AES-128-CFB模式进行加密/解密
- 使用动态IV机制,其中第一个字节会被替换为传入的iv8参数,增强安全性
- 提供原地加密/解密,即输入和输出使用相同的缓冲区
- 返回处理的数据长度或失败状态
CRC校验和计算
/**
* @brief 计算数据的CRC16校验和
*
* 该函数计算数据的CRC16校验和,用于验证BLUFI协议传输的数据完整性。
* 主要特点如下:
* 1. 使用标准的CRC16-BE(大端序)算法
* 2. 初始值为0
* 3. 接口兼容其他安全函数,包含iv8参数但不使用
*
* @param iv8 本函数不使用此参数,仅为了保持接口一致性
* @param data 需要计算校验和的数据指针
* @param len 数据长度,字节单位
* @return uint16_t 返回16位CRC校验和值
**/
uint16_t blufi_crc_checksum(uint8_t iv8, uint8_t *data, int len)
{
/* 这里忽略iv8参数,不使用 */
// 使用ESP-IDF提供的大端序CRC16算法
// 参数说明:
// 1. 0 - CRC初始值
// 2. data - 要计算的数据指针
// 3. len - 数据长度
return crc16_be(0, data, len); // 返回计算出的CRC16校验和
}
该函数计算数据的CRC-16校验和,用于验证数据完整性。它使用ESP-IDF提供的crc16_be
函数,计算大端序CRC值。
安全模块初始化与销毁
/**
* @brief 初始化BLUFI安全模块
*
* 该函数初始化BLUFI安全组件,为DH密钥交换和AES加密做准备。
* 主要操作包括:
* 1. 分配安全结构体内存并清零
* 2. 初始化mbedTLS库的DH和AES上下文
* 3. 初始化IV向量
*
* 该函数会在蓝牙连接建立时被调用。
*
* @return esp_err_t 成功返回ESP_OK,失败返回ESP_FAIL
**/
esp_err_t blufi_security_init(void)
{
// 1. 分配并清零安全结构体内存
blufi_sec = (struct blufi_security *)audio_calloc(1, sizeof(struct blufi_security));
if (blufi_sec == NULL) {
return ESP_FAIL; // 内存分配失败则返回错误
}
// 2. 初始化mbedTLS的DH和AES上下文
mbedtls_dhm_init(&blufi_sec->dhm); // 初始化DH上下文
mbedtls_aes_init(&blufi_sec->aes); // 初始化AES上下文
// 3. 初始化IV向量为全零
memset(blufi_sec->iv, 0x0, 16); // 设置16字节的IV初始值为0
return ESP_OK; // 初始化成功
}
/**
* @brief 销毁BLUFI安全模块
*
* 该函数负责清理并释放BLUFI安全模块使用的资源,主要操作包括:
* 1. 释放DH参数缓冲区(如果存在)
* 2. 清理mbedTLS的DH和AES上下文
* 3. 清零敏感数据(密钥等)
* 4. 释放安全结构体内存
*
* 该函数会在蓝牙连接断开或配网结束时被调用。
*
* @return esp_err_t 成功返回ESP_OK,失败返回ESP_FAIL
**/
esp_err_t blufi_security_deinit(void)
{
// 检查安全上下文是否存在
if (blufi_sec == NULL) {
return ESP_FAIL; // 上下文不存在,返回错误
}
// 释放DH参数缓冲区(如果存在)
if (blufi_sec->dh_param) {
audio_free(blufi_sec->dh_param);
blufi_sec->dh_param = NULL;
}
// 清理mbedTLS的DH和AES上下文
mbedtls_dhm_free(&blufi_sec->dhm); // 释放DH上下文资源
mbedtls_aes_free(&blufi_sec->aes); // 释放AES上下文资源
// 清零并释放安全结构体
memset(blufi_sec, 0x0, sizeof(struct blufi_security)); // 清零敏感数据
audio_free(blufi_sec); // 释放结构体内存
blufi_sec = NULL; // 重置指针
return ESP_OK; // 清理成功
}
这两个函数分别用于初始化和销毁BLUFI安全模块:
-
blufi_security_init
:- 分配安全结构体内存
- 初始化DH和AES上下文
- 初始化IV向量
-
blufi_security_deinit
:- 释放DH参数内存
- 清理DH和AES上下文
- 释放安全结构体内存
安全协议流程
BLUFI安全协议的完整流程如下:
安全措施分析
BLUFI安全机制融合了多种密码学技术,提供了较强的安全保障:
-
密钥交换安全:
- 使用DH算法安全地在不安全信道上建立共享密钥
- 使用1024位DH密钥,具有足够的安全强度
- 动态生成参数,避免固定密钥被破解
-
数据加密安全:
- 使用AES-128-CFB模式提供数据机密性
- 动态IV机制增强安全性,防止重放攻击
- 每次加密使用不同的IV首字节
-
数据完整性保护:
- 使用CRC-16校验和验证数据完整性
- 防止数据被篡改或损坏
-
内存安全:
- 使用动态内存分配和适当的错误处理
- 及时释放敏感数据所占用的内存
- 初始化过程中使用安全的清零操作
与标准库的集成
BLUFI安全模块充分利用了mbedTLS库提供的密码学功能,而不是自行实现密码算法:
- 使用
mbedtls_dhm
进行DH密钥交换 - 使用
mbedtls_aes
进行AES加密 - 使用
mbedtls_md5
进行密钥派生
这种方式充分利用了经过安全审计和验证的密码库,减少了潜在的安全漏洞。
版本兼容性处理
代码中包含了对不同ESP-IDF版本的适配:
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0))
const int dhm_len = mbedtls_dhm_get_len(&blufi_sec->dhm);
ret = mbedtls_dhm_make_public(&blufi_sec->dhm, dhm_len, blufi_sec->self_public_key, dhm_len, myrand, NULL);
#else
ret = mbedtls_dhm_make_public(&blufi_sec->dhm, (int) mbedtls_mpi_size( &blufi_sec->dhm.P ), blufi_sec->self_public_key, blufi_sec->dhm.len, myrand, NULL);
#endif
这种适配确保了代码在不同版本的ESP-IDF上的兼容性,主要针对:
- ESP-IDF 4.0及以上版本中CRC函数位置的变化
- ESP-IDF 5.0及以上版本中mbedTLS API的变化
- 不同版本中随机数生成API的变化
安全实现的优势
BLUFI安全实现的主要优势包括:
- 安全性:使用成熟的DH+AES加密方案,提供较高级别的安全保障
- 可移植性:通过使用mbedTLS库,代码具有良好的可移植性
- 灵活性:支持自定义数据传输,可用于设备绑定等高级场景
- 兼容性:处理不同ESP-IDF版本之间的API变化
- 资源效率:针对ESP32等资源受限设备进行了优化
安全最佳实践
从BLUFI安全实现中可以总结出以下安全最佳实践:
- 使用标准密码学库而非自行实现
- 实现动态密钥协商而非硬编码密钥
- 加密前对数据进行校验和保护
- 密钥派生使用单向散列函数
- 适当处理内存分配和释放,避免泄露
- 安全相关操作增加错误检查和日志
潜在安全风险
尽管BLUFI实现了较完善的安全机制,但仍存在一些潜在风险:
- DH参数如果选择不当可能导致弱密钥
- CRC-16提供完整性检查但不能抵御有针对性的篡改
- 没有明确的会话管理机制
- 缺乏身份认证环节,可能面临中间人攻击
针对这些风险,在实际应用中可以考虑:
- 结合设备绑定流程增强身份认证
- 考虑增加消息认证码(MAC)以增强数据完整性保护
- 实现更完善的会话管理机制