+/* -*-c-*-
+ *
+ * The CCM authenticated-encryption mode
+ *
+ * (c) 2017 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_CCM_DEF_H
+#define CATACOMB_CCM_DEF_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <string.h>
+
+#include <mLib/bits.h>
+#include <mLib/sub.h>
+
+#ifndef CATACOMB_ARENA_H
+# include "arena.h"
+#endif
+
+#ifndef CATACOMB_BLKC_H
+# include "blkc.h"
+#endif
+
+#ifndef CATACOMB_CT_H
+# include "ct.h"
+#endif
+
+#ifndef CATACOMB_KEYSZ_H
+# include "keysz.h"
+#endif
+
+#ifndef CATACOMB_PARANOIA_H
+# include "paranoia.h"
+#endif
+
+#ifndef CATACOMB_RSVR_H
+# include "rsvr.h"
+#endif
+
+/*----- Common machinery --------------------------------------------------*/
+
+/* --- @ccm_check@ --- *
+ *
+ * Arguments: @const ccm_params *p@ = pointer to parameters
+ *
+ * Returns: True (nonzero) if the parameters are OK; false (zero) if
+ * there's a problem.
+ *
+ * Use: Verify that the CCM parameters are acceptable.
+ */
+
+extern int ccm_check(const ccm_params */*p*/);
+
+/* --- @ccm_fmthdr@ --- *
+ *
+ * Arguments: @const ccm_params *p@ = pointer to parameters
+ * @octet *b@ = block-size buffer to write header
+ * @const void *n@ = pointer to nonce
+ *
+ * Returns: ---
+ *
+ * Use: Format a MAC header block.
+ */
+
+extern void ccm_fmthdr(const ccm_params */*p*/,
+ octet */*b*/, const void */*n*/);
+
+/* --- @ccm_fmtctr@ --- *
+ *
+ * Arguments: @const ccm_params *p@ = pointer to parameters
+ * @octet *b@ = block-size buffer to write header
+ * @const void *n@ = pointer to nonce
+ *
+ * Returns: ---
+ *
+ * Use: Format an initial counter block.
+ */
+
+extern void ccm_fmtctr(const ccm_params */*p*/,
+ octet */*b*/, const void */*n*/);
+
+/*----- Macros ------------------------------------------------------------*/
+
+/* --- @CCM_DEF@ --- *
+ *
+ * Arguments: @PRE@, @pre@ = prefixes for the underlying block cipher
+ *
+ * Use: Creates an implementation for the CCM authenticated-
+ * encryption mode.
+ */
+
+#define CCM_DEF(PRE, pre) CCM_DEFX(PRE, pre, #pre, #pre)
+
+#define CCM_DEFX(PRE, pre, name, fname) \
+ \
+const octet pre##_ccmnoncesz[] = \
+ { KSZ_RANGE, PRE##_BLKSZ/2 - (PRE##_BLKSZ <= 16 ? 1 : 2), \
+ CCM_NSZMIN(PRE), CCM_NSZMAX(PRE), 1 }; \
+const octet pre##_ccmtagsz[] = \
+ { KSZ_RANGE, CCM_TSZMAX(PRE), \
+ CCM_TSZMIN(PRE), CCM_TSZMAX(PRE), PRE##_BLKSZ == 16 ? 2 : 1 }; \
+ \
+static const rsvr_policy pre##_ccmpolicy = \
+ { RSVRF_FULL, PRE##_BLKSZ, PRE##_BLKSZ }; \
+ \
+/* --- @pre_ccminthash@ --- * \
+ * \
+ * Arguments: @pre_ccmctx *ctx@ = pointer to context block \
+ * @const void *p@ = pointer to material to hash \
+ * @size_t sz@ = size of the input buffer \
+ * \
+ * Returns: --- \
+ * \
+ * Use: Internal operation for feeding stuff into the CBC-MAC \
+ * context. \
+ */ \
+ \
+static void pre##_ccminthash(pre##_ccmctx *ctx, \
+ const void *p, size_t sz) \
+{ \
+ rsvr_state st; \
+ const octet *q; \
+ \
+ rsvr_setup(&st, &pre##_ccmpolicy, ctx->b, &ctx->off, p, sz); \
+ RSVR_DO(&st) while ((q = RSVR_NEXT(&st, PRE##_BLKSZ)) != 0) { \
+ BLKC_XLOAD(PRE, ctx->a, q); \
+ pre##_eblk(&ctx->k, ctx->a, ctx->a); \
+ } \
+} \
+ \
+/* --- @pre_ccminit@ --- * \
+ * \
+ * Arguments: @pre_ccmctx *aad@ = pointer to CCM context \
+ * @const pre_ctx *k@ = pointer to key material \
+ * @const void *n@ = pointer to nonce \
+ * @size_t nsz@ = size of the nonce \
+ * @size_t hsz@ = size of the AAD \
+ * @size_t msz@ = size of the message/ciphertext \
+ * @size_t tsz@ = size of the tag to produce \
+ * \
+ * Returns: Zero on success; nonzero if the parameters are invalid. \
+ * \
+ * Use: Initialize an CCM operation context with a given key. \
+ * \
+ * The original key needn't be kept around any more. \
+ */ \
+ \
+int pre##_ccminit(pre##_ccmctx *ctx, const pre##_ctx *k, \
+ const void *n, size_t nsz, \
+ size_t hsz, size_t msz, size_t tsz) \
+ { ctx->k = *k; return (pre##_ccmreinit(ctx, n, nsz, hsz, msz, tsz)); } \
+ \
+/* --- @pre_ccmreinit@ --- * \
+ * \
+ * Arguments: @pre_ccmctx *ctx@ = pointer to CCM context \
+ * @const void *n@ = pointer to nonce \
+ * @size_t nsz@ = size of nonce \
+ * @size_t hsz@ = size of the AAD \
+ * @size_t msz@ = size of the message/ciphertext \
+ * @size_t tsz@ = size of the tag to produce \
+ * \
+ * Returns: Zero on success; nonzero if the parameters are invalid. \
+ * \
+ * Use: Reinitialize an CCM operation context, changing the \
+ * nonce. \
+ */ \
+ \
+int pre##_ccmreinit(pre##_ccmctx *ctx, const void *n, size_t nsz, \
+ size_t hsz, size_t msz, size_t tsz) \
+{ \
+ kludge64 t; \
+ octet b[12]; \
+ size_t sz; \
+ \
+ /* Set up the parameters and check that they make sense. */ \
+ ctx->p.hsz = hsz; ctx->p.msz = msz; \
+ ctx->p.bsz = PRE##_BLKSZ; ctx->p.nsz = nsz; ctx->p.tsz = tsz; \
+ if (!ccm_check(&ctx->p)) return (-1); \
+ \
+ /* Prepare the counter and the final MAC mask. The initial counter \
+ * is used to make the MAC mask, so generate that, keeping it for \
+ * later. \
+ */ \
+ ccm_fmtctr(&ctx->p, ctx->b, n); \
+ BLKC_LOAD(PRE, ctx->c, ctx->b); \
+ pre##_eblk(&ctx->k, ctx->c, ctx->s0); \
+ \
+ /* Prepare the MAC header and leave it in the buffer. */ \
+ ccm_fmthdr(&ctx->p, ctx->b, n); \
+ BLKC_ZERO(PRE, ctx->a); \
+ \
+ /* Initialize our state. The buffer is currently full (with the \
+ * MAC header), and we're always awaiting AAD, though we've not yet \
+ * seen any. (Even if we're not expecting AAD, this will trigger \
+ * appropriate initialization when encryption or decryption begins.) \
+ */ \
+ ctx->off = PRE##_BLKSZ; ctx->i = 0; \
+ ctx->st = CCMST_AAD; \
+ \
+ /* If there's AAD to come, then do the AAD framing. This aligns \
+ * badly with the blocking, so feed the framing in the hard way. \
+ */ \
+ if (hsz) { \
+ if (hsz < 0xfffe) \
+ { STORE16(b, hsz); sz = 2; } \
+ else if (hsz <= MASK32) \
+ { b[0] = 0xff; b[1] = 0xfe; STORE32(b + 2, hsz); sz = 6; } \
+ else { \
+ b[0] = b[1] = 0xff; \
+ ASSIGN64(t, hsz); STORE64_(b + 2, t); \
+ sz = 10; \
+ } \
+ pre##_ccminthash(ctx, b, sz); \
+ } \
+ \
+ /* All done. */ \
+ return (0); \
+} \
+ \
+/* --- @pre_ccmaadhash@ --- * \
+ * \
+ * Arguments: @pre_ccmctx *ctx@ = pointer to AAD context \
+ * @const void *p@ = pointer to AAD material \
+ * @size_t sz@ = length of AAD material \
+ * \
+ * Returns: --- \
+ * \
+ * Use: Feeds AAD into the context. This must be done before \
+ * any of the message/ciphertext is processed because CCM \
+ * is really annoying like that. \
+ */ \
+ \
+void pre##_ccmaadhash(pre##_ccmctx *ctx, const void *p, size_t sz) \
+{ \
+ assert(ctx->st == CCMST_AAD); \
+ assert(sz <= ctx->p.hsz - ctx->i); \
+ ctx->i += sz; \
+ pre##_ccminthash(ctx, p, sz); \
+} \
+ \
+/* --- @pre_ccmencdecsetup@ --- * \
+ * \
+ * Arguments: @pre_ccmctx *ctx@ = pointer to context block \
+ * @size_t sz@ = size of message block \
+ * \
+ * Returns: --- \
+ * \
+ * Use: Prepares for an encrypt or decryption operation, \
+ * transitioning from the AAD state and updating the \
+ * message size. \
+ */ \
+ \
+static void pre##_ccmencdecsetup(pre##_ccmctx *ctx, size_t sz) \
+{ \
+ if (ctx->st != CCMST_MSG) { \
+ /* Make sure we're currently in the AAD state and we've seen all of \
+ * the AAD we expected. \
+ */ \
+ assert(ctx->st == CCMST_AAD); \
+ assert(ctx->i == ctx->p.hsz); \
+ \
+ /* Pad the final AAD block out until we hit a block boundary. Note \
+ * that we don't cycle the block cipher here: instead, leave the \
+ * buffer full so that we do that next time. \
+ */ \
+ memset(ctx->b + ctx->off, 0, PRE##_BLKSZ - ctx->off); \
+ ctx->off = PRE##_BLKSZ; \
+ \
+ /* Now we're ready to process the message text. */ \
+ ctx->st = CCMST_MSG; ctx->i = 0; \
+ } \
+ \
+ /* Update the size. */ \
+ assert(sz <= ctx->p.msz - ctx->i); \
+ ctx->i += sz; \
+} \
+ \
+/* --- @pre_ccmencrypt@ --- * \
+ * \
+ * Arguments: @pre_ccmctx *ctx@ = pointer to CCM operation context \
+ * @const void *src@ = pointer to plaintext message chunk \
+ * @size_t sz@ = size of the plaintext \
+ * @buf *dst@ = a buffer to write the ciphertext to \
+ * \
+ * Returns: Zero on success; @-1@ on failure. \
+ * \
+ * Use: Encrypts a chunk of a plaintext message, writing a \
+ * chunk of ciphertext to the output buffer and updating \
+ * the operation state. \
+ * \
+ * For CCM, we always write a ciphertext chunk the same \
+ * size as the plaintext. The messing about with @buf@ \
+ * objects makes the interface consistent with other AEAD \
+ * schemes which can't do this. \
+ */ \
+ \
+int pre##_ccmencrypt(pre##_ccmctx *ctx, \
+ const void *src, size_t sz, buf *dst) \
+{ \
+ rsvr_plan plan; \
+ uint32 t[PRE##_BLKSZ/4], u[PRE##_BLKSZ]; \
+ const octet *p = src; \
+ octet *q, *r, y; \
+ \
+ /* Allocate space for the ciphertext. */ \
+ if (sz) { q = buf_get(dst, sz); if (!q) return (-1); } \
+ else q = 0; \
+ \
+ /* Set stuff up. */ \
+ pre##_ccmencdecsetup(ctx, sz); \
+ \
+ /* Determine the buffering plan. Our buffer is going to do double- \
+ * duty here. The end portion is going to contain mask from the \
+ * encrypted counter which we mix into the plaintext to encrypt it; \
+ * the start portion, which originally mask bytes we've already used, \
+ * will hold the input plaintext, which will eventually be \
+ * collected into the CBC-MAC state. \
+ */ \
+ rsvr_mkplan(&plan, &pre##_ccmpolicy, ctx->off, sz); \
+ \
+ /* Initial portion, fulfilled from the buffer. If the buffer is \
+ * empty, then that means that we haven't yet encrypted the current \
+ * counter, so we should do that and advance it. \
+ */ \
+ if (plan.head) { \
+ if (!ctx->off) { \
+ BLKC_BSTEP(PRE, ctx->c); pre##_eblk(&ctx->k, ctx->c, t); \
+ BLKC_STORE(PRE, ctx->b, t); \
+ } \
+ r = ctx->b + ctx->off; ctx->off += plan.head; \
+ while (plan.head--) { y = *p++; *q++ = y ^ *r; *r++ = y; } \
+ } \
+ \
+ /* If we've filled up the buffer then we need to cycle the MAC and \
+ * reset the offset. \
+ */ \
+ if (plan.from_rsvr) { \
+ BLKC_XLOAD(PRE, ctx->a, ctx->b); \
+ pre##_eblk(&ctx->k, ctx->a, ctx->a); \
+ ctx->off = 0; \
+ } \
+ \
+ /* Now to process the main body of the input. */ \
+ while (plan.from_input) { \
+ BLKC_BSTEP(PRE, ctx->c); pre##_eblk(&ctx->k, ctx->c, t); \
+ BLKC_LOAD(PRE, u, p); p += PRE##_BLKSZ; \
+ BLKC_XSTORE(PRE, q, t, u); q += PRE##_BLKSZ; \
+ BLKC_XMOVE(PRE, ctx->a, u); pre##_eblk(&ctx->k, ctx->a, ctx->a); \
+ plan.from_input -= PRE##_BLKSZ; \
+ } \
+ \
+ /* Finally, deal with any final portion. If there is one, we know \
+ * that the buffer is empty: we must have filled it above, or this \
+ * would all count as `initial' data. \
+ */ \
+ if (plan.tail) { \
+ BLKC_BSTEP(PRE, ctx->c); pre##_eblk(&ctx->k, ctx->c, t); \
+ BLKC_STORE(PRE, ctx->b, t); \
+ r = ctx->b; ctx->off = plan.tail; \
+ while (plan.tail--) { y = *p++; *q++ = y ^ *r; *r++ = y; } \
+ } \
+ \
+ /* Done. */ \
+ return (0); \
+} \
+ \
+/* --- @pre_ccmdecrypt@ --- * \
+ * \
+ * Arguments: @pre_ccmctx *ctx@ = pointer to CCM operation context \
+ * @const void *src@ = pointer to ciphertext message chunk \
+ * @size_t sz@ = size of the ciphertext \
+ * @buf *dst@ = a buffer to write the plaintext to \
+ * \
+ * Returns: Zero on success; @-1@ on failure. \
+ * \
+ * Use: Decrypts a chunk of a ciphertext message, writing a \
+ * chunk of plaintext to the output buffer and updating \
+ * the operation state. \
+ * \
+ * For CCM, we always write a plaintext chunk the same \
+ * size as the ciphertext. The messing about with @buf@ \
+ * objects makes the interface consistent with other AEAD \
+ * schemes which can't do this. \
+ */ \
+ \
+int pre##_ccmdecrypt(pre##_ccmctx *ctx, \
+ const void *src, size_t sz, buf *dst) \
+{ \
+ rsvr_plan plan; \
+ uint32 t[PRE##_BLKSZ/4]; \
+ const octet *p = src; \
+ octet *q, *r, y; \
+ \
+ /* Allocate space for the plaintext. */ \
+ if (sz) { q = buf_get(dst, sz); if (!q) return (-1); } \
+ else q = 0; \
+ \
+ /* Set stuff up. */ \
+ pre##_ccmencdecsetup(ctx, sz); \
+ \
+ /* Determine the buffering plan. Our buffer is going to do double- \
+ * duty here. The end portion is going to contain mask from the \
+ * encrypted counter which we mix into the plaintext to encrypt it; \
+ * the start portion, which originally mask bytes we've already used, \
+ * will hold the recovered plaintext, which will eventually be \
+ * collected into the CBC-MAC state. \
+ */ \
+ rsvr_mkplan(&plan, &pre##_ccmpolicy, ctx->off, sz); \
+ \
+ /* Initial portion, fulfilled from the buffer. If the buffer is \
+ * empty, then that means that we haven't yet encrypted the current \
+ * counter, so we should do that and advance it. \
+ */ \
+ if (plan.head) { \
+ if (!ctx->off) { \
+ BLKC_BSTEP(PRE, ctx->c); pre##_eblk(&ctx->k, ctx->c, t); \
+ BLKC_STORE(PRE, ctx->b, t); \
+ } \
+ r = ctx->b + ctx->off; ctx->off += plan.head; \
+ while (plan.head--) { y = *p++ ^ *r; *q++ = *r++ = y; } \
+ } \
+ \
+ /* If we've filled up the buffer then we need to cycle the MAC and \
+ * reset the offset. \
+ */ \
+ if (plan.from_rsvr) { \
+ BLKC_XLOAD(PRE, ctx->a, ctx->b); \
+ pre##_eblk(&ctx->k, ctx->a, ctx->a); \
+ ctx->off = 0; \
+ } \
+ \
+ /* Now to process the main body of the input. */ \
+ while (plan.from_input) { \
+ BLKC_BSTEP(PRE, ctx->c); pre##_eblk(&ctx->k, ctx->c, t); \
+ BLKC_XLOAD(PRE, t, p); p += PRE##_BLKSZ; \
+ BLKC_STORE(PRE, q, t); q += PRE##_BLKSZ; \
+ BLKC_XMOVE(PRE, ctx->a, t); pre##_eblk(&ctx->k, ctx->a, ctx->a); \
+ plan.from_input -= PRE##_BLKSZ; \
+ } \
+ \
+ /* Finally, deal with any final portion. If there is one, we know \
+ * that the buffer is empty: we must have filled it above, or this \
+ * would all count as `initial' data. \
+ */ \
+ if (plan.tail) { \
+ BLKC_BSTEP(PRE, ctx->c); pre##_eblk(&ctx->k, ctx->c, t); \
+ BLKC_STORE(PRE, ctx->b, t); \
+ r = ctx->b; ctx->off = plan.tail; \
+ while (plan.tail--) { y = *p++ ^ *r; *q++ = *r++ = y; } \
+ } \
+ \
+ /* Done. */ \
+ return (0); \
+} \
+ \
+/* --- @pre_ccmtag@ --- * \
+ * \
+ * Arguments: @pre_ccmctx *ctx@ = pointer to an CCM context \
+ * @octet *t@ = where to write a (full-length) tag \
+ * @size_t tsz@ = size of the tag (to check) \
+ * \
+ * Returns: --- \
+ * \
+ * Use: Finishes an CCM operation, by calculating the tag. \
+ */ \
+ \
+static void pre##_ccmtag(pre##_ccmctx *ctx, octet *t, size_t tsz) \
+{ \
+ /* Make sure we're in good shape. It's just about possible that \
+ * we're still in the AAD state, but there was no actual message, so \
+ * handle this situation. \
+ */ \
+ switch (ctx->st) { \
+ case CCMST_AAD: \
+ assert(ctx->i == ctx->p.hsz); \
+ assert(!ctx->p.msz); \
+ break; \
+ case CCMST_MSG: \
+ /* hsz already checked in `pre_ccmencdecsetup'. */ \
+ assert(ctx->i == ctx->p.msz); \
+ break; \
+ default: abort(); \
+ } \
+ assert(tsz == ctx->p.tsz); \
+ \
+ /* Pad the final plaintext block out and cycle the block cipher one \
+ * last time. \
+ */ \
+ memset(ctx->b + ctx->off, 0, PRE##_BLKSZ - ctx->off); \
+ BLKC_XLOAD(PRE, ctx->a, ctx->b); \
+ pre##_eblk(&ctx->k, ctx->a, ctx->a); \
+ \
+ /* Mask the CBC-MAC tag (which prevents the standard extension \
+ * attack) and store the result. \
+ */ \
+ BLKC_XSTORE(PRE, t, ctx->a, ctx->s0); \
+} \
+ \
+/* --- @pre_ccmencryptdone@ --- * \
+ * \
+ * Arguments: @pre_ccmctx *ctx@ = pointer to an CCM context \
+ * @buf *dst@ = buffer for remaining ciphertext \
+ * @void *tag@ = where to write the tag \
+ * @size_t tsz@ = length of tag to store \
+ * \
+ * Returns: Zero on success; @-1@ on failure. \
+ * \
+ * Use: Completes an CCM encryption operation. The @aad@ \
+ * pointer may be null if there is no additional \
+ * authenticated data. CCM doesn't buffer ciphertext, but \
+ * the output buffer is provided anyway for consistency \
+ * with other AEAD schemes which don't have this property; \
+ * the function will fail if the output buffer is broken. \
+ */ \
+ \
+int pre##_ccmencryptdone(pre##_ccmctx *ctx, buf *dst, \
+ void *tag, size_t tsz) \
+{ \
+ octet t[PRE##_BLKSZ]; \
+ \
+ /* Some initial checks. */ \
+ if (!BOK(dst)) return (-1); \
+ \
+ /* Calculate and return the tag. */ \
+ pre##_ccmtag(ctx, t, tsz); \
+ memcpy(tag, t, tsz); \
+ \
+ /* Done. */ \
+ return (0); \
+} \
+ \
+/* --- @pre_ccmdecryptdone@ --- * \
+ * \
+ * Arguments: @pre_ccmctx *ctx@ = pointer to an CCM context \
+ * @buf *dst@ = buffer for remaining plaintext \
+ * @const void *tag@ = tag to verify \
+ * @size_t tsz@ = length of tag \
+ * \
+ * Returns: @+1@ for complete success; @0@ if tag verification \
+ * failed; @-1@ for other kinds of errors. \
+ * \
+ * Use: Completes an CCM decryption operation. The @aad@ \
+ * pointer may be null if there is no additional \
+ * authenticated data. CCM doesn't buffer plaintext, but \
+ * the output buffer is provided anyway for consistency \
+ * with other AEAD schemes which don't have this property; \
+ * the function will fail if the output buffer is broken. \
+ */ \
+ \
+int pre##_ccmdecryptdone(pre##_ccmctx *ctx, buf *dst, \
+ const void *tag, size_t tsz) \
+{ \
+ octet t[PRE##_BLKSZ]; \
+ \
+ /* Some initial checks. */ \
+ if (!BOK(dst)) return (-1); \
+ \
+ /* Calculate and check the tag. */ \
+ pre##_ccmtag(ctx, t, tsz); \
+ if (!ct_memeq(tag, t, tsz)) return (0); \
+ else return (+1); \
+} \
+ \
+/* --- Generic AEAD interface --- */ \
+ \
+typedef struct gctx { \
+ gaead_aad a; \
+ pre##_ccmctx ctx; \
+} gctx; \
+ \
+static void gahash(gaead_aad *a, const void *h, size_t hsz) \
+ { gctx *ctx = (gctx *)a; pre##_ccmaadhash(&ctx->ctx, h, hsz); } \
+ \
+static void gadestroy(gaead_aad *a) { ; } \
+ \
+static const gaead_aadops gaops = \
+ { &pre##_ccm, 0, gahash, gadestroy }; \
+ \
+typedef struct gectx { \
+ gaead_enc e; \
+ gctx g; \
+} gectx; \
+ \
+static gaead_aad *geaad(gaead_enc *e) \
+ { gectx *enc = (gectx *)e; return (&enc->g.a); } \
+ \
+static int gereinit(gaead_enc *e, const void *n, size_t nsz, \
+ size_t hsz, size_t msz, size_t tsz) \
+{ \
+ gectx *enc = (gectx *)e; \
+ return (pre##_ccmreinit(&enc->g.ctx, n, nsz, hsz, msz, tsz)); \
+} \
+ \
+static int geenc(gaead_enc *e, const void *m, size_t msz, buf *b) \
+{ \
+ gectx *enc = (gectx *)e; \
+ return (pre##_ccmencrypt(&enc->g.ctx, m, msz, b)); \
+} \
+ \
+static int gedone(gaead_enc *e, const gaead_aad *a, \
+ buf *b, void *t, size_t tsz) \
+{ \
+ gectx *enc = (gectx *)e; \
+ assert((!a && !enc->g.ctx.p.hsz) || a == &enc->g.a); \
+ return (pre##_ccmencryptdone(&enc->g.ctx, b, t, tsz)); \
+} \
+ \
+static void gedestroy(gaead_enc *e) \
+ { gectx *enc = (gectx *)e; BURN(*enc); S_DESTROY(enc); } \
+ \
+static const gaead_encops geops = \
+ { &pre##_ccm, geaad, gereinit, geenc, gedone, gedestroy }; \
+ \
+typedef struct gdctx { \
+ gaead_dec d; \
+ gctx g; \
+} gdctx; \
+ \
+static gaead_aad *gdaad(gaead_dec *d) \
+ { gdctx *dec = (gdctx *)d; return (&dec->g.a); } \
+ \
+static int gdreinit(gaead_dec *d, const void *n, size_t nsz, \
+ size_t hsz, size_t csz, size_t tsz) \
+{ \
+ gdctx *dec = (gdctx *)d; \
+ return (pre##_ccmreinit(&dec->g.ctx, n, nsz, hsz, csz, tsz)); \
+} \
+ \
+static int gddec(gaead_dec *d, const void *c, size_t csz, buf *b) \
+{ \
+ gdctx *dec = (gdctx *)d; \
+ return (pre##_ccmdecrypt(&dec->g.ctx, c, csz, b)); \
+} \
+ \
+static int gddone(gaead_dec *d, const gaead_aad *a, \
+ buf *b, const void *t, size_t tsz) \
+{ \
+ gdctx *dec = (gdctx *)d; \
+ assert((!a && !dec->g.ctx.p.hsz) || a == &dec->g.a); \
+ return (pre##_ccmdecryptdone(&dec->g.ctx, b, t, tsz)); \
+} \
+ \
+static void gddestroy(gaead_dec *d) \
+ { gdctx *dec = (gdctx *)d; BURN(*dec); S_DESTROY(dec); } \
+ \
+static const gaead_decops gdops = \
+ { &pre##_ccm, gdaad, gdreinit, gddec, gddone, gddestroy }; \
+ \
+typedef struct gkctx { \
+ gaead_key k; \
+ pre##_ctx key; \
+} gkctx; \
+ \
+static gaead_enc *gkenc(const gaead_key *k, const void *n, size_t nsz, \
+ size_t hsz, size_t msz, size_t tsz) \
+{ \
+ gkctx *key = (gkctx *)k; \
+ gectx *enc = S_CREATE(gectx); \
+ \
+ enc->e.ops = &geops; enc->g.a.ops = &gaops; \
+ if (pre##_ccminit(&enc->g.ctx, &key->key, n, nsz, hsz, msz, tsz)) \
+ { gedestroy(&enc->e); return (0); } \
+ return (&enc->e); \
+} \
+ \
+static gaead_dec *gkdec(const gaead_key *k, const void *n, size_t nsz, \
+ size_t hsz, size_t csz, size_t tsz) \
+{ \
+ gkctx *key = (gkctx *)k; \
+ gdctx *dec = S_CREATE(gdctx); \
+ \
+ dec->d.ops = &gdops; dec->g.a.ops = &gaops; \
+ if (pre##_ccminit(&dec->g.ctx, &key->key, n, nsz, hsz, csz, tsz)) \
+ { gddestroy(&dec->d); return (0); } \
+ return (&dec->d); \
+} \
+ \
+static void gkdestroy(gaead_key *k) \
+ { gkctx *key = (gkctx *)k; BURN(*key); S_DESTROY(key); } \
+ \
+static const gaead_keyops gkops = \
+ { &pre##_ccm, 0, gkenc, gkdec, gkdestroy }; \
+ \
+static gaead_key *gckey(const void *k, size_t ksz) \
+{ \
+ gkctx *key = S_CREATE(gkctx); \
+ key->k.ops = &gkops; \
+ pre##_init(&key->key, k, ksz); \
+ return (&key->k); \
+} \
+ \
+const gcaead pre##_ccm = { \
+ name "-ccm", \
+ pre##_keysz, pre##_ccmnoncesz, pre##_ccmtagsz, \
+ PRE##_BLKSZ, 0, 0, \
+ AEADF_PCHSZ | AEADF_PCMSZ | AEADF_PCTSZ | \
+ AEADF_AADNDEP | AEADF_AADFIRST, \
+ gckey \
+}; \
+ \
+CCM_TESTX(PRE, pre, name, fname)
+
+/*----- Test rig ----------------------------------------------------------*/
+
+#define CCM_TEST(PRE, pre) CCM_TESTX(PRE, pre, #pre, #pre)
+
+/* --- @CCM_TEST@ --- *
+ *
+ * Arguments: @PRE, pre@ = prefixes for the underlying block cipher
+ *
+ * Use: Standard test rig for CCM functions.
+ */
+
+#ifdef TEST_RIG
+
+#include <stdio.h>
+
+#include <mLib/dstr.h>
+#include <mLib/quis.h>
+#include <mLib/testrig.h>
+
+#define CCM_TESTX(PRE, pre, name, fname) \
+ \
+static int ccmverify(dstr *v) \
+{ \
+ pre##_ctx key; \
+ pre##_ccmctx ctx; \
+ int ok = 1, win; \
+ int i; \
+ octet *p; \
+ int szs[] = { 1, 7, 192, -1, 0 }, *ip; \
+ size_t hsz, msz; \
+ dstr d = DSTR_INIT, t = DSTR_INIT; \
+ buf b; \
+ \
+ dstr_ensure(&d, v[4].len > v[3].len ? v[4].len : v[3].len); \
+ dstr_ensure(&t, v[5].len); t.len = v[5].len; \
+ \
+ pre##_init(&key, v[0].buf, v[0].len); \
+ \
+ for (ip = szs; *ip; ip++) { \
+ \
+ pre##_ccminit(&ctx, &key, (octet *)v[1].buf, v[1].len, \
+ v[2].len, v[3].len, v[5].len); \
+ \
+ i = *ip; \
+ hsz = v[2].len; \
+ if (i == -1) i = hsz; \
+ if (i > hsz) continue; \
+ p = (octet *)v[2].buf; \
+ while (hsz) { \
+ if (i > hsz) i = hsz; \
+ pre##_ccmaadhash(&ctx, p, i); \
+ p += i; hsz -= i; \
+ } \
+ \
+ buf_init(&b, d.buf, d.sz); \
+ i = *ip; \
+ msz = v[3].len; \
+ if (i == -1) i = msz; \
+ if (i > msz) continue; \
+ p = (octet *)v[3].buf; \
+ while (msz) { \
+ if (i > msz) i = msz; \
+ if (pre##_ccmencrypt(&ctx, p, i, &b)) { \
+ puts("!! ccmencrypt reports failure"); \
+ goto fail_enc; \
+ } \
+ p += i; msz -= i; \
+ } \
+ \
+ if (pre##_ccmencryptdone(&ctx, &b, (octet *)t.buf, t.len)) { \
+ puts("!! ccmencryptdone reports failure"); \
+ goto fail_enc; \
+ } \
+ d.len = BLEN(&b); \
+ \
+ if (d.len != v[4].len || \
+ memcmp(d.buf, v[4].buf, v[4].len) != 0 || \
+ memcmp(t.buf, v[5].buf, v[5].len) != 0) { \
+ fail_enc: \
+ printf("\nfail encrypt:\n\tstep = %i", *ip); \
+ fputs("\n\tkey = ", stdout); type_hex.dump(&v[0], stdout); \
+ fputs("\n\tnonce = ", stdout); type_hex.dump(&v[1], stdout); \
+ fputs("\n\theader = ", stdout); type_hex.dump(&v[2], stdout); \
+ fputs("\n\tmessage = ", stdout); type_hex.dump(&v[3], stdout); \
+ fputs("\n\texp ct = ", stdout); type_hex.dump(&v[4], stdout); \
+ fputs("\n\tcalc ct = ", stdout); type_hex.dump(&d, stdout); \
+ fputs("\n\texp tag = ", stdout); type_hex.dump(&v[5], stdout); \
+ fputs("\n\tcalc tag = ", stdout); type_hex.dump(&t, stdout); \
+ putchar('\n'); \
+ ok = 0; \
+ } \
+ \
+ pre##_ccminit(&ctx, &key, (octet *)v[1].buf, v[1].len, \
+ v[2].len, v[4].len, v[5].len); \
+ \
+ i = *ip; \
+ hsz = v[2].len; \
+ if (i == -1) i = hsz; \
+ if (i > hsz) continue; \
+ p = (octet *)v[2].buf; \
+ while (hsz) { \
+ if (i > hsz) i = hsz; \
+ pre##_ccmaadhash(&ctx, p, i); \
+ p += i; hsz -= i; \
+ } \
+ \
+ buf_init(&b, d.buf, d.sz); \
+ i = *ip; \
+ msz = v[4].len; \
+ if (i == -1) i = msz; \
+ if (i > msz) continue; \
+ p = (octet *)v[4].buf; \
+ while (msz) { \
+ if (i > msz) i = msz; \
+ if (pre##_ccmdecrypt(&ctx, p, i, &b)) { \
+ puts("!! ccmdecrypt reports failure"); \
+ win = 0; goto fail_dec; \
+ } \
+ p += i; msz -= i; \
+ } \
+ \
+ win = pre##_ccmdecryptdone(&ctx, &b, (octet *)v[5].buf, v[5].len); \
+ if (win < 0) { \
+ puts("!! ccmdecryptdone reports failure"); \
+ goto fail_dec; \
+ } \
+ d.len = BLEN(&b); \
+ \
+ if (d.len != v[3].len || !win || \
+ memcmp(d.buf, v[3].buf, v[3].len) != 0) { \
+ fail_dec: \
+ printf("\nfail decrypt:\n\tstep = %i", *ip); \
+ fputs("\n\tkey = ", stdout); type_hex.dump(&v[0], stdout); \
+ fputs("\n\tnonce = ", stdout); type_hex.dump(&v[1], stdout); \
+ fputs("\n\theader = ", stdout); type_hex.dump(&v[2], stdout); \
+ fputs("\n\tciphertext = ", stdout); type_hex.dump(&v[4], stdout); \
+ fputs("\n\texp pt = ", stdout); type_hex.dump(&v[3], stdout); \
+ fputs("\n\tcalc pt = ", stdout); type_hex.dump(&d, stdout); \
+ fputs("\n\ttag = ", stdout); type_hex.dump(&v[5], stdout); \
+ printf("\n\tverify %s", win ? "ok" : "FAILED"); \
+ putchar('\n'); \
+ ok = 0; \
+ } \
+ } \
+ \
+ dstr_destroy(&d); dstr_destroy(&t); \
+ return (ok); \
+} \
+ \
+static test_chunk aeaddefs[] = { \
+ { name "-ccm", ccmverify, \
+ { &type_hex, &type_hex, &type_hex, &type_hex, \
+ &type_hex, &type_hex, 0 } }, \
+ { 0, 0, { 0 } } \
+}; \
+ \
+int main(int argc, char *argv[]) \
+{ \
+ ego(argv[0]); \
+ test_run(argc, argv, aeaddefs, SRCDIR"/t/" fname); \
+ return (0); \
+}
+
+#else
+# define CCM_TESTX(PRE, pre, name, fname)
+#endif
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif