1
0
Fork 0
alistair23-linux/security/keys/secure_key.c

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