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
