1. 项目概述与核心价值
在物联网和嵌入式设备安全需求日益增长的今天,数据在传输和存储过程中的机密性与完整性保护变得至关重要。AES(高级加密标准)作为全球公认的对称加密算法基石,其硬件加速实现是平衡嵌入式系统性能与安全性的关键。德州仪器(TI)的MSPM0 G系列微控制器集成了先进的AES硬件加速器(AESADV模块),它不仅仅是一个简单的加密/解密引擎,更是一个支持多种工作模式,特别是GCM和CCM这两种认证加密模式的完整安全协处理器。
对于嵌入式开发者而言,直接调用软件库进行AES运算虽然简单,但在处理大量数据或对实时性有要求的场景下,CPU负载和能耗会成为瓶颈。MSPM0的硬件加速器能够独立处理加密链式操作,将CPU解放出来处理其他任务,同时通过DMA实现数据流的自动搬运,实现了接近“零开销”的加密操作。本文将从原理到实践,深入解析如何利用MSPM0的硬件加速器,高效、正确地配置并运行AES-GCM和AES-CCM模式。你将不仅了解寄存器如何配置,更能掌握其背后的设计逻辑、数据流管理以及在实际项目中必须注意的“坑点”,例如AAD数据对齐、中断与DMA的协同、以及长数据流的中断与恢复机制。
2. AES-GCM/CCM模式原理与MSPM0实现机制
2.1 GCM模式深度解析:为何它是物联网通信的首选
GCM(Galois/Counter Mode)是一种认证加密模式,它同时提供数据的机密性(加密)、完整性(认证)和可关联性(使用初始化向量IV)。其核心优势在于并行化和高性能。
2.1.1 GCM的核心组件与工作流程GCM可以看作是两个独立操作的结合:CTR模式加密和GMAC认证。
- CTR加密:使用一个递增的计数器(Counter)通过AES加密生成密钥流(Keystream),再将此密钥流与明文进行异或(XOR)得到密文。由于计数器可预测,加密过程可以并行化,非常适合硬件流水线处理。
- GMAC认证:基于伽罗瓦域(Galois Field, GF(2^128))的乘法。它接收附加认证数据(AAD)和密文(或明文,在仅认证时),通过一个称为
H的密钥(由加密密钥通过AES加密一个全零块得到)进行一系列复杂的域运算,最终生成一个认证标签(TAG)。
在MSPM0硬件中,这两个过程被高度集成。引擎内部维护着H(哈希子密钥)和Y0(经过加密的初始计数器)等上下文。当配置为GCM模式时,硬件会自动按顺序处理AAD和加密数据,并最终输出密文和TAG。开发者需要理解的关键是,硬件期望的输入数据流是:先提供所有AAD数据块,再提供所有明文/密文数据块。即使AAD长度为0,这个顺序也不能乱。
2.1.2 MSPM0对GCM的硬件优化MSPM0的AESADV模块为GCM提供了三种子模式(通过CTRL[GCM]位域选择),这直接对应了不同的性能与灵活性权衡:
01b(GHASH with H loaded and Y0-encrypted forced to zero):此模式下,你需要预先在软件中计算好H(即E(K, 0))并写入GHASH_Hx寄存器,同时将Y0(即E(K, IV || 0^31 1))强制设为零。这通常用于纯认证(GMAC)场景,因为此时没有加密过程,Y0无需计算。硬件直接使用你提供的H进行GHASH运算,效率最高。10b(GHASH with H loaded and Y0-encrypted calculated internally):这是最常用的GCM加密/解密模式。你仍需提供预先计算好的H,但硬件会内部计算Y0。这省去了软件计算Y0的步骤,但要求你提前计算H。适用于对同一密钥进行多次加密的场景,可以复用H,提升效率。11b(Autonomous GHASH):全自动模式。硬件内部自行计算H和Y0。使用最简便,但每次操作开始时会有额外的计算开销。适合密钥频繁变换或单次操作的场景。
选择哪种模式,取决于你的应用场景:是频繁使用同一密钥(选模式2),还是每次密钥都不同(选模式3),或仅需认证(选模式1)。
2.2 CCM模式深度解析:兼顾安全与确定性的经典模式
CCM(Counter with CBC-MAC)是另一种广泛使用的认证加密模式,尤其在一些传统的无线通信协议(如IEEE 802.15.4, Zigbee)中常见。与GCM的并行设计不同,CCM是串行工作的。
2.2.1 CCM的核心组件与工作流程CCM同样结合了加密和认证,但顺序是固定的:先认证,后加密(加密时也交织着认证)。
- CBC-MAC认证:首先,它将AAD和明文数据(对于加密过程)通过密码分组链接(CBC)模式进行处理,生成一个消息认证码(MAC)。这个过程是串行的,每一个数据块的认证都依赖于前一个块的结果。
- CTR加密:然后,它使用CTR模式对明文(和上一步得到的MAC)进行加密。值得注意的是,在CCM中,认证阶段处理的也是明文。在解密时,先进行CTR解密得到明文,再用同样的CBC-MAC过程验证明文的完整性。
MSPM0硬件严格遵循这一流程。在CCM模式下,硬件首先进入“仅哈希(Hash-Only)”阶段处理AAD数据,然后进入“加密/解密”阶段处理载荷数据。在加密阶段,每处理一个明文块,会同步更新CBC-MAC链。这就要求输入数据必须严格按照AAD在前,加密数据在后的顺序提供,且中间不能穿插其他操作。
2.2.2 CCM参数:L和M的配置要点CCM模式有两个关键参数,通过CTRL寄存器的CCML和CCMM位域配置:
CCML(L值):定义了用于表示消息长度的字段大小(字节数),计算公式为L = CCML + 1。L决定了计数器(Counter)的宽度。例如,CCML=2表示L=3字节,计数器部分占3字节。这限制了单次CCM操作能处理的最大消息长度:最大消息字节数 =2^(8L) - 1。必须根据你实际要加密的数据最大长度来选择合适的L值,确保计数器不会溢出。CCMM(M值):定义了生成的认证标签(TAG)的长度(字节数),计算公式为TAG长度 = 2 * (CCMM + 1)。例如,CCMM=3表示TAG长度为8字节。更长的TAG提供更高的安全性,但也会增加通信开销。常见的TAG长度是8字节(64位)或16字节(128位)。硬件始终计算一个完整的128位TAG,但只将最低的2*(CCMM+1)个字节视为有效,你需要从TAGx寄存器中提取相应字节。
2.2.3 CCM与GCM的关键差异与选型建议
- 性能:GCM支持并行计算,通常比串行的CCM更快,尤其在硬件加速环境下优势明显。
- 数据依赖:CCM要求先知道消息总长度才能开始处理(因为长度信息被编码在初始块B0中),而GCM无此要求,可以处理流式数据。
- 资源占用:GCM需要伽罗瓦域乘法器,硬件实现可能略复杂,但MSPM0已集成。CCM仅使用标准的AES-CBC和AES-CTR,结构相对简单。
- 标准化与兼容性:两者都是NIST标准。选择时需优先考虑行业协议要求(如TLS 1.2/1.3普遍使用GCM,某些物联网协议指定CCM)。
对于全新的嵌入式设计,如果没有遗留协议兼容性要求,GCM通常是更推荐的选择,因为它性能更高,且对数据格式更灵活。
2.3 MSPM0 AES加速器架构与数据流管理
理解硬件架构是正确配置的前提。MSPM0的AESADV模块不是一个简单的“黑盒”,而是一个有状态、可编程的数据处理引擎。
2.3.1 核心寄存器组与上下文概念模块的寄存器可分为几大类:
- 密钥与初始化向量(Key/IV)寄存器(
KEY0-KEY7,IV0-IV3):用于加载加密密钥和初始向量/计数器。对于GCM/CCM,IV的构造有特定格式,需在软件中组装好后写入。 - 控制与状态寄存器(
CTRL):这是大脑。它选择工作模式(GCM/CCM/CBC等)、方向(加密/解密)、密钥长度,并反映引擎就绪状态(INPUT_RDY,OUTPUT_RDY,CNTXT_RDY)。 - 长度寄存器(
C_LENGTH_0/1,AAD_LENGTH):分别指定加密数据和附加认证数据(AAD)的字节长度。硬件根据这些值自动管理数据流的结束。特别注意:写入AAD_LENGTH或C_LENGTH寄存器会触发引擎开始使用当前上下文(Context)处理数据。 - 数据输入/输出寄存器(
DATA0-DATA3,DATA_IN,DATA_OUT):数据交换的窗口。DATAx是四个32位寄存器,共同组成128位数据块。DATA_IN和DATA_OUT是同一物理地址的别名,方便DMA配置——DMA只需指向一个固定地址进行读写。 - 哈希与标签寄存器(
GHASH_Hx,GCMCCM_TAGx,TAGx):用于GCM的H密钥、中间状态以及最终认证标签的存储。TAGx寄存器在操作完成后保存最终的128位TAG,你需要根据CCMM设置截取有效部分。 - 辅助寄存器(
BLK_CNT0/1,CCM_ALN_WRD):用于支持长数据流操作的中断与恢复,是高级用法中的关键。
2.3.2 数据对齐与填充的硬性要求这是手册中强调但极易出错的一点。AES引擎以128位(16字节)块为单位处理数据。对于GCM和CCM模式:
- AAD和加密数据都可以不是16字节的整数倍。
- 如果数据不是块对齐的,必须由CPU在内存中进行填充,填充内容为零(
0x00)。 - 填充的长度
n必须满足0 <= n <= 127比特,且因为引擎按字节操作,n必须是8的倍数(即整数个字节)。 - 核心约束:加密数据的起始必须在内存中128位对齐。这意味着,如果你有一段M字节的AAD和N字节的明文,你需要将它们连续存放在内存中,并在AAD末尾添加零填充,使得明文的起始地址是16字节的整数倍。
- DMA模式下的连续性要求:当使用单个DMA通道为引擎提供数据时(这是典型的高效做法),AAD和明文必须在内存中连续存放,即
AAD数据 + 填充 + 明文数据作为一个连续的字节流。如果使用CPU通过中断服务程序(ISR)逐个写入数据,则无此连续性要求,但性能会下降。
例如,你有13字节AAD和25字节明文。AAD需要填充3字节(16 - (13 % 16) = 3)的零,使得总长度为16字节(一块)。明文25字节不是16的倍数,需要填充7字节零,使其成为32字节(两块)。在内存中,你需要准备一个13 + 3 + 25 + 7 = 48字节的缓冲区,前16字节是(AAD+填充),后32字节是(明文+填充)。AAD_LENGTH应写入13,C_LENGTH_0/1应写入25。硬件会根据你写入的长度,自动忽略你添加的填充字节。
3. 基于DMA的GCM/CCM完整配置与实操流程
理论清晰后,我们进入实战环节。使用DMA是发挥硬件加速器效能的关键,它能实现数据搬运与加密计算的全重叠。
3.1 系统初始化与DMA通道配置
在开始任何加密操作前,必须完成系统和外设的初始化。
3.1.1 时钟与电源使能首先确保AES模块的时钟和电源已开启。这通常通过系统控制模块的寄存器完成。对于MSPM0,你需要访问AES模块的PWREN寄存器(偏移量0x800),写入密钥0x26后,将ENABLE位置1。同时,确认DMA控制器的时钟也已使能。
3.1.2 DMA通道配置详解我们需要两个DMA通道:一个用于将数据从内存搬运到AES引擎(输入),另一个用于将结果从引擎搬运回内存(输出)。
输出DMA通道(保存密文/TAG)配置:
- 触发源选择:设置DMA通道的触发源为
AES Trig1(对应DMA_TRIG_DATAOUT事件)。这意味着当AES引擎产生一个输出数据就绪事件时,会自动触发此DMA传输。 - 源地址:设置为AES模块的
DATA_OUT寄存器地址(0x1188)。这是一个只读寄存器别名,读取它会自动从输出缓冲区获取128位数据。 - 目的地址:设置为SRAM中用于存储密文(或解密后的明文)的缓冲区起始地址。
- 传输大小:设置为
N * 4,其中N是加密数据块的个数(128位为一个块)。注意,这里是字数(32-bit Word),不是字节数。例如,25字节明文填充为32字节(2个块),那么N=2,传输大小应配置为2 * 4 = 8个字的传输。 - 传输模式:设置为单次传输模式(Burst或Single Transfer,取决于DMA控制器支持)。每次触发搬运一个32位字。
- 事件使能:在AES模块的
DMA_TRIG_DATAOUT事件组中,找到IMASK寄存器,将其中的TRIG1中断掩码位置1,以允许该事件发布给DMA。
- 触发源选择:设置DMA通道的触发源为
输入DMA通道(加载AAD和明文)配置:
- 触发源选择:设置为
AES Trig0(对应DMA_TRIG_DATAIN事件)。当AES引擎输入缓冲区为空,准备好接收新数据时触发。 - 源地址:设置为SRAM中连续存放的AAD+明文数据缓冲区的起始地址。这个缓冲区必须按照之前所述的对齐规则准备好。
- 目的地址:设置为AES模块的
DATA_IN寄存器地址(0x1184)。这是一个只写寄存器别名,写入数据会自动送入输入缓冲区。 - 传输大小:设置为
(M + N) * 4,其中M是AAD的块数,N是加密数据的块数。同样,单位是字(Word)。 - 传输模式:单次传输模式。
- 事件使能:在AES模块的
DMA_TRIG_DATAIN事件组的IMASK寄存器中,将TRIG0位置1。
- 触发源选择:设置为
3.1.3 配置DMA握手模式为了让DMA和AES引擎无缝协作,需要启用DMA握手模式。将AES模块的DMA_HS寄存器(偏移量0x11F4)的DMA_DATA_ACK位设置为1。此设置后,数据的就绪/应答信号将通过DMA事件线传递,而不是依赖CPU轮询INPUT_RDY/OUTPUT_RDY状态位。这是实现全自动流水线的关键一步。
3.2 GCM模式加密操作分步指南
假设我们要使用GCM模式(内部计算H和Y0,即CTRL[GCM]=2)加密一段数据。
3.2.1 步骤一:准备密钥、IV与数据
- 将128位或256位的AES密钥写入
KEY0至KEY7寄存器(根据KEY_SIZE选择)。 - 构造GCM IV。GCM通常期望一个12字节的IV(96位),但硬件支持更长的IV。将IV写入
IV0至IV3寄存器。注意:如果IV不是128位,需要参考NIST SP 800-38D规范在软件中将其处理成128位的J0(对于GCM),但MSPM0硬件可能期望直接将IV写入。务必查阅具体器件参考手册的示例。 - 在内存中准备数据缓冲区:
AAD数据+填充零(如需)+明文数据+填充零(如需)。计算AAD实际长度(字节)和明文实际长度(字节)。
3.2.2 步骤二:配置AES控制寄存器(CTRL)这是最核心的配置。我们通过写入CTRL寄存器(0x1150)来设置操作模式。
// 假设使用128位密钥,GCM模式(内部计算H和Y0),加密方向 uint32_t ctrl_value = 0; ctrl_value |= (0x01 << 4); // KEY_SIZE = 1 (128-bit key), 位[4:3] ctrl_value |= (0x01 << 2); // DIR = 1 (加密) ctrl_value |= (0x01 << 6); // CTR = 1 (必须为GCM加密启用CTR模式) ctrl_value |= (0x02 << 16); // GCM[1:0] = 2 (10b, 内部计算Y0) ctrl_value |= (0x01 << 29); // SAVE_CNTXT = 1 (操作完成后保存TAG) // 其他位保持默认0,如CBC, CFB, ICM, OFB等均为0 AES->CTRL = ctrl_value; // 假设AES是映射到该模块的基地址指针关键点:SAVE_CNTXT位必须置1,这样在操作结束后,认证标签(TAG)才会被保存到TAGx寄存器中,并且引擎会等待你读取TAG后才接受新的上下文,防止上下文被意外覆盖。
3.2.3 步骤三:写入数据长度寄存器写入长度寄存器会触发引擎开始处理当前已加载的上下文(密钥、IV、模式)。
// 写入加密数据长度(字节数) AES->C_LENGTH_0 = plaintext_actual_length & 0xFFFFFFFF; // 低32位 AES->C_LENGTH_1 = (plaintext_actual_length >> 32) & 0x1FFFFFFF; // 高29位,寄存器只有29位有效 // 写入AAD数据长度(字节数) AES->AAD_LENGTH = aad_actual_length;特别注意:C_LENGTH_1寄存器只有低29位有效(位[28:0]),与C_LENGTH_0的32位共同组成一个61位的长度值,足以应对超大数据。写入AAD_LENGTH后,引擎立即开始期待AAD数据输入。
3.2.4 步骤四:启动DMA并等待完成使能配置好的输入和输出DMA通道。一旦DMA使能,且AES引擎因写入长度寄存器而被激活,整个过程将自动进行:
- AES引擎发出
Trig0事件,输入DMA将第一个数据块(AAD的第一个字)搬运到DATA_IN。 - 引擎处理该数据块,并准备接收下一个数据块,再次触发
Trig0。 - 当所有AAD数据(包括填充)处理完毕,引擎自动切换到期待加密数据,流程继续。
- 加密完成一个数据块后,引擎发出
Trig1事件,输出DMA将结果从DATA_OUT搬走。 - 当
C_LENGTH计数归零,所有数据处理完毕,引擎会置位SAVED_CNTXT_RDY状态位(如果SAVE_CNTXT=1)。
你可以通过查询CTRL寄存器的SAVED_CNTXT_RDY位,或配置CPU中断来感知操作完成。
3.2.5 步骤五:获取结果操作完成后:
- 密文已通过DMA存放在你指定的输出缓冲区中。
- 从
TAG0-TAG3寄存器中读取128位的认证标签。GCM通常使用完整的128位或截断的96/64位标签,根据你的协议要求截取相应字节。
3.3 CCM模式加密操作分步指南
CCM模式的配置流程与GCM类似,但控制寄存器的设置和上下文准备有所不同。
3.3.1 步骤一:准备密钥、IV与数据
- 写入AES密钥。
- 构造CCM IV。CCM的IV(或称Nonce)需要与长度信息
L和标签长度信息M一起,按照RFC 3610规范组装成一个128位的B0块和A0块。在MSPM0中,你需要将组装好的、包含Flag、Nonce和长度的完整IV,写入IV0-IV3寄存器。这通常需要在软件中完成。 - 数据缓冲区的准备与GCM相同:连续存放AAD(含填充)和明文(含填充)。
3.3.2 步骤二:配置AES控制寄存器(CTRL)
uint32_t ctrl_value = 0; ctrl_value |= (0x01 << 4); // KEY_SIZE = 1 (128-bit) ctrl_value |= (0x01 << 2); // DIR = 1 (加密) ctrl_value |= (0x01 << 6); // CTR = 1 (必须为CCM启用CTR模式) ctrl_value |= (0x01 << 18); // CCM = 1 (启用CCM模式) ctrl_value |= (0x01 << 29); // SAVE_CNTXT = 1 // 设置CCM参数L和M。例如,L=3字节(CCML=2),M=8字节(CCMM=3) ctrl_value |= (0x02 << 19); // CCML = 2 ctrl_value |= (0x03 << 22); // CCMM = 3 // 设置CTR_WIDTH,必须足够宽以容纳L值指定的计数器字段。L=3字节,计数器占3字节,CTR_WIDTH需>=3。 // CTR_WIDTH=3表示128位计数器,足够。 ctrl_value |= (0x03 << 7); // CTR_WIDTH[1:0] = 3 (位于位[8:7]) AES->CTRL = ctrl_value;关键点:CCM位和CTR位必须同时置1。CTR_WIDTH必须大于等于CCML+1,否则计数器可能溢出导致错误。
3.3.3 步骤三:写入长度寄存器并启动与GCM完全相同,写入AAD_LENGTH和C_LENGTH寄存器以启动流程。DMA的配置和启动过程也完全一致。
3.3.4 步骤四:获取结果操作完成后,密文在输出缓冲区。从TAG0-TAG3寄存器中读取128位标签,但根据CCMM的设置,只有一部分字节有效。例如CCMM=3时,只有低8字节(TAG0和TAG1)是有效的认证标签。
4. 高级主题:长数据流、中断与上下文保存
在实际应用中,你可能需要处理超过一次DMA传输所能容纳的数据,或者需要在加密过程中被高优先级任务打断。MSPM0 AES模块提供了强大的上下文保存与恢复功能。
4.1 处理超长数据流与操作中断
C_LENGTH寄存器支持最大2^61字节的长度,足以应对绝大多数场景。对于超长数据流,最佳实践依然是配置好DMA并让其自动处理。但是,如果系统需要在中途暂停加密操作(例如响应实时事件),MSPM0提供了机制。
4.1.1 主动中断操作与获取中间摘要在GCM或CCM操作过程中(无论是在AAD阶段还是加密阶段),你可以通过设置CTRL寄存器的GET_DIGEST位来请求一个“中间摘要”。
- 在引擎处理完一个完整的128位数据块后,它会暂停。
- 状态位
SAVED_CNTXT_RDY会被置位。 - 此时,你可以安全地读取
GCMCCM_TAG0-3寄存器来获取当前的中间认证标签(TAG),以及BLK_CNT0/1寄存器来获取剩余的数据块计数。 - 将这些上下文信息(密钥、IV、当前TAG、剩余块计数)保存到内存中。
- 之后,当需要恢复操作时,重新写入密钥、IV和保存的中间TAG到
GCMCCM_TAGx寄存器,将剩余块计数写入BLK_CNT0/1。 - 最后,在写入
CTRL寄存器重新启动操作时,同时设置GCM_CONT位(对于GCM/CCM的加密阶段)或OFB_GCM_CCM_CONT位(对于GCM/CCM的AAD阶段)。这会告诉引擎这不是一个新的操作,而是之前中断操作的继续。
4.1.2 CCM对齐字的保存对于CCM模式,如果在AAD阶段中断,还有一个额外的步骤。AAD数据可能不是块对齐的,引擎内部会缓存未满一个块的部分数据。这个部分数据保存在CCM_ALN_WRD寄存器中。在中断时,你必须也读取并保存这个寄存器的值。在恢复操作时,在写入其他上下文后,需要先将保存的值写回CCM_ALN_WRD寄存器,然后再继续。
4.2 事件与中断管理
MSPM0 AES模块提供了丰富的事件系统,与CPU和DMA交互。
4.2.1 CPU中断CPU中断事件(CPU_INT)主要用于非DMA模式,或者用于通知操作完成。相关寄存器在CPU_EVENT组(偏移0x1020开始):
IIDX:指示当前最高优先级的中断索引。IMASK:中断掩码,使能OUTPUTRDY、INPUTRDY、SAVEDCNTXTRDY、CNTXTRDY等中断。RIS/MIS:原始/已屏蔽中断状态。ISET/ICLR:软件置位/清除中断。
在DMA握手模式(DMA_DATA_ACK=1)下,INPUTRDY和OUTPUTRDY中断不应被使用,因为数据流由DMA事件管理。SAVEDCNTXTRDY在需要获取TAG或处理中断恢复时很有用。
4.2.2 DMA触发事件这是高效操作的核心。DMA_TRIG_DATAIN和DMA_TRIG_DATAOUT事件组分别管理数据输入和输出的触发信号。其配置与CPU中断组类似,但更简单,通常只需要使能TRIG0或TRIG1的掩码。当引擎需要数据或数据就绪时,会发布相应事件,触发配置好的DMA通道进行传输。
4.2.3 事件模式配置EVT_MODE寄存器(0x10E0)决定了事件线的工作模式。对于DMA触发,通常配置为硬件模式(EVTx_CFG = 2),这样当DMA完成传输后,硬件会自动清除事件标志,无需软件干预。对于CPU中断,可以配置为软件模式(=1),需要在ISR中手动清除标志。
5. 实战陷阱与调试技巧
即使理解了所有寄存器,实际调试中仍会遇到问题。以下是一些常见的“坑”和解决思路。
5.1 数据对齐与填充错误
- 症状:加密/解密结果错误,或DMA/AES引擎挂起。
- 排查:
- 确认
AAD_LENGTH和C_LENGTH写入的是实际数据字节数,不是填充后的长度。 - 使用调试器检查内存中AAD+明文缓冲区的内容。确认在AAD末尾添加的零填充是否正确,使得明文起始地址是
0x10(16)的整数倍。 - 确认填充字节数
n是8的倍数(即整数字节)。
- 确认
5.2 TAG验证失败
- 症状:加解密过程正常,但对方验证认证标签失败。
- 排查:
- IV不一致:确保加解密双方使用的IV完全相同。GCM对IV的复用是灾难性的。
- AAD不一致:确保加解密双方提供的AAD数据完全一致,包括长度和内容。
- TAG截取错误:对于CCM,确认你从128位的
TAGx寄存器中截取了正确数量的字节(2*(CCMM+1)字节)。对于GCM,确认双方约定的TAG长度一致。 - 数据长度错误:确认
C_LENGTH和AAD_LENGTH设置正确,没有把填充长度也算进去。 - 字节序问题:检查密钥、IV、输入数据、输出数据的字节序(Endianness)是否与协议规范或对方实现一致。MSPM0寄存器是32位小端访问,但你的数据在内存中的存储格式需要留意。
5.3 DMA传输不启动或数据错误
- 症状:程序卡住,没有加密结果。
- 排查:
- 确认
DMA_HS.DMA_DATA_ACK已设置为1。 - 确认AES事件系统中对应DMA触发器的
IMASK已使能(DMA_TRIG_DATAIN.IMASK.TRIG0=1,DMA_TRIG_DATAOUT.IMASK.TRIG1=1)。 - 检查DMA通道的源/目标地址、传输大小配置是否正确。特别是传输大小,是字数(Word Count),不是字节数。
- 在调试器中,观察AES的
CTRL寄存器中的INPUT_RDY和OUTPUT_RDY位。在DMA握手模式下,它们可能始终为0,这是正常的。可以尝试先禁用DMA握手,用CPU轮询方式写入/读取一个数据块,测试基本功能是否正常。
- 确认
5.4 上下文覆盖导致错误
- 症状:连续进行多次操作时,后面的操作结果混乱。
- 排查:
- 确保在前一个操作完全完成(
SAVEDCNTXTRDY置位且TAG已被读取)之前,不要写入新的密钥、IV或长度寄存器。CTRL.CNTXT_RDY位为1时,才表示可以写入新上下文。 - 如果使用了
SAVE_CNTXT=1,必须在读取TAG后,引擎才会释放上下文并准备接收新的。可以通过检查SAVED_CNTXT_RDY位下降或CNTXT_RDY位上升来判断。
- 确保在前一个操作完全完成(
5.5 性能优化建议
- 密钥预计算:对于GCM模式,如果使用同一密钥进行多次操作,务必在软件中预计算
H(E(K,0)),并使用CTRL[GCM]=2模式,避免硬件重复计算。 - DMA双缓冲:对于持续的数据流,可以配置DMA为Ping-Pong模式(双缓冲),在一个缓冲区被AES处理时,DMA填充另一个缓冲区,实现无缝流水。
- 避免频繁启停:对于一系列小数据包,如果使用相同密钥和相似的IV(如计数器递增),考虑使用“继续”模式(
GCM_CONT),而不是为每个包完全重新初始化上下文,可以节省密钥和IV重新加载的时间。 - 监控引擎状态:在复杂应用中,可以通过轮询
CTRL寄存器中的CNTXT_RDY和SAVED_CNTXT_RDY位来可靠地管理引擎状态机,避免竞争条件。
通过深入理解上述原理、配置步骤和调试技巧,你应该能够 confidently 在MSPM0平台上构建高效、可靠的基于AES-GCM/CCM的嵌入式安全应用。硬件加速器的正确使用,能将加密解密这一计算密集型任务对系统主CPU的影响降至最低,为产品在安全性和性能之间取得最佳平衡。