- 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