+/* -*-c-*-
+ *
+ * The OCB1 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_OCB1_DEF_H
+#define CATACOMB_OCB1_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
+
+/*----- Macros ------------------------------------------------------------*/
+
+/* --- @OCB1_DEF@ --- *
+ *
+ * Arguments: @PRE@, @pre@ = prefixes for the underlying block cipher
+ *
+ * Use: Creates an implementation for the OCB1 authenticated-
+ * encryption mode.
+ */
+
+#define OCB1_DEF(PRE, pre) OCB1_DEFX(PRE, pre, #pre, #pre)
+
+#define OCB1_DEFX(PRE, pre, name, fname) \
+ \
+const octet \
+ pre##_ocb1noncesz[] = { KSZ_SET, PRE##_BLKSZ, 0 }, \
+ pre##_ocb1tagsz[] = { KSZ_RANGE, PRE##_BLKSZ, 0, PRE##_BLKSZ, 1 }; \
+ \
+/* --- @pre_ocb1init@ --- * \
+ * \
+ * Arguments: @pre_ocb1ctx *ctx@ = pointer to OCB1 context \
+ * @const pre_ocb1key *key@ = pointer to key block \
+ * @const void *n@ = pointer to nonce \
+ * @size_t nsz@ = size of nonce \
+ * \
+ * Returns: Zero on success, @-1@ if the nonce length is bad. \
+ * \
+ * Use: Initialize an OCB1 operation context with a given key. \
+ * \
+ * The original key needn't be kept around any more. \
+ */ \
+ \
+int pre##_ocb1init(pre##_ocb1ctx *ctx, const pre##_ocb1key *k, \
+ const void *n, size_t nsz) \
+ { ctx->k = *k; return (pre##_ocb1reinit(ctx, n, nsz)); } \
+ \
+/* --- @pre_ocb1reinit@ --- * \
+ * \
+ * Arguments: @pre_ocb1ctx *ctx@ = pointer to OCB1 context \
+ * @const void *n@ = pointer to nonce \
+ * @size_t nsz@ = size of nonce \
+ * \
+ * Returns: Zero on success, @-1@ if the nonce length is bad. \
+ * \
+ * Use: Reinitialize an OCB1 operation context, changing the \
+ * nonce. \
+ */ \
+ \
+int pre##_ocb1reinit(pre##_ocb1ctx *ctx, const void *n, size_t nsz) \
+{ \
+ if (nsz != PRE##_BLKSZ) return (-1); \
+ ctx->off = 0; BLKC_ZERO(PRE, ctx->a); \
+ BLKC_LOAD(PRE, ctx->o, n); BLKC_XMOVE(PRE, ctx->o, ctx->k.lmask[0]); \
+ pre##_eblk(&ctx->k.ctx, ctx->o, ctx->o); ctx->i = 1; \
+ return (0); \
+} \
+ \
+/* --- @pre_ocb1encrypt@ --- * \
+ * \
+ * Arguments: @pre_ocb1ctx *ctx@ = pointer to OCB1 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 OCB1 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##_ocb1encrypt(pre##_ocb1ctx *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##_ocb1policy, 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_ocb1decrypt@ --- * \
+ * \
+ * Arguments: @pre_ocb1ctx *ctx@ = pointer to OCB1 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 OCB1 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##_ocb1decrypt(pre##_ocb1ctx *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##_ocb1policy, 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_ocb1encdecfinal@ --- * \
+ * \
+ * Arguments: @pre_ocb1ctx *ctx@ = pointer to an OCB1 context \
+ * @const pre_ocb1aadctx *aad@ = pointer to AAD context, \
+ * or null \
+ * @buf *dst@ = buffer for remaining ciphertext \
+ * @int encp@ = nonzero if we're encrypting \
+ * \
+ * Returns: Zero on success; @-1@ on failure. \
+ * \
+ * Use: Common end-of-message handling for encryption and \
+ * decryption. The full-length tag is left in the \
+ * context's buffer. \
+ */ \
+ \
+static int pre##_ocb1encdecfinal(pre##_ocb1ctx *ctx, \
+ const pre##_ocb1aadctx *aad, buf *dst, \
+ int encp) \
+{ \
+ 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); } \
+ \
+ /* Calculate the final offset. Mix it into the checksum before it \
+ * gets clobbered. \
+ */ \
+ OCB_OFFSET(PRE, ctx->o, ctx->k.lmask, ctx->i++); \
+ BLKC_XMOVE(PRE, ctx->a, ctx->o); \
+ \
+ /* Mix the magic final mask and tail length into the offset. */ \
+ BLKC_XMOVE(PRE, ctx->o, ctx->k.lxinv); \
+ ctx->o[PRE##_BLKSZ/4 - 1] ^= BLKC_BWORD(PRE, 8*ctx->off); \
+ \
+ /* If we're decrypting, then trim the end of the plaintext and fold \
+ * that into the checksum. \
+ */ \
+ if (!encp) { \
+ memset(ctx->b + ctx->off, 0, PRE##_BLKSZ - ctx->off); \
+ BLKC_XLOAD(PRE, ctx->a, ctx->b); \
+ } \
+ \
+ /* Cycle the block cipher for the last plaintext block. */ \
+ pre##_eblk(&ctx->k.ctx, ctx->o, ctx->o); \
+ \
+ /* Fold this mask into the checksum before it gets clobbered. */ \
+ BLKC_XMOVE(PRE, ctx->a, ctx->o); \
+ \
+ /* Encrypt the message tail. */ \
+ BLKC_XLOAD(PRE, ctx->o, ctx->b); \
+ BLKC_STORE(PRE, ctx->b, ctx->o); \
+ if (ctx->off) memcpy(q, ctx->b, ctx->off); \
+ \
+ /* If we're encrypting, then trim the end of the ciphertext and fold \
+ * that into the checksum. \
+ */ \
+ if (encp) { \
+ memset(ctx->b + ctx->off, 0, PRE##_BLKSZ - ctx->off); \
+ BLKC_XLOAD(PRE, ctx->a, ctx->b); \
+ } \
+ \
+ /* Encrypt the checksum to produce a tag. */ \
+ pre##_eblk(&ctx->k.ctx, ctx->a, ctx->a); \
+ \
+ /* If there's AAD then mix the PMAC tag in too. */ \
+ if (aad && aad->off) \
+ { pre##_ocb1aadtag(aad, ctx->o); BLKC_XMOVE(PRE, ctx->a, ctx->o); } \
+ \
+ /* Write the final tag. */ \
+ BLKC_STORE(PRE, ctx->b, ctx->a); \
+ \
+ /* Done. */ \
+ return (0); \
+} \
+ \
+/* --- @pre_ocb1encryptdone@ --- * \
+ * \
+ * Arguments: @pre_ocb1ctx *ctx@ = pointer to an OCB1 context \
+ * @const pre_ocb1aadctx *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 OCB1 encryption operation. The @aad@ \
+ * pointer may be null if there is no additional \
+ * authenticated data. OCB1 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##_ocb1encryptdone(pre##_ocb1ctx *ctx, \
+ const pre##_ocb1aadctx *aad, buf *dst, \
+ void *tag, size_t tsz) \
+{ \
+ assert(tsz <= PRE##_BLKSZ); \
+ if (pre##_ocb1encdecfinal(ctx, aad, dst, 1)) return (-1); \
+ memcpy(tag, ctx->b, tsz); \
+ return (0); \
+} \
+ \
+/* --- @pre_ocb1decryptdone@ --- * \
+ * \
+ * Arguments: @pre_ocb1ctx *ctx@ = pointer to an OCB1 context \
+ * @const pre_ocb1aadctx *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 OCB1 decryption operation. The @aad@ \
+ * pointer may be null if there is no additional \
+ * authenticated data. OCB1 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##_ocb1decryptdone(pre##_ocb1ctx *ctx, \
+ const pre##_ocb1aadctx *aad, buf *dst, \
+ const void *tag, size_t tsz) \
+{ \
+ assert(tsz <= PRE##_BLKSZ); \
+ if (pre##_ocb1encdecfinal(ctx, aad, dst, 0)) return (-1); \
+ else if (ct_memeq(tag, ctx->b, tsz)) return (+1); \
+ else return (0); \
+} \
+ \
+/* --- Generic AEAD interface --- */ \
+ \
+typedef struct gactx { \
+ gaead_aad a; \
+ pre##_ocb1aadctx 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##_ocb1aadhash(&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##_ocb1, gadup, gahash, gadestroy }; \
+ \
+static gaead_aad *gaad(const pre##_ocb1key *k) \
+{ \
+ gactx *aad = S_CREATE(gactx); \
+ aad->a.ops = &gaops; \
+ pre##_ocb1aadinit(&aad->aad, k); \
+ return (&aad->a); \
+} \
+ \
+typedef struct gectx { \
+ gaead_enc e; \
+ pre##_ocb1ctx 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##_ocb1reinit(&enc->ctx, n, nsz)); \
+} \
+ \
+static int geenc(gaead_enc *e, const void *m, size_t msz, buf *b) \
+{ \
+ gectx *enc = (gectx *)e; \
+ return (pre##_ocb1encrypt(&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##_ocb1encryptdone(&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##_ocb1, geaad, gereinit, geenc, gedone, gedestroy }; \
+ \
+typedef struct gdctx { \
+ gaead_dec d; \
+ pre##_ocb1ctx 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##_ocb1reinit(&dec->ctx, n, nsz)); \
+} \
+ \
+static int gddec(gaead_dec *d, const void *c, size_t csz, buf *b) \
+{ \
+ gdctx *dec = (gdctx *)d; \
+ return (pre##_ocb1decrypt(&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##_ocb1decryptdone(&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##_ocb1, gdaad, gdreinit, gddec, gddone, gddestroy }; \
+ \
+typedef struct gkctx { \
+ gaead_key k; \
+ pre##_ocb1key 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##_ocb1init(&enc->ctx, &key->key, n, nsz)) \
+ { 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##_ocb1init(&dec->ctx, &key->key, n, nsz)) \
+ { 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##_ocb1, gkaad, gkenc, gkdec, gkdestroy }; \
+ \
+static gaead_key *gckey(const void *k, size_t ksz) \
+{ \
+ gkctx *key = S_CREATE(gkctx); \
+ key->k.ops = &gkops; \
+ pre##_ocb1setkey(&key->key, k, ksz); \
+ return (&key->k); \
+} \
+ \
+const gcaead pre##_ocb1 = { \
+ name "-ocb1", \
+ pre##_keysz, pre##_ocb1noncesz, pre##_ocb1tagsz, \
+ PRE##_BLKSZ, PRE##_BLKSZ, 0, 0, \
+ gckey \
+}; \
+ \
+OCB1_TESTX(PRE, pre, name, fname)
+
+/*----- Test rig ----------------------------------------------------------*/
+
+#define OCB1_TEST(PRE, pre) OCB1_TESTX(PRE, pre, #pre, #pre)
+
+/* --- @OCB1_TEST@ --- *
+ *
+ * Arguments: @PRE, pre@ = prefixes for the underlying block cipher
+ *
+ * Use: Standard test rig for OCB1 functions.
+ */
+
+#ifdef TEST_RIG
+
+#include <stdio.h>
+
+#include <mLib/dstr.h>
+#include <mLib/quis.h>
+#include <mLib/testrig.h>
+
+#define OCB1_TESTX(PRE, pre, name, fname) \
+ \
+static int ocb1verify(dstr *v) \
+{ \
+ pre##_ocb1key key; \
+ pre##_ocb1aadctx aad; \
+ pre##_ocb1ctx 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##_ocb1setkey(&key, v[0].buf, v[0].len); \
+ \
+ for (ip = szs; *ip; ip++) { \
+ \
+ pre##_ocb1init(&ctx, &key, (octet *)v[1].buf, v[1].len); \
+ \
+ i = *ip; \
+ hsz = v[2].len; \
+ if (i == -1) i = hsz; \
+ if (i > hsz) continue; \
+ p = (octet *)v[2].buf; \
+ pre##_ocb1aadinit(&aad, &key); \
+ while (hsz) { \
+ if (i > hsz) i = hsz; \
+ pre##_ocb1aadhash(&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##_ocb1encrypt(&ctx, p, i, &b)) { \
+ puts("!! ocb1encrypt reports failure"); \
+ goto fail_enc; \
+ } \
+ p += i; msz -= i; \
+ } \
+ \
+ if (pre##_ocb1encryptdone(&ctx, &aad, &b, (octet *)t.buf, t.len)) { \
+ puts("!! ocb1encryptdone 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##_ocb1init(&ctx, &key, (octet *)v[1].buf, v[1].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##_ocb1decrypt(&ctx, p, i, &b)) { \
+ puts("!! ocb1decrypt reports failure"); \
+ win = 0; goto fail_dec; \
+ } \
+ p += i; msz -= i; \
+ } \
+ \
+ win = pre##_ocb1decryptdone(&ctx, &aad, &b, \
+ (octet *)v[5].buf, v[5].len); \
+ if (win < 0) { \
+ puts("!! ocb1decryptdone 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 "-ocb1", ocb1verify, \
+ { &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 OCB1_TESTX(PRE, pre, name, fname)
+#endif
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif