找回密码
 立即注册
首页 业界区 业界 BLE 4.2 Controller 加密流程与实现

BLE 4.2 Controller 加密流程与实现

俏襟选 昨天 18:00
在 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 事件,表示连接已成功加密。
成功加密时序图
sequenceDiagram    participant Master_Host as 主设备主机    participant Master_Link_Layer as 主设备链路层    participant Slave_Link_Layer as 从设备链路层    participant Slave_Host as 从设备主机    Master_Host->>Master_Link_Layer: 1. HCI_LE_Start_Encryption 开始加密        Master_Link_Layer->>Master_Link_Layer: 2. 暂停数据发送,等待发送队列清空    Master_Link_Layer->>Slave_Link_Layer: 3. 发送 LL_ENC_REQ (携带 IVm, SKDm, Rand, EDIV)        Slave_Link_Layer->>Slave_Link_Layer: 4. 暂停数据收发,生成 IVs, SKDs    Slave_Link_Layer->>Master_Link_Layer: 5. 回复 LL_ENC_RSP (携带 IVs, SKDs)    Slave_Link_Layer->>Slave_Host: 6. 发送LL_LTK_REQ_IND 事件        Slave_Host->>Slave_Link_Layer: 发送 HCI_LE_LTK_Request_Reply    Master_Link_Layer->>Master_Link_Layer: 通过 LTK 和 SKD 计算会话密钥 Session Key    Slave_Link_Layer->>Slave_Link_Layer: 通过 LTK 和 SKD 计算会话密钥 Session Key    Slave_Link_Layer->>Master_Link_Layer: 11. 发送 LL_START_ENC_REQ (此包未加密)        Master_Link_Layer->>Master_Link_Layer: 使能发送和接收加密    Master_Link_Layer->>Slave_Link_Layer: 12. 回复 LL_START_ENC_RSP (此包已加密)        Master_Link_Layer->>Master_Link_Layer: 恢复数据收发        Slave_Link_Layer->>Slave_Link_Layer: 使能发送加密    Master_Link_Layer->>Master_Host: 发送LL_ENC_CHANGE_IND 事件,通知Host已加密        Slave_Link_Layer->>Master_Link_Layer: 13. 发送 LL_START_ENC_RSP (此包已加密)        Slave_Link_Layer->>Slave_Link_Layer: 恢复数据收发    Slave_Link_Layer->>Slave_Host: 发送LL_ENC_CHANGE_IND 事件,通知Host已加密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)。
从设备不支持加密时序图
sequenceDiagram    participant Master_Host as 主设备主机    participant Master_Link_Layer as 主设备链路层    participant Slave_Link_Layer as 从设备链路层    Master_Host->>Master_Link_Layer: LE Start Encryption 请求启用加密    Master_Link_Layer->>Slave_Link_Layer: LL_ENC_REQ        Slave_Link_Layer->>Master_Link_Layer: LL_REJECT_IND / LL_REJECT_IND_EXT (Unsupported Remote feature 错误码: 0x1A)    Master_Link_Layer->>Master_Host: 通知:加密失败 (对方不支持)主机未提供 LTK 时序图
sequenceDiagram    participant Master_Host as 主设备主机    participant Master_Link_Layer as 主设备链路层    participant Slave_Link_Layer as 从设备链路层    participant Slave_Host as 从设备主机    Master_Link_Layer->>Slave_Link_Layer: LL_ENC_REQ    Slave_Link_Layer->>Master_Link_Layer: LL_ENC_RSP        Master_Link_Layer->>Master_Host: 请求 LTK    Master_Host-->>Master_Link_Layer: 未提供 LTK        Slave_Link_Layer->>Slave_Host: 请求 LTK    Slave_Host-->>Slave_Link_Layer: 未提供 LTK        Slave_Link_Layer->>Master_Link_Layer: LL_REJECT_IND / LL_REJECT_IND_EXT (错误码: PIN or key Missing)    Master_Link_Layer->>Master_Host: 通知:加密失败 (密钥丢失)1.3 加密暂停流程

如果需要在不中断连接的情况下更换加密密钥,就需要先暂停当前的加密,然后再重新开始加密流程。在暂停期间,为保护数据安全,不允许发送未加密的数据 PDU。
流程如下:

  • 主设备发送 LL_PAUSE_ENC_REQ PDU(加密的)来发起暂停请求。在发送前,它会确保当前的数据包已发送完毕,并暂停发送新的数据包。
  • 从设备收到请求后,同样完成当前数据包的发送,然后回复一个 LL_PAUSE_ENC_RSP PDU(加密的)。同时,从设备将自己设置为准备接收未加密的数据。
  • 主设备收到从设备的响应后,也将自己设置为收发都不加密的状态,并向从设备再发送一个 LL_PAUSE_ENC_RSP PDU(这次是未加密的)。
  • 从设备收到这个未加密的响应后,也切换到发送不加密的状态。
至此,双方都暂停了加密。接下来,系统将自动启动前述的“加密开始流程”,以协商并启用新的会话密钥。
加密暂停时序图
sequenceDiagram    participant Master_Link_Layer as 主设备链路层    participant Slave_Link_Layer as 从设备链路层    Master_Link_Layer->>Slave_Link_Layer: 1. LL_PAUSE_ENC_REQ (已加密)    Slave_Link_Layer->>Master_Link_Layer: 2. LL_PAUSE_ENC_RSP (已加密)        Master_Link_Layer->>Master_Link_Layer: 3. 切换到未加密模式    Master_Link_Layer->>Slave_Link_Layer: 4. LL_PAUSE_ENC_RSP (未加密)        Slave_Link_Layer->>Slave_Link_Layer: 5. 切换到未加密模式        Note over Master_Link_Layer, Slave_Link_Layer: 双方现在都处于未加密状态,暂停完成。        Note over Master_Link_Layer, Slave_Link_Layer: 接下来将自动执行“加密开始流程”以启用新密钥。异常处理:与加密开始流程类似,如果在暂停过程中收到非预期的 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。
加密流程图
graph TD    subgraph "输入准备"        A[原始 Payload] --> B{长度 > 0?}        B -- Yes --> C[准备 Header Octet 0]        C --> D[掩码处理: NESN/SN/MD 设为 0]        E[Packet Counter + Direction + IV] --> F[构造 13-byte Nonce]    end    subgraph "生成MIC"        D --> G[构造 B0, B1 块]        A --> G        F --> G        G --> H[AES-CBC-MAC 计算]        SK[Session Key] --> H        H --> I[生成 4-byte MIC]    end    subgraph "生成密钥"        F --> J[构造 A0, A1... 块]        SK --> K[AES 引擎]        J --> K        K --> L[生成 Keystream 密钥流]    end    subgraph "最终组包 (Final Assembly)"        A --> M[XOR 异或运算]        I --> M        L --> M        M --> N[加密后的 Payload + 加密后的 MIC]        N --> O[添加原始 Header 发送]    end2.2 Cordio 代码分析

PalCryptoAesCcmEncrypt  函数用于对发送的数据包进行软件 AES-CCM 加密。
  1. /*
  2. * 位于: platform/targets/nordic/sources/pal_crypto.c
  3. * 功能: 执行 AES-CCM 加密
  4. */
  5. bool_t PalCryptoAesCcmEncrypt(PalCryptoEnc_t *pEnc, uint8_t *pHdr, uint8_t *pBuf, uint8_t *pMic)
  6. {
  7.   // 1. 基础检查
  8.   // 如果未启用加密,直接返回
  9.   if (!pEnc->enaEncrypt) { return FALSE; }
  10.   // 2. 准备 Nonce
  11.   // 将当前的 Packet Counter 填入 Nonce 结构中
  12.   // PAL_BB_NONCE_MODE_PKT_CNTR 模式下,计数器由软件维护
  13.   if (pEnc->nonceMode == PAL_BB_NONCE_MODE_PKT_CNTR) {
  14.       palCryptoIncPktCnt(pCb);
  15.   }
  16.   // 3. 加载密钥
  17.   // 将 Session Key 加载到硬件 ECB 引擎
  18.   palCryptoLoadEcbData(pEnc);
  19.   // 4. 计算 MIC (认证 - Authentication)
  20.   // 使用 AES-CBC-MAC 算法计算 MIC
  21.   if (pEnc->enaAuth) {
  22.     palCryptoAuthPdu(pEnc->type, pCb, pMic, pHdr, pBuf, pldLen);
  23.   }
  24.   // 5. 加密 Payload (加密 - Encryption)
  25.   // 使用 AES-CTR 模式加密数据 (异或密钥流)
  26.   palCryptoPdu(pCb, pMic, pBuf, pldLen);
  27.   // 6. 更新计数器
  28.   // 为下一个包准备 Packet Counter
  29.   if (pEnc->nonceMode == PAL_BB_NONCE_MODE_PKT_CNTR) {
  30.     palCryptoIncPktCnt(pCb);
  31.   }
  32.   return pEnc->enaAuth;
  33. }
复制代码
PalCryptoAesCcmDecrypt 函数用于对接收的数据包进行软件 AES-CCM 解密和校验。
  1. /*
  2. * 位于: platform/targets/nordic/sources/pal_crypto.c
  3. * 功能: 执行 AES-CCM 解密与校验
  4. */
  5. bool_t PalCryptoAesCcmDecrypt(PalCryptoEnc_t *pEnc, uint8_t *pBuf)
  6. {
  7.   // 1. 基础检查
  8.   if (!pEnc->enaDecrypt) { return TRUE; }
  9.   // 2. 解密 Payload
  10.   // CTR 模式下,解密操作与加密操作数学上是对称的 (都是 XOR 密钥流)
  11.   // 这里会还原出明文 Payload 和明文 MIC
  12.   palCryptoPdu(pCb, pMic, pBuf, pldLen);
  13.   // 3. 计算预期 MIC
  14.   // 基于解密后的明文,重新计算一遍 MIC
  15.   if (pEnc->enaAuth) {
  16.     palCryptoAuthPdu(pEnc->type, pCb, actMic, pHdr, pBuf, pldLen);
  17.   }
  18.   // 4. 验证 MIC
  19.   // 将计算得到的 actMic 与包中携带的 pMic 进行比较
  20.   if (memcmp(actMic, pMic, 4) != 0) {
  21.     return FALSE; // MIC 校验失败,Controller 将断开连接
  22.   }
  23.   
  24.   return TRUE;
  25. }
复制代码
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

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册