parent
1abb875397
commit
547a557a14
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
The SHA256 package file.
|
||||
|
||||
Copyright (C) 2019 Benjamin Devlin and Zcash Foundation
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package sha256_pkg;
|
||||
|
||||
// K values used during each round
|
||||
parameter [63:0][31:0] K = {
|
||||
32'hc67178f2,32'hbef9a3f7,32'ha4506ceb,32'h90befffa,32'h8cc70208,32'h84c87814,32'h78a5636f,32'h748f82ee,
|
||||
32'h682e6ff3,32'h5b9cca4f,32'h4ed8aa4a,32'h391c0cb3,32'h34b0bcb5,32'h2748774c,32'h1e376c08,32'h19a4c116,
|
||||
32'h106aa070,32'hf40e3585,32'hd6990624,32'hd192e819,32'hc76c51a3,32'hc24b8b70,32'ha81a664b,32'ha2bfe8a1,
|
||||
32'h92722c85,32'h81c2c92e,32'h766a0abb,32'h650a7354,32'h53380d13,32'h4d2c6dfc,32'h2e1b2138,32'h27b70a85,
|
||||
32'h14292967,32'h06ca6351,32'hd5a79147,32'hc6e00bf3,32'hbf597fc7,32'hb00327c8,32'ha831c66d,32'h983e5152,
|
||||
32'h76f988da,32'h5cb0a9dc,32'h4a7484aa,32'h2de92c6f,32'h240ca1cc,32'h0fc19dc6,32'hefbe4786,32'he49b69c1,
|
||||
32'hc19bf174,32'h9bdc06a7,32'h80deb1fe,32'h72be5d74,32'h550c7dc3,32'h243185be,32'h12835b01,32'hd807aa98,
|
||||
32'hab1c5ed5,32'h923f82a4,32'h59f111f1,32'h3956c25b,32'he9b5dba5,32'hb5c0fbcf,32'h71374491,32'h428a2f98
|
||||
};
|
||||
|
||||
// Initial values used for H
|
||||
parameter [7:0][31:0] IV = {
|
||||
32'h5be0cd19, 32'h1f83d9ab, 32'h9b05688c, 32'h510e527f,
|
||||
32'ha54ff53a, 32'h3c6ef372, 32'hbb67ae85, 32'h6a09e667
|
||||
};
|
||||
|
||||
endpackage
|
|
@ -0,0 +1,224 @@
|
|||
/*
|
||||
This is an implementation of the SHA256 hash algorithm.
|
||||
|
||||
Takes an AXI stream 512 bit interface. We back pressure the input
|
||||
if the message size is > 512 bits while we process the 64 rounds.
|
||||
|
||||
Copyright (C) 2019 Benjamin Devlin and Zcash Foundation
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
module sha256_top
|
||||
import sha256_pkg::*;
|
||||
(
|
||||
input i_clk, i_rst,
|
||||
|
||||
if_axi_stream.sink i_block, // Message stream to be hashed, must be 512 bits
|
||||
if_axi_stream.source o_hash // Resulting hash digest (32 bytes)
|
||||
);
|
||||
|
||||
localparam DAT_BYTS = 64;
|
||||
localparam DAT_BITS = DAT_BYTS*8; // Must be 512
|
||||
|
||||
logic [7:0][31:0] V; // Internal state, V[0] == A, V[7] == H
|
||||
logic [31:0] T1, T2;
|
||||
logic [15:0][31:0] W;
|
||||
logic [15:0] W_nxt;
|
||||
logic eop_l, padding_only, final_block;
|
||||
logic [63:0] bit_len;
|
||||
logic [$clog2(64)-1:0] rnd_cntr;
|
||||
|
||||
enum {SHA_IDLE = 0,
|
||||
SHA_ROUNDS = 1,
|
||||
SHA_UPDATE_HV = 2,
|
||||
SHA_FINAL = 3} sha_state;
|
||||
|
||||
// Used to make compression function easier to read
|
||||
localparam A = 0;
|
||||
localparam B = 1;
|
||||
localparam C = 2;
|
||||
localparam D = 3;
|
||||
localparam E = 4;
|
||||
localparam F = 5;
|
||||
localparam G = 6;
|
||||
localparam H = 7;
|
||||
|
||||
|
||||
always_ff @ (posedge i_clk) begin
|
||||
if (i_rst) begin
|
||||
sha_state <= SHA_IDLE;
|
||||
rnd_cntr <= 0;
|
||||
o_hash.reset_source();
|
||||
i_block.rdy <= 0;
|
||||
bit_len <= 0;
|
||||
W <= 0;
|
||||
eop_l <= 0;
|
||||
padding_only <= 0;
|
||||
final_block <= 0;
|
||||
end else begin
|
||||
o_hash.sop <= 1;
|
||||
o_hash.eop <= 1;
|
||||
case(sha_state)
|
||||
SHA_IDLE: begin
|
||||
update_HV(1);
|
||||
rnd_cntr <= 0;
|
||||
final_block <= 0;
|
||||
eop_l <= 0;
|
||||
padding_only <= 0;
|
||||
i_block.rdy <= 1;
|
||||
// As soon as we have one write on the input we can start
|
||||
if (i_block.rdy && i_block.val && i_block.sop) begin
|
||||
W <= i_block.dat;
|
||||
bit_len <= DAT_BITS;
|
||||
i_block.rdy <= 0;
|
||||
if (i_block.eop)
|
||||
msg_eop();
|
||||
sha_state <= SHA_ROUNDS;
|
||||
end
|
||||
end
|
||||
SHA_ROUNDS: begin
|
||||
for (int i = 0; i < 16; i++) W[i] <= W[i+i];
|
||||
W[15] <= W_nxt;
|
||||
compress();
|
||||
rnd_cntr <= rnd_cntr + 1;
|
||||
if (rnd_cntr == 62) begin
|
||||
rnd_cntr <= 0;
|
||||
i_block.rdy <= ~(final_block || padding_only);
|
||||
sha_state <= SHA_UPDATE_HV;
|
||||
end
|
||||
end
|
||||
SHA_UPDATE_HV: begin
|
||||
update_H(0);
|
||||
if (final_block) begin
|
||||
sha_state <= SHA_FINAL;
|
||||
i_block.rdy <= 0;
|
||||
end else if (padding_only) begin
|
||||
final_block <= 1;
|
||||
W <= 0;
|
||||
W[(64-8)*8 +: 64] <= bit_len;
|
||||
W[(bit_len/8) % 64 +: 8] <= 1;
|
||||
end else if (i_block.rdy && i_block.val) begin
|
||||
W <= i_block.dat;
|
||||
bit_len <= bit_len + DAT_BITS;
|
||||
if (i_block.eop) begin
|
||||
msg_eop();
|
||||
i_block.rdy <= 0;
|
||||
end
|
||||
sha_state <= SHA_ROUNDS;
|
||||
end
|
||||
end
|
||||
SHA_FINAL: begin
|
||||
o_hash.val <= 1;
|
||||
o_hash.dat <= H;
|
||||
if (o_hash.val && o_hash.rdy) begin
|
||||
o_hash.val <= 0;
|
||||
sha_state <= SHA_IDLE;
|
||||
end
|
||||
end
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
// On the msg .eop we need to make sure we add padding if there is space other
|
||||
// wise set a flag so next time we call we only add the padding block
|
||||
task msg_eop();
|
||||
|
||||
eop_l <= 1;
|
||||
bit_len <= bit_len + (i_block.mod == 0 ? DAT_BITS : i_block.mod*8);
|
||||
if (i_block.mod == 0 || i_block.mod > 64-9) begin
|
||||
padding_only <= 1; // Means we need one block extra with only len (and possibly the terminating 0x1)
|
||||
end else begin
|
||||
final_block <= 1; // This is the final block and includes padding
|
||||
end
|
||||
|
||||
for (int i = 0; i < 64; i++)
|
||||
M[i*8 +: 8] <= (i_block.mod == 0 || i < i_block.mod) ? i_block.dat[i*8 +: 8] : 0;
|
||||
|
||||
if (i_block.mod != 0)
|
||||
M[i_block.mod*8 +: 8] <= 1;
|
||||
|
||||
if (i_block.mod < 64-9)
|
||||
M[(64-8)*8 +: 64] <= bit_len + i_block.mod*8;
|
||||
|
||||
endtask
|
||||
|
||||
always_comb begin
|
||||
W_nxt = little_sig1(W[14]) + W[9] + little_sig0(W[1]) + W[0];
|
||||
end
|
||||
always_comb begin
|
||||
T1 = V[H] + big_sig1(V[E]) + ch(V[E], V[F], V[G]) + sha256_pkg::K[rnd_cntr] + W[0];
|
||||
T2 = big_sig0(V[A]) + maj(V[A], V[B], V[C]);
|
||||
end
|
||||
|
||||
task compress();
|
||||
V[H] <= V[G];
|
||||
V[G] <= V[F];
|
||||
V[F] <= V[E];
|
||||
V[E] <= V[D] + T1;
|
||||
V[D] <= V[C];
|
||||
V[C] <= V[B];
|
||||
V[B] <= V[A];
|
||||
V[A] <= T1 + T2;
|
||||
endtask
|
||||
|
||||
task update_HV(logic init);
|
||||
if (init) begin
|
||||
for (int i = 0; i < 8; i++) begin
|
||||
H[i] <= sha256_pkg::IV[i];
|
||||
V[i] <= sha256_pkg::IV[i];
|
||||
end
|
||||
end else begin
|
||||
for (int i = 0; i < 8; i++) begin
|
||||
H[i] <= H[i] + V[i];
|
||||
V[i] <= H[i] + V[i];
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
function [31:0] litte_sig0(input logic [31:0] in);
|
||||
litte_sig0 = {rotr(in, 7)} ^ {rotr(in, 18)} ^ {shr(in, 3)};
|
||||
endfunction
|
||||
|
||||
function [31:0] litte_sig1(input logic [31:0] in);
|
||||
litte_sig1 = {rotr(in, 17)} ^ {rotr(in, 19)} ^ {shr(in, 10)};
|
||||
endfunction
|
||||
|
||||
function [31:0] big_sig0(input logic [31:0] in);
|
||||
big_sig0 = {rotr(in, 2)} ^ {rotr(in, 13)} ^ {rotr(in, 22)};
|
||||
endfunction
|
||||
|
||||
function [31:0] big_sig1(input logic [31:0] in);
|
||||
big_sig1 = {rotr(in, 6)} ^ {rotr(in, 11)} ^ {rotr(in, 25)};
|
||||
endfunction
|
||||
|
||||
function [31:0] ch(input logic [31:0] x, y, z);
|
||||
ch = (x & y) ^ (~x & z);
|
||||
endfunction
|
||||
|
||||
function [31:0] maj(input logic [31:0] x, y, z);
|
||||
maj = (x & y) ^ (x & z) ^ (y & z);
|
||||
endfunction
|
||||
|
||||
function [31:0] rotr(input logic [31:0] in, input int bits);
|
||||
for (int i = 0; i < 32; i++) rotr[i] = in[(i+bits) % 32];
|
||||
endfunction
|
||||
|
||||
function [31:0] shr(input logic [31:0] in, input int bits);
|
||||
shr = 0;
|
||||
shr = in >> bits;
|
||||
endfunction
|
||||
|
||||
|
||||
endmodule
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
The SHA256 testbench.
|
||||
|
||||
Copyright (C) 2019 Benjamin Devlin and Zcash Foundation
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
module sha256_top_tb();
|
||||
|
||||
import common_pkg::*;
|
||||
|
||||
logic clk, rst;
|
||||
|
||||
if_axi_stream #(.DAT_BYTS(64)) i_block(clk);
|
||||
if_axi_stream #(.DAT_BYTS(32)) out_hash(clk);
|
||||
|
||||
initial begin
|
||||
rst = 0;
|
||||
#100ns rst = 1;
|
||||
#100ns rst = 0;
|
||||
end
|
||||
|
||||
initial begin
|
||||
clk = 0;
|
||||
forever #10ns clk = ~clk;
|
||||
end
|
||||
|
||||
sha256_top DUT (
|
||||
.i_clk ( clk ),
|
||||
.i_rst ( rst ),
|
||||
.i_block ( i_block ),
|
||||
.o_hash ( out_hash )
|
||||
);
|
||||
|
||||
|
||||
// This test runs the hash which is shown in the RFC, for "abc"
|
||||
task rfc_test();
|
||||
begin
|
||||
integer signed get_len;
|
||||
logic [common_pkg::MAX_SIM_BYTS*8-1:0] get_dat;
|
||||
$display("Running rfc_test...\n");
|
||||
expected = 'h239900d4ed8623b95a92f1dba88ad31895cc3345ded552c22d79ab2a39c5877dd1a2ffdb6fbb124bb7c45a68142f214ce9f6129fb697276a0d4d1c983fa580ba;
|
||||
i_block.put_stream("cba", 3);
|
||||
out_hash.get_stream(get_dat, get_len);
|
||||
common_pkg::compare_and_print(get_dat, expected);
|
||||
$display("rfc_test PASSED");
|
||||
end
|
||||
endtask
|
||||
|
||||
|
||||
// Main testbench calls
|
||||
initial begin
|
||||
i_block.reset_source();
|
||||
out_hash.rdy = 1;
|
||||
|
||||
#200ns;
|
||||
|
||||
rfc_test();
|
||||
|
||||
#10us $finish();
|
||||
|
||||
end
|
||||
|
||||
endmodule
|
Loading…
Reference in New Issue