[PATCH 01/13] Initial implementation of GCM

Jussi Kivilinna jussi.kivilinna at iki.fi
Wed Nov 20 18:00:02 CET 2013


From: Dmitry Eremin-Solenikov <dbaryshkov at gmail.com>

* cipher/Makefile.am: Add 'cipher-gcm.c'.
* cipher/cipher-ccm.c (_gcry_ciphert_ccm_set_lengths)
(_gcry_cipher_ccm_authenticate, _gcry_cipher_ccm_tag)
(_gcry_cipher_ccm_encrypt, _gcry_cipher_ccm_decrypt): Change
'c->u_mode.ccm.tag' to 'c->marks.tag'.
* cipher/cipher-gcm.c: New.
* cipher/cipher-internal.h (GCM_USE_TABLES): New.
(gcry_cipher_handle): Add 'marks.tag', 'u_tag', 'length' and
'gcm_table'; Remove 'u_mode.ccm.tag'.
(_gcry_cipher_gcm_encrypt, _gcry_cipher_gcm_decrypt)
(_gcry_cipher_gcm_setiv, _gcry_cipher_gcm_authenticate)
(_gcry_cipher_gcm_get_tag, _gcry_cipher_gcm_check_tag): New.
* cipher/cipher.c (_gcry_cipher_open_internal, cipher_setkey)
(cipher_encrypt, cipher_decrypt, _gcry_cipher_authenticate)
(_gcry_cipher_gettag, _gcry_cipher_checktag): Add GCM mode handling.
* src/gcrypt.h.in (gcry_cipher_modes): Add GCRY_CIPHER_MODE_GCM.
(GCRY_GCM_BLOCK_LEN): New.
* tests/basic.c (check_gcm_cipher): New.
(check_ciphers): Add GCM check.
(check_cipher_modes): Call 'check_gcm_cipher'.
* tests/bench-slope.c (bench_gcm_encrypt_do_bench)
(bench_gcm_decrypt_do_bench, bench_gcm_authenticate_do_bench)
(gcm_encrypt_ops, gcm_decrypt_ops, gcm_authenticate_ops): New.
(cipher_modes): Add GCM enc/dec/auth.
(cipher_bench_one): Limit GCM to block ciphers with 16 byte block-size.
* tests/benchmark.c (cipher_bench): Add GCM.
--

Currently it is still quite slow.

Still no support for generate_iv(). Is it really necessary?

TODO: Merge/reuse cipher-internal state used by CCM.

Changelog entry will be present in final patch submission.

Changes since v1:
- 6x-7x speedup.
- added bench-slope support

Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov at gmail.com>
[jk: mangle new file throught 'indent -nut']
[jk: few fixes]
[jk: changelog]
---
 cipher/Makefile.am       |    2 
 cipher/cipher-ccm.c      |   12 +
 cipher/cipher-gcm.c      |  483 ++++++++++++++++++++++++++++++++++++++++++++++
 cipher/cipher-internal.h |   36 +++
 cipher/cipher.c          |   28 +++
 src/gcrypt.h.in          |    6 -
 tests/basic.c            |  337 ++++++++++++++++++++++++++++++++
 tests/bench-slope.c      |  128 ++++++++++++
 tests/benchmark.c        |    2 
 9 files changed, 1026 insertions(+), 8 deletions(-)
 create mode 100644 cipher/cipher-gcm.c

diff --git a/cipher/Makefile.am b/cipher/Makefile.am
index 2fd9822..e29b28d 100644
--- a/cipher/Makefile.am
+++ b/cipher/Makefile.am
@@ -40,7 +40,7 @@ libcipher_la_LIBADD = $(GCRYPT_MODULES)
 libcipher_la_SOURCES = \
 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-ccm.c cipher-cmac.c cipher-gcm.c \
 cipher-selftest.c cipher-selftest.h \
 pubkey.c pubkey-internal.h pubkey-util.c \
 md.c \
diff --git a/cipher/cipher-ccm.c b/cipher/cipher-ccm.c
index 1eba7f0..d2b8841 100644
--- a/cipher/cipher-ccm.c
+++ b/cipher/cipher-ccm.c
@@ -164,7 +164,7 @@ _gcry_cipher_ccm_set_lengths (gcry_cipher_hd_t c, size_t encryptlen,
   /* Authentication field must be 4, 6, 8, 10, 12, 14 or 16. */
   if ((M_ * 2 + 2) != M || M < 4 || M > 16)
     return GPG_ERR_INV_LENGTH;
-  if (!c->u_mode.ccm.nonce || c->u_mode.ccm.tag)
+  if (!c->u_mode.ccm.nonce || c->marks.tag)
     return GPG_ERR_INV_STATE;
   if (c->u_mode.ccm.lengths)
     return GPG_ERR_INV_STATE;
@@ -235,7 +235,7 @@ _gcry_cipher_ccm_authenticate (gcry_cipher_hd_t c, const unsigned char *abuf,
 
   if (abuflen > 0 && !abuf)
     return GPG_ERR_INV_ARG;
-  if (!c->u_mode.ccm.nonce || !c->u_mode.ccm.lengths || c->u_mode.ccm.tag)
+  if (!c->u_mode.ccm.nonce || !c->u_mode.ccm.lengths || c->marks.tag)
     return GPG_ERR_INV_STATE;
   if (abuflen > c->u_mode.ccm.aadlen)
     return GPG_ERR_INV_LENGTH;
@@ -267,7 +267,7 @@ _gcry_cipher_ccm_tag (gcry_cipher_hd_t c, unsigned char *outbuf,
   if (c->u_mode.ccm.encryptlen > 0)
     return GPG_ERR_UNFINISHED;
 
-  if (!c->u_mode.ccm.tag)
+  if (!c->marks.tag)
     {
       burn = do_cbc_mac (c, NULL, 0, 1); /* Perform final padding.  */
 
@@ -280,6 +280,8 @@ _gcry_cipher_ccm_tag (gcry_cipher_hd_t c, unsigned char *outbuf,
 
       if (burn)
         _gcry_burn_stack (burn + sizeof(void *) * 5);
+
+      c->marks.tag = 1;
     }
 
   if (!check)
@@ -320,7 +322,7 @@ _gcry_cipher_ccm_encrypt (gcry_cipher_hd_t c, unsigned char *outbuf,
 
   if (outbuflen < inbuflen)
     return GPG_ERR_BUFFER_TOO_SHORT;
-  if (!c->u_mode.ccm.nonce || c->u_mode.ccm.tag || !c->u_mode.ccm.lengths ||
+  if (!c->u_mode.ccm.nonce || c->marks.tag || !c->u_mode.ccm.lengths ||
       c->u_mode.ccm.aadlen > 0)
     return GPG_ERR_INV_STATE;
   if (inbuflen > c->u_mode.ccm.encryptlen)
@@ -345,7 +347,7 @@ _gcry_cipher_ccm_decrypt (gcry_cipher_hd_t c, unsigned char *outbuf,
 
   if (outbuflen < inbuflen)
     return GPG_ERR_BUFFER_TOO_SHORT;
-  if (!c->u_mode.ccm.nonce || c->u_mode.ccm.tag || !c->u_mode.ccm.lengths ||
+  if (!c->u_mode.ccm.nonce || c->marks.tag || !c->u_mode.ccm.lengths ||
       c->u_mode.ccm.aadlen > 0)
     return GPG_ERR_INV_STATE;
   if (inbuflen > c->u_mode.ccm.encryptlen)
diff --git a/cipher/cipher-gcm.c b/cipher/cipher-gcm.c
new file mode 100644
index 0000000..b6c50bf
--- /dev/null
+++ b/cipher/cipher-gcm.c
@@ -0,0 +1,483 @@
+/* cipher-gcm.c  - Generic Galois Counter Mode implementation
+ * Copyright (C) 2013 Dmitry Eremin-Solenikov
+ *
+ * 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 "ath.h"
+#include "bufhelp.h"
+#include "./cipher-internal.h"
+
+#ifdef GCM_USE_TABLES
+static const byte gcmR[256][2] = {
+  {0x00, 0x00,}, {0x01, 0xc2,}, {0x03, 0x84,}, {0x02, 0x46,},
+  {0x07, 0x08,}, {0x06, 0xca,}, {0x04, 0x8c,}, {0x05, 0x4e,},
+  {0x0e, 0x10,}, {0x0f, 0xd2,}, {0x0d, 0x94,}, {0x0c, 0x56,},
+  {0x09, 0x18,}, {0x08, 0xda,}, {0x0a, 0x9c,}, {0x0b, 0x5e,},
+  {0x1c, 0x20,}, {0x1d, 0xe2,}, {0x1f, 0xa4,}, {0x1e, 0x66,},
+  {0x1b, 0x28,}, {0x1a, 0xea,}, {0x18, 0xac,}, {0x19, 0x6e,},
+  {0x12, 0x30,}, {0x13, 0xf2,}, {0x11, 0xb4,}, {0x10, 0x76,},
+  {0x15, 0x38,}, {0x14, 0xfa,}, {0x16, 0xbc,}, {0x17, 0x7e,},
+  {0x38, 0x40,}, {0x39, 0x82,}, {0x3b, 0xc4,}, {0x3a, 0x06,},
+  {0x3f, 0x48,}, {0x3e, 0x8a,}, {0x3c, 0xcc,}, {0x3d, 0x0e,},
+  {0x36, 0x50,}, {0x37, 0x92,}, {0x35, 0xd4,}, {0x34, 0x16,},
+  {0x31, 0x58,}, {0x30, 0x9a,}, {0x32, 0xdc,}, {0x33, 0x1e,},
+  {0x24, 0x60,}, {0x25, 0xa2,}, {0x27, 0xe4,}, {0x26, 0x26,},
+  {0x23, 0x68,}, {0x22, 0xaa,}, {0x20, 0xec,}, {0x21, 0x2e,},
+  {0x2a, 0x70,}, {0x2b, 0xb2,}, {0x29, 0xf4,}, {0x28, 0x36,},
+  {0x2d, 0x78,}, {0x2c, 0xba,}, {0x2e, 0xfc,}, {0x2f, 0x3e,},
+  {0x70, 0x80,}, {0x71, 0x42,}, {0x73, 0x04,}, {0x72, 0xc6,},
+  {0x77, 0x88,}, {0x76, 0x4a,}, {0x74, 0x0c,}, {0x75, 0xce,},
+  {0x7e, 0x90,}, {0x7f, 0x52,}, {0x7d, 0x14,}, {0x7c, 0xd6,},
+  {0x79, 0x98,}, {0x78, 0x5a,}, {0x7a, 0x1c,}, {0x7b, 0xde,},
+  {0x6c, 0xa0,}, {0x6d, 0x62,}, {0x6f, 0x24,}, {0x6e, 0xe6,},
+  {0x6b, 0xa8,}, {0x6a, 0x6a,}, {0x68, 0x2c,}, {0x69, 0xee,},
+  {0x62, 0xb0,}, {0x63, 0x72,}, {0x61, 0x34,}, {0x60, 0xf6,},
+  {0x65, 0xb8,}, {0x64, 0x7a,}, {0x66, 0x3c,}, {0x67, 0xfe,},
+  {0x48, 0xc0,}, {0x49, 0x02,}, {0x4b, 0x44,}, {0x4a, 0x86,},
+  {0x4f, 0xc8,}, {0x4e, 0x0a,}, {0x4c, 0x4c,}, {0x4d, 0x8e,},
+  {0x46, 0xd0,}, {0x47, 0x12,}, {0x45, 0x54,}, {0x44, 0x96,},
+  {0x41, 0xd8,}, {0x40, 0x1a,}, {0x42, 0x5c,}, {0x43, 0x9e,},
+  {0x54, 0xe0,}, {0x55, 0x22,}, {0x57, 0x64,}, {0x56, 0xa6,},
+  {0x53, 0xe8,}, {0x52, 0x2a,}, {0x50, 0x6c,}, {0x51, 0xae,},
+  {0x5a, 0xf0,}, {0x5b, 0x32,}, {0x59, 0x74,}, {0x58, 0xb6,},
+  {0x5d, 0xf8,}, {0x5c, 0x3a,}, {0x5e, 0x7c,}, {0x5f, 0xbe,},
+  {0xe1, 0x00,}, {0xe0, 0xc2,}, {0xe2, 0x84,}, {0xe3, 0x46,},
+  {0xe6, 0x08,}, {0xe7, 0xca,}, {0xe5, 0x8c,}, {0xe4, 0x4e,},
+  {0xef, 0x10,}, {0xee, 0xd2,}, {0xec, 0x94,}, {0xed, 0x56,},
+  {0xe8, 0x18,}, {0xe9, 0xda,}, {0xeb, 0x9c,}, {0xea, 0x5e,},
+  {0xfd, 0x20,}, {0xfc, 0xe2,}, {0xfe, 0xa4,}, {0xff, 0x66,},
+  {0xfa, 0x28,}, {0xfb, 0xea,}, {0xf9, 0xac,}, {0xf8, 0x6e,},
+  {0xf3, 0x30,}, {0xf2, 0xf2,}, {0xf0, 0xb4,}, {0xf1, 0x76,},
+  {0xf4, 0x38,}, {0xf5, 0xfa,}, {0xf7, 0xbc,}, {0xf6, 0x7e,},
+  {0xd9, 0x40,}, {0xd8, 0x82,}, {0xda, 0xc4,}, {0xdb, 0x06,},
+  {0xde, 0x48,}, {0xdf, 0x8a,}, {0xdd, 0xcc,}, {0xdc, 0x0e,},
+  {0xd7, 0x50,}, {0xd6, 0x92,}, {0xd4, 0xd4,}, {0xd5, 0x16,},
+  {0xd0, 0x58,}, {0xd1, 0x9a,}, {0xd3, 0xdc,}, {0xd2, 0x1e,},
+  {0xc5, 0x60,}, {0xc4, 0xa2,}, {0xc6, 0xe4,}, {0xc7, 0x26,},
+  {0xc2, 0x68,}, {0xc3, 0xaa,}, {0xc1, 0xec,}, {0xc0, 0x2e,},
+  {0xcb, 0x70,}, {0xca, 0xb2,}, {0xc8, 0xf4,}, {0xc9, 0x36,},
+  {0xcc, 0x78,}, {0xcd, 0xba,}, {0xcf, 0xfc,}, {0xce, 0x3e,},
+  {0x91, 0x80,}, {0x90, 0x42,}, {0x92, 0x04,}, {0x93, 0xc6,},
+  {0x96, 0x88,}, {0x97, 0x4a,}, {0x95, 0x0c,}, {0x94, 0xce,},
+  {0x9f, 0x90,}, {0x9e, 0x52,}, {0x9c, 0x14,}, {0x9d, 0xd6,},
+  {0x98, 0x98,}, {0x99, 0x5a,}, {0x9b, 0x1c,}, {0x9a, 0xde,},
+  {0x8d, 0xa0,}, {0x8c, 0x62,}, {0x8e, 0x24,}, {0x8f, 0xe6,},
+  {0x8a, 0xa8,}, {0x8b, 0x6a,}, {0x89, 0x2c,}, {0x88, 0xee,},
+  {0x83, 0xb0,}, {0x82, 0x72,}, {0x80, 0x34,}, {0x81, 0xf6,},
+  {0x84, 0xb8,}, {0x85, 0x7a,}, {0x87, 0x3c,}, {0x86, 0xfe,},
+  {0xa9, 0xc0,}, {0xa8, 0x02,}, {0xaa, 0x44,}, {0xab, 0x86,},
+  {0xae, 0xc8,}, {0xaf, 0x0a,}, {0xad, 0x4c,}, {0xac, 0x8e,},
+  {0xa7, 0xd0,}, {0xa6, 0x12,}, {0xa4, 0x54,}, {0xa5, 0x96,},
+  {0xa0, 0xd8,}, {0xa1, 0x1a,}, {0xa3, 0x5c,}, {0xa2, 0x9e,},
+  {0xb5, 0xe0,}, {0xb4, 0x22,}, {0xb6, 0x64,}, {0xb7, 0xa6,},
+  {0xb2, 0xe8,}, {0xb3, 0x2a,}, {0xb1, 0x6c,}, {0xb0, 0xae,},
+  {0xbb, 0xf0,}, {0xba, 0x32,}, {0xb8, 0x74,}, {0xb9, 0xb6,},
+  {0xbc, 0xf8,}, {0xbd, 0x3a,}, {0xbf, 0x7c,}, {0xbe, 0xbe,},
+};
+
+static unsigned
+bshift (unsigned char *b)
+{
+  unsigned char c;
+  int i;
+  c = b[15] & 1;
+  for (i = 15; i > 0; i--)
+    {
+      b[i] = (b[i] >> 1) | (b[i - 1] << 7);
+    }
+  b[i] >>= 1;
+  return c;
+}
+
+static void
+fillM (unsigned char *h, unsigned char *M)
+{
+  int i, j;
+  memset (&M[0 * 16], 0, 16);
+  memcpy (&M[8 * 16], h, 16);
+  for (i = 4; i > 0; i /= 2)
+    {
+      memcpy (&M[i * 16], &M[2 * i * 16], 16);
+      if (bshift (&M[i * 16]))
+        M[i * 16 + 0] ^= 0xe1;
+    }
+  for (i = 2; i < 16; i *= 2)
+    for (j = 1; j < i; j++)
+      buf_xor (&M[(i + j) * 16], &M[i * 16], &M[j * 16], 16);
+}
+
+static void
+ghash (unsigned char *result, const unsigned char *buf,
+       const unsigned char *gcmM)
+{
+  unsigned char V[16];
+  int i;
+
+  buf_xor (V, result, buf, 16);
+
+  memset (result, 0, 16);
+
+  for (i = 15; i >= 0; i--)
+    {
+      byte A = result[15];
+      byte T[16];
+      int j;
+      const byte *M = &gcmM[(V[i] & 0xf) * 16];
+
+      memmove (result + 1, result, 15);
+      result[0] = gcmR[A][0];
+      result[1] ^= gcmR[A][1];
+
+      T[0] = M[0] >> 4;
+      for (j = 1; j < 16; j++)
+        T[j] = (M[j] >> 4) | (M[j - 1] << 4);
+      T[0] ^= gcmR[(M[15] & 0xf) << 4][0];
+      T[1] ^= gcmR[(M[15] & 0xf) << 4][1];
+      buf_xor (T, T, &gcmM[(V[i] >> 4) * 16], 16);
+      buf_xor (result, result, T, 16);
+    }
+}
+
+#define GHASH(c, result, buf) ghash (result, buf, c->gcm_table);
+
+#else
+
+static unsigned long
+bshift (unsigned long *b)
+{
+  unsigned long c;
+  int i;
+  c = b[3] & 1;
+  for (i = 3; i > 0; i--)
+    {
+      b[i] = (b[i] >> 1) | (b[i - 1] << 31);
+    }
+  b[i] >>= 1;
+  return c;
+}
+
+static void
+ghash (unsigned char *hsub, unsigned char *result, const unsigned char *buf)
+{
+  unsigned long V[4];
+  int i, j;
+  byte *p;
+
+#ifdef WORDS_BIGENDIAN
+  p = result;
+#else
+  unsigned long T[4];
+
+  buf_xor (V, result, buf, 16);
+  for (i = 0; i < 4; i++)
+    {
+      V[i] = (V[i] & 0x00ff00ff) << 8 | (V[i] & 0xff00ff00) >> 8;
+      V[i] = (V[i] & 0x0000ffff) << 16 | (V[i] & 0xffff0000) >> 16;
+    }
+  p = (byte *) T;
+#endif
+
+  memset (p, 0, 16);
+
+  for (i = 0; i < 16; i++)
+    {
+      for (j = 0x80; j; j >>= 1)
+        {
+          if (hsub[i] & j)
+            buf_xor (p, p, V, 16);
+          if (bshift (V))
+            V[0] ^= 0xe1000000;
+        }
+    }
+#ifndef WORDS_BIGENDIAN
+  for (i = 0, p = (byte *) T; i < 16; i += 4, p += 4)
+    {
+      result[i + 0] = p[3];
+      result[i + 1] = p[2];
+      result[i + 2] = p[1];
+      result[i + 3] = p[0];
+    }
+#endif
+}
+
+#define fillM(h, M) do { } while (0)
+
+#define GHASH(c, result, buf) ghash (c->u_iv.iv, result, buf);
+#endif
+
+
+gcry_err_code_t
+_gcry_cipher_gcm_encrypt (gcry_cipher_hd_t c,
+                          byte * outbuf, unsigned int outbuflen,
+                          const byte * inbuf, unsigned int inbuflen)
+{
+  unsigned int n;
+  int i;
+  unsigned int blocksize = c->spec->blocksize;
+  unsigned char tmp[MAX_BLOCKSIZE];
+
+  if (blocksize >= 0x20)
+    return GPG_ERR_CIPHER_ALGO;
+  if (blocksize != 0x10)
+    return GPG_ERR_CIPHER_ALGO;
+  if (outbuflen < inbuflen)
+    return GPG_ERR_BUFFER_TOO_SHORT;
+
+  if (!c->marks.iv)
+    {
+      memset (tmp, 0, 16);
+      _gcry_cipher_gcm_setiv (c, tmp, 16);
+    }
+
+  while (inbuflen)
+    {
+      for (i = blocksize; i > blocksize - 4; i--)
+        {
+          c->u_ctr.ctr[i - 1]++;
+          if (c->u_ctr.ctr[i - 1] != 0)
+            break;
+        }
+
+      n = blocksize < inbuflen ? blocksize : inbuflen;
+
+      i = blocksize - 1;
+      c->length[i] += n * 8;
+      for (; c->length[i] == 0 && i > blocksize / 2; i--)
+        c->length[i - 1]++;
+
+      c->spec->encrypt (&c->context.c, tmp, c->u_ctr.ctr);
+      if (n < blocksize)
+        {
+          buf_xor_2dst (outbuf, tmp, inbuf, n);
+          memset (tmp + n, 0, blocksize - n);
+          GHASH (c, c->u_tag.tag, tmp);
+        }
+      else
+        {
+          buf_xor (outbuf, tmp, inbuf, n);
+          GHASH (c, c->u_tag.tag, outbuf);
+        }
+
+      inbuflen -= n;
+      outbuf += n;
+      inbuf += n;
+    }
+
+  return 0;
+}
+
+gcry_err_code_t
+_gcry_cipher_gcm_decrypt (gcry_cipher_hd_t c,
+                          byte * outbuf, unsigned int outbuflen,
+                          const byte * inbuf, unsigned int inbuflen)
+{
+  unsigned int n;
+  int i;
+  unsigned int blocksize = c->spec->blocksize;
+  unsigned char tmp[MAX_BLOCKSIZE];
+
+  if (blocksize >= 0x20)
+    return GPG_ERR_CIPHER_ALGO;
+  if (blocksize != 0x10)
+    return GPG_ERR_CIPHER_ALGO;
+  if (outbuflen < inbuflen)
+    return GPG_ERR_BUFFER_TOO_SHORT;
+
+  if (!c->marks.iv)
+    {
+      memset (tmp, 0, 16);
+      _gcry_cipher_gcm_setiv (c, tmp, 16);
+    }
+
+  while (inbuflen)
+    {
+      for (i = blocksize; i > blocksize - 4; i--)
+        {
+          c->u_ctr.ctr[i - 1]++;
+          if (c->u_ctr.ctr[i - 1] != 0)
+            break;
+        }
+
+      n = blocksize < inbuflen ? blocksize : inbuflen;
+      if (n < blocksize)
+        {
+          memcpy (tmp, inbuf, n);
+          memset (tmp + n, 0, blocksize - n);
+          GHASH (c, c->u_tag.tag, tmp);
+        }
+      else
+        {
+          GHASH (c, c->u_tag.tag, inbuf);
+        }
+
+      i = blocksize - 1;
+      c->length[i] += n * 8;
+      for (; c->length[i] == 0 && i > blocksize / 2; i--)
+        c->length[i - 1]++;
+
+      c->spec->encrypt (&c->context.c, tmp, c->u_ctr.ctr);
+
+      buf_xor (outbuf, inbuf, tmp, n);
+
+      inbuflen -= n;
+      outbuf += n;
+      inbuf += n;
+    }
+
+  return 0;
+}
+
+gcry_err_code_t
+_gcry_cipher_gcm_authenticate (gcry_cipher_hd_t c,
+                               const byte * aadbuf, unsigned int aadbuflen)
+{
+  unsigned int n;
+  int i;
+  unsigned int blocksize = c->spec->blocksize;
+  unsigned char tmp[MAX_BLOCKSIZE];
+
+  if (!c->marks.iv)
+    {
+      memset (tmp, 0, 16);
+      _gcry_cipher_gcm_setiv (c, tmp, 16);
+    }
+
+  n = aadbuflen;
+  i = blocksize / 2;
+  c->length[i - 1] = (n % 0x20) * 8;
+  n /= 0x20;
+  for (; n && i > 0; i--, n >>= 8)
+    c->length[i - 1] = n & 0xff;
+
+  while (aadbuflen >= blocksize)
+    {
+      GHASH (c, c->u_tag.tag, aadbuf);
+
+      aadbuflen -= blocksize;
+      aadbuf += blocksize;
+    }
+
+  if (aadbuflen != 0)
+    {
+      memcpy (tmp, aadbuf, aadbuflen);
+      memset (tmp + aadbuflen, 0, blocksize - aadbuflen);
+
+      GHASH (c, c->u_tag.tag, tmp);
+    }
+
+  return 0;
+}
+
+void
+_gcry_cipher_gcm_setiv (gcry_cipher_hd_t c,
+                        const byte * iv, unsigned int ivlen)
+{
+  memset (c->length, 0, 16);
+  memset (c->u_tag.tag, 0, 16);
+  c->spec->encrypt (&c->context.c, c->u_iv.iv, c->u_tag.tag);
+
+  fillM (c->u_iv.iv, c->gcm_table);
+
+  if (ivlen != 16 - 4)
+    {
+      unsigned char tmp[MAX_BLOCKSIZE];
+      unsigned n;
+      memset (c->u_ctr.ctr, 0, 16);
+      for (n = ivlen; n >= 16; n -= 16, iv += 16)
+        GHASH (c, c->u_ctr.ctr, iv);
+      if (n != 0)
+        {
+          memcpy (tmp, iv, n);
+          memset (tmp + n, 0, 16 - n);
+          GHASH (c, c->u_ctr.ctr, tmp);
+        }
+      memset (tmp, 0, 16);
+      n = 16;
+      tmp[n - 1] = (ivlen % 0x20) * 8;
+      ivlen /= 0x20;
+      n--;
+      for (; n > 0; n--, ivlen >>= 8)
+        tmp[n - 1] = ivlen & 0xff;
+      GHASH (c, c->u_ctr.ctr, tmp);
+    }
+  else
+    {
+      memcpy (c->u_ctr.ctr, iv, ivlen);
+      c->u_ctr.ctr[12] = c->u_ctr.ctr[13] = c->u_ctr.ctr[14] = 0;
+      c->u_ctr.ctr[15] = 1;
+    }
+
+  c->spec->encrypt (&c->context.c, c->lastiv, c->u_ctr.ctr);
+  c->marks.iv = 1;
+
+}
+
+static gcry_err_code_t
+_gcry_cipher_gcm_tag (gcry_cipher_hd_t c,
+                      byte * outbuf, unsigned int outbuflen, int check)
+{
+  if (outbuflen < 16)
+    return GPG_ERR_BUFFER_TOO_SHORT;
+
+  if (!c->marks.tag)
+    {
+      GHASH (c, c->u_tag.tag, c->length);
+      buf_xor (c->u_tag.tag, c->lastiv, c->u_tag.tag, 16);
+      c->marks.tag = 1;
+    }
+  memcpy (outbuf, c->u_tag.tag, 16);
+  if (!check)
+    {
+      memcpy (outbuf, c->u_tag.tag, outbuflen);
+      return GPG_ERR_NO_ERROR;
+    }
+  else
+    {
+      int diff, i;
+
+      /* Constant-time compare. */
+      for (i = 0, diff = 0; i < outbuflen; i++)
+        diff -= ! !(outbuf[i] - c->u_tag.tag[i]);
+
+      return !diff ? GPG_ERR_NO_ERROR : GPG_ERR_CHECKSUM;
+    }
+
+  return 0;
+}
+
+gcry_err_code_t
+_gcry_cipher_gcm_get_tag (gcry_cipher_hd_t c, unsigned char *outtag,
+                          size_t taglen)
+{
+  return _gcry_cipher_gcm_tag (c, outtag, taglen, 0);
+}
+
+
+gcry_err_code_t
+_gcry_cipher_gcm_check_tag (gcry_cipher_hd_t c, const unsigned char *intag,
+                            size_t taglen)
+{
+  return _gcry_cipher_gcm_tag (c, (unsigned char *) intag, taglen, 1);
+}
diff --git a/cipher/cipher-internal.h b/cipher/cipher-internal.h
index 44f43a4..7da9c68 100644
--- a/cipher/cipher-internal.h
+++ b/cipher/cipher-internal.h
@@ -35,6 +35,9 @@
 # define NEED_16BYTE_ALIGNED_CONTEXT 1
 #endif
 
+/* Undef this symbol to trade GCM speed for 256 bytes of memory per context */
+#define GCM_USE_TABLES 1
+
 
 /* A VIA processor with the Padlock engine as well as the Intel AES_NI
    instructions require an alignment of most data on a 16 byte
@@ -96,6 +99,7 @@ struct gcry_cipher_handle
   struct {
     unsigned int key:1; /* Set to 1 if a key has been set.  */
     unsigned int iv:1;  /* Set to 1 if a IV has been set.  */
+    unsigned int tag:1; /* Set to 1 if a tag is finalized. */
   } marks;
 
   /* The initialization vector.  For best performance we make sure
@@ -114,9 +118,19 @@ struct gcry_cipher_handle
     unsigned char ctr[MAX_BLOCKSIZE];
   } u_ctr;
 
+  /* The interim tag for GCM mode.  */
+  union {
+    cipher_context_alignment_t iv_align;
+    unsigned char tag[MAX_BLOCKSIZE];
+  } u_tag;
+
   /* Space to save an IV or CTR for chaining operations.  */
   unsigned char lastiv[MAX_BLOCKSIZE];
   int unused;  /* Number of unused bytes in LASTIV. */
+  unsigned char length[MAX_BLOCKSIZE]; /* bit counters for GCM */
+#ifdef GCM_USE_TABLES
+  unsigned char gcm_table[16 * 16]; /* pre-calculated table for GCM */
+#endif
 
   union {
     /* Mode specific storage for CCM mode. */
@@ -134,7 +148,6 @@ struct gcry_cipher_handle
       unsigned int nonce:1;/* Set to 1 if nonce has been set.  */
       unsigned int lengths:1; /* Set to 1 if CCM length parameters has been
                                  processed.  */
-      unsigned int tag:1; /* Set to 1 if tag has been finalized.  */
     } ccm;
     /* Mode specific storage for CMAC mode. */
     struct {
@@ -224,5 +237,26 @@ gcry_err_code_t _gcry_cipher_ccm_check_tag
 /*           */ (gcry_cipher_hd_t c,
                  const unsigned char *intag, size_t taglen);
 
+/*-- cipher-gcm.c --*/
+gcry_err_code_t _gcry_cipher_gcm_encrypt
+/*           */   (gcry_cipher_hd_t c,
+                   unsigned char *outbuf, unsigned int outbuflen,
+                   const unsigned char *inbuf, unsigned int inbuflen);
+gcry_err_code_t _gcry_cipher_gcm_decrypt
+/*           */   (gcry_cipher_hd_t c,
+                   unsigned char *outbuf, unsigned int outbuflen,
+                   const unsigned char *inbuf, unsigned int inbuflen);
+void _gcry_cipher_gcm_setiv
+/*           */   (gcry_cipher_hd_t c,
+                   const unsigned char *iv, unsigned int ivlen);
+gcry_err_code_t _gcry_cipher_gcm_authenticate
+/*           */   (gcry_cipher_hd_t c,
+                   const unsigned char *aadbuf, unsigned int aadbuflen);
+gcry_err_code_t _gcry_cipher_gcm_get_tag
+/*           */   (gcry_cipher_hd_t c,
+                   unsigned char *outtag, size_t taglen);
+gcry_err_code_t _gcry_cipher_gcm_check_tag
+/*           */   (gcry_cipher_hd_t c,
+                   const unsigned char *intag, size_t taglen);
 
 #endif /*G10_CIPHER_INTERNAL_H*/
diff --git a/cipher/cipher.c b/cipher/cipher.c
index b68703e..bba6abb 100644
--- a/cipher/cipher.c
+++ b/cipher/cipher.c
@@ -407,6 +407,7 @@ _gcry_cipher_open_internal (gcry_cipher_hd_t *handle,
       case GCRY_CIPHER_MODE_CTR:
       case GCRY_CIPHER_MODE_AESWRAP:
       case GCRY_CIPHER_MODE_CMAC:
+      case GCRY_CIPHER_MODE_GCM:
 	if (!spec->encrypt || !spec->decrypt)
 	  err = GPG_ERR_INV_CIPHER_MODE;
 	break;
@@ -609,6 +610,13 @@ cipher_setkey (gcry_cipher_hd_t c, byte *key, size_t keylen)
 static void
 cipher_setiv (gcry_cipher_hd_t c, const byte *iv, size_t ivlen)
 {
+  /* GCM has its own IV handler */
+  if (c->mode == GCRY_CIPHER_MODE_GCM)
+    {
+      _gcry_cipher_gcm_setiv (c, iv, ivlen);
+      return;
+    }
+
   /* If the cipher has its own IV handler, we use only this one.  This
      is currently used for stream ciphers requiring a nonce.  */
   if (c->spec->setiv)
@@ -767,6 +775,10 @@ cipher_encrypt (gcry_cipher_hd_t c, byte *outbuf, size_t outbuflen,
       rc = GPG_ERR_INV_CIPHER_MODE;
       break;
 
+    case GCRY_CIPHER_MODE_GCM:
+      rc = _gcry_cipher_gcm_encrypt (c, outbuf, outbuflen, inbuf, inbuflen);
+      break;
+
     case GCRY_CIPHER_MODE_STREAM:
       c->spec->stencrypt (&c->context.c,
                           outbuf, (byte*)/*arggg*/inbuf, inbuflen);
@@ -871,6 +883,10 @@ cipher_decrypt (gcry_cipher_hd_t c, byte *outbuf, size_t outbuflen,
       rc = GPG_ERR_INV_CIPHER_MODE;
       break;
 
+    case GCRY_CIPHER_MODE_GCM:
+      rc = _gcry_cipher_gcm_decrypt (c, outbuf, outbuflen, inbuf, inbuflen);
+      break;
+
     case GCRY_CIPHER_MODE_STREAM:
       c->spec->stdecrypt (&c->context.c,
                           outbuf, (byte*)/*arggg*/inbuf, inbuflen);
@@ -1000,6 +1016,10 @@ _gcry_cipher_authenticate (gcry_cipher_hd_t hd, const void *abuf,
       rc = _gcry_cipher_cmac_authenticate (hd, abuf, abuflen);
       break;
 
+    case GCRY_CIPHER_MODE_GCM:
+      rc = _gcry_cipher_gcm_authenticate (hd, abuf, abuflen);
+      break;
+
     default:
       log_error ("gcry_cipher_authenticate: invalid mode %d\n", hd->mode);
       rc = GPG_ERR_INV_CIPHER_MODE;
@@ -1024,6 +1044,10 @@ _gcry_cipher_gettag (gcry_cipher_hd_t hd, void *outtag, size_t taglen)
       rc = _gcry_cipher_cmac_get_tag (hd, outtag, taglen);
       break;
 
+    case GCRY_CIPHER_MODE_GCM:
+      rc = _gcry_cipher_gcm_get_tag (hd, outtag, taglen);
+      break;
+
     default:
       log_error ("gcry_cipher_gettag: invalid mode %d\n", hd->mode);
       rc = GPG_ERR_INV_CIPHER_MODE;
@@ -1048,6 +1072,10 @@ _gcry_cipher_checktag (gcry_cipher_hd_t hd, const void *intag, size_t taglen)
       rc = _gcry_cipher_cmac_check_tag (hd, intag, taglen);
       break;
 
+    case GCRY_CIPHER_MODE_GCM:
+      rc = _gcry_cipher_gcm_check_tag (hd, intag, taglen);
+      break;
+
     default:
       log_error ("gcry_cipher_checktag: invalid mode %d\n", hd->mode);
       rc = GPG_ERR_INV_CIPHER_MODE;
diff --git a/src/gcrypt.h.in b/src/gcrypt.h.in
index 3e8e72c..24f7af6 100644
--- a/src/gcrypt.h.in
+++ b/src/gcrypt.h.in
@@ -895,7 +895,8 @@ enum gcry_cipher_modes
     GCRY_CIPHER_MODE_OFB    = 5,  /* Outer feedback. */
     GCRY_CIPHER_MODE_CTR    = 6,  /* Counter. */
     GCRY_CIPHER_MODE_AESWRAP= 7,  /* AES-WRAP algorithm.  */
-    GCRY_CIPHER_MODE_CCM    = 8   /* Counter with CBC-MAC.  */
+    GCRY_CIPHER_MODE_CCM    = 8,  /* Counter with CBC-MAC.  */
+    GCRY_CIPHER_MODE_GCM    = 9   /* Galois Counter Mode. */
   };
 
 /* Flags used with the open function. */
@@ -907,6 +908,9 @@ enum gcry_cipher_flags
     GCRY_CIPHER_CBC_MAC     = 8   /* Enable CBC message auth. code (MAC). */
   };
 
+/* GCM works only with blocks of 128 bits */
+#define GCRY_GCM_BLOCK_LEN  (128 / 8)
+
 /* CCM works only with blocks of 128 bits.  */
 #define GCRY_CCM_BLOCK_LEN  (128 / 8)
 
diff --git a/tests/basic.c b/tests/basic.c
index f9c6497..b4541a0 100644
--- a/tests/basic.c
+++ b/tests/basic.c
@@ -1137,6 +1137,340 @@ check_ofb_cipher (void)
     fprintf (stderr, "  Completed OFB checks.\n");
 }
 
+static void
+check_gcm_cipher (void)
+{
+  struct tv
+  {
+    int algo;
+    char key[MAX_DATA_LEN];
+    char iv[MAX_DATA_LEN];
+    int ivlen;
+    unsigned char aad[MAX_DATA_LEN];
+    int aadlen;
+    unsigned char plaintext[MAX_DATA_LEN];
+    int inlen;
+    char out[MAX_DATA_LEN];
+    char tag[MAX_DATA_LEN];
+  } tv[] =
+    {
+      /* http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-revised-spec.pdf */
+      { GCRY_CIPHER_AES,
+        "\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", 12,
+        "", 0,
+        "",
+        0,
+        "",
+        "\x58\xe2\xfc\xce\xfa\x7e\x30\x61\x36\x7f\x1d\x57\xa4\xe7\x45\x5a" },
+      { GCRY_CIPHER_AES,
+        "\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", 12,
+        "", 0,
+        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+        16,
+        "\x03\x88\xda\xce\x60\xb6\xa3\x92\xf3\x28\xc2\xb9\x71\xb2\xfe\x78",
+        "\xab\x6e\x47\xd4\x2c\xec\x13\xbd\xf5\x3a\x67\xb2\x12\x57\xbd\xdf" },
+      { GCRY_CIPHER_AES,
+        "\xfe\xff\xe9\x92\x86\x65\x73\x1c\x6d\x6a\x8f\x94\x67\x30\x83\x08",
+        "\xca\xfe\xba\xbe\xfa\xce\xdb\xad\xde\xca\xf8\x88", 12,
+        "", 0,
+        "\xd9\x31\x32\x25\xf8\x84\x06\xe5\xa5\x59\x09\xc5\xaf\xf5\x26\x9a"
+        "\x86\xa7\xa9\x53\x15\x34\xf7\xda\x2e\x4c\x30\x3d\x8a\x31\x8a\x72"
+        "\x1c\x3c\x0c\x95\x95\x68\x09\x53\x2f\xcf\x0e\x24\x49\xa6\xb5\x25"
+        "\xb1\x6a\xed\xf5\xaa\x0d\xe6\x57\xba\x63\x7b\x39\x1a\xaf\xd2\x55",
+        64,
+        "\x42\x83\x1e\xc2\x21\x77\x74\x24\x4b\x72\x21\xb7\x84\xd0\xd4\x9c"
+        "\xe3\xaa\x21\x2f\x2c\x02\xa4\xe0\x35\xc1\x7e\x23\x29\xac\xa1\x2e"
+        "\x21\xd5\x14\xb2\x54\x66\x93\x1c\x7d\x8f\x6a\x5a\xac\x84\xaa\x05"
+        "\x1b\xa3\x0b\x39\x6a\x0a\xac\x97\x3d\x58\xe0\x91\x47\x3f\x59\x85",
+        "\x4d\x5c\x2a\xf3\x27\xcd\x64\xa6\x2c\xf3\x5a\xbd\x2b\xa6\xfa\xb4" },
+      { GCRY_CIPHER_AES,
+        "\xfe\xff\xe9\x92\x86\x65\x73\x1c\x6d\x6a\x8f\x94\x67\x30\x83\x08",
+        "\xca\xfe\xba\xbe\xfa\xce\xdb\xad\xde\xca\xf8\x88", 12,
+        "\xfe\xed\xfa\xce\xde\xad\xbe\xef\xfe\xed\xfa\xce\xde\xad\xbe\xef"
+        "\xab\xad\xda\xd2", 20,
+        "\xd9\x31\x32\x25\xf8\x84\x06\xe5\xa5\x59\x09\xc5\xaf\xf5\x26\x9a"
+        "\x86\xa7\xa9\x53\x15\x34\xf7\xda\x2e\x4c\x30\x3d\x8a\x31\x8a\x72"
+        "\x1c\x3c\x0c\x95\x95\x68\x09\x53\x2f\xcf\x0e\x24\x49\xa6\xb5\x25"
+        "\xb1\x6a\xed\xf5\xaa\x0d\xe6\x57\xba\x63\x7b\x39",
+        60,
+        "\x42\x83\x1e\xc2\x21\x77\x74\x24\x4b\x72\x21\xb7\x84\xd0\xd4\x9c"
+        "\xe3\xaa\x21\x2f\x2c\x02\xa4\xe0\x35\xc1\x7e\x23\x29\xac\xa1\x2e"
+        "\x21\xd5\x14\xb2\x54\x66\x93\x1c\x7d\x8f\x6a\x5a\xac\x84\xaa\x05"
+        "\x1b\xa3\x0b\x39\x6a\x0a\xac\x97\x3d\x58\xe0\x91\x47\x3f\x59\x85",
+        "\x5b\xc9\x4f\xbc\x32\x21\xa5\xdb\x94\xfa\xe9\x5a\xe7\x12\x1a\x47" },
+      { GCRY_CIPHER_AES,
+        "\xfe\xff\xe9\x92\x86\x65\x73\x1c\x6d\x6a\x8f\x94\x67\x30\x83\x08",
+        "\xca\xfe\xba\xbe\xfa\xce\xdb\xad", 8,
+        "\xfe\xed\xfa\xce\xde\xad\xbe\xef\xfe\xed\xfa\xce\xde\xad\xbe\xef"
+        "\xab\xad\xda\xd2", 20,
+        "\xd9\x31\x32\x25\xf8\x84\x06\xe5\xa5\x59\x09\xc5\xaf\xf5\x26\x9a"
+        "\x86\xa7\xa9\x53\x15\x34\xf7\xda\x2e\x4c\x30\x3d\x8a\x31\x8a\x72"
+        "\x1c\x3c\x0c\x95\x95\x68\x09\x53\x2f\xcf\x0e\x24\x49\xa6\xb5\x25"
+        "\xb1\x6a\xed\xf5\xaa\x0d\xe6\x57\xba\x63\x7b\x39",
+        60,
+        "\x61\x35\x3b\x4c\x28\x06\x93\x4a\x77\x7f\xf5\x1f\xa2\x2a\x47\x55"
+        "\x69\x9b\x2a\x71\x4f\xcd\xc6\xf8\x37\x66\xe5\xf9\x7b\x6c\x74\x23"
+        "\x73\x80\x69\x00\xe4\x9f\x24\xb2\x2b\x09\x75\x44\xd4\x89\x6b\x42"
+        "\x49\x89\xb5\xe1\xeb\xac\x0f\x07\xc2\x3f\x45\x98",
+        "\x36\x12\xd2\xe7\x9e\x3b\x07\x85\x56\x1b\xe1\x4a\xac\xa2\xfc\xcb" },
+      { GCRY_CIPHER_AES,
+        "\xfe\xff\xe9\x92\x86\x65\x73\x1c\x6d\x6a\x8f\x94\x67\x30\x83\x08",
+        "\x93\x13\x22\x5d\xf8\x84\x06\xe5\x55\x90\x9c\x5a\xff\x52\x69\xaa"
+        "\x6a\x7a\x95\x38\x53\x4f\x7d\xa1\xe4\xc3\x03\xd2\xa3\x18\xa7\x28"
+        "\xc3\xc0\xc9\x51\x56\x80\x95\x39\xfc\xf0\xe2\x42\x9a\x6b\x52\x54"
+        "\x16\xae\xdb\xf5\xa0\xde\x6a\x57\xa6\x37\xb3\x9b", 60,
+        "\xfe\xed\xfa\xce\xde\xad\xbe\xef\xfe\xed\xfa\xce\xde\xad\xbe\xef"
+        "\xab\xad\xda\xd2", 20,
+        "\xd9\x31\x32\x25\xf8\x84\x06\xe5\xa5\x59\x09\xc5\xaf\xf5\x26\x9a"
+        "\x86\xa7\xa9\x53\x15\x34\xf7\xda\x2e\x4c\x30\x3d\x8a\x31\x8a\x72"
+        "\x1c\x3c\x0c\x95\x95\x68\x09\x53\x2f\xcf\x0e\x24\x49\xa6\xb5\x25"
+        "\xb1\x6a\xed\xf5\xaa\x0d\xe6\x57\xba\x63\x7b\x39",
+        60,
+        "\x8c\xe2\x49\x98\x62\x56\x15\xb6\x03\xa0\x33\xac\xa1\x3f\xb8\x94"
+        "\xbe\x91\x12\xa5\xc3\xa2\x11\xa8\xba\x26\x2a\x3c\xca\x7e\x2c\xa7"
+        "\x01\xe4\xa9\xa4\xfb\xa4\x3c\x90\xcc\xdc\xb2\x81\xd4\x8c\x7c\x6f"
+        "\xd6\x28\x75\xd2\xac\xa4\x17\x03\x4c\x34\xae\xe5",
+        "\x61\x9c\xc5\xae\xff\xfe\x0b\xfa\x46\x2a\xf4\x3c\x16\x99\xd0\x50" },
+      { GCRY_CIPHER_AES192,
+        "\xfe\xff\xe9\x92\x86\x65\x73\x1c\x6d\x6a\x8f\x94\x67\x30\x83\x08"
+        "\xfe\xff\xe9\x92\x86\x65\x73\x1c",
+        "\x93\x13\x22\x5d\xf8\x84\x06\xe5\x55\x90\x9c\x5a\xff\x52\x69\xaa"
+        "\x6a\x7a\x95\x38\x53\x4f\x7d\xa1\xe4\xc3\x03\xd2\xa3\x18\xa7\x28"
+        "\xc3\xc0\xc9\x51\x56\x80\x95\x39\xfc\xf0\xe2\x42\x9a\x6b\x52\x54"
+        "\x16\xae\xdb\xf5\xa0\xde\x6a\x57\xa6\x37\xb3\x9b", 60,
+        "\xfe\xed\xfa\xce\xde\xad\xbe\xef\xfe\xed\xfa\xce\xde\xad\xbe\xef"
+        "\xab\xad\xda\xd2", 20,
+        "\xd9\x31\x32\x25\xf8\x84\x06\xe5\xa5\x59\x09\xc5\xaf\xf5\x26\x9a"
+        "\x86\xa7\xa9\x53\x15\x34\xf7\xda\x2e\x4c\x30\x3d\x8a\x31\x8a\x72"
+        "\x1c\x3c\x0c\x95\x95\x68\x09\x53\x2f\xcf\x0e\x24\x49\xa6\xb5\x25"
+        "\xb1\x6a\xed\xf5\xaa\x0d\xe6\x57\xba\x63\x7b\x39",
+        60,
+        "\xd2\x7e\x88\x68\x1c\xe3\x24\x3c\x48\x30\x16\x5a\x8f\xdc\xf9\xff"
+        "\x1d\xe9\xa1\xd8\xe6\xb4\x47\xef\x6e\xf7\xb7\x98\x28\x66\x6e\x45"
+        "\x81\xe7\x90\x12\xaf\x34\xdd\xd9\xe2\xf0\x37\x58\x9b\x29\x2d\xb3"
+        "\xe6\x7c\x03\x67\x45\xfa\x22\xe7\xe9\xb7\x37\x3b",
+        "\xdc\xf5\x66\xff\x29\x1c\x25\xbb\xb8\x56\x8f\xc3\xd3\x76\xa6\xd9" },
+      { GCRY_CIPHER_AES256,
+        "\xfe\xff\xe9\x92\x86\x65\x73\x1c\x6d\x6a\x8f\x94\x67\x30\x83\x08"
+        "\xfe\xff\xe9\x92\x86\x65\x73\x1c\x6d\x6a\x8f\x94\x67\x30\x83\x08",
+        "\x93\x13\x22\x5d\xf8\x84\x06\xe5\x55\x90\x9c\x5a\xff\x52\x69\xaa"
+        "\x6a\x7a\x95\x38\x53\x4f\x7d\xa1\xe4\xc3\x03\xd2\xa3\x18\xa7\x28"
+        "\xc3\xc0\xc9\x51\x56\x80\x95\x39\xfc\xf0\xe2\x42\x9a\x6b\x52\x54"
+        "\x16\xae\xdb\xf5\xa0\xde\x6a\x57\xa6\x37\xb3\x9b", 60,
+        "\xfe\xed\xfa\xce\xde\xad\xbe\xef\xfe\xed\xfa\xce\xde\xad\xbe\xef"
+        "\xab\xad\xda\xd2", 20,
+        "\xd9\x31\x32\x25\xf8\x84\x06\xe5\xa5\x59\x09\xc5\xaf\xf5\x26\x9a"
+        "\x86\xa7\xa9\x53\x15\x34\xf7\xda\x2e\x4c\x30\x3d\x8a\x31\x8a\x72"
+        "\x1c\x3c\x0c\x95\x95\x68\x09\x53\x2f\xcf\x0e\x24\x49\xa6\xb5\x25"
+        "\xb1\x6a\xed\xf5\xaa\x0d\xe6\x57\xba\x63\x7b\x39",
+        60,
+        "\x5a\x8d\xef\x2f\x0c\x9e\x53\xf1\xf7\x5d\x78\x53\x65\x9e\x2a\x20"
+        "\xee\xb2\xb2\x2a\xaf\xde\x64\x19\xa0\x58\xab\x4f\x6f\x74\x6b\xf4"
+        "\x0f\xc0\xc3\xb7\x80\xf2\x44\x45\x2d\xa3\xeb\xf1\xc5\xd8\x2c\xde"
+        "\xa2\x41\x89\x97\x20\x0e\xf8\x2e\x44\xae\x7e\x3f",
+        "\xa4\x4a\x82\x66\xee\x1c\x8e\xb0\xc8\xb5\xd4\xcf\x5a\xe9\xf1\x9a" }
+    };
+
+  gcry_cipher_hd_t hde, hdd;
+  unsigned char out[MAX_DATA_LEN];
+  int i, keylen;
+  gcry_error_t err = 0;
+
+  if (verbose)
+    fprintf (stderr, "  Starting GCM checks.\n");
+
+  for (i = 0; i < sizeof (tv) / sizeof (tv[0]); i++)
+    {
+      if (verbose)
+        fprintf (stderr, "    checking GCM 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, 0);
+      if (!err)
+        err = gcry_cipher_open (&hdd, tv[i].algo, GCRY_CIPHER_MODE_GCM, 0);
+      if (err)
+        {
+          fail ("aes-gcm, gcry_cipher_open failed: %s\n", gpg_strerror (err));
+          return;
+        }
+
+      keylen = gcry_cipher_get_algo_keylen(tv[i].algo);
+      if (!keylen)
+        {
+          fail ("aes-gcm, 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, 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].iv, tv[i].ivlen);
+      if (!err)
+        err = gcry_cipher_setiv (hdd, tv[i].iv, tv[i].ivlen);
+      if (err)
+        {
+          fail ("aes-gcm, gcry_cipher_setiv failed: %s\n",
+                gpg_strerror (err));
+          gcry_cipher_close (hde);
+          gcry_cipher_close (hdd);
+          return;
+        }
+
+      err = gcry_cipher_authenticate(hde, tv[i].aad, tv[i].aadlen);
+      if (err)
+        {
+          fail ("aes-gcm, gcry_cipher_authenticate (%d) failed: %s\n",
+                i, gpg_strerror (err));
+          gcry_cipher_close (hde);
+          gcry_cipher_close (hdd);
+          return;
+        }
+      if (!err)
+        err = gcry_cipher_authenticate(hdd, tv[i].aad, tv[i].aadlen);
+      if (err)
+        {
+          fail ("aes-gcm, de gcry_cipher_authenticate (%d) failed: %s\n",
+                i, gpg_strerror (err));
+          gcry_cipher_close (hde);
+          gcry_cipher_close (hdd);
+          return;
+        }
+
+      err = gcry_cipher_encrypt (hde, out, MAX_DATA_LEN,
+                                 tv[i].plaintext,
+                                 tv[i].inlen);
+      if (err)
+        {
+          fail ("aes-gcm, 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))
+        fail ("aes-gcm, encrypt mismatch entry %d\n", i);
+
+      err = gcry_cipher_decrypt (hdd, out, tv[i].inlen, NULL, 0);
+      if (err)
+        {
+          fail ("aes-gcm, 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, decrypt mismatch entry %d\n", i);
+
+#define TAGLEN 16
+      err = gcry_cipher_gettag (hde, out, TAGLEN); /* FIXME */
+      if (err)
+        {
+          fail ("aes-gcm, 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, out, TAGLEN))
+        fail ("aes-gcm, encrypt tag mismatch entry %d\n", i);
+
+
+      err = gcry_cipher_checktag (hdd, out, TAGLEN);
+      if (err)
+        {
+          fail ("aes-gcm, gcry_cipher_checktag(%d) failed: %s\n",
+                i, gpg_strerror (err));
+          gcry_cipher_close (hde);
+          gcry_cipher_close (hdd);
+          return;
+        }
+
+      err = gcry_cipher_reset(hde);
+      if (!err)
+        err = gcry_cipher_reset(hdd);
+      if (err)
+        {
+          fail ("aes-gcm, gcry_cipher_reset (%d) failed: %s\n",
+                i, gpg_strerror (err));
+          gcry_cipher_close (hde);
+          gcry_cipher_close (hdd);
+          return;
+        }
+
+
+#if 0
+      /* gcry_cipher_reset clears the IV */
+      err = gcry_cipher_setiv (hde, tv[i].iv, tv[i].ivlen);
+      if (!err)
+        err = gcry_cipher_setiv (hdd, tv[i].iv, tv[i].ivlen);
+      if (err)
+        {
+          fail ("aes-gcm, gcry_cipher_setiv failed: %s\n",
+                gpg_strerror (err));
+          gcry_cipher_close (hde);
+          gcry_cipher_close (hdd);
+          return;
+        }
+
+      /* this time we encrypt and decrypt one byte at a time */
+      int byteNum;
+      for (byteNum = 0; byteNum < tv[i].inlen; ++byteNum)
+        {
+          err = gcry_cipher_encrypt (hde, out+byteNum, 1,
+                                     (tv[i].plaintext) + byteNum,
+                                     1);
+          if (err)
+            {
+              fail ("aes-gcm, 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))
+        fail ("aes-gcm, encrypt mismatch entry %d\n", i);
+
+      for (byteNum = 0; byteNum < tv[i].inlen; ++byteNum)
+        {
+          err = gcry_cipher_decrypt (hdd, out+byteNum, 1, NULL, 0);
+          if (err)
+            {
+              fail ("aes-gcm, 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, decrypt mismatch entry %d\n", i);
+#endif
+
+      gcry_cipher_close (hde);
+      gcry_cipher_close (hdd);
+    }
+  if (verbose)
+    fprintf (stderr, "  Completed GCM checks.\n");
+}
+
 
 static void
 check_ccm_cipher (void)
@@ -3188,6 +3522,8 @@ check_ciphers (void)
       check_one_cipher (algos[i], GCRY_CIPHER_MODE_CBC, 0);
       check_one_cipher (algos[i], GCRY_CIPHER_MODE_CBC, GCRY_CIPHER_CBC_CTS);
       check_one_cipher (algos[i], GCRY_CIPHER_MODE_CTR, 0);
+      if (gcry_cipher_get_algo_blklen (algos[i]) == GCRY_GCM_BLOCK_LEN)
+        check_one_cipher (algos[i], GCRY_CIPHER_MODE_GCM, 0);
     }
 
   for (i = 0; algos2[i]; i++)
@@ -3226,6 +3562,7 @@ check_cipher_modes(void)
   check_cfb_cipher ();
   check_ofb_cipher ();
   check_ccm_cipher ();
+  check_gcm_cipher ();
   check_stream_cipher ();
   check_stream_cipher_large_block ();
 
diff --git a/tests/bench-slope.c b/tests/bench-slope.c
index f9d3c46..d29a4ad 100644
--- a/tests/bench-slope.c
+++ b/tests/bench-slope.c
@@ -787,6 +787,127 @@ static struct bench_ops ccm_authenticate_ops = {
   &bench_ccm_authenticate_do_bench
 };
 
+static void
+bench_gcm_encrypt_do_bench (struct bench_obj *obj, void *buf, size_t buflen)
+{
+  gcry_cipher_hd_t hd = obj->priv;
+  int err;
+  char tag[16];
+  char nonce[12] = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce,
+                     0xdb, 0xad, 0xde, 0xca, 0xf8, 0x88, };
+
+  gcry_cipher_setiv (hd, nonce, sizeof (nonce));
+
+  err = gcry_cipher_encrypt (hd, buf, buflen, buf, buflen);
+  if (err)
+    {
+      fprintf (stderr, PGM ": gcry_cipher_encrypt failed: %s\n",
+           gpg_strerror (err));
+      gcry_cipher_close (hd);
+      exit (1);
+    }
+
+  err = gcry_cipher_gettag (hd, tag, sizeof (tag));
+  if (err)
+    {
+      fprintf (stderr, PGM ": gcry_cipher_gettag failed: %s\n",
+           gpg_strerror (err));
+      gcry_cipher_close (hd);
+      exit (1);
+    }
+}
+
+static void
+bench_gcm_decrypt_do_bench (struct bench_obj *obj, void *buf, size_t buflen)
+{
+  gcry_cipher_hd_t hd = obj->priv;
+  int err;
+  char tag[16] = { 0, };
+  char nonce[12] = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce,
+                     0xdb, 0xad, 0xde, 0xca, 0xf8, 0x88, };
+
+  gcry_cipher_setiv (hd, nonce, sizeof (nonce));
+
+  err = gcry_cipher_decrypt (hd, buf, buflen, buf, buflen);
+  if (err)
+    {
+      fprintf (stderr, PGM ": gcry_cipher_encrypt failed: %s\n",
+           gpg_strerror (err));
+      gcry_cipher_close (hd);
+      exit (1);
+    }
+
+  err = gcry_cipher_checktag (hd, tag, sizeof (tag));
+  if (gpg_err_code (err) == GPG_ERR_CHECKSUM)
+    err = gpg_error (GPG_ERR_NO_ERROR);
+  if (err)
+    {
+      fprintf (stderr, PGM ": gcry_cipher_gettag failed: %s\n",
+           gpg_strerror (err));
+      gcry_cipher_close (hd);
+      exit (1);
+    }
+}
+
+static void
+bench_gcm_authenticate_do_bench (struct bench_obj *obj, void *buf,
+           size_t buflen)
+{
+  gcry_cipher_hd_t hd = obj->priv;
+  int err;
+  char tag[16] = { 0, };
+  char nonce[12] = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce,
+                     0xdb, 0xad, 0xde, 0xca, 0xf8, 0x88, };
+  char data = 0xff;
+
+  gcry_cipher_setiv (hd, nonce, sizeof (nonce));
+
+  err = gcry_cipher_authenticate (hd, buf, buflen);
+  if (err)
+    {
+      fprintf (stderr, PGM ": gcry_cipher_authenticate failed: %s\n",
+           gpg_strerror (err));
+      gcry_cipher_close (hd);
+      exit (1);
+    }
+
+  err = gcry_cipher_encrypt (hd, &data, sizeof (data), &data, sizeof (data));
+  if (err)
+    {
+      fprintf (stderr, PGM ": gcry_cipher_encrypt failed: %s\n",
+           gpg_strerror (err));
+      gcry_cipher_close (hd);
+      exit (1);
+    }
+
+  err = gcry_cipher_gettag (hd, tag, sizeof (tag));
+  if (err)
+    {
+      fprintf (stderr, PGM ": gcry_cipher_gettag failed: %s\n",
+           gpg_strerror (err));
+      gcry_cipher_close (hd);
+      exit (1);
+    }
+}
+
+static struct bench_ops gcm_encrypt_ops = {
+  &bench_encrypt_init,
+  &bench_encrypt_free,
+  &bench_gcm_encrypt_do_bench
+};
+
+static struct bench_ops gcm_decrypt_ops = {
+  &bench_encrypt_init,
+  &bench_encrypt_free,
+  &bench_gcm_decrypt_do_bench
+};
+
+static struct bench_ops gcm_authenticate_ops = {
+  &bench_encrypt_init,
+  &bench_encrypt_free,
+  &bench_gcm_authenticate_do_bench
+};
+
 
 static struct bench_cipher_mode cipher_modes[] = {
   {GCRY_CIPHER_MODE_ECB, "ECB enc", &encrypt_ops},
@@ -802,6 +923,9 @@ static struct bench_cipher_mode cipher_modes[] = {
   {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},
+  {GCRY_CIPHER_MODE_GCM, "GCM enc", &gcm_encrypt_ops},
+  {GCRY_CIPHER_MODE_GCM, "GCM dec", &gcm_decrypt_ops},
+  {GCRY_CIPHER_MODE_GCM, "GCM auth", &gcm_authenticate_ops},
   {0},
 };
 
@@ -834,6 +958,10 @@ cipher_bench_one (int algo, struct bench_cipher_mode *pmode)
   if (mode.mode == GCRY_CIPHER_MODE_CCM && blklen != GCRY_CCM_BLOCK_LEN)
     return;
 
+  /* CCM has restrictions for block-size */
+  if (mode.mode == GCRY_CIPHER_MODE_GCM && blklen != GCRY_GCM_BLOCK_LEN)
+    return;
+
   printf (" %14s | ", mode.name);
   fflush (stdout);
 
diff --git a/tests/benchmark.c b/tests/benchmark.c
index c4accd3..3058bd5 100644
--- a/tests/benchmark.c
+++ b/tests/benchmark.c
@@ -620,6 +620,8 @@ cipher_bench ( const char *algoname )
     { GCRY_CIPHER_MODE_CTR, "      CTR", 0 },
     { GCRY_CIPHER_MODE_CCM, "      CCM", 0,
       ccm_aead_init, GCRY_CCM_BLOCK_LEN, 8 },
+    { GCRY_CIPHER_MODE_GCM, "      GCM", 0,
+      NULL, GCRY_GCM_BLOCK_LEN, GCRY_GCM_BLOCK_LEN },
     { GCRY_CIPHER_MODE_STREAM, "", 0 },
     {0}
   };




More information about the Gcrypt-devel mailing list