diff options
author | Jussi Kivilinna <jussi.kivilinna@iki.fi> | 2013-11-14 14:10:27 +0200 |
---|---|---|
committer | Jussi Kivilinna <jussi.kivilinna@iki.fi> | 2013-11-19 19:18:56 +0200 |
commit | b49cd64aaaff2e5488a84665362ef7150683226c (patch) | |
tree | 08a9aa2bd510d0231fa63ca481b03c5f42457aee | |
parent | fcd6da37d55f248d3558ee0ff385b41b866e7ded (diff) | |
download | libgcrypt-b49cd64aaaff2e5488a84665362ef7150683226c.tar.gz |
Add CMAC (Cipher-based MAC) to MAC API
* cipher/Makefile.am: Add 'cipher-cmac.c' and 'mac-cmac.c'.
* cipher/cipher-cmac.c: New.
* cipher/cipher-internal.h (gcry_cipher_handle.u_mode): Add 'cmac'.
* cipher/cipher.c (gcry_cipher_open): Rename to...
(_gcry_cipher_open_internal): ...this and add CMAC.
(gcry_cipher_open): New wrapper that disallows use of internal
modes (CMAC) from outside.
(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' and do not clear subkeys in
'u_mode' in CMAC mode.
* cipher/mac-cmac.c: New.
* cipher/mac-internal.h: Add CMAC support and algorithms.
* cipher/mac.c: Add CMAC algorithms.
* doc/gcrypt.texi: Add documentation for CMAC.
* src/cipher.h (gcry_cipher_internal_modes): New.
(_gcry_cipher_open_internal, _gcry_cipher_cmac_authenticate)
(_gcry_cipher_cmac_get_tag, _gcry_cipher_cmac_check_tag)
(_gcry_cipher_cmac_set_subkeys): New prototypes.
* src/gcrypt.h.in (gcry_mac_algos): Add CMAC algorithms.
* tests/basic.c (check_mac): Add CMAC test vectors.
--
Patch adds CMAC (Cipher-based MAC) as defined in RFC 4493 and NIST
Special Publication 800-38B.
Internally CMAC is added to cipher module, but is available to outside
only through MAC API.
[v2]:
- Add documentation.
[v3]:
- CMAC algorithm ids start from 201.
- Coding style fixes.
Signed-off-by: Jussi Kivilinna <jussi.kivilinna@iki.fi>
-rw-r--r-- | cipher/Makefile.am | 4 | ||||
-rw-r--r-- | cipher/cipher-cmac.c | 238 | ||||
-rw-r--r-- | cipher/cipher-internal.h | 9 | ||||
-rw-r--r-- | cipher/cipher.c | 68 | ||||
-rw-r--r-- | cipher/mac-cmac.c | 226 | ||||
-rw-r--r-- | cipher/mac-internal.h | 42 | ||||
-rw-r--r-- | cipher/mac.c | 33 | ||||
-rw-r--r-- | doc/gcrypt.texi | 48 | ||||
-rw-r--r-- | src/cipher.h | 24 | ||||
-rw-r--r-- | src/gcrypt.h.in | 14 | ||||
-rw-r--r-- | tests/basic.c | 141 |
11 files changed, 841 insertions, 6 deletions
diff --git a/cipher/Makefile.am b/cipher/Makefile.am index 1d54b078..2fd98227 100644 --- a/cipher/Makefile.am +++ b/cipher/Makefile.am @@ -40,12 +40,12 @@ 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 \ mac.c mac-internal.h \ -mac-hmac.c \ +mac-hmac.c mac-cmac.c \ kdf.c kdf-internal.h \ hmac-tests.c \ bithelp.h \ diff --git a/cipher/cipher-cmac.c b/cipher/cipher-cmac.c new file mode 100644 index 00000000..25d5db26 --- /dev/null +++ b/cipher/cipher-cmac.c @@ -0,0 +1,238 @@ +/* cmac.c - CMAC, Cipher-based MAC. + * Copyright © 2013 Jussi Kivilinna <jussi.kivilinna@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_mode.cmac.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]; + } + + 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_mode.cmac.subkeys[0]; /* K1 */ + else + { + subkey = c->u_mode.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 + { + return buf_eq_const (tag, c->u_iv.iv, taglen) ? + 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 db068232..44f43a40 100644 --- a/cipher/cipher-internal.h +++ b/cipher/cipher-internal.h @@ -136,8 +136,16 @@ 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. */ + + /* Subkeys for tag creation, not cleared by gcry_cipher_reset. */ + unsigned char subkeys[2][MAX_BLOCKSIZE]; + } cmac; } u_mode; + /* 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 +225,4 @@ gcry_err_code_t _gcry_cipher_ccm_check_tag const unsigned char *intag, size_t taglen); - #endif /*G10_CIPHER_INTERNAL_H*/ diff --git a/cipher/cipher.c b/cipher/cipher.c index 80aa7a74..b68703e3 100644 --- a/cipher/cipher.c +++ b/cipher/cipher.c @@ -344,6 +344,24 @@ gcry_error_t gcry_cipher_open (gcry_cipher_hd_t *handle, int algo, int mode, unsigned int flags) { + gcry_err_code_t err; + gcry_cipher_hd_t h = NULL; + + if (mode >= GCRY_CIPHER_MODE_INTERNAL) + err = GPG_ERR_INV_CIPHER_MODE; + else + err = _gcry_cipher_open_internal (&h, algo, mode, flags); + + *handle = err ? NULL : h; + + return gcry_error (err); +} + + +gcry_err_code_t +_gcry_cipher_open_internal (gcry_cipher_hd_t *handle, + int algo, int mode, unsigned int flags) +{ int secure = (flags & GCRY_CIPHER_SECURE); gcry_cipher_spec_t *spec; gcry_cipher_hd_t h = NULL; @@ -388,6 +406,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 +586,20 @@ cipher_setkey (gcry_cipher_hd_t c, byte *key, size_t 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 +642,10 @@ cipher_setiv (gcry_cipher_hd_t c, const byte *iv, size_t 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); @@ -620,8 +653,21 @@ cipher_reset (gcry_cipher_hd_t c) memset (c->u_iv.iv, 0, c->spec->blocksize); memset (c->lastiv, 0, c->spec->blocksize); 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; + + switch (c->mode) + { + case GCRY_CIPHER_MODE_CMAC: + /* Only clear 'tag' for cmac, keep subkeys. */ + c->u_mode.cmac.tag = 0; + break; + + default: + memset (&c->u_mode, 0, sizeof c->u_mode); + break; + } } @@ -717,6 +763,10 @@ cipher_encrypt (gcry_cipher_hd_t c, byte *outbuf, size_t 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); @@ -817,6 +867,10 @@ cipher_decrypt (gcry_cipher_hd_t c, byte *outbuf, size_t 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); @@ -942,6 +996,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; @@ -962,6 +1020,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; @@ -982,6 +1044,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/cipher/mac-cmac.c b/cipher/mac-cmac.c new file mode 100644 index 00000000..fddce394 --- /dev/null +++ b/cipher/mac-cmac.c @@ -0,0 +1,226 @@ +/* mac-cmac.c - CMAC glue for MAC API + * Copyright © 2013 Jussi Kivilinna <jussi.kivilinna@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 "./mac-internal.h" + + +static int +map_mac_algo_to_cipher (int mac_algo) +{ + switch (mac_algo) + { + default: + return GCRY_CIPHER_NONE; + case GCRY_MAC_CMAC_AES: + return GCRY_CIPHER_AES; + case GCRY_MAC_CMAC_3DES: + return GCRY_CIPHER_3DES; + case GCRY_MAC_CMAC_CAMELLIA: + return GCRY_CIPHER_CAMELLIA128; + case GCRY_MAC_CMAC_IDEA: + return GCRY_CIPHER_IDEA; + case GCRY_MAC_CMAC_CAST5: + return GCRY_CIPHER_CAST5; + case GCRY_MAC_CMAC_BLOWFISH: + return GCRY_CIPHER_BLOWFISH; + case GCRY_MAC_CMAC_TWOFISH: + return GCRY_CIPHER_TWOFISH; + case GCRY_MAC_CMAC_SERPENT: + return GCRY_CIPHER_SERPENT128; + case GCRY_MAC_CMAC_SEED: + return GCRY_CIPHER_SEED; + case GCRY_MAC_CMAC_RFC2268: + return GCRY_CIPHER_RFC2268_128; + case GCRY_MAC_CMAC_GOST28147: + return GCRY_CIPHER_GOST28147; + } +} + + +static gcry_err_code_t +cmac_open (gcry_mac_hd_t h) +{ + gcry_err_code_t err; + gcry_cipher_hd_t hd; + int secure = (h->magic == CTX_MAGIC_SECURE); + int cipher_algo; + unsigned int flags; + + cipher_algo = map_mac_algo_to_cipher (h->spec->algo); + flags = (secure ? GCRY_CIPHER_SECURE : 0); + + err = _gcry_cipher_open_internal (&hd, cipher_algo, GCRY_CIPHER_MODE_CMAC, + flags); + if (err) + return err; + + h->u.cmac.cipher_algo = cipher_algo; + h->u.cmac.ctx = hd; + h->u.cmac.blklen = _gcry_cipher_get_algo_blklen (cipher_algo); + return 0; +} + + +static void +cmac_close (gcry_mac_hd_t h) +{ + _gcry_cipher_close (h->u.cmac.ctx); + h->u.cmac.ctx = NULL; +} + + +static gcry_err_code_t +cmac_setkey (gcry_mac_hd_t h, const unsigned char *key, size_t keylen) +{ + return _gcry_cipher_setkey (h->u.cmac.ctx, key, keylen); +} + + +static gcry_err_code_t +cmac_reset (gcry_mac_hd_t h) +{ + return gcry_cipher_reset (h->u.cmac.ctx); +} + + +static gcry_err_code_t +cmac_write (gcry_mac_hd_t h, const unsigned char *buf, size_t buflen) +{ + return _gcry_cipher_cmac_authenticate (h->u.cmac.ctx, buf, buflen); +} + + +static gcry_err_code_t +cmac_read (gcry_mac_hd_t h, unsigned char *outbuf, size_t * outlen) +{ + if (*outlen > h->u.cmac.blklen) + *outlen = h->u.cmac.blklen; + return _gcry_cipher_cmac_get_tag (h->u.cmac.ctx, outbuf, *outlen); +} + + +static gcry_err_code_t +cmac_verify (gcry_mac_hd_t h, const unsigned char *buf, size_t buflen) +{ + return _gcry_cipher_cmac_check_tag (h->u.cmac.ctx, buf, buflen); +} + + +static unsigned int +cmac_get_maclen (int algo) +{ + return _gcry_cipher_get_algo_blklen (map_mac_algo_to_cipher (algo)); +} + + +static unsigned int +cmac_get_keylen (int algo) +{ + return _gcry_cipher_get_algo_keylen (map_mac_algo_to_cipher (algo)); +} + + +static gcry_mac_spec_ops_t cmac_ops = { + cmac_open, + cmac_close, + cmac_setkey, + NULL, + cmac_reset, + cmac_write, + cmac_read, + cmac_verify, + cmac_get_maclen, + cmac_get_keylen +}; + + +#if USE_BLOWFISH +gcry_mac_spec_t _gcry_mac_type_spec_cmac_blowfish = { + GCRY_MAC_CMAC_BLOWFISH, {0, 0}, "CMAC_BLOWFISH", + &cmac_ops +}; +#endif +#if USE_DES +gcry_mac_spec_t _gcry_mac_type_spec_cmac_tripledes = { + GCRY_MAC_CMAC_3DES, {0, 1}, "CMAC_3DES", + &cmac_ops +}; +#endif +#if USE_CAST5 +gcry_mac_spec_t _gcry_mac_type_spec_cmac_cast5 = { + GCRY_MAC_CMAC_CAST5, {0, 0}, "CMAC_CAST5", + &cmac_ops +}; +#endif +#if USE_AES +gcry_mac_spec_t _gcry_mac_type_spec_cmac_aes = { + GCRY_MAC_CMAC_AES, {0, 1}, "CMAC_AES", + &cmac_ops +}; +#endif +#if USE_TWOFISH +gcry_mac_spec_t _gcry_mac_type_spec_cmac_twofish = { + GCRY_MAC_CMAC_TWOFISH, {0, 0}, "CMAC_TWOFISH", + &cmac_ops +}; +#endif +#if USE_SERPENT +gcry_mac_spec_t _gcry_mac_type_spec_cmac_serpent = { + GCRY_MAC_CMAC_SERPENT, {0, 0}, "CMAC_SERPENT", + &cmac_ops +}; +#endif +#if USE_RFC2268 +gcry_mac_spec_t _gcry_mac_type_spec_cmac_rfc2268 = { + GCRY_MAC_CMAC_RFC2268, {0, 0}, "CMAC_RFC2268", + &cmac_ops +}; +#endif +#if USE_SEED +gcry_mac_spec_t _gcry_mac_type_spec_cmac_seed = { + GCRY_MAC_CMAC_SEED, {0, 0}, "CMAC_SEED", + &cmac_ops +}; +#endif +#if USE_CAMELLIA +gcry_mac_spec_t _gcry_mac_type_spec_cmac_camellia = { + GCRY_MAC_CMAC_CAMELLIA, {0, 0}, "CMAC_CAMELLIA", + &cmac_ops +}; +#endif +#ifdef USE_IDEA +gcry_mac_spec_t _gcry_mac_type_spec_cmac_idea = { + GCRY_MAC_CMAC_IDEA, {0, 0}, "CMAC_IDEA", + &cmac_ops +}; +#endif +#if USE_GOST28147 +gcry_mac_spec_t _gcry_mac_type_spec_cmac_gost28147 = { + GCRY_MAC_CMAC_GOST28147, {0, 0}, "CMAC_GOST28147", + &cmac_ops +}; +#endif diff --git a/cipher/mac-internal.h b/cipher/mac-internal.h index 9ebc0357..146b2180 100644 --- a/cipher/mac-internal.h +++ b/cipher/mac-internal.h @@ -98,6 +98,11 @@ struct gcry_mac_handle gcry_md_hd_t md_ctx; int md_algo; } hmac; + struct { + gcry_cipher_hd_t ctx; + int cipher_algo; + unsigned int blklen; + } cmac; } u; }; @@ -138,3 +143,40 @@ extern gcry_mac_spec_t _gcry_mac_type_spec_hmac_md5; #if USE_MD4 extern gcry_mac_spec_t _gcry_mac_type_spec_hmac_md4; #endif + +/* + * The CMAC algorithm specifications (mac-cmac.c). + */ +#if USE_BLOWFISH +extern gcry_mac_spec_t _gcry_mac_type_spec_cmac_blowfish; +#endif +#if USE_DES +extern gcry_mac_spec_t _gcry_mac_type_spec_cmac_tripledes; +#endif +#if USE_CAST5 +extern gcry_mac_spec_t _gcry_mac_type_spec_cmac_cast5; +#endif +#if USE_AES +extern gcry_mac_spec_t _gcry_mac_type_spec_cmac_aes; +#endif +#if USE_TWOFISH +extern gcry_mac_spec_t _gcry_mac_type_spec_cmac_twofish; +#endif +#if USE_SERPENT +extern gcry_mac_spec_t _gcry_mac_type_spec_cmac_serpent; +#endif +#if USE_RFC2268 +extern gcry_mac_spec_t _gcry_mac_type_spec_cmac_rfc2268; +#endif +#if USE_SEED +extern gcry_mac_spec_t _gcry_mac_type_spec_cmac_seed; +#endif +#if USE_CAMELLIA +extern gcry_mac_spec_t _gcry_mac_type_spec_cmac_camellia; +#endif +#ifdef USE_IDEA +extern gcry_mac_spec_t _gcry_mac_type_spec_cmac_idea; +#endif +#if USE_GOST28147 +extern gcry_mac_spec_t _gcry_mac_type_spec_cmac_gost28147; +#endif diff --git a/cipher/mac.c b/cipher/mac.c index 9708b3a6..8d1dec05 100644 --- a/cipher/mac.c +++ b/cipher/mac.c @@ -63,6 +63,39 @@ static gcry_mac_spec_t *mac_list[] = { #if USE_MD4 &_gcry_mac_type_spec_hmac_md4, #endif +#if USE_BLOWFISH + &_gcry_mac_type_spec_cmac_blowfish, +#endif +#if USE_DES + &_gcry_mac_type_spec_cmac_tripledes, +#endif +#if USE_CAST5 + &_gcry_mac_type_spec_cmac_cast5, +#endif +#if USE_AES + &_gcry_mac_type_spec_cmac_aes, +#endif +#if USE_TWOFISH + &_gcry_mac_type_spec_cmac_twofish, +#endif +#if USE_SERPENT + &_gcry_mac_type_spec_cmac_serpent, +#endif +#if USE_RFC2268 + &_gcry_mac_type_spec_cmac_rfc2268, +#endif +#if USE_SEED + &_gcry_mac_type_spec_cmac_seed, +#endif +#if USE_CAMELLIA + &_gcry_mac_type_spec_cmac_camellia, +#endif +#ifdef USE_IDEA + &_gcry_mac_type_spec_cmac_idea, +#endif +#if USE_GOST28147 + &_gcry_mac_type_spec_cmac_gost28147, +#endif NULL, }; diff --git a/doc/gcrypt.texi b/doc/gcrypt.texi index b2041269..b8ee4946 100644 --- a/doc/gcrypt.texi +++ b/doc/gcrypt.texi @@ -3479,6 +3479,50 @@ algorithm described in GOST R 34.11-2012. This is HMAC message authentication algorithm based on the 512-bit hash algorithm described in GOST R 34.11-2012. +@item GCRY_MAC_CMAC_AES +This is CMAC (Cipher-based MAC) message authentication algorithm based on +the AES block cipher algorithm. + +@item GCRY_MAC_CMAC_3DES +This is CMAC message authentication algorithm based on the three-key EDE +Triple-DES block cipher algorithm. + +@item GCRY_MAC_CMAC_CAMELLIA +This is CMAC message authentication algorithm based on the Camellia block cipher +algorithm. + +@item GCRY_MAC_CMAC_CAST5 +This is CMAC message authentication algorithm based on the CAST128-5 +block cipher algorithm. + +@item GCRY_MAC_CMAC_BLOWFISH +This is CMAC message authentication algorithm based on the Blowfish +block cipher algorithm. + +@item GCRY_MAC_CMAC_TWOFISH +This is CMAC message authentication algorithm based on the Twofish +block cipher algorithm. + +@item GCRY_MAC_CMAC_SERPENT +This is CMAC message authentication algorithm based on the Serpent +block cipher algorithm. + +@item GCRY_MAC_CMAC_SEED +This is CMAC message authentication algorithm based on the SEED +block cipher algorithm. + +@item GCRY_MAC_CMAC_RFC2268 +This is CMAC message authentication algorithm based on the Ron's Cipher 2 +block cipher algorithm. + +@item GCRY_MAC_CMAC_IDEA +This is CMAC message authentication algorithm based on the IDEA +block cipher algorithm. + +@item GCRY_MAC_CMAC_GOST28147 +This is CMAC message authentication algorithm based on the GOST 28147-89 +block cipher algorithm. + @end table @c end table of MAC algorithms @@ -3518,7 +3562,9 @@ In order to use a handle for performing MAC algorithm operations, a @deftypefun gcry_error_t gcry_mac_setkey (gcry_mac_hd_t @var{h}, const void *@var{key}, size_t @var{keylen}) Set the MAC key to the value of @var{key} of length @var{keylen} bytes. With -HMAC algorithms, there is no restriction on the length of the key. +HMAC algorithms, there is no restriction on the length of the key. With CMAC +algorithms, the length of the key is restricted to those supported by the +underlying block cipher. @end deftypefun diff --git a/src/cipher.h b/src/cipher.h index 38d2a977..10bfe0c5 100644 --- a/src/cipher.h +++ b/src/cipher.h @@ -84,6 +84,30 @@ struct pk_encoding_ctx #include "cipher-proto.h" +/* The internal encryption modes. */ +enum gcry_cipher_internal_modes + { + GCRY_CIPHER_MODE_INTERNAL = 0x10000, + GCRY_CIPHER_MODE_CMAC = 0x10000 + 1 /* Cipher-based MAC. */ + }; + + +/*-- cipher.c --*/ +gcry_err_code_t _gcry_cipher_open_internal (gcry_cipher_hd_t *handle, + int algo, int mode, + unsigned int flags); + +/*-- 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); /*-- rmd160.c --*/ void _gcry_rmd160_hash_buffer (void *outbuf, diff --git a/src/gcrypt.h.in b/src/gcrypt.h.in index a9bc5ffb..3e8e72c2 100644 --- a/src/gcrypt.h.in +++ b/src/gcrypt.h.in @@ -1309,7 +1309,19 @@ enum gcry_mac_algos GCRY_MAC_HMAC_WHIRLPOOL = 110, GCRY_MAC_HMAC_GOSTR3411_94 = 111, GCRY_MAC_HMAC_STRIBOG256 = 112, - GCRY_MAC_HMAC_STRIBOG512 = 113 + GCRY_MAC_HMAC_STRIBOG512 = 113, + + GCRY_MAC_CMAC_AES = 201, + GCRY_MAC_CMAC_3DES = 202, + GCRY_MAC_CMAC_CAMELLIA = 203, + GCRY_MAC_CMAC_CAST5 = 204, + GCRY_MAC_CMAC_BLOWFISH = 205, + GCRY_MAC_CMAC_TWOFISH = 206, + GCRY_MAC_CMAC_SERPENT = 207, + GCRY_MAC_CMAC_SEED = 208, + GCRY_MAC_CMAC_RFC2268 = 209, + GCRY_MAC_CMAC_IDEA = 210, + GCRY_MAC_CMAC_GOST28147 = 211 }; /* Flags used with the open function. */ diff --git a/tests/basic.c b/tests/basic.c index c4530f9e..f9c64976 100644 --- a/tests/basic.c +++ b/tests/basic.c @@ -4416,6 +4416,147 @@ check_mac (void) "\xde\xbd\x71\xf8\x86\x72\x89\x86\x5d\xf5\xa3\x2d\x20\xcd\xc9\x44" "\xb6\x02\x2c\xac\x3c\x49\x82\xb1\x0d\x5e\xeb\x55\xc3\xe4\xde\x15" "\x13\x46\x76\xfb\x6d\xe0\x44\x60\x65\xc9\x74\x40\xfa\x8c\x6a\x58" }, + /* CMAC AES and DES test vectors from + http://web.archive.org/web/20130930212819/http://csrc.nist.gov/publica\ + tions/nistpubs/800-38B/Updated_CMAC_Examples.pdf */ + { GCRY_MAC_CMAC_AES, + "", + "\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c", + "\xbb\x1d\x69\x29\xe9\x59\x37\x28\x7f\xa3\x7d\x12\x9b\x75\x67\x46" }, + { GCRY_MAC_CMAC_AES, + "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a", + "\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c", + "\x07\x0a\x16\xb4\x6b\x4d\x41\x44\xf7\x9b\xdd\x9d\xd0\x4a\x28\x7c" }, + { GCRY_MAC_CMAC_AES, + "\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", + "\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c", + "\xdf\xa6\x67\x47\xde\x9a\xe6\x30\x30\xca\x32\x61\x14\x97\xc8\x27" }, + { GCRY_MAC_CMAC_AES, + "\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", + "\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c", + "\x51\xf0\xbe\xbf\x7e\x3b\x9d\x92\xfc\x49\x74\x17\x79\x36\x3c\xfe" }, + { GCRY_MAC_CMAC_AES, + "", + "\x8e\x73\xb0\xf7\xda\x0e\x64\x52\xc8\x10\xf3\x2b\x80\x90\x79\xe5" + "\x62\xf8\xea\xd2\x52\x2c\x6b\x7b", + "\xd1\x7d\xdf\x46\xad\xaa\xcd\xe5\x31\xca\xc4\x83\xde\x7a\x93\x67" }, + { GCRY_MAC_CMAC_AES, + "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a", + "\x8e\x73\xb0\xf7\xda\x0e\x64\x52\xc8\x10\xf3\x2b\x80\x90\x79\xe5" + "\x62\xf8\xea\xd2\x52\x2c\x6b\x7b", + "\x9e\x99\xa7\xbf\x31\xe7\x10\x90\x06\x62\xf6\x5e\x61\x7c\x51\x84" }, + { GCRY_MAC_CMAC_AES, + "\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", + "\x8e\x73\xb0\xf7\xda\x0e\x64\x52\xc8\x10\xf3\x2b\x80\x90\x79\xe5" + "\x62\xf8\xea\xd2\x52\x2c\x6b\x7b", + "\x8a\x1d\xe5\xbe\x2e\xb3\x1a\xad\x08\x9a\x82\xe6\xee\x90\x8b\x0e" }, + { GCRY_MAC_CMAC_AES, + "\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", + "\x8e\x73\xb0\xf7\xda\x0e\x64\x52\xc8\x10\xf3\x2b\x80\x90\x79\xe5" + "\x62\xf8\xea\xd2\x52\x2c\x6b\x7b", + "\xa1\xd5\xdf\x0e\xed\x79\x0f\x79\x4d\x77\x58\x96\x59\xf3\x9a\x11" }, + { GCRY_MAC_CMAC_AES, + "", + "\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", + "\x02\x89\x62\xf6\x1b\x7b\xf8\x9e\xfc\x6b\x55\x1f\x46\x67\xd9\x83" }, + { GCRY_MAC_CMAC_AES, + "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a", + "\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", + "\x28\xa7\x02\x3f\x45\x2e\x8f\x82\xbd\x4b\xf2\x8d\x8c\x37\xc3\x5c" }, + { GCRY_MAC_CMAC_AES, + "\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", + "\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", + "\xaa\xf3\xd8\xf1\xde\x56\x40\xc2\x32\xf5\xb1\x69\xb9\xc9\x11\xe6" }, + { GCRY_MAC_CMAC_AES, + "\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", + "\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", + "\xe1\x99\x21\x90\x54\x9f\x6e\xd5\x69\x6a\x2c\x05\x6c\x31\x54\x10" }, + { GCRY_MAC_CMAC_3DES, + "", + "\x8a\xa8\x3b\xf8\xcb\xda\x10\x62\x0b\xc1\xbf\x19\xfb\xb6\xcd\x58" + "\xbc\x31\x3d\x4a\x37\x1c\xa8\xb5", + "\xb7\xa6\x88\xe1\x22\xff\xaf\x95" }, + { GCRY_MAC_CMAC_3DES, + "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96", + "\x8a\xa8\x3b\xf8\xcb\xda\x10\x62\x0b\xc1\xbf\x19\xfb\xb6\xcd\x58" + "\xbc\x31\x3d\x4a\x37\x1c\xa8\xb5", + "\x8e\x8f\x29\x31\x36\x28\x37\x97" }, + { GCRY_MAC_CMAC_3DES, + "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a" + "\xae\x2d\x8a\x57", + "\x8a\xa8\x3b\xf8\xcb\xda\x10\x62\x0b\xc1\xbf\x19\xfb\xb6\xcd\x58" + "\xbc\x31\x3d\x4a\x37\x1c\xa8\xb5", + "\x74\x3d\xdb\xe0\xce\x2d\xc2\xed" }, + { GCRY_MAC_CMAC_3DES, + "\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", + "\x8a\xa8\x3b\xf8\xcb\xda\x10\x62\x0b\xc1\xbf\x19\xfb\xb6\xcd\x58" + "\xbc\x31\x3d\x4a\x37\x1c\xa8\xb5", + "\x33\xe6\xb1\x09\x24\x00\xea\xe5" }, + { GCRY_MAC_CMAC_3DES, + "", + "\x4c\xf1\x51\x34\xa2\x85\x0d\xd5\x8a\x3d\x10\xba\x80\x57\x0d\x38" + "\x4c\xf1\x51\x34\xa2\x85\x0d\xd5", + "\xbd\x2e\xbf\x9a\x3b\xa0\x03\x61" }, + { GCRY_MAC_CMAC_3DES, + "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96", + "\x4c\xf1\x51\x34\xa2\x85\x0d\xd5\x8a\x3d\x10\xba\x80\x57\x0d\x38" + "\x4c\xf1\x51\x34\xa2\x85\x0d\xd5", + "\x4f\xf2\xab\x81\x3c\x53\xce\x83" }, + { GCRY_MAC_CMAC_3DES, + "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a" + "\xae\x2d\x8a\x57", + "\x4c\xf1\x51\x34\xa2\x85\x0d\xd5\x8a\x3d\x10\xba\x80\x57\x0d\x38" + "\x4c\xf1\x51\x34\xa2\x85\x0d\xd5", + "\x62\xdd\x1b\x47\x19\x02\xbd\x4e" }, + { GCRY_MAC_CMAC_3DES, + "\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", + "\x4c\xf1\x51\x34\xa2\x85\x0d\xd5\x8a\x3d\x10\xba\x80\x57\x0d\x38" + "\x4c\xf1\x51\x34\xa2\x85\x0d\xd5", + "\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_MAC_CMAC_CAMELLIA, + "", + "\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c", + "\xba\x92\x57\x82\xaa\xa1\xf5\xd9\xa0\x0f\x89\x64\x80\x94\xfc\x71" }, + { GCRY_MAC_CMAC_CAMELLIA, + "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a", + "\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c", + "\x6d\x96\x28\x54\xa3\xb9\xfd\xa5\x6d\x7d\x45\xa9\x5e\xe1\x79\x93" }, + { GCRY_MAC_CMAC_CAMELLIA, + "\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", + "\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c", + "\x5c\x18\xd1\x19\xcc\xd6\x76\x61\x44\xac\x18\x66\x13\x1d\x9f\x22" }, + { GCRY_MAC_CMAC_CAMELLIA, + "\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", + "\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c", + "\xc2\x69\x9a\x6e\xba\x55\xce\x9d\x93\x9a\x8a\x4e\x19\x46\x6e\xe9" }, { 0 }, }; int i; |