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
