[PATCH] Add CMAC mode (Cipher-based MAC)
Jussi Kivilinna
jussi.kivilinna at iki.fi
Thu Oct 31 13:41:41 CET 2013
* cipher/Makefile.am: Add 'cipher-cmac.c'.
* cipher/cipher-cmac.c: New.
* cipher/cipher-internal.h (gcry_cipher_handle): Add 'u_keys'.
(gcry_cipher_handle.u_mode): Add 'cmac'.
(_gcry_cipher_cmac_authenticate, _gcry_cipher_cmac_get_tag)
(_gcry_cipher_cmac_check_tag, _gcry_cipher_cmac_set_subkeys): New
prototypes.
* cipher/cipher.c (gcry_cipher_open, cipher_setkey, cipher_encrypt)
(cipher_decrypt, _gcry_cipher_authenticate, _gcry_cipher_gettag)
(_gcry_cipher_checktag): Add handling for CMAC mode.
(cipher_reset): Do not reset 'marks.key'.
* src/gcrypt.h.in (gcry_cipher_modes): Add 'GCRY_CIPHER_MODE_CMAC'.
* doc/gcrypt.texi: Add documentation for GCRY_CIPHER_MODE_CMAC.
* tests/basic.c (check_mac_cipher): New.
(check_cipher_modes): Call 'check_mac_cipher'.
* tests/bench-slope.c (bench_authenticate_do_bench): New.
(authenticate_ops): New.
(cipher_modes): Add CMAC test.
--
Patch adds CMAC (Cipher-based MAC) mode as defined in RFC 4493 and NIST
Special Publication 800-38B.
Example of usage:
/* Message 1 is split to two buffers, buf1_a and buf1_b. */
gcry_cipher_setkey(h, key, len(key));
gcry_cipher_authenticate(h, buf1_a, len(buf1_a));
gcry_cipher_authenticate(h, buf1_b, len(buf1_b));
gcry_cipher_gettag(h, buf1_tag, len(buf1_tag));
/* Message 2, MAC with same key.. can use reset instead of setkey. */
gcry_cipher_reset(h);
gcry_cipher_authenticate(h, buf2, len(buf2));
gcry_cipher_gettag(h, buf2_tag, len(buf2_tag));
Checking tag:
/* Message 3, compare with existing tag. */
gcry_cipher_setkey(h, key, len(key));
gcry_cipher_authenticate(h, buf3, len(buf3));
gcry_cipher_checktag(h, buf3_tag, len(buf3_tag));
if (gpg_err_code (err) == GPG_ERR_CHECKSUM)
{ /* Authentication failed. */ }
else if (err == 0)
{ /* Authentication ok. */ }
Signed-off-by: Jussi Kivilinna <jussi.kivilinna at iki.fi>
---
cipher/Makefile.am | 2
cipher/cipher-cmac.c | 242 +++++++++++++++++++++++++++++++++++++++
cipher/cipher-internal.h | 24 ++++
cipher/cipher.c | 37 ++++++
doc/gcrypt.texi | 25 +++-
src/gcrypt.h.in | 3
tests/basic.c | 287 ++++++++++++++++++++++++++++++++++++++++++++++
tests/bench-slope.c | 42 +++++++
8 files changed, 651 insertions(+), 11 deletions(-)
create mode 100644 cipher/cipher-cmac.c
diff --git a/cipher/Makefile.am b/cipher/Makefile.am
index 95d484e..87f693e 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-cmac.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
new file mode 100644
index 0000000..236a0dc
--- /dev/null
+++ b/cipher/cipher-cmac.c
@@ -0,0 +1,242 @@
+/* cmac.c - CMAC, Cipher-based MAC.
+ * Copyright © 2013 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 "g10lib.h"
+#include "cipher.h"
+#include "cipher-internal.h"
+#include "bufhelp.h"
+
+
+#define set_burn(burn, nburn) do { \
+ unsigned int __nburn = (nburn); \
+ (burn) = (burn) > __nburn ? (burn) : __nburn; } while (0)
+
+
+static void
+cmac_write (gcry_cipher_hd_t c, const byte * inbuf, size_t inlen)
+{
+ gcry_cipher_encrypt_t enc_fn = c->spec->encrypt;
+ const unsigned int blocksize = c->spec->blocksize;
+ byte outbuf[MAX_BLOCKSIZE];
+ unsigned int burn = 0;
+ unsigned int nblocks;
+
+ if (!inlen || !inbuf)
+ return;
+
+ /* Last block is needed for cmac_final. */
+ if (c->unused + inlen <= blocksize)
+ {
+ for (; inlen && c->unused < blocksize; inlen--)
+ c->lastiv[c->unused++] = *inbuf++;
+ return;
+ }
+
+ if (c->unused)
+ {
+ for (; inlen && c->unused < blocksize; inlen--)
+ c->lastiv[c->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));
+
+ c->unused = 0;
+ }
+
+ if (c->bulk.cbc_enc && inlen > blocksize)
+ {
+ nblocks = inlen / blocksize;
+ nblocks -= (nblocks * blocksize == inlen);
+
+ c->bulk.cbc_enc (&c->context.c, c->u_iv.iv, outbuf, inbuf, nblocks, 1);
+ inbuf += nblocks * blocksize;
+ inlen -= nblocks * blocksize;
+
+ wipememory (outbuf, sizeof (outbuf));
+ }
+ 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));
+ inlen -= blocksize;
+ inbuf += blocksize;
+ }
+
+ /* Make sure that last block is passed to cmac_final. */
+ if (inlen == 0)
+ BUG ();
+
+ for (; inlen && c->unused < blocksize; inlen--)
+ c->lastiv[c->unused++] = *inbuf++;
+
+ if (burn)
+ _gcry_burn_stack (burn + 4 * sizeof (void *));
+}
+
+
+static void cmac_generate_subkeys (gcry_cipher_hd_t c)
+{
+ const unsigned int blocksize = c->spec->blocksize;
+ byte rb, carry, t, bi;
+ unsigned int burn;
+ int i, j;
+ union
+ {
+ size_t _aligned;
+ byte buf[MAX_BLOCKSIZE];
+ } u;
+
+ if (MAX_BLOCKSIZE < blocksize)
+ BUG();
+
+ /* encrypt zero block */
+ memset (u.buf, 0, blocksize);
+ 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 */ ;
+
+ for (j = 0; j < 2; j++)
+ {
+ /* Generate subkeys K1 and K2 */
+ carry = 0;
+ for (i = blocksize - 1; i >= 0; i--)
+ {
+ bi = u.buf[i];
+ t = carry | (bi << 1);
+ carry = bi >> 7;
+ u.buf[i] = t & 0xff;
+ c->u_keys.cmac.subkeys[j][i] = u.buf[i];
+ }
+ u.buf[blocksize - 1] ^= carry ? rb : 0;
+ c->u_keys.cmac.subkeys[j][blocksize - 1] = u.buf[blocksize - 1];
+ }
+
+ wipememory (&u, sizeof (u));
+ if (burn)
+ _gcry_burn_stack (burn + 4 * sizeof (void *));
+}
+
+
+static void
+cmac_final (gcry_cipher_hd_t c)
+{
+ const unsigned int blocksize = c->spec->blocksize;
+ unsigned int count = c->unused;
+ unsigned int burn;
+ byte *subkey;
+
+ if (count == blocksize)
+ subkey = c->u_keys.cmac.subkeys[0]; /* K1 */
+ else
+ {
+ subkey = c->u_keys.cmac.subkeys[1]; /* K2 */
+ c->lastiv[count++] = 0x80;
+ while (count < blocksize)
+ c->lastiv[count++] = 0;
+ }
+
+ buf_xor (c->lastiv, c->lastiv, 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);
+ if (burn)
+ _gcry_burn_stack (burn + 4 * sizeof (void *));
+
+ c->unused = 0;
+}
+
+
+static gcry_err_code_t
+cmac_tag (gcry_cipher_hd_t c, unsigned char *tag, size_t taglen, int check)
+{
+ if (!tag || taglen == 0 || taglen > c->spec->blocksize)
+ return GPG_ERR_INV_ARG;
+
+ if (!c->u_mode.cmac.tag)
+ {
+ cmac_final (c);
+ c->u_mode.cmac.tag = 1;
+ }
+
+ if (!check)
+ {
+ memcpy (tag, c->u_iv.iv, taglen);
+ return GPG_ERR_NO_ERROR;
+ }
+ else
+ {
+ int diff, i;
+
+ /* Constant-time compare. */
+ for (i = 0, diff = 0; i < taglen; i++)
+ diff -= !!(tag[i] - c->u_iv.iv[i]);
+
+ return !diff ? GPG_ERR_NO_ERROR : GPG_ERR_CHECKSUM;
+ }
+}
+
+
+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;
+}
+
+
+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);
+}
+
+
+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);
+}
+
+gcry_err_code_t
+_gcry_cipher_cmac_set_subkeys (gcry_cipher_hd_t c)
+{
+ cmac_generate_subkeys (c);
+
+ return GPG_ERR_NO_ERROR;
+}
diff --git a/cipher/cipher-internal.h b/cipher/cipher-internal.h
index f528c84..74e57d7 100644
--- a/cipher/cipher-internal.h
+++ b/cipher/cipher-internal.h
@@ -136,8 +136,20 @@ struct gcry_cipher_handle
processed. */
unsigned int tag:1; /* Set to 1 if tag has been finalized. */
} ccm;
+ /* Mode specific storage for CMAC mode. */
+ struct {
+ unsigned int tag:1; /* Set to 1 if tag has been finalized. */
+ } cmac;
} u_mode;
+ /* Mode specific storage for subkeys. _Not_ cleared by gcry_cipher_reset. */
+ union {
+ /* Mode specific storage for CMAC mode. */
+ struct {
+ unsigned char subkeys[2][MAX_BLOCKSIZE];
+ } cmac;
+ } u_keys;
+
/* What follows are two contexts of the cipher in use. The first
one needs to be aligned well enough for the cipher operation
whereas the second one is a copy created by cipher_setkey and
@@ -217,5 +229,17 @@ gcry_err_code_t _gcry_cipher_ccm_check_tag
const unsigned char *intag, size_t taglen);
+/*-- cipher-cmac.c --*/
+gcry_err_code_t _gcry_cipher_cmac_authenticate
+/* */ (gcry_cipher_hd_t c, const unsigned char *abuf, size_t abuflen);
+gcry_err_code_t _gcry_cipher_cmac_get_tag
+/* */ (gcry_cipher_hd_t c,
+ unsigned char *outtag, size_t taglen);
+gcry_err_code_t _gcry_cipher_cmac_check_tag
+/* */ (gcry_cipher_hd_t c,
+ const unsigned char *intag, size_t taglen);
+gcry_err_code_t _gcry_cipher_cmac_set_subkeys
+/* */ (gcry_cipher_hd_t c);
+
#endif /*G10_CIPHER_INTERNAL_H*/
diff --git a/cipher/cipher.c b/cipher/cipher.c
index 73a97b1..d9117dd 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_CMAC:
if (!spec->encrypt || !spec->decrypt)
err = GPG_ERR_INV_CIPHER_MODE;
break;
@@ -567,10 +568,20 @@ cipher_setkey (gcry_cipher_hd_t c, byte *key, unsigned int keylen)
(void *) &c->context.c,
c->spec->contextsize);
c->marks.key = 1;
+
+ switch (c->mode)
+ {
+ case GCRY_CIPHER_MODE_CMAC:
+ _gcry_cipher_cmac_set_subkeys (c);
+ break;
+ default:
+ break;
+ };
}
else
c->marks.key = 0;
+
return gcry_error (ret);
}
@@ -613,6 +624,10 @@ cipher_setiv (gcry_cipher_hd_t c, const byte *iv, unsigned ivlen)
static void
cipher_reset (gcry_cipher_hd_t c)
{
+ unsigned int marks_key;
+
+ marks_key = c->marks.key;
+
memcpy (&c->context.c,
(char *) &c->context.c + c->spec->contextsize,
c->spec->contextsize);
@@ -622,6 +637,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;
+
+ c->marks.key = marks_key;
}
@@ -717,6 +734,10 @@ 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_CMAC:
+ rc = GPG_ERR_INV_CIPHER_MODE;
+ break;
+
case GCRY_CIPHER_MODE_STREAM:
c->spec->stencrypt (&c->context.c,
outbuf, (byte*)/*arggg*/inbuf, inbuflen);
@@ -814,6 +835,10 @@ 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_CMAC:
+ rc = GPG_ERR_INV_CIPHER_MODE;
+ break;
+
case GCRY_CIPHER_MODE_STREAM:
c->spec->stdecrypt (&c->context.c,
outbuf, (byte*)/*arggg*/inbuf, inbuflen);
@@ -936,6 +961,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_CMAC:
+ rc = _gcry_cipher_cmac_authenticate (hd, abuf, abuflen);
+ break;
+
default:
log_error ("gcry_cipher_authenticate: invalid mode %d\n", hd->mode);
rc = GPG_ERR_INV_CIPHER_MODE;
@@ -956,6 +985,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_CMAC:
+ rc = _gcry_cipher_cmac_get_tag (hd, outtag, taglen);
+ break;
+
default:
log_error ("gcry_cipher_gettag: invalid mode %d\n", hd->mode);
rc = GPG_ERR_INV_CIPHER_MODE;
@@ -976,6 +1009,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_CMAC:
+ rc = _gcry_cipher_cmac_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/doc/gcrypt.texi b/doc/gcrypt.texi
index 4a202dd..f30384b 100644
--- a/doc/gcrypt.texi
+++ b/doc/gcrypt.texi
@@ -1641,6 +1641,12 @@ Counter with CBC-MAC mode is an Authenticated Encryption with
Associated Data (AEAD) block cipher mode, which is specified in
'NIST Special Publication 800-38C' and RFC 3610.
+ at item GCRY_CIPHER_MODE_CMAC
+ at cindex CMAC, Cipher-based MAC
+In this mode, the block cipher algorithm becomes a CMAC message
+authentication algorithm. This mode is specified in 'NIST Special Publication
+800-38B' and RFC 4493.
+
@end table
@node Working with cipher handles
@@ -1670,10 +1676,10 @@ with some algorithms - in particular, stream mode
(@code{GCRY_CIPHER_MODE_STREAM}) only works with stream ciphers. The
block cipher modes (@code{GCRY_CIPHER_MODE_ECB},
@code{GCRY_CIPHER_MODE_CBC}, @code{GCRY_CIPHER_MODE_CFB},
- at code{GCRY_CIPHER_MODE_OFB} and @code{GCRY_CIPHER_MODE_CTR}) will work
-with any block cipher algorithm. The @code{GCRY_CIPHER_MODE_CCM} will
-only work with block cipher algorithms which have the block size of
-16 bytes.
+ at code{GCRY_CIPHER_MODE_OFB}, @code{GCRY_CIPHER_MODE_CTR}) and
+ at code{GCRY_CIPHER_MODE_CMAC} will work with any block cipher algorithm.
+The @code{GCRY_CIPHER_MODE_CCM} 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.
@@ -1762,15 +1768,16 @@ call to gcry_cipher_setkey and clear the initialization vector.
Note that gcry_cipher_reset is implemented as a macro.
@end deftypefun
-Authenticated Encryption with Associated Data (AEAD) block cipher
-modes require the handling of the authentication tag and the additional
-authenticated data, which can be done by using the following
+Message Authentication Code (MAC) and Authenticated Encryption with Associated
+Data (AEAD) block cipher modes require the handling of the authentication tag
+and the authenticated data, which can be done by using the following
functions:
@deftypefun gcry_error_t gcry_cipher_authenticate (gcry_cipher_hd_t @var{h}, const void *@var{abuf}, size_t @var{abuflen})
-Process the buffer @var{abuf} of length @var{abuflen} as the additional
-authenticated data (AAD) for AEAD cipher modes.
+Process the buffer @var{abuf} of length @var{abuflen} as the authenticated
+data for MAC cipher modes or the additional authenticated data (AAD) for
+AEAD cipher modes.
@end deftypefun
diff --git a/src/gcrypt.h.in b/src/gcrypt.h.in
index 2742556..2d27fdb 100644
--- a/src/gcrypt.h.in
+++ b/src/gcrypt.h.in
@@ -886,7 +886,8 @@ 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_CMAC = 9 /* Cipher-based MAC. */
};
/* Flags used with the open function. */
diff --git a/tests/basic.c b/tests/basic.c
index 21af21d..0cf31bf 100644
--- a/tests/basic.c
+++ b/tests/basic.c
@@ -1909,6 +1909,292 @@ check_ccm_cipher (void)
static void
+check_mac_cipher (void)
+{
+ static const struct tv
+ {
+ int algo;
+ int mode;
+ int klen;
+ const char *key;
+ struct {
+ int dlen;
+ const char *data;
+ int tlen;
+ const char *tag;
+ } data[MAX_DATA_LEN];
+ } tv[] = {
+ /* CMAC AES and DES test vectors from
+ http://web.archive.org/web/20130930212819/http://csrc.nist.gov/publications/nistpubs/800-38B/Updated_CMAC_Examples.pdf */
+ { GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CMAC,
+ 16,
+ "\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c",
+ { {
+ 0,
+ "",
+ 16,
+ "\xbb\x1d\x69\x29\xe9\x59\x37\x28\x7f\xa3\x7d\x12\x9b\x75\x67\x46"
+ }, {
+ 16,
+ "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a",
+ 16,
+ "\x07\x0a\x16\xb4\x6b\x4d\x41\x44\xf7\x9b\xdd\x9d\xd0\x4a\x28\x7c"
+ }, {
+ 40,
+ "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
+ "\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf\x8e\x51"
+ "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11",
+ 16,
+ "\xdf\xa6\x67\x47\xde\x9a\xe6\x30\x30\xca\x32\x61\x14\x97\xc8\x27"
+ }, {
+ 64,
+ "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
+ "\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf\x8e\x51"
+ "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11\xe5\xfb\xc1\x19\x1a\x0a\x52\xef"
+ "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17\xad\x2b\x41\x7b\xe6\x6c\x37\x10",
+ 16,
+ "\x51\xf0\xbe\xbf\x7e\x3b\x9d\x92\xfc\x49\x74\x17\x79\x36\x3c\xfe"
+ } } },
+ { GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CMAC,
+ 24,
+ "\x8e\x73\xb0\xf7\xda\x0e\x64\x52\xc8\x10\xf3\x2b\x80\x90\x79\xe5"
+ "\x62\xf8\xea\xd2\x52\x2c\x6b\x7b",
+ { {
+ 0,
+ "",
+ 16,
+ "\xd1\x7d\xdf\x46\xad\xaa\xcd\xe5\x31\xca\xc4\x83\xde\x7a\x93\x67"
+ }, {
+ 16,
+ "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a",
+ 16,
+ "\x9e\x99\xa7\xbf\x31\xe7\x10\x90\x06\x62\xf6\x5e\x61\x7c\x51\x84"
+ }, {
+ 40,
+ "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
+ "\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf\x8e\x51"
+ "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11",
+ 16,
+ "\x8a\x1d\xe5\xbe\x2e\xb3\x1a\xad\x08\x9a\x82\xe6\xee\x90\x8b\x0e"
+ }, {
+ 64,
+ "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
+ "\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf\x8e\x51"
+ "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11\xe5\xfb\xc1\x19\x1a\x0a\x52\xef"
+ "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17\xad\x2b\x41\x7b\xe6\x6c\x37\x10",
+ 16,
+ "\xa1\xd5\xdf\x0e\xed\x79\x0f\x79\x4d\x77\x58\x96\x59\xf3\x9a\x11"
+ } } },
+ { GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CMAC,
+ 32,
+ "\x60\x3d\xeb\x10\x15\xca\x71\xbe\x2b\x73\xae\xf0\x85\x7d\x77\x81"
+ "\x1f\x35\x2c\x07\x3b\x61\x08\xd7\x2d\x98\x10\xa3\x09\x14\xdf\xf4",
+ { {
+ 0,
+ "",
+ 16,
+ "\x02\x89\x62\xf6\x1b\x7b\xf8\x9e\xfc\x6b\x55\x1f\x46\x67\xd9\x83"
+ }, {
+ 16,
+ "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a",
+ 16,
+ "\x28\xa7\x02\x3f\x45\x2e\x8f\x82\xbd\x4b\xf2\x8d\x8c\x37\xc3\x5c"
+ }, {
+ 40,
+ "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
+ "\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf\x8e\x51"
+ "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11",
+ 16,
+ "\xaa\xf3\xd8\xf1\xde\x56\x40\xc2\x32\xf5\xb1\x69\xb9\xc9\x11\xe6"
+ }, {
+ 64,
+ "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
+ "\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf\x8e\x51"
+ "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11\xe5\xfb\xc1\x19\x1a\x0a\x52\xef"
+ "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17\xad\x2b\x41\x7b\xe6\x6c\x37\x10",
+ 16,
+ "\xe1\x99\x21\x90\x54\x9f\x6e\xd5\x69\x6a\x2c\x05\x6c\x31\x54\x10"
+ } } },
+ { GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_CMAC,
+ 24,
+ "\x8a\xa8\x3b\xf8\xcb\xda\x10\x62\x0b\xc1\xbf\x19\xfb\xb6\xcd\x58"
+ "\xbc\x31\x3d\x4a\x37\x1c\xa8\xb5",
+ { {
+ 0,
+ "",
+ 8,
+ "\xb7\xa6\x88\xe1\x22\xff\xaf\x95"
+ }, {
+ 8,
+ "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96",
+ 8,
+ "\x8e\x8f\x29\x31\x36\x28\x37\x97"
+ }, {
+ 20,
+ "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
+ "\xae\x2d\x8a\x57",
+ 8,
+ "\x74\x3d\xdb\xe0\xce\x2d\xc2\xed"
+ }, {
+ 32,
+ "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
+ "\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf\x8e\x51",
+ 8,
+ "\x33\xe6\xb1\x09\x24\x00\xea\xe5"
+ } } },
+ { GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_CMAC,
+ 24,
+ "\x4c\xf1\x51\x34\xa2\x85\x0d\xd5\x8a\x3d\x10\xba\x80\x57\x0d\x38"
+ "\x4c\xf1\x51\x34\xa2\x85\x0d\xd5",
+ { {
+ 0,
+ "",
+ 8,
+ "\xbd\x2e\xbf\x9a\x3b\xa0\x03\x61"
+ }, {
+ 8,
+ "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96",
+ 8,
+ "\x4f\xf2\xab\x81\x3c\x53\xce\x83"
+ }, {
+ 20,
+ "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
+ "\xae\x2d\x8a\x57",
+ 8,
+ "\x62\xdd\x1b\x47\x19\x02\xbd\x4e"
+ }, {
+ 32,
+ "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
+ "\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf\x8e\x51",
+ 8,
+ "\x31\xb1\xe4\x31\xda\xbc\x4e\xb8"
+ } } },
+ /* CMAC Camellia test vectors from
+ http://tools.ietf.org/html/draft-kato-ipsec-camellia-cmac96and128-05 */
+ { GCRY_CIPHER_CAMELLIA128, GCRY_CIPHER_MODE_CMAC,
+ 16,
+ "\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c",
+ { {
+ 0,
+ "",
+ 16,
+ "\xba\x92\x57\x82\xaa\xa1\xf5\xd9\xa0\x0f\x89\x64\x80\x94\xfc\x71"
+ }, {
+ 16,
+ "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a",
+ 16,
+ "\x6d\x96\x28\x54\xa3\xb9\xfd\xa5\x6d\x7d\x45\xa9\x5e\xe1\x79\x93"
+ }, {
+ 40,
+ "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
+ "\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf\x8e\x51"
+ "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11",
+ 16,
+ "\x5c\x18\xd1\x19\xcc\xd6\x76\x61\x44\xac\x18\x66\x13\x1d\x9f\x22"
+ }, {
+ 64,
+ "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
+ "\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf\x8e\x51"
+ "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11\xe5\xfb\xc1\x19\x1a\x0a\x52\xef"
+ "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17\xad\x2b\x41\x7b\xe6\x6c\x37\x10",
+ 16,
+ "\xc2\x69\x9a\x6e\xba\x55\xce\x9d\x93\x9a\x8a\x4e\x19\x46\x6e\xe9"
+ } } }
+ };
+
+ gcry_cipher_hd_t hda;
+ unsigned char out[MAX_DATA_LEN];
+ int i, j;
+ gcry_error_t err = 0;
+
+ if (verbose)
+ fprintf (stderr, " Starting cipher MAC checks.\n");
+
+ for (i = 0; i < sizeof (tv) / sizeof (tv[0]); i++)
+ {
+ if (verbose)
+ fprintf (stderr, " checking MAC mode %d for %s [%i]\n",
+ tv[i].mode, gcry_cipher_algo_name (tv[i].algo), tv[i].algo);
+
+ err = gcry_cipher_open (&hda, tv[i].algo, tv[i].mode, 0);
+ if (err)
+ {
+ fail ("MAC, gcry_cipher_open for MAC mode failed: %s\n",
+ gpg_strerror (err));
+ continue;
+ }
+
+ err = gcry_cipher_setkey (hda, tv[i].key, tv[i].klen);
+ if (err)
+ {
+ fail ("MAC, gcry_cipher_setkey failed: %s\n", gpg_strerror (err));
+ goto next;
+ }
+
+ for (j = 0; tv[i].data[j].dlen > 0; j++)
+ {
+ err = gcry_cipher_reset (hda);
+ if (err)
+ {
+ fail ("MAC, gcry_cipher_reset failed: %s\n",
+ gpg_strerror (err));
+ goto next;
+ }
+
+ err =
+ gcry_cipher_authenticate (hda, tv[i].data[j].data,
+ tv[i].data[j].dlen);
+ if (err)
+ {
+ fail ("MAC, gcry_cipher_authenticate failed: %s\n",
+ gpg_strerror (err));
+ goto next;
+ }
+
+ err =
+ gcry_cipher_checktag (hda, tv[i].data[j].tag, tv[i].data[j].tlen);
+ if (gpg_err_code (err) == GPG_ERR_CHECKSUM)
+ fail ("MAC, checktag mismatch entry %d:%d (checktag)\n", i, j);
+
+ err = gcry_cipher_reset (hda);
+ if (err)
+ {
+ fail ("MAC, gcry_cipher_reset failed: %s\n",
+ gpg_strerror (err));
+ goto next;
+ }
+
+ err =
+ gcry_cipher_authenticate (hda, tv[i].data[j].data,
+ tv[i].data[j].dlen);
+ if (err)
+ {
+ fail ("MAC, gcry_cipher_authenticate failed: %s\n",
+ gpg_strerror (err));
+ goto next;
+ }
+
+ err = gcry_cipher_gettag (hda, out, tv[i].data[j].tlen);
+ if (err)
+ {
+ fail ("MAC, gcry_cipher_gettag failed: %s\n",
+ gpg_strerror (err));
+ goto next;
+ }
+
+ if (memcmp (tv[i].data[j].tag, out, tv[i].data[j].tlen))
+ fail ("MAC, gettag mismatch entry %d:%d (gettag)\n", i, j);
+ }
+ next:
+ gcry_cipher_close (hda);
+ }
+
+ if (verbose)
+ fprintf (stderr, " Completed cipher MAC checks.\n");
+}
+
+
+static void
check_stream_cipher (void)
{
struct tv
@@ -3226,6 +3512,7 @@ check_cipher_modes(void)
check_cfb_cipher ();
check_ofb_cipher ();
check_ccm_cipher ();
+ check_mac_cipher ();
check_stream_cipher ();
check_stream_cipher_large_block ();
diff --git a/tests/bench-slope.c b/tests/bench-slope.c
index 5687bf1..e821618 100644
--- a/tests/bench-slope.c
+++ b/tests/bench-slope.c
@@ -610,6 +610,41 @@ bench_decrypt_do_bench (struct bench_obj *obj, void *buf, size_t buflen)
}
}
+static void
+bench_authenticate_do_bench (struct bench_obj *obj, void *buf, size_t buflen)
+{
+ gcry_cipher_hd_t hd = obj->priv;
+ char tag[8];
+ int err;
+
+ err = gcry_cipher_reset (hd);
+ if (err)
+ {
+ fprintf (stderr, PGM ": gcry_cipher_reset failed: %s\n",
+ gpg_strerror (err));
+ gcry_cipher_close (hd);
+ exit (1);
+ }
+
+ 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_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 encrypt_ops = {
&bench_encrypt_init,
&bench_encrypt_free,
@@ -622,6 +657,12 @@ static struct bench_ops decrypt_ops = {
&bench_decrypt_do_bench
};
+static struct bench_ops authenticate_ops = {
+ &bench_encrypt_init,
+ &bench_encrypt_free,
+ &bench_authenticate_do_bench
+};
+
static void
@@ -802,6 +843,7 @@ 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_CMAC, "CMAC auth", &authenticate_ops},
{0},
};
More information about the Gcrypt-devel
mailing list