- block and stream ciphers have their keys copied from userspace
  just like before
- for aead composite ciphers, the cipher and hmac keys are
  combined into a single key

Signed-off-by: Cristian Stoica <cristian.sto...@freescale.com>
---
 cryptlib.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 cryptlib.h |  3 +++
 ioctl.c    | 21 +++++++++++++-----
 3 files changed, 92 insertions(+), 6 deletions(-)

diff --git a/cryptlib.c b/cryptlib.c
index a7fbff4..44ce763 100644
--- a/cryptlib.c
+++ b/cryptlib.c
@@ -34,6 +34,8 @@
 #include <crypto/hash.h>
 #include <crypto/cryptodev.h>
 #include <crypto/aead.h>
+#include <linux/rtnetlink.h>
+#include <crypto/authenc.h>
 #include "cryptodev_int.h"
 
 
@@ -53,6 +55,78 @@ static void cryptodev_complete(struct crypto_async_request 
*req, int err)
        complete(&res->completion);
 }
 
+int cryptodev_get_cipher_keylen(unsigned int *keylen, struct session_op *sop,
+               int aead)
+{
+       /*
+        * For blockciphers (AES-CBC) or non-composite aead ciphers (like 
AES-GCM),
+        * the key length is simply the cipher keylen obtained from userspace. 
If
+        * the cipher is composite aead, the keylen is the sum of cipher keylen,
+        * hmac keylen and a key header length. This key format is the one used 
in
+        * Linux kernel for composite aead ciphers (crypto/authenc.c)
+        */
+       unsigned int klen = sop->keylen;
+
+       if (unlikely(sop->keylen > CRYPTO_CIPHER_MAX_KEY_LEN))
+               return -EINVAL;
+
+       if (aead && sop->mackeylen) {
+               if (unlikely(sop->mackeylen > CRYPTO_HMAC_MAX_KEY_LEN))
+                       return -EINVAL;
+               klen += sop->mackeylen;
+               klen += RTA_SPACE(sizeof(struct crypto_authenc_key_param));
+       }
+
+       *keylen = klen;
+       return 0;
+}
+
+int cryptodev_get_cipher_key(uint8_t *key, struct session_op *sop, int aead)
+{
+       /*
+        * Get cipher key from user-space. For blockciphers just copy it from
+        * user-space. For composite aead ciphers combine it with the hmac key 
in
+        * the format used by Linux kernel in crypto/authenc.c:
+        *
+        * [[AUTHENC_KEY_HEADER + CIPHER_KEYLEN] [AUTHENTICATION KEY] [CIPHER 
KEY]]
+        */
+       struct crypto_authenc_key_param *param;
+       struct rtattr *rta;
+       int ret = 0;
+
+       if (aead && sop->mackeylen) {
+               /*
+                * Composite aead ciphers. The first four bytes are the header 
type and
+                * header length for aead keys
+                */
+               rta = (void *)key;
+               rta->rta_type = CRYPTO_AUTHENC_KEYA_PARAM;
+               rta->rta_len = RTA_LENGTH(sizeof(*param));
+
+               /*
+                * The next four bytes hold the length of the encryption key
+                */
+               param = RTA_DATA(rta);
+               param->enckeylen = cpu_to_be32(sop->keylen);
+
+               /* Advance key pointer eight bytes and copy the hmac key */
+               key += RTA_SPACE(sizeof(*param));
+               if (unlikely(copy_from_user(key, sop->mackey, sop->mackeylen))) 
{
+                       ret = -EFAULT;
+                       goto error;
+               }
+               /* Advance key pointer past the hmac key */
+               key += sop->mackeylen;
+       }
+       /* now copy the blockcipher key */
+       if (unlikely(copy_from_user(key, sop->key, sop->keylen)))
+               ret = -EFAULT;
+
+error:
+       return ret;
+}
+
+
 int cryptodev_cipher_init(struct cipher_data *out, const char *alg_name,
                                uint8_t *keyp, size_t keylen, int stream, int 
aead)
 {
diff --git a/cryptlib.h b/cryptlib.h
index 0744284..a0a8a63 100644
--- a/cryptlib.h
+++ b/cryptlib.h
@@ -25,6 +25,9 @@ struct cipher_data {
 int cryptodev_cipher_init(struct cipher_data *out, const char *alg_name,
                          uint8_t *key, size_t keylen, int stream, int aead);
 void cryptodev_cipher_deinit(struct cipher_data *cdata);
+int cryptodev_get_cipher_key(uint8_t *key, struct session_op *sop, int aead);
+int cryptodev_get_cipher_keylen(unsigned int *keylen, struct session_op *sop,
+               int aead);
 ssize_t cryptodev_cipher_decrypt(struct cipher_data *cdata,
                        const struct scatterlist *sg1,
                        struct scatterlist *sg2, size_t len);
diff --git a/ioctl.c b/ioctl.c
index fecbbff..5a55a76 100644
--- a/ioctl.c
+++ b/ioctl.c
@@ -45,6 +45,8 @@
 #include <linux/uaccess.h>
 #include <crypto/cryptodev.h>
 #include <linux/scatterlist.h>
+#include <linux/rtnetlink.h>
+#include <crypto/authenc.h>
 
 #include <linux/sysctl.h>
 
@@ -109,9 +111,16 @@ crypto_create_session(struct fcrypt *fcr, struct 
session_op *sop)
        const char *alg_name = NULL;
        const char *hash_name = NULL;
        int hmac_mode = 1, stream = 0, aead = 0;
+       /*
+        * With composite aead ciphers, only ckey is used and it can cover all 
the
+        * structure space; otherwise both keys may be used simultaneously but 
they
+        * are confined to their spaces
+        */
        struct {
                uint8_t ckey[CRYPTO_CIPHER_MAX_KEY_LEN];
                uint8_t mkey[CRYPTO_HMAC_MAX_KEY_LEN];
+               /* padding space for aead keys */
+               uint8_t pad[RTA_SPACE(sizeof(struct crypto_authenc_key_param))];
        } keys;
 
        /* Does the request make sense? */
@@ -226,20 +235,20 @@ crypto_create_session(struct fcrypt *fcr, struct 
session_op *sop)
 
        /* Set-up crypto transform. */
        if (alg_name) {
-               if (unlikely(sop->keylen > CRYPTO_CIPHER_MAX_KEY_LEN)) {
+               unsigned int keylen;
+               ret = cryptodev_get_cipher_keylen(&keylen, sop, aead);
+               if (unlikely(ret < 0)) {
                        ddebug(1, "Setting key failed for %s-%zu.",
                                alg_name, (size_t)sop->keylen*8);
-                       ret = -EINVAL;
                        goto error_cipher;
                }
 
-               if (unlikely(copy_from_user(keys.ckey, sop->key, sop->keylen))) 
{
-                       ret = -EFAULT;
+               ret = cryptodev_get_cipher_key(keys.ckey, sop, aead);
+               if (unlikely(ret < 0))
                        goto error_cipher;
-               }
 
                ret = cryptodev_cipher_init(&ses_new->cdata, alg_name, 
keys.ckey,
-                                               sop->keylen, stream, aead);
+                                               keylen, stream, aead);
                if (ret < 0) {
                        ddebug(1, "Failed to load cipher for %s", alg_name);
                        ret = -EINVAL;
-- 
1.8.3.1


_______________________________________________
Cryptodev-linux-devel mailing list
Cryptodev-linux-devel@gna.org
https://mail.gna.org/listinfo/cryptodev-linux-devel

Reply via email to