Message ID | 20220428140145.870527-4-a.fatoum@pengutronix.de |
---|---|
State | Superseded |
Headers | show |
Series | KEYS: trusted: Introduce support for NXP CAAM-based trusted keys | expand |
Hi, > The NXP Cryptographic Acceleration and Assurance Module (CAAM) > can be used to protect user-defined data across system reboot: > > - When the system is fused and boots into secure state, the master > key is a unique never-disclosed device-specific key > - random key is encrypted by key derived from master key > - data is encrypted using the random key > - encrypted data and its encrypted random key are stored alongside > - This blob can now be safely stored in non-volatile memory > > On next power-on: > - blob is loaded into CAAM > - CAAM writes decrypted data either into memory or key register > > Add functions to realize encrypting and decrypting into memory alongside > the CAAM driver. > > They will be used in a later commit as a source for the trusted key > seal/unseal mechanism. Thanks for the work on this and I'm excited to try this. I'm currently playing with this and one thing I've noticed is that an export restricted CAAM isn't handled properly. That is, there are CAAM's which aren't fully featured. Normally, the caam driver will take care of it. For example, see commit f20311cc9c58 ("crypto: caam - disable pkc for non-E SoCs"). For the trusted keys case, it would be nice if the kernel will not even probe (or similar). Right now, everything seems to work fine, but once I try to add a new key, I'll get the following errros: # keyctl add trusted mykey "new 32" @u add_key: Invalid argument [ 23.138714] caam_jr 8020000.jr: 20000b0f: CCB: desc idx 11: : Invalid CHA selected. [ 23.138740] trusted_key: key_seal failed (-22) Again this is expected, because I run it on a non-E version. IMHO, it should be that the trusted keys shouldn't be enabled at all. Like it is for example if an unknown rng is given: trusted_key: Unsupported RNG. Supported: kernel, default Thanks, -michael
Hello Michael, On 03.05.22 20:24, Michael Walle wrote: >> Add functions to realize encrypting and decrypting into memory alongside >> the CAAM driver. >> >> They will be used in a later commit as a source for the trusted key >> seal/unseal mechanism. > > Thanks for the work on this and I'm excited to try this. I'm currently > playing with this and one thing I've noticed is that an export restricted > CAAM isn't handled properly. I didn't know there are still crypto export restrictions in place ;o > That is, there are CAAM's which aren't fully featured. Normally, the > caam driver will take care of it. For example, see commit f20311cc9c58 > ("crypto: caam - disable pkc for non-E SoCs"). For the trusted keys case, > it would be nice if the kernel will not even probe (or similar). > > Right now, everything seems to work fine, but once I try to add a new key, > I'll get the following errros: > > # keyctl add trusted mykey "new 32" @u > add_key: Invalid argument > [ 23.138714] caam_jr 8020000.jr: 20000b0f: CCB: desc idx 11: : Invalid CHA selected. > [ 23.138740] trusted_key: key_seal failed (-22) Trusted key core will attempt TPM and TEE if enabled before trying CAAM unless CAAM was explicitly requested. Silently failing in this case would not be helpful to users. I think an info message (not error, as it'd be annoying to see it every time booting a restricted SoC) is a good idea. Thanks for the feedback. > Again this is expected, because I run it on a non-E version. IMHO, it > should be that the trusted keys shouldn't be enabled at all. Like it is > for example if an unknown rng is given: > > trusted_key: Unsupported RNG. Supported: kernel, default Other backends return -ENODEV and Trusted key core will ignore and try next in list. Please give below patch a try. I tested it on normal unrestricted i.MX6. If that's what you had in mind, I can incorporate it into v9. If you have any Tested-by's or the like you want me to add, please tell. :) Cheers, Ahmad ------------------------------ 8< ------------------------------ diff --git a/drivers/crypto/caam/blob_gen.c b/drivers/crypto/caam/blob_gen.c index d0b1a0015308..1d07e056a5dd 100644 --- a/drivers/crypto/caam/blob_gen.c +++ b/drivers/crypto/caam/blob_gen.c @@ -4,6 +4,8 @@ * Copyright (C) 2021 Pengutronix, Ahmad Fatoum <kernel@pengutronix.de> */ +#define pr_fmt(fmt) "caam blob_gen: " fmt + #include <linux/device.h> #include <soc/fsl/caam-blob.h> @@ -147,11 +149,27 @@ EXPORT_SYMBOL(caam_process_blob); struct caam_blob_priv *caam_blob_gen_init(void) { + struct caam_drv_private *ctrlpriv; struct device *jrdev; + /* + * caam_blob_gen_init() may expectedly fail with -ENODEV, e.g. when + * CAAM driver didn't probe or when SoC lacks BLOB support. An + * error would be harsh in this case, so we stick to info level. + */ + jrdev = caam_jr_alloc(); - if (IS_ERR(jrdev)) - return ERR_CAST(jrdev); + if (IS_ERR(jrdev)) { + pr_info("no job ring available\n"); + return ERR_PTR(-ENODEV); + } + + ctrlpriv = dev_get_drvdata(jrdev->parent); + if (!ctrlpriv->blob_present) { + dev_info(jrdev, "no hardware blob generation support\n"); + caam_jr_free(jrdev); + return ERR_PTR(-ENODEV); + } return container_of(jrdev, struct caam_blob_priv, jrdev); } diff --git a/drivers/crypto/caam/ctrl.c b/drivers/crypto/caam/ctrl.c index ca0361b2dbb0..a0a622ca5dd4 100644 --- a/drivers/crypto/caam/ctrl.c +++ b/drivers/crypto/caam/ctrl.c @@ -660,6 +660,10 @@ static int caam_probe(struct platform_device *pdev) caam_little_end = !(bool)(rd_reg32(&ctrl->perfmon.status) & (CSTA_PLEND | CSTA_ALT_PLEND)); + + comp_params = rd_reg32(&ctrl->perfmon.comp_parms_ls); + ctrlpriv->blob_present = !!(comp_params & CTPR_LS_BLOB); + comp_params = rd_reg32(&ctrl->perfmon.comp_parms_ms); if (comp_params & CTPR_MS_PS && rd_reg32(&ctrl->mcr) & MCFGR_LONG_PTR) caam_ptr_sz = sizeof(u64); diff --git a/drivers/crypto/caam/intern.h b/drivers/crypto/caam/intern.h index 7d45b21bd55a..e92210e2ab76 100644 --- a/drivers/crypto/caam/intern.h +++ b/drivers/crypto/caam/intern.h @@ -92,6 +92,7 @@ struct caam_drv_private { */ u8 total_jobrs; /* Total Job Rings in device */ u8 qi_present; /* Nonzero if QI present in device */ + u8 blob_present; /* Nonzero if BLOB support present in device */ u8 mc_en; /* Nonzero if MC f/w is active */ int secvio_irq; /* Security violation interrupt number */ int virt_en; /* Virtualization enabled in CAAM */ diff --git a/drivers/crypto/caam/regs.h b/drivers/crypto/caam/regs.h index 3738625c0250..b829066f5063 100644 --- a/drivers/crypto/caam/regs.h +++ b/drivers/crypto/caam/regs.h @@ -414,6 +414,7 @@ struct caam_perfmon { #define CTPR_MS_PG_SZ_MASK 0x10 #define CTPR_MS_PG_SZ_SHIFT 4 u32 comp_parms_ms; /* CTPR - Compile Parameters Register */ +#define CTPR_LS_BLOB BIT(1) u32 comp_parms_ls; /* CTPR - Compile Parameters Register */ u64 rsvd1[2]; diff --git a/include/soc/fsl/caam-blob.h b/include/soc/fsl/caam-blob.h index ec57eec4f2d2..8e821bd56e54 100644 --- a/include/soc/fsl/caam-blob.h +++ b/include/soc/fsl/caam-blob.h @@ -38,8 +38,9 @@ struct caam_blob_info { /** * caam_blob_gen_init - initialize blob generation - * Return: pointer to new caam_blob_priv instance on success - * and error pointer otherwise + * Return: pointer to new &struct caam_blob_priv instance on success + * and ``ERR_PTR(-ENODEV)`` if CAAM has no hardware blobbing support + * or no job ring could be allocated. */ struct caam_blob_priv *caam_blob_gen_init(void); diff --git a/security/keys/trusted-keys/trusted_caam.c b/security/keys/trusted-keys/trusted_caam.c index 46cb2484ec36..e3415c520c0a 100644 --- a/security/keys/trusted-keys/trusted_caam.c +++ b/security/keys/trusted-keys/trusted_caam.c @@ -55,10 +55,8 @@ static int trusted_caam_init(void) int ret; blobifier = caam_blob_gen_init(); - if (IS_ERR(blobifier)) { - pr_err("Job Ring Device allocation for transform failed\n"); + if (IS_ERR(blobifier)) return PTR_ERR(blobifier); - } ret = register_key_type(&key_type_trusted); if (ret)
diff --git a/drivers/crypto/caam/Kconfig b/drivers/crypto/caam/Kconfig index 84ea7cba5ee5..ea9f8b1ae981 100644 --- a/drivers/crypto/caam/Kconfig +++ b/drivers/crypto/caam/Kconfig @@ -151,6 +151,9 @@ config CRYPTO_DEV_FSL_CAAM_RNG_API Selecting this will register the SEC4 hardware rng to the hw_random API for supplying the kernel entropy pool. +config CRYPTO_DEV_FSL_CAAM_BLOB_GEN + bool + endif # CRYPTO_DEV_FSL_CAAM_JR endif # CRYPTO_DEV_FSL_CAAM diff --git a/drivers/crypto/caam/Makefile b/drivers/crypto/caam/Makefile index 3570286eb9ce..25f7ae5a4642 100644 --- a/drivers/crypto/caam/Makefile +++ b/drivers/crypto/caam/Makefile @@ -21,6 +21,7 @@ caam_jr-$(CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API_QI) += caamalg_qi.o caam_jr-$(CONFIG_CRYPTO_DEV_FSL_CAAM_AHASH_API) += caamhash.o caam_jr-$(CONFIG_CRYPTO_DEV_FSL_CAAM_RNG_API) += caamrng.o caam_jr-$(CONFIG_CRYPTO_DEV_FSL_CAAM_PKC_API) += caampkc.o pkc_desc.o +caam_jr-$(CONFIG_CRYPTO_DEV_FSL_CAAM_BLOB_GEN) += blob_gen.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/blob_gen.c b/drivers/crypto/caam/blob_gen.c new file mode 100644 index 000000000000..d0b1a0015308 --- /dev/null +++ b/drivers/crypto/caam/blob_gen.c @@ -0,0 +1,164 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2015 Pengutronix, Steffen Trumtrar <kernel@pengutronix.de> + * Copyright (C) 2021 Pengutronix, Ahmad Fatoum <kernel@pengutronix.de> + */ + +#include <linux/device.h> +#include <soc/fsl/caam-blob.h> + +#include "compat.h" +#include "desc_constr.h" +#include "desc.h" +#include "error.h" +#include "intern.h" +#include "jr.h" +#include "regs.h" + +#define CAAM_BLOB_DESC_BYTES_MAX \ + /* Command to initialize & stating length of descriptor */ \ + (CAAM_CMD_SZ + \ + /* Command to append the key-modifier + key-modifier data */ \ + CAAM_CMD_SZ + CAAM_BLOB_KEYMOD_LENGTH + \ + /* Command to include input key + pointer to the input key */ \ + CAAM_CMD_SZ + CAAM_PTR_SZ_MAX + \ + /* Command to include output key + pointer to the output key */ \ + CAAM_CMD_SZ + CAAM_PTR_SZ_MAX + \ + /* Command describing the operation to perform */ \ + CAAM_CMD_SZ) + +struct caam_blob_priv { + struct device jrdev; +}; + +struct caam_blob_job_result { + int err; + struct completion completion; +}; + +static void caam_blob_job_done(struct device *dev, u32 *desc, u32 err, void *context) +{ + struct caam_blob_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); + + res->err = ecode; + + /* + * Upon completion, desc points to a buffer containing a CAAM job + * descriptor which encapsulates data into an externally-storable + * blob. + */ + complete(&res->completion); +} + +int caam_process_blob(struct caam_blob_priv *priv, + struct caam_blob_info *info, bool encap) +{ + struct caam_blob_job_result testres; + struct device *jrdev = &priv->jrdev; + dma_addr_t dma_in, dma_out; + int op = OP_PCLID_BLOB; + size_t output_len; + u32 *desc; + int ret; + + if (info->key_mod_len > CAAM_BLOB_KEYMOD_LENGTH) + return -EINVAL; + + if (encap) { + op |= OP_TYPE_ENCAP_PROTOCOL; + output_len = info->input_len + CAAM_BLOB_OVERHEAD; + } else { + op |= OP_TYPE_DECAP_PROTOCOL; + output_len = info->input_len - CAAM_BLOB_OVERHEAD; + } + + desc = kzalloc(CAAM_BLOB_DESC_BYTES_MAX, GFP_KERNEL | GFP_DMA); + if (!desc) + return -ENOMEM; + + dma_in = dma_map_single(jrdev, info->input, info->input_len, + DMA_TO_DEVICE); + if (dma_mapping_error(jrdev, dma_in)) { + dev_err(jrdev, "unable to map input DMA buffer\n"); + ret = -ENOMEM; + goto out_free; + } + + dma_out = dma_map_single(jrdev, info->output, output_len, + DMA_FROM_DEVICE); + if (dma_mapping_error(jrdev, dma_out)) { + dev_err(jrdev, "unable to map output DMA buffer\n"); + ret = -ENOMEM; + goto out_unmap_in; + } + + /* + * A data blob is encrypted using a blob key (BK); a random number. + * The BK is used as an AES-CCM key. The initial block (B0) and the + * initial counter (Ctr0) are generated automatically and stored in + * Class 1 Context DWords 0+1+2+3. The random BK is stored in the + * Class 1 Key Register. Operation Mode is set to AES-CCM. + */ + + init_job_desc(desc, 0); + append_key_as_imm(desc, info->key_mod, info->key_mod_len, + info->key_mod_len, CLASS_2 | KEY_DEST_CLASS_REG); + append_seq_in_ptr_intlen(desc, dma_in, info->input_len, 0); + append_seq_out_ptr_intlen(desc, dma_out, output_len, 0); + append_operation(desc, op); + + print_hex_dump_debug("data@"__stringify(__LINE__)": ", + DUMP_PREFIX_ADDRESS, 16, 1, info->input, + info->input_len, false); + print_hex_dump_debug("jobdesc@"__stringify(__LINE__)": ", + DUMP_PREFIX_ADDRESS, 16, 1, desc, + desc_bytes(desc), false); + + testres.err = 0; + init_completion(&testres.completion); + + ret = caam_jr_enqueue(jrdev, desc, caam_blob_job_done, &testres); + if (ret == -EINPROGRESS) { + wait_for_completion(&testres.completion); + ret = testres.err; + print_hex_dump_debug("output@"__stringify(__LINE__)": ", + DUMP_PREFIX_ADDRESS, 16, 1, info->output, + output_len, false); + } + + if (ret == 0) + info->output_len = output_len; + + dma_unmap_single(jrdev, dma_out, output_len, DMA_FROM_DEVICE); +out_unmap_in: + dma_unmap_single(jrdev, dma_in, info->input_len, DMA_TO_DEVICE); +out_free: + kfree(desc); + + return ret; +} +EXPORT_SYMBOL(caam_process_blob); + +struct caam_blob_priv *caam_blob_gen_init(void) +{ + struct device *jrdev; + + jrdev = caam_jr_alloc(); + if (IS_ERR(jrdev)) + return ERR_CAST(jrdev); + + return container_of(jrdev, struct caam_blob_priv, jrdev); +} +EXPORT_SYMBOL(caam_blob_gen_init); + +void caam_blob_gen_exit(struct caam_blob_priv *priv) +{ + caam_jr_free(&priv->jrdev); +} +EXPORT_SYMBOL(caam_blob_gen_exit); diff --git a/include/soc/fsl/caam-blob.h b/include/soc/fsl/caam-blob.h new file mode 100644 index 000000000000..ec57eec4f2d2 --- /dev/null +++ b/include/soc/fsl/caam-blob.h @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2020 Pengutronix, Ahmad Fatoum <kernel@pengutronix.de> + */ + +#ifndef __CAAM_BLOB_GEN +#define __CAAM_BLOB_GEN + +#include <linux/types.h> +#include <linux/errno.h> + +#define CAAM_BLOB_KEYMOD_LENGTH 16 +#define CAAM_BLOB_OVERHEAD (32 + 16) +#define CAAM_BLOB_MAX_LEN 4096 + +struct caam_blob_priv; + +/** + * struct caam_blob_info - information for CAAM blobbing + * @input: pointer to input buffer (must be DMAable) + * @input_len: length of @input buffer in bytes. + * @output: pointer to output buffer (must be DMAable) + * @output_len: length of @output buffer in bytes. + * @key_mod: key modifier + * @key_mod_len: length of @key_mod in bytes. + * May not exceed %CAAM_BLOB_KEYMOD_LENGTH + */ +struct caam_blob_info { + void *input; + size_t input_len; + + void *output; + size_t output_len; + + const void *key_mod; + size_t key_mod_len; +}; + +/** + * caam_blob_gen_init - initialize blob generation + * Return: pointer to new caam_blob_priv instance on success + * and error pointer otherwise + */ +struct caam_blob_priv *caam_blob_gen_init(void); + +/** + * caam_blob_gen_exit - free blob generation resources + * @priv: instance returned by caam_blob_gen_init + */ +void caam_blob_gen_exit(struct caam_blob_priv *priv); + +/** + * caam_process_blob - encapsulate or decapsulate blob + * @priv: instance returned by caam_blob_gen_init + * @info: pointer to blobbing info describing key, blob and + * key modifier buffers. + * @encap: true for encapsulation, false for decapsulation + * + * Return: %0 and sets info->output_len on success and a negative + * error code otherwise. + */ +int caam_process_blob(struct caam_blob_priv *priv, + struct caam_blob_info *info, bool encap); + +/** + * caam_encap_blob - encapsulate blob + * @priv: instance returned by caam_blob_gen_init + * @info: pointer to blobbing info describing input key, + * output blob and key modifier buffers. + * + * Return: %0 and sets @info->output_len on success and + * a negative error code otherwise. + */ +static inline int caam_encap_blob(struct caam_blob_priv *priv, + struct caam_blob_info *info) +{ + if (info->output_len < info->input_len + CAAM_BLOB_OVERHEAD) + return -EINVAL; + + return caam_process_blob(priv, info, true); +} + +/** + * caam_decap_blob - decapsulate blob + * @priv: instance returned by caam_blob_gen_init + * @info: pointer to blobbing info describing output key, + * input blob and key modifier buffers. + * + * Return: %0 and sets @info->output_len on success and + * a negative error code otherwise. + */ +static inline int caam_decap_blob(struct caam_blob_priv *priv, + struct caam_blob_info *info) +{ + if (info->input_len < CAAM_BLOB_OVERHEAD || + info->output_len < info->input_len - CAAM_BLOB_OVERHEAD) + return -EINVAL; + + return caam_process_blob(priv, info, false); +} + +#endif