[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