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