4. AES-128密钥扩展
任务目标
本案例实现RISC-V标量加密扩展(Zkne)下的AES-128密钥扩展。输入16字节初始密钥,利用aes32esmi和aes32esm0两条指令生成11轮轮密钥(含初始轮),每轮16字节,共176字节输出。
核心原理
AES-128密钥扩展中,每4字节为一个字。当字索引i % 4 == 0时,需要先对前一个字执行RotWord(循环左移1字节)、SubWord(S盒替换),再与轮常数Rcon[i/4]异或。RISC-V的aes32esmi指令恰好将RotWord、SubWord和异或合并为单条指令:aes32esmi(rd, rs1, rs2, bs)对rs1循环左移8位后,对指定字节位置bs做S盒替换,再与rd异或。通过将上一轮密钥字的低24位传入rs1、轮常数传入rd、S盒字节位置设为3,即可一步完成RotWord+SubWord+Rcon异或。其余位置的字直接用aes32esm0(仅S盒替换)或普通异或完成。
完整代码(GNU汇编)
.section .text
.globl aes128_keyexp
# 调用: aes128_keyexp(密钥指针, 轮密钥输出指针)
# 参数: a0 = 16字节初始密钥, a1 = 176字节输出缓冲区
aes128_keyexp:
# 复制初始密钥到输出前16字节
lw t0, 0(a0)
lw t1, 4(a0)
lw t2, 8(a0)
lw t3, 12(a0)
sw t0, 0(a1)
sw t1, 4(a1)
sw t2, 8(a1)
sw t3, 12(a1)
li t4, 10 # 10轮扩展
li t5, 16 # 输出偏移量,从第2轮开始
li t6, 1 # 轮常数 Rcon
1: # 每轮生成4个字
# 字0: temp = aes32esmi(temp, prev_word, 3) ^ Rcon
lw a2, -4(a1) # 上一轮最后一个字(即前一字)
srli a3, a2, 24 # 取最高字节作为bs参数(实际用3)
li a3, 3
aes32esmi a2, a2, a3, 3 # rd=prev, rs1=prev, bs=3 → RotWord+SubWord
xor a2, a2, t6 # 异或轮常数
sw a2, 0(a1)
# 字1: 前一字 ^ 字0
lw a2, 0(a1) # 刚生成的字0
lw a3, -12(a1) # 前一轮字1
xor a2, a2, a3
sw a2, 4(a1)
# 字2: 前一字 ^ 字1
lw a2, 4(a1)
lw a3, -8(a1)
xor a2, a2, a3
sw a2, 8(a1)
# 字3: 前一字 ^ 字2
lw a2, 8(a1)
lw a3, -4(a1)
xor a2, a2, a3
sw a2, 12(a1)
# 更新轮常数 Rcon = xtime(Rcon)
slli t6, t6, 1
andi a4, t6, 0x100
beqz a4, 2f
xori t6, t6, 0x11B
2:
addi a1, a1, 16
addi t5, t5, 16
addi t4, t4, -1
bnez t4, 1b
ret
关键说明
上述代码中aes32esmi a2, a2, a3, 3的编码格式为aes32esmi rd, rs1, rs2, bs,其中rs2为S盒字节索引(此处固定为3,表示对循环移位后结果的第3字节做S盒),rs1为待处理字。该指令执行:rd = rd ^ SBox[(rs1 rol 8) >> (8*bs)]。由于rs1 rol 8等效于RotWord,且bs=3选取的是原最高字节经S盒替换后的位置,配合rd中预置的轮常数,单条指令完成全部三个操作。轮常数更新采用GF(2^8)乘法(xtime),当溢出时异或0x11B。
编译与运行
# 使用支持Zkne扩展的GCC编译(如riscv64-unknown-elf-gcc)
riscv64-unknown-elf-gcc -march=rv64i_zkne -c aes128_keyexp.s -o aes128_keyexp.o
# 链接测试程序(需自行提供main函数调用aes128_keyexp)
riscv64-unknown-elf-gcc -march=rv64i_zkne test.c aes128_keyexp.o -o test
验证方法
使用FIPS-197附录A.1的标准测试向量:初始密钥000102030405060708090a0b0c0d0e0f,预期第10轮轮密钥为d014f9a8c9ee2589e13f0cc8b6630ca6。在测试程序中调用aes128_keyexp后,检查输出缓冲区偏移160字节处是否为该值。若匹配则通过验证。也可用OpenSSL的AES_set_encrypt_key结果逐字节比对全部176字节。