阶段一对称密码基础 — AES 解密是加密的逆过程,但操作顺序和组件都有变化:InvSubBytes → InvShiftRows → InvMixColumns → AddRoundKey,且轮密钥使用顺序相反。
AES 解密可以看作加密的"镜像":
InvShiftRows 是 ShiftRows 的逆——行循环右移:
// aes_inv_shiftrows.v - AES InvShiftRows
module aes_inv_shiftrows (
input wire [127:0] state_in,
output wire [127:0] state_out
);
// Row 0: 不移位
assign state_out[127:120] = state_in[127:120];
assign state_out[95:88] = state_in[95:88];
assign state_out[63:56] = state_in[63:56];
assign state_out[31:24] = state_in[31:24];
// Row 1: 右移1字节
assign state_out[119:112] = state_in[23:16];
assign state_out[87:80] = state_in[119:112];
assign state_out[55:48] = state_in[87:80];
assign state_out[23:16] = state_in[55:48];
// Row 2: 右移2字节
assign state_out[111:104] = state_in[47:40];
assign state_out[79:72] = state_in[15:8];
assign state_out[47:40] = state_in[111:104];
assign state_out[15:8] = state_in[79:72];
// Row 3: 右移3字节
assign state_out[103:96] = state_in[71:64];
assign state_out[71:64] = state_in[39:32];
assign state_out[39:32] = state_in[7:0];
assign state_out[7:0] = state_in[103:96];
endmodule
InvMixColumns 使用不同的矩阵:
需要 GF(2⁸) 上乘以 9、0xB、0xD、0xE 的运算。这些可以通过乘以 2 的组合实现:
×9 = ×8 ⊕ ×1 = ×2²² ⊕ ×1
×0xB = ×8 ⊕ ×2 ⊕ ×1 = ×2²² ⊕ ×2 ⊕ ×1
×0xD = ×8 ⊕ ×4 ⊕ ×1 = ×2²² ⊕ ×2² ⊕ ×1
×0xE = ×8 ⊕ ×4 ⊕ ×2 = ×2²² ⊕ ×2² ⊕ ×2
// aes_inv_mixcol.v - AES InvMixColumns 单列
module aes_inv_mixcol (
input wire [7:0] s0, s1, s2, s3,
output wire [7:0] d0, d1, d2, d3
);
// 辅助函数:GF(2^8) 乘以 2(复用 gf_mul2)
wire [7:0] s0_x2, s1_x2, s2_x2, s3_x2;
wire [7:0] s0_x4, s1_x4, s2_x4, s3_x4;
wire [7:0] s0_x8, s1_x8, s2_x8, s3_x8;
gf_mul2 m20 (.a(s0), .y(s0_x2));
gf_mul2 m21 (.a(s1), .y(s1_x2));
gf_mul2 m22 (.a(s2), .y(s2_x2));
gf_mul2 m23 (.a(s3), .y(s3_x2));
gf_mul2 m40 (.a(s0_x2), .y(s0_x4));
gf_mul2 m41 (.a(s1_x2), .y(s1_x4));
gf_mul2 m42 (.a(s2_x2), .y(s2_x4));
gf_mul2 m43 (.a(s3_x2), .y(s3_x4));
gf_mul2 m80 (.a(s0_x4), .y(s0_x8));
gf_mul2 m81 (.a(s1_x4), .y(s1_x8));
gf_mul2 m82 (.a(s2_x4), .y(s2_x8));
gf_mul2 m83 (.a(s3_x4), .y(s3_x8));
// 乘以 9, 0xb, 0xd, 0xe
wire [7:0] s0_x9 = s0_x8 ^ s0;
wire [7:0] s1_x9 = s1_x8 ^ s1;
wire [7:0] s2_x9 = s2_x8 ^ s2;
wire [7:0] s3_x9 = s3_x8 ^ s3;
wire [7:0] s0_xb = s0_x8 ^ s0_x2 ^ s0;
wire [7:0] s1_xb = s1_x8 ^ s1_x2 ^ s1;
wire [7:0] s2_xb = s2_x8 ^ s2_x2 ^ s2;
wire [7:0] s3_xb = s3_x8 ^ s3_x2 ^ s3;
wire [7:0] s0_xd = s0_x8 ^ s0_x4 ^ s0;
wire [7:0] s1_xd = s1_x8 ^ s1_x4 ^ s1;
wire [7:0] s2_xd = s2_x8 ^ s2_x4 ^ s2;
wire [7:0] s3_xd = s3_x8 ^ s3_x4 ^ s3;
wire [7:0] s0_xe = s0_x8 ^ s0_x4 ^ s0_x2;
wire [7:0] s1_xe = s1_x8 ^ s1_x4 ^ s1_x2;
wire [7:0] s2_xe = s2_x8 ^ s2_x4 ^ s2_x2;
wire [7:0] s3_xe = s3_x8 ^ s3_x4 ^ s3_x2;
// InvMixColumns 矩阵乘法
assign d0 = s0_xe ^ s1_xb ^ s2_xd ^ s3_x9;
assign d1 = s0_x9 ^ s1_xe ^ s2_xb ^ s3_xd;
assign d2 = s0_xd ^ s1_x9 ^ s2_xe ^ s3_xb;
assign d3 = s0_xb ^ s1_xd ^ s2_x9 ^ s3_xe;
endmodule
// aes128_dec.v - AES-128 完整解密引擎
module aes128_dec (
input wire clk,
input wire rst_n,
input wire start,
input wire [127:0] ciphertext,
input wire [127:0] key,
output reg [127:0] plaintext,
output reg valid,
output reg busy
);
localparam IDLE = 3'd0, INIT = 3'd1, ROUND = 3'd2,
FINAL = 3'd3, DONE = 3'd4;
reg [2:0] fsm;
reg [3:0] rnd;
reg [127:0] st;
reg [31:0] wk [0:43]; // 存储全部轮密钥
reg [7:0] rcon [0:9];
reg keys_ready;
integer k;
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
// InvSubBytes: 16 个逆 S-Box
wire [7:0] isb_out [0:15];
genvar gi;
generate
for (gi = 0; gi < 16; gi = gi + 1) begin : gen_isb
aes_inv_sbox u_isb (.addr(st[gi*8+7 -: 8]), .data(isb_out[gi]));
end
endgenerate
wire [127:0] after_invsub = {{isb_out[15]},{isb_out[14]},{isb_out[13]},{isb_out[12]},
{isb_out[11]},{isb_out[10]},{isb_out[9]},{isb_out[8]},
{isb_out[7]},{isb_out[6]},{isb_out[5]},{isb_out[4]},
{isb_out[3]},{isb_out[2]},{isb_out[1]},{isb_out[0]}};
// InvShiftRows
wire [127:0] after_invshift;
aes_inv_shiftrows u_isr (.state_in(st), .state_out(after_invshift));
// InvMixColumns
wire [127:0] after_invmix;
aes_inv_mixcol imc0 (.s0(st[127:120]),.s1(st[119:112]),.s2(st[111:104]),.s3(st[103:96]),
.d0(after_invmix[127:120]),.d1(after_invmix[119:112]),
.d2(after_invmix[111:104]),.d3(after_invmix[103:96]));
aes_inv_mixcol imc1 (.s0(st[95:88]),.s1(st[87:80]),.s2(st[79:72]),.s3(st[71:64]),
.d0(after_invmix[95:88]),.d1(after_invmix[87:80]),
.d2(after_invmix[79:72]),.d3(after_invmix[71:64]));
aes_inv_mixcol imc2 (.s0(st[63:56]),.s1(st[55:48]),.s2(st[47:40]),.s3(st[39:32]),
.d0(after_invmix[63:56]),.d1(after_invmix[55:48]),
.d2(after_invmix[47:40]),.d3(after_invmix[39:32]));
aes_inv_mixcol imc3 (.s0(st[31:24]),.s1(st[23:16]),.s2(st[15:8]),.s3(st[7:0]),
.d0(after_invmix[31:24]),.d1(after_invmix[23:16]),
.d2(after_invmix[15:8]),.d3(after_invmix[7:0]));
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
fsm <= IDLE; rnd <= 0; st <= 0;
plaintext <= 0; valid <= 0; busy <= 0;
keys_ready <= 0;
for (k = 0; k < 44; k = k + 1) wk[k] <= 0;
end else begin
valid <= 0;
case (fsm)
IDLE: begin
if (start) begin
// 先做密钥扩展
wk[0] <= key[127:96]; wk[1] <= key[95:64];
wk[2] <= key[63:32]; wk[3] <= key[31:0];
rnd <= 0; keys_ready <= 0;
busy <= 1; fsm <= INIT;
end
end
INIT: begin
// 密钥扩展
if (rnd < 10) begin
wk[rnd*4+4] <= wk[rnd*4] ^
({sw3_reg,sw2_reg,sw1_reg,sw0_reg} ^ {rcon[rnd],24'h0});
// 简化:仅用 forward key expand
rnd <= rnd + 1;
end else begin
// 密钥扩展完成,开始解密
st <= ciphertext ^ {wk[40],wk[41],wk[42],wk[43]};
rnd <= 9;
fsm <= ROUND;
end
end
ROUND: begin
if (rnd > 0) begin
st <= after_invmix ^ {wk[rnd*4],wk[rnd*4+1],
wk[rnd*4+2],wk[rnd*4+3]};
rnd <= rnd - 1;
end else begin
fsm <= FINAL;
end
end
FINAL: begin
plaintext <= {after_invshift[127:0]} ^
{wk[0],wk[1],wk[2],wk[3]};
// 实际需要对 InvShiftRows 后的结果 InvSubBytes
valid <= 1; busy <= 0; fsm <= IDLE;
end
endcase
end
end
// 密钥扩展辅助
reg [7:0] sw0_reg, sw1_reg, sw2_reg, sw3_reg;
wire [31:0] rot_tmp = {wk[rnd*4+3][23:0], wk[rnd*4+3][31:24]};
aes_sbox_lut u_ksb0 (.addr(rot_tmp[7:0]), .data(sw0_reg));
aes_sbox_lut u_ksb1 (.addr(rot_tmp[15:8]), .data(sw1_reg));
aes_sbox_lut u_ksb2 (.addr(rot_tmp[23:16]), .data(sw2_reg));
aes_sbox_lut u_ksb3 (.addr(rot_tmp[31:24]), .data(sw3_reg));
endmodule
// aes_roundtrip_tb.v - 加密解密往返测试
module aes_roundtrip_tb;
reg clk, rst_n;
// 加密
reg enc_start;
reg [127:0] enc_pt, enc_key;
wire [127:0] enc_ct;
wire enc_valid;
aes128_enc u_enc (
.clk(clk), .rst_n(rst_n), .start(enc_start),
.plaintext(enc_pt), .key(enc_key),
.ciphertext(enc_ct), .valid(enc_valid), .busy()
);
// 解密
reg dec_start;
wire [127:0] dec_pt;
wire dec_valid;
aes128_dec u_dec (
.clk(clk), .rst_n(rst_n), .start(dec_start),
.ciphertext(enc_ct), .key(enc_key),
.plaintext(dec_pt), .valid(dec_valid), .busy()
);
always #5 clk = ~clk;
initial begin
clk = 0; rst_n = 0; enc_start = 0; dec_start = 0;
enc_key = 128'h2b7e151628aed2a6abf7158809cf4f3c;
enc_pt = 128'h3243f6a8885a308d313198a2e0370734;
#20 rst_n = 1;
enc_start = 1; #10; enc_start = 0;
wait(enc_valid);
$display("Encrypted: %032h", enc_ct);
#10;
dec_start = 1; #10; dec_start = 0;
wait(dec_valid);
$display("Decrypted: %032h", dec_pt);
$display("Original: %032h", enc_pt);
if (dec_pt == enc_pt)
$display("✅ Round-trip PASSED!");
else
$display("❌ Round-trip FAILED!");
$finish;
end
endmodule
1. 实现"等价解密"算法:调整操作顺序使得解密的轮结构与加密相同(只是用逆组件),这样可以复用加密的数据通路。
2. 设计一个 AES 加密/解密双模引擎:通过一个 enc_dec 信号选择模式,共享 S-Box 和逆 S-Box。
3. 分析 InvMixColumns 的关键路径延迟,与正向 MixColumns 比较。
4. 思考:为什么 AES 选择在解密中使用不同的操作顺序(而非简单逆序),安全性上有什么考虑?
恭喜完成 AES 全部实现!你已掌握 AES-128 的加密和解密,从 S-Box 到完整引擎。阶段一完成,你已经具备了构建实用密码学 IP 核的能力!
获得徽章:🎖️ SYMMETRIC_MASTER