阶段一对称密码基础 — AES-128 需要 11 个轮密钥(初始轮 + 10 轮),密钥扩展算法从 128 位原始密钥派生出全部 176 字节的轮密钥材料。
AES 密钥扩展将 4 字(16 字节)的初始密钥扩展为 44 字(176 字节)的轮密钥序列。算法核心是三个操作:
将一个字(4 字节)循环左移一个字节:
对字的每个字节分别应用 S-Box:
Rcon[i] = [RC[i], 0, 0, 0],其中 RC[1]=0x01,RC[i]=2·RC[i-1] in GF(2⁸)
RC[1]=0x01 RC[2]=0x02 RC[3]=0x04 RC[4]=0x08 RC[5]=0x10
RC[6]=0x20 RC[7]=0x40 RC[8]=0x80 RC[9]=0x1B RC[10]=0x36
设 W[i] 为第 i 个扩展字(4 字节),初始密钥为 W[0]~W[3]:
其中 T = SubWord(RotWord(·)) ⊕ Rcon[i/4]
// aes_key_expand.v - AES-128 密钥扩展模块
module aes_key_expand (
input wire clk,
input wire rst_n,
input wire start, // 启动密钥扩展
input wire [127:0] key_in, // 128位初始密钥
output wire [127:0] round_key, // 当前轮密钥输出
output wire [3:0] round_num, // 当前轮号(0-10)
output wire ready // 扩展完成信号
);
// Rcon 常量
reg [7:0] rcon [0:9];
initial begin
rcon[0] = 8'h01; rcon[1] = 8'h02; rcon[2] = 8'h04; rcon[3] = 8'h08;
rcon[4] = 8'h10; rcon[5] = 8'h20; rcon[6] = 8'h40; rcon[7] = 8'h80;
rcon[8] = 8'h1b; rcon[9] = 8'h36;
end
// S-Box 实例(用于 SubWord)
wire [7:0] sbox_out0, sbox_out1, sbox_out2, sbox_out3;
aes_sbox_lut u_sbox0 (.addr(temp[7:0]), .data(sbox_out0));
aes_sbox_lut u_sbox1 (.addr(temp[15:8]), .data(sbox_out1));
aes_sbox_lut u_sbox2 (.addr(temp[23:16]), .data(sbox_out2));
aes_sbox_lut u_sbox3 (.addr(temp[31:24]), .data(sbox_out3));
// 内部寄存器:保存当前4个字
reg [31:0] w0, w1, w2, w3;
reg [3:0] rnd;
reg done;
// RotWord + SubWord + Rcon 的中间结果
wire [31:0] temp;
assign temp = {w3[23:0], w3[31:24]}; // RotWord
wire [31:0] sub_word;
assign sub_word = {sbox_out3, sbox_out2, sbox_out1, sbox_out0};
wire [31:0] t_result;
assign t_result = sub_word ^ {rcon[rnd], 24'h0};
// 轮密钥输出
assign round_key = {w0, w1, w2, w3};
assign round_num = rnd;
assign ready = done;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
w0 <= 32'h0;
w1 <= 32'h0;
w2 <= 32'h0;
w3 <= 32'h0;
rnd <= 4'h0;
done <= 1'b0;
end else if (start) begin
// 加载初始密钥
w0 <= key_in[127:96];
w1 <= key_in[95:64];
w2 <= key_in[63:32];
w3 <= key_in[31:0];
rnd <= 4'h0;
done <= 1'b0;
end else if (!done && rnd < 4'd10) begin
// 执行一轮扩展
w0 <= w0 ^ t_result;
w1 <= w1 ^ (w0 ^ t_result);
w2 <= w2 ^ ((w0 ^ t_result) ^ (w1 ^ (w0 ^ t_result)));
w3 <= w3 ^ (((w0 ^ t_result) ^ (w1 ^ (w0 ^ t_result))) ^ (w2 ^ ((w0 ^ t_result) ^ (w1 ^ (w0 ^ t_result)))));
rnd <= rnd + 4'd1;
if (rnd == 4'd9)
done <= 1'b1;
end
end
endmodule
// aes_key_expand_v2.v - AES-128 密钥扩展(修正版)
module aes_key_expand_v2 (
input wire clk,
input wire rst_n,
input wire start,
input wire [127:0] key_in,
output reg [127:0] round_key,
output reg [3:0] round_num,
output wire ready
);
reg [7:0] rcon [0:9];
initial begin
rcon[0]=8'h01; rcon[1]=8'h02; rcon[2]=8'h04; rcon[3]=8'h08;
rcon[4]=8'h10; rcon[5]=8'h20; rcon[6]=8'h40; rcon[7]=8'h80;
rcon[8]=8'h1b; rcon[9]=8'h36;
end
reg [31:0] w [0:3];
reg [3:0] rnd;
reg done;
assign ready = done;
// RotWord
wire [31:0] rot_word = {w[3][23:0], w[3][31:24]};
// SubWord
wire [7:0] sw0, sw1, sw2, sw3;
aes_sbox_lut u_sb0 (.addr(rot_word[7:0]), .data(sw0));
aes_sbox_lut u_sb1 (.addr(rot_word[15:8]), .data(sw1));
aes_sbox_lut u_sb2 (.addr(rot_word[23:16]), .data(sw2));
aes_sbox_lut u_sb3 (.addr(rot_word[31:24]), .data(sw3));
wire [31:0] sub_word = {sw3, sw2, sw1, sw0};
wire [31:0] t_func = sub_word ^ {rcon[rnd], 24'h0};
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
w[0] <= 32'h0; w[1] <= 32'h0;
w[2] <= 32'h0; w[3] <= 32'h0;
rnd <= 4'h0; done <= 1'b0;
round_key <= 128'h0; round_num <= 4'h0;
end else if (start) begin
w[0] <= key_in[127:96]; w[1] <= key_in[95:64];
w[2] <= key_in[63:32]; w[3] <= key_in[31:0];
rnd <= 4'h0; done <= 1'b0;
round_key <= key_in; round_num <= 4'h0;
end else if (!done && rnd < 4'd10) begin
w[0] <= w[0] ^ t_func;
w[1] <= w[1] ^ (w[0] ^ t_func);
w[2] <= w[2] ^ (w[1] ^ (w[0] ^ t_func));
w[3] <= w[3] ^ (w[2] ^ (w[1] ^ (w[0] ^ t_func)));
round_key <= {w[0]^t_func,
w[1]^(w[0]^t_func),
w[2]^(w[1]^(w[0]^t_func)),
w[3]^(w[2]^(w[1]^(w[0]^t_func)))};
round_num <= rnd + 4'd1;
if (rnd == 4'd9) done <= 1'b1;
end
end
endmodule
// aes_key_expand_tb.v
module aes_key_expand_tb;
reg clk, rst_n, start;
reg [127:0] key_in;
wire [127:0] round_key;
wire [3:0] round_num;
wire ready;
aes_key_expand_v2 uut (
.clk(clk), .rst_n(rst_n), .start(start),
.key_in(key_in), .round_key(round_key),
.round_num(round_num), .ready(ready)
);
always #5 clk = ~clk;
initial begin
clk = 0; rst_n = 0; start = 0; key_in = 0;
#20 rst_n = 1;
// NIST FIPS-197 Appendix A.1 测试向量
key_in = 128'h2b7e151628aed2a6abf7158809cf4f3c;
start = 1; #10; start = 0;
// 等待第0轮密钥
#10;
$display("Round 0: %032h (expect 2b7e151628aed2a6abf7158809cf4f3c)",
round_key);
// 等待后续轮密钥
repeat(10) @(posedge ready);
$display("Key expansion complete!");
$display("Round 10: %032h", round_key);
$finish;
end
endmodule
Rcon 中的 RC 值在 GF(2⁸) 上计算:每个值是前一个值乘以 2(即 x)。当最高位为 1 时,需要异或不可约多项式 0x1B:
RC[1] = 0x01
RC[2] = 0x02 (0x01 × 2)
RC[3] = 0x04 (0x02 × 2)
...
RC[8] = 0x80 (0x40 × 2)
RC[9] = 0x1B (0x80 × 2 = 0x100 → XOR 0x11B → 0x1B)
RC[10]= 0x36 (0x1B × 2 = 0x36)
1. 使用 NIST 测试向量验证密钥扩展模块的每一轮输出是否正确。
2. 实现一个纯组合逻辑版本的密钥扩展:输入 128 位密钥,一次性输出全部 11 个轮密钥(共 1408 位)。分析面积与延迟的 trade-off。
3. AES-256 需要 15 个轮密钥,且每 8 个字(而非 4 个字)才执行一次 RotWord+SubWord。修改模块支持 AES-256。
4. 安全分析:如果两个不同的初始密钥产生了相同的某个轮密钥,是否意味着整个轮密钥序列相同?为什么?
你已掌握 AES 密钥扩展算法(RotWord、SubWord、Rcon),并能用硬件实现每周期产生一个轮密钥的流水线模块。密钥扩展是 AES 引擎的关键前端!
获得徽章:🔗 KEY_SCHEDULER
推荐使用以下工具链进行课程实践:
# 安装 Verilator
sudo apt install verilator
# 安装 Icarus Verilog(可选)
sudo apt install iverilog
# 安装 GTKWave(波形查看器)
sudo apt install gtkwave
# 验证安装
verilator --lint-only --version
iverilog -V
密码学硬件实现的关键性能指标:
这些指标之间通常存在 trade-off,设计时需根据应用场景权衡。
相关密钥攻击利用密钥扩展的结构特性。如果攻击者知道两个密钥之间的关系(如 K₂ = K₁ ⊕ Δ),可以推导出所有轮密钥之间的关系。
AES-128 的密钥扩展相对简单(每 4 个字才执行一次非线性变换),因此比 AES-256 更容易受到相关密钥攻击。
是否存在两个不同的初始密钥 K₁ ≠ K₂,使得它们产生相同的轮密钥序列?对于 AES-128,目前没有发现等价密钥,但这是开放的研究问题。
本课涉及的核心概念和技术关系:
Verilog 仿真调试的常用方法:
// 调试示例
initial begin
$dumpfile("sim.vcd");
$dumpvars(0, uut);
end
// 断言验证
assert property (@(posedge clk) valid |-> data !== 'x)
else $error("Invalid data when valid!");
# 1. 语法检查
verilator --lint-only module.v
# 2. 创建 C++ 测试主函数
cat > sim_main.cpp << 'EOF'
#include "Vmodule.h"
#include "verilated.h"
int main(int argc, char** argv) {
Verilated::commandArgs(argc, argv);
Vmodule* top = new Vmodule;
top->clk = 0; top->rst_n = 0;
top->eval();
top->rst_n = 1;
for (int i = 0; i < 100; i++) {
top->clk = !top->clk;
top->eval();
}
delete top;
return 0;
}
EOF
# 3. 编译
verilator -cc module.v --exe sim_main.cpp
make -C obj_dir -f Vmodule.mk
# 4. 运行
./obj_dir/Vmodule
密码硬件的性能评估维度:
| 指标 | 单位 | 说明 |
|---|---|---|
| 面积 | GE / LUT | 等效门数或查找表数量 |
| 频率 | MHz | 最大时钟频率 |
| 吞吐量 | Gbps | 每秒处理的数据量 |
| 延迟 | 周期数 | 从输入到输出的周期 |
| 能效 | pJ/bit | 每比特能耗 |
| 面积效率 | Gbps/GE | 单位面积吞吐量 |
不同应用场景对指标优先级不同:IoT 偏重面积和能效,服务器偏重吞吐量。