From 0b77774c92ca945c44160447738f3021b999ec2f Mon Sep 17 00:00:00 2001 From: Nagaraj P N Date: Tue, 21 May 2024 09:38:08 -0700 Subject: [PATCH] vse: Add support for HMAC SHA Sign/Verify Add support for both old and new chip Jira ESSS-1336 Jira ESSS-1371 Change-Id: Ie4010bc7f3d8f3b71e3f49197c21911cd11a6dea Signed-off-by: Nagaraj P N Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3144907 GVS: buildbot_gerritrpt Reviewed-by: Sandeep Trasi Reviewed-by: Leo Chiu --- drivers/crypto/tegra-hv-vse-safety.c | 575 +++++++++++++++++++++- drivers/crypto/tegra-hv-vse.h | 20 + drivers/crypto/tegra-nvvse-cryptodev.c | 231 +++++++++ include/uapi/misc/tegra-nvvse-cryptodev.h | 62 +++ 4 files changed, 868 insertions(+), 20 deletions(-) diff --git a/drivers/crypto/tegra-hv-vse-safety.c b/drivers/crypto/tegra-hv-vse-safety.c index c13c3c36..67cd3ffc 100644 --- a/drivers/crypto/tegra-hv-vse-safety.c +++ b/drivers/crypto/tegra-hv-vse-safety.c @@ -70,6 +70,7 @@ /* Command categories for SHA Engine */ #define TEGRA_VIRTUAL_SE_CMD_CATEGORY_SHA 0x00010000U +#define TEGRA_VIRTUAL_SE_CMD_CATEGORY_HMAC 0x00030000U /* Command categories for TSEC Engine */ #define TEGRA_VIRTUAL_SE_CMD_CATEGORY_TSEC_KEYS 0x00010000U @@ -112,6 +113,11 @@ /* Commands in the SHA Category */ #define TEGRA_VIRTUAL_SE_CMD_OP_SHA 0x00000001U +/* Commands in the HMAC Category */ +#define TEGRA_VIRTUAL_SE_CMD_OP_HMAC_SIGN 0x00000001U +#define TEGRA_VIRTUAL_SE_CMD_OP_HMAC_VERIFY 0x00000002U +#define TEGRA_VIRTUAL_SE_CMD_OP_HMAC_GET_VERIFY 0x00000004U + /* Commands in the TSEC keys category */ #define TEGRA_VIRTUAL_SE_CMD_OP_TSEC_KEYLOAD_STATUS 0x00000001U @@ -221,6 +227,19 @@ #define TEGRA_VIRTUAL_SE_CMD_SHA_HASH (TEGRA_VIRTUAL_SE_CMD_ENG_SHA \ | TEGRA_VIRTUAL_SE_CMD_CATEGORY_SHA \ | TEGRA_VIRTUAL_SE_CMD_OP_SHA) + +#define TEGRA_VIRTUAL_SE_CMD_HMAC_SIGN (TEGRA_VIRTUAL_SE_CMD_ENG_SHA \ + | TEGRA_VIRTUAL_SE_CMD_CATEGORY_HMAC \ + | TEGRA_VIRTUAL_SE_CMD_OP_HMAC_SIGN) + +#define TEGRA_VIRTUAL_SE_CMD_HMAC_VERIFY (TEGRA_VIRTUAL_SE_CMD_ENG_SHA \ + | TEGRA_VIRTUAL_SE_CMD_CATEGORY_HMAC \ + | TEGRA_VIRTUAL_SE_CMD_OP_HMAC_VERIFY) + +#define TEGRA_VIRTUAL_SE_CMD_HMAC_GET_VERIFY (TEGRA_VIRTUAL_SE_CMD_ENG_SHA \ + | TEGRA_VIRTUAL_SE_CMD_CATEGORY_HMAC \ + | TEGRA_VIRTUAL_SE_CMD_OP_HMAC_GET_VERIFY) + #define TEGRA_VIRTUAL_SE_SHA_HASH_BLOCK_SIZE_512BIT (512 / 8) #define TEGRA_VIRTUAL_SE_SHA_HASH_BLOCK_SIZE_576BIT (576 / 8) #define TEGRA_VIRTUAL_SE_SHA_HASH_BLOCK_SIZE_832BIT (832 / 8) @@ -228,6 +247,8 @@ #define TEGRA_VIRTUAL_SE_SHA_HASH_BLOCK_SIZE_1088BIT (1088 / 8) #define TEGRA_VIRTUAL_SE_SHA_HASH_BLOCK_SIZE_1344BIT (1344 / 8) +#define TEGRA_VIRTUAL_SE_SHA_MAX_HMAC_SHA_LENGTH (32U) + #define SHA3_STATE_SIZE 200 #define TEGRA_VIRTUAL_SE_TIMEOUT_1S 1000000 @@ -346,6 +367,21 @@ struct tegra_vse_gmac_req_data { uint8_t result; }; +enum hmac_sha_request_type { + HMAC_SHA_SIGN = 0U, + HMAC_SHA_VERIFY +}; + +/* HMAC-SHA request data */ +struct tegra_vse_hmac_sha_req_data { + /* Enum to specify HMAC-SHA request type i.e. SIGN/VERIFY */ + enum hmac_sha_request_type request_type; + /* Expected digest for HMAC_SHA_VERIFY request */ + char *expected_digest; + /* Hash comparison result for HMAC_SHA_VERIFY request */ + uint8_t result; +}; + struct tegra_vse_priv_data { struct skcipher_request *req; struct tegra_virtual_se_dev *se_dev; @@ -456,6 +492,20 @@ union tegra_virtual_se_sha_args { } op_hash; } __attribute__((__packed__)); +struct tegra_virtual_se_hmac_sha_args { + u8 keyslot[KEYSLOT_SIZE_BYTES]; + u32 mode; + u32 lastblock_len; + u8 lastblock[TEGRA_VIRTUAL_SE_SHA_HASH_BLOCK_SIZE_512BIT]; + u32 msg_total_length[4]; + u32 msg_left_length[4]; + u64 dst_addr; + u64 src_addr; + u32 src_buf_size; + u8 expected_hmac_sha[TEGRA_VIRTUAL_SE_SHA_MAX_HMAC_SHA_LENGTH]; + uint64_t hmac_addr; +}; + struct tegra_virtual_tsec_args { /** * Keyslot index for keyslot containing TSEC key @@ -506,6 +556,7 @@ struct tegra_virtual_se_ivc_tx_msg_t { union tegra_virtual_se_aes_args aes; union tegra_virtual_se_sha_args sha; struct tegra_virtual_tsec_args tsec; + struct tegra_virtual_se_hmac_sha_args hmac; }; }; @@ -591,6 +642,25 @@ struct crypto_dev_to_ivc_map *tegra_hv_vse_get_db(void) } EXPORT_SYMBOL(tegra_hv_vse_get_db); +static int status_to_errno(u32 err) +{ + switch (err) { + case 1: /* VSE_MSG_ERR_INVALID_CMD */ + case 3: /* VSE_MSG_ERR_INVALID_ARGS */ + case 11: /* VSE_MSG_ERR_MAC_INVALID */ + return -EINVAL; + case 4: /* VSE_MSG_ERR_INVALID_KEY */ + case 5: /* VSE_MSG_ERR_CTR_OVERFLOW */ + case 6: /* VSE_MSG_ERR_INVALID_SUBKEY */ + case 7: /* VSE_MSG_ERR_CTR_NONCE_INVALID */ + case 8: /* VSE_MSG_ERR_GCM_IV_INVALID */ + case 9: /* VSE_MSG_ERR_GCM_NONCE_INVALID */ + case 10: /* VSE_MSG_ERR_GMAC_INVALID_PARAMS */ + return -EPERM; + } + return err; +} + static int32_t validate_header( struct tegra_virtual_se_dev *se_dev, struct tegra_virtual_se_ivc_hdr_t *pivc_hdr, @@ -1807,6 +1877,463 @@ static int tegra_hv_vse_safety_sha_digest(struct ahash_request *req) return ret; } +static int tegra_hv_vse_safety_hmac_sha_setkey(struct crypto_ahash *tfm, const u8 *key, + unsigned int keylen) +{ + struct tegra_virtual_se_hmac_sha_context *ctx = + crypto_tfm_ctx(crypto_ahash_tfm(tfm)); + struct tegra_virtual_se_dev *se_dev; + int err = 0; + s8 label[TEGRA_VIRTUAL_SE_AES_MAX_KEY_SIZE]; + bool is_keyslot_label; + + if (!ctx) + return -EINVAL; + + se_dev = g_virtual_se_dev[g_crypto_to_ivc_map[ctx->node_id].se_engine]; + + if (keylen != 32) { + dev_err(se_dev->dev, "%s: Unsupported key length: %d", __func__, keylen); + return -EINVAL; + } + + /* format: 'NVSEAES 1234567\0' */ + is_keyslot_label = sscanf(key, "%s", label) == 1 && + !strcmp(label, TEGRA_VIRTUAL_SE_AES_KEYSLOT_LABEL); + + if (is_keyslot_label) { + ctx->keylen = keylen; + memcpy(ctx->aes_keyslot, key + KEYSLOT_OFFSET_BYTES, KEYSLOT_SIZE_BYTES); + ctx->is_key_slot_allocated = true; + } else { + dev_err(se_dev->dev, "%s: Invalid keyslot label %s\n", __func__, key); + return -EINVAL; + } + + return err; +} + +static int tegra_hv_vse_safety_hmac_sha_init(struct ahash_request *req) +{ + struct crypto_ahash *tfm; + struct tegra_virtual_se_req_context *req_ctx; + struct tegra_virtual_se_hmac_sha_context *hmac_ctx; + struct tegra_virtual_se_dev *se_dev = g_virtual_se_dev[VIRTUAL_SE_SHA]; + + if (!req) { + dev_err(se_dev->dev, "HMAC SHA request not valid\n"); + return -EINVAL; + } + + /* Return error if engine is in suspended state */ + if (atomic_read(&se_dev->se_suspended)) + return -ENODEV; + + req_ctx = ahash_request_ctx(req); + if (!req_ctx) { + dev_err(se_dev->dev, "HMAC SHA req_ctx not valid\n"); + return -EINVAL; + } + + tfm = crypto_ahash_reqtfm(req); + if (!tfm) { + dev_err(se_dev->dev, "HMAC SHA transform not valid\n"); + return -EINVAL; + } + + hmac_ctx = crypto_ahash_ctx(tfm); + hmac_ctx->digest_size = crypto_ahash_digestsize(tfm); + + if (!hmac_ctx->is_key_slot_allocated) { + pr_err("%s key is not allocated\n", __func__); + return -EINVAL; + } + + if (strcmp(crypto_ahash_alg_name(tfm), "hmac-sha256-vse") == 0) { + hmac_ctx->mode = VIRTUAL_SE_OP_MODE_SHA256; + hmac_ctx->blk_size = TEGRA_VIRTUAL_SE_SHA_HASH_BLOCK_SIZE_512BIT; + } else { + dev_err(se_dev->dev, "Invalid HMAC-SHA Alg\n"); + return -EINVAL; + } + + req_ctx->total_count = 0; + req_ctx->is_first = true; + req_ctx->req_context_initialized = true; + + return 0; +} + +static int tegra_hv_vse_safety_hmac_sha_sv_op(struct ahash_request *req, bool is_last) +{ + struct tegra_virtual_se_req_context *req_ctx = ahash_request_ctx(req); + struct tegra_virtual_se_hmac_sha_context *hmac_ctx = + crypto_ahash_ctx(crypto_ahash_reqtfm(req)); + struct tegra_vse_hmac_sha_req_data *hmac_req_data; + struct tegra_virtual_se_dev *se_dev = + g_virtual_se_dev[g_crypto_to_ivc_map[hmac_ctx->node_id].se_engine]; + struct tegra_virtual_se_ivc_hdr_t *ivc_hdr; + struct tegra_virtual_se_ivc_tx_msg_t *ivc_tx; + struct tegra_virtual_se_ivc_msg_t ivc_req_msg = {0}; + struct tegra_virtual_se_hmac_sha_args *phmac; + struct tegra_hv_ivc_cookie *pivck = g_crypto_to_ivc_map[hmac_ctx->node_id].ivck; + int err = 0; + struct tegra_vse_priv_data priv = {0}; + struct tegra_vse_tag *priv_data_ptr; + + u32 cmd = 0; + void *src_buf = NULL; + dma_addr_t src_buf_addr; + void *hash_buf = NULL; + dma_addr_t hash_buf_addr; + void *verify_result_buf = NULL; + dma_addr_t verify_result_addr; + void *match_code_buf = NULL; + dma_addr_t match_code_addr; + u32 matchcode = SE_HW_VALUE_MATCH_CODE; + u32 mismatch_code = SE_HW_VALUE_MISMATCH_CODE; + + u32 match_code_buf_size = 4; + u32 blocks_to_process, last_block_bytes = 0; + u64 msg_len = 0, temp_len = 0; + + if ((req->nbytes == 0) || (req->nbytes > TEGRA_VIRTUAL_SE_MAX_SUPPORTED_BUFLEN)) { + dev_err(se_dev->dev, "%s: input buffer size is invalid\n", __func__); + return -EINVAL; + } + + if (!is_last) { + if (req->nbytes % TEGRA_VIRTUAL_SE_SHA_HASH_BLOCK_SIZE_512BIT != 0) { + dev_err(se_dev->dev, "%s: non-last buffer size is invalid\n", __func__); + return -EINVAL; + } + } + + hmac_req_data = (struct tegra_vse_hmac_sha_req_data *) req->priv; + + src_buf = dma_alloc_coherent( + se_dev->dev, req->nbytes, + &src_buf_addr, GFP_KERNEL); + if (!src_buf) { + dev_err(se_dev->dev, "Cannot allocate memory for source buffer\n"); + return -ENOMEM; + } + + if (hmac_req_data->request_type == HMAC_SHA_SIGN) { + hash_buf = dma_alloc_coherent( + se_dev->dev, hmac_ctx->digest_size, + &hash_buf_addr, GFP_KERNEL); + if (!hash_buf) { + dev_err(se_dev->dev, "Cannot allocate memory for hash buffer\n"); + err = -ENOMEM; + goto unmap_exit; + } + memset(hash_buf, 0, TEGRA_VIRTUAL_SE_SHA_MAX_HMAC_SHA_LENGTH); + cmd = TEGRA_VIRTUAL_SE_CMD_HMAC_SIGN; + } else { + cmd = TEGRA_VIRTUAL_SE_CMD_HMAC_VERIFY; + } + + if ((se_dev->chipdata->hmac_verify_hw_support == true) + && (is_last && (hmac_req_data->request_type == HMAC_SHA_VERIFY))) { + verify_result_buf = dma_alloc_coherent( + se_dev->dev, TEGRA_VIRTUAL_SE_SHA_MAX_HMAC_SHA_LENGTH, + &verify_result_addr, GFP_KERNEL); + if (!verify_result_buf) { + dev_err(se_dev->dev, "Cannot allocate memory for verify_result_buf buffer\n"); + err = -ENOMEM; + goto unmap_exit; + } + match_code_buf = dma_alloc_coherent( + se_dev->dev, match_code_buf_size, + &match_code_addr, GFP_KERNEL); + if (!match_code_buf) { + dev_err(se_dev->dev, "Cannot allocate memory for match_code_buf buffer\n"); + err = -ENOMEM; + goto unmap_exit; + } + } + + g_crypto_to_ivc_map[hmac_ctx->node_id].vse_thread_start = true; + + msg_len = req->nbytes; + temp_len = msg_len; + req_ctx->total_count += msg_len; + + ivc_tx = &ivc_req_msg.tx[0]; + ivc_hdr = &ivc_req_msg.ivc_hdr; + ivc_hdr->num_reqs = 1; + ivc_hdr->header_magic[0] = 'N'; + ivc_hdr->header_magic[1] = 'V'; + ivc_hdr->header_magic[2] = 'D'; + ivc_hdr->header_magic[3] = 'A'; + ivc_hdr->engine = VIRTUAL_SE_SHA; + ivc_tx->cmd = cmd; + + phmac = &(ivc_tx->hmac); + phmac->mode = hmac_ctx->mode; + phmac->msg_total_length[2] = 0; + phmac->msg_total_length[3] = 0; + phmac->msg_left_length[2] = 0; + phmac->msg_left_length[3] = 0; + memcpy(phmac->keyslot, hmac_ctx->aes_keyslot, KEYSLOT_SIZE_BYTES); + phmac->src_addr = src_buf_addr; + + if (hmac_req_data->request_type == HMAC_SHA_SIGN) + phmac->dst_addr = hash_buf_addr; + + if (is_last) { + /* Set msg left length equal to input buffer size */ + phmac->msg_left_length[0] = msg_len & 0xFFFFFFFF; + phmac->msg_left_length[1] = msg_len >> 32; + + /* Set msg total length equal to sum of all input buffer size */ + phmac->msg_total_length[0] = req_ctx->total_count & 0xFFFFFFFF; + phmac->msg_total_length[1] = req_ctx->total_count >> 32; + + } else { + /* Set msg left length greater than input buffer size */ + temp_len += 8; + phmac->msg_left_length[0] = temp_len & 0xFFFFFFFF; + phmac->msg_left_length[1] = temp_len >> 32; + + /* Set msg total length greater than msg left length for non-first request */ + if (req_ctx->is_first) + req_ctx->is_first = false; + else + temp_len += 8; + + phmac->msg_total_length[0] = temp_len & 0xFFFFFFFF; + phmac->msg_total_length[1] = temp_len >> 32; + + } + + if (se_dev->chipdata->hmac_verify_hw_support == false) { + if (is_last && (hmac_req_data->request_type == HMAC_SHA_VERIFY)) { + blocks_to_process = msg_len / hmac_ctx->blk_size; + /* num of bytes less than block size */ + + if ((req->nbytes % hmac_ctx->blk_size) || + blocks_to_process == 0) { + last_block_bytes = + msg_len % hmac_ctx->blk_size; + } else { + /* decrement num of blocks */ + blocks_to_process--; + last_block_bytes = hmac_ctx->blk_size; + } + + if (blocks_to_process > 0) + sg_copy_to_buffer(req->src, (u32)sg_nents(req->src), src_buf, + blocks_to_process * hmac_ctx->blk_size); + + phmac->src_buf_size = blocks_to_process * hmac_ctx->blk_size; + phmac->lastblock_len = last_block_bytes; + + sg_pcopy_to_buffer(req->src, + (u32)sg_nents(req->src), + phmac->lastblock, + last_block_bytes, + blocks_to_process * hmac_ctx->blk_size); + + memcpy(phmac->expected_hmac_sha, hmac_req_data->expected_digest, + TEGRA_VIRTUAL_SE_SHA_MAX_HMAC_SHA_LENGTH); + } else { + phmac->src_buf_size = msg_len; + phmac->lastblock_len = 0; + sg_copy_to_buffer(req->src, (u32)sg_nents(req->src), + src_buf, msg_len); + } + } else { + phmac->src_buf_size = msg_len; + phmac->lastblock_len = 0; + sg_copy_to_buffer(req->src, (u32)sg_nents(req->src), + src_buf, msg_len); + if (is_last && (hmac_req_data->request_type == HMAC_SHA_VERIFY)) { + phmac->hmac_addr = verify_result_addr; + memcpy(verify_result_buf, hmac_req_data->expected_digest, + TEGRA_VIRTUAL_SE_SHA_MAX_HMAC_SHA_LENGTH); + phmac->dst_addr = match_code_addr; + } + } + + priv_data_ptr = (struct tegra_vse_tag *)ivc_req_msg.ivc_hdr.tag; + priv_data_ptr->priv_data = (unsigned int *)&priv; + priv.cmd = VIRTUAL_SE_PROCESS; + priv.se_dev = se_dev; + init_completion(&priv.alg_complete); + + err = tegra_hv_vse_safety_send_ivc_wait(se_dev, pivck, &priv, &ivc_req_msg, + sizeof(struct tegra_virtual_se_ivc_msg_t), hmac_ctx->node_id); + if (err) { + dev_err(se_dev->dev, "failed to send data over ivc err %d\n", err); + goto unmap_exit; + } + + if (priv.rx_status != 0) { + err = status_to_errno(priv.rx_status); + dev_err(se_dev->dev, "%s: SE server returned error %u\n", + __func__, priv.rx_status); + goto unmap_exit; + } + + if (is_last) { + if (hmac_req_data->request_type == HMAC_SHA_VERIFY) { + if (se_dev->chipdata->hmac_verify_hw_support == false) { + ivc_tx->cmd = TEGRA_VIRTUAL_SE_CMD_HMAC_GET_VERIFY; + priv.cmd = VIRTUAL_SE_PROCESS; + init_completion(&priv.alg_complete); + err = tegra_hv_vse_safety_send_ivc_wait(se_dev, pivck, &priv, + &ivc_req_msg, sizeof(struct tegra_virtual_se_ivc_msg_t), + hmac_ctx->node_id); + if (err) { + dev_err(se_dev->dev, + "failed to send data over ivc err %d\n", err); + goto unmap_exit; + } + + if (priv.rx_status == 0) { + hmac_req_data->result = 0; + } else if (priv.rx_status != TEGRA_VIRTUAL_SE_ERR_MAC_INVALID) { + dev_dbg(se_dev->dev, "%s: tag mismatch", __func__); + hmac_req_data->result = 1; + } else { + err = status_to_errno(priv.rx_status); + dev_err(se_dev->dev, "%s: SE server returned error %u\n", + __func__, priv.rx_status); + } + } else { + if (memcmp(match_code_buf, &matchcode, 4) == 0) { + hmac_req_data->result = 0; + } else if (memcmp(match_code_buf, &mismatch_code, 4) == 0) { + dev_dbg(se_dev->dev, "%s: tag mismatch", __func__); + hmac_req_data->result = 1; + } else { + dev_err(se_dev->dev, "%s: invalid tag match code", + __func__); + err = -EINVAL; + } + } + } else { + memcpy(req->result, hash_buf, TEGRA_VIRTUAL_SE_SHA_MAX_HMAC_SHA_LENGTH); + } + } + +unmap_exit: + if (src_buf) + dma_free_coherent(se_dev->dev, msg_len, src_buf, src_buf_addr); + + if (hash_buf) + dma_free_coherent(se_dev->dev, hmac_ctx->digest_size, hash_buf, hash_buf_addr); + + if (verify_result_buf) + dma_free_coherent(se_dev->dev, TEGRA_VIRTUAL_SE_SHA_MAX_HMAC_SHA_LENGTH, + verify_result_buf, verify_result_addr); + + if (match_code_buf) + dma_free_coherent(se_dev->dev, match_code_buf_size, match_code_buf, + match_code_addr); + + return err; +} + +static int tegra_hv_vse_safety_hmac_sha_update(struct ahash_request *req) +{ + struct tegra_virtual_se_req_context *req_ctx = ahash_request_ctx(req); + struct tegra_virtual_se_hmac_sha_context *hmac_ctx; + struct tegra_virtual_se_dev *se_dev; + int ret = 0; + + if (!req) { + pr_err("%s HMAC SHA request not valid\n", __func__); + return -EINVAL; + } + + if (!req_ctx->req_context_initialized) { + pr_err("%s Request ctx not initialized\n", __func__); + return -EINVAL; + } + + hmac_ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req)); + if (!hmac_ctx) { + pr_err("%s HMAC SHA req_ctx not valid\n", __func__); + return -EINVAL; + } + + if (!hmac_ctx->is_key_slot_allocated) { + pr_err("%s key is not allocated\n", __func__); + return -EINVAL; + } + + se_dev = g_virtual_se_dev[g_crypto_to_ivc_map[hmac_ctx->node_id].se_engine]; + + /* Return error if engine is in suspended state */ + if (atomic_read(&se_dev->se_suspended)) + return -ENODEV; + + ret = tegra_hv_vse_safety_hmac_sha_sv_op(req, false); + if (ret) + dev_err(se_dev->dev, "tegra_se_hmac_sha_update failed - %d\n", ret); + + return ret; +} + +static int tegra_hv_vse_safety_hmac_sha_finup(struct ahash_request *req) +{ + struct tegra_virtual_se_req_context *req_ctx = ahash_request_ctx(req); + struct tegra_virtual_se_hmac_sha_context *hmac_ctx = NULL; + struct tegra_virtual_se_dev *se_dev; + int ret = 0; + + if (!req) { + pr_err("%s HMAC-SHA request not valid\n", __func__); + return -EINVAL; + } + + hmac_ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req)); + if (!hmac_ctx) { + pr_err("%s HMAC-SHA req_ctx not valid\n", __func__); + return -EINVAL; + } + + if (!hmac_ctx->is_key_slot_allocated) { + pr_err("%s key is not allocated\n", __func__); + return -EINVAL; + } + + if (!req_ctx->req_context_initialized) { + pr_err("%s Request ctx not initialized\n", __func__); + return -EINVAL; + } + + se_dev = g_virtual_se_dev[g_crypto_to_ivc_map[hmac_ctx->node_id].se_engine]; + + /* Return error if engine is in suspended state */ + if (atomic_read(&se_dev->se_suspended)) + return -ENODEV; + + ret = tegra_hv_vse_safety_hmac_sha_sv_op(req, true); + if (ret) + dev_err(se_dev->dev, "tegra_se_hmac_sha_finup failed - %d\n", ret); + + hmac_ctx->is_key_slot_allocated = false; + req_ctx->req_context_initialized = false; + + return ret; +} + +static int tegra_hv_vse_safety_hmac_sha_final(struct ahash_request *req) +{ + // Unsupported + return -EINVAL; +} + +static int tegra_hv_vse_safety_hmac_sha_digest(struct ahash_request *req) +{ + // Unsupported + return -EINVAL; +} + static int tegra_hv_vse_safety_sha_export(struct ahash_request *req, void *out) { struct tegra_virtual_se_req_context *req_ctx = ahash_request_ctx(req); @@ -1868,25 +2395,6 @@ static void tegra_hv_vse_safety_prepare_cmd(struct tegra_virtual_se_dev *se_dev, } } -static int status_to_errno(u32 err) -{ - switch (err) { - case 1: /* VSE_MSG_ERR_INVALID_CMD */ - case 3: /* VSE_MSG_ERR_INVALID_ARGS */ - case 11: /* VSE_MSG_ERR_MAC_INVALID */ - return -EINVAL; - case 4: /* VSE_MSG_ERR_INVALID_KEY */ - case 5: /* VSE_MSG_ERR_CTR_OVERFLOW */ - case 6: /* VSE_MSG_ERR_INVALID_SUBKEY */ - case 7: /* VSE_MSG_ERR_CTR_NONCE_INVALID */ - case 8: /* VSE_MSG_ERR_GCM_IV_INVALID */ - case 9: /* VSE_MSG_ERR_GCM_NONCE_INVALID */ - case 10: /* VSE_MSG_ERR_GMAC_INVALID_PARAMS */ - return -EPERM; - } - return err; -} - static int tegra_hv_vse_safety_aes_gen_random_iv( struct tegra_virtual_se_dev *se_dev, struct skcipher_request *req, @@ -4822,7 +5330,31 @@ static struct ahash_alg sha_algs[] = { .cra_init = tegra_hv_vse_safety_sha_cra_init, .cra_exit = tegra_hv_vse_safety_sha_cra_exit, } - }, + }, { + .init = tegra_hv_vse_safety_hmac_sha_init, + .update = tegra_hv_vse_safety_hmac_sha_update, + .final = tegra_hv_vse_safety_hmac_sha_final, + .finup = tegra_hv_vse_safety_hmac_sha_finup, + .digest = tegra_hv_vse_safety_hmac_sha_digest, + .export = tegra_hv_vse_safety_sha_export, + .import = tegra_hv_vse_safety_sha_import, + .setkey = tegra_hv_vse_safety_hmac_sha_setkey, + .halg.digestsize = SHA256_DIGEST_SIZE, + .halg.statesize = sizeof(struct tegra_virtual_se_req_context), + .halg.base = { + .cra_name = "hmac-sha256-vse", + .cra_driver_name = "tegra-hv-vse-safety-hmac-sha256", + .cra_priority = 300, + .cra_flags = CRYPTO_ALG_TYPE_AHASH, + .cra_blocksize = SHA256_BLOCK_SIZE, + .cra_ctxsize = + sizeof(struct tegra_virtual_se_hmac_sha_context), + .cra_alignmask = 0, + .cra_module = THIS_MODULE, + .cra_init = tegra_hv_vse_safety_sha_cra_init, + .cra_exit = tegra_hv_vse_safety_sha_cra_exit, + } + } }; static const struct tegra_vse_soc_info t194_vse_sinfo = { @@ -4830,6 +5362,7 @@ static const struct tegra_vse_soc_info t194_vse_sinfo = { .cmac_hw_verify_supported = false, .sm_supported = false, .gcm_hw_iv_supported = false, + .hmac_verify_hw_support = false, }; static const struct tegra_vse_soc_info t234_vse_sinfo = { @@ -4837,6 +5370,7 @@ static const struct tegra_vse_soc_info t234_vse_sinfo = { .cmac_hw_verify_supported = false, .sm_supported = false, .gcm_hw_iv_supported = false, + .hmac_verify_hw_support = false, }; static const struct tegra_vse_soc_info se_51_vse_sinfo = { @@ -4844,6 +5378,7 @@ static const struct tegra_vse_soc_info se_51_vse_sinfo = { .cmac_hw_verify_supported = true, .sm_supported = true, .gcm_hw_iv_supported = true, + .hmac_verify_hw_support = true, }; static const struct of_device_id tegra_hv_vse_safety_of_match[] = { diff --git a/drivers/crypto/tegra-hv-vse.h b/drivers/crypto/tegra-hv-vse.h index 74de2853..9cfe089f 100644 --- a/drivers/crypto/tegra-hv-vse.h +++ b/drivers/crypto/tegra-hv-vse.h @@ -15,6 +15,7 @@ struct tegra_vse_soc_info { bool cmac_hw_verify_supported; bool sm_supported; bool gcm_hw_iv_supported; + bool hmac_verify_hw_support; }; /* GCM Operation Supported Flag */ @@ -143,6 +144,25 @@ struct tegra_virtual_se_sha_context { uint32_t node_id; }; +struct tegra_virtual_se_hmac_sha_context { + /* Security Engine device */ + struct tegra_virtual_se_dev *se_dev; + /* SHA operation mode */ + u8 mode; + u32 blk_size; + unsigned int digest_size; + /* Total bytes in all the requests */ + u64 total_count; + bool is_key_slot_allocated; + /* Keyslot for HMAC-SHA request */ + u8 aes_keyslot[KEYSLOT_SIZE_BYTES]; + /* key length in bits */ + u32 keylen; + /*Crypto dev instance*/ + uint32_t node_id; +}; + + /* Security Engine request context */ struct tegra_virtual_se_req_context { /* Security Engine device */ diff --git a/drivers/crypto/tegra-nvvse-cryptodev.c b/drivers/crypto/tegra-nvvse-cryptodev.c index 4fc4238b..5a8ebc33 100644 --- a/drivers/crypto/tegra-nvvse-cryptodev.c +++ b/drivers/crypto/tegra-nvvse-cryptodev.c @@ -147,6 +147,21 @@ struct tnvvse_cmac_req_data { uint8_t result; }; +enum tnvvse_hmac_sha_request_type { + HMAC_SHA_SIGN, + HMAC_SHA_VERIFY +}; + +/* HMAC SHA request data */ +struct tnvvse_hmac_sha_req_data { + /* Enum to specify HMAC-SHA request type i.e. SIGN/VERIFY */ + enum tnvvse_hmac_sha_request_type request_type; + /* Expected digest for HMAC_SHA_VERIFY request */ + char *expected_digest; + /* Hash comparison result for HMAC_SHA_VERIFY request */ + uint8_t result; +}; + #if (KERNEL_VERSION(6, 3, 0) <= LINUX_VERSION_CODE) static void tnvvse_crypto_complete(void *data, int err) { @@ -469,6 +484,193 @@ stop_sha: return ret; } +static int tnvvse_crypto_hmac_sha_sign_verify(struct tnvvse_crypto_ctx *ctx, + struct tegra_nvvse_hmac_sha_sv_ctl *hmac_sha_ctl) +{ + struct crypto_sha_state *sha_state = &ctx->sha_state; + struct tegra_virtual_se_hmac_sha_context *hmac_ctx; + struct crypto_ahash *tfm = NULL; + struct ahash_request *req = NULL; + char *src_buffer; + const char *driver_name; + struct tnvvse_crypto_completion sha_complete; + char key_as_keyslot[AES_KEYSLOT_NAME_SIZE] = {0,}; + struct tnvvse_hmac_sha_req_data priv_data; + struct scatterlist sg; + int ret = -ENOMEM; + uint32_t in_sz; + uint8_t *in_buf = NULL; + char *result = NULL; + + if (hmac_sha_ctl->data_length > ivc_database.max_buffer_size[ctx->node_id]) { + pr_err("%s(): Input size is (data = %d) is not supported\n", + __func__, hmac_sha_ctl->data_length); + return -EINVAL; + } + + if (sha_state->total_bytes == 0) { + if (hmac_sha_ctl->is_first != 1) { + pr_err("%s: HMAC-SHA first request is not yet received\n", + __func__); + return -EINVAL; + goto exit; + } + } + + if (hmac_sha_ctl->is_first == 1) { + tfm = crypto_alloc_ahash("hmac-sha256-vse", 0, 0); + if (IS_ERR(tfm)) { + ret = PTR_ERR(tfm); + pr_err("%s(): Failed to allocate ahash for hmac-sha256-vse: %d\n", + __func__, ret); + ret = -ENOMEM; + goto exit; + } + + hmac_ctx = crypto_ahash_ctx(tfm); + hmac_ctx->node_id = ctx->node_id; + + driver_name = crypto_tfm_alg_driver_name(crypto_ahash_tfm(tfm)); + if (driver_name == NULL) { + crypto_free_ahash(tfm); + pr_err("%s(): Failed to get_driver_name for hmac-sha256-vse returned NULL", + __func__); + ret = -EINVAL; + goto exit; + } + + req = ahash_request_alloc(tfm, GFP_KERNEL); + if (!req) { + crypto_free_ahash(tfm); + pr_err("%s(): Failed to allocate request for cmac-vse(aes)\n", __func__); + ret = -ENOMEM; + goto exit; + } + + ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, + tnvvse_crypto_complete, &sha_complete); + sha_state->tfm = tfm; + sha_state->req = req; + + (void)snprintf(key_as_keyslot, AES_KEYSLOT_NAME_SIZE, "NVSEAES "); + memcpy(key_as_keyslot + KEYSLOT_OFFSET_BYTES, hmac_sha_ctl->key_slot, + KEYSLOT_SIZE_BYTES); + + ret = crypto_ahash_setkey(tfm, key_as_keyslot, hmac_sha_ctl->key_length); + if (ret) { + pr_err("%s(): Failed to set keys for hmac: %d\n", __func__, ret); + goto free_tfm; + } + } else { + tfm = sha_state->tfm; + req = sha_state->req; + } + + init_completion(&sha_state->sha_complete.restart); + sha_state->sha_complete.req_err = 0; + + in_sz = hmac_sha_ctl->data_length; + in_buf = kzalloc(in_sz, GFP_KERNEL); + if (in_buf == NULL) { + ret = -ENOMEM; + goto free_tfm; + } + + result = kzalloc(crypto_ahash_digestsize(tfm), GFP_KERNEL); + if (result == NULL) { + ret = -ENOMEM; + goto free_buf; + } + + crypto_ahash_clear_flags(tfm, ~0U); + + if (hmac_sha_ctl->hmac_sha_type == TEGRA_NVVSE_HMAC_SHA_SIGN) + priv_data.request_type = HMAC_SHA_SIGN; + else + priv_data.request_type = HMAC_SHA_VERIFY; + + priv_data.result = 0; + req->priv = &priv_data; + + if (hmac_sha_ctl->is_first == 1) { + ret = wait_async_op(&sha_state->sha_complete, crypto_ahash_init(req)); + if (ret) { + pr_err("%s(): Failed to initialize ahash: %d\n", __func__, ret); + goto free_buf; + } + } + + src_buffer = hmac_sha_ctl->src_buffer; + + /* copy input buffer */ + ret = copy_from_user(in_buf, src_buffer, in_sz); + if (ret) { + pr_err("%s(): Failed to copy user input data: %d\n", __func__, ret); + goto free_buf; + } + + sg_init_one(&sg, in_buf, in_sz); + ahash_request_set_crypt(req, &sg, result, in_sz); + sha_state->total_bytes += in_sz; + + if (hmac_sha_ctl->is_last == 0) { + ret = wait_async_op(&sha_state->sha_complete, crypto_ahash_update(req)); + if (ret) { + pr_err("%s(): Failed to ahash_update: %d\n", __func__, ret); + goto free_buf; + } + } else { + if (hmac_sha_ctl->hmac_sha_type == TEGRA_NVVSE_HMAC_SHA_VERIFY) { + ret = copy_from_user((void *)result, + (void __user *)hmac_sha_ctl->digest_buffer, + crypto_ahash_digestsize(tfm)); + if (ret) { + pr_err("%s(): Failed to copy_from_user: %d\n", __func__, ret); + goto free_buf; + } + priv_data.expected_digest = result; + } + + ret = wait_async_op(&sha_state->sha_complete, crypto_ahash_finup(req)); + if (ret) { + pr_err("%s(): Failed to ahash_finup: %d\n", __func__, ret); + goto free_buf; + } + + if (hmac_sha_ctl->hmac_sha_type == TEGRA_NVVSE_HMAC_SHA_SIGN) { + ret = copy_to_user((void __user *)hmac_sha_ctl->digest_buffer, + (const void *)result, + crypto_ahash_digestsize(tfm)); + if (ret) + pr_err("%s(): Failed to copy_to_user: %d\n", __func__, ret); + } else { + hmac_sha_ctl->result = priv_data.result; + } + + sha_state->total_bytes = 0; + ahash_request_free(sha_state->req); + sha_state->req = NULL; + crypto_free_ahash(sha_state->tfm); + sha_state->tfm = NULL; + } + +free_buf: + //kfree won't fail even if input is NULL + kfree(result); + kfree(in_buf); + +free_tfm: + if (ret != 0) { + if (sha_state->req) + ahash_request_free(sha_state->req); + if (sha_state->tfm) + crypto_free_ahash(sha_state->tfm); + } + +exit: + return ret; +} + static int tnvvse_crypto_tsec_get_keyload_status(struct tnvvse_crypto_ctx *ctx, struct tegra_nvvse_tsec_get_keyload_status *tsec_keyload_status) { @@ -1613,6 +1815,8 @@ static long tnvvse_crypto_dev_ioctl(struct file *filp, struct tegra_nvvse_sha_init_ctl *sha_init_ctl; struct tegra_nvvse_sha_update_ctl *sha_update_ctl; struct tegra_nvvse_sha_final_ctl *sha_final_ctl; + struct tegra_nvvse_hmac_sha_sv_ctl *hmac_sha_sv_ctl; + struct tegra_nvvse_hmac_sha_sv_ctl __user *arg_hmac_sha_sv_ctl; struct tegra_nvvse_aes_enc_dec_ctl *aes_enc_dec_ctl; struct tegra_nvvse_aes_cmac_sign_verify_ctl *aes_cmac_sign_verify_ctl; struct tegra_nvvse_aes_drng_ctl *aes_drng_ctl; @@ -1692,6 +1896,33 @@ static long tnvvse_crypto_dev_ioctl(struct file *filp, kfree(sha_final_ctl); break; + case NVVSE_IOCTL_CMDID_HMAC_SHA_SIGN_VERIFY: + hmac_sha_sv_ctl = kzalloc(sizeof(*hmac_sha_sv_ctl), GFP_KERNEL); + if (!hmac_sha_sv_ctl) + return -ENOMEM; + + arg_hmac_sha_sv_ctl = (void __user *)arg; + + ret = copy_from_user(hmac_sha_sv_ctl, arg_hmac_sha_sv_ctl, + sizeof(*hmac_sha_sv_ctl)); + if (ret) { + pr_err("%s(): Failed to copy_from_user hmac_sha_sv_ctl:%d\n", __func__, + ret); + goto out; + } + + ret = tnvvse_crypto_hmac_sha_sign_verify(ctx, hmac_sha_sv_ctl); + + if (hmac_sha_sv_ctl->hmac_sha_type == TEGRA_NVVSE_HMAC_SHA_VERIFY) { + ret = copy_to_user(&arg_hmac_sha_sv_ctl->result, &hmac_sha_sv_ctl->result, + sizeof(uint8_t)); + if (ret) + pr_err("%s(): Failed to copy_to_user:%d\n", __func__, ret); + } + + kfree(hmac_sha_sv_ctl); + break; + case NVVSE_IOCTL_CMDID_AES_ENCDEC: aes_enc_dec_ctl = kzalloc(sizeof(*aes_enc_dec_ctl), GFP_KERNEL); if (!aes_enc_dec_ctl) { diff --git a/include/uapi/misc/tegra-nvvse-cryptodev.h b/include/uapi/misc/tegra-nvvse-cryptodev.h index e1667a11..789fe466 100644 --- a/include/uapi/misc/tegra-nvvse-cryptodev.h +++ b/include/uapi/misc/tegra-nvvse-cryptodev.h @@ -29,6 +29,7 @@ #define TEGRA_NVVSE_CMDID_GET_IVC_DB 12 #define TEGRA_NVVSE_CMDID_TSEC_SIGN_VERIFY 13 #define TEGRA_NVVSE_CMDID_TSEC_GET_KEYLOAD_STATUS 14 +#define TEGRA_NVVSE_CMDID_HMAC_SHA_SIGN_VERIFY 15 /** Defines the length of the AES-CBC Initial Vector */ #define TEGRA_NVVSE_AES_IV_LEN 16U @@ -42,6 +43,8 @@ #define TEGRA_NVVSE_AES_CMAC_LEN 16U /** Defines the counter offset byte in the AES Initial counter*/ #define TEGRA_COUNTER_OFFSET 12U +/** Defines the length of the HMAC SHA Hash */ +#define TEGRA_NVVSE_HMAC_SHA256_LEN 32U /** * @brief Defines SHA Types. @@ -69,6 +72,16 @@ enum tegra_nvvse_sha_type { TEGRA_NVVSE_SHA_TYPE_MAX, }; +/** + * \brief Defines HMAC SHA request type. + */ +enum tegra_nvvse_hmac_sha_sv_type { + /** Defines AES GMAC Sign */ + TEGRA_NVVSE_HMAC_SHA_SIGN = 0u, + /** Defines AES GMAC Verify */ + TEGRA_NVVSE_HMAC_SHA_VERIFY, +}; + /** * \brief Defines AES modes. */ @@ -142,6 +155,55 @@ struct tegra_nvvse_sha_final_ctl { #define NVVSE_IOCTL_CMDID_FINAL_SHA _IOWR(TEGRA_NVVSE_IOC_MAGIC, TEGRA_NVVSE_CMDID_FINAL_SHA, \ struct tegra_nvvse_sha_final_ctl) +struct tegra_nvvse_hmac_sha_sv_ctl { + /** [in] Holds the enum which indicates SHA mode */ + enum tegra_nvvse_sha_type hmac_sha_mode; + /** [in] Holds the enum which indicates HMAC SHA Sign or Verify */ + enum tegra_nvvse_hmac_sha_sv_type hmac_sha_type; + /** [in] Holds a Boolean that specifies whether this is first + * chunk of message for HMAC-SHA Sign/Verify. + * '0' value indicates it is not First call and + * Non zero value indicates it is the first call. + */ + uint8_t is_first; + /** [in] Holds a Boolean that specifies whether this is last + * chunk of message for HMAC-SHA Sign/Verify. + * '0' value indicates it is not Last call and + * Non zero value indicates it is the Last call. + */ + uint8_t is_last; + /** [in] Holds a keyslot handle which is used for HMAC-SHA operation */ + uint8_t key_slot[KEYSLOT_SIZE_BYTES]; + /** [in] Holds the Key length + * Supported keylength is only 16 bytes and 32 bytes + */ + uint8_t key_length; + /** [in] Holds a pointer to the input source buffer for which + * HMAC-SHA is to be calculated/verified. + */ + uint8_t *src_buffer; + /** [in] Holds the Length of the input source buffer. + * data_length shall not be "0" supported for single part sign and verify + * data_length shall be multiple of hashblock size if it is not the last chunk + * i.e when is_last is "0" + */ + uint32_t data_length; + /** Holds the pointer of the digest buffer */ + uint8_t *digest_buffer; + /** Holds the digest buffer length */ + uint32_t digest_length; + /** [out] Holds HMAC-SHA verification result, which the driver updates. + * Valid only when hmac_sha_type is TEGRA_NVVSE_HMAC_SHA_VERIFY. + * Result values are: + * - '0' indicates HMAC-SHA verification success. + * - Non-zero value indicates HMAC-SHA verification failure. + */ + uint8_t result; +}; +#define NVVSE_IOCTL_CMDID_HMAC_SHA_SIGN_VERIFY _IOWR(TEGRA_NVVSE_IOC_MAGIC, \ + TEGRA_NVVSE_CMDID_HMAC_SHA_SIGN_VERIFY, \ + struct tegra_nvvse_hmac_sha_sv_ctl) + /** * \brief Holds AES encrypt/decrypt parameters for IO Control. */