行业资讯
📅 2026/7/24 1:20:48
嵌入式MCU硬件AES-GCM/CCM加速与DMA协同配置实战
1. 项目概述在嵌入式系统开发中数据安全是至关重要的一个环节。无论是物联网设备间的通信还是固件的安全启动都需要确保数据的机密性不被窃听和完整性不被篡改。AES高级加密标准作为最广泛使用的对称加密算法其各种工作模式满足了不同场景的需求。其中AES-GCMGalois/Counter Mode和AES-CCMCounter with CBC-MAC是两种集成了加密和认证功能的“认证加密”模式它们在一次处理中同时完成数据的加密和生成用于验证的认证标签TAG效率极高。然而在资源受限的微控制器MCU上如果完全由CPU通过软件库来处理AES运算尤其是GCM/CCM这类涉及多次乘法、异或和块加密的复杂模式会消耗大量的CPU周期严重影响系统实时性和整体性能。因此现代MCU普遍集成了硬件AES加速器并允许与DMA直接内存访问控制器协同工作将数据搬运的任务从CPU卸载出去实现“设置好就走”set-and-forget的高效数据处理流程。本文将以德州仪器TIMSPM0 G系列微控制器中的AESADV高级加密加速器模块为例深入剖析AES-GCM和AES-CCM模式的硬件工作原理、数据流并重点讲解如何结合DMA控制器进行实战化配置。我会结合寄存器手册和实际调试经验拆解从初始化、数据准备、DMA通道设置到结果读取的完整步骤并分享在实现过程中容易遇到的“坑”及其解决方案。无论你是正在评估芯片加密性能的架构师还是埋头调试加密功能的嵌入式软件工程师这篇文章都能为你提供从理论到实践的清晰指引。2. AES-GCM/CCM模式原理与硬件加速机制要高效地驱动硬件必须首先理解它要完成的任务。AES-GCM和AES-CCM都属于认证加密AEAD模式但它们的内部构造和计算顺序有所不同这直接影响了硬件加速器的数据流设计和我们的编程模型。2.1 AES-GCM模式深度解析GCM模式可以看作是两个并行操作的组合CTR模式加密和GMAC认证。其核心优势在于认证部分使用了高效的伽罗瓦域Galois Field, GF(2^128)乘法非常适合硬件并行化实现。2.1.1 加密与认证的并行流水线在GCM中加密过程是典型的CTR模式一个初始计数器由IV和计数器部分构成经过AES加密后生成一个密钥流该密钥流与明文进行异或得到密文。这个流程与单纯的AES-CTR模式完全一致。与此同时认证过程独立进行。它处理两类数据附加认证数据AAD例如报文头和上面产生的密文如果是解密则处理接收到的密文。认证算法GHASH的核心是计算(AAD || 密文 || 长度信息)在伽罗瓦域上的哈希值最后再将此哈希值与一个由密钥和IV生成的特定值称为E(K, Y0)进行异或最终得到认证标签TAG。在硬件实现上像MSPM0 AESADV这样的模块其内部很可能有一个独立的伽罗瓦域乘法器单元可以与AES加密核心并行工作。这意味着当数据块进入引擎后加密和认证计算可以同时发生从而极大地提升了吞吐率。这也是GCM模式在高速网络协议如TLS 1.2/1.3中备受青睐的原因。2.1.2 关键参数H与Y0理解以下两个预计算值对配置硬件至关重要H (Hash Key): 这是将全零数据块用当前密钥通过AES-ECB模式加密后的结果。它是伽罗瓦域乘法的固定乘数。在硬件中我们可以选择让引擎内部自动计算HAutonomous GHASH也可以由CPU预先计算好并通过GHASH_Hx寄存器组写入以节省初始化时间。Y0-encrypted: 这是由IV和特定格式构成的数据块通常为IV || 0...0 1经过AES加密后的结果。它是生成最终TAG前的最后一个异或操作数。同样它可以由硬件内部计算或由外部提供。硬件模块的CTRL[GCM]字段正是用来配置这些选项的01b: GHASH with H loaded and Y0-encrypted forced to zero。此模式仅用于纯认证GMAC即N0没有加密数据。10b: GHASH with H loaded and Y0-encrypted calculated internally。这是我们最常用的模式CPU提供H硬件计算Y0。11b: Autonomous GHASH。硬件自己计算H和Y0CPU干预最少。2.2 AES-CCM模式深度解析CCM模式是另一种流行的认证加密模式它是CBC-MAC用于认证和CTR模式用于加密的串行组合。与GCM的并行不同CCM是严格顺序执行的先完成整个报文的CBC-MAC计算得到认证标签然后再用CTR模式加密数据和这个标签。2.2.1 串行化的认证与加密流程CCM的操作顺序决定了其数据必须分两阶段处理认证阶段CBC-MAC首先处理AAD如果有然后处理明文数据。注意此阶段处理的是明文。对于解密操作则是先进行CTR解密得到明文再对明文进行CBC-MAC验证。硬件在内部会维护一个临时的认证结果Temp Result随着每个数据块的输入而更新。加密/解密阶段CTR在认证阶段之后对于加密或之前对于解密使用CTR模式对数据进行加密或解密。这种串行特性意味着对于加密操作硬件需要先“看到”所有明文以完成CBC-MAC然后再对其进行CTR加密。在纯DMA模式下这要求AAD和明文数据在内存中必须是连续存放的因为DMA通道会一次性将整个数据流AAD 明文送入引擎。手册中特别强调“因为单个DMA通道同时提供AAD和明文整个数据必须在内存中连续组织首先是M个块的AAD接着是N个块的明文。”2.2.2 数据格式与对齐要求无论是GCM还是CCMAAD和加密数据都可能不是128位16字节的整数倍。硬件要求CPU负责将它们填充到128位边界填充内容为0。更正式地说填充必须满足位字符串0^n其中0 n 127且因为引擎只支持字节n必须是8的倍数。这意味着如果你的AAD长度是30字节你需要填充2个字节的0x00使其成为32字节2个块。同样明文/密文也需要按16字节对齐填充。这是一个非常关键的细节如果填充不正确计算出的TAG将与标准实现如OpenSSL的结果不匹配导致认证失败。3. 硬件模块与DMA协同工作框架MSPM0的AESADV模块是一个高度集成化的协处理器它通过一组精心设计的寄存器与CPU/DMA交互并通过事件Events系统来触发DMA传输。3.1 AESADV事件系统CPU与DMA的桥梁模块提供了三类事件发布者Publisher它们是硬件与外部世界通信的触发器事件名称类型目的地功能描述CPU_INT中断事件CPU子系统向CPU发送中断请求通知操作完成、数据就绪等状态。DMA_TRIG_DATAINDMA触发事件0DMA控制器当引擎输入缓冲区就绪可以接收新数据时触发。用于控制DMA将数据从内存搬运到DATA_IN寄存器。DMA_TRIG_DATAOUTDMA触发事件1DMA控制器当引擎输出缓冲区有有效数据可读时触发。用于控制DMA将数据从DATA_OUT寄存器搬运到内存。核心工作流程初始化CPU配置好AES密钥、IV、模式(CTRL寄存器)、数据长度(C_LENGTH,AAD_LENGTH)等上下文Context。启动DMACPU配置DMA通道将DMA_TRIG_DATAIN作为源触发事件并启动DMA。数据输入AES引擎准备好接收数据时发出DMA_TRIG_DATAIN事件。DMA控制器捕获该事件自动从内存读取一个数据块如16字节写入DATA_IN或它的别名DATA0/1/2/3。引擎处理AES引擎收到数据后开始计算。数据输出计算完成结果就绪后引擎发出DMA_TRIG_DATAOUT事件。DMA控制器捕获该事件自动从DATA_OUT读取结果并写入目标内存地址。循环与完成步骤3-5循环进行直到所有数据块处理完毕。最后引擎可能通过CPU_INT通知CPU读取最终的认证标签(TAG)。通过这种方式CPU仅在初始化和最终处理时介入中间的数据搬运全部由DMA完成实现了极高的效率。3.2 关键寄存器组功能详解要正确驱动AESADV必须理解以下几组核心寄存器3.2.1 控制与状态寄存器 (CTRL)这是整个模块的“大脑”。除了选择工作模式GCM, CCM, CBC, CTR等、密钥长度、方向加密/解密外有几个位对GCM/CCM和DMA操作至关重要SAVE_CNTXT(位29):必须置1。这告诉引擎在操作结束后保留上下文即最终的TAG并等待CPU来读取。如果不设置引擎会在处理完最后一个数据块后立即请求新的上下文导致你来不及读取TAG。GCM_CONT(位28) /OFB_GCM_CCM_CONT(位26): 用于恢复一个被中断的GCM/CCM操作。例如处理一个超长数据流时你可以中途读取中间TAG通过GET_DIGEST保存状态稍后恢复。GCM_CONT用于恢复加密/解密阶段OFB_GCM_CCM_CONT用于恢复AAD阶段。GET_DIGEST(位27): 请求引擎在下一个128位边界处暂停并输出当前的中间认证结果TAG。用于处理流式数据或实现“分段验证”。CNTXT_RDY(位31) /SAVED_CNTXT_RDY(位30): 只读状态位。前者为1表示可以写入新上下文后者为1表示有保存的上下文TAG可供读取。它们是CPU轮询或中断判断的依据。3.2.2 数据长度寄存器 (C_LENGTH_0/1,AAD_LENGTH)C_LENGTH: 指定加密/解密数据的字节长度。对于GCM最大为2^36 - 32字节对于CCM由CTRL[CCML]定义的L值决定最大消息长度。AAD_LENGTH: 仅用于GCM和CCM模式指定**附加认证数据(AAD)**的字节长度。CCM的AAD长度范围是0到2^16 - 2^8字节。重要提示向C_LENGTH或AAD_LENGTH寄存器写入值会触发引擎开始使用当前已配置的上下文。对于GCM/CCM必须两个长度都正确写入后引擎才会开始请求数据。3.2.3 数据与标签寄存器 (DATA0-3,TAG0-3,DATA_IN,DATA_OUT)DATA0-3: 映射到同一地址的输入/输出缓冲区。写操作指向输入缓冲区读操作指向输出缓冲区。这简化了编程模型。DATA_IN/DATA_OUT: 上述四个寄存器的别名位于单一地址。这是为DMA设计的关键特性。DMA通道可以配置为始终向DATA_IN的固定地址写入或从DATA_OUT的固定地址读取而无需关心内部是四个独立的32位寄存器。这极大简化了DMA配置。TAG0-3: 操作完成后从这里读取128位的认证标签。仅当SAVED_CNTXT_RDY位为1时读取的数据才是有效的TAG。3.2.4 DMA握手寄存器 (DMA_HS)DMA_DATA_ACK(位0): 此位决定数据确认机制。0: I/O寄存器模式。CPU需要通过轮询CTRL[INPUT_RDY]和CTRL[OUTPUT_RDY]位来判断数据缓冲区状态。1(DMA模式)使能基于DMA握手信号的数据传输。此时INPUT_RDY和OUTPUT_RDY中断应被禁用在对应的IMASK寄存器中屏蔽数据流的控制完全由DMA_TRIG_DATAIN和DMA_TRIG_DATAOUT事件与DMA通道配合完成。这是我们实现全自动DMA传输必须设置的位。4. DMA配置实战以CCM加密为例理论铺垫完毕现在我们进入实战环节。我们以AES-CCM加密一个包含AAD和明文的数据包为例详细讲解DMA通道的配置步骤和寄存器操作序列。假设我们要加密N个块的明文和M个块的AAD。4.1 内存数据布局与对齐处理首先CPU需要在内存如SRAM中准备好数据。根据手册要求当使用单个DMA通道输入时AAD和明文必须在内存中连续存放。内存布局示例 地址偏移 | 内容 ----------------------------- 0x2000_0000 | AAD 块 1 (16字节) 0x2000_0010 | AAD 块 2 (16字节) ... | ... (共M块) 0x2000_0000 M*16 | 明文块 1 (16字节) 0x2000_0010 M*16 | 明文块 2 (16字节) ... | ... (共N块)关键操作填充在将数据放入内存之前必须确保AAD和明文的字节长度都是16的倍数。如果不是需要在每个部分的末尾添加值为0的填充字节。例如13字节的AAD需要填充3字节的0x0027字节的明文需要填充5字节的0x00。填充后的长度才是你写入AAD_LENGTH和C_LENGTH寄存器的值。4.2 DMA通道详细配置步骤我们需要配置两个DMA通道一个用于将数据从内存送入AES引擎输入通道另一个用于将加密结果从引擎搬回内存输出通道。4.2.1 步骤一配置输出DMA通道搬运密文这个通道负责在AES引擎产出密文后将其搬运到目标内存区域。选择触发源设置DMA通道的触发选择为AES Trig1即DMA_TRIG_DATAOUT事件。这意味着该通道由AES引擎的“数据输出就绪”事件触发。设置源地址DMA通道的源地址应设置为AES模块的DATA_OUT寄存器地址例如0x4003_5188。DMA会从这个固定地址读取数据。设置目标地址DMA通道的目标地址设置为SRAM中预留的用于存放密文的内存区域起始地址。设置传输大小传输大小Transfer Size应设置为N * 4。因为DATA_OUT是32位4字节寄存器每个AES数据块是128位16字节需要4次32位读取。对于N个块总共需要N * 4次传输。设置传输模式设置为单次传输模式Single Transfer Mode。每次Trig1事件触发DMA就搬运一个32位字。使能DMA触发事件在AES模块的DMA_TRIG_DATAOUT事件组中找到IMASK寄存器将其中的TRIG1中断掩码位设置为1取消屏蔽。这样AES引擎产生的TRIG1事件才能被发布到DMA控制器。4.2.2 步骤二配置输入DMA通道搬运AAD和明文这个通道负责将内存中连续的AAD和明文数据块搬运到AES引擎。选择触发源设置DMA通道的触发选择为AES Trig0即DMA_TRIG_DATAIN事件。这意味着该通道由AES引擎的“请求输入数据”事件触发。设置源地址DMA通道的源地址设置为SRAM中连续数据区AAD明文的起始地址。设置目标地址DMA通道的目标地址设置为AES模块的DATA_IN寄存器地址例如0x4003_5184。DMA会向这个固定地址写入数据。设置传输大小传输大小应设置为(M N) * 4。因为需要搬运M块AAD和N块明文总共(MN)个块每个块4次32位写入。设置传输模式同样设置为单次传输模式。使能DMA触发事件在AES模块的DMA_TRIG_DATAIN事件组中找到IMASK寄存器将其中的TRIG0中断掩码位设置为1。4.2.3 步骤三配置DMA握手与中断使能DMA握手在AES模块的DMA_HS寄存器中将DMA_DATA_ACK位设置为1。这将把数据缓冲区的就绪/确认信号从寄存器轮询模式切换到DMA握手模式。配置DMA中断可选但推荐为输出DMA通道配置传输完成中断。这样当所有N个块的密文都搬运完成后DMA会产生一个中断通知CPU整个加密操作已完成可以安全地读取TAG了。4.3 AES引擎上下文配置与启动序列在DMA配置好后CPU需要配置AES引擎并启动操作。以下是配置CCM加密的典型寄存器写入序列写密钥将128/192/256位的AES密钥写入KEY0到KEY7寄存器根据密钥长度选择。写初始化向量IV将IV包含CCM格式要求的Flag和Nonce写入IV0到IV3寄存器。特别注意对于CCMIV中包含了认证和加密所需的标志位和随机数其构造必须符合CCM规范RFC 3610。配置控制寄存器CTRLKEY_SIZ: 选择密钥大小如00b为128位。DIR: 设置为1表示加密。CCM: 设置为1启用CCM模式。CTR: 必须设置为1因为CCM内部使用CTR模式加密。SAVE_CNTXT:必须设置为1以便操作结束后保存TAG。CCML: 根据你的消息长度设置L值长度字段宽度 L 1 字节。CCMM: 设置M值认证字段长度 2*(M1) 字节。例如需要8字节TAG则设为3因为2*(31)8。CTR_WIDTH: 设置的计数器宽度必须足够覆盖CCML定义的字段。写加密数据长度将N * 16加密数据的字节数必须是16的倍数写入C_LENGTH_0和C_LENGTH_1寄存器。写AAD数据长度将M * 16AAD的字节数必须是16的倍数写入AAD_LENGTH寄存器。写入此寄存器是触发GCM/CCM操作开始的信号之一。启动DMA通道使能之前配置好的输入和输出DMA通道。等待完成CPU进入低功耗模式或处理其他任务等待输出DMA通道的传输完成中断。读取结果中断触发后首先检查CTRL[SAVED_CNTXT_RDY]是否为1。确认后从TAG0到TAG3寄存器读取128位的认证标签。根据CCMM的设置只取低位的有效字节如8字节。同时密文已经由DMA自动存放到目标内存地址。关键提示手册中特别警告不要将两个长度寄存器C_LENGTH和AAD_LENGTH同时写为零。如果一个数据流结束下一个数据流使用相同的密钥和控制设置只需要重新加载IV和长度值即可。5. GCM模式操作的特殊考量与优化GCM模式的操作流程与CCM类似但由于其并行特性和预计算需求在配置上有些许不同。5.1 GCM的三种操作流程根据是否预计算H和Y0GCM有三种操作序列完全内部计算Autonomous设置CTRL[GCM]3。CPU只需提供密钥、IV和数据。硬件内部自动计算H和Y0。流程最简但初始延迟可能稍大。外部H内部Y0设置CTRL[GCM]2。这是平衡性能和灵活性的常用方法。CPU预先计算H通过一次AES-ECB加密全零块得到并写入GHASH_Hx寄存器然后提供GCM上下文含密钥、IV、H。硬件计算Y0。操作序列如手册所述提供上下文 - 提供AAD数据 - 提供加密数据 - 读取结果和TAG。GHASH OnlyGMAC设置CTRL[GCM]1且CTRL[DIR]0(解密方向)并且CTRL[CTR]0。这是纯认证模式用于仅需验证数据完整性而无须加密的场景即N0。此时C_LENGTH寄存器应设置为0。5.2 DMA配置的异同对于GCM模式DMA的配置逻辑与CCM完全一致。因为对于硬件接口来说它看到的同样是一个需要连续输入AAD和加密数据、并连续输出结果的流式接口。内存中数据的连续存放要求、对齐填充要求也同样适用。唯一的区别在于CTRL寄存器的模式位设置以及长度寄存器的含义GCM的C_LENGTH最大支持到2^36-32字节。DMA通道的触发、握手配置没有任何变化。5.3 中断恢复与长数据流处理GCM和CCM都支持操作的中断与恢复这对于处理超过内部缓冲区或需要分片处理的数据流非常有用。中断操作在数据传输过程中设置CTRL[GET_DIGEST]1。引擎会在处理完当前128位块后暂停并置起SAVED_CNTXT_RDY标志。此时CPU可以读取TAGx寄存器获得中间认证结果并读取BLK_CNT0/1寄存器获得剩余的块计数。同时GCMCCM_TAGx寄存器中保存了当前的中间TAG值。恢复操作要恢复操作CPU需要将之前保存的中间TAG值写回GCMCCM_TAGx寄存器。将剩余的块计数值写回BLK_CNT0/1寄存器。对于CCM如果中断发生在AAD阶段还需要恢复CCM_ALN_WRD寄存器。重新写入C_LENGTH和/或AAD_LENGTH剩余的长度。设置CTRL[GCM_CONT]恢复加密阶段或CTRL[OFB_GCM_CCM_CONT]恢复AAD阶段为1同时设置正确的GCM/CCM模式位。重新启动DMA传输。这个机制使得处理网络数据流或大文件时可以分段进行而不丢失安全上下文。6. 常见问题排查与调试心得在实际项目中使用硬件AES加速器时我遇到过不少问题。下面将一些典型故障现象和排查思路整理成表并分享几点核心心得。6.1 典型问题排查速查表问题现象可能原因排查步骤与解决方案计算出的TAG与软件如OpenSSL结果不匹配1.数据填充错误AAD或加密数据未按16字节对齐填充0。2.长度值错误C_LENGTH或AAD_LENGTH寄存器写入的是原始字节长度而非填充后的长度。3.IV格式错误GCM/CCM对IV的构造有特定格式如CCM的FlagsNonce。4.字节序问题密钥、IV、数据在内存中的字节序Endianness与硬件期望的不一致。1. 确认填充计算(原始长度 15) / 16 * 16得到填充后长度并确保内存末尾填充了正确的0。2. 核对寄存器写入长度寄存器的值必须是填充后的字节长度。3. 仔细对照标准如NIST SP 800-38C/D或芯片手册检查IV的构造。4. 尝试交换密钥、IV和数据块的字节顺序。通常硬件期望的是大端序Big-endian或小端序Little-endian需与CPU内存序匹配。DMA传输启动后AES引擎无反应不触发数据请求1.DMA握手未使能DMA_HS.DMA_DATA_ACK位仍为0。2.长度寄存器未写入对于GCM/CCMAAD_LENGTH寄存器未写入会阻止引擎启动。3.触发事件未取消屏蔽DMA_TRIG_DATAIN或DMA_TRIG_DATAOUT的IMASK寄存器中对应位未置1。4.DMA通道配置错误触发源选择、地址、传输大小配置有误。1. 检查DMA_HS寄存器确保DMA_DATA_ACK1。2. 确认C_LENGTH和AAD_LENGTH已正确写入非零。3. 检查AES事件模块的IMASK寄存器确保TRIG0和TRIG1已使能。4. 使用调试器检查DMA通道的配置寄存器并与上述步骤逐一核对。只能成功处理第一个数据包后续包失败1.上下文未重置上一个操作的TAG或中间状态残留在引擎中影响下一个包。2.长度寄存器未更新处理新数据包前未重新写入新的C_LENGTH和AAD_LENGTH。3.SAVE_CNTXT位处理不当读取TAG后引擎未自动进入就绪状态。1. 在每个数据包处理前执行一次AES模块的软复位通过RSTCTL寄存器或确保已正确读取并清理了上一包的TAG读取TAGx寄存器会自动清除SAVED_CNTXT_RDY。2. 确保为每个新数据包重新配置长度寄存器。3. 确认在读取TAG后CTRL[CNTXT_RDY]位是否变回1。只有此位为1才能写入新的上下文。使用DMA时CPU中断仍被触发中断未正确屏蔽在DMA握手模式下CPU_INT相关的INPUTRDY和OUTPUTRDY中断应该被屏蔽。检查CPU_INT事件组的IMASK寄存器确保INPUTRDY和OUTPUTRDY位被清除设置为0。6.2 调试心得与最佳实践从CPU轮询模式开始在集成复杂的DMA流程之前先用CPU轮询CTRL[INPUT_RDY]和CTRL[OUTPUT_RDY]的方式手动读写DATA_IN/DATA_OUT寄存器处理一个小数据块如1个AAD块1个明文块。这能最快地验证你的基础配置密钥、IV、模式、长度是否正确TAG计算是否准确。这是隔离硬件问题与DMA配置问题的有效方法。善用内存查看器在调试器中实时查看作为DMA源和目的的内存区域。确认数据在传输前已按正确的格式包括填充存放传输后的密文是否正确写入。同时可以查看DATA_IN/DATA_OUT寄存器地址观察DMA是否在正确读写。分阶段验证DMA先配置输出DMA通道用CPU模拟输入验证输出通道能否正确地将结果搬移到内存。然后再配置输入DMA通道用CPU读取输出验证输入通道能否正确喂数据。最后将两者联动。注意电源和时钟确保AES模块和DMA控制器的时钟已使能并且模块已上电通过PWREN寄存器。有些MCU的加密模块可能位于特殊的电源域。理解“块”与“字”AES引擎以128位16字节/块为单位处理数据但通过32位4字节/字的寄存器接口与总线交互。DMA的传输大小配置、循环次数都要基于“字”来考虑。(MN)*4这个公式是连接“数据块数”和“DMA传输次数”的桥梁。通过深入理解AES-GCM/CCM的原理、硬件模块的数据流、以及DMA的协同机制我们就能在嵌入式平台上构建出既安全又高效的加密通信和数据保护方案。希望这篇结合了原理、手册和实战经验的解析能帮助你在下一个嵌入式安全项目中更加游刃有余地驾驭硬件加密加速器。