From 620ab95c435ab986fa234e16e9eef6008f69cbfd Mon Sep 17 00:00:00 2001
From: Nikos Mavrogiannopoulos <nmav@gnutls.org>
Date: Sat, 3 Dec 2011 19:19:44 +0100
Subject: [PATCH 154/291] cryptodev: AES-GCM

---
 drivers/staging/crypto/cryptodev/authenc.c         | 198 +++++++++++++++------
 drivers/staging/crypto/cryptodev/cryptlib.c        |  14 +-
 drivers/staging/crypto/cryptodev/cryptlib.h        |  90 ++++++++++
 .../staging/crypto/cryptodev/crypto/cryptodev.h    |   2 +-
 drivers/staging/crypto/cryptodev/cryptodev_int.h   |  76 +-------
 .../crypto/cryptodev/examples/cipher-aead-srtp.c   |   2 +-
 .../staging/crypto/cryptodev/examples/cipher-gcm.c | 112 ++++++------
 drivers/staging/crypto/cryptodev/ioctl.c           |  14 +-
 drivers/staging/crypto/cryptodev/main.c            |  70 +-------
 drivers/staging/crypto/cryptodev/zc.c              |  77 ++++++++
 drivers/staging/crypto/cryptodev/zc.h              |  14 ++
 11 files changed, 407 insertions(+), 262 deletions(-)
 create mode 100644 drivers/staging/crypto/cryptodev/cryptlib.h

diff --git a/drivers/staging/crypto/cryptodev/authenc.c b/drivers/staging/crypto/cryptodev/authenc.c
index dab3963cecdb..83cb1c6f81f9 100644
--- a/drivers/staging/crypto/cryptodev/authenc.c
+++ b/drivers/staging/crypto/cryptodev/authenc.c
@@ -41,6 +41,7 @@
 #include <linux/scatterlist.h>
 #include "cryptodev_int.h"
 #include "zc.h"
+#include "cryptlib.h"
 #include "version.h"
 
 
@@ -162,8 +163,10 @@ static int get_userbuf_srtp(struct csession *ses, struct kernel_crypt_auth_op *k
 	struct crypt_auth_op *caop = &kcaop->caop;
 	int rc;
 
-	if (caop->dst == NULL && caop->auth_src == NULL)
+	if (caop->dst == NULL && caop->auth_src == NULL) {
+		dprintk(1, KERN_ERR, "dst and auth_src cannot be both null\n");
 		return -EINVAL;
+	}
 
 	if (ses->alignmask) {
 		if (!IS_ALIGNED((unsigned long)caop->dst, ses->alignmask))
@@ -192,8 +195,10 @@ static int get_userbuf_srtp(struct csession *ses, struct kernel_crypt_auth_op *k
 	(*tot_pages) = pagecount = auth_pagecount;
 
 	rc = adjust_sg_array(ses, pagecount*2); /* double pages to have pages for dst(=auth_src) */
-	if (rc)
+	if (rc) {
+		dprintk(1, KERN_ERR, "cannot adjust sg array\n");
 		return rc;
+	}
 
 	rc = __get_userbuf(caop->auth_src, caop->auth_len, 1, auth_pagecount,
 			   ses->pages, ses->sg, kcaop->task, kcaop->mm);
@@ -218,6 +223,9 @@ static int get_userbuf_srtp(struct csession *ses, struct kernel_crypt_auth_op *k
 	return 0;
 }
 
+/* XXX: inefficient. We could use the getuserbuf, but don't bother
+ * for now.
+ */
 int copy_from_user_to_user( void* __user dst, void* __user src, int len)
 {
 uint8_t *buffer;
@@ -399,6 +407,9 @@ static int verify_tls_record_pad( struct scatterlist *dst_sg, int len, int block
 	return pad_size+1;
 }
 
+/* Authenticate and encrypt the TLS way (also perform padding). 
+ * During decryption it verifies the pad and tag and returns -ECANCELED on error.
+ */
 static int
 tls_auth_n_crypt(struct csession *ses_ptr, struct kernel_crypt_auth_op *kcaop,
 		struct scatterlist *auth_sg, uint32_t auth_len,
@@ -418,7 +429,7 @@ tls_auth_n_crypt(struct csession *ses_ptr, struct kernel_crypt_auth_op *kcaop,
 								auth_sg, auth_len);
 				if (unlikely(ret)) {
 					dprintk(0, KERN_ERR, "cryptodev_hash_update: %d\n", ret);
-					goto out_err;
+					return ret;
 				}
 			}
 
@@ -427,14 +438,14 @@ tls_auth_n_crypt(struct csession *ses_ptr, struct kernel_crypt_auth_op *kcaop,
 								dst_sg, len);
 				if (unlikely(ret)) {
 					dprintk(0, KERN_ERR, "cryptodev_hash_update: %d\n", ret);
-					goto out_err;
+					return ret;
 				}
 			}
 
 			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;
+				return ret;
 			}
 
 			copy_tls_hash( dst_sg, len, hash_output, caop->tag_len);
@@ -451,7 +462,7 @@ tls_auth_n_crypt(struct csession *ses_ptr, struct kernel_crypt_auth_op *kcaop,
 							dst_sg, dst_sg, len);
 			if (unlikely(ret)) {
 				dprintk(0, KERN_ERR, "cryptodev_cipher_encrypt: %d\n", ret);
-				goto out_err;
+				return ret;
 			}
 		}
 	} else {
@@ -461,13 +472,13 @@ tls_auth_n_crypt(struct csession *ses_ptr, struct kernel_crypt_auth_op *kcaop,
 
 			if (unlikely(ret)) {
 				dprintk(0, KERN_ERR, "cryptodev_cipher_decrypt: %d\n", ret);
-				goto out_err;
+				return ret;
 			}
 
 			if (ses_ptr->cdata.blocksize > 1) {
 				ret = verify_tls_record_pad(dst_sg, len, ses_ptr->cdata.blocksize);
 				if (unlikely(ret < 0)) {
-					dprintk(0, KERN_ERR, "verify_record_pad: %d\n", ret);
+					dprintk(2, KERN_ERR, "verify_record_pad: %d\n", ret);
 					fail = 1;
 				} else {
 					len -= ret;
@@ -478,8 +489,7 @@ tls_auth_n_crypt(struct csession *ses_ptr, struct kernel_crypt_auth_op *kcaop,
 		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;
+				return -EINVAL;
 			}
 
 			read_tls_hash( dst_sg, len, vhash, caop->tag_len);
@@ -490,7 +500,7 @@ tls_auth_n_crypt(struct csession *ses_ptr, struct kernel_crypt_auth_op *kcaop,
 								auth_sg, auth_len);
 				if (unlikely(ret)) {
 					dprintk(0, KERN_ERR, "cryptodev_hash_update: %d\n", ret);
-					goto out_err;
+					return ret;
 				}
 			}
 
@@ -499,29 +509,29 @@ tls_auth_n_crypt(struct csession *ses_ptr, struct kernel_crypt_auth_op *kcaop,
 									dst_sg, len);
 				if (unlikely(ret)) {
 					dprintk(0, KERN_ERR, "cryptodev_hash_update: %d\n", ret);
-					goto out_err;
+					return ret;
 				}
 			}
 
 			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;
+				return ret;
 			}
 
 			if (memcmp(vhash, hash_output, caop->tag_len) != 0 || fail != 0) {
-				dprintk(1, KERN_ERR, "MAC verification failed (tag_len: %d)\n", caop->tag_len);
-				ret = -ECANCELED;
-				goto out_err;
+				dprintk(2, KERN_ERR, "MAC verification failed (tag_len: %d)\n", caop->tag_len);
+				return -ECANCELED;
 			}
 		}
 	}
 	kcaop->dst_len = len;
 	return 0;
-out_err:
-	return ret;
 }
 
+/* Authenticate and encrypt the SRTP way. During decryption
+ * it verifies the tag and returns -ECANCELED on error.
+ */
 static int
 srtp_auth_n_crypt(struct csession *ses_ptr, struct kernel_crypt_auth_op *kcaop,
 		  struct scatterlist *auth_sg, uint32_t auth_len,
@@ -538,15 +548,14 @@ srtp_auth_n_crypt(struct csession *ses_ptr, struct kernel_crypt_auth_op *kcaop,
 		if (ses_ptr->cdata.init != 0) {
 			if (ses_ptr->cdata.stream == 0) {
 				dprintk(0, KERN_ERR, "Only stream modes are allowed in SRTP mode\n");
-				ret = -EINVAL;
-				goto out_err;
+				return -EINVAL;
 			}
 
 			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;
+				return ret;
 			}
 		}
 
@@ -556,19 +565,18 @@ srtp_auth_n_crypt(struct csession *ses_ptr, struct kernel_crypt_auth_op *kcaop,
 								auth_sg, auth_len);
 				if (unlikely(ret)) {
 					dprintk(0, KERN_ERR, "cryptodev_hash_update: %d\n", ret);
-					goto out_err;
+					return ret;
 				}
 			}
 
 			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;
+				return ret;
 			}
 
 			if (unlikely(copy_to_user(caop->tag, hash_output, caop->tag_len))) {
-				ret = -EFAULT;
-				goto out_err;
+				return -EFAULT;
 			}
 		}
 
@@ -576,40 +584,36 @@ srtp_auth_n_crypt(struct csession *ses_ptr, struct kernel_crypt_auth_op *kcaop,
 		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;
+				return -EINVAL;
 			}
 
 			if (unlikely(copy_from_user(vhash, caop->tag, caop->tag_len))) {
-				ret = -EFAULT;
-				goto out_err;
+				return -EFAULT;
 			}
 
 			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;
+				return ret;
 			}
 
 			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;
+				return ret;
 			}
 
 			if (memcmp(vhash, hash_output, caop->tag_len) != 0 || fail != 0) {
-				dprintk(1, KERN_ERR, "MAC verification failed\n");
-				ret = -ECANCELED;
-				goto out_err;
+				dprintk(2, KERN_ERR, "MAC verification failed\n");
+				return -ECANCELED;
 			}
 		}
 
 		if (ses_ptr->cdata.init != 0) {
 			if (ses_ptr->cdata.stream == 0) {
 				dprintk(0, KERN_ERR, "Only stream modes are allowed in SRTP mode\n");
-				ret = -EINVAL;
-				goto out_err;
+				return -EINVAL;
 			}
 
 			ret = cryptodev_cipher_decrypt(&ses_ptr->cdata,
@@ -617,53 +621,118 @@ srtp_auth_n_crypt(struct csession *ses_ptr, struct kernel_crypt_auth_op *kcaop,
 
 			if (unlikely(ret)) {
 				dprintk(0, KERN_ERR, "cryptodev_cipher_decrypt: %d\n", ret);
-				goto out_err;
+				return ret;
 			}
 		}
 
 	}
 	kcaop->dst_len = len;
 	return 0;
-out_err:
-	return ret;
+}
+
+/* Typical AEAD (i.e. GCM) encryption/decryption.
+ * During decryption the tag is verified.
+ */
+static int
+auth_n_crypt(struct csession *ses_ptr, struct kernel_crypt_auth_op *kcaop,
+		  struct scatterlist *auth_sg, uint32_t auth_len,
+		  struct scatterlist *src_sg,
+		  struct scatterlist *dst_sg, uint32_t len)
+{
+	int ret;
+	struct crypt_auth_op *caop = &kcaop->caop;
+	int max_tag_len;
+
+
+	if (unlikely(ses_ptr->cdata.init == 0))
+		return -EINVAL;
+
+	if (unlikely(ses_ptr->cdata.stream == 0 || ses_ptr->cdata.aead == 0)) {
+		dprintk(0, KERN_ERR, "Only stream and AEAD ciphers are allowed for authenc\n");
+		return -EINVAL;
+	}
+
+	max_tag_len = cryptodev_cipher_get_tag_size(&ses_ptr->cdata);
+	if (unlikely(caop->tag_len > max_tag_len)) {
+		dprintk(0, KERN_ERR, "Illegal tag length: %d\n", caop->tag_len);
+		return -EINVAL;
+	}
+	
+	if (caop->tag_len)
+		cryptodev_cipher_set_tag_size(&ses_ptr->cdata, caop->tag_len);
+	else
+		caop->tag_len = max_tag_len;
+
+	if (caop->op == COP_ENCRYPT) {
+		if (auth_len > 0)
+			cryptodev_cipher_auth(&ses_ptr->cdata, auth_sg, auth_len);
+		else /* for some reason we _have_ to call that */
+			cryptodev_cipher_auth(&ses_ptr->cdata, NULL, 0);
+
+		ret = cryptodev_cipher_encrypt(&ses_ptr->cdata,
+						src_sg, dst_sg, len);
+		if (unlikely(ret)) {
+			dprintk(0, KERN_ERR, "cryptodev_cipher_encrypt: %d\n", ret);
+			return ret;
+		}
+		kcaop->dst_len = len + caop->tag_len;
+		caop->tag = caop->dst + len;
+	} else {
+		if (auth_len > 0)
+			cryptodev_cipher_auth(&ses_ptr->cdata, auth_sg, auth_len);
+
+		ret = cryptodev_cipher_decrypt(&ses_ptr->cdata,
+						src_sg, dst_sg, len);
+
+		if (unlikely(ret)) {
+			dprintk(0, KERN_ERR, "cryptodev_cipher_decrypt: %d\n", ret);
+			return ret;
+		}
+		kcaop->dst_len = len - caop->tag_len;
+		caop->tag = caop->dst + len - caop->tag_len;
+	}
+
+	return 0;
 }
 
 /* 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 scatterlist *dst_sg, *auth_sg, *src_sg;
 	struct crypt_auth_op *caop = &kcaop->caop;
 	int ret = 0, pagecount = 0;
 
 	if (caop->flags & COP_FLAG_AEAD_SRTP_TYPE) {
 		ret = get_userbuf_srtp(ses_ptr, kcaop, &auth_sg, &dst_sg, &pagecount);
 		if (unlikely(ret)) {
-			dprintk(1, KERN_ERR, "Error getting user pages.\n");
+			dprintk(1, KERN_ERR, "get_userbuf_srtp(): Error getting user pages.\n");
 			return ret;
 		}
 
 		ret = srtp_auth_n_crypt(ses_ptr, kcaop, auth_sg, caop->auth_len, 
 			   dst_sg, caop->len);
-	} else {
-		unsigned char* buf;
+	} else { /* TLS and normal cases. Here auth data are usually small
+	          * so we just copy them to a free page, instead of trying
+	          * to map them.
+	          */
+		unsigned char* auth_buf = NULL;
 		struct scatterlist tmp;
-		
+
 		if (unlikely(caop->auth_len > PAGE_SIZE))
 			return -EINVAL;
 
-		buf = (char *)__get_free_page(GFP_KERNEL);
-
-		if (unlikely(!buf))
+		auth_buf = (char *)__get_free_page(GFP_KERNEL);
+		if (unlikely(!auth_buf))
 			return -ENOMEM;
 
 		if (caop->auth_len > 0) {
-			if (unlikely(copy_from_user(buf, caop->auth_src, caop->auth_len))) {
+			if (unlikely(copy_from_user(auth_buf, caop->auth_src, caop->auth_len))) {
 				ret = -EFAULT;
 				goto fail;
 			}
 
-			sg_init_one(&tmp, buf, caop->auth_len);
+			sg_init_one(&tmp, auth_buf, caop->auth_len);
 			auth_sg = &tmp;
 		} else {
 			auth_sg = NULL;
@@ -672,19 +741,31 @@ __crypto_auth_run_zc(struct csession *ses_ptr, struct kernel_crypt_auth_op *kcao
 		if (caop->flags & COP_FLAG_AEAD_TLS_TYPE) {
 			ret = get_userbuf_tls(ses_ptr, kcaop, &dst_sg, &pagecount);
 			if (unlikely(ret)) {
-				dprintk(1, KERN_ERR, "Error getting user pages.\n");
+				dprintk(1, KERN_ERR, "get_userbuf_tls(): Error getting user pages.\n");
 				goto fail;
 			}
 
 			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");
-			return -EINVAL;
+			int dst_len;
+			
+			if (caop->op == COP_ENCRYPT) dst_len = caop->len + cryptodev_cipher_get_tag_size(&ses_ptr->cdata);
+			else dst_len = caop->len - cryptodev_cipher_get_tag_size(&ses_ptr->cdata);
+			
+			ret = get_userbuf(ses_ptr, caop->src, caop->len, caop->dst, dst_len,
+					  kcaop->task, kcaop->mm, &src_sg, &dst_sg, &pagecount);
+			if (unlikely(ret)) {
+				dprintk(1, KERN_ERR, "get_userbuf(): Error getting user pages.\n");
+				goto fail;
+			}
+
+			ret = auth_n_crypt(ses_ptr, kcaop, auth_sg, caop->auth_len, 
+					   src_sg, dst_sg, caop->len);
 		}
 
 fail:
-		free_page((unsigned long)buf);
+		free_page((unsigned long)auth_buf);
 	}
 
 	release_user_pages(ses_ptr->pages, pagecount);
@@ -712,9 +793,11 @@ int crypto_auth_run(struct fcrypt *fcr, struct kernel_crypt_auth_op *kcaop)
 
 	if (unlikely(ses_ptr->cdata.init == 0)) {
 		dprintk(1, KERN_ERR, "cipher context not initialized\n");
-		return -EINVAL;
+		ret = -EINVAL;
+		goto out_unlock;
 	}
 
+	/* If we have a hash/mac handle reset its state */
 	if (ses_ptr->hdata.init != 0) {
 		ret = cryptodev_hash_reset(&ses_ptr->hdata);
 		if (unlikely(ret)) {
@@ -731,12 +814,17 @@ int crypto_auth_run(struct fcrypt *fcr, struct kernel_crypt_auth_op *kcaop)
 		ret = __crypto_auth_run_zc(ses_ptr, kcaop);
 		if (unlikely(ret))
 			goto out_unlock;
+	} else {
+		ret = -EINVAL;
+		goto out_unlock;
 	}
 
+	ret = 0;
+
 	cryptodev_cipher_get_iv(&ses_ptr->cdata, kcaop->iv,
 				min(ses_ptr->cdata.ivsize, kcaop->ivlen));
 
 out_unlock:
-	mutex_unlock(&ses_ptr->sem);
+	crypto_put_session(ses_ptr);
 	return ret;
 }
diff --git a/drivers/staging/crypto/cryptodev/cryptlib.c b/drivers/staging/crypto/cryptodev/cryptlib.c
index 73c75cb69c4b..4bd073ab88c5 100644
--- a/drivers/staging/crypto/cryptodev/cryptlib.c
+++ b/drivers/staging/crypto/cryptodev/cryptlib.c
@@ -179,7 +179,7 @@ void cryptodev_cipher_deinit(struct cipher_data *cdata)
 		} else {
 			if (cdata->async.arequest)
 				aead_request_free(cdata->async.arequest);
-			if (cdata->async.s)
+			if (cdata->async.as)
 				crypto_free_aead(cdata->async.as);
 		}
 
@@ -217,7 +217,7 @@ static inline int waitfor(struct cryptodev_result *cr, ssize_t ret)
 }
 
 ssize_t cryptodev_cipher_encrypt(struct cipher_data *cdata,
-		const struct scatterlist *sg1, struct scatterlist *sg2,
+		const struct scatterlist *src, struct scatterlist *dst,
 		size_t len)
 {
 	int ret;
@@ -226,12 +226,12 @@ ssize_t cryptodev_cipher_encrypt(struct cipher_data *cdata,
 	
 	if (cdata->aead == 0) {
 		ablkcipher_request_set_crypt(cdata->async.request,
-			(struct scatterlist *)sg1, sg2,
+			(struct scatterlist *)src, dst,
 			len, cdata->async.iv);
 		ret = crypto_ablkcipher_encrypt(cdata->async.request);
 	} else {
 		aead_request_set_crypt(cdata->async.arequest,
-			(struct scatterlist *)sg1, sg2,
+			(struct scatterlist *)src, dst,
 			len, cdata->async.iv);
 		ret = crypto_aead_encrypt(cdata->async.arequest);
 	}
@@ -240,7 +240,7 @@ ssize_t cryptodev_cipher_encrypt(struct cipher_data *cdata,
 }
 
 ssize_t cryptodev_cipher_decrypt(struct cipher_data *cdata,
-		const struct scatterlist *sg1, struct scatterlist *sg2,
+		const struct scatterlist *src, struct scatterlist *dst,
 		size_t len)
 {
 	int ret;
@@ -248,12 +248,12 @@ ssize_t cryptodev_cipher_decrypt(struct cipher_data *cdata,
 	INIT_COMPLETION(cdata->async.result->completion);
 	if (cdata->aead == 0) {
 		ablkcipher_request_set_crypt(cdata->async.request,
-			(struct scatterlist *)sg1, sg2,
+			(struct scatterlist *)src, dst,
 			len, cdata->async.iv);
 		ret = crypto_ablkcipher_decrypt(cdata->async.request);
 	} else {
 		aead_request_set_crypt(cdata->async.arequest,
-			(struct scatterlist *)sg1, sg2,
+			(struct scatterlist *)src, dst,
 			len, cdata->async.iv);
 		ret = crypto_aead_decrypt(cdata->async.arequest);
 	}
diff --git a/drivers/staging/crypto/cryptodev/cryptlib.h b/drivers/staging/crypto/cryptodev/cryptlib.h
new file mode 100644
index 000000000000..6bec659ea12e
--- /dev/null
+++ b/drivers/staging/crypto/cryptodev/cryptlib.h
@@ -0,0 +1,90 @@
+#ifndef CRYPTLIB_H
+# define CRYPTLIB_H
+
+struct cipher_data {
+	int init; /* 0 uninitialized */
+	int blocksize;
+	int aead;
+	int stream;
+	int ivsize;
+	int alignmask;
+	struct {
+		/* block ciphers */
+		struct crypto_ablkcipher *s;
+		struct ablkcipher_request *request;
+
+		/* AEAD ciphers */
+		struct crypto_aead *as;
+		struct aead_request *arequest;
+
+		struct cryptodev_result *result;
+		uint8_t iv[EALG_MAX_BLOCK_LEN];
+	} async;
+};
+
+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);
+ssize_t cryptodev_cipher_decrypt(struct cipher_data *cdata,
+			const struct scatterlist *sg1,
+			struct scatterlist *sg2, size_t len);
+ssize_t cryptodev_cipher_encrypt(struct cipher_data *cdata,
+				const struct scatterlist *sg1,
+				struct scatterlist *sg2, size_t len);
+
+/* AEAD */
+inline static void cryptodev_cipher_auth(struct cipher_data *cdata,
+	  				 const struct scatterlist *sg1, size_t len)
+{
+	aead_request_set_assoc(cdata->async.arequest,
+			(struct scatterlist *)sg1, len);
+}
+
+inline static void cryptodev_cipher_set_tag_size(struct cipher_data *cdata, int size)
+{
+	if (likely(cdata->aead != 0))
+		crypto_aead_setauthsize(cdata->async.as, size);
+}
+
+inline static int cryptodev_cipher_get_tag_size(struct cipher_data *cdata)
+{
+	if (likely(cdata->init && cdata->aead != 0))
+		return crypto_aead_authsize(cdata->async.as);
+	else
+		return 0;
+}
+
+inline static void cryptodev_cipher_set_iv(struct cipher_data *cdata,
+				void *iv, size_t iv_size)
+{
+	memcpy(cdata->async.iv, iv, min(iv_size, sizeof(cdata->async.iv)));
+}
+
+inline static void cryptodev_cipher_get_iv(struct cipher_data *cdata,
+				void *iv, size_t iv_size)
+{
+	memcpy(iv, cdata->async.iv, min(iv_size, sizeof(cdata->async.iv)));
+}
+
+/* Hash */
+struct hash_data {
+	int init; /* 0 uninitialized */
+	int digestsize;
+	int alignmask;
+	struct {
+		struct crypto_ahash *s;
+		struct cryptodev_result *result;
+		struct ahash_request *request;
+	} async;
+};
+
+int cryptodev_hash_final(struct hash_data *hdata, void *output);
+ssize_t cryptodev_hash_update(struct hash_data *hdata,
+			struct scatterlist *sg, size_t len);
+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);
+
+
+#endif
diff --git a/drivers/staging/crypto/cryptodev/crypto/cryptodev.h b/drivers/staging/crypto/cryptodev/crypto/cryptodev.h
index 408c2a517f5b..5f46e4721d89 100644
--- a/drivers/staging/crypto/cryptodev/crypto/cryptodev.h
+++ b/drivers/staging/crypto/cryptodev/crypto/cryptodev.h
@@ -125,7 +125,6 @@ struct crypt_auth_op {
 	__u16	flags;		/* see COP_FLAG_AEAD_* */
 	__u32	len;		/* length of source data */
 	__u32	auth_len;	/* length of auth data */
-	__u32	tag_len;	/* the length of the tag. Use zero for digest size. */
 	__u8	__user *auth_src;	/* authenticated-only data */
 
 	/* The current implementation is more efficient if data are
@@ -138,6 +137,7 @@ struct crypt_auth_op {
 	__u8    __user *tag;    /* where the tag will be copied to. TLS mode
                                  * doesn't use that as tag is copied to dst.
                                  * SRTP mode copies tag there. */
+	__u32	tag_len;	/* the length of the tag. Use zero for digest size or max tag. */
 
 	/* initialization vector for encryption operations */
 	__u8	__user *iv;
diff --git a/drivers/staging/crypto/cryptodev/cryptodev_int.h b/drivers/staging/crypto/cryptodev/cryptodev_int.h
index e0523db14cd5..93c436946a21 100644
--- a/drivers/staging/crypto/cryptodev/cryptodev_int.h
+++ b/drivers/staging/crypto/cryptodev/cryptodev_int.h
@@ -25,86 +25,12 @@
 
 extern int cryptodev_verbosity;
 
-struct cipher_data {
-	int init; /* 0 uninitialized */
-	int blocksize;
-	int aead;
-	int stream;
-	int ivsize;
-	int alignmask;
-	struct {
-		struct crypto_ablkcipher *s;
-		struct ablkcipher_request *request;
-
-		struct crypto_aead *as;
-		struct aead_request *arequest;
-
-		struct cryptodev_result *result;
-		uint8_t iv[EALG_MAX_BLOCK_LEN];
-	} 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, int stream, int aead);
-void cryptodev_cipher_deinit(struct cipher_data *cdata);
-ssize_t cryptodev_cipher_decrypt(struct cipher_data *cdata,
-			const struct scatterlist *sg1,
-			struct scatterlist *sg2, size_t len);
-ssize_t cryptodev_cipher_encrypt(struct cipher_data *cdata,
-				const struct scatterlist *sg1,
-				struct scatterlist *sg2, size_t len);
-
-/* AEAD */
-inline static void cryptodev_cipher_auth(struct cipher_data *cdata,
-	  				 const struct scatterlist *sg1, size_t len)
-{
-	aead_request_set_assoc(cdata->async.arequest,
-			(struct scatterlist *)sg1, len);
-}
-
-inline static void cryptodev_cipher_set_tag_size(struct cipher_data *cdata, int size)
-{
-	if (cdata->aead != 0)
-		crypto_aead_setauthsize(cdata->async.as, size);
-}
-
-inline static void cryptodev_cipher_set_iv(struct cipher_data *cdata,
-				void *iv, size_t iv_size)
-{
-	memcpy(cdata->async.iv, iv, min(iv_size, sizeof(cdata->async.iv)));
-}
-
-inline static void cryptodev_cipher_get_iv(struct cipher_data *cdata,
-				void *iv, size_t iv_size)
-{
-	memcpy(iv, cdata->async.iv, min(iv_size, sizeof(cdata->async.iv)));
-}
-
-/* hash stuff */
-struct hash_data {
-	int init; /* 0 uninitialized */
-	int digestsize;
-	int alignmask;
-	struct {
-		struct crypto_ahash *s;
-		struct cryptodev_result *result;
-		struct ahash_request *request;
-	} async;
-};
-
-int cryptodev_hash_final(struct hash_data *hdata, void *output);
-ssize_t cryptodev_hash_update(struct hash_data *hdata,
-			struct scatterlist *sg, size_t len);
-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);
-
 /* compatibility stuff */
 #ifdef CONFIG_COMPAT
 #include <linux/compat.h>
@@ -178,6 +104,8 @@ int kcaop_to_user(struct kernel_crypt_auth_op *kcaop,
 int crypto_auth_run(struct fcrypt *fcr, struct kernel_crypt_auth_op *kcaop);
 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/drivers/staging/crypto/cryptodev/examples/cipher-aead-srtp.c b/drivers/staging/crypto/cryptodev/examples/cipher-aead-srtp.c
index c940d7a5858a..d15fc3ee4cd8 100644
--- a/drivers/staging/crypto/cryptodev/examples/cipher-aead-srtp.c
+++ b/drivers/staging/crypto/cryptodev/examples/cipher-aead-srtp.c
@@ -14,7 +14,7 @@
 #include <crypto/cryptodev.h>
 
 #define	DATA_SIZE	(8*1024)
-#define HEADER_SIZE 1020
+#define HEADER_SIZE 193
 #define PLAINTEXT_SIZE 1021
 #define FOOTER_SIZE 15
 #define	BLOCK_SIZE	16
diff --git a/drivers/staging/crypto/cryptodev/examples/cipher-gcm.c b/drivers/staging/crypto/cryptodev/examples/cipher-gcm.c
index 064341666d4c..9b5f4f947075 100644
--- a/drivers/staging/crypto/cryptodev/examples/cipher-gcm.c
+++ b/drivers/staging/crypto/cryptodev/examples/cipher-gcm.c
@@ -18,6 +18,7 @@
 #define	BLOCK_SIZE	16
 #define	KEY_SIZE	16
 
+#define my_perror(x) {fprintf(stderr, "%s: %d\n", __func__, __LINE__); perror(x); }
 
 static void print_buf(char *desc, const unsigned char *buf, int size)
 {
@@ -46,25 +47,28 @@ struct aes_gcm_vectors_st aes_gcm_vectors[] = {
 	 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
 	 .auth = NULL,
 	 .auth_size = 0,
-	 .plaintext = NULL,
-	 .plaintext_size = 0,
-	 .ciphertext = NULL,
+	 .plaintext =
+	 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	 .plaintext_size = 16,
+	 .ciphertext =
+	 "\x03\x88\xda\xce\x60\xb6\xa3\x92\xf3\x28\xc2\xb9\x71\xb2\xfe\x78",
 	 .iv = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
 	 .tag =
-	 "\x58\xe2\xfc\xce\xfa\x7e\x30\x61\x36\x7f\x1d\x57\xa4\xe7\x45\x5a"},
+	 "\xab\x6e\x47\xd4\x2c\xec\x13\xbd\xf5\x3a\x67\xb2\x12\x57\xbd\xdf"
+	},
 	{
 	 .key =
-	 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	 "\xfe\xff\xe9\x92\x86\x65\x73\x1c\x6d\x6a\x8f\x94\x67\x30\x83\x08",
 	 .auth = NULL,
 	 .auth_size = 0,
 	 .plaintext =
-	 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
-	 .plaintext_size = 16,
+	 "\xd9\x31\x32\x25\xf8\x84\x06\xe5\xa5\x59\x09\xc5\xaf\xf5\x26\x9a\x86\xa7\xa9\x53\x15\x34\xf7\xda\x2e\x4c\x30\x3d\x8a\x31\x8a\x72\x1c\x3c\x0c\x95\x95\x68\x09\x53\x2f\xcf\x0e\x24\x49\xa6\xb5\x25\xb1\x6a\xed\xf5\xaa\x0d\xe6\x57\xba\x63\x7b\x39\x1a\xaf\xd2\x55",
+	 .plaintext_size = 64,
 	 .ciphertext =
-	 "\x03\x88\xda\xce\x60\xb6\xa3\x92\xf3\x28\xc2\xb9\x71\xb2\xfe\x78",
-	 .iv = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
-	 .tag =
-	 "\xab\x6e\x47\xd4\x2c\xec\x13\xbd\xf5\x3a\x67\xb2\x12\x57\xbd\xdf"},
+	 "\x42\x83\x1e\xc2\x21\x77\x74\x24\x4b\x72\x21\xb7\x84\xd0\xd4\x9c\xe3\xaa\x21\x2f\x2c\x02\xa4\xe0\x35\xc1\x7e\x23\x29\xac\xa1\x2e\x21\xd5\x14\xb2\x54\x66\x93\x1c\x7d\x8f\x6a\x5a\xac\x84\xaa\x05\x1b\xa3\x0b\x39\x6a\x0a\xac\x97\x3d\x58\xe0\x91\x47\x3f\x59\x85",
+	 .iv = "\xca\xfe\xba\xbe\xfa\xce\xdb\xad\xde\xca\xf8\x88",
+	 .tag = "\x4d\x5c\x2a\xf3\x27\xcd\x64\xa6\x2c\xf3\x5a\xbd\x2b\xa6\xfa\xb4"
+	},
 	{
 	 .key =
 	 "\xfe\xff\xe9\x92\x86\x65\x73\x1c\x6d\x6a\x8f\x94\x67\x30\x83\x08",
@@ -78,7 +82,8 @@ struct aes_gcm_vectors_st aes_gcm_vectors[] = {
 	 "\x42\x83\x1e\xc2\x21\x77\x74\x24\x4b\x72\x21\xb7\x84\xd0\xd4\x9c\xe3\xaa\x21\x2f\x2c\x02\xa4\xe0\x35\xc1\x7e\x23\x29\xac\xa1\x2e\x21\xd5\x14\xb2\x54\x66\x93\x1c\x7d\x8f\x6a\x5a\xac\x84\xaa\x05\x1b\xa3\x0b\x39\x6a\x0a\xac\x97\x3d\x58\xe0\x91",
 	 .iv = "\xca\xfe\xba\xbe\xfa\xce\xdb\xad\xde\xca\xf8\x88",
 	 .tag =
-	 "\x5b\xc9\x4f\xbc\x32\x21\xa5\xdb\x94\xfa\xe9\x5a\xe7\x12\x1a\x47"}
+	 "\x5b\xc9\x4f\xbc\x32\x21\xa5\xdb\x94\xfa\xe9\x5a\xe7\x12\x1a\x47"
+	}
 };
 
 
@@ -99,23 +104,7 @@ static int test_crypto(int cfd)
 
 	/* Get crypto session for AES128 */
 
-	if (ioctl(cfd, CIOCGSESSION, &sess)) {
-		perror("ioctl(CIOCGSESSION)");
-		return 1;
-	}
-#ifdef CIOCGSESSINFO
-	siop.ses = sess.ses;
-	if (ioctl(cfd, CIOCGSESSINFO, &siop)) {
-		perror("ioctl(CIOCGSESSINFO)");
-		return 1;
-	}
-	printf
-	    ("requested cipher CRYPTO_AES_CBC/HMAC-SHA1, got %s with driver %s\n",
-	     siop.cipher_info.cra_name, siop.cipher_info.cra_driver_name);
-#endif
-
-
-	fprintf(stdout, "Tests on AES-GCM: ");
+	fprintf(stdout, "Tests on AES-GCM vectors: ");
 	fflush(stdout);
 	for (i = 0;
 	     i < sizeof(aes_gcm_vectors) / sizeof(aes_gcm_vectors[0]);
@@ -127,6 +116,11 @@ static int test_crypto(int cfd)
 		sess.keylen = 16;
 		sess.key = (void *) aes_gcm_vectors[i].key;
 
+		if (ioctl(cfd, CIOCGSESSION, &sess)) {
+			my_perror("ioctl(CIOCGSESSION)");
+			return 1;
+		}
+
 		memset(&cao, 0, sizeof(cao));
 
 		cao.ses = sess.ses;
@@ -147,7 +141,7 @@ static int test_crypto(int cfd)
 		}
 
 		if (ioctl(cfd, CIOCAUTHCRYPT, &cao)) {
-			perror("ioctl(CIOCAUTHCRYPT)");
+			my_perror("ioctl(CIOCAUTHCRYPT)");
 			return 1;
 		}
 
@@ -183,7 +177,7 @@ static int test_crypto(int cfd)
 
 	/* Finish crypto session */
 	if (ioctl(cfd, CIOCFSESSION, &sess.ses)) {
-		perror("ioctl(CIOCFSESSION)");
+		my_perror("ioctl(CIOCFSESSION)");
 		return 1;
 	}
 
@@ -209,6 +203,9 @@ static int test_encrypt_decrypt(int cfd)
 	struct session_info_op siop;
 #endif
 
+	fprintf(stdout, "Tests on AES-GCM encryption/decryption: ");
+	fflush(stdout);
+
 	memset(&sess, 0, sizeof(sess));
 	memset(&cao, 0, sizeof(cao));
 
@@ -222,14 +219,14 @@ static int test_encrypt_decrypt(int cfd)
 	sess.key = key;
 
 	if (ioctl(cfd, CIOCGSESSION, &sess)) {
-		perror("ioctl(CIOCGSESSION)");
+		my_perror("ioctl(CIOCGSESSION)");
 		return 1;
 	}
 
 #ifdef CIOCGSESSINFO
 	siop.ses = sess.ses;
 	if (ioctl(cfd, CIOCGSESSINFO, &siop)) {
-		perror("ioctl(CIOCGSESSINFO)");
+		my_perror("ioctl(CIOCGSESSINFO)");
 		return 1;
 	}
 //      printf("requested cipher CRYPTO_AES_CBC/HMAC-SHA1, got %s with driver %s\n",
@@ -255,11 +252,12 @@ static int test_encrypt_decrypt(int cfd)
 	cao.src = plaintext;
 	cao.dst = ciphertext;
 	cao.iv = iv;
+	cao.iv_len = 12;
 	cao.op = COP_ENCRYPT;
 	cao.flags = 0;
 
 	if (ioctl(cfd, CIOCAUTHCRYPT, &cao)) {
-		perror("ioctl(CIOCAUTHCRYPT)");
+		my_perror("ioctl(CIOCAUTHCRYPT)");
 		return 1;
 	}
 
@@ -267,7 +265,7 @@ static int test_encrypt_decrypt(int cfd)
 	//printf("Original plaintext size: %d, ciphertext: %d\n", DATA_SIZE, enc_len);
 
 	if (ioctl(cfd, CIOCFSESSION, &sess.ses)) {
-		perror("ioctl(CIOCFSESSION)");
+		my_perror("ioctl(CIOCFSESSION)");
 		return 1;
 	}
 
@@ -278,7 +276,7 @@ static int test_encrypt_decrypt(int cfd)
 	sess.key = key;
 
 	if (ioctl(cfd, CIOCGSESSION, &sess)) {
-		perror("ioctl(CIOCGSESSION)");
+		my_perror("ioctl(CIOCGSESSION)");
 		return 1;
 	}
 
@@ -290,10 +288,12 @@ static int test_encrypt_decrypt(int cfd)
 	cao.src = ciphertext;
 	cao.dst = ciphertext;
 	cao.iv = iv;
+	cao.iv_len = 12;
 	cao.op = COP_DECRYPT;
 	cao.flags = 0;
+
 	if (ioctl(cfd, CIOCAUTHCRYPT, &cao)) {
-		perror("ioctl(CIOCAUTHCRYPT)");
+		my_perror("ioctl(CIOCAUTHCRYPT)");
 		return 1;
 	}
 
@@ -323,15 +323,15 @@ static int test_encrypt_decrypt(int cfd)
 		return 1;
 	}
 
-	printf("Test passed\n");
-
-
 	/* Finish crypto session */
 	if (ioctl(cfd, CIOCFSESSION, &sess.ses)) {
-		perror("ioctl(CIOCFSESSION)");
+		my_perror("ioctl(CIOCFSESSION)");
 		return 1;
 	}
 
+	fprintf(stdout, "ok\n");
+	fprintf(stdout, "\n");
+
 	return 0;
 }
 
@@ -351,6 +351,9 @@ static int test_encrypt_decrypt_error(int cfd, int err)
 	struct session_info_op siop;
 #endif
 
+	fprintf(stdout, "Tests on AES-GCM tag verification: ");
+	fflush(stdout);
+
 	memset(&sess, 0, sizeof(sess));
 	memset(&cao, 0, sizeof(cao));
 	memset(&co, 0, sizeof(co));
@@ -371,13 +374,13 @@ static int test_encrypt_decrypt_error(int cfd, int err)
 	    "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b";
 
 	if (ioctl(cfd, CIOCGSESSION, &sess)) {
-		perror("ioctl(CIOCGSESSION)");
+		my_perror("ioctl(CIOCGSESSION)");
 		return 1;
 	}
 #ifdef CIOCGSESSINFO
 	siop.ses = sess.ses;
 	if (ioctl(cfd, CIOCGSESSINFO, &siop)) {
-		perror("ioctl(CIOCGSESSINFO)");
+		my_perror("ioctl(CIOCGSESSINFO)");
 		return 1;
 	}
 //      printf("requested cipher CRYPTO_AES_CBC/HMAC-SHA1, got %s with driver %s\n",
@@ -407,7 +410,7 @@ static int test_encrypt_decrypt_error(int cfd, int err)
 	cao.flags = COP_FLAG_AEAD_TLS_TYPE;
 
 	if (ioctl(cfd, CIOCAUTHCRYPT, &cao)) {
-		perror("ioctl(CIOCAUTHCRYPT)");
+		my_perror("ioctl(CIOCAUTHCRYPT)");
 		return 1;
 	}
 
@@ -415,7 +418,7 @@ static int test_encrypt_decrypt_error(int cfd, int err)
 	//printf("Original plaintext size: %d, ciphertext: %d\n", DATA_SIZE, enc_len);
 
 	if (ioctl(cfd, CIOCFSESSION, &sess.ses)) {
-		perror("ioctl(CIOCFSESSION)");
+		my_perror("ioctl(CIOCFSESSION)");
 		return 1;
 	}
 
@@ -431,7 +434,7 @@ static int test_encrypt_decrypt_error(int cfd, int err)
 	    "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b";
 
 	if (ioctl(cfd, CIOCGSESSION, &sess)) {
-		perror("ioctl(CIOCGSESSION)");
+		my_perror("ioctl(CIOCGSESSION)");
 		return 1;
 	}
 
@@ -452,17 +455,18 @@ static int test_encrypt_decrypt_error(int cfd, int err)
 	cao.flags = COP_FLAG_AEAD_TLS_TYPE;
 	if (ioctl(cfd, CIOCAUTHCRYPT, &cao)) {
 		if (ioctl(cfd, CIOCFSESSION, &sess.ses)) {
-			perror("ioctl(CIOCFSESSION)");
+			my_perror("ioctl(CIOCFSESSION)");
 			return 1;
 		}
 
-		printf("Test passed\n");
+		fprintf(stdout, "ok\n");
+		fprintf(stdout, "\n");
 		return 0;
 	}
 
 	/* Finish crypto session */
 	if (ioctl(cfd, CIOCFSESSION, &sess.ses)) {
-		perror("ioctl(CIOCFSESSION)");
+		my_perror("ioctl(CIOCFSESSION)");
 		return 1;
 	}
 
@@ -478,19 +482,19 @@ int main()
 	/* Open the crypto device */
 	fd = open("/dev/crypto", O_RDWR, 0);
 	if (fd < 0) {
-		perror("open(/dev/crypto)");
+		my_perror("open(/dev/crypto)");
 		return 1;
 	}
 
 	/* Clone file descriptor */
 	if (ioctl(fd, CRIOGET, &cfd)) {
-		perror("ioctl(CRIOGET)");
+		my_perror("ioctl(CRIOGET)");
 		return 1;
 	}
 
 	/* Set close-on-exec (not really neede here) */
 	if (fcntl(cfd, F_SETFD, 1) == -1) {
-		perror("fcntl(F_SETFD)");
+		my_perror("fcntl(F_SETFD)");
 		return 1;
 	}
 
@@ -510,13 +514,13 @@ int main()
 
 	/* Close cloned descriptor */
 	if (close(cfd)) {
-		perror("close(cfd)");
+		my_perror("close(cfd)");
 		return 1;
 	}
 
 	/* Close the original descriptor */
 	if (close(fd)) {
-		perror("close(fd)");
+		my_perror("close(fd)");
 		return 1;
 	}
 
diff --git a/drivers/staging/crypto/cryptodev/ioctl.c b/drivers/staging/crypto/cryptodev/ioctl.c
index 67f797b5a909..5b8169dea106 100644
--- a/drivers/staging/crypto/cryptodev/ioctl.c
+++ b/drivers/staging/crypto/cryptodev/ioctl.c
@@ -2,7 +2,8 @@
  * Driver for /dev/crypto device (aka CryptoDev)
  *
  * Copyright (c) 2004 Michal Ludvig <mludvig@logix.net.nz>, SuSE Labs
- * Copyright (c) 2009,2010 Nikos Mavrogiannopoulos <nmav@gnutls.org>
+ * Copyright (c) 2009,2010,2011 Nikos Mavrogiannopoulos <nmav@gnutls.org>
+ * Copyright (c) 2010 Phil Sutter
  *
  * This file is part of linux cryptodev.
  *
@@ -389,7 +390,10 @@ crypto_finish_all_sessions(struct fcrypt *fcr)
 struct csession *
 crypto_get_session_by_sid(struct fcrypt *fcr, uint32_t sid)
 {
-	struct csession *ses_ptr, *retval = 0;
+	struct csession *ses_ptr, *retval = NULL;
+	
+	if (unlikely(fcr == NULL))
+		return NULL;
 
 	mutex_lock(&fcr->sem);
 	list_for_each_entry(ses_ptr, &fcr->list, entry) {
@@ -701,8 +705,12 @@ static int get_session_info(struct fcrypt *fcr, struct session_info_op *siop)
 	}
 
 	if (ses_ptr->cdata.init) {
-		tfm_info_to_alg_info(&siop->cipher_info,
+		if (ses_ptr->cdata.aead == 0) 
+			tfm_info_to_alg_info(&siop->cipher_info,
 				crypto_ablkcipher_tfm(ses_ptr->cdata.async.s));
+		else
+			tfm_info_to_alg_info(&siop->cipher_info,
+				crypto_aead_tfm(ses_ptr->cdata.async.as));
 	}
 	if (ses_ptr->hdata.init) {
 		tfm_info_to_alg_info(&siop->hash_info,
diff --git a/drivers/staging/crypto/cryptodev/main.c b/drivers/staging/crypto/cryptodev/main.c
index f1167e5c4c7f..bea9fb7669d8 100644
--- a/drivers/staging/crypto/cryptodev/main.c
+++ b/drivers/staging/crypto/cryptodev/main.c
@@ -46,6 +46,7 @@
 #include <linux/scatterlist.h>
 #include "cryptodev_int.h"
 #include "zc.h"
+#include "cryptlib.h"
 #include "version.h"
 
 /* This file contains the traditional operations of encryption
@@ -151,72 +152,6 @@ __crypto_run_std(struct csession *ses_ptr, struct crypt_op *cop)
 	return ret;
 }
 
-/* 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,
-                       int *tot_pages)
-{
-	int src_pagecount, dst_pagecount = 0, pagecount, write_src = 1;
-	struct crypt_op *cop = &kcop->cop;
-	int rc;
-
-	if (cop->src == NULL)
-		return -EINVAL;
-
-	if (ses->alignmask && !IS_ALIGNED((unsigned long)cop->src, ses->alignmask)) {
-		dprintk(2, KERN_WARNING, "%s: careful - source address %lx is not %d byte aligned\n",
-				__func__, (unsigned long)cop->src, ses->alignmask + 1);
-	}
-
-	src_pagecount = PAGECOUNT(cop->src, cop->len);
-	if (!ses->cdata.init) {		/* hashing only */
-		write_src = 0;
-	} else if (cop->src != cop->dst) {	/* non-in-situ transformation */
-		if (cop->dst == NULL)
-			return -EINVAL;
-
-		dst_pagecount = PAGECOUNT(cop->dst, cop->len);
-		write_src = 0;
-
-		if (ses->alignmask && !IS_ALIGNED((unsigned long)cop->dst, ses->alignmask)) {
-			dprintk(2, KERN_WARNING, "%s: careful - destination address %lx is not %d byte aligned\n",
-					__func__, (unsigned long)cop->dst, ses->alignmask + 1);
-		}
-
-	}
-	(*tot_pages) = pagecount = src_pagecount + dst_pagecount;
-
-	if (pagecount > ses->array_size) {
-		rc = adjust_sg_array(ses, pagecount);
-		if (rc)
-			return rc;
-	}
-
-	rc = __get_userbuf(cop->src, cop->len, write_src, src_pagecount,
-	                   ses->pages, ses->sg, kcop->task, kcop->mm);
-	if (unlikely(rc)) {
-		dprintk(1, KERN_ERR,
-			"failed to get user pages for data input\n");
-		return -EINVAL;
-	}
-	(*src_sg) = (*dst_sg) = ses->sg;
-
-	if (!dst_pagecount)
-		return 0;
-
-	(*dst_sg) = ses->sg + src_pagecount;
-
-	rc = __get_userbuf(cop->dst, cop->len, 1, dst_pagecount,
-	                   ses->pages + src_pagecount, *dst_sg,
-			   kcop->task, kcop->mm);
-	if (unlikely(rc)) {
-		dprintk(1, KERN_ERR,
-		        "failed to get user pages for data output\n");
-		release_user_pages(ses->pages, src_pagecount);
-		return -EINVAL;
-	}
-	return 0;
-}
 
 
 /* This is the main crypto function - zero-copy edition */
@@ -227,7 +162,8 @@ __crypto_run_zc(struct csession *ses_ptr, struct kernel_crypt_op *kcop)
 	struct crypt_op *cop = &kcop->cop;
 	int ret = 0, pagecount;
 
-	ret = get_userbuf(ses_ptr, kcop, &src_sg, &dst_sg, &pagecount);
+	ret = get_userbuf(ses_ptr, cop->src, cop->len, cop->dst, cop->len, 
+	                  kcop->task, kcop->mm, &src_sg, &dst_sg, &pagecount);
 	if (unlikely(ret)) {
 		dprintk(1, KERN_ERR, "Error getting user pages. "
 					"Falling back to non zero copy.\n");
diff --git a/drivers/staging/crypto/cryptodev/zc.c b/drivers/staging/crypto/cryptodev/zc.c
index 906dd060402b..cf1aa25782de 100644
--- a/drivers/staging/crypto/cryptodev/zc.c
+++ b/drivers/staging/crypto/cryptodev/zc.c
@@ -34,6 +34,7 @@
 #include <crypto/scatterwalk.h>
 #include <linux/scatterlist.h>
 #include "cryptodev_int.h"
+#include "zc.h"
 #include "version.h"
 
 /* Helper functions to assist zero copy. 
@@ -108,3 +109,79 @@ void release_user_pages(struct page **pg, int pagecount)
 		page_cache_release(pg[pagecount]);
 	}
 }
+
+/* make src and dst available in scatterlists.
+ * dst might be the same as src.
+ */
+int get_userbuf(struct csession *ses, void* __user src, int src_len,
+                void* __user dst, int dst_len,
+		struct task_struct *task, struct mm_struct *mm,
+                struct scatterlist **src_sg, 
+                struct scatterlist **dst_sg,
+                int *tot_pages)
+{
+	int src_pagecount, dst_pagecount = 0, pagecount, write_src = 1;
+	int rc;
+
+	if (src == NULL)
+		return -EINVAL;
+
+	if (ses->alignmask && !IS_ALIGNED((unsigned long)src, ses->alignmask)) {
+		dprintk(2, KERN_WARNING, "%s: careful - source address %lx is not %d byte aligned\n",
+				__func__, (unsigned long)src, ses->alignmask + 1);
+	}
+
+	if (src == dst) {
+                /* dst == src */
+	        src_len = max(src_len, dst_len);
+	        dst_len = src_len;
+        }
+
+	src_pagecount = PAGECOUNT(src, src_len);
+	if (!ses->cdata.init) {		/* hashing only */
+		write_src = 0;
+	} else if (src != dst) {	/* non-in-situ transformation */
+		if (dst == NULL)
+			return -EINVAL;
+
+		dst_pagecount = PAGECOUNT(dst, dst_len);
+		write_src = 0;
+
+		if (ses->alignmask && !IS_ALIGNED((unsigned long)dst, ses->alignmask)) {
+			dprintk(2, KERN_WARNING, "%s: careful - destination address %lx is not %d byte aligned\n",
+					__func__, (unsigned long)dst, ses->alignmask + 1);
+		}
+        }
+	(*tot_pages) = pagecount = src_pagecount + dst_pagecount;
+
+	if (pagecount > ses->array_size) {
+		rc = adjust_sg_array(ses, pagecount);
+		if (rc)
+			return rc;
+	}
+
+	rc = __get_userbuf(src, src_len, write_src, src_pagecount,
+	                   ses->pages, ses->sg, task, mm);
+	if (unlikely(rc)) {
+		dprintk(1, KERN_ERR,
+			"failed to get user pages for data input\n");
+		return -EINVAL;
+	}
+	(*src_sg) = (*dst_sg) = ses->sg;
+
+	if (!dst_pagecount)
+		return 0;
+
+	(*dst_sg) = ses->sg + src_pagecount;
+
+	rc = __get_userbuf(dst, dst_len, 1, dst_pagecount,
+	                   ses->pages + src_pagecount, *dst_sg,
+			   task, mm);
+	if (unlikely(rc)) {
+		dprintk(1, KERN_ERR,
+		        "failed to get user pages for data output\n");
+		release_user_pages(ses->pages, src_pagecount);
+		return -EINVAL;
+	}
+	return 0;
+}
diff --git a/drivers/staging/crypto/cryptodev/zc.h b/drivers/staging/crypto/cryptodev/zc.h
index e9f4cd4f5809..e914f0f6d27f 100644
--- a/drivers/staging/crypto/cryptodev/zc.h
+++ b/drivers/staging/crypto/cryptodev/zc.h
@@ -1,12 +1,26 @@
+#ifndef ZC_H
+# define ZC_H
+
+#include "cryptodev_int.h"
+
 /* For zero copy */
 int __get_userbuf(uint8_t __user *addr, uint32_t len, int write,
 		int pgcount, struct page **pg, struct scatterlist *sg,
 		struct task_struct *task, struct mm_struct *mm);
 void release_user_pages(struct page **pg, int pagecount);
 
+int get_userbuf(struct csession *ses, void* __user src, int src_len,
+                void* __user dst, int dst_len,
+		struct task_struct *task, struct mm_struct *mm,
+                struct scatterlist **src_sg, 
+                struct scatterlist **dst_sg,
+                int *tot_pages);
+
 /* last page - first page + 1 */
 #define PAGECOUNT(buf, buflen) \
 	((((unsigned long)(buf + buflen - 1) & PAGE_MASK) >> PAGE_SHIFT) - \
 	(((unsigned long) buf               & PAGE_MASK) >> PAGE_SHIFT) + 1)
 
 #define DEFAULT_PREALLOC_PAGES 32
+
+#endif
-- 
2.1.0

