Add HMAC-SHA256 calculation (ufs_rpmb_calc_hmac() over frame bytes
[228:511] per JESD220H Section 12.4.4.2) using QCryptoHmac, and handle
authentication key programming (0x0001 -> 0x0100 with zeroed Key/MAC
per JESD220H Section 12.4.7.3.1 Figure 12.5, rejecting duplicate key
writes with UFS_RPMB_RES_WRITE_FAILURE per Section 12.4.3.1) and write
counter read requests (0x0002 -> 0x0200, returning
UFS_RPMB_RES_NO_AUTH_KEY before a key is programmed and a signed
response frame thereafter).

Signed-off-by: Stanley Jhu <[email protected]>
---
 hw/ufs/rpmb.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 63 insertions(+)

diff --git a/hw/ufs/rpmb.c b/hw/ufs/rpmb.c
index 3032e0e098..edd4b91fd8 100644
--- a/hw/ufs/rpmb.c
+++ b/hw/ufs/rpmb.c
@@ -6,9 +6,34 @@
 
 #include "qemu/osdep.h"
 #include "qapi/error.h"
+#include "crypto/hmac.h"
 #include "trace.h"
 #include "ufs.h"
 
+/*
+ * JESD220H Section 12.4.4.2: the HMAC-SHA256 input message is the
+ * concatenation of bytes [228:511] (UFS_RPMB_HASH_OFFSET..511, 284 bytes)
+ * of each RPMB Message Data Frame in the order in which the frames are sent.
+ */
+static void ufs_rpmb_calc_hmac(const UfsRpmbRegion *r,
+                               const UfsRpmbFrame *frames,
+                               uint16_t count, uint8_t *mac)
+{
+    g_autoptr(QCryptoHmac) hmac = NULL;
+    size_t mac_len = UFS_RPMB_KEY_MAC_LEN;
+    g_autofree struct iovec *iov = g_new(struct iovec, count);
+    uint16_t i;
+
+    hmac = qcrypto_hmac_new(QCRYPTO_HASH_ALGO_SHA256, r->key,
+                            UFS_RPMB_KEY_MAC_LEN, &error_abort);
+    for (i = 0; i < count; i++) {
+        iov[i].iov_base = (uint8_t *)&frames[i] + UFS_RPMB_HASH_OFFSET;
+        iov[i].iov_len = UFS_RPMB_HASH_LEN;
+    }
+
+    qcrypto_hmac_bytesv(hmac, iov, count, &mac, &mac_len, &error_abort);
+}
+
 static void ufs_rpmb_handle_req(UfsHc *u, UfsRpmbRegion *r,
                                 const UfsRpmbFrame *frames,
                                 uint32_t nframes)
@@ -16,6 +41,7 @@ static void ufs_rpmb_handle_req(UfsHc *u, UfsRpmbRegion *r,
     const UfsRpmbFrame *req = &frames[0];
     uint16_t req_type = lduw_be_p(&req->req_resp);
     uint16_t result = UFS_RPMB_RES_GENERAL_FAILURE;
+    bool sign_resp = false;
 
     if (req_type == UFS_RPMB_REQ_READ_RESULT) {
         trace_ufs_rpmb_request(req_type, lduw_be_p(&r->resp.result));
@@ -26,6 +52,39 @@ static void ufs_rpmb_handle_req(UfsHc *u, UfsRpmbRegion *r,
     memset(&r->resp, 0, sizeof(r->resp));
 
     switch (req_type) {
+    case UFS_RPMB_REQ_PROGRAM_KEY:
+        /*
+         * JESD220H Section 12.4.7.3.1 Figure 12.5: the Authentication Key
+         * Programming Result Read response (0x0100) has bytes [196:227]
+         * (Key/MAC) and [484:499] (Nonce) set to 0...00h.
+         */
+        r->resp.req_resp = cpu_to_be16(UFS_RPMB_RESP_PROGRAM_KEY);
+        /*
+         * JESD220H Section 12.4.7.3.1 (Figure 12.5) and Section 12.4.3.1:
+         * programming an Authentication Key after one is already programmed
+         * fails with Write failure (0x0005).
+         */
+        if (r->key_set) {
+            result = UFS_RPMB_RES_WRITE_FAILURE;
+            break;
+        }
+        memcpy(r->key, req->key_mac, UFS_RPMB_KEY_MAC_LEN);
+        r->key_set = true;
+        result = UFS_RPMB_RES_OK;
+        break;
+
+    case UFS_RPMB_REQ_READ_COUNTER:
+        memcpy(r->resp.nonce, req->nonce, UFS_RPMB_NONCE_LEN);
+        r->resp.req_resp = cpu_to_be16(UFS_RPMB_RESP_READ_COUNTER);
+        if (!r->key_set) {
+            result = UFS_RPMB_RES_NO_AUTH_KEY;
+            break;
+        }
+        r->resp.write_counter = cpu_to_be32(r->write_counter);
+        result = UFS_RPMB_RES_OK;
+        sign_resp = true;
+        break;
+
     default:
         r->resp.req_resp = cpu_to_be16(req_type);
         result = UFS_RPMB_RES_GENERAL_FAILURE;
@@ -37,6 +96,10 @@ static void ufs_rpmb_handle_req(UfsHc *u, UfsRpmbRegion *r,
     }
     r->resp.result = cpu_to_be16(result);
 
+    if (sign_resp) {
+        ufs_rpmb_calc_hmac(r, &r->resp, 1, r->resp.key_mac);
+    }
+
     trace_ufs_rpmb_request(req_type, result);
 }
 
-- 
2.56.0.307.g5fe7bd680c-goog


Reply via email to