crypto: rockchip: cryptodev_linux: add rsa support

Add new ioctl cmd RIOCCRYPT_RSA_CRYPT for rsa encrypt/decrypt.

Signed-off-by: Lin Jinhan <troy.lin@rock-chips.com>
Change-Id: If92e1a71b1ce4fcf6b414736cf6405a1d7b6334c
This commit is contained in:
Lin Jinhan
2022-03-07 11:14:40 +08:00
committed by Tao Huang
parent cf394eddf2
commit 02bd80254d
3 changed files with 187 additions and 2 deletions

View File

@@ -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

View File

@@ -7,6 +7,8 @@
* Author: Lin Jinhan <troy.lin@rock-chips.com>
*
*/
#include <crypto/internal/akcipher.h>
#include <crypto/internal/rsa.h>
#include <linux/kernel.h>
#include <linux/scatterlist.h>
#include <linux/rtnetlink.h>
@@ -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_);
}
}

View File

@@ -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);