Add ufstest_rpmb_key_counter() and shared helper ufs_rpmb_program_key()
to test write counter reads before key programming (0x0007), key
programming (0x0000 with zeroed Key/MAC per JESD220H Figure 12.5),
duplicate key programming rejection (0x0005), and authenticated write
counter reads with HMAC-SHA256 verification.

Signed-off-by: Stanley Jhu <[email protected]>
---
 tests/qtest/ufs-test.c | 105 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 105 insertions(+)

diff --git a/tests/qtest/ufs-test.c b/tests/qtest/ufs-test.c
index 9b43422d4b..9fce5fff82 100644
--- a/tests/qtest/ufs-test.c
+++ b/tests/qtest/ufs-test.c
@@ -3985,6 +3985,24 @@ static void ufs_rpmb_build_cdb(uint8_t 
cdb[UFS_CDB_SIZE], uint8_t opcode,
     stl_be_p(&cdb[6], len);
 }
 
+static void ufs_rpmb_compute_hmac(const uint8_t key[UFS_RPMB_KEY_MAC_LEN],
+                                  const UfsRpmbFrame *frames, size_t nframes,
+                                  uint8_t mac[UFS_RPMB_KEY_MAC_LEN])
+{
+    GHmac *hmac = g_hmac_new(G_CHECKSUM_SHA256, key, UFS_RPMB_KEY_MAC_LEN);
+    size_t mac_len = UFS_RPMB_KEY_MAC_LEN;
+    size_t i;
+
+    for (i = 0; i < nframes; i++) {
+        g_hmac_update(hmac,
+                      (const guchar *)&frames[i] + UFS_RPMB_HASH_OFFSET,
+                      UFS_RPMB_HASH_LEN);
+    }
+    g_hmac_get_digest(hmac, mac, &mac_len);
+    g_assert_cmpuint(mac_len, ==, UFS_RPMB_KEY_MAC_LEN);
+    g_hmac_unref(hmac);
+}
+
 static void ufs_rpmb_sec_out_frames(QUfs *ufs, uint8_t region,
                                     const UfsRpmbFrame *frames, size_t nframes,
                                     UtpUpiuRsp *rsp)
@@ -4258,6 +4276,91 @@ static void ufstest_rpmb_descriptor_cdb(void *obj, void 
*data,
     ufs_exit(ufs, alloc);
 }
 
+static const uint8_t ufs_rpmb_test_key[UFS_RPMB_KEY_MAC_LEN] = {
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+};
+
+static void ufs_rpmb_program_key(QUfs *ufs, uint8_t region,
+                                 const uint8_t *key)
+{
+    UfsRpmbFrame req = { 0 }, resp;
+    UtpUpiuRsp rsp_upiu;
+    uint8_t zero_mac[UFS_RPMB_KEY_MAC_LEN] = { 0 };
+
+    memcpy(req.key_mac, key, UFS_RPMB_KEY_MAC_LEN);
+    stw_be_p(&req.req_resp, UFS_RPMB_REQ_PROGRAM_KEY);
+    ufs_rpmb_sec_out_frames(ufs, region, &req, 1, &rsp_upiu);
+    g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, GOOD);
+
+    ufs_rpmb_read_result(ufs, region, &resp);
+    g_assert_cmpuint(lduw_be_p(&resp.req_resp), ==, UFS_RPMB_RESP_PROGRAM_KEY);
+    g_assert_cmpuint(lduw_be_p(&resp.result), ==, UFS_RPMB_RES_OK);
+    g_assert_cmpmem(resp.key_mac, UFS_RPMB_KEY_MAC_LEN,
+                    zero_mac, UFS_RPMB_KEY_MAC_LEN);
+}
+
+static void ufstest_rpmb_key_counter(void *obj, void *data,
+                                     QGuestAllocator *alloc)
+{
+    QUfs *ufs = obj;
+    UtpUpiuRsp rsp_upiu;
+    UfsRpmbFrame req, resp;
+    uint8_t mac[UFS_RPMB_KEY_MAC_LEN];
+
+    ufs_init(ufs, alloc);
+
+    /* 1. Read write counter before key is programmed -> 0x0007 (NO_AUTH_KEY) 
*/
+    memset(&req, 0, sizeof(req));
+    memset(req.nonce, 0xa1, sizeof(req.nonce));
+    stw_be_p(&req.req_resp, UFS_RPMB_REQ_READ_COUNTER);
+    ufs_rpmb_sec_out_frames(ufs, 0, &req, 1, &rsp_upiu);
+    g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, GOOD);
+
+    ufs_rpmb_sec_in_frames(ufs, 0, &resp, 1, &rsp_upiu);
+    g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, GOOD);
+    g_assert_cmpuint(lduw_be_p(&resp.req_resp), ==, 
UFS_RPMB_RESP_READ_COUNTER);
+    g_assert_cmpuint(lduw_be_p(&resp.result), ==, UFS_RPMB_RES_NO_AUTH_KEY);
+    g_assert_cmpmem(resp.nonce, UFS_RPMB_NONCE_LEN,
+                    req.nonce, UFS_RPMB_NONCE_LEN);
+
+    /* 2. Program key -> 0x0000 (OK) with zeroed Key/MAC (JESD220H Fig 12.5) */
+    ufs_rpmb_program_key(ufs, 0, ufs_rpmb_test_key);
+
+    /* 3. Re-program key -> 0x0005 (WRITE_FAILURE) */
+    memset(&req, 0, sizeof(req));
+    memcpy(req.key_mac, ufs_rpmb_test_key, UFS_RPMB_KEY_MAC_LEN);
+    stw_be_p(&req.req_resp, UFS_RPMB_REQ_PROGRAM_KEY);
+    ufs_rpmb_sec_out_frames(ufs, 0, &req, 1, &rsp_upiu);
+    g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, GOOD);
+
+    ufs_rpmb_read_result(ufs, 0, &resp);
+    g_assert_cmpuint(lduw_be_p(&resp.req_resp), ==, UFS_RPMB_RESP_PROGRAM_KEY);
+    g_assert_cmpuint(lduw_be_p(&resp.result), ==, UFS_RPMB_RES_WRITE_FAILURE);
+
+    /* 4. Read write counter -> 0x0000, counter == 0, valid MAC */
+    memset(&req, 0, sizeof(req));
+    memset(req.nonce, 0xb2, sizeof(req.nonce));
+    stw_be_p(&req.req_resp, UFS_RPMB_REQ_READ_COUNTER);
+    ufs_rpmb_sec_out_frames(ufs, 0, &req, 1, &rsp_upiu);
+    g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, GOOD);
+
+    ufs_rpmb_sec_in_frames(ufs, 0, &resp, 1, &rsp_upiu);
+    g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, GOOD);
+    g_assert_cmpuint(lduw_be_p(&resp.req_resp), ==, 
UFS_RPMB_RESP_READ_COUNTER);
+    g_assert_cmpuint(lduw_be_p(&resp.result), ==, UFS_RPMB_RES_OK);
+    g_assert_cmpuint(ldl_be_p(&resp.write_counter), ==, 0);
+    g_assert_cmpmem(resp.nonce, UFS_RPMB_NONCE_LEN,
+                    req.nonce, UFS_RPMB_NONCE_LEN);
+    ufs_rpmb_compute_hmac(ufs_rpmb_test_key, &resp, 1, mac);
+    g_assert_cmpmem(resp.key_mac, UFS_RPMB_KEY_MAC_LEN,
+                    mac, UFS_RPMB_KEY_MAC_LEN);
+
+    ufs_exit(ufs, alloc);
+}
+
 static void ufs_register_nodes(void)
 {
     const char *arch;
@@ -4353,6 +4456,8 @@ static void ufs_register_nodes(void)
                  &zufs_test_opts);
     qos_add_test("rpmb-descriptor-cdb", "ufs", ufstest_rpmb_descriptor_cdb,
                  &io_test_opts);
+    qos_add_test("rpmb-key-counter", "ufs", ufstest_rpmb_key_counter,
+                 &io_test_opts);
 }
 
 libqos_init(ufs_register_nodes);
-- 
2.56.0.307.g5fe7bd680c-goog


Reply via email to