| // Copyright lowRISC contributors. |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| // SPDX-License-Identifier: Apache-2.0 |
| // |
| // SHA-256 algorithm |
| // |
| |
| module sha2 import hmac_pkg::*; ( |
| input clk_i, |
| input rst_ni, |
| |
| input wipe_secret, |
| input sha_word_t wipe_v, |
| |
| // FIFO read signal |
| input fifo_rvalid, |
| input sha_fifo_t fifo_rdata, |
| output logic fifo_rready, |
| |
| // Control signals |
| input sha_en, // If disabled, it clears internal content. |
| input hash_start, |
| input hash_process, |
| output logic hash_done, |
| |
| input [63:0] message_length, // bits but byte based |
| output sha_word_t [7:0] digest, |
| |
| output logic idle |
| ); |
| |
| localparam int unsigned RoundWidth = $clog2(NumRound); |
| |
| logic msg_feed_complete; |
| |
| logic shaf_rready; |
| sha_word_t shaf_rdata; |
| logic shaf_rvalid; |
| |
| logic [RoundWidth-1:0] round; |
| |
| logic [3:0] w_index; |
| sha_word_t [15:0] w; |
| |
| localparam sha_word_t ZeroWord = '0; |
| |
| // w, hash, digest update logic control signals |
| logic update_w_from_fifo, calculate_next_w; |
| logic init_hash, run_hash, complete_one_chunk; |
| logic update_digest, clear_digest; |
| |
| logic hash_done_next; // to meet the phase with digest value. |
| |
| sha_word_t [7:0] hash; // a,b,c,d,e,f,g,h |
| |
| // Fill up w |
| always_ff @(posedge clk_i or negedge rst_ni) begin : fill_w |
| if (!rst_ni) begin |
| w <= '0; |
| end else if (wipe_secret) begin |
| w <= w ^ {16{wipe_v}}; |
| end else if (!sha_en) begin |
| w <= '0; |
| end else if (!run_hash && update_w_from_fifo) begin |
| // this logic runs at the first stage of SHA. |
| w <= {shaf_rdata, w[15:1]}; |
| end else if (calculate_next_w) begin |
| w <= {calc_w(w[0], w[1], w[9], w[14]), w[15:1]}; |
| //end else if (run_hash && update_w_from_fifo) begin |
| // // This code runs when round is in [48, 63]. At this time, it reads from the fifo |
| // // to fill the register if available. If FIFO goes to empty, w_index doesn't increase |
| // // and it cannot reach 15. Then the sha engine doesn't start, which introduces latency. |
| // // |
| // // But in this case, still w should be shifted to feed SHA compress engine. Then |
| // // fifo_rdata should be inserted in the middle of w index. |
| // // w[64-round + w_index] <= fifo_rdata; |
| // for (int i = 0 ; i < 16 ; i++) begin |
| // if (i == (64 - round + w_index)) begin |
| // w[i] <= shaf_rdata; |
| // end else if (i == 15) begin |
| // w[i] <= '0; |
| // end else begin |
| // w[i] <= w[i+1]; |
| // end |
| // end |
| end else if (run_hash) begin |
| // Just shift-out. There's no incoming data |
| w <= {ZeroWord, w[15:1]}; |
| end |
| end : fill_w |
| |
| // Update engine |
| always_ff @(posedge clk_i or negedge rst_ni) begin : compress_round |
| if (!rst_ni) begin |
| hash <= '{default:'0}; |
| end else if (wipe_secret) begin |
| for (int i = 0 ; i < 8 ; i++) begin |
| hash[i] <= hash[i] ^ wipe_v; |
| end |
| end else if (init_hash) begin |
| hash <= digest; |
| end else if (run_hash) begin |
| hash <= compress( w[0], CubicRootPrime[round], hash); |
| end |
| end : compress_round |
| |
| // Digest |
| always_ff @(posedge clk_i or negedge rst_ni) begin |
| if (!rst_ni) begin |
| digest <= '{default: '0}; |
| end else if (wipe_secret) begin |
| for (int i = 0 ; i < 8 ; i++) begin |
| digest[i] <= digest[i] ^ wipe_v; |
| end |
| end else if (hash_start) begin |
| for (int i = 0 ; i < 8 ; i++) begin |
| digest[i] <= InitHash[i]; |
| end |
| end else if (!sha_en || clear_digest) begin |
| digest <= '0; |
| end else if (update_digest) begin |
| for (int i = 0 ; i < 8 ; i++) begin |
| digest[i] <= digest[i] + hash[i]; |
| end |
| end |
| end |
| |
| // round |
| always_ff @(posedge clk_i or negedge rst_ni) begin |
| if (!rst_ni) begin |
| round <= '0; |
| end else if (!sha_en) begin |
| round <= '0; |
| end else if (run_hash) begin |
| if (round == RoundWidth'(unsigned'(NumRound-1))) begin |
| round <= '0; |
| end else begin |
| round <= round + 1; |
| end |
| end |
| end |
| |
| // w_index |
| always_ff @(posedge clk_i or negedge rst_ni) begin |
| if (!rst_ni) begin |
| w_index <= '0; |
| end else if (!sha_en) begin |
| w_index <= '0; |
| end else if (update_w_from_fifo) begin |
| w_index <= w_index + 1; |
| end |
| end |
| |
| assign shaf_rready = update_w_from_fifo; |
| |
| always_ff @(posedge clk_i or negedge rst_ni) begin |
| if (!rst_ni) hash_done <= 1'b0; |
| else hash_done <= hash_done_next; |
| end |
| |
| typedef enum logic [1:0] { |
| FifoIdle, |
| FifoLoadFromFifo, |
| FifoWait |
| } fifoctl_state_e; |
| |
| fifoctl_state_e fifo_st_q, fifo_st_d; |
| |
| always_ff @(posedge clk_i or negedge rst_ni) begin |
| if (!rst_ni) begin |
| fifo_st_q <= FifoIdle; |
| end else begin |
| fifo_st_q <= fifo_st_d; |
| end |
| end |
| |
| always_comb begin |
| fifo_st_d = FifoIdle; |
| update_w_from_fifo = 1'b0; |
| hash_done_next = 1'b0; |
| |
| unique case (fifo_st_q) |
| FifoIdle: begin |
| if (hash_start) begin |
| fifo_st_d = FifoLoadFromFifo; |
| end else begin |
| fifo_st_d = FifoIdle; |
| end |
| end |
| |
| FifoLoadFromFifo: begin |
| if (!sha_en) begin |
| fifo_st_d = FifoIdle; |
| update_w_from_fifo = 1'b0; |
| end else if (!shaf_rvalid) begin |
| // Wait until it is filled |
| fifo_st_d = FifoLoadFromFifo; |
| update_w_from_fifo = 1'b0; |
| end else if (w_index == 4'd 15) begin |
| fifo_st_d = FifoWait; |
| update_w_from_fifo = 1'b1; |
| end else begin |
| fifo_st_d = FifoLoadFromFifo; |
| update_w_from_fifo = 1'b1; |
| end |
| end |
| |
| FifoWait: begin |
| // Wait until next fetch begins (begin at round == 48)a |
| if (msg_feed_complete && complete_one_chunk) begin |
| fifo_st_d = FifoIdle; |
| |
| hash_done_next = 1'b1; |
| end else if (complete_one_chunk) begin |
| fifo_st_d = FifoLoadFromFifo; |
| end else begin |
| fifo_st_d = FifoWait; |
| end |
| end |
| |
| default: begin |
| fifo_st_d = FifoIdle; |
| end |
| endcase |
| end |
| |
| // SHA control |
| typedef enum logic [1:0] { |
| ShaIdle, |
| ShaCompress, |
| ShaUpdateDigest |
| } sha_st_t; |
| |
| sha_st_t sha_st_q, sha_st_d; |
| |
| always_ff @(posedge clk_i or negedge rst_ni) begin |
| if (!rst_ni) begin |
| sha_st_q <= ShaIdle; |
| end else begin |
| sha_st_q <= sha_st_d; |
| end |
| end |
| |
| assign clear_digest = hash_start; |
| |
| always_comb begin |
| update_digest = 1'b0; |
| calculate_next_w = 1'b0; |
| |
| init_hash = 1'b0; |
| run_hash = 1'b0; |
| |
| unique case (sha_st_q) |
| ShaIdle: begin |
| if (fifo_st_q == FifoWait) begin |
| init_hash = 1'b1; |
| sha_st_d = ShaCompress; |
| end else begin |
| sha_st_d = ShaIdle; |
| end |
| end |
| |
| ShaCompress: begin |
| run_hash = 1'b1; |
| |
| if (round < 48) begin |
| calculate_next_w = 1'b1; |
| end |
| |
| if (complete_one_chunk) begin |
| sha_st_d = ShaUpdateDigest; |
| end else begin |
| sha_st_d = ShaCompress; |
| end |
| end |
| |
| ShaUpdateDigest: begin |
| update_digest = 1'b1; |
| if (fifo_st_q == FifoWait) begin |
| init_hash = 1'b1; |
| sha_st_d = ShaCompress; |
| end else begin |
| sha_st_d = ShaIdle; |
| end |
| end |
| |
| default: begin |
| sha_st_d = ShaIdle; |
| end |
| endcase |
| end |
| |
| // complete_one_chunk |
| assign complete_one_chunk = (round == 6'd63); |
| |
| sha2_pad u_pad ( |
| .clk_i, |
| .rst_ni, |
| |
| .wipe_secret, |
| .wipe_v, |
| |
| .fifo_rvalid, |
| .fifo_rdata, |
| .fifo_rready, |
| |
| .shaf_rvalid, |
| .shaf_rdata, |
| .shaf_rready, |
| |
| .sha_en, |
| .hash_start, |
| .hash_process, |
| .hash_done, |
| |
| .message_length, |
| .msg_feed_complete |
| ); |
| |
| // Idle |
| assign idle = (fifo_st_q == FifoIdle) && (sha_st_q == ShaIdle) && !hash_start; |
| |
| endmodule : sha2 |