diff --git a/Documentation/security/keys/secure-key.rst b/Documentation/security/keys/secure-key.rst new file mode 100644 index 000000000000..a33ffd09d7bd --- /dev/null +++ b/Documentation/security/keys/secure-key.rst @@ -0,0 +1,67 @@ +========== +Secure Key +========== + +Secure key is the new type added to kernel key ring service. +Secure key is a symmetric type key of minimum length 32 bytes +and with maximum possible length to be 128 bytes. It is produced +in kernel using the CAAM crypto engine. Userspace can only see +the blob for the corresponding key. All the blobs are displayed +or loaded in hex ascii. + +Secure key can be created on platforms which supports CAAM +hardware block. Secure key can also be used as a master key to +create the encrypted keys along with the existing key types in +kernel. + +Secure key uses CAAM hardware to generate the key and blobify its +content for userspace. Generated blobs are tied up with the hardware +secret key stored in CAAM, hence the same blob will not be able to +de-blobify with the different secret key on another machine. + +Usage:: + + keyctl add secure "new " + keyctl load secure "load " + keyctl print + +"keyctl add secure" option will create the random data of the +specified key len using CAAM and store it as a key in kernel. +Key contents will be displayed as blobs to the user in hex ascii. +User can input key len from 32 bytes to 128 bytes. + +"keyctl load secure" option will load the blob contents. In kernel, +key will be deirved using input blob and CAAM, along with the secret +key stored in CAAM. + +"keyctl print" will return the hex string of the blob corresponding to +key_id. Returned blob will be of key_len + 48 bytes. Extra 48 bytes are +the header bytes added by the CAAM. + +Example of secure key usage:: + +1. Create the secure key with name kmk-master of length 32 bytes:: + + $ keyctl add secure kmk-master "new 32" @u + 46001928 + + $keyctl show + Session Keyring + 1030783626 --alswrv 0 65534 keyring: _uid_ses.0 + 695927745 --alswrv 0 65534 \_ keyring: _uid.0 + 46001928 --als-rv 0 0 \_ secure: kmk-master + +2. Print the blob contents for the kmk-master key:: + + $ keyctl print 46001928 + d9743445b640f3d59c1670dddc0bc9c2 + 34fc9aab7dd05c965e6120025012f029b + 07faa4776c4f6ed02899e35a135531e9a + 6e5c2b51132f9d5aef28f68738e658296 + 3fe583177cfe50d2542b659a13039 + + $ keyctl pipe 46001928 > secure_key.blob + +3. Load the blob in the user key ring:: + + $ keyctl load secure kmk-master "load 'cat secure_key.blob'" @u diff --git a/MAINTAINERS b/MAINTAINERS index 9d3a5c54a41d..0e91c5c8ce56 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9061,6 +9061,17 @@ F: include/keys/trusted-type.h F: security/keys/trusted.c F: include/keys/trusted.h +KEYS-SECURE +M: Udit Agarwal +R: Sahil Malhotra +L: linux-security-module@vger.kernel.org +L: keyrings@vger.kernel.org +S: Supported +F: include/keys/secure-type.h +F: security/keys/secure_key.c +F: security/keys/securekey_desc.c +F: security/keys/securekey_desc.h + KEYS/KEYRINGS: M: David Howells M: Jarkko Sakkinen diff --git a/include/keys/secure-type.h b/include/keys/secure-type.h new file mode 100644 index 000000000000..5b7a5f144e41 --- /dev/null +++ b/include/keys/secure-type.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 NXP. + * + */ + +#ifndef _KEYS_SECURE_TYPE_H +#define _KEYS_SECURE_TYPE_H + +#include +#include + +/* Minimum key size to be used is 32 bytes and maximum key size fixed + * is 128 bytes. + * Blob size to be kept is Maximum key size + blob header added by CAAM. + */ + +#define MIN_KEY_SIZE 32 +#define MAX_KEY_SIZE 128 +#define BLOB_HEADER_SIZE 48 + +#define MAX_BLOB_SIZE (MAX_KEY_SIZE + BLOB_HEADER_SIZE) + +struct secure_key_payload { + struct rcu_head rcu; + unsigned int key_len; + unsigned int blob_len; + unsigned char key[MAX_KEY_SIZE + 1]; + unsigned char blob[MAX_BLOB_SIZE]; +}; + +extern struct key_type key_type_secure; +#endif diff --git a/security/keys/Kconfig b/security/keys/Kconfig index dd313438fecf..a5fc3b9a00b8 100644 --- a/security/keys/Kconfig +++ b/security/keys/Kconfig @@ -90,6 +90,17 @@ config TRUSTED_KEYS If you are unsure as to whether this is required, answer N. +config SECURE_KEYS + tristate "SECURE_KEYS" + depends on KEYS && CRYPTO_DEV_FSL_CAAM && CRYPTO_DEV_FSL_CAAM_JR + help + This option provide support for creating secure-type key and blobs + in kernel. Secure keys are random number symmetric keys generated + from CAAM. The CAAM creates the blobs for the random key. + Userspace will only be able to see the blob. + + If you are unsure as to whether this is required, answer N. + config ENCRYPTED_KEYS tristate "ENCRYPTED KEYS" depends on KEYS diff --git a/security/keys/Makefile b/security/keys/Makefile index 9cef54064f60..65ac07a1ec5b 100644 --- a/security/keys/Makefile +++ b/security/keys/Makefile @@ -29,4 +29,9 @@ obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += keyctl_pkey.o # obj-$(CONFIG_BIG_KEYS) += big_key.o obj-$(CONFIG_TRUSTED_KEYS) += trusted.o +CFLAGS_secure_key.o += -I$(obj)/../../drivers/crypto/caam/ +CFLAGS_securekey_desc.o += -I$(obj)/../../drivers/crypto/caam/ +obj-$(CONFIG_SECURE_KEYS) += securekey.o +securekey-y := securekey_desc.o \ + secure_key.o obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted-keys/ diff --git a/security/keys/secure_key.c b/security/keys/secure_key.c new file mode 100644 index 000000000000..ec8ad4394549 --- /dev/null +++ b/security/keys/secure_key.c @@ -0,0 +1,339 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2018 NXP + * Secure key is generated using NXP CAAM hardware block. CAAM generates the + * random number (used as a key) and creates its blob for the user. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "securekey_desc.h" + +static const char hmac_alg[] = "hmac(sha1)"; +static const char hash_alg[] = "sha1"; + +static struct crypto_shash *hashalg; +static struct crypto_shash *hmacalg; + +enum { + error = -1, + new_key, + load_blob, +}; + +static const match_table_t key_tokens = { + {new_key, "new"}, + {load_blob, "load"}, + {error, NULL} +}; + +static struct secure_key_payload *secure_payload_alloc(struct key *key) +{ + struct secure_key_payload *sec_key = NULL; + int ret = 0; + + ret = key_payload_reserve(key, sizeof(*sec_key)); + if (ret < 0) + goto out; + + sec_key = kzalloc(sizeof(*sec_key), GFP_KERNEL); + if (!sec_key) + goto out; + +out: + return sec_key; +} + +/* + * parse_inputdata - parse the keyctl input data and fill in the + * payload structure for key or its blob. + * param[in]: data pointer to the data to be parsed for creating key. + * param[in]: p pointer to secure key payload structure to fill parsed data + * On success returns 0, otherwise -EINVAL. + */ +static int parse_inputdata(char *data, struct secure_key_payload *p) +{ + substring_t args[MAX_OPT_ARGS]; + long keylen = 0; + int ret = -EINVAL; + int key_cmd = -EINVAL; + char *c = NULL; + + c = strsep(&data, " \t"); + if (!c) { + ret = -EINVAL; + goto out; + } + + /* Get the keyctl command i.e. new_key or load_blob etc */ + key_cmd = match_token(c, key_tokens, args); + + switch (key_cmd) { + case new_key: + /* first argument is key size */ + c = strsep(&data, " \t"); + if (!c) { + ret = -EINVAL; + goto out; + } + + ret = kstrtol(c, 10, &keylen); + if (ret < 0 || keylen < MIN_KEY_SIZE || + keylen > MAX_KEY_SIZE) { + ret = -EINVAL; + goto out; + } + + p->key_len = keylen; + ret = new_key; + + break; + case load_blob: + /* first argument is blob data for CAAM*/ + c = strsep(&data, " \t"); + if (!c) { + ret = -EINVAL; + goto out; + } + + /* Blob_len = No of characters in blob/2 */ + p->blob_len = strlen(c) / 2; + if (p->blob_len > MAX_BLOB_SIZE) { + ret = -EINVAL; + goto out; + } + + ret = hex2bin(p->blob, c, p->blob_len); + if (ret < 0) { + ret = -EINVAL; + goto out; + } + ret = load_blob; + + break; + case error: + ret = -EINVAL; + break; + } + +out: + return ret; +} + +/* + * secure_instantiate - create a new secure type key. + * Supports the operation to generate a new key. A random number + * is generated from CAAM as key data and the corresponding red blob + * is formed and stored as key_blob. + * Also supports the operation to load the blob and key is derived using + * that blob from CAAM. + * On success, return 0. Otherwise return errno. + */ +static int secure_instantiate(struct key *key, + struct key_preparsed_payload *prep) +{ + struct secure_key_payload *payload = NULL; + size_t datalen = prep->datalen; + char *data = NULL; + int key_cmd = 0; + int ret = 0; + enum sk_req_type sk_op_type; + struct device *dev = NULL; + + if (datalen <= 0 || datalen > 32767 || !prep->data) { + ret = -EINVAL; + goto out; + } + + data = kmalloc(datalen + 1, GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto out; + } + + memcpy(data, prep->data, datalen); + data[datalen] = '\0'; + + payload = secure_payload_alloc(key); + if (!payload) { + ret = -ENOMEM; + goto out; + } + + /* Allocate caam job ring for operation to be performed from CAAM */ + dev = caam_jr_alloc(); + if (!dev) { + pr_info("caam_jr_alloc failed\n"); + ret = -ENODEV; + goto out; + } + + key_cmd = parse_inputdata(data, payload); + if (key_cmd < 0) { + ret = key_cmd; + goto out; + } + + switch (key_cmd) { + case load_blob: + /* + * Red blob decryption to be done for load operation + * to derive the key. + */ + sk_op_type = sk_red_blob_dec; + ret = key_deblob(payload, sk_op_type, dev); + if (ret != 0) { + pr_info("secure_key: key_blob decap fail (%d)\n", ret); + goto out; + } + break; + case new_key: + /* Get Random number from caam of the specified length */ + sk_op_type = sk_get_random; + ret = caam_get_random(payload, sk_op_type, dev); + if (ret != 0) { + pr_info("secure_key: get_random fail (%d)\n", ret); + goto out; + } + + /* Generate red blob of key random bytes with CAAM */ + sk_op_type = sk_red_blob_enc; + ret = key_blob(payload, sk_op_type, dev); + if (ret != 0) { + pr_info("secure_key: key_blob encap fail (%d)\n", ret); + goto out; + } + break; + default: + ret = -EINVAL; + goto out; + } +out: + if (data) + kzfree(data); + if (dev) + caam_jr_free(dev); + + if (!ret) + rcu_assign_keypointer(key, payload); + else + kzfree(payload); + + return ret; +} + +/* + * secure_read - copy the blob data to userspace in hex. + * param[in]: key pointer to key struct + * param[in]: buffer pointer to user data for creating key + * param[in]: buflen is the length of the buffer + * On success, return to userspace the secure key data size. + */ +static long secure_read(const struct key *key, char __user *buffer, + size_t buflen) +{ + const struct secure_key_payload *p = NULL; + char *ascii_buf; + char *bufp; + int i; + + p = dereference_key_locked(key); + if (!p) + return -EINVAL; + + if (buffer && buflen >= 2 * p->blob_len) { + ascii_buf = kmalloc(2 * p->blob_len, GFP_KERNEL); + if (!ascii_buf) + return -ENOMEM; + + bufp = ascii_buf; + for (i = 0; i < p->blob_len; i++) + bufp = hex_byte_pack(bufp, p->blob[i]); + if (copy_to_user(buffer, ascii_buf, 2 * p->blob_len) != 0) { + kzfree(ascii_buf); + return -EFAULT; + } + kzfree(ascii_buf); + } + return 2 * p->blob_len; +} + +/* + * secure_destroy - clear and free the key's payload + */ +static void secure_destroy(struct key *key) +{ + kzfree(key->payload.data[0]); +} + +struct key_type key_type_secure = { + .name = "secure", + .instantiate = secure_instantiate, + .destroy = secure_destroy, + .read = secure_read, +}; +EXPORT_SYMBOL_GPL(key_type_secure); + +static void secure_shash_release(void) +{ + if (hashalg) + crypto_free_shash(hashalg); + if (hmacalg) + crypto_free_shash(hmacalg); +} + +static int __init secure_shash_alloc(void) +{ + int ret; + + hmacalg = crypto_alloc_shash(hmac_alg, 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(hmacalg)) { + pr_info("secure_key: could not allocate crypto %s\n", + hmac_alg); + return PTR_ERR(hmacalg); + } + + hashalg = crypto_alloc_shash(hash_alg, 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(hashalg)) { + pr_info("secure_key: could not allocate crypto %s\n", + hash_alg); + ret = PTR_ERR(hashalg); + goto hashalg_fail; + } + + return 0; + +hashalg_fail: + crypto_free_shash(hmacalg); + return ret; +} + +static int __init init_secure_key(void) +{ + int ret; + + ret = secure_shash_alloc(); + if (ret < 0) + return ret; + + ret = register_key_type(&key_type_secure); + if (ret < 0) + secure_shash_release(); + return ret; +} + +static void __exit cleanup_secure_key(void) +{ + secure_shash_release(); + unregister_key_type(&key_type_secure); +} + +late_initcall(init_secure_key); +module_exit(cleanup_secure_key); + +MODULE_LICENSE("GPL"); diff --git a/security/keys/securekey_desc.c b/security/keys/securekey_desc.c new file mode 100644 index 000000000000..03a57cb93a74 --- /dev/null +++ b/security/keys/securekey_desc.c @@ -0,0 +1,608 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 NXP + * + */ + +#include +#include "securekey_desc.h" + +/* key modifier for blob encapsulation & decapsulation descriptor */ +u8 key_modifier[] = "SECURE_KEY"; +u32 key_modifier_len = 10; + +void caam_sk_rng_desc(struct sk_req *skreq, struct sk_desc *skdesc) +{ + struct sk_fetch_rnd_data *fetch_rnd_data = NULL; + struct random_desc *rnd_desc = NULL; + size_t len = 0; + u32 *desc = skreq->hwdesc; + + init_job_desc(desc, 0); + + fetch_rnd_data = &skreq->req_u.sk_fetch_rnd_data; + rnd_desc = &skdesc->dma_u.random_descp; + len = fetch_rnd_data->key_len; + + /* command 0x82500000 */ + append_cmd(desc, CMD_OPERATION | OP_TYPE_CLASS1_ALG | + OP_ALG_ALGSEL_RNG); + /* command 0x60340000 | len */ + append_cmd(desc, CMD_FIFO_STORE | FIFOST_TYPE_RNGSTORE | len); + append_ptr(desc, rnd_desc->rnd_data); +} + +void caam_sk_redblob_encap_desc(struct sk_req *skreq, struct sk_desc *skdesc) +{ + struct redblob_encap_desc *red_blob_desc = + &skdesc->dma_u.redblob_encapdesc; + struct sk_red_blob_encap *red_blob_req = + &skreq->req_u.sk_red_blob_encap; + u32 *desc = skreq->hwdesc; + + init_job_desc(desc, 0); + + /* Load class 2 key with key modifier. */ + append_key_as_imm(desc, key_modifier, key_modifier_len, + key_modifier_len, CLASS_2 | KEY_DEST_CLASS_REG); + + /* SEQ IN PTR Command. */ + append_seq_in_ptr(desc, red_blob_desc->in_data, red_blob_req->data_sz, + 0); + + /* SEQ OUT PTR Command. */ + append_seq_out_ptr(desc, red_blob_desc->redblob, + red_blob_req->redblob_sz, 0); + + /* RedBlob encapsulation PROTOCOL Command. */ + append_operation(desc, OP_TYPE_ENCAP_PROTOCOL | OP_PCLID_BLOB); +} + +/* void caam_sk_redblob_decap_desc(struct sk_req *skreq, struct sk_desc *skdesc) + * brief CAAM Descriptor creator from redblob to plaindata. + * param[in] skreq Pointer to secure key request structure + * param[in] skdesc Pointer to secure key descriptor structure + */ +void caam_sk_redblob_decap_desc(struct sk_req *skreq, struct sk_desc *skdesc) +{ + struct redblob_decap_desc *red_blob_desc = + &skdesc->dma_u.redblob_decapdesc; + struct sk_red_blob_decap *red_blob_req = + &skreq->req_u.sk_red_blob_decap; + u32 *desc = skreq->hwdesc; + + init_job_desc(desc, 0); + + /* Load class 2 key with key modifier. */ + append_key_as_imm(desc, key_modifier, key_modifier_len, + key_modifier_len, CLASS_2 | KEY_DEST_CLASS_REG); + + /* SEQ IN PTR Command. */ + append_seq_in_ptr(desc, red_blob_desc->redblob, + red_blob_req->redblob_sz, 0); + + /* SEQ OUT PTR Command. */ + append_seq_out_ptr(desc, red_blob_desc->out_data, + red_blob_req->data_sz, 0); + + /* RedBlob decapsulation PROTOCOL Command. */ + append_operation(desc, OP_TYPE_DECAP_PROTOCOL | OP_PCLID_BLOB); +} + +/* int caam_sk_get_random_map(struct device *dev, struct sk_req *req, + * struct sk_desc *skdesc) + * brief DMA map the buffer virtual pointers to physical address. + * param[in] dev Pointer to job ring device structure + * param[in] req Pointer to secure key request structure + * param[in] skdesc Pointer to secure key descriptor structure + * return 0 on success, error value otherwise. + */ +int caam_sk_get_random_map(struct device *dev, struct sk_req *req, + struct sk_desc *skdesc) +{ + struct sk_fetch_rnd_data *fetch_rnd_data; + struct random_desc *rnd_desc; + + fetch_rnd_data = &req->req_u.sk_fetch_rnd_data; + rnd_desc = &skdesc->dma_u.random_descp; + + rnd_desc->rnd_data = dma_map_single(dev, fetch_rnd_data->data, + fetch_rnd_data->key_len, DMA_FROM_DEVICE); + + if (dma_mapping_error(dev, rnd_desc->rnd_data)) { + dev_err(dev, "Unable to map memory\n"); + goto sk_random_map_fail; + } + return 0; + +sk_random_map_fail: + return -ENOMEM; +} + +/* int caam_sk_redblob_encap_map(struct device *dev, struct sk_req *req, + * struct sk_desc *skdesc) + * brief DMA map the buffer virtual pointers to physical address. + * param[in] dev Pointer to job ring device structure + * param[in] req Pointer to secure key request structure + * param[in] skdesc Pointer to secure key descriptor structure + * return 0 on success, error value otherwise. + */ +int caam_sk_redblob_encap_map(struct device *dev, struct sk_req *req, + struct sk_desc *skdesc) +{ + struct sk_red_blob_encap *red_blob_encap; + struct redblob_encap_desc *red_blob_desc; + + red_blob_encap = &req->req_u.sk_red_blob_encap; + red_blob_desc = &skdesc->dma_u.redblob_encapdesc; + + red_blob_desc->in_data = dma_map_single(dev, red_blob_encap->data, + red_blob_encap->data_sz, DMA_TO_DEVICE); + if (dma_mapping_error(dev, red_blob_desc->in_data)) { + dev_err(dev, "Unable to map memory\n"); + goto sk_data_fail; + } + + red_blob_desc->redblob = dma_map_single(dev, red_blob_encap->redblob, + red_blob_encap->redblob_sz, DMA_FROM_DEVICE); + if (dma_mapping_error(dev, red_blob_desc->redblob)) { + dev_err(dev, "Unable to map memory\n"); + goto sk_redblob_fail; + } + + return 0; + +sk_redblob_fail: + dma_unmap_single(dev, red_blob_desc->in_data, red_blob_encap->data_sz, + DMA_TO_DEVICE); +sk_data_fail: + return -ENOMEM; +} + +/* static int caam_sk_redblob_decap_map(struct device *dev, + * struct sk_req *req, + * struct sk_desc *skdesc) + * brief DMA map the buffer virtual pointers to physical address. + * param[in] dev Pointer to job ring device structure + * param[in] req Pointer to secure key request structure + * param[in] skdesc Pointer to secure key descriptor structure + * return 0 on success, error value otherwise. + */ +int caam_sk_redblob_decap_map(struct device *dev, struct sk_req *req, + struct sk_desc *skdesc) +{ + struct sk_red_blob_decap *red_blob_decap; + struct redblob_decap_desc *red_blob_desc; + + red_blob_decap = &req->req_u.sk_red_blob_decap; + red_blob_desc = &skdesc->dma_u.redblob_decapdesc; + + red_blob_desc->redblob = dma_map_single(dev, red_blob_decap->redblob, + red_blob_decap->redblob_sz, DMA_TO_DEVICE); + if (dma_mapping_error(dev, red_blob_desc->redblob)) { + dev_err(dev, "Unable to map memory\n"); + goto sk_redblob_fail; + } + + red_blob_desc->out_data = dma_map_single(dev, red_blob_decap->data, + red_blob_decap->data_sz, DMA_FROM_DEVICE); + if (dma_mapping_error(dev, red_blob_desc->out_data)) { + dev_err(dev, "Unable to map memory\n"); + goto sk_data_fail; + } + + return 0; + +sk_data_fail: + dma_unmap_single(dev, red_blob_desc->redblob, + red_blob_decap->redblob_sz, DMA_TO_DEVICE); +sk_redblob_fail: + return -ENOMEM; +} + +/* @fn void securekey_unmap(struct device *dev, + * struct sk_desc *skdesc, struct sk_req *req) + * @brief DMA unmap the buffer pointers. + * @param[in] dev Pointer to job ring device structure + * @param[in] skdesc Pointer to secure key descriptor structure + * @param[in] req Pointer to secure key request structure + */ +void securekey_unmap(struct device *dev, + struct sk_desc *skdesc, struct sk_req *req) +{ + + switch (req->type) { + case sk_get_random: + { + struct sk_fetch_rnd_data *fetch_rnd_data; + struct random_desc *rnd_desc; + + fetch_rnd_data = &req->req_u.sk_fetch_rnd_data; + rnd_desc = &skdesc->dma_u.random_descp; + + /* Unmap Descriptor buffer pointers. */ + dma_unmap_single(dev, rnd_desc->rnd_data, + fetch_rnd_data->key_len, + DMA_FROM_DEVICE); + break; + } + case sk_red_blob_enc: + { + struct sk_red_blob_encap *red_blob_encap; + struct redblob_encap_desc *red_blob_desc; + + red_blob_encap = &req->req_u.sk_red_blob_encap; + red_blob_desc = &skdesc->dma_u.redblob_encapdesc; + + /* Unmap Descriptor buffer pointers. */ + dma_unmap_single(dev, red_blob_desc->in_data, + red_blob_encap->data_sz, + DMA_TO_DEVICE); + + dma_unmap_single(dev, red_blob_desc->redblob, + red_blob_encap->redblob_sz, + DMA_FROM_DEVICE); + + break; + } + case sk_red_blob_dec: + { + struct sk_red_blob_decap *red_blob_decap; + struct redblob_decap_desc *red_blob_desc; + + red_blob_decap = &req->req_u.sk_red_blob_decap; + red_blob_desc = &skdesc->dma_u.redblob_decapdesc; + + /* Unmap Descriptor buffer pointers. */ + dma_unmap_single(dev, red_blob_desc->redblob, + red_blob_decap->redblob_sz, + DMA_TO_DEVICE); + + dma_unmap_single(dev, red_blob_desc->out_data, + red_blob_decap->data_sz, + DMA_FROM_DEVICE); + + break; + } + default: + dev_err(dev, "Unable to find request type\n"); + break; + } + kfree(skdesc); +} + +/* int caam_securekey_desc_init(struct device *dev, struct sk_req *req) + * brief CAAM Descriptor creator for secure key operations. + * param[in] dev Pointer to job ring device structure + * param[in] req Pointer to secure key request structure + * return 0 on success, error value otherwise. + */ +int caam_securekey_desc_init(struct device *dev, struct sk_req *req) +{ + struct sk_desc *skdesc = NULL; + int ret = 0; + + switch (req->type) { + case sk_get_random: + { + skdesc = kmalloc(sizeof(*skdesc), GFP_DMA); + if (!skdesc) { + ret = -ENOMEM; + goto out; + } + skdesc->req_type = req->type; + + if (caam_sk_get_random_map(dev, req, skdesc)) { + dev_err(dev, "caam get_random map fail\n"); + ret = -ENOMEM; + goto out; + } + caam_sk_rng_desc(req, skdesc); + break; + } + case sk_red_blob_enc: + { + skdesc = kmalloc(sizeof(*skdesc), GFP_DMA); + if (!skdesc) { + ret = -ENOMEM; + goto out; + } + + skdesc->req_type = req->type; + + if (caam_sk_redblob_encap_map(dev, req, skdesc)) { + dev_err(dev, "caam redblob_encap map fail\n"); + ret = -ENOMEM; + goto out; + } + + /* Descriptor function to create redblob from data. */ + caam_sk_redblob_encap_desc(req, skdesc); + break; + } + + case sk_red_blob_dec: + { + skdesc = kmalloc(sizeof(*skdesc), GFP_DMA); + if (!skdesc) { + ret = -ENOMEM; + goto out; + } + + skdesc->req_type = req->type; + + if (caam_sk_redblob_decap_map(dev, req, skdesc)) { + dev_err(dev, "caam redblob_decap map fail\n"); + ret = -ENOMEM; + goto out; + } + + /* Descriptor function to decap data from redblob. */ + caam_sk_redblob_decap_desc(req, skdesc); + break; + } + default: + pr_debug("Unknown request type\n"); + ret = -EINVAL; + goto out; + } + + req->desc_pointer = (void *)skdesc; + +out: + return ret; +} + +/* static void caam_op_done (struct device *dev, u32 *desc, u32 ret, + * void *context) + * brief callback function to be called when descriptor executed. + * param[in] dev Pointer to device structure + * param[in] desc descriptor pointer + * param[in] ret return status of Job submitted + * param[in] context void pointer + */ +static void caam_op_done(struct device *dev, u32 *desc, u32 ret, + void *context) +{ + struct sk_req *req = context; + + if (ret) { + dev_err(dev, "caam op done err: %x\n", ret); + /* print the error source name. */ + caam_jr_strstatus(dev, ret); + } + /* Call securekey_unmap function for unmapping the buffer pointers. */ + securekey_unmap(dev, req->desc_pointer, req); + + req->ret = ret; + complete(&req->comp); +} + + +/* static int sk_job_submit(struct device *jrdev, struct sk_req *req) + * brief Enqueue a Job descriptor to Job ring and wait until SEC returns. + * param[in] jrdev Pointer to job ring device structure + * param[in] req Pointer to secure key request structure + * return 0 on success, error value otherwise. + */ +static int sk_job_submit(struct device *jrdev, struct sk_req *req) +{ + int ret; + + init_completion(&req->comp); + + /* caam_jr_enqueue function for Enqueue a job descriptor */ + ret = caam_jr_enqueue(jrdev, req->hwdesc, caam_op_done, req); + if (!ret) + wait_for_completion_interruptible(&req->comp); + + ret = req->ret; + return ret; +} + +/* caam_get_random(struct secure_key_payload *p, enum sk_req_type fetch_rnd, + * struct device *dev) + * Create the random number of the specified length using CAAM block + * param[in]: out pointer to place the random bytes + * param[in]: length for the random data bytes. + * param[in]: dev Pointer to job ring device structure + * If operation is successful return 0, otherwise error. + */ +int caam_get_random(struct secure_key_payload *p, enum sk_req_type fetch_rnd, + struct device *dev) +{ + struct sk_fetch_rnd_data *fetch_rnd_data = NULL; + struct sk_req *req = NULL; + int ret = 0; + void *temp = NULL; + + req = kmalloc(sizeof(struct sk_req), GFP_DMA); + if (!req) { + ret = -ENOMEM; + goto out; + } + + req->type = fetch_rnd; + fetch_rnd_data = &(req->req_u.sk_fetch_rnd_data); + + /* initialise with key length */ + fetch_rnd_data->key_len = p->key_len; + + temp = kmalloc(fetch_rnd_data->key_len, GFP_DMA); + if (!temp) { + ret = -ENOMEM; + goto out; + } + fetch_rnd_data->data = temp; + + ret = caam_securekey_desc_init(dev, req); + + if (ret) { + pr_info("caam_securekey_desc_init failed\n"); + goto out; + } + + ret = sk_job_submit(dev, req); + if (!ret) { + /*Copy output to key buffer. */ + memcpy(p->key, fetch_rnd_data->data, p->key_len); + } else { + ret = -EINVAL; + } + +out: + if (req) + kfree(req); + + if (temp) + kfree(temp); + + return ret; +} +EXPORT_SYMBOL(caam_get_random); + +/* key_deblob(struct secure_key_payload *p, enum sk_req_type decap_type + * struct device *dev) + * Deblobify the blob to get the key data and fill in secure key payload struct + * param[in] p pointer to the secure key payload + * param[in] decap_type operation to be done. + * param[in] dev dev Pointer to job ring device structure + * If operation is successful return 0, otherwise error. + */ +int key_deblob(struct secure_key_payload *p, enum sk_req_type decap_type, + struct device *dev) +{ + unsigned int blob_len; + struct sk_red_blob_decap *d_blob; + struct sk_req *req = NULL; + int total_sz = 0, *temp = NULL, ret = 0; + + req = kmalloc(sizeof(struct sk_req), GFP_DMA); + if (!req) { + ret = -ENOMEM; + goto out; + } + + d_blob = &(req->req_u.sk_red_blob_decap); + blob_len = p->blob_len; + req->type = decap_type; + + /* + * Red blob size is the blob_len filled in payload struct + * Data_sz i.e. key is the blob_len - blob header size + */ + + d_blob->redblob_sz = blob_len; + d_blob->data_sz = blob_len - (SK_BLOB_KEY_SZ + SK_BLOB_MAC_SZ); + total_sz = d_blob->data_sz + d_blob->redblob_sz; + + temp = kmalloc(total_sz, GFP_DMA); + if (!temp) { + ret = -ENOMEM; + goto out; + } + + req->mem_pointer = temp; + d_blob->redblob = temp; + d_blob->data = d_blob->redblob + d_blob->redblob_sz; + memcpy(d_blob->redblob, p->blob, blob_len); + + ret = caam_securekey_desc_init(dev, req); + + if (ret) { + pr_info("caam_securekey_desc_init: Failed\n"); + goto out; + } + + ret = sk_job_submit(dev, req); + if (!ret) { + /*Copy output to key buffer. */ + p->key_len = d_blob->data_sz; + memcpy(p->key, d_blob->data, p->key_len); + } else { + ret = -EINVAL; + } + +out: + if (temp) + kfree(temp); + if (req) + kfree(req); + return ret; +} +EXPORT_SYMBOL(key_deblob); + +/* key_blob(struct secure_key_payload *p, enum sk_req_type encap_type, + * struct device *dev) + * To blobify the key data to get the blob. This blob can only be seen by + * userspace. + * param[in] p pointer to the secure key payload + * param[in] decap_type operation to be done. + * param[in] dev dev Pointer to job ring device structure + * If operation is successful return 0, otherwise error. + */ +int key_blob(struct secure_key_payload *p, enum sk_req_type encap_type, + struct device *dev) +{ + unsigned int key_len; + struct sk_red_blob_encap *k_blob; + struct sk_req *req = NULL; + int total_sz = 0, *temp = NULL, ret = 0; + + req = kmalloc(sizeof(struct sk_req), GFP_DMA); + if (!req) { + ret = -ENOMEM; + goto out; + } + + key_len = p->key_len; + + req->type = encap_type; + k_blob = &(req->req_u.sk_red_blob_encap); + + /* + * Data_sz i.e. key len and the corresponding blob_len is + * key_len + BLOB header size. + */ + + k_blob->data_sz = key_len; + k_blob->redblob_sz = key_len + SK_BLOB_KEY_SZ + SK_BLOB_MAC_SZ; + total_sz = k_blob->data_sz + k_blob->redblob_sz; + + temp = kmalloc(total_sz, GFP_DMA); + if (!temp) { + ret = -ENOMEM; + goto out; + } + + req->mem_pointer = temp; + k_blob->data = temp; + + k_blob->redblob = k_blob->data + k_blob->data_sz; + memcpy(k_blob->data, p->key, key_len); + + ret = caam_securekey_desc_init(dev, req); + + if (ret) { + pr_info("caam_securekey_desc_init failed\n"); + goto out; + } + + ret = sk_job_submit(dev, req); + if (!ret) { + /*Copy output to key buffer. */ + p->blob_len = k_blob->redblob_sz; + memcpy(p->blob, k_blob->redblob, p->blob_len); + } else { + ret = -EINVAL; + } + +out: + if (temp) + kfree(req->mem_pointer); + if (req) + kfree(req); + return ret; + +} +EXPORT_SYMBOL(key_blob); diff --git a/security/keys/securekey_desc.h b/security/keys/securekey_desc.h new file mode 100644 index 000000000000..0ee26e95b205 --- /dev/null +++ b/security/keys/securekey_desc.h @@ -0,0 +1,141 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2018 NXP + * + */ +#ifndef _SECUREKEY_DESC_H_ +#define _SECUREKEY_DESC_H_ + +#include "compat.h" +#include "regs.h" +#include "intern.h" +#include "desc.h" +#include "desc_constr.h" +#include "jr.h" +#include "error.h" +#include "pdb.h" + +#define SK_BLOB_KEY_SZ 32 /* Blob key size. */ +#define SK_BLOB_MAC_SZ 16 /* Blob MAC size. */ + +/* + * brief defines different kinds of operations supported by this module. + */ +enum sk_req_type { + sk_get_random, + sk_red_blob_enc, + sk_red_blob_dec, +}; + + +/* + * struct random_des + * param[out] rnd_data output buffer for random data. + */ +struct random_desc { + dma_addr_t rnd_data; +}; + +/* struct redblob_encap_desc + * details Structure containing dma address for redblob encapsulation. + * param[in] in_data input data to redblob encap descriptor. + * param[out] redblob output buffer for redblob. + */ +struct redblob_encap_desc { + dma_addr_t in_data; + dma_addr_t redblob; +}; + +/* struct redblob_decap_desc + * details Structure containing dma address for redblob decapsulation. + * param[in] redblob input buffer to redblob decap descriptor. + * param[out] out_data output data from redblob decap descriptor. + */ +struct redblob_decap_desc { + dma_addr_t redblob; + dma_addr_t out_data; +}; + +/* struct sk_desc + * details Structure for securekey descriptor creation. + * param[in] req_type operation supported. + * param[in] dma_u union of struct for supported operation. + */ +struct sk_desc { + u32 req_type; + union { + struct redblob_encap_desc redblob_encapdesc; + struct redblob_decap_desc redblob_decapdesc; + struct random_desc random_descp; + } dma_u; +}; + +/* struct sk_fetch_rnd_data + * decriptor structure containing key length. + */ +struct sk_fetch_rnd_data { + void *data; + size_t key_len; +}; + +/* struct sk_red_blob_encap + * details Structure containing buffer pointers for redblob encapsulation. + * param[in] data Input data. + * param[in] data_sz size of Input data. + * param[out] redblob output buffer for redblob. + * param[in] redblob_sz size of redblob. + */ +struct sk_red_blob_encap { + void *data; + uint32_t data_sz; + void *redblob; + uint32_t redblob_sz; +}; + +/* struct sk_red_blob_decap + * details Structure containing buffer pointers for redblob decapsulation. + * param[in] redblob Input redblob. + * param[in] redblob_sz size of redblob. + * param[out] data output buffer for data. + * param[in] data_sz size of output data. + */ +struct sk_red_blob_decap { + void *redblob; + uint32_t redblob_sz; + void *data; + uint32_t data_sz; +}; + +/* struct sk_req + * details Structure for securekey request creation. + * param[in] type operation supported. + * param[in] req_u union of struct for supported operation. + * param[out] ret return status of CAAM operation. + * param[in] mem_pointer memory pointer for allocated kernel memory. + * param[in] desc_pointer Pointer to securekey descriptor creation structure. + * param[in] comp struct completion object. + * param[in] hwdesc contains descriptor instructions. + */ +struct sk_req { + enum sk_req_type type; + void *arg; + union { + struct sk_red_blob_encap sk_red_blob_encap; + struct sk_red_blob_decap sk_red_blob_decap; + struct sk_fetch_rnd_data sk_fetch_rnd_data; + } req_u; + int ret; + void *mem_pointer; + void *desc_pointer; + struct completion comp; + u32 hwdesc[MAX_CAAM_DESCSIZE]; +}; + +int caam_get_random(struct secure_key_payload *p, enum sk_req_type fetch_rnd, + struct device *dev); +int key_blob(struct secure_key_payload *p, enum sk_req_type encap_type, + struct device *dev); +int key_deblob(struct secure_key_payload *p, enum sk_req_type decap_type, + struct device *dev); + +#endif /*_SECUREKEY_DESC_H_*/