aboutsummaryrefslogtreecommitdiffstats
path: root/recipes-kernel/cryptodev/sdk_patches/0007-add-support-for-RSA-public-and-private-key-operation.patch
diff options
context:
space:
mode:
Diffstat (limited to 'recipes-kernel/cryptodev/sdk_patches/0007-add-support-for-RSA-public-and-private-key-operation.patch')
-rw-r--r--recipes-kernel/cryptodev/sdk_patches/0007-add-support-for-RSA-public-and-private-key-operation.patch440
1 files changed, 440 insertions, 0 deletions
diff --git a/recipes-kernel/cryptodev/sdk_patches/0007-add-support-for-RSA-public-and-private-key-operation.patch b/recipes-kernel/cryptodev/sdk_patches/0007-add-support-for-RSA-public-and-private-key-operation.patch
new file mode 100644
index 00000000..803b90ad
--- /dev/null
+++ b/recipes-kernel/cryptodev/sdk_patches/0007-add-support-for-RSA-public-and-private-key-operation.patch
@@ -0,0 +1,440 @@
+From 6213ae5228a2ff0bb3521474ae37effda95a5d46 Mon Sep 17 00:00:00 2001
+From: Cristian Stoica <cristian.stoica@nxp.com>
+Date: Fri, 12 May 2017 17:04:40 +0300
+Subject: [PATCH 7/9] add support for RSA public and private key operations
+
+Only form 1 support is added with this patch. To maintain
+compatibility with OpenBSD we need to reverse bignum buffers before
+giving them to the kernel. This adds an artificial performance
+penalty that can be resolved only with a CIOCKEY extension in
+cryptodev API.
+
+As of Linux kernel 4.12 it is not possible to give to the kernel
+directly a pointer to a RSA key structure and must resort to a BER
+encoding scheme.
+
+Support for private keys in form 3 (CRT) must wait for updates and
+fixes in Linux kernel crypto API.
+
+Known issue:
+Kernels <= v4.7 strip leading zeros from the result and we get padding
+errors from Openssl: RSA_EAY_PUBLIC_DECRYPT: padding check failed
+(Fixed with kernel commit "crypto: rsa - Generate fixed-length output"
+9b45b7bba3d22de52e09df63c50f390a193a3f53)
+
+Signed-off-by: Cristian Stoica <cristian.stoica@nxp.com>
+---
+ cryptlib.c | 234 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ cryptlib.h | 4 +-
+ cryptodev_int.h | 17 ++++
+ ioctl.c | 17 +++-
+ main.c | 42 ++++++++++
+ 5 files changed, 312 insertions(+), 2 deletions(-)
+
+diff --git a/cryptlib.c b/cryptlib.c
+index 2c6028e..1c044a4 100644
+--- a/cryptlib.c
++++ b/cryptlib.c
+@@ -37,6 +37,10 @@
+ #include <crypto/authenc.h>
+ #include "cryptodev_int.h"
+ #include "cipherapi.h"
++#if (LINUX_VERSION_CODE > KERNEL_VERSION(4, 3, 0))
++#include <linux/asn1_ber_bytecode.h>
++#include <crypto/akcipher.h>
++#endif
+
+ extern const struct crypto_type crypto_givcipher_type;
+
+@@ -435,3 +439,233 @@ int cryptodev_hash_final(struct hash_data *hdata, void *output)
+ return waitfor(&hdata->async.result, ret);
+ }
+
++#if (LINUX_VERSION_CODE > KERNEL_VERSION(4, 3, 0))
++/* This function is necessary because the bignums in Linux kernel are MSB first
++ * (big endian) as opposed to LSB first as OpenBSD crypto layer uses */
++void reverse_buf(uint8_t *buf, size_t sz)
++{
++ int i;
++ uint8_t *end;
++ uint8_t tmp;
++
++ end = buf + sz;
++
++ for (i = 0; i < sz/2; i++) {
++ end--;
++
++ tmp = *buf;
++ *buf = *end;
++ *end = tmp;
++
++ buf++;
++ }
++}
++
++int ber_wr_tag(uint8_t **ber_ptr, uint8_t tag)
++{
++ **ber_ptr = tag;
++ *ber_ptr += 1;
++
++ return 0;
++}
++
++int ber_wr_len(uint8_t **ber_ptr, size_t len, size_t sz)
++{
++ if (len < 127) {
++ **ber_ptr = len;
++ *ber_ptr += 1;
++ } else {
++ size_t sz_save = sz;
++
++ sz--;
++ **ber_ptr = 0x80 | sz;
++
++ while (sz > 0) {
++ *(*ber_ptr + sz) = len & 0xff;
++ len >>= 8;
++ sz--;
++ }
++ *ber_ptr += sz_save;
++ }
++
++ return 0;
++}
++
++int ber_wr_int(uint8_t **ber_ptr, uint8_t *crp_p, size_t sz)
++{
++ int ret;
++
++ ret = copy_from_user(*ber_ptr, crp_p, sz);
++ reverse_buf(*ber_ptr, sz);
++
++ *ber_ptr += sz;
++
++ return ret;
++}
++
++/* calculate the size of the length field itself in BER encoding */
++size_t ber_enc_len(size_t len)
++{
++ size_t sz;
++
++ sz = 1;
++ if (len > 127) { /* long encoding */
++ while (len != 0) {
++ len >>= 8;
++ sz++;
++ }
++ }
++
++ return sz;
++}
++
++void *cryptodev_alloc_rsa_pub_key(struct kernel_crypt_pkop *pkop,
++ uint32_t *key_len)
++{
++ struct crypt_kop *cop = &pkop->pkop;
++ uint8_t *ber_key;
++ uint8_t *ber_ptr;
++ uint32_t ber_key_len;
++ size_t s_sz;
++ size_t e_sz;
++ size_t n_sz;
++ size_t s_enc_len;
++ size_t e_enc_len;
++ size_t n_enc_len;
++ int err;
++
++ /* BER public key format:
++ * SEQUENCE TAG 1 byte
++ * SEQUENCE LENGTH s_enc_len bytes
++ * INTEGER TAG 1 byte
++ * INTEGER LENGTH n_enc_len bytes
++ * INTEGER (n modulus) n_sz bytes
++ * INTEGER TAG 1 byte
++ * INTEGER LENGTH e_enc_len bytes
++ * INTEGER (e exponent) e_sz bytes
++ */
++
++ e_sz = (cop->crk_param[1].crp_nbits + 7)/8;
++ n_sz = (cop->crk_param[2].crp_nbits + 7)/8;
++
++ e_enc_len = ber_enc_len(e_sz);
++ n_enc_len = ber_enc_len(n_sz);
++
++ /*
++ * Sequence length is the size of all the fields following the sequence
++ * tag, added together. The two added bytes account for the two INT
++ * tags in the Public Key sequence
++ */
++ s_sz = e_sz + e_enc_len + n_sz + n_enc_len + 2;
++ s_enc_len = ber_enc_len(s_sz);
++
++ /* The added byte accounts for the SEQ tag at the start of the key */
++ ber_key_len = s_sz + s_enc_len + 1;
++
++ /* Linux asn1_ber_decoder doesn't like keys that are too large */
++ if (ber_key_len > 65535) {
++ return NULL;
++ }
++
++ ber_key = kmalloc(ber_key_len, GFP_DMA);
++ if (!ber_key) {
++ return NULL;
++ }
++
++ ber_ptr = ber_key;
++
++ err = ber_wr_tag(&ber_ptr, _tag(UNIV, CONS, SEQ)) ||
++ ber_wr_len(&ber_ptr, s_sz, s_enc_len) ||
++ ber_wr_tag(&ber_ptr, _tag(UNIV, PRIM, INT)) ||
++ ber_wr_len(&ber_ptr, n_sz, n_enc_len) ||
++ ber_wr_int(&ber_ptr, cop->crk_param[2].crp_p, n_sz) ||
++ ber_wr_tag(&ber_ptr, _tag(UNIV, PRIM, INT)) ||
++ ber_wr_len(&ber_ptr, e_sz, e_enc_len) ||
++ ber_wr_int(&ber_ptr, cop->crk_param[1].crp_p, e_sz);
++ if (err != 0) {
++ goto free_key;
++ }
++
++ *key_len = ber_key_len;
++ return ber_key;
++
++free_key:
++ kfree(ber_key);
++ return NULL;
++}
++
++int crypto_bn_modexp(struct kernel_crypt_pkop *pkop)
++{
++ struct crypt_kop *cop = &pkop->pkop;
++ uint8_t *ber_key;
++ uint32_t ber_key_len;
++ size_t m_sz;
++ size_t c_sz;
++ size_t c_sz_max;
++ uint8_t *m_buf;
++ uint8_t *c_buf;
++ struct scatterlist src;
++ struct scatterlist dst;
++ int err;
++
++ ber_key = cryptodev_alloc_rsa_pub_key(pkop, &ber_key_len);
++ if (!ber_key) {
++ return -ENOMEM;
++ }
++
++ err = crypto_akcipher_set_pub_key(pkop->s, ber_key, ber_key_len);
++ if (err != 0) {
++ goto free_key;
++ }
++
++ m_sz = (cop->crk_param[0].crp_nbits + 7)/8;
++ c_sz = (cop->crk_param[3].crp_nbits + 7)/8;
++
++ m_buf = kmalloc(m_sz, GFP_DMA);
++ if (!m_buf) {
++ err = -ENOMEM;
++ goto free_key;
++ }
++
++ err = copy_from_user(m_buf, cop->crk_param[0].crp_p, m_sz);
++ if (err != 0) {
++ goto free_m_buf;
++ }
++ reverse_buf(m_buf, m_sz);
++
++ c_sz_max = crypto_akcipher_maxsize(pkop->s);
++ if (c_sz > c_sz_max) {
++ err = -EINVAL;
++ goto free_m_buf;
++ }
++
++ c_buf = kzalloc(c_sz_max, GFP_KERNEL);
++ if (!c_buf) {
++ goto free_m_buf;
++ }
++
++ sg_init_one(&src, m_buf, m_sz);
++ sg_init_one(&dst, c_buf, c_sz);
++
++ init_completion(&pkop->result.completion);
++ akcipher_request_set_callback(pkop->req, 0,
++ cryptodev_complete, &pkop->result);
++ akcipher_request_set_crypt(pkop->req, &src, &dst, m_sz, c_sz);
++
++ err = crypto_akcipher_encrypt(pkop->req);
++ err = waitfor(&pkop->result, err);
++
++ if (err == 0) {
++ reverse_buf(c_buf, c_sz);
++ err = copy_to_user(cop->crk_param[3].crp_p, c_buf, c_sz);
++ }
++
++ kfree(c_buf);
++free_m_buf:
++ kfree(m_buf);
++free_key:
++ kfree(ber_key);
++
++ return err;
++}
++#endif
+diff --git a/cryptlib.h b/cryptlib.h
+index 48fe9bd..f909c34 100644
+--- a/cryptlib.h
++++ b/cryptlib.h
+@@ -95,6 +95,8 @@ int cryptodev_hash_reset(struct hash_data *hdata);
+ void cryptodev_hash_deinit(struct hash_data *hdata);
+ int cryptodev_hash_init(struct hash_data *hdata, const char *alg_name,
+ int hmac_mode, void *mackey, size_t mackeylen);
+-
++#if (LINUX_VERSION_CODE > KERNEL_VERSION(4, 3, 0))
++int crypto_bn_modexp(struct kernel_crypt_pkop *pkop);
++#endif
+
+ #endif
+diff --git a/cryptodev_int.h b/cryptodev_int.h
+index c1879fd..7860c39 100644
+--- a/cryptodev_int.h
++++ b/cryptodev_int.h
+@@ -19,6 +19,10 @@
+ #include <linux/scatterlist.h>
+ #include <crypto/cryptodev.h>
+ #include <crypto/aead.h>
++#if (LINUX_VERSION_CODE > KERNEL_VERSION(4, 3, 0))
++#include <crypto/internal/rsa.h>
++#endif
++
+
+ #define PFX "cryptodev: "
+ #define dprintk(level, severity, format, a...) \
+@@ -111,6 +115,18 @@ struct kernel_crypt_auth_op {
+ struct mm_struct *mm;
+ };
+
++#if (LINUX_VERSION_CODE > KERNEL_VERSION(4, 3, 0))
++struct kernel_crypt_pkop {
++ struct crypt_kop pkop;
++
++ struct crypto_akcipher *s; /* Transform pointer from CryptoAPI */
++ struct akcipher_request *req; /* PKC request allocated from CryptoAPI */
++ struct cryptodev_result result; /* updated by completion handler */
++};
++
++int crypto_run_asym(struct kernel_crypt_pkop *pkop);
++#endif
++
+ /* auth */
+
+ int kcaop_from_user(struct kernel_crypt_auth_op *kcop,
+@@ -122,6 +138,7 @@ int crypto_run(struct fcrypt *fcr, struct kernel_crypt_op *kcop);
+
+ #include <cryptlib.h>
+
++
+ /* other internal structs */
+ struct csession {
+ struct list_head entry;
+diff --git a/ioctl.c b/ioctl.c
+index db7207a..8b0df4e 100644
+--- a/ioctl.c
++++ b/ioctl.c
+@@ -810,6 +810,9 @@ cryptodev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg_)
+ struct session_op sop;
+ struct kernel_crypt_op kcop;
+ struct kernel_crypt_auth_op kcaop;
++#if (LINUX_VERSION_CODE > KERNEL_VERSION(4, 3, 0))
++ struct kernel_crypt_pkop pkop;
++#endif
+ struct crypt_priv *pcr = filp->private_data;
+ struct fcrypt *fcr;
+ struct session_info_op siop;
+@@ -823,7 +826,11 @@ cryptodev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg_)
+
+ switch (cmd) {
+ case CIOCASYMFEAT:
+- return put_user(0, p);
++ ses = 0;
++ if (crypto_has_alg("rsa", 0, 0)) {
++ ses = CRF_MOD_EXP;
++ }
++ return put_user(ses, p);
+ case CRIOGET:
+ fd = clonefd(filp);
+ ret = put_user(fd, p);
+@@ -859,6 +866,14 @@ cryptodev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg_)
+ if (unlikely(ret))
+ return ret;
+ return copy_to_user(arg, &siop, sizeof(siop));
++#if (LINUX_VERSION_CODE > KERNEL_VERSION(4, 3, 0))
++ case CIOCKEY:
++ ret = copy_from_user(&pkop.pkop, arg, sizeof(struct crypt_kop));
++ if (ret == 0) {
++ ret = crypto_run_asym(&pkop);
++ }
++ return ret;
++#endif
+ case CIOCCRYPT:
+ if (unlikely(ret = kcop_from_user(&kcop, fcr, arg))) {
+ dwarning(1, "Error copying from user");
+diff --git a/main.c b/main.c
+index 57e5c38..2bfe6f0 100644
+--- a/main.c
++++ b/main.c
+@@ -48,6 +48,9 @@
+ #include "zc.h"
+ #include "cryptlib.h"
+ #include "version.h"
++#if (LINUX_VERSION_CODE > KERNEL_VERSION(4, 3, 0))
++#include <crypto/akcipher.h>
++#endif
+
+ /* This file contains the traditional operations of encryption
+ * and hashing of /dev/crypto.
+@@ -265,3 +268,42 @@ out_unlock:
+ crypto_put_session(ses_ptr);
+ return ret;
+ }
++
++#if (LINUX_VERSION_CODE > KERNEL_VERSION(4, 3, 0))
++int crypto_run_asym(struct kernel_crypt_pkop *pkop)
++{
++ int err;
++
++ pkop->s = crypto_alloc_akcipher("rsa", 0, 0);
++ if (IS_ERR(pkop->s)) {
++ return PTR_ERR(pkop->s);
++ }
++
++ pkop->req = akcipher_request_alloc(pkop->s, GFP_KERNEL);
++ if (pkop->req == NULL) {
++ err = -ENOMEM;
++ goto out_free_tfm;
++ }
++
++ switch (pkop->pkop.crk_op) {
++ case CRK_MOD_EXP: /* RSA_PUB or PRIV form 1 */
++ if (pkop->pkop.crk_iparams != 3 && pkop->pkop.crk_oparams != 1) {
++ err = -EINVAL;
++ goto out_free_req;
++ }
++ err = crypto_bn_modexp(pkop);
++ break;
++ default:
++ err = -EINVAL;
++ break;
++ }
++
++out_free_req:
++ kfree(pkop->req);
++
++out_free_tfm:
++ crypto_free_akcipher(pkop->s);
++
++ return err;
++}
++#endif
+--
+2.7.4
+