1
0
Fork 0

MLK-24420-2 crypto: caam - add support for black keys and blobs

CAAM's Black Key mechanism is intended for protection
of user keys against bus snooping. This automatically
encapsulates and decapsulates cryptographic keys ''on-the-fly''
in an encrypted data structure called a Black Key.
Before a value is copied from a Key Register to memory,
CAAM will automatically encrypt the key as a Black Key
(encrypted key) using the current value in the JDKEKR or
TDKEKR as the encryption key.

CAAM's built-in Blob Protocol provides a method for protecting
user-defined data across system power cycles. CAAM protects data
in a data structure called a Blob, which provides both confidentiality
and integrity protection. The data to be protected is encrypted so that
it can be safely placed into non-volatile storage before the SoC is
powered down.

This patch includes the support to generate a black key from random or
from a plaintext. Also one can encapsulate it into a blob or decapsulate
a black key from a blob.
The key and blob generation descriptors are exported into a separate file,
such that they could be shared with other interfaces (qi, qi2).

This feature has support only for black keys, encapsulated in
black blobs in General Memory.

In caamkeyblob_test.c file is a test that validates the above
operations: create a black key from plaintext or from random,
encapsulate and decapsulate a blob and compare the obtained black key.
This test is configured as a kernel module.

Signed-off-by: Franck LENORMAND <franck.lenormand@nxp.com>
Signed-off-by: Iuliana Prodan <iuliana.prodan@nxp.com>
Reviewed-by: Horia Geantă <horia.geanta@nxp.com>
5.4-rM2-2.2.x-imx-squashed
Iuliana Prodan 2020-07-30 14:19:24 +03:00
parent 04cab5a13d
commit 84287c5d3b
9 changed files with 1548 additions and 4 deletions

View File

@ -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"

View File

@ -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),)

View File

@ -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);

View File

@ -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 <linux/device.h>
#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_ */

View File

@ -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");

View File

@ -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 <linux/types.h>
#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_ */

View File

@ -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 <linux/module.h>
#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");

View File

@ -4,7 +4,7 @@
* JobR backend functionality
*
* Copyright 2008-2012 Freescale Semiconductor, Inc.
* Copyright 2019 NXP
* Copyright 2019-2020 NXP
*/
#include <linux/of_irq.h>
@ -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)
{

View File

@ -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 <linux/completion.h>
/**
* 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 */