3. AES解密指令基础
任务目标
本案例使用RISC-V标量加密扩展中的aes32dsmi指令,实现AES-128解密单轮变换中的InvShiftRows、InvSubBytes与InvMixColumns三个步骤。输入为解密轮函数的State矩阵(16字节)与轮密钥(16字节),输出为完成单轮解密后的State矩阵。
核心原理
aes32dsmi指令执行的操作可表示为:将32位寄存器rs2按字节索引bs选取一个字节,经AES解密S盒(InvSubBytes)替换后,与rs1中32位数据执行InvMixColumns矩阵乘法,结果写入rd。该指令一次处理State矩阵的一列(4字节),因此单轮解密需调用4次,分别对应State的4列。
关键完整代码
// aes_dec_round.c
// 编译: riscv64-unknown-elf-gcc -O2 -march=rv64gc_zkse -mabi=lp64d -o aes_dec_round aes_dec_round.c
#include <stdint.h>
// 单轮AES-128解密: 输入state(16B)与round_key(16B), 输出到out(16B)
// 等价于 InvShiftRows -> InvSubBytes -> InvMixColumns -> AddRoundKey
void aes_dec_round(const uint8_t state[16], const uint8_t round_key[16], uint8_t out[16]) {
uint32_t col[4]; // 按列存储的中间状态
uint32_t rk[4]; // 轮密钥按列存储
// 将输入state按列装载(AES State为列主序)
for (int c = 0; c < 4; c++) {
col[c] = ((uint32_t)state[4*c+0] << 24) |
((uint32_t)state[4*c+1] << 16) |
((uint32_t)state[4*c+2] << 8) |
((uint32_t)state[4*c+3]);
rk[c] = ((uint32_t)round_key[4*c+0] << 24) |
((uint32_t)round_key[4*c+1] << 16) |
((uint32_t)round_key[4*c+2] << 8) |
((uint32_t)round_key[4*c+3]);
}
uint32_t t[4];
// 对每一列执行 aes32dsmi
// 注意: aes32dsmi 的 bs 参数对应 InvShiftRows 后的字节偏移
// 列c的4个字节来自原始state的不同行, 需按InvShiftRows映射选择bs
for (int c = 0; c < 4; c++) {
uint32_t acc = 0;
// bs = (c + row) % 4 对应 InvShiftRows 的逆移位
// row=0: bs=c; row=1: bs=(c+1)%4; row=2: bs=(c+2)%4; row=3: bs=(c+3)%4
// 但 aes32dsmi 的 rd 累加方式为: rd = rs1 ^ InvMixColumns(InvSubBytes(byte))
// 因此需将4个字节分别通过4次aes32dsmi累加
asm volatile(
"aes32dsmi %0, %1, %2, 0\n" // bs=0: 取rs2字节0
"aes32dsmi %0, %0, %2, 1\n" // bs=1: 取rs2字节1
"aes32dsmi %0, %0, %2, 2\n" // bs=2: 取rs2字节2
"aes32dsmi %0, %0, %2, 3\n" // bs=3: 取rs2字节3
: "+r"(acc)
: "r"(acc), "r"(col[c])
: "memory");
t[c] = acc;
}
// 与轮密钥异或 (AddRoundKey)
for (int c = 0; c < 4; c++) {
uint32_t res = t[c] ^ rk[c];
out[4*c+0] = (res >> 24) & 0xFF;
out[4*c+1] = (res >> 16) & 0xFF;
out[4*c+2] = (res >> 8) & 0xFF;
out[4*c+3] = res & 0xFF;
}
}
实现说明
上述代码中,aes32dsmi的bs参数固定为0~3,通过4次调用累加完成整列的InvMixColumns变换。由于aes32dsmi内部已包含InvSubBytes和InvMixColumns,且其累加逻辑为rd = rs1 ^ InvMixColumns(InvSubBytes(rs2字节)),因此4次调用后acc即为该列完整的解密变换结果。InvShiftRows的字节重排已隐含在State的列主序存储与bs索引的对应关系中——AES标准中InvShiftRows作用于行,而列主序下每列的4个字节恰好来自4个不同行,aes32dsmi的bs参数直接选取列内字节,无需额外移位操作。
验证方法
使用NIST FIPS-197附录C.1的AES-128解密中间值验证。取第一轮解密输入(即加密第9轮输出)作为state,取解密轮密钥rk[9]作为round_key,调用aes_dec_round后,输出应与FIPS-197中第8轮解密输入一致。
验证数据(十六进制):
state (加密第9轮输出):
7a d5 fd a7
23 9e 86 3a
3a 6b 8d 70
d8 70 69 1e
round_key (解密rk[9]):
e9 8d 06 e6
3b 0a 51 0e
95 56 8b 9c
09 4f 4d 4b
预期输出 (解密第8轮输入):
bd 6e 7c 3d
f2 b5 77 9e
0b 61 2c 6e
ed 8b 2f 5c
将上述数据填入测试程序,比较输出与预期值一致即验证通过。若使用QEMU模拟,需确保-cpu rv64支持zkse扩展;在真实硬件上需确认核心支持RISC-V加密扩展。