[PATCH 2/3] Add EAX mode
Jussi Kivilinna
jussi.kivilinna at iki.fi
Sat Jan 20 21:04:50 CET 2018
* cipher/Makefile.am: Add 'cipher-eax.c'.
* cipher/cipher-cmac.c (cmac_write): Rename to ...
(_gcry_cmac_write): ... this; Take CMAC context as new input
parameter; Return error code.
(cmac_generate_subkeys): Rename to ...
(_gcry_cmac_generate_subkeys): ... this; Take CMAC context as new
input parameter; Return error code.
(cmac_final): Rename to ...
(_gcry_cmac_final): ... this; Take CMAC context as new input
parameter; Return error code.
(cmac_tag): Take CMAC context as new input parameter.
(_gcry_cmac_reset): New.
(_gcry_cipher_cmac_authenticate): Remove duplicate tag flag check;
Adapt to changes above.
(_gcry_cipher_cmac_get_tag): Adapt to changes above.
(_gcry_cipher_cmac_check_tag): Ditto.
(_gcry_cipher_cmac_set_subkeys): Ditto.
* cipher-eax.c: New.
* cipher-internal.h (gcry_cmac_context_t): New.
(gcry_cipher_handle): Update u_mode.cmac; Add u_mode.eax.
(_gcry_cmac_write, _gcry_cmac_generate_subkeys, _gcry_cmac_final)
(_gcry_cmac_reset, _gcry_cipher_eax_encrypt, _gcry_cipher_eax_decrypt)
(_gcry_cipher_eax_set_nonce, _gcry_cipher_eax_authenticate)
(_gcry_cipher_eax_get_tag, _gcry_cipher_eax_check_tag)
(_gcry_cipher_eax_setkey): New prototypes.
* cipher/cipher.c (_gcry_cipher_open_internal, cipher_setkey)
(cipher_reset, cipher_encrypt, cipher_decrypt, _gcry_cipher_setiv)
(_gcry_cipher_authenticate, _gcry_cipher_gettag, _gcry_cipher_checktag)
(_gcry_cipher_info): Add EAX mode.
* doc/gcrypt.texi: Add EAX mode.
* src/gcrypt.h.in (GCRY_CIPHER_MODE_EAX): New.
* tests/basic.c (_check_gcm_cipher, _check_poly1305_cipher): Constify
test vectors array.
(_check_eax_cipher, check_eax_cipher): New.
(check_ciphers, check_cipher_modes): Add EAX mode.
* tests/bench-slope.c (bench_eax_encrypt_do_bench)
(bench_eax_decrypt_do_bench, bench_eax_authenticate_do_bench)
(eax_encrypt_ops, eax_decrypt_ops, eax_authenticate_ops): New.
(cipher_modes): Add EAX mode.
* tests/benchmark.c (cipher_bench): Add EAX mode.
--
Signed-off-by: Jussi Kivilinna <jussi.kivilinna at iki.fi>
---
0 files changed
diff --git a/cipher/Makefile.am b/cipher/Makefile.am
index bba815bbe..6e6c5ac03 100644
--- a/cipher/Makefile.am
+++ b/cipher/Makefile.am
@@ -44,7 +44,7 @@ cipher.c cipher-internal.h \
cipher-cbc.c cipher-cfb.c cipher-ofb.c cipher-ctr.c cipher-aeswrap.c \
cipher-ccm.c cipher-cmac.c cipher-gcm.c cipher-gcm-intel-pclmul.c \
cipher-gcm-armv8-aarch32-ce.S cipher-gcm-armv8-aarch64-ce.S \
-cipher-poly1305.c cipher-ocb.c cipher-xts.c \
+cipher-poly1305.c cipher-ocb.c cipher-xts.c cipher-eax.c \
cipher-selftest.c cipher-selftest.h \
pubkey.c pubkey-internal.h pubkey-util.c \
md.c \
diff --git a/cipher/cipher-cmac.c b/cipher/cipher-cmac.c
index da3ef7592..30567b7fc 100644
--- a/cipher/cipher-cmac.c
+++ b/cipher/cipher-cmac.c
@@ -1,5 +1,5 @@
/* cmac.c - CMAC, Cipher-based MAC.
- * Copyright (C) 2013 Jussi Kivilinna <jussi.kivilinna at iki.fi>
+ * Copyright (C) 2013,2018 Jussi Kivilinna <jussi.kivilinna at iki.fi>
*
* This file is part of Libgcrypt.
*
@@ -33,8 +33,9 @@
(burn) = (burn) > __nburn ? (burn) : __nburn; } while (0)
-static void
-cmac_write (gcry_cipher_hd_t c, const byte * inbuf, size_t inlen)
+gcry_err_code_t
+_gcry_cmac_write (gcry_cipher_hd_t c, gcry_cmac_context_t *ctx,
+ const byte * inbuf, size_t inlen)
{
gcry_cipher_encrypt_t enc_fn = c->spec->encrypt;
const unsigned int blocksize = c->spec->blocksize;
@@ -42,31 +43,37 @@ cmac_write (gcry_cipher_hd_t c, const byte * inbuf, size_t inlen)
unsigned int burn = 0;
unsigned int nblocks;
+ if (ctx->tag)
+ return GPG_ERR_INV_STATE;
+
/* Tell compiler that we require a cipher with a 64bit or 128 bit block
* length, to allow better optimization of this function. */
if (blocksize > 16 || blocksize < 8 || blocksize & (8 - 1))
- return;
+ return GPG_ERR_INV_CIPHER_MODE;
- if (!inlen || !inbuf)
- return;
+ if (!inbuf)
+ return GPG_ERR_INV_ARG;
+
+ if (inlen == 0)
+ return 0;
/* Last block is needed for cmac_final. */
- if (c->unused + inlen <= blocksize)
+ if (ctx->mac_unused + inlen <= blocksize)
{
- for (; inlen && c->unused < blocksize; inlen--)
- c->lastiv[c->unused++] = *inbuf++;
- return;
+ for (; inlen && ctx->mac_unused < blocksize; inlen--)
+ ctx->macbuf[ctx->mac_unused++] = *inbuf++;
+ return 0;
}
- if (c->unused)
+ if (ctx->mac_unused)
{
- for (; inlen && c->unused < blocksize; inlen--)
- c->lastiv[c->unused++] = *inbuf++;
+ for (; inlen && ctx->mac_unused < blocksize; inlen--)
+ ctx->macbuf[ctx->mac_unused++] = *inbuf++;
- buf_xor (c->u_iv.iv, c->u_iv.iv, c->lastiv, blocksize);
- set_burn (burn, enc_fn (&c->context.c, c->u_iv.iv, c->u_iv.iv));
+ buf_xor (ctx->u_iv.iv, ctx->u_iv.iv, ctx->macbuf, blocksize);
+ set_burn (burn, enc_fn (&c->context.c, ctx->u_iv.iv, ctx->u_iv.iv));
- c->unused = 0;
+ ctx->mac_unused = 0;
}
if (c->bulk.cbc_enc && inlen > blocksize)
@@ -74,7 +81,7 @@ cmac_write (gcry_cipher_hd_t c, const byte * inbuf, size_t inlen)
nblocks = inlen / blocksize;
nblocks -= (nblocks * blocksize == inlen);
- c->bulk.cbc_enc (&c->context.c, c->u_iv.iv, outbuf, inbuf, nblocks, 1);
+ c->bulk.cbc_enc (&c->context.c, ctx->u_iv.iv, outbuf, inbuf, nblocks, 1);
inbuf += nblocks * blocksize;
inlen -= nblocks * blocksize;
@@ -83,8 +90,8 @@ cmac_write (gcry_cipher_hd_t c, const byte * inbuf, size_t inlen)
else
while (inlen > blocksize)
{
- buf_xor (c->u_iv.iv, c->u_iv.iv, inbuf, blocksize);
- set_burn (burn, enc_fn (&c->context.c, c->u_iv.iv, c->u_iv.iv));
+ buf_xor (ctx->u_iv.iv, ctx->u_iv.iv, inbuf, blocksize);
+ set_burn (burn, enc_fn (&c->context.c, ctx->u_iv.iv, ctx->u_iv.iv));
inlen -= blocksize;
inbuf += blocksize;
}
@@ -93,16 +100,18 @@ cmac_write (gcry_cipher_hd_t c, const byte * inbuf, size_t inlen)
if (inlen == 0)
BUG ();
- for (; inlen && c->unused < blocksize; inlen--)
- c->lastiv[c->unused++] = *inbuf++;
+ for (; inlen && ctx->mac_unused < blocksize; inlen--)
+ ctx->macbuf[ctx->mac_unused++] = *inbuf++;
if (burn)
_gcry_burn_stack (burn + 4 * sizeof (void *));
+
+ return 0;
}
-static void
-cmac_generate_subkeys (gcry_cipher_hd_t c)
+gcry_err_code_t
+_gcry_cmac_generate_subkeys (gcry_cipher_hd_t c, gcry_cmac_context_t *ctx)
{
const unsigned int blocksize = c->spec->blocksize;
byte rb, carry, t, bi;
@@ -117,7 +126,7 @@ cmac_generate_subkeys (gcry_cipher_hd_t c)
/* Tell compiler that we require a cipher with a 64bit or 128 bit block
* length, to allow better optimization of this function. */
if (blocksize > 16 || blocksize < 8 || blocksize & (8 - 1))
- return;
+ return GPG_ERR_INV_CIPHER_MODE;
if (MAX_BLOCKSIZE < blocksize)
BUG ();
@@ -127,7 +136,7 @@ cmac_generate_subkeys (gcry_cipher_hd_t c)
burn = c->spec->encrypt (&c->context.c, u.buf, u.buf);
/* Currently supported blocksizes are 16 and 8. */
- rb = blocksize == 16 ? 0x87 : 0x1B /*blocksize == 8 */ ;
+ rb = blocksize == 16 ? 0x87 : 0x1B /* blocksize == 8 */ ;
for (j = 0; j < 2; j++)
{
@@ -139,93 +148,113 @@ cmac_generate_subkeys (gcry_cipher_hd_t c)
t = carry | (bi << 1);
carry = bi >> 7;
u.buf[i] = t & 0xff;
- c->u_mode.cmac.subkeys[j][i] = u.buf[i];
+ ctx->subkeys[j][i] = u.buf[i];
}
u.buf[blocksize - 1] ^= carry ? rb : 0;
- c->u_mode.cmac.subkeys[j][blocksize - 1] = u.buf[blocksize - 1];
+ ctx->subkeys[j][blocksize - 1] = u.buf[blocksize - 1];
}
wipememory (&u, sizeof (u));
if (burn)
_gcry_burn_stack (burn + 4 * sizeof (void *));
+
+ return 0;
}
-static void
-cmac_final (gcry_cipher_hd_t c)
+gcry_err_code_t
+_gcry_cmac_final (gcry_cipher_hd_t c, gcry_cmac_context_t *ctx)
{
const unsigned int blocksize = c->spec->blocksize;
- unsigned int count = c->unused;
+ unsigned int count = ctx->mac_unused;
unsigned int burn;
byte *subkey;
/* Tell compiler that we require a cipher with a 64bit or 128 bit block
* length, to allow better optimization of this function. */
if (blocksize > 16 || blocksize < 8 || blocksize & (8 - 1))
- return;
+ return GPG_ERR_INV_CIPHER_MODE;
if (count == blocksize)
- subkey = c->u_mode.cmac.subkeys[0]; /* K1 */
+ subkey = ctx->subkeys[0]; /* K1 */
else
{
- subkey = c->u_mode.cmac.subkeys[1]; /* K2 */
- c->lastiv[count++] = 0x80;
+ subkey = ctx->subkeys[1]; /* K2 */
+ ctx->macbuf[count++] = 0x80;
while (count < blocksize)
- c->lastiv[count++] = 0;
+ ctx->macbuf[count++] = 0;
}
- buf_xor (c->lastiv, c->lastiv, subkey, blocksize);
+ buf_xor (ctx->macbuf, ctx->macbuf, subkey, blocksize);
- buf_xor (c->u_iv.iv, c->u_iv.iv, c->lastiv, blocksize);
- burn = c->spec->encrypt (&c->context.c, c->u_iv.iv, c->u_iv.iv);
+ buf_xor (ctx->u_iv.iv, ctx->u_iv.iv, ctx->macbuf, blocksize);
+ burn = c->spec->encrypt (&c->context.c, ctx->u_iv.iv, ctx->u_iv.iv);
if (burn)
_gcry_burn_stack (burn + 4 * sizeof (void *));
- c->unused = 0;
+ ctx->mac_unused = 0;
+
+ return 0;
}
static gcry_err_code_t
-cmac_tag (gcry_cipher_hd_t c, unsigned char *tag, size_t taglen, int check)
+cmac_tag (gcry_cipher_hd_t c, gcry_cmac_context_t *ctx,
+ unsigned char *tag, size_t taglen, int check)
{
+ gcry_err_code_t ret;
+
if (!tag || taglen == 0 || taglen > c->spec->blocksize)
return GPG_ERR_INV_ARG;
- if (!c->u_mode.cmac.tag)
+ if (!ctx->tag)
{
- cmac_final (c);
- c->u_mode.cmac.tag = 1;
+ ret = _gcry_cmac_final (c, ctx);
+ if (ret != 0)
+ return ret;
+
+ ctx->tag = 1;
}
if (!check)
{
- memcpy (tag, c->u_iv.iv, taglen);
+ memcpy (tag, ctx->u_iv.iv, taglen);
return GPG_ERR_NO_ERROR;
}
else
{
- return buf_eq_const (tag, c->u_iv.iv, taglen) ?
+ return buf_eq_const (tag, ctx->u_iv.iv, taglen) ?
GPG_ERR_NO_ERROR : GPG_ERR_CHECKSUM;
}
}
+void
+_gcry_cmac_reset (gcry_cmac_context_t *ctx)
+{
+ char tmp_buf[sizeof(ctx->subkeys)];
+
+ /* Only keep subkeys when reseting context. */
+
+ buf_cpy (tmp_buf, ctx->subkeys, sizeof(ctx->subkeys));
+ memset (ctx, 0, sizeof(*ctx));
+ buf_cpy (ctx->subkeys, tmp_buf, sizeof(ctx->subkeys));
+ wipememory (tmp_buf, sizeof(tmp_buf));
+}
+
+
gcry_err_code_t
_gcry_cipher_cmac_authenticate (gcry_cipher_hd_t c,
const unsigned char *abuf, size_t abuflen)
{
if (abuflen > 0 && !abuf)
return GPG_ERR_INV_ARG;
- if (c->u_mode.cmac.tag)
- return GPG_ERR_INV_STATE;
/* To support new blocksize, update cmac_generate_subkeys() then add new
blocksize here. */
if (c->spec->blocksize != 16 && c->spec->blocksize != 8)
return GPG_ERR_INV_CIPHER_MODE;
- cmac_write (c, abuf, abuflen);
-
- return GPG_ERR_NO_ERROR;
+ return _gcry_cmac_write (c, &c->u_mode.cmac, abuf, abuflen);
}
@@ -233,7 +262,7 @@ gcry_err_code_t
_gcry_cipher_cmac_get_tag (gcry_cipher_hd_t c,
unsigned char *outtag, size_t taglen)
{
- return cmac_tag (c, outtag, taglen, 0);
+ return cmac_tag (c, &c->u_mode.cmac, outtag, taglen, 0);
}
@@ -241,13 +270,11 @@ gcry_err_code_t
_gcry_cipher_cmac_check_tag (gcry_cipher_hd_t c,
const unsigned char *intag, size_t taglen)
{
- return cmac_tag (c, (unsigned char *) intag, taglen, 1);
+ return cmac_tag (c, &c->u_mode.cmac, (unsigned char *) intag, taglen, 1);
}
gcry_err_code_t
_gcry_cipher_cmac_set_subkeys (gcry_cipher_hd_t c)
{
- cmac_generate_subkeys (c);
-
- return GPG_ERR_NO_ERROR;
+ return _gcry_cmac_generate_subkeys (c, &c->u_mode.cmac);
}
diff --git a/cipher/cipher-eax.c b/cipher/cipher-eax.c
new file mode 100644
index 000000000..1ce479755
--- /dev/null
+++ b/cipher/cipher-eax.c
@@ -0,0 +1,248 @@
+/* cipher-eax.c - EAX implementation
+ * Copyright (C) 2018 Jussi Kivilinna <jussi.kivilinna at iki.fi>
+ *
+ * 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 "bufhelp.h"
+#include "./cipher-internal.h"
+
+
+gcry_err_code_t
+_gcry_cipher_eax_encrypt (gcry_cipher_hd_t c,
+ byte *outbuf, size_t outbuflen,
+ const byte *inbuf, size_t inbuflen)
+{
+ gcry_err_code_t err;
+
+ if (outbuflen < inbuflen)
+ return GPG_ERR_BUFFER_TOO_SHORT;
+ if (c->marks.tag)
+ return GPG_ERR_INV_STATE;
+
+ if (!c->marks.iv)
+ {
+ err = _gcry_cipher_eax_set_nonce (c, NULL, 0);
+ if (err != 0)
+ return err;
+ }
+
+ err = _gcry_cipher_ctr_encrypt (c, outbuf, outbuflen, inbuf, inbuflen);
+ if (err != 0)
+ return err;
+
+ return _gcry_cmac_write (c, &c->u_mode.eax.cmac_ciphertext, outbuf, inbuflen);
+}
+
+
+gcry_err_code_t
+_gcry_cipher_eax_decrypt (gcry_cipher_hd_t c,
+ byte *outbuf, size_t outbuflen,
+ const byte *inbuf, size_t inbuflen)
+{
+ gcry_err_code_t err;
+
+ if (outbuflen < inbuflen)
+ return GPG_ERR_BUFFER_TOO_SHORT;
+ if (c->marks.tag)
+ return GPG_ERR_INV_STATE;
+
+ if (!c->marks.iv)
+ {
+ err = _gcry_cipher_eax_set_nonce (c, NULL, 0);
+ if (err != 0)
+ return err;
+ }
+
+ err = _gcry_cmac_write (c, &c->u_mode.eax.cmac_ciphertext, inbuf, inbuflen);
+ if (err != 0)
+ return err;
+
+ return _gcry_cipher_ctr_encrypt (c, outbuf, outbuflen, inbuf, inbuflen);
+}
+
+
+gcry_err_code_t
+_gcry_cipher_eax_authenticate (gcry_cipher_hd_t c,
+ const byte * aadbuf, size_t aadbuflen)
+{
+ gcry_err_code_t err;
+
+ if (c->marks.tag)
+ return GPG_ERR_INV_STATE;
+
+ if (!c->marks.iv)
+ {
+ err = _gcry_cipher_eax_set_nonce (c, NULL, 0);
+ if (err != 0)
+ return err;
+ }
+
+ return _gcry_cmac_write (c, &c->u_mode.eax.cmac_header, aadbuf, aadbuflen);
+}
+
+
+gcry_err_code_t
+_gcry_cipher_eax_setkey (gcry_cipher_hd_t c)
+{
+ gcry_err_code_t err;
+
+ err = _gcry_cmac_generate_subkeys (c, &c->u_mode.eax.cmac_header);
+ if (err != 0)
+ return err;
+
+ buf_cpy (c->u_mode.eax.cmac_ciphertext.subkeys,
+ c->u_mode.eax.cmac_header.subkeys,
+ sizeof(c->u_mode.eax.cmac_header.subkeys));
+
+ return 0;
+}
+
+
+gcry_err_code_t
+_gcry_cipher_eax_set_nonce (gcry_cipher_hd_t c, const byte *nonce,
+ size_t noncelen)
+{
+ gcry_cmac_context_t nonce_cmac;
+ unsigned char initbuf[MAX_BLOCKSIZE];
+ gcry_err_code_t err;
+
+ c->marks.iv = 0;
+ c->marks.tag = 0;
+
+ _gcry_cmac_reset (&c->u_mode.eax.cmac_header);
+ _gcry_cmac_reset (&c->u_mode.eax.cmac_ciphertext);
+
+ /* Calculate nonce CMAC */
+
+ memset(&nonce_cmac, 0, sizeof(nonce_cmac));
+ memset(&initbuf, 0, sizeof(initbuf));
+
+ buf_cpy (&nonce_cmac.subkeys, c->u_mode.eax.cmac_header.subkeys,
+ sizeof(c->u_mode.eax.cmac_header.subkeys));
+
+ err = _gcry_cmac_write (c, &nonce_cmac, initbuf, c->spec->blocksize);
+ if (err != 0)
+ return err;
+
+ if (noncelen != 0)
+ {
+ err = _gcry_cmac_write (c, &nonce_cmac, nonce, noncelen);
+ if (err != 0)
+ return err;
+ }
+
+ err = _gcry_cmac_final (c, &nonce_cmac);
+ if (err != 0)
+ return err;
+
+ buf_cpy (c->u_iv.iv, nonce_cmac.u_iv.iv, MAX_BLOCKSIZE);
+ buf_cpy (c->u_ctr.ctr, nonce_cmac.u_iv.iv, MAX_BLOCKSIZE);
+
+ wipememory (&nonce_cmac, sizeof(nonce_cmac));
+
+ /* Prepare header CMAC */
+
+ initbuf[c->spec->blocksize - 1] = 1;
+ err = _gcry_cmac_write (c, &c->u_mode.eax.cmac_header, initbuf,
+ c->spec->blocksize);
+ if (err != 0)
+ return err;
+
+ /* Prepare ciphertext CMAC */
+
+ initbuf[c->spec->blocksize - 1] = 2;
+ err = _gcry_cmac_write (c, &c->u_mode.eax.cmac_ciphertext, initbuf,
+ c->spec->blocksize);
+ if (err != 0)
+ return err;
+
+ c->marks.iv = 1;
+ c->marks.tag = 0;
+
+ return 0;
+}
+
+
+static gcry_err_code_t
+_gcry_cipher_eax_tag (gcry_cipher_hd_t c,
+ byte *outbuf, size_t outbuflen, int check)
+{
+ gcry_err_code_t err;
+
+ if (!c->marks.tag)
+ {
+ err = _gcry_cmac_final (c, &c->u_mode.eax.cmac_header);
+ if (err != 0)
+ return err;
+
+ err = _gcry_cmac_final (c, &c->u_mode.eax.cmac_ciphertext);
+ if (err != 0)
+ return err;
+
+ buf_xor_1 (c->u_iv.iv, c->u_mode.eax.cmac_header.u_iv.iv, MAX_BLOCKSIZE);
+ buf_xor_1 (c->u_iv.iv, c->u_mode.eax.cmac_ciphertext.u_iv.iv,
+ MAX_BLOCKSIZE);
+
+ _gcry_cmac_reset (&c->u_mode.eax.cmac_header);
+ _gcry_cmac_reset (&c->u_mode.eax.cmac_ciphertext);
+
+ c->marks.tag = 1;
+ }
+
+ if (!check)
+ {
+ if (outbuflen > c->spec->blocksize)
+ outbuflen = c->spec->blocksize;
+
+ /* NB: We already checked that OUTBUF is large enough to hold
+ * the result or has valid truncated length. */
+ memcpy (outbuf, c->u_iv.iv, outbuflen);
+ }
+ else
+ {
+ /* OUTBUFLEN gives the length of the user supplied tag in OUTBUF
+ * and thus we need to compare its length first. */
+ if (!(outbuflen <= c->spec->blocksize)
+ || !buf_eq_const (outbuf, c->u_iv.iv, outbuflen))
+ return GPG_ERR_CHECKSUM;
+ }
+
+ return 0;
+}
+
+
+gcry_err_code_t
+_gcry_cipher_eax_get_tag (gcry_cipher_hd_t c, unsigned char *outtag,
+ size_t taglen)
+{
+ return _gcry_cipher_eax_tag (c, outtag, taglen, 0);
+}
+
+gcry_err_code_t
+_gcry_cipher_eax_check_tag (gcry_cipher_hd_t c, const unsigned char *intag,
+ size_t taglen)
+{
+ return _gcry_cipher_eax_tag (c, (unsigned char *) intag, taglen, 1);
+}
diff --git a/cipher/cipher-internal.h b/cipher/cipher-internal.h
index 8c897d7b5..a0ede5e03 100644
--- a/cipher/cipher-internal.h
+++ b/cipher/cipher-internal.h
@@ -109,6 +109,25 @@ typedef union
} cipher_context_alignment_t;
+/* Storage structure for CMAC, for CMAC and EAX modes. */
+typedef struct {
+ /* The initialization vector. Also contains tag after finalization. */
+ union {
+ cipher_context_alignment_t iv_align;
+ unsigned char iv[MAX_BLOCKSIZE];
+ } u_iv;
+
+ /* Subkeys for tag creation, not cleared by gcry_cipher_reset. */
+ unsigned char subkeys[2][MAX_BLOCKSIZE];
+
+ /* Space to save partial input lengths for MAC. */
+ unsigned char macbuf[MAX_BLOCKSIZE];
+
+ int mac_unused; /* Number of unprocessed bytes in MACBUF. */
+ unsigned int tag:1; /* Set to 1 if tag has been finalized. */
+} gcry_cmac_context_t;
+
+
/* The handle structure. */
struct gcry_cipher_handle
{
@@ -197,7 +216,7 @@ struct gcry_cipher_handle
unsigned char s0[GCRY_CCM_BLOCK_LEN];
- unsigned int nonce:1;/* Set to 1 if nonce has been set. */
+ 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. */
} ccm;
@@ -217,12 +236,16 @@ struct gcry_cipher_handle
} poly1305;
/* Mode specific storage for CMAC mode. */
+ gcry_cmac_context_t cmac;
+
+ /* Mode specific storage for EAX mode. */
struct {
- unsigned int tag:1; /* Set to 1 if tag has been finalized. */
+ /* CMAC for header (AAD). */
+ gcry_cmac_context_t cmac_header;
- /* Subkeys for tag creation, not cleared by gcry_cipher_reset. */
- unsigned char subkeys[2][MAX_BLOCKSIZE];
- } cmac;
+ /* CMAC for ciphertext. */
+ gcry_cmac_context_t cmac_ciphertext;
+ } eax;
/* Mode specific storage for GCM mode. */
struct {
@@ -236,7 +259,6 @@ struct gcry_cipher_handle
unsigned char macbuf[GCRY_CCM_BLOCK_LEN];
int mac_unused; /* Number of unprocessed bytes in MACBUF. */
-
/* byte counters for GCM */
u32 aadlen[2];
u32 datalen[2];
@@ -309,7 +331,6 @@ struct gcry_cipher_handle
processed. */
unsigned int data_finalized:1;
unsigned int aad_finalized:1;
-
} ocb;
/* Mode specific storage for XTS mode. */
@@ -406,6 +427,42 @@ gcry_err_code_t _gcry_cipher_ccm_check_tag
const unsigned char *intag, size_t taglen);
+/*-- cipher-cmac.c --*/
+gcry_err_code_t _gcry_cmac_generate_subkeys
+/* */ (gcry_cipher_hd_t c, gcry_cmac_context_t *ctx);
+gcry_err_code_t _gcry_cmac_write
+/* */ (gcry_cipher_hd_t c, gcry_cmac_context_t *ctx,
+ const byte * inbuf, size_t inlen);
+gcry_err_code_t _gcry_cmac_final
+/* */ (gcry_cipher_hd_t c, gcry_cmac_context_t *ctx);
+void _gcry_cmac_reset (gcry_cmac_context_t *ctx);
+
+
+/*-- cipher-eax.c --*/
+gcry_err_code_t _gcry_cipher_eax_encrypt
+/* */ (gcry_cipher_hd_t c,
+ unsigned char *outbuf, size_t outbuflen,
+ const unsigned char *inbuf, size_t inbuflen);
+gcry_err_code_t _gcry_cipher_eax_decrypt
+/* */ (gcry_cipher_hd_t c,
+ unsigned char *outbuf, size_t outbuflen,
+ const unsigned char *inbuf, size_t inbuflen);
+gcry_err_code_t _gcry_cipher_eax_set_nonce
+/* */ (gcry_cipher_hd_t c,
+ const unsigned char *nonce, size_t noncelen);
+gcry_err_code_t _gcry_cipher_eax_authenticate
+/* */ (gcry_cipher_hd_t c,
+ const unsigned char *aadbuf, size_t aadbuflen);
+gcry_err_code_t _gcry_cipher_eax_get_tag
+/* */ (gcry_cipher_hd_t c,
+ unsigned char *outtag, size_t taglen);
+gcry_err_code_t _gcry_cipher_eax_check_tag
+/* */ (gcry_cipher_hd_t c,
+ const unsigned char *intag, size_t taglen);
+gcry_err_code_t _gcry_cipher_eax_setkey
+/* */ (gcry_cipher_hd_t c);
+
+
/*-- cipher-gcm.c --*/
gcry_err_code_t _gcry_cipher_gcm_encrypt
/* */ (gcry_cipher_hd_t c,
diff --git a/cipher/cipher.c b/cipher/cipher.c
index 18b25911a..1bef766cb 100644
--- a/cipher/cipher.c
+++ b/cipher/cipher.c
@@ -420,6 +420,7 @@ _gcry_cipher_open_internal (gcry_cipher_hd_t *handle,
case GCRY_CIPHER_MODE_CTR:
case GCRY_CIPHER_MODE_AESWRAP:
case GCRY_CIPHER_MODE_CMAC:
+ case GCRY_CIPHER_MODE_EAX:
case GCRY_CIPHER_MODE_GCM:
if (!spec->encrypt || !spec->decrypt)
err = GPG_ERR_INV_CIPHER_MODE;
@@ -688,7 +689,11 @@ cipher_setkey (gcry_cipher_hd_t c, byte *key, size_t keylen)
switch (c->mode)
{
case GCRY_CIPHER_MODE_CMAC:
- _gcry_cipher_cmac_set_subkeys (c);
+ rc = _gcry_cipher_cmac_set_subkeys (c);
+ break;
+
+ case GCRY_CIPHER_MODE_EAX:
+ rc = _gcry_cipher_eax_setkey (c);
break;
case GCRY_CIPHER_MODE_GCM:
@@ -782,8 +787,12 @@ cipher_reset (gcry_cipher_hd_t c)
switch (c->mode)
{
case GCRY_CIPHER_MODE_CMAC:
- /* Only clear 'tag' for cmac, keep subkeys. */
- c->u_mode.cmac.tag = 0;
+ _gcry_cmac_reset(&c->u_mode.cmac);
+ break;
+
+ case GCRY_CIPHER_MODE_EAX:
+ _gcry_cmac_reset(&c->u_mode.eax.cmac_header);
+ _gcry_cmac_reset(&c->u_mode.eax.cmac_ciphertext);
break;
case GCRY_CIPHER_MODE_GCM:
@@ -929,6 +938,10 @@ cipher_encrypt (gcry_cipher_hd_t c, byte *outbuf, size_t outbuflen,
rc = GPG_ERR_INV_CIPHER_MODE;
break;
+ case GCRY_CIPHER_MODE_EAX:
+ rc = _gcry_cipher_eax_encrypt (c, outbuf, outbuflen, inbuf, inbuflen);
+ break;
+
case GCRY_CIPHER_MODE_GCM:
rc = _gcry_cipher_gcm_encrypt (c, outbuf, outbuflen, inbuf, inbuflen);
break;
@@ -1060,6 +1073,10 @@ cipher_decrypt (gcry_cipher_hd_t c, byte *outbuf, size_t outbuflen,
rc = GPG_ERR_INV_CIPHER_MODE;
break;
+ case GCRY_CIPHER_MODE_EAX:
+ rc = _gcry_cipher_eax_decrypt (c, outbuf, outbuflen, inbuf, inbuflen);
+ break;
+
case GCRY_CIPHER_MODE_GCM:
rc = _gcry_cipher_gcm_decrypt (c, outbuf, outbuflen, inbuf, inbuflen);
break;
@@ -1158,6 +1175,10 @@ _gcry_cipher_setiv (gcry_cipher_hd_t hd, const void *iv, size_t ivlen)
rc = _gcry_cipher_ccm_set_nonce (hd, iv, ivlen);
break;
+ case GCRY_CIPHER_MODE_EAX:
+ rc = _gcry_cipher_eax_set_nonce (hd, iv, ivlen);
+ break;
+
case GCRY_CIPHER_MODE_GCM:
rc = _gcry_cipher_gcm_setiv (hd, iv, ivlen);
break;
@@ -1226,6 +1247,10 @@ _gcry_cipher_authenticate (gcry_cipher_hd_t hd, const void *abuf,
rc = _gcry_cipher_cmac_authenticate (hd, abuf, abuflen);
break;
+ case GCRY_CIPHER_MODE_EAX:
+ rc = _gcry_cipher_eax_authenticate (hd, abuf, abuflen);
+ break;
+
case GCRY_CIPHER_MODE_GCM:
rc = _gcry_cipher_gcm_authenticate (hd, abuf, abuflen);
break;
@@ -1263,6 +1288,10 @@ _gcry_cipher_gettag (gcry_cipher_hd_t hd, void *outtag, size_t taglen)
rc = _gcry_cipher_cmac_get_tag (hd, outtag, taglen);
break;
+ case GCRY_CIPHER_MODE_EAX:
+ rc = _gcry_cipher_eax_get_tag (hd, outtag, taglen);
+ break;
+
case GCRY_CIPHER_MODE_GCM:
rc = _gcry_cipher_gcm_get_tag (hd, outtag, taglen);
break;
@@ -1300,6 +1329,10 @@ _gcry_cipher_checktag (gcry_cipher_hd_t hd, const void *intag, size_t taglen)
rc = _gcry_cipher_cmac_check_tag (hd, intag, taglen);
break;
+ case GCRY_CIPHER_MODE_EAX:
+ rc = _gcry_cipher_eax_check_tag (hd, intag, taglen);
+ break;
+
case GCRY_CIPHER_MODE_GCM:
rc = _gcry_cipher_gcm_check_tag (hd, intag, taglen);
break;
@@ -1501,6 +1534,10 @@ _gcry_cipher_info (gcry_cipher_hd_t h, int cmd, void *buffer, size_t *nbytes)
*nbytes = h->u_mode.ccm.authlen;
break;
+ case GCRY_CIPHER_MODE_EAX:
+ *nbytes = h->spec->blocksize;
+ break;
+
case GCRY_CIPHER_MODE_GCM:
*nbytes = GCRY_GCM_BLOCK_LEN;
break;
diff --git a/doc/gcrypt.texi b/doc/gcrypt.texi
index 78315052b..ccb4b820b 100644
--- a/doc/gcrypt.texi
+++ b/doc/gcrypt.texi
@@ -1722,6 +1722,12 @@ value is automatically incremented after each call of
Auto-increment allows avoiding need of setting IV between processing
of sequential data units.
+ at item GCRY_CIPHER_MODE_EAX
+ at cindex EAX, EAX mode
+EAX is an Authenticated Encryption with Associated Data (AEAD) block cipher
+mode by Bellare, Rogaway, and Wagner (see
+ at uref{http://web.cs.ucdavis.edu/~rogaway/papers/eax.html}).
+
@end table
@node Working with cipher handles
@@ -1752,12 +1758,13 @@ with some algorithms - in particular, stream mode
Poly1305 AEAD mode (@code{GCRY_CIPHER_MODE_POLY1305}) only works with
ChaCha20 stream cipher. The block cipher modes
(@code{GCRY_CIPHER_MODE_ECB}, @code{GCRY_CIPHER_MODE_CBC},
- at code{GCRY_CIPHER_MODE_CFB}, @code{GCRY_CIPHER_MODE_OFB} and
- at code{GCRY_CIPHER_MODE_CTR}) will work with any block cipher
-algorithm. GCM mode (@code{GCRY_CIPHER_MODE_CCM}), CCM mode
-(@code{GCRY_CIPHER_MODE_GCM}), OCB mode (@code{GCRY_CIPHER_MODE_OCB}),
-and XTS mode (@code{GCRY_CIPHER_MODE_XTS}) will only work
-with block cipher algorithms which have the block size of 16 bytes.
+ at code{GCRY_CIPHER_MODE_CFB}, @code{GCRY_CIPHER_MODE_OFB},
+ at code{GCRY_CIPHER_MODE_CTR} and @code{GCRY_CIPHER_MODE_EAX}) will work
+with any block cipher algorithm. GCM mode
+(@code{GCRY_CIPHER_MODE_CCM}), CCM mode (@code{GCRY_CIPHER_MODE_GCM}),
+OCB mode (@code{GCRY_CIPHER_MODE_OCB}), and XTS mode
+(@code{GCRY_CIPHER_MODE_XTS}) will only work with block cipher
+algorithms which have the block size of 16 bytes.
The third argument @var{flags} can either be passed as @code{0} or as
the bit-wise OR of the following constants.
diff --git a/src/gcrypt.h.in b/src/gcrypt.h.in
index 1eb3d7c0f..83f94b687 100644
--- a/src/gcrypt.h.in
+++ b/src/gcrypt.h.in
@@ -971,7 +971,8 @@ enum gcry_cipher_modes
GCRY_CIPHER_MODE_POLY1305 = 10, /* Poly1305 based AEAD mode. */
GCRY_CIPHER_MODE_OCB = 11, /* OCB3 mode. */
GCRY_CIPHER_MODE_CFB8 = 12, /* Cipher feedback (8 bit mode). */
- GCRY_CIPHER_MODE_XTS = 13 /* XTS mode. */
+ GCRY_CIPHER_MODE_XTS = 13, /* XTS mode. */
+ GCRY_CIPHER_MODE_EAX = 14 /* EAX mode. */
};
/* Flags used with the open function. */
diff --git a/tests/basic.c b/tests/basic.c
index c2b42082a..c883eb39f 100644
--- a/tests/basic.c
+++ b/tests/basic.c
@@ -1347,7 +1347,7 @@ check_ofb_cipher (void)
static void
_check_gcm_cipher (unsigned int step)
{
- struct tv
+ static const struct tv
{
int algo;
char key[MAX_DATA_LEN];
@@ -1890,10 +1890,543 @@ check_gcm_cipher (void)
}
+static void
+_check_eax_cipher (unsigned int step)
+{
+ static const struct tv
+ {
+ int algo;
+ char key[MAX_DATA_LEN];
+ char nonce[MAX_DATA_LEN];
+ int noncelen;
+ unsigned char header[MAX_DATA_LEN];
+ int headerlen;
+ unsigned char plaintext[MAX_DATA_LEN];
+ int inlen;
+ char out[MAX_DATA_LEN];
+ char tag[MAX_DATA_LEN];
+ int taglen;
+ int should_fail;
+ } tv[] =
+ {
+ /* Test vectors from http://www.cs.ucdavis.edu/~rogaway/papers/eax.pdf */
+ { GCRY_CIPHER_AES,
+ "\x23\x39\x52\xDE\xE4\xD5\xED\x5F\x9B\x9C\x6D\x6F\xF8\x0F\xF4\x78",
+ "\x62\xEC\x67\xF9\xC3\xA4\xA4\x07\xFC\xB2\xA8\xC4\x90\x31\xA8\xB3", 16,
+ "\x6B\xFB\x91\x4F\xD0\x7E\xAE\x6B", 8,
+ "",
+ 0,
+ "",
+ "\xE0\x37\x83\x0E\x83\x89\xF2\x7B\x02\x5A\x2D\x65\x27\xE7\x9D\x01", 16,
+ 0
+ },
+ { GCRY_CIPHER_AES,
+ "\x91\x94\x5D\x3F\x4D\xCB\xEE\x0B\xF4\x5E\xF5\x22\x55\xF0\x95\xA4",
+ "\xBE\xCA\xF0\x43\xB0\xA2\x3D\x84\x31\x94\xBA\x97\x2C\x66\xDE\xBD", 16,
+ "\xFA\x3B\xFD\x48\x06\xEB\x53\xFA", 8,
+ "\xF7\xFB",
+ 2,
+ "\x19\xDD",
+ "\x5C\x4C\x93\x31\x04\x9D\x0B\xDA\xB0\x27\x74\x08\xF6\x79\x67\xE5", 16,
+ 0
+ },
+ { GCRY_CIPHER_AES,
+ "\x01\xF7\x4A\xD6\x40\x77\xF2\xE7\x04\xC0\xF6\x0A\xDA\x3D\xD5\x23",
+ "\x70\xC3\xDB\x4F\x0D\x26\x36\x84\x00\xA1\x0E\xD0\x5D\x2B\xFF\x5E", 16,
+ "\x23\x4A\x34\x63\xC1\x26\x4A\xC6", 8,
+ "\x1A\x47\xCB\x49\x33",
+ 5,
+ "\xD8\x51\xD5\xBA\xE0",
+ "\x3A\x59\xF2\x38\xA2\x3E\x39\x19\x9D\xC9\x26\x66\x26\xC4\x0F\x80", 16,
+ 0
+ },
+ { GCRY_CIPHER_AES,
+ "\xD0\x7C\xF6\xCB\xB7\xF3\x13\xBD\xDE\x66\xB7\x27\xAF\xD3\xC5\xE8",
+ "\x84\x08\xDF\xFF\x3C\x1A\x2B\x12\x92\xDC\x19\x9E\x46\xB7\xD6\x17", 16,
+ "\x33\xCC\xE2\xEA\xBF\xF5\xA7\x9D", 8,
+ "\x48\x1C\x9E\x39\xB1",
+ 5,
+ "\x63\x2A\x9D\x13\x1A",
+ "\xD4\xC1\x68\xA4\x22\x5D\x8E\x1F\xF7\x55\x93\x99\x74\xA7\xBE\xDE", 16,
+ 0
+ },
+ { GCRY_CIPHER_AES,
+ "\x35\xB6\xD0\x58\x00\x05\xBB\xC1\x2B\x05\x87\x12\x45\x57\xD2\xC2",
+ "\xFD\xB6\xB0\x66\x76\xEE\xDC\x5C\x61\xD7\x42\x76\xE1\xF8\xE8\x16", 16,
+ "\xAE\xB9\x6E\xAE\xBE\x29\x70\xE9", 8,
+ "\x40\xD0\xC0\x7D\xA5\xE4",
+ 6,
+ "\x07\x1D\xFE\x16\xC6\x75",
+ "\xCB\x06\x77\xE5\x36\xF7\x3A\xFE\x6A\x14\xB7\x4E\xE4\x98\x44\xDD", 16,
+ 0
+ },
+ { GCRY_CIPHER_AES,
+ "\xBD\x8E\x6E\x11\x47\x5E\x60\xB2\x68\x78\x4C\x38\xC6\x2F\xEB\x22",
+ "\x6E\xAC\x5C\x93\x07\x2D\x8E\x85\x13\xF7\x50\x93\x5E\x46\xDA\x1B", 16,
+ "\xD4\x48\x2D\x1C\xA7\x8D\xCE\x0F", 8,
+ "\x4D\xE3\xB3\x5C\x3F\xC0\x39\x24\x5B\xD1\xFB\x7D",
+ 12,
+ "\x83\x5B\xB4\xF1\x5D\x74\x3E\x35\x0E\x72\x84\x14",
+ "\xAB\xB8\x64\x4F\xD6\xCC\xB8\x69\x47\xC5\xE1\x05\x90\x21\x0A\x4F", 16,
+ 0
+ },
+ { GCRY_CIPHER_AES,
+ "\x7C\x77\xD6\xE8\x13\xBE\xD5\xAC\x98\xBA\xA4\x17\x47\x7A\x2E\x7D",
+ "\x1A\x8C\x98\xDC\xD7\x3D\x38\x39\x3B\x2B\xF1\x56\x9D\xEE\xFC\x19", 16,
+ "\x65\xD2\x01\x79\x90\xD6\x25\x28", 8,
+ "\x8B\x0A\x79\x30\x6C\x9C\xE7\xED\x99\xDA\xE4\xF8\x7F\x8D\xD6\x16\x36",
+ 17,
+ "\x02\x08\x3E\x39\x79\xDA\x01\x48\x12\xF5\x9F\x11\xD5\x26\x30\xDA\x30",
+ "\x13\x73\x27\xD1\x06\x49\xB0\xAA\x6E\x1C\x18\x1D\xB6\x17\xD7\xF2", 16,
+ 0
+ },
+ { GCRY_CIPHER_AES,
+ "\x5F\xFF\x20\xCA\xFA\xB1\x19\xCA\x2F\xC7\x35\x49\xE2\x0F\x5B\x0D",
+ "\xDD\xE5\x9B\x97\xD7\x22\x15\x6D\x4D\x9A\xFF\x2B\xC7\x55\x98\x26", 16,
+ "\x54\xB9\xF0\x4E\x6A\x09\x18\x9A", 8,
+ "\x1B\xDA\x12\x2B\xCE\x8A\x8D\xBA\xF1\x87\x7D\x96\x2B\x85\x92\xDD"
+ "\x2D\x56",
+ 18,
+ "\x2E\xC4\x7B\x2C\x49\x54\xA4\x89\xAF\xC7\xBA\x48\x97\xED\xCD\xAE"
+ "\x8C\xC3",
+ "\x3B\x60\x45\x05\x99\xBD\x02\xC9\x63\x82\x90\x2A\xEF\x7F\x83\x2A", 16,
+ 0
+ },
+ { GCRY_CIPHER_AES,
+ "\xA4\xA4\x78\x2B\xCF\xFD\x3E\xC5\xE7\xEF\x6D\x8C\x34\xA5\x61\x23",
+ "\xB7\x81\xFC\xF2\xF7\x5F\xA5\xA8\xDE\x97\xA9\xCA\x48\xE5\x22\xEC", 16,
+ "\x89\x9A\x17\x58\x97\x56\x1D\x7E", 8,
+ "\x6C\xF3\x67\x20\x87\x2B\x85\x13\xF6\xEA\xB1\xA8\xA4\x44\x38\xD5"
+ "\xEF\x11",
+ 18,
+ "\x0D\xE1\x8F\xD0\xFD\xD9\x1E\x7A\xF1\x9F\x1D\x8E\xE8\x73\x39\x38"
+ "\xB1\xE8",
+ "\xE7\xF6\xD2\x23\x16\x18\x10\x2F\xDB\x7F\xE5\x5F\xF1\x99\x17\x00", 16,
+ 0
+ },
+ { GCRY_CIPHER_AES,
+ "\x83\x95\xFC\xF1\xE9\x5B\xEB\xD6\x97\xBD\x01\x0B\xC7\x66\xAA\xC3",
+ "\x22\xE7\xAD\xD9\x3C\xFC\x63\x93\xC5\x7E\xC0\xB3\xC1\x7D\x6B\x44", 16,
+ "\x12\x67\x35\xFC\xC3\x20\xD2\x5A", 8,
+ "\xCA\x40\xD7\x44\x6E\x54\x5F\xFA\xED\x3B\xD1\x2A\x74\x0A\x65\x9F"
+ "\xFB\xBB\x3C\xEA\xB7",
+ 21,
+ "\xCB\x89\x20\xF8\x7A\x6C\x75\xCF\xF3\x96\x27\xB5\x6E\x3E\xD1\x97"
+ "\xC5\x52\xD2\x95\xA7",
+ "\xCF\xC4\x6A\xFC\x25\x3B\x46\x52\xB1\xAF\x37\x95\xB1\x24\xAB\x6E", 16,
+ 0
+ },
+ /* Negative test for bad tag. */
+ { GCRY_CIPHER_AES,
+ "\x23\x39\x52\xDE\xE4\xD5\xED\x5F\x9B\x9C\x6D\x6F\xF8\x0F\xF4\x78",
+ "\x62\xEC\x67\xF9\xC3\xA4\xA4\x07\xFC\xB2\xA8\xC4\x90\x31\xA8\xB3", 16,
+ "\x6B\xFB\x91\x4F\xD0\x7E\xAE\x6B", 8,
+ "",
+ 0,
+ "",
+ "\x00\x37\x83\x0E\x83\x89\xF2\x7B\x02\x5A\x2D\x65\x27\xE7\x9D\x01", 16,
+ 1
+ },
+ /* Test vectors from libtomcrypt. */
+ {
+ GCRY_CIPHER_AES,
+ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
+ "", 0,
+ "", 0,
+ "",
+ 0,
+ "",
+ "\x9a\xd0\x7e\x7d\xbf\xf3\x01\xf5\x05\xde\x59\x6b\x96\x15\xdf\xff", 16,
+ 0
+ },
+ {
+ GCRY_CIPHER_AES,
+ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
+ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", 16,
+ "", 0,
+ "",
+ 0,
+ "",
+ "\x1c\xe1\x0d\x3e\xff\xd4\xca\xdb\xe2\xe4\x4b\x58\xd6\x0a\xb9\xec", 16,
+ 0
+ },
+ {
+ GCRY_CIPHER_AES,
+ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
+ "", 0,
+ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", 16,
+ "",
+ 0,
+ "",
+ "\x3a\x69\x8f\x7a\x27\x0e\x51\xb0\xf6\x5b\x3d\x3e\x47\x19\x3c\xff", 16,
+ 0
+ },
+ {
+ GCRY_CIPHER_AES,
+ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
+ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", 16,
+ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", 16,
+ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
+ "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f",
+ 32,
+ "\x29\xd8\x78\xd1\xa3\xbe\x85\x7b\x6f\xb8\xc8\xea\x59\x50\xa7\x78"
+ "\x33\x1f\xbf\x2c\xcf\x33\x98\x6f\x35\xe8\xcf\x12\x1d\xcb\x30\xbc",
+ "\x4f\xbe\x03\x38\xbe\x1c\x8c\x7e\x1d\x7a\xe7\xe4\x5b\x92\xc5\x87", 16,
+ 0
+ },
+ {
+ GCRY_CIPHER_AES,
+ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
+ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e", 15,
+ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d", 14,
+ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
+ "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c",
+ 29,
+ "\xdd\x25\xc7\x54\xc5\xb1\x7c\x59\x28\xb6\x9b\x73\x15\x5f\x7b\xb8"
+ "\x88\x8f\xaf\x37\x09\x1a\xd9\x2c\x8a\x24\xdb\x86\x8b",
+ "\x0d\x1a\x14\xe5\x22\x24\xff\xd2\x3a\x05\xfa\x02\xcd\xef\x52\xda", 16,
+ 0
+ },
+ };
+
+ gcry_cipher_hd_t hde, hdd;
+ unsigned char out[MAX_DATA_LEN];
+ unsigned char tag[16];
+ int i, keylen;
+ gcry_error_t err = 0;
+ size_t pos, poslen, taglen2;
+ int byteNum;
+
+ if (verbose)
+ fprintf (stderr, " Starting EAX checks.\n");
+
+ for (i = 0; i < sizeof (tv) / sizeof (tv[0]); i++)
+ {
+ if (gcry_cipher_test_algo (tv[i].algo) && in_fips_mode)
+ {
+ if (verbose)
+ fprintf (stderr, " algorithm %d not available in fips mode\n",
+ tv[i].algo);
+ continue;
+ }
+
+ if (verbose)
+ fprintf (stderr, " checking EAX 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_EAX, 0);
+ if (!err)
+ err = gcry_cipher_open (&hdd, tv[i].algo, GCRY_CIPHER_MODE_EAX, 0);
+ if (err)
+ {
+ fail ("aes-eax, gcry_cipher_open failed: %s\n", gpg_strerror (err));
+ return;
+ }
+
+ keylen = gcry_cipher_get_algo_keylen(tv[i].algo);
+ if (!keylen)
+ {
+ fail ("aes-eax, 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-eax, 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].nonce, tv[i].noncelen);
+ if (!err)
+ err = gcry_cipher_setiv (hdd, tv[i].nonce, tv[i].noncelen);
+ if (err)
+ {
+ fail ("aes-eax, gcry_cipher_setiv failed: %s\n",
+ gpg_strerror (err));
+ gcry_cipher_close (hde);
+ gcry_cipher_close (hdd);
+ return;
+ }
+
+ err = gcry_cipher_info (hde, GCRYCTL_GET_TAGLEN, NULL, &taglen2);
+ if (err)
+ {
+ fail ("cipher-eax, gcryctl_get_taglen failed (tv %d): %s\n",
+ i, gpg_strerror (err));
+ gcry_cipher_close (hde);
+ gcry_cipher_close (hdd);
+ return;
+ }
+ if (taglen2 != 16)
+ {
+ fail ("cipher-eax, gcryctl_get_taglen returned bad length"
+ " (tv %d): got=%zu want=%d\n",
+ i, taglen2, 16);
+ gcry_cipher_close (hde);
+ gcry_cipher_close (hdd);
+ return;
+ }
+
+ for (pos = 0; pos < tv[i].headerlen; pos += step)
+ {
+ poslen = (pos + step < tv[i].headerlen) ?
+ step : tv[i].headerlen - pos;
+
+ err = gcry_cipher_authenticate(hde, tv[i].header + pos, poslen);
+ if (err)
+ {
+ fail ("aes-eax, gcry_cipher_authenticate (%d) (%lu:%d) failed: "
+ "%s\n", i, (unsigned long) pos, step, gpg_strerror (err));
+ gcry_cipher_close (hde);
+ gcry_cipher_close (hdd);
+ return;
+ }
+ err = gcry_cipher_authenticate(hdd, tv[i].header + pos, poslen);
+ if (err)
+ {
+ fail ("aes-eax, de gcry_cipher_authenticate (%d) (%lu:%d) failed: "
+ "%s\n", i, (unsigned long) pos, step, gpg_strerror (err));
+ gcry_cipher_close (hde);
+ gcry_cipher_close (hdd);
+ return;
+ }
+ }
+
+ for (pos = 0; pos < tv[i].inlen; pos += step)
+ {
+ poslen = (pos + step < tv[i].inlen) ? step : tv[i].inlen - pos;
+
+ err = gcry_cipher_encrypt (hde, out + pos, poslen,
+ tv[i].plaintext + pos, poslen);
+ if (err)
+ {
+ fail ("aes-eax, gcry_cipher_encrypt (%d) (%lu:%d) failed: %s\n",
+ i, (unsigned long) pos, step, gpg_strerror (err));
+ gcry_cipher_close (hde);
+ gcry_cipher_close (hdd);
+ return;
+ }
+ }
+
+ if (memcmp (tv[i].out, out, tv[i].inlen))
+ fail ("aes-eax, encrypt mismatch entry %d (step %d)\n", i, step);
+
+ for (pos = 0; pos < tv[i].inlen; pos += step)
+ {
+ poslen = (pos + step < tv[i].inlen) ? step : tv[i].inlen - pos;
+
+ err = gcry_cipher_decrypt (hdd, out + pos, poslen, NULL, 0);
+ if (err)
+ {
+ fail ("aes-eax, gcry_cipher_decrypt (%d) (%lu:%d) failed: %s\n",
+ i, (unsigned long) pos, step, gpg_strerror (err));
+ gcry_cipher_close (hde);
+ gcry_cipher_close (hdd);
+ return;
+ }
+ }
+
+ if (memcmp (tv[i].plaintext, out, tv[i].inlen))
+ fail ("aes-eax, decrypt mismatch entry %d (step %d)\n", i, step);
+
+ taglen2 = tv[i].taglen ? tv[i].taglen : 16;
+
+ err = gcry_cipher_gettag (hde, out, taglen2);
+ if (err)
+ {
+ if (tv[i].should_fail)
+ goto next_tv;
+
+ fail ("aes-eax, 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, taglen2) != 0) ^ tv[i].should_fail)
+ fail ("aes-eax, encrypt tag mismatch entry %d\n", i);
+
+ err = gcry_cipher_checktag (hdd, tv[i].tag, taglen2);
+ if (err)
+ {
+ if (tv[i].should_fail)
+ goto next_tv;
+
+ fail ("aes-eax, 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-eax, gcry_cipher_reset (%d) failed: %s\n",
+ i, gpg_strerror (err));
+ gcry_cipher_close (hde);
+ gcry_cipher_close (hdd);
+ return;
+ }
+
+ /* gcry_cipher_reset clears the IV */
+ err = gcry_cipher_setiv (hde, tv[i].nonce, tv[i].noncelen);
+ if (!err)
+ err = gcry_cipher_setiv (hdd, tv[i].nonce, tv[i].noncelen);
+ if (err)
+ {
+ fail ("aes-eax, gcry_cipher_setiv failed: %s\n",
+ gpg_strerror (err));
+ gcry_cipher_close (hde);
+ gcry_cipher_close (hdd);
+ return;
+ }
+
+ /* this time we authenticate, encrypt and decrypt one byte at a time */
+ for (byteNum = 0; byteNum < tv[i].headerlen; ++byteNum)
+ {
+ err = gcry_cipher_authenticate(hde, tv[i].header + byteNum, 1);
+ if (err)
+ {
+ fail ("aes-eax, gcry_cipher_authenticate (%d) (byte-buf) failed: "
+ "%s\n", i, gpg_strerror (err));
+ gcry_cipher_close (hde);
+ gcry_cipher_close (hdd);
+ return;
+ }
+ err = gcry_cipher_authenticate(hdd, tv[i].header + byteNum, 1);
+ if (err)
+ {
+ fail ("aes-eax, de gcry_cipher_authenticate (%d) (byte-buf) "
+ "failed: %s\n", i, gpg_strerror (err));
+ gcry_cipher_close (hde);
+ gcry_cipher_close (hdd);
+ return;
+ }
+ }
+
+ 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-eax, gcry_cipher_encrypt (%d) (byte-buf) 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-eax, encrypt mismatch entry %d, (byte-buf)\n", i);
+
+ /* Test output to larger than 16-byte buffer. */
+ taglen2 = tv[i].taglen ? tv[i].taglen : 16 + 1;
+
+ err = gcry_cipher_gettag (hde, tag, taglen2);
+ if (err)
+ {
+ if (tv[i].should_fail)
+ goto next_tv;
+
+ fail ("aes-eax, gcry_cipher_gettag(%d, %lu) (byte-buf) failed: %s\n",
+ i, (unsigned long) taglen2, gpg_strerror (err));
+ gcry_cipher_close (hde);
+ gcry_cipher_close (hdd);
+ return;
+ }
+
+ taglen2 = tv[i].taglen ? tv[i].taglen : 16;
+
+ if ((memcmp (tv[i].tag, tag, taglen2) != 0) ^ tv[i].should_fail)
+ fail ("aes-eax, encrypt tag mismatch entry %d, (byte-buf)\n", i);
+
+ for (byteNum = 0; byteNum < tv[i].inlen; ++byteNum)
+ {
+ err = gcry_cipher_decrypt (hdd, out+byteNum, 1, NULL, 0);
+ if (err)
+ {
+ fail ("aes-eax, gcry_cipher_decrypt (%d) (byte-buf) 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-eax, decrypt mismatch entry %d\n", i);
+
+ err = gcry_cipher_checktag (hdd, tv[i].tag, taglen2);
+ if (err)
+ {
+ if (tv[i].should_fail)
+ goto next_tv;
+
+ fail ("aes-eax, gcry_cipher_checktag(%d) (byte-buf) failed: %s\n",
+ i, gpg_strerror (err));
+ gcry_cipher_close (hde);
+ gcry_cipher_close (hdd);
+ return;
+ }
+
+ err = gcry_cipher_checktag (hdd, tag, 17);
+ if (!err)
+ {
+ fail ("aes-eax, gcry_cipher_checktag(%d) did not fail for invalid "
+ " tag length of '%d'\n", i, 17);
+ gcry_cipher_close (hde);
+ gcry_cipher_close (hdd);
+ return;
+ }
+
+ if (tv[i].should_fail)
+ {
+ fail ("aes-eax, negative test succeeded %d\n", i);
+ gcry_cipher_close (hde);
+ gcry_cipher_close (hdd);
+ return;
+ }
+
+ next_tv:
+ gcry_cipher_close (hde);
+ gcry_cipher_close (hdd);
+ }
+ if (verbose)
+ fprintf (stderr, " Completed EAX checks.\n");
+}
+
+
+static void
+check_eax_cipher (void)
+{
+ /* Large buffers, no splitting. */
+ _check_eax_cipher(0xffffffff);
+ /* Split input to one byte buffers. */
+ _check_eax_cipher(1);
+ /* Split input to 7 byte buffers. */
+ _check_eax_cipher(7);
+ /* Split input to 16 byte buffers. */
+ _check_eax_cipher(16);
+}
+
+
static void
_check_poly1305_cipher (unsigned int step)
{
- struct tv
+ static const struct tv
{
int algo;
const char *key;
@@ -5813,6 +6346,7 @@ get_algo_mode_blklen (int algo, int mode)
case GCRY_CIPHER_MODE_CTR:
case GCRY_CIPHER_MODE_CCM:
case GCRY_CIPHER_MODE_GCM:
+ case GCRY_CIPHER_MODE_EAX:
case GCRY_CIPHER_MODE_POLY1305:
return 1;
}
@@ -5894,7 +6428,7 @@ check_one_cipher_core (int algo, int mode, int flags,
if ((mode == GCRY_CIPHER_MODE_CBC && (flags & GCRY_CIPHER_CBC_CTS)) ||
mode == GCRY_CIPHER_MODE_XTS)
{
- /* Input cannot be split in to multiple operations with CTS . */
+ /* Input cannot be split in to multiple operations with CTS. */
blklen = nplain;
}
@@ -6281,6 +6815,7 @@ 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);
+ check_one_cipher (algos[i], GCRY_CIPHER_MODE_EAX, 0);
if (gcry_cipher_get_algo_blklen (algos[i]) == GCRY_CCM_BLOCK_LEN)
check_one_cipher (algos[i], GCRY_CIPHER_MODE_CCM, 0);
if (gcry_cipher_get_algo_blklen (algos[i]) == GCRY_GCM_BLOCK_LEN)
@@ -6333,6 +6868,7 @@ check_cipher_modes(void)
check_poly1305_cipher ();
check_ocb_cipher ();
check_xts_cipher ();
+ check_eax_cipher ();
check_gost28147_cipher ();
check_stream_cipher ();
check_stream_cipher_large_block ();
diff --git a/tests/bench-slope.c b/tests/bench-slope.c
index 75e6e43d3..e34104f7b 100644
--- a/tests/bench-slope.c
+++ b/tests/bench-slope.c
@@ -1231,6 +1231,53 @@ static struct bench_ops ocb_authenticate_ops = {
&bench_ocb_authenticate_do_bench
};
+static void
+bench_eax_encrypt_do_bench (struct bench_obj *obj, void *buf,
+ size_t buflen)
+{
+ char nonce[16] = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce,
+ 0xdb, 0xad, 0xde, 0xca, 0xf8, 0x88,
+ 0x00, 0x00, 0x01, 0x00 };
+ bench_aead_encrypt_do_bench (obj, buf, buflen, nonce, sizeof(nonce));
+}
+
+static void
+bench_eax_decrypt_do_bench (struct bench_obj *obj, void *buf,
+ size_t buflen)
+{
+ char nonce[16] = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce,
+ 0xdb, 0xad, 0xde, 0xca, 0xf8, 0x88,
+ 0x00, 0x00, 0x01, 0x00 };
+ bench_aead_decrypt_do_bench (obj, buf, buflen, nonce, sizeof(nonce));
+}
+
+static void
+bench_eax_authenticate_do_bench (struct bench_obj *obj, void *buf,
+ size_t buflen)
+{
+ char nonce[16] = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce,
+ 0xdb, 0xad, 0xde, 0xca, 0xf8, 0x88,
+ 0x00, 0x00, 0x01, 0x00 };
+ bench_aead_authenticate_do_bench (obj, buf, buflen, nonce, sizeof(nonce));
+}
+
+static struct bench_ops eax_encrypt_ops = {
+ &bench_encrypt_init,
+ &bench_encrypt_free,
+ &bench_eax_encrypt_do_bench
+};
+
+static struct bench_ops eax_decrypt_ops = {
+ &bench_encrypt_init,
+ &bench_encrypt_free,
+ &bench_eax_decrypt_do_bench
+};
+
+static struct bench_ops eax_authenticate_ops = {
+ &bench_encrypt_init,
+ &bench_encrypt_free,
+ &bench_eax_authenticate_do_bench
+};
static void
bench_poly1305_encrypt_do_bench (struct bench_obj *obj, void *buf,
@@ -1291,6 +1338,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_EAX, "EAX enc", &eax_encrypt_ops},
+ {GCRY_CIPHER_MODE_EAX, "EAX dec", &eax_decrypt_ops},
+ {GCRY_CIPHER_MODE_EAX, "EAX auth", &eax_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},
diff --git a/tests/benchmark.c b/tests/benchmark.c
index 44a8711d9..59ea32c66 100644
--- a/tests/benchmark.c
+++ b/tests/benchmark.c
@@ -779,6 +779,8 @@ cipher_bench ( const char *algoname )
NULL, GCRY_GCM_BLOCK_LEN, GCRY_GCM_BLOCK_LEN },
{ GCRY_CIPHER_MODE_OCB, " OCB", 1,
NULL, 16, 16, 15 },
+ { GCRY_CIPHER_MODE_EAX, " EAX", 0,
+ NULL, 0, 8, 8 },
{ GCRY_CIPHER_MODE_STREAM, "", 0 },
{0}
};
More information about the Gcrypt-devel
mailing list