[RFC PATCH v2] Initial implementation of GCM

Dmitry Eremin-Solenikov dbaryshkov at gmail.com
Fri Nov 8 11:03:38 CET 2013


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>
---
 cipher/Makefile.am       |   2 +-
 cipher/cipher-ccm.c      |  12 +-
 cipher/cipher-gcm.c      | 476 +++++++++++++++++++++++++++++++++++++++++++++++
 cipher/cipher-internal.h |  36 +++-
 cipher/cipher.c          |  32 ++++
 src/gcrypt.h.in          |   6 +-
 tests/basic.c            | 337 +++++++++++++++++++++++++++++++++
 tests/bench-slope.c      | 128 +++++++++++++
 tests/benchmark.c        |   2 +
 9 files changed, 1023 insertions(+), 8 deletions(-)
 create mode 100644 cipher/cipher-gcm.c

diff --git a/cipher/Makefile.am b/cipher/Makefile.am
index 95d484e..ed61eb2 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-ccm.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 ebcbf1e..5d555fd 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)
@@ -325,7 +327,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)
@@ -350,7 +352,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..8afd2d3
--- /dev/null
+++ b/cipher/cipher-gcm.c
@@ -0,0 +1,476 @@
+/* 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;
+
+}
+
+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 f528c84..5d50e17 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;
   } u_mode;
 
@@ -216,6 +229,27 @@ 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 *outbuf, unsigned int outbuflen);
+gcry_err_code_t _gcry_cipher_gcm_check_tag
+/*           */   (gcry_cipher_hd_t c,
+                   const unsigned char *outbuf, unsigned int outbuflen);
 
 
 #endif /*G10_CIPHER_INTERNAL_H*/
diff --git a/cipher/cipher.c b/cipher/cipher.c
index 705696c..90635b8 100644
--- a/cipher/cipher.c
+++ b/cipher/cipher.c
@@ -388,6 +388,7 @@ gcry_cipher_open (gcry_cipher_hd_t *handle,
       case GCRY_CIPHER_MODE_OFB:
       case GCRY_CIPHER_MODE_CTR:
       case GCRY_CIPHER_MODE_AESWRAP:
+      case GCRY_CIPHER_MODE_GCM:
 	if (!spec->encrypt || !spec->decrypt)
 	  err = GPG_ERR_INV_CIPHER_MODE;
 	break;
@@ -580,6 +581,13 @@ cipher_setkey (gcry_cipher_hd_t c, byte *key, unsigned int keylen)
 static void
 cipher_setiv (gcry_cipher_hd_t c, const byte *iv, unsigned 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)
@@ -622,6 +630,8 @@ cipher_reset (gcry_cipher_hd_t c)
   memset (c->u_ctr.ctr, 0, c->spec->blocksize);
   memset (&c->u_mode, 0, sizeof c->u_mode);
   c->unused = 0;
+/*  memset (c->u_tag.tag, 0, c->spec->blocksize);
+  memset (c->length, 0, c->spec->blocksize); */
 }
 
 
@@ -717,6 +727,11 @@ cipher_encrypt (gcry_cipher_hd_t c, byte *outbuf, unsigned int outbuflen,
       rc = _gcry_cipher_ccm_encrypt (c, outbuf, outbuflen, inbuf, inbuflen);
       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);
@@ -817,6 +832,11 @@ cipher_decrypt (gcry_cipher_hd_t c, byte *outbuf, unsigned int outbuflen,
       rc = _gcry_cipher_ccm_decrypt (c, outbuf, outbuflen, inbuf, inbuflen);
       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);
@@ -942,6 +962,10 @@ _gcry_cipher_authenticate (gcry_cipher_hd_t hd, const void *abuf,
       rc = _gcry_cipher_ccm_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;
@@ -962,6 +986,10 @@ _gcry_cipher_gettag (gcry_cipher_hd_t hd, void *outtag, size_t taglen)
       rc = _gcry_cipher_ccm_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;
@@ -982,6 +1010,10 @@ _gcry_cipher_checktag (gcry_cipher_hd_t hd, const void *intag, size_t taglen)
       rc = _gcry_cipher_ccm_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 234e8a4..a60d8c9 100644
--- a/src/gcrypt.h.in
+++ b/src/gcrypt.h.in
@@ -894,9 +894,13 @@ 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. */
   };
 
+/* GCM works only with blocks of 128 bits */
+#define GCRY_GCM_BLOCK_LEN   (128 / 8)
+
 /* Flags used with the open function. */
 enum gcry_cipher_flags
   {
diff --git a/tests/basic.c b/tests/basic.c
index 99d63ae..7f1536c 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 79b868c..fced8de 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 93874fa..d70383e 100644
--- a/tests/benchmark.c
+++ b/tests/benchmark.c
@@ -497,6 +497,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}
   };
-- 
1.8.4.rc3




More information about the Gcrypt-devel mailing list