BluFi ^^^^^ :link_to_translation:`en:[English]` 概览 ----- BluFi 是一款基于蓝牙通道的 Wi-Fi 网络配置功能,适用于 ESP32。它通过安全协议将 Wi-Fi 配置和证书传输到 ESP32,然后 ESP32 可基于这些信息连接到 AP 或建立 SoftAP。 BluFi 流程的关键部分包括数据的分片、加密、校验和验证。 用户可按需自定义用于对称加密、非对称加密和校验的算法。这里我们采用 DH 算法进行密钥协商、128-AES 算法用于数据加密、CRC16 算法用于校验和验证。 BluFi 流程 ---------- BluFi 配网功能包含配置 SoftAP 和 Station 两部分。 下面以配置 Station 为例说明配置步骤。 BluFi 配网的配置 Station 包含广播、连接、服务发现、协商共享密钥、传输数据、回传连接状态等步骤。 ESP32 配网流程 -------------- 1. ESP32 开启 GATT Server 功能,发送带有特定 *adv data* 的广播。你可以自定义该广播,该广播不属于 BluFi Profile。 2. 使用手机 APP 搜索到该特定广播,手机作为 GATT Client 连接 ESP32。你可以决定使用哪款手机 APP。 3. GATT 连接建立成功后,手机向 ESP32 发送“协商过程”数据帧(详情见 :ref:`frame_formats` )。 4. ESP32 收到“协商过程”数据帧后,会按照使用者自定义的协商过程来解析。 5. 手机与 ESP32 进行密钥协商。协商过程可使用 DH/RSA/ECC 等加密算法进行。 6. 协商结束后,手机端向 ESP32 发送“设置安全模式”控制帧。 7. ESP32 收到“设置安全模式”控制帧后,使用经过协商的共享密钥以及配置的安全策略对通信数据进行加密和解密。 8. 手机向 ESP32 发送“BluFi 传输格式”定义的 SSID、Password 等用于 Wi-Fi 连接的必要信息。 9. 手机向 ESP32 发送“Wi-Fi 连接请求”控制帧,ESP32 收到之后,识别为手机已将必要的信息传输完毕,准备连接 Wi-Fi。 10. ESP32 连接到 Wi-Fi 后,发送“Wi-Fi 连接状态报告”控制帧到手机,以报告连接状态。至此配网结束。 .. note:: 1. 安全模式设置可在任何时候进行,ESP32 收到安全模式的配置后,会根据安全模式指定的模式进行安全相关的操作。 2. 进行对称加密和解密时,加密和解密前后的数据长度必须一致,支持原地加密和解密。 配网流程图 ----------- .. seqdiag:: :caption: BluFi Flow Chart :align: center seqdiag blufi { activation = none; node_width = 80; node_height = 60; edge_length = 380; span_height = 10; default_fontsize = 12; Phone <- ESP32 [label="广播"]; Phone -> ESP32 [label="建立 GATT 链接"]; Phone <- ESP32 [label="协商密钥"]; Phone -> ESP32 [label="协商密钥"]; Phone -> ESP32 [label="CTRL: 设置 ESP32 手机安全模式"]; Phone -> ESP32 [label="DATA: SSID"]; Phone -> ESP32 [label="DATA: Password"]; Phone -> ESP32 [label="DATA: 其他信息,如 CA 认证"]; Phone -> ESP32 [label="CTRL: 连接到 AP"]; Phone <- ESP32 [label="DATA: 连接状态报告"]; } .. _frame_formats: BluFi 传输格式 -------------- 手机 APP 与 ESP32 之间的 BluFi 通信格式定义如下: 帧不分片情况下的标准格式 (8 bit): +-----------------+----------------+ | Description | Value | +=================+================+ | LSB - Type | 1 | +-----------------+----------------+ | Frame Control | 1 | +-----------------+----------------+ | Sequence Number | 1 | +-----------------+----------------+ | Data Length | 1 | +-----------------+----------------+ | Data | ${Data Length} | +-----------------+----------------+ | MSB - CheckSum | 2 | +-----------------+----------------+ 如果 **Frame Control** 帧中的 **More Frag** 使能,则 **Total Content Length** 为数据帧中剩余部分的总长度,用于报告终端需要分配多少内存。 帧分片格式(8 bit): +--------------------+-------------------------------------------+ | Description | Value | +====================+===========================================+ | LSB - Type | 1 | +--------------------+-------------------------------------------+ | FrameControl(Frag) | 1 | +--------------------+-------------------------------------------+ | SequenceNumber | 1 | +--------------------+-------------------------------------------+ | DataLength | 1 | +--------------------+----------------------+--------------------+ | | Total Content Length | 2 | + Data +----------------------+--------------------+ | | Content | ${Data Length} - 2 | +--------------------+----------------------+--------------------+ | MSB - CheckSum | 2 | +--------------------+-------------------------------------------+ 通常情况下,控制帧不包含数据位,Ack 帧类型除外。 Ack 帧格式(8 bit): +------------------+--------------------------------------------+ | Description | Value | +------------------+--------------------------------------------+ | LSB - Type (Ack) | 1 | +------------------+--------------------------------------------+ | Frame Control | 1 | +------------------+--------------------------------------------+ | SequenceNumber | 1 | +------------------+--------------------------------------------+ | DataLength | 1 | +------------------+-----------------------+--------------------+ + Data + Acked Sequence Number + 2 + | | | | +------------------+-----------------------+--------------------+ | MSB - CheckSum | 2 | +------------------+--------------------------------------------+ 1. Type 类型域,占 1 byte。分为 Type 和 Subtype(子类型域)两部分, Type 占低 2 bit,Subtype 占高 6 bit。 * 控制帧,暂不进行加密,可校验; * 数据帧,可加密,可校验。 **1.1 控制帧 (0x0b’00)** +------------------+-----------------------------------+----------------------------------------------------------------+----------------------------------------------------------------------+ | 控制帧 / 0x0b’00 | 含义 | 解释 | 备注 | +==================+===================================+================================================================+======================================================================+ | 0x0b’000000 | Ack | 用来回复对方发的帧, | Data 域使用1 byte Sequence 值, | | | | Ack 帧的 Data 域使用回复对象帧的 Sequence 值。 | 与恢复对象帧的Sequence 值相同。 | +------------------+-----------------------------------+----------------------------------------------------------------+----------------------------------------------------------------------+ | 0x1b’000001 | Set ESP32 to the security mode. | 通知 ESP32 发送数据时使用的安全模式, | Data 域占用 1 byte。 | | | | 在该过程中可设置多次,每次设置后影响后续安全模式。 | 高 4 bit 为控制帧的安全模式,低 4bit 为数据帧的安全模式。 | + + + 在不设置的情况下,ESP32 默认控制帧和数据帧均为无校验、无加密。 +----------------------------------------------------------------------+ | | | 手机到 ESP32 方向依赖于帧 Control 域。 | b’0000:无校验、无加密; | + + + +----------------------------------------------------------------------+ | | | | b’0001:有校验、无加密; | + + + +----------------------------------------------------------------------+ | | | | b’0010:无校验、有加密; | + + + +----------------------------------------------------------------------+ | | | | b’0011:有校验、有加密。 | +------------------+-----------------------------------+----------------------------------------------------------------+----------------------------------------------------------------------+ | 0x2b’000010 | Set the Wi-Fi opmode of ESP32. | 设置 ESP32 的 Wi-Fi 模式,帧包含 opmode 信息。 | data[0] 用于表示 opmode 类型,包括: | + + + +----------------------------------------------------------------------+ | | | | 0x00: NULL; | + + + +----------------------------------------------------------------------+ | | | | 0x01: STA; | + + + +----------------------------------------------------------------------+ | | | | 0x02: SoftAP; | + + + +----------------------------------------------------------------------+ | | | | 0x03: SoftAP&STA. | + + + +----------------------------------------------------------------------+ | | | | 如果设置有包含 AP,请尽量优先 | | | | | 设置 AP 模式的SSID/Password/Max Conn Number 等。 | +------------------+-----------------------------------+----------------------------------------------------------------+----------------------------------------------------------------------+ | 0x3b’000011 | Connect ESP32 to the AP. | 通知 ESP32,必要的信息已经发送完毕,可以连接 AP。 | 不包含 Data 域。 | +------------------+-----------------------------------+----------------------------------------------------------------+----------------------------------------------------------------------+ | 0x4b’000100 | Disconnect ESP32 from the AP. | 通知 ESP32 断开与 AP 的连接 | 不包含 Data 域。 | +------------------+-----------------------------------+----------------------------------------------------------------+----------------------------------------------------------------------+ | 0x5b’000101 | Get the status of Wi-Fi. | 获取 ESP32 的 Wi-Fi 模式和状态等信息。 | 不包含 Data 域。 | | | | | ESP32 收到此控制帧后,后续会通过 Wi-Fi 连接状态 | | | | | 报告 (Wi-Fi Connection State Report) 数据帧来回复手机端当前 | | | | | 所处的 opmode、连接状态、SSID 等信息。提供给手机端的信息由应用决定。 | +------------------+-----------------------------------+----------------------------------------------------------------+----------------------------------------------------------------------+ | 0x6b’000110 | Disconnect the STA device | 处于 SoftAP 模式时,踢掉某个 STA 设备。 | data[0~5] 为 STA 设备的 MAC 地址, | | | from the SoftAP in SoftAP mode. | | 如有多个 STA,则 [6-11] 为第二个,依次类推。 | +------------------+-----------------------------------+----------------------------------------------------------------+----------------------------------------------------------------------+ | 0x7b'000111 | Get the version. | | | +------------------+-----------------------------------+----------------------------------------------------------------+----------------------------------------------------------------------+ | 0x8b’001000 | Tell ESP32 to disconnect | 通知 ESP32 断开蓝牙连接。 | ESP32 收到该指令后主动断开蓝牙连接。 | | | the BLE GATT link. | | | +------------------+-----------------------------------+----------------------------------------------------------------+----------------------------------------------------------------------+ | 0x9b’001001 | Tell ESP32 to get the Wi-Fi list. | 通知 ESP32 扫描周围的 Wi-Fi 热点 | 不包含 Data 域。 | | | | | ESP32 收到此控制帧后,会发送包含 Wi-Fi 热点 | | | | | 报告 (Wi-Fi List Report) 的数据帧回复 | | | | | 手机端 ESP32 周围的 Wi-Fi 热点。 | +------------------+-----------------------------------+----------------------------------------------------------------+----------------------------------------------------------------------+ **1.2 数据帧 (0x1b’01)** +---------------+----------------------------------------+------------------------------------------------+------------------------------------------------------+ | 数据帧 | 含义 | 解释 | 备注 | +===============+========================================+================================================+======================================================+ | 0x0 b’000000 | Negotiation data. | 用来发送协商数据,传输到应用层注册的回调函数。 | 数据长度与 Length 域有关。 | +---------------+----------------------------------------+------------------------------------------------+------------------------------------------------------+ | 0x1 b’000001 | BSSID for STA mode. | STA 将要连接的 AP 的 BSSID(用于隐藏SSID)。 | 数据长度与 Length 域有关。 | + + + +------------------------------------------------------+ | | | | 当传输方向为 ESP32 到手机时, | | | | | 表示向手机端提供信息。 | +---------------+----------------------------------------+------------------------------------------------+------------------------------------------------------+ | 0x2 b’000010 | SSID for STA mode. | STA 将要连接的 AP 的 SSID。 | 数据长度与 Length 域有关。 | + + + +------------------------------------------------------+ | | | | 当传输方向为 ESP32 到手机时, | | | | | 表示向手机端提供信息。 | +---------------+----------------------------------------+------------------------------------------------+------------------------------------------------------+ | 0x3 b’000011 | Password for STA mode. | STA 将要连接的 AP 的密码。 | 数据长度与 Length 域有关。 | + + + +------------------------------------------------------+ | | | | 当传输方向为 ESP32 到手机时, | | | | | 表示向手机端提供信息。 | +---------------+----------------------------------------+------------------------------------------------+------------------------------------------------------+ | 0x4 b’000100 | SSID for SoftAP mode. | SoftAP 模式使用的 SSID。 | 数据长度与 Length 域有关。 | + + + +------------------------------------------------------+ | | | | 当传输方向为 ESP32 到手机时, | | | | | 表示向手机端提供信息。 | +---------------+----------------------------------------+------------------------------------------------+------------------------------------------------------+ | 0x5 b’000101 | Password for SoftAPmode. | SoftAP 模式使用的密码。 | 数据长度与 Length 域有关。 | + + + +------------------------------------------------------+ | | | | 当传输方向为 ESP32 到手机时, | | | | | 表示向手机端提供信息。 | +---------------+----------------------------------------+------------------------------------------------+------------------------------------------------------+ | 0x6 b’000110 | Max connection number for SoftAP mode. | AP 模式的最大连接数。 | data[0] 表示连接数的值,范围 1~4。 | + + + +------------------------------------------------------+ | | | | 当传输方向为 ESP32 到手机时, | | | | | 表示向手机端提供信息。 | +---------------+----------------------------------------+------------------------------------------------+------------------------------------------------------+ | 0x7b’000111 | Authentication mode for SoftAP mode. | AP 模式的认证模式。 | data[0]: | + + + +------------------------------------------------------+ | | | | 0x00: OPEN; | + + + +------------------------------------------------------+ | | | | 0x01: WEP; | + + + +------------------------------------------------------+ | | | | 0x02: WPA_PSK; | + + + +------------------------------------------------------+ | | | | 0x03: WPA2_PSK; | + + + +------------------------------------------------------+ | | | | 0x04: WPA_WPA2_PSK. | + + + +------------------------------------------------------+ | | | | 当传输方向为 ESP32 到手机时, | | | | | 表示向手机端提供信息。 | +---------------+----------------------------------------+------------------------------------------------+------------------------------------------------------+ | 0x8b’001000 | Channel for SoftAP mode. | SoftAP 模式的通道数量。 | data[0] 表示通道的数量,范围 1~14。 | + + + +------------------------------------------------------+ | | | | 当传输方向为 ESP32 到手机时, | | | | | 表示向手机端提供信息。 | +---------------+----------------------------------------+------------------------------------------------+------------------------------------------------------+ | 0x9b’001001 | Username. | 使用企业级加密时,Client 端的用户名。 | 数据长度与 Length 域有关。 | +---------------+----------------------------------------+------------------------------------------------+------------------------------------------------------+ | 0xab’001010 | CA certification. | 进行企业级加密时使用的 CA 证书。 | 数据长度与 Length 域有关, | | | | | 长度不够,可用分片。 | +---------------+----------------------------------------+------------------------------------------------+------------------------------------------------------+ | 0xbb’001011 | Client certification. | 进行企业级加密时,Client 端的证书。 | 数据长度与 Length 域有关, | + + +------------------------------------------------+ 长度不够,可用分片。 + | | | 可包含或不包含私钥,由证书内容决定。 | | +---------------+----------------------------------------+------------------------------------------------+------------------------------------------------------+ | 0xcb’001100 | Server certification. | 进行企业级加密时,Server 端的证书。 | 数据长度与 Length 域有关, | + + +------------------------------------------------+ 长度不够,可用分片。 + | | | 可包含或不包含私钥,由证书内容决定。 | | +---------------+----------------------------------------+------------------------------------------------+------------------------------------------------------+ | 0xdb’001101 | Client private key. | 进行企业级加密时,Client 端的私钥。 | 数据长度与 Length 域有关, | | | | | 长度不够,可用分片。 | +---------------+----------------------------------------+------------------------------------------------+------------------------------------------------------+ | 0xeb’001110 | Server private key. | 进行企业级加密时,Server 端的私钥。 | 数据长度与 Length 域有关, | | | | | 长度不够,可用分片。 | +---------------+----------------------------------------+------------------------------------------------+------------------------------------------------------+ | 0xf b’001111 | Wi-Fi connection state report. | 通知手机 ESP32 的 Wi-Fi 状态, | data[0] 表示 opmode,包括: | | | | 包括 STA状态和 SoftAP 状态, | | | | | 用于手机配置 STA 连接时的通知, | | | | | 或有 STA 连接上 SoftAP 时的通知。 | | + + +------------------------------------------------+------------------------------------------------------+ | | | 但收到手机询问 Wi-Fi 状态时, | 0x00: NULL; | + + + 除了回复此帧外,还可回复其他数据帧。 +------------------------------------------------------+ | | | | 0x01: STA; | + + + +------------------------------------------------------+ | | | | 0x02: SoftAP; | + + + +------------------------------------------------------+ | | | | 0x03: SoftAP&STA | + + + +------------------------------------------------------+ | | | | data[1]:STA 的连接状态, | | | | | 0x0 表示处于连接状态, | | | | | 其他表示处于非连接状态; | + + + +------------------------------------------------------+ | | | | data[2]:SoftAP 的连接状态, | | | | | 即表示有多少 STA 已经连接。 | + + + +------------------------------------------------------+ | | | | data[3] 及以后:为按照本协议格式 SSID\BSSID 等信息。 | +---------------+----------------------------------------+------------------------------------------------+------------------------------------------------------+ | 0x10 b’010000 | Version. | | data[0]= great version | + + + +------------------------------------------------------+ | | | | data[1]=sub version | +---------------+----------------------------------------+------------------------------------------------+------------------------------------------------------+ | 0x11 B’010001 | Wi-Fi list. | 通知手机 ESP32 周围的 Wi-Fi 热点列表。 | 数据帧数据格式为 Length + RSSI + SSID, | | | | | 数据较长时可分片发送。 | +---------------+----------------------------------------+------------------------------------------------+------------------------------------------------------+ | 0x12 B’010010 | Report error. | 通知手机 BluFi 过程出现异常错误。 | 0x00: sequence error; | + + + +------------------------------------------------------+ | | | | 0x01: checksum error; | + + + +------------------------------------------------------+ | | | | 0x02: decrypt error; | + + + +------------------------------------------------------+ | | | | 0x03: encrypt error; | + + + +------------------------------------------------------+ | | | | 0x04: init security error; | + + + +------------------------------------------------------+ | | | | 0x05: dh malloc error; | + + + +------------------------------------------------------+ | | | | 0x06: dh param error; | + + + +------------------------------------------------------+ | | | | 0x07: read param error; | + + + +------------------------------------------------------+ | | | | 0x08: make public error. | +---------------+----------------------------------------+------------------------------------------------+------------------------------------------------------+ | 0x13 B’010011 | Custom data. | 用户发送或者接收自定义数据。 | 数据较长时可分片发送。 | +---------------+----------------------------------------+------------------------------------------------+------------------------------------------------------+ 2. Frame Control 帧控制域,占 1 byte,每个 bit 表示不同含义。 +----------------+-------------------------------------------------------------------------------------------------------------------------------+ | 位 | 含义 | +================+===============================================================================================================================+ | 0x01 | 表示帧是否加密。 | + +-------------------------------------------------------------------------------------------------------------------------------+ | | 1 表示加密,0 表示未加密。 | + +-------------------------------------------------------------------------------------------------------------------------------+ | | 加密部分帧括完整的 DATA 域加密之前的明文(不帧含末尾的校验)。 | + +-------------------------------------------------------------------------------------------------------------------------------+ | | 控制帧暂不加密,故控制帧此位为 0。 | +----------------+-------------------------------------------------------------------------------------------------------------------------------+ | 0x02 | 表示帧 Data 域结尾是否帧含校验(例如 SHA1,MD5,CRC等)需要校验的数据域包括 sequcne + data length + 明文 data。 | + +-------------------------------------------------------------------------------------------------------------------------------+ | | 控制帧和数据帧都可以包含校验位或不包含。 | +----------------+-------------------------------------------------------------------------------------------------------------------------------+ | 0x04 | 表示数据方向。 | + +-------------------------------------------------------------------------------------------------------------------------------+ | | 0 表示手机发向 ESP32; | + +-------------------------------------------------------------------------------------------------------------------------------+ | | 1 表示 ESP32 发向手机。 | +----------------+-------------------------------------------------------------------------------------------------------------------------------+ | 0x08 | 表示是否要求对方回复 ack。 | + +-------------------------------------------------------------------------------------------------------------------------------+ | | 0 表示不要求; | + +-------------------------------------------------------------------------------------------------------------------------------+ | | 1 表示要求回复 ack。 | +----------------+-------------------------------------------------------------------------------------------------------------------------------+ | 0x10 | 表示是否有后续的数据分片。 | + +-------------------------------------------------------------------------------------------------------------------------------+ | | 0 表示此帧没有后续数据分片; | + +-------------------------------------------------------------------------------------------------------------------------------+ | | 1 表示还有后续数据分片,用来传输较长的数据。 | + +-------------------------------------------------------------------------------------------------------------------------------+ | | 如果是 Frag 帧,则告知当前 content 部分+后续 content 部分的总长度,位于 Data 域的前 2 字节(即最大支持 64K 的 content 数据)。 | +----------------+-------------------------------------------------------------------------------------------------------------------------------+ | 0x10~0x80 保留 | | +----------------+-------------------------------------------------------------------------------------------------------------------------------+ 3. Sequence Control 序列控制域。帧发送时,无论帧的类型是什么,序列 (Sequence) 都会自动加 1,用来防止重放攻击 (Replay Attack)。每次重现连接后,序列清零。 4. Length Data 域的长度,不包含 CheckSum。 5. Data 不同的 Type 或 Subtype,Data 域的含义均不同。请参考上方表格。 6. CheckSum 此域为 2 byte 的校验,用来校验『序列 + 数据长度 + 明文数据』。 ESP32 端的安全实现 ------------------ 1. 保证数据安全 为了保证 Wi-Fi SSID 和密码的传输过程是安全的,需要使用对称加密算法(例如 AES、DES等)对报文进行加密。在使用对称加密算法之前,需要使用非对称加密算法(DH、RSA、ECC 等)协商出(或生成出)一个共享密钥。 2. 保证数据完整性 保证数据完整性,需要加入校验算法(例如 SHA1、MD5、CRC 等)。 3. 身份安全(签名) 某些算法如 RSA 可以保证身份安全。有些算法如 DH,本身不能保证身份安全,需要添加其他算法来签名。 4. 防止重放攻击 (Replay Attack) 加入帧发送序列(Sequence),并且序列参与数据校验。 在 ESP32 端的代码中,你可以决定和开发密钥协商等安全处理的流程参考上述流程图)。手机应用向 ESP32 发送协商数据,将传送给应用层处理。如果应用层不处理,可使用 BluFi 提供的 DH 加密算法来磋商密钥。应用层需向 BluFi 注册以下几个与安全相关的函数: .. code-block:: c typedef void (*esp_blufi_negotiate_data_handler_t)(uint8_t *data, int len, uint8_t **output_data, int *output_len, bool *need_free); 该函数用来接收协商期间的正常数据 (normal data),处理完成后,需要将待发送的数据使用 output_data 和 output_len 传出。 BluFi 会在调用完 negotiate_data_handler 后,发送 negotiate_data_handler 传出的 output_data。 这里的两个『*』,因为需要发出去的数据长度未知,所以需要函数自行分配 (malloc) 或者指向全局变量,通过 need_free 通知是否需要释放内存。 .. code-block:: c typedef int (* esp_blufi_encrypt_func_t)(uint8_t iv8, uint8_t *crypt_data, int cyprt_len); 加密和解密的数据长度必须一致。其中 iv8 为帧的 8 bit 序列 (sequence),可作为 iv 的某 8 bit 来使用。 .. code-block:: c typedef int (* esp_blufi_decrypt_func_t)(uint8_t iv8, uint8_t *crypt_data, int crypt_len); 加密和解密的数据长度必须一致。其中 iv8 为帧的 8 bit 序列 (sequence),可作为 iv 的某 8 bit 来使用。 .. code-block:: c typedef uint16_t (*esp_blufi_checksum_func_t)(uint8_t iv8, uint8_t *data, int len); 该函数用来计算 CheckSum,返回值为 CheckSum 的值。BluFi 会使用该函数返回值与包末尾的 CheckSum 做比较。 GATT 相关说明 ------------- UUID >>>>> BluFi Service UUID: 0xFFFF,16 bit BluFi (手机 -> ESP32) 特性:0xFF01,主要权限:可写 BluFi (ESP32 -> 手机) 特性:0xFF02,主要权限:可读可通知 .. note:: 1. 目前 Ack 机制已经在该 Profile 协议中定义,但是还没有代码实现。 2. 其他部分均已实现。