Add algorithm driver for XTS mode. Fix several bugs in EVP XTS implementation.
diff --git a/CHANGES b/CHANGES
index 70d2382..fd59e9d 100644
--- a/CHANGES
+++ b/CHANGES
@@ -9,9 +9,10 @@
      to use callback. Always run all selftests even if one fails.
      [Steve Henson]
 
-  *) Provisional XTS support. Note: this does increase the maximum key
-     length from 32 to 64 bytes but there should be no binary compatibility
-     issues as existing applications will never use XTS mode.
+  *) XTS support including algorithm test driver in the fips_gcmtest program.
+     Note: this does increase the maximum key length from 32 to 64 bytes but
+     there should be no binary compatibility issues as existing applications
+     will never use XTS mode.
      [Steve Henson]
 
   *) Extensive reorganisation of FIPS PRNG behaviour. Remove all dependencies
diff --git a/crypto/evp/e_aes.c b/crypto/evp/e_aes.c
index 019c910..c093eb5 100644
--- a/crypto/evp/e_aes.c
+++ b/crypto/evp/e_aes.c
@@ -471,8 +471,6 @@
 	/* key1 and key2 are used as an indicator both key and IV are set */
 	xctx->xts.key1 = NULL;
 	xctx->xts.key2 = NULL;
-	xctx->xts.block1 = (block128_f)AES_encrypt;
-	xctx->xts.block2 = (block128_f)AES_encrypt;
 	return 1;
 	}
 
@@ -485,13 +483,23 @@
 
 	if (key)
 		{
-		AES_set_encrypt_key(key, ctx->key_len * 8, &xctx->ks1);
-		AES_set_encrypt_key(key + ctx->key_len, ctx->key_len * 8,
-								&xctx->ks2);
+		/* key_len is two AES keys */
+		if (ctx->encrypt)
+			{
+			AES_set_encrypt_key(key, ctx->key_len * 4, &xctx->ks1);
+			xctx->xts.block1 = (block128_f)AES_encrypt;
+			}
+		else
+			{
+			AES_set_decrypt_key(key, ctx->key_len * 4, &xctx->ks1);
+			xctx->xts.block1 = (block128_f)AES_decrypt;
+			}
+
+		AES_set_encrypt_key(key + ctx->key_len/2,
+						ctx->key_len * 4, &xctx->ks2);
+		xctx->xts.block2 = (block128_f)AES_encrypt;
 
 		xctx->xts.key1 = &xctx->ks1;
-		xctx->xts.block1 = (block128_f)AES_encrypt;
-		xctx->xts.block2 = (block128_f)AES_encrypt;
 		}
 
 	if (iv)
diff --git a/fips/aes/fips_gcmtest.c b/fips/aes/fips_gcmtest.c
index 7c9efe7..cfd8647 100644
--- a/fips/aes/fips_gcmtest.c
+++ b/fips/aes/fips_gcmtest.c
@@ -263,9 +263,84 @@
 		}
 	}
 
+static void xtstest(FILE *in, FILE *out)
+	{
+	char buf[2048];
+	char lbuf[2048];
+	char *keyword, *value;
+	int inlen;
+	int encrypt = 0;
+	int rv;
+	long l;
+	unsigned char *key = NULL, *iv = NULL;
+	unsigned char *inbuf = NULL, *outbuf = NULL;
+	EVP_CIPHER_CTX ctx;
+	const EVP_CIPHER *xts = NULL;
+	FIPS_cipher_ctx_init(&ctx);
+
+	while(fgets(buf,sizeof buf,in) != NULL)
+		{
+		fputs(buf,out);
+		if (buf[0] == '[' && strlen(buf) >= 9)
+			{
+			if(!strncmp(buf,"[ENCRYPT]", 9))
+				encrypt = 1;
+			else if(!strncmp(buf,"[DECRYPT]", 9))
+				encrypt = 0;
+			}
+		if (!parse_line(&keyword, &value, lbuf, buf))
+			continue;
+		else if(!strcmp(keyword,"Key"))
+			{
+			key = hex2bin_m(value, &l);
+			if (l == 32)
+				xts = EVP_aes_128_xts();
+			else if (l == 64)
+				xts = EVP_aes_256_xts();
+			else
+				{
+				fprintf(stderr, "Inconsistent Key length\n");
+				exit(1);
+				}
+			}
+		else if(!strcmp(keyword,"i"))
+			{
+			iv = hex2bin_m(value, &l);
+			if (l != 16)
+				{
+				fprintf(stderr, "Inconsistent i length\n");
+				exit(1);
+				}
+			}
+		else if(encrypt && !strcmp(keyword,"PT"))
+			{
+			inbuf = hex2bin_m(value, &l);
+			inlen = l;
+			}
+		else if(!encrypt && !strcmp(keyword,"CT"))
+			{
+			inbuf = hex2bin_m(value, &l);
+			inlen = l;
+			}
+		if (inbuf)
+			{
+			FIPS_cipherinit(&ctx, xts, key, iv, encrypt);
+			outbuf = OPENSSL_malloc(inlen);
+			rv = FIPS_cipher(&ctx, outbuf, inbuf, inlen);
+			OutputValue(encrypt ? "CT":"PT", outbuf, inlen, out, 0);
+			OPENSSL_free(inbuf);
+			OPENSSL_free(outbuf);
+			OPENSSL_free(key);
+			OPENSSL_free(iv);
+			iv = key = inbuf = outbuf = NULL;
+			}	
+		}
+	}
+
 int main(int argc,char **argv)
 	{
 	int encrypt;
+	int xts = 0;
 	FILE *in, *out;
 	if (argc == 4)
 		{
@@ -299,13 +374,18 @@
 		encrypt = 2;
 	else if(!strcmp(argv[1],"-decrypt"))
 		encrypt = 0;
+	else if(!strcmp(argv[1],"-xts"))
+		xts = 1;
 	else
 		{
 		fprintf(stderr,"Don't know how to %s.\n",argv[1]);
 		exit(1);
 		}
 
-	gcmtest(in, out, encrypt);
+	if (xts)
+		xtstest(in, out);
+	else
+		gcmtest(in, out, encrypt);
 
 	if (argc == 4)
 		{