[otp_ctrl] Improve security of Merkle-Damgard digest
The one-way function used in the Merkle-Damgard digest function was
previously constructed with a PRESENT cipher primitive only.
This commit improves this one way function by using the Davies-Meyer
construction, which additionally chains the digest states with an
XOR operation.
Signed-off-by: Michael Schaffner <msf@opentitan.org>
diff --git a/hw/ip/otp_ctrl/dv/env/otp_ctrl_scoreboard.sv b/hw/ip/otp_ctrl/dv/env/otp_ctrl_scoreboard.sv
index c2045ad..96be395 100644
--- a/hw/ip/otp_ctrl/dv/env/otp_ctrl_scoreboard.sv
+++ b/hw/ip/otp_ctrl/dv/env/otp_ctrl_scoreboard.sv
@@ -354,7 +354,8 @@
// Trigger 32 round of PRESENT encrypt
crypto_dpi_present_pkg::sv_dpi_present_encrypt(input_data, key, key_size_80, enc_array);
- digests[part_idx] = enc_array[NUM_ROUND-1];
+ // XOR the previous state into the digest result according to the Davies-Meyer scheme.
+ digests[part_idx] = enc_array[NUM_ROUND-1] ^ input_data;
end
// Last 32 round of digest is calculated with a digest constant
@@ -362,7 +363,8 @@
RndCnstDigestConstDefault[0],
key_size_80,
enc_array);
- digests[part_idx] = enc_array[NUM_ROUND-1];
+ // XOR the previous state into the digest result according to the Davies-Meyer scheme.
+ digests[part_idx] ^= enc_array[NUM_ROUND-1];
endfunction
// when secret data write into otp_array, it will be scrambled
@@ -395,10 +397,13 @@
bit [SCRAMBLE_KEY_SIZE-1:0] key,
bit [SCRAMBLE_KEY_SIZE-1:0] final_const);
bit [NUM_ROUND-1:0] [SCRAMBLE_DATA_SIZE-1:0] enc_array;
+ bit [SCRAMBLE_DATA_SIZE-1:0] intermediate_state;
crypto_dpi_present_pkg::sv_dpi_present_encrypt(data, key, key_size_80, enc_array);
- crypto_dpi_present_pkg::sv_dpi_present_encrypt(enc_array[NUM_ROUND-1], final_const,
+ // XOR the previous state into the digest result according to the Davies-Meyer scheme.
+ crypto_dpi_present_pkg::sv_dpi_present_encrypt(intermediate_state ^ data, final_const,
key_size_80, enc_array);
- present_encode_with_final_const = enc_array[NUM_ROUND-1];
+ // XOR the previous state into the digest result according to the Davies-Meyer scheme.
+ present_encode_with_final_const = enc_array[NUM_ROUND-1] ^ intermediate_state;
endfunction
function bit [TL_AW-1:0] get_normalized_dai_addr();
diff --git a/hw/ip/otp_ctrl/rtl/otp_ctrl.sv b/hw/ip/otp_ctrl/rtl/otp_ctrl.sv
index f3cfc65..d895e2f 100644
--- a/hw/ip/otp_ctrl/rtl/otp_ctrl.sv
+++ b/hw/ip/otp_ctrl/rtl/otp_ctrl.sv
@@ -619,6 +619,7 @@
// See also https://docs.opentitan.org/hw/ip/otp_ctrl/doc/index.html#block-diagram for details.
typedef struct packed {
otp_scrmbl_cmd_e cmd;
+ digest_mode_e mode;
logic [ConstSelWidth-1:0] sel;
logic [ScrmblBlockWidth-1:0] data;
logic valid;
@@ -669,6 +670,7 @@
.clk_i,
.rst_ni,
.cmd_i ( scrmbl_req_bundle.cmd ),
+ .mode_i ( scrmbl_req_bundle.mode ),
.sel_i ( scrmbl_req_bundle.sel ),
.data_i ( scrmbl_req_bundle.data ),
.valid_i ( scrmbl_req_bundle.valid ),
@@ -760,6 +762,7 @@
.scrmbl_mtx_req_o ( part_scrmbl_mtx_req[DaiIdx] ),
.scrmbl_mtx_gnt_i ( part_scrmbl_mtx_gnt[DaiIdx] ),
.scrmbl_cmd_o ( part_scrmbl_req_bundle[DaiIdx].cmd ),
+ .scrmbl_mode_o ( part_scrmbl_req_bundle[DaiIdx].mode ),
.scrmbl_sel_o ( part_scrmbl_req_bundle[DaiIdx].sel ),
.scrmbl_data_o ( part_scrmbl_req_bundle[DaiIdx].data ),
.scrmbl_valid_o ( part_scrmbl_req_bundle[DaiIdx].valid ),
@@ -839,6 +842,7 @@
.scrmbl_mtx_req_o ( part_scrmbl_mtx_req[KdiIdx] ),
.scrmbl_mtx_gnt_i ( part_scrmbl_mtx_gnt[KdiIdx] ),
.scrmbl_cmd_o ( part_scrmbl_req_bundle[KdiIdx].cmd ),
+ .scrmbl_mode_o ( part_scrmbl_req_bundle[KdiIdx].mode ),
.scrmbl_sel_o ( part_scrmbl_req_bundle[KdiIdx].sel ),
.scrmbl_data_o ( part_scrmbl_req_bundle[KdiIdx].data ),
.scrmbl_valid_o ( part_scrmbl_req_bundle[KdiIdx].valid ),
@@ -945,6 +949,7 @@
.scrmbl_mtx_req_o ( part_scrmbl_mtx_req[k] ),
.scrmbl_mtx_gnt_i ( part_scrmbl_mtx_gnt[k] ),
.scrmbl_cmd_o ( part_scrmbl_req_bundle[k].cmd ),
+ .scrmbl_mode_o ( part_scrmbl_req_bundle[k].mode ),
.scrmbl_sel_o ( part_scrmbl_req_bundle[k].sel ),
.scrmbl_data_o ( part_scrmbl_req_bundle[k].data ),
.scrmbl_valid_o ( part_scrmbl_req_bundle[k].valid ),
@@ -989,15 +994,17 @@
.otp_rvalid_i ( part_otp_rvalid[k] ),
.otp_rdata_i ( part_otp_rdata ),
.otp_err_i ( part_otp_err ),
- .scrmbl_mtx_req_o ( part_scrmbl_mtx_req[k] ),
- .scrmbl_mtx_gnt_i ( part_scrmbl_mtx_gnt[k] ),
- .scrmbl_cmd_o ( part_scrmbl_req_bundle[k].cmd ),
- .scrmbl_sel_o ( part_scrmbl_req_bundle[k].sel ),
- .scrmbl_data_o ( part_scrmbl_req_bundle[k].data ),
- .scrmbl_valid_o ( part_scrmbl_req_bundle[k].valid ),
- .scrmbl_ready_i ( part_scrmbl_req_ready[k] ),
- .scrmbl_valid_i ( part_scrmbl_rsp_valid[k] ),
- .scrmbl_data_i ( part_scrmbl_rsp_data )
+ // The LC partition does not need any scrambling features.
+ .scrmbl_mtx_req_o ( ),
+ .scrmbl_mtx_gnt_i ( 1'b0 ),
+ .scrmbl_cmd_o ( ),
+ .scrmbl_mode_o ( ),
+ .scrmbl_sel_o ( ),
+ .scrmbl_data_o ( ),
+ .scrmbl_valid_o ( ),
+ .scrmbl_ready_i ( 1'b0 ),
+ .scrmbl_valid_i ( 1'b0 ),
+ .scrmbl_data_i ( '0 )
);
// Buffered partitions are not accessible via the TL-UL window.
@@ -1008,6 +1015,16 @@
assign part_tlul_rvalid[k] = 1'b0;
assign part_tlul_rdata[k] = '0;
+ // Tie off unused connections.
+ assign part_scrmbl_mtx_req[k] = '0;
+ assign part_scrmbl_req_bundle[k] = '0;
+
+ // This stops lint from complaining about unused signals.
+ logic unused_part_scrmbl_sigs;
+ assign unused_part_scrmbl_sigs = ^{part_scrmbl_mtx_gnt[k],
+ part_scrmbl_req_ready[k],
+ part_scrmbl_rsp_valid[k]};
+
end else begin : gen_invalid
// This is invalid and should break elaboration
assert_static_in_generate_invalid assert_static_in_generate_invalid();
diff --git a/hw/ip/otp_ctrl/rtl/otp_ctrl_dai.sv b/hw/ip/otp_ctrl/rtl/otp_ctrl_dai.sv
index b8f863b..089880b 100644
--- a/hw/ip/otp_ctrl/rtl/otp_ctrl_dai.sv
+++ b/hw/ip/otp_ctrl/rtl/otp_ctrl_dai.sv
@@ -52,6 +52,7 @@
input scrmbl_mtx_gnt_i,
// Scrambling datapath interface
output otp_scrmbl_cmd_e scrmbl_cmd_o,
+ output digest_mode_e scrmbl_mode_o,
output logic [ConstSelWidth-1:0] scrmbl_sel_o,
output logic [ScrmblBlockWidth-1:0] scrmbl_data_o,
output logic scrmbl_valid_o,
@@ -171,6 +172,7 @@
// Scrambling datapath
scrmbl_cmd_o = LoadShadow;
scrmbl_sel_o = CnstyDigest;
+ scrmbl_mode_o = StandardMode;
scrmbl_valid_o = 1'b0;
// Counter
@@ -467,7 +469,6 @@
scrmbl_mtx_req_o = 1'b1;
scrmbl_valid_o = 1'b1;
// Need to reset the digest state and set digest mode to "standard".
- scrmbl_sel_o = StandardMode;
scrmbl_cmd_o = DigestInit;
if (scrmbl_mtx_gnt_i && scrmbl_ready_i) begin
state_d = DigReadSt;
@@ -744,6 +745,7 @@
`ASSERT_KNOWN(OtpAddrKnown_A, otp_addr_o)
`ASSERT_KNOWN(ScrmblMtxReqKnown_A, scrmbl_mtx_req_o)
`ASSERT_KNOWN(ScrmblCmdKnown_A, scrmbl_cmd_o)
+ `ASSERT_KNOWN(ScrmblModeKnown_A, scrmbl_mode_o)
`ASSERT_KNOWN(ScrmblSelKnown_A, scrmbl_sel_o)
`ASSERT_KNOWN(ScrmblDataKnown_A, scrmbl_data_o)
`ASSERT_KNOWN(ScrmblValidKnown_A, scrmbl_valid_o)
diff --git a/hw/ip/otp_ctrl/rtl/otp_ctrl_kdi.sv b/hw/ip/otp_ctrl/rtl/otp_ctrl_kdi.sv
index 4180f9f..2f9746f 100644
--- a/hw/ip/otp_ctrl/rtl/otp_ctrl_kdi.sv
+++ b/hw/ip/otp_ctrl/rtl/otp_ctrl_kdi.sv
@@ -44,6 +44,7 @@
input scrmbl_mtx_gnt_i,
// Scrambling datapath interface
output otp_scrmbl_cmd_e scrmbl_cmd_o,
+ output digest_mode_e scrmbl_mode_o,
output logic [ConstSelWidth-1:0] scrmbl_sel_o,
output logic [ScrmblBlockWidth-1:0] scrmbl_data_o,
output logic scrmbl_valid_o,
@@ -306,6 +307,8 @@
scrmbl_mtx_req_o = 1'b0;
scrmbl_sel_o = req_bundle.digest_sel;
scrmbl_cmd_o = LoadShadow;
+ scrmbl_mode_o = StandardMode;
+
scrmbl_valid_o = 1'b0;
// Request acknowledgement
@@ -334,7 +337,6 @@
scrmbl_mtx_req_o = 1'b1;
scrmbl_valid_o = 1'b1;
// Need to reset the digest state and set digest mode to "standard".
- scrmbl_sel_o = StandardMode;
scrmbl_cmd_o = DigestInit;
if (scrmbl_mtx_gnt_i && scrmbl_ready_i) begin
state_d = DigLoadSt;
@@ -514,6 +516,7 @@
`ASSERT_KNOWN(OtbnOtpKeyRspKnown_A, otbn_otp_key_o)
`ASSERT_KNOWN(ScrmblMtxReqKnown_A, scrmbl_mtx_req_o)
`ASSERT_KNOWN(ScrmblCmdKnown_A, scrmbl_cmd_o)
+ `ASSERT_KNOWN(ScrmblModeKnown_A, scrmbl_mode_o)
`ASSERT_KNOWN(ScrmblSelKnown_A, scrmbl_sel_o)
`ASSERT_KNOWN(ScrmblDataKnown_A, scrmbl_data_o)
`ASSERT_KNOWN(ScrmblValidKnown_A, scrmbl_valid_o)
diff --git a/hw/ip/otp_ctrl/rtl/otp_ctrl_part_buf.sv b/hw/ip/otp_ctrl/rtl/otp_ctrl_part_buf.sv
index a4a8075..e7e6a67 100644
--- a/hw/ip/otp_ctrl/rtl/otp_ctrl_part_buf.sv
+++ b/hw/ip/otp_ctrl/rtl/otp_ctrl_part_buf.sv
@@ -54,6 +54,7 @@
input scrmbl_mtx_gnt_i,
// Scrambling datapath interface
output otp_scrmbl_cmd_e scrmbl_cmd_o,
+ output digest_mode_e scrmbl_mode_o,
output logic [ConstSelWidth-1:0] scrmbl_sel_o,
output logic [ScrmblBlockWidth-1:0] scrmbl_data_o,
output logic scrmbl_valid_o,
@@ -167,6 +168,7 @@
// Scrambling datapath
scrmbl_cmd_o = LoadShadow;
scrmbl_sel_o = CnstyDigest;
+ scrmbl_mode_o = StandardMode;
scrmbl_valid_o = 1'b0;
// Counter
@@ -357,14 +359,14 @@
// mode if this partition is scrambled.
scrmbl_cmd_o = DigestInit;
if (Info.secret) begin
- scrmbl_sel_o = ChainedMode;
+ scrmbl_mode_o = ChainedMode;
if (scrmbl_mtx_gnt_i && scrmbl_ready_i) begin
state_d = IntegScrSt;
end
// If this partition is not scrambled, we can just directly
// jump to the digest state.
end else begin
- scrmbl_sel_o = StandardMode;
+ scrmbl_mode_o = StandardMode;
if (scrmbl_mtx_gnt_i && scrmbl_ready_i) begin
state_d = IntegDigSt;
end
@@ -652,6 +654,7 @@
`ASSERT_KNOWN(OtpAddrKnown_A, otp_addr_o)
`ASSERT_KNOWN(ScrmblMtxReqKnown_A, scrmbl_mtx_req_o)
`ASSERT_KNOWN(ScrmblCmdKnown_A, scrmbl_cmd_o)
+ `ASSERT_KNOWN(ScrmblModeKnown_A, scrmbl_mode_o)
`ASSERT_KNOWN(ScrmblSelKnown_A, scrmbl_sel_o)
`ASSERT_KNOWN(ScrmblDataKnown_A, scrmbl_data_o)
`ASSERT_KNOWN(ScrmblValidKnown_A, scrmbl_valid_o)
diff --git a/hw/ip/otp_ctrl/rtl/otp_ctrl_scrmbl.sv b/hw/ip/otp_ctrl/rtl/otp_ctrl_scrmbl.sv
index 2f3e2cb..07337b4 100644
--- a/hw/ip/otp_ctrl/rtl/otp_ctrl_scrmbl.sv
+++ b/hw/ip/otp_ctrl/rtl/otp_ctrl_scrmbl.sv
@@ -22,9 +22,10 @@
// state would be lost.
//
// The scrambling datapath is arranged such that it can also be used for calculating a digest using
-// the encryption primitive in a Merkle-Damgard construction. Note however that this makes the
-// digest block size 128bit wide, since the Merkle-Damgard construction leverages the cipher key
-// input to ingest data.
+// the encryption primitive in a Merkle-Damgard construction. To that end, the PRESENT block cipher
+// is turned into a one way function according to the Davies-Meyer scheme. Note however that this
+// makes the digest block size 128bit wide, since the Merkle-Damgard construction leverages the
+// cipher key input to ingest data.
//
// The scrambling datapath exposes a few simple commands and the FSM hides the complexity
// of steering the appropriate muxes and keeping track of the cipher rounds. These commands are
@@ -44,10 +45,10 @@
// into the shadow register.
//
// DigestInit: This ensures that the digest initialization vector (IV) is selected upon the next
-// call of the Digest command. Also, sel_i can be used to set the digest mode. If sel_i
-// is set to "StandardMode", the data to be digested has to be provided via data_i and
-// LoadShadow. If sel_i is set to "ChainedMode", the digest input is formed by
-// concatenating the results of the revious two encryption commands.
+// call of the Digest command. Also, mode_i can be used to set the digest mode. If
+// mode_i is set to "StandardMode", the data to be digested has to be provided via
+// data_i and LoadShadow. If mode_i is set to "ChainedMode", the digest input is formed
+// by concatenating the results of the revious two encryption commands.
//
// Digest: In "StandardMode", this command concatenates the data input supplied via data_i with
// the shadow register in order to form a 128bit block ({data_i, data_shadow_q}). This block
@@ -62,6 +63,7 @@
// References: - https://docs.opentitan.org/hw/ip/otp_ctrl/doc/index.html#design-details
// - https://docs.opentitan.org/hw/ip/prim/doc/prim_present/
// - https://en.wikipedia.org/wiki/Merkle-Damgard_construction
+// - https://en.wikipedia.org/wiki/One-way_compression_function#Davies%E2%80%93Meyer
// - https://en.wikipedia.org/wiki/PRESENT
// - http://www.lightweightcrypto.org/present/present_ches2007.pdf
//
@@ -76,6 +78,7 @@
input rst_ni,
// input data and command
input otp_scrmbl_cmd_e cmd_i,
+ input digest_mode_e mode_i,
input [ConstSelWidth-1:0] sel_i,
input [ScrmblBlockWidth-1:0] data_i,
input valid_i,
@@ -115,7 +118,7 @@
logic [ScrmblKeyWidth-1:0] key_state_d, key_state_q;
logic [ScrmblBlockWidth-1:0] data_state_d, data_state_q, data_shadow_q;
logic [ScrmblBlockWidth-1:0] digest_state_d, digest_state_q;
- logic [ScrmblBlockWidth-1:0] enc_data_out, dec_data_out;
+ logic [ScrmblBlockWidth-1:0] enc_data_out, enc_data_out_xor, dec_data_out;
logic [ScrmblKeyWidth-1:0] dec_key_out, enc_key_out;
logic [4:0] dec_idx_out, enc_idx_out;
logic [ScrmblKeyWidth-1:0] otp_digest_const_mux, otp_enc_key_mux, otp_dec_key_mux;
@@ -124,7 +127,7 @@
typedef enum logic [2:0] {SelEncDataOut,
SelDecDataOut,
SelDigestState,
- SelDigestIV,
+ SelEncDataOutXor,
SelDataInput} data_state_sel_e;
typedef enum logic [2:0] {SelDecKeyOut,
@@ -135,31 +138,31 @@
SelDigestInput,
SelDigestChained} key_state_sel_e;
+ logic digest_init;
data_state_sel_e data_state_sel;
key_state_sel_e key_state_sel;
logic data_state_en, data_shadow_copy, data_shadow_load, digest_state_en, key_state_en;
- logic [ConstSelWidth-1:0] sel_d, sel_q;
digest_mode_e digest_mode_d, digest_mode_q;
- assign otp_enc_key_mux = (sel_d < NumScrmblKeys) ?
- RndCnstKey[sel_d[vbits(NumScrmblKeys)-1:0]] : '0;
- assign otp_dec_key_mux = (sel_d < NumScrmblKeys) ?
- otp_dec_key_lut[sel_d[vbits(NumScrmblKeys)-1:0]] : '0;
- assign otp_digest_const_mux = (sel_d < NumDigestSets) ?
- RndCnstDigestConst[sel_d[vbits(NumDigestSets)-1:0]] : '0;
- assign otp_digest_iv_mux = (sel_d < NumDigestSets) ?
- RndCnstDigestIV[sel_d[vbits(NumDigestSets)-1:0]] : '0;
+ assign otp_enc_key_mux = (sel_i < NumScrmblKeys) ?
+ RndCnstKey[sel_i[vbits(NumScrmblKeys)-1:0]] : '0;
+ assign otp_dec_key_mux = (sel_i < NumScrmblKeys) ?
+ otp_dec_key_lut[sel_i[vbits(NumScrmblKeys)-1:0]] : '0;
+ assign otp_digest_const_mux = (sel_i < NumDigestSets) ?
+ RndCnstDigestConst[sel_i[vbits(NumDigestSets)-1:0]] : '0;
+ assign otp_digest_iv_mux = (sel_i < NumDigestSets) ?
+ RndCnstDigestIV[sel_i[vbits(NumDigestSets)-1:0]] : '0;
// Make sure we always select a valid key / digest constant.
- `ASSERT(CheckNumEncKeys_A, key_state_sel == SelEncKeyInit |-> sel_d < NumScrmblKeys)
- `ASSERT(CheckNumDecKeys_A, key_state_sel == SelDecKeyInit |-> sel_d < NumScrmblKeys)
- `ASSERT(CheckNumDigest1_A, key_state_sel == SelDigestConst |-> sel_d < NumDigestSets)
+ `ASSERT(CheckNumEncKeys_A, key_state_sel == SelEncKeyInit |-> sel_i < NumScrmblKeys)
+ `ASSERT(CheckNumDecKeys_A, key_state_sel == SelDecKeyInit |-> sel_i < NumScrmblKeys)
+ `ASSERT(CheckNumDigest1_A, key_state_sel == SelDigestConst |-> sel_i < NumDigestSets)
- assign data_state_d = (data_state_sel == SelEncDataOut) ? enc_data_out :
- (data_state_sel == SelDecDataOut) ? dec_data_out :
- (data_state_sel == SelDigestState) ? digest_state_q :
- (data_state_sel == SelDigestIV) ? otp_digest_iv_mux :
- data_i;
+ assign data_state_d = (data_state_sel == SelEncDataOut) ? enc_data_out :
+ (data_state_sel == SelDecDataOut) ? dec_data_out :
+ (data_state_sel == SelDigestState) ? digest_state_q :
+ (data_state_sel == SelEncDataOutXor) ? enc_data_out_xor :
+ data_i;
assign key_state_d = (key_state_sel == SelDecKeyOut) ? dec_key_out :
(key_state_sel == SelEncKeyOut) ? enc_key_out :
@@ -175,7 +178,9 @@
(key_state_sel == SelDecKeyInit) ? unsigned'(5'(NumPresentRounds)) :
5'd1;
- assign digest_state_d = enc_data_out;
+ // The XOR is for the Davies-Mayer one-way function construction.
+ assign enc_data_out_xor = enc_data_out ^ digest_state_q;
+ assign digest_state_d = (digest_init) ? otp_digest_iv_mux : enc_data_out_xor;
assign data_o = data_state_q;
@@ -214,7 +219,6 @@
logic [CntWidth-1:0] cnt_d, cnt_q;
logic cnt_clr, cnt_en;
logic valid_d, valid_q;
- logic is_first_d, is_first_q;
assign valid_o = valid_q;
@@ -224,11 +228,10 @@
always_comb begin : p_fsm
state_d = state_q;
- is_first_d = is_first_q;
- sel_d = sel_q;
digest_mode_d = digest_mode_q;
data_state_sel = SelDataInput;
key_state_sel = SelDigestInput;
+ digest_init = 1'b0;
data_state_en = 1'b0;
data_shadow_copy = 1'b0;
data_shadow_load = 1'b0;
@@ -249,21 +252,18 @@
ready_o = 1'b1;
if (valid_i) begin
- sel_d = sel_q;
unique case (cmd_i)
Decrypt: begin
state_d = DecryptSt;
key_state_sel = SelDecKeyInit;
data_state_en = 1'b1;
key_state_en = 1'b1;
- sel_d = sel_i;
end
Encrypt: begin
state_d = EncryptSt;
key_state_sel = SelEncKeyInit;
data_state_en = 1'b1;
key_state_en = 1'b1;
- sel_d = sel_i;
end
LoadShadow: begin
if (digest_mode_q == ChainedMode) begin
@@ -274,26 +274,23 @@
end
Digest: begin
state_d = DigestSt;
- data_state_sel = (is_first_q) ? SelDigestIV : SelDigestState;
+ data_state_sel = SelDigestState;
key_state_sel = (digest_mode_q == ChainedMode) ? SelDigestChained : SelDigestInput;
data_state_en = 1'b1;
key_state_en = 1'b1;
- is_first_d = 1'b0;
- sel_d = sel_i;
end
DigestInit: begin
- digest_mode_d = digest_mode_e'(sel_i);
- is_first_d = 1'b1;
+ digest_mode_d = mode_i;
+ digest_init = 1'b1;
+ digest_state_en = 1'b1;
end
- DigestFinalize: begin
+ DigestFinalize: begin
state_d = DigestSt;
- data_state_sel = (is_first_q) ? SelDigestIV : SelDigestState;
+ data_state_sel = SelDigestState;
key_state_sel = SelDigestConst;
data_state_en = 1'b1;
key_state_en = 1'b1;
- is_first_d = 1'b0;
digest_mode_d = StandardMode;
- sel_d = sel_i;
end
default: ; // ignore
endcase // cmd_i
@@ -337,6 +334,8 @@
if (cnt_q == NumPresentRounds-1) begin
state_d = IdleSt;
valid_d = 1'b1;
+ // Apply XOR for Davies-Meyer construction.
+ data_state_sel = SelEncDataOutXor;
// Backup digest state for next round of updates. We can't keep this state in the
// data state register as a digest may be calculated together with encryption
// operations in an interleaved way.
@@ -422,14 +421,10 @@
data_shadow_q <= '0;
digest_state_q <= '0;
valid_q <= 1'b0;
- is_first_q <= 1'b1;
- sel_q <= '0;
digest_mode_q <= StandardMode;
end else begin
cnt_q <= cnt_d;
valid_q <= valid_d;
- is_first_q <= is_first_d;
- sel_q <= sel_d;
digest_mode_q <= digest_mode_d;
// enable regs
diff --git a/hw/ip/otp_ctrl/rtl/otp_ctrl_token_const.sv b/hw/ip/otp_ctrl/rtl/otp_ctrl_token_const.sv
index a4b6f6a..f4fa6cb 100644
--- a/hw/ip/otp_ctrl/rtl/otp_ctrl_token_const.sv
+++ b/hw/ip/otp_ctrl/rtl/otp_ctrl_token_const.sv
@@ -37,6 +37,8 @@
// Each hash takes four invocations, see diagram c) on
// https://docs.opentitan.org/hw/ip/otp_ctrl/doc/index.html#scrambling-datapath
for (genvar k = 0; k < 4; k++) begin : gen_invocations
+ logic [ScrmblBlockWidth-1:0] next_state;
+
// This relies on constant propagation to
// statically precompute the hashed token values.
prim_present #(
@@ -46,10 +48,13 @@
.data_i ( state[j][k] ),
.key_i ( data[j][k%2] ),
.idx_i ( 5'h1 ),
- .data_o ( state[j][k+1] ),
+ .data_o ( next_state ),
.key_o ( ),
.idx_o ( )
);
+
+ // XOR in last state according to the Davies-Meyer scheme.
+ assign state[j][k+1] = next_state ^ state[j][k];
end
end