diff options
Diffstat (limited to 'security/keys/secure_key.c')
-rw-r--r-- | security/keys/secure_key.c | 339 |
1 files changed, 339 insertions, 0 deletions
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 <linux/slab.h> +#include <linux/parser.h> +#include <linux/string.h> +#include <linux/key-type.h> +#include <linux/rcupdate.h> +#include <keys/secure-type.h> +#include <linux/completion.h> + +#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"); |