diff --git a/Documentation/ioctl/ioctl-number.rst b/Documentation/ioctl/ioctl-number.rst index c1f0d574fec6..30b228007f62 100644 --- a/Documentation/ioctl/ioctl-number.rst +++ b/Documentation/ioctl/ioctl-number.rst @@ -158,7 +158,8 @@ Code Seq# Include File Comments 'I' 00-0F drivers/isdn/divert/isdn_divert.h conflict! 'I' 40-4F linux/mISDNif.h conflict! 'J' 00-1F drivers/scsi/gdth_ioctl.h -'K' all linux/kd.h +'K' all linux/kd.h conflict! +'K' 00-01 linux/caam_keygen.h conflict! caam driver 'L' 00-1F linux/loop.h conflict! 'L' 10-1F drivers/scsi/mpt3sas/mpt3sas_ctl.h conflict! 'L' 20-2F linux/lightnvm.h diff --git a/drivers/crypto/caam/Makefile b/drivers/crypto/caam/Makefile index 3b0c574af802..8945f110727c 100644 --- a/drivers/crypto/caam/Makefile +++ b/drivers/crypto/caam/Makefile @@ -18,7 +18,7 @@ obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_JR_UIO) += fsl_jr_uio.o caam-y := ctrl.o caam_jr-y := jr.o key_gen.o -caam_jr-$(CONFIG_CRYPTO_DEV_FSL_CAAM_TK_API) += tag_object.o caamkeyblob.o +caam_jr-$(CONFIG_CRYPTO_DEV_FSL_CAAM_TK_API) += tag_object.o caamkeyblob.o caamkeygen.o caam_jr-$(CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API) += caamalg.o caam_jr-$(CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API_QI) += caamalg_qi.o caam_jr-$(CONFIG_CRYPTO_DEV_FSL_CAAM_AHASH_API) += caamhash.o diff --git a/drivers/crypto/caam/caamkeygen.c b/drivers/crypto/caam/caamkeygen.c new file mode 100644 index 000000000000..55cabbf0d1d1 --- /dev/null +++ b/drivers/crypto/caam/caamkeygen.c @@ -0,0 +1,556 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright 2020 NXP + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "caamkeyblob.h" +#include "intern.h" +#include + +#define DEVICE_NAME "caam-keygen" + +static long caam_keygen_ioctl(struct file *file, unsigned int cmd, + unsigned long arg); + +/** + * tag_black_key - Tag a black key with a tag object header. + * + * @info : keyblob_info structure, which contains + * the black key, obtained from CAAM, + * that needs to be tagged + * @black_key_max_len : The maximum size of a black key + * + * Return : '0' on success, error code otherwise + */ +static int tag_black_key(struct keyblob_info *info, size_t black_key_max_len) +{ + struct header_conf tag; + u32 type; + int ret; + u32 size_tagged = black_key_max_len; + + if (!info) + return -EINVAL; + + type = info->type; + + /* Prepare the tag */ + init_tag_object_header(&tag, 0, type, info->black_key_len); + + /* Set the tag */ + ret = set_tag_object_header_conf(&tag, info->black_key, + info->black_key_len, &size_tagged); + if (ret) + return ret; + + /* Update the size of the black key tagged */ + info->black_key_len = size_tagged; + + return ret; +} + +/** + * send_err_msg - Send the error message from kernel to user-space + * + * @msg : The message to be sent + * @output : The output buffer where we want to copy the error msg + * @size : The size of output buffer + */ +static void send_err_msg(char *msg, void __user *output, size_t size) +{ + size_t min_s; + char null_ch = 0; + + /* Not enough space to copy any message */ + if (size <= 1) + return; + + min_s = min(size - 1, strlen(msg)); + /* + * Avoid compile and checkpatch warnings, since we don't + * care about return value from copy_to_user + */ + (void)(copy_to_user(output, msg, min_s) + 1); + /* Copy null terminator */ + (void)(copy_to_user((output + min_s), &null_ch, 1) + 1); +} + +/** + * validate_key_size - Validate the key size from user. + * This can be the exact size given by user when + * generating a black key from random (with -s), + * or the size of the plaintext (with -t). + * + * @key_len : The size of key we want to validate + * @output : The output buffer where we want to copy the error msg + * @size : The size of output buffer + * + *Return : '0' on success, error code otherwise + */ +static int validate_key_size(size_t key_len, void __user *output, size_t size) +{ + char *msg = NULL; + + if (key_len < MIN_KEY_SIZE || key_len > MAX_KEY_SIZE) { + msg = "Invalid key size, expected values are between 16 and 64 bytes.\n"; + send_err_msg(msg, output, size); + return -EINVAL; + } + + return 0; +} + +/** + * validate_input - Validate the input from user and set the + * keyblob_info structure. + * This contains the input key in case of black key + * generated from plaintext or size for random + * black key. + * + * @key_crt : Structure with data from user + * @arg : User-space argument from ioctl call + * @info : keyblob_info structure, will be updated with all the + * data from user-space + * @create_key_op : Used to determine if it's a create or import operation + * + * Return : '0' on success, error code otherwise + */ +static int validate_input(struct caam_keygen_cmd *key_crt, unsigned long arg, + struct keyblob_info *info, bool create_key_op) +{ + char *tmp, *msg; + size_t tmp_size; + bool random = false; + int ret = 0; + u32 tmp_len = 0; + + /* + * So far, we only support Black keys, encrypted with JDKEK, + * kept in general memory, non-secure state. + * Therefore, default value for type is 1. + */ + u32 type = 1; + + if (copy_from_user(key_crt, (void __user *)arg, + sizeof(struct caam_keygen_cmd))) + return -EFAULT; + + /* Get blob_len from user. */ + info->blob_len = key_crt->blob_len; + /* Get black_key_len from user. */ + info->black_key_len = key_crt->black_key_len; + + /* + * Based on operation type validate a different set of input data. + * + * For key creation, validate the Encrypted Key Type, + * the Key Mode and Key Value + */ + if (create_key_op) { + /* + * Allocate memory for temporary buffer used to + * get the user arguments from user-space + */ + tmp_size = max_t(size_t, key_crt->key_enc_len, + max_t(size_t, key_crt->key_mode_len, + key_crt->key_value_len)); + tmp = kmalloc(tmp_size, GFP_KERNEL); + if (!tmp) { + msg = "Unable to allocate memory for temporary buffer.\n"; + send_err_msg(msg, u64_to_user_ptr(key_crt->blob), + key_crt->blob_len); + return -ENOMEM; + } + /* + * Validate and set, in type, the Encrypted Key Type + * given from user-space. + * This must be ecb or ccm. + */ + if (copy_from_user(tmp, u64_to_user_ptr(key_crt->key_enc), + key_crt->key_enc_len)) { + msg = "Unable to copy from user the Encrypted Key Type.\n"; + send_err_msg(msg, u64_to_user_ptr(key_crt->blob), + key_crt->blob_len); + ret = -EFAULT; + goto free_resource; + } + if (!strcmp(tmp, "ccm")) { + type |= BIT(TAG_OBJ_EKT_OFFSET); + } else if (strcmp(tmp, "ecb")) { + msg = "Invalid argument for Encrypted Key Type, expected ecb or ccm.\n"; + send_err_msg(msg, u64_to_user_ptr(key_crt->blob), + key_crt->blob_len); + ret = -EINVAL; + goto free_resource; + } + /* + * Validate the Key Mode given from user-space. + * This must be -t (text), for a black key generated + * from a plaintext, or -s (size) for a black key + * generated from random. + */ + if (copy_from_user(tmp, u64_to_user_ptr(key_crt->key_mode), + key_crt->key_mode_len)) { + msg = "Unable to copy from user the Key Mode: random (-s) or plaintext (-t).\n"; + send_err_msg(msg, u64_to_user_ptr(key_crt->blob), + key_crt->blob_len); + ret = -EFAULT; + goto free_resource; + } + if (!strcmp(tmp, "-s")) { + random = true; /* black key generated from random */ + } else if (strcmp(tmp, "-t")) { + msg = "Invalid argument for Key Mode, expected -s or -t.\n"; + send_err_msg(msg, u64_to_user_ptr(key_crt->blob), + key_crt->blob_len); + ret = -EINVAL; + goto free_resource; + } + /* + * Validate and set, into keyblob_info structure, + * the plaintext or key size, based on Key Mode. + */ + if (copy_from_user(tmp, u64_to_user_ptr(key_crt->key_value), + key_crt->key_value_len)) { + msg = "Unable to copy from user the Key Value: size or plaintext.\n"; + send_err_msg(msg, u64_to_user_ptr(key_crt->blob), + key_crt->blob_len); + ret = -EFAULT; + goto free_resource; + } + /* Black key generated from random, get its size */ + if (random) { + info->key = NULL; + ret = kstrtou32(tmp, 10, &tmp_len); + if (ret != 0) { + msg = "Invalid key size.\n"; + send_err_msg(msg, u64_to_user_ptr(key_crt->blob), + key_crt->blob_len); + goto free_resource; + } + ret = validate_key_size(tmp_len, + u64_to_user_ptr(key_crt->blob), + key_crt->blob_len); + if (ret) + goto free_resource; + + info->key_len = tmp_len; + } else { + /* + * Black key generated from plaintext, + * get the plaintext (input key) and its size + */ + ret = validate_key_size(strlen(tmp), + u64_to_user_ptr(key_crt->blob), + key_crt->blob_len); + if (ret) + goto free_resource; + + info->key = tmp; + info->key_len = strlen(tmp); + } + info->type = type; + } else { + /* For key import, get the blob from user-space */ + if (copy_from_user(info->blob, u64_to_user_ptr(key_crt->blob), + info->blob_len)) { + msg = "Unable to copy from user the blob.\n"; + send_err_msg(msg, u64_to_user_ptr(key_crt->black_key), + key_crt->black_key_len); + return -EFAULT; + } + } + + goto exit; + +free_resource: + kfree(tmp); + +exit: + return ret; +} + +/** + * keygen_create_keyblob - Generate key and blob + * + * @info : keyblob_info structure, will be updated with + * the black key and blob data from CAAM + * + * Return : '0' on success, error code otherwise + */ +static int keygen_create_keyblob(struct keyblob_info *info) +{ + int ret = 0; + struct device *jrdev; + + /* Allocate caam job ring for operation to be performed from CAAM */ + jrdev = caam_jr_alloc(); + if (IS_ERR(jrdev)) { + pr_err("Job Ring Device allocation failed\n"); + return PTR_ERR(jrdev); + } + + /* Create a black key */ + ret = generate_black_key(jrdev, info); + if (ret) { + dev_err(jrdev, "Black key generation failed: (%d)\n", ret); + goto free_jr; + } + + /* Clear the input key, if exists */ + if (info->key) + memset(info->key, 0, info->key_len); + + /* Set key modifier, used as revision number, for blob */ + info->key_mod = caam_key_modifier; + info->key_mod_len = ARRAY_SIZE(caam_key_modifier); + + /* + * Encapsulate the key, into a black blob, in general memory + * (the only memory type supported, right now) + */ + ret = caam_blob_encap(jrdev, info); + if (ret) { + dev_err(jrdev, "Blob encapsulation of black key failed: %d\n", + ret); + goto free_jr; + } + + /* Tag the black key so it can be passed to CAAM Crypto API */ + ret = tag_black_key(info, sizeof(info->black_key)); + if (ret) { + dev_err(jrdev, "Black key tagging failed: %d\n", ret); + goto free_jr; + } + + /* Add the object type as a header to the blob */ + memcpy(info->blob, &info->type, BLOB_HEADER_SIZE); + /* Update blob length with the new added header */ + info->blob_len += BLOB_HEADER_SIZE; + +free_jr: + caam_jr_free(jrdev); + + return ret; +} + +/** + * keygen_import_key - Import a black key from a blob + * + * @info : keyblob_info structure, will be updated with + * the black key obtained after blob decapsulation by CAAM + * + * Return : '0' on success, error code otherwise + */ +static int keygen_import_key(struct keyblob_info *info) +{ + int ret = 0; + struct device *jrdev; + + /* Allocate CAAM Job Ring for operation to be performed from CAAM */ + jrdev = caam_jr_alloc(); + if (IS_ERR(jrdev)) { + pr_err("Job Ring Device allocation failed\n"); + return PTR_ERR(jrdev); + } + + /* Set key modifier, used as revision number, for blob */ + info->key_mod = caam_key_modifier; + info->key_mod_len = ARRAY_SIZE(caam_key_modifier); + + print_hex_dump_debug("input blob @ " __stringify(__LINE__) " : ", + DUMP_PREFIX_ADDRESS, 16, 4, info->blob, + info->blob_len, 1); + + /* Get object type from blob header */ + memcpy(&info->type, info->blob, BLOB_HEADER_SIZE); + + /* Update blob length by removing the header size */ + info->blob_len -= BLOB_HEADER_SIZE; + + /* + * Decapsulate the blob into a black key, + * in general memory (the only memory type supported, right now) + */ + ret = caam_blob_decap(jrdev, info); + if (ret) { + dev_err(jrdev, "Blob decapsulation failed: %d\n", ret); + goto free_jr; + } + + /* Tag the black key so it can be passed to CAAM Crypto API */ + ret = tag_black_key(info, sizeof(info->black_key)); + if (ret) + dev_err(jrdev, "Black key tagging failed: %d\n", ret); + +free_jr: + caam_jr_free(jrdev); + + return ret; +} + +/** + * send_output - Send the output data (tagged key and blob) + * from kernel to user-space. + * + * @key_crt : Structure used to transfer data + * from user-space to kernel + * @info : keyblob_info structure, which contains all + * the data obtained from CAAM that needs to + * be transferred to user-space + * @create_key_op : Used to determine if it's a create or import operation + * @err : Error code received from previous operations + * + * Return : '0' on success, error code otherwise + */ +static int send_output(struct caam_keygen_cmd *key_crt, unsigned long arg, + struct keyblob_info *info, bool create_key_op, int err) +{ + int ret = 0; + char *msg; + + /* Free resource used on validate_input */ + kfree(info->key); + + if (err) + return err; + + /* Check if there's enough space to copy black key to user */ + if (key_crt->black_key_len < info->black_key_len) { + msg = "Not enough space for black key.\n"; + send_err_msg(msg, u64_to_user_ptr(key_crt->blob), + key_crt->blob_len); + /* Send, to user, the necessary size for key */ + key_crt->black_key_len = info->black_key_len; + + ret = -EINVAL; + goto exit; + } + key_crt->black_key_len = info->black_key_len; + + /* For key import, copy to user only the black key */ + if (copy_to_user(u64_to_user_ptr(key_crt->black_key), + info->black_key, info->black_key_len)) + return -EFAULT; + + /* For key creation, copy to user, also, the blob */ + if (create_key_op) { + /* Check if there's enough space to copy blob user */ + if (key_crt->blob_len < info->blob_len) { + msg = "Not enough space for blob key.\n"; + send_err_msg(msg, u64_to_user_ptr(key_crt->blob), + key_crt->blob_len); + /* Send, to user, the necessary size for blob */ + key_crt->blob_len = info->blob_len; + + ret = -EINVAL; + goto exit; + } + + key_crt->blob_len = info->blob_len; + + if (copy_to_user(u64_to_user_ptr(key_crt->blob), info->blob, + info->blob_len)) + return -EFAULT; + } + +exit: + if (copy_to_user((void __user *)arg, key_crt, + sizeof(struct caam_keygen_cmd))) + return -EFAULT; + + return ret; +} + +static long caam_keygen_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int ret = 0; + struct keyblob_info info = {.key = NULL}; + struct caam_keygen_cmd key_crt; + /* Used to determine if it's a create or import operation */ + bool create_key_op = false; + + switch (cmd) { + case CAAM_KEYGEN_IOCTL_CREATE: + { + create_key_op = true; + + /* Validate user-space input */ + ret = validate_input(&key_crt, arg, &info, create_key_op); + if (ret) + break; + + /* Create tagged key and blob */ + ret = keygen_create_keyblob(&info); + + /* Send data from kernel to user-space */ + ret = send_output(&key_crt, arg, &info, create_key_op, ret); + + break; + } + case CAAM_KEYGEN_IOCTL_IMPORT: + { + /* Validate user-space input */ + ret = validate_input(&key_crt, arg, &info, create_key_op); + if (ret) + break; + + /* Import tagged key from blob */ + ret = keygen_import_key(&info); + + /* Send data from kernel to user-space */ + ret = send_output(&key_crt, arg, &info, create_key_op, ret); + + break; + } + default: + ret = -ENOTTY; + } + + return ret; +} + +static const struct file_operations fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = caam_keygen_ioctl, + .compat_ioctl = compat_ptr_ioctl, +}; + +static struct miscdevice caam_keygen_dev = { + .minor = MISC_DYNAMIC_MINOR, + .name = DEVICE_NAME, + .fops = &fops +}; + +int caam_keygen_init(void) +{ + int ret; + + ret = misc_register(&caam_keygen_dev); + if (ret) { + pr_err("Failed to register device %s\n", + caam_keygen_dev.name); + return ret; + } + + pr_info("Device %s registered\n", caam_keygen_dev.name); + + return 0; +} + +void caam_keygen_exit(void) +{ + misc_deregister(&caam_keygen_dev); + + pr_info("caam_keygen unregistered\n"); +} diff --git a/drivers/crypto/caam/intern.h b/drivers/crypto/caam/intern.h index 038a1cede82c..b06c9eba83c5 100644 --- a/drivers/crypto/caam/intern.h +++ b/drivers/crypto/caam/intern.h @@ -252,6 +252,24 @@ static inline void caam_sm_shutdown(struct device *dev) #endif /* CONFIG_CRYPTO_DEV_FSL_CAAM_SM */ +#ifdef CONFIG_CRYPTO_DEV_FSL_CAAM_TK_API + +int caam_keygen_init(void); +void caam_keygen_exit(void); + +#else + +static inline int caam_keygen_init(void) +{ + return 0; +} + +static inline void caam_keygen_exit(void) +{ +} + +#endif /* CONFIG_CRYPTO_DEV_FSL_CAAM_TK_API */ + #ifdef CONFIG_DEBUG_FS static int caam_debugfs_u64_get(void *data, u64 *val) { diff --git a/drivers/crypto/caam/jr.c b/drivers/crypto/caam/jr.c index 7797534b7a35..77c33cc3a4f7 100644 --- a/drivers/crypto/caam/jr.c +++ b/drivers/crypto/caam/jr.c @@ -40,6 +40,7 @@ static void register_algs(struct device *dev) caam_pkc_init(dev); caam_rng_init(dev); caam_qi_algapi_init(dev); + caam_keygen_init(); algs_unlock: mutex_unlock(&algs_lock); @@ -52,6 +53,7 @@ static void unregister_algs(struct device *dev) if (--active_devs != 0) goto algs_unlock; + caam_keygen_exit(); caam_qi_algapi_exit(); caam_rng_exit(); diff --git a/include/uapi/linux/caam_keygen.h b/include/uapi/linux/caam_keygen.h new file mode 100644 index 000000000000..061696e10dcc --- /dev/null +++ b/include/uapi/linux/caam_keygen.h @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: ((GPL-2.0+ WITH Linux-syscall-note) OR BSD-3-Clause) */ +/* + * NXP CAAM key generation ioctl and kernel interface + * + * Copyright 2020 NXP + * + * This file is used by the NXP CAAM Crypto driver. + * It can be included by applications that need to communicate + * with the driver via the ioctl interface. + */ + +#ifndef _UAPI_CAAM_KEYGEN_H +#define _UAPI_CAAM_KEYGEN_H + +#include + +/** + * struct caam_keygen_cmd - Structure that contains all the necessary + * information to transfer user-space data to + * kernel space. This data is used to generate + * a black key and encapsulate it into a blob + * + * @key_enc_len : Length of the key_enc field + * @key_mode_len : Length of the key_mode field + * @key_value_len : Length of the key_value field + * @black_key_len : Length of the generated black key + * @key_enc : Encrypted Key Type + * Can be either ecb or ccm (AES-ECB or AES-CCM) + * @key_mode : The key mode used to generate the black key + * Can be either -s (for random black key) or + * -t (for black key generated from a plaintext) + * @key_value : Based on the key_mode field, can be either the + * plaintext (for black key generated from a plaintext) + * or size of key in case of black key generated + * from random + * @black_key : Black key data obtained from CAAM + * @blob_len : Length of the blob that encapsulates the black key + * @blob : Blob data obtained from CAAM + */ +struct caam_keygen_cmd { + __u8 key_enc_len; + __u8 key_mode_len; + __u16 key_value_len; + + __u32 black_key_len; + + __u64 key_enc; + __u64 key_mode; + __u64 key_value; + + __u64 black_key; + + __u32 blob_len; + __u64 blob; +}; + +/* The ioctl type, documented in ioctl-number.txt */ +#define CAAM_KEYGEN_IOCTL_TYPE 'K' + +/* Create a key */ +#define CAAM_KEYGEN_IOCTL_CREATE \ + _IOWR(CAAM_KEYGEN_IOCTL_TYPE, 0, struct caam_keygen_cmd) + +/* Import a key from a blob */ +#define CAAM_KEYGEN_IOCTL_IMPORT \ + _IOWR(CAAM_KEYGEN_IOCTL_TYPE, 1, struct caam_keygen_cmd) + +#endif /* _UAPI_CAAM_KEYGEN_H */