linux-brain/security/keys/secure_key.c
Udit Agarwal e2173ee987 security/keys/secure_key: Adds the secure key support based on CAAM.
Secure keys are derieved using CAAM crypto block.

Secure keys derieved are the random number symmetric keys from CAAM.
Blobs corresponding to the key are formed using CAAM. User space
will only be able to view the blob of the key.

Signed-off-by: Udit Agarwal <udit.agarwal@nxp.com>
Reviewed-by: Sahil Malhotra <sahil.malhotra@nxp.com>
2019-11-25 15:43:20 +08:00

340 lines
6.9 KiB
C

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