[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