From 96a5a09cc7d5327fad4992a8e1000d409853a000 Mon Sep 17 00:00:00 2001 From: Mark Wooding Date: Fri, 9 Nov 2018 18:17:42 +0000 Subject: [PATCH] symm/gaead.h: Introduce a new abstraction for authenticated encryption. ... with additional data. The build system is aware that these can be constructed from blockciphers, and there is a table of the things. Alas, there aren't any implemented yet, so the table is empty. For now, at any rate... --- symm/Makefile.am | 14 ++ symm/gaead.c | 123 +++++++++++++++++ symm/gaead.h | 392 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ symm/modes.am.in | 6 + 4 files changed, 535 insertions(+) create mode 100644 symm/gaead.c create mode 100644 symm/gaead.h diff --git a/symm/Makefile.am b/symm/Makefile.am index 0de914f3..b394d86a 100644 --- a/symm/Makefile.am +++ b/symm/Makefile.am @@ -68,6 +68,7 @@ $(srcdir)/modes.am: modes.am.in Makefile.am blkc="$(BLKCS)" \ blkcmode="$(BLKCMODES)" \ blkcciphermode="$(BLKCCIPHERMODES)" \ + blkcaeadmode="$(BLKCAEADMODES)" \ blkcmacmode="$(BLKCMACMODES)" \ hash="$(HASHES)" \ hashmode="$(HASHMODES)" \ @@ -79,6 +80,7 @@ $(srcdir)/stubs.am: stubs.am.in Makefile.am ## Initialize lists of known classes. ALL_CIPHERS = $(CIPHER_MODES) +ALL_AEADS = $(AEAD_MODES) ALL_HASHES = $(HASHES) ALL_MACS = $(MAC_MODES) @@ -95,6 +97,9 @@ BLKCMODES = BLKCCIPHERMODES = BLKCMODES += $(BLKCCIPHERMODES) +BLKCAEADMODES = +BLKCMODES += $(BLKCAEADMODES) + BLKCMACMODES = BLKCMODES += $(BLKCMACMODES) @@ -639,6 +644,15 @@ gciphertab.c: gthingtab.c.in Makefile.am $(AM_V_GEN)$(multigen) -g $(srcdir)/gthingtab.c.in gciphertab.c \ what=gcipher cls=gccipher thing="$(ALL_CIPHERS)" +## Table of AEAD classes. +pkginclude_HEADERS += gaead.h +CLEANFILES += gaeadtab.c +libsymm_la_SOURCES += gaead.c +nodist_libsymm_la_SOURCES += gaeadtab.c +gaeadtab.c: gthingtab.c.in Makefile.am + $(AM_V_GEN)$(multigen) -g $(srcdir)/gthingtab.c.in gaeadtab.c \ + what=gaead cls=gcaead thing="$(ALL_AEADS)" + ## Table of hash classes. pkginclude_HEADERS += ghash.h ghash-def.h CLEANFILES += ghashtab.c diff --git a/symm/gaead.c b/symm/gaead.c new file mode 100644 index 00000000..45aeb614 --- /dev/null +++ b/symm/gaead.c @@ -0,0 +1,123 @@ +/* -*-c-*- + * + * Generic authenticated encryption interface + * + * (c) 2018 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software: you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Catacomb 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/*----- Header files ------------------------------------------------------*/ + +#include "gaead.h" + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @gaead_encrypt@ --- * + * + * Arguments: @const gaead_key *k@ = the AEAD key, already prepared + * @const void *n@, @size_t nsz@ = nonce + * @const void *h@, @size_t hsz@ = additional `header' data + * @const void *m@, @size_t msz@ = message input + * @void *c@, @size_t *csz_input@ = ciphertext output + * @void *t@, @size_t tsz@ = tag output + * + * Returns: Zero on success, @-1@ if the output buffer is too small. + * + * Use: Encrypts and authenticates a message in a single operation. + * This just saves a bunch of messing about with the various + * @gaead_...@ objects. + * + * On entry, @*csz_inout@ should be the capacity of the + * ciphertext buffer; on exit, it will be updated with the + * actual size of ciphertext produced. The function will not + * fail if @*csz_inout >= msz + k->c->ohd@. + */ + +int gaead_encrypt(const gaead_key *k, const void *n, size_t nsz, + const void *h, size_t hsz, + const void *m, size_t msz, + void *c, size_t *csz_inout, + void *t, size_t tsz) +{ + gaead_enc *e = 0; + gaead_aad *a = 0; + buf b; + int rc; + + buf_init(&b, c, *csz_inout); + e = GAEAD_ENC(k, n, nsz, hsz, msz, tsz); if (!e) { rc = -1; goto end; } + if (hsz) { a = GAEAD_AAD(e); GAEAD_HASH(a, h, hsz); } + rc = GAEAD_ENCRYPT(e, m, msz, &b); if (rc) goto end; + rc = GAEAD_DONE(e, a, &b, t, tsz); +end: + if (rc >= 0) *csz_inout = BLEN(&b); + if (e) GAEAD_DESTROY(e); + if (a) GAEAD_DESTROY(a); + return (rc); +} + +/* --- @gaead_decrypt@ --- * + * + * Arguments: @const gaead_key *k@ = the AEAD key, already prepared + * @const void *n@, @size_t nsz@ = nonce + * @const void *h@, @size_t hsz@ = additional `header' data + * @const void *c@, @size_t csz@ = ciphertext input + * @void *m@, @size_t *msz_inout@ = message output + * @const void *t@, @size_t tsz@ = tag input + * + * Returns: @+1@ if everything is good; zero for authentication failure, + * @-1@ for other problems. + * + * Use: Decrypts and verifies a message in a single operation. + * This just saves a bunch of messing about with the various + * @gaead_...@ objects. + * + * On entry, @*msz_inout@ should be the capacity of the + * message buffer; on exit, it will be updated with the + * actual size of message produced. The function will not + * fail if @*msz_inout >= csz@. + */ + +int gaead_decrypt(const gaead_key *k, const void *n, size_t nsz, + const void *h, size_t hsz, + const void *c, size_t csz, + void *m, size_t *msz_inout, + const void *t, size_t tsz) +{ + gaead_dec *d = 0; + gaead_aad *a = 0; + buf b; + int rc; + + buf_init(&b, m, *msz_inout); + d = GAEAD_DEC(k, n, nsz, hsz, csz, tsz); if (!d) { rc = -1; goto end; } + if (hsz) { a = GAEAD_AAD(d); GAEAD_HASH(a, h, hsz); } + rc = GAEAD_DECRYPT(d, c, csz, &b); if (rc) goto end; + rc = GAEAD_DONE(d, a, &b, t, tsz); +end: + if (rc >= 0) *msz_inout = BLEN(&b); + if (d) GAEAD_DESTROY(d); + if (a) GAEAD_DESTROY(a); + return (rc); +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/symm/gaead.h b/symm/gaead.h new file mode 100644 index 00000000..cad250c9 --- /dev/null +++ b/symm/gaead.h @@ -0,0 +1,392 @@ +/* -*-c-*- + * + * Generic authenticated encryption interface + * + * (c) 2018 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software: you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Catacomb 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb. If not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef CATACOMB_GAEAD_H +#define CATACOMB_GAEAD_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#include + +#include + +#ifndef CATACOMB_BUF_H +# include "buf.h" +#endif + +#ifndef CATACOMB_KEYSZ_H +# include "keysz.h" +#endif + +/*----- Generic AEAD interface --------------------------------------------*/ + +typedef struct gaead_key { + const struct gaead_keyops *ops; +} gaead_key; + +typedef struct gaead_enc { + const struct gaead_encops *ops; +} gaead_enc; + +typedef struct gaead_dec { + const struct gaead_decops *ops; +} gaead_dec; + +typedef struct gaead_aad { + const struct gaead_aadops *ops; +} gaead_aad; + +typedef struct gaead_keyops { + const struct gcaead *c; /* Pointer to AEAD class */ + + gaead_aad *(*aad)(const gaead_key */*k*/); + /* Return an AAD-hashing object for this key. Only available if + * the @AEADF_AADNDEP@ class flag is clear. + */ + + gaead_enc *(*enc)(const gaead_key */*k*/, + const void */*n*/, size_t /*nsz*/, + size_t /*hsz*/, size_t /*msz*/, size_t /*tsz*/); + /* Return a message encryption object for this key, with the given + * nonce. If the @AEADF_PCHSZ@, @AEADF_PCMSZ@ and/or @AEADF_PCTSZ@ + * class flags are set then the caller must provide the AAD length + * @hsz@, message length @msz@ and/or tag length @tsz@ respectively; + * otherwise these arguments will be ignored. + * + * The caller is expected to have ensured that the nonce and tag + * lengths are acceptable, e.g., by checking against the tables + * provided in the class object. Some unfortunate AEAD schemes have + * more complicated requirements: if the sizes are unacceptable in + * combination, this function returns null. + */ + + gaead_dec *(*dec)(const gaead_key */*k*/, + const void */*n*/, size_t /*nsz*/, + size_t /*hsz*/, size_t /*csz*/, size_t /*tsz*/); + /* Return a message encryption object for this key, with the given + * nonce. If the @AEADF_PCHSZ@, @AEADF_PCMSZ@ and/or @AEADF_PCTSZ@ + * class flags are set then the caller must provide the AAD length + * @hsz@, ciphertext length @csz@ and/or tag length @tsz@ + * respectively; otherwise these arguments will be ignored. + * + * The caller is expected to have ensured that the nonce and tag + * lengths are acceptable, e.g., by checking against the tables + * provided in the class object. Some unfortunate AEAD schemes have + * more complicated requirements: if the sizes are unacceptable in + * combination, this function returns null. + */ + + void (*destroy)(gaead_key */*k*/); + /* Destroy the key object. This will not invalidate AAD-hashing, + * encryption or decryption objects. + */ + +} gaead_keyops; + +typedef struct gaead_aadops { + const struct gcaead *c; /* Pointer to AEAD class */ + + gaead_aad *(*dup)(const gaead_aad */*a*/); + /* Return a new AAD-hashing object with a copy of this object's + * state. This is useful if the AAD for multiple messages shares a + * common prefix: the prefix can be processed once, and a copy + * created for each different suffix. Only available if the + * @AEADF_AADNDEP@ class flag is clear. + */ + + void (*hash)(gaead_aad */*a*/, const void */*h*/, size_t /*hsz*/); + /* Feed header (additional authenticated) data into the AAD-hashing + * object. + */ + + void (*destroy)(gaead_aad */*a*/); + /* Destroy the AAD-hashing object. */ + +} gaead_aadops; + +typedef struct gaead_encops { + const struct gcaead *c; /* Pointer to AEAD class */ + + gaead_aad *(*aad)(gaead_enc */*e*/); + /* Return a new AAD-hashing object for the current key and nonce. If + * the @AEADF_AADNDEP@ class flag is clear then this works just as if + * the @aad@ method on the key had been called instead: the new + * object is in fact independent of the nonce and can be used with + * any encryption or decryption operation. If @AEADF_AADNDEP@ is + * set, then the returned AAD-hashing object is specific to this + * encryption operation. If @AEADF_AADFIRST@ is also set, then all + * additional data must be hashed before any message data is + * presented for encryption. + */ + + int (*reinit)(gaead_enc */*e*/, const void */*n*/, size_t /*nsz*/, + size_t /*hsz*/, size_t /*msz*/, size_t /*tsz*/); + /* Reinitialize this object for a new encryption operation with a + * different nonce. The data lengths @hsz@, @msz@, and @tsz@ are as + * for the key @enc@ method. Returns zero on success. + * + * The caller is expected to have ensured that the nonce and tag + * lengths are acceptable, e.g., by checking against the tables + * provided in the class object. Some unfortunate AEAD schemes have + * more complicated requirements: if the sizes are unacceptable in + * combination, this function returns @-1@. + */ + + int (*encrypt)(gaead_enc */*e*/, const void */*m*/, size_t /*msz*/, + buf */*b*/); + /* Encrypt a chunk of data, writing the result to the output buffer + * @b@. This will succeed if @BLEFT(b) >= msz + e->c->bufsz@; + * otherwise it might fail. Failure doesn't affect the encryption + * operation's state. Returns zero on success, or @-1@ on failure. + */ + + int (*done)(gaead_enc */*e*/, const gaead_aad */*a*/, buf */*b*/, + void */*t*/, size_t /*tsz*/); + /* Completes encryption, returning the authentication tag for the + * message and any additional authenticated data accumulated in @a@. + * The pointer @a@ may be null if there is no AAD. If the + * @AEADF_AADNDEP@ class flag is set, and any header data has been + * provided to the operation's AAD-hashing object, then a pointer to + * this object must be provided as @a@. If @AEADF_AADNDEP@ is clear, + * then any AAD-hashing object for this key may be provided. + * Internally buffered ciphertext may be written to @b@. This will + * succeed if @BLEFT(b) >= e->c->bufsz@; otherwise it might fail. + * Failure doesn't affect the encryption operation's state. Returns + * zero on success, or @-1@ on failure. + */ + + void (*destroy)(gaead_enc */*e*/); + /* Destroy the encryption object. */ + +} gaead_encops; + +typedef struct gaead_decops { + const struct gcaead *c; /* Pointer to AEAD class */ + + gaead_aad *(*aad)(gaead_dec */*d*/); + /* Return a new AAD-hashing object for the current key and nonce. If + * the @AEADF_AADNDEP@ class flag is clear then this works just as if + * the @aad@ method on the key had been called instead: the new + * object is in fact independent of the nonce and can be used with + * any encryption or decryption operation. If @AEADF_AADNDEP@ is + * set, then the returned AAD-hashing object is specific to this + * decryption operation. If @AEADF_AADFIRST@ is also set, then all + * additional data must be hashed before any ciphertext is presented + * for decryption. + */ + + int (*reinit)(gaead_dec */*d*/, const void */*n*/, size_t /*nsz*/, + size_t /*hsz*/, size_t /*csz*/, size_t /*tsz*/); + /* Reinitialize this object for a new decryption operation with a + * different nonce. The data lengths @hsz@, @csz@, and @tsz@ are as + * for the key @dec@ method. + * + * The caller is expected to have ensured that the nonce and tag + * lengths are acceptable, e.g., by checking against the tables + * provided in the class object. Some unfortunate AEAD schemes have + * more complicated requirements: if the sizes are unacceptable in + * combination, this function returns @-1@. + */ + + int (*decrypt)(gaead_dec */*d*/, const void */*c*/, size_t /*csz*/, + buf */*b*/); + /* Decrypt a chunk of data, writing the result to the output buffer + * @b@. This will succeed if @BLEFT(b) >= msz + e->c->bufsz@; + * otherwise it might fail. Failure doesn't affect the decryption + * operation's state. Returns zero on success, or @-1@ on failure. + * + * CAUTION: the decrypted data may be inauthentic. Don't do anything + * risky with it until its tag has been verified. + */ + + int (*done)(gaead_dec */*d*/, const gaead_aad */*a*/, buf */*b*/, + const void */*t*/, size_t /*tsz*/); + /* Completes decryption, verifying the authentication tag for the + * message and any additional authenticated data accumulated in @a@. + * The pointer @a@ may be null if there is no AAD. If the + * @AEADF_AADNDEP@ class flag is set, and any header data has been + * provided to the operation's AAD-hashing object, then a pointer to + * this object must be provided as @a@. If @AEADF_AADNDEP@ is clear, + * then any AAD-hashing object for this key may be provided. + * Internally buffered plaintext may be written to @b@. This will + * succeed if @BLEFT(b) >= e->c->bufsz@; otherwise it might fail. + * Failure doesn't affect the decryption operation's state. Returns + * @+1@ on success, @0@ on verification failure, or @-1@ on other + * kinds of failures. + */ + + void (*destroy)(gaead_dec */*d*/); + /* Destroy the decryption object. */ + +} gaead_decops; + +typedef struct gcaead { + const char *name; /* AEAD scheme name */ + const octet *keysz; /* Acceptable keys-size table */ + const octet *noncesz; /* Acceptable nonce-size table */ + const octet *tagsz; /* Acceptable tag-size table */ + size_t blksz; /* Block size, or zero if none */ + unsigned bufsz; /* Maximum extra msg/ct output */ + unsigned ohd; /* Maximum encryption overhead */ + unsigned f; /* Various other flags */ +#define AEADF_PCHSZ 1u /* Precommit to AAD size */ +#define AEADF_PCMSZ 2u /* Precommit to message size */ +#define AEADF_PCTSZ 4u /* Precommit to tag size */ +#define AEADF_AADNDEP 8u /* AAD hash is nonce-dependent */ +#define AEADF_AADFIRST 16u /* AAD must precede msg/ct */ + + gaead_key *(*key)(const void */*k*/, size_t /*ksz*/); + /* Return a key object (above) with the given key material. */ + + int (*szok)(size_t /*nsz*/, size_t /*hsz*/, + size_t /*msz*/, size_t /*tsz*/); + /* Return true (nonzero) if the given collection of sizes for nonce, + * header, message, and tag are acceptable in combination. Mostly + * this will be true if the nonce length and tag size are are + * acceptable independently (and the header and message lengths are + * irrelevant), but some schemes are more awkward. + */ +} gcaead; + +#define GAEAD_KEY(cc, k, ksz) (cc)->key((k), (ksz)) +#define GAEAD_CLASS(obj) (obj)->ops->c +#define GAEAD_AAD(ked) (ked)->ops->aad((ked)) +#define GAEAD_REINIT(ed, n, nsz, hsz, msz, tsz) \ + (ed)->ops->reinit((ed), (n), (nsz), (hsz), (msz), (tsz)) +#define GAEAD_ENC(k, n, nsz, hsz, msz, tsz) \ + (k)->ops->enc((k), (n), (nsz), (hsz), (msz), (tsz)) +#define GAEAD_DEC(k, n, nsz, hsz, msz, tsz) \ + (k)->ops->dec((k), (n), (nsz), (hsz), (msz), (tsz)) +#define GAEAD_DUP(a) (a)->ops->dup((a)) +#define GAEAD_HASH(a, h, hsz) (a)->ops->hash((a), (h), (hsz)) +#define GAEAD_ENCRYPT(e, m, msz, b) \ + (e)->ops->encrypt((e), (m), (msz), (b)) +#define GAEAD_DECRYPT(d, c, csz, b) \ + (d)->ops->decrypt((d), (c), (csz), (b)) +#define GAEAD_DONE(ed, aad, b, t, tsz) \ + (ed)->ops->done((ed), (aad), (b), (t), (tsz)) +#define GAEAD_DESTROY(obj) (obj)->ops->destroy((obj)) + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @gaead_szokcommon@ --- * + * + * Arguments: @const gcaead *aec@ = pointer to AEAD class + * @size_t nsz@, @size_t hsz@, @size_t msz@, @size_t tsz@ = + * nonce, header, message, and tag sizes + * + * Returns: Nonzero if the sizes are acceptable to the AEAD scheme in + * combination. + * + * Use: Generic implementation for sensible AEAD schemes. + */ + +extern int gaead_szokcommon(const gcaead */*aec*/, + size_t /*nsz*/, size_t /*hsz*/, + size_t /*msz*/, size_t /*tsz*/); + +/* --- @gaead_encrypt@ --- * + * + * Arguments: @const gaead_key *k@ = the AEAD key, already prepared + * @const void *n@, @size_t nsz@ = nonce + * @const void *h@, @size_t hsz@ = additional `header' data + * @const void *m@, @size_t msz@ = message input + * @void *c@, @size_t *csz_input@ = ciphertext output + * @void *t@, @size_t tsz@ = tag output + * + * Returns: Zero on success, @-1@ if the output buffer is too small. + * + * Use: Encrypts and authenticates a message in a single operation. + * This just saves a bunch of messing about with the various + * @gaead_...@ objects. + * + * On entry, @*csz_inout@ should be the capacity of the + * ciphertext buffer; on exit, it will be updated with the + * actual size of ciphertext produced. The function will not + * fail if @*csz_inout >= msz + k->c->ohd@. + */ + +extern int gaead_encrypt(const gaead_key */*k*/, + const void */*n*/, size_t /*nsz*/, + const void */*h*/, size_t /*hsz*/, + const void */*m*/, size_t /*msz*/, + void */*c*/, size_t */*csz_inout*/, + void */*t*/, size_t /*tsz*/); + +/* --- @gaead_decrypt@ --- * + * + * Arguments: @const gaead_key *k@ = the AEAD key, already prepared + * @const void *n@, @size_t nsz@ = nonce + * @const void *h@, @size_t hsz@ = additional `header' data + * @const void *c@, @size_t csz@ = ciphertext input + * @void *m@, @size_t *msz_inout@ = message output + * @const void *t@, @size_t tsz@ = tag input + * + * Returns: @+1@ if everything is good; zero for authentication failure, + * @-1@ for other problems. + * + * Use: Decrypts and verifies a message in a single operation. + * This just saves a bunch of messing about with the various + * @gaead_...@ objects. + * + * On entry, @*msz_inout@ should be the capacity of the + * message buffer; on exit, it will be updated with the + * actual size of message produced. The function will not + * fail if @*msz_inout >= csz@. + */ + +extern int gaead_decrypt(const gaead_key */*k*/, + const void */*n*/, size_t /*nsz*/, + const void */*h*/, size_t /*hsz*/, + const void */*c*/, size_t /*csz*/, + void */*m*/, size_t */*msz_inout*/, + const void */*t*/, size_t /*tsz*/); + +/*----- Tables ------------------------------------------------------------*/ + +extern const gcaead *const gaeadtab[]; + +/* --- @gaead_byname@ --- * + * + * Arguments: @const char *p@ = pointer to name string + * + * Returns: The named AEAD class, or null. + */ + +extern const gcaead *gaead_byname(const char */*p*/); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/symm/modes.am.in b/symm/modes.am.in index 2adbac72..d95a5f1c 100644 --- a/symm/modes.am.in +++ b/symm/modes.am.in @@ -61,6 +61,12 @@ CIPHER_MODES += @{blkc:f}-@blkcciphermode CIPHER_MODES += @{hash:f}-@hashciphermode %end +## Modes for authenticated encryption. +AEAD_MODES = +%repeat +AEAD_MODES += @{blkc:f}-@blkcaeadmode +%end + ## Modes for message authentication. MAC_MODES = %repeat -- 2.11.0