+/*----- The implicit-IV transform -----------------------------------------*
+ *
+ * The v0 transform makes everything explicit. There's an IV because the
+ * cipher needs an IV; there's a sequence number because replay prevention
+ * needs a sequence number.
+ *
+ * This new transform works rather differently. We make use of a block
+ * cipher to encrypt the sequence number, and use that as the IV. We
+ * transmit the sequence number in the clear, as before. This reduces
+ * overhead; and it's not a significant privacy leak because the adversary
+ * can see the order in which the messages are transmitted -- i.e., the
+ * sequence numbers are almost completely predictable anyway.
+ *
+ * So, a MAC is computed over
+ *
+ * +------+ +------+------...------+
+ * | type | | seq | ciphertext |
+ * +------+ +------+------...------+
+ * 32 32 sz
+ *
+ * and we actually transmit the following as the cryptogram.
+ *
+ * +---...---+------+------...------+
+ * | tag | seq | ciphertext |
+ * +---...---+------+------...------+
+ * tagsz 32 sz
+ */
+
+typedef struct iiv_algs {
+ bulkalgs _b;
+ gencomp_algs ga;
+ const gccipher *b; size_t bksz;
+} iiv_algs;
+
+typedef struct iiv_ctx {
+ bulkctx _b;
+ size_t tagsz;
+ struct {
+ gcipher *c, *b;
+ gmac *m;
+ } d[NDIR];
+} iiv_ctx;
+
+
+static bulkalgs *iiv_getalgs(const algswitch *asw, dstr *e,
+ key_file *kf, key *k)
+{
+ iiv_algs *a = CREATE(iiv_algs);
+ dstr d = DSTR_INIT, dd = DSTR_INIT;
+ const char *p;
+ char *q;
+
+ if (gencomp_getalgs(&a->ga, asw, e, kf, k)) goto fail;
+
+ if ((p = key_getattr(kf, k, "blkc")) == 0) {
+ dstr_puts(&dd, a->ga.c->name);
+ if ((q = strrchr(dd.buf, '-')) != 0) *q = 0;
+ p = dd.buf;
+ }
+ dstr_putf(&d, "%s-ecb", p);
+ if ((a->b = gcipher_byname(d.buf)) == 0) {
+ a_format(e, "unknown-blkc", "%s", p, A_END);
+ goto fail;
+ }
+
+ dstr_destroy(&d); dstr_destroy(&dd);
+ return (&a->_b);
+fail:
+ dstr_destroy(&d); dstr_destroy(&dd);
+ DESTROY(a);
+ return (0);
+}
+
+#ifndef NTRACE
+static void iiv_tracealgs(const bulkalgs *aa)
+{
+ const iiv_algs *a = (const iiv_algs *)aa;
+
+ gencomp_tracealgs(&a->ga);
+ trace(T_CRYPTO, "crypto: blkc = %.*s", strlen(a->b->name) - 4, a->b->name);
+}
+#endif
+
+static int iiv_checkalgs(bulkalgs *aa, const algswitch *asw, dstr *e)
+{
+ iiv_algs *a = (iiv_algs *)aa;
+
+ if (gencomp_checkalgs(&a->ga, asw, e)) return (-1);
+
+ if ((a->bksz = keysz(asw->hashsz, a->b->keysz)) == 0) {
+ a_format(e, "blkc", "%.*s", strlen(a->b->name) - 4, a->b->name,
+ "no-key-size", "%lu", (unsigned long)asw->hashsz,
+ A_END);
+ return (-1);
+ }
+ if (a->b->blksz < a->ga.c->blksz) {
+ a_format(e, "blkc", "%.*s", strlen(a->b->name) - 4, a->b->name,
+ "blksz-insufficient", A_END);
+ return (-1);
+ }
+ return (0);
+}
+
+static int iiv_samealgsp(const bulkalgs *aa, const bulkalgs *bb)
+{
+ const iiv_algs *a = (const iiv_algs *)aa, *b = (const iiv_algs *)bb;
+ return (gencomp_samealgsp(&a->ga, &b->ga) && a->b == b->b);
+}
+
+static void iiv_alginfo(const bulkalgs *aa, admin *adm)
+{
+ const iiv_algs *a = (const iiv_algs *)aa;
+ gencomp_alginfo(&a->ga, adm);
+ a_info(adm,
+ "blkc=%.*s", strlen(a->b->name) - 4, a->b->name,
+ "blkc-keysz=%lu", (unsigned long)a->bksz,
+ "blkc-blksz=%lu", (unsigned long)a->b->blksz,
+ A_END);
+}
+
+static size_t iiv_overhead(const bulkalgs *aa)
+ { const iiv_algs *a = (const iiv_algs *)aa; return (a->ga.tagsz + SEQSZ); }
+
+static size_t iiv_expsz(const bulkalgs *aa)
+{
+ const iiv_algs *a = (const iiv_algs *)aa;
+ return (gencomp_expsz(&a->ga));
+}
+
+static bulkctx *iiv_genkeys(const bulkalgs *aa, const struct rawkey *rk)
+{
+ const iiv_algs *a = (const iiv_algs *)aa;
+ iiv_ctx *bc = CREATE(iiv_ctx);
+ octet k[MAXHASHSZ];
+ int i;
+
+ bc->tagsz = a->ga.tagsz;
+ for (i = 0; i < NDIR; i++) {
+ ks_derivekey(k, a->ga.cksz, rk, i, "encryption");
+ bc->d[i].c = GC_INIT(a->ga.c, k, a->ga.cksz);
+ ks_derivekey(k, a->bksz, rk, i, "blkc");
+ bc->d[i].b = GC_INIT(a->b, k, a->bksz);
+ ks_derivekey(k, a->ga.mksz, rk, i, "integrity");
+ bc->d[i].m = GM_KEY(a->ga.m, k, a->ga.mksz);
+ }
+ return (&bc->_b);
+}
+
+static bulkchal *iiv_genchal(const bulkalgs *aa)
+{
+ const iiv_algs *a = (const iiv_algs *)aa;
+ return (gencomp_genchal(&a->ga));
+}
+#define iiv_chaltag gencomp_chaltag
+#define iiv_chalvrf gencomp_chalvrf
+#define iiv_freechal gencomp_freechal
+
+static void iiv_freealgs(bulkalgs *aa)
+ { iiv_algs *a = (iiv_algs *)aa; DESTROY(a); }
+
+static void iiv_freectx(bulkctx *bbc)
+{
+ iiv_ctx *bc = (iiv_ctx *)bbc;
+ int i;
+
+ for (i = 0; i < NDIR; i++) {
+ GC_DESTROY(bc->d[i].c);
+ GC_DESTROY(bc->d[i].b);
+ GM_DESTROY(bc->d[i].m);
+ }
+ DESTROY(bc);
+}
+
+#define TRACE_PRESEQ(qseq, ivsz) do { IF_TRACING(T_KEYSET, { \
+ trace_block(T_CRYPTO, "crypto: IV derivation input", (qseq), (ivsz)); \
+}) } while (0)
+
+static int iiv_encrypt(bulkctx *bbc, unsigned ty,
+ buf *b, buf *bb, uint32 seq)
+{
+ iiv_ctx *bc = (iiv_ctx *)bbc;
+ ghash *h;
+ gcipher *c = bc->d[DIR_OUT].c, *blkc = bc->d[DIR_OUT].b;
+ const octet *p = BCUR(b);
+ size_t sz = BLEFT(b);
+ octet *qmac, *qseq, *qpk;
+ size_t ivsz = GC_CLASS(c)->blksz, blkcsz = GC_CLASS(blkc)->blksz;
+ size_t tagsz = bc->tagsz;
+ octet t[4];
+
+ /* --- Determine the ciphertext layout --- */
+
+ if (buf_ensure(bb, tagsz + SEQSZ + sz)) return (0);
+ qmac = BCUR(bb); qseq = qmac + tagsz; qpk = qseq + SEQSZ;
+ BSTEP(bb, tagsz + SEQSZ + sz);
+
+ /* --- Store the type --- *
+ *
+ * This isn't transmitted, but it's covered by the MAC.
+ */
+
+ STORE32(t, ty);
+
+ /* --- Store the sequence number --- */
+
+ STORE32(qseq, seq);
+
+ /* --- Establish an initialization vector if necessary --- */
+
+ if (ivsz) {
+ memset(buf_u, 0, blkcsz - SEQSZ);
+ memcpy(buf_u + blkcsz - SEQSZ, qseq, SEQSZ);
+ TRACE_PRESEQ(buf_u, ivsz);
+ GC_ENCRYPT(blkc, buf_u, buf_u, blkcsz);
+ GC_SETIV(c, buf_u);
+ TRACE_IV(buf_u, ivsz);
+ }
+
+ /* --- Encrypt the packet --- */
+
+ GC_ENCRYPT(c, p, qpk, sz);
+ TRACE_CT(qpk, sz);
+
+ /* --- Compute a MAC over type, sequence number, and ciphertext --- */
+
+ if (tagsz) {
+ h = GM_INIT(bc->d[DIR_OUT].m);
+ GH_HASH(h, t, sizeof(t));
+ GH_HASH(h, qseq, SEQSZ + sz);
+ memcpy(qmac, GH_DONE(h, 0), tagsz);
+ GH_DESTROY(h);
+ TRACE_MAC(qmac, tagsz);
+ }
+
+ /* --- We're done --- */
+
+ return (0);
+}
+
+static int iiv_decrypt(bulkctx *bbc, unsigned ty,
+ buf *b, buf *bb, uint32 *seq)
+{
+ iiv_ctx *bc = (iiv_ctx *)bbc;
+ const octet *pmac, *pseq, *ppk;
+ size_t psz = BLEFT(b);
+ size_t sz;
+ octet *q = BCUR(bb);
+ ghash *h;
+ gcipher *c = bc->d[DIR_IN].c, *blkc = bc->d[DIR_IN].b;
+ size_t ivsz = GC_CLASS(c)->blksz, blkcsz = GC_CLASS(blkc)->blksz;
+ size_t tagsz = bc->tagsz;
+ octet t[4];
+
+ /* --- Break up the packet into its components --- */
+
+ if (psz < SEQSZ + tagsz) {
+ T( trace(T_KEYSET, "keyset: block too small for keyset"); )
+ return (KSERR_MALFORMED);
+ }
+ sz = psz - SEQSZ - tagsz;
+ pmac = BCUR(b); pseq = pmac + tagsz; ppk = pseq + SEQSZ;
+ STORE32(t, ty);
+
+ /* --- Verify the MAC on the packet --- */
+
+ if (tagsz) {
+ h = GM_INIT(bc->d[DIR_IN].m);
+ GH_HASH(h, t, sizeof(t));
+ GH_HASH(h, pseq, SEQSZ + sz);
+ CHECK_MAC(h, pmac, tagsz);
+ }
+
+ /* --- Decrypt the packet --- */
+
+ if (ivsz) {
+ memset(buf_u, 0, blkcsz - SEQSZ);
+ memcpy(buf_u + blkcsz - SEQSZ, pseq, SEQSZ);
+ TRACE_PRESEQ(buf_u, ivsz);
+ GC_ENCRYPT(blkc, buf_u, buf_u, blkcsz);
+ GC_SETIV(c, buf_u);
+ TRACE_IV(buf_u, ivsz);
+ }
+ GC_DECRYPT(c, ppk, q, sz);
+
+ /* --- Finished --- */
+
+ *seq = LOAD32(pseq);
+ BSTEP(bb, sz);
+ return (0);
+}
+