[PATCH] Add XTS cipher mode

Jussi Kivilinna jussi.kivilinna at iki.fi
Wed Jan 4 16:15:06 CET 2017


* cipher/Makefile.am: Add 'cipher-xts.c'.
* cipher/cipher-internal.h (gcry_cipher_handle): Add 'bulk.xts_crypt'
and 'u_mode.xts' members.
(_gcry_cipher_xts_crypt): New prototype.
* cipher/cipher-xts.c: New.
* cipher/cipher.c (_gcry_cipher_open_internal, cipher_setkey)
(cipher_reset, cipher_encrypt, cipher_decrypt): Add XTS mode handling.
* doc/gcrypt.texi: Add XTS mode to documentation.
* src/gcrypt.h.in (GCRY_CIPHER_MODE_XTS, GCRY_XTS_BLOCK_LEN): New.
* tests/basic.c (do_check_xts_cipher, check_xts_cipher): New.
(check_bulk_cipher_modes): Add XTS test-vectors.
(check_one_cipher_core, check_one_cipher, check_ciphers): Add XTS
testing support.
(check_cipher_modes): Add XTS test.
* tests/bench-slope.c (bench_xts_encrypt_init)
(bench_xts_encrypt_do_bench, bench_xts_decrypt_do_bench)
(xts_encrypt_ops, xts_decrypt_ops): New.
(cipher_modes, cipher_bench_one): Add XTS.
* tests/benchmark.c (cipher_bench): Add XTS testing.
--

Signed-off-by: Jussi Kivilinna <jussi.kivilinna at iki.fi>
---
 0 files changed

diff --git a/cipher/Makefile.am b/cipher/Makefile.am
index ac0ec58..71a25ed 100644
--- a/cipher/Makefile.am
+++ b/cipher/Makefile.am
@@ -44,7 +44,7 @@ cipher.c cipher-internal.h \
 cipher-cbc.c cipher-cfb.c cipher-ofb.c cipher-ctr.c cipher-aeswrap.c \
 cipher-ccm.c cipher-cmac.c cipher-gcm.c cipher-gcm-intel-pclmul.c \
   cipher-gcm-armv8-aarch32-ce.S cipher-gcm-armv8-aarch64-ce.S \
-cipher-poly1305.c cipher-ocb.c \
+cipher-poly1305.c cipher-ocb.c cipher-xts.c \
 cipher-selftest.c cipher-selftest.h \
 pubkey.c pubkey-internal.h pubkey-util.c \
 md.c \
diff --git a/cipher/cipher-internal.h b/cipher/cipher-internal.h
index 7204d48..33d0629 100644
--- a/cipher/cipher-internal.h
+++ b/cipher/cipher-internal.h
@@ -146,6 +146,9 @@ struct gcry_cipher_handle
 			const void *inbuf_arg, size_t nblocks, int encrypt);
     size_t (*ocb_auth)(gcry_cipher_hd_t c, const void *abuf_arg,
 		       size_t nblocks);
+    void (*xts_crypt)(gcry_cipher_hd_t c, unsigned char *tweak,
+		      void *outbuf_arg, const void *inbuf_arg,
+		      size_t nblocks, int encrypt);
   } bulk;
 
 
@@ -309,6 +312,12 @@ struct gcry_cipher_handle
 
     } ocb;
 
+    /* Mode specific storage for XTS mode. */
+    struct {
+      /* Pointer to tweak cipher context, allocated after actual
+       * cipher context. */
+      char *tweak_context;
+    } xts;
   } u_mode;
 
   /* What follows are two contexts of the cipher in use.  The first
@@ -461,6 +470,12 @@ gcry_err_code_t _gcry_cipher_ocb_check_tag
                  const unsigned char *intag, size_t taglen);
 
 
+/*-- cipher-xts.c --*/
+gcry_err_code_t _gcry_cipher_xts_crypt
+/*           */ (gcry_cipher_hd_t c, unsigned char *outbuf, size_t outbuflen,
+		 const unsigned char *inbuf, size_t inbuflen, int encrypt);
+
+
 /* Return the L-value for block N.  Note: 'cipher_ocb.c' ensures that N
  * will never be multiple of 65536 (1 << OCB_L_TABLE_SIZE), thus N can
  * be directly passed to _gcry_ctz() function and resulting index will
diff --git a/cipher/cipher-xts.c b/cipher/cipher-xts.c
new file mode 100644
index 0000000..699382b
--- /dev/null
+++ b/cipher/cipher-xts.c
@@ -0,0 +1,165 @@
+/* cipher-xts.c  - XTS mode implementation
+ * Copyright (C) 2017 Jussi Kivilinna <jussi.kivilinna at iki.fi>
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser general Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "g10lib.h"
+#include "cipher.h"
+#include "bufhelp.h"
+#include "./cipher-internal.h"
+
+
+static inline void xts_gfmul_byA (unsigned char *out, const unsigned char *in)
+{
+  u64 hi = buf_get_le64 (in + 8);
+  u64 lo = buf_get_le64 (in + 0);
+  u64 carry = -(hi >> 63) & 0x87;
+
+  hi = (hi << 1) + (lo >> 63);
+  lo = (lo << 1) ^ carry;
+
+  buf_put_le64 (out + 8, hi);
+  buf_put_le64 (out + 0, lo);
+}
+
+
+static inline void xts_inc128 (unsigned char *seqno)
+{
+  u64 lo = buf_get_le64 (seqno + 0);
+  u64 hi = buf_get_le64 (seqno + 8);
+
+  hi += !(++lo);
+
+  buf_put_le64 (seqno + 0, lo);
+  buf_put_le64 (seqno + 8, hi);
+}
+
+
+gcry_err_code_t
+_gcry_cipher_xts_crypt (gcry_cipher_hd_t c,
+			unsigned char *outbuf, size_t outbuflen,
+			const unsigned char *inbuf, size_t inbuflen,
+			int encrypt)
+{
+  gcry_cipher_encrypt_t tweak_fn = c->spec->encrypt;
+  gcry_cipher_encrypt_t crypt_fn =
+    encrypt ? c->spec->encrypt : c->spec->decrypt;
+  unsigned char tmp[GCRY_XTS_BLOCK_LEN];
+  unsigned int burn, nburn;
+  size_t nblocks;
+
+  if (c->spec->blocksize != GCRY_XTS_BLOCK_LEN)
+    return GPG_ERR_CIPHER_ALGO;
+  if (outbuflen < inbuflen)
+    return GPG_ERR_BUFFER_TOO_SHORT;
+  if (inbuflen < GCRY_XTS_BLOCK_LEN)
+    return GPG_ERR_BUFFER_TOO_SHORT;
+
+  /* Data-unit max length: 2^20 blocks. */
+  if (inbuflen > GCRY_XTS_BLOCK_LEN << 20)
+    return GPG_ERR_INV_LENGTH;
+
+  nblocks = inbuflen / GCRY_XTS_BLOCK_LEN;
+  nblocks -= !encrypt && (inbuflen % GCRY_XTS_BLOCK_LEN) != 0;
+
+  /* Generate first tweak value.  */
+  burn = tweak_fn (c->u_mode.xts.tweak_context, c->u_ctr.ctr, c->u_iv.iv);
+
+  /* Use a bulk method if available.  */
+  if (nblocks && c->bulk.xts_crypt)
+    {
+      c->bulk.xts_crypt (c, c->u_ctr.ctr, outbuf, inbuf, nblocks, encrypt);
+      inbuf  += nblocks * GCRY_XTS_BLOCK_LEN;
+      outbuf += nblocks * GCRY_XTS_BLOCK_LEN;
+      inbuflen -= nblocks * GCRY_XTS_BLOCK_LEN;
+      nblocks = 0;
+    }
+
+  /* If we don't have a bulk method use the standard method.  We also
+    use this method for the a remaining partial block.  */
+
+  while (nblocks)
+    {
+      /* Xor-Encrypt/Decrypt-Xor block. */
+      buf_xor (tmp, inbuf, c->u_ctr.ctr, GCRY_XTS_BLOCK_LEN);
+      nburn = crypt_fn (&c->context.c, tmp, tmp);
+      burn = nburn > burn ? nburn : burn;
+      buf_xor (outbuf, tmp, c->u_ctr.ctr, GCRY_XTS_BLOCK_LEN);
+
+      outbuf += GCRY_XTS_BLOCK_LEN;
+      inbuf += GCRY_XTS_BLOCK_LEN;
+      inbuflen -= GCRY_XTS_BLOCK_LEN;
+      nblocks--;
+
+      /* Generate next tweak. */
+      xts_gfmul_byA (c->u_ctr.ctr, c->u_ctr.ctr);
+    }
+
+  /* Handle remaining data with ciphertext stealing. */
+  if (inbuflen)
+    {
+      if (!encrypt)
+	{
+	  gcry_assert (inbuflen > GCRY_XTS_BLOCK_LEN);
+	  gcry_assert (inbuflen < GCRY_XTS_BLOCK_LEN * 2);
+
+	  /* Generate last tweak. */
+	  xts_gfmul_byA (tmp, c->u_ctr.ctr);
+
+	  /* Decrypt last block first. */
+	  buf_xor (outbuf, inbuf, tmp, GCRY_XTS_BLOCK_LEN);
+	  nburn = crypt_fn (&c->context.c, outbuf, outbuf);
+	  burn = nburn > burn ? nburn : burn;
+	  buf_xor (outbuf, outbuf, tmp, GCRY_XTS_BLOCK_LEN);
+
+	  inbuflen -= GCRY_XTS_BLOCK_LEN;
+	  inbuf += GCRY_XTS_BLOCK_LEN;
+	  outbuf += GCRY_XTS_BLOCK_LEN;
+	}
+
+      gcry_assert (inbuflen < GCRY_XTS_BLOCK_LEN);
+      outbuf -= GCRY_XTS_BLOCK_LEN;
+
+      /* Steal ciphertext from previous block. */
+      buf_cpy (tmp, outbuf, GCRY_XTS_BLOCK_LEN);
+      buf_cpy (tmp, inbuf, inbuflen);
+      buf_cpy (outbuf + GCRY_XTS_BLOCK_LEN, outbuf, inbuflen);
+
+      /* Decrypt/Encrypt last block. */
+      buf_xor (tmp, tmp, c->u_ctr.ctr, GCRY_XTS_BLOCK_LEN);
+      nburn = crypt_fn (&c->context.c, tmp, tmp);
+      burn = nburn > burn ? nburn : burn;
+      buf_xor (outbuf, tmp, c->u_ctr.ctr, GCRY_XTS_BLOCK_LEN);
+    }
+
+  /* Auto-increment data-unit sequence number */
+  xts_inc128 (c->u_iv.iv);
+
+  wipememory (tmp, sizeof(tmp));
+  wipememory (c->u_ctr.ctr, sizeof(c->u_ctr.ctr));
+
+  if (burn > 0)
+    _gcry_burn_stack (burn + 4 * sizeof(void *));
+
+  return 0;
+}
diff --git a/cipher/cipher.c b/cipher/cipher.c
index 55853da..aa4e925 100644
--- a/cipher/cipher.c
+++ b/cipher/cipher.c
@@ -405,6 +405,13 @@ _gcry_cipher_open_internal (gcry_cipher_hd_t *handle,
 	  err = GPG_ERR_INV_CIPHER_MODE;
 	break;
 
+      case GCRY_CIPHER_MODE_XTS:
+	if (spec->blocksize != GCRY_XTS_BLOCK_LEN)
+	  err = GPG_ERR_INV_CIPHER_MODE;
+	if (!spec->encrypt || !spec->decrypt)
+	  err = GPG_ERR_INV_CIPHER_MODE;
+	break;
+
       case GCRY_CIPHER_MODE_ECB:
       case GCRY_CIPHER_MODE_CBC:
       case GCRY_CIPHER_MODE_CFB:
@@ -468,6 +475,18 @@ _gcry_cipher_open_internal (gcry_cipher_hd_t *handle,
 #endif /*NEED_16BYTE_ALIGNED_CONTEXT*/
                      );
 
+      /* Space needed per mode.  */
+      switch (mode)
+	{
+	case GCRY_CIPHER_MODE_XTS:
+	  /* Additional cipher context for tweak. */
+	  size += 2 * spec->contextsize + 15;
+	  break;
+
+	default:
+	  break;
+	}
+
       if (secure)
 	h = xtrycalloc_secure (1, size);
       else
@@ -478,6 +497,7 @@ _gcry_cipher_open_internal (gcry_cipher_hd_t *handle,
       else
 	{
           size_t off = 0;
+	  char *tc;
 
 #ifdef NEED_16BYTE_ALIGNED_CONTEXT
           if ( ((uintptr_t)h & 0x0f) )
@@ -578,6 +598,13 @@ _gcry_cipher_open_internal (gcry_cipher_hd_t *handle,
               h->u_mode.ocb.taglen = 16; /* Bytes.  */
               break;
 
+	    case GCRY_CIPHER_MODE_XTS:
+	      tc = h->context.c + spec->contextsize * 2;
+	      tc += (16 - (uintptr_t)tc % 16) % 16;
+	      h->u_mode.xts.tweak_context = tc;
+
+	      break;
+
             default:
               break;
             }
@@ -630,6 +657,14 @@ cipher_setkey (gcry_cipher_hd_t c, byte *key, size_t keylen)
 {
   gcry_err_code_t rc;
 
+  if (c->mode == GCRY_CIPHER_MODE_XTS)
+    {
+      /* XTS uses two keys. */
+      if (keylen % 2)
+	return GPG_ERR_INV_KEYLEN;
+      keylen /= 2;
+    }
+
   rc = c->spec->setkey (&c->context.c, key, keylen);
   if (!rc)
     {
@@ -653,6 +688,20 @@ cipher_setkey (gcry_cipher_hd_t c, byte *key, size_t keylen)
           _gcry_cipher_poly1305_setkey (c);
           break;
 
+	case GCRY_CIPHER_MODE_XTS:
+	  /* Setup tweak cipher with second part of XTS key. */
+	  rc = c->spec->setkey (c->u_mode.xts.tweak_context, key + keylen,
+				keylen);
+	  if (!rc)
+	    {
+	      /* Duplicate initial tweak context.  */
+	      memcpy (c->u_mode.xts.tweak_context + c->spec->contextsize,
+		      c->u_mode.xts.tweak_context, c->spec->contextsize);
+	    }
+	  else
+	    c->marks.key = 0;
+	  break;
+
         default:
           break;
         };
@@ -751,6 +800,12 @@ cipher_reset (gcry_cipher_hd_t c)
       c->u_mode.ocb.taglen = 16;
       break;
 
+    case GCRY_CIPHER_MODE_XTS:
+      memcpy (c->u_mode.xts.tweak_context,
+	      c->u_mode.xts.tweak_context + c->spec->contextsize,
+	      c->spec->contextsize);
+      break;
+
     default:
       break; /* u_mode unused by other modes. */
     }
@@ -872,6 +927,10 @@ cipher_encrypt (gcry_cipher_hd_t c, byte *outbuf, size_t outbuflen,
       rc = _gcry_cipher_ocb_encrypt (c, outbuf, outbuflen, inbuf, inbuflen);
       break;
 
+    case GCRY_CIPHER_MODE_XTS:
+      rc = _gcry_cipher_xts_crypt (c, outbuf, outbuflen, inbuf, inbuflen, 1);
+      break;
+
     case GCRY_CIPHER_MODE_STREAM:
       c->spec->stencrypt (&c->context.c,
                           outbuf, (byte*)/*arggg*/inbuf, inbuflen);
@@ -995,6 +1054,10 @@ cipher_decrypt (gcry_cipher_hd_t c, byte *outbuf, size_t outbuflen,
       rc = _gcry_cipher_ocb_decrypt (c, outbuf, outbuflen, inbuf, inbuflen);
       break;
 
+    case GCRY_CIPHER_MODE_XTS:
+      rc = _gcry_cipher_xts_crypt (c, outbuf, outbuflen, inbuf, inbuflen, 0);
+      break;
+
     case GCRY_CIPHER_MODE_STREAM:
       c->spec->stdecrypt (&c->context.c,
                           outbuf, (byte*)/*arggg*/inbuf, inbuflen);
diff --git a/doc/gcrypt.texi b/doc/gcrypt.texi
index 47ac19e..80c369b 100644
--- a/doc/gcrypt.texi
+++ b/doc/gcrypt.texi
@@ -1692,6 +1692,23 @@ set to 12 (for 96 bit) or 8 (for 64 bit) provided for the
 
 Note that the use of @code{gcry_cipher_final} is required.
 
+ at item  GCRY_CIPHER_MODE_XTS
+ at cindex XTS, XTS mode
+XEX-based tweaked-codebook mode with ciphertext stealing (XTS) mode
+is used to implement the AES-XTS as specified in IEEE 1619 Standard
+Architecture for Encrypted Shared Storage Media and NIST SP800-38E.
+
+The XTS mode requires doubling key-length, for example, using 512-bit
+key with AES-256 (@code{GCRY_CIPHER_AES256}). The 128-bit tweak value
+is feed to XTS mode as little-endian byte array using
+ at code{gcry_cipher_setiv} function. When encrypting or decrypting,
+full-sized data unit buffers needs to be passed to
+ at code{gcry_cipher_encrypt} or @code{gcry_cipher_decrypt}. The tweak
+value is automatically incremented after each call of
+ at code{gcry_cipher_encrypt} and @code{gcry_cipher_decrypt}.
+Auto-increment allows avoiding need of setting IV between processing
+of sequential data units.
+
 @end table
 
 @node Working with cipher handles
@@ -1725,9 +1742,9 @@ ChaCha20 stream cipher. The block cipher modes
 @code{GCRY_CIPHER_MODE_CFB}, @code{GCRY_CIPHER_MODE_OFB} and
 @code{GCRY_CIPHER_MODE_CTR}) will work with any block cipher
 algorithm.  GCM mode (@code{GCRY_CIPHER_MODE_CCM}), CCM mode
-(@code{GCRY_CIPHER_MODE_GCM}), and OCB mode
-(@code{GCRY_CIPHER_MODE_OCB}) will only work with block cipher
-algorithms which have the block size of 16 bytes.
+(@code{GCRY_CIPHER_MODE_GCM}), OCB mode (@code{GCRY_CIPHER_MODE_OCB}),
+and XTS mode (@code{GCRY_CIPHER_MODE_XTS}) will only work
+with block cipher algorithms which have the block size of 16 bytes.
 
 The third argument @var{flags} can either be passed as @code{0} or as
 the bit-wise OR of the following constants.
diff --git a/src/gcrypt.h.in b/src/gcrypt.h.in
index 77ff947..a0fdaf9 100644
--- a/src/gcrypt.h.in
+++ b/src/gcrypt.h.in
@@ -961,7 +961,8 @@ enum gcry_cipher_modes
     GCRY_CIPHER_MODE_GCM      = 9,   /* Galois Counter Mode. */
     GCRY_CIPHER_MODE_POLY1305 = 10,  /* Poly1305 based AEAD mode. */
     GCRY_CIPHER_MODE_OCB      = 11,  /* OCB3 mode.  */
-    GCRY_CIPHER_MODE_CFB8     = 12   /* Cipher feedback (8 bit mode). */
+    GCRY_CIPHER_MODE_CFB8     = 12,  /* Cipher feedback (8 bit mode). */
+    GCRY_CIPHER_MODE_XTS      = 13  /* XTS mode.  */
   };
 
 /* Flags used with the open function. */
@@ -982,6 +983,9 @@ enum gcry_cipher_flags
 /* OCB works only with blocks of 128 bits.  */
 #define GCRY_OCB_BLOCK_LEN  (128 / 8)
 
+/* XTS works only with blocks of 128 bits.  */
+#define GCRY_XTS_BLOCK_LEN  (128 / 8)
+
 /* Create a handle for algorithm ALGO to be used in MODE.  FLAGS may
    be given as an bitwise OR of the gcry_cipher_flags values. */
 gcry_error_t gcry_cipher_open (gcry_cipher_hd_t *handle,
diff --git a/tests/basic.c b/tests/basic.c
index 9223222..9b6fc13 100644
--- a/tests/basic.c
+++ b/tests/basic.c
@@ -3831,6 +3831,337 @@ check_ocb_cipher (void)
   check_ocb_cipher_splitaad ();
 }
 
+
+
+static void
+do_check_xts_cipher (int inplace)
+{
+  /* Note that we use hex strings and not binary strings in TV.  That
+     makes it easier to maintain the test vectors.  */
+  static const struct
+  {
+    int algo;
+    const char *key;    /* NULL means "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F" */
+    const char *iv;
+    const char *plain;
+    const char *ciph;
+  } tv[] = {
+    /* CAVS; hex/XTSGenAES128.rsp; COUNT=100 */
+    { GCRY_CIPHER_AES,
+      "bcb6613c495de4bdad9c19f04e4b3915f9ecb379e1a575b633337e934fca1050",
+      "64981173159d58ac355a20120c8e81f1",
+      "189acacee06dfa7c94484c7dae59e166",
+      "7900191d0f19a97668fdba9def84eedc"
+    },
+    /* CAVS; hex/XTSGenAES128.rsp; COUNT=101 */
+    { GCRY_CIPHER_AES,
+      "b7b93f516aef295eff3a29d837cf1f135347e8a21dae616ff5062b2e8d78ce5e",
+      "873edea653b643bd8bcf51403197ed14",
+      "236f8a5b58dd55f6194ed70c4ac1a17f1fe60ec9a6c454d087ccb77d6b638c47",
+      "22e6a3c6379dcf7599b052b5a749c7f78ad8a11b9f1aa9430cf3aef445682e19"
+    },
+    /* CAVS; hex/XTSGenAES128.rsp; COUNT=301 */
+    { GCRY_CIPHER_AES,
+      "394c97881abd989d29c703e48a72b397a7acf51b59649eeea9b33274d8541df4",
+      "4b15c684a152d485fe9937d39b168c29",
+      "2f3b9dcfbae729583b1d1ffdd16bb6fe2757329435662a78f0",
+      "f3473802e38a3ffef4d4fb8e6aa266ebde553a64528a06463e"
+    },
+    /* CAVS; hex/XTSGenAES128.rsp; COUNT=500 */
+    { GCRY_CIPHER_AES,
+      "783a83ec52a27405dff9de4c57f9c979b360b6a5df88d67ec1a052e6f582a717",
+      "886e975b29bdf6f0c01bb47f61f6f0f5",
+      "b04d84da856b9a59ce2d626746f689a8051dacd6bce3b990aa901e4030648879",
+      "f941039ebab8cac39d59247cbbcb4d816c726daed11577692c55e4ac6d3e6820"
+    },
+    /* CAVS; hex/XTSGenAES256.rsp; COUNT=1 */
+    { GCRY_CIPHER_AES256,
+      "1ea661c58d943a0e4801e42f4b0947149e7f9f8e3e68d0c7505210bd311a0e7c"
+      "d6e13ffdf2418d8d1911c004cda58da3d619b7e2b9141e58318eea392cf41b08",
+      "adf8d92627464ad2f0428e84a9f87564",
+      "2eedea52cd8215e1acc647e810bbc3642e87287f8d2e57e36c0a24fbc12a202e",
+      "cbaad0e2f6cea3f50b37f934d46a9b130b9d54f07e34f36af793e86f73c6d7db"
+    },
+    /* CAVS; hex/XTSGenAES256.rsp; COUNT=101 */
+    { GCRY_CIPHER_AES256,
+      "266c336b3b01489f3267f52835fd92f674374b88b4e1ebd2d36a5f457581d9d0"
+      "42c3eef7b0b7e5137b086496b4d9e6ac658d7196a23f23f036172fdb8faee527",
+      "06b209a7a22f486ecbfadb0f3137ba42",
+      "ca7d65ef8d3dfad345b61ccddca1ad81de830b9e86c7b426d76cb7db766852d9"
+      "81c6b21409399d78f42cc0b33a7bbb06",
+      "c73256870cc2f4dd57acc74b5456dbd776912a128bc1f77d72cdebbf270044b7"
+      "a43ceed29025e1e8be211fa3c3ed002d"
+    },
+    /* CAVS; hex/XTSGenAES256.rsp; COUNT=401 */
+    { GCRY_CIPHER_AES256,
+      "33e89e817ff8d037d6ac5a2296657503f20885d94c483e26449066bd9284d130"
+      "2dbdbb4b66b6b9f4687f13dd028eb6aa528ca91deb9c5f40db93218806033801",
+      "a78c04335ab7498a52b81ed74b48e6cf",
+      "14c3ac31291b075f40788247c3019e88c7b40bac3832da45bbc6c4fe7461371b"
+      "4dfffb63f71c9f8edb98f28ff4f33121",
+      "dead7e587519bc78c70d99279fbe3d9b1ad13cdaae69824e0ab8135413230bfd"
+      "b13babe8f986fbb30d46ab5ec56b916e"
+    },
+    /* From https://github.com/heisencoder/XTS-AES/blob/master/testvals/ */
+    { GCRY_CIPHER_AES,
+      "fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0",
+      "9a785634120000000000000000000000",
+      "000102030405060708090a0b0c0d0e0f10",
+      "7fb2e8beccbb5c118aa52ddca31220bb1b"
+    },
+    { GCRY_CIPHER_AES,
+      "fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0bfbebdbcbbbab9b8b7b6b5b4b3b2b1b0",
+      "9a785634120000000000000000000000",
+      "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e",
+      "d05bc090a8e04f1b3d3ecdd5baec0fd4edbf9dace45d6f6a7306e64be5dd82"
+    },
+    { GCRY_CIPHER_AES,
+      "2718281828459045235360287471352631415926535897932384626433832795",
+      "00000000000000000000000000000000",
+      "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F"
+      "20212223",
+      "27A7479BEFA1D476489F308CD4CFA6E288F548E5C4239F91712A587E2B05AC3D"
+      "A96E4BBE"
+    },
+    { GCRY_CIPHER_AES256,
+      "2718281828459045235360287471352662497757247093699959574966967627"
+      "3141592653589793238462643383279502884197169399375105820974944592",
+      "11000000000000000000000000000000",
+      "3A060A8CAD115A6F44572E3759E43C8F8832FEDC28A8E35B357B5CF3EDBEF788"
+      "CAD8BFCB23",
+      "6D1C78A8BAD91DB2924C507CCEDE835F5BADD157DA0AF55C98BBC28CF676F9FA"
+      "61618FA696"
+    },
+    { GCRY_CIPHER_AES256,
+      "2718281828459045235360287471352662497757247093699959574966967627"
+      "3141592653589793238462643383279502884197169399375105820974944592",
+      "11000000000000000000000000000000",
+      "3A060A8CAD115A6F44572E3759E43C8F8832FEDC28A8E35B357B5CF3EDBEF788"
+      "CAD8BFCB23",
+      "6D1C78A8BAD91DB2924C507CCEDE835F5BADD157DA0AF55C98BBC28CF676F9FA"
+      "61618FA696"
+    },
+    { GCRY_CIPHER_AES,
+      "e0e1e2e3e4e5e6e7e8e9eaebecedeeefc0c1c2c3c4c5c6c7c8c9cacbcccdcecf",
+      "21436587a90000000000000000000000",
+      "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"
+      "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f"
+      "404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f"
+      "606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f"
+      "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f"
+      "a0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf"
+      "c0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedf"
+      "e0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"
+      "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"
+      "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f"
+      "404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f"
+      "606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f"
+      "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f"
+      "a0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf"
+      "c0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedf"
+      "e0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"
+      "0001020304050607",
+      "38b45812ef43a05bd957e545907e223b954ab4aaf088303ad910eadf14b42be6"
+      "8b2461149d8c8ba85f992be970bc621f1b06573f63e867bf5875acafa04e42cc"
+      "bd7bd3c2a0fb1fff791ec5ec36c66ae4ac1e806d81fbf709dbe29e471fad3854"
+      "9c8e66f5345d7c1eb94f405d1ec785cc6f6a68f6254dd8339f9d84057e01a177"
+      "41990482999516b5611a38f41bb6478e6f173f320805dd71b1932fc333cb9ee3"
+      "9936beea9ad96fa10fb4112b901734ddad40bc1878995f8e11aee7d141a2f5d4"
+      "8b7a4e1e7f0b2c04830e69a4fd1378411c2f287edf48c6c4e5c247a19680f7fe"
+      "41cefbd49b582106e3616cbbe4dfb2344b2ae9519391f3e0fb4922254b1d6d2d"
+      "19c6d4d537b3a26f3bcc51588b32f3eca0829b6a5ac72578fb814fb43cf80d64"
+      "a233e3f997a3f02683342f2b33d25b492536b93becb2f5e1a8b82f5b88334272"
+      "9e8ae09d16938841a21a97fb543eea3bbff59f13c1a18449e398701c1ad51648"
+      "346cbc04c27bb2da3b93a1372ccae548fb53bee476f9e9c91773b1bb19828394"
+      "d55d3e1a20ed69113a860b6829ffa847224604435070221b257e8dff783615d2"
+      "cae4803a93aa4334ab482a0afac9c0aeda70b45a481df5dec5df8cc0f423c77a"
+      "5fd46cd312021d4b438862419a791be03bb4d97c0e59578542531ba466a83baf"
+      "92cefc151b5cc1611a167893819b63fb37ec662bc0fc907db74a94468a55a7bc"
+      "8a6b18e86de60290"
+    },
+    { GCRY_CIPHER_AES256,
+      "2718281828459045235360287471352662497757247093699959574966967627"
+      "3141592653589793238462643383279502884197169399375105820974944592",
+      "ffffffff000000000000000000000000",
+      "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"
+      "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f"
+      "404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f"
+      "606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f"
+      "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f"
+      "a0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf"
+      "c0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedf"
+      "e0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"
+      "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"
+      "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f"
+      "404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f"
+      "606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f"
+      "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f"
+      "a0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf"
+      "c0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedf"
+      "e0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
+      "bf53d2dade78e822a4d949a9bc6766b01b06a8ef70d26748c6a7fc36d80ae4c5"
+      "520f7c4ab0ac8544424fa405162fef5a6b7f229498063618d39f0003cb5fb8d1"
+      "c86b643497da1ff945c8d3bedeca4f479702a7a735f043ddb1d6aaade3c4a0ac"
+      "7ca7f3fa5279bef56f82cd7a2f38672e824814e10700300a055e1630b8f1cb0e"
+      "919f5e942010a416e2bf48cb46993d3cb6a51c19bacf864785a00bc2ecff15d3"
+      "50875b246ed53e68be6f55bd7e05cfc2b2ed6432198a6444b6d8c247fab941f5"
+      "69768b5c429366f1d3f00f0345b96123d56204c01c63b22ce78baf116e525ed9"
+      "0fdea39fa469494d3866c31e05f295ff21fea8d4e6e13d67e47ce722e9698a1c"
+      "1048d68ebcde76b86fcf976eab8aa9790268b7068e017a8b9b749409514f1053"
+      "027fd16c3786ea1bac5f15cb79711ee2abe82f5cf8b13ae73030ef5b9e4457e7"
+      "5d1304f988d62dd6fc4b94ed38ba831da4b7634971b6cd8ec325d9c61c00f1df"
+      "73627ed3745a5e8489f3a95c69639c32cd6e1d537a85f75cc844726e8a72fc00"
+      "77ad22000f1d5078f6b866318c668f1ad03d5a5fced5219f2eabbd0aa5c0f460"
+      "d183f04404a0d6f469558e81fab24a167905ab4c7878502ad3e38fdbe62a4155"
+      "6cec37325759533ce8f25f367c87bb5578d667ae93f9e2fd99bcbc5f2fbba88c"
+      "f6516139420fcff3b7361d86322c4bd84c82f335abb152c4a93411373aaa8220"
+    }
+  };
+  gpg_error_t err = 0;
+  gcry_cipher_hd_t hde, hdd;
+  int tidx;
+
+  if (verbose)
+    fprintf (stderr, "  Starting XTS checks.\n");
+
+  for (tidx = 0; tidx < DIM (tv); tidx++)
+    {
+      const char *hexkey = tv[tidx].key;
+      char *key, *iv, *ciph, *plain, *out;
+      size_t keylen, ivlen, ciphlen, plainlen, outlen;
+
+      if (verbose)
+        fprintf (stderr, "    checking XTS mode for %s [%i] (tv %d)\n",
+                 gcry_cipher_algo_name (tv[tidx].algo), tv[tidx].algo, tidx);
+
+      if (!hexkey)
+	hexkey = "000102030405060708090A0B0C0D0E0F"
+	         "101112131415161718191A1B1C1D1E1F";
+
+      /* Convert to hex strings to binary.  */
+      key   = hex2buffer (hexkey, &keylen);
+      iv    = hex2buffer (tv[tidx].iv, &ivlen);
+      plain = hex2buffer (tv[tidx].plain, &plainlen);
+      ciph  = hex2buffer (tv[tidx].ciph, &ciphlen);
+      outlen = plainlen + 5;
+      out   = xmalloc (outlen);
+
+      assert (plainlen == ciphlen);
+      assert (plainlen <= outlen);
+      assert (out);
+
+      err = gcry_cipher_open (&hde, tv[tidx].algo, GCRY_CIPHER_MODE_XTS, 0);
+      if (!err)
+        err = gcry_cipher_open (&hdd, tv[tidx].algo, GCRY_CIPHER_MODE_XTS, 0);
+      if (err)
+        {
+          fail ("cipher-xts, gcry_cipher_open failed (tv %d): %s\n",
+                tidx, gpg_strerror (err));
+          return;
+        }
+
+      err = gcry_cipher_setkey (hde, key, keylen);
+      if (!err)
+        err = gcry_cipher_setkey (hdd, key, keylen);
+      if (err)
+        {
+          fail ("cipher-ocb, gcry_cipher_setkey failed (tv %d): %s\n",
+                tidx, gpg_strerror (err));
+          gcry_cipher_close (hde);
+          gcry_cipher_close (hdd);
+          return;
+        }
+
+      err = gcry_cipher_setiv (hde, iv, ivlen);
+      if (!err)
+        err = gcry_cipher_setiv (hdd, iv, ivlen);
+      if (err)
+        {
+          fail ("cipher-ocb, gcry_cipher_setiv failed (tv %d): %s\n",
+                tidx, gpg_strerror (err));
+          gcry_cipher_close (hde);
+          gcry_cipher_close (hdd);
+          return;
+        }
+
+      if (inplace)
+	{
+	  memcpy(out, plain, plainlen);
+	  err = gcry_cipher_encrypt (hde, out, plainlen, NULL, 0);
+	}
+      else
+	{
+	  err = gcry_cipher_encrypt (hde, out, outlen, plain, plainlen);
+	}
+      if (err)
+        {
+          fail ("cipher-xts, gcry_cipher_encrypt failed (tv %d): %s\n",
+                tidx, gpg_strerror (err));
+          gcry_cipher_close (hde);
+          gcry_cipher_close (hdd);
+          return;
+        }
+
+      /* Check that the encrypt output matches the expected cipher text.  */
+      if (memcmp (ciph, out, plainlen))
+        {
+          mismatch (ciph, plainlen, out, plainlen);
+          fail ("cipher-xts, encrypt data mismatch (tv %d)\n", tidx);
+        }
+
+      /* Now for the decryption.  */
+      if (inplace)
+	{
+	  err = gcry_cipher_decrypt (hdd, out, plainlen, NULL, 0);
+	}
+      else
+	{
+	  memcpy(ciph, out, ciphlen);
+	  err = gcry_cipher_decrypt (hdd, out, plainlen, ciph, ciphlen);
+	}
+      if (err)
+        {
+          fail ("cipher-xts, gcry_cipher_decrypt (tv %d) failed: %s\n",
+                tidx, gpg_strerror (err));
+          gcry_cipher_close (hde);
+          gcry_cipher_close (hdd);
+          return;
+        }
+
+      /* Check that the decrypt output matches the expected plain text.  */
+      if (memcmp (plain, out, plainlen))
+        {
+          mismatch (plain, plainlen, out, plainlen);
+          fail ("cipher-xts, decrypt data mismatch (tv %d)\n", tidx);
+        }
+
+      gcry_cipher_close (hde);
+      gcry_cipher_close (hdd);
+
+      xfree (iv);
+      xfree (ciph);
+      xfree (plain);
+      xfree (key);
+      xfree (out);
+    }
+
+  if (verbose)
+    fprintf (stderr, "  Completed XTS checks.\n");
+}
+
+
+static void
+check_xts_cipher (void)
+{
+  /* Check XTS cipher with separate destination and source buffers for
+   * encryption/decryption. */
+  do_check_xts_cipher(0);
+
+  /* Check XTS cipher with inplace encrypt/decrypt. */
+  do_check_xts_cipher(1);
+}
+
+
 static void
 check_gost28147_cipher (void)
 {
@@ -5233,6 +5564,20 @@ check_bulk_cipher_modes (void)
 /*[14]*/
       { 0x2d, 0x71, 0x54, 0xb9, 0xc5, 0x28, 0x76, 0xff, 0x76, 0xb5,
         0x99, 0x37, 0x99, 0x9d, 0xf7, 0x10, 0x6d, 0x86, 0x4f, 0x3f }
+    },
+    { GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_XTS,
+      "abcdefghijklmnopABCDEFGHIJKLMNOP", 32,
+      "1234567890123456", 16,
+/*[15]*/
+      { 0x71, 0x46, 0x40, 0xb0, 0xed, 0x6f, 0xc4, 0x82, 0x2b, 0x3f,
+        0xb6, 0xf7, 0x81, 0x08, 0x4c, 0x8b, 0xc1, 0x66, 0x4c, 0x1b }
+    },
+    { GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_XTS,
+      "abcdefghijklmnopABCDEFGHIJKLMNOP_abcdefghijklmnopABCDEFGHIJKLMNO", 64,
+      "1234567890123456", 16,
+/*[16]*/
+      { 0x8e, 0xbc, 0xa5, 0x21, 0x0a, 0x4b, 0x53, 0x14, 0x79, 0x81,
+        0x25, 0xad, 0x24, 0x45, 0x98, 0xbd, 0x9f, 0x27, 0x5f, 0x01 }
     }
   };
   gcry_cipher_hd_t hde = NULL;
@@ -5437,15 +5782,16 @@ check_one_cipher_core (int algo, int mode, int flags,
 
   blklen = get_algo_mode_blklen(algo, mode);
 
-  assert (nkey == 32);
+  assert (nkey == 64);
   assert (nplain == 1040);
   assert (sizeof(in_buffer) == nplain + 1);
   assert (sizeof(out_buffer) == sizeof(in_buffer));
   assert (blklen > 0);
 
-  if (mode == GCRY_CIPHER_MODE_CBC && (flags & GCRY_CIPHER_CBC_CTS))
+  if ((mode == GCRY_CIPHER_MODE_CBC && (flags & GCRY_CIPHER_CBC_CTS)) ||
+      mode == GCRY_CIPHER_MODE_XTS)
     {
-      /* TODO: examine why CBC with CTS fails. */
+      /* Input cannot be split in to multiple operations with CTS . */
       blklen = nplain;
     }
 
@@ -5484,6 +5830,11 @@ check_one_cipher_core (int algo, int mode, int flags,
       return -1;
     }
 
+  if (mode == GCRY_CIPHER_MODE_XTS)
+    {
+      keylen *= 2;
+    }
+
   err = gcry_cipher_open (&hd, algo, mode, flags);
   if (err)
     {
@@ -5695,14 +6046,15 @@ check_one_cipher_core (int algo, int mode, int flags,
 static void
 check_one_cipher (int algo, int mode, int flags)
 {
-  char key[32+1];
+  char key[64+1];
   unsigned char plain[1040+1];
   int bufshift, i;
 
   for (bufshift=0; bufshift < 4; bufshift++)
     {
       /* Pass 0: Standard test.  */
-      memcpy (key, "0123456789abcdef.,;/[]{}-=ABCDEF", 32);
+      memcpy (key, "0123456789abcdef.,;/[]{}-=ABCDEF_"
+		   "0123456789abcdef.,;/[]{}-=ABCDEF", 64);
       memcpy (plain, "foobar42FOOBAR17", 16);
       for (i = 16; i < 1040; i += 16)
         {
@@ -5713,25 +6065,25 @@ check_one_cipher (int algo, int mode, int flags)
             plain[i+14]++;
         }
 
-      if (check_one_cipher_core (algo, mode, flags, key, 32, plain, 1040,
+      if (check_one_cipher_core (algo, mode, flags, key, 64, plain, 1040,
                                  bufshift, 0+10*bufshift))
         return;
 
       /* Pass 1: Key not aligned.  */
-      memmove (key+1, key, 32);
-      if (check_one_cipher_core (algo, mode, flags, key+1, 32, plain, 1040,
+      memmove (key+1, key, 64);
+      if (check_one_cipher_core (algo, mode, flags, key+1, 64, plain, 1040,
                                  bufshift, 1+10*bufshift))
         return;
 
       /* Pass 2: Key not aligned and data not aligned.  */
       memmove (plain+1, plain, 1040);
-      if (check_one_cipher_core (algo, mode, flags, key+1, 32, plain+1, 1040,
+      if (check_one_cipher_core (algo, mode, flags, key+1, 64, plain+1, 1040,
                                  bufshift, 2+10*bufshift))
         return;
 
       /* Pass 3: Key aligned and data not aligned.  */
-      memmove (key, key+1, 32);
-      if (check_one_cipher_core (algo, mode, flags, key, 32, plain+1, 1040,
+      memmove (key, key+1, 64);
+      if (check_one_cipher_core (algo, mode, flags, key, 64, plain+1, 1040,
                                  bufshift, 3+10*bufshift))
         return;
     }
@@ -5831,6 +6183,8 @@ check_ciphers (void)
         check_one_cipher (algos[i], GCRY_CIPHER_MODE_GCM, 0);
       if (gcry_cipher_get_algo_blklen (algos[i]) == GCRY_OCB_BLOCK_LEN)
         check_one_cipher (algos[i], GCRY_CIPHER_MODE_OCB, 0);
+      if (gcry_cipher_get_algo_blklen (algos[i]) == GCRY_XTS_BLOCK_LEN)
+        check_one_cipher (algos[i], GCRY_CIPHER_MODE_XTS, 0);
     }
 
   for (i = 0; algos2[i]; i++)
@@ -5874,6 +6228,7 @@ check_cipher_modes(void)
   check_gcm_cipher ();
   check_poly1305_cipher ();
   check_ocb_cipher ();
+  check_xts_cipher ();
   check_gost28147_cipher ();
   check_stream_cipher ();
   check_stream_cipher_large_block ();
diff --git a/tests/bench-slope.c b/tests/bench-slope.c
index 4ed98cb..6d93ad2 100644
--- a/tests/bench-slope.c
+++ b/tests/bench-slope.c
@@ -742,6 +742,126 @@ static struct bench_ops decrypt_ops = {
 };
 
 
+static int
+bench_xts_encrypt_init (struct bench_obj *obj)
+{
+  struct bench_cipher_mode *mode = obj->priv;
+  gcry_cipher_hd_t hd;
+  int err, keylen;
+
+  /* For XTS, benchmark with typical data-unit size (512 byte sectors). */
+  obj->min_bufsize = 512;
+  obj->max_bufsize = 16 * obj->min_bufsize;
+  obj->step_size = obj->min_bufsize;
+  obj->num_measure_repetitions = num_measurement_repetitions;
+
+  err = gcry_cipher_open (&hd, mode->algo, mode->mode, 0);
+  if (err)
+    {
+      fprintf (stderr, PGM ": error opening cipher `%s'\n",
+	       gcry_cipher_algo_name (mode->algo));
+      exit (1);
+    }
+
+  /* Double key-length for XTS. */
+  keylen = gcry_cipher_get_algo_keylen (mode->algo) * 2;
+  if (keylen)
+    {
+      char key[keylen];
+      int i;
+
+      for (i = 0; i < keylen; i++)
+	key[i] = 0x33 ^ (11 - i);
+
+      err = gcry_cipher_setkey (hd, key, keylen);
+      if (err)
+	{
+	  fprintf (stderr, PGM ": gcry_cipher_setkey failed: %s\n",
+		   gpg_strerror (err));
+	  gcry_cipher_close (hd);
+	  exit (1);
+	}
+    }
+  else
+    {
+      fprintf (stderr, PGM ": failed to get key length for algorithm `%s'\n",
+	       gcry_cipher_algo_name (mode->algo));
+      gcry_cipher_close (hd);
+      exit (1);
+    }
+
+  obj->priv = hd;
+
+  return 0;
+}
+
+static void
+bench_xts_encrypt_do_bench (struct bench_obj *obj, void *buf, size_t buflen)
+{
+  gcry_cipher_hd_t hd = obj->priv;
+  unsigned int pos;
+  static const char tweak[16] = { 0xff, 0xff, 0xfe, };
+  size_t sectorlen = obj->step_size;
+  char *cbuf = buf;
+  int err;
+
+  gcry_cipher_setiv (hd, tweak, sizeof (tweak));
+
+  /* Process each sector separately. */
+
+  for (pos = 0; pos < buflen; pos += sectorlen, cbuf += sectorlen)
+    {
+      err = gcry_cipher_encrypt (hd, cbuf, sectorlen, cbuf, sectorlen);
+      if (err)
+	{
+	  fprintf (stderr, PGM ": gcry_cipher_encrypt failed: %s\n",
+		  gpg_strerror (err));
+	  gcry_cipher_close (hd);
+	  exit (1);
+	}
+    }
+}
+
+static void
+bench_xts_decrypt_do_bench (struct bench_obj *obj, void *buf, size_t buflen)
+{
+  gcry_cipher_hd_t hd = obj->priv;
+  unsigned int pos;
+  static const char tweak[16] = { 0xff, 0xff, 0xfe, };
+  size_t sectorlen = obj->step_size;
+  char *cbuf = buf;
+  int err;
+
+  gcry_cipher_setiv (hd, tweak, sizeof (tweak));
+
+  /* Process each sector separately. */
+
+  for (pos = 0; pos < buflen; pos += sectorlen, cbuf += sectorlen)
+    {
+      err = gcry_cipher_decrypt (hd, cbuf, sectorlen, cbuf, sectorlen);
+      if (err)
+	{
+	  fprintf (stderr, PGM ": gcry_cipher_encrypt failed: %s\n",
+		  gpg_strerror (err));
+	  gcry_cipher_close (hd);
+	  exit (1);
+	}
+    }
+}
+
+static struct bench_ops xts_encrypt_ops = {
+  &bench_xts_encrypt_init,
+  &bench_encrypt_free,
+  &bench_xts_encrypt_do_bench
+};
+
+static struct bench_ops xts_decrypt_ops = {
+  &bench_xts_encrypt_init,
+  &bench_encrypt_free,
+  &bench_xts_decrypt_do_bench
+};
+
+
 static void
 bench_ccm_encrypt_do_bench (struct bench_obj *obj, void *buf, size_t buflen)
 {
@@ -1166,6 +1286,8 @@ static struct bench_cipher_mode cipher_modes[] = {
   {GCRY_CIPHER_MODE_OFB, "OFB dec", &decrypt_ops},
   {GCRY_CIPHER_MODE_CTR, "CTR enc", &encrypt_ops},
   {GCRY_CIPHER_MODE_CTR, "CTR dec", &decrypt_ops},
+  {GCRY_CIPHER_MODE_XTS, "XTS enc", &xts_encrypt_ops},
+  {GCRY_CIPHER_MODE_XTS, "XTS dec", &xts_decrypt_ops},
   {GCRY_CIPHER_MODE_CCM, "CCM enc", &ccm_encrypt_ops},
   {GCRY_CIPHER_MODE_CCM, "CCM dec", &ccm_decrypt_ops},
   {GCRY_CIPHER_MODE_CCM, "CCM auth", &ccm_authenticate_ops},
@@ -1219,8 +1341,12 @@ cipher_bench_one (int algo, struct bench_cipher_mode *pmode)
   if (mode.mode == GCRY_CIPHER_MODE_GCM && blklen != GCRY_GCM_BLOCK_LEN)
     return;
 
+  /* XTS has restrictions for block-size */
+  if (mode.mode == GCRY_CIPHER_MODE_XTS && blklen != GCRY_XTS_BLOCK_LEN)
+    return;
+
   /* Our OCB implementaion has restrictions for block-size.  */
-  if (mode.mode == GCRY_CIPHER_MODE_OCB && blklen != 16)
+  if (mode.mode == GCRY_CIPHER_MODE_OCB && blklen != GCRY_OCB_BLOCK_LEN)
     return;
 
   bench_print_mode (14, mode.name);
diff --git a/tests/benchmark.c b/tests/benchmark.c
index a63cce3..44a8711 100644
--- a/tests/benchmark.c
+++ b/tests/benchmark.c
@@ -764,12 +764,15 @@ cipher_bench ( const char *algoname )
     int req_blocksize;
     int authlen;
     int noncelen;
+    int doublekey;
   } modes[] = {
     { GCRY_CIPHER_MODE_ECB, "   ECB/Stream", 1 },
     { GCRY_CIPHER_MODE_CBC, "      CBC", 1 },
     { GCRY_CIPHER_MODE_CFB, "      CFB", 0 },
     { GCRY_CIPHER_MODE_OFB, "      OFB", 0 },
     { GCRY_CIPHER_MODE_CTR, "      CTR", 0 },
+    { GCRY_CIPHER_MODE_XTS, "      XTS", 0,
+      NULL, GCRY_XTS_BLOCK_LEN, 0, 0, 1 },
     { GCRY_CIPHER_MODE_CCM, "      CCM", 0,
       ccm_aead_init, GCRY_CCM_BLOCK_LEN, 8 },
     { GCRY_CIPHER_MODE_GCM, "      GCM", 0,
@@ -841,13 +844,13 @@ cipher_bench ( const char *algoname )
 	       algoname);
       exit (1);
     }
-  if ( keylen > sizeof key )
+  if ( keylen * 2 > sizeof key )
     {
         fprintf (stderr, PGM ": algo %d, keylength problem (%d)\n",
                  algo, keylen );
         exit (1);
     }
-  for (i=0; i < keylen; i++)
+  for (i=0; i < keylen * 2; i++)
     key[i] = i + (clock () & 0xff);
 
   blklen = gcry_cipher_get_algo_blklen (algo);
@@ -863,6 +866,8 @@ cipher_bench ( const char *algoname )
 
   for (modeidx=0; modes[modeidx].mode; modeidx++)
     {
+      size_t modekeylen = keylen * (!!modes[modeidx].doublekey + 1);
+
       if ((blklen > 1 && modes[modeidx].mode == GCRY_CIPHER_MODE_STREAM)
           || (blklen == 1 && modes[modeidx].mode != GCRY_CIPHER_MODE_STREAM))
         continue;
@@ -886,7 +891,7 @@ cipher_bench ( const char *algoname )
 
       if (!cipher_with_keysetup)
         {
-          err = gcry_cipher_setkey (hd, key, keylen);
+          err = gcry_cipher_setkey (hd, key, modekeylen);
           if (err)
             {
               fprintf (stderr, "gcry_cipher_setkey failed: %s\n",
@@ -905,7 +910,7 @@ cipher_bench ( const char *algoname )
         {
           if (cipher_with_keysetup)
             {
-              err = gcry_cipher_setkey (hd, key, keylen);
+              err = gcry_cipher_setkey (hd, key, modekeylen);
               if (err)
                 {
                   fprintf (stderr, "gcry_cipher_setkey failed: %s\n",
@@ -969,7 +974,7 @@ cipher_bench ( const char *algoname )
 
       if (!cipher_with_keysetup)
         {
-          err = gcry_cipher_setkey (hd, key, keylen);
+          err = gcry_cipher_setkey (hd, key, modekeylen);
           if (err)
             {
               fprintf (stderr, "gcry_cipher_setkey failed: %s\n",
@@ -984,7 +989,7 @@ cipher_bench ( const char *algoname )
         {
           if (cipher_with_keysetup)
             {
-              err = gcry_cipher_setkey (hd, key, keylen);
+              err = gcry_cipher_setkey (hd, key, modekeylen);
               if (err)
                 {
                   fprintf (stderr, "gcry_cipher_setkey failed: %s\n",




More information about the Gcrypt-devel mailing list