From 668ae9d3dffecc561abe3dac7e05aada60e9c13e Mon Sep 17 00:00:00 2001
From: Nikos Mavrogiannopoulos <nmav@gnutls.org>
Date: Thu, 24 Nov 2011 20:44:03 +0100
Subject: [PATCH 150/291] cryptodev: Added

---
 drivers/staging/crypto/cryptodev/cryptodev_auth.c  |  32 ++-
 .../crypto/cryptodev/examples/cipher-aead-srtp.c   | 318 +++++++++++++++++++++
 .../crypto/cryptodev/examples/cipher-aead.c        | 312 +++++++++++++++++++-
 3 files changed, 641 insertions(+), 21 deletions(-)

diff --git a/drivers/staging/crypto/cryptodev/cryptodev_auth.c b/drivers/staging/crypto/cryptodev/cryptodev_auth.c
index 2acd69df2626..597515989daa 100644
--- a/drivers/staging/crypto/cryptodev/cryptodev_auth.c
+++ b/drivers/staging/crypto/cryptodev/cryptodev_auth.c
@@ -363,12 +363,12 @@ int kcaop_to_user(struct kernel_crypt_auth_op *kcaop,
 	return 0;
 }
 
-static void copy_hash( struct scatterlist *dst_sg, int len, void* hash, int hash_len)
+static void copy_tls_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)
+static void read_tls_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);
 }
@@ -385,25 +385,28 @@ static int pad_record( struct scatterlist *dst_sg, int len, int block_size)
 	return pad_size;
 }
 
-static int verify_record_pad( struct scatterlist *dst_sg, int len, int block_size)
+static int verify_tls_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)
+	if (pad_size+1 > len) {
+		dprintk(1, KERN_ERR, "Pad size: %d\n", pad_size);
 		return -ECANCELED;
+	}
 
-	scatterwalk_map_and_copy(pad, dst_sg, len-pad_size, pad_size, 0);
+	scatterwalk_map_and_copy(pad, dst_sg, len-pad_size-1, pad_size+1, 0);
 
 	for (i=0;i<pad_size;i++)
-		if (pad[i] != pad_size)
+		if (pad[i] != pad_size) {
+			dprintk(1, KERN_ERR, "Pad size: %d, pad: %d\n", pad_size, (int)pad[i]);
 			return -ECANCELED;
+		}
 
-	return 0;
+	return pad_size+1;
 }
 
 static int
@@ -444,7 +447,7 @@ tls_auth_n_crypt(struct csession *ses_ptr, struct kernel_crypt_auth_op *kcaop,
 				goto out_err;
 			}
 
-			copy_hash( dst_sg, len, hash_output, caop->tag_len);
+			copy_tls_hash( dst_sg, len, hash_output, caop->tag_len);
 			len += caop->tag_len;
 		}
 
@@ -472,12 +475,13 @@ tls_auth_n_crypt(struct csession *ses_ptr, struct kernel_crypt_auth_op *kcaop,
 			}
 
 			if (ses_ptr->cdata.blocksize > 1) {
-				ret = verify_record_pad(dst_sg, len, ses_ptr->cdata.blocksize);
-				if (unlikely(ret)) {
+				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);
 					fail = 1;
-				} else
+				} else {
 					len -= ret;
+				}
 			}
 		}
 
@@ -488,7 +492,7 @@ tls_auth_n_crypt(struct csession *ses_ptr, struct kernel_crypt_auth_op *kcaop,
 				goto out_err;
 			}
 
-			read_hash( dst_sg, len, vhash, caop->tag_len);
+			read_tls_hash( dst_sg, len, vhash, caop->tag_len);
 			len -= caop->tag_len;
 
 			if (auth_len > 0) {
@@ -516,7 +520,7 @@ tls_auth_n_crypt(struct csession *ses_ptr, struct kernel_crypt_auth_op *kcaop,
 			}
 
 			if (memcmp(vhash, hash_output, caop->tag_len) != 0 || fail != 0) {
-				dprintk(1, KERN_ERR, "MAC verification failed\n");
+				dprintk(1, KERN_ERR, "MAC verification failed (tag_len: %d)\n", caop->tag_len);
 				ret = -ECANCELED;
 				goto out_err;
 			}
diff --git a/drivers/staging/crypto/cryptodev/examples/cipher-aead-srtp.c b/drivers/staging/crypto/cryptodev/examples/cipher-aead-srtp.c
index 3ca8866aa532..c940d7a5858a 100644
--- a/drivers/staging/crypto/cryptodev/examples/cipher-aead-srtp.c
+++ b/drivers/staging/crypto/cryptodev/examples/cipher-aead-srtp.c
@@ -227,6 +227,315 @@ test_crypto(int cfd)
 	return 0;
 }
 
+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];
+	unsigned char sha1mac[20];
+	unsigned char tag[20];
+	unsigned char mackey[] = "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b";
+	int mackey_len = 16;
+	int pad, i;
+
+	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));
+
+	/* Get crypto session for AES128 */
+	sess.cipher = CRYPTO_AES_CTR;
+	sess.keylen = KEY_SIZE;
+	sess.key = key;
+
+	sess.mac = CRYPTO_SHA1_HMAC;
+	sess.mackeylen = mackey_len;
+	sess.mackey = mackey;
+
+	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, HEADER_SIZE); /* header */
+	memset(&plaintext[HEADER_SIZE], 0x17, PLAINTEXT_SIZE); /* payload */
+	memset(&plaintext[HEADER_SIZE+PLAINTEXT_SIZE], 0x22, FOOTER_SIZE);
+
+	memcpy(ciphertext, plaintext, DATA_SIZE);
+
+	/* Encrypt data.in to data.encrypted */
+	cao.ses = sess.ses;
+	cao.len = PLAINTEXT_SIZE;
+	cao.auth_len = HEADER_SIZE+PLAINTEXT_SIZE+FOOTER_SIZE;
+	cao.auth_src = ciphertext;
+	cao.src = ciphertext+HEADER_SIZE;
+	cao.dst = cao.src;
+	cao.iv = iv;
+	cao.op = COP_ENCRYPT;
+	cao.flags = COP_FLAG_AEAD_SRTP_TYPE;
+	cao.tag = tag;
+	cao.tag_len = 20;
+
+	if (ioctl(cfd, CIOCAUTHCRYPT, &cao)) {
+		perror("ioctl(CIOCAUTHCRYPT)");
+		return 1;
+	}
+
+
+	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_CTR;
+	sess.keylen = KEY_SIZE;
+	sess.key = key;
+
+	sess.mac = CRYPTO_SHA1_HMAC;
+	sess.mackeylen = mackey_len;
+	sess.mackey = mackey;
+
+	if (ioctl(cfd, CIOCGSESSION, &sess)) {
+		perror("ioctl(CIOCGSESSION)");
+		return 1;
+	}
+
+	/* Decrypt data.encrypted to data.decrypted */
+	/* Encrypt data.in to data.encrypted */
+	cao.ses = sess.ses;
+	cao.len = PLAINTEXT_SIZE;
+	cao.auth_len = HEADER_SIZE+PLAINTEXT_SIZE+FOOTER_SIZE;
+	cao.auth_src = ciphertext;
+	cao.src = ciphertext+HEADER_SIZE;
+	cao.dst = cao.src;
+	cao.iv = iv;
+	cao.op = COP_DECRYPT;
+	cao.flags = COP_FLAG_AEAD_SRTP_TYPE;
+	cao.tag = tag;
+	cao.tag_len = 20;
+	if (ioctl(cfd, CIOCAUTHCRYPT, &cao)) {
+		perror("ioctl(CIOCCRYPT)");
+		return 1;
+	}
+
+	/* Verify the result */
+	if (memcmp(plaintext+HEADER_SIZE, ciphertext+HEADER_SIZE, PLAINTEXT_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 ", (unsigned int)plaintext[i]);
+		}
+		printf("ciphertext:");
+		for (i = 0; i < DATA_SIZE; i++) {
+			if ((i % 30) == 0)
+				printf("\n");
+			printf("%02x ", (unsigned int)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];
+	unsigned char sha1mac[20];
+	unsigned char tag[20];
+	unsigned char mackey[] = "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b";
+	int mackey_len = 16;
+	int pad, i;
+
+	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));
+
+	/* Get crypto session for AES128 */
+	sess.cipher = CRYPTO_AES_CTR;
+	sess.keylen = KEY_SIZE;
+	sess.key = key;
+
+	sess.mac = CRYPTO_SHA1_HMAC;
+	sess.mackeylen = mackey_len;
+	sess.mackey = mackey;
+
+	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, HEADER_SIZE); /* header */
+	memset(&plaintext[HEADER_SIZE], 0x17, PLAINTEXT_SIZE); /* payload */
+	memset(&plaintext[HEADER_SIZE+PLAINTEXT_SIZE], 0x22, FOOTER_SIZE);
+
+	memcpy(ciphertext, plaintext, DATA_SIZE);
+
+	/* Encrypt data.in to data.encrypted */
+	cao.ses = sess.ses;
+	cao.len = PLAINTEXT_SIZE;
+	cao.auth_len = HEADER_SIZE+PLAINTEXT_SIZE+FOOTER_SIZE;
+	cao.auth_src = ciphertext;
+	cao.src = ciphertext+HEADER_SIZE;
+	cao.dst = cao.src;
+	cao.iv = iv;
+	cao.op = COP_ENCRYPT;
+	cao.flags = COP_FLAG_AEAD_SRTP_TYPE;
+	cao.tag = tag;
+	cao.tag_len = 20;
+
+	if (ioctl(cfd, CIOCAUTHCRYPT, &cao)) {
+		perror("ioctl(CIOCAUTHCRYPT)");
+		return 1;
+	}
+
+
+	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_CTR;
+	sess.keylen = KEY_SIZE;
+	sess.key = key;
+
+	sess.mac = CRYPTO_SHA1_HMAC;
+	sess.mackeylen = mackey_len;
+	sess.mackey = mackey;
+
+	if (ioctl(cfd, CIOCGSESSION, &sess)) {
+		perror("ioctl(CIOCGSESSION)");
+		return 1;
+	}
+
+	/* Decrypt data.encrypted to data.decrypted */
+	/* Encrypt data.in to data.encrypted */
+	if (err == 0)
+		ciphertext[1]++;
+	else
+		ciphertext[HEADER_SIZE+3]++;
+	cao.ses = sess.ses;
+	cao.len = PLAINTEXT_SIZE;
+	cao.auth_len = HEADER_SIZE+PLAINTEXT_SIZE+FOOTER_SIZE;
+	cao.auth_src = ciphertext;
+	cao.src = ciphertext+HEADER_SIZE;
+	cao.dst = cao.src;
+	cao.iv = iv;
+	cao.op = COP_DECRYPT;
+	cao.flags = COP_FLAG_AEAD_SRTP_TYPE;
+	cao.tag = tag;
+	cao.tag_len = 20;
+	if (ioctl(cfd, CIOCAUTHCRYPT, &cao)) {
+		if (ioctl(cfd, CIOCFSESSION, &sess.ses)) {
+			perror("ioctl(CIOCFSESSION)");
+			return 1;
+		}
+
+		printf("Test passed\n");
+		return 0;
+	}
+
+	/* Verify the result */
+	if (memcmp(plaintext+HEADER_SIZE, ciphertext+HEADER_SIZE, PLAINTEXT_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 ", (unsigned int)plaintext[i]);
+		}
+		printf("ciphertext:");
+		for (i = 0; i < DATA_SIZE; i++) {
+			if ((i % 30) == 0)
+				printf("\n");
+			printf("%02x ", (unsigned int)ciphertext[i]);
+		}
+		printf("\n");
+		return 1;
+	}
+
+	printf("Test failed\n");
+
+
+	/* Finish crypto session */
+	if (ioctl(cfd, CIOCFSESSION, &sess.ses)) {
+		perror("ioctl(CIOCFSESSION)");
+		return 1;
+	}
+
+	return 1;
+}
+
 int
 main()
 {
@@ -256,6 +565,15 @@ main()
 	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)");
diff --git a/drivers/staging/crypto/cryptodev/examples/cipher-aead.c b/drivers/staging/crypto/cryptodev/examples/cipher-aead.c
index d04c6af537ce..ab29aeca8109 100644
--- a/drivers/staging/crypto/cryptodev/examples/cipher-aead.c
+++ b/drivers/staging/crypto/cryptodev/examples/cipher-aead.c
@@ -13,7 +13,7 @@
 #include <sys/ioctl.h>
 #include <crypto/cryptodev.h>
 
-#define	DATA_SIZE	(8*1024+11)
+#define	DATA_SIZE	(8*1024)
 #define AUTH_SIZE       31
 #define	BLOCK_SIZE	16
 #define	KEY_SIZE	16
@@ -165,7 +165,7 @@ test_crypto(int cfd)
 		return 1;
 	}
 
-	printf("Original plaintext size: %d, ciphertext: %d\n", DATA_SIZE, cao.len);
+	//printf("Original plaintext size: %d, ciphertext: %d\n", DATA_SIZE, cao.len);
 
 	if (ioctl(cfd, CIOCFSESSION, &sess.ses)) {
 		perror("ioctl(CIOCFSESSION)");
@@ -216,19 +216,19 @@ test_crypto(int cfd)
 		return 1;
 	}
 
-	if (memcmp(&ciphertext[cao.len-MAC_SIZE-1], sha1mac, 20) != 0) {
+	pad = ciphertext[cao.len-1];
+	if (memcmp(&ciphertext[cao.len-MAC_SIZE-pad-1], sha1mac, 20) != 0) {
 		fprintf(stderr, "AEAD SHA1 MAC does not match plain MAC\n");
 		print_buf("SHA1: ", sha1mac, 20);
-		print_buf("SHA1-TLS: ", &ciphertext[cao.len-MAC_SIZE-1], 20);
+		print_buf("SHA1-TLS: ", &ciphertext[cao.len-MAC_SIZE-pad-1], 20);
 		return 1;
 	}
 
-	pad = ciphertext[cao.len-1];
 
 	for (i=0;i<pad;i++)
-		if (ciphertext[cao.len-MAC_SIZE-1-i] != pad) {
+		if (ciphertext[cao.len-1-i] != pad) {
 			fprintf(stderr, "Pad does not match (expected %d)\n", pad);
-			print_buf("PAD", &ciphertext[cao.len-MAC_SIZE-1-pad], pad);
+			print_buf("PAD: ", &ciphertext[cao.len-1-pad], pad);
 			return 1;
 		}
 
@@ -244,6 +244,295 @@ test_crypto(int cfd)
 	return 0;
 }
 
+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_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);
+
+	if (get_sha1_hmac(cfd, sess.mackey, sess.mackeylen, auth, sizeof(auth), plaintext, DATA_SIZE, sha1mac) != 0) {
+		fprintf(stderr, "SHA1 MAC failed\n");
+		return 1;
+	}
+
+	/* 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;
+	}
+
+	/* 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)) {
+		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];
+	unsigned char sha1mac[20];
+	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);
+
+	if (get_sha1_hmac(cfd, sess.mackey, sess.mackeylen, auth, sizeof(auth), plaintext, DATA_SIZE, sha1mac) != 0) {
+		fprintf(stderr, "SHA1 MAC failed\n");
+		return 1;
+	}
+
+	/* 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()
 {
@@ -273,6 +562,15 @@ main()
 	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)");
-- 
2.1.0

