From ed6068ff364652e76b4b5ff86371ff2446cb5a9b Mon Sep 17 00:00:00 2001
From: Alex Jones <alex.jones@lowrisc.org>
Date: Wed, 19 Mar 2025 17:29:57 +0000
Subject: [PATCH] [ot] hw/opentitan: ot_hmac: Restore correct msg_length with
 HMAC_EN

HMAC can operate either with HMAC_EN, using HMAC algorithms, or without,
using standard SHA algorithms. The HMAC algorithms introduce additional
logic surrounding a key, and inner and outer padding. Relevant to this
commit is that when computing HMAC, we first process a block of inner
padding XORed with the key. This means that the message length reported
to software in the msg_length register diverges from the message length
reported by the tomcrypt cryptographic library's state. Specifically,
with HMAC_EN=1, it undercounts by a block.

This caused an error where, when saving and restoring context with
HMAC_EN=1, the hash length would be undercounted by a block and thus the
incorrect digest would be computed. This meant that save/restore and
streaming operations were not working properly with HMAC_EN.

This commit introduces the additional logic to fix this edge case.

Signed-off-by: Alex Jones <alex.jones@lowrisc.org>
---
 hw/opentitan/ot_hmac.c | 17 ++++++++++++++---
 1 file changed, 14 insertions(+), 3 deletions(-)

diff --git a/hw/opentitan/ot_hmac.c b/hw/opentitan/ot_hmac.c
index 3bfdb578da31..ba5cd002dad8 100644
--- a/hw/opentitan/ot_hmac.c
+++ b/hw/opentitan/ot_hmac.c
@@ -2,7 +2,7 @@
  * QEMU OpenTitan HMAC device
  *
  * Copyright (c) 2022-2024 Rivos, Inc.
- * Copyright (c) 2024 lowRISC contributors.
+ * Copyright (c) 2024-2025 lowRISC contributors.
  *
  * Author(s):
  *  Loïc Lefort <loic@rivosinc.com>
@@ -443,10 +443,21 @@ static void ot_hmac_writeback_digest_state(OtHMACState *s)
 
 static void ot_hmac_restore_context(OtHMACState *s)
 {
+    /*
+     * When restoring context, if HMAC is enabled we must add the block size to
+     * the message length to keep our cryptographic library consistent with the
+     * SW interface. This is because the extra block containing the key XORed
+     * with the inner pad is not included in the SW-visible message length.
+     */
+    unsigned msg_length = s->regs->msg_length;
+    if (s->regs->cfg & R_CFG_HMAC_EN_MASK) {
+        msg_length += ot_hmac_get_block_size_bytes(s) * 8u;
+    }
+
     switch (s->ctx->digest_size_started) {
     case HMAC_SHA2_256:
         s->ctx->state.sha256.curlen = 0;
-        s->ctx->state.sha256.length = s->regs->msg_length;
+        s->ctx->state.sha256.length = msg_length;
         for (unsigned idx = 0; idx < 8u; idx++) {
             LOAD32H(s->ctx->state.sha256.state[idx], s->regs->digest + idx);
         }
@@ -458,7 +469,7 @@ static void ot_hmac_restore_context(OtHMACState *s)
          */
     case HMAC_SHA2_512:
         s->ctx->state.sha512.curlen = 0;
-        s->ctx->state.sha512.length = s->regs->msg_length;
+        s->ctx->state.sha512.length = msg_length;
         for (unsigned idx = 0; idx < 8u; idx++) {
             LOAD64H(s->ctx->state.sha512.state[idx], s->regs->digest + 2 * idx);
         }