[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