4、蓝牙加密引擎:AES-CCM加密算法在蓝牙中的应用、密钥生成与派生函数

聊到蓝牙安全,加密算法是绕不开的核心。我个人习惯把蓝牙的加密引擎比作一个「黑盒子」——你给它一把钥匙,它帮你把数据锁起来,别人没有钥匙就打不开。这个黑盒子的核心,就是 AES-CCM

你可能会问,为什么蓝牙偏偏选了 AES-CCM?其实原因很简单:它兼顾了加密和完整性校验。我在项目中遇到过不少只加密不校验的情况,结果数据被篡改了都不知道,那叫一个头疼。AES-CCM 一次性搞定两件事,省心。

4.1 AES-CCM 在蓝牙中的角色

AES-CCM 全称是 Advanced Encryption Standard - Counter with CBC-MAC。说白了,它把两种模式合在了一起:

  • CTR 模式:负责数据加密,速度快,支持并行计算。
  • CBC-MAC:负责消息认证,确保数据没有被篡改。

在蓝牙协议栈里,AES-CCM 主要用于 LE 安全连接(LE Secure Connections)传统配对(Legacy Pairing) 的加密阶段。嗯,这里要注意:传统配对用的是 AES-128 的 ECB 模式,而 LE Secure Connections 才真正用上了 AES-CCM。

核心要点: AES-CCM 的输入包括密钥、随机数(Nonce)、附加认证数据(AAD)和明文数据。输出是密文和消息认证码(MIC)。

我记得有一次调试一个蓝牙耳机项目,发现连接后数据传输偶尔会断开。排查了半天,最后发现是 MIC 校验失败导致的。你想想看,如果 MIC 长度配置不对,接收方算出来的校验码和发送方对不上,就会直接丢弃数据包。这个坑,我踩过。

4.2 密钥生成与派生函数

加密算法有了,密钥从哪来?蓝牙里不会直接用你输入的 PIN 码或者密码去加密数据,而是通过一套 密钥派生函数(KDF) 来生成会话密钥。这样做的好处是:即使长期密钥泄露,之前的通信记录依然是安全的(前向安全性)。

蓝牙规范里定义了两种主要的密钥派生方式:

函数名称 用途 输入参数
f1 生成确认值(用于配对阶段) U、V、X、Y、Z
f2 生成长期密钥(LTK)和随机数 DHKey、N1、N2、A1、A2
f3 生成认证值(用于 LE Secure Connections) DHKey、N1、N2、A1、A2
f4 生成确认值(用于 LE Secure Connections) DHKey、N1、N2、A1、A2
h1 生成哈希值(用于传统配对) U、V、X、Y、Z
h2 生成密钥(用于传统配对) U、V、X、Y、Z

这些函数本质上都是基于 AES-128 的 CMAC 模式构建的。你想想看,蓝牙规范的设计者很聪明,他们不想引入额外的加密原语,而是把 AES 用到了极致。

4.3 密钥派生流程详解

LE Secure Connections 为例,密钥派生的流程大致如下:

  1. 生成 DHKey:双方通过椭圆曲线 Diffie-Hellman(ECDH)交换公钥,计算出共享密钥 DHKey。
  2. 调用 f2 函数:输入 DHKey、双方的随机数(N1、N2)和蓝牙地址(A1、A2),输出长期密钥(LTK)和额外的随机数。
  3. 调用 f3 函数:生成认证值,用于验证对方是否真的拥有 DHKey。
  4. 调用 f4 函数:生成确认值,用于配对阶段的确认。

我曾经在实现 f2 函数时犯过一个低级错误:把输入参数的顺序搞反了。结果配对总是失败,折腾了两天才发现是字节序的问题。嗯,蓝牙协议里很多参数都是小端序,这一点千万要注意。

个人建议: 在实现密钥派生函数时,最好先写一个单元测试,用蓝牙规范里的测试向量(Test Vectors)来验证。规范里每个函数都给出了输入和期望输出,直接对照着测,能省去很多调试时间。

4.4 加密引擎的初始化

在实际的蓝牙芯片中,AES-CCM 引擎的初始化通常包括以下几个步骤:

  • 加载密钥:从安全模块中读取 LTK,加载到 AES 引擎的密钥寄存器中。
  • 设置 Nonce:Nonce 由数据包序号(Packet Sequence Number)、方向位(Direction)和随机数组成。每个数据包的 Nonce 都不同,防止重放攻击。
  • 设置 AAD:附加认证数据通常包含蓝牙数据包的头部信息,确保头部不被篡改。
  • 启动加密:将明文数据送入引擎,输出密文和 MIC。

我记得有一次在低功耗蓝牙(BLE)项目中,发现加密后的数据包长度变长了。原因很简单:AES-CCM 会附加一个 4 字节或 8 字节的 MIC。如果上层应用没有预留这个空间,就会导致缓冲区溢出。这个坑,我建议你在设计协议栈时提前考虑进去。

4.5 避坑指南

我曾经踩过的坑:

  • Nonce 重复使用:AES-CCM 的安全性高度依赖 Nonce 的唯一性。如果两个数据包使用了相同的 Nonce 和密钥,攻击者可以恢复出密钥流。我在一个项目中就遇到过这种情况,原因是数据包序号没有正确递增。
  • MIC 长度配置错误:蓝牙规范允许 MIC 长度为 4 字节或 8 字节。如果发送方和接收方配置不一致,会导致校验失败。
  • 字节序问题:蓝牙协议中,多字节字段通常使用小端序。但在 AES-CCM 的输入中,Nonce 和 AAD 的字节序需要严格按照规范来,否则加密结果会完全错误。

4.6 性能优化建议

在实际的嵌入式系统中,AES-CCM 的计算可能会成为性能瓶颈。我个人习惯从以下几个方面优化:

  • 硬件加速:如果芯片支持硬件 AES 引擎,尽量使用硬件加速。软件实现 AES-CCM 在低功耗 MCU 上可能会消耗大量 CPU 时间。
  • 预计算:对于固定的密钥和 Nonce,可以预计算 CTR 模式的密钥流,减少实时计算量。
  • 批量处理:如果多个数据包需要加密,尽量批量处理,减少上下文切换的开销。

嗯,这里要注意:预计算密钥流虽然能提升性能,但会占用额外的内存。在资源受限的嵌入式设备上,需要权衡利弊。

4.7 小结

AES-CCM 是蓝牙加密引擎的基石,它把加密和完整性校验合二为一,设计得非常精巧。密钥派生函数则确保了会话密钥的安全生成,防止了密钥泄露的风险。你在实际开发中,只要注意 Nonce 的唯一性、MIC 的配置和字节序问题,基本不会出大问题。

下一章,我会聊聊蓝牙的 安全配对协议,包括传统配对和 LE Secure Connections 的详细流程。到时候我会分享一些我在配对调试中遇到的奇葩问题,敬请期待。