diff options
Diffstat (limited to 'drivers/crypto/caam/caamkeyblob.c')
-rw-r--r-- | drivers/crypto/caam/caamkeyblob.c | 670 |
1 files changed, 670 insertions, 0 deletions
diff --git a/drivers/crypto/caam/caamkeyblob.c b/drivers/crypto/caam/caamkeyblob.c new file mode 100644 index 000000000000..5551f6725d79 --- /dev/null +++ b/drivers/crypto/caam/caamkeyblob.c @@ -0,0 +1,670 @@ +// 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 black_key_dma; + u8 *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); + + /* 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 exit; + } + + /* Construct descriptor for black key */ + if (not_random) + ret = cnstr_desc_black_key(&desc, info->key, 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); + +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; + size_t blob_req_len = 0; + u8 mem_type, color, key_enc, trusted_key; + dma_addr_t black_key_dma, blob_dma; + unsigned char *blob = info->blob; + u8 *tmp_black_key = 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->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; + + blob_req_len = CCM_BLACK_KEY_SIZE(info->key_len); + + /* Check if the blob can be stored */ + if (info->blob_len < (blob_req_len + BLOB_OVERHEAD)) + return -EINVAL; + + /* Update the blob length */ + info->blob_len = blob_req_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 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_black_key; + } + } 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, info->key_len, + color, key_enc, trusted_key, mem_type, + info->key_mod, 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_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 black_key_dma, blob_dma; + unsigned char *blob = info->blob + TAG_OVERHEAD_SIZE; + u8 *tmp_black_key = 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->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; + + /* + * 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_real_len = 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_real_len = CCM_BLACK_KEY_SIZE(info->key_len); + + /* Check if there is enough space for black key */ + 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 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_blob; + } + } 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, + info->key_mod, info->key_mod_len, + black_key_dma, info->key_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_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); |