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 加密流程分析

阶段一:主设备发送加密请求

  1. 主设备主机发送请求:主设备主机(Master Host)通过 HCI 接口向链路层发送 HCI_LE_Start_Encryption 命令,携带 Connection_HandleRandom_Number (Rand)、Encrypted_Diversifier (EDIV) 和 Long_Term_Key (LTK)。
  2. 主设备暂停发送数据 PDU:主设备链路层在收到指令后,会先发送完当前队列中的数据 PDU,随后暂停发送所有数据 PDU,仅允许发送控制 PDU(如 LL_ENC_REQ)或空包。
  3. 主设备链路层发送请求: 发送 LL_ENC_REQ 。其中 RandEDIV (来自 Host),SKDmIVm (控制器内部生成的随机数)。

第二步:从设备接收加密请求
4. 暂停接收数据 PDU:从设备接收到主设备的 LL_ENC_REQ 之后,会暂停 TX 和 RX 数据流。从设备的链路层应完成当前数据通道 PDU 的发送,并可以完成控制器中排队的其他数据通道 PDU 的发送。在这些数据通道 PDU 被确认后,从设备的链路层只允许发送空 PDU 或 LL_ENC_RSPLL_START_ENC_REQLL_START_ENC_RSPLL_TERMINATE_INDLL_REJECT_INDLL_REJECT_IND_EXT PDU。
5. 从设备发送响应: 发送 LL_ENC_RSP (携带 SKDs 和 IVs)。主设备收到后也将暂停数据 PDU接收。
6. 获取 LTK: 向主机发送 LL_LTK_REQ_IND,包含 Master 发来的 RandEDIV。等待从设备主机回复 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。
流程如下:

  1. 主设备发送 LL_PAUSE_ENC_REQ PDU(加密的)来发起暂停请求。在发送前,它会确保当前的数据包已发送完毕,并暂停发送新的数据包。
  2. 从设备收到请求后,同样完成当前数据包的发送,然后回复一个 LL_PAUSE_ENC_RSP PDU(加密的)。同时,从设备将自己设置为准备接收未加密的数据。
  3. 主设备收到从设备的响应后,也将自己设置为收发都不加密的状态,并向从设备再发送一个 LL_PAUSE_ENC_RSP PDU(这次是未加密的)。
  4. 从设备收到这个未加密的响应后,也切换到发送不加密的状态。

至此,双方都暂停了加密。接下来,系统将自动启动前述的“加密开始流程”,以协商并启用新的会话密钥。

加密暂停时序图

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 数据加密流程

数据加密流程分为以下几个部分 :

  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 的首字节。将其中的 NESNSNMD 比特位强制置为 0。这是为了确保重传(导致这些位变化)不会破坏解密校验。
    • Session Key: 用 LTK 作为密钥对 SKD进行 AES-128 运算。这在上文中开始加密的阶段已经准备好了。
  2. 生成 MIC
    将 AAD、Nonce 和 明文 Payload 填入 CCM 规范定义的 \(B_0, B_1...\) 数据块中。使用 Session Key 对这些块进行 AES-CBC-MAC 运算,生成 MIC。
  3. 加密 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 发送] end

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 参考文献

posted @ 2025-12-24 16:50  ixbwer  阅读(58)  评论(0)    收藏  举报