Hi,
This is pull request for trusted keys subsystem containing a new key type for the Data Co-Processor (DCP), which is an IP core built into many NXP SoCs such as i.mx6ull. BR, Jarkko -----BEGIN PGP SIGNATURE----- iJYEABYKAD4WIQRE6pSOnaBC00OEHEIaerohdGur0gUCZjzswCAcamFya2tvLnNh a2tpbmVuQGxpbnV4LmludGVsLmNvbQAKCRAaerohdGur0iVQAP9lxVjTKjMHQB01 KFAXUogNU42JuJjzEiC5TaDxFPNHlAEAqVBYnPIZdP4VMF3UalVgIu/eRfvxTW/t klC+q7WiEwg= =+33z -----END PGP SIGNATURE----- Merge tag 'keys-trusted-next-6.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jarkko/linux-tpmdd Pull trusted keys updates from Jarkko Sakkinen: "This contains a new key type for the Data Co-Processor (DCP), which is an IP core built into many NXP SoCs such as i.mx6ull" * tag 'keys-trusted-next-6.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jarkko/linux-tpmdd: docs: trusted-encrypted: add DCP as new trust source docs: document DCP-backed trusted keys kernel params MAINTAINERS: add entry for DCP-based trusted keys KEYS: trusted: Introduce NXP DCP-backed trusted keys KEYS: trusted: improve scalability of trust source config crypto: mxs-dcp: Add support for hardware-bound keys
This commit is contained in:
commit
c024814828
@ -6765,6 +6765,7 @@
|
||||
- "tpm"
|
||||
- "tee"
|
||||
- "caam"
|
||||
- "dcp"
|
||||
If not specified then it defaults to iterating through
|
||||
the trust source list starting with TPM and assigns the
|
||||
first trust source as a backend which is initialized
|
||||
@ -6780,6 +6781,18 @@
|
||||
If not specified, "default" is used. In this case,
|
||||
the RNG's choice is left to each individual trust source.
|
||||
|
||||
trusted.dcp_use_otp_key
|
||||
This is intended to be used in combination with
|
||||
trusted.source=dcp and will select the DCP OTP key
|
||||
instead of the DCP UNIQUE key blob encryption.
|
||||
|
||||
trusted.dcp_skip_zk_test
|
||||
This is intended to be used in combination with
|
||||
trusted.source=dcp and will disable the check if the
|
||||
blob key is all zeros. This is helpful for situations where
|
||||
having this key zero'ed is acceptable. E.g. in testing
|
||||
scenarios.
|
||||
|
||||
tsc= Disable clocksource stability checks for TSC.
|
||||
Format: <string>
|
||||
[x86] reliable: mark tsc clocksource as reliable, this
|
||||
|
@ -42,6 +42,14 @@ safe.
|
||||
randomly generated and fused into each SoC at manufacturing time.
|
||||
Otherwise, a common fixed test key is used instead.
|
||||
|
||||
(4) DCP (Data Co-Processor: crypto accelerator of various i.MX SoCs)
|
||||
|
||||
Rooted to a one-time programmable key (OTP) that is generally burnt
|
||||
in the on-chip fuses and is accessible to the DCP encryption engine only.
|
||||
DCP provides two keys that can be used as root of trust: the OTP key
|
||||
and the UNIQUE key. Default is to use the UNIQUE key, but selecting
|
||||
the OTP key can be done via a module parameter (dcp_use_otp_key).
|
||||
|
||||
* Execution isolation
|
||||
|
||||
(1) TPM
|
||||
@ -57,6 +65,12 @@ safe.
|
||||
|
||||
Fixed set of operations running in isolated execution environment.
|
||||
|
||||
(4) DCP
|
||||
|
||||
Fixed set of cryptographic operations running in isolated execution
|
||||
environment. Only basic blob key encryption is executed there.
|
||||
The actual key sealing/unsealing is done on main processor/kernel space.
|
||||
|
||||
* Optional binding to platform integrity state
|
||||
|
||||
(1) TPM
|
||||
@ -79,6 +93,11 @@ safe.
|
||||
Relies on the High Assurance Boot (HAB) mechanism of NXP SoCs
|
||||
for platform integrity.
|
||||
|
||||
(4) DCP
|
||||
|
||||
Relies on Secure/Trusted boot process (called HAB by vendor) for
|
||||
platform integrity.
|
||||
|
||||
* Interfaces and APIs
|
||||
|
||||
(1) TPM
|
||||
@ -94,6 +113,11 @@ safe.
|
||||
|
||||
Interface is specific to silicon vendor.
|
||||
|
||||
(4) DCP
|
||||
|
||||
Vendor-specific API that is implemented as part of the DCP crypto driver in
|
||||
``drivers/crypto/mxs-dcp.c``.
|
||||
|
||||
* Threat model
|
||||
|
||||
The strength and appropriateness of a particular trust source for a given
|
||||
@ -129,6 +153,13 @@ selected trust source:
|
||||
CAAM HWRNG, enable CRYPTO_DEV_FSL_CAAM_RNG_API and ensure the device
|
||||
is probed.
|
||||
|
||||
* DCP (Data Co-Processor: crypto accelerator of various i.MX SoCs)
|
||||
|
||||
The DCP hardware device itself does not provide a dedicated RNG interface,
|
||||
so the kernel default RNG is used. SoCs with DCP like the i.MX6ULL do have
|
||||
a dedicated hardware RNG that is independent from DCP which can be enabled
|
||||
to back the kernel RNG.
|
||||
|
||||
Users may override this by specifying ``trusted.rng=kernel`` on the kernel
|
||||
command-line to override the used RNG with the kernel's random number pool.
|
||||
|
||||
@ -231,6 +262,19 @@ Usage::
|
||||
CAAM-specific format. The key length for new keys is always in bytes.
|
||||
Trusted Keys can be 32 - 128 bytes (256 - 1024 bits).
|
||||
|
||||
Trusted Keys usage: DCP
|
||||
-----------------------
|
||||
|
||||
Usage::
|
||||
|
||||
keyctl add trusted name "new keylen" ring
|
||||
keyctl add trusted name "load hex_blob" ring
|
||||
keyctl print keyid
|
||||
|
||||
"keyctl print" returns an ASCII hex copy of the sealed key, which is in format
|
||||
specific to this DCP key-blob implementation. The key length for new keys is
|
||||
always in bytes. Trusted Keys can be 32 - 128 bytes (256 - 1024 bits).
|
||||
|
||||
Encrypted Keys usage
|
||||
--------------------
|
||||
|
||||
@ -426,3 +470,12 @@ string length.
|
||||
privkey is the binary representation of TPM2B_PUBLIC excluding the
|
||||
initial TPM2B header which can be reconstructed from the ASN.1 octed
|
||||
string length.
|
||||
|
||||
DCP Blob Format
|
||||
---------------
|
||||
|
||||
.. kernel-doc:: security/keys/trusted-keys/trusted_dcp.c
|
||||
:doc: dcp blob format
|
||||
|
||||
.. kernel-doc:: security/keys/trusted-keys/trusted_dcp.c
|
||||
:identifiers: struct dcp_blob_fmt
|
||||
|
@ -12037,6 +12037,15 @@ S: Maintained
|
||||
F: include/keys/trusted_caam.h
|
||||
F: security/keys/trusted-keys/trusted_caam.c
|
||||
|
||||
KEYS-TRUSTED-DCP
|
||||
M: David Gstir <david@sigma-star.at>
|
||||
R: sigma star Kernel Team <upstream+dcp@sigma-star.at>
|
||||
L: linux-integrity@vger.kernel.org
|
||||
L: keyrings@vger.kernel.org
|
||||
S: Supported
|
||||
F: include/keys/trusted_dcp.h
|
||||
F: security/keys/trusted-keys/trusted_dcp.c
|
||||
|
||||
KEYS-TRUSTED-TEE
|
||||
M: Sumit Garg <sumit.garg@linaro.org>
|
||||
L: linux-integrity@vger.kernel.org
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/stmp_device.h>
|
||||
#include <linux/clk.h>
|
||||
#include <soc/fsl/dcp.h>
|
||||
|
||||
#include <crypto/aes.h>
|
||||
#include <crypto/sha1.h>
|
||||
@ -101,6 +102,7 @@ struct dcp_async_ctx {
|
||||
struct crypto_skcipher *fallback;
|
||||
unsigned int key_len;
|
||||
uint8_t key[AES_KEYSIZE_128];
|
||||
bool key_referenced;
|
||||
};
|
||||
|
||||
struct dcp_aes_req_ctx {
|
||||
@ -155,6 +157,7 @@ static struct dcp *global_sdcp;
|
||||
#define MXS_DCP_CONTROL0_HASH_TERM (1 << 13)
|
||||
#define MXS_DCP_CONTROL0_HASH_INIT (1 << 12)
|
||||
#define MXS_DCP_CONTROL0_PAYLOAD_KEY (1 << 11)
|
||||
#define MXS_DCP_CONTROL0_OTP_KEY (1 << 10)
|
||||
#define MXS_DCP_CONTROL0_CIPHER_ENCRYPT (1 << 8)
|
||||
#define MXS_DCP_CONTROL0_CIPHER_INIT (1 << 9)
|
||||
#define MXS_DCP_CONTROL0_ENABLE_HASH (1 << 6)
|
||||
@ -168,6 +171,8 @@ static struct dcp *global_sdcp;
|
||||
#define MXS_DCP_CONTROL1_CIPHER_MODE_ECB (0 << 4)
|
||||
#define MXS_DCP_CONTROL1_CIPHER_SELECT_AES128 (0 << 0)
|
||||
|
||||
#define MXS_DCP_CONTROL1_KEY_SELECT_SHIFT 8
|
||||
|
||||
static int mxs_dcp_start_dma(struct dcp_async_ctx *actx)
|
||||
{
|
||||
int dma_err;
|
||||
@ -224,13 +229,16 @@ static int mxs_dcp_run_aes(struct dcp_async_ctx *actx,
|
||||
struct dcp *sdcp = global_sdcp;
|
||||
struct dcp_dma_desc *desc = &sdcp->coh->desc[actx->chan];
|
||||
struct dcp_aes_req_ctx *rctx = skcipher_request_ctx(req);
|
||||
bool key_referenced = actx->key_referenced;
|
||||
int ret;
|
||||
|
||||
key_phys = dma_map_single(sdcp->dev, sdcp->coh->aes_key,
|
||||
2 * AES_KEYSIZE_128, DMA_TO_DEVICE);
|
||||
ret = dma_mapping_error(sdcp->dev, key_phys);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (!key_referenced) {
|
||||
key_phys = dma_map_single(sdcp->dev, sdcp->coh->aes_key,
|
||||
2 * AES_KEYSIZE_128, DMA_TO_DEVICE);
|
||||
ret = dma_mapping_error(sdcp->dev, key_phys);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
src_phys = dma_map_single(sdcp->dev, sdcp->coh->aes_in_buf,
|
||||
DCP_BUF_SZ, DMA_TO_DEVICE);
|
||||
@ -255,8 +263,12 @@ static int mxs_dcp_run_aes(struct dcp_async_ctx *actx,
|
||||
MXS_DCP_CONTROL0_INTERRUPT |
|
||||
MXS_DCP_CONTROL0_ENABLE_CIPHER;
|
||||
|
||||
/* Payload contains the key. */
|
||||
desc->control0 |= MXS_DCP_CONTROL0_PAYLOAD_KEY;
|
||||
if (key_referenced)
|
||||
/* Set OTP key bit to select the key via KEY_SELECT. */
|
||||
desc->control0 |= MXS_DCP_CONTROL0_OTP_KEY;
|
||||
else
|
||||
/* Payload contains the key. */
|
||||
desc->control0 |= MXS_DCP_CONTROL0_PAYLOAD_KEY;
|
||||
|
||||
if (rctx->enc)
|
||||
desc->control0 |= MXS_DCP_CONTROL0_CIPHER_ENCRYPT;
|
||||
@ -270,6 +282,9 @@ static int mxs_dcp_run_aes(struct dcp_async_ctx *actx,
|
||||
else
|
||||
desc->control1 |= MXS_DCP_CONTROL1_CIPHER_MODE_CBC;
|
||||
|
||||
if (key_referenced)
|
||||
desc->control1 |= sdcp->coh->aes_key[0] << MXS_DCP_CONTROL1_KEY_SELECT_SHIFT;
|
||||
|
||||
desc->next_cmd_addr = 0;
|
||||
desc->source = src_phys;
|
||||
desc->destination = dst_phys;
|
||||
@ -284,9 +299,9 @@ aes_done_run:
|
||||
err_dst:
|
||||
dma_unmap_single(sdcp->dev, src_phys, DCP_BUF_SZ, DMA_TO_DEVICE);
|
||||
err_src:
|
||||
dma_unmap_single(sdcp->dev, key_phys, 2 * AES_KEYSIZE_128,
|
||||
DMA_TO_DEVICE);
|
||||
|
||||
if (!key_referenced)
|
||||
dma_unmap_single(sdcp->dev, key_phys, 2 * AES_KEYSIZE_128,
|
||||
DMA_TO_DEVICE);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -453,7 +468,7 @@ static int mxs_dcp_aes_enqueue(struct skcipher_request *req, int enc, int ecb)
|
||||
struct dcp_aes_req_ctx *rctx = skcipher_request_ctx(req);
|
||||
int ret;
|
||||
|
||||
if (unlikely(actx->key_len != AES_KEYSIZE_128))
|
||||
if (unlikely(actx->key_len != AES_KEYSIZE_128 && !actx->key_referenced))
|
||||
return mxs_dcp_block_fallback(req, enc);
|
||||
|
||||
rctx->enc = enc;
|
||||
@ -500,6 +515,7 @@ static int mxs_dcp_aes_setkey(struct crypto_skcipher *tfm, const u8 *key,
|
||||
* there can still be an operation in progress.
|
||||
*/
|
||||
actx->key_len = len;
|
||||
actx->key_referenced = false;
|
||||
if (len == AES_KEYSIZE_128) {
|
||||
memcpy(actx->key, key, len);
|
||||
return 0;
|
||||
@ -516,6 +532,32 @@ static int mxs_dcp_aes_setkey(struct crypto_skcipher *tfm, const u8 *key,
|
||||
return crypto_skcipher_setkey(actx->fallback, key, len);
|
||||
}
|
||||
|
||||
static int mxs_dcp_aes_setrefkey(struct crypto_skcipher *tfm, const u8 *key,
|
||||
unsigned int len)
|
||||
{
|
||||
struct dcp_async_ctx *actx = crypto_skcipher_ctx(tfm);
|
||||
|
||||
if (len != DCP_PAES_KEYSIZE)
|
||||
return -EINVAL;
|
||||
|
||||
switch (key[0]) {
|
||||
case DCP_PAES_KEY_SLOT0:
|
||||
case DCP_PAES_KEY_SLOT1:
|
||||
case DCP_PAES_KEY_SLOT2:
|
||||
case DCP_PAES_KEY_SLOT3:
|
||||
case DCP_PAES_KEY_UNIQUE:
|
||||
case DCP_PAES_KEY_OTP:
|
||||
memcpy(actx->key, key, len);
|
||||
actx->key_len = len;
|
||||
actx->key_referenced = true;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxs_dcp_aes_fallback_init_tfm(struct crypto_skcipher *tfm)
|
||||
{
|
||||
const char *name = crypto_tfm_alg_name(crypto_skcipher_tfm(tfm));
|
||||
@ -539,6 +581,13 @@ static void mxs_dcp_aes_fallback_exit_tfm(struct crypto_skcipher *tfm)
|
||||
crypto_free_skcipher(actx->fallback);
|
||||
}
|
||||
|
||||
static int mxs_dcp_paes_init_tfm(struct crypto_skcipher *tfm)
|
||||
{
|
||||
crypto_skcipher_set_reqsize(tfm, sizeof(struct dcp_aes_req_ctx));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Hashing (SHA1/SHA256)
|
||||
*/
|
||||
@ -889,6 +938,39 @@ static struct skcipher_alg dcp_aes_algs[] = {
|
||||
.ivsize = AES_BLOCK_SIZE,
|
||||
.init = mxs_dcp_aes_fallback_init_tfm,
|
||||
.exit = mxs_dcp_aes_fallback_exit_tfm,
|
||||
}, {
|
||||
.base.cra_name = "ecb(paes)",
|
||||
.base.cra_driver_name = "ecb-paes-dcp",
|
||||
.base.cra_priority = 401,
|
||||
.base.cra_alignmask = 15,
|
||||
.base.cra_flags = CRYPTO_ALG_ASYNC | CRYPTO_ALG_INTERNAL,
|
||||
.base.cra_blocksize = AES_BLOCK_SIZE,
|
||||
.base.cra_ctxsize = sizeof(struct dcp_async_ctx),
|
||||
.base.cra_module = THIS_MODULE,
|
||||
|
||||
.min_keysize = DCP_PAES_KEYSIZE,
|
||||
.max_keysize = DCP_PAES_KEYSIZE,
|
||||
.setkey = mxs_dcp_aes_setrefkey,
|
||||
.encrypt = mxs_dcp_aes_ecb_encrypt,
|
||||
.decrypt = mxs_dcp_aes_ecb_decrypt,
|
||||
.init = mxs_dcp_paes_init_tfm,
|
||||
}, {
|
||||
.base.cra_name = "cbc(paes)",
|
||||
.base.cra_driver_name = "cbc-paes-dcp",
|
||||
.base.cra_priority = 401,
|
||||
.base.cra_alignmask = 15,
|
||||
.base.cra_flags = CRYPTO_ALG_ASYNC | CRYPTO_ALG_INTERNAL,
|
||||
.base.cra_blocksize = AES_BLOCK_SIZE,
|
||||
.base.cra_ctxsize = sizeof(struct dcp_async_ctx),
|
||||
.base.cra_module = THIS_MODULE,
|
||||
|
||||
.min_keysize = DCP_PAES_KEYSIZE,
|
||||
.max_keysize = DCP_PAES_KEYSIZE,
|
||||
.setkey = mxs_dcp_aes_setrefkey,
|
||||
.encrypt = mxs_dcp_aes_cbc_encrypt,
|
||||
.decrypt = mxs_dcp_aes_cbc_decrypt,
|
||||
.ivsize = AES_BLOCK_SIZE,
|
||||
.init = mxs_dcp_paes_init_tfm,
|
||||
},
|
||||
};
|
||||
|
||||
|
11
include/keys/trusted_dcp.h
Normal file
11
include/keys/trusted_dcp.h
Normal file
@ -0,0 +1,11 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (C) 2021 sigma star gmbh
|
||||
*/
|
||||
|
||||
#ifndef TRUSTED_DCP_H
|
||||
#define TRUSTED_DCP_H
|
||||
|
||||
extern struct trusted_key_ops dcp_trusted_key_ops;
|
||||
|
||||
#endif
|
20
include/soc/fsl/dcp.h
Normal file
20
include/soc/fsl/dcp.h
Normal file
@ -0,0 +1,20 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (C) 2021 sigma star gmbh
|
||||
*
|
||||
* Specifies paes key slot handles for NXP's DCP (Data Co-Processor) to be used
|
||||
* with the crypto_skcipher_setkey().
|
||||
*/
|
||||
|
||||
#ifndef MXS_DCP_H
|
||||
#define MXS_DCP_H
|
||||
|
||||
#define DCP_PAES_KEYSIZE 1
|
||||
#define DCP_PAES_KEY_SLOT0 0x00
|
||||
#define DCP_PAES_KEY_SLOT1 0x01
|
||||
#define DCP_PAES_KEY_SLOT2 0x02
|
||||
#define DCP_PAES_KEY_SLOT3 0x03
|
||||
#define DCP_PAES_KEY_UNIQUE 0xfe
|
||||
#define DCP_PAES_KEY_OTP 0xff
|
||||
|
||||
#endif /* MXS_DCP_H */
|
@ -1,3 +1,6 @@
|
||||
config HAVE_TRUSTED_KEYS
|
||||
bool
|
||||
|
||||
config TRUSTED_KEYS_TPM
|
||||
bool "TPM-based trusted keys"
|
||||
depends on TCG_TPM >= TRUSTED_KEYS
|
||||
@ -9,6 +12,7 @@ config TRUSTED_KEYS_TPM
|
||||
select ASN1_ENCODER
|
||||
select OID_REGISTRY
|
||||
select ASN1
|
||||
select HAVE_TRUSTED_KEYS
|
||||
help
|
||||
Enable use of the Trusted Platform Module (TPM) as trusted key
|
||||
backend. Trusted keys are random number symmetric keys,
|
||||
@ -20,6 +24,7 @@ config TRUSTED_KEYS_TEE
|
||||
bool "TEE-based trusted keys"
|
||||
depends on TEE >= TRUSTED_KEYS
|
||||
default y
|
||||
select HAVE_TRUSTED_KEYS
|
||||
help
|
||||
Enable use of the Trusted Execution Environment (TEE) as trusted
|
||||
key backend.
|
||||
@ -29,10 +34,19 @@ config TRUSTED_KEYS_CAAM
|
||||
depends on CRYPTO_DEV_FSL_CAAM_JR >= TRUSTED_KEYS
|
||||
select CRYPTO_DEV_FSL_CAAM_BLOB_GEN
|
||||
default y
|
||||
select HAVE_TRUSTED_KEYS
|
||||
help
|
||||
Enable use of NXP's Cryptographic Accelerator and Assurance Module
|
||||
(CAAM) as trusted key backend.
|
||||
|
||||
if !TRUSTED_KEYS_TPM && !TRUSTED_KEYS_TEE && !TRUSTED_KEYS_CAAM
|
||||
comment "No trust source selected!"
|
||||
config TRUSTED_KEYS_DCP
|
||||
bool "DCP-based trusted keys"
|
||||
depends on CRYPTO_DEV_MXS_DCP >= TRUSTED_KEYS
|
||||
default y
|
||||
select HAVE_TRUSTED_KEYS
|
||||
help
|
||||
Enable use of NXP's DCP (Data Co-Processor) as trusted key backend.
|
||||
|
||||
if !HAVE_TRUSTED_KEYS
|
||||
comment "No trust source selected!"
|
||||
endif
|
||||
|
@ -14,3 +14,5 @@ trusted-$(CONFIG_TRUSTED_KEYS_TPM) += tpm2key.asn1.o
|
||||
trusted-$(CONFIG_TRUSTED_KEYS_TEE) += trusted_tee.o
|
||||
|
||||
trusted-$(CONFIG_TRUSTED_KEYS_CAAM) += trusted_caam.o
|
||||
|
||||
trusted-$(CONFIG_TRUSTED_KEYS_DCP) += trusted_dcp.o
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <keys/trusted-type.h>
|
||||
#include <keys/trusted_tee.h>
|
||||
#include <keys/trusted_caam.h>
|
||||
#include <keys/trusted_dcp.h>
|
||||
#include <keys/trusted_tpm.h>
|
||||
#include <linux/capability.h>
|
||||
#include <linux/err.h>
|
||||
@ -30,7 +31,7 @@ MODULE_PARM_DESC(rng, "Select trusted key RNG");
|
||||
|
||||
static char *trusted_key_source;
|
||||
module_param_named(source, trusted_key_source, charp, 0);
|
||||
MODULE_PARM_DESC(source, "Select trusted keys source (tpm, tee or caam)");
|
||||
MODULE_PARM_DESC(source, "Select trusted keys source (tpm, tee, caam or dcp)");
|
||||
|
||||
static const struct trusted_key_source trusted_key_sources[] = {
|
||||
#if defined(CONFIG_TRUSTED_KEYS_TPM)
|
||||
@ -42,6 +43,9 @@ static const struct trusted_key_source trusted_key_sources[] = {
|
||||
#if defined(CONFIG_TRUSTED_KEYS_CAAM)
|
||||
{ "caam", &trusted_key_caam_ops },
|
||||
#endif
|
||||
#if defined(CONFIG_TRUSTED_KEYS_DCP)
|
||||
{ "dcp", &dcp_trusted_key_ops },
|
||||
#endif
|
||||
};
|
||||
|
||||
DEFINE_STATIC_CALL_NULL(trusted_key_seal, *trusted_key_sources[0].ops->seal);
|
||||
|
332
security/keys/trusted-keys/trusted_dcp.c
Normal file
332
security/keys/trusted-keys/trusted_dcp.c
Normal file
@ -0,0 +1,332 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2021 sigma star gmbh
|
||||
*/
|
||||
|
||||
#include <crypto/aead.h>
|
||||
#include <crypto/aes.h>
|
||||
#include <crypto/algapi.h>
|
||||
#include <crypto/gcm.h>
|
||||
#include <crypto/skcipher.h>
|
||||
#include <keys/trusted-type.h>
|
||||
#include <linux/key-type.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <soc/fsl/dcp.h>
|
||||
|
||||
#define DCP_BLOB_VERSION 1
|
||||
#define DCP_BLOB_AUTHLEN 16
|
||||
|
||||
/**
|
||||
* DOC: dcp blob format
|
||||
*
|
||||
* The Data Co-Processor (DCP) provides hardware-bound AES keys using its
|
||||
* AES encryption engine only. It does not provide direct key sealing/unsealing.
|
||||
* To make DCP hardware encryption keys usable as trust source, we define
|
||||
* our own custom format that uses a hardware-bound key to secure the sealing
|
||||
* key stored in the key blob.
|
||||
*
|
||||
* Whenever a new trusted key using DCP is generated, we generate a random 128-bit
|
||||
* blob encryption key (BEK) and 128-bit nonce. The BEK and nonce are used to
|
||||
* encrypt the trusted key payload using AES-128-GCM.
|
||||
*
|
||||
* The BEK itself is encrypted using the hardware-bound key using the DCP's AES
|
||||
* encryption engine with AES-128-ECB. The encrypted BEK, generated nonce,
|
||||
* BEK-encrypted payload and authentication tag make up the blob format together
|
||||
* with a version number, payload length and authentication tag.
|
||||
*/
|
||||
|
||||
/**
|
||||
* struct dcp_blob_fmt - DCP BLOB format.
|
||||
*
|
||||
* @fmt_version: Format version, currently being %1.
|
||||
* @blob_key: Random AES 128 key which is used to encrypt @payload,
|
||||
* @blob_key itself is encrypted with OTP or UNIQUE device key in
|
||||
* AES-128-ECB mode by DCP.
|
||||
* @nonce: Random nonce used for @payload encryption.
|
||||
* @payload_len: Length of the plain text @payload.
|
||||
* @payload: The payload itself, encrypted using AES-128-GCM and @blob_key,
|
||||
* GCM auth tag of size DCP_BLOB_AUTHLEN is attached at the end of it.
|
||||
*
|
||||
* The total size of a DCP BLOB is sizeof(struct dcp_blob_fmt) + @payload_len +
|
||||
* DCP_BLOB_AUTHLEN.
|
||||
*/
|
||||
struct dcp_blob_fmt {
|
||||
__u8 fmt_version;
|
||||
__u8 blob_key[AES_KEYSIZE_128];
|
||||
__u8 nonce[AES_KEYSIZE_128];
|
||||
__le32 payload_len;
|
||||
__u8 payload[];
|
||||
} __packed;
|
||||
|
||||
static bool use_otp_key;
|
||||
module_param_named(dcp_use_otp_key, use_otp_key, bool, 0);
|
||||
MODULE_PARM_DESC(dcp_use_otp_key, "Use OTP instead of UNIQUE key for sealing");
|
||||
|
||||
static bool skip_zk_test;
|
||||
module_param_named(dcp_skip_zk_test, skip_zk_test, bool, 0);
|
||||
MODULE_PARM_DESC(dcp_skip_zk_test, "Don't test whether device keys are zero'ed");
|
||||
|
||||
static unsigned int calc_blob_len(unsigned int payload_len)
|
||||
{
|
||||
return sizeof(struct dcp_blob_fmt) + payload_len + DCP_BLOB_AUTHLEN;
|
||||
}
|
||||
|
||||
static int do_dcp_crypto(u8 *in, u8 *out, bool do_encrypt)
|
||||
{
|
||||
struct skcipher_request *req = NULL;
|
||||
struct scatterlist src_sg, dst_sg;
|
||||
struct crypto_skcipher *tfm;
|
||||
u8 paes_key[DCP_PAES_KEYSIZE];
|
||||
DECLARE_CRYPTO_WAIT(wait);
|
||||
int res = 0;
|
||||
|
||||
if (use_otp_key)
|
||||
paes_key[0] = DCP_PAES_KEY_OTP;
|
||||
else
|
||||
paes_key[0] = DCP_PAES_KEY_UNIQUE;
|
||||
|
||||
tfm = crypto_alloc_skcipher("ecb-paes-dcp", CRYPTO_ALG_INTERNAL,
|
||||
CRYPTO_ALG_INTERNAL);
|
||||
if (IS_ERR(tfm)) {
|
||||
res = PTR_ERR(tfm);
|
||||
tfm = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
req = skcipher_request_alloc(tfm, GFP_NOFS);
|
||||
if (!req) {
|
||||
res = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
skcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG |
|
||||
CRYPTO_TFM_REQ_MAY_SLEEP,
|
||||
crypto_req_done, &wait);
|
||||
res = crypto_skcipher_setkey(tfm, paes_key, sizeof(paes_key));
|
||||
if (res < 0)
|
||||
goto out;
|
||||
|
||||
sg_init_one(&src_sg, in, AES_KEYSIZE_128);
|
||||
sg_init_one(&dst_sg, out, AES_KEYSIZE_128);
|
||||
skcipher_request_set_crypt(req, &src_sg, &dst_sg, AES_KEYSIZE_128,
|
||||
NULL);
|
||||
|
||||
if (do_encrypt)
|
||||
res = crypto_wait_req(crypto_skcipher_encrypt(req), &wait);
|
||||
else
|
||||
res = crypto_wait_req(crypto_skcipher_decrypt(req), &wait);
|
||||
|
||||
out:
|
||||
skcipher_request_free(req);
|
||||
crypto_free_skcipher(tfm);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int do_aead_crypto(u8 *in, u8 *out, size_t len, u8 *key, u8 *nonce,
|
||||
bool do_encrypt)
|
||||
{
|
||||
struct aead_request *aead_req = NULL;
|
||||
struct scatterlist src_sg, dst_sg;
|
||||
struct crypto_aead *aead;
|
||||
int ret;
|
||||
|
||||
aead = crypto_alloc_aead("gcm(aes)", 0, CRYPTO_ALG_ASYNC);
|
||||
if (IS_ERR(aead)) {
|
||||
ret = PTR_ERR(aead);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = crypto_aead_setauthsize(aead, DCP_BLOB_AUTHLEN);
|
||||
if (ret < 0) {
|
||||
pr_err("Can't set crypto auth tag len: %d\n", ret);
|
||||
goto free_aead;
|
||||
}
|
||||
|
||||
aead_req = aead_request_alloc(aead, GFP_KERNEL);
|
||||
if (!aead_req) {
|
||||
ret = -ENOMEM;
|
||||
goto free_aead;
|
||||
}
|
||||
|
||||
sg_init_one(&src_sg, in, len);
|
||||
if (do_encrypt) {
|
||||
/*
|
||||
* If we encrypt our buffer has extra space for the auth tag.
|
||||
*/
|
||||
sg_init_one(&dst_sg, out, len + DCP_BLOB_AUTHLEN);
|
||||
} else {
|
||||
sg_init_one(&dst_sg, out, len);
|
||||
}
|
||||
|
||||
aead_request_set_crypt(aead_req, &src_sg, &dst_sg, len, nonce);
|
||||
aead_request_set_callback(aead_req, CRYPTO_TFM_REQ_MAY_SLEEP, NULL,
|
||||
NULL);
|
||||
aead_request_set_ad(aead_req, 0);
|
||||
|
||||
if (crypto_aead_setkey(aead, key, AES_KEYSIZE_128)) {
|
||||
pr_err("Can't set crypto AEAD key\n");
|
||||
ret = -EINVAL;
|
||||
goto free_req;
|
||||
}
|
||||
|
||||
if (do_encrypt)
|
||||
ret = crypto_aead_encrypt(aead_req);
|
||||
else
|
||||
ret = crypto_aead_decrypt(aead_req);
|
||||
|
||||
free_req:
|
||||
aead_request_free(aead_req);
|
||||
free_aead:
|
||||
crypto_free_aead(aead);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int decrypt_blob_key(u8 *key)
|
||||
{
|
||||
return do_dcp_crypto(key, key, false);
|
||||
}
|
||||
|
||||
static int encrypt_blob_key(u8 *key)
|
||||
{
|
||||
return do_dcp_crypto(key, key, true);
|
||||
}
|
||||
|
||||
static int trusted_dcp_seal(struct trusted_key_payload *p, char *datablob)
|
||||
{
|
||||
struct dcp_blob_fmt *b = (struct dcp_blob_fmt *)p->blob;
|
||||
int blen, ret;
|
||||
|
||||
blen = calc_blob_len(p->key_len);
|
||||
if (blen > MAX_BLOB_SIZE)
|
||||
return -E2BIG;
|
||||
|
||||
b->fmt_version = DCP_BLOB_VERSION;
|
||||
get_random_bytes(b->nonce, AES_KEYSIZE_128);
|
||||
get_random_bytes(b->blob_key, AES_KEYSIZE_128);
|
||||
|
||||
ret = do_aead_crypto(p->key, b->payload, p->key_len, b->blob_key,
|
||||
b->nonce, true);
|
||||
if (ret) {
|
||||
pr_err("Unable to encrypt blob payload: %i\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = encrypt_blob_key(b->blob_key);
|
||||
if (ret) {
|
||||
pr_err("Unable to encrypt blob key: %i\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
b->payload_len = get_unaligned_le32(&p->key_len);
|
||||
p->blob_len = blen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int trusted_dcp_unseal(struct trusted_key_payload *p, char *datablob)
|
||||
{
|
||||
struct dcp_blob_fmt *b = (struct dcp_blob_fmt *)p->blob;
|
||||
int blen, ret;
|
||||
|
||||
if (b->fmt_version != DCP_BLOB_VERSION) {
|
||||
pr_err("DCP blob has bad version: %i, expected %i\n",
|
||||
b->fmt_version, DCP_BLOB_VERSION);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
p->key_len = le32_to_cpu(b->payload_len);
|
||||
blen = calc_blob_len(p->key_len);
|
||||
if (blen != p->blob_len) {
|
||||
pr_err("DCP blob has bad length: %i != %i\n", blen,
|
||||
p->blob_len);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = decrypt_blob_key(b->blob_key);
|
||||
if (ret) {
|
||||
pr_err("Unable to decrypt blob key: %i\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = do_aead_crypto(b->payload, p->key, p->key_len + DCP_BLOB_AUTHLEN,
|
||||
b->blob_key, b->nonce, false);
|
||||
if (ret) {
|
||||
pr_err("Unwrap of DCP payload failed: %i\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int test_for_zero_key(void)
|
||||
{
|
||||
/*
|
||||
* Encrypting a plaintext of all 0x55 bytes will yield
|
||||
* this ciphertext in case the DCP test key is used.
|
||||
*/
|
||||
static const u8 bad[] = {0x9a, 0xda, 0xe0, 0x54, 0xf6, 0x3d, 0xfa, 0xff,
|
||||
0x5e, 0xa1, 0x8e, 0x45, 0xed, 0xf6, 0xea, 0x6f};
|
||||
void *buf = NULL;
|
||||
int ret = 0;
|
||||
|
||||
if (skip_zk_test)
|
||||
goto out;
|
||||
|
||||
buf = kmalloc(AES_BLOCK_SIZE, GFP_KERNEL);
|
||||
if (!buf) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
memset(buf, 0x55, AES_BLOCK_SIZE);
|
||||
|
||||
ret = do_dcp_crypto(buf, buf, true);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (memcmp(buf, bad, AES_BLOCK_SIZE) == 0) {
|
||||
pr_warn("Device neither in secure nor trusted mode!\n");
|
||||
ret = -EINVAL;
|
||||
}
|
||||
out:
|
||||
kfree(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int trusted_dcp_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (use_otp_key)
|
||||
pr_info("Using DCP OTP key\n");
|
||||
|
||||
ret = test_for_zero_key();
|
||||
if (ret) {
|
||||
pr_warn("Test for zero'ed keys failed: %i\n", ret);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return register_key_type(&key_type_trusted);
|
||||
}
|
||||
|
||||
static void trusted_dcp_exit(void)
|
||||
{
|
||||
unregister_key_type(&key_type_trusted);
|
||||
}
|
||||
|
||||
struct trusted_key_ops dcp_trusted_key_ops = {
|
||||
.exit = trusted_dcp_exit,
|
||||
.init = trusted_dcp_init,
|
||||
.seal = trusted_dcp_seal,
|
||||
.unseal = trusted_dcp_unseal,
|
||||
.migratable = 0,
|
||||
};
|
Loading…
Reference in New Issue
Block a user