【AES加密专题】6.功能函数的编写(3)
目录
AES加密专题:
10.轮密钥加(AddRoundKey)
一、核心定义与执行流程
二、数学表示
三、核心作用
11.字节替换
12.位移操作
13.有限域乘2
规则
示例:
14.列混合
15.单块数据加密
16.单块数据解密
AES加密专题:
【AES加密专题】1.AES的原理详解和加密过程-CSDN博客
【AES加密专题】2.AES头文件详解-CSDN博客
【AES加密专题】3.工具函数的编写(1)-CSDN博客
【AES加密专题】4.Sbox的解析和生成-CSDN博客
【AES加密专题】5.功能函数的编写(2)-CSDN博客
10.轮密钥加(AddRoundKey)
一、核心定义与执行流程
轮密钥加(AddRoundKey)是 AES 加密算法中唯一直接将数据与密钥结合的核心步骤,通过逐字节异或(XOR) 操作,将 “中间数据载体” 与 “轮密钥” 强制融合,以此增强数据的混淆性和加密安全性。
其核心前提是:轮密钥并非原始密钥,而是由原始密钥通过 “AES 密钥扩展算法” 生成的子密钥(不同轮次对应不同轮密钥),且轮密钥长度与 AES 数据块长度一致(固定 16 字节,对应 128 位数据块,存储为 4×4 字节矩阵)。
轮密钥加贯穿 AES 加密全流程,分三个关键阶段执行:
1.初始轮密钥加(加密第一步)原始明文数据块(首次被称为 “状态”,即 4×4 字节矩阵)与 “第一个轮密钥” 执行逐字节异或,这是明文与密钥的第一次直接结合,标志数据从 “明文态” 进入 “加密中间态”。
2.每轮轮密钥加(每轮加密最后一步)AES 加密包含多轮变换(128 位密钥对应 10 轮、192 位对应 12 轮、256 位对应 14 轮),每一轮变换的最后一步,都会将当前的 “中间状态” 与 “该轮专属轮密钥” 逐字节异或。此步骤确保每一轮的中间结果都融入新的密钥信息,避免数据与密钥的关联被轻易破解。
3.最终轮密钥加(生成密文)最后一轮加密变换结束后,执行最终一次轮密钥加,此时的异或结果即为最终的 AES 密文,完成 “数据 - 密钥” 的最终融合。
二、数学表示
轮密钥加的计算逻辑简洁且高效,核心是 “状态” 与 “轮密钥” 对应位置字节的逐位异或(XOR) 操作,数学表达式如下:状态[i][j] = 状态[i][j] ⊕ 轮密钥[i][j]
-
符号说明:
⊕
代表逐位异或操作(二进制位规则:0⊕0=0、0⊕1=1、1⊕0=1、1⊕1=0); -
维度说明:
状态
和轮密钥
均为 4×4 字节矩阵(i
表示行索引 0-3,j
表示列索引 0-3),操作时严格对应矩阵的同一位置字节; -
特点:异或操作具有 “可逆性”—— 若对结果再次与同一轮密钥异或,可还原原始数据(这是 AES 解密时能复用轮密钥加逻辑的核心原因)。
三、核心作用
轮密钥加是 AES 安全性的关键支撑,其核心价值体现在两点:
1.唯一的 “数据 - 密钥直接结合点”AES 的其他步骤(如字节替换 SubBytes、行移位 ShiftRows、列混淆 MixColumns)均是对 “状态” 的独立数据变换,仅轮密钥加能直接将密钥信息注入数据,确保加密结果与原始密钥强关联 —— 无密钥则无法还原数据。
2.扩散密钥信息,增强混淆性通过多轮次的异或操作,原始密钥的每一位信息会逐步扩散到 “状态” 的每一个字节中,打破 “局部数据对应局部密钥” 的简单关联。最终生成的密文中,每一位都融合了密钥和明文的双重信息,大幅提升攻击者的破解难度(需同时突破数据变换和密钥扩散的双重逻辑)。
/*** @brief 执行AES加密中的“轮密钥加”操作* * 功能说明:将中间状态数据与当前轮的子密钥进行逐字节异或(XOR)操作,* 异或结果直接覆盖存储到状态数据中,实现“状态-子密钥”的融合。* 此操作是AES中唯一直接结合数据与密钥的步骤,固定处理16字节数据(4×4字节矩阵)。* * @param[in,out] pState 指向状态数据的指针(4×4字节矩阵):* - 输入:当前轮的中间状态数据;* - 输出:异或后更新的状态数据(覆盖原始值)。* @param[in] pRoundKey 指向当前轮子密钥的指针(4×4字节矩阵):* 子密钥由原始密钥经扩展生成,仅作为异或输入,内容不被修改。* * @note 1. 数据长度:固定为4×Nb字节(AES中Nb=4,即16字节),与AES数据块大小一致;* 2. 指针安全:函数未对pState、pRoundKey进行空指针检查,调用者需确保指针非空且指向有效内存(至少16字节);* 3. 实现依赖:内部通过调用xor_bytes函数完成逐字节异或操作。*/
static void AddRoundKey(unsigned char *pState, const unsigned char *pRoundKey)
{// 调用批量异或函数,处理4×Nb字节(16字节)数据,结果存入pStatexor_bytes(pState, pRoundKey, 4 * Nb);
}/*** @brief 轮密钥加操作的宏定义形式* * 说明:通过宏展开直接调用xor_bytes函数,实现与AddRoundKey函数完全相同的功能。* 相比函数形式,宏定义可节省4字节的data存储空间(适合资源受限的嵌入式环境,如8051单片机)。* * @param pState 同AddRoundKey函数的pState参数(状态数据指针)* @param pRoundKey 同AddRoundKey函数的pRoundKey参数(子密钥指针)*///AddRoundKey的宏形式,比函数形式可以节省4字节的data数据
#define AddRoundKey(pState, pRoundKey) \xor_bytes((pState), (pRoundKey), 4 * Nb)
宏形式相当于的内联函数,在调用这个函数的地方直接展开,避免了函数调用时形参的压栈开销;
11.字节替换
这个sub_bytes 函数在AES加密中用于字节替换步骤,其主要作用是用S-盒(S-Box)对状态数据的每个字节进行替换。
示例:
// 示例状态数据
unsigned char pState[16] = {0x32, 0x88, 0x31, 0xE0,0x43, 0x5A, 0x31, 0x37,0xF6, 0x30, 0x98, 0x07,0xA8, 0x8D, 0xA2, 0x34 };
// 加密操作
sub_bytes(pState,16,0);//使用S-盒进行替换
// 解密操作
sub_bytes(pState,16,1);//使用反向 S-盒进行替换// 假设 S-盒替换后 pState 为:
unsigned char pState[16] = {0x6D, 0xD2, 0xB4, 0x04,0x56, 0x92, 0xB4, 0x67,0xF9, 0xA1, 0x1A, 0x4C,0xC1, 0xA0, 0xE5, 0x4F };
// 解密操作后 pState 恢复为原始状态数据:
unsigned char pState[16] = {0x32, 0x88, 0x31, 0xE0,0x43, 0x5A, 0x31, 0x37,0xF6, 0x30, 0x98, 0x07,0xA8, 0x8D, 0xA2, 0x34 };
/*** @brief 对状态数据执行字节替换(AES中的SubBytes操作)* * 功能说明:遍历状态数据中的每个字节,根据选择的替换表(S-盒或逆S-盒)进行替换,* 实现数据的非线性混淆。加密时使用S-盒,解密时使用逆S-盒,确保变换可逆。* * @param[in,out] pState 指向状态数据的指针(字节数组):* - 输入:原始状态数据(加密时为明文/中间态,解密时为密文/中间态);* - 输出:替换后的数据(加密时为S-盒替换结果,解密时为逆S-盒还原结果)。* @param[in] nCount 状态数据的字节长度(AES中固定为16字节,对应4×4矩阵)。* @param[in] bInvert 替换模式选择:* - FALSE(0):使用S-盒(加密时的正向替换);* - TRUE(1):使用逆S-盒(解密时的反向替换)。* * @note * 1. 存储类型:S-盒(sBox)和逆S-盒(invsBox)通过`code`关键字指定存储在程序存储器(ROM),* 适合资源受限的嵌入式环境(如8051单片机),节省RAM空间。* 2. 指针安全:函数未对pState进行空指针检查,调用者必须确保pState指向有效内存,且长度不小于nCount。* 3. 核心逻辑:通过字节值作为索引查表,直接替换(如pState[i] = sBox[pState[i]]),效率极高。*/
static void sub_bytes(unsigned char *pState, unsigned char nCount, BOOL bInvert)
{unsigned char i; // 循环索引,用于遍历每个字节// 根据bInvert选择替换表:加密用S-盒,解密用逆S-盒const unsigned char code *pSBox = bInvert ? invsBox : sBox;// 遍历所有字节,执行替换操作for (i = 0; i < nCount; i++){// 用替换表中对应的值替换当前字节(当前字节值作为查表索引)pState[i] = pSBox[pState[i]];}
}
12.位移操作
在AES加密算法中执行行移位操作。
示例:
假设状态数据位
[ 0x01, 0x02, 0x03, 0x04,0x05, 0x06, 0x07, 0x08,0x09, 0x0A, 0x0B, 0x0C,0x0D, 0x0E, 0x0F, 0x10 ]
-
加密时,shift_rows将第2行(0x05,0x06,0x07,0x08)向左移1位,结果为(0x06,0x07,0x08,0x05)。
-
第3行(0x09,0x0A,0x0B,0x0C)向左移2位,结果为(0x0B,0x0C,0x09,0x0A)。
-
第4行(0xOD,OxOE,0xOF,0x10)向左移3位,结果为(0x10,0xOD,OxOE,0xOF)。
/*** @brief 对AES状态矩阵执行行移位操作(ShiftRows)* * 功能说明:严格遵循AES算法规定,对4×4状态矩阵的第1-3行(第0行固定不移位)执行循环移位,* 加密时正向移位,解密时反向移位,实现数据的“扩散”效果(打破列内数据的局部关联性)。* * 关键注意:AES状态矩阵以「列为主序存储」(非行序),即状态数组pState的索引规则为:* pState[r + 4*c] = 状态矩阵第r行(0≤r<4)第c列(0≤c<4)的字节* (例:第1行第2列 → r=1, c=2 → 索引=1+4*2=9)* * @param[in,out] pState 指向AES状态数据的指针(16字节数组,对应4×4矩阵):* - 输入:原始状态矩阵(加密时为SubBytes后的中间态,解密时为InvMixColumns后的中间态);* - 输出:行移位后的新状态矩阵。* @param[in] bInvert 移位方向选择:* - FALSE(0):正向移位(加密时使用,第1行移1位、第2行移2位、第3行移3位);* - TRUE(1):反向移位(解密时使用,第1行移3位、第2行移2位、第3行移1位)。* * @note 1. 行移位范围:仅处理第1-3行(r=1,2,3),第0行(r=0)因移位步数为0,固定不变;* 2. 指针安全:函数未对pState进行空指针检查,调用者必须确保pState指向有效内存(至少16字节);* 3. 循环移位:通过“模4运算”实现字节的循环移动,避免数据丢失。*/
static void shift_rows(unsigned char *pState, BOOL bInvert)
{unsigned char r; // 行索引(0-3,仅处理r=1,2,3)unsigned char c; // 列索引(0-3)unsigned char temp; // 临时变量,存储移位步数unsigned char rowData[4]; // 临时数组,备份当前行的4个字节(避免移位时覆盖原始数据)// 循环处理第1-3行(r从1开始,r=0不移位,直接跳过)for (r = 1; r < 4; r++){// 第一步:备份当前行的4个字节(按列序从状态数组中提取当前行数据)for (c = 0; c < 4; c++){// 按列序索引规则:当前行r、列c的字节 → pState[r + 4*c]rowData[c] = pState[r + 4 * c];}// 第二步:计算移位步数(根据加密/解密选择正向/反向步数)// 加密(bInvert=0):步数=r(第1行移1位,第2行移2位,第3行移3位)// 解密(bInvert=1):步数=4-r(第1行移3位,第2行移2位,第3行移1位)temp = bInvert ? (4 - r) : r;// 第三步:执行循环移位,更新状态数组for (c = 0; c < 4; c++){// (c + temp) % 4:实现循环移位(正向时取后续字节,反向时取前序字节)// 例:r=1、加密(temp=1)、c=0 → (0+1)%4=1 → 取rowData[1]填入当前位置pState[r + 4 * c] = rowData[(c + temp) % 4];}}
}
13.有限域乘2
在GF(2^8)(有限域)内,乘以2的操作遵循特定的规则,以确保计算结果符合有限域的定义。
规则
i.左移操作:在GF(2^8)中,将数值乘以2通常等效于左移一位。
ii. 模多项式运算:如果最高位(第8位)为1,左移后的结果会超出8位边界。为确保结果仍在GF(2^8)范围内,需要将结果与一个固定的模多项式 BPOLY 进行异或。通常,BPOLY 值为 0x11B多项式 x^8+x^4+x^3+x+1),这是AES中GF(2)的既定模多项式。
示例:
假设我们有两个数:num1 = 0x57和num2 = 0x8D
-
对num1 = 0x57的计算:
1.十六进制转换为二进制
0x57 = 01010111(最高位是0)
2.最高位检查:
最高位是0,直接左移(乘以 2):
01010111 << 1 = 10101110
3.结果:
10101110 = 0xB5(乘以2的结果)
-
对num2 = 0x8D的计算:
1.十六进制转换为二进制
0x8D = 10001101(最高位是1)
2.最高位检查:
最高位是1,所有左移并与BPOLY异或:
10001101<< 1 = 00011010(左移后的结果)
3.与BPOLY(0x1B = 00011011)异或:
00011010 ^ 00011011 = 0000 0001
4.结果:
0000 0001= 0x01(乘以2的结果)
#include <stdint.h>// 必须提前定义GF(2^8)的不可约模多项式(AES标准规定为0x11B,对应x⁸+x⁴+x³+x+1)
// 注意:不可用0x1B(缺少x⁸项),否则无法正确处理最高位为1的情况
#define BPOLY 0x11B/*** @brief 在GF(2^8)有限域内实现“乘以2”的运算* * 核心逻辑:GF(2^8)中“乘以2”等价于二进制左移1位,但需通过模运算确保结果仍为8位:* 1. 若输入数的最高位(第7位,对应二进制10000000,即0x80)为0:左移1位后无溢出,直接得到结果;* 2. 若最高位为1:左移1位后会产生第8位(溢出),需与模多项式BPOLY异或,消除溢出位,确保结果在8位范围内。* * @param[in] num 待乘2的输入值(8位无符号整数,范围0x00~0xFF)* @return 运算结果(8位无符号整数,num在GF(2^8)内乘2的结果)* * @note 1. 依赖宏BPOLY的正确定义:必须为0x11B(AES标准模多项式),否则结果错误;* 2. 位索引说明:8位整数的位编号为0(最低位,2⁰)~7(最高位,2⁷),注释中“最高位”均指第7位;* 3. 可逆性:该运算可逆,若结果再次调用本函数并结合模运算,可还原部分输入(需符合GF(2^8)逆运算规则)。*/
static unsigned char gf_mult_by02(unsigned char num)
{// 检查输入num的最高位(第7位)是否为0:0x80是二进制10000000,与num按位与后为0则最高位为0if ((num & 0x80) == 0) {// 最高位为0:左移1位(等价于乘2),无溢出,直接更新numnum = num << 1; }else {// 最高位为1:左移1位后会产生第8位(溢出),需与BPOLY异或消除溢出// 步骤:1. 左移1位(num << 1);2. 与BPOLY异或(模运算),结果自动截断为8位num = (num << 1) ^ BPOLY; }// 返回GF(2^8)内乘2的最终结果(确保为8位)return num;
}
14.列混合
该函数对 AES状态数据的每一列进行混合操作,以增加数据的扩散性。在加密过程中,每一列的字节将被线性变换。在解密时,需要执行反向混合操作。
示例
假设我们有一个状态矩阵(列主序)如下:
| 0x01 | 0x02 | 0x03 | 0x04 |
| 0x05 | 0x06 | 0x07 | 0x08 |
| 0x09 | 0x0A | 0x0B | 0x0C |
| 0x0D | 0x0E | 0x0F | 0x10 |
加密过程中的列融合
在加密时,每一列的数据会按照AES的规则进行混合。对第一列进行处理:
1.处理异或的结果
temp = 0x01 ^ 0x05 ^ 0x09 ^ 0x0D;
2.计算混合后的每个字节:
-
对于第一个字节:
result[0] = temp ^ pState[0] ^ gf_mult_by02((0x01 ^ 0x02))= 0x00 ^ 0x01 ^ gf_mult_by02(0x03)= 0x00 ^ 0x01 ^ 0x06 = 0x07
-
对于第二个字节:
result[1] = temp ^ pState[1] ^ gf_mult_by02((0x02 ^ 0x03)) = 0x00 ^ 0x02 ^ gf_mult_by02(0x01) = 0x00 ^ 0x02 ^ 0x04 = 0x06
-
对于第三个字节:
result[2] = temp ^ pState[3] ^ gf_mult_by02((0x03 ^ 0x04)) = 0x00 ^ 0x03 ^ gf_mult_by02(0x07) = 0x00 ^ 0x03 ^ 0x0E = 0x0D
-
对于第四个字节:
result[3] = temp ^ pState[3] ^ gf_mult_by02((0x04 ^ 0x01)) = 0x00 ^ 0x04 ^ gf_mult_by02(0x05) = 0x00 ^ 0x04 ^ 0x0A = 0x0E
3.更新状态数据:
pState =| 0x07 | 0x06 | 0x0D | 0x0E || 0x05 | 0x06 | 0x07 | 0x08 || 0x09 | 0x0A | 0x0B | 0x0C || 0x0D | 0x0E | 0x0F | 0x10 |
解密过程中的反向混合
如果 bInvert为真,反向混合操作会根据特定的系数进行计算。这些系数不同于加密过程中的。
例如,在反向混合时,计算反向的每个字节会用到如下的公式:
b0' = 14 x a0 + 11 x a1 + 13 x a2 + 9 x a3
#include <stdint.h>
#include <string.h> // 用于 memcpy 函数// 假设已实现 GF(2^8) 域内乘2运算(之前整理的 gf_mult_by02 函数)
extern static unsigned char gf_mult_by02(unsigned char num);/*** @brief 执行AES的列混淆(MixColumns)或逆列混淆(InvMixColumns)操作* * 功能说明:对AES 4×4状态矩阵的每一列执行有限域线性变换,实现数据“扩散”——* 加密时(bInvert=0)执行列混淆,将一列内的4个字节通过有限域乘法/加法(异或)混合;* 解密时(bInvert=1)执行逆列混淆,通过逆变换还原加密前的列数据,确保变换可逆。* * 关键前提:状态矩阵以「列为主序存储」,即 pState 中每4个连续字节对应一列(如 pState[0-3]是第0列,pState[4-7]是第1列)。* * @param[in,out] pState 指向AES状态矩阵的指针(16字节数组):* - 输入:加密时为ShiftRows后的中间态,解密时为逆ShiftRows后的中间态;* - 输出:列混淆/逆列混淆后的新状态矩阵。* @param[in] bInvert 变换模式选择:* - FALSE(0):加密模式,执行列混淆(MixColumns);* - TRUE(1):解密模式,执行逆列混淆(InvMixColumns)。* * @note 1. 依赖 gf_mult_by02 函数:实现GF(2^8)域内“乘2”运算,是列混淆中有限域乘法的基础;* 2. 内存安全:需确保 pState 指向有效内存(至少16字节),函数不做空指针检查;* 3. 列处理逻辑:循环遍历4列(i=0~3),每列处理后 pState 指针偏移4字节,指向-next列。*/
static void mix_columns(unsigned char *pState, BOOL bInvert)
{unsigned char i; // 循环索引(遍历4列,i=0~3)unsigned char temp; // 临时变量:存储当前列4个字节的异或和(简化加密计算)unsigned char a0Pa2_M4; // 临时变量:4*(a0 + a2)(a0/a2是当前列第0/2字节,+是异或,*4是两次gf_mult_by02)unsigned char a1Pa3_M4; // 临时变量:4*(a1 + a3)(a1/a3是当前列第1/3字节)unsigned char result[4]; // 临时数组:存储当前列混合后的4个字节(避免覆盖原始数据)// 遍历状态矩阵的4列(每列4字节),pState每次偏移4字节指向当前列for (i = 0; i < 4; i++, pState += 4){// -------------------------- 第一步:计算加密模式的列混淆结果(基础变换) --------------------------// 当前列的4个字节:pState[0] = a0, pState[1] = a1, pState[2] = a2, pState[3] = a3// 列混淆核心公式(加密):// b0 = 2a0 + 3a1 + 1a2 + 1a3// b1 = 1a0 + 2a1 + 3a2 + 1a3// b2 = 1a0 + 1a1 + 2a2 + 3a3// b3 = 3a0 + 1a0 + 1a1 + 2a3// 简化计算:利用有限域加法(异或)的性质,temp = a0^a1^a2^a3(所有字节异或和),减少重复运算temp = pState[0] ^ pState[1] ^ pState[2] ^ pState[3]; // 计算当前列4字节的异或和// 计算b0:temp ^ a0 ^ 2*(a0^a1) → 等价于 2a0 + 3a1 + a2 + a3result[0] = temp ^ pState[0] ^ gf_mult_by02((unsigned char)(pState[0] ^ pState[1]));// 计算b1:temp ^ a1 ^ 2*(a1^a2) → 等价于 a0 + 2a1 + 3a2 + a3result[1] = temp ^ pState[1] ^ gf_mult_by02((unsigned char)(pState[1] ^ pState[2]));// 计算b2:temp ^ a2 ^ 2*(a2^a3) → 等价于 a0 + a1 + 2a2 + 3a3result[2] = temp ^ pState[2] ^ gf_mult_by02((unsigned char)(pState[2] ^ pState[3]));// 计算b3:temp ^ a3 ^ 2*(a3^a0) → 等价于 3a0 + a1 + a2 + 2a3result[3] = temp ^ pState[3] ^ gf_mult_by02((unsigned char)(pState[3] ^ pState[0]));// -------------------------- 第二步:若为解密模式,叠加逆列混淆变换 --------------------------if (bInvert){// 逆列混淆核心公式(解密):// b0' = 14a0 + 11a1 + 13a2 + 9a3// b1' = 9a0 + 14a1 + 11a2 + 13a3// b2' = 13a0 + 9a1 + 14a2 + 11a3// b3' = 11a0 + 13a1 + 9a2 + 14a3// 简化计算:利用“乘4”(两次gf_mult_by02)和异或,减少复杂乘法(14=8+4+2,11=8+2+1等)// 计算 4*(a0^a2):gf_mult_by02两次等价于乘4(2*2=4),a0^a2是有限域加法a0Pa2_M4 = gf_mult_by02(gf_mult_by02((unsigned char)(pState[0] ^ pState[2])));// 计算 4*(a1^a3)a1Pa3_M4 = gf_mult_by02(gf_mult_by02((unsigned char)(pState[1] ^ pState[3])));// 计算 2*(a0^a2^a1^a3):temp是a0^a1^a2^a3,乘2后用于后续叠加temp = gf_mult_by02((unsigned char)(a0Pa2_M4 ^ a1Pa3_M4));// 叠加逆变换,更新result为逆列混淆结果result[0] ^= temp ^ a0Pa2_M4;result[1] ^= temp ^ a1Pa3_M4;result[2] ^= temp ^ a0Pa2_M4;result[3] ^= temp ^ a1Pa3_M4;}// -------------------------- 第三步:将混合结果写回当前列 --------------------------memcpy(pState, result, 4); // 把result的4个字节(当前列结果)复制回pState(当前列地址)}
}
15.单块数据加密
对输入的状态数据 pState 进行AES加密处理。加密过程包括多个步骤:添加轮密钥、字节替换、行移位和列混合。
/*** @brief 实现AES单块数据加密(完整流程)* * 功能说明:严格遵循AES加密标准,对16字节(4×4矩阵)的状态数据pState执行完整加密,* 核心流程为“初始轮密钥加 → Nr轮主循环(字节替换→行移位→列混合→轮密钥加,最后一轮无列混合)”,* 加密结果直接覆盖原始状态数据。* * @param[in,out] pState 指向16字节状态数据的指针:* - 输入:待加密的明文数据块(需按AES“列为主序”存储);* - 输出:加密后的密文数据块。* * @note 1. 依赖全局变量:* - g_roundKeyTable:轮密钥表(需提前通过密钥扩展生成,包含初始轮+Nr轮的所有子密钥);* - Nr:加密轮数(需根据原始密钥长度正确赋值,如128位密钥Nr=10);* - Nb:固定为4(AES数据块大小,4×32位字);* 2. 数据长度:pState必须指向16字节有效内存(4×4矩阵),函数不做长度检查;* 3. 流程关键:最后一轮(i=Nr)不执行列混合(mix_columns),这是AES标准规定的核心细节。*/
static void block_encrypt(unsigned char *pState)
{unsigned char i; // 循环索引:表示当前加密轮次(1~Nr)// -------------------------- 步骤1:初始轮密钥加(Initial Round Key Addition) --------------------------// 用轮密钥表的第0轮密钥(起始地址)与初始状态异或,首次将密钥注入数据AddRoundKey(pState, g_roundKeyTable);// -------------------------- 步骤2:Nr轮主加密循环(Main Rounds,1~Nr轮) --------------------------// 循环执行Nr轮,每轮包含“字节替换→行移位→列混合(非最后轮)→轮密钥加”for (i = 1; i <= Nr; i++) // 轮次从1开始,到Nr结束(共Nr轮){// 2.1 字节替换(SubBytes):用S-盒正向替换(第三个参数0=加密模式),实现数据混淆sub_bytes(pState, 4 * Nb, 0); // 4*Nb=16字节(状态总长度)// 2.2 行移位(ShiftRows):正向行移位(第二个参数0=加密模式),实现数据扩散shift_rows(pState, 0);// 2.3 列混合(MixColumns):仅在非最后轮执行(i≠Nr),正向列混合(第二个参数0=加密模式)// 最后一轮不执行列混合,避免后续轮密钥加前的数据过度扩散(AES标准规定)if (i != Nr){mix_columns(pState, 0);}// 2.4 当前轮密钥加(Round Key Addition):用第i轮的轮密钥与当前状态异或// 轮密钥表中第i轮密钥的偏移:4*Nb*i(每轮密钥占16字节,i轮对应偏移16*i字节)AddRoundKey(pState, &g_roundKeyTable[4 * Nb * i]);}// 注:以下为冗余代码(原代码标注“合并到循环执行”),已整合到上述for循环中,无需重复调用// sub_bytes(pState, 4*Nb);// shift_rows(pState, 0);// AddRoundKey(pState, &g_roundKeyTable[4*Nb*Nr]);
}
16.单块数据解密
对输入的状态数据 pState 进行AES解密处理。解密过程包括多个步骤:添加轮密钥、反向字节替换、反向行移位和反向列混合。
/*** @brief 实现AES单块数据解密(完整流程)* * 功能说明:严格遵循AES解密标准,对16字节(4×4矩阵)的密文状态数据pState执行完整解密,* 核心逻辑是加密流程的“逆操作+反向轮次”:先添加加密最后一轮的密钥,再通过Nr轮主循环(逆行移位→逆字节替换→* 轮密钥加→非首轮逆列混淆),最终将密文还原为明文,结果直接覆盖原始状态数据。* * @param[in,out] pState 指向16字节状态数据的指针:* - 输入:待解密的密文数据块(需按AES“列为主序”存储,与加密时存储格式一致);* - 输出:解密后的明文数据块。* * @note 1. 轮密钥使用逻辑:解密需“反向使用轮密钥表”——首步用加密最后一轮的密钥,后续轮次从后往前取密钥;* 2. 操作顺序关键:解密主循环的操作顺序(逆行移位→逆字节替换)与加密(字节替换→行移位)相反,* 且仅“非首轮”执行逆列混淆(对应加密“非最后轮”执行列混淆);* 3. 依赖前提:轮密钥表g_roundKeyTable需提前生成(与加密共用同一表,无需重复扩展)。*/
static void block_decrypt(unsigned char *pState)
{unsigned char i; // 循环索引:表示解密轮次(从Nr反向遍历到1)// -------------------------- 步骤1:初始轮操作——添加加密最后一轮的密钥 --------------------------// 解密首步需用加密最后一轮的子密钥(轮密钥表中索引为4*Nb*Nr的位置),建立密文与密钥的初始关联AddRoundKey(pState, &g_roundKeyTable[4 * Nb * Nr]);// -------------------------- 步骤2:Nr轮主解密循环(轮次从Nr反向到1) --------------------------// 每轮核心操作:逆行移位 → 逆字节替换 → 轮密钥加 → 非首轮逆列混淆(与加密操作顺序、轮次方向均相反)for (i = Nr; i > 0; i--) // 轮次遍历:i = Nr → Nr-1 → ... → 1(共Nr轮){// 2.1 逆行移位(InvShiftRows):bInvert=1表示反向,还原加密时的行移位操作shift_rows(pState, 1);// 2.2 逆字节替换(InvSubBytes):bInvert=1表示用逆S-盒,还原加密时的字节替换sub_bytes(pState, 4 * Nb, 1); // 4*Nb=16字节(状态总长度,与加密一致)// 2.3 轮密钥加:添加当前轮对应的子密钥(轮次为i-1,因轮密钥表按加密顺序存储)// 例:i=Nr时,取i-1=Nr-1轮的密钥;i=1时,取i-1=0轮的密钥(加密初始轮密钥)AddRoundKey(pState, &g_roundKeyTable[4 * Nb * (i - 1)]);// 2.4 逆列混淆(InvMixColumns):仅“非首轮”执行(i≠1),还原加密时的列混淆// 首轮(i=1)不执行,对应加密最后一轮不执行列混淆,确保解密与加密流程可逆if (i != 1){mix_columns(pState, 1); // bInvert=1表示逆列混淆}}// 注:以下为冗余代码(原代码标注“合并到循环执行”),已整合到上述for循环中,无需重复调用// shift_rows(pState, 1);// sub_bytes(pState, 4*Nb, 1);// AddRoundKey(pState, g_roundKeyTable);
}