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