From 02bd80254da718aacdb3014f9820ca6cfe448666 Mon Sep 17 00:00:00 2001 From: Lin Jinhan Date: Mon, 7 Mar 2022 11:14:40 +0800 Subject: [PATCH] crypto: rockchip: cryptodev_linux: add rsa support Add new ioctl cmd RIOCCRYPT_RSA_CRYPT for rsa encrypt/decrypt. Signed-off-by: Lin Jinhan Change-Id: If92e1a71b1ce4fcf6b414736cf6405a1d7b6334c --- .../cryptodev_linux/crypto/rk_cryptodev.h | 24 +++ .../rockchip/cryptodev_linux/rk_cryptodev.c | 157 +++++++++++++++++- .../cryptodev_linux/rk_cryptodev_int.h | 8 + 3 files changed, 187 insertions(+), 2 deletions(-) diff --git a/drivers/crypto/rockchip/cryptodev_linux/crypto/rk_cryptodev.h b/drivers/crypto/rockchip/cryptodev_linux/crypto/rk_cryptodev.h index fabbbb87d129..400d75a716b1 100644 --- a/drivers/crypto/rockchip/cryptodev_linux/crypto/rk_cryptodev.h +++ b/drivers/crypto/rockchip/cryptodev_linux/crypto/rk_cryptodev.h @@ -33,10 +33,34 @@ struct crypt_fd_map_op { __u32 phys_addr; /* physics addr */ }; +#define AOP_ENCRYPT 0 +#define AOP_DECRYPT 1 + +#define COP_FLAG_RSA_PUB (0 << 8) /* decode as rsa pub key */ +#define COP_FLAG_RSA_PRIV (1 << 8) /* decode as rsa priv key */ + +#define RK_RSA_BER_KEY_MAX 8192 /* The key encoded by ber does not exceed 8K Byte */ +#define RK_RSA_KEY_MAX_BITS 4096 +#define RK_RSA_KEY_MAX_BYTES (RK_RSA_KEY_MAX_BITS / 8) + +/* input of RIOCCRYPT_RSA_CRYPT */ +struct crypt_rsa_op { + __u16 op; /* AOP_ENCRYPT/AOP_DECRYPT */ + __u16 flags; /* see COP_FLAG_* */ + __u8 reserve[4]; + __u64 key; /* BER coding RSA key */ + __u64 in; /* pointer to input data */ + __u64 out; /* pointer to output data */ + __u32 key_len; /* length of key data */ + __u32 in_len; /* length of input data */ + __u32 out_len; /* length of output data */ +}; + #define RIOCCRYPT_FD _IOWR('r', 104, struct crypt_fd_op) #define RIOCCRYPT_FD_MAP _IOWR('r', 105, struct crypt_fd_map_op) #define RIOCCRYPT_FD_UNMAP _IOW('r', 106, struct crypt_fd_map_op) #define RIOCCRYPT_CPU_ACCESS _IOW('r', 107, struct crypt_fd_map_op) #define RIOCCRYPT_DEV_ACCESS _IOW('r', 108, struct crypt_fd_map_op) +#define RIOCCRYPT_RSA_CRYPT _IOWR('r', 109, struct crypt_rsa_op) #endif diff --git a/drivers/crypto/rockchip/cryptodev_linux/rk_cryptodev.c b/drivers/crypto/rockchip/cryptodev_linux/rk_cryptodev.c index e2202b8af0c2..2cb9a30a0d0e 100644 --- a/drivers/crypto/rockchip/cryptodev_linux/rk_cryptodev.c +++ b/drivers/crypto/rockchip/cryptodev_linux/rk_cryptodev.c @@ -7,6 +7,8 @@ * Author: Lin Jinhan * */ +#include +#include #include #include #include @@ -381,7 +383,7 @@ exit: return ret; } -int crypto_fd_run(struct fcrypt *fcr, struct kernel_crypt_fd_op *kcop) +static int crypto_fd_run(struct fcrypt *fcr, struct kernel_crypt_fd_op *kcop) { struct csession *ses_ptr; struct crypt_fd_op *cop = &kcop->cop; @@ -595,11 +597,148 @@ static int dma_fd_end_cpu_access(struct fcrypt *fcr, struct kernel_crypt_fd_map_ return dma_buf_end_cpu_access(map_node->dmabuf, DMA_BIDIRECTIONAL); } +static int kcop_rsa_from_user(struct kernel_crypt_rsa_op *kcop, + struct fcrypt *fcr, void __user *arg) +{ + if (unlikely(copy_from_user(&kcop->rop, arg, sizeof(kcop->rop)))) + return -EFAULT; + + return 0; +} + +static int kcop_rsa_to_user(struct kernel_crypt_rsa_op *kcop, + struct fcrypt *fcr, void __user *arg) +{ + if (unlikely(copy_to_user(arg, &kcop->rop, sizeof(kcop->rop)))) { + derr(1, "Cannot copy to userspace"); + return -EFAULT; + } + + return 0; +} + +static int crypto_rsa_run(struct fcrypt *fcr, struct kernel_crypt_rsa_op *krop) +{ + int ret; + u8 *key = NULL, *in = NULL, *out = NULL; + u32 out_len_max; + struct crypt_rsa_op *rop = &krop->rop; + const char *driver = "rsa-rk"; + struct crypto_akcipher *tfm = NULL; + struct akcipher_request *req = NULL; + struct crypto_wait wait; + struct scatterlist src, dst; + bool is_priv_key = (rop->flags & COP_FLAG_RSA_PRIV) == COP_FLAG_RSA_PRIV; + + /* The key size cannot exceed RK_RSA_BER_KEY_MAX Byte */ + if (rop->key_len > RK_RSA_BER_KEY_MAX) + return -ENOKEY; + + if (rop->in_len > RK_RSA_KEY_MAX_BYTES || + rop->out_len > RK_RSA_KEY_MAX_BYTES) + return -EINVAL; + + tfm = crypto_alloc_akcipher(driver, 0, 0); + if (IS_ERR(tfm)) { + ddebug(2, "alg: akcipher: Failed to load tfm for %s: %ld\n", + driver, PTR_ERR(tfm)); + return PTR_ERR(tfm); + } + + req = akcipher_request_alloc(tfm, GFP_KERNEL); + if (!req) { + ddebug(2, "akcipher_request_alloc failed\n"); + ret = -ENOMEM; + goto exit; + } + + key = kzalloc(rop->key_len, GFP_KERNEL); + if (!key) { + ret = -ENOMEM; + goto exit; + } + + if (unlikely(copy_from_user(key, u64_to_user_ptr(rop->key), rop->key_len))) { + ret = -EFAULT; + goto exit; + } + + in = kzalloc(rop->in_len, GFP_KERNEL); + if (!in) { + ret = -ENOMEM; + goto exit; + } + + if (unlikely(copy_from_user(in, u64_to_user_ptr(rop->in), rop->in_len))) { + ret = -EFAULT; + goto exit; + } + + if (is_priv_key) + ret = crypto_akcipher_set_priv_key(tfm, key, rop->key_len); + else + ret = crypto_akcipher_set_pub_key(tfm, key, rop->key_len); + if (ret) { + derr(1, "crypto_akcipher_set_%s_key error[%d]", + is_priv_key ? "priv" : "pub", ret); + ret = -ENOKEY; + goto exit; + } + + out_len_max = crypto_akcipher_maxsize(tfm); + out = kzalloc(out_len_max, GFP_KERNEL); + if (!out) { + ret = -ENOMEM; + goto exit; + } + + sg_init_one(&src, in, rop->in_len); + sg_init_one(&dst, out, out_len_max); + + crypto_init_wait(&wait); + akcipher_request_set_crypt(req, &src, &dst, rop->in_len, out_len_max); + + switch (rop->op) { + case AOP_ENCRYPT: + ret = crypto_wait_req(crypto_akcipher_encrypt(req), &wait); + break; + case AOP_DECRYPT: + ret = crypto_wait_req(crypto_akcipher_decrypt(req), &wait); + break; + default: + derr(1, "unknown ops %x", rop->op); + ret = -EINVAL; + break; + } + + if (ret) { + derr(1, "alg: akcipher: failed %d\n", ret); + goto exit; + } + + if (unlikely(copy_to_user(u64_to_user_ptr(rop->out), out, req->dst_len))) { + derr(1, "Cannot copy to userspace"); + ret = -EFAULT; + goto exit; + } + + rop->out_len = req->dst_len; +exit: + kfree(out); + kfree(in); + kfree(key); + akcipher_request_free(req); + crypto_free_akcipher(tfm); + + return ret; +} + long rk_cryptodev_ioctl(struct fcrypt *fcr, unsigned int cmd, unsigned long arg_) { struct kernel_crypt_fd_op kcop; struct kernel_crypt_fd_map_op kmop; + struct kernel_crypt_rsa_op krop; void __user *arg = (void __user *)arg_; int ret; @@ -668,6 +807,20 @@ rk_cryptodev_ioctl(struct fcrypt *fcr, unsigned int cmd, unsigned long arg_) dwarning(1, "Error in dma_fd_end_cpu_access"); return ret; + case RIOCCRYPT_RSA_CRYPT: + ret = kcop_rsa_from_user(&krop, fcr, arg); + if (unlikely(ret)) { + dwarning(1, "Error copying from user"); + return ret; + } + + ret = crypto_rsa_run(fcr, &krop); + if (unlikely(ret)) { + dwarning(1, "Error in rsa_run"); + return ret; + } + + return kcop_rsa_to_user(&krop, fcr, arg); default: return -EINVAL; } @@ -853,7 +1006,7 @@ rk_compat_cryptodev_ioctl(struct fcrypt *fcr, unsigned int cmd, unsigned long ar return ret; default: - return -EINVAL; + return rk_cryptodev_ioctl(fcr, cmd, arg_); } } diff --git a/drivers/crypto/rockchip/cryptodev_linux/rk_cryptodev_int.h b/drivers/crypto/rockchip/cryptodev_linux/rk_cryptodev_int.h index 1b3b1442d8ba..c45c820f06ab 100644 --- a/drivers/crypto/rockchip/cryptodev_linux/rk_cryptodev_int.h +++ b/drivers/crypto/rockchip/cryptodev_linux/rk_cryptodev_int.h @@ -60,6 +60,14 @@ struct kernel_crypt_fd_map_op { struct crypt_fd_map_op mop; }; +/* kernel-internal extension to struct crypt_op */ +struct kernel_crypt_rsa_op { + struct crypt_rsa_op rop; + + struct task_struct *task; + struct mm_struct *mm; +}; + #if IS_ENABLED(CONFIG_CRYPTO_DEV_ROCKCHIP_DEV) int rk_cryptodev_register_dev(struct device *dev, const char *name); int rk_cryptodev_unregister_dev(struct device *dev);