From d030fa0e9759156e2a1ba1870c63bb775fede69f Mon Sep 17 00:00:00 2001 From: Nikos Mavrogiannopoulos Date: Wed, 23 Nov 2011 20:35:33 +0100 Subject: [PATCH] Added CIOCAUTHCRYPT ioctl() that allows encrypting TLS records. --- .gitignore | 2 + Makefile | 2 +- crypto/cryptodev.h | 28 ++- cryptodev_auth.c | 458 +++++++++++++++++++++++++++++++++++++++++++++++++ cryptodev_int.h | 54 ++++++ cryptodev_main.c | 114 ++++++------ examples/Makefile | 3 +- examples/cipher-aead.c | 271 +++++++++++++++++++++++++++++ 8 files changed, 873 insertions(+), 59 deletions(-) create mode 100644 cryptodev_auth.c create mode 100644 examples/cipher-aead.c diff --git a/.gitignore b/.gitignore index 356d53a..247b562 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,5 @@ examples/hashcrypt_speed releases scripts version.h +examples/cipher-aead +examples/fullspeed diff --git a/Makefile b/Makefile index d12c08b..a90cd8b 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ KBUILD_CFLAGS += -I$(src) KERNEL_DIR = /lib/modules/$(shell uname -r)/build VERSION = 1.0 -cryptodev-objs = cryptodev_main.o cryptodev_cipher.o +cryptodev-objs = cryptodev_main.o cryptodev_cipher.o cryptodev_auth.o obj-m += cryptodev.o diff --git a/crypto/cryptodev.h b/crypto/cryptodev.h index 6b0205b..0610282 100644 --- a/crypto/cryptodev.h +++ b/crypto/cryptodev.h @@ -57,7 +57,7 @@ enum cryptodev_crypto_op_t { #define DES3_BLOCK_LEN 8 #define RIJNDAEL128_BLOCK_LEN 16 #define AES_BLOCK_LEN RIJNDAEL128_BLOCK_LEN -#define CAMELLIA_BLOCK_LEN +#define CAMELLIA_BLOCK_LEN 16 #define BLOWFISH_BLOCK_LEN 8 #define SKIPJACK_BLOCK_LEN 8 #define CAST128_BLOCK_LEN 8 @@ -104,7 +104,7 @@ struct session_info_op { #define COP_DECRYPT 1 /* input of CIOCCRYPT */ - struct crypt_op { +struct crypt_op { __u32 ses; /* session identifier */ __u16 op; /* COP_ENCRYPT or COP_DECRYPT */ __u16 flags; /* see COP_FLAG_* */ @@ -117,6 +117,24 @@ struct session_info_op { __u8 __user *iv; }; +/* input of CIOCAUTHCRYPT */ +struct crypt_auth_op { + __u32 ses; /* session identifier */ + __u16 op; /* COP_ENCRYPT or COP_DECRYPT */ + __u16 flags; /* see COP_FLAG_* */ + __u32 len; /* length of source data */ + __u32 auth_len; /* length of auth data */ + __u32 tag_len; /* the length of the tag */ + __u8 __user *auth_src; /* authenticated-only data */ + __u8 __user *src; /* data to be encrypted and authenticated */ + __u8 __user *dst; /* pointer to output data. Must have + * space for tag. This should be at least + * src_len + tag_size + block_size for padding */ + + /* initialization vector for encryption operations */ + __u8 __user *iv; +}; + /* struct crypt_op flags */ #define COP_FLAG_NONE (0 << 0) /* totally no flag */ @@ -124,6 +142,9 @@ struct session_info_op { #define COP_FLAG_FINAL (1 << 1) /* multi-update final hash mode */ #define COP_FLAG_WRITE_IV (1 << 2) /* update the IV during operation */ #define COP_FLAG_NO_ZC (1 << 3) /* do not zero-copy */ +#define COP_FLAG_AEAD_TLS_TYPE (1 << 4) /* authenticate and encrypt using the + * protocol TLS rules */ + /* Stuff for bignum arithmetic and public key * cryptography - not supported yet by linux @@ -189,4 +210,7 @@ enum cryptodev_crk_op_t { #define CIOCASYNCCRYPT _IOW('c', 107, struct crypt_op) #define CIOCASYNCFETCH _IOR('c', 108, struct crypt_op) +/* additional ioctls for AEAD */ +#define CIOCAUTHCRYPT _IOWR('c', 109, struct crypt_auth_op) + #endif /* L_CRYPTODEV_H */ diff --git a/cryptodev_auth.c b/cryptodev_auth.c new file mode 100644 index 0000000..546f295 --- /dev/null +++ b/cryptodev_auth.c @@ -0,0 +1,458 @@ +/* + * Driver for /dev/crypto device (aka CryptoDev) + * + * Copyright (c) 2004 Michal Ludvig , SuSE Labs + * Copyright (c) 2009,2010 Nikos Mavrogiannopoulos + * + * This file is part of linux cryptodev. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* + * Device /dev/crypto provides an interface for + * accessing kernel CryptoAPI algorithms (ciphers, + * hashes) from userspace programs. + * + * /dev/crypto interface was originally introduced in + * OpenBSD and this module attempts to keep the API. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cryptodev_int.h" +#include "version.h" + + +/* Idea zero-copy src data + * Read source data and encrypt to temporary buffer + * then copy temporary buffer to dst. + */ + +/* make cop->src and cop->dst available in scatterlists */ +static int get_userbuf3(struct csession *ses, struct kernel_crypt_auth_op *kcaop, + struct scatterlist **auth_sg, struct scatterlist **dst_sg, + int *tot_pages) +{ + int dst_pagecount = 0, pagecount; + int auth_pagecount = 0; + struct crypt_auth_op *caop = &kcaop->caop; + int rc; + + if (caop->dst == NULL && caop->auth_src == NULL) + return -EINVAL; + + if (ses->alignmask) { + if (!IS_ALIGNED((unsigned long)caop->dst, ses->alignmask)) + dprintk(2, KERN_WARNING, "%s: careful - source address %lx is not %d byte aligned\n", + __func__, (unsigned long)caop->dst, ses->alignmask + 1); + if (!IS_ALIGNED((unsigned long)caop->auth_src, ses->alignmask)) + dprintk(2, KERN_WARNING, "%s: careful - source address %lx is not %d byte aligned\n", + __func__, (unsigned long)caop->auth_src, ses->alignmask + 1); + } + + if (kcaop->dst_len == 0) { + dprintk(1, KERN_WARNING, "Destination cannot be zero\n"); + return -EINVAL; + } + + if (caop->auth_len != 0) + auth_pagecount = PAGECOUNT(caop->auth_src, caop->auth_len); + + dst_pagecount = PAGECOUNT(caop->dst, kcaop->dst_len); + + (*tot_pages) = pagecount = auth_pagecount + dst_pagecount; + + rc = adjust_sg_array(ses, pagecount); + if (rc) + return rc; + + if (auth_pagecount > 0) { + rc = __get_userbuf(caop->auth_src, caop->auth_len, 0, auth_pagecount, + ses->pages, ses->sg, kcaop->task, kcaop->mm); + if (unlikely(rc)) { + dprintk(1, KERN_ERR, + "failed to get user pages for data input\n"); + return -EINVAL; + } + (*auth_sg) = ses->sg; + } else { + (*auth_sg) = NULL; + } + + rc = __get_userbuf(caop->dst, kcaop->dst_len, 1, dst_pagecount, + ses->pages + auth_pagecount, ses->sg, kcaop->task, kcaop->mm); + if (unlikely(rc)) { + release_user_pages(ses->pages, auth_pagecount); + dprintk(1, KERN_ERR, + "failed to get user pages for data input\n"); + return -EINVAL; + } + + (*dst_sg) = ses->sg + auth_pagecount; + + return 0; +} + + +static int fill_kcaop_from_caop(struct kernel_crypt_auth_op *kcaop, struct fcrypt *fcr) +{ + struct crypt_auth_op *caop = &kcaop->caop; + struct csession *ses_ptr; + int rc; + + /* this also enters ses_ptr->sem */ + ses_ptr = crypto_get_session_by_sid(fcr, caop->ses); + if (unlikely(!ses_ptr)) { + dprintk(1, KERN_ERR, "invalid session ID=0x%08X\n", caop->ses); + return -EINVAL; + } + + if (caop->src != caop->dst) { + dprintk(1, KERN_ERR, + "Only inplace encryption and decryption is supported\n"); + rc = -EINVAL; + goto out_unlock; + } + + if (caop->tag_len == 0) + caop->tag_len = ses_ptr->hdata.digestsize; + + kcaop->ivlen = caop->iv ? ses_ptr->cdata.ivsize : 0; + kcaop->dst_len = caop->len + ses_ptr->cdata.blocksize /* pad */ + caop->tag_len; + + kcaop->task = current; + kcaop->mm = current->mm; + + if (caop->iv) { + rc = copy_from_user(kcaop->iv, caop->iv, kcaop->ivlen); + if (unlikely(rc)) { + dprintk(1, KERN_ERR, + "error copying IV (%d bytes), copy_from_user returned %d for address %lx\n", + kcaop->ivlen, rc, (unsigned long)caop->iv); + rc = -EFAULT; + goto out_unlock; + } + } + + rc = 0; + +out_unlock: + crypto_put_session(ses_ptr); + return rc; + +} + +static int fill_caop_from_kcaop(struct kernel_crypt_auth_op *kcaop, struct fcrypt *fcr) +{ + int ret; + + kcaop->caop.len = kcaop->dst_len; + + if (kcaop->ivlen && kcaop->caop.flags & COP_FLAG_WRITE_IV) { + ret = copy_to_user(kcaop->caop.iv, + kcaop->iv, kcaop->ivlen); + if (unlikely(ret)) { + dprintk(1, KERN_ERR, "Error in copying to userspace\n"); + return -EFAULT; + } + } + return 0; +} + + +int kcaop_from_user(struct kernel_crypt_auth_op *kcaop, + struct fcrypt *fcr, void __user *arg) +{ + if (unlikely(copy_from_user(&kcaop->caop, arg, sizeof(kcaop->caop)))) { + dprintk(1, KERN_ERR, "Error in copying from userspace\n"); + return -EFAULT; + } + + return fill_kcaop_from_caop(kcaop, fcr); +} + +int kcaop_to_user(struct kernel_crypt_auth_op *kcaop, + struct fcrypt *fcr, void __user *arg) +{ + int ret; + + ret = fill_caop_from_kcaop(kcaop, fcr); + if (unlikely(ret)) { + dprintk(1, KERN_ERR, "fill_caop_from_kcaop\n"); + return ret; + } + + if (unlikely(copy_to_user(arg, &kcaop->caop, sizeof(kcaop->caop)))) { + dprintk(1, KERN_ERR, "Error in copying to userspace\n"); + return -EFAULT; + } + return 0; +} + +static void copy_hash( struct scatterlist *dst_sg, int len, void* hash, int hash_len) +{ + scatterwalk_map_and_copy(hash, dst_sg, len, hash_len, 1); +} + +static void read_hash( struct scatterlist *dst_sg, int len, void* hash, int hash_len) +{ + scatterwalk_map_and_copy(hash, dst_sg, len-hash_len, hash_len, 0); +} + +static int pad_record( struct scatterlist *dst_sg, int len, int block_size) +{ + uint8_t pad[block_size]; + int pad_size = block_size - (len % block_size); + + memset(pad, pad_size-1, pad_size); + + scatterwalk_map_and_copy(pad, dst_sg, len, pad_size, 1); + + return pad_size; +} + +static int verify_record_pad( struct scatterlist *dst_sg, int len, int block_size) +{ + uint8_t pad[256]; /* the maximum allowed */ + uint8_t pad_size; + int i; + + scatterwalk_map_and_copy(&pad_size, dst_sg, len-1, 1, 0); + pad_size++; + + if (pad_size > len) + return -ECANCELED; + + scatterwalk_map_and_copy(pad, dst_sg, len-pad_size, pad_size, 0); + + for (i=0;icaop; + uint8_t vhash[AALG_MAX_RESULT_LEN]; + uint8_t hash_output[AALG_MAX_RESULT_LEN]; + + /* Always hash before encryption and after decryption. Maybe + * we should introduce a flag to switch... TBD later on. + */ + if (caop->op == COP_ENCRYPT) { + if (ses_ptr->hdata.init != 0) { + if (auth_len > 0) { + ret = cryptodev_hash_update(&ses_ptr->hdata, + auth_sg, auth_len); + if (unlikely(ret)) { + dprintk(0, KERN_ERR, "cryptodev_hash_update: %d\n", ret); + goto out_err; + } + } + + if (len > 0) { + ret = cryptodev_hash_update(&ses_ptr->hdata, + dst_sg, len); + if (unlikely(ret)) { + dprintk(0, KERN_ERR, "cryptodev_hash_update: %d\n", ret); + goto out_err; + } + } + + ret = cryptodev_hash_final(&ses_ptr->hdata, hash_output); + if (unlikely(ret)) { + dprintk(0, KERN_ERR, "cryptodev_hash_final: %d\n", ret); + goto out_err; + } + + copy_hash( dst_sg, len, hash_output, caop->tag_len); + len += caop->tag_len; + } + + if (ses_ptr->cdata.init != 0) { + if (ses_ptr->cdata.blocksize > 1) { + ret = pad_record(dst_sg, len, ses_ptr->cdata.blocksize); + len += ret; + } + + ret = cryptodev_cipher_encrypt(&ses_ptr->cdata, + dst_sg, dst_sg, len); + if (unlikely(ret)) { + dprintk(0, KERN_ERR, "cryptodev_cipher_encrypt: %d\n", ret); + goto out_err; + } + } + } else { + if (ses_ptr->cdata.init != 0) { + ret = cryptodev_cipher_decrypt(&ses_ptr->cdata, + dst_sg, dst_sg, len); + + if (unlikely(ret)) { + dprintk(0, KERN_ERR, "cryptodev_cipher_decrypt: %d\n", ret); + goto out_err; + } + + if (ses_ptr->cdata.blocksize > 1) { + ret = verify_record_pad(dst_sg, len, ses_ptr->cdata.blocksize); + if (unlikely(ret)) { + dprintk(0, KERN_ERR, "verify_record_pad: %d\n", ret); + fail = 1; + } else + len -= ret; + } + } + + if (ses_ptr->hdata.init != 0) { + if (unlikely(caop->tag_len > sizeof(vhash) || caop->tag_len > len)) { + dprintk(1, KERN_ERR, "Illegal tag len size\n"); + ret = -EINVAL; + goto out_err; + } + + read_hash( dst_sg, len, vhash, caop->tag_len); + len -= caop->tag_len; + + if (auth_len > 0) { + ret = cryptodev_hash_update(&ses_ptr->hdata, + auth_sg, auth_len); + if (unlikely(ret)) { + dprintk(0, KERN_ERR, "cryptodev_hash_update: %d\n", ret); + goto out_err; + } + } + + if (len > 0) { + ret = cryptodev_hash_update(&ses_ptr->hdata, + dst_sg, len); + if (unlikely(ret)) { + dprintk(0, KERN_ERR, "cryptodev_hash_update: %d\n", ret); + goto out_err; + } + } + + ret = cryptodev_hash_final(&ses_ptr->hdata, hash_output); + if (unlikely(ret)) { + dprintk(0, KERN_ERR, "cryptodev_hash_final: %d\n", ret); + goto out_err; + } + + if (memcmp(vhash, hash_output, caop->tag_len) != 0 || fail != 0) { + dprintk(1, KERN_ERR, "MAC verification failed\n"); + ret = -ECANCELED; + goto out_err; + } + } + } + kcaop->dst_len = len; + return 0; +out_err: + return ret; +} + +/* This is the main crypto function - zero-copy edition */ +static int +__crypto_auth_run_zc(struct csession *ses_ptr, struct kernel_crypt_auth_op *kcaop) +{ + struct scatterlist *dst_sg, *auth_sg; + struct crypt_auth_op *caop = &kcaop->caop; + int ret = 0, pagecount; + + ret = get_userbuf3(ses_ptr, kcaop, &auth_sg, &dst_sg, &pagecount); + if (unlikely(ret)) { + dprintk(1, KERN_ERR, "Error getting user pages.\n"); + return ret; + } + + if (caop->flags & COP_FLAG_AEAD_TLS_TYPE) + ret = tls_auth_n_crypt(ses_ptr, kcaop, auth_sg, caop->auth_len, + dst_sg, caop->len); + else { + dprintk(1, KERN_ERR, "Unsupported flag for authenc\n"); + ret = -EINVAL; + } + + release_user_pages(ses_ptr->pages, pagecount); + return ret; +} + + +int crypto_auth_run(struct fcrypt *fcr, struct kernel_crypt_auth_op *kcaop) +{ + struct csession *ses_ptr; + struct crypt_auth_op *caop = &kcaop->caop; + int ret; + + if (unlikely(caop->op != COP_ENCRYPT && caop->op != COP_DECRYPT)) { + dprintk(1, KERN_DEBUG, "invalid operation op=%u\n", caop->op); + return -EINVAL; + } + + /* this also enters ses_ptr->sem */ + ses_ptr = crypto_get_session_by_sid(fcr, caop->ses); + if (unlikely(!ses_ptr)) { + dprintk(1, KERN_ERR, "invalid session ID=0x%08X\n", caop->ses); + return -EINVAL; + } + + if (unlikely(ses_ptr->cdata.init == 0)) { + dprintk(1, KERN_ERR, "cipher context not initialized\n"); + return -EINVAL; + } + + if (ses_ptr->hdata.init != 0) { + ret = cryptodev_hash_reset(&ses_ptr->hdata); + if (unlikely(ret)) { + dprintk(1, KERN_ERR, + "error in cryptodev_hash_reset()\n"); + goto out_unlock; + } + } + + cryptodev_cipher_set_iv(&ses_ptr->cdata, kcaop->iv, + min(ses_ptr->cdata.ivsize, kcaop->ivlen)); + + if (likely(caop->len || caop->auth_len)) { + ret = __crypto_auth_run_zc(ses_ptr, kcaop); + if (unlikely(ret)) + goto out_unlock; + } + + cryptodev_cipher_get_iv(&ses_ptr->cdata, kcaop->iv, + min(ses_ptr->cdata.ivsize, kcaop->ivlen)); + +out_unlock: + mutex_unlock(&ses_ptr->sem); + return ret; +} diff --git a/cryptodev_int.h b/cryptodev_int.h index 4690398..f24b389 100644 --- a/cryptodev_int.h +++ b/cryptodev_int.h @@ -2,6 +2,8 @@ #ifndef CRYPTODEV_INT_H # define CRYPTODEV_INT_H +#define CRYPTODEV_STATS + #include #include #include @@ -50,6 +52,11 @@ struct cipher_data { } async; }; +struct fcrypt { + struct list_head list; + struct mutex sem; +}; + int cryptodev_cipher_init(struct cipher_data *out, const char *alg_name, uint8_t *key, size_t keylen); void cryptodev_cipher_deinit(struct cipher_data *cdata); @@ -145,4 +152,51 @@ struct kernel_crypt_op { struct mm_struct *mm; }; +struct kernel_crypt_auth_op { + struct crypt_auth_op caop; + + int dst_len; /* based on src_len + pad + tag */ + int ivlen; + __u8 iv[EALG_MAX_BLOCK_LEN]; + + struct task_struct *task; + struct mm_struct *mm; +}; + +/* auth */ + +int kcaop_from_user(struct kernel_crypt_auth_op *kcop, + struct fcrypt *fcr, void __user *arg); +int kcaop_to_user(struct kernel_crypt_auth_op *kcaop, + struct fcrypt *fcr, void __user *arg); +int crypto_auth_run(struct fcrypt *fcr, struct kernel_crypt_auth_op *kcaop); + +/* other internal structs */ +struct csession { + struct list_head entry; + struct mutex sem; + struct cipher_data cdata; + struct hash_data hdata; + uint32_t sid; + uint32_t alignmask; +#ifdef CRYPTODEV_STATS +#if !((COP_ENCRYPT < 2) && (COP_DECRYPT < 2)) +#error Struct csession.stat uses COP_{ENCRYPT,DECRYPT} as indices. Do something! +#endif + unsigned long long stat[2]; + size_t stat_max_size, stat_count; +#endif + int array_size; + struct page **pages; + struct scatterlist *sg; +}; + +struct csession *crypto_get_session_by_sid(struct fcrypt *fcr, uint32_t sid); + +inline static void crypto_put_session(struct csession * ses_ptr) +{ + mutex_unlock(&ses_ptr->sem); +} +int adjust_sg_array(struct csession * ses, int pagecount); + #endif /* CRYPTODEV_INT_H */ diff --git a/cryptodev_main.c b/cryptodev_main.c index 092766c..cbb9ef4 100644 --- a/cryptodev_main.c +++ b/cryptodev_main.c @@ -53,8 +53,6 @@ MODULE_LICENSE("GPL"); /* ====== Compile-time config ====== */ -#define CRYPTODEV_STATS - /* Default (pre-allocated) and maximum size of the job queue. * These are free, pending and done items all together. */ #define DEF_COP_RINGSIZE 16 @@ -73,11 +71,6 @@ MODULE_PARM_DESC(enable_stats, "collect statictics about cryptodev usage"); #endif /* ====== CryptoAPI ====== */ -struct fcrypt { - struct list_head list; - struct mutex sem; -}; - struct todo_list_item { struct list_head __hook; struct kernel_crypt_op kcop; @@ -105,25 +98,6 @@ struct crypt_priv { (sg)->dma_address = 0; \ } while (0) -struct csession { - struct list_head entry; - struct mutex sem; - struct cipher_data cdata; - struct hash_data hdata; - uint32_t sid; - uint32_t alignmask; -#ifdef CRYPTODEV_STATS -#if !((COP_ENCRYPT < 2) && (COP_DECRYPT < 2)) -#error Struct csession.stat uses COP_{ENCRYPT,DECRYPT} as indices. Do something! -#endif - unsigned long long stat[2]; - size_t stat_max_size, stat_count; -#endif - int array_size; - struct page **pages; - struct scatterlist *sg; -}; - /* cryptodev's own workqueue, keeps crypto tasks from disturbing the force */ static struct workqueue_struct *cryptodev_wq; @@ -423,7 +397,7 @@ crypto_finish_all_sessions(struct fcrypt *fcr) } /* Look up session by session ID. The returned session is locked. */ -static struct csession * +struct csession * crypto_get_session_by_sid(struct fcrypt *fcr, uint32_t sid) { struct csession *ses_ptr, *retval = 0; @@ -583,6 +557,33 @@ int __get_userbuf(uint8_t __user *addr, uint32_t len, int write, return 0; } +int adjust_sg_array(struct csession * ses, int pagecount) +{ +struct scatterlist *sg; +struct page **pages; +int array_size; + + for (array_size = ses->array_size; array_size < pagecount; + array_size *= 2) + ; + + dprintk(2, KERN_DEBUG, "%s: reallocating to %d elements\n", + __func__, array_size); + pages = krealloc(ses->pages, array_size * sizeof(struct page *), + GFP_KERNEL); + if (unlikely(!pages)) + return -ENOMEM; + ses->pages = pages; + sg = krealloc(ses->sg, array_size * sizeof(struct scatterlist), + GFP_KERNEL); + if (unlikely(!sg)) + return -ENOMEM; + ses->sg = sg; + ses->array_size = array_size; + + return 0; +} + /* make cop->src and cop->dst available in scatterlists */ static int get_userbuf(struct csession *ses, struct kernel_crypt_op *kcop, struct scatterlist **src_sg, struct scatterlist **dst_sg, @@ -619,27 +620,9 @@ static int get_userbuf(struct csession *ses, struct kernel_crypt_op *kcop, (*tot_pages) = pagecount = src_pagecount + dst_pagecount; if (pagecount > ses->array_size) { - struct scatterlist *sg; - struct page **pages; - int array_size; - - for (array_size = ses->array_size; array_size < pagecount; - array_size *= 2) - ; - - dprintk(2, KERN_DEBUG, "%s: reallocating to %d elements\n", - __func__, array_size); - pages = krealloc(ses->pages, array_size * sizeof(struct page *), - GFP_KERNEL); - if (unlikely(!pages)) - return -ENOMEM; - ses->pages = pages; - sg = krealloc(ses->sg, array_size * sizeof(struct scatterlist), - GFP_KERNEL); - if (unlikely(!sg)) - return -ENOMEM; - ses->sg = sg; - ses->array_size = array_size; + rc = adjust_sg_array(ses, pagecount); + if (rc) + return rc; } rc = __get_userbuf(cop->src, cop->len, write_src, src_pagecount, @@ -769,7 +752,7 @@ static int crypto_run(struct fcrypt *fcr, struct kernel_crypt_op *kcop) #endif out_unlock: - mutex_unlock(&ses_ptr->sem); + crypto_put_session(ses_ptr); return ret; } @@ -989,7 +972,7 @@ static int fill_kcop_from_cop(struct kernel_crypt_op *kcop, struct fcrypt *fcr) kcop->ivlen = cop->iv ? ses_ptr->cdata.ivsize : 0; kcop->digestsize = 0; /* will be updated during operation */ - mutex_unlock(&ses_ptr->sem); + crypto_put_session(ses_ptr); kcop->task = current; kcop->mm = current->mm; @@ -1080,7 +1063,7 @@ static int get_session_info(struct fcrypt *fcr, struct session_info_op *siop) siop->alignmask = ses_ptr->alignmask; - mutex_unlock(&ses_ptr->sem); + crypto_put_session(ses_ptr); return 0; } @@ -1091,6 +1074,7 @@ cryptodev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg_) int __user *p = arg; struct session_op sop; struct kernel_crypt_op kcop; + struct kernel_crypt_auth_op kcaop; struct crypt_priv *pcr = filp->private_data; struct fcrypt *fcr; struct session_info_op siop; @@ -1141,14 +1125,30 @@ cryptodev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg_) return ret; return copy_to_user(arg, &siop, sizeof(siop)); case CIOCCRYPT: - if (unlikely(ret = kcop_from_user(&kcop, fcr, arg))) + if (unlikely(ret = kcop_from_user(&kcop, fcr, arg))) { + dprintk(1, KERN_WARNING, "Error copying from user"); return ret; + } ret = crypto_run(fcr, &kcop); - if (unlikely(ret)) + if (unlikely(ret)) { + dprintk(1, KERN_WARNING, "Error in crypto_run"); return ret; + } return kcop_to_user(&kcop, fcr, arg); + case CIOCAUTHCRYPT: + if (unlikely(ret = kcaop_from_user(&kcaop, fcr, arg))) { + dprintk(1, KERN_WARNING, "Error copying from user"); + return ret; + } + + ret = crypto_auth_run(fcr, &kcaop); + if (unlikely(ret)) { + dprintk(1, KERN_WARNING, "Error in crypto_auth_run"); + return ret; + } + return kcaop_to_user(&kcaop, fcr, arg); case CIOCASYNCCRYPT: if (unlikely(ret = kcop_from_user(&kcop, fcr, arg))) return ret; @@ -1241,12 +1241,16 @@ static int compat_kcop_to_user(struct kernel_crypt_op *kcop, struct compat_crypt_op compat_cop; ret = fill_cop_from_kcop(kcop, fcr); - if (unlikely(ret)) + if (unlikely(ret)) { + dprintk(1, KERN_WARNING, "Error in fill_cop_from_kcop"); return ret; + } crypt_op_to_compat(&kcop->cop, &compat_cop); - if (unlikely(copy_to_user(arg, &compat_cop, sizeof(compat_cop)))) + if (unlikely(copy_to_user(arg, &compat_cop, sizeof(compat_cop)))) { + dprintk(1, KERN_WARNING, "Error copying to user"); return -EFAULT; + } return 0; } diff --git a/examples/Makefile b/examples/Makefile index 0443242..eeb9d62 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -1,8 +1,9 @@ KERNEL_DIR ?= /lib/modules/$(shell uname -r)/build KBUILD_CFLAGS += -I.. -hostprogs := cipher hmac speed async_cipher async_hmac async_speed sha_speed hashcrypt_speed fullspeed +hostprogs := cipher cipher-aead hmac speed async_cipher async_hmac async_speed sha_speed hashcrypt_speed fullspeed example-cipher-objs := cipher.o +example-cipher-aead-objs := cipher-aead.o example-hmac-objs := hmac.o example-speed-objs := speed.c example-fullspeed-objs := fullspeed.c diff --git a/examples/cipher-aead.c b/examples/cipher-aead.c new file mode 100644 index 0000000..8b69cb1 --- /dev/null +++ b/examples/cipher-aead.c @@ -0,0 +1,271 @@ +/* + * Demo on how to use /dev/crypto device for ciphering. + * + * Placed under public domain. + * + */ +#include +#include +#include +#include +#include + +#include +#include + +#define DATA_SIZE (8*1024+11) +#define BLOCK_SIZE 16 +#define KEY_SIZE 16 + +#define MAC_SIZE 20 /* SHA1 */ + +static int +get_sha1_hmac(int cfd, void* key, int key_size, void* data, int data_size, void* mac) +{ + struct session_op sess; + struct crypt_op cryp; + int i; + + memset(&sess, 0, sizeof(sess)); + memset(&cryp, 0, sizeof(cryp)); + + sess.cipher = 0; + sess.mac = CRYPTO_SHA1_HMAC; + sess.mackeylen = key_size; + sess.mackey = key; + if (ioctl(cfd, CIOCGSESSION, &sess)) { + perror("ioctl(CIOCGSESSION)"); + return 1; + } + + /* Encrypt data.in to data.encrypted */ + cryp.ses = sess.ses; + cryp.len = data_size; + cryp.src = data; + cryp.dst = NULL; + cryp.iv = NULL; + cryp.mac = mac; + cryp.op = COP_ENCRYPT; + if (ioctl(cfd, CIOCCRYPT, &cryp)) { + perror("ioctl(CIOCCRYPT)"); + return 1; + } + + /* Finish crypto session */ + if (ioctl(cfd, CIOCFSESSION, &sess.ses)) { + perror("ioctl(CIOCFSESSION)"); + return 1; + } + + return 0; +} + +static void print_buf(char* desc, unsigned char* buf, int size) +{ +int i; + fputs(desc, stdout); + for (i=0;i