3. AES解密指令基础

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加密扩展。