[RFC 2/2] FIXME: initial implementation of GCM

Dmitry Eremin-Solenikov dbaryshkov at gmail.com
Fri Aug 2 09:14:15 CEST 2013


Currently it is very slow.

Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov at gmail.com>
---
 cipher/Makefile.am       |    1 +
 cipher/cipher-gcm.c      |  282 +++++++++++++++++++++++++++++++++++++++
 cipher/cipher-internal.h |   27 ++++
 cipher/cipher.c          |   30 +++++
 src/gcrypt.h.in          |    6 +-
 tests/basic.c            |  332 ++++++++++++++++++++++++++++++++++++++++++++++
 tests/benchmark.c        |    5 +-
 7 files changed, 681 insertions(+), 2 deletions(-)
 create mode 100644 cipher/cipher-gcm.c

diff --git a/cipher/Makefile.am b/cipher/Makefile.am
index 75ad987..e185975 100644
--- a/cipher/Makefile.am
+++ b/cipher/Makefile.am
@@ -40,6 +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-gcm.c \
 cipher-selftest.c cipher-selftest.h \
 pubkey.c pubkey-internal.h \
 md.c \
diff --git a/cipher/cipher-gcm.c b/cipher/cipher-gcm.c
new file mode 100644
index 0000000..c216b90
--- /dev/null
+++ b/cipher/cipher-gcm.c
@@ -0,0 +1,282 @@
+/* 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"
+
+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 ghash(unsigned char *hsub, unsigned char *result, const unsigned char *buf)
+{
+  unsigned char V[16];
+  int i, j;
+
+  memcpy(V, result, 16);
+  buf_xor(V, V, buf, 16);
+
+  memset(result, 0, 16);
+
+  for (i = 0; i < 16; i++)
+    {
+      for (j = 0x80; j ; j >>= 1)
+        {
+          if (hsub[i] & j)
+            buf_xor(result, result, V, 16);
+          if (bshift(V))
+            V[0] ^= 0xe1;
+        }
+    }
+}
+
+
+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->cipher->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->cipher->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->u_iv.iv, c->u_tag.tag, tmp);
+        } else {
+          buf_xor (outbuf, tmp, inbuf, n);
+          ghash (c->u_iv.iv, 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->cipher->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->u_iv.iv, c->u_tag.tag, tmp);
+        } else {
+          ghash (c->u_iv.iv, 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->cipher->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->cipher->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->u_iv.iv, c->u_tag.tag, aadbuf);
+
+      aadbuflen -= blocksize;
+      aadbuf += blocksize;
+   }
+
+  if (aadbuflen != 0)
+    {
+      memcpy(tmp, aadbuf, aadbuflen);
+      memset(tmp + aadbuflen, 0, blocksize - aadbuflen);
+
+      ghash (c->u_iv.iv, 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->cipher->encrypt ( &c->context.c, c->u_iv.iv, c->u_tag.tag );
+
+  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->u_iv.iv, c->u_ctr.ctr, iv);
+      if (n != 0)
+        {
+          memcpy(tmp, iv, n);
+          memset(tmp + n, 0, 16 - n);
+          ghash (c->u_iv.iv, 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->u_iv.iv, 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->cipher->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)
+{
+  if (outbuflen < 16)
+    return GPG_ERR_BUFFER_TOO_SHORT;
+
+  if (!c->marks.tag)
+    {
+      ghash (c->u_iv.iv, 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);
+
+  return 0;
+}
diff --git a/cipher/cipher-internal.h b/cipher/cipher-internal.h
index 025bf2e..76d8540 100644
--- a/cipher/cipher-internal.h
+++ b/cipher/cipher-internal.h
@@ -98,6 +98,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
@@ -115,9 +116,16 @@ 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 */
 
   /* What follows are two contexts of the cipher in use.  The first
      one needs to be aligned well enough for the cipher operation
@@ -177,5 +185,24 @@ gcry_err_code_t _gcry_cipher_aeswrap_decrypt
                    const byte *inbuf, unsigned int inbuflen);
 
 
+/*-- cipher-gcm.c --*/
+gcry_err_code_t _gcry_cipher_gcm_encrypt
+/*           */   (gcry_cipher_hd_t c,
+                   byte *outbuf, unsigned int outbuflen,
+                   const byte *inbuf, unsigned int inbuflen);
+gcry_err_code_t _gcry_cipher_gcm_decrypt
+/*           */   (gcry_cipher_hd_t c,
+                   byte *outbuf, unsigned int outbuflen,
+                   const byte *inbuf, unsigned int inbuflen);
+void _gcry_cipher_gcm_setiv
+/*           */   (gcry_cipher_hd_t c,
+                   const byte *iv, unsigned int ivlen);
+gcry_err_code_t _gcry_cipher_gcm_authenticate
+/*           */   (gcry_cipher_hd_t c,
+                   const byte *aadbuf, unsigned int aadbuflen);
+gcry_err_code_t _gcry_cipher_gcm_tag
+/*           */   (gcry_cipher_hd_t c,
+                   byte *outbuf, unsigned int outbuflen);
+
 
 #endif /*G10_CIPHER_INTERNAL_H*/
diff --git a/cipher/cipher.c b/cipher/cipher.c
index 99bd3cd..e61f576 100644
--- a/cipher/cipher.c
+++ b/cipher/cipher.c
@@ -638,6 +638,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 ((cipher->encrypt == dummy_encrypt_block)
 	    || (cipher->decrypt == dummy_decrypt_block))
 	  err = GPG_ERR_INV_CIPHER_MODE;
@@ -851,6 +852,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->extraspec && c->extraspec->setiv)
@@ -891,6 +899,8 @@ cipher_reset (gcry_cipher_hd_t c)
   memset (c->u_iv.iv, 0, c->cipher->blocksize);
   memset (c->lastiv, 0, c->cipher->blocksize);
   memset (c->u_ctr.ctr, 0, c->cipher->blocksize);
+  memset (c->u_tag.tag, 0, c->cipher->blocksize);
+  memset (c->length, 0, c->cipher->blocksize);
 }
 
 
@@ -982,6 +992,11 @@ cipher_encrypt (gcry_cipher_hd_t c, byte *outbuf, unsigned int 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->cipher->stencrypt (&c->context.c,
                             outbuf, (byte*)/*arggg*/inbuf, inbuflen);
@@ -1075,6 +1090,11 @@ cipher_decrypt (gcry_cipher_hd_t c, byte *outbuf, unsigned int 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->cipher->stdecrypt (&c->context.c,
                             outbuf, (byte*)/*arggg*/inbuf, inbuflen);
@@ -1178,6 +1198,11 @@ gcry_error_t
 _gcry_cipher_authenticate (gcry_cipher_hd_t hd,
                            const void *aad, size_t aadsize)
 {
+  if (hd->mode == GCRY_CIPHER_MODE_GCM)
+    {
+      return gpg_error (_gcry_cipher_gcm_authenticate (hd, aad, aadsize));
+    }
+
   log_fatal ("gcry_cipher_tag: invalid mode %d\n", hd->mode );
   return gpg_error (GPG_ERR_INV_CIPHER_MODE);
 }
@@ -1185,6 +1210,11 @@ _gcry_cipher_authenticate (gcry_cipher_hd_t hd,
 gcry_error_t
 _gcry_cipher_tag (gcry_cipher_hd_t hd, void *out, size_t outsize)
 {
+  if (hd->mode == GCRY_CIPHER_MODE_GCM)
+    {
+      return gpg_error (_gcry_cipher_gcm_tag (hd, out, outsize));
+    }
+
   log_fatal ("gcry_cipher_tag: invalid mode %d\n", hd->mode );
   return gpg_error (GPG_ERR_INV_CIPHER_MODE);
 }
diff --git a/src/gcrypt.h.in b/src/gcrypt.h.in
index faedb33..3fa3410 100644
--- a/src/gcrypt.h.in
+++ b/src/gcrypt.h.in
@@ -841,9 +841,13 @@ enum gcry_cipher_modes
     GCRY_CIPHER_MODE_STREAM = 4,  /* Used with stream ciphers. */
     GCRY_CIPHER_MODE_OFB    = 5,  /* Outer feedback. */
     GCRY_CIPHER_MODE_CTR    = 6,  /* Counter. */
-    GCRY_CIPHER_MODE_AESWRAP= 7   /* AES-WRAP algorithm.  */
+    GCRY_CIPHER_MODE_AESWRAP= 7,  /* AES-WRAP algorithm. */
+    GCRY_CIPHER_MODE_GCM    = 8   /* 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 46e213c..c0fc0f9 100644
--- a/tests/basic.c
+++ b/tests/basic.c
@@ -1137,6 +1137,335 @@ 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)
+        err = gcry_cipher_authenticate(hdd, 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;
+        }
+
+      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_tag (hde, out, TAGLEN); /* FIXME */
+      if (err)
+        {
+          fail ("aes-gcm, gcry_cipher_tag(%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_tag (hdd, out, TAGLEN); /* FIXME */
+      if (err)
+        {
+          fail ("aes-gcm, gcry_cipher_tag(%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, decrypt tag mismatch entry %d\n", i);
+
+      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_stream_cipher (void)
@@ -2197,6 +2526,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++)
@@ -2234,6 +2565,7 @@ check_cipher_modes(void)
   check_ctr_cipher ();
   check_cfb_cipher ();
   check_ofb_cipher ();
+  check_gcm_cipher ();
   check_stream_cipher ();
   check_stream_cipher_large_block ();
 
diff --git a/tests/benchmark.c b/tests/benchmark.c
index 79048a3..2f0e20b 100644
--- a/tests/benchmark.c
+++ b/tests/benchmark.c
@@ -536,6 +536,7 @@ cipher_bench ( const char *algoname )
     { GCRY_CIPHER_MODE_CFB, "      CFB", 0 },
     { GCRY_CIPHER_MODE_OFB, "      OFB", 0 },
     { GCRY_CIPHER_MODE_CTR, "      CTR", 0 },
+    { GCRY_CIPHER_MODE_GCM, "      GCM", 0 },
     { GCRY_CIPHER_MODE_STREAM, "", 0 },
     {0}
   };
@@ -624,7 +625,9 @@ cipher_bench ( const char *algoname )
   for (modeidx=0; modes[modeidx].mode; modeidx++)
     {
       if ((blklen > 1 && modes[modeidx].mode == GCRY_CIPHER_MODE_STREAM)
-          | (blklen == 1 && modes[modeidx].mode != GCRY_CIPHER_MODE_STREAM))
+          || (blklen == 1 && modes[modeidx].mode != GCRY_CIPHER_MODE_STREAM)
+          || (blklen != GCRY_GCM_BLOCK_LEN && modes[modeidx].mode ==
+                              GCRY_CIPHER_MODE_GCM))
         continue;
 
       for (i=0; i < sizeof buf; i++)
-- 
1.7.10.4




More information about the Gcrypt-devel mailing list