On Wed, 2018-01-10 at 11:24 +0100, Niels Möller wrote: > Nikos Mavrogiannopoulos <[email protected]> writes: > > > The attached patch brings support for AES-128-CMAC. The code is > > based > > on the samba code. > > Nice. I think I'll have to read up a bit to understand what it's > doing. > > > The rshift and lshift functions come from the AES > > implementation bundled with samba. > > These to just a single bit right or left shift of a 16-byte block? To > me > it seems odd to do that with a table lookup for each byte.
Indeed, and I don't see any obvious benefit of that code. I've replaced it with a simpler version. > May be natural as functions operating on nettle_block16, and if > platform > endianness is right, could do the shifts in units of unsigned long or > uint64_t. Shift and mask on 64-bit values may be preferable also if > the endiannness is wrong. I've now used the nettle_block16 to ensure values are aligned. I didn't try to optimize the shift as it is only used on set_key which doesn't really affect performance. regards, Nikos
From 7ca5eb00a2a3d3b07f8bec2172975d5a4cb58cc0 Mon Sep 17 00:00:00 2001 From: Nikos Mavrogiannopoulos <[email protected]> Date: Wed, 10 Jan 2018 09:29:17 +0100 Subject: [PATCH] Added support for CMAC That adds support for CMAC as a generic framework for 128-bit block and key ciphers, as well as API for AES-128-CMAC. Signed-off-by: Nikos Mavrogiannopoulos <[email protected]> --- Makefile.in | 3 + cmac-aes128.c | 60 ++++++++++++++++++ cmac.c | 150 ++++++++++++++++++++++++++++++++++++++++++++ cmac.h | 117 ++++++++++++++++++++++++++++++++++ examples/nettle-benchmark.c | 24 ++++++- testsuite/.test-rules.make | 3 + testsuite/Makefile.in | 1 + testsuite/cmac-test.c | 57 +++++++++++++++++ 8 files changed, 414 insertions(+), 1 deletion(-) create mode 100644 cmac-aes128.c create mode 100644 cmac.c create mode 100644 cmac.h create mode 100644 testsuite/cmac-test.c diff --git a/Makefile.in b/Makefile.in index 6a0c13ec..8d6219b6 100644 --- a/Makefile.in +++ b/Makefile.in @@ -103,6 +103,7 @@ nettle_SOURCES = aes-decrypt-internal.c aes-decrypt.c \ gcm-aes256.c gcm-aes256-meta.c \ gcm-camellia128.c gcm-camellia128-meta.c \ gcm-camellia256.c gcm-camellia256-meta.c \ + cmac.c cmac-aes128.c \ gosthash94.c gosthash94-meta.c \ hmac.c hmac-md5.c hmac-ripemd160.c hmac-sha1.c \ hmac-sha224.c hmac-sha256.c hmac-sha384.c hmac-sha512.c \ @@ -195,6 +196,7 @@ HEADERS = aes.h arcfour.h arctwo.h asn1.h blowfish.h \ gcm.h gosthash94.h hmac.h \ knuth-lfib.h hkdf.h \ macros.h \ + cmac.h \ md2.h md4.h \ md5.h md5-compat.h \ memops.h memxor.h \ @@ -226,6 +228,7 @@ DISTFILES = $(SOURCES) $(HEADERS) getopt.h getopt_int.h \ nettle.pc.in hogweed.pc.in \ $(des_headers) descore.README \ aes-internal.h camellia-internal.h serpent-internal.h \ + cmac-internal.h \ cast128_sboxes.h desinfo.h desCode.h \ memxor-internal.h nettle-internal.h nettle-write.h \ gmp-glue.h ecc-internal.h fat-setup.h \ diff --git a/cmac-aes128.c b/cmac-aes128.c new file mode 100644 index 00000000..e53e114e --- /dev/null +++ b/cmac-aes128.c @@ -0,0 +1,60 @@ +/* cmac-aes128.c + + CMAC using AES128 as the underlying cipher. + + Copyright (C) 2017 Red Hat, Inc. + + This file is part of GNU Nettle. + + GNU Nettle is free software: you can redistribute it and/or + modify it under the terms of either: + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your + option) any later version. + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + or both in parallel, as here. + + GNU Nettle 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 + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see http://www.gnu.org/licenses/. +*/ + +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#include <assert.h> + +#include "cmac.h" + +void +cmac_aes128_set_key(struct cmac_aes128_ctx *ctx, const uint8_t *key) +{ + CMAC128_SET_KEY(ctx, aes128_set_encrypt_key, aes128_encrypt, key); +} + +void +cmac_aes128_update (struct cmac_aes128_ctx *ctx, + size_t length, const uint8_t *data) +{ + CMAC128_UPDATE (ctx, aes128_encrypt, length, data); +} + +void +cmac_aes128_digest(struct cmac_aes128_ctx *ctx, + size_t length, uint8_t *digest) +{ + CMAC128_DIGEST(ctx, aes128_encrypt, length, digest); +} diff --git a/cmac.c b/cmac.c new file mode 100644 index 00000000..b4886808 --- /dev/null +++ b/cmac.c @@ -0,0 +1,150 @@ +/* + AES-CMAC-128 (rfc 4493) + Copyright (C) Stefan Metzmacher 2012 + Copyright (C) Jeremy Allison 2012 + Copyright (C) Michael Adam 2012 + Copyright (C) 2017, Red Hat Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include "cmac.h" + +#include "memxor.h" +#include "nettle-internal.h" +#include "macros.h" + +static const uint8_t const_Zero[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const uint8_t const_Rb[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87 +}; + +static inline void block_lshift(const union nettle_block16 *in, + union nettle_block16 *out) +{ + uint64_t b1 = READ_UINT64(in->b); + uint64_t b2 = READ_UINT64(in->b+8); + unsigned overflow = b2 & 0x8000000000000000; + + b1 <<= 1; + b2 <<= 1; + + if (overflow) + b1 |= 0x01; + + WRITE_UINT64(out->b, b1); + WRITE_UINT64(out->b+8, b2); + +} + +void cmac128_set_key(struct cmac128 *ctx, void *key, + nettle_cipher_func *encrypt) +{ + memset(ctx, 0, sizeof(*ctx)); + + /* step 1 - generate subkeys k1 and k2 */ + encrypt(key, 16, ctx->L.b, const_Zero); + + block_lshift(ctx->L.b, ctx->K1.b); + if (ctx->L.b[0] & 0x80) { + memxor(ctx->K1.b, const_Rb, 16); + } + + block_lshift(ctx->K1.b, ctx->K2.b); + if (ctx->K1.b[0] & 0x80) { + memxor(ctx->K2.b, const_Rb, 16); + } +} + +#define MIN(x,y) ((x)<(y)?(x):(y)) + +void cmac128_update(struct cmac128 *ctx, void *key, + nettle_cipher_func *encrypt, + size_t msg_len, const uint8_t *msg) +{ + /* + * check if we expand the block + */ + if (ctx->last_len < 16) { + size_t len = MIN(16 - ctx->last_len, msg_len); + memcpy(&ctx->last.b[ctx->last_len], msg, len); + msg += len; + msg_len -= len; + ctx->last_len += len; + } + + if (msg_len == 0) { + /* if it is still the last block, we are done */ + return; + } + + /* + * now checksum everything but the last block + */ + memxor3(ctx->Y.b, ctx->X.b, ctx->last.b, 16); + encrypt(key, 16, ctx->X.b, ctx->Y.b); + + while (msg_len > 16) { + memxor3(ctx->Y.b, ctx->X.b, msg, 16); + encrypt(key, 16, ctx->X.b, ctx->Y.b); + msg += 16; + msg_len -= 16; + } + + /* + * copy the last block, it will be processed in + * cmac128_digest(). + */ + memset(ctx->last.b, 0, sizeof(ctx->last.b)); + memcpy(ctx->last.b, msg, msg_len); + ctx->last_len = msg_len; +} + +void cmac128_digest(struct cmac128 *ctx, void *key, + nettle_cipher_func *encrypt, + unsigned length, + uint8_t *out) +{ + uint8_t tmp[16]; + + if (ctx->last_len < 16) { + ctx->last.b[ctx->last_len] = 0x80; + memxor3(tmp, ctx->last.b, ctx->K2.b, 16); + } else { + memxor3(tmp, ctx->last.b, ctx->K1.b, 16); + } + + memxor3(ctx->Y.b, tmp, ctx->X.b, 16); + + assert(length <= 16); + if (length == 16) { + encrypt(key, length, out, ctx->Y.b); + } else { + encrypt(key, length, tmp, ctx->Y.b); + memcpy(out, tmp, length); + } +} diff --git a/cmac.h b/cmac.h new file mode 100644 index 00000000..5f0553b7 --- /dev/null +++ b/cmac.h @@ -0,0 +1,117 @@ +/* cmac.h + + CMAC mode, as specified in RFC4493 + + Copyright (C) 2017 Red Hat, Inc. + + Contributed by Nikos Mavrogiannopoulos + + This file is part of GNU Nettle. + + GNU Nettle is free software: you can redistribute it and/or + modify it under the terms of either: + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your + option) any later version. + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + or both in parallel, as here. + + GNU Nettle 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 + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see http://www.gnu.org/licenses/. +*/ + +#ifndef NETTLE_CMAC_H_INCLUDED +#define NETTLE_CMAC_H_INCLUDED + +#include "aes.h" +#include "nettle-types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define cmac128_set_key nettle_cmac128_set_key +#define cmac128_update nettle_cmac128_update +#define cmac128_digest nettle_cmac128_digest +#define cmac_aes128_set_key nettle_cmac_aes128_set_key +#define cmac_aes128_update nettle_cmac_aes128_update +#define cmac_aes128_digest nettle_cmac_aes128_digest + +struct cmac128 { + union nettle_block16 K1; + union nettle_block16 K2; + + union nettle_block16 L; + union nettle_block16 X; + union nettle_block16 Y; + + union nettle_block16 last; + size_t last_len; +}; + +void cmac128_set_key(struct cmac128 *ctx, void *key, + nettle_cipher_func *encrypt); +void cmac128_update(struct cmac128 *ctx, void *key, + nettle_cipher_func *encrypt, + size_t msg_len, const uint8_t *msg); +void cmac128_digest(struct cmac128 *ctx, void *key, + nettle_cipher_func *encrypt, + unsigned length, + uint8_t *out); + + +#define CMAC128_CTX(type) \ + { struct cmac128 data; type cipher; } + +/* NOTE: Avoid using NULL, as we don't include anything defining it. */ +#define CMAC128_SET_KEY(ctx, set_key, encrypt, cmac_key) \ + do { \ + (set_key)(&(ctx)->cipher, (cmac_key)); \ + if (0) (encrypt)(&(ctx)->cipher, ~(size_t) 0, \ + (uint8_t *) 0, (const uint8_t *) 0); \ + cmac128_set_key(&(ctx)->data, &(ctx)->cipher, \ + (nettle_cipher_func *) (encrypt)); \ + } while (0) + +#define CMAC128_UPDATE(ctx, encrypt, length, data) \ + cmac128_update(&(ctx)->data, &(ctx)->cipher, \ + (nettle_cipher_func *)encrypt, (length), (data)) + +#define CMAC128_DIGEST(ctx, encrypt, length, digest) \ + (0 ? (encrypt)(&(ctx)->cipher, ~(size_t) 0, \ + (uint8_t *) 0, (const uint8_t *) 0) \ + : cmac128_digest(&(ctx)->data, &(ctx)->cipher, \ + (nettle_cipher_func *) (encrypt), \ + (length), (digest))) + +struct cmac_aes128_ctx CMAC128_CTX(struct aes128_ctx); + +void +cmac_aes128_set_key(struct cmac_aes128_ctx *ctx, const uint8_t *key); + +void +cmac_aes128_update(struct cmac_aes128_ctx *ctx, + size_t length, const uint8_t *data); + +void +cmac_aes128_digest(struct cmac_aes128_ctx *ctx, + size_t length, uint8_t *digest); + +#ifdef __cplusplus +} +#endif + +#endif /* CMAC_H_INCLUDED */ diff --git a/examples/nettle-benchmark.c b/examples/nettle-benchmark.c index b73196b0..b9cef924 100644 --- a/examples/nettle-benchmark.c +++ b/examples/nettle-benchmark.c @@ -64,6 +64,7 @@ #include "sha3.h" #include "twofish.h" #include "umac.h" +#include "cmac.h" #include "poly1305.h" #include "nettle-meta.h" @@ -403,7 +404,7 @@ time_umac(void) struct umac64_ctx ctx64; struct umac96_ctx ctx96; struct umac128_ctx ctx128; - + uint8_t key[16]; umac32_set_key (&ctx32, key); @@ -439,6 +440,24 @@ time_umac(void) time_function(bench_hash, &info)); } +static void +time_cmac(void) +{ + static uint8_t data[BENCH_BLOCK]; + struct bench_hash_info info; + struct cmac_aes128_ctx ctx; + + uint8_t key[16]; + + cmac_aes128_set_key (&ctx, key); + info.ctx = &ctx; + info.update = (nettle_hash_update_func *) cmac_aes128_update; + info.data = data; + + display("cmac-aes128", "update", AES_BLOCK_SIZE, + time_function(bench_hash, &info)); +} + static void time_poly1305_aes(void) { @@ -843,6 +862,9 @@ main(int argc, char **argv) if (!alg || strstr ("umac", alg)) time_umac(); + if (!alg || strstr ("cmac", alg)) + time_cmac(); + if (!alg || strstr ("poly1305-aes", alg)) time_poly1305_aes(); diff --git a/testsuite/.test-rules.make b/testsuite/.test-rules.make index 1f780310..c7e04cb4 100644 --- a/testsuite/.test-rules.make +++ b/testsuite/.test-rules.make @@ -130,6 +130,9 @@ eax-test$(EXEEXT): eax-test.$(OBJEXT) ccm-test$(EXEEXT): ccm-test.$(OBJEXT) $(LINK) ccm-test.$(OBJEXT) $(TEST_OBJS) -o ccm-test$(EXEEXT) +cmac-test$(EXEEXT): cmac-test.$(OBJEXT) + $(LINK) cmac-test.$(OBJEXT) $(TEST_OBJS) -o cmac-test$(EXEEXT) + poly1305-test$(EXEEXT): poly1305-test.$(OBJEXT) $(LINK) poly1305-test.$(OBJEXT) $(TEST_OBJS) -o poly1305-test$(EXEEXT) diff --git a/testsuite/Makefile.in b/testsuite/Makefile.in index 3117d66d..dd1ecf41 100644 --- a/testsuite/Makefile.in +++ b/testsuite/Makefile.in @@ -26,6 +26,7 @@ TS_NETTLE_SOURCES = aes-test.c arcfour-test.c arctwo-test.c \ serpent-test.c twofish-test.c version-test.c \ knuth-lfib-test.c \ cbc-test.c cfb-test.c ctr-test.c gcm-test.c eax-test.c ccm-test.c \ + cmac-test.c \ poly1305-test.c chacha-poly1305-test.c \ hmac-test.c umac-test.c \ meta-hash-test.c meta-cipher-test.c\ diff --git a/testsuite/cmac-test.c b/testsuite/cmac-test.c new file mode 100644 index 00000000..45cad3c2 --- /dev/null +++ b/testsuite/cmac-test.c @@ -0,0 +1,57 @@ +#include "testutils.h" +#include "nettle-internal.h" +#include "cmac.h" + +static void +test_cmac_hash (const struct tstring *key, const struct tstring *msg, const struct tstring *ref) +{ + struct cmac_aes128_ctx ctx; + uint8_t digest[16]; + + ASSERT (ref->length == sizeof(digest)); + ASSERT (key->length == 16); + cmac_aes128_set_key (&ctx, key->data); + cmac_aes128_update (&ctx, msg->length, msg->data); + cmac_aes128_digest (&ctx, sizeof(digest), digest); + if (!MEMEQ (ref->length, ref->data, digest)) + { + fprintf (stderr, "cmac_hash failed, msg: "); + print_hex (msg->length, msg->data); + fprintf(stderr, "Output:"); + print_hex (16, digest); + fprintf(stderr, "Expected:"); + tstring_print_hex(ref); + fprintf(stderr, "\n"); + FAIL(); + } +} + +void +test_main(void) +{ + /* + * CMAC-AES Test Vectors from RFC4493. + */ + + test_cmac_hash (SHEX("2b7e151628aed2a6abf7158809cf4f3c"), + SDATA(""), + SHEX("bb1d6929e95937287fa37d129b756746")); + + test_cmac_hash (SHEX("2b7e151628aed2a6abf7158809cf4f3c"), + SHEX("6bc1bee22e409f96e93d7e117393172a"), + SHEX("070a16b46b4d4144f79bdd9dd04a287c")); + + test_cmac_hash (SHEX("2b7e151628aed2a6abf7158809cf4f3c"), + SHEX("6bc1bee22e409f96e93d7e117393172a" + "ae2d8a571e03ac9c9eb76fac45af8e51" + "30c81c46a35ce411"), + SHEX("dfa66747de9ae63030ca32611497c827")); + + test_cmac_hash (SHEX("2b7e151628aed2a6abf7158809cf4f3c"), + SHEX("6bc1bee22e409f96e93d7e117393172a" + "ae2d8a571e03ac9c9eb76fac45af8e51" + "30c81c46a35ce411e5fbc1191a0a52ef" + "f69f2445df4f9b17ad2b417be66c3710"), + SHEX("51f0bebf7e3b9d92fc49741779363cfe")); +} + -- 2.14.3
_______________________________________________ nettle-bugs mailing list [email protected] http://lists.lysator.liu.se/mailman/listinfo/nettle-bugs
