For fun, I've tried implementing the EAX mode for "authenticated encryption with associated data". Patch below. It's pretty simple, the main file, eax.c, is only some 150 lines including comments.
Comments appreciated, particularly on the interface.
Some features suggested in the paper, http://www.cs.ucdavis.edu/~rogaway/papers/eax.pdf, and currently not implemented:
1. There's no interface for providing the nonce incrementally. I don't think that's needed. In any case, the nonce must be provided before any of the plaintext can be processed. The interface could be extended if there are any reasonable use-cases.
2. There's no interface for reusing the same associated data for multiple messages, which might be an important optimization in some cases. I don't quite like the suggestion that not providing any associated data should imply reuse of the previous message; it seems cleaner that not providing associated data means that the associated data is the empty string. So some additional functions may be needed to provide that feature (if it is deemed important enough).
For blocking, the convention is the same as for the CTR and GCM implementations: When the message (or associated data) is provided using multiple calls to _encrypt (or _decrypt or _update), the data size for each call, except the final one, *must* be a multiple of the block size.
Regards, /Niels
diff --git a/Makefile.in b/Makefile.in index ebc9357..e7eb2e0 100644 --- a/Makefile.in +++ b/Makefile.in @@ -74,8 +74,8 @@ nettle_SOURCES = aes-decrypt-internal.c aes-decrypt.c \ cast128.c cast128-meta.c \ blowfish.c \ cbc.c ctr.c gcm.c gcm-aes.c \ - des.c \ - des3.c des-compat.c \ + des.c des3.c des-compat.c \ + eax.c eax-aes.c \ hmac.c hmac-md5.c hmac-ripemd160.c hmac-sha1.c \ hmac-sha224.c hmac-sha256.c hmac-sha384.c hmac-sha512.c \ pbkdf2.c pbkdf2-hmac-sha1.c pbkdf2-hmac-sha256.c \ diff --git a/eax-aes.c b/eax-aes.c index e69de29..24a5560 100644 --- a/eax-aes.c +++ b/eax-aes.c @@ -0,0 +1,70 @@ +/* eax-aes.c + * + * EAX mode using AES as the underlying cipher. + */ + +/* nettle, low-level cryptographics library + * + * Copyright (C) 2011, 2013 Niels Möller + * + * The nettle library 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. + * + * The nettle library 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 the nettle library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02111-1301, USA. + */ + +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#include "eax.h" + +void +eax_aes_set_key(struct eax_aes_ctx *ctx, size_t length, const uint8_t *key) +{ + EAX_SET_KEY(ctx, aes_set_encrypt_key, aes_encrypt, length, key); +} + +void +eax_aes_set_nonce(struct eax_aes_ctx *ctx, + size_t length, const uint8_t *iv) +{ + EAX_SET_NONCE(ctx, aes_encrypt, length, iv); +} + +void +eax_aes_update(struct eax_aes_ctx *ctx, size_t length, const uint8_t *data) +{ + EAX_UPDATE(ctx, aes_encrypt, length, data); +} + +void +eax_aes_encrypt(struct eax_aes_ctx *ctx, + size_t length, uint8_t *dst, const uint8_t *src) +{ + EAX_ENCRYPT(ctx, aes_encrypt, length, dst, src); +} + +void +eax_aes_decrypt(struct eax_aes_ctx *ctx, + size_t length, uint8_t *dst, const uint8_t *src) +{ + EAX_DECRYPT(ctx, aes_encrypt, length, dst, src); +} + +void +eax_aes_digest(struct eax_aes_ctx *ctx, + size_t length, uint8_t *digest) +{ + EAX_DIGEST(ctx, aes_encrypt, length, digest); +} diff --git a/eax.c b/eax.c index e69de29..ec0dda9 100644 --- a/eax.c +++ b/eax.c @@ -0,0 +1,152 @@ +/* eax.c + * + * EAX mode, see http://www.cs.ucdavis.edu/~rogaway/papers/eax.pdf + */ + +/* nettle, low-level cryptographics library + * + * Copyright (C) 2013 Niels Möller + * + * The nettle library 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. + * + * The nettle library 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 the nettle library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02111-1301, USA. + */ + +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#include <assert.h> +#include <string.h> + +#include "eax.h" + +#include "ctr.h" +#include "memxor.h" + +static void +omac_init (uint8_t *state, unsigned t) +{ + memset (state, 0, EAX_BLOCK_SIZE - 1); + state[EAX_BLOCK_SIZE - 1] = t; +} + +static void +omac_update (uint8_t *state, const struct eax_key *key, + void *cipher, nettle_crypt_func *f, + size_t length, const uint8_t *data) +{ + for (; length >= EAX_BLOCK_SIZE; + length -= EAX_BLOCK_SIZE, data += EAX_BLOCK_SIZE) + { + f (cipher, EAX_BLOCK_SIZE, state, state); + memxor (state, data, EAX_BLOCK_SIZE); + } + if (length > 0) + { + /* Allowed only for the last call */ + f (cipher, EAX_BLOCK_SIZE, state, state); + memxor (state, data, length); + state[length] ^= 0x80; + /* XOR with (P ^ B), since the digest processing + * unconditionally XORs with B */ + memxor (state, key->pad_partial, EAX_BLOCK_SIZE); + } +} + +static void +omac_final (uint8_t *state, const struct eax_key *key, + void *cipher, nettle_crypt_func *f) +{ + memxor (state, key->pad_block, EAX_BLOCK_SIZE); + f (cipher, EAX_BLOCK_SIZE, state, state); +} + +/* Allows r == a */ +static void +gf2_double (uint8_t *r, const uint8_t *a) +{ + unsigned high = - (a[0] >> 7); + unsigned i; + /* Shift left */ + for (i = 0; i < EAX_BLOCK_SIZE - 1; i++) + r[i] = (a[i] << 1) + (a[i+1] >> 7); + + /* Wrap around for x^{128} = x^7 + x^2 + x + 1 */ + r[EAX_BLOCK_SIZE - 1] = (a[EAX_BLOCK_SIZE - 1] << 1) ^ (high & 0x87); +} + +void +eax_set_key (struct eax_key *key, void *cipher, nettle_crypt_func *f) +{ + static const uint8_t zero_block[EAX_BLOCK_SIZE]; + f (cipher, EAX_BLOCK_SIZE, key->pad_block, zero_block); + gf2_double (key->pad_block, key->pad_block); + gf2_double (key->pad_partial, key->pad_block); + memxor (key->pad_partial, key->pad_block, EAX_BLOCK_SIZE); +} + +void +eax_set_nonce (struct eax_ctx *eax, const struct eax_key *key, + void *cipher, nettle_crypt_func *f, + size_t nonce_length, const uint8_t *nonce) +{ + omac_init (eax->omac_nonce, 0); + omac_update (eax->omac_nonce, key, cipher, f, nonce_length, nonce); + omac_final (eax->omac_nonce, key, cipher, f); + memcpy (eax->ctr, eax->omac_nonce, EAX_BLOCK_SIZE); + + omac_init (eax->omac_data, 1); + omac_init (eax->omac_message, 2); +} + +void +eax_update (struct eax_ctx *eax, const struct eax_key *key, + void *cipher, nettle_crypt_func *f, + size_t data_length, const uint8_t *data) +{ + omac_update (eax->omac_data, key, cipher, f, data_length, data); +} + +void +eax_encrypt (struct eax_ctx *eax, const struct eax_key *key, + void *cipher, nettle_crypt_func *f, + size_t length, uint8_t *dst, const uint8_t *src) +{ + ctr_crypt (cipher, f, EAX_BLOCK_SIZE, eax->ctr, length, dst, src); + omac_update (eax->omac_message, key, cipher, f, length, dst); +} + +void +eax_decrypt (struct eax_ctx *eax, const struct eax_key *key, + void *cipher, nettle_crypt_func *f, + size_t length, uint8_t *dst, const uint8_t *src) +{ + omac_update (eax->omac_message, key, cipher, f, length, src); + ctr_crypt (cipher, f, EAX_BLOCK_SIZE, eax->ctr, length, dst, src); +} + +void +eax_digest (struct eax_ctx *eax, const struct eax_key *key, + void *cipher, nettle_crypt_func *f, + size_t length, uint8_t *digest) +{ + assert (length > 0); + assert (length <= EAX_BLOCK_SIZE); + omac_final (eax->omac_data, key, cipher, f); + omac_final (eax->omac_message, key, cipher, f); + + memxor (eax->omac_nonce, eax->omac_data, length); + memxor3 (digest, eax->omac_nonce, eax->omac_message, length); +} diff --git a/eax.h b/eax.h index e69de29..0177767 100644 --- a/eax.h +++ b/eax.h @@ -0,0 +1,169 @@ +/* eax.h + * + * EAX mode, see http://www.cs.ucdavis.edu/~rogaway/papers/eax.pdf + */ + +/* nettle, low-level cryptographics library + * + * Copyright (C) 2013 Niels Möller + * + * The nettle library 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. + * + * The nettle library 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 the nettle library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02111-1301, USA. + */ + +#ifndef NETTLE_EAX_H_INCLUDED +#define NETTLE_EAX_H_INCLUDED + +#include "aes.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Name mangling */ +#define eax_set_key nettle_eax_set_key +#define eax_set_nonce nettle_eax_set_nonce +#define eax_update nettle_eax_update +#define eax_encrypt nettle_eax_encrypt +#define eax_decrypt nettle_eax_decrypt +#define eax_digest nettle_eax_digest + +#define eax_aes_set_key nettle_eax_aes_set_key +#define eax_aes_set_nonce nettle_eax_aes_set_nonce +#define eax_aes_update nettle_eax_aes_update +#define eax_aes_encrypt nettle_eax_aes_encrypt +#define eax_aes_decrypt nettle_eax_aes_decrypt +#define eax_aes_digest nettle_eax_aes_digest + +/* Restricted to block ciphers with 128 bit block size. FIXME: Reflect + this in naming? */ + +#define EAX_BLOCK_SIZE 16 + +/* FIXME: Ensure nice alignment. See gcm.h, union gcm_block. */ + +/* Values independent of message and nonce */ +struct eax_key +{ + uint8_t pad_block[EAX_BLOCK_SIZE]; + uint8_t pad_partial[EAX_BLOCK_SIZE]; +}; + +struct eax_ctx +{ + uint8_t omac_nonce[EAX_BLOCK_SIZE]; + uint8_t omac_data[EAX_BLOCK_SIZE]; + uint8_t omac_message[EAX_BLOCK_SIZE]; + uint8_t ctr[EAX_BLOCK_SIZE]; +}; + +void +eax_set_key (struct eax_key *key, void *cipher, nettle_crypt_func *f); + +void +eax_set_nonce (struct eax_ctx *eax, const struct eax_key *key, + void *cipher, nettle_crypt_func *f, + size_t nonce_length, const uint8_t *nonce); + +void +eax_update (struct eax_ctx *eax, const struct eax_key *key, + void *cipher, nettle_crypt_func *f, + size_t data_length, const uint8_t *data); + +void +eax_encrypt (struct eax_ctx *eax, const struct eax_key *key, + void *cipher, nettle_crypt_func *f, + size_t length, uint8_t *dst, const uint8_t *src); + +void +eax_decrypt (struct eax_ctx *eax, const struct eax_key *key, + void *cipher, nettle_crypt_func *f, + size_t length, uint8_t *dst, const uint8_t *src); + +void +eax_digest (struct eax_ctx *eax, const struct eax_key *key, + void *cipher, nettle_crypt_func *f, + size_t length, uint8_t *digest); + +#define EAX_CTX(type) \ + { type cipher; struct eax_key key; struct eax_ctx eax; } + +#define EAX_SET_KEY(ctx, set_key, encrypt, length, data) \ + do { \ + (set_key)(&(ctx)->cipher, (length), (data)); \ + if (0) (encrypt) (&(ctx)->cipher, 0, (void *) 0, (void *) 0); \ + eax_set_key (&(ctx)->key, &(ctx)->cipher, (nettle_crypt_func *) encrypt); \ + } while (0) + +#define EAX_SET_NONCE(ctx, encrypt, length, nonce) \ + (0 ? (encrypt) (&(ctx)->cipher, 0, (void *) 0, (void *) 0) \ + : eax_set_nonce (&(ctx)->eax, &(ctx)->key, \ + &(ctx)->cipher, (nettle_crypt_func *) (encrypt), \ + (length), (nonce))) + +#define EAX_UPDATE(ctx, encrypt, length, data) \ + (0 ? (encrypt) (&(ctx)->cipher, 0, (void *) 0, (void *) 0) \ + : eax_update (&(ctx)->eax, &(ctx)->key, \ + &(ctx)->cipher, (nettle_crypt_func *) (encrypt), \ + (length), (data))) + +#define EAX_ENCRYPT(ctx, encrypt, length, dst, src) \ + (0 ? (encrypt) (&(ctx)->cipher, 0, (void *) 0, (void *) 0) \ + : eax_encrypt (&(ctx)->eax, &(ctx)->key, \ + &(ctx)->cipher, (nettle_crypt_func *) (encrypt), \ + (length), (dst), (src))) + +#define EAX_DECRYPT(ctx, encrypt, length, dst, src) \ + (0 ? (encrypt) (&(ctx)->cipher, 0, (void *) 0, (void *) 0) \ + : eax_decrypt (&(ctx)->eax, &(ctx)->key, \ + &(ctx)->cipher, (nettle_crypt_func *) (encrypt), \ + (length), (dst), (src))) + +#define EAX_DIGEST(ctx, encrypt, length, digest) \ + (0 ? (encrypt) (&(ctx)->cipher, 0, (void *) 0, (void *) 0) \ + : eax_digest (&(ctx)->eax, &(ctx)->key, \ + &(ctx)->cipher, (nettle_crypt_func *) (encrypt), \ + (length), (digest))) + +struct eax_aes_ctx EAX_CTX(struct aes_ctx); + +void +eax_aes_set_key(struct eax_aes_ctx *ctx, + size_t length, const uint8_t *key); + +void +eax_aes_set_nonce(struct eax_aes_ctx *ctx, + size_t length, const uint8_t *iv); + +void +eax_aes_update(struct eax_aes_ctx *ctx, + size_t length, const uint8_t *data); + +void +eax_aes_encrypt(struct eax_aes_ctx *ctx, + size_t length, uint8_t *dst, const uint8_t *src); + +void +eax_aes_decrypt(struct eax_aes_ctx *ctx, + size_t length, uint8_t *dst, const uint8_t *src); + +void +eax_aes_digest(struct eax_aes_ctx *ctx, size_t length, uint8_t *digest); + +#ifdef __cplusplus +} +#endif + +#endif /* NETTLE_EAX_H_INCLUDED */ diff --git a/nettle-internal.c b/nettle-internal.c index 5cd5828..f6ff864 100644 --- a/nettle-internal.c +++ b/nettle-internal.c @@ -34,6 +34,7 @@ #include "nettle-internal.h" #include "blowfish.h" #include "des.h" +#include "eax.h" #include "gcm.h" #include "salsa20.h"
@@ -112,3 +113,12 @@ const struct nettle_aead nettle_gcm_aes192 = _NETTLE_AEAD(gcm, GCM, aes, 192); const struct nettle_aead nettle_gcm_aes256 = _NETTLE_AEAD(gcm, GCM, aes, 256); + +#define eax_aes_set_iv eax_aes_set_nonce + +const struct nettle_aead +nettle_eax_aes128 = _NETTLE_AEAD(eax, EAX, aes, 128); +const struct nettle_aead +nettle_eax_aes192 = _NETTLE_AEAD(eax, EAX, aes, 192); +const struct nettle_aead +nettle_eax_aes256 = _NETTLE_AEAD(eax, EAX, aes, 256); diff --git a/nettle-internal.h b/nettle-internal.h index e094064..d9f1195 100644 --- a/nettle-internal.h +++ b/nettle-internal.h @@ -127,4 +127,8 @@ extern const struct nettle_aead nettle_gcm_twofish128; extern const struct nettle_aead nettle_gcm_twofish192; extern const struct nettle_aead nettle_gcm_twofish256;
+extern const struct nettle_aead nettle_eax_aes128; +extern const struct nettle_aead nettle_eax_aes192; +extern const struct nettle_aead nettle_eax_aes256; + #endif /* NETTLE_INTERNAL_H_INCLUDED */ diff --git a/testsuite/.test-rules.make b/testsuite/.test-rules.make index 93ba9a8..5549fc0 100644 --- a/testsuite/.test-rules.make +++ b/testsuite/.test-rules.make @@ -103,6 +103,9 @@ ctr-test$(EXEEXT): ctr-test.$(OBJEXT) gcm-test$(EXEEXT): gcm-test.$(OBJEXT) $(LINK) gcm-test.$(OBJEXT) $(TEST_OBJS) -o gcm-test$(EXEEXT)
+eax-test$(EXEEXT): eax-test.$(OBJEXT) + $(LINK) eax-test.$(OBJEXT) $(TEST_OBJS) -o eax-test$(EXEEXT) + hmac-test$(EXEEXT): hmac-test.$(OBJEXT) $(LINK) hmac-test.$(OBJEXT) $(TEST_OBJS) -o hmac-test$(EXEEXT)
diff --git a/testsuite/Makefile.in b/testsuite/Makefile.in index bf0e53c..4a4aa69 100644 --- a/testsuite/Makefile.in +++ b/testsuite/Makefile.in @@ -25,7 +25,8 @@ TS_NETTLE_SOURCES = aes-test.c arcfour-test.c arctwo-test.c \ sha3-384-test.c sha3-512-test.c \ serpent-test.c twofish-test.c \ knuth-lfib-test.c \ - cbc-test.c ctr-test.c gcm-test.c hmac-test.c umac-test.c \ + cbc-test.c ctr-test.c gcm-test.c eax-test \ + hmac-test.c umac-test.c \ meta-hash-test.c meta-cipher-test.c meta-armor-test.c \ buffer-test.c yarrow-test.c pbkdf2-test.c
diff --git a/testsuite/eax-test.c b/testsuite/eax-test.c index e69de29..5fe0b3f 100644 --- a/testsuite/eax-test.c +++ b/testsuite/eax-test.c @@ -0,0 +1,87 @@ +#include "testutils.h" +#include "nettle-internal.h" + +void +test_main(void) +{ + /* From the EAX specification, http://www.cs.ucdavis.edu/~rogaway/papers/eax.pdf */ + test_aead(&nettle_eax_aes128, + SHEX("233952DEE4D5ED5F9B9C6D6FF80FF478"), /* key */ + SHEX("6BFB914FD07EAE6B"), /* auth data */ + SHEX(""), /* plaintext */ + SHEX(""), /* ciphertext */ + SHEX("62EC67F9C3A4A407FCB2A8C49031A8B3"), /* nonce */ + SHEX("E037830E8389F27B025A2D6527E79D01")); /* tag */ + + test_aead(&nettle_eax_aes128, + SHEX("91945D3F4DCBEE0BF45EF52255F095A4"), + SHEX("FA3BFD4806EB53FA"), + SHEX("F7FB"), + SHEX("19DD"), + SHEX("BECAF043B0A23D843194BA972C66DEBD"), + SHEX("5C4C9331049D0BDAB0277408F67967E5")); + + test_aead(&nettle_eax_aes128, + SHEX("01F74AD64077F2E704C0F60ADA3DD523"), + SHEX("234A3463C1264AC6"), + SHEX("1A47CB4933"), + SHEX("D851D5BAE0"), + SHEX("70C3DB4F0D26368400A10ED05D2BFF5E"), + SHEX("3A59F238A23E39199DC9266626C40F80")); + + test_aead(&nettle_eax_aes128, + SHEX("D07CF6CBB7F313BDDE66B727AFD3C5E8"), + SHEX("33CCE2EABFF5A79D"), + SHEX("481C9E39B1"), + SHEX("632A9D131A"), + SHEX("8408DFFF3C1A2B1292DC199E46B7D617"), + SHEX("D4C168A4225D8E1FF755939974A7BEDE")); + + test_aead(&nettle_eax_aes128, + SHEX("35B6D0580005BBC12B0587124557D2C2"), + SHEX("AEB96EAEBE2970E9"), + SHEX("40D0C07DA5E4"), + SHEX("071DFE16C675"), + SHEX("FDB6B06676EEDC5C61D74276E1F8E816"), + SHEX("CB0677E536F73AFE6A14B74EE49844DD")); + + test_aead(&nettle_eax_aes128, + SHEX("BD8E6E11475E60B268784C38C62FEB22"), + SHEX("D4482D1CA78DCE0F"), + SHEX("4DE3B35C3FC039245BD1FB7D"), + SHEX("835BB4F15D743E350E728414"), + SHEX("6EAC5C93072D8E8513F750935E46DA1B"), + SHEX("ABB8644FD6CCB86947C5E10590210A4F")); + + test_aead(&nettle_eax_aes128, + SHEX("7C77D6E813BED5AC98BAA417477A2E7D"), + SHEX("65D2017990D62528"), + SHEX("8B0A79306C9CE7ED99DAE4F87F8DD61636"), + SHEX("02083E3979DA014812F59F11D52630DA30"), + SHEX("1A8C98DCD73D38393B2BF1569DEEFC19"), + SHEX("137327D10649B0AA6E1C181DB617D7F2")); + + test_aead(&nettle_eax_aes128, + SHEX("5FFF20CAFAB119CA2FC73549E20F5B0D"), + SHEX("54B9F04E6A09189A"), + SHEX("1BDA122BCE8A8DBAF1877D962B8592DD2D56"), + SHEX("2EC47B2C4954A489AFC7BA4897EDCDAE8CC3"), + SHEX("DDE59B97D722156D4D9AFF2BC7559826"), + SHEX("3B60450599BD02C96382902AEF7F832A")); + + test_aead(&nettle_eax_aes128, + SHEX("A4A4782BCFFD3EC5E7EF6D8C34A56123"), + SHEX("899A175897561D7E"), + SHEX("6CF36720872B8513F6EAB1A8A44438D5EF11"), + SHEX("0DE18FD0FDD91E7AF19F1D8EE8733938B1E8"), + SHEX("B781FCF2F75FA5A8DE97A9CA48E522EC"), + SHEX("E7F6D2231618102FDB7FE55FF1991700")); + + test_aead(&nettle_eax_aes128, + SHEX("8395FCF1E95BEBD697BD010BC766AAC3"), + SHEX("126735FCC320D25A"), + SHEX("CA40D7446E545FFAED3BD12A740A659FFBBB3CEAB7"), + SHEX("CB8920F87A6C75CFF39627B56E3ED197C552D295A7"), + SHEX("22E7ADD93CFC6393C57EC0B3C17D6B44"), + SHEX("CFC46AFC253B4652B1AF3795B124AB6E")); +}