symm/ocb3.h, symm/ocb3-def.h: Implement the OCB3 auth'ned encryption mode.
[catacomb] / symm / ocb3-def.h
diff --git a/symm/ocb3-def.h b/symm/ocb3-def.h
new file mode 100644 (file)
index 0000000..14a22f6
--- /dev/null
@@ -0,0 +1,1006 @@
+/* -*-c-*-
+ *
+ * The OCB3 authenticated encryption mode
+ *
+ * (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_OCB3_DEF_H
+#define CATACOMB_OCB3_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_RSVR_H
+#  include "rsvr.h"
+#endif
+
+#ifndef CATACOMB_PARANOIA_H
+#  include "paranoia.h"
+#endif
+
+/*----- Macros ------------------------------------------------------------*/
+
+#define OCB3_TSHIFT(PRE) BLKC_GLUE(OCB3_TSHIFT_, BLKC_BITS(PRE))
+#define OCB3_TSHIFT_64 2
+#define OCB3_TSHIFT_96 1
+#define OCB3_TSHIFT_128 1
+#define OCB3_TSHIFT_192 0
+#define OCB3_TSHIFT_256 0
+
+#define OCB3_STRETCHMASK(PRE) BLKC_GLUE(OCB3_STRETCHMASK_, BLKC_BITS(PRE))
+#define OCB3_STRETCHMASK_64 0x1f
+#define OCB3_STRETCHMASK_96 0x3f
+#define OCB3_STRETCHMASK_128 0x3f
+#define OCB3_STRETCHMASK_192 0x7f
+#define OCB3_STRETCHMASK_256 0xff
+
+#define OCB3_STRETCHSHIFT(PRE) BLKC_GLUE(OCB3_STRETCHSHIFT_, BLKC_BITS(PRE))
+#define OCB3_STRETCHSHIFT_64 25
+#define OCB3_STRETCHSHIFT_96 33
+#define OCB3_STRETCHSHIFT_128 8
+#define OCB3_STRETCHSHIFT_192 40
+#define OCB3_STRETCHSHIFT_256 1
+
+/* --- @OCB3_DEF@ --- *
+ *
+ * Arguments:  @PRE@, @pre@ = prefixes for the underlying block cipher
+ *
+ * Use:                Creates an implementation for the OCB3 authenticated-
+ *             encryption mode.
+ */
+
+#define OCB3_DEF(PRE, pre) OCB3_DEFX(PRE, pre, #pre, #pre)
+
+#define OCB3_DEFX(PRE, pre, name, fname)                               \
+                                                                       \
+static const rsvr_policy pre##_ocb3policy =                            \
+  { 0, PRE##_BLKSZ, PRE##_BLKSZ };                                     \
+                                                                       \
+const octet                                                            \
+  pre##_ocb3noncesz[] = { KSZ_RANGE, OCB3_NSZMAX(PRE),                 \
+                         0, OCB3_NSZMAX(PRE), 1 },                     \
+  pre##_ocb3tagsz[] = { KSZ_RANGE, PRE##_BLKSZ, 0, PRE##_BLKSZ, 1 };   \
+                                                                       \
+/* --- @pre_ocb3setkey@ --- *                                          \
+ *                                                                     \
+ * Arguments:  @pre_ocb3key *key@ = pointer to OCB3 key block          \
+ *             @ocnst void *k@ = pointer to key material               \
+ *             @size_t ksz@ = size of key material                     \
+ *                                                                     \
+ * Returns:    ---                                                     \
+ *                                                                     \
+ * Use:                Initializes a OCB3 key.  This can be used for           \
+ *             several encryption/or MAC operations.                   \
+ */                                                                    \
+                                                                       \
+void pre##_ocb3setkey(pre##_ocb3key *key, const void *k, size_t ksz)   \
+{                                                                      \
+  unsigned i;                                                          \
+                                                                       \
+  pre##_init(&key->ctx, k, ksz);                                       \
+  BLKC_ZERO(PRE, key->lstar);                                          \
+  pre##_eblk(&key->ctx, key->lstar, key->lstar);                       \
+  BLKC_BLSHIFT(PRE, IRRED, key->ldollar, key->lstar);                  \
+  BLKC_BLSHIFT(PRE, IRRED, key->lmask[0], key->ldollar);               \
+  for (i = 1; i < OCB_NCALC; i++)                                      \
+    BLKC_BLSHIFT(PRE, IRRED, key->lmask[i], key->lmask[i - 1]);                \
+}                                                                      \
+                                                                       \
+/* --- @pre_ocb3aadinit@ --- *                                         \
+ *                                                                     \
+ * Arguments:  @pre_ocb3aadctx *aad@ = pointer to context block        \
+ *             @pre_ocb3key *k@ = key block                            \
+ *                                                                     \
+ * Returns:    ---                                                     \
+ *                                                                     \
+ * Use:                Initializes an OCB3 AAD (`additional authenticated      \
+ *             data') context associated witha a given key.            \
+ *             AAD contexts can be copied and/or reused, saving time   \
+ *             if the AAD for a number of messages has a common        \
+ *             prefix.                                                 \
+ *                                                                     \
+ *             The @key@ doesn't need to be kept around.               \
+ */                                                                    \
+                                                                       \
+void pre##_ocb3aadinit(pre##_ocb3aadctx *aad, const pre##_ocb3key *k)  \
+{                                                                      \
+  aad->k = *k;                                                         \
+  aad->off = 0; aad->i = 1;                                            \
+  BLKC_ZERO(PRE, aad->a);                                              \
+  BLKC_ZERO(PRE, aad->o);                                              \
+}                                                                      \
+                                                                       \
+/* --- @pre_ocb3aadhash@ --- *                                         \
+ *                                                                     \
+ * Arguments:  @pre_ocb3aadctx *aad@ = pointer to context block        \
+ *             @ocnst void *p@ = pointer to message buffer             \
+ *             @size_t sz@ = size of message buffer                    \
+ *                                                                     \
+ * Returns:    ---                                                     \
+ *                                                                     \
+ * Use:                Hashes some AAD input data.                             \
+ */                                                                    \
+                                                                       \
+void pre##_ocb3aadhash(pre##_ocb3aadctx *aad, const void *p, size_t sz)        \
+{                                                                      \
+  rsvr_state st;                                                       \
+  uint32 t[PRE##_BLKSZ/4];                                             \
+  const octet *q;                                                      \
+                                                                       \
+  rsvr_setup(&st, &pre##_ocb3policy, aad->b, &aad->off, p, sz);                \
+  RSVR_DO(&st) while ((q = RSVR_NEXT(&st, PRE##_BLKSZ)) != 0) {                \
+    OCB_OFFSET(PRE, aad->o, aad->k.lmask, aad->i++);                   \
+    BLKC_LOAD(PRE, t, q); BLKC_XMOVE(PRE, t, aad->o);                  \
+    pre##_eblk(&aad->k.ctx, t, t);                                     \
+    BLKC_XMOVE(PRE, aad->a, t);                                                \
+  }                                                                    \
+}                                                                      \
+                                                                       \
+/* --- @pre_ocb3augment@ --- *                                         \
+ *                                                                     \
+ * Arguments:  @uint32 *nn@ = where to write the augmented nonce       \
+ *             @const octet *n@ = pointer to input nonce data          \
+ *             @size_t nsz@ = size of input nonce                      \
+ *             @size_t tsz@ = tag length                               \
+ *                                                                     \
+ * Returns:    The nonce shift index.                                  \
+ *                                                                     \
+ * Use:                Constructs the augmented base nonce, mixing in the tag  \
+ *             length appropriately.                                   \
+ */                                                                    \
+                                                                       \
+static unsigned pre##_ocb3augment(uint32 *nn, const octet *n,          \
+                                 size_t nsz, size_t tsz)               \
+{                                                                      \
+  octet b[PRE##_BLKSZ] = { 0 };                                                \
+  uint32 t;                                                            \
+  unsigned nix;                                                                \
+                                                                       \
+  b[0] = 8*(tsz%PRE##_BLKSZ) << OCB3_TSHIFT(PRE);                      \
+  b[PRE##_BLKSZ - nsz - 1] |= 0x01;                                    \
+  memcpy(b + PRE##_BLKSZ - nsz, n, nsz);                               \
+  BLKC_LOAD(PRE, nn, b);                                               \
+  t = BLKC_BWORD(PRE, nn[PRE##_BLKSZ/4 - 1]);                          \
+  nix = t&OCB3_STRETCHMASK(PRE);                                       \
+  t &= ~(uint32)OCB3_STRETCHMASK(PRE);                                 \
+  nn[PRE##_BLKSZ/4 - 1] = BLKC_BWORD(PRE, t);                          \
+  return (nix);                                                                \
+}                                                                      \
+                                                                       \
+/* --- @pre_ocb3stretch@ --- *                                         \
+ *                                                                     \
+ * Arguments:  @pre_ocb3ctx *ctx@ = pointer to OCB3 context            \
+ *                                                                     \
+ * Returns:    ---                                                     \
+ *                                                                     \
+ * Use:                Stretches the augmented nonce.                          \
+ */                                                                    \
+                                                                       \
+static void pre##_ocb3stretch(pre##_ocb3ctx *ctx)                      \
+{                                                                      \
+  unsigned nw = OCB3_STRETCHSHIFT(PRE)/32,                             \
+    nl = OCB3_STRETCHSHIFT(PRE)%32, nr = 32 - nl;                      \
+  unsigned i;                                                          \
+  uint32 c = 0, t, u;                                                  \
+                                                                       \
+  pre##_eblk(&ctx->k.ctx, ctx->nbase, ctx->nstretch);                  \
+  for (i = 0; i < PRE##_BLKSZ/4; i++)                                  \
+    ctx->nstretch[i] = BLKC_BWORD(PRE, ctx->nstretch[i]);              \
+  i = PRE##_BLKSZ/4;                                                   \
+  while (i > PRE##_BLKSZ/4 - nw)                                       \
+    { i--; ctx->nstretch[i + PRE##_BLKSZ/4] = ctx->nstretch[i]; }      \
+  while (i--) {                                                                \
+    u = ctx->nstretch[i]; t = ctx->nstretch[i + nw];                   \
+    ctx->nstretch[i + PRE##_BLKSZ/4] = u ^ (t << nl) ^ c;              \
+    if (nr < 32) c = U32(t) >> nr;                                     \
+  }                                                                    \
+}                                                                      \
+                                                                       \
+/* --- @pre_ocb3shift@ --- *                                           \
+ *                                                                     \
+ * Arguments:  @pre_ocb3ctx *ctx@ = pointer to OCB3 context            \
+ *             @unsigned nix@ = nonce index                            \
+ *                                                                     \
+ * Returns:    ---                                                     \
+ *                                                                     \
+ * Use:                Extracts a chunk out of the OCB3 stretched nonce and    \
+ *             writes it to @ctx->o@.                                  \
+ */                                                                    \
+                                                                       \
+static void pre##_ocb3shift(pre##_ocb3ctx *ctx, unsigned nix)          \
+{                                                                      \
+  unsigned nw = nix/32, nl = nix%32, nr = 32 - nl;                     \
+  uint32 c, t;                                                         \
+  unsigned i;                                                          \
+                                                                       \
+  i = PRE##_BLKSZ/4;                                                   \
+  if (nr < 32) c = U32(ctx->nstretch[PRE##_BLKSZ/4 + nw]) >> nr;       \
+  else c = 0;                                                          \
+  while (i--) {                                                                \
+    t = ctx->nstretch[i + nw];                                         \
+    ctx->o[i] = BLKC_BWORD(PRE, (t << nl) | c);                                \
+    if (nr < 32) c = U32(t) >> nr;                                     \
+  }                                                                    \
+}                                                                      \
+                                                                       \
+/* --- @pre_ocb3init@ --- *                                            \
+ *                                                                     \
+ * Arguments:  @pre_ocb3ctx *ctx@ = pointer to OCB3 context            \
+ *             @const pre_ocb3key *key@ = pointer to key block         \
+ *             @const void *n@ = pointer to nonce                      \
+ *             @size_t nsz@ = size of nonce                            \
+ *             @size_t tsz@ = tag length                               \
+ *                                                                     \
+ * Returns:    Zero on success, @-1@ if the nonce or tag length is     \
+ *             bad.                                                    \
+ *                                                                     \
+ * Use:                Initialize an OCB3 operation context with a given key.  \
+ *                                                                     \
+ *             The original key needn't be kept around any more.       \
+ */                                                                    \
+                                                                       \
+int pre##_ocb3init(pre##_ocb3ctx *ctx, const pre##_ocb3key *k,         \
+                  const void *n, size_t nsz, size_t tsz)               \
+{                                                                      \
+  unsigned nix;                                                                \
+                                                                       \
+  /* Preflight checking. */                                            \
+  if (nsz > OCB3_NSZMAX(PRE)) return (-1);                             \
+  if (tsz > PRE##_BLKSZ) return (-1);                                  \
+                                                                       \
+  /* Copy over the blockcipher key. */                                 \
+  ctx->k = *k;                                                         \
+                                                                       \
+  /* Sort out the nonce. */                                            \
+  nix = pre##_ocb3augment(ctx->nbase, n, nsz, tsz);                    \
+  ctx->nix = nix; ctx->tsz = tsz;                                      \
+  pre##_ocb3stretch(ctx);                                              \
+  pre##_ocb3shift(ctx, nix);                                           \
+                                                                       \
+  /* Other random things. */                                           \
+  ctx->off = 0;        ctx->i = 1;                                             \
+  BLKC_ZERO(PRE, ctx->a);                                              \
+                                                                       \
+  /* Done. */                                                          \
+  return (0);                                                          \
+}                                                                      \
+                                                                       \
+/* --- @pre_ocb3reinit@ --- *                                          \
+ *                                                                     \
+ * Arguments:  @pre_ocb3ctx *ctx@ = pointer to OCB3 context            \
+ *             @const void *n@ = pointer to nonce                      \
+ *             @size_t nsz@ = size of nonce                            \
+ *             @size_t tsz@ = tag length                               \
+ *                                                                     \
+ * Returns:    Zero on success, @-1@ if the nonce or tag length is     \
+ *             bad.                                                    \
+ *                                                                     \
+ * Use:                Reinitialize an OCB3 operation context, changing the    \
+ *             nonce and/or tag length.                                \
+ */                                                                    \
+                                                                       \
+int pre##_ocb3reinit(pre##_ocb3ctx *ctx,                               \
+                    const void *n, size_t nsz, size_t tsz)             \
+{                                                                      \
+  uint32 t[PRE##_BLKSZ/4];                                             \
+  unsigned nix, i;                                                     \
+                                                                       \
+  /* Preflight checking. */                                            \
+  if (nsz > OCB3_NSZMAX(PRE)) return (-1);                             \
+  if (tsz > PRE##_BLKSZ) return (-1);                                  \
+                                                                       \
+  /* Sort out the nonce. */                                            \
+  nix = pre##_ocb3augment(t, n, nsz, tsz);                             \
+  for (i = 0; i < PRE##_BLKSZ/4; i++) {                                        \
+    if (t[i] == ctx->nbase[i]) continue;                               \
+    ctx->nix = nix; ctx->tsz = tsz;                                    \
+    BLKC_MOVE(PRE, ctx->nbase, t); pre##_ocb3stretch(ctx);             \
+    break;                                                             \
+  }                                                                    \
+  pre##_ocb3shift(ctx, nix);                                           \
+                                                                       \
+  /* Other random things. */                                           \
+  ctx->off = 0;        ctx->i = 1;                                             \
+  BLKC_ZERO(PRE, ctx->a);                                              \
+                                                                       \
+  /* Done. */                                                          \
+  return (0);                                                          \
+}                                                                      \
+                                                                       \
+/* --- @pre_ocb3step@ --- *                                            \
+ *                                                                     \
+ * Arguments:  @pre_ocb3ctx *ctx@ = pointer to OCB3 context            \
+ *                                                                     \
+ * Returns:    ---                                                     \
+ *                                                                     \
+ * Use:                Reinitialize an OCB3 operation context, stepping to     \
+ *             the `next' nonce along.                                 \
+ */                                                                    \
+                                                                       \
+void pre##_ocb3step(pre##_ocb3ctx *ctx)                                        \
+{                                                                      \
+  /* Sort out the nonce. */                                            \
+  if (ctx->nix < OCB3_STRETCHMASK(PRE))                                        \
+    pre##_ocb3shift(ctx, ++ctx->nix);                                  \
+  else {                                                               \
+    ctx->nix = 0;                                                      \
+    BLKC_BADD(PRE, ctx->nbase, OCB3_STRETCHMASK(PRE) + 1);             \
+    pre##_ocb3stretch(ctx);                                            \
+    pre##_ocb3shift(ctx, 0);                                           \
+  }                                                                    \
+                                                                       \
+  /* Other random things. */                                           \
+  ctx->off = 0;        ctx->i = 1;                                             \
+  BLKC_ZERO(PRE, ctx->a);                                              \
+}                                                                      \
+                                                                       \
+/* --- @pre_ocb3encrypt@ --- *                                         \
+ *                                                                     \
+ * Arguments:  @pre_ocb3ctx *ctx@ = pointer to OCB3 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.                                    \
+ *                                                                     \
+ *             Note that OCB3 delays output if its input is not a      \
+ *             whole number of blocks.  This means that the output     \
+ *             might be smaller or larger the input by up to the block \
+ *             size.                                                   \
+ */                                                                    \
+                                                                       \
+int pre##_ocb3encrypt(pre##_ocb3ctx *ctx,                              \
+                     const void *src, size_t sz, buf *dst)             \
+{                                                                      \
+  rsvr_state st;                                                       \
+  size_t osz;                                                          \
+  uint32 t[PRE##_BLKSZ/4];                                             \
+  const octet *p;                                                      \
+  octet *q;                                                            \
+                                                                       \
+  /* Figure out what we're going to do. */                             \
+  rsvr_setup(&st, &pre##_ocb3policy, ctx->b, &ctx->off, src, sz);      \
+                                                                       \
+  /* Determine the output size and verify that there is enough         \
+   * space.                                                            \
+   */                                                                  \
+  osz = st.plan.from_rsvr + st.plan.from_input;                                \
+  if (!osz) q = 0;                                                     \
+  else { q = buf_get(dst, osz); if (!q) return (-1); }                 \
+                                                                       \
+  /* Process the input in whole blocks at a time. */                   \
+  RSVR_DO(&st) while ((p = RSVR_NEXT(&st, PRE##_BLKSZ)) != 0) {                \
+    OCB_OFFSET(PRE, ctx->o, ctx->k.lmask, ctx->i++);                   \
+    BLKC_LOAD(PRE, t, p); BLKC_XMOVE(PRE, ctx->a, t);                  \
+    BLKC_XMOVE(PRE, t, ctx->o); pre##_eblk(&ctx->k.ctx, t, t);         \
+    BLKC_XSTORE(PRE, q, t, ctx->o); q += PRE##_BLKSZ;                  \
+  }                                                                    \
+                                                                       \
+  /* Done. */                                                          \
+  return (0);                                                          \
+}                                                                      \
+                                                                       \
+/* --- @pre_ocb3decrypt@ --- *                                         \
+ *                                                                     \
+ * Arguments:  @pre_ocb3ctx *ctx@ = pointer to OCB3 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.                                    \
+ *                                                                     \
+ *             Note that OCB3 delays output if its input is not a      \
+ *             whole number of blocks.  This means that the output     \
+ *             might be smaller or larger the input by up to the block \
+ *             size.                                                   \
+ */                                                                    \
+                                                                       \
+int pre##_ocb3decrypt(pre##_ocb3ctx *ctx,                              \
+                     const void *src, size_t sz, buf *dst)             \
+{                                                                      \
+  rsvr_state st;                                                       \
+  size_t osz;                                                          \
+  uint32 t[PRE##_BLKSZ/4];                                             \
+  const octet *p;                                                      \
+  octet *q;                                                            \
+                                                                       \
+  /* Figure out what we're going to do. */                             \
+  rsvr_setup(&st, &pre##_ocb3policy, ctx->b, &ctx->off, src, sz);      \
+                                                                       \
+  /* Determine the output size and verify that there is enough         \
+   * space.                                                            \
+   */                                                                  \
+  osz = st.plan.from_rsvr + st.plan.from_input;                                \
+  if (!osz) q = 0;                                                     \
+  else { q = buf_get(dst, osz); if (!q) return (-1); }                 \
+                                                                       \
+  /* Process the input in whole blocks at a time. */                   \
+  RSVR_DO(&st) while ((p = RSVR_NEXT(&st, PRE##_BLKSZ)) != 0) {                \
+    OCB_OFFSET(PRE, ctx->o, ctx->k.lmask, ctx->i++);                   \
+    BLKC_LOAD(PRE, t, p);                                              \
+    BLKC_XMOVE(PRE, t, ctx->o); pre##_dblk(&ctx->k.ctx, t, t);         \
+    BLKC_XMOVE(PRE, t, ctx->o); BLKC_XMOVE(PRE, ctx->a, t);            \
+    BLKC_STORE(PRE, q, t); q += PRE##_BLKSZ;                           \
+  }                                                                    \
+                                                                       \
+  /* Done. */                                                          \
+  return (0);                                                          \
+}                                                                      \
+                                                                       \
+/* --- @pre_ocb3tag@ --- *                                             \
+ *                                                                     \
+ * Arguments:  @pre_ocb3ctx *ctx@ = pointer to an OCB3 context         \
+ *             @const pre_ocb3aadctx *aad@ = pointer to AAD context,   \
+ *                     or null                                         \
+ *             @buf *dst@ = buffer for remaining ciphertext            \
+ *                                                                     \
+ * Returns:    Zero on success; @-1@ on failure.                       \
+ *                                                                     \
+ * Use:                Common end-of-message handling for encryption and       \
+ *             decryption.  The caller is expected to have processed   \
+ *             the last partial block, mixing the padded plaintext     \
+ *             into the checksum and leaving the partial output in     \
+ *             the context's buffer: this function will write the      \
+ *             output to the caller's output buffer.  It will compute  \
+ *             the final full-length tag and leave it in the           \
+ *             context's buffer.                                       \
+ */                                                                    \
+                                                                       \
+static int pre##_ocb3tag(pre##_ocb3ctx *ctx,                           \
+                        const pre##_ocb3aadctx *aad, buf *dst)         \
+{                                                                      \
+  octet *q;                                                            \
+                                                                       \
+  /* Arrange space for the final output (if any). */                   \
+  if (!ctx->off) { q = 0; if (!BOK(dst)) return (-1); }                        \
+  else { q = buf_get(dst, ctx->off); if (!q) return (-1); }            \
+                                                                       \
+  /* Deal with whatever's left in the input buffer, if anything */     \
+  if (ctx->off) memcpy(q, ctx->b, ctx->off);                           \
+                                                                       \
+  /* Wrap up the checksum calculation. */                              \
+  BLKC_XMOVE(PRE, ctx->o, ctx->k.ldollar);                             \
+  BLKC_XMOVE(PRE, ctx->a, ctx->o);                                     \
+  pre##_eblk(&ctx->k.ctx, ctx->a, ctx->a);                             \
+                                                                       \
+  /* Finish off the AAD processing. */                                 \
+  if (aad) {                                                           \
+    if (aad->i) BLKC_XMOVE(PRE, ctx->a, aad->a);                       \
+    if (aad->off) {                                                    \
+      memcpy(ctx->b, aad->b, aad->off);                                        \
+      ctx->b[aad->off] = 0x80;                                         \
+      memset(ctx->b + aad->off + 1, 0, PRE##_BLKSZ - aad->off - 1);    \
+      BLKC_LOAD(PRE, ctx->o, ctx->b);                                  \
+      BLKC_XMOVE(PRE, ctx->o, aad->o);                                 \
+      BLKC_XMOVE(PRE, ctx->o, ctx->k.lstar);                           \
+      pre##_eblk(&ctx->k.ctx, ctx->o, ctx->o);                         \
+      BLKC_XMOVE(PRE, ctx->a, ctx->o);                                 \
+    }                                                                  \
+  }                                                                    \
+                                                                       \
+  /* Write the final tag. */                                           \
+  BLKC_STORE(PRE, ctx->b, ctx->a);                                     \
+                                                                       \
+  /* Done. */                                                          \
+  return (0);                                                          \
+}                                                                      \
+                                                                       \
+/* --- @pre_ocb3encryptdone@ --- *                                     \
+ *                                                                     \
+ * Arguments:  @pre_ocb3ctx *ctx@ = pointer to an OCB3 context         \
+ *             @const pre_ocb3aadctx *aad@ = pointer to AAD context,   \
+ *                     or null                                         \
+ *             @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 OCB3 encryption operation.  The @aad@      \
+ *             pointer may be null if there is no additional           \
+ *             authenticated data.  OCB3 delays output, so this will   \
+ *             cause any remaining buffered plaintext to be encrypted  \
+ *             and written to @dst@.  Anyway, the function will fail   \
+ *             if the output buffer is broken.                         \
+ */                                                                    \
+                                                                       \
+int pre##_ocb3encryptdone(pre##_ocb3ctx *ctx,                          \
+                         const pre##_ocb3aadctx *aad, buf *dst,        \
+                         void *tag, size_t tsz)                        \
+{                                                                      \
+  uint32 t[PRE##_BLKSZ/4], u[PRE##_BLKSZ];                             \
+                                                                       \
+  /* Deal with any final partial block. */                             \
+  if (ctx->off) {                                                      \
+    BLKC_XMOVE(PRE, ctx->o, ctx->k.lstar);                             \
+    ctx->b[ctx->off] = 0x80;                                           \
+    memset(ctx->b + ctx->off + 1, 0, PRE##_BLKSZ - ctx->off - 1);      \
+    BLKC_LOAD(PRE, t, ctx->b);                                         \
+    BLKC_XMOVE(PRE, ctx->a, t);                                                \
+    pre##_eblk(&ctx->k.ctx, ctx->o, u);                                        \
+    BLKC_XSTORE(PRE, ctx->b, t, u);                                    \
+  }                                                                    \
+                                                                       \
+  /* Return the tag. */                                                        \
+  assert(tsz == ctx->tsz);                                             \
+  if (pre##_ocb3tag(ctx, aad, dst)) return (-1);                       \
+  memcpy(tag, ctx->b, tsz);                                            \
+  return (0);                                                          \
+}                                                                      \
+                                                                       \
+/* --- @pre_ocb3decryptdone@ --- *                                     \
+ *                                                                     \
+ * Arguments:  @pre_ocb3ctx *ctx@ = pointer to an OCB3 context         \
+ *             @const pre_ocb3aadctx *aad@ = pointer to AAD context,   \
+ *                     or null                                         \
+ *             @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 OCB3 decryption operation.  The @aad@      \
+ *             pointer may be null if there is no additional           \
+ *             authenticated data.  OCB3 delays output, so this will   \
+ *             cause any remaining buffered ciphertext to be decrypted \
+ *             and written to @dst@.  Anyway, the function will fail   \
+ *             if the output buffer is broken.                         \
+ */                                                                    \
+                                                                       \
+int pre##_ocb3decryptdone(pre##_ocb3ctx *ctx,                          \
+                         const pre##_ocb3aadctx *aad, buf *dst,        \
+                         const void *tag, size_t tsz)                  \
+{                                                                      \
+  uint32 t[PRE##_BLKSZ/4], u[PRE##_BLKSZ];                             \
+                                                                       \
+  /* Deal with any final partial block. */                             \
+  if (ctx->off) {                                                      \
+    BLKC_XMOVE(PRE, ctx->o, ctx->k.lstar);                             \
+    BLKC_LOAD(PRE, t, ctx->b);                                         \
+    pre##_eblk(&ctx->k.ctx, ctx->o, u);                                        \
+    BLKC_XSTORE(PRE, ctx->b, t, u);                                    \
+    ctx->b[ctx->off] = 0x80;                                           \
+    memset(ctx->b + ctx->off + 1, 0, PRE##_BLKSZ - ctx->off - 1);      \
+    BLKC_XLOAD(PRE, ctx->a, ctx->b);                                   \
+  }                                                                    \
+                                                                       \
+  /* Check the tag. */                                                 \
+  assert(tsz == ctx->tsz);                                             \
+  if (pre##_ocb3tag(ctx, aad, dst)) return (-1);                       \
+  if (ct_memeq(tag, ctx->b, ctx->tsz)) return (+1);                    \
+  else return (0);                                                     \
+}                                                                      \
+                                                                       \
+/* --- Generic AEAD interface --- */                                   \
+                                                                       \
+typedef struct gactx {                                                 \
+  gaead_aad a;                                                         \
+  pre##_ocb3aadctx aad;                                                        \
+} gactx;                                                               \
+                                                                       \
+static gaead_aad *gadup(const gaead_aad *a)                            \
+  { gactx *aad = S_CREATE(gactx); *aad = *(gactx *)a; return (&aad->a); } \
+                                                                       \
+static void gahash(gaead_aad *a, const void *h, size_t hsz)            \
+  { gactx *aad = (gactx *)a; pre##_ocb3aadhash(&aad->aad, h, hsz); }   \
+                                                                       \
+static void gadestroy(gaead_aad *a)                                    \
+  { gactx *aad = (gactx *)a; BURN(*aad); S_DESTROY(aad); }             \
+                                                                       \
+static const gaead_aadops gaops =                                      \
+  { &pre##_ocb3, gadup, gahash, gadestroy };                           \
+                                                                       \
+static gaead_aad *gaad(const pre##_ocb3key *k)                         \
+{                                                                      \
+  gactx *aad = S_CREATE(gactx);                                                \
+  aad->a.ops = &gaops;                                                 \
+  pre##_ocb3aadinit(&aad->aad, k);                                     \
+  return (&aad->a);                                                    \
+}                                                                      \
+                                                                       \
+typedef struct gectx {                                                 \
+  gaead_enc e;                                                         \
+  pre##_ocb3ctx ctx;                                                   \
+} gectx;                                                               \
+                                                                       \
+static gaead_aad *geaad(gaead_enc *e)                                  \
+  { gectx *enc = (gectx *)e; return (gaad(&enc->ctx.k)); }             \
+                                                                       \
+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##_ocb3reinit(&enc->ctx, n, nsz, tsz));                   \
+}                                                                      \
+                                                                       \
+static int geenc(gaead_enc *e, const void *m, size_t msz, buf *b)      \
+{                                                                      \
+  gectx *enc = (gectx *)e;                                             \
+  return (pre##_ocb3encrypt(&enc->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; gactx *aad = (gactx *)a;                    \
+  assert(!a || a->ops == &gaops);                                      \
+  return (pre##_ocb3encryptdone(&enc->ctx, a ? &aad->aad : 0, b, t, tsz)); \
+}                                                                      \
+                                                                       \
+static void gedestroy(gaead_enc *e)                                    \
+  { gectx *enc = (gectx *)e; BURN(*enc); S_DESTROY(enc); }             \
+                                                                       \
+static const gaead_encops geops =                                      \
+  { &pre##_ocb3, geaad, gereinit, geenc, gedone, gedestroy };          \
+                                                                       \
+typedef struct gdctx {                                                 \
+  gaead_dec d;                                                         \
+  pre##_ocb3ctx ctx;                                                   \
+} gdctx;                                                               \
+                                                                       \
+static gaead_aad *gdaad(gaead_dec *d)                                  \
+  { gdctx *dec = (gdctx *)d; return (gaad(&dec->ctx.k)); }             \
+                                                                       \
+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##_ocb3reinit(&dec->ctx, n, nsz, tsz));                   \
+}                                                                      \
+                                                                       \
+static int gddec(gaead_dec *d, const void *c, size_t csz, buf *b)      \
+{                                                                      \
+  gdctx *dec = (gdctx *)d;                                             \
+  return (pre##_ocb3decrypt(&dec->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; gactx *aad = (gactx *)a;                    \
+  assert(!a || a->ops == &gaops);                                      \
+  return (pre##_ocb3decryptdone(&dec->ctx, a ? &aad->aad : 0, b, t, tsz)); \
+}                                                                      \
+                                                                       \
+static void gddestroy(gaead_dec *d)                                    \
+  { gdctx *dec = (gdctx *)d; BURN(*dec); S_DESTROY(dec); }             \
+                                                                       \
+static const gaead_decops gdops =                                      \
+  { &pre##_ocb3, gdaad, gdreinit, gddec, gddone, gddestroy };          \
+                                                                       \
+typedef struct gkctx {                                                 \
+  gaead_key k;                                                         \
+  pre##_ocb3key key;                                                   \
+} gkctx;                                                               \
+                                                                       \
+static gaead_aad *gkaad(const gaead_key *k)                            \
+  { gkctx *key = (gkctx *)k; return (gaad(&key->key)); }               \
+                                                                       \
+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;                                                 \
+  if (pre##_ocb3init(&enc->ctx, &key->key, n, nsz, 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;                                                 \
+  if (pre##_ocb3init(&dec->ctx, &key->key, n, nsz, 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##_ocb3, gkaad, gkenc, gkdec, gkdestroy };                     \
+                                                                       \
+static gaead_key *gckey(const void *k, size_t ksz)                     \
+{                                                                      \
+  gkctx *key = S_CREATE(gkctx);                                                \
+  key->k.ops = &gkops;                                                 \
+  pre##_ocb3setkey(&key->key, k, ksz);                                 \
+  return (&key->k);                                                    \
+}                                                                      \
+                                                                       \
+const gcaead pre##_ocb3 = {                                            \
+  name "-ocb3",                                                                \
+  pre##_keysz, pre##_ocb3noncesz, pre##_ocb3tagsz,                     \
+  PRE##_BLKSZ, PRE##_BLKSZ - 1, 0, AEADF_PCTSZ,                                \
+  gckey                                                                        \
+};                                                                     \
+                                                                       \
+OCB3_TESTX(PRE, pre, name, fname)
+
+/*----- Test rig ----------------------------------------------------------*/
+
+#define OCB3_TEST(PRE, pre) OCB3_TESTX(PRE, pre, #pre, #pre)
+
+/* --- @OCB3_TEST@ --- *
+ *
+ * Arguments:  @PRE, pre@ = prefixes for the underlying block cipher
+ *
+ * Use:                Standard test rig for OCB3 functions.
+ */
+
+#ifdef TEST_RIG
+
+#include <stdio.h>
+
+#include <mLib/dstr.h>
+#include <mLib/quis.h>
+#include <mLib/testrig.h>
+
+#define OCB3_TESTX(PRE, pre, name, fname)                              \
+                                                                       \
+static int ocb3verify(dstr *v)                                         \
+{                                                                      \
+  pre##_ocb3key key;                                                   \
+  pre##_ocb3aadctx aad;                                                        \
+  pre##_ocb3ctx 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##_ocb3setkey(&key, v[0].buf, v[0].len);                          \
+                                                                       \
+  for (ip = szs; *ip; ip++) {                                          \
+                                                                       \
+    pre##_ocb3init(&ctx, &key, (octet *)v[1].buf, v[1].len, v[5].len); \
+                                                                       \
+    i = *ip;                                                           \
+    hsz = v[2].len;                                                    \
+    if (i == -1) i = hsz;                                              \
+    if (i > hsz) continue;                                             \
+    p = (octet *)v[2].buf;                                             \
+    pre##_ocb3aadinit(&aad, &key);                                     \
+    while (hsz) {                                                      \
+      if (i > hsz) i = hsz;                                            \
+      pre##_ocb3aadhash(&aad, 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##_ocb3encrypt(&ctx, p, i, &b)) {                         \
+       puts("!! ocb3encrypt reports failure");                         \
+       goto fail_enc;                                                  \
+      }                                                                        \
+      p += i; msz -= i;                                                        \
+    }                                                                  \
+                                                                       \
+    if (pre##_ocb3encryptdone(&ctx, &aad, &b, (octet *)t.buf, t.len)) {        \
+      puts("!! ocb3encryptdone 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##_ocb3init(&ctx, &key, (octet *)v[1].buf, v[1].len, v[5].len); \
+                                                                       \
+    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##_ocb3decrypt(&ctx, p, i, &b)) {                         \
+       puts("!! ocb3decrypt reports failure");                         \
+       win = 0; goto fail_dec;                                         \
+      }                                                                        \
+      p += i; msz -= i;                                                        \
+    }                                                                  \
+                                                                       \
+    win = pre##_ocb3decryptdone(&ctx, &aad, &b,                                \
+                              (octet *)v[5].buf, v[5].len);            \
+    if (win < 0) {                                                     \
+      puts("!! ocb3decryptdone 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 int ocb3mct(dstr *v)                                            \
+{                                                                      \
+  unsigned ksz = *(unsigned long *)v[0].buf, tsz = v[1].len;           \
+  dstr d = DSTR_INIT;                                                  \
+  octet z[128];                                                                \
+  pre##_ocb3key k;                                                     \
+  pre##_ocb3ctx ctx;                                                   \
+  pre##_ocb3aadctx oaad, iaad;                                         \
+  int rc;                                                              \
+  buf b;                                                               \
+  unsigned i;                                                          \
+  int ok = 1;                                                          \
+                                                                       \
+  dstr_ensure(&d, ksz); memset(d.buf, 0, ksz - 4);                     \
+  STORE32_B(d.buf + ksz - 4, 8*tsz);                                   \
+  pre##_ocb3setkey(&k, d.buf, ksz);                                    \
+                                                                       \
+  pre##_ocb3aadinit(&oaad, &k);                                                \
+                                                                       \
+  dstr_ensure(&d, 128 + tsz);                                          \
+  memset(z, 0, 128);                                                   \
+  pre##_ocb3init(&ctx, &k, z, PRE##_BLKSZ - 4, tsz);                   \
+                                                                       \
+  for (i = 0; i < 128; i++) {                                          \
+    pre##_ocb3aadinit(&iaad, &k); pre##_ocb3aadhash(&iaad, z, i);      \
+                                                                       \
+    pre##_ocb3step(&ctx); buf_init(&b, d.buf, i);                      \
+    rc = pre##_ocb3encrypt(&ctx, z, i, &b); if (rc) goto fail;         \
+    rc = pre##_ocb3encryptdone(&ctx, &iaad, &b, d.buf + i, tsz);       \
+    if (rc) goto fail;                                                 \
+    pre##_ocb3aadhash(&oaad, d.buf, i + tsz);                          \
+                                                                       \
+    pre##_ocb3step(&ctx); buf_init(&b, d.buf, i);                      \
+    rc = pre##_ocb3encrypt(&ctx, z, i, &b); if (rc) goto fail;         \
+    rc = pre##_ocb3encryptdone(&ctx, 0, &b, d.buf + i, tsz);           \
+    if (rc) goto fail;                                                 \
+    pre##_ocb3aadhash(&oaad, d.buf, i + tsz);                          \
+                                                                       \
+    pre##_ocb3step(&ctx); buf_init(&b, 0, 0);                          \
+    rc = pre##_ocb3encryptdone(&ctx, &iaad, &b, d.buf, tsz);           \
+    if (rc) goto fail;                                                 \
+    pre##_ocb3aadhash(&oaad, d.buf, tsz);                              \
+  }                                                                    \
+                                                                       \
+  pre##_ocb3step(&ctx); buf_init(&b, 0, 0);                            \
+  rc = pre##_ocb3encryptdone(&ctx, &oaad, &b, d.buf, tsz);             \
+  if (rc) goto fail;                                                   \
+                                                                       \
+  d.len = tsz;                                                         \
+  if (memcmp(d.buf, v[1].buf, tsz) != 0) {                             \
+  fail:                                                                        \
+    printf("\nfail mct: ksz = %u, tsz = %u", ksz, tsz);                        \
+    fputs("\n\texp tag = ", stdout); type_hex.dump(&v[1], stdout);     \
+    fputs("\n\tcalc tag = ", stdout); type_hex.dump(&d, stdout);       \
+    putchar('\n');                                                     \
+    ok = 0;                                                            \
+  }                                                                    \
+                                                                       \
+  return (ok);                                                         \
+}                                                                      \
+                                                                       \
+static test_chunk aeaddefs[] = {                                       \
+  { name "-ocb3", ocb3verify,                                          \
+    { &type_hex, &type_hex, &type_hex, &type_hex,                      \
+      &type_hex, &type_hex, 0 } },                                     \
+  { name "-ocb3-mct", ocb3mct,                                         \
+    { &type_ulong, &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 OCB3_TESTX(PRE, pre, name, fname)
+#endif
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif