1. AES CCM 概述:CCM模式的定义、应用场景与GCM对比
各位好,我是老周。今天咱们开始聊AES CCM的内存占用问题。不过别急,在深入内存细节之前,得先把CCM模式本身搞清楚。说白了,CCM就是CTR模式加上CBC-MAC——一个负责加密,一个负责认证。我当年第一次看到这个组合时,心里想的是:这不就是把两个模式硬拼在一起吗?后来踩过坑才明白,这里面的门道可不少。
1.1 CCM模式到底是什么?
CCM的全称是Counter with CBC-MAC。它是一种认证加密模式,同时提供机密性和完整性保护。你想想看,很多场景下光加密是不够的——别人虽然看不懂你的数据,但可以篡改它。CCM就是来解决这个问题的。
它的核心结构其实很清晰:
- 加密部分:使用CTR(计数器)模式,把AES当做一个流密码来用
- 认证部分:使用CBC-MAC,对明文和附加数据计算一个消息认证码
这里有个关键点:CCM先做认证,后做加密。我习惯叫它「先认证后加密」。为什么是这个顺序?因为CBC-MAC需要完整的明文输入,而CTR模式可以边加密边输出。这个顺序直接影响内存占用——后面我们会详细分析。
核心要点:CCM = CTR + CBC-MAC,先认证后加密。Nonce长度为7-13字节,这是WiFi和蓝牙协议里常见的配置。
1.2 应用场景:WiFi和蓝牙
CCM最常见的两个战场,就是WiFi和蓝牙。我做过几个无线协议栈的移植项目,几乎每次都要跟CCM打交道。
WiFi(IEEE 802.11i/WPA2)
WiFi安全协议里,CCMP(Counter Mode with CBC-MAC Protocol)是强制要求的加密方案。WPA2个人版和企业版都用它。我记得有一次调试WiFi驱动,发现吞吐量上不去,查了半天原来是CCM的Nonce生成逻辑在某个边界条件下出了bug——每次重连都复用同一个Nonce,加密直接废掉。
蓝牙(Bluetooth 4.0+/LE Secure Connections)
蓝牙低功耗(BLE)在4.2版本之后引入了LE Secure Connections,底层用的就是AES-CCM。我做过一个蓝牙门锁的项目,MCU只有64KB的RAM,CCM的内存占用成了大问题。那时候我才真正开始研究CCM的内存布局——每个中间缓冲区、每个状态变量都得精打细算。
其他场景还包括:
- Zigbee(802.15.4安全套件)
- IPsec(RFC 4309定义了CCM的使用方式)
- TLS 1.2(虽然现在主流是GCM,但CCM也是可选套件)
1.3 CCM vs GCM:到底差在哪?
很多人问我:CCM和GCM都是认证加密,为什么选CCM?我一般会反问:你的硬件支持GCM的并行计算吗?
咱们直接看对比表:
| 对比维度 | CCM | GCM |
|---|---|---|
| 加密模式 | CTR(计数器模式) | CTR(计数器模式) |
| 认证方式 | CBC-MAC | GHASH(Galois域乘法) |
| 处理顺序 | 先认证后加密 | 先加密后认证 |
| 并行能力 | 差(CBC-MAC串行依赖) | 好(GHASH可流水线化) |
| 内存占用 | 较低(但需要额外认证缓冲区) | 较高(GHASH表需要预计算) |
| 硬件加速 | 简单(仅需AES核心) | 复杂(需要GF乘法器) |
| Nonce长度 | 7-13字节(固定) | 12字节(推荐) |
从表里能看出几个关键差异:
- 认证部分:CCM用CBC-MAC,GCM用GHASH。CBC-MAC是串行的,每算一个块都得等前一个块的结果。GHASH虽然也是串行,但它的乘法可以在硬件里流水线化。
- 内存占用:CCM在认证阶段需要缓存整个明文(或者至少一个完整的认证块),而GCM可以边加密边认证。这个差异在资源受限的MCU上特别明显。
- Nonce管理:CCM的Nonce长度是可变的(7-13字节),GCM固定12字节。我遇到过有人把Nonce长度设错,结果加密出来的数据别人解不了——这种坑最好在设计阶段就规避。
避坑提醒:我曾经在一个项目里,为了省内存把CCM的Nonce长度设成7字节。结果WiFi协议栈要求Nonce必须12字节,两边对不上,调试了整整两天。所以,Nonce长度一定要跟协议规范对齐,别自己瞎改。
1.4 CCM的工作流程
咱们用一张图把CCM的完整流程画清楚。这张图我画了很多遍,每次给团队新人讲CCM时都会拿出来用。
流程其实不复杂:
- 格式化:把Nonce、标志位和长度信息拼成B_0块。这是CBC-MAC的第一个输入块。
- CBC-MAC认证:从B_0开始,依次处理所有认证数据和明文数据。每个块的输出是下一个块的输入——这就是串行依赖的来源。
- CTR加密:用计数器模式加密明文,同时把上一步算出的MAC也加密一遍,生成最终的认证标签。
- 输出:密文 + 加密后的MAC(也叫MIC,Message Integrity Code)。
我的经验:在实现CCM时,很多人会忽略B_0块的构造细节。B_0里的标志位决定了Nonce长度和MAC长度,一旦算错,整个认证过程就全废了。我建议你在写代码时,先把B_0的每个bit都画出来,对着协议规范逐位核对。
1.5 为什么内存占用是核心问题?
好,现在咱们回到课程的主题——内存占用。CCM的内存问题主要出在认证阶段:
- CBC-MAC需要完整输入:认证计算时,你得把整个明文(或者至少一个完整的16字节块)准备好。如果明文很大,缓冲区就得很大。
- 先认证后加密导致双重缓冲:认证阶段需要明文,加密阶段也需要明文。如果你不能原地操作,就得准备两份缓冲区。
- Nonce和附加数据也要存:这些数据虽然不大,但在资源受限的系统里,每一字节都得计较。
相比之下,GCM的GHASH虽然计算复杂,但它的内存模型更友好——可以边收数据边认证,不需要额外的大缓冲区。这就是为什么很多高性能场景选GCM,而资源受限场景(比如物联网)还在用CCM。
嗯,这一章咱们把CCM的底子打好了。后面的章节会深入每个模块的内存布局,包括CBC-MAC的中间状态、CTR计数器的管理、以及如何在只有几KB RAM的MCU上跑通CCM。到时候我会拿实际项目的代码来拆解,咱们一步步把内存占用算清楚。