第05课:AES 完整引擎

阶段一对称密码基础 — 将前四课的组件整合:S-Box + 密钥扩展 + 轮函数 = 完整的 AES-128 加密引擎。这是第一个可独立运行的密码学 IP 核。

1. AES-128 加密流程回顾

AES-128 加密包含初始轮密钥加 + 9 轮标准轮 + 1 轮最终轮:

  1. 初始 AddRoundKey:明文 ⊕ K₀
  2. Round 1-9:SubBytes → ShiftRows → MixColumns → AddRoundKey
  3. Round 10:SubBytes → ShiftRows → AddRoundKey(无 MixColumns)

2. 架构选择:迭代 vs 流水线

架构面积吞吐量适用场景
迭代1× S-Box集合1块/11周期面积受限
流水线11× S-Box集合1块/周期高吞吐
混合2-4× S-Box集合折中通用

本课实现迭代架构:一轮硬件循环使用 11 次,面积最小。

3. 完整 AES-128 加密引擎

✅Verilator验证通过
// aes128_enc.v - AES-128 完整加密引擎(迭代架构)
module aes128_enc (
    input  wire         clk,
    input  wire         rst_n,
    input  wire         start,        // 启动加密
    input  wire [127:0] plaintext,    // 明文输入
    input  wire [127:0] key,          // 128位密钥
    output reg  [127:0] ciphertext,   // 密文输出
    output reg          valid,        // 输出有效信号
    output reg          busy          // 引擎忙碌信号
);

    // 状态机定义
    localparam IDLE    = 3'd0;
    localparam INIT    = 3'd1;   // 初始 AddRoundKey
    localparam ROUND   = 3'd2;   // 标准轮
    localparam FINAL   = 3'd3;   // 最终轮
    localparam DONE    = 3'd4;

    reg [2:0]   state_fsm;
    reg [3:0]   round_cnt;
    reg [127:0] state_reg;
    reg [31:0]  w [0:3];        // 当前轮密钥的4个字
    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 实例化(16个并行)
    wire [7:0] sb_in  [0:15];
    wire [7:0] sb_out [0:15];
    genvar gi;
    generate
        for (gi = 0; gi < 16; gi = gi + 1) begin : gen_sb
            assign sb_in[gi] = state_reg[gi*8+7 -: 8];
            aes_sbox_lut u_sb (.addr(sb_in[gi]), .data(sb_out[gi]));
        end
    endgenerate

    wire [127:0] after_sub = {sb_out[15],sb_out[14],sb_out[13],sb_out[12],
                               sb_out[11],sb_out[10],sb_out[9],sb_out[8],
                               sb_out[7],sb_out[6],sb_out[5],sb_out[4],
                               sb_out[3],sb_out[2],sb_out[1],sb_out[0]};

    // ShiftRows
    wire [127:0] after_shift;
    aes_shiftrows u_sr (.state_in(after_sub), .state_out(after_shift));

    // MixColumns
    wire [127:0] after_mix;
    aes_mixcol mc0 (.s0(after_shift[127:120]), .s1(after_shift[119:112]),
                     .s2(after_shift[111:104]), .s3(after_shift[103:96]),
                     .d0(after_mix[127:120]),   .d1(after_mix[119:112]),
                     .d2(after_mix[111:104]),   .d3(after_mix[103:96]));
    aes_mixcol mc1 (.s0(after_shift[95:88]),   .s1(after_shift[87:80]),
                     .s2(after_shift[79:72]),   .s3(after_shift[71:64]),
                     .d0(after_mix[95:88]),     .d1(after_mix[87:80]),
                     .d2(after_mix[79:72]),     .d3(after_mix[71:64]));
    aes_mixcol mc2 (.s0(after_shift[63:56]),   .s1(after_shift[55:48]),
                     .s2(after_shift[47:40]),   .s3(after_shift[39:32]),
                     .d0(after_mix[63:56]),     .d1(after_mix[55:48]),
                     .d2(after_mix[47:40]),     .d3(after_mix[39:32]));
    aes_mixcol mc3 (.s0(after_shift[31:24]),   .s1(after_shift[23:16]),
                     .s2(after_shift[15:8]),    .s3(after_shift[7:0]),
                     .d0(after_mix[31:24]),     .d1(after_mix[23:16]),
                     .d2(after_mix[15:8]),      .d3(after_mix[7:0]));

    // 密钥扩展辅助:RotWord + SubWord + Rcon
    wire [31:0] rot_w = {w[3][23:0], w[3][31:24]};
    wire [7:0] sw0, sw1, sw2, sw3;
    aes_sbox_lut u_rsb0 (.addr(rot_w[7:0]),   .data(sw0));
    aes_sbox_lut u_rsb1 (.addr(rot_w[15:8]),  .data(sw1));
    aes_sbox_lut u_rsb2 (.addr(rot_w[23:16]), .data(sw2));
    aes_sbox_lut u_rsb3 (.addr(rot_w[31:24]), .data(sw3));
    wire [31:0] t_func = {sw3, sw2, sw1, sw0} ^ {rcon[round_cnt-1], 24'h0};

    // 轮密钥
    wire [127:0] round_key = {w[0], w[1], w[2], w[3]};

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            state_fsm <= IDLE;
            round_cnt <= 4'd0;
            state_reg <= 128'h0;
            ciphertext <= 128'h0;
            valid <= 1'b0;
            busy <= 1'b0;
            w[0] <= 32'h0; w[1] <= 32'h0;
            w[2] <= 32'h0; w[3] <= 32'h0;
        end else begin
            valid <= 1'b0;
            case (state_fsm)
                IDLE: begin
                    if (start) begin
                        w[0] <= key[127:96]; w[1] <= key[95:64];
                        w[2] <= key[63:32];  w[3] <= key[31:0];
                        state_reg <= plaintext ^ {key[127:96],key[95:64],
                                                   key[63:32],key[31:0]};
                        round_cnt <= 4'd1;
                        busy <= 1'b1;
                        state_fsm <= ROUND;
                    end
                end
                ROUND: 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)));
                    // 更新状态
                    state_reg <= after_mix ^ {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_cnt <= round_cnt + 4'd1;
                    if (round_cnt == 4'd9)
                        state_fsm <= FINAL;
                end
                FINAL: 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)));
                    state_reg <= after_shift ^ {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)))};
                    state_fsm <= DONE;
                end
                DONE: begin
                    ciphertext <= state_reg;
                    valid <= 1'b1;
                    busy <= 1'b0;
                    state_fsm <= IDLE;
                end
            endcase
        end
    end

endmodule

4. NIST 测试向量验证

// aes128_enc_tb.v - AES-128 加密引擎测试台
module aes128_enc_tb;

    reg         clk, rst_n, start;
    reg  [127:0] plaintext, key;
    wire [127:0] ciphertext;
    wire         valid, busy;

    aes128_enc uut (
        .clk(clk), .rst_n(rst_n), .start(start),
        .plaintext(plaintext), .key(key),
        .ciphertext(ciphertext), .valid(valid), .busy(busy)
    );

    always #5 clk = ~clk;

    initial begin
        clk = 0; rst_n = 0; start = 0;
        #20 rst_n = 1;

        // NIST FIPS-197 Appendix B 测试向量
        key       = 128'h2b7e151628aed2a6abf7158809cf4f3c;
        plaintext = 128'h3243f6a8885a308d313198a2e0370734;
        start = 1; #10; start = 0;

        // 期望密文: 3925841d02dc09fbdc118597196a0b32
        wait(valid);
        $display("Ciphertext: %032h", ciphertext);
        $display("Expected:   3925841d02dc09fbdc118597196a0b32");
        if (ciphertext == 128'h3925841d02dc09fbdc118597196a0b32)
            $display("✅ NIST test PASSED!");
        else
            $display("❌ NIST test FAILED!");
        $finish;
    end

endmodule

5. 性能分析

迭代架构性能指标

指标
延迟11 个时钟周期(1 初始 + 9 标准轮 + 1 最终轮)
吞吐量128位 / 11 周期(约 11.6 位/周期)
S-Box 数量20(16 SubBytes + 4 密钥扩展)
关键路径S-Box → ShiftRows → MixColumns → AddRoundKey

1. 使用 NIST 测试向量完整验证 AES-128 加密,确保每一位都正确。

2. 将迭代架构改为流水线架构:11 级流水线,每级处理一轮。计算面积开销和吞吐量提升。

3. 实现 AES 的 ECB 模式:连续加密多个 128 位块。然后实现 CBC 模式(需要反馈 XOR)。

4. 安全思考:迭代架构中,轮密钥的中间值是否可能通过功耗侧信道泄露?如何防护?

🏆 成就解锁:AES 引擎完成

你已成功构建完整的 AES-128 加密引擎!从 S-Box 到轮函数到顶层集成,所有组件协同工作,通过了 NIST 标准测试向量验证。这是密码学硬件工程的第一个里程碑!

获得徽章:🔐 AES_ENGINE

💡 扩展阅读与参考资源

🔧 实践环境搭建

推荐使用以下工具链进行课程实践:

# 安装 Verilator
sudo apt install verilator

# 安装 Icarus Verilog(可选)
sudo apt install iverilog

# 安装 GTKWave(波形查看器)
sudo apt install gtkwave

# 验证安装
verilator --lint-only --version
iverilog -V

📊 性能指标对比

密码学硬件实现的关键性能指标:

这些指标之间通常存在 trade-off,设计时需根据应用场景权衡。

5. AES 流水线架构详解

5.1 全流水线架构

11 级流水线,每级处理一轮。面积开销大(11× S-Box = 176 个 S-Box),但吞吐量极高。

// 流水线 AES:每周期输出一个密文块
Stage 0: AddRoundKey(K0)
Stage 1: SubBytes → ShiftRows → MixColumns → AddRoundKey(K1)
...
Stage 10: SubBytes → ShiftRows → AddRoundKey(K10)

5.2 面向区域的迭代架构

复用同一套 S-Box 和 MixColumns 逻辑,通过 MUX 选择当前轮的输入。面积最小,但每块需 11 周期。

5.3 性能比较

架构S-Box数延迟吞吐量
迭代2011周期11.6 bit/cyc
2级流水线406周期21.3 bit/cyc
全流水线1761周期128 bit/cyc