[PATCH 2/4] Add AES-GCM-SIV mode (RFC 8452)

Jussi Kivilinna jussi.kivilinna at iki.fi
Fri Aug 13 17:01:27 CEST 2021


* cipher/Makefile.am: Add 'cipher-gcm-siv.c'.
* cipher/cipher-gcm-siv.c: New.
* cipher/cipher-gcm.c (_gcry_cipher_gcm_setupM): New.
* cipher/cipher-internal.h (gcry_cipher_handle): Add 'siv_keylen'.
(_gcry_cipher_gcm_setupM, _gcry_cipher_gcm_siv_encrypt)
(_gcry_cipher_gcm_siv_decrypt, _gcry_cipher_gcm_siv_set_nonce)
(_gcry_cipher_gcm_siv_authenticate)
(_gcry_cipher_gcm_siv_set_decryption_tag)
(_gcry_cipher_gcm_siv_get_tag, _gcry_cipher_gcm_siv_check_tag)
(_gcry_cipher_gcm_siv_setkey): New prototypes.
(cipher_block_bswap): New helper function.
* cipher/cipher.c (_gcry_cipher_open_internal): Add
'GCRY_CIPHER_MODE_GCM_SIV'; Refactor mode requirement checks for
better size optimization (check pointers & blocksize in same order
for all).
(cipher_setkey, cipher_reset, _gcry_cipher_setup_mode_ops)
(_gcry_cipher_setup_mode_ops, _gcry_cipher_info): Add GCM-SIV.
(_gcry_cipher_ctl): Handle 'set decryption tag' for GCM-SIV.
* doc/gcrypt.texi: Add GCM-SIV.
* src/gcrypt.h.in (GCRY_CIPHER_MODE_GCM_SIV): New.
(GCRY_SIV_BLOCK_LEN, gcry_cipher_set_decryption_tag): Add to comment
that these are also for GCM-SIV in addition to SIV mode.
Signed-off-by: Jussi Kivilinna <jussi.kivilinna at iki.fi>
* tests/basic.c (check_gcm_siv_cipher): New.
(check_cipher_modes): Check for GCM-SIV.
* tests/bench-slope.c (bench_gcm_siv_encrypt_do_bench)
(bench_gcm_siv_decrypt_do_bench, bench_gcm_siv_authenticate_do_bench)
(gcm_siv_encrypt_ops, gcm_siv_decrypt_ops)
(gcm_siv_authenticate_ops): New.
(cipher_modes): Add GCM-SIV.
(cipher_bench_one): Check key length requirement for GCM-SIV.
--

Signed-off-by: Jussi Kivilinna <jussi.kivilinna at iki.fi>
---
 cipher/Makefile.am       |   1 +
 cipher/cipher-gcm-siv.c  | 637 +++++++++++++++++++++++++++
 cipher/cipher-gcm.c      |   7 +
 cipher/cipher-internal.h |  56 ++-
 cipher/cipher-siv.c      |   2 +-
 cipher/cipher.c          |  69 ++-
 doc/gcrypt.texi          |  29 +-
 src/gcrypt.h.in          |   7 +-
 tests/basic.c            | 914 +++++++++++++++++++++++++++++++++++++++
 tests/bench-slope.c      |  62 +++
 10 files changed, 1759 insertions(+), 25 deletions(-)
 create mode 100644 cipher/cipher-gcm-siv.c

diff --git a/cipher/Makefile.am b/cipher/Makefile.am
index 4d3e0d19..801e726a 100644
--- a/cipher/Makefile.am
+++ b/cipher/Makefile.am
@@ -54,6 +54,7 @@ libcipher_la_SOURCES = \
 	cipher-xts.c \
 	cipher-eax.c \
 	cipher-siv.c \
+	cipher-gcm-siv.c \
 	cipher-selftest.c cipher-selftest.h \
 	pubkey.c pubkey-internal.h pubkey-util.c \
 	md.c \
diff --git a/cipher/cipher-gcm-siv.c b/cipher/cipher-gcm-siv.c
new file mode 100644
index 00000000..b735d199
--- /dev/null
+++ b/cipher/cipher-gcm-siv.c
@@ -0,0 +1,637 @@
+/* cipher-gcm-siv.c  - GCM-SIV implementation (RFC 8452)
+ * Copyright (C) 2021 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"
+
+
+#define GCM_SIV_NONCE_LENGTH (96 / 8)
+
+
+static inline void
+mulx_ghash (byte *a)
+{
+  u64 t[2], mask;
+
+  t[0] = buf_get_be64(a + 0);
+  t[1] = buf_get_be64(a + 8);
+  mask = -(t[1] & 1) & 0xe1;
+  mask <<= 56;
+
+  buf_put_be64(a + 8, (t[1] >> 1) ^ (t[0] << 63));
+  buf_put_be64(a + 0, (t[0] >> 1) ^ mask);
+}
+
+
+static inline void
+gcm_siv_bytecounter_add (u32 ctr[2], size_t add)
+{
+  if (sizeof(add) > sizeof(u32))
+    {
+      u32 high_add = ((add >> 31) >> 1) & 0xffffffff;
+      ctr[1] += high_add;
+    }
+
+  ctr[0] += add;
+  if (ctr[0] >= add)
+    return;
+  ++ctr[1];
+}
+
+
+static inline int
+gcm_siv_check_len (u32 ctr[2])
+{
+  /* len(plaintext/aadlen) <= 2^39-256 bits == 2^36-32 bytes == 2^32-2 blocks */
+  if (ctr[1] > 0xfU)
+    return 0;
+  if (ctr[1] < 0xfU)
+    return 1;
+
+  if (ctr[0] <= 0xffffffe0U)
+    return 1;
+
+  return 0;
+}
+
+
+static void
+polyval_set_key (gcry_cipher_hd_t c, const byte *auth_key)
+{
+  cipher_block_bswap (c->u_mode.gcm.u_ghash_key.key, auth_key,
+		      GCRY_SIV_BLOCK_LEN);
+  mulx_ghash (c->u_mode.gcm.u_ghash_key.key);
+  _gcry_cipher_gcm_setupM (c);
+}
+
+
+static void
+do_polyval_buf(gcry_cipher_hd_t c, byte *hash, const byte *buf,
+	       size_t buflen, int do_padding)
+{
+  unsigned int blocksize = GCRY_SIV_BLOCK_LEN;
+  unsigned int unused = c->u_mode.gcm.mac_unused;
+  ghash_fn_t ghash_fn = c->u_mode.gcm.ghash_fn;
+  byte tmp_blocks[16][GCRY_SIV_BLOCK_LEN];
+  size_t nblocks, n;
+  unsigned int burn = 0, nburn;
+  unsigned int num_blks_used = 0;
+
+  if (buflen == 0 && (unused == 0 || !do_padding))
+    return;
+
+  do
+    {
+      if (buflen > 0 && (buflen + unused < blocksize || unused > 0))
+        {
+          n = blocksize - unused;
+          n = n < buflen ? n : buflen;
+
+          buf_cpy (&c->u_mode.gcm.macbuf[unused], buf, n);
+
+          unused += n;
+          buf += n;
+          buflen -= n;
+        }
+      if (!buflen)
+        {
+          if (!do_padding && unused < blocksize)
+	    {
+	      break;
+	    }
+
+	  n = blocksize - unused;
+	  if (n > 0)
+	    {
+	      memset (&c->u_mode.gcm.macbuf[unused], 0, n);
+	      unused = blocksize;
+	    }
+        }
+
+      if (unused > 0)
+        {
+          gcry_assert (unused == blocksize);
+
+          /* Process one block from macbuf.  */
+          cipher_block_bswap (c->u_mode.gcm.macbuf, c->u_mode.gcm.macbuf,
+			      blocksize);
+          nburn = ghash_fn (c, hash, c->u_mode.gcm.macbuf, 1);
+          burn = nburn > burn ? nburn : burn;
+          unused = 0;
+        }
+
+      nblocks = buflen / blocksize;
+
+      while (nblocks)
+        {
+	  for (n = 0; n < (nblocks > 16 ? 16 : nblocks); n++)
+	    cipher_block_bswap (tmp_blocks[n], buf + n * blocksize, blocksize);
+
+	  num_blks_used = n > num_blks_used ? n : num_blks_used;
+
+          nburn = ghash_fn (c, hash, tmp_blocks[0], n);
+          burn = nburn > burn ? nburn : burn;
+          buf += n * blocksize;
+          buflen -= n * blocksize;
+          nblocks -= n;
+        }
+    }
+  while (buflen > 0);
+
+  c->u_mode.gcm.mac_unused = unused;
+
+  if (num_blks_used)
+    wipememory (tmp_blocks, num_blks_used * blocksize);
+  if (burn)
+    _gcry_burn_stack (burn);
+}
+
+
+static void
+do_ctr_le32 (gcry_cipher_hd_t c, byte *outbuf, const byte *inbuf,
+	     size_t inbuflen)
+{
+  gcry_cipher_encrypt_t enc_fn = c->spec->encrypt;
+  unsigned char tmp[GCRY_SIV_BLOCK_LEN];
+  unsigned int burn = 0, nburn;
+  size_t n;
+
+  if (inbuflen == 0)
+    return;
+
+  n = GCRY_SIV_BLOCK_LEN;
+  do
+    {
+      nburn = enc_fn (c->context.c, tmp, c->u_ctr.ctr);
+      burn = nburn > burn ? nburn : burn;
+
+      buf_put_le32(c->u_ctr.ctr, buf_get_le32(c->u_ctr.ctr) + 1);
+
+      if (inbuflen < GCRY_SIV_BLOCK_LEN)
+	break;
+      cipher_block_xor(outbuf, inbuf, tmp, GCRY_SIV_BLOCK_LEN);
+
+      inbuflen -= n;
+      outbuf += n;
+      inbuf += n;
+    }
+  while (inbuflen);
+
+  if (inbuflen)
+    {
+      n = inbuflen;
+      buf_xor(outbuf, inbuf, tmp, inbuflen);
+
+      inbuflen -= n;
+      outbuf += n;
+      inbuf += n;
+    }
+
+  wipememory (tmp, sizeof(tmp));
+
+  if (burn > 0)
+    _gcry_burn_stack (burn + 4 * sizeof(void *));
+}
+
+
+static int
+gcm_siv_selftest (gcry_cipher_hd_t c)
+{
+  static const byte in1[GCRY_SIV_BLOCK_LEN] =
+      "\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
+  static const byte out1[GCRY_SIV_BLOCK_LEN] =
+      "\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
+  static const byte in2[GCRY_SIV_BLOCK_LEN] =
+      "\x9c\x98\xc0\x4d\xf9\x38\x7d\xed\x82\x81\x75\xa9\x2b\xa6\x52\xd8";
+  static const byte out2[GCRY_SIV_BLOCK_LEN] =
+      "\x4e\x4c\x60\x26\xfc\x9c\x3e\xf6\xc1\x40\xba\xd4\x95\xd3\x29\x6c";
+  static const byte polyval_key[GCRY_SIV_BLOCK_LEN] =
+      "\x25\x62\x93\x47\x58\x92\x42\x76\x1d\x31\xf8\x26\xba\x4b\x75\x7b";
+  static const byte ghash_key[GCRY_SIV_BLOCK_LEN] =
+      "\xdc\xba\xa5\xdd\x13\x7c\x18\x8e\xbb\x21\x49\x2c\x23\xc9\xb1\x12";
+  static const byte polyval_data[GCRY_SIV_BLOCK_LEN * 2] =
+      "\x4f\x4f\x95\x66\x8c\x83\xdf\xb6\x40\x17\x62\xbb\x2d\x01\xa2\x62"
+      "\xd1\xa2\x4d\xdd\x27\x21\xd0\x06\xbb\xe4\x5f\x20\xd3\xc9\xf3\x62";
+  static const byte polyval_tag[GCRY_SIV_BLOCK_LEN] =
+      "\xf7\xa3\xb4\x7b\x84\x61\x19\xfa\xe5\xb7\x86\x6c\xf5\xe5\xb7\x7e";
+  byte tmp[GCRY_SIV_BLOCK_LEN];
+
+  /* Test mulx_ghash */
+  memcpy (tmp, in1, GCRY_SIV_BLOCK_LEN);
+  mulx_ghash (tmp);
+  if (memcmp (tmp, out1, GCRY_SIV_BLOCK_LEN) != 0)
+    return -1;
+
+  memcpy (tmp, in2, GCRY_SIV_BLOCK_LEN);
+  mulx_ghash (tmp);
+  if (memcmp (tmp, out2, GCRY_SIV_BLOCK_LEN) != 0)
+    return -1;
+
+  /* Test GHASH key generation */
+  memcpy (tmp, polyval_key, GCRY_SIV_BLOCK_LEN);
+  cipher_block_bswap (tmp, tmp, GCRY_SIV_BLOCK_LEN);
+  mulx_ghash (tmp);
+  if (memcmp (tmp, ghash_key, GCRY_SIV_BLOCK_LEN) != 0)
+    return -1;
+
+  /* Test POLYVAL */
+  memset (&c->u_mode.gcm, 0, sizeof(c->u_mode.gcm));
+  polyval_set_key (c, polyval_key);
+  memset (&tmp, 0, sizeof(tmp));
+  do_polyval_buf (c, tmp, polyval_data, GCRY_SIV_BLOCK_LEN * 2, 1);
+  cipher_block_bswap (tmp, tmp, GCRY_SIV_BLOCK_LEN);
+  if (memcmp (tmp, polyval_tag, GCRY_SIV_BLOCK_LEN) != 0)
+    return -1;
+
+  return 0;
+}
+
+
+gcry_err_code_t
+_gcry_cipher_gcm_siv_setkey (gcry_cipher_hd_t c, unsigned int keylen)
+{
+  static int done;
+
+  if (keylen != 16 && keylen != 32)
+    return GPG_ERR_INV_KEYLEN;
+
+  if (!done)
+    {
+      if (gcm_siv_selftest (c))
+	return GPG_ERR_SELFTEST_FAILED;
+
+      done = 1;
+    }
+
+  c->marks.iv = 0;
+  c->marks.tag = 0;
+  memset (&c->u_mode.gcm, 0, sizeof(c->u_mode.gcm));
+  c->u_mode.gcm.siv_keylen = keylen;
+  return 0;
+}
+
+
+gcry_err_code_t
+_gcry_cipher_gcm_siv_set_nonce (gcry_cipher_hd_t c, const byte *iv,
+				size_t ivlen)
+{
+  byte auth_key[GCRY_SIV_BLOCK_LEN];
+  byte tmp_in[GCRY_SIV_BLOCK_LEN];
+  byte tmp[GCRY_SIV_BLOCK_LEN];
+  byte enc_key[32];
+  gcry_err_code_t err;
+
+  if (c->spec->blocksize != GCRY_SIV_BLOCK_LEN)
+    return GPG_ERR_CIPHER_ALGO;
+  if (ivlen != GCM_SIV_NONCE_LENGTH)
+    return GPG_ERR_INV_ARG;
+  if (c->u_mode.gcm.siv_keylen == 0)
+    return GPG_ERR_INV_STATE;
+  if (c->marks.iv)
+    {
+      /* If nonce is already set, use cipher_reset or setkey first to reset
+       * cipher state. */
+      return GPG_ERR_INV_STATE;
+    }
+
+  memset (c->u_mode.gcm.aadlen, 0, sizeof(c->u_mode.gcm.aadlen));
+  memset (c->u_mode.gcm.datalen, 0, sizeof(c->u_mode.gcm.datalen));
+  memset (c->u_mode.gcm.u_tag.tag, 0, sizeof(c->u_mode.gcm.u_tag.tag));
+  c->u_mode.gcm.datalen_over_limits = 0;
+  c->u_mode.gcm.ghash_data_finalized = 0;
+  c->u_mode.gcm.ghash_aad_finalized = 0;
+
+  memset (c->u_iv.iv, 0, GCRY_SIV_BLOCK_LEN);
+  memcpy (c->u_iv.iv, iv, ivlen);
+  memcpy (tmp_in + 4, iv, ivlen);
+
+  /* Derive message authentication key */
+  buf_put_le32(tmp_in, 0);
+  c->spec->encrypt (&c->context.c, tmp, tmp_in);
+  memcpy (auth_key + 0, tmp, 8);
+
+  buf_put_le32(tmp_in, 1);
+  c->spec->encrypt (&c->context.c, tmp, tmp_in);
+  memcpy (auth_key + 8, tmp, 8);
+
+  polyval_set_key (c, auth_key);
+  wipememory (auth_key, sizeof(auth_key));
+
+  /* Derive message encryption key */
+  buf_put_le32(tmp_in, 2);
+  c->spec->encrypt (&c->context.c, tmp, tmp_in);
+  memcpy (enc_key + 0, tmp, 8);
+
+  buf_put_le32(tmp_in, 3);
+  c->spec->encrypt (&c->context.c, tmp, tmp_in);
+  memcpy (enc_key + 8, tmp, 8);
+
+  if (c->u_mode.gcm.siv_keylen >= 24)
+    {
+      buf_put_le32(tmp_in, 4);
+      c->spec->encrypt (&c->context.c, tmp, tmp_in);
+      memcpy (enc_key + 16, tmp, 8);
+    }
+
+  if (c->u_mode.gcm.siv_keylen >= 32)
+    {
+      buf_put_le32(tmp_in, 5);
+      c->spec->encrypt (&c->context.c, tmp, tmp_in);
+      memcpy (enc_key + 24, tmp, 8);
+    }
+
+  wipememory (tmp, sizeof(tmp));
+  wipememory (tmp_in, sizeof(tmp_in));
+
+  err = c->spec->setkey (&c->context.c, enc_key, c->u_mode.gcm.siv_keylen,
+			 &c->bulk);
+  wipememory (enc_key, sizeof(enc_key));
+  if (err)
+    return err;
+
+  c->marks.iv = 1;
+  return 0;
+}
+
+
+gcry_err_code_t
+_gcry_cipher_gcm_siv_authenticate (gcry_cipher_hd_t c,
+				   const byte *aadbuf, size_t aadbuflen)
+{
+  if (c->spec->blocksize != GCRY_SIV_BLOCK_LEN)
+    return GPG_ERR_CIPHER_ALGO;
+  if (c->u_mode.gcm.datalen_over_limits)
+    return GPG_ERR_INV_LENGTH;
+  if (c->marks.tag
+      || !c->marks.iv
+      || c->u_mode.gcm.ghash_aad_finalized
+      || c->u_mode.gcm.ghash_data_finalized
+      || !c->u_mode.gcm.ghash_fn)
+    return GPG_ERR_INV_STATE;
+
+  gcm_siv_bytecounter_add (c->u_mode.gcm.aadlen, aadbuflen);
+  if (!gcm_siv_check_len (c->u_mode.gcm.aadlen))
+    {
+      c->u_mode.gcm.datalen_over_limits = 1;
+      return GPG_ERR_INV_LENGTH;
+    }
+
+  do_polyval_buf (c, c->u_mode.gcm.u_tag.tag, aadbuf, aadbuflen, 0);
+
+  return 0;
+}
+
+
+gcry_err_code_t
+_gcry_cipher_gcm_siv_encrypt (gcry_cipher_hd_t c,
+			      byte *outbuf, size_t outbuflen,
+			      const byte *inbuf, size_t inbuflen)
+{
+  u32 bitlengths[2][2];
+
+  if (c->spec->blocksize != GCRY_SIV_BLOCK_LEN)
+    return GPG_ERR_CIPHER_ALGO;
+  if (outbuflen < inbuflen)
+    return GPG_ERR_BUFFER_TOO_SHORT;
+  if (c->u_mode.gcm.datalen_over_limits)
+    return GPG_ERR_INV_LENGTH;
+  if (c->marks.tag
+      || !c->marks.iv
+      || c->u_mode.gcm.ghash_data_finalized
+      || !c->u_mode.gcm.ghash_fn)
+    return GPG_ERR_INV_STATE;
+
+  if (!c->u_mode.gcm.ghash_aad_finalized)
+    {
+      /* Start of encryption marks end of AAD stream. */
+      do_polyval_buf(c, c->u_mode.gcm.u_tag.tag, NULL, 0, 1);
+      c->u_mode.gcm.ghash_aad_finalized = 1;
+    }
+
+  gcm_siv_bytecounter_add (c->u_mode.gcm.datalen, inbuflen);
+  if (!gcm_siv_check_len (c->u_mode.gcm.datalen))
+    {
+      c->u_mode.gcm.datalen_over_limits = 1;
+      return GPG_ERR_INV_LENGTH;
+    }
+
+  /* Plaintext and padding to POLYVAL. */
+  do_polyval_buf (c, c->u_mode.gcm.u_tag.tag, inbuf, inbuflen, 1);
+  c->u_mode.gcm.ghash_data_finalized = 1;
+
+  /* aad length */
+  bitlengths[0][0] = le_bswap32(c->u_mode.gcm.aadlen[0] << 3);
+  bitlengths[0][1] = le_bswap32((c->u_mode.gcm.aadlen[0] >> 29) |
+                                (c->u_mode.gcm.aadlen[1] << 3));
+  /* data length */
+  bitlengths[1][0] = le_bswap32(c->u_mode.gcm.datalen[0] << 3);
+  bitlengths[1][1] = le_bswap32((c->u_mode.gcm.datalen[0] >> 29) |
+                                (c->u_mode.gcm.datalen[1] << 3));
+
+  /* Length block to POLYVAL. */
+  do_polyval_buf(c, c->u_mode.gcm.u_tag.tag, (byte *)bitlengths,
+		 GCRY_SIV_BLOCK_LEN, 1);
+  wipememory (bitlengths, sizeof(bitlengths));
+
+  /* Prepare tag and counter. */
+  cipher_block_bswap (c->u_mode.gcm.u_tag.tag, c->u_mode.gcm.u_tag.tag,
+		      GCRY_SIV_BLOCK_LEN);
+  cipher_block_xor (c->u_mode.gcm.tagiv, c->u_iv.iv, c->u_mode.gcm.u_tag.tag,
+		    GCRY_SIV_BLOCK_LEN);
+  c->u_mode.gcm.tagiv[GCRY_SIV_BLOCK_LEN - 1] &= 0x7f;
+  c->spec->encrypt (&c->context.c, c->u_mode.gcm.tagiv, c->u_mode.gcm.tagiv);
+  c->marks.tag = 1;
+  memcpy (c->u_ctr.ctr, c->u_mode.gcm.tagiv, GCRY_SIV_BLOCK_LEN);
+  c->u_ctr.ctr[GCRY_SIV_BLOCK_LEN - 1] |= 0x80;
+
+  /* Encrypt data */
+  do_ctr_le32 (c, outbuf, inbuf, inbuflen);
+  return 0;
+}
+
+
+gcry_err_code_t
+_gcry_cipher_gcm_siv_set_decryption_tag (gcry_cipher_hd_t c,
+					 const byte *tag, size_t taglen)
+{
+  if (taglen != GCRY_SIV_BLOCK_LEN)
+    return GPG_ERR_INV_ARG;
+  if (c->spec->blocksize != GCRY_SIV_BLOCK_LEN)
+    return GPG_ERR_CIPHER_ALGO;
+  if (c->marks.tag)
+    return GPG_ERR_INV_STATE;
+
+  memcpy (c->u_mode.gcm.tagiv, tag, GCRY_SIV_BLOCK_LEN);
+  c->marks.tag = 1;
+
+  return 0;
+}
+
+
+gcry_err_code_t
+_gcry_cipher_gcm_siv_decrypt (gcry_cipher_hd_t c,
+			      byte *outbuf, size_t outbuflen,
+			      const byte *inbuf, size_t inbuflen)
+{
+  byte expected_tag[GCRY_SIV_BLOCK_LEN];
+  u32 bitlengths[2][2];
+  gcry_err_code_t rc = 0;
+
+  if (c->spec->blocksize != GCRY_SIV_BLOCK_LEN)
+    return GPG_ERR_CIPHER_ALGO;
+  if (outbuflen < inbuflen)
+    return GPG_ERR_BUFFER_TOO_SHORT;
+  if (c->u_mode.gcm.datalen_over_limits)
+    return GPG_ERR_INV_LENGTH;
+  if (!c->marks.tag
+      || !c->marks.iv
+      || c->u_mode.gcm.ghash_data_finalized
+      || !c->u_mode.gcm.ghash_fn)
+    return GPG_ERR_INV_STATE;
+
+  if (!c->u_mode.gcm.ghash_aad_finalized)
+    {
+      /* Start of encryption marks end of AAD stream. */
+      do_polyval_buf(c, c->u_mode.gcm.u_tag.tag, NULL, 0, 1);
+      c->u_mode.gcm.ghash_aad_finalized = 1;
+    }
+
+  gcm_siv_bytecounter_add (c->u_mode.gcm.datalen, inbuflen);
+  if (!gcm_siv_check_len (c->u_mode.gcm.datalen))
+    {
+      c->u_mode.gcm.datalen_over_limits = 1;
+      return GPG_ERR_INV_LENGTH;
+    }
+
+  /* Prepare counter. */
+  memcpy (c->u_ctr.ctr, c->u_mode.gcm.tagiv, GCRY_SIV_BLOCK_LEN);
+  c->u_ctr.ctr[GCRY_SIV_BLOCK_LEN - 1] |= 0x80;
+
+  /* Decrypt data. */
+  do_ctr_le32 (c, outbuf, inbuf, inbuflen);
+
+  /* Plaintext and padding to POLYVAL. */
+  do_polyval_buf (c, c->u_mode.gcm.u_tag.tag, outbuf, inbuflen, 1);
+  c->u_mode.gcm.ghash_data_finalized = 1;
+
+  /* aad length */
+  bitlengths[0][0] = le_bswap32(c->u_mode.gcm.aadlen[0] << 3);
+  bitlengths[0][1] = le_bswap32((c->u_mode.gcm.aadlen[0] >> 29) |
+                                (c->u_mode.gcm.aadlen[1] << 3));
+  /* data length */
+  bitlengths[1][0] = le_bswap32(c->u_mode.gcm.datalen[0] << 3);
+  bitlengths[1][1] = le_bswap32((c->u_mode.gcm.datalen[0] >> 29) |
+                                (c->u_mode.gcm.datalen[1] << 3));
+
+  /* Length block to POLYVAL. */
+  do_polyval_buf(c, c->u_mode.gcm.u_tag.tag, (byte *)bitlengths,
+		 GCRY_SIV_BLOCK_LEN, 1);
+  wipememory (bitlengths, sizeof(bitlengths));
+
+  /* Prepare tag. */
+  cipher_block_bswap (c->u_mode.gcm.u_tag.tag, c->u_mode.gcm.u_tag.tag,
+		      GCRY_SIV_BLOCK_LEN);
+  cipher_block_xor (expected_tag, c->u_iv.iv, c->u_mode.gcm.u_tag.tag,
+		    GCRY_SIV_BLOCK_LEN);
+  expected_tag[GCRY_SIV_BLOCK_LEN - 1] &= 0x7f;
+  c->spec->encrypt (&c->context.c, expected_tag, expected_tag);
+
+  if (!buf_eq_const(c->u_mode.gcm.tagiv, expected_tag, GCRY_SIV_BLOCK_LEN))
+    {
+      wipememory (outbuf, inbuflen);
+      rc = GPG_ERR_CHECKSUM;
+    }
+
+  wipememory (expected_tag, sizeof(expected_tag));
+  return rc;
+}
+
+
+static gcry_err_code_t
+_gcry_cipher_gcm_siv_tag (gcry_cipher_hd_t c,
+			  byte * outbuf, size_t outbuflen, int check)
+{
+  gcry_err_code_t err;
+
+  if (!c->marks.tag)
+    {
+      if (!c->u_mode.gcm.ghash_fn)
+        return GPG_ERR_INV_STATE;
+
+      if (!c->marks.tag)
+        {
+          /* Finalize GCM-SIV with zero-length plaintext. */
+          err = _gcry_cipher_gcm_siv_encrypt (c, NULL, 0, NULL, 0);
+          if (err != 0)
+            return err;
+        }
+    }
+
+  if (c->u_mode.gcm.datalen_over_limits)
+    return GPG_ERR_INV_LENGTH;
+  if (!c->u_mode.gcm.ghash_data_finalized)
+    return GPG_ERR_INV_STATE;
+  if (!c->marks.tag)
+    return GPG_ERR_INV_STATE;
+
+  if (!check)
+    {
+      if (outbuflen > GCRY_SIV_BLOCK_LEN)
+        outbuflen = GCRY_SIV_BLOCK_LEN;
+
+      /* NB: We already checked that OUTBUF is large enough to hold
+       * the result or has valid truncated length.  */
+      memcpy (outbuf, c->u_mode.gcm.tagiv, outbuflen);
+    }
+  else
+    {
+      /* OUTBUFLEN gives the length of the user supplied tag in OUTBUF
+       * and thus we need to compare its length first.  */
+      if (outbuflen != GCRY_SIV_BLOCK_LEN
+          || !buf_eq_const (outbuf, c->u_mode.gcm.tagiv, outbuflen))
+        return GPG_ERR_CHECKSUM;
+    }
+
+  return 0;
+}
+
+
+gcry_err_code_t
+_gcry_cipher_gcm_siv_get_tag (gcry_cipher_hd_t c, unsigned char *outtag,
+			      size_t taglen)
+{
+  return _gcry_cipher_gcm_siv_tag (c, outtag, taglen, 0);
+}
+
+
+gcry_err_code_t
+_gcry_cipher_gcm_siv_check_tag (gcry_cipher_hd_t c,
+				   const unsigned char *intag,
+				   size_t taglen)
+{
+  return _gcry_cipher_gcm_siv_tag (c, (unsigned char *)intag, taglen, 1);
+}
diff --git a/cipher/cipher-gcm.c b/cipher/cipher-gcm.c
index 4ce85408..64b9179c 100644
--- a/cipher/cipher-gcm.c
+++ b/cipher/cipher-gcm.c
@@ -1005,6 +1005,13 @@ _gcry_cipher_gcm_authenticate (gcry_cipher_hd_t c,
 }
 
 
+void
+_gcry_cipher_gcm_setupM (gcry_cipher_hd_t c)
+{
+  setupM (c);
+}
+
+
 void
 _gcry_cipher_gcm_setkey (gcry_cipher_hd_t c)
 {
diff --git a/cipher/cipher-internal.h b/cipher/cipher-internal.h
index e9f48a2f..8b04cff7 100644
--- a/cipher/cipher-internal.h
+++ b/cipher/cipher-internal.h
@@ -301,7 +301,7 @@ struct gcry_cipher_handle
       gcry_cmac_context_t cmac_ciphertext;
     } eax;
 
-    /* Mode specific storage for GCM mode. */
+    /* Mode specific storage for GCM mode and GCM-SIV mode. */
     struct {
       /* The interim tag for GCM mode.  */
       union {
@@ -347,6 +347,9 @@ struct gcry_cipher_handle
 
       /* GHASH implementation in use. */
       ghash_fn_t ghash_fn;
+
+      /* Key length used for GCM-SIV key generating key. */
+      unsigned int siv_keylen;
     } gcm;
 
     /* Mode specific storage for OCB mode. */
@@ -583,6 +586,8 @@ gcry_err_code_t _gcry_cipher_gcm_check_tag
                    const unsigned char *intag, size_t taglen);
 void _gcry_cipher_gcm_setkey
 /*           */   (gcry_cipher_hd_t c);
+void _gcry_cipher_gcm_setupM
+/*           */   (gcry_cipher_hd_t c);
 
 
 /*-- cipher-poly1305.c --*/
@@ -679,6 +684,32 @@ gcry_err_code_t _gcry_cipher_siv_setkey
                  const unsigned char *ctrkey, size_t ctrkeylen);
 
 
+/*-- cipher-gcm-siv.c --*/
+gcry_err_code_t _gcry_cipher_gcm_siv_encrypt
+/*           */ (gcry_cipher_hd_t c,
+                 unsigned char *outbuf, size_t outbuflen,
+                 const unsigned char *inbuf, size_t inbuflen);
+gcry_err_code_t _gcry_cipher_gcm_siv_decrypt
+/*           */ (gcry_cipher_hd_t c,
+                 unsigned char *outbuf, size_t outbuflen,
+                 const unsigned char *inbuf, size_t inbuflen);
+gcry_err_code_t _gcry_cipher_gcm_siv_set_nonce
+/*           */ (gcry_cipher_hd_t c, const unsigned char *nonce,
+                 size_t noncelen);
+gcry_err_code_t _gcry_cipher_gcm_siv_authenticate
+/*           */ (gcry_cipher_hd_t c, const unsigned char *abuf, size_t abuflen);
+gcry_err_code_t _gcry_cipher_gcm_siv_set_decryption_tag
+/*           */ (gcry_cipher_hd_t c, const byte *tag, size_t taglen);
+gcry_err_code_t _gcry_cipher_gcm_siv_get_tag
+/*           */ (gcry_cipher_hd_t c,
+                 unsigned char *outtag, size_t taglen);
+gcry_err_code_t _gcry_cipher_gcm_siv_check_tag
+/*           */ (gcry_cipher_hd_t c,
+                 const unsigned char *intag, size_t taglen);
+gcry_err_code_t _gcry_cipher_gcm_siv_setkey
+/*           */ (gcry_cipher_hd_t c, unsigned int keylen);
+
+
 /* 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
@@ -865,6 +896,29 @@ cipher_block_xor_n_copy_2(void *_dst_xor, const void *_src_xor,
 }
 
 
+/* Optimized function for combined cipher block byte-swapping.  */
+static inline void
+cipher_block_bswap (void *_dst_bswap, const void *_src_bswap,
+                    size_t blocksize)
+{
+  byte *dst_bswap = _dst_bswap;
+  const byte *src_bswap = _src_bswap;
+  u64 t[2];
+
+  if (blocksize == 8)
+    {
+      buf_put_le64(dst_bswap, buf_get_be64(src_bswap));
+    }
+  else
+    {
+      t[0] = buf_get_be64(src_bswap + 0);
+      t[1] = buf_get_be64(src_bswap + 8);
+      buf_put_le64(dst_bswap + 8, t[0]);
+      buf_put_le64(dst_bswap + 0, t[1]);
+    }
+}
+
+
 /* Optimized function for combined cipher block xoring and copying.
    Used by mainly CFB mode decryption.  */
 static inline void
diff --git a/cipher/cipher-siv.c b/cipher/cipher-siv.c
index 9a71f2ef..11f25340 100644
--- a/cipher/cipher-siv.c
+++ b/cipher/cipher-siv.c
@@ -1,4 +1,4 @@
-/* cipher-siv.c  -  SIV implementation
+/* cipher-siv.c  -  SIV implementation (RFC 5297)
  * Copyright (C) 2021 Jussi Kivilinna <jussi.kivilinna at iki.fi>
  *
  * This file is part of Libgcrypt.
diff --git a/cipher/cipher.c b/cipher/cipher.c
index a274466f..2bde99ef 100644
--- a/cipher/cipher.c
+++ b/cipher/cipher.c
@@ -526,33 +526,46 @@ _gcry_cipher_open_internal (gcry_cipher_hd_t *handle,
   if (! err)
     switch (mode)
       {
-      case GCRY_CIPHER_MODE_CCM:
-	if (spec->blocksize != GCRY_CCM_BLOCK_LEN)
+      case GCRY_CIPHER_MODE_ECB:
+      case GCRY_CIPHER_MODE_CBC:
+      case GCRY_CIPHER_MODE_CFB:
+      case GCRY_CIPHER_MODE_CFB8:
+      case GCRY_CIPHER_MODE_OFB:
+      case GCRY_CIPHER_MODE_CTR:
+      case GCRY_CIPHER_MODE_AESWRAP:
+      case GCRY_CIPHER_MODE_CMAC:
+      case GCRY_CIPHER_MODE_EAX:
+	if (!spec->encrypt || !spec->decrypt)
 	  err = GPG_ERR_INV_CIPHER_MODE;
+	break;
+
+      case GCRY_CIPHER_MODE_CCM:
 	if (!spec->encrypt || !spec->decrypt)
 	  err = GPG_ERR_INV_CIPHER_MODE;
+	else if (spec->blocksize != GCRY_CCM_BLOCK_LEN)
+	  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;
+	else if (spec->blocksize != GCRY_XTS_BLOCK_LEN)
+	  err = GPG_ERR_INV_CIPHER_MODE;
 	break;
 
-      case GCRY_CIPHER_MODE_ECB:
-      case GCRY_CIPHER_MODE_CBC:
-      case GCRY_CIPHER_MODE_CFB:
-      case GCRY_CIPHER_MODE_CFB8:
-      case GCRY_CIPHER_MODE_OFB:
-      case GCRY_CIPHER_MODE_CTR:
-      case GCRY_CIPHER_MODE_AESWRAP:
-      case GCRY_CIPHER_MODE_CMAC:
-      case GCRY_CIPHER_MODE_EAX:
       case GCRY_CIPHER_MODE_GCM:
+	if (!spec->encrypt || !spec->decrypt)
+	  err = GPG_ERR_INV_CIPHER_MODE;
+	else if (spec->blocksize != GCRY_GCM_BLOCK_LEN)
+	  err = GPG_ERR_INV_CIPHER_MODE;
+	break;
+
       case GCRY_CIPHER_MODE_SIV:
+      case GCRY_CIPHER_MODE_GCM_SIV:
 	if (!spec->encrypt || !spec->decrypt)
 	  err = GPG_ERR_INV_CIPHER_MODE;
+	else if (spec->blocksize != GCRY_SIV_BLOCK_LEN)
+	  err = GPG_ERR_INV_CIPHER_MODE;
 	break;
 
       case GCRY_CIPHER_MODE_POLY1305:
@@ -569,7 +582,7 @@ _gcry_cipher_open_internal (gcry_cipher_hd_t *handle,
            security too much.  */
 	if (!spec->encrypt || !spec->decrypt)
 	  err = GPG_ERR_INV_CIPHER_MODE;
-	else if (spec->blocksize != (128/8))
+	else if (spec->blocksize != GCRY_OCB_BLOCK_LEN)
 	  err = GPG_ERR_INV_CIPHER_MODE;
 	break;
 
@@ -769,6 +782,12 @@ cipher_setkey (gcry_cipher_hd_t c, byte *key, size_t keylen)
           _gcry_cipher_gcm_setkey (c);
           break;
 
+        case GCRY_CIPHER_MODE_GCM_SIV:
+          rc = _gcry_cipher_gcm_siv_setkey (c, keylen);
+          if (rc)
+	    c->marks.key = 0;
+          break;
+
         case GCRY_CIPHER_MODE_OCB:
           _gcry_cipher_ocb_setkey (c);
           break;
@@ -884,6 +903,7 @@ cipher_reset (gcry_cipher_hd_t c)
       break;
 
     case GCRY_CIPHER_MODE_GCM:
+    case GCRY_CIPHER_MODE_GCM_SIV:
       /* Only clear head of u_mode, keep ghash_key and gcm_table. */
       {
         byte *u_mode_pos = (void *)&c->u_mode;
@@ -1375,6 +1395,11 @@ _gcry_cipher_setup_mode_ops(gcry_cipher_hd_t c, int mode)
       c->mode_ops.decrypt = _gcry_cipher_siv_decrypt;
       break;
 
+    case GCRY_CIPHER_MODE_GCM_SIV:
+      c->mode_ops.encrypt = _gcry_cipher_gcm_siv_encrypt;
+      c->mode_ops.decrypt = _gcry_cipher_gcm_siv_decrypt;
+      break;
+
     default:
       c->mode_ops.encrypt = do_encrypt_none_unknown;
       c->mode_ops.decrypt = do_decrypt_none_unknown;
@@ -1408,6 +1433,10 @@ _gcry_cipher_setup_mode_ops(gcry_cipher_hd_t c, int mode)
       c->mode_ops.setiv = _gcry_cipher_siv_set_nonce;
       break;
 
+    case GCRY_CIPHER_MODE_GCM_SIV:
+      c->mode_ops.setiv = _gcry_cipher_gcm_siv_set_nonce;
+      break;
+
     default:
       c->mode_ops.setiv = cipher_setiv;
       break;
@@ -1459,6 +1488,12 @@ _gcry_cipher_setup_mode_ops(gcry_cipher_hd_t c, int mode)
       c->mode_ops.check_tag    = _gcry_cipher_siv_check_tag;
       break;
 
+    case GCRY_CIPHER_MODE_GCM_SIV:
+      c->mode_ops.authenticate = _gcry_cipher_gcm_siv_authenticate;
+      c->mode_ops.get_tag      = _gcry_cipher_gcm_siv_get_tag;
+      c->mode_ops.check_tag    = _gcry_cipher_gcm_siv_check_tag;
+      break;
+
     default:
       c->mode_ops.authenticate = NULL;
       c->mode_ops.get_tag      = NULL;
@@ -1540,6 +1575,8 @@ _gcry_cipher_ctl (gcry_cipher_hd_t h, int cmd, void *buffer, size_t buflen)
 
         if (h->mode == GCRY_CIPHER_MODE_SIV)
           rc = _gcry_cipher_siv_set_decryption_tag (h, buffer, buflen);
+        else if (h->mode == GCRY_CIPHER_MODE_GCM_SIV)
+          rc = _gcry_cipher_gcm_siv_set_decryption_tag (h, buffer, buflen);
         else
           rc = GPG_ERR_INV_CIPHER_MODE;
       }
@@ -1682,6 +1719,10 @@ _gcry_cipher_info (gcry_cipher_hd_t h, int cmd, void *buffer, size_t *nbytes)
               *nbytes = GCRY_SIV_BLOCK_LEN;
               break;
 
+            case GCRY_CIPHER_MODE_GCM_SIV:
+              *nbytes = GCRY_SIV_BLOCK_LEN;
+              break;
+
             default:
               rc = GPG_ERR_INV_CIPHER_MODE;
               break;
diff --git a/doc/gcrypt.texi b/doc/gcrypt.texi
index e5c4b64e..6ef68884 100644
--- a/doc/gcrypt.texi
+++ b/doc/gcrypt.texi
@@ -1782,6 +1782,22 @@ full-sized plaintext or ciphertext needs to be passed to
 needs to be given to SIV mode before decryption using
 @code{gcry_cipher_set_decryption_tag}.
 
+ at item  GCRY_CIPHER_MODE_GCM_SIV
+ at cindex GCM-SIV, GCM-SIV mode, AES-GCM-SIV
+This mode implements is GCM-SIV Authenticated Encryption with
+Associated Data (AEAD) block cipher mode specified in RFC-5297
+(AES-GCM-SIV: Nonce Misuse-Resistant Authenticated Encryption).
+This implementations works with block ciphers with block size of
+128 bits and uses tag length of 128 bits.  Supported key lengths
+by the mode are 128 bits and 256 bits. GCM-SIV is specified as
+nonce misuse resistant, so that it does not fail catastrophically
+if a nonce is repeated.
+
+When encrypting or decrypting, full-sized plaintext or ciphertext
+needs to be passed to @code{gcry_cipher_encrypt} or
+ at code{gcry_cipher_decrypt}. Decryption tag needs to be given to
+GCM-SIV mode before decryption using @code{gcry_cipher_set_decryption_tag}.
+
 @end table
 
 @node Working with cipher handles
@@ -1817,8 +1833,9 @@ ChaCha20 stream cipher. The block cipher modes
 with any block cipher algorithm.  GCM mode
 (@code{GCRY_CIPHER_MODE_GCM}), CCM mode (@code{GCRY_CIPHER_MODE_CCM}),
 OCB mode (@code{GCRY_CIPHER_MODE_OCB}), XTS mode
-(@code{GCRY_CIPHER_MODE_XTS}) and SIV mode
-(@code{GCRY_CIPHER_MODE_SIV}) will only work with block cipher
+(@code{GCRY_CIPHER_MODE_XTS}), SIV mode
+(@code{GCRY_CIPHER_MODE_SIV}) and GCM-SIV mode
+(@code{GCRY_CIPHER_MODE_GCM_SIV}) 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
@@ -2011,13 +2028,13 @@ implemented as a macro.
 @end deftypefun
 
 
-The SIV mode requires decryption tag to be input before decryption.
-This is done with:
+The SIV mode and the GCM-SIV mode requires decryption tag to be input
+before decryption. This is done with:
 
 @deftypefun gcry_error_t gcry_cipher_set_decryption_tag (gcry_cipher_hd_t @var{h}, const void *@var{tag}, size_t @var{taglen})
 
-Set decryption tag for the SIV mode decryption.  This is implemented
-as a macro.
+Set decryption tag for SIV or GCM-SIV mode decryption.  This is
+implemented as a macro.
 @end deftypefun
 
 
diff --git a/src/gcrypt.h.in b/src/gcrypt.h.in
index 99b21276..0540c60a 100644
--- a/src/gcrypt.h.in
+++ b/src/gcrypt.h.in
@@ -977,7 +977,8 @@ enum gcry_cipher_modes
     GCRY_CIPHER_MODE_CFB8     = 12,  /* Cipher feedback (8 bit mode). */
     GCRY_CIPHER_MODE_XTS      = 13,  /* XTS mode.  */
     GCRY_CIPHER_MODE_EAX      = 14,  /* EAX mode.  */
-    GCRY_CIPHER_MODE_SIV      = 15   /* SIV mode.  */
+    GCRY_CIPHER_MODE_SIV      = 15,  /* SIV mode.  */
+    GCRY_CIPHER_MODE_GCM_SIV  = 16   /* GCM-SIV mode.  */
   };
 
 /* Flags used with the open function. */
@@ -1001,7 +1002,7 @@ enum gcry_cipher_flags
 /* XTS works only with blocks of 128 bits.  */
 #define GCRY_XTS_BLOCK_LEN  (128 / 8)
 
-/* SIV works only with blocks of 128 bits */
+/* SIV and GCM-SIV works only with blocks of 128 bits */
 #define GCRY_SIV_BLOCK_LEN  (128 / 8)
 
 /* Create a handle for algorithm ALGO to be used in MODE.  FLAGS may
@@ -1106,7 +1107,7 @@ size_t gcry_cipher_get_algo_blklen (int algo);
 #define gcry_cipher_test_algo(a) \
             gcry_cipher_algo_info( (a), GCRYCTL_TEST_ALGO, NULL, NULL )
 
-/* Setup tag for decryption (for SIV mode). */
+/* Setup tag for decryption (for SIV and GCM-SIV mode). */
 #define gcry_cipher_set_decryption_tag(a, tag, taglen) \
             gcry_cipher_ctl ((a), GCRYCTL_SET_DECRYPTION_TAG, \
                              (void *)(tag), (taglen))
diff --git a/tests/basic.c b/tests/basic.c
index 989a5aca..148aaec6 100644
--- a/tests/basic.c
+++ b/tests/basic.c
@@ -5220,6 +5220,919 @@ check_siv_cipher (void)
 }
 
 
+static void
+check_gcm_siv_cipher (void)
+{
+  static const struct tv
+  {
+    int algo;
+    char key[MAX_DATA_LEN];
+    char nonce[12];
+    char ad[MAX_DATA_LEN];
+    int adlen;
+    unsigned char plaintext[MAX_DATA_LEN];
+    int inlen;
+    char out[MAX_DATA_LEN];
+    char tag[MAX_DATA_LEN];
+  } tv[] =
+    {
+      /* Test vectors from RFC8452 */
+      {
+	GCRY_CIPHER_AES128,
+	"\xee\x8e\x1e\xd9\xff\x25\x40\xae\x8f\x2b\xa9\xf5\x0b\xc2\xf2\x7c",
+	"\x75\x2a\xba\xd3\xe0\xaf\xb5\xf4\x34\xdc\x43\x10",
+	"example",
+	7,
+	"Hello world",
+	11,
+	"\x5d\x34\x9e\xad\x17\x5e\xf6\xb1\xde\xf6\xfd",
+	"\x4f\xbc\xde\xb7\xe4\x79\x3f\x4a\x1d\x7e\x4f\xaa\x70\x10\x0a\xf1"
+      },
+      {
+	GCRY_CIPHER_AES128,
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"",
+	-1,
+	"",
+	0,
+	"",
+	"\xdc\x20\xe2\xd8\x3f\x25\x70\x5b\xb4\x9e\x43\x9e\xca\x56\xde\x25"
+      },
+      {
+	GCRY_CIPHER_AES128,
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"",
+	0,
+	"",
+	0,
+	"",
+	"\xdc\x20\xe2\xd8\x3f\x25\x70\x5b\xb4\x9e\x43\x9e\xca\x56\xde\x25",
+      },
+      {
+	GCRY_CIPHER_AES128,
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"",
+	0,
+	"\x01\x00\x00\x00\x00\x00\x00\x00",
+	8,
+	"\xb5\xd8\x39\x33\x0a\xc7\xb7\x86",
+	"\x57\x87\x82\xff\xf6\x01\x3b\x81\x5b\x28\x7c\x22\x49\x3a\x36\x4c",
+      },
+      {
+	GCRY_CIPHER_AES128,
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"",
+	0,
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	12,
+	"\x73\x23\xea\x61\xd0\x59\x32\x26\x00\x47\xd9\x42",
+	"\xa4\x97\x8d\xb3\x57\x39\x1a\x0b\xc4\xfd\xec\x8b\x0d\x10\x66\x39",
+      },
+      {
+	GCRY_CIPHER_AES128,
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"",
+	0,
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	16,
+	"\x74\x3f\x7c\x80\x77\xab\x25\xf8\x62\x4e\x2e\x94\x85\x79\xcf\x77",
+	"\x30\x3a\xaf\x90\xf6\xfe\x21\x19\x9c\x60\x68\x57\x74\x37\xa0\xc4",
+      },
+      {
+	GCRY_CIPHER_AES128,
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"",
+	0,
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	32,
+	"\x84\xe0\x7e\x62\xba\x83\xa6\x58\x54\x17\x24\x5d\x7e\xc4\x13\xa9"
+	"\xfe\x42\x7d\x63\x15\xc0\x9b\x57\xce\x45\xf2\xe3\x93\x6a\x94\x45",
+	"\x1a\x8e\x45\xdc\xd4\x57\x8c\x66\x7c\xd8\x68\x47\xbf\x61\x55\xff",
+      },
+      {
+	GCRY_CIPHER_AES128,
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"",
+	0,
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	48,
+	"\x3f\xd2\x4c\xe1\xf5\xa6\x7b\x75\xbf\x23\x51\xf1\x81\xa4\x75\xc7"
+	"\xb8\x00\xa5\xb4\xd3\xdc\xf7\x01\x06\xb1\xee\xa8\x2f\xa1\xd6\x4d"
+	"\xf4\x2b\xf7\x22\x61\x22\xfa\x92\xe1\x7a\x40\xee\xaa\xc1\x20\x1b",
+	"\x5e\x6e\x31\x1d\xbf\x39\x5d\x35\xb0\xfe\x39\xc2\x71\x43\x88\xf8",
+      },
+      {
+	GCRY_CIPHER_AES128,
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"",
+	0,
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	64,
+	"\x24\x33\x66\x8f\x10\x58\x19\x0f\x6d\x43\xe3\x60\xf4\xf3\x5c\xd8"
+	"\xe4\x75\x12\x7c\xfc\xa7\x02\x8e\xa8\xab\x5c\x20\xf7\xab\x2a\xf0"
+	"\x25\x16\xa2\xbd\xcb\xc0\x8d\x52\x1b\xe3\x7f\xf2\x8c\x15\x2b\xba"
+	"\x36\x69\x7f\x25\xb4\xcd\x16\x9c\x65\x90\xd1\xdd\x39\x56\x6d\x3f",
+	"\x8a\x26\x3d\xd3\x17\xaa\x88\xd5\x6b\xdf\x39\x36\xdb\xa7\x5b\xb8",
+      },
+      {
+	GCRY_CIPHER_AES128,
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x01",
+	1,
+	"\x02\x00\x00\x00\x00\x00\x00\x00",
+	8,
+	"\x1e\x6d\xab\xa3\x56\x69\xf4\x27",
+	"\x3b\x0a\x1a\x25\x60\x96\x9c\xdf\x79\x0d\x99\x75\x9a\xbd\x15\x08",
+      },
+      {
+	GCRY_CIPHER_AES128,
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x01",
+	1,
+	"\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	12,
+	"\x29\x6c\x78\x89\xfd\x99\xf4\x19\x17\xf4\x46\x20",
+	"\x08\x29\x9c\x51\x02\x74\x5a\xaa\x3a\x0c\x46\x9f\xad\x9e\x07\x5a",
+      },
+      {
+	GCRY_CIPHER_AES128,
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x01",
+	1,
+	"\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	16,
+	"\xe2\xb0\xc5\xda\x79\xa9\x01\xc1\x74\x5f\x70\x05\x25\xcb\x33\x5b",
+	"\x8f\x89\x36\xec\x03\x9e\x4e\x4b\xb9\x7e\xbd\x8c\x44\x57\x44\x1f",
+      },
+      {
+	GCRY_CIPHER_AES128,
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x01",
+	1,
+	"\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	32,
+	"\x62\x00\x48\xef\x3c\x1e\x73\xe5\x7e\x02\xbb\x85\x62\xc4\x16\xa3"
+	"\x19\xe7\x3e\x4c\xaa\xc8\xe9\x6a\x1e\xcb\x29\x33\x14\x5a\x1d\x71",
+	"\xe6\xaf\x6a\x7f\x87\x28\x7d\xa0\x59\xa7\x16\x84\xed\x34\x98\xe1",
+      },
+      {
+	GCRY_CIPHER_AES128,
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x01",
+	1,
+	"\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	48,
+	"\x50\xc8\x30\x3e\xa9\x39\x25\xd6\x40\x90\xd0\x7b\xd1\x09\xdf\xd9"
+	"\x51\x5a\x5a\x33\x43\x10\x19\xc1\x7d\x93\x46\x59\x99\xa8\xb0\x05"
+	"\x32\x01\xd7\x23\x12\x0a\x85\x62\xb8\x38\xcd\xff\x25\xbf\x9d\x1e",
+	"\x6a\x8c\xc3\x86\x5f\x76\x89\x7c\x2e\x4b\x24\x5c\xf3\x1c\x51\xf2",
+      },
+      {
+	GCRY_CIPHER_AES128,
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x01",
+	1,
+	"\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	64,
+	"\x2f\x5c\x64\x05\x9d\xb5\x5e\xe0\xfb\x84\x7e\xd5\x13\x00\x37\x46"
+	"\xac\xa4\xe6\x1c\x71\x1b\x5d\xe2\xe7\xa7\x7f\xfd\x02\xda\x42\xfe"
+	"\xec\x60\x19\x10\xd3\x46\x7b\xb8\xb3\x6e\xbb\xae\xbc\xe5\xfb\xa3"
+	"\x0d\x36\xc9\x5f\x48\xa3\xe7\x98\x0f\x0e\x7a\xc2\x99\x33\x2a\x80",
+	"\xcd\xc4\x6a\xe4\x75\x56\x3d\xe0\x37\x00\x1e\xf8\x4a\xe2\x17\x44",
+      },
+      {
+	GCRY_CIPHER_AES128,
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	12,
+	"\x02\x00\x00\x00",
+	4,
+	"\xa8\xfe\x3e\x87",
+	"\x07\xeb\x1f\x84\xfb\x28\xf8\xcb\x73\xde\x8e\x99\xe2\xf4\x8a\x14",
+      },
+      {
+	GCRY_CIPHER_AES128,
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x02\x00",
+	18,
+	"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x04\x00\x00\x00",
+	20,
+	"\x6b\xb0\xfe\xcf\x5d\xed\x9b\x77\xf9\x02\xc7\xd5\xda\x23\x6a\x43"
+	"\x91\xdd\x02\x97",
+	"\x24\xaf\xc9\x80\x5e\x97\x6f\x45\x1e\x6d\x87\xf6\xfe\x10\x65\x14",
+      },
+      {
+	GCRY_CIPHER_AES128,
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x02\x00\x00\x00",
+	20,
+	"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x04\x00",
+	18,
+	"\x44\xd0\xaa\xf6\xfb\x2f\x1f\x34\xad\xd5\xe8\x06\x4e\x83\xe1\x2a"
+	"\x2a\xda",
+	"\xbf\xf9\xb2\xef\x00\xfb\x47\x92\x0c\xc7\x2a\x0c\x0f\x13\xb9\xfd",
+      },
+      {
+	GCRY_CIPHER_AES128,
+	"\xe6\x60\x21\xd5\xeb\x8e\x4f\x40\x66\xd4\xad\xb9\xc3\x35\x60\xe4",
+	"\xf4\x6e\x44\xbb\x3d\xa0\x01\x5c\x94\xf7\x08\x87",
+	"",
+	0,
+	"",
+	0,
+	"",
+	"\xa4\x19\x4b\x79\x07\x1b\x01\xa8\x7d\x65\xf7\x06\xe3\x94\x95\x78",
+      },
+      {
+	GCRY_CIPHER_AES128,
+	"\x36\x86\x42\x00\xe0\xea\xf5\x28\x4d\x88\x4a\x0e\x77\xd3\x16\x46",
+	"\xba\xe8\xe3\x7f\xc8\x34\x41\xb1\x60\x34\x56\x6b",
+	"\x46\xbb\x91\xc3\xc5",
+	5,
+	"\x7a\x80\x6c",
+	3,
+	"\xaf\x60\xeb",
+	"\x71\x1b\xd8\x5b\xc1\xe4\xd3\xe0\xa4\x62\xe0\x74\xee\xa4\x28\xa8",
+      },
+      {
+	GCRY_CIPHER_AES128,
+	"\xae\xdb\x64\xa6\xc5\x90\xbc\x84\xd1\xa5\xe2\x69\xe4\xb4\x78\x01",
+	"\xaf\xc0\x57\x7e\x34\x69\x9b\x9e\x67\x1f\xdd\x4f",
+	"\xfc\x88\x0c\x94\xa9\x51\x98\x87\x42\x96",
+	10,
+	"\xbd\xc6\x6f\x14\x65\x45",
+	6,
+	"\xbb\x93\xa3\xe3\x4d\x3c",
+	"\xd6\xa9\xc4\x55\x45\xcf\xc1\x1f\x03\xad\x74\x3d\xba\x20\xf9\x66",
+      },
+      {
+	GCRY_CIPHER_AES128,
+	"\xd5\xcc\x1f\xd1\x61\x32\x0b\x69\x20\xce\x07\x78\x7f\x86\x74\x3b",
+	"\x27\x5d\x1a\xb3\x2f\x6d\x1f\x04\x34\xd8\x84\x8c",
+	"\x04\x67\x87\xf3\xea\x22\xc1\x27\xaa\xf1\x95\xd1\x89\x47\x28",
+	15,
+	"\x11\x77\x44\x1f\x19\x54\x95\x86\x0f",
+	9,
+	"\x4f\x37\x28\x1f\x7a\xd1\x29\x49\xd0",
+	"\x1d\x02\xfd\x0c\xd1\x74\xc8\x4f\xc5\xda\xe2\xf6\x0f\x52\xfd\x2b",
+      },
+      {
+	GCRY_CIPHER_AES128,
+	"\xb3\xfe\xd1\x47\x3c\x52\x8b\x84\x26\xa5\x82\x99\x59\x29\xa1\x49",
+	"\x9e\x9a\xd8\x78\x0c\x8d\x63\xd0\xab\x41\x49\xc0",
+	"\xc9\x88\x2e\x53\x86\xfd\x9f\x92\xec\x48\x9c\x8f\xde\x2b\xe2\xcf"
+	"\x97\xe7\x4e\x93",
+	20,
+	"\x9f\x57\x2c\x61\x4b\x47\x45\x91\x44\x74\xe7\xc7",
+	12,
+	"\xf5\x46\x73\xc5\xdd\xf7\x10\xc7\x45\x64\x1c\x8b",
+	"\xc1\xdc\x2f\x87\x1f\xb7\x56\x1d\xa1\x28\x6e\x65\x5e\x24\xb7\xb0",
+      },
+      {
+	GCRY_CIPHER_AES128,
+	"\x2d\x4e\xd8\x7d\xa4\x41\x02\x95\x2e\xf9\x4b\x02\xb8\x05\x24\x9b",
+	"\xac\x80\xe6\xf6\x14\x55\xbf\xac\x83\x08\xa2\xd4",
+	"\x29\x50\xa7\x0d\x5a\x1d\xb2\x31\x6f\xd5\x68\x37\x8d\xa1\x07\xb5"
+	"\x2b\x0d\xa5\x52\x10\xcc\x1c\x1b\x0a",
+	25,
+	"\x0d\x8c\x84\x51\x17\x80\x82\x35\x5c\x9e\x94\x0f\xea\x2f\x58",
+	15,
+	"\xc9\xff\x54\x5e\x07\xb8\x8a\x01\x5f\x05\xb2\x74\x54\x0a\xa1",
+	"\x83\xb3\x44\x9b\x9f\x39\x55\x2d\xe9\x9d\xc2\x14\xa1\x19\x0b\x0b",
+      },
+      {
+	GCRY_CIPHER_AES128,
+	"\xbd\xe3\xb2\xf2\x04\xd1\xe9\xf8\xb0\x6b\xc4\x7f\x97\x45\xb3\xd1",
+	"\xae\x06\x55\x6f\xb6\xaa\x78\x90\xbe\xbc\x18\xfe",
+	"\x18\x60\xf7\x62\xeb\xfb\xd0\x82\x84\xe4\x21\x70\x2d\xe0\xde\x18"
+	"\xba\xa9\xc9\x59\x62\x91\xb0\x84\x66\xf3\x7d\xe2\x1c\x7f",
+	30,
+	"\x6b\x3d\xb4\xda\x3d\x57\xaa\x94\x84\x2b\x98\x03\xa9\x6e\x07\xfb"
+	"\x6d\xe7",
+	18,
+	"\x62\x98\xb2\x96\xe2\x4e\x8c\xc3\x5d\xce\x0b\xed\x48\x4b\x7f\x30"
+	"\xd5\x80",
+	"\x3e\x37\x70\x94\xf0\x47\x09\xf6\x4d\x7b\x98\x53\x10\xa4\xdb\x84",
+      },
+      {
+	GCRY_CIPHER_AES128,
+	"\xf9\x01\xcf\xe8\xa6\x96\x15\xa9\x3f\xdf\x7a\x98\xca\xd4\x81\x79",
+	"\x62\x45\x70\x9f\xb1\x88\x53\xf6\x8d\x83\x36\x40",
+	"\x75\x76\xf7\x02\x8e\xc6\xeb\x5e\xa7\xe2\x98\x34\x2a\x94\xd4\xb2"
+	"\x02\xb3\x70\xef\x97\x68\xec\x65\x61\xc4\xfe\x6b\x7e\x72\x96\xfa"
+	"\x85\x9c\x21",
+	35,
+	"\xe4\x2a\x3c\x02\xc2\x5b\x64\x86\x9e\x14\x6d\x7b\x23\x39\x87\xbd"
+	"\xdf\xc2\x40\x87\x1d",
+	21,
+	"\x39\x1c\xc3\x28\xd4\x84\xa4\xf4\x64\x06\x18\x1b\xcd\x62\xef\xd9"
+	"\xb3\xee\x19\x7d\x05",
+	"\x2d\x15\x50\x6c\x84\xa9\xed\xd6\x5e\x13\xe9\xd2\x4a\x2a\x6e\x70",
+      },
+      {
+	GCRY_CIPHER_AES256,
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"",
+	0,
+	"",
+	0,
+	"",
+	"\x07\xf5\xf4\x16\x9b\xbf\x55\xa8\x40\x0c\xd4\x7e\xa6\xfd\x40\x0f",
+      },
+      {
+	GCRY_CIPHER_AES256,
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"",
+	0,
+	"\x01\x00\x00\x00\x00\x00\x00\x00",
+	8,
+	"\xc2\xef\x32\x8e\x5c\x71\xc8\x3b",
+	"\x84\x31\x22\x13\x0f\x73\x64\xb7\x61\xe0\xb9\x74\x27\xe3\xdf\x28",
+      },
+      {
+	GCRY_CIPHER_AES256,
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"",
+	0,
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	12,
+	"\x9a\xab\x2a\xeb\x3f\xaa\x0a\x34\xae\xa8\xe2\xb1",
+	"\x8c\xa5\x0d\xa9\xae\x65\x59\xe4\x8f\xd1\x0f\x6e\x5c\x9c\xa1\x7e",
+      },
+      {
+	GCRY_CIPHER_AES256,
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"",
+	0,
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	16,
+	"\x85\xa0\x1b\x63\x02\x5b\xa1\x9b\x7f\xd3\xdd\xfc\x03\x3b\x3e\x76",
+	"\xc9\xea\xc6\xfa\x70\x09\x42\x70\x2e\x90\x86\x23\x83\xc6\xc3\x66",
+      },
+      {
+	GCRY_CIPHER_AES256,
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"",
+	0,
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	32,
+	"\x4a\x6a\x9d\xb4\xc8\xc6\x54\x92\x01\xb9\xed\xb5\x30\x06\xcb\xa8"
+	"\x21\xec\x9c\xf8\x50\x94\x8a\x7c\x86\xc6\x8a\xc7\x53\x9d\x02\x7f",
+	"\xe8\x19\xe6\x3a\xbc\xd0\x20\xb0\x06\xa9\x76\x39\x76\x32\xeb\x5d",
+      },
+      {
+	GCRY_CIPHER_AES256,
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"",
+	0,
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	48,
+	"\xc0\x0d\x12\x18\x93\xa9\xfa\x60\x3f\x48\xcc\xc1\xca\x3c\x57\xce"
+	"\x74\x99\x24\x5e\xa0\x04\x6d\xb1\x6c\x53\xc7\xc6\x6f\xe7\x17\xe3"
+	"\x9c\xf6\xc7\x48\x83\x7b\x61\xf6\xee\x3a\xdc\xee\x17\x53\x4e\xd5",
+	"\x79\x0b\xc9\x68\x80\xa9\x9b\xa8\x04\xbd\x12\xc0\xe6\xa2\x2c\xc4",
+      },
+      {
+	GCRY_CIPHER_AES256,
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"",
+	0,
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	64,
+	"\xc2\xd5\x16\x0a\x1f\x86\x83\x83\x49\x10\xac\xda\xfc\x41\xfb\xb1"
+	"\x63\x2d\x4a\x35\x3e\x8b\x90\x5e\xc9\xa5\x49\x9a\xc3\x4f\x96\xc7"
+	"\xe1\x04\x9e\xb0\x80\x88\x38\x91\xa4\xdb\x8c\xaa\xa1\xf9\x9d\xd0"
+	"\x04\xd8\x04\x87\x54\x07\x35\x23\x4e\x37\x44\x51\x2c\x6f\x90\xce",
+	"\x11\x28\x64\xc2\x69\xfc\x0d\x9d\x88\xc6\x1f\xa4\x7e\x39\xaa\x08",
+      },
+      {
+	GCRY_CIPHER_AES256,
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x01",
+	1,
+	"\x02\x00\x00\x00\x00\x00\x00\x00",
+	8,
+	"\x1d\xe2\x29\x67\x23\x7a\x81\x32",
+	"\x91\x21\x3f\x26\x7e\x3b\x45\x2f\x02\xd0\x1a\xe3\x3e\x4e\xc8\x54",
+      },
+      {
+	GCRY_CIPHER_AES256,
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x01",
+	1,
+	"\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	12,
+	"\x16\x3d\x6f\x9c\xc1\xb3\x46\xcd\x45\x3a\x2e\x4c",
+	"\xc1\xa4\xa1\x9a\xe8\x00\x94\x1c\xcd\xc5\x7c\xc8\x41\x3c\x27\x7f",
+      },
+      {
+	GCRY_CIPHER_AES256,
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x01",
+	1,
+	"\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	16,
+	"\xc9\x15\x45\x82\x3c\xc2\x4f\x17\xdb\xb0\xe9\xe8\x07\xd5\xec\x17",
+	"\xb2\x92\xd2\x8f\xf6\x11\x89\xe8\xe4\x9f\x38\x75\xef\x91\xaf\xf7",
+      },
+      {
+	GCRY_CIPHER_AES256,
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x01",
+	1,
+	"\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	32,
+	"\x07\xda\xd3\x64\xbf\xc2\xb9\xda\x89\x11\x6d\x7b\xef\x6d\xaa\xaf"
+	"\x6f\x25\x55\x10\xaa\x65\x4f\x92\x0a\xc8\x1b\x94\xe8\xba\xd3\x65",
+	"\xae\xa1\xba\xd1\x27\x02\xe1\x96\x56\x04\x37\x4a\xab\x96\xdb\xbc",
+      },
+      {
+	GCRY_CIPHER_AES256,
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x01",
+	1,
+	"\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	48,
+	"\xc6\x7a\x1f\x0f\x56\x7a\x51\x98\xaa\x1f\xcc\x8e\x3f\x21\x31\x43"
+	"\x36\xf7\xf5\x1c\xa8\xb1\xaf\x61\xfe\xac\x35\xa8\x64\x16\xfa\x47"
+	"\xfb\xca\x3b\x5f\x74\x9c\xdf\x56\x45\x27\xf2\x31\x4f\x42\xfe\x25",
+	"\x03\x33\x27\x42\xb2\x28\xc6\x47\x17\x36\x16\xcf\xd4\x4c\x54\xeb",
+      },
+      {
+	GCRY_CIPHER_AES256,
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x01",
+	1,
+	"\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	64,
+	"\x67\xfd\x45\xe1\x26\xbf\xb9\xa7\x99\x30\xc4\x3a\xad\x2d\x36\x96"
+	"\x7d\x3f\x0e\x4d\x21\x7c\x1e\x55\x1f\x59\x72\x78\x70\xbe\xef\xc9"
+	"\x8c\xb9\x33\xa8\xfc\xe9\xde\x88\x7b\x1e\x40\x79\x99\x88\xdb\x1f"
+	"\xc3\xf9\x18\x80\xed\x40\x5b\x2d\xd2\x98\x31\x88\x58\x46\x7c\x89",
+	"\x5b\xde\x02\x85\x03\x7c\x5d\xe8\x1e\x5b\x57\x0a\x04\x9b\x62\xa0",
+      },
+      {
+	GCRY_CIPHER_AES256,
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	12,
+	"\x02\x00\x00\x00",
+	4,
+	"\x22\xb3\xf4\xcd",
+	"\x18\x35\xe5\x17\x74\x1d\xfd\xdc\xcf\xa0\x7f\xa4\x66\x1b\x74\xcf",
+      },
+      {
+	GCRY_CIPHER_AES256,
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x02\x00",
+	18,
+	"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x04\x00\x00\x00",
+	20,
+	"\x43\xdd\x01\x63\xcd\xb4\x8f\x9f\xe3\x21\x2b\xf6\x1b\x20\x19\x76"
+	"\x06\x7f\x34\x2b",
+	"\xb8\x79\xad\x97\x6d\x82\x42\xac\xc1\x88\xab\x59\xca\xbf\xe3\x07",
+      },
+      {
+	GCRY_CIPHER_AES256,
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x02\x00\x00\x00",
+	20,
+	"\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x04\x00",
+	18,
+	"\x46\x24\x01\x72\x4b\x5c\xe6\x58\x8d\x5a\x54\xaa\xe5\x37\x55\x13"
+	"\xa0\x75",
+	"\xcf\xcd\xf5\x04\x21\x12\xaa\x29\x68\x5c\x91\x2f\xc2\x05\x65\x43",
+      },
+      {
+	GCRY_CIPHER_AES256,
+	"\xe6\x60\x21\xd5\xeb\x8e\x4f\x40\x66\xd4\xad\xb9\xc3\x35\x60\xe4"
+	"\xf4\x6e\x44\xbb\x3d\xa0\x01\x5c\x94\xf7\x08\x87\x36\x86\x42\x00",
+	"\xe0\xea\xf5\x28\x4d\x88\x4a\x0e\x77\xd3\x16\x46",
+	"",
+	0,
+	"",
+	0,
+	"",
+	"\x16\x9f\xbb\x2f\xbf\x38\x9a\x99\x5f\x63\x90\xaf\x22\x22\x8a\x62",
+      },
+      {
+	GCRY_CIPHER_AES256,
+	"\xba\xe8\xe3\x7f\xc8\x34\x41\xb1\x60\x34\x56\x6b\x7a\x80\x6c\x46"
+	"\xbb\x91\xc3\xc5\xae\xdb\x64\xa6\xc5\x90\xbc\x84\xd1\xa5\xe2\x69",
+	"\xe4\xb4\x78\x01\xaf\xc0\x57\x7e\x34\x69\x9b\x9e",
+	"\x4f\xbd\xc6\x6f\x14",
+	5,
+	"\x67\x1f\xdd",
+	3,
+	"\x0e\xac\xcb",
+	"\x93\xda\x9b\xb8\x13\x33\xae\xe0\xc7\x85\xb2\x40\xd3\x19\x71\x9d",
+      },
+      {
+	GCRY_CIPHER_AES256,
+	"\x65\x45\xfc\x88\x0c\x94\xa9\x51\x98\x87\x42\x96\xd5\xcc\x1f\xd1"
+	"\x61\x32\x0b\x69\x20\xce\x07\x78\x7f\x86\x74\x3b\x27\x5d\x1a\xb3",
+	"\x2f\x6d\x1f\x04\x34\xd8\x84\x8c\x11\x77\x44\x1f",
+	"\x67\x87\xf3\xea\x22\xc1\x27\xaa\xf1\x95",
+	10,
+	"\x19\x54\x95\x86\x0f\x04",
+	6,
+	"\xa2\x54\xda\xd4\xf3\xf9",
+	"\x6b\x62\xb8\x4d\xc4\x0c\x84\x63\x6a\x5e\xc1\x20\x20\xec\x8c\x2c",
+      },
+      {
+	GCRY_CIPHER_AES256,
+	"\xd1\x89\x47\x28\xb3\xfe\xd1\x47\x3c\x52\x8b\x84\x26\xa5\x82\x99"
+	"\x59\x29\xa1\x49\x9e\x9a\xd8\x78\x0c\x8d\x63\xd0\xab\x41\x49\xc0",
+	"\x9f\x57\x2c\x61\x4b\x47\x45\x91\x44\x74\xe7\xc7",
+	"\x48\x9c\x8f\xde\x2b\xe2\xcf\x97\xe7\x4e\x93\x2d\x4e\xd8\x7d",
+	15,
+	"\xc9\x88\x2e\x53\x86\xfd\x9f\x92\xec",
+	9,
+	"\x0d\xf9\xe3\x08\x67\x82\x44\xc4\x4b",
+	"\xc0\xfd\x3d\xc6\x62\x8d\xfe\x55\xeb\xb0\xb9\xfb\x22\x95\xc8\xc2",
+      },
+      {
+	GCRY_CIPHER_AES256,
+	"\xa4\x41\x02\x95\x2e\xf9\x4b\x02\xb8\x05\x24\x9b\xac\x80\xe6\xf6"
+	"\x14\x55\xbf\xac\x83\x08\xa2\xd4\x0d\x8c\x84\x51\x17\x80\x82\x35",
+	"\x5c\x9e\x94\x0f\xea\x2f\x58\x29\x50\xa7\x0d\x5a",
+	"\x0d\xa5\x52\x10\xcc\x1c\x1b\x0a\xbd\xe3\xb2\xf2\x04\xd1\xe9\xf8"
+	"\xb0\x6b\xc4\x7f",
+	20,
+	"\x1d\xb2\x31\x6f\xd5\x68\x37\x8d\xa1\x07\xb5\x2b",
+	12,
+	"\x8d\xbe\xb9\xf7\x25\x5b\xf5\x76\x9d\xd5\x66\x92",
+	"\x40\x40\x99\xc2\x58\x7f\x64\x97\x9f\x21\x82\x67\x06\xd4\x97\xd5",
+      },
+      {
+	GCRY_CIPHER_AES256,
+	"\x97\x45\xb3\xd1\xae\x06\x55\x6f\xb6\xaa\x78\x90\xbe\xbc\x18\xfe"
+	"\x6b\x3d\xb4\xda\x3d\x57\xaa\x94\x84\x2b\x98\x03\xa9\x6e\x07\xfb",
+	"\x6d\xe7\x18\x60\xf7\x62\xeb\xfb\xd0\x82\x84\xe4",
+	"\xf3\x7d\xe2\x1c\x7f\xf9\x01\xcf\xe8\xa6\x96\x15\xa9\x3f\xdf\x7a"
+	"\x98\xca\xd4\x81\x79\x62\x45\x70\x9f",
+	25,
+	"\x21\x70\x2d\xe0\xde\x18\xba\xa9\xc9\x59\x62\x91\xb0\x84\x66",
+	15,
+	"\x79\x35\x76\xdf\xa5\xc0\xf8\x87\x29\xa7\xed\x3c\x2f\x1b\xff",
+	"\xb3\x08\x0d\x28\xf6\xeb\xb5\xd3\x64\x8c\xe9\x7b\xd5\xba\x67\xfd",
+      },
+      {
+	GCRY_CIPHER_AES256,
+	"\xb1\x88\x53\xf6\x8d\x83\x36\x40\xe4\x2a\x3c\x02\xc2\x5b\x64\x86"
+	"\x9e\x14\x6d\x7b\x23\x39\x87\xbd\xdf\xc2\x40\x87\x1d\x75\x76\xf7",
+	"\x02\x8e\xc6\xeb\x5e\xa7\xe2\x98\x34\x2a\x94\xd4",
+	"\x9c\x21\x59\x05\x8b\x1f\x0f\xe9\x14\x33\xa5\xbd\xc2\x0e\x21\x4e"
+	"\xab\x7f\xec\xef\x44\x54\xa1\x0e\xf0\x65\x7d\xf2\x1a\xc7",
+	30,
+	"\xb2\x02\xb3\x70\xef\x97\x68\xec\x65\x61\xc4\xfe\x6b\x7e\x72\x96"
+	"\xfa\x85",
+	18,
+	"\x85\x7e\x16\xa6\x49\x15\xa7\x87\x63\x76\x87\xdb\x4a\x95\x19\x63"
+	"\x5c\xdd",
+	"\x45\x4f\xc2\xa1\x54\xfe\xa9\x1f\x83\x63\xa3\x9f\xec\x7d\x0a\x49",
+      },
+      {
+	GCRY_CIPHER_AES256,
+	"\x3c\x53\x5d\xe1\x92\xea\xed\x38\x22\xa2\xfb\xbe\x2c\xa9\xdf\xc8"
+	"\x82\x55\xe1\x4a\x66\x1b\x8a\xa8\x2c\xc5\x42\x36\x09\x3b\xbc\x23",
+	"\x68\x80\x89\xe5\x55\x40\xdb\x18\x72\x50\x4e\x1c",
+	"\x73\x43\x20\xcc\xc9\xd9\xbb\xbb\x19\xcb\x81\xb2\xaf\x4e\xcb\xc3"
+	"\xe7\x28\x34\x32\x1f\x7a\xa0\xf7\x0b\x72\x82\xb4\xf3\x3d\xf2\x3f"
+	"\x16\x75\x41",
+	35,
+	"\xce\xd5\x32\xce\x41\x59\xb0\x35\x27\x7d\x4d\xfb\xb7\xdb\x62\x96"
+	"\x8b\x13\xcd\x4e\xec",
+	21,
+	"\x62\x66\x60\xc2\x6e\xa6\x61\x2f\xb1\x7a\xd9\x1e\x8e\x76\x76\x39"
+	"\xed\xd6\xc9\xfa\xee",
+	"\x9d\x6c\x70\x29\x67\x5b\x89\xea\xf4\xba\x1d\xed\x1a\x28\x65\x94",
+      },
+      {
+	GCRY_CIPHER_AES256,
+	"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"",
+	0,
+	"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x4d\xb9\x23\xdc\x79\x3e\xe6\x49\x7c\x76\xdc\xc0\x3a\x98\xe1\x08",
+	32,
+	"\xf3\xf8\x0f\x2c\xf0\xcb\x2d\xd9\xc5\x98\x4f\xcd\xa9\x08\x45\x6c"
+	"\xc5\x37\x70\x3b\x5b\xa7\x03\x24\xa6\x79\x3a\x7b\xf2\x18\xd3\xea",
+	"\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+      },
+      {
+	GCRY_CIPHER_AES256,
+	"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+	"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+	"",
+	0,
+	"\xeb\x36\x40\x27\x7c\x7f\xfd\x13\x03\xc7\xa5\x42\xd0\x2d\x3e\x4c"
+	"\x00\x00\x00\x00\x00\x00\x00\x00",
+	24,
+	"\x18\xce\x4f\x0b\x8c\xb4\xd0\xca\xc6\x5f\xea\x8f\x79\x25\x7b\x20"
+	"\x88\x8e\x53\xe7\x22\x99\xe5\x6d",
+	"\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+      }
+    };
+
+  gcry_cipher_hd_t hde, hdd;
+  unsigned char out[MAX_DATA_LEN];
+  unsigned char tag[16];
+  int i, keylen;
+  gcry_error_t err = 0;
+  size_t taglen2;
+
+  if (verbose)
+    fprintf (stderr, "  Starting GCM-SIV checks.\n");
+
+  for (i = 0; i < sizeof (tv) / sizeof (tv[0]); i++)
+    {
+      if (gcry_cipher_test_algo (tv[i].algo) && in_fips_mode)
+	{
+	  if (verbose)
+	    fprintf (stderr, "  algorithm %d not available in fips mode\n",
+		     tv[i].algo);
+	  continue;
+	}
+
+      if (verbose)
+	fprintf (stderr, "    checking GCM-SIV mode for %s [%i]\n",
+		 gcry_cipher_algo_name (tv[i].algo),
+		 tv[i].algo);
+      err = gcry_cipher_open (&hde, tv[i].algo, GCRY_CIPHER_MODE_GCM_SIV, 0);
+      if (!err)
+	err = gcry_cipher_open (&hdd, tv[i].algo, GCRY_CIPHER_MODE_GCM_SIV, 0);
+      if (err)
+	{
+	  fail ("aes-gcm-siv, gcry_cipher_open failed: %s\n", gpg_strerror (err));
+	  return;
+	}
+
+      keylen = gcry_cipher_get_algo_keylen (tv[i].algo);
+      if (!keylen)
+	{
+	  fail ("aes-gcm-siv, gcry_cipher_get_algo_keylen failed\n");
+	  return;
+	}
+
+      err = gcry_cipher_setkey (hde, tv[i].key, keylen);
+      if (!err)
+	err = gcry_cipher_setkey (hdd, tv[i].key, keylen);
+      if (err)
+	{
+	  fail ("aes-gcm-siv, gcry_cipher_setkey failed: %s\n",
+		gpg_strerror (err));
+	  gcry_cipher_close (hde);
+	  gcry_cipher_close (hdd);
+	  return;
+	}
+
+      err = gcry_cipher_setiv (hde, tv[i].nonce, 12);
+      if (!err)
+	err = gcry_cipher_setiv (hdd, tv[i].nonce, 12);
+      if (err)
+	{
+	  fail ("aes-gcm-siv, gcry_cipher_setiv failed: %s\n",
+		gpg_strerror (err));
+	  gcry_cipher_close (hde);
+	  gcry_cipher_close (hdd);
+	  return;
+	}
+
+      if (tv[i].adlen >= 0)
+	{
+	  err = gcry_cipher_authenticate (hde, tv[i].ad, tv[i].adlen);
+	  if (!err)
+	    err = gcry_cipher_authenticate (hdd, tv[i].ad, tv[i].adlen);
+	  if (err)
+	    {
+	      fail ("aes-gcm-siv, gcry_cipher_authenticate failed: %s\n",
+		    gpg_strerror (err));
+	      gcry_cipher_close (hde);
+	      gcry_cipher_close (hdd);
+	      return;
+	    }
+	}
+
+      err = gcry_cipher_info (hde, GCRYCTL_GET_TAGLEN, NULL, &taglen2);
+      if (err)
+	{
+	  fail ("cipher-siv, gcryctl_get_taglen failed (tv %d): %s\n",
+		i, gpg_strerror (err));
+	  gcry_cipher_close (hde);
+	  gcry_cipher_close (hdd);
+	  return;
+	}
+      if (taglen2 != 16)
+	{
+	  fail ("cipher-siv, gcryctl_get_taglen returned bad length"
+		" (tv %d): got=%zu want=%d\n",
+		i, taglen2, 16);
+	  gcry_cipher_close (hde);
+	  gcry_cipher_close (hdd);
+	  return;
+	}
+
+      if (tv[i].inlen)
+	{
+	  err = gcry_cipher_encrypt (hde, out, tv[i].inlen,
+				     tv[i].plaintext, tv[i].inlen);
+	  if (err)
+	    {
+	      fail ("aes-gcm-siv, gcry_cipher_encrypt (%d) failed: %s\n",
+		    i, gpg_strerror (err));
+	      gcry_cipher_close (hde);
+	      gcry_cipher_close (hdd);
+	      return;
+	    }
+
+	  if (memcmp (tv[i].out, out, tv[i].inlen))
+	    {
+	      mismatch (tv[i].out, tv[i].inlen, out, tv[i].inlen);
+	      fail ("aes-gcm-siv, encrypt mismatch entry %d\n", i);
+	    }
+
+	  err = gcry_cipher_gettag (hde, tag, taglen2);
+	  if (err)
+	    {
+	      fail ("aes-gcm-siv, gcry_cipher_gettag(%d) failed: %s\n",
+		    i, gpg_strerror (err));
+	      gcry_cipher_close (hde);
+	      gcry_cipher_close (hdd);
+	      return;
+	    }
+
+	  if (memcmp (tv[i].tag, tag, taglen2))
+	    {
+	      mismatch (tv[i].tag, taglen2, tag, taglen2);
+	      fail ("aes-gcm-siv, tag mismatch entry %d\n", i);
+	    }
+
+	  err = gcry_cipher_set_decryption_tag (hdd, tag, taglen2);
+	  if (err)
+	    {
+	      fail ("aes-gcm-siv, gcry_cipher_set_decryption_tag (%d) failed: %s\n",
+		    i, gpg_strerror (err));
+	      gcry_cipher_close (hde);
+	      gcry_cipher_close (hdd);
+	      return;
+	    }
+
+	  err = gcry_cipher_decrypt (hdd, out, tv[i].inlen, NULL, 0);
+	  if (err)
+	    {
+	      fail ("aes-gcm-siv, gcry_cipher_decrypt (%d) failed: %s\n",
+		    i, gpg_strerror (err));
+	      gcry_cipher_close (hde);
+	      gcry_cipher_close (hdd);
+	      return;
+	    }
+
+	  if (memcmp (tv[i].plaintext, out, tv[i].inlen))
+	    fail ("aes-gcm-siv, decrypt mismatch entry %d\n", i);
+
+	  err = gcry_cipher_checktag (hdd, tag, taglen2);
+	  if (err)
+	    {
+	      fail ("aes-gcm-siv, gcry_cipher_checktag (%d) failed: %s\n",
+		    i, gpg_strerror (err));
+	      gcry_cipher_close (hde);
+	      gcry_cipher_close (hdd);
+	      return;
+	    }
+	}
+      else
+	{
+	  err = gcry_cipher_gettag (hde, tag, taglen2);
+	  if (err)
+	    {
+	      fail ("aes-gcm-siv, gcry_cipher_gettag(%d) failed: %s\n",
+		    i, gpg_strerror (err));
+	      gcry_cipher_close (hde);
+	      gcry_cipher_close (hdd);
+	      return;
+	    }
+
+	  if (memcmp (tv[i].tag, tag, taglen2))
+	    {
+	      mismatch (tv[i].tag, taglen2, tag, taglen2);
+	      fail ("aes-gcm-siv, tag mismatch entry %d\n", i);
+	    }
+
+	  err = gcry_cipher_checktag (hdd, tv[i].tag, taglen2);
+	  if (err)
+	    {
+	      fail ("aes-gcm-siv, gcry_cipher_checktag (%d) failed: %s\n",
+		    i, gpg_strerror (err));
+	      gcry_cipher_close (hde);
+	      gcry_cipher_close (hdd);
+	      return;
+	    }
+
+	  tag[13] ^= 0x4;
+	  err = gcry_cipher_checktag (hdd, tag, taglen2);
+	  if (gpg_err_code (err) != GPG_ERR_CHECKSUM)
+	    {
+	      fail ("aes-gcm-siv, gcry_cipher_checktag (%d) expected checksum fail: %s\n",
+		    i, gpg_strerror (err));
+	      gcry_cipher_close (hde);
+	      gcry_cipher_close (hdd);
+	      return;
+	    }
+	}
+
+      gcry_cipher_close (hde);
+      gcry_cipher_close (hdd);
+    }
+  if (verbose)
+    fprintf (stderr, "  Completed GCM-SIV checks.\n");
+}
+
+
 static void
 _check_poly1305_cipher (unsigned int step)
 {
@@ -10554,6 +11467,7 @@ check_cipher_modes(void)
   check_xts_cipher ();
   check_eax_cipher ();
   check_siv_cipher ();
+  check_gcm_siv_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 91eb7cc5..00cb11de 100644
--- a/tests/bench-slope.c
+++ b/tests/bench-slope.c
@@ -1544,6 +1544,52 @@ static struct bench_ops siv_authenticate_ops = {
 };
 
 
+static void
+bench_gcm_siv_encrypt_do_bench (struct bench_obj *obj, void *buf,
+				size_t buflen)
+{
+  char nonce[12] = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce,
+                     0xdb, 0xad, 0xde, 0xca, 0xf8, 0x88 };
+  bench_aead_encrypt_do_bench (obj, buf, buflen, nonce, sizeof(nonce));
+}
+
+static void
+bench_gcm_siv_decrypt_do_bench (struct bench_obj *obj, void *buf,
+				size_t buflen)
+{
+  char nonce[12] = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce,
+                     0xdb, 0xad, 0xde, 0xca, 0xf8, 0x88 };
+  bench_aead_decrypt_do_bench (obj, buf, buflen, nonce, sizeof(nonce));
+}
+
+static void
+bench_gcm_siv_authenticate_do_bench (struct bench_obj *obj, void *buf,
+				     size_t buflen)
+{
+  char nonce[12] = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce,
+                     0xdb, 0xad, 0xde, 0xca, 0xf8, 0x88 };
+  bench_aead_authenticate_do_bench (obj, buf, buflen, nonce, sizeof(nonce));
+}
+
+static struct bench_ops gcm_siv_encrypt_ops = {
+  &bench_encrypt_init,
+  &bench_encrypt_free,
+  &bench_gcm_siv_encrypt_do_bench
+};
+
+static struct bench_ops gcm_siv_decrypt_ops = {
+  &bench_encrypt_init,
+  &bench_encrypt_free,
+  &bench_gcm_siv_decrypt_do_bench
+};
+
+static struct bench_ops gcm_siv_authenticate_ops = {
+  &bench_encrypt_init,
+  &bench_encrypt_free,
+  &bench_gcm_siv_authenticate_do_bench
+};
+
+
 static void
 bench_eax_encrypt_do_bench (struct bench_obj *obj, void *buf,
 			    size_t buflen)
@@ -1663,6 +1709,9 @@ static struct bench_cipher_mode cipher_modes[] = {
   {GCRY_CIPHER_MODE_SIV, "SIV enc", &siv_encrypt_ops},
   {GCRY_CIPHER_MODE_SIV, "SIV dec", &siv_decrypt_ops},
   {GCRY_CIPHER_MODE_SIV, "SIV auth", &siv_authenticate_ops},
+  {GCRY_CIPHER_MODE_GCM_SIV, "GCM-SIV enc", &gcm_siv_encrypt_ops},
+  {GCRY_CIPHER_MODE_GCM_SIV, "GCM-SIV dec", &gcm_siv_decrypt_ops},
+  {GCRY_CIPHER_MODE_GCM_SIV, "GCM-SIV auth", &gcm_siv_authenticate_ops},
   {GCRY_CIPHER_MODE_POLY1305, "POLY1305 enc", &poly1305_encrypt_ops},
   {GCRY_CIPHER_MODE_POLY1305, "POLY1305 dec", &poly1305_decrypt_ops},
   {GCRY_CIPHER_MODE_POLY1305, "POLY1305 auth", &poly1305_authenticate_ops},
@@ -1677,6 +1726,7 @@ cipher_bench_one (int algo, struct bench_cipher_mode *pmode)
   struct bench_obj obj = { 0 };
   double result;
   unsigned int blklen;
+  unsigned int keylen;
 
   mode.algo = algo;
 
@@ -1685,6 +1735,10 @@ cipher_bench_one (int algo, struct bench_cipher_mode *pmode)
   if (!blklen)
     return;
 
+  keylen = gcry_cipher_get_algo_keylen (algo);
+  if (!keylen)
+    return;
+
   /* Stream cipher? Only test with "ECB" and POLY1305. */
   if (blklen == 1 && (mode.mode != GCRY_CIPHER_MODE_ECB &&
 		      mode.mode != GCRY_CIPHER_MODE_POLY1305))
@@ -1715,6 +1769,14 @@ cipher_bench_one (int algo, struct bench_cipher_mode *pmode)
   if (mode.mode == GCRY_CIPHER_MODE_SIV && blklen != GCRY_SIV_BLOCK_LEN)
     return;
 
+  /* GCM-SIV has restrictions for block-size */
+  if (mode.mode == GCRY_CIPHER_MODE_GCM_SIV && blklen != GCRY_SIV_BLOCK_LEN)
+    return;
+
+  /* GCM-SIV has restrictions for key length */
+  if (mode.mode == GCRY_CIPHER_MODE_GCM_SIV && !(keylen == 16 || keylen == 32))
+    return;
+
   /* Our OCB implementation has restrictions for block-size.  */
   if (mode.mode == GCRY_CIPHER_MODE_OCB && blklen != GCRY_OCB_BLOCK_LEN)
     return;
-- 
2.30.2




More information about the Gcrypt-devel mailing list