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函数从当前轮密钥计算前一轮密钥。