BLE 4.2 Controller 加密流程与实现
在 BLE(Bluetooth Low Energy)连接建立后,为了保障数据传输的机密性与完整性,链路层(Link Layer)可根据上层主机(Host)的请求启动加密流程。
本文将详细解析 BLE 控制器的加密握手流程、异常处理机制、AES-CCM 底层原理,并结合 Cordio 协议栈源码进行分析。
1 加密流程 (Encryption Procedure)
加密的核心在于主从设备双方协商出一致的会话密钥 (Session Key) 和 初始化向量 (IV)。这两个参数由双方各自生成的片段组合而成: IV (Initialization Vector) = IVm (主) + IVs (从), SKD (Session Key Diversifier) = SKDm (主) + SKDs (从)。
它们通过 LL_ENC_REQ(加密请求)和 LL_ENC_RSP(加密响应)这两个 PDU(协议数据单元)进行交换。
1.1 加密流程分析
阶段一:主设备发送加密请求
- 主设备主机发送请求:主设备主机(Master Host)通过 HCI 接口向链路层发送
HCI_LE_Start_Encryption命令,携带Connection_Handle、Random_Number(Rand)、Encrypted_Diversifier(EDIV) 和Long_Term_Key(LTK)。 - 主设备暂停发送数据 PDU:主设备链路层在收到指令后,会先发送完当前队列中的数据 PDU,随后暂停发送所有数据 PDU,仅允许发送控制 PDU(如
LL_ENC_REQ)或空包。 - 主设备链路层发送请求: 发送
LL_ENC_REQ。其中Rand,EDIV(来自 Host),SKDm,IVm(控制器内部生成的随机数)。
第二步:从设备接收加密请求
4. 暂停接收数据 PDU:从设备接收到主设备的 LL_ENC_REQ 之后,会暂停 TX 和 RX 数据流。从设备的链路层应完成当前数据通道 PDU 的发送,并可以完成控制器中排队的其他数据通道 PDU 的发送。在这些数据通道 PDU 被确认后,从设备的链路层只允许发送空 PDU 或 LL_ENC_RSP、LL_START_ENC_REQ、LL_START_ENC_RSP、LL_TERMINATE_IND、LL_REJECT_IND 或 LL_REJECT_IND_EXT PDU。
5. 从设备发送响应: 发送 LL_ENC_RSP (携带 SKDs 和 IVs)。主设备收到后也将暂停数据 PDU接收。
6. 获取 LTK: 向主机发送 LL_LTK_REQ_IND,包含 Master 发来的 Rand 和 EDIV。等待从设备主机回复 HCI_LE_LTK_Request_Reply,提供对应的 Long_Term_Key (LTK)。
第三步:密钥的组合与生成
此时,主从双方都拥有了对方的 IV 和 SKD 片段。它们将按照以下规则将各部分拼接成完整的 SKD 和 IV:
SKD = SKDm || SKDs(SKDm 的低位字节在前,SKDs 的高位字节在后)IV = IVm || IVs(IVm 的低位字节在前,IVs 的高位字节在后)
从设备的链路层会使用加密引擎,以 LTK 作为加密密钥,以拼接好的 SKD 作为输入明文,计算出本次通信的会话密钥(sessionKey)。这个sessionKey将用于后续所有数据包的加解密。
第四步:完成加密握手
在成功生成 sessionKey 后:
11. 从设备发送 LL_START_ENC_REQ PDU(此包未加密),通知 Master 已准备好接收一个加密的响应。
12. 主设备收到后,发送 LL_START_ENC_RSP PDU(此包已加密),并准备好收发加密数据,此时主设备进入加密状态。
13. 从设备收到加密的 LL_START_ENC_RSP 后,也回复一个已加密的 LL_START_ENC_RSP PDU,此时从设备进入加密状态。
第五步:通知主机:
双方链路层都会通知各自的上层主机,发送 LL_START_ENC_RSP 事件,表示连接已成功加密。
成功加密时序图
1.2 异常情况
在加密流程中,可能会遇到各种异常,链路层需根据情况进行处理。
1.2.1 从设备不支持加密
若从设备不支持加密,会直接拒绝 LL_ENC_REQ。然后发送 LL_REJECT_IND 或 LL_REJECT_IND_EXT。 错误码:0x1A (Unsupported Remote Feature)。Master 通知 Host 加密失败,连接保持未加密状态继续通信。
1.2.2 密钥丢失 (LTK Missing)
场景 A:初始加密时缺失
若 Slave Host 无法提供 LTK(例如未配对或密钥丢失),应拒绝加密。错误码:0x06 (PIN or Key Missing)。连接保持未加密状态。
场景 B:加密暂停后缺失
若在“加密暂停”流程(即更换密钥)后无法提供 LTK,属于严重安全错误。必须断开连接。
1.2.3 MIC 校验失败 (MIC Failure)
在加密开启后的任何阶段,如果收到解密校验失败的数据包(MIC 不匹配),或者在握手阶段收到非预期的明文/密文。立即断开连接。0x3D (Connection Terminated Due to MIC Failure)。
从设备不支持加密时序图
主机未提供 LTK 时序图
1.3 加密暂停流程
如果需要在不中断连接的情况下更换加密密钥,就需要先暂停当前的加密,然后再重新开始加密流程。在暂停期间,为保护数据安全,不允许发送未加密的数据 PDU。
流程如下:
- 主设备发送
LL_PAUSE_ENC_REQPDU(加密的)来发起暂停请求。在发送前,它会确保当前的数据包已发送完毕,并暂停发送新的数据包。 - 从设备收到请求后,同样完成当前数据包的发送,然后回复一个
LL_PAUSE_ENC_RSPPDU(加密的)。同时,从设备将自己设置为准备接收未加密的数据。 - 主设备收到从设备的响应后,也将自己设置为收发都不加密的状态,并向从设备再发送一个
LL_PAUSE_ENC_RSPPDU(这次是未加密的)。 - 从设备收到这个未加密的响应后,也切换到发送不加密的状态。
至此,双方都暂停了加密。接下来,系统将自动启动前述的“加密开始流程”,以协商并启用新的会话密钥。
加密暂停时序图
异常处理:与加密开始流程类似,如果在暂停过程中收到非预期的 PDU,也应立即断开连接,并报告 "Connection Terminated Due to MIC Failure" (0 x 3 D) 错误。
加密重新启动 (Encryption Restart) 在需要动态增加或减少安全级别时非常有用。例如,初始建立连接时安全级别较低,但在传输敏感数据时,可以使用此过程更换链路密钥并提升安全级别。
2 数据加密和解密
BLE 使用 AES-128 作为基础加密引擎,结合 CCM (Counter with CBC-MAC) 模式。CCM 模式同时提供加密(机密性) 和认证(完整性)。
2.1 数据加密流程
数据加密流程分为以下几个部分 :
- 准备Nonce, AAD, Session Key
- Nonce(Number Used Once)总长度 13 字节,由以下各部分拼接:PacketCounter,39 bits,每个连接独立的计数器。每发送一个新包就 +1,重传时不增加;DirectionBit,1 bit,主设备 (Master) 发送时为 1,从设备 (Slave) 为 0;IV (初始化向量) ,8 octets,连接开始时生成的随机数,双方共享。
- AAD (Additional Authenticated Data)。提取 Data PDU 的首字节。将其中的
NESN、SN、MD比特位强制置为 0。这是为了确保重传(导致这些位变化)不会破坏解密校验。 - Session Key: 用 LTK 作为密钥对 SKD进行 AES-128 运算。这在上文中开始加密的阶段已经准备好了。
- 生成 MIC
将 AAD、Nonce 和 明文 Payload 填入 CCM 规范定义的 \(B_0, B_1...\) 数据块中。使用 Session Key 对这些块进行 AES-CBC-MAC 运算,生成 MIC。 - 加密 Payload 和 MIC
使用 Session Key 和 Nonce 生成密钥流 (Keystream)。将明文 Payload 与密钥流进行 XOR(异或) 运算,得到加密后的 Payload。将步骤 2 算出的 MIC 也与密钥流的第一段进行 XOR 运算,得到加密后的 MIC。
加密流程图
2.2 Cordio 代码分析
PalCryptoAesCcmEncrypt 函数用于对发送的数据包进行软件 AES-CCM 加密。
/*
* 位于: platform/targets/nordic/sources/pal_crypto.c
* 功能: 执行 AES-CCM 加密
*/
bool_t PalCryptoAesCcmEncrypt(PalCryptoEnc_t *pEnc, uint8_t *pHdr, uint8_t *pBuf, uint8_t *pMic)
{
// 1. 基础检查
// 如果未启用加密,直接返回
if (!pEnc->enaEncrypt) { return FALSE; }
// 2. 准备 Nonce
// 将当前的 Packet Counter 填入 Nonce 结构中
// PAL_BB_NONCE_MODE_PKT_CNTR 模式下,计数器由软件维护
if (pEnc->nonceMode == PAL_BB_NONCE_MODE_PKT_CNTR) {
palCryptoIncPktCnt(pCb);
}
// 3. 加载密钥
// 将 Session Key 加载到硬件 ECB 引擎
palCryptoLoadEcbData(pEnc);
// 4. 计算 MIC (认证 - Authentication)
// 使用 AES-CBC-MAC 算法计算 MIC
if (pEnc->enaAuth) {
palCryptoAuthPdu(pEnc->type, pCb, pMic, pHdr, pBuf, pldLen);
}
// 5. 加密 Payload (加密 - Encryption)
// 使用 AES-CTR 模式加密数据 (异或密钥流)
palCryptoPdu(pCb, pMic, pBuf, pldLen);
// 6. 更新计数器
// 为下一个包准备 Packet Counter
if (pEnc->nonceMode == PAL_BB_NONCE_MODE_PKT_CNTR) {
palCryptoIncPktCnt(pCb);
}
return pEnc->enaAuth;
}
PalCryptoAesCcmDecrypt 函数用于对接收的数据包进行软件 AES-CCM 解密和校验。
/*
* 位于: platform/targets/nordic/sources/pal_crypto.c
* 功能: 执行 AES-CCM 解密与校验
*/
bool_t PalCryptoAesCcmDecrypt(PalCryptoEnc_t *pEnc, uint8_t *pBuf)
{
// 1. 基础检查
if (!pEnc->enaDecrypt) { return TRUE; }
// 2. 解密 Payload
// CTR 模式下,解密操作与加密操作数学上是对称的 (都是 XOR 密钥流)
// 这里会还原出明文 Payload 和明文 MIC
palCryptoPdu(pCb, pMic, pBuf, pldLen);
// 3. 计算预期 MIC
// 基于解密后的明文,重新计算一遍 MIC
if (pEnc->enaAuth) {
palCryptoAuthPdu(pEnc->type, pCb, actMic, pHdr, pBuf, pldLen);
}
// 4. 验证 MIC
// 将计算得到的 actMic 与包中携带的 pMic 进行比较
if (memcmp(actMic, pMic, 4) != 0) {
return FALSE; // MIC 校验失败,Controller 将断开连接
}
return TRUE;
}
3 参考文献
- https://blog.csdn.net/weixin_43946212/article/details/108116251
- Bluetooth 4.2 Spec:
- Vol 6, PartB, 5.1.3 Encryption procedure
- Vol 6, PartD, 5 Initiating State
- Vol 6, Part E Low Energy Link Layer Security
- https://github.com/apache/mynewt-nimble/tree/Master
- https://github.com/packetcraft-inc/stacks
- https://www.scribd.com/document/582984411/BLE4-0低功耗蓝牙协议完全解析#sidebar
本文版权归作者:ixbwer所有,转载请注明原文链接:https://www.cnblogs.com/ixbwer/p/19393586,否则保留追究法律责任的权利。

浙公网安备 33010602011771号