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字节。