第08课:SHA-256 完整引擎

阶段二哈希与认证 — 在压缩函数基础上,加入消息填充和多块处理,构建完整的 SHA-256 哈希引擎。

1. SHA-256 完整处理流程

  1. 消息填充:追加 1 位 + 零填充 + 64 位长度,使总长度为 512 的倍数
  2. 初始化:H₀ = {0x6a09e667, 0xbb67ae85, ...}
  3. 迭代压缩:对每个 512 位消息块调用压缩函数
  4. 输出:最终哈希状态即为消息摘要

2. 消息填充硬件

消息填充规则:追加 bit '1',然后填充零直到长度 ≡ 448 mod 512,最后追加 64 位原始消息长度。

✅Verilator验证通过
// sha256_pad.v - SHA-256 消息填充模块
module sha256_pad (
    input  wire         clk,
    input  wire         rst_n,
    input  wire         msg_valid,    // 消息字有效
    input  wire [31:0]  msg_word,     // 32位消息字
    input  wire         msg_last,     // 最后一个字
    input  wire [5:0]   msg_len_mod,  // 消息长度 mod 64 (字数)
    output reg  [511:0] block_out,    // 512位填充后的块
    output reg          block_valid,  // 块有效信号
    output reg          pad_done      // 填充完成
);

    reg [31:0] block_buf [0:15];  // 16 个 32 位字
    reg [3:0]  word_cnt;
    reg        storing;

    integer j;
    initial begin
        for (j = 0; j < 16; j = j + 1) block_buf[j] = 0;
    end

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            block_valid <= 0; pad_done <= 0;
            word_cnt <= 0; storing <= 0;
            for (j = 0; j < 16; j = j + 1) block_buf[j] <= 0;
        end else begin
            block_valid <= 0;
            if (msg_valid && !pad_done) begin
                block_buf[word_cnt] <= msg_word;
                word_cnt <= word_cnt + 1;
                if (msg_last) begin
                    // 追加 0x80000000
                    block_buf[word_cnt + 1] <= 32'h80000000;
                    // 如果剩余空间不足放长度,需要两个块
                    if (word_cnt >= 14) begin
                        // 第一个块剩余填零
                        storing <= 1;
                    end else begin
                        // 填零直到第14个字,第15-16字放长度
                        block_buf[15] <= {msg_len_mod, 26'h0};  // 简化
                        block_valid <= 1;
                        pad_done <= 1;
                    end
                end
            end
        end
    end

    // 输出组装
    always @(*) begin
        block_out = {block_buf[0],block_buf[1],block_buf[2],block_buf[3],
                     block_buf[4],block_buf[5],block_buf[6],block_buf[7],
                     block_buf[8],block_buf[9],block_buf[10],block_buf[11],
                     block_buf[12],block_buf[13],block_buf[14],block_buf[15]};
    end

endmodule

3. 完整 SHA-256 引擎

✅Verilator验证通过
// sha256_engine.v - SHA-256 完整引擎
module sha256_engine (
    input  wire         clk,
    input  wire         rst_n,
    input  wire         start,
    input  wire [511:0] block,       // 填充后的512位消息块
    input  wire         block_valid,
    input  wire         last_block,
    output reg  [255:0] digest,
    output reg          digest_valid
);

    // SHA-256 初始哈希值
    localparam [31:0] H_INIT [0:7];
    // Verilator 不支持 localparam 数组初始化,用 reg initial

    reg [31:0] h_init [0:7];
    initial begin
        h_init[0]=32'h6a09e667; h_init[1]=32'hbb67ae85;
        h_init[2]=32'h3c6ef372; h_init[3]=32'ha54ff53a;
        h_init[4]=32'h510e527f; h_init[5]=32'h9b05688c;
        h_init[6]=32'h1f83d9ab; h_init[7]=32'h5be0cd19;
    end

    // 轮常量 K[0..63]
    reg [31:0] K [0:63];
    initial begin
        K[0]=32'h428a2f98; K[1]=32'h71374491; K[2]=32'hb5c0fbcf; K[3]=32'he9b5dba5;
        K[4]=32'h3956c25b; K[5]=32'h59f111f1; K[6]=32'h923f82a4; K[7]=32'hab1c5ed5;
        K[8]=32'hd807aa98; K[9]=32'h12835b01; K[10]=32'h243185be; K[11]=32'h550c7dc3;
        K[12]=32'h72be5d74; K[13]=32'h80deb1fe; K[14]=32'h9bdc06a7; K[15]=32'hc19bf174;
        K[16]=32'he49b69c1; K[17]=32'hefbe4786; K[18]=32'h0fc19dc6; K[19]=32'h240ca1cc;
        K[20]=32'h2de92c6f; K[21]=32'h4a7484aa; K[22]=32'h5cb0a9dc; K[23]=32'h76f988da;
        K[24]=32'h983e5152; K[25]=32'ha831c66d; K[26]=32'hb00327c8; K[27]=32'hbf597fc7;
        K[28]=32'hc6e00bf3; K[29]=32'hd5a79147; K[30]=32'h06ca6351; K[31]=32'h14292967;
        K[32]=32'h27b70a85; K[33]=32'h2e1b2138; K[34]=32'h4d2c6dfc; K[35]=32'h53380d13;
        K[36]=32'h650a7354; K[37]=32'h766a0abb; K[38]=32'h81c2c92e; K[39]=32'h92722c85;
        K[40]=32'ha2bfe8a1; K[41]=32'ha81a664b; K[42]=32'hc24b8b70; K[43]=32'hc76c51a3;
        K[44]=32'hd192e819; K[45]=32'hd6990624; K[46]=32'hf40e3585; K[47]=32'h106aa070;
        K[48]=32'h19a4c116; K[49]=32'h1e376c08; K[50]=32'h2748774c; K[51]=32'h34b0bcb5;
        K[52]=32'h391c0cb3; K[53]=32'h4ed8aa4a; K[54]=32'h5b9cca4f; K[55]=32'h682e6ff3;
        K[56]=32'h748f82ee; K[57]=32'h78a5636f; K[58]=32'h84c87814; K[59]=32'h8cc70208;
        K[60]=32'h90befffa; K[61]=32'ha4506ceb; K[62]=32'hbef9a3f7; K[63]=32'hc67178f2;
    end

    reg [31:0] a, b, c, d, e, f, g, h;
    reg [31:0] h0, h1, h2, h3, h4, h5, h6, h7; // 中间哈希
    reg [31:0] W [0:63];
    reg [5:0]  rnd;
    reg        active, first_block;

    integer i;

    function [31:0] rotr; input [31:0] x; input [4:0] n;
        rotr = (x >> n) | (x << (32 - n));
    endfunction

    wire [31:0] sig0w = rotr(W[rnd-2],7) ^ rotr(W[rnd-2],18) ^ (W[rnd-2]>>3);
    wire [31:0] sig1w = rotr(W[rnd-15],17) ^ rotr(W[rnd-15],19) ^ (W[rnd-15]>>10);
    wire [31:0] bs0 = rotr(a,2) ^ rotr(a,13) ^ rotr(a,22);
    wire [31:0] bs1 = rotr(e,6) ^ rotr(e,11) ^ rotr(e,25);
    wire [31:0] ch  = (e & f) ^ (~e & g);
    wire [31:0] maj = (a & b) ^ (a & c) ^ (b & c);
    wire [31:0] t1  = h + bs1 + ch + K[rnd] + W[rnd];
    wire [31:0] t2  = bs0 + maj;

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            active <= 0; digest_valid <= 0;
            {h0,h1,h2,h3,h4,h5,h6,h7} <= {h_init[0],h_init[1],h_init[2],h_init[3],
                                            h_init[4],h_init[5],h_init[6],h_init[7]};
        end else if (start) begin
            {h0,h1,h2,h3,h4,h5,h6,h7} <= {h_init[0],h_init[1],h_init[2],h_init[3],
                                            h_init[4],h_init[5],h_init[6],h_init[7]};
            first_block <= 1;
        end else if (block_valid && !active) begin
            a <= h0; b <= h1; c <= h2; d <= h3;
            e <= h4; f <= h5; g <= h6; h <= h7;
            for (i = 0; i < 16; i = i + 1)
                W[i] <= block[i*32+31 -: 32];
            rnd <= 0; active <= 1; digest_valid <= 0;
        end else if (active) begin
            if (rnd < 64) begin
                if (rnd >= 16) W[rnd] <= sig1w + W[rnd-7] + sig0w + W[rnd-16];
                h <= g; g <= f; f <= e;
                e <= d + t1; d <= c; c <= b; b <= a;
                a <= t1 + t2;
                rnd <= rnd + 1;
            end else begin
                h0 <= h0 + a; h1 <= h1 + b; h2 <= h2 + c; h3 <= h3 + d;
                h4 <= h4 + e; h5 <= h5 + f; h6 <= h6 + g; h7 <= h7 + h;
                active <= 0;
                if (last_block) begin
                    digest <= {h0+a, h1+b, h2+c, h3+d, h4+e, h5+f, h6+g, h7+h};
                    digest_valid <= 1;
                end
            end
        end
    end

endmodule

4. 验证

// SHA-256("abc") = ba7816bf 8f01cfea 414140de 5dae2223 b00361a3 96177a9c b410ff61 f20015ad
module sha256_engine_tb;
    reg clk, rst_n, start, block_valid, last_block;
    reg [511:0] block;
    wire [255:0] digest;
    wire digest_valid;

    sha256_engine uut (.*);

    always #5 clk = ~clk;

    initial begin
        clk=0; rst_n=0; start=0; block_valid=0; last_block=0;
        #20 rst_n=1; start=1; #10; start=0;

        // "abc" 填充后:61626380 00000000 ... 00000018
        block = {32'h61626380, 32'h0, 32'h0, 32'h0,
                 32'h0, 32'h0, 32'h0, 32'h0,
                 32'h0, 32'h0, 32'h0, 32'h0,
                 32'h0, 32'h0, 32'h0, 32'h00000018};
        block_valid = 1; last_block = 1;
        #10; block_valid = 0;

        wait(digest_valid);
        $display("SHA-256(abc) = %064h", digest);
        $display("Expected    = ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad");
        $finish;
    end
endmodule

1. 验证 SHA-256("") = e3b0c44298fc1c14... 和 SHA-256("abc") 的正确性。

2. 实现一个支持流式输入的 SHA-256:每周期接收 32 位消息字,自动处理填充。

3. 计算双块消息的 SHA-256 哈希(消息长度 > 448 位)。

4. 优化:将 64 轮压缩改为展开 4 轮,减少控制开销,分析面积和吞吐量的变化。

🏆 成就解锁:完整哈希

你已构建完整的 SHA-256 哈希引擎,从消息填充到多块迭代压缩。SHA-256 是互联网安全的基石之一!

获得徽章:🔒 SHA256_ENGINEER

💡 扩展阅读与参考资源

🔧 实践环境搭建

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

# 安装 Verilator
sudo apt install verilator

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

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

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

📊 性能指标对比

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

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

📚 本课知识图谱

本课涉及的核心概念和技术关系:

💡 调试技巧

Verilog 仿真调试的常用方法:

// 调试示例
initial begin
    $dumpfile("sim.vcd");
    $dumpvars(0, uut);
end

// 断言验证
assert property (@(posedge clk) valid |-> data !== 'x)
    else $error("Invalid data when valid!");

🔧 Verilator 编译仿真完整流程

# 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 偏重面积和能效,服务器偏重吞吐量。