Add ufstest_rpmb_read_write() to test single-frame and multi-frame (N = 4) authenticated data writes and reads with HMAC-SHA256 verification, Result Read (0x0005) both after a multi-frame read and while a pending 0x0004 read has had no SECURITY PROTOCOL IN (verifying zeroed stuff bytes and zero write_counter per JESD220H Figure 12.8), residual underflow accounting, JESD220H Sections 12.4.7.3.3 and 12.4.7.3.4 check ordering and UFS_RPMB_RES_GENERAL_FAILURE (0x0001) cases, multi-frame reads past region end, SECURITY PROTOCOL IN block count mismatch sense reporting, multi-region key and data isolation across regions 0..3, short-PRDT rejection while read_pending is set, and HCE 1->0->1 reset clearing volatile state while preserving keys, write counters, and stored blocks.
Signed-off-by: Stanley Jhu <[email protected]> --- tests/qtest/ufs-test.c | 569 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 569 insertions(+) diff --git a/tests/qtest/ufs-test.c b/tests/qtest/ufs-test.c index 9fce5fff82..c118ee86be 100644 --- a/tests/qtest/ufs-test.c +++ b/tests/qtest/ufs-test.c @@ -4361,6 +4361,573 @@ static void ufstest_rpmb_key_counter(void *obj, void *data, ufs_exit(ufs, alloc); } +static void ufstest_rpmb_read_write(void *obj, void *data, + QGuestAllocator *alloc) +{ + QUfs *ufs = obj; + UtpUpiuRsp rsp_upiu; + enum UtpOcsCodes ocs; + UfsRpmbFrame req, resp, zero_frame; + UfsRpmbFrame wframes[4], rframes[4]; + uint8_t mac[UFS_RPMB_KEY_MAC_LEN]; + uint8_t zero_mac[UFS_RPMB_KEY_MAC_LEN] = { 0 }; + uint8_t cdb[UFS_CDB_SIZE]; + const uint8_t *key = ufs_rpmb_test_key; + uint8_t region_keys[UFS_RPMB_MAX_REGIONS][UFS_RPMB_KEY_MAC_LEN]; + int i, j, r; + + ufs_init(ufs, alloc); + memset(&zero_frame, 0, sizeof(zero_frame)); + ufs_rpmb_program_key(ufs, 0, key); + + /* 1. Single-frame authenticated write (block 3, counter 0 -> 1) + read */ + memset(&req, 0, sizeof(req)); + for (i = 0; i < UFS_RPMB_DATA_LEN; i++) { + req.data[i] = (uint8_t)(0x5a ^ i); + } + stl_be_p(&req.write_counter, 0); + stw_be_p(&req.addr, 3); + stw_be_p(&req.block_count, 1); + stw_be_p(&req.req_resp, UFS_RPMB_REQ_AUTH_DATA_WRITE); + ufs_rpmb_compute_hmac(key, &req, 1, req.key_mac); + 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_AUTH_DATA_WRITE); + g_assert_cmpuint(lduw_be_p(&resp.result), ==, UFS_RPMB_RES_OK); + g_assert_cmpuint(lduw_be_p(&resp.addr), ==, 3); + g_assert_cmpuint(ldl_be_p(&resp.write_counter), ==, 1); + ufs_rpmb_compute_hmac(key, &resp, 1, mac); + g_assert_cmpmem(resp.key_mac, UFS_RPMB_KEY_MAC_LEN, + mac, UFS_RPMB_KEY_MAC_LEN); + + memset(&req, 0, sizeof(req)); + memset(req.nonce, 0xc3, sizeof(req.nonce)); + stw_be_p(&req.addr, 3); + stw_be_p(&req.block_count, 1); + stw_be_p(&req.req_resp, UFS_RPMB_REQ_AUTH_DATA_READ); + 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_AUTH_DATA_READ); + g_assert_cmpuint(lduw_be_p(&resp.result), ==, UFS_RPMB_RES_OK); + g_assert_cmpuint(lduw_be_p(&resp.addr), ==, 3); + g_assert_cmpuint(lduw_be_p(&resp.block_count), ==, 1); + g_assert_cmpmem(resp.nonce, UFS_RPMB_NONCE_LEN, + req.nonce, UFS_RPMB_NONCE_LEN); + for (i = 0; i < UFS_RPMB_DATA_LEN; i++) { + g_assert_cmpuint(resp.data[i], ==, (uint8_t)(0x5a ^ i)); + } + ufs_rpmb_compute_hmac(key, &resp, 1, mac); + g_assert_cmpmem(resp.key_mac, UFS_RPMB_KEY_MAC_LEN, + mac, UFS_RPMB_KEY_MAC_LEN); + + /* 2. Multi-frame authenticated write (N = 4, addr 10..13, wc 1 -> 2) */ + memset(wframes, 0, sizeof(wframes)); + for (i = 0; i < 4; i++) { + for (j = 0; j < UFS_RPMB_DATA_LEN; j++) { + wframes[i].data[j] = (uint8_t)((i + 1) * 0x11 + j); + } + stl_be_p(&wframes[i].write_counter, 1); + stw_be_p(&wframes[i].addr, 10); + stw_be_p(&wframes[i].block_count, 4); + stw_be_p(&wframes[i].req_resp, UFS_RPMB_REQ_AUTH_DATA_WRITE); + } + ufs_rpmb_compute_hmac(key, wframes, 4, wframes[3].key_mac); + ufs_rpmb_sec_out_frames(ufs, 0, wframes, 4, &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_AUTH_DATA_WRITE); + g_assert_cmpuint(lduw_be_p(&resp.result), ==, UFS_RPMB_RES_OK); + g_assert_cmpuint(lduw_be_p(&resp.addr), ==, 10); + g_assert_cmpuint(ldl_be_p(&resp.write_counter), ==, 2); + ufs_rpmb_compute_hmac(key, &resp, 1, mac); + g_assert_cmpmem(resp.key_mac, UFS_RPMB_KEY_MAC_LEN, + mac, UFS_RPMB_KEY_MAC_LEN); + + /* Multi-frame authenticated read (N = 4, addr 10..13) with MAC check */ + memset(&req, 0, sizeof(req)); + memset(req.nonce, 0xd4, sizeof(req.nonce)); + stw_be_p(&req.addr, 10); + stw_be_p(&req.block_count, 4); + stw_be_p(&req.req_resp, UFS_RPMB_REQ_AUTH_DATA_READ); + ufs_rpmb_sec_out_frames(ufs, 0, &req, 1, &rsp_upiu); + g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, GOOD); + + memset(rframes, 0, sizeof(rframes)); + ufs_rpmb_sec_in_frames(ufs, 0, rframes, 4, &rsp_upiu); + g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, GOOD); + for (i = 0; i < 4; i++) { + g_assert_cmpuint(lduw_be_p(&rframes[i].req_resp), ==, + UFS_RPMB_RESP_AUTH_DATA_READ); + g_assert_cmpuint(lduw_be_p(&rframes[i].result), ==, UFS_RPMB_RES_OK); + g_assert_cmpuint(lduw_be_p(&rframes[i].addr), ==, 10); + g_assert_cmpuint(lduw_be_p(&rframes[i].block_count), ==, 4); + g_assert_cmpmem(rframes[i].nonce, UFS_RPMB_NONCE_LEN, + req.nonce, UFS_RPMB_NONCE_LEN); + g_assert_cmpmem(rframes[i].data, UFS_RPMB_DATA_LEN, + wframes[i].data, UFS_RPMB_DATA_LEN); + if (i < 3) { + g_assert_cmpmem(rframes[i].key_mac, UFS_RPMB_KEY_MAC_LEN, + zero_mac, UFS_RPMB_KEY_MAC_LEN); + } + } + ufs_rpmb_compute_hmac(key, rframes, 4, mac); + g_assert_cmpmem(rframes[3].key_mac, UFS_RPMB_KEY_MAC_LEN, + mac, UFS_RPMB_KEY_MAC_LEN); + + /* + * READ_RESULT + 1-frame SECURITY PROTOCOL IN after a multi-frame + * AUTH_DATA_READ returns the latched result frame without failing the + * multi-frame block count check. + */ + ufs_rpmb_read_result(ufs, 0, &resp); + g_assert_cmpmem(resp.stuff, sizeof(resp.stuff), + zero_frame.stuff, sizeof(resp.stuff)); + g_assert_cmpuint(ldl_be_p(&resp.write_counter), ==, 0); + g_assert_cmpuint(lduw_be_p(&resp.req_resp), ==, + UFS_RPMB_RESP_AUTH_DATA_READ); + g_assert_cmpuint(lduw_be_p(&resp.result), ==, UFS_RPMB_RES_OK); + g_assert_cmpuint(lduw_be_p(&resp.addr), ==, 10); + g_assert_cmpuint(lduw_be_p(&resp.block_count), ==, 4); + g_assert_cmpmem(&resp, sizeof(resp), &rframes[3], sizeof(resp)); + + /* + * Pending AUTH_DATA_READ (OUT 0x0004) immediately followed by READ_RESULT + * (OUT 0x0005) + 1-frame SECURITY PROTOCOL IN (512 bytes, with no prior IN + * issued for the read) returns a fully initialized, signed 0x0400 frame + * with stuff[0..195] == 0 and write_counter == 0 per JESD220H Figure 12.8. + */ + memset(&req, 0, sizeof(req)); + memset(req.nonce, 0xd9, sizeof(req.nonce)); + stw_be_p(&req.addr, 3); + stw_be_p(&req.block_count, 1); + stw_be_p(&req.req_resp, UFS_RPMB_REQ_AUTH_DATA_READ); + 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_cmpmem(resp.stuff, sizeof(resp.stuff), + zero_frame.stuff, sizeof(resp.stuff)); + g_assert_cmpuint(ldl_be_p(&resp.write_counter), ==, 0); + g_assert_cmpuint(lduw_be_p(&resp.req_resp), ==, + UFS_RPMB_RESP_AUTH_DATA_READ); + g_assert_cmpuint(lduw_be_p(&resp.result), ==, UFS_RPMB_RES_OK); + g_assert_cmpuint(lduw_be_p(&resp.addr), ==, 3); + g_assert_cmpuint(lduw_be_p(&resp.block_count), ==, 1); + g_assert_cmpmem(resp.nonce, UFS_RPMB_NONCE_LEN, + req.nonce, UFS_RPMB_NONCE_LEN); + for (i = 0; i < UFS_RPMB_DATA_LEN; i++) { + g_assert_cmpuint(resp.data[i], ==, (uint8_t)(0x5a ^ i)); + } + ufs_rpmb_compute_hmac(key, &resp, 1, mac); + g_assert_cmpmem(resp.key_mac, UFS_RPMB_KEY_MAC_LEN, + mac, UFS_RPMB_KEY_MAC_LEN); + + ufs_rpmb_build_cdb(cdb, SECURITY_PROTOCOL_IN, UFS_RPMB_SEC_PROTOCOL, + UFS_RPMB_SEC_PROTOCOL_ID, sizeof(resp)); + ocs = ufs_send_scsi_command(ufs, UFS_UPIU_RPMB_WLUN, cdb, + NULL, 0, (uint8_t *)rframes, + 2 * sizeof(UfsRpmbFrame), &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, GOOD); + g_assert_cmpuint(rsp_upiu.header.flags & UFS_UPIU_FLAG_UNDERFLOW, ==, + UFS_UPIU_FLAG_UNDERFLOW); + g_assert_cmpuint(be32_to_cpu(rsp_upiu.sr.residual_transfer_count), ==, + sizeof(UfsRpmbFrame)); + + /* + * 3. Write check order per JESD220H Section 12.4.7.3.3: address range + * (0x0004) before block count > max (0x0001), MAC (0x0002), and write + * counter (0x0003), plus N-frame read past region end -> 0x0004 on all + * returned frames without out-of-bounds read. + */ + memset(&req, 0, sizeof(req)); + stl_be_p(&req.write_counter, 99); + stw_be_p(&req.addr, 32 * UFS_RPMB_BLOCKS_PER_UNIT); + stw_be_p(&req.block_count, 1); + stw_be_p(&req.req_resp, UFS_RPMB_REQ_AUTH_DATA_WRITE); + ufs_rpmb_compute_hmac(key, &req, 1, req.key_mac); + req.key_mac[0] ^= 0xff; + 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.result), ==, UFS_RPMB_RES_ADDR_FAILURE); + g_assert_cmpuint(ldl_be_p(&resp.write_counter), ==, 2); + + /* Bad address + block_count > bRPMB_ReadWriteSize (64) -> 0x0004 */ + memset(&req, 0, sizeof(req)); + stl_be_p(&req.write_counter, 2); + stw_be_p(&req.addr, 32 * UFS_RPMB_BLOCKS_PER_UNIT); + stw_be_p(&req.block_count, 65); + stw_be_p(&req.req_resp, UFS_RPMB_REQ_AUTH_DATA_WRITE); + ufs_rpmb_compute_hmac(key, &req, 1, req.key_mac); + 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.result), ==, UFS_RPMB_RES_ADDR_FAILURE); + g_assert_cmpuint(ldl_be_p(&resp.write_counter), ==, 2); + + /* Good address + bad MAC (+ bad counter) -> 0x0002 */ + memset(&req, 0, sizeof(req)); + stl_be_p(&req.write_counter, 99); + stw_be_p(&req.addr, 0); + stw_be_p(&req.block_count, 1); + stw_be_p(&req.req_resp, UFS_RPMB_REQ_AUTH_DATA_WRITE); + ufs_rpmb_compute_hmac(key, &req, 1, req.key_mac); + req.key_mac[0] ^= 0xff; + 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.result), ==, UFS_RPMB_RES_AUTH_FAILURE); + g_assert_cmpuint(ldl_be_p(&resp.write_counter), ==, 2); + + /* Good address + good MAC + bad counter -> 0x0003 */ + ufs_rpmb_compute_hmac(key, &req, 1, req.key_mac); + 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.result), ==, UFS_RPMB_RES_COUNT_FAILURE); + g_assert_cmpuint(ldl_be_p(&resp.write_counter), ==, 2); + + memset(wframes, 0, sizeof(wframes)); + for (i = 0; i < 2; i++) { + stl_be_p(&wframes[i].write_counter, 2); + stw_be_p(&wframes[i].addr, 32 * UFS_RPMB_BLOCKS_PER_UNIT - 1); + stw_be_p(&wframes[i].block_count, 2); + stw_be_p(&wframes[i].req_resp, UFS_RPMB_REQ_AUTH_DATA_WRITE); + } + ufs_rpmb_compute_hmac(key, wframes, 2, wframes[1].key_mac); + ufs_rpmb_sec_out_frames(ufs, 0, wframes, 2, &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.result), ==, UFS_RPMB_RES_ADDR_FAILURE); + g_assert_cmpuint(ldl_be_p(&resp.write_counter), ==, 2); + + /* N-frame read near end of region (block_count=0 in OUT, N=2 in IN) */ + memset(&req, 0, sizeof(req)); + memset(req.nonce, 0xe5, sizeof(req.nonce)); + stw_be_p(&req.addr, 32 * UFS_RPMB_BLOCKS_PER_UNIT - 1); + stw_be_p(&req.block_count, 0); + stw_be_p(&req.req_resp, UFS_RPMB_REQ_AUTH_DATA_READ); + ufs_rpmb_sec_out_frames(ufs, 0, &req, 1, &rsp_upiu); + g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, GOOD); + + memset(rframes, 0, sizeof(rframes)); + ufs_rpmb_sec_in_frames(ufs, 0, rframes, 2, &rsp_upiu); + g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, GOOD); + ufs_rpmb_compute_hmac(key, &rframes[0], 1, mac); + for (i = 0; i < 2; i++) { + g_assert_cmpuint(lduw_be_p(&rframes[i].req_resp), ==, + UFS_RPMB_RESP_AUTH_DATA_READ); + g_assert_cmpuint(lduw_be_p(&rframes[i].result), ==, + UFS_RPMB_RES_ADDR_FAILURE); + g_assert_cmpuint(lduw_be_p(&rframes[i].addr), ==, + 32 * UFS_RPMB_BLOCKS_PER_UNIT - 1); + g_assert_cmpuint(lduw_be_p(&rframes[i].block_count), ==, 2); + g_assert_cmpmem(rframes[i].nonce, UFS_RPMB_NONCE_LEN, + req.nonce, UFS_RPMB_NONCE_LEN); + g_assert_cmpmem(rframes[i].key_mac, UFS_RPMB_KEY_MAC_LEN, + mac, UFS_RPMB_KEY_MAC_LEN); + } + + /* N-frame read near end of region (block_count=4 in OUT, N=4 in IN) */ + memset(&req, 0, sizeof(req)); + memset(req.nonce, 0xf6, sizeof(req.nonce)); + stw_be_p(&req.addr, 32 * UFS_RPMB_BLOCKS_PER_UNIT - 2); + stw_be_p(&req.block_count, 4); + stw_be_p(&req.req_resp, UFS_RPMB_REQ_AUTH_DATA_READ); + ufs_rpmb_sec_out_frames(ufs, 0, &req, 1, &rsp_upiu); + g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, GOOD); + + memset(rframes, 0, sizeof(rframes)); + ufs_rpmb_sec_in_frames(ufs, 0, rframes, 4, &rsp_upiu); + g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, GOOD); + ufs_rpmb_compute_hmac(key, &rframes[0], 1, mac); + for (i = 0; i < 4; i++) { + g_assert_cmpuint(lduw_be_p(&rframes[i].req_resp), ==, + UFS_RPMB_RESP_AUTH_DATA_READ); + g_assert_cmpuint(lduw_be_p(&rframes[i].result), ==, + UFS_RPMB_RES_ADDR_FAILURE); + g_assert_cmpuint(lduw_be_p(&rframes[i].addr), ==, + 32 * UFS_RPMB_BLOCKS_PER_UNIT - 2); + g_assert_cmpuint(lduw_be_p(&rframes[i].block_count), ==, 4); + g_assert_cmpmem(rframes[i].nonce, UFS_RPMB_NONCE_LEN, + req.nonce, UFS_RPMB_NONCE_LEN); + g_assert_cmpmem(rframes[i].key_mac, UFS_RPMB_KEY_MAC_LEN, + mac, UFS_RPMB_KEY_MAC_LEN); + } + + /* + * Read check order per JESD220H Section 12.4.7.3.4: bad address + + * block_count > bRPMB_ReadWriteSize (64) -> 0x0004 (before 0x0001). + */ + memset(&req, 0, sizeof(req)); + memset(req.nonce, 0xf7, sizeof(req.nonce)); + stw_be_p(&req.addr, 32 * UFS_RPMB_BLOCKS_PER_UNIT); + stw_be_p(&req.block_count, 65); + stw_be_p(&req.req_resp, UFS_RPMB_REQ_AUTH_DATA_READ); + 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_AUTH_DATA_READ); + g_assert_cmpuint(lduw_be_p(&resp.result), ==, UFS_RPMB_RES_ADDR_FAILURE); + + /* + * UFS_RPMB_RES_GENERAL_FAILURE (0x0001) paths: + * (a) 2-frame write with mismatched addr across frames + * (b) 1-frame write with block_count != nframes + * (c) 0x0004 read with valid addr and block_count > bRPMB_ReadWriteSize + * (d) unknown request type 0x0006 + */ + memset(wframes, 0, sizeof(wframes)); + for (i = 0; i < 2; i++) { + stl_be_p(&wframes[i].write_counter, 2); + stw_be_p(&wframes[i].block_count, 2); + stw_be_p(&wframes[i].req_resp, UFS_RPMB_REQ_AUTH_DATA_WRITE); + } + stw_be_p(&wframes[0].addr, 4); + stw_be_p(&wframes[1].addr, 5); + ufs_rpmb_compute_hmac(key, wframes, 2, wframes[1].key_mac); + ufs_rpmb_sec_out_frames(ufs, 0, wframes, 2, &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_AUTH_DATA_WRITE); + g_assert_cmpuint(lduw_be_p(&resp.result), ==, + UFS_RPMB_RES_GENERAL_FAILURE); + g_assert_cmpuint(ldl_be_p(&resp.write_counter), ==, 2); + + memset(&req, 0, sizeof(req)); + stl_be_p(&req.write_counter, 2); + stw_be_p(&req.addr, 4); + stw_be_p(&req.block_count, 2); + stw_be_p(&req.req_resp, UFS_RPMB_REQ_AUTH_DATA_WRITE); + ufs_rpmb_compute_hmac(key, &req, 1, req.key_mac); + 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_AUTH_DATA_WRITE); + g_assert_cmpuint(lduw_be_p(&resp.result), ==, + UFS_RPMB_RES_GENERAL_FAILURE); + g_assert_cmpuint(ldl_be_p(&resp.write_counter), ==, 2); + + memset(&req, 0, sizeof(req)); + memset(req.nonce, 0xf8, sizeof(req.nonce)); + stw_be_p(&req.addr, 0); + stw_be_p(&req.block_count, 65); + stw_be_p(&req.req_resp, UFS_RPMB_REQ_AUTH_DATA_READ); + 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_AUTH_DATA_READ); + g_assert_cmpuint(lduw_be_p(&resp.result), ==, + UFS_RPMB_RES_GENERAL_FAILURE); + + memset(&req, 0, sizeof(req)); + stw_be_p(&req.req_resp, 0x0006); + 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), ==, 0x0006); + g_assert_cmpuint(lduw_be_p(&resp.result), ==, + UFS_RPMB_RES_GENERAL_FAILURE); + + /* + * 4. Authenticated data read SECURITY PROTOCOL IN block count mismatch + * sense check. + */ + memset(&req, 0, sizeof(req)); + stw_be_p(&req.addr, 10); + stw_be_p(&req.block_count, 2); + stw_be_p(&req.req_resp, UFS_RPMB_REQ_AUTH_DATA_READ); + 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, ==, CHECK_CONDITION); + g_assert_cmpuint(rsp_upiu.sr.sense_data[2] & 0x0f, ==, ILLEGAL_REQUEST); + g_assert_cmpuint(rsp_upiu.sr.sense_data[12], ==, 0x24); + g_assert_cmpuint(rsp_upiu.sr.sense_data[13], ==, 0x00); + + /* + * 5. Multi-region (regions 1..3) key programming and cross-region data + * isolation at block 0. + */ + memset(&req, 0, sizeof(req)); + for (i = 0; i < UFS_RPMB_DATA_LEN; i++) { + req.data[i] = (uint8_t)(0xa5 ^ i); + } + stl_be_p(&req.write_counter, 2); + stw_be_p(&req.addr, 0); + stw_be_p(&req.block_count, 1); + stw_be_p(&req.req_resp, UFS_RPMB_REQ_AUTH_DATA_WRITE); + ufs_rpmb_compute_hmac(key, &req, 1, req.key_mac); + 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.result), ==, UFS_RPMB_RES_OK); + g_assert_cmpuint(ldl_be_p(&resp.write_counter), ==, 3); + + memcpy(region_keys[0], key, UFS_RPMB_KEY_MAC_LEN); + for (r = 1; r < UFS_RPMB_MAX_REGIONS; r++) { + for (i = 0; i < UFS_RPMB_KEY_MAC_LEN; i++) { + region_keys[r][i] = (uint8_t)(key[i] ^ (r * 0x33)); + } + ufs_rpmb_program_key(ufs, r, region_keys[r]); + } + + memset(&req, 0, sizeof(req)); + for (i = 0; i < UFS_RPMB_DATA_LEN; i++) { + req.data[i] = (uint8_t)(0x3c ^ i); + } + stl_be_p(&req.write_counter, 0); + stw_be_p(&req.addr, 0); + stw_be_p(&req.block_count, 1); + stw_be_p(&req.req_resp, UFS_RPMB_REQ_AUTH_DATA_WRITE); + ufs_rpmb_compute_hmac(region_keys[1], &req, 1, req.key_mac); + ufs_rpmb_sec_out_frames(ufs, 1, &req, 1, &rsp_upiu); + g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, GOOD); + + ufs_rpmb_read_result(ufs, 1, &resp); + g_assert_cmpuint(lduw_be_p(&resp.req_resp), ==, + UFS_RPMB_RESP_AUTH_DATA_WRITE); + g_assert_cmpuint(lduw_be_p(&resp.result), ==, UFS_RPMB_RES_OK); + g_assert_cmpuint(ldl_be_p(&resp.write_counter), ==, 1); + ufs_rpmb_compute_hmac(region_keys[1], &resp, 1, mac); + g_assert_cmpmem(resp.key_mac, UFS_RPMB_KEY_MAC_LEN, + mac, UFS_RPMB_KEY_MAC_LEN); + + for (r = 0; r < UFS_RPMB_MAX_REGIONS; r++) { + memset(&req, 0, sizeof(req)); + memset(req.nonce, (uint8_t)(0x90 + r), sizeof(req.nonce)); + stw_be_p(&req.addr, 0); + stw_be_p(&req.block_count, 1); + stw_be_p(&req.req_resp, UFS_RPMB_REQ_AUTH_DATA_READ); + ufs_rpmb_sec_out_frames(ufs, r, &req, 1, &rsp_upiu); + g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, GOOD); + + memset(&resp, 0, sizeof(resp)); + ufs_rpmb_sec_in_frames(ufs, r, &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_AUTH_DATA_READ); + g_assert_cmpuint(lduw_be_p(&resp.result), ==, UFS_RPMB_RES_OK); + for (i = 0; i < UFS_RPMB_DATA_LEN; i++) { + uint8_t expected = 0; + + if (r == 0) { + expected = (uint8_t)(0xa5 ^ i); + } else if (r == 1) { + expected = (uint8_t)(0x3c ^ i); + } + g_assert_cmpuint(resp.data[i], ==, expected); + } + ufs_rpmb_compute_hmac(region_keys[r], &resp, 1, mac); + g_assert_cmpmem(resp.key_mac, UFS_RPMB_KEY_MAC_LEN, + mac, UFS_RPMB_KEY_MAC_LEN); + } + + /* + * 6. Short-PRDT SECURITY_PROTOCOL_IN (256 bytes) while read_pending is + * set must return UFS_OCS_MISMATCH_DATA_BUF_SIZE without clearing + * read_pending. + */ + memset(&req, 0, sizeof(req)); + stw_be_p(&req.addr, 0); + stw_be_p(&req.block_count, 1); + stw_be_p(&req.req_resp, UFS_RPMB_REQ_AUTH_DATA_READ); + ufs_rpmb_sec_out_frames(ufs, 0, &req, 1, &rsp_upiu); + g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, GOOD); + + ufs_rpmb_build_cdb(cdb, SECURITY_PROTOCOL_IN, UFS_RPMB_SEC_PROTOCOL, + UFS_RPMB_SEC_PROTOCOL_ID, UFS_RPMB_FRAME_LEN); + ocs = ufs_rpmb_send_short_prdt(ufs, cdb, UFS_RPMB_FRAME_LEN, 256, false); + g_assert_cmpuint(ocs, ==, UFS_OCS_MISMATCH_DATA_BUF_SIZE); + /* read_pending stays armed until a valid IN or reset */ + 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_AUTH_DATA_READ); + g_assert_cmpuint(lduw_be_p(&resp.result), ==, UFS_RPMB_RES_OK); + + /* + * 7. HCE 1->0->1 reset clears volatile read_pending and Result Register + * state while keeping programmed keys, write counters, and stored + * blocks intact. + */ + memset(&req, 0, sizeof(req)); + stw_be_p(&req.addr, 0); + stw_be_p(&req.block_count, 2); + stw_be_p(&req.req_resp, UFS_RPMB_REQ_AUTH_DATA_READ); + ufs_rpmb_sec_out_frames(ufs, 0, &req, 1, &rsp_upiu); + g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, GOOD); + + memset(&req, 0, sizeof(req)); + memset(req.nonce, 0x5a, sizeof(req.nonce)); + stw_be_p(&req.req_resp, UFS_RPMB_REQ_READ_COUNTER); + ufs_rpmb_sec_out_frames(ufs, 1, &req, 1, &rsp_upiu); + g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, GOOD); + + ufs_wreg(ufs, A_HCE, 0); + ufs_exit(ufs, alloc); + ufs_init(ufs, alloc); + + /* read_pending was 2 frames on region 0, so 1-frame IN returns zero */ + memset(&resp, 0xff, sizeof(resp)); + ufs_rpmb_sec_in_frames(ufs, 0, &resp, 1, &rsp_upiu); + g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, GOOD); + g_assert_cmpmem(&resp, sizeof(resp), &zero_frame, sizeof(zero_frame)); + + memset(&resp, 0xff, sizeof(resp)); + ufs_rpmb_sec_in_frames(ufs, 1, &resp, 1, &rsp_upiu); + g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, GOOD); + g_assert_cmpmem(&resp, sizeof(resp), &zero_frame, sizeof(zero_frame)); + + /* Key, write counter (1), and block 0 data on region 1 still persist */ + memset(&req, 0, sizeof(req)); + memset(req.nonce, 0x3c, sizeof(req.nonce)); + stw_be_p(&req.req_resp, UFS_RPMB_REQ_READ_COUNTER); + ufs_rpmb_sec_out_frames(ufs, 1, &req, 1, &rsp_upiu); + ufs_rpmb_sec_in_frames(ufs, 1, &resp, 1, &rsp_upiu); + g_assert_cmpuint(lduw_be_p(&resp.result), ==, UFS_RPMB_RES_OK); + g_assert_cmpuint(ldl_be_p(&resp.write_counter), ==, 1); + ufs_rpmb_compute_hmac(region_keys[1], &resp, 1, mac); + g_assert_cmpmem(resp.key_mac, UFS_RPMB_KEY_MAC_LEN, + mac, UFS_RPMB_KEY_MAC_LEN); + + memset(&req, 0, sizeof(req)); + memset(req.nonce, 0x7e, sizeof(req.nonce)); + stw_be_p(&req.addr, 0); + stw_be_p(&req.block_count, 1); + stw_be_p(&req.req_resp, UFS_RPMB_REQ_AUTH_DATA_READ); + ufs_rpmb_sec_out_frames(ufs, 1, &req, 1, &rsp_upiu); + g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, GOOD); + + memset(&resp, 0, sizeof(resp)); + ufs_rpmb_sec_in_frames(ufs, 1, &resp, 1, &rsp_upiu); + g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, GOOD); + g_assert_cmpuint(lduw_be_p(&resp.result), ==, UFS_RPMB_RES_OK); + for (i = 0; i < UFS_RPMB_DATA_LEN; i++) { + g_assert_cmpuint(resp.data[i], ==, (uint8_t)(0x3c ^ i)); + } + ufs_rpmb_compute_hmac(region_keys[1], &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; @@ -4458,6 +5025,8 @@ static void ufs_register_nodes(void) &io_test_opts); qos_add_test("rpmb-key-counter", "ufs", ufstest_rpmb_key_counter, &io_test_opts); + qos_add_test("rpmb-read-write", "ufs", ufstest_rpmb_read_write, + &io_test_opts); } libqos_init(ufs_register_nodes); -- 2.56.0.307.g5fe7bd680c-goog
