340 lines
6.9 KiB
C
Executable File
340 lines
6.9 KiB
C
Executable File
// SPDX-License-Identifier: GPL-2.0
|
|
/* Copyright 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");
|