blob: d16aea089044e0a2b49ede57e65aeb5eaf465731 [file] [log] [blame]
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/clk-provider.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/hw_random.h>
#include <linux/platform_device.h>
#include <linux/of_device.h>
#include <linux/mutex.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <uapi/linux/hwrsa_ioctl.h>
#ifdef CONFIG_TEE
#include <linux/tee_drv.h>
#endif
#include "asr-te200-optee.h"
#include "asr-rsa-optee.h"
static struct teec_uuid pta_rsa_uuid = ASR_RSA_ACCESS_UUID;
static int asr_optee_rsa_sign(struct hwrsa_arg *rsa_arg, u_int cmd)
{
struct tee_ioctl_invoke_arg invoke_arg;
struct tee_param params[4];
struct asrte200_tee_context asrte200_tee_ctx;
struct tee_shm *shm;
int ret = 0;
char *ma = NULL;
struct rsa_ioctl_key *key = rsa_arg->rsa_key;
uint8_t *n = key->n, *e = key->e, *d = key->d, *p = key->p, *q= key->q;
size_t n_size = key->n_size, e_size = key->e_size, d_size = key->d_size;
size_t p_size = key->p_size, q_size = key->q_size;
uint8_t *msg = rsa_arg->msg, *sign = rsa_arg->sign;
size_t msg_size = rsa_arg->msg_size, sign_size = key->n_size;
int is_blinding;
u_int optee_cmd;
switch (cmd) {
case HWRSA_SIGN_PKCS_V15_SHA1:
optee_cmd = CMD_RSA_SIGN_PKCS_V15_SHA1;
break;
case HWRSA_SIGN_PKCS_V15_SHA256:
optee_cmd = CMD_RSA_SIGN_PKCS_V15_SHA256;
break;
case HWRSA_SIGN_PKCS_V21_SHA1:
optee_cmd = CMD_RSA_SIGN_PKCS_V21_SHA1;
break;
case HWRSA_SIGN_PKCS_V21_SHA256:
optee_cmd = CMD_RSA_SIGN_PKCS_V21_SHA256;
break;
default:
ret = -EINVAL;
goto exit;
}
if (!p || !q || !p_size || !q_size) {
is_blinding = 0;
} else {
is_blinding = 1;
}
ret = asrte200_optee_open_ta(&asrte200_tee_ctx, &pta_rsa_uuid);
if (ret != 0) {
return ret;
}
memset(&invoke_arg, 0x0, sizeof(struct tee_ioctl_invoke_arg));
invoke_arg.func = optee_cmd;
invoke_arg.session = asrte200_tee_ctx.session;
if (is_blinding) {
shm = tee_shm_alloc(asrte200_tee_ctx.tee_ctx,
n_size + e_size + d_size + p_size + q_size + msg_size + sign_size,
TEE_SHM_MAPPED | TEE_SHM_DMA_BUF);
} else {
shm = tee_shm_alloc(asrte200_tee_ctx.tee_ctx,
n_size + e_size + d_size + msg_size + sign_size,
TEE_SHM_MAPPED | TEE_SHM_DMA_BUF);
}
if (!shm) {
ret = -EINVAL;
goto exit;
}
ma = tee_shm_get_va(shm, 0);
memcpy(ma, n, n_size);
memcpy(ma + n_size, e, e_size);
memcpy(ma + n_size + e_size, d, d_size);
if (is_blinding) {
memcpy(ma + n_size + e_size + d_size, p, p_size);
memcpy(ma + n_size + e_size + d_size + q_size, q, q_size);
}
/* import rsa key */
params[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
params[0].u.memref.shm_offs = 0;
if (is_blinding) {
params[0].u.memref.size = n_size + e_size + d_size + p_size + q_size;
} else {
params[0].u.memref.size = n_size + e_size + d_size;
}
params[0].u.memref.shm = shm;
params[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT;
params[1].u.value.a = 0;
params[1].u.value.a |= n_size;
params[1].u.value.a |= e_size << 10;
params[1].u.value.a |= d_size << 20;
params[1].u.value.a |= is_blinding << 30;
if (is_blinding) {
params[1].u.value.b = 0;
params[1].u.value.b |= p_size;
params[1].u.value.b |= q_size << 10;
}
/* import message */
params[2].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
if (is_blinding) {
memcpy(ma + n_size + e_size + d_size + p_size + q_size, msg, msg_size);
params[2].u.memref.shm_offs = n_size + e_size + d_size + p_size + q_size;
} else {
memcpy(ma + n_size + e_size + d_size, msg, msg_size);
params[2].u.memref.shm_offs = n_size + e_size + d_size;
}
params[2].u.memref.size = msg_size;
params[2].u.memref.shm = shm;
/* import signature */
params[3].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT;
if (is_blinding) {
params[3].u.memref.shm_offs = n_size + e_size + d_size + p_size + q_size + msg_size;
} else {
params[3].u.memref.shm_offs = n_size + e_size + d_size + msg_size;
}
params[3].u.memref.size = sign_size;
params[3].u.memref.shm = shm;
invoke_arg.num_params = 4;
ret = tee_client_invoke_func(asrte200_tee_ctx.tee_ctx, &invoke_arg, params);
if (ret != 0) {
rsa_arg->result = 0;
} else if (invoke_arg.ret != 0) {
rsa_arg->result = 0;
goto free_shm;
}
rsa_arg->result = 1;
if (is_blinding) {
memcpy(sign, ma + n_size + e_size + d_size + p_size + q_size + msg_size, sign_size);
} else {
memcpy(sign, ma + n_size + e_size + d_size + msg_size, sign_size);
}
free_shm:
tee_shm_free(shm);
exit:
asrte200_optee_close_ta(&asrte200_tee_ctx);
return ret;
}
static int asr_optee_rsa_verify(struct hwrsa_arg *rsa_arg, u_int cmd)
{
struct tee_ioctl_invoke_arg invoke_arg;
struct tee_param params[4];
struct asrte200_tee_context asrte200_tee_ctx;
struct tee_shm *shm;
int ret = 0;
char *ma = NULL;
struct rsa_ioctl_key *key = rsa_arg->rsa_key;
uint8_t *n = key->n, *e = key->e;
size_t n_size = key->n_size, e_size = key->e_size;
uint8_t *msg = rsa_arg->msg, *sign = rsa_arg->sign;
size_t msg_size = rsa_arg->msg_size, sign_size = rsa_arg->sign_size;
u_int optee_cmd;
switch (cmd) {
case HWRSA_VERIFY_PKCS_V15_SHA1:
optee_cmd = CMD_RSA_VERIFY_PKCS_V15_SHA1;
break;
case HWRSA_VERIFY_PKCS_V15_SHA256:
optee_cmd = CMD_RSA_VERIFY_PKCS_V15_SHA256;
break;
case HWRSA_VERIFY_PKCS_V21_SHA1:
optee_cmd = CMD_RSA_VERIFY_PKCS_V21_SHA1;
break;
case HWRSA_VERIFY_PKCS_V21_SHA256:
optee_cmd = CMD_RSA_VERIFY_PKCS_V21_SHA256;
break;
default:
ret = -EINVAL;
goto exit;
}
ret = asrte200_optee_open_ta(&asrte200_tee_ctx, &pta_rsa_uuid);
if (ret != 0) {
return ret;
}
memset(&invoke_arg, 0x0, sizeof(struct tee_ioctl_invoke_arg));
invoke_arg.func = optee_cmd;
invoke_arg.session = asrte200_tee_ctx.session;
shm = tee_shm_alloc(asrte200_tee_ctx.tee_ctx,
n_size + e_size + msg_size + sign_size,
TEE_SHM_MAPPED | TEE_SHM_DMA_BUF);
if (!shm) {
ret = -EINVAL;
goto exit;
}
ma = tee_shm_get_va(shm, 0);
memcpy(ma, n, n_size);
memcpy(ma + n_size, e, e_size);
/* import rsa key */
params[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
params[0].u.memref.shm_offs = 0;
params[0].u.memref.size = n_size + e_size;
params[0].u.memref.shm = shm;
/* import msg */
memcpy(ma + n_size + e_size, msg, msg_size);
params[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
params[1].u.memref.shm_offs = n_size + e_size;
params[1].u.memref.size = msg_size;
params[1].u.memref.shm = shm;
/* import sign */
memcpy(ma + n_size + e_size + msg_size, sign, sign_size);
params[2].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
params[2].u.memref.shm_offs = n_size + e_size + msg_size;
params[2].u.memref.size = sign_size;
params[2].u.memref.shm = shm;
params[3].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT;
params[3].u.value.a = n_size;
invoke_arg.num_params = 4;
ret = tee_client_invoke_func(asrte200_tee_ctx.tee_ctx, &invoke_arg, params);
if (ret != 0) {
rsa_arg->result = 0;
goto free_shm;
} else if (invoke_arg.ret != 0) {
rsa_arg->result = 0;
goto free_shm;
}
rsa_arg->result = 1;
free_shm:
tee_shm_free(shm);
exit:
asrte200_optee_close_ta(&asrte200_tee_ctx);
return ret;
}
static int asr_rsa_open(struct inode *inode, struct file *file)
{
return 0;
}
static int asr_rsa_close(struct inode *inode, struct file *file)
{
return 0;
}
static long asr_rsa_ioctl(struct file *file, u_int cmd, u_long arg)
{
int ret = 0;
struct miscdevice *miscdev;
struct asr_te200_rsa *rsa;
struct hwrsa_arg rsa_arg;
struct rsa_ioctl_key *key;
struct hwrsa_arg *u_arg = (void __user *)arg;
miscdev = file->private_data;
rsa = container_of(miscdev, struct asr_te200_rsa, rsa_misc);
if (copy_from_user(&rsa_arg, (void __user *)arg, sizeof(rsa_arg))) {
return -EFAULT;
}
key = rsa_arg.rsa_key;
if (!rsa_arg.rsa_key) {
return -EFAULT;
}
if (!rsa_arg.msg || !rsa_arg.msg_size) {
return -EFAULT;
}
switch (cmd) {
case HWRSA_SIGN_PKCS_V15_SHA1:
case HWRSA_SIGN_PKCS_V15_SHA256:
case HWRSA_SIGN_PKCS_V21_SHA1:
case HWRSA_SIGN_PKCS_V21_SHA256:
if (!rsa_arg.sign || !key->is_private) {
ret = -EINVAL;
goto exit;
}
ret = asr_optee_rsa_sign(&rsa_arg, cmd);
put_user(rsa_arg.result, &u_arg->result);
break;
case HWRSA_VERIFY_PKCS_V15_SHA1:
case HWRSA_VERIFY_PKCS_V15_SHA256:
case HWRSA_VERIFY_PKCS_V21_SHA1:
case HWRSA_VERIFY_PKCS_V21_SHA256:
if (!rsa_arg.sign || !rsa_arg.sign_size || key->is_private) {
ret = -EINVAL;
goto exit;
}
ret = asr_optee_rsa_verify(&rsa_arg, cmd);
put_user(rsa_arg.result, &u_arg->result);
break;
default:
dev_err(rsa->dev, "asr te200: rsa iotcl invald command %x\n", cmd);
ret = -EINVAL;
goto exit;
}
exit:
return ret;
}
static const struct file_operations asr_rsa_fops = {
.owner = THIS_MODULE,
.open = asr_rsa_open,
.release = asr_rsa_close,
.unlocked_ioctl = asr_rsa_ioctl,
};
int asr_te200_rsa_register(struct asr_te200_dev *te200_dd)
{
int ret = 0;
struct asr_te200_rsa *prsa;
struct miscdevice *misc;
struct device *dev = te200_dd->dev;
prsa = &te200_dd->asr_rsa;
misc = &prsa->rsa_misc;
misc->name = "hwrsa";
misc->minor = MISC_DYNAMIC_MINOR;
misc->fops = &asr_rsa_fops;
misc->this_device = NULL;
prsa->dev = te200_dd->dev;
/* register the device */
ret = misc_register(misc);
if (ret < 0) {
dev_err(dev,
"asr rsa: unable to register device node /dev/hwrsa\n");
return ret;
}
return 0;
}
EXPORT_SYMBOL_GPL(asr_te200_rsa_register);
int asr_te200_rsa_unregister(struct asr_te200_dev *te200_dd)
{
struct miscdevice *miscdev;
miscdev = &te200_dd->asr_rsa.rsa_misc;
misc_deregister(miscdev);
return 0;
}
EXPORT_SYMBOL_GPL(asr_te200_rsa_unregister);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Yonggan Wang <yongganwang@asrmicro.com>");
MODULE_DESCRIPTION("ASR hwrsa driver");