diff --git a/drivers/crypto/caam/Kconfig b/drivers/crypto/caam/Kconfig index 3931165bb9f7..845f69f0916b 100644 --- a/drivers/crypto/caam/Kconfig +++ b/drivers/crypto/caam/Kconfig @@ -8,6 +8,9 @@ config CRYPTO_DEV_FSL_CAAM_CRYPTO_API_DESC config CRYPTO_DEV_FSL_CAAM_AHASH_API_DESC tristate +config CRYPTO_DEV_FSL_CAAM_KEYBLOB_API_DESC + tristate + config CRYPTO_DEV_FSL_CAAM_SECVIO tristate "CAAM/SNVS Security Violation Handler (EXPERIMENTAL)" depends on ARCH_MXC @@ -158,12 +161,25 @@ config CRYPTO_DEV_FSL_CAAM_RNG_API config CRYPTO_DEV_FSL_CAAM_TK_API bool "Register tagged key cryptography implementations with Crypto API" + default y select CRYPTO_DEV_FSL_CAAM_CRYPTO_API + select CRYPTO_DEV_FSL_CAAM_KEYBLOB_API_DESC help - Selecting this will register algorithms supporting tagged key. + Selecting this will register algorithms supporting tagged key and + generate black keys and encapsulate them into black blobs. - Tagged keys are keys that contain metadata indicating what + Tagged keys are black keys that contain metadata indicating what they are and how to handle them. + CAAM protects data in a data structure called a Blob, which provides + both confidentiality and integrity protection. + +config CRYPTO_DEV_FSL_CAAM_TK_API_TEST + tristate "CAAM keys and blobs test" + depends on CRYPTO_DEV_FSL_CAAM_TK_API + depends on m + help + Test to exercise black key generation and blob encapsulation and + decapsulation. config CRYPTO_DEV_FSL_CAAM_RNG_TEST bool "Test caam rng" diff --git a/drivers/crypto/caam/Makefile b/drivers/crypto/caam/Makefile index 79da340a069c..3b0c574af802 100644 --- a/drivers/crypto/caam/Makefile +++ b/drivers/crypto/caam/Makefile @@ -13,11 +13,12 @@ obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM) += caam.o obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_JR) += caam_jr.o obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API_DESC) += caamalg_desc.o obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_AHASH_API_DESC) += caamhash_desc.o +obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_KEYBLOB_API_DESC) += caamkeyblob_desc.o obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_JR_UIO) += fsl_jr_uio.o caam-y := ctrl.o caam_jr-y := jr.o key_gen.o -caam_jr-$(CONFIG_CRYPTO_DEV_FSL_CAAM_TK_API) += tag_object.o +caam_jr-$(CONFIG_CRYPTO_DEV_FSL_CAAM_TK_API) += tag_object.o caamkeyblob.o caam_jr-$(CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API) += caamalg.o caam_jr-$(CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API_QI) += caamalg_qi.o caam_jr-$(CONFIG_CRYPTO_DEV_FSL_CAAM_AHASH_API) += caamhash.o @@ -26,6 +27,7 @@ caam_jr-$(CONFIG_CRYPTO_DEV_FSL_CAAM_PKC_API) += caampkc.o pkc_desc.o caam_jr-$(CONFIG_CRYPTO_DEV_FSL_CAAM_SM) += sm_store.o obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_SM_TEST) += sm_test.o obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_SECVIO) += secvio.o +obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_TK_API_TEST) += caamkeyblob_test.o caam-$(CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API_QI) += qi.o ifneq ($(CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API_QI),) diff --git a/drivers/crypto/caam/caamkeyblob.c b/drivers/crypto/caam/caamkeyblob.c new file mode 100644 index 000000000000..9ef30f5e7fef --- /dev/null +++ b/drivers/crypto/caam/caamkeyblob.c @@ -0,0 +1,696 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Black key generation and blob encapsulation/decapsulation for CAAM + * + * Copyright 2018-2020 NXP + */ +#include "caamkeyblob.h" +#include "error.h" + +/* Black key generation and blob encap/decap job completion handler */ +static void caam_key_blob_done(struct device *dev, u32 *desc, u32 err, + void *context) +{ + struct jr_job_result *res = context; + int ecode = 0; + + dev_dbg(dev, "%s %d: err 0x%x\n", __func__, __LINE__, err); + + if (err) + ecode = caam_jr_strstatus(dev, err); + + /* Save the error for post-processing */ + res->error = ecode; + /* Mark job as complete */ + complete(&res->completion); +} + +/** + * map_write_data - Prepare data to be written to CAAM + * + * @dev : struct device of the job ring to be used + * @data : The data to be prepared + * @size : The size of data to be prepared + * @dma_addr : The retrieve DMA address of the input data + * @allocated_data : Pointer to a DMA-able address where the input + * data is copied and synchronized + * + * Return : '0' on success, error code otherwise + */ +static int map_write_data(struct device *dev, const u8 *data, size_t size, + dma_addr_t *dma_addr, u8 **allocated_data) +{ + int ret = 0; + + /* Allocate memory for data and copy it to DMA zone */ + *allocated_data = kmemdup(data, size, GFP_KERNEL | GFP_DMA); + if (!*allocated_data) { + ret = -ENOMEM; + goto exit; + } + + *dma_addr = dma_map_single(dev, *allocated_data, size, DMA_TO_DEVICE); + if (dma_mapping_error(dev, *dma_addr)) { + dev_err(dev, "Unable to map write data\n"); + ret = -ENOMEM; + goto free_alloc; + } + + goto exit; + +free_alloc: + kfree(*allocated_data); + +exit: + return ret; +} + +/** + * map_read_data - Prepare data to be read from CAAM + * + * @dev : struct device of the job ring to be used + * @size : The size of data to be prepared + * @dma_addr : The retrieve DMA address of the data to be read + * @allocated_data : Pointer to a DMA-able address where the data + * to be read will be copied and synchronized + * + * Return : '0' on success, error code otherwise + */ +static int map_read_data(struct device *dev, size_t size, dma_addr_t *dma_addr, + u8 **allocated_data) +{ + int ret = 0; + + /* Allocate memory for data compatible with DMA */ + *allocated_data = kmalloc(size, GFP_KERNEL | GFP_DMA); + if (!*allocated_data) { + ret = -ENOMEM; + goto exit; + } + + *dma_addr = dma_map_single(dev, *allocated_data, size, DMA_FROM_DEVICE); + if (dma_mapping_error(dev, *dma_addr)) { + dev_err(dev, "Unable to map read data\n"); + ret = -ENOMEM; + goto free_alloc; + } + + goto exit; + +free_alloc: + kfree(*allocated_data); + +exit: + return ret; +} + +/** + * read_map_data - Read the data from CAAM + * + * @dev : struct device of the job ring to be used + * @data : The read data from CAAM will be copied here + * @dma_addr : The DMA address of the data to be read + * @allocated_data : Pointer to a DMA-able address where the data + * to be read is + * @size : The size of data to be read + */ +static void read_map_data(struct device *dev, u8 *data, dma_addr_t dma_addr, + u8 *allocated_data, size_t size) +{ + /* Synchronize the DMA and copy the data */ + dma_sync_single_for_cpu(dev, dma_addr, size, DMA_FROM_DEVICE); + memcpy(data, allocated_data, size); +} + +/** + * unmap_read_write_data - Unmap the data needed for or from CAAM + * + * @dev : struct device of the job ring to be used + * @dma_addr : The DMA address of the data used for DMA transfer + * @allocated_data : The data used for DMA transfer + * @size : The size of data + * @dir : The DMA_API direction + */ +static void unmap_read_write_data(struct device *dev, dma_addr_t dma_addr, + u8 *allocated_data, size_t size, + enum dma_data_direction dir) +{ + /* Free the resources and clear the data*/ + dma_unmap_single(dev, dma_addr, size, dir); + kzfree(allocated_data); +} + +/** + * get_caam_dma_addr - Get the CAAM DMA address of a physical address. + * + * @phy_address : The physical address + * + * Return : The CAAM DMA address + */ +static dma_addr_t get_caam_dma_addr(const void *phy_address) +{ + uintptr_t ptr_conv; + dma_addr_t caam_dma_address = 0; + + /* Check if conversion is possible */ + if (sizeof(caam_dma_address) < sizeof(phy_address)) { + /* + * Check that all bits sets in the phy_address + * can be stored in caam_dma_address + */ + + /* Generate a mask of the representable bits */ + u64 mask = GENMASK_ULL(sizeof(caam_dma_address) * 8 - 1, 0); + + /* + * Check that the bits not representable of + * the physical address are not set + */ + if ((uintptr_t)phy_address & ~mask) + goto exit; + } + + /* Convert address to caam_dma_address */ + ptr_conv = (uintptr_t)phy_address; + caam_dma_address = (dma_addr_t)ptr_conv; + +exit: + return caam_dma_address; +} + +/** + * generate_black_key - Generate a black key from a plaintext or random, + * based on the given input: a size for a random black + * key, or a plaintext (input key). + * + * If the memory type is Secure Memory, the key to cover is read + * directly by CAAM from Secure Memory without intermediate copy. + * The value of the input key (plaintext) must be a physical address + * in Secure Memory. + * + * Notes: + * Limited to Class 1 keys, at the present time. + * The input and output data are copied to temporary arrays + * except for the input key if the memory type is Secure Memory. + * For now, we have support for Black keys, stored in General Memory. + * + * @dev : struct device of the job ring to be used + * @info : keyblob_info structure, will be updated with + * the black key data from CAAM. + * This contains, also, all the data necessary to generate + * a black key from plaintext/random like: key encryption + * key, memory type, input key, etc. + * + * Return : '0' on success, error code otherwise + */ +int generate_black_key(struct device *dev, struct keyblob_info *info) +{ + int ret = 0; + bool not_random = false; + u8 trusted_key, key_enc; + u32 *desc = NULL; + size_t black_key_length_req = 0; + dma_addr_t key_dma, black_key_dma; + u8 *tmp_key = NULL, *tmp_black_key = NULL; + + /* Validate device */ + if (!dev) + return -EINVAL; + + /* + * If an input key (plaintext) is given, + * generate a black key from it, not from random + */ + if (info->key) + not_random = true; + + /* Get trusted key and key encryption type from type */ + trusted_key = (info->type >> TAG_OBJ_TK_OFFSET) & 0x1; + key_enc = (info->type >> TAG_OBJ_EKT_OFFSET) & 0x1; + + dev_dbg(dev, "%s input: [key: (%zu) black_key: %p(%zu), key_enc: %x]\n", + __func__, info->key_len, info->black_key, info->black_key_len, + key_enc); + if (not_random) + print_hex_dump_debug("input key @" __stringify(__LINE__) ": ", + DUMP_PREFIX_ADDRESS, 16, 4, info->key, + info->key_len, 1); + + /* Validate key type - only JDKEK keys are supported */ + if (!is_key_type(info->type) || is_trusted_type(info->type)) + return -EINVAL; + + /* + * Validate key size, expected values are + * between 16 and 64 bytes. + * See TODO from cnstr_desc_black_key(). + */ + if (info->key_len < MIN_KEY_SIZE || info->key_len > MAX_KEY_SIZE) + return -EINVAL; + + /* + * Based on key encryption type (ecb or ccm), + * compute the black key size + */ + if (key_enc == KEY_COVER_ECB) + /* + * ECB-Black Key will be padded with zeros to make it a + * multiple of 16 bytes long before it is encrypted, + * and the resulting Black Key will be this length. + */ + black_key_length_req = ECB_BLACK_KEY_SIZE(info->key_len); + else if (key_enc == KEY_COVER_CCM) + /* + * CCM-Black Key will always be at least 12 bytes longer, + * since the encapsulation uses a 6-byte nonce and adds + * a 6-byte ICV. But first, the key is padded as necessary so + * that CCM-Black Key is a multiple of 8 bytes long. + */ + black_key_length_req = CCM_BLACK_KEY_SIZE(info->key_len); + + /* Check if there is enough space for black key */ + if (info->black_key_len < black_key_length_req) { + info->black_key_len = black_key_length_req; + return -EINVAL; + } + + /* Black key will have at least the same length as the input key */ + info->black_key_len = info->key_len; + + dev_dbg(dev, "%s processing: [key: (%zu) black_key: %p(%zu)", + __func__, info->key_len, info->black_key, info->black_key_len); + dev_dbg(dev, "req:%zu, key_enc: 0x%x]\n", black_key_length_req, key_enc); + + if (not_random) { + /* Map input key, this will be sent to CAAM */ + if (map_write_data(dev, info->key, info->key_len, + &key_dma, &tmp_key)) { + dev_err(dev, "Unable to map input key\n"); + ret = -ENOMEM; + goto exit; + } + } + + /* Map black key, this will be read from CAAM */ + if (map_read_data(dev, black_key_length_req, + &black_key_dma, &tmp_black_key)) { + dev_err(dev, "Unable to map black key\n"); + ret = -ENOMEM; + goto unmap_input_key; + } + + /* Construct descriptor for black key */ + if (not_random) + ret = cnstr_desc_black_key(&desc, key_dma, info->key_len, + black_key_dma, info->black_key_len, + key_enc, trusted_key); + else + ret = cnstr_desc_random_black_key(&desc, info->key_len, + black_key_dma, + info->black_key_len, + key_enc, trusted_key); + + if (ret) { + dev_err(dev, + "Failed to construct the descriptor for black key\n"); + goto unmap_black_key; + } + + /* Execute descriptor and wait for its completion */ + ret = caam_jr_run_and_wait_for_completion(dev, desc, + caam_key_blob_done); + if (ret) { + dev_err(dev, "Failed to execute black key descriptor\n"); + goto free_desc; + } + + /* Read black key from CAAM */ + read_map_data(dev, info->black_key, black_key_dma, + tmp_black_key, black_key_length_req); + + /* Update black key length with the correct size */ + info->black_key_len = black_key_length_req; + +free_desc: + kfree(desc); + +unmap_black_key: + unmap_read_write_data(dev, black_key_dma, tmp_black_key, + black_key_length_req, DMA_FROM_DEVICE); + +unmap_input_key: + if (not_random) + unmap_read_write_data(dev, key_dma, tmp_key, info->key_len, + DMA_TO_DEVICE); + +exit: + return ret; +} +EXPORT_SYMBOL(generate_black_key); + +/** + * caam_blob_encap - Encapsulate a black key into a blob + * + * If the memory type is Secure Memory, the key to encapsulate is read + * directly by CAAM from Secure Memory without intermediate copy. + * The value of the key (black key) must be a physical address + * in Secure Memory. + * + * Notes: + * For now, we have support for Black keys, stored in General Memory and + * encapsulated into black blobs. + * + * @dev : struct device of the job ring to be used + * @info : keyblob_info structure, will be updated with + * the blob data from CAAM. + * This contains, also, all the data necessary to + * encapsulate a black key into a blob: key encryption + * key, memory type, color, etc. + * + * Return : '0' on success, error code otherwise + */ +int caam_blob_encap(struct device *dev, struct keyblob_info *info) +{ + int ret = 0; + u32 *desc = NULL; + size_t black_key_real_len = 0; + u8 mem_type, color, key_enc, trusted_key; + dma_addr_t black_key_dma, key_mod_dma, blob_dma; + unsigned char *blob = info->blob + BLOB_HEADER_SIZE; + u8 *tmp_black_key = NULL, *tmp_key_mod = NULL, *tmp_blob = NULL; + + /* Validate device */ + if (!dev) + return -EINVAL; + + /* + * Get memory type, trusted key, key encryption + * type and color from type + */ + mem_type = (info->type >> TAG_OBJ_MEM_OFFSET) & 0x1; + color = (info->type >> TAG_OBJ_COLOR_OFFSET) & 0x1; + key_enc = (info->type >> TAG_OBJ_EKT_OFFSET) & 0x1; + trusted_key = (info->type >> TAG_OBJ_TK_OFFSET) & 0x1; + + /* Validate input data*/ + if (!info->black_key || !info->key_mod || !blob) + return -EINVAL; + + /* Validate object type - only JDKEK keys are supported */ + if (is_trusted_type(info->type)) + return -EINVAL; + + dev_dbg(dev, "%s input:[black_key: %p (%zu) color: %x, key_enc: %x", + __func__, info->black_key, info->black_key_len, color, key_enc); + dev_dbg(dev, ", key_mod: %p (%zu)", info->key_mod, info->key_mod_len); + dev_dbg(dev, "blob: %p (%zu)]\n", blob, info->blob_len); + + /* + * Based on memory type, the key modifier length + * can be 8-byte or 16-byte. + */ + if (mem_type == DATA_SECMEM) + info->key_mod_len = KEYMOD_SIZE_SM; + else + info->key_mod_len = KEYMOD_SIZE_GM; + + /* Adapt the size of the black key */ + black_key_real_len = info->black_key_len; + + /* Check if the blob can be stored */ + if (info->blob_len < (black_key_real_len + BLOB_OVERHEAD)) + return -EINVAL; + + /* Update the blob length */ + info->blob_len = black_key_real_len + BLOB_OVERHEAD; + + dev_dbg(dev, "%s processing: [black_key: %p (%zu) cnstr: %zu", + __func__, info->black_key, info->black_key_len, + black_key_real_len); + dev_dbg(dev, " color: %x key_enc: %x, mem_type: %x,", + color, key_enc, mem_type); + dev_dbg(dev, ", key_mod: %p (%zu) ", info->key_mod, info->key_mod_len); + dev_dbg(dev, "blob: %p (%zu)]\n", blob, info->blob_len); + + /* Map black key, this will be transferred to CAAM */ + if (mem_type == DATA_GENMEM) { + if (map_write_data(dev, info->black_key, info->black_key_len, + &black_key_dma, &tmp_black_key)) { + dev_err(dev, "Unable to map black key for blob\n"); + ret = -ENOMEM; + goto exit; + } + } else { + black_key_dma = get_caam_dma_addr(info->black_key); + if (!black_key_dma) + return -ENOMEM; + } + + /* Map key modifier, this will be sent to CAAM */ + if (mem_type == DATA_GENMEM) { + if (map_write_data(dev, info->key_mod, info->key_mod_len, + &key_mod_dma, &tmp_key_mod)) { + dev_err(dev, "Unable to map key_mod for blob\n"); + ret = -ENOMEM; + goto unmap_black_key; + } + } else { + key_mod_dma = get_caam_dma_addr(info->key_mod); + if (!key_mod_dma) + return -ENOMEM; + } + + /* Map blob, this will be read to CAAM */ + if (mem_type == DATA_GENMEM) { + if (map_read_data(dev, info->blob_len, &blob_dma, &tmp_blob)) { + dev_err(dev, "Unable to map blob\n"); + ret = -ENOMEM; + goto unmap_key_mod; + } + } else { + blob_dma = get_caam_dma_addr(info->blob); + if (!blob_dma) + return -ENOMEM; + } + + /* Construct descriptor for blob encapsulation */ + ret = cnstr_desc_blob_encap(&desc, black_key_dma, black_key_real_len, + color, key_enc, trusted_key, mem_type, + key_mod_dma, info->key_mod_len, + blob_dma, info->blob_len); + if (ret) { + dev_err(dev, + "Failed to construct the descriptor for blob encap\n"); + goto unmap_blob; + } + + /* Execute descriptor and wait for its completion */ + ret = caam_jr_run_and_wait_for_completion(dev, desc, + caam_key_blob_done); + if (ret) { + dev_err(dev, "Failed to execute blob encap descriptor\n"); + goto free_desc; + } + + /* Read blob from CAAM */ + if (mem_type == DATA_GENMEM) + read_map_data(dev, blob, blob_dma, tmp_blob, info->blob_len); + + print_hex_dump_debug("blob @" __stringify(__LINE__) ": ", + DUMP_PREFIX_ADDRESS, 16, 4, blob, + info->blob_len, 1); +free_desc: + kfree(desc); + +unmap_blob: + if (mem_type == DATA_GENMEM) + unmap_read_write_data(dev, blob_dma, tmp_blob, + info->blob_len, DMA_FROM_DEVICE); + +unmap_key_mod: + if (mem_type == DATA_GENMEM) + unmap_read_write_data(dev, key_mod_dma, tmp_key_mod, + info->key_mod_len, DMA_TO_DEVICE); + +unmap_black_key: + if (mem_type == DATA_GENMEM) + unmap_read_write_data(dev, black_key_dma, tmp_black_key, + info->black_key_len, DMA_TO_DEVICE); + +exit: + return ret; +} +EXPORT_SYMBOL(caam_blob_encap); + +/** + * caam_blob_decap - Decapsulate a black key from a blob + * + * Notes: + * For now, we have support for Black blob, stored in General Memory and + * can be decapsulated into a black key. + * + * @dev : struct device of the job ring to be used + * @info : keyblob_info structure, will be updated with + * the black key decapsulated from the blob. + * This contains, also, all the data necessary to + * encapsulate a black key into a blob: key encryption + * key, memory type, color, etc. + * + * Return : '0' on success, error code otherwise + */ +int caam_blob_decap(struct device *dev, struct keyblob_info *info) +{ + int ret = 0; + u32 *desc = NULL; + u8 mem_type, color, key_enc, trusted_key; + size_t black_key_real_len; + dma_addr_t key_mod_dma, black_key_dma, blob_dma; + unsigned char *blob = info->blob + BLOB_HEADER_SIZE; + u8 *tmp_black_key = NULL, *tmp_key_mod = NULL, *tmp_blob = NULL; + + /* Validate device */ + if (!dev) + return -EINVAL; + + /* + * Get memory type, trusted key, key encryption + * type and color from type + */ + mem_type = (info->type >> TAG_OBJ_MEM_OFFSET) & 0x1; + color = (info->type >> TAG_OBJ_COLOR_OFFSET) & 0x1; + key_enc = (info->type >> TAG_OBJ_EKT_OFFSET) & 0x1; + trusted_key = (info->type >> TAG_OBJ_TK_OFFSET) & 0x1; + + /* Validate input data*/ + if (!info->black_key || !info->key_mod || !blob) + return -EINVAL; + + dev_dbg(dev, "%s input: [blob: %p (%zu), mem_type: %x, color: %x", + __func__, blob, info->blob_len, mem_type, color); + dev_dbg(dev, " keymod: %p (%zu)", info->key_mod, info->key_mod_len); + dev_dbg(dev, " secret: %p (%zu) key_enc: %x]\n", + info->black_key, info->black_key_len, key_enc); + + /* Validate object type - only JDKEK keys are supported */ + if (is_trusted_type(info->type)) + return -EINVAL; + + print_hex_dump_debug("blob @" __stringify(__LINE__) ": ", + DUMP_PREFIX_ADDRESS, 16, 4, blob, + info->blob_len, 1); + + /* + * Based on memory type, the key modifier length + * can be 8-byte or 16-byte. + */ + if (mem_type == DATA_SECMEM) + info->key_mod_len = KEYMOD_SIZE_SM; + else + info->key_mod_len = KEYMOD_SIZE_GM; + + /* Check if the blob is valid */ + if (info->blob_len <= BLOB_OVERHEAD) + return -EINVAL; + + /* Initialize black key length */ + black_key_real_len = info->blob_len - BLOB_OVERHEAD; + + /* Check if the black key has enough space to be stored */ + if (info->black_key_len < black_key_real_len) + return -EINVAL; + + /* Update black key length with the one computed based on key_enc */ + info->black_key_len = black_key_real_len; + + dev_dbg(dev, "%s processing: [blob: %p (%zu), mem_type: %x, color: %x,", + __func__, blob, info->blob_len, mem_type, color); + dev_dbg(dev, " key_mod: %p (%zu), black_key: %p (%zu) real_len: %zu]\n", + info->key_mod, info->key_mod_len, info->black_key, + info->black_key_len, black_key_real_len); + + /* Map blob, this will be transferred to CAAM */ + if (mem_type == DATA_GENMEM) { + if (map_write_data(dev, blob, info->blob_len, + &blob_dma, &tmp_blob)) { + dev_err(dev, "Unable to map blob for decap\n"); + ret = -ENOMEM; + goto exit; + } + } else { + blob_dma = get_caam_dma_addr(blob); + if (!blob_dma) + return -ENOMEM; + } + + /* Map key modifier, this will be sent to CAAM */ + if (mem_type == DATA_GENMEM) { + if (map_write_data(dev, info->key_mod, info->key_mod_len, + &key_mod_dma, &tmp_key_mod)) { + dev_err(dev, "Unable to map key_mod for blob decap\n"); + ret = -ENOMEM; + goto unmap_blob; + } + } else { + key_mod_dma = get_caam_dma_addr(info->key_mod); + if (!key_mod_dma) + return -ENOMEM; + } + + /* Map black key, this will be read from CAAM */ + if (mem_type == DATA_GENMEM) { + if (map_read_data(dev, info->black_key_len, + &black_key_dma, &tmp_black_key)) { + dev_err(dev, "Unable to map black key for blob decap\n"); + ret = -ENOMEM; + goto unmap_key_mod; + } + } else { + black_key_dma = get_caam_dma_addr(info->black_key); + if (!black_key_dma) + return -ENOMEM; + } + + ret = cnstr_desc_blob_decap(&desc, blob_dma, info->blob_len, + key_mod_dma, info->key_mod_len, + black_key_dma, black_key_real_len, + color, key_enc, trusted_key, mem_type); + if (ret) { + dev_err(dev, + "Failed to construct the descriptor for blob decap\n"); + goto unmap_black_key; + } + + ret = caam_jr_run_and_wait_for_completion(dev, desc, + caam_key_blob_done); + if (ret) { + dev_err(dev, "Failed to execute blob decap descriptor\n"); + goto free_desc; + } + + /* Read black key from CAAM */ + if (mem_type == DATA_GENMEM) + read_map_data(dev, info->black_key, black_key_dma, + tmp_black_key, info->black_key_len); + +free_desc: + kfree(desc); + +unmap_black_key: + if (mem_type == DATA_GENMEM) + unmap_read_write_data(dev, black_key_dma, tmp_black_key, + info->black_key_len, DMA_FROM_DEVICE); + +unmap_key_mod: + if (mem_type == DATA_GENMEM) + unmap_read_write_data(dev, key_mod_dma, tmp_key_mod, + info->key_mod_len, DMA_TO_DEVICE); + +unmap_blob: + if (mem_type == DATA_GENMEM) + unmap_read_write_data(dev, blob_dma, tmp_blob, + info->blob_len, DMA_TO_DEVICE); + +exit: + return ret; +} +EXPORT_SYMBOL(caam_blob_decap); diff --git a/drivers/crypto/caam/caamkeyblob.h b/drivers/crypto/caam/caamkeyblob.h new file mode 100644 index 000000000000..a1b21b95072f --- /dev/null +++ b/drivers/crypto/caam/caamkeyblob.h @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */ +/* + * Black key generation and blob encapsulation/decapsualtion for CAAM + * + * Copyright 2018-2020 NXP + */ + +#ifndef _CAAMKEYBLOB_H_ +#define _CAAMKEYBLOB_H_ + +#include +#include "caamkeyblob_desc.h" + +/* + * Minimum key size to be used is 16 bytes and maximum key size fixed + * is 64 bytes. + * Blob size to be kept is Maximum key size + blob header added by CAAM. + */ + +#define MIN_KEY_SIZE 16 +#define MAX_KEY_SIZE 64 + +#define MAX_BLACK_KEY_SIZE (MAX_KEY_SIZE + CCM_OVERHEAD +\ + TAG_OVERHEAD_SIZE) + +#define BLOB_HEADER_SIZE 4 +#define MAX_BLOB_SIZE (MAX_KEY_SIZE + BLOB_OVERHEAD +\ + BLOB_HEADER_SIZE) + +/* Key modifier for CAAM blobs, used as a revision number */ +static const char caam_key_modifier[KEYMOD_SIZE_GM] = { + 'C', 'A', 'A', 'M', '_', 'K', 'E', 'Y', + '_', 'T', 'Y', 'P', 'E', '_', 'V', '1', +}; + +/** + * struct keyblob_info - Structure that contains all the data necessary + * to generate a black key and encapsulate it into a blob + * + * @key : The plaintext used as input key + * for black key generation + * @key_len : Size of plaintext or size of key in case of + * black key generated from random + * @type : The type of data contained (e.g. black key, blob, etc.) + * @black_key_len : Length of the generated black key + * @black_key : Black key data obtained from CAAM + * @blob_len : Length of the blob that encapsulates the black key + * @blob : Blob data obtained from CAAM + * @key_modifier_len : 8-byte or 16-byte Key_Modifier based on general or + * secure memory blob type + * @key_modifier : can be either a secret value, or used as a revision + * number, revision date or nonce + * In this case is used as a revision number. + */ +struct keyblob_info { + char *key; + size_t key_len; + + u32 type; + + size_t black_key_len; + unsigned char black_key[MAX_BLACK_KEY_SIZE]; + + size_t blob_len; + unsigned char blob[MAX_BLOB_SIZE]; + + size_t key_mod_len; + const void *key_mod; +}; + +int generate_black_key(struct device *dev, struct keyblob_info *info); + +int caam_blob_encap(struct device *dev, struct keyblob_info *info); + +int caam_blob_decap(struct device *dev, struct keyblob_info *info); + +#endif /* _CAAMKEYBLOB_H_ */ diff --git a/drivers/crypto/caam/caamkeyblob_desc.c b/drivers/crypto/caam/caamkeyblob_desc.c new file mode 100644 index 000000000000..a65cc46d7130 --- /dev/null +++ b/drivers/crypto/caam/caamkeyblob_desc.c @@ -0,0 +1,455 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Shared descriptors for CAAM black key + * and blob encapsulation/decapsulation + * + * Copyright 2018-2020 NXP + */ +#include "caamkeyblob_desc.h" + +/* Size of tmp buffer for descriptor const. */ +#define INITIAL_DESCSZ 16 + +/* + * Construct a black key conversion job descriptor + * + * This function constructs a job descriptor capable of performing + * a key blackening operation on a plaintext secure memory resident object. + * + * @desc : Pointer to a pointer to the descriptor generated by this + * function. Caller will be responsible to kfree() this + * descriptor after execution. + * @key : Physical pointer to the plaintext, which will also hold + * the result. Since encryption occurs in place, caller must + * ensure that the space is large enough to accommodate the + * blackened key + * @key_len : Size of the plaintext + * @black_key : DMA address of the black key obtained from hardware + * @black_key_len : Size of the black key + * @key_enc : Encrypted Key Type (AES-ECB or AES-CCM) + * @trusted_key : Trusted Key (use Job Descriptor Key Encryption Key (JDKEK) + * or Trusted Descriptor Key Encryption Key (TDKEK) to + * decrypt the key to be loaded into a Key Register). + * + * Return : '0' on success, error code otherwise + */ +int cnstr_desc_black_key(u32 **desc, dma_addr_t key, size_t key_len, + dma_addr_t black_key, size_t black_key_len, + u8 key_enc, u8 trusted_key) +{ + u32 *tdesc, tmpdesc[INITIAL_DESCSZ]; + u16 dsize, idx; + u32 key_length_for_desc = key_len; + + /* Trusted key not supported */ + if (trusted_key != UNTRUSTED_KEY) + return -ENOTSUPP; + + memset(tmpdesc, 0, sizeof(tmpdesc)); + idx = 1; + + /* + * KEY commands seems limited to 32 bytes, so we should use the load + * command instead which can load up to 64 bytes. + * The size must also be loaded. + * + * TODO: The KEY command indicate it should be able to load key bigger + * than 32bytes but it doesn't work in practice + * + * TODO: The LOAD command indicate it should be able to load up to 96 + * byte keys it doesn't work in practice and is limited to 64 bytes + */ + + /* Load key to class 1 key register */ + tmpdesc[idx++] = CMD_LOAD | LDST_CLASS_1_CCB | LDST_SRCDST_BYTE_KEY | + key_length_for_desc; + tmpdesc[idx++] = (uintptr_t)key; + /* Load the size of the key */ + tmpdesc[idx++] = CMD_LOAD | LDST_CLASS_1_CCB | LDST_IMM | + LDST_SRCDST_WORD_KEYSZ_REG | + sizeof(key_length_for_desc); + tmpdesc[idx++] = key_length_for_desc; + + /* ...and write back out via FIFO store*/ + tmpdesc[idx] = CMD_FIFO_STORE | CLASS_1 | + (black_key_len & KEY_LENGTH_MASK); + + /* Plus account for ECB/CCM option in FIFO_STORE */ + if (key_enc == KEY_COVER_ECB) + tmpdesc[idx] |= FIFOST_TYPE_KEY_KEK; + else + tmpdesc[idx] |= FIFOST_TYPE_KEY_CCM_JKEK; + + idx++; + tmpdesc[idx++] = (uintptr_t)black_key; + + /* Finish off the job header */ + tmpdesc[0] = CMD_DESC_HDR | HDR_ONE | (idx & HDR_DESCLEN_MASK); + dsize = desc_bytes(&tmpdesc); + + /* Now allocate execution buffer and coat it with executable */ + tdesc = kmemdup(tmpdesc, dsize, GFP_KERNEL | GFP_DMA); + if (!tdesc) + return -ENOMEM; + + *desc = tdesc; + + print_hex_dump_debug("black key desc@" __stringify(__LINE__) ":", + DUMP_PREFIX_ADDRESS, 16, 4, *desc, + desc_bytes(*desc), 1); + + return 0; +} +EXPORT_SYMBOL(cnstr_desc_black_key); + +/* + * Construct a black key using RNG job descriptor + * + * This function constructs a job descriptor capable of performing + * a key blackening operation on RNG generated. + * + * @desc : Pointer to a pointer to the descriptor generated by this + * function. Caller will be responsible to kfree() this + * descriptor after execution. + * @key_len : Size of the random plaintext + * @black_key : DMA address of the black key obtained from hardware + * @black_key_len : Size of the black key + * @key_enc : Encrypted Key Type (AES-ECB or AES-CCM) + * @trusted_key : Trusted Key (use Job Descriptor Key Encryption Key (JDKEK) + * or Trusted Descriptor Key Encryption Key (TDKEK) to + * decrypt the key to be loaded into a Key Register). + * + * Return : '0' on success, error code otherwise + */ +int cnstr_desc_random_black_key(u32 **desc, size_t key_len, + dma_addr_t black_key, size_t black_key_len, + u8 key_enc, u8 trusted_key) +{ + u32 *tdesc, tmpdesc[INITIAL_DESCSZ]; + u16 dsize; + u32 bk_store; + + memset(tmpdesc, 0, sizeof(tmpdesc)); + + init_job_desc(tmpdesc, CMD_DESC_HDR); + + /* Prepare RNG */ + append_operation(tmpdesc, OP_ALG_ALGSEL_RNG | OP_TYPE_CLASS1_ALG); + + /* Generate RNG and left it in output data fifo */ + append_cmd(tmpdesc, CMD_FIFO_STORE | FIFOST_TYPE_RNGFIFO | key_len); + + /* Copy RNG from outfifo to class 1 Key register */ + append_move(tmpdesc, MOVE_SRC_OUTFIFO | MOVE_DEST_CLASS1KEY | + MOVE_WAITCOMP | (key_len & MOVE_LEN_MASK)); + + /* Write the size of the key moved */ + append_load_imm_u32(tmpdesc, key_len, LDST_CLASS_1_CCB | + LDST_SRCDST_WORD_KEYSZ_REG | LDST_IMM); + + bk_store = CLASS_1; + if (key_enc == KEY_COVER_ECB) + bk_store |= FIFOST_TYPE_KEY_KEK; + else + bk_store |= FIFOST_TYPE_KEY_CCM_JKEK; + + /* Fifo store to save the key as black key in memory */ + append_fifo_store(tmpdesc, black_key, black_key_len, bk_store); + + dsize = desc_bytes(&tmpdesc); + + /* Now allocate execution buffer and coat it with executable */ + tdesc = kmemdup(tmpdesc, dsize, GFP_KERNEL | GFP_DMA); + if (!tdesc) + return -ENOMEM; + + *desc = tdesc; + + print_hex_dump_debug("black key random desc@" __stringify(__LINE__) ":", + DUMP_PREFIX_ADDRESS, 16, 4, *desc, + desc_bytes(*desc), 1); + + return 0; +} +EXPORT_SYMBOL(cnstr_desc_random_black_key); + +/* + * Construct a blob encapsulation job descriptor + * + * This function dynamically constructs a blob encapsulation job descriptor + * from the following arguments: + * + * @desc : Pointer to a pointer to the descriptor generated by this + * function. Caller will be responsible to kfree() this + * descriptor after execution. + * @black_key : Physical pointer to a secret, normally a black or red key, + * possibly residing within an accessible secure memory page, + * of the secret to be encapsulated to an output blob. + * @black_key_len : Size of input secret, in bytes. This is limited to 65536 + * less the size of blob overhead, since the length embeds + * into DECO pointer in/out instructions. + * @keycolor : Determines if the source data is covered (black key) or + * plaintext (red key). RED_KEY or BLACK_KEY are defined in + * for this purpose. + * @key_enc : If BLACK_KEY source is covered via AES-CCM, specify + * KEY_COVER_CCM, else uses AES-ECB (KEY_COVER_ECB). + * @trusted_key : Trusted Key (use Job Descriptor Key Encryption Key (JDKEK) + * or Trusted Descriptor Key Encryption Key (TDKEK) to + * decrypt the key to be loaded into a Key Register). + * @mem_type : Determine if encapsulated blob should be a secure memory + * blob (DATA_SECMEM), with partition data embedded with key + * material, or a general memory blob (DATA_GENMEM). + * @key_mod : Physical pointer to a key modifier, which must reside in a + * contiguous piece of memory. Modifier will be assumed to be + * 8 bytes long for a blob of type DATA_SECMEM, or 16 bytes + * long for a blob of type DATA_GENMEM + * @key_mod_len : Modifier length is 8 bytes long for a blob of type + * DATA_SECMEM, or 16 bytes long for a blob of type DATA_GENMEM + * @blob : Physical pointer to the destination buffer to receive the + * encapsulated output. This buffer will need to be 48 bytes + * larger than the input because of the added encapsulation + * data. The generated descriptor will account for the + * increase in size, but the caller must also account for + * this increase in the buffer allocator. + * @blob_len : Size of the destination buffer to receive the + * encapsulated output. + * Return : '0' on success, error code otherwise + * + * Upon completion, desc points to a buffer containing a CAAM job + * descriptor which encapsulates data into an externally-storable blob + * suitable for use across power cycles. + * + * This is an example of a black key encapsulation job into a general memory + * blob. Notice the 16-byte key modifier in the LOAD instruction. Also note + * the output 48 bytes longer than the input: + * + * [00] B0800008 jobhdr: stidx=0 len=8 + * [01] 14400010 ld: ccb2-key len=16 offs=0 + * [02] 08144891 ptr->@0x08144891 + * [03] F800003A seqoutptr: len=58 + * [04] 01000000 out_ptr->@0x01000000 + * [05] F000000A seqinptr: len=10 + * [06] 09745090 in_ptr->@0x09745090 + * [07] 870D0004 operation: encap blob reg=memory, black, format=normal + * + * This is an example of a red key encapsulation job for storing a red key + * into a secure memory blob. Note the 8 byte modifier on the 12 byte offset + * in the LOAD instruction; this accounts for blob permission storage: + * + * [00] B0800008 jobhdr: stidx=0 len=8 + * [01] 14400C08 ld: ccb2-key len=8 offs=12 + * [02] 087D0784 ptr->@0x087d0784 + * [03] F8000050 seqoutptr: len=80 + * [04] 09251BB2 out_ptr->@0x09251bb2 + * [05] F0000020 seqinptr: len=32 + * [06] 40000F31 in_ptr->@0x40000f31 + * [07] 870D0008 operation: encap blob reg=memory, red, sec_mem, + * format=normal + */ +int cnstr_desc_blob_encap(u32 **desc, dma_addr_t black_key, + size_t black_key_len, u8 keycolor, u8 key_enc, + u8 trusted_key, u8 mem_type, dma_addr_t key_mod, + size_t key_mod_len, dma_addr_t blob, size_t blob_len) +{ + u32 *tdesc, tmpdesc[INITIAL_DESCSZ]; + u16 dsize, idx; + + /* Trusted key not supported */ + if (trusted_key != UNTRUSTED_KEY) + return -ENOTSUPP; + + memset(tmpdesc, 0, sizeof(tmpdesc)); + idx = 1; + + /* + * Key modifier works differently for secure/general memory blobs + * This accounts for the permission/protection data encapsulated + * within the blob if a secure memory blob is requested + */ + if (mem_type == DATA_SECMEM) + tmpdesc[idx++] = CMD_LOAD | LDST_CLASS_2_CCB | + LDST_SRCDST_BYTE_KEY | + ((12 << LDST_OFFSET_SHIFT) & LDST_OFFSET_MASK) + | (key_mod_len & LDST_LEN_MASK); + else /* is general memory blob */ + tmpdesc[idx++] = CMD_LOAD | LDST_CLASS_2_CCB + | LDST_SRCDST_BYTE_KEY + | (key_mod_len & LDST_LEN_MASK); + + tmpdesc[idx++] = (u32)key_mod; + + /* + * Encapsulation output must include space for blob key encryption + * key and MAC tag + */ + tmpdesc[idx++] = CMD_SEQ_OUT_PTR | (black_key_len + BLOB_OVERHEAD); + tmpdesc[idx++] = (u32)blob; + + /* Input data, should be somewhere in secure memory */ + tmpdesc[idx++] = CMD_SEQ_IN_PTR | black_key_len; + tmpdesc[idx++] = (uintptr_t)black_key; + + /* Set blob encap, then color */ + tmpdesc[idx] = CMD_OPERATION | OP_TYPE_ENCAP_PROTOCOL | OP_PCLID_BLOB; + + if (mem_type == DATA_SECMEM) + tmpdesc[idx] |= OP_PCL_BLOB_PTXT_SECMEM; + + if (key_enc == KEY_COVER_CCM) + tmpdesc[idx] |= OP_PCL_BLOB_EKT; + + /* An input black key cannot be stored in a red blob */ + if (keycolor == BLACK_KEY) + tmpdesc[idx] |= OP_PCL_BLOB_BLACK; + + idx++; + tmpdesc[0] = CMD_DESC_HDR | HDR_ONE | (idx & HDR_DESCLEN_MASK); + dsize = desc_bytes(&tmpdesc); + + tdesc = kmemdup(tmpdesc, dsize, GFP_KERNEL | GFP_DMA); + if (!tdesc) + return -ENOMEM; + + *desc = tdesc; + + print_hex_dump_debug("blob encap desc@" __stringify(__LINE__) ":", + DUMP_PREFIX_ADDRESS, 16, 4, *desc, + desc_bytes(*desc), 1); + return 0; +} +EXPORT_SYMBOL(cnstr_desc_blob_encap); + +/* + * Construct a blob decapsulation job descriptor + * + * This function dynamically constructs a blob decapsulation job descriptor + * from the following arguments: + * + * @desc : Pointer to a pointer to the descriptor generated by this + * function. Caller will be responsible to kfree() this + * descriptor after execution. + * @blob : Physical pointer (into external memory) of the blob to + * be decapsulated. Blob must reside in a contiguous memory + * segment. + * @blob_len : Size of the blob buffer to be decapsulated. + * @key_mod : Physical pointer to a key modifier, which must reside in a + * contiguous piece of memory. Modifier will be assumed to be + * 8 bytes long for a blob of type DATA_SECMEM, or 16 bytes + * long for a blob of type DATA_GENMEM + * @key_mod_len : Modifier length is 8 bytes long for a blob of type + * DATA_SECMEM, or 16 bytes long for a blob of type DATA_GENMEM + * @black_key : Physical pointer of the decapsulated output, possibly into + * a location within a secure memory page. Must be contiguous. + * @black_key_len : Size of encapsulated secret in bytes (not the size of the + * input blob). + * @keycolor : Determines if the source data is covered (black key) or + * plaintext (red key). RED_KEY or BLACK_KEY are defined in + * for this purpose. + * @key_enc : If BLACK_KEY source is covered via AES-CCM, specify + * KEY_COVER_CCM, else uses AES-ECB (KEY_COVER_ECB). + * @trusted_key : Trusted Key (use Job Descriptor Key Encryption Key (JDKEK) + * or Trusted Descriptor Key Encryption Key (TDKEK) to + * decrypt the key to be loaded into a Key Register). + * @mem_type : Determine if encapsulated blob should be a secure memory + * blob (DATA_SECMEM), with partition data embedded with key + * material, or a general memory blob (DATA_GENMEM). + * Return : '0' on success, error code otherwise + * + * Upon completion, desc points to a buffer containing a CAAM job descriptor + * that decapsulates a key blob from external memory into a black (encrypted) + * key or red (plaintext) content. + * + * This is an example of a black key decapsulation job from a general memory + * blob. Notice the 16-byte key modifier in the LOAD instruction. + * + * [00] B0800008 jobhdr: stidx=0 len=8 + * [01] 14400010 ld: ccb2-key len=16 offs=0 + * [02] 08A63B7F ptr->@0x08a63b7f + * [03] F8000010 seqoutptr: len=16 + * [04] 01000000 out_ptr->@0x01000000 + * [05] F000003A seqinptr: len=58 + * [06] 01000010 in_ptr->@0x01000010 + * [07] 860D0004 operation: decap blob reg=memory, black, format=normal + * + * This is an example of a red key decapsulation job for restoring a red key + * from a secure memory blob. Note the 8 byte modifier on the 12 byte offset + * in the LOAD instruction: + * + * [00] B0800008 jobhdr: stidx=0 len=8 + * [01] 14400C08 ld: ccb2-key len=8 offs=12 + * [02] 01000000 ptr->@0x01000000 + * [03] F8000020 seqoutptr: len=32 + * [04] 400000E6 out_ptr->@0x400000e6 + * [05] F0000050 seqinptr: len=80 + * [06] 08F0C0EA in_ptr->@0x08f0c0ea + * [07] 860D0008 operation: decap blob reg=memory, red, sec_mem, + * format=normal + */ +int cnstr_desc_blob_decap(u32 **desc, dma_addr_t blob, size_t blob_len, + dma_addr_t key_mod, size_t key_mod_len, + dma_addr_t black_key, size_t black_key_len, + u8 keycolor, u8 key_enc, u8 trusted_key, u8 mem_type) +{ + u32 *tdesc, tmpdesc[INITIAL_DESCSZ]; + u16 dsize, idx; + + /* Trusted key not supported */ + if (trusted_key != UNTRUSTED_KEY) + return -ENOTSUPP; + + memset(tmpdesc, 0, sizeof(tmpdesc)); + idx = 1; + + /* Load key modifier */ + if (mem_type == DATA_SECMEM) + tmpdesc[idx++] = CMD_LOAD | LDST_CLASS_2_CCB | + LDST_SRCDST_BYTE_KEY | + ((12 << LDST_OFFSET_SHIFT) & LDST_OFFSET_MASK) + | (key_mod_len & LDST_LEN_MASK); + else /* is general memory blob */ + tmpdesc[idx++] = CMD_LOAD + | LDST_CLASS_2_CCB + | LDST_SRCDST_BYTE_KEY + | (key_mod_len & LDST_LEN_MASK); + + tmpdesc[idx++] = (u32)key_mod; + + /* Compensate BKEK + MAC tag over size of encapsulated secret */ + tmpdesc[idx++] = CMD_SEQ_IN_PTR | blob_len; + tmpdesc[idx++] = (u32)blob; + tmpdesc[idx++] = CMD_SEQ_OUT_PTR | black_key_len; + tmpdesc[idx++] = (uintptr_t)black_key; + + /* Decapsulate from secure memory partition to black blob */ + tmpdesc[idx] = CMD_OPERATION | OP_TYPE_DECAP_PROTOCOL | OP_PCLID_BLOB; + + if (mem_type == DATA_SECMEM) + tmpdesc[idx] |= OP_PCL_BLOB_PTXT_SECMEM; + + if (key_enc == KEY_COVER_CCM) + tmpdesc[idx] |= OP_PCL_BLOB_EKT; + + if (keycolor == BLACK_KEY) + tmpdesc[idx] |= OP_PCL_BLOB_BLACK; + + idx++; + tmpdesc[0] = CMD_DESC_HDR | HDR_ONE | (idx & HDR_DESCLEN_MASK); + dsize = desc_bytes(&tmpdesc); + + tdesc = kmemdup(tmpdesc, dsize, GFP_KERNEL | GFP_DMA); + if (!tdesc) + return -ENOMEM; + + *desc = tdesc; + + print_hex_dump_debug("blob decap desc@" __stringify(__LINE__) ":", + DUMP_PREFIX_ADDRESS, 16, 4, *desc, + desc_bytes(*desc), 1); + + return 0; +} +EXPORT_SYMBOL(cnstr_desc_blob_decap); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("NXP CAAM Black Key and Blob descriptors"); +MODULE_AUTHOR("NXP Semiconductors"); diff --git a/drivers/crypto/caam/caamkeyblob_desc.h b/drivers/crypto/caam/caamkeyblob_desc.h new file mode 100644 index 000000000000..1cbc8126a847 --- /dev/null +++ b/drivers/crypto/caam/caamkeyblob_desc.h @@ -0,0 +1,101 @@ +/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */ +/* + * Shared descriptors for CAAM black key and blob + * + * Copyright 2018-2020 NXP + */ + +#ifndef _CAAMKEYBLOB_DESC_H_ +#define _CAAMKEYBLOB_DESC_H_ + +#include + +#include "jr.h" +#include "regs.h" +#include "desc.h" + +#include "compat.h" +#include "tag_object.h" +#include "desc_constr.h" + +/* Defines for secure memory and general memory blobs */ +#define DATA_GENMEM 0 +#define DATA_SECMEM 1 + +/* Encrypted key */ +#define BLACK_KEY 1 + +/* Define key encryption/covering options */ +#define KEY_COVER_ECB 0 /* cover key in AES-ECB */ +#define KEY_COVER_CCM 1 /* cover key with AES-CCM */ + +/* Define the trust in the key, to select either JDKEK or TDKEK */ +#define UNTRUSTED_KEY 0 +#define TRUSTED_KEY 1 + +/* Define space required for BKEK + MAC tag storage in any blob */ +#define BLOB_OVERHEAD (32 + 16) + +#define PAD_16_BYTE(_key_size) (roundup(_key_size, 16)) +#define PAD_8_BYTE(_key_size) (roundup(_key_size, 8)) + +/* + * ECB-Black Key will be padded with zeros to make it a + * multiple of 16 bytes long before it is encrypted, + * and the resulting Black Key will be this length. + */ +#define ECB_BLACK_KEY_SIZE(_key_size) (PAD_16_BYTE(_key_size)) + +/* + * CCM-Black Key will always be at least 12 bytes longer, + * since the encapsulation uses a 6-byte nonce and adds + * a 6-byte ICV. But first, the key is padded as necessary so + * that CCM-Black Key is a multiple of 8 bytes long. + */ +#define NONCE_SIZE 6 +#define ICV_SIZE 6 +#define CCM_OVERHEAD (NONCE_SIZE + ICV_SIZE) +#define CCM_BLACK_KEY_SIZE(_key_size) (PAD_8_BYTE(_key_size) \ + + CCM_OVERHEAD) + +static inline int secret_size_in_ccm_black_key(int key_size) +{ + return ((key_size >= CCM_OVERHEAD) ? key_size - CCM_OVERHEAD : 0); +} + +#define SECRET_SIZE_IN_CCM_BLACK_KEY(_key_size) \ + secret_size_in_ccm_black_key(_key_size) + +/* A red key is not encrypted so its size is the same */ +#define RED_KEY_SIZE(_key_size) (_key_size) + +/* + * Based on memory type, the key modifier length + * can be either 8-byte or 16-byte. + */ +#define KEYMOD_SIZE_SM 8 +#define KEYMOD_SIZE_GM 16 + +/* Create job descriptor to cover key */ +int cnstr_desc_black_key(u32 **desc, dma_addr_t key, size_t key_len, + dma_addr_t black_key, size_t black_key_len, + u8 key_enc, u8 trusted_key); + +/* Create job descriptor to generate a random key and cover it */ +int cnstr_desc_random_black_key(u32 **desc, size_t key_len, + dma_addr_t black_key, size_t black_key_len, + u8 key_enc, u8 trusted_key); + +/* Encapsulate data in a blob */ +int cnstr_desc_blob_encap(u32 **desc, dma_addr_t black_key, + size_t black_key_len, u8 color, u8 key_enc, + u8 trusted_key, u8 mem_type, dma_addr_t key_mod, + size_t key_mod_len, dma_addr_t blob, size_t blob_len); + +/* Decapsulate data from a blob */ +int cnstr_desc_blob_decap(u32 **desc, dma_addr_t blob, size_t blob_len, + dma_addr_t key_mod, size_t key_mod_len, + dma_addr_t black_key, size_t black_key_len, + u8 keycolor, u8 key_enc, u8 trusted_key, u8 mem_type); + +#endif /* _CAAMKEYBLOB_DESC_H_ */ diff --git a/drivers/crypto/caam/caamkeyblob_test.c b/drivers/crypto/caam/caamkeyblob_test.c new file mode 100644 index 000000000000..d4ca4ee3bb13 --- /dev/null +++ b/drivers/crypto/caam/caamkeyblob_test.c @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright 2018-2020 NXP + * + * Test of black key generation from a plaintext (of different size) + * and from random. + * The random black key is encapsulated into a blob. + * Next, the blob is decapsulated and the new obtained black key is + * compared to the random black key. + */ +#include +#include "caamkeyblob.h" + +#define MAX_INPUT_SIZE 64 +#define KEY_ENCRYPTION_ECB 1 +#define KEY_ENCRYPTION_CCM 9 + +static char input[MAX_INPUT_SIZE]; + +static int create_black_key(struct device *dev, struct keyblob_info *info) +{ + int ret; + + ret = generate_black_key(dev, info); + if (ret) + dev_err(dev, "black key of size: %zd, type: %d returned %d", + info->key_len, info->type, ret); + + return (ret) ? 1 : 0; +} + +static int black_key_test(void) +{ + struct device *dev; + struct keyblob_info *info; + int i, ret = 0, nb_errors = 0; + unsigned char tmp_black_key[MAX_BLACK_KEY_SIZE]; + size_t tmp_black_key_len = 0; + + dev = caam_jr_alloc(); + if (!dev) + return -ENOMEM; + + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->key = input; + + for (i = 1; i <= MAX_INPUT_SIZE; i++) { + info->key_len = i; + + /* Create a black key encrypted with AES-ECB */ + info->type = KEY_ENCRYPTION_ECB; + info->black_key_len = MAX_INPUT_SIZE; + nb_errors += create_black_key(dev, info); + + /* Create a black key encrypted with AES-CCM */ + info->type = KEY_ENCRYPTION_CCM; + info->black_key_len = MAX_INPUT_SIZE + CCM_OVERHEAD; + nb_errors += create_black_key(dev, info); + } + + /* Create, from random, a black key encrypted with AES-ECB */ + info->key = NULL; + info->type = KEY_ENCRYPTION_ECB; + info->black_key_len = MAX_INPUT_SIZE; + ret = create_black_key(dev, info); + nb_errors += (ret) ? 1 : 0; + + /* Save it for later to compare it */ + tmp_black_key_len = info->black_key_len; + memcpy(tmp_black_key, info->black_key, tmp_black_key_len); + + /* Encapsulate the random black key into a black blob */ + info->key = NULL; + /* Set key modifier, used as revision number, for blob */ + info->key_mod = caam_key_modifier; + info->key_mod_len = ARRAY_SIZE(caam_key_modifier); + /* Black key, encrypted with ECB-AES, in General Memory */ + info->type = KEY_ENCRYPTION_ECB; + info->key_len = MAX_INPUT_SIZE; + info->blob_len = MAX_BLOB_SIZE; + ret = caam_blob_encap(dev, info); + nb_errors += (ret) ? 1 : 0; + + /* Decapsulate the black key from the above black blob */ + ret = caam_blob_decap(dev, info); + nb_errors += (ret) ? 1 : 0; + + /* + * Compare the generated black key with + * the one decapsulated from the blob + */ + if (info->black_key_len == tmp_black_key_len) + ret = memcmp(tmp_black_key, info->black_key, tmp_black_key_len); + else + ret = 1; + nb_errors += ret; + + /* + * Check number of errors. + * If nb_errors > 0, at least one operation failed, success otherwise + */ + pr_info("Nb errors: %d\n", nb_errors); + + caam_jr_free(dev); + kfree(info); + + return nb_errors; +} + +static int black_key_test_init(void) +{ + return black_key_test(); +} + +static void black_key_test_exit(void) +{ +} + +module_init(black_key_test_init); +module_exit(black_key_test_exit); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("NXP CAAM Black Key and Blob Test"); +MODULE_AUTHOR("NXP Semiconductors"); diff --git a/drivers/crypto/caam/jr.c b/drivers/crypto/caam/jr.c index 31559e6ef7e8..7797534b7a35 100644 --- a/drivers/crypto/caam/jr.c +++ b/drivers/crypto/caam/jr.c @@ -4,7 +4,7 @@ * JobR backend functionality * * Copyright 2008-2012 Freescale Semiconductor, Inc. - * Copyright 2019 NXP + * Copyright 2019-2020 NXP */ #include @@ -534,6 +534,57 @@ int caam_jr_enqueue(struct device *dev, u32 *desc, } EXPORT_SYMBOL(caam_jr_enqueue); +/** + * caam_jr_run_and_wait_for_completion() - Enqueue a job and wait for its + * completion. Returns 0 if OK, -ENOSPC if the queue is full, + * -EIO if it cannot map the caller's descriptor. + * @dev: struct device of the job ring to be used + * @desc: points to a job descriptor that execute our request. All + * descriptors (and all referenced data) must be in a DMAable + * region, and all data references must be physical addresses + * accessible to CAAM (i.e. within a PAMU window granted + * to it). + * @cbk: pointer to a callback function to be invoked upon completion + * of this request. This has the form: + * callback(struct device *dev, u32 *desc, u32 stat, void *arg) + * where: + * @dev: contains the job ring device that processed this + * response. + * @desc: descriptor that initiated the request, same as + * "desc" being argued to caam_jr_enqueue(). + * @status: untranslated status received from CAAM. See the + * reference manual for a detailed description of + * error meaning, or see the JRSTA definitions in the + * register header file + * @areq: optional pointer to an argument passed with the + * original request + **/ +int caam_jr_run_and_wait_for_completion(struct device *dev, u32 *desc, + void (*cbk)(struct device *dev, + u32 *desc, u32 status, + void *areq)) +{ + int ret = 0; + struct jr_job_result jobres = {0}; + + /* Initialize the completion structure */ + init_completion(&jobres.completion); + + /* Enqueue job for execution */ + ret = caam_jr_enqueue(dev, desc, cbk, &jobres); + if (ret != -EINPROGRESS) + return ret; + + /* Wait for job completion */ + wait_for_completion(&jobres.completion); + + /* Get return code processed in cbk */ + ret = jobres.error; + + return ret; +} +EXPORT_SYMBOL(caam_jr_run_and_wait_for_completion); + static void caam_jr_init_hw(struct device *dev, dma_addr_t inpbusaddr, dma_addr_t outbusaddr) { diff --git a/drivers/crypto/caam/jr.h b/drivers/crypto/caam/jr.h index 7183baa5652f..768df0a6ac35 100644 --- a/drivers/crypto/caam/jr.h +++ b/drivers/crypto/caam/jr.h @@ -3,11 +3,26 @@ * CAAM public-level include definitions for the JobR backend * * Copyright 2008-2011 Freescale Semiconductor, Inc. + * Copyright 2020 NXP */ #ifndef JR_H #define JR_H +#include + + /** + * struct jr_job_result - Job Ring result structure, used for requests + * that need to run and wait for their completion + * + * @error : The result returned after request was executed + * @completion : Structure used to maintain state for a "completion" + */ +struct jr_job_result { + int error; + struct completion completion; +}; + /* Prototypes for backend-level services exposed to APIs */ int caam_jr_driver_probed(void); struct device *caam_jr_alloc(void); @@ -17,5 +32,9 @@ int caam_jr_enqueue(struct device *dev, u32 *desc, void (*cbk)(struct device *dev, u32 *desc, u32 status, void *areq), void *areq); +int caam_jr_run_and_wait_for_completion(struct device *dev, u32 *desc, + void (*cbk)(struct device *dev, + u32 *desc, u32 status, + void *areq)); #endif /* JR_H */