From 49ab0dc66b31b24f48922dcbfa0d82bb23892f8b Mon Sep 17 00:00:00 2001
From: Nikos Mavrogiannopoulos <nmav@gnutls.org>
Date: Thu, 1 Dec 2011 10:49:00 +0100
Subject: [PATCH 152/291] cryptodev: Preliminary

---
 drivers/staging/crypto/cryptodev/cryptlib.c        | 151 ++++--
 .../staging/crypto/cryptodev/crypto/cryptodev.h    |   2 +
 drivers/staging/crypto/cryptodev/cryptodev_int.h   |  24 +-
 drivers/staging/crypto/cryptodev/examples/Makefile |   4 +-
 .../staging/crypto/cryptodev/examples/cipher-gcm.c | 524 +++++++++++++++++++++
 drivers/staging/crypto/cryptodev/ioctl.c           |  12 +-
 6 files changed, 665 insertions(+), 52 deletions(-)
 create mode 100644 drivers/staging/crypto/cryptodev/examples/cipher-gcm.c

diff --git a/drivers/staging/crypto/cryptodev/cryptlib.c b/drivers/staging/crypto/cryptodev/cryptlib.c
index ba6d7c0afa19..73c75cb69c4b 100644
--- a/drivers/staging/crypto/cryptodev/cryptlib.c
+++ b/drivers/staging/crypto/cryptodev/cryptlib.c
@@ -1,7 +1,7 @@
 /*
  * Driver for /dev/crypto device (aka CryptoDev)
  *
- * Copyright (c) 2010 Nikos Mavrogiannopoulos <nmav@gnutls.org>
+ * Copyright (c) 2010,2011 Nikos Mavrogiannopoulos <nmav@gnutls.org>
  * Portions Copyright (c) 2010 Michael Weiser
  * Portions Copyright (c) 2010 Phil Sutter
  *
@@ -33,6 +33,7 @@
 #include <crypto/algapi.h>
 #include <crypto/hash.h>
 #include <crypto/cryptodev.h>
+#include <crypto/aead.h>
 #include "cryptodev_int.h"
 
 
@@ -53,38 +54,58 @@ static void cryptodev_complete(struct crypto_async_request *req, int err)
 }
 
 int cryptodev_cipher_init(struct cipher_data *out, const char *alg_name,
-				uint8_t *keyp, size_t keylen, int stream)
+				uint8_t *keyp, size_t keylen, int stream, int aead)
 {
-	struct ablkcipher_alg *alg;
 	int ret;
 
 	memset(out, 0, sizeof(*out));
 
-	out->async.s = crypto_alloc_ablkcipher(alg_name, 0, 0);
-	if (unlikely(IS_ERR(out->async.s))) {
-		dprintk(1, KERN_DEBUG, "%s: Failed to load cipher %s\n",
-			__func__, alg_name);
-		return -EINVAL;
-	}
+	if (aead == 0) {
+		struct ablkcipher_alg *alg;
 
-	alg = crypto_ablkcipher_alg(out->async.s);
+		out->async.s = crypto_alloc_ablkcipher(alg_name, 0, 0);
+		if (unlikely(IS_ERR(out->async.s))) {
+			dprintk(1, KERN_DEBUG, "%s: Failed to load cipher %s\n",
+				__func__, alg_name);
+				return -EINVAL;
+		}
 
-	if (alg != NULL) {
-		/* Was correct key length supplied? */
-		if (alg->max_keysize > 0 &&
-				unlikely((keylen < alg->min_keysize) ||
+		alg = crypto_ablkcipher_alg(out->async.s);
+		if (alg != NULL) {
+			/* Was correct key length supplied? */
+			if (alg->max_keysize > 0 &&
+					unlikely((keylen < alg->min_keysize) ||
 					(keylen > alg->max_keysize))) {
-			dprintk(1, KERN_DEBUG,
-				"Wrong keylen '%zu' for algorithm '%s'. \
-				Use %u to %u.\n",
-				   keylen, alg_name, alg->min_keysize,
-				   alg->max_keysize);
-			ret = -EINVAL;
-			goto error;
+				dprintk(1, KERN_DEBUG,
+					"Wrong keylen '%zu' for algorithm '%s'. \
+					Use %u to %u.\n",
+					   keylen, alg_name, alg->min_keysize,
+					   alg->max_keysize);
+				ret = -EINVAL;
+				goto error;
+			}
+		}
+
+		out->blocksize = crypto_ablkcipher_blocksize(out->async.s);
+		out->ivsize = crypto_ablkcipher_ivsize(out->async.s);
+		out->alignmask = crypto_ablkcipher_alignmask(out->async.s);
+
+		ret = crypto_ablkcipher_setkey(out->async.s, keyp, keylen);
+	} else {
+		out->async.as = crypto_alloc_aead(alg_name, 0, 0);
+		if (unlikely(IS_ERR(out->async.s))) {
+			dprintk(1, KERN_DEBUG, "%s: Failed to load cipher %s\n",
+				__func__, alg_name);
+			return -EINVAL;
 		}
+
+		out->blocksize = crypto_aead_blocksize(out->async.as);
+		out->ivsize = crypto_aead_ivsize(out->async.as);
+		out->alignmask = crypto_aead_alignmask(out->async.as);
+
+		ret = crypto_aead_setkey(out->async.as, keyp, keylen);
 	}
 
-	ret = crypto_ablkcipher_setkey(out->async.s, keyp, keylen);
 	if (unlikely(ret)) {
 		dprintk(1, KERN_DEBUG, "Setting key failed for %s-%zu.\n",
 			alg_name, keylen*8);
@@ -93,9 +114,7 @@ int cryptodev_cipher_init(struct cipher_data *out, const char *alg_name,
 	}
 
 	out->stream = stream;
-	out->blocksize = crypto_ablkcipher_blocksize(out->async.s);
-	out->ivsize = crypto_ablkcipher_ivsize(out->async.s);
-	out->alignmask = crypto_ablkcipher_alignmask(out->async.s);
+	out->aead = aead;
 
 	out->async.result = kmalloc(sizeof(*out->async.result), GFP_KERNEL);
 	if (unlikely(!out->async.result)) {
@@ -106,25 +125,45 @@ int cryptodev_cipher_init(struct cipher_data *out, const char *alg_name,
 	memset(out->async.result, 0, sizeof(*out->async.result));
 	init_completion(&out->async.result->completion);
 
-	out->async.request = ablkcipher_request_alloc(out->async.s, GFP_KERNEL);
-	if (unlikely(!out->async.request)) {
-		dprintk(1, KERN_ERR, "error allocating async crypto request\n");
-		ret = -ENOMEM;
-		goto error;
-	}
+	if (aead == 0) {
+		out->async.request = ablkcipher_request_alloc(out->async.s, GFP_KERNEL);
+		if (unlikely(!out->async.request)) {
+			dprintk(1, KERN_ERR, "error allocating async crypto request\n");
+			ret = -ENOMEM;
+			goto error;
+		}
 
-	ablkcipher_request_set_callback(out->async.request,
+		ablkcipher_request_set_callback(out->async.request,
 					CRYPTO_TFM_REQ_MAY_BACKLOG,
 					cryptodev_complete, out->async.result);
+	} else {
+		out->async.arequest = aead_request_alloc(out->async.as, GFP_KERNEL);
+		if (unlikely(!out->async.arequest)) {
+			dprintk(1, KERN_ERR, "error allocating async crypto request\n");
+			ret = -ENOMEM;
+			goto error;
+		}
+
+		aead_request_set_callback(out->async.arequest,
+					CRYPTO_TFM_REQ_MAY_BACKLOG,
+					cryptodev_complete, out->async.result);
+	}
 
 	out->init = 1;
 	return 0;
 error:
-	if (out->async.request)
-		ablkcipher_request_free(out->async.request);
+	if (aead == 0) {
+		if (out->async.request)
+			ablkcipher_request_free(out->async.request);
+		if (out->async.s)
+			crypto_free_ablkcipher(out->async.s);
+	} else {
+		if (out->async.arequest)
+			aead_request_free(out->async.arequest);
+		if (out->async.s)
+			crypto_free_aead(out->async.as);
+	}
 	kfree(out->async.result);
-	if (out->async.s)
-		crypto_free_ablkcipher(out->async.s);
 
 	return ret;
 }
@@ -132,12 +171,19 @@ error:
 void cryptodev_cipher_deinit(struct cipher_data *cdata)
 {
 	if (cdata->init) {
-		if (cdata->async.request)
-			ablkcipher_request_free(cdata->async.request);
-		kfree(cdata->async.result);
-		if (cdata->async.s)
-			crypto_free_ablkcipher(cdata->async.s);
+		if (cdata->aead == 0) {
+			if (cdata->async.request)
+				ablkcipher_request_free(cdata->async.request);
+			if (cdata->async.s)
+				crypto_free_ablkcipher(cdata->async.s);
+		} else {
+			if (cdata->async.arequest)
+				aead_request_free(cdata->async.arequest);
+			if (cdata->async.s)
+				crypto_free_aead(cdata->async.as);
+		}
 
+		kfree(cdata->async.result);
 		cdata->init = 0;
 	}
 }
@@ -177,10 +223,18 @@ ssize_t cryptodev_cipher_encrypt(struct cipher_data *cdata,
 	int ret;
 
 	INIT_COMPLETION(cdata->async.result->completion);
-	ablkcipher_request_set_crypt(cdata->async.request,
+	
+	if (cdata->aead == 0) {
+		ablkcipher_request_set_crypt(cdata->async.request,
+			(struct scatterlist *)sg1, sg2,
+			len, cdata->async.iv);
+		ret = crypto_ablkcipher_encrypt(cdata->async.request);
+	} else {
+		aead_request_set_crypt(cdata->async.arequest,
 			(struct scatterlist *)sg1, sg2,
 			len, cdata->async.iv);
-	ret = crypto_ablkcipher_encrypt(cdata->async.request);
+		ret = crypto_aead_encrypt(cdata->async.arequest);
+	}
 
 	return waitfor(cdata->async.result, ret);
 }
@@ -192,10 +246,17 @@ ssize_t cryptodev_cipher_decrypt(struct cipher_data *cdata,
 	int ret;
 
 	INIT_COMPLETION(cdata->async.result->completion);
-	ablkcipher_request_set_crypt(cdata->async.request,
+	if (cdata->aead == 0) {
+		ablkcipher_request_set_crypt(cdata->async.request,
+			(struct scatterlist *)sg1, sg2,
+			len, cdata->async.iv);
+		ret = crypto_ablkcipher_decrypt(cdata->async.request);
+	} else {
+		aead_request_set_crypt(cdata->async.arequest,
 			(struct scatterlist *)sg1, sg2,
 			len, cdata->async.iv);
-	ret = crypto_ablkcipher_decrypt(cdata->async.request);
+		ret = crypto_aead_decrypt(cdata->async.arequest);
+	}
 
 	return waitfor(cdata->async.result, ret);
 }
diff --git a/drivers/staging/crypto/cryptodev/crypto/cryptodev.h b/drivers/staging/crypto/cryptodev/crypto/cryptodev.h
index f56a043ba93d..408c2a517f5b 100644
--- a/drivers/staging/crypto/cryptodev/crypto/cryptodev.h
+++ b/drivers/staging/crypto/cryptodev/crypto/cryptodev.h
@@ -41,6 +41,7 @@ enum cryptodev_crypto_op_t {
 	CRYPTO_AES_CTR = 21,
 	CRYPTO_AES_XTS = 22,
 	CRYPTO_AES_ECB = 23,
+	CRYPTO_AES_GCM = 50,
 
 	CRYPTO_CAMELLIA_CBC = 101,
 	CRYPTO_RIPEMD160,
@@ -140,6 +141,7 @@ struct crypt_auth_op {
 
 	/* initialization vector for encryption operations */
 	__u8	__user *iv;
+	__u32   iv_len;
 };
 
 /* In TLS mode the following are required:
diff --git a/drivers/staging/crypto/cryptodev/cryptodev_int.h b/drivers/staging/crypto/cryptodev/cryptodev_int.h
index d7412a69cee6..634f195e1beb 100644
--- a/drivers/staging/crypto/cryptodev/cryptodev_int.h
+++ b/drivers/staging/crypto/cryptodev/cryptodev_int.h
@@ -12,6 +12,7 @@
 #include <linux/moduleparam.h>
 #include <linux/scatterlist.h>
 #include <crypto/cryptodev.h>
+#include <crypto/aead.h>
 
 #define PFX "cryptodev: "
 #define dprintk(level, severity, format, a...)			\
@@ -40,13 +41,18 @@ void release_user_pages(struct page **pg, int pagecount);
 struct cipher_data {
 	int init; /* 0 uninitialized */
 	int blocksize;
+	int aead;
 	int stream;
 	int ivsize;
 	int alignmask;
 	struct {
 		struct crypto_ablkcipher *s;
-		struct cryptodev_result *result;
 		struct ablkcipher_request *request;
+
+		struct crypto_aead *as;
+		struct aead_request *arequest;
+
+		struct cryptodev_result *result;
 		uint8_t iv[EALG_MAX_BLOCK_LEN];
 	} async;
 };
@@ -57,7 +63,7 @@ struct fcrypt {
 };
 
 int cryptodev_cipher_init(struct cipher_data *out, const char *alg_name,
-			  uint8_t *key, size_t keylen, int stream);
+			  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,
@@ -66,6 +72,20 @@ 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)
 {
diff --git a/drivers/staging/crypto/cryptodev/examples/Makefile b/drivers/staging/crypto/cryptodev/examples/Makefile
index eeb9d62d3e06..4e8639031af5 100644
--- a/drivers/staging/crypto/cryptodev/examples/Makefile
+++ b/drivers/staging/crypto/cryptodev/examples/Makefile
@@ -1,7 +1,9 @@
 KERNEL_DIR ?= /lib/modules/$(shell uname -r)/build
 KBUILD_CFLAGS += -I..
 
-hostprogs := cipher cipher-aead 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 cipher-gcm
+
 example-cipher-objs := cipher.o
 example-cipher-aead-objs := cipher-aead.o
 example-hmac-objs := hmac.o
diff --git a/drivers/staging/crypto/cryptodev/examples/cipher-gcm.c b/drivers/staging/crypto/cryptodev/examples/cipher-gcm.c
new file mode 100644
index 000000000000..064341666d4c
--- /dev/null
+++ b/drivers/staging/crypto/cryptodev/examples/cipher-gcm.c
@@ -0,0 +1,524 @@
+/*
+ * Demo on how to use /dev/crypto device for ciphering.
+ *
+ * Placed under public domain.
+ *
+ */
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <sys/ioctl.h>
+#include <crypto/cryptodev.h>
+
+#define	DATA_SIZE	(8*1024)
+#define AUTH_SIZE       31
+#define	BLOCK_SIZE	16
+#define	KEY_SIZE	16
+
+
+static void print_buf(char *desc, const unsigned char *buf, int size)
+{
+	int i;
+	fputs(desc, stdout);
+	for (i = 0; i < size; i++) {
+		printf("%.2x", (uint8_t) buf[i]);
+	}
+	fputs("\n", stdout);
+}
+
+struct aes_gcm_vectors_st {
+	const uint8_t *key;
+	const uint8_t *auth;
+	int auth_size;
+	const uint8_t *plaintext;
+	int plaintext_size;
+	const uint8_t *iv;
+	const uint8_t *ciphertext;
+	const uint8_t *tag;
+};
+
+struct aes_gcm_vectors_st aes_gcm_vectors[] = {
+	{
+	 .key =
+	 "\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,
+	 .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"},
+	{
+	 .key =
+	 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	 .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,
+	 .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"},
+	{
+	 .key =
+	 "\xfe\xff\xe9\x92\x86\x65\x73\x1c\x6d\x6a\x8f\x94\x67\x30\x83\x08",
+	 .auth =
+	 "\xfe\xed\xfa\xce\xde\xad\xbe\xef\xfe\xed\xfa\xce\xde\xad\xbe\xef\xab\xad\xda\xd2",
+	 .auth_size = 20,
+	 .plaintext =
+	 "\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",
+	 .plaintext_size = 60,
+	 .ciphertext =
+	 "\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"}
+};
+
+
+/* Test against AES-GCM test vectors.
+ */
+static int test_crypto(int cfd)
+{
+	char iv[BLOCK_SIZE];
+	unsigned char tag[16];
+	int pad, i;
+	int8_t tmp[128];
+
+	struct session_op sess;
+	struct crypt_auth_op cao;
+#ifdef CIOCGSESSINFO
+	struct session_info_op siop;
+#endif
+
+	/* 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: ");
+	fflush(stdout);
+	for (i = 0;
+	     i < sizeof(aes_gcm_vectors) / sizeof(aes_gcm_vectors[0]);
+	     i++) {
+		memset(&sess, 0, sizeof(sess));
+		memset(tmp, 0, sizeof(tmp));
+
+		sess.cipher = CRYPTO_AES_GCM;
+		sess.keylen = 16;
+		sess.key = (void *) aes_gcm_vectors[i].key;
+
+		memset(&cao, 0, sizeof(cao));
+
+		cao.ses = sess.ses;
+		cao.dst = tmp;
+		cao.iv = (void *) aes_gcm_vectors[i].iv;
+		cao.iv_len = 12;
+		cao.op = COP_ENCRYPT;
+		cao.flags = 0;
+
+		if (aes_gcm_vectors[i].auth_size > 0) {
+			cao.auth_src = (void *) aes_gcm_vectors[i].auth;
+			cao.auth_len = aes_gcm_vectors[i].auth_size;
+		}
+
+		if (aes_gcm_vectors[i].plaintext_size > 0) {
+			cao.src = (void *) aes_gcm_vectors[i].plaintext;
+			cao.len = aes_gcm_vectors[i].plaintext_size;
+		}
+
+		if (ioctl(cfd, CIOCAUTHCRYPT, &cao)) {
+			perror("ioctl(CIOCAUTHCRYPT)");
+			return 1;
+		}
+
+		if (aes_gcm_vectors[i].plaintext_size > 0)
+			if (memcmp
+			    (tmp, aes_gcm_vectors[i].ciphertext,
+			     aes_gcm_vectors[i].plaintext_size) != 0) {
+				fprintf(stderr,
+					"AES-GCM test vector %d failed!\n",
+					i);
+
+				print_buf("Cipher: ", tmp, aes_gcm_vectors[i].plaintext_size);
+				print_buf("Expected: ", aes_gcm_vectors[i].ciphertext, aes_gcm_vectors[i].plaintext_size);
+				return 1;
+			}
+
+		if (memcmp
+		    (&tmp[cao.len - cao.tag_len], aes_gcm_vectors[i].tag,
+		     16) != 0) {
+			fprintf(stderr,
+				"AES-GCM test vector %d failed (tag)!\n",
+				i);
+
+			print_buf("Tag: ", tmp, cao.tag_len);
+			print_buf("Expected tag: ",
+				  aes_gcm_vectors[i].tag, 16);
+			return 1;
+		}
+
+	}
+	fprintf(stdout, "ok\n");
+	fprintf(stdout, "\n");
+
+	/* Finish crypto session */
+	if (ioctl(cfd, CIOCFSESSION, &sess.ses)) {
+		perror("ioctl(CIOCFSESSION)");
+		return 1;
+	}
+
+	return 0;
+}
+
+/* Checks if encryption and subsequent decryption 
+ * produces the same data.
+ */
+static int test_encrypt_decrypt(int cfd)
+{
+	char plaintext_raw[DATA_SIZE + 63], *plaintext;
+	char ciphertext_raw[DATA_SIZE + 63], *ciphertext;
+	char iv[BLOCK_SIZE];
+	char key[KEY_SIZE];
+	char auth[AUTH_SIZE];
+	unsigned char sha1mac[20];
+	int pad, i, enc_len;
+
+	struct session_op sess;
+	struct crypt_auth_op cao;
+#ifdef CIOCGSESSINFO
+	struct session_info_op siop;
+#endif
+
+	memset(&sess, 0, sizeof(sess));
+	memset(&cao, 0, sizeof(cao));
+
+	memset(key, 0x33, sizeof(key));
+	memset(iv, 0x03, sizeof(iv));
+	memset(auth, 0xf1, sizeof(auth));
+
+	/* Get crypto session for AES128 */
+	sess.cipher = CRYPTO_AES_GCM;
+	sess.keylen = KEY_SIZE;
+	sess.key = key;
+
+	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);
+
+	plaintext =
+	    (char *) (((unsigned long) plaintext_raw + siop.alignmask) &
+		      ~siop.alignmask);
+	ciphertext =
+	    (char *) (((unsigned long) ciphertext_raw + siop.alignmask) &
+		      ~siop.alignmask);
+#else
+	plaintext = plaintext_raw;
+	ciphertext = ciphertext_raw;
+#endif
+	memset(plaintext, 0x15, DATA_SIZE);
+
+	/* Encrypt data.in to data.encrypted */
+	cao.ses = sess.ses;
+	cao.auth_src = auth;
+	cao.auth_len = sizeof(auth);
+	cao.len = DATA_SIZE;
+	cao.src = plaintext;
+	cao.dst = ciphertext;
+	cao.iv = iv;
+	cao.op = COP_ENCRYPT;
+	cao.flags = 0;
+
+	if (ioctl(cfd, CIOCAUTHCRYPT, &cao)) {
+		perror("ioctl(CIOCAUTHCRYPT)");
+		return 1;
+	}
+
+	enc_len = cao.len;
+	//printf("Original plaintext size: %d, ciphertext: %d\n", DATA_SIZE, enc_len);
+
+	if (ioctl(cfd, CIOCFSESSION, &sess.ses)) {
+		perror("ioctl(CIOCFSESSION)");
+		return 1;
+	}
+
+	/* Get crypto session for AES128 */
+	memset(&sess, 0, sizeof(sess));
+	sess.cipher = CRYPTO_AES_GCM;
+	sess.keylen = KEY_SIZE;
+	sess.key = key;
+
+	if (ioctl(cfd, CIOCGSESSION, &sess)) {
+		perror("ioctl(CIOCGSESSION)");
+		return 1;
+	}
+
+	/* Decrypt data.encrypted to data.decrypted */
+	cao.ses = sess.ses;
+	cao.auth_src = auth;
+	cao.auth_len = sizeof(auth);
+	cao.len = enc_len;
+	cao.src = ciphertext;
+	cao.dst = ciphertext;
+	cao.iv = iv;
+	cao.op = COP_DECRYPT;
+	cao.flags = 0;
+	if (ioctl(cfd, CIOCAUTHCRYPT, &cao)) {
+		perror("ioctl(CIOCAUTHCRYPT)");
+		return 1;
+	}
+
+	if (cao.len != DATA_SIZE) {
+		fprintf(stderr, "decrypted data size incorrect!\n");
+		return 1;
+	}
+
+	/* Verify the result */
+	if (memcmp(plaintext, ciphertext, DATA_SIZE) != 0) {
+		int i;
+		fprintf(stderr,
+			"FAIL: Decrypted data are different from the input data.\n");
+		printf("plaintext:");
+		for (i = 0; i < DATA_SIZE; i++) {
+			if ((i % 30) == 0)
+				printf("\n");
+			printf("%02x ", plaintext[i]);
+		}
+		printf("ciphertext:");
+		for (i = 0; i < DATA_SIZE; i++) {
+			if ((i % 30) == 0)
+				printf("\n");
+			printf("%02x ", ciphertext[i]);
+		}
+		printf("\n");
+		return 1;
+	}
+
+	printf("Test passed\n");
+
+
+	/* Finish crypto session */
+	if (ioctl(cfd, CIOCFSESSION, &sess.ses)) {
+		perror("ioctl(CIOCFSESSION)");
+		return 1;
+	}
+
+	return 0;
+}
+
+static int test_encrypt_decrypt_error(int cfd, int err)
+{
+	char plaintext_raw[DATA_SIZE + 63], *plaintext;
+	char ciphertext_raw[DATA_SIZE + 63], *ciphertext;
+	char iv[BLOCK_SIZE];
+	char key[KEY_SIZE];
+	char auth[AUTH_SIZE];
+	int pad, i, enc_len;
+
+	struct session_op sess;
+	struct crypt_op co;
+	struct crypt_auth_op cao;
+#ifdef CIOCGSESSINFO
+	struct session_info_op siop;
+#endif
+
+	memset(&sess, 0, sizeof(sess));
+	memset(&cao, 0, sizeof(cao));
+	memset(&co, 0, sizeof(co));
+
+	memset(key, 0x33, sizeof(key));
+	memset(iv, 0x03, sizeof(iv));
+	memset(auth, 0xf1, sizeof(auth));
+
+	/* Get crypto session for AES128 */
+	sess.cipher = CRYPTO_AES_CBC;
+	sess.keylen = KEY_SIZE;
+	sess.key = key;
+
+	sess.mac = CRYPTO_SHA1_HMAC;
+	sess.mackeylen = 16;
+	sess.mackey =
+	    (uint8_t *)
+	    "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b";
+
+	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);
+
+	plaintext =
+	    (char *) (((unsigned long) plaintext_raw + siop.alignmask) &
+		      ~siop.alignmask);
+	ciphertext =
+	    (char *) (((unsigned long) ciphertext_raw + siop.alignmask) &
+		      ~siop.alignmask);
+#else
+	plaintext = plaintext_raw;
+	ciphertext = ciphertext_raw;
+#endif
+	memset(plaintext, 0x15, DATA_SIZE);
+
+	/* Encrypt data.in to data.encrypted */
+	cao.ses = sess.ses;
+	cao.auth_src = auth;
+	cao.auth_len = sizeof(auth);
+	cao.len = DATA_SIZE;
+	cao.src = plaintext;
+	cao.dst = ciphertext;
+	cao.iv = iv;
+	cao.op = COP_ENCRYPT;
+	cao.flags = COP_FLAG_AEAD_TLS_TYPE;
+
+	if (ioctl(cfd, CIOCAUTHCRYPT, &cao)) {
+		perror("ioctl(CIOCAUTHCRYPT)");
+		return 1;
+	}
+
+	enc_len = cao.len;
+	//printf("Original plaintext size: %d, ciphertext: %d\n", DATA_SIZE, enc_len);
+
+	if (ioctl(cfd, CIOCFSESSION, &sess.ses)) {
+		perror("ioctl(CIOCFSESSION)");
+		return 1;
+	}
+
+	/* Get crypto session for AES128 */
+	memset(&sess, 0, sizeof(sess));
+	sess.cipher = CRYPTO_AES_CBC;
+	sess.keylen = KEY_SIZE;
+	sess.key = key;
+	sess.mac = CRYPTO_SHA1_HMAC;
+	sess.mackeylen = 16;
+	sess.mackey =
+	    (uint8_t *)
+	    "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b";
+
+	if (ioctl(cfd, CIOCGSESSION, &sess)) {
+		perror("ioctl(CIOCGSESSION)");
+		return 1;
+	}
+
+	if (err == 0)
+		auth[2]++;
+	else
+		ciphertext[4]++;
+
+	/* Decrypt data.encrypted to data.decrypted */
+	cao.ses = sess.ses;
+	cao.auth_src = auth;
+	cao.auth_len = sizeof(auth);
+	cao.len = enc_len;
+	cao.src = ciphertext;
+	cao.dst = ciphertext;
+	cao.iv = iv;
+	cao.op = COP_DECRYPT;
+	cao.flags = COP_FLAG_AEAD_TLS_TYPE;
+	if (ioctl(cfd, CIOCAUTHCRYPT, &cao)) {
+		if (ioctl(cfd, CIOCFSESSION, &sess.ses)) {
+			perror("ioctl(CIOCFSESSION)");
+			return 1;
+		}
+
+		printf("Test passed\n");
+		return 0;
+	}
+
+	/* Finish crypto session */
+	if (ioctl(cfd, CIOCFSESSION, &sess.ses)) {
+		perror("ioctl(CIOCFSESSION)");
+		return 1;
+	}
+
+
+	fprintf(stderr, "Modification to ciphertext was not detected\n");
+	return 1;
+}
+
+int main()
+{
+	int fd = -1, cfd = -1;
+
+	/* Open the crypto device */
+	fd = open("/dev/crypto", O_RDWR, 0);
+	if (fd < 0) {
+		perror("open(/dev/crypto)");
+		return 1;
+	}
+
+	/* Clone file descriptor */
+	if (ioctl(fd, CRIOGET, &cfd)) {
+		perror("ioctl(CRIOGET)");
+		return 1;
+	}
+
+	/* Set close-on-exec (not really neede here) */
+	if (fcntl(cfd, F_SETFD, 1) == -1) {
+		perror("fcntl(F_SETFD)");
+		return 1;
+	}
+
+	/* Run the test itself */
+
+	if (test_crypto(cfd))
+		return 1;
+
+	if (test_encrypt_decrypt(cfd))
+		return 1;
+
+	if (test_encrypt_decrypt_error(cfd, 0))
+		return 1;
+
+	if (test_encrypt_decrypt_error(cfd, 1))
+		return 1;
+
+	/* Close cloned descriptor */
+	if (close(cfd)) {
+		perror("close(cfd)");
+		return 1;
+	}
+
+	/* Close the original descriptor */
+	if (close(fd)) {
+		perror("close(fd)");
+		return 1;
+	}
+
+	return 0;
+}
diff --git a/drivers/staging/crypto/cryptodev/ioctl.c b/drivers/staging/crypto/cryptodev/ioctl.c
index 6177007cf489..aa8ad51d9c40 100644
--- a/drivers/staging/crypto/cryptodev/ioctl.c
+++ b/drivers/staging/crypto/cryptodev/ioctl.c
@@ -103,7 +103,7 @@ crypto_create_session(struct fcrypt *fcr, struct session_op *sop)
 	int ret = 0;
 	const char *alg_name = NULL;
 	const char *hash_name = NULL;
-	int hmac_mode = 1, stream = 0;
+	int hmac_mode = 1, stream = 0, aead = 0;
 
 	/* Does the request make sense? */
 	if (unlikely(!sop->cipher && !sop->mac)) {
@@ -136,6 +136,11 @@ crypto_create_session(struct fcrypt *fcr, struct session_op *sop)
 		alg_name = "ctr(aes)";
 		stream = 1;
 		break;
+	case CRYPTO_AES_GCM:
+		alg_name = "gcm(aes)";
+		stream = 1;
+		aead = 1;
+		break;
 	case CRYPTO_NULL:
 		alg_name = "ecb(cipher_null)";
 		stream = 1;
@@ -193,7 +198,6 @@ crypto_create_session(struct fcrypt *fcr, struct session_op *sop)
 		hash_name = "sha512";
 		hmac_mode = 0;
 		break;
-
 	default:
 		dprintk(1, KERN_DEBUG, "%s: bad mac: %d\n", __func__,
 			sop->mac);
@@ -223,7 +227,7 @@ crypto_create_session(struct fcrypt *fcr, struct session_op *sop)
 		}
 
 		ret = cryptodev_cipher_init(&ses_new->cdata, alg_name, keyp,
-						sop->keylen, stream);
+						sop->keylen, stream, aead);
 		if (ret < 0) {
 			dprintk(1, KERN_DEBUG,
 				"%s: Failed to load cipher for %s\n",
@@ -233,7 +237,7 @@ crypto_create_session(struct fcrypt *fcr, struct session_op *sop)
 		}
 	}
 
-	if (hash_name) {
+	if (hash_name && aead == 0) {
 		uint8_t keyp[CRYPTO_HMAC_MAX_KEY_LEN];
 
 		if (unlikely(sop->mackeylen > CRYPTO_HMAC_MAX_KEY_LEN)) {
-- 
2.1.0

