2. AES-128解密引擎的RTL设计
任务目标
设计一个AES-128解密引擎的RTL实现,包含完整的逆轮函数(InvShiftRows、InvSubBytes、InvMixColumns、AddRoundKey)与逆密钥扩展逻辑,并通过仿真验证:密文经解密后能正确恢复出原始明文。
实现方案与接口定义
采用单模块设计,数据通路为128比特,轮密钥通过逆密钥扩展实时生成(而非预计算存储),以展示完整的解密流程。顶层模块接口如下:
input wire clk,
input wire rst_n,
input wire start, // 脉冲启动解密
input wire [127:0] ciphertext, // 输入密文
input wire [127:0] cipher_key, // 原始加密密钥(用于逆扩展)
output reg [127:0] plaintext_out, // 解密输出
output reg done // 解密完成脉冲
关键完整代码(AES-128解密引擎)
module aes128_decrypt_engine (
input wire clk,
input wire rst_n,
input wire start,
input wire [127:0] ciphertext,
input wire [127:0] cipher_key,
output reg [127:0] plaintext_out,
output reg done
);
// ---------- 内部状态 ----------
reg [127:0] state_reg;
reg [127:0] round_key;
reg [3:0] round_cnt;
reg busy;
reg [127:0] key_schedule_reg; // 当前轮密钥扩展状态
// ---------- S盒(逆)查找表 ----------
function [7:0] inv_sbox;
input [7:0] addr;
case (addr)
8'h00: inv_sbox = 8'h52; 8'h01: inv_sbox = 8'h09; 8'h02: inv_sbox = 8'h6a;
8'h03: inv_sbox = 8'hd5; 8'h04: inv_sbox = 8'h30; 8'h05: inv_sbox = 8'h36;
8'h06: inv_sbox = 8'ha5; 8'h07: inv_sbox = 8'h38; 8'h08: inv_sbox = 8'hbf;
8'h09: inv_sbox = 8'h40; 8'h0a: inv_sbox = 8'ha3; 8'h0b: inv_sbox = 8'h9e;
8'h0c: inv_sbox = 8'h81; 8'h0d: inv_sbox = 8'hf3; 8'h0e: inv_sbox = 8'hd7;
8'h0f: inv_sbox = 8'hfb; 8'h10: inv_sbox = 8'h7c; 8'h11: inv_sbox = 8'he3;
8'h12: inv_sbox = 8'h39; 8'h13: inv_sbox = 8'h82; 8'h14: inv_sbox = 8'h9b;
8'h15: inv_sbox = 8'h2f; 8'h16: inv_sbox = 8'hff; 8'h17: inv_sbox = 8'h87;
8'h18: inv_sbox = 8'h34; 8'h19: inv_sbox = 8'h8e; 8'h1a: inv_sbox = 8'h43;
8'h1b: inv_sbox = 8'h44; 8'h1c: inv_sbox = 8'hc4; 8'h1d: inv_sbox = 8'hde;
8'h1e: inv_sbox = 8'he9; 8'h1f: inv_sbox = 8'hcb; 8'h20: inv_sbox = 8'h54;
8'h21: inv_sbox = 8'h7b; 8'h22: inv_sbox = 8'h94; 8'h23: inv_sbox = 8'h32;
8'h24: inv_sbox = 8'ha6; 8'h25: inv_sbox = 8'hc2; 8'h26: inv_sbox = 8'h23;
8'h27: inv_sbox = 8'h3d; 8'h28: inv_sbox = 8'hee; 8'h29: inv_sbox = 8'h4c;
8'h2a: inv_sbox = 8'h95; 8'h2b: inv_sbox = 8'h0b; 8'h2c: inv_sbox = 8'h42;
8'h2d: inv_sbox = 8'hfa; 8'h2e: inv_sbox = 8'hc3; 8'h2f: inv_sbox = 8'h4e;
8'h30: inv_sbox = 8'h08; 8'h31: inv_sbox = 8'h2e; 8'h32: inv_sbox = 8'ha1;
8'h33: inv_sbox = 8'h66; 8'h34: inv_sbox = 8'h28; 8'h35: inv_sbox = 8'hd9;
8'h36: inv_sbox = 8'h24; 8'h37: inv_sbox = 8'hb2; 8'h38: inv_sbox = 8'h76;
8'h39: inv_sbox = 8'h5b; 8'h3a: inv_sbox = 8'ha2; 8'h3b: inv_sbox = 8'h49;
8'h3c: inv_sbox = 8'h6d; 8'h3d: inv_sbox = 8'h8b; 8'h3e: inv_sbox = 8'hd1;
8'h3f: inv_sbox = 8'h25; 8'h40: inv_sbox = 8'h72; 8'h41: inv_sbox = 8'hf8;
8'h42: inv_sbox = 8'hf6; 8'h43: inv_sbox = 8'h64; 8'h44: inv_sbox = 8'h86;
8'h45: inv_sbox = 8'h68; 8'h46: inv_sbox = 8'h98; 8'h47: inv_sbox = 8'h16;
8'h48: inv_sbox = 8'hd4; 8'h49: inv_sbox = 8'ha4; 8'h4a: inv_sbox = 8'h5c;
8'h4b: inv_sbox = 8'hcc; 8'h4c: inv_sbox = 8'h5d; 8'h4d: inv_sbox = 8'h65;
8'h4e: inv_sbox = 8'hb6; 8'h4f: inv_sbox = 8'h92; 8'h50: inv_sbox = 8'h6c;
8'h51: inv_sbox = 8'h70; 8'h52: inv_sbox = 8'h48; 8'h53: inv_sbox = 8'h50;
8'h54: inv_sbox = 8'hfd; 8'h55: inv_sbox = 8'hed; 8'h56: inv_sbox = 8'hb9;
8'h57: inv_sbox = 8'hda; 8'h58: inv_sbox = 8'h5e; 8'h59: inv_sbox = 8'h15;
8'h5a: inv_sbox = 8'h46; 8'h5b: inv_sbox = 8'h57; 8'h5c: inv_sbox = 8'ha7;
8'h5d: inv_sbox = 8'h8d; 8'h5e: inv_sbox = 8'h9d; 8'h5f: inv_sbox = 8'h84;
8'h60: inv_sbox = 8'h90; 8'h61: inv_sbox = 8'hd8; 8'h62: inv_sbox = 8'hab;
8'h63: inv_sbox = 8'h00; 8'h64: inv_sbox = 8'h8c; 8'h65: inv_sbox = 8'hbc;
8'h66: inv_sbox = 8'hd3; 8'h67: inv_sbox = 8'h0a; 8'h68: inv_sbox = 8'hf7;
8'h69: inv_sbox = 8'he4; 8'h6a: inv_sbox = 8'h58; 8'h6b: inv_sbox = 8'h05;
8'h6c: inv_sbox = 8'hb8; 8'h6d: inv_sbox = 8'hb3; 8'h6e: inv_sbox = 8'h45;
8'h6f: inv_sbox = 8'h06; 8'h70: inv_sbox = 8'hd0; 8'h71: inv_sbox = 8'h2c;
8'h72: inv_sbox = 8'h1e; 8'h73: inv_sbox = 8'h8f; 8'h74: inv_sbox = 8'hca;
8'h75: inv_sbox = 8'h3f; 8'h76: inv_sbox = 8'h0f; 8'h77: inv_sbox = 8'h02;
8'h78: inv_sbox = 8'hc1; 8'h79: inv_sbox = 8'haf; 8'h7a: inv_sbox = 8'hbd;
8'h7b: inv_sbox = 8'h03; 8'h7c: inv_sbox = 8'h01; 8'h7d: inv_sbox = 8'h13;
8'h7e: inv_sbox = 8'h8a; 8'h7f: inv_sbox = 8'h6b; 8'h80: inv_sbox = 8'h3a;
8'h81: inv_sbox = 8'h91; 8'h82: inv_sbox = 8'h11; 8'h83: inv_sbox = 8'h41;
8'h84: inv_sbox = 8'h4f; 8'h85: inv_sbox = 8'h67; 8'h86: inv_sbox = 8'hdc;
8'h87: inv_sbox = 8'hea; 8'h88: inv_sbox = 8'h97; 8'h89: inv_sbox = 8'hf2;
8'h8a: inv_sbox = 8'hcf; 8'h8b: inv_sbox = 8'hce; 8'h8c: inv_sbox = 8'hf0;
8'h8d: inv_sbox = 8'hb4; 8'h8e: inv_sbox = 8'he6; 8'h8f: inv_sbox = 8'h73;
8'h90: inv_sbox = 8'h96; 8'h91: inv_sbox = 8'hac; 8'h92: inv_sbox = 8'h74;
8'h93: inv_sbox = 8'h22; 8'h94: inv_sbox = 8'he7; 8'h95: inv_sbox = 8'had;
8'h96: inv_sbox = 8'h35; 8'h97: inv_sbox = 8'h85; 8'h98: inv_sbox = 8'he2;
8'h99: inv_sbox = 8'hf9; 8'h9a: inv_sbox = 8'h37; 8'h9b: inv_sbox = 8'he8;
8'h9c: inv_sbox = 8'h1c; 8'h9d: inv_sbox = 8'h75; 8'h9e: inv_sbox = 8'hdf;
8'h9f: inv_sbox = 8'h6e; 8'ha0: inv_sbox = 8'h47; 8'ha1: inv_sbox = 8'hf1;
8'ha2: inv_sbox = 8'h1a; 8'ha3: inv_sbox = 8'h71; 8'ha4: inv_sbox = 8'h1d;
8'ha5: inv_sbox = 8'h29; 8'ha6: inv_sbox = 8'hc5; 8'ha7: inv_sbox = 8'h89;
8'ha8: inv_sbox = 8'h6f; 8'ha9: inv_sbox = 8'hb7; 8'haa: inv_sbox = 8'h62;
8'hab: inv_sbox = 8'h0e; 8'hac: inv_sbox = 8'haa; 8'had: inv_sbox = 8'h18;
8'hae: inv_sbox = 8'hbe; 8'haf: inv_sbox = 8'h1b; 8'hb0: inv_sbox = 8'hfc;
8'hb1: inv_sbox = 8'h56; 8'hb2: inv_sbox = 8'h3e; 8'hb3: inv_sbox = 8'h4b;
8'hb4: inv_sbox = 8'hc6; 8'hb5: inv_sbox = 8'hd2; 8'hb6: inv_sbox = 8'h79;
8'hb7: inv_sbox = 8'h20; 8'hb8: inv_sbox = 8'h9a; 8'hb9: inv_sbox = 8'hdb;
8'hba: inv_sbox = 8'hc0; 8'hbb: inv_sbox = 8'hfe; 8'hbc: inv_sbox = 8'h78;
8'hbd: inv_sbox = 8'hcd; 8'hbe: inv_sbox = 8'h5a; 8'hbf: inv_sbox = 8'hf4;
8'hc0: inv_sbox = 8'h1f; 8'hc1: inv_sbox = 8'hdd; 8'hc2: inv_sbox = 8'ha8;
8'hc3: inv_sbox = 8'h33; 8'hc4: inv_sbox = 8'h88; 8'hc5: inv_sbox = 8'h07;
8'hc6: inv_sbox = 8'hc7; 8'hc7: inv_sbox = 8'h31; 8'hc8: inv_sbox = 8'hb1;
8'hc9: inv_sbox = 8'h12; 8'hca: inv_sbox = 8'h10; 8'hcb: inv_sbox = 8'h59;
8'hcc: inv_sbox = 8'h27; 8'hcd: inv_sbox = 8'h80; 8'hce: inv_sbox = 8'hec;
8'hcf: inv_sbox = 8'h5f; 8'hd0: inv_sbox = 8'h60; 8'hd1: inv_sbox = 8'h51;
8'hd2: inv_sbox = 8'h7f; 8'hd3: inv_sbox = 8'ha9; 8'hd4: inv_sbox = 8'h19;
8'hd5: inv_sbox = 8'hb5; 8'hd6: inv_sbox = 8'h4a; 8'hd7: inv_sbox = 8'h0d;
8'hd8: inv_sbox = 8'h2d; 8'hd9: inv_sbox = 8'he5; 8'hda: inv_sbox = 8'h7a;
8'hdb: inv_sbox = 8'h9f; 8'hdc: inv_sbox = 8'h93; 8'hdd: inv_sbox = 8'hc9;
8'hde: inv_sbox = 8'h9c; 8'hdf: inv_sbox = 8'hef; 8'he0: inv_sbox = 8'ha0;
8'he1: inv_sbox = 8'he0; 8'he2: inv_sbox = 8'h3b; 8'he3: inv_sbox = 8'h4d;
8'he4: inv_sbox = 8'hae; 8'he5: inv_sbox = 8'h2a; 8'he6: inv_sbox = 8'hf5;
8'he7: inv_sbox = 8'hb0; 8'he8: inv_sbox = 8'hc8; 8'he9: inv_sbox = 8'heb;
8'hea: inv_sbox = 8'hbb; 8'heb: inv_sbox = 8'h3c; 8'hec: inv_sbox = 8'h83;
8'hed: inv_sbox = 8'h53; 8'hee: inv_sbox = 8'h99; 8'hef: inv_sbox = 8'h61;
8'hf0: inv_sbox = 8'h17; 8'hf1: inv_sbox = 8'h2b; 8'hf2: inv_sbox = 8'h04;
8'hf3: inv_sbox = 8'h7e; 8'hf4: inv_sbox = 8'hba; 8'hf5: inv_sbox = 8'h77;
8'hf6: inv_sbox = 8'hd6; 8'hf7: inv_sbox = 8'h26; 8'hf8: inv_sbox = 8'he1;
8'hf9: inv_sbox = 8'h69; 8'hfa: inv_sbox = 8'h14; 8'hfb: inv_sbox = 8'h63;
8'hfc: inv_sbox = 8'h55; 8'hfd: inv_sbox = 8'h21; 8'hfe: inv_sbox = 8'h0c;
8'hff: inv_sbox = 8'h7d;
default: inv_sbox = 8'h00;
endcase
endfunction
// ---------- 轮常数(用于逆密钥扩展) ----------
function [7:0] rcon;
input [3:0] round;
case (round)
4'd1: rcon = 8'h01; 4'd2: rcon = 8'h02; 4'd3: rcon = 8'h04;
4'd4: rcon = 8'h08; 4'd5: rcon = 8'h10; 4'd6: rcon = 8'h20;
4'd7: rcon = 8'h40; 4'd8: rcon = 8'h80; 4'd9: rcon = 8'h1b;
4'd10: rcon = 8'h36; default: rcon = 8'h00;
endcase
endfunction
// ---------- 逆列混合(单列) ----------
function [31:0] inv_mix_column;
input [31:0] col;
reg [7:0] a0, a1, a2, a3;
reg [7:0] b0, b1, b2, b3;
reg [7:0] t;
begin
a0 = col[31:24]; a1 = col[23:16]; a2 = col[15:8]; a3 = col[7:0];
// xtime 辅助
t = (a0 & 8'h80) ? (a0 << 1) ^ 8'h1b : (a0 << 1);
b0 = t ^ ((a1 & 8'h80) ? ((a1 << 1) ^ 8'h1b) : (a1 << 1)) ^ a1 ^ a2 ^ a3;
t = (a1 & 8'h80) ? (a1 << 1) ^ 8'h1b : (a1 << 1);
b1 = t ^ ((a2 & 8'h80) ? ((a2 << 1) ^ 8'h1b) : (a2 << 1)) ^ a2 ^ a0 ^ a3;
t = (a2 & 8'h80) ? (a2 << 1) ^ 8'h1b : (a2 << 1);
b2 = t ^ ((a3 & 8'h80) ? ((a3 << 1) ^ 8'h1b) : (a3 << 1)) ^ a3 ^ a0 ^ a1;
t = (a3 & 8'h80) ? (a3 << 1) ^ 8'h1b : (a3 << 1);
b3 = t ^ ((a0 & 8'h80) ? ((a0 << 1) ^ 8'h1b) : (a0 << 1)) ^ a0 ^ a1 ^ a2;
inv_mix_column = {b0, b1, b2, b3};
end
endfunction
// ---------- 逆轮函数(单轮组合逻辑) ----------
function [127:0] inv_round;
input [127:0] state_in;
input [127:0] rk;
reg [127:0] st;
reg [31:0] c0, c1, c2, c3;
integer i;
begin
// 1. InvShiftRows
st[127:120] = state_in[127:120]; // row0 不变
st[95:88] = state_in[23:16]; // row1 右移1
st[63:56] = state_in[47:40]; // row1 右移2
st[31:24] = state_in[71:64]; // row1 右移3
st[119:112] = state_in[119:112]; // row2 不变
st[87:80] = state_in[55:48];
st[55:48] = state_in[87:80];
st[23:16] = state_in[119:112];
st[111:104] = state_in[111:104]; // row3 不变
st[79:72] = state_in[31:24];
st[47:40] = state_in[63:56];
st[15:8] = state_in[95:88];
st[103:96] = state_in[103:96]; // row4 不变
st[71:64] = state_in[15:8];
st[39:32] = state_in[31:24];
st[7:0] = state_in[47:40];
// 2. InvSubBytes
for (i = 0; i < 16; i = i + 1) begin
st[i*8 +: 8] = inv_sbox(st[i*8 +: 8]);
end
// 3. AddRoundKey(先加轮密钥)
st = st ^ rk;
// 4. InvMixColumns(最后一轮不执行,由外部控制)
c0 = inv_mix_column(st[127:96]);
c1 = inv_mix_column(st[95:64]);
c2 = inv_mix_column(st[63:32]);
c3 = inv_mix_column(st[31:0]);
inv_round = {c0, c1, c2, c3};
end
endfunction
// ---------- 逆密钥扩展(每轮生成前一轮密钥) ----------
function [127:0] inv_key_schedule;
input [127:0] current_key; // 当前轮密钥(更早一轮)
input [127:0] next_key; // 下一轮密钥(更晚一轮,即已知的)
input [3:0] round; // 当前轮号(1~10)
reg [31:0] w0, w1, w2, w3;
reg [31:0] temp;
reg [7:0] rcon_val;
begin
w0 = next_key[127:96];
w1 = next_key[95:64];
w2 = next_key[63:32];
w3 = next_key[31:0];
rcon_val = rcon(round);
// 计算前一轮的w0
temp = w3;
// RotWord
temp = {temp[23:0], temp[31:24]};
// SubWord
temp = {inv_sbox(temp[31:24]), inv_sbox(temp[23:16]), inv_sbox(temp[15:8]), inv_sbox(temp[7:0])};
temp = temp ^ {rcon_val, 24'h000000};
w0 = w0 ^ temp;
w1 = w1 ^ w0;
w2 = w2 ^ w1;
w3 = w3 ^ w2;
inv_key_schedule = {w0, w1, w2, w3};
end
endfunction
// ---------- 主状态机 ----------
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state_reg <= 128'h0;
round_key <= 128'h0;
round_cnt <= 4'd0;
busy <= 1'b0;
done <= 1'b0;
plaintext_out <= 128'h0;
key_schedule_reg <= 128'h0;
end else if (start && !busy) begin
busy <= 1'b1;
done <= 1'b0;
state_reg <= ciphertext;
round_cnt <= 4'd10;
key_schedule_reg <= cipher_key; // 初始为原始密钥(第10轮密钥)
// 先执行初始AddRoundKey(使用第10轮密钥)
state_reg <= ciphertext ^ cipher_key;
end else if (busy) begin
if (round_cnt == 4'd0) begin
// 完成
plaintext_out <= state_reg;
busy <= 1'b0;
done <= 1'b1;
end else begin
// 执行一轮解密
if (round_cnt == 4'd1) begin
// 最后一轮:无InvMixColumns
// 先InvShiftRows + InvSubBytes + AddRoundKey
reg [127:0] st;
integer i;
begin
st = state_reg;
// InvShiftRows
st[127:120] = state_reg[127:120];
st[95:88] = state_reg[23:16];
st[63:56] = state_reg[47:40];
st[31:24] = state_reg[71:64];
st[119:112] = state_reg[119:112];
st[87:80] = state_reg[55:48];
st[55:48] = state_reg[87:80];
st[23:16] = state_reg[119:112];
st[111:104] = state_reg[111:104];
st[79:72] = state_reg[31:24];
st[47:40] = state_reg[63:56];
st[15:8] = state_reg[95:88];
st[103:96] = state_reg[103:96];
st[71:64] = state_reg[15:8];
st[39:32] = state_reg[31:24];
st[7:0] = state_reg[47:40];
// InvSubBytes
for (i = 0; i < 16; i = i + 1) begin
st[i*8 +: 8] = inv_sbox(st[i*8 +: 8]);
end
// 生成前一轮密钥(round_cnt=1时,当前key_schedule_reg是第1轮密钥,需要生成第0轮密钥)
round_key <= inv_key_schedule(key_schedule_reg, key_schedule_reg, 4'd1);
state_reg <= st ^ round_key;
end
end else begin
// 普通轮:完整逆轮
// 先计算逆密钥扩展
round_key <= inv_key_schedule(key_schedule_reg, key_schedule_reg, round_cnt);
// 执行逆轮(注意:inv_round内部已包含AddRoundKey,但我们需要先扩展密钥再使用)
// 因此这里分两步:先扩展,再在下一周期使用
// 简化:直接使用当前key_schedule_reg作为本轮密钥(因为逆扩展是从后往前)
// 但inv_round内部会做AddRoundKey,所以需要传入正确的轮密钥
// 这里采用:先扩展得到前一轮密钥,下一周期使用
// 为简化,我们直接在当前周期使用key_schedule_reg(即当前轮密钥)进行解密
// 然后更新key_schedule_reg为前一轮密钥
state_reg <= inv_round(state_reg, key_schedule_reg);
key_schedule_reg <= inv_key_schedule(key_schedule_reg, key_schedule_reg, round_cnt);
end
round_cnt <= round_cnt - 1;
end
end
end
endmodule
仿真验证与通过标准
使用以下测试向量进行仿真(NIST标准示例):
明文: 00112233445566778899aabbccddeeff
密钥: 000102030405060708090a0b0c0d0e0f
密文: 69c4e0d86a7b0430d8cdb78070b4c55a
编写testbench:将上述密文和密钥输入模块,拉高start脉冲,等待done信号。通过条件:plaintext_out输出等于00112233445566778899aabbccddeeff。仿真波形中应观察到done在约11个时钟周期后拉高,且输出与明文一致。
运行方式
使用Verilog仿真器(如Vivado XSim、ModelSim或Icarus Verilog)编译上述模块与testbench。若使用Icarus,命令为:
iverilog -o sim aes128_decrypt_engine.v tb_aes128_decrypt.v
vvp sim
观察波形或打印输出值,比对是否与预期明文一致。
常见问题
- 时序问题:逆密钥扩展与逆轮函数存在数据依赖,本设计采用每周期完成一轮的方式,密钥扩展与轮函数并行计算,需确保组合逻辑延迟满足时钟约束。
- 最后一轮无InvMixColumns:AES解密最后一轮(round=1)不执行InvMixColumns,代码中已单独处理。
- 密钥扩展方向:逆密钥扩展从第10轮密钥反向推导第0轮密钥,本实现通过
inv_key_schedule函数从当前轮密钥计算前一轮密钥。