progs/catcrypt.c, progs/cc-kem.c: Refactor bulk encryption.
authorMark Wooding <mdw@distorted.org.uk>
Thu, 26 May 2016 08:26:09 +0000 (09:26 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Sat, 22 Apr 2017 19:25:46 +0000 (20:25 +0100)
The bulk crypto transform is now owned by the KEM machinery, and
provided to callers as one object rather than a bunch of little
components.  There are some conceptual changes in the UI, but in fact
everything still works the way it did before.

progs/catcrypt.1
progs/catcrypt.c
progs/cc-kem.c
progs/cc.h

index 597118f..af5e6b3 100644 (file)
@@ -134,6 +134,14 @@ A
 has the syntax
 .IR kem \c
 .RB [ / \c
+.IR bulk \c
+.RB [ \- \c
+.IR cipher ] \c
+.RB [ / \c
+.IR hash ]]
+or
+.IR kem \c
+.RB [ / \c
 .IR cipher \c
 .RB [ / \c
 .IR hash ]].
@@ -197,16 +205,39 @@ command (see
 .BR key (1))
 to generate the key.
 .PP
+The bulk crypto transform is chosen based on the
+.B bulk
+attribute on the key, or, failing that,
+from the
+.I bulk
+stated in the
+.IR kemalgspec .
+Run
+.B catcrypt show bulk
+for a list of supported bulk crypto transforms.
+.TP
+.B gencomp
+A generic composition of
+a cipher secure against chosen-plaintext attack,
+and a message authentication code.
+Makes use of
+.B cipher
+and
+.B mac
+attributes.
+This is the default transform.
+.PP
 As well as the KEM itself, a number of supporting algorithms are used.
 These are taken from appropriately named attributes on the key or,
 failing that, derived from other attributes as described below.
 .TP
 .B cipher
-This is the symmetric encryption algorithm used for bulk data
-encryption.  If there is no
+This is the symmetric encryption algorithm
+used by the bulk data transform.
+If there is no
 .B cipher
 attribute then the
-.I cipher
+.I bulk
 in the
 .I kemalgspec
 is used; if that it absent, then the default of
@@ -230,9 +261,13 @@ is used.  Run
 for a list of supported symmetric encryption algorithms.
 .TP
 .B mac
-This is the message authentication algorithm used during bulk data
-encryption to ensure integrity of the encrypted message and defend
-against chosen-ciphertext attacks.  If there is no
+This is the message authentication algorithm
+used by the
+.B gencomp
+bulk data transform
+to ensure integrity of the encrypted message and
+defend against chosen-ciphertext attacks.
+If there is no
 .B mac
 attribute then
 .IB hash -hmac
index 5879ecb..72c7780 100644 (file)
@@ -137,7 +137,6 @@ static int encrypt(int argc, char *argv[])
   int en;
   size_t n, chsz;
   dstr d = DSTR_INIT;
-  octet *tag, *ct;
   buf b;
   size_t seq;
   char bb[65536];
@@ -146,10 +145,8 @@ static int encrypt(int argc, char *argv[])
   key *k;
   key *sk = 0;
   kem *km;
+  bulk *bc;
   sig *s = 0;
-  gcipher *cx, *c;
-  gmac *m;
-  ghash *h;
   const encops *eo;
   enc *e;
 
@@ -213,7 +210,7 @@ static int encrypt(int argc, char *argv[])
   dstr_reset(&d);
   key_fulltag(k, &d);
   e = initenc(eo, ofp, "CATCRYPT ENCRYPTED MESSAGE");
-  km = getkem(k, "cckem", 0);
+  km = getkem(k, "cckem", 0, &bc);
   if (!(f & f_nocheck) && (err = km->ops->check(km)) != 0)
     moan("key %s fails check: %s", d.buf, err);
   if (sk) {
@@ -238,8 +235,7 @@ static int encrypt(int argc, char *argv[])
   /* --- Build the KEM chunk --- */
 
   dstr_reset(&d);
-  if (setupkem(km, &d, &cx, &c, &m))
-    die(EXIT_FAILURE, "failed to encapsulate key");
+  if (setupkem(km, &d, bc)) die(EXIT_FAILURE, "failed to encapsulate key");
   buf_init(&b, d.buf, d.len);
   BSTEP(&b, d.len);
   if (s) GH_HASHBUF16(s->h, BBASE(&b), BLEN(&b));
@@ -248,7 +244,7 @@ static int encrypt(int argc, char *argv[])
   /* --- Write the signature chunk --- */
 
   if (s) {
-    GC_ENCRYPT(cx, 0, bb, 1024);
+    GC_ENCRYPT(km->cx, 0, bb, 1024);
     GH_HASH(s->h, bb, 1024);
     dstr_reset(&d);
     if ((en = s->ops->doit(s, &d)) != 0)
@@ -267,48 +263,27 @@ static int encrypt(int argc, char *argv[])
     }
   }
 
-  assert(GC_CLASS(c)->blksz <= sizeof(bb));
-  dstr_ensure(&d, sizeof(bb) + GM_CLASS(m)->hashsz);
+  n = bc->ops->overhead(bc);
+  dstr_ensure(&d, sizeof(bb) + n);
   seq = 0;
-  chsz = MASK16 - GM_CLASS(m)->hashsz;
-  for (;;) {
-    h = GM_INIT(m);
-    GH_HASHU32(h, seq);
-    seq++;
-    if (GC_CLASS(c)->blksz) {
-      GC_ENCRYPT(cx, 0, bb, GC_CLASS(c)->blksz);
-      GC_SETIV(c, bb);
-    }
+  chsz = MASK16 - n;
+  do {
     n = fread(bb, 1, chsz, fp);
-    if (!n) break;
     if (f & f_progress) fprogress_update(&ff, n);
     buf_init(&b, d.buf, d.sz);
-    tag = buf_get(&b, GM_CLASS(m)->hashsz);
-    ct = buf_get(&b, n);
-    assert(tag); assert(ct);
-    GC_ENCRYPT(c, bb, ct, n);
-    GH_HASH(h, ct, n);
-    GH_DONE(h, tag);
-    GH_DESTROY(h);
+    if ((err = bc->ops->doit(bc, seq, &b, bb, n)) != 0) {
+      if (f & f_progress) fprogress_done(&ff);
+      die(EXIT_FAILURE, "failed to encrypt packet: %s\n", err);
+    }
+    seq++;
     chunk_write(e, &b);
-  }
-
-  /* --- Final terminator packet --- */
-
-  buf_init(&b, d.buf, d.sz);
-  tag = buf_get(&b, GM_CLASS(m)->hashsz);
-  assert(tag);
-  GH_DONE(h, tag);
-  GH_DESTROY(h);
-  chunk_write(e, &b);
+  } while (n);
 
   /* --- All done --- */
 
   if (f & f_progress) fprogress_done(&ff);
   e->ops->encdone(e);
-  GM_DESTROY(m);
-  GC_DESTROY(c);
-  GC_DESTROY(cx);
+  bc->ops->destroy(bc);
   freeenc(e);
   if (s) freesig(s);
   freekem(km);
@@ -335,6 +310,7 @@ static int decrypt(int argc, char *argv[])
   int i;
   size_t n;
   dstr d = DSTR_INIT;
+  char bb[65536];
   buf b;
   key_file kf;
   size_t seq;
@@ -343,11 +319,7 @@ static int decrypt(int argc, char *argv[])
   key *sk = 0;
   kem *km;
   sig *s = 0;
-  gcipher *cx;
-  gcipher *c;
-  ghash *h;
-  gmac *m;
-  octet *tag;
+  bulk *bc;
   unsigned f = 0;
   const encops *eo;
   const char *err;
@@ -456,14 +428,14 @@ static int decrypt(int argc, char *argv[])
 
   /* --- Find the key --- */
 
-  km = getkem(k, "cckem", 1);
+  km = getkem(k, "cckem", 1, &bc);
 
   /* --- Read the KEM chunk --- */
 
   chunk_read(e, &d, &b);
   if (f & f_progress)
     fprogress_update(&ff, BLEFT(&b)*e->ops->ncook/e->ops->nraw);
-  if (setupkem(km, &d, &cx, &c, &m)) {
+  if (setupkem(km, &d, bc)) {
     if (f & f_progress) fprogress_done(&ff);
     if (verb) printf("FAIL failed to decapsulate key\n");
     exit(EXIT_FAILURE);
@@ -475,7 +447,7 @@ static int decrypt(int argc, char *argv[])
   if (sk) {
     dstr_reset(&d);
     dstr_ensure(&d, 1024);
-    GC_ENCRYPT(cx, 0, d.buf, 1024);
+    GC_ENCRYPT(km->cx, 0, d.buf, 1024);
     GH_HASH(s->h, d.buf, 1024);
     chunk_read(e, &d, &b);
     if (f & f_progress)
@@ -516,36 +488,21 @@ static int decrypt(int argc, char *argv[])
   }
 
   seq = 0;
-  dstr_ensure(&d, GC_CLASS(c)->blksz);
+  dstr_ensure(&d, bc->ops->overhead(bc));
   dstr_ensure(&d, 4);
   for (;;) {
-    if (GC_CLASS(c)->blksz) {
-      GC_ENCRYPT(cx, 0, d.buf, GC_CLASS(c)->blksz);
-      GC_SETIV(c, d.buf);
-    }
-    h = GM_INIT(m);
-    GH_HASHU32(h, seq);
-    seq++;
     chunk_read(e, &d, &b);
     if (f & f_progress)
       fprogress_update(&ff, BLEFT(&b)*e->ops->ncook/e->ops->nraw);
-    if ((tag = buf_get(&b, GM_CLASS(m)->hashsz)) == 0) {
+    buf_init(&b, bb, sizeof(bb));
+    if ((err = bc->ops->doit(bc, seq, &b, d.buf, d.len)) != 0) {
       if (f & f_progress) fprogress_done(&ff);
-      if (verb) printf("FAIL bad ciphertext chunk: no tag\n");
+      if (verb) printf("FAIL bad ciphertext chunk: %s\n", err);
       exit(EXIT_FAILURE);
     }
-    GH_HASH(h, BCUR(&b), BLEFT(&b));
-    if (!ct_memeq(tag, GH_DONE(h, 0), GM_CLASS(m)->hashsz)) {
-      if (f & f_progress) fprogress_done(&ff);
-      if (verb)
-       printf("FAIL bad ciphertext chunk: authentication failure\n");
-      exit(EXIT_FAILURE);
-    }
-    GH_DESTROY(h);
-    if (!BLEFT(&b))
-      break;
-    GC_DECRYPT(c, BCUR(&b), BCUR(&b), BLEFT(&b));
-    if (fwrite(BCUR(&b), 1, BLEFT(&b), rfp) != BLEFT(&b)) {
+    seq++;
+    if (!BLEN(&b)) break;
+    if (fwrite(BBASE(&b), 1, BLEN(&b), rfp) != BLEN(&b)) {
       if (f & f_progress) fprogress_done(&ff);
       if (verb) printf("FAIL error writing output: %s\n", strerror(errno));
       exit(EXIT_FAILURE);
@@ -586,10 +543,8 @@ static int decrypt(int argc, char *argv[])
     printf("OK decrypted successfully\n");
   if (ofp && (fflush(ofp) || ferror(ofp) || fclose(ofp)))
       die(EXIT_FAILURE, "error writing output: %s", strerror(errno));
+  bc->ops->destroy(bc);
   freeenc(e);
-  GC_DESTROY(c);
-  GC_DESTROY(cx);
-  GM_DESTROY(m);
   freekem(km);
   if (fp != stdin) fclose(fp);
   key_close(&kf);
@@ -609,6 +564,8 @@ static int decrypt(int argc, char *argv[])
      listtab[i].name, listtab[i].name)                                 \
   LI("Key-encapsulation mechanisms", kem,                              \
      kemtab[i].name, kemtab[i].name)                                   \
+  LI("Bulk crypto transforms", bulk,                                   \
+     bulktab[i].name, bulktab[i].name)                                 \
   LI("Signature schemes", sig,                                         \
      sigtab[i].name, sigtab[i].name)                                   \
   LI("Encodings", enc,                                                 \
index e165389..6c4d7f3 100644 (file)
 
 #include "cc.h"
 
+/*----- Bulk crypto -------------------------------------------------------*/
+
+/* --- Generic composition --- */
+
+typedef struct gencomp_encctx {
+  bulk b;
+  const gccipher *cc;
+  const gcmac *mc;
+  gcipher *c, *cx;
+  gmac *m;
+  octet *t; size_t tsz;
+} gencomp_encctx;
+
+static bulk *gencomp_init(key *k, const char *calg, const char *halg)
+{
+  gencomp_encctx *ctx = CREATE(gencomp_encctx);
+  const char *q;
+  dstr d = DSTR_INIT, t = DSTR_INIT;
+
+  key_fulltag(k, &t);
+
+  if ((q = key_getattr(0, k, "cipher")) != 0) calg = q;
+  if (!calg) ctx->cc = &blowfish_cbc;
+  else if ((ctx->cc = gcipher_byname(calg)) == 0) {
+    die(EXIT_FAILURE, "encryption scheme `%s' not found in key `%s'",
+       calg, t.buf);
+  }
+
+  dstr_reset(&d);
+  if ((q = key_getattr(0, k, "mac")) == 0) {
+    dstr_putf(&d, "%s-hmac", halg);
+    q = d.buf;
+  }
+  if ((ctx->mc = gmac_byname(q)) == 0) {
+    die(EXIT_FAILURE,
+       "message authentication code `%s' not found in key `%s'",
+       q, t.buf);
+  }
+
+  return (&ctx->b);
+}
+
+static int gencomp_setup(bulk *b, gcipher *cx)
+{
+  gencomp_encctx *ctx = (gencomp_encctx *)b;
+  size_t cn, mn, n;
+  octet *kd;
+
+  ctx->cx = cx;
+  n = ctx->cc->blksz;
+  cn = keysz(0, ctx->cc->keysz); if (cn > n) n = cn;
+  mn = keysz(0, ctx->mc->keysz); if (mn > n) n = mn;
+  ctx->t = kd = xmalloc(n); ctx->tsz = n;
+  GC_ENCRYPT(cx, 0, kd, cn);
+  ctx->c = GC_INIT(ctx->cc, kd, cn);
+  GC_ENCRYPT(cx, 0, kd, mn);
+  ctx->m = GM_KEY(ctx->mc, kd, mn);
+  return (0);
+}
+
+static size_t gencomp_overhead(bulk *b)
+{
+  gencomp_encctx *ctx = (gencomp_encctx *)b;
+  return (ctx->cc->blksz + ctx->mc->hashsz); }
+
+static void gencomp_destroy(bulk *b)
+{
+  gencomp_encctx *ctx = (gencomp_encctx *)b;
+
+  GC_DESTROY(ctx->c);
+  GC_DESTROY(ctx->m);
+  xfree(ctx->t);
+  DESTROY(ctx);
+}
+
+static const char *gencomp_encdoit(bulk *b, uint32 seq, buf *bb,
+                                  const void *p, size_t sz)
+{
+  gencomp_encctx *ctx = (gencomp_encctx *)b;
+  octet *tag, *ct;
+  ghash *h = GM_INIT(ctx->m);
+
+  GH_HASHU32(h, seq);
+  if (ctx->cc->blksz) {
+    GC_ENCRYPT(ctx->cx, 0, ctx->t, ctx->cc->blksz);
+    GC_SETIV(ctx->c, ctx->t);
+  }
+  tag = buf_get(bb, ctx->mc->hashsz); assert(tag);
+  ct = buf_get(bb, sz); assert(ct);
+  GC_ENCRYPT(ctx->c, p, ct, sz);
+  GH_HASH(h, ct, sz);
+  GH_DONE(h, tag);
+  GH_DESTROY(h);
+  return (0);
+}
+
+static const char *gencomp_decdoit(bulk *b, uint32 seq, buf *bb,
+                                  const void *p, size_t sz)
+{
+  gencomp_encctx *ctx = (gencomp_encctx *)b;
+  buf bin;
+  const octet *tag, *ct;
+  octet *pt;
+  ghash *h;
+  int ok;
+
+  buf_init(&bin, (/*unconst*/ void *)p, sz);
+  if ((tag = buf_get(&bin, ctx->mc->hashsz)) == 0) return ("no tag");
+  ct = BCUR(&bin); sz = BLEFT(&bin);
+  pt = buf_get(bb, sz); assert(pt);
+
+  h = GM_INIT(ctx->m);
+  GH_HASHU32(h, seq);
+  GH_HASH(h, ct, sz);
+  ok = ct_memeq(tag, GH_DONE(h, 0), ctx->mc->hashsz);
+  GH_DESTROY(h);
+  if (!ok) return ("authentication failure");
+
+  if (ctx->cc->blksz) {
+    GC_ENCRYPT(ctx->cx, 0, ctx->t, ctx->cc->blksz);
+    GC_SETIV(ctx->c, ctx->t);
+  }
+  GC_DECRYPT(ctx->c, ct, pt, sz);
+  return (0);
+}
+
+static const bulkops gencomp_encops = {
+  gencomp_init, gencomp_setup, gencomp_overhead,
+  gencomp_encdoit, gencomp_destroy
+}, gencomp_decops = {
+  gencomp_init, gencomp_setup, gencomp_overhead,
+  gencomp_decdoit, gencomp_destroy
+};
+
+const struct bulktab bulktab[] = {
+  { "gencomp", &gencomp_encops,        &gencomp_decops },
+  { 0,         0,                      0 }
+};
+
 /*----- Key encapsulation -------------------------------------------------*/
 
 /* --- RSA --- */
@@ -427,15 +566,16 @@ const struct kemtab kemtab[] = {
  * Arguments:  @key *k@ = the key to load
  *             @const char *app@ = application name
  *             @int wantpriv@ = nonzero if we want to decrypt
+ *             @bulk **bc@ = bulk crypto context to set up
  *
  * Returns:    A key-encapsulating thing.
  *
  * Use:                Loads a key.
  */
 
-kem *getkem(key *k, const char *app, int wantpriv)
+kem *getkem(key *k, const char *app, int wantpriv, bulk **bc)
 {
-  const char *kalg, *halg = 0, *calg = 0;
+  const char *kalg, *halg = 0, *balg = 0;
   dstr d = DSTR_INIT;
   dstr t = DSTR_INIT;
   size_t n;
@@ -444,6 +584,8 @@ kem *getkem(key *k, const char *app, int wantpriv)
   kem *kk;
   const struct kemtab *kt;
   const kemops *ko;
+  const struct bulktab *bt;
+  const bulkops *bo;
   void *kd;
   int e;
   key_packdef *kp;
@@ -468,17 +610,17 @@ kem *getkem(key *k, const char *app, int wantpriv)
     die(EXIT_FAILURE, "no KEM for key `%s'", t.buf);
   kalg = p;
 
-  /* --- Grab the encryption scheme --- *
+  /* --- Grab the bulk encryption scheme --- *
    *
    * Grab it from the KEM if it's there, but override it from the attribute.
    */
 
   if (p && (p = strchr(p, '/')) != 0) {
     *p++ = 0;
-    calg = p;
+    balg = p;
   }
-  if ((q = key_getattr(0, k, "cipher")) != 0)
-    calg = q;
+  if ((q = key_getattr(0, k, "bulk")) != 0)
+    balg = q;
 
   /* --- Grab the hash function --- */
 
@@ -516,42 +658,41 @@ k_found:;
   kk->ops = ko;
   kk->kd = kd;
 
-  /* --- Set up the algorithms --- */
+  /* --- Set up the bulk crypto --- */
 
   if (!halg)
-    kk->h = &rmd160;
-  else if ((kk->h = ghash_byname(halg)) == 0) {
+    kk->hc = &rmd160;
+  else if ((kk->hc = ghash_byname(halg)) == 0) {
     die(EXIT_FAILURE, "hash algorithm `%s' not found in key `%s'",
        halg, t.buf);
   }
 
-  if (!calg)
-    kk->c = &blowfish_cbc;
-  else if ((kk->c = gcipher_byname(calg)) == 0) {
-    die(EXIT_FAILURE, "encryption scheme `%s' not found in key `%s'",
-       calg, t.buf);
-  }
-
   dstr_reset(&d);
   if ((q = key_getattr(0, k, "kdf")) == 0) {
-    dstr_putf(&d, "%s-mgf", kk->h->name);
+    dstr_putf(&d, "%s-mgf", kk->hc->name);
     q = d.buf;
   }
-  if ((kk->cx = gcipher_byname(q)) == 0) {
+  if ((kk->cxc = gcipher_byname(q)) == 0) {
     die(EXIT_FAILURE, "encryption scheme (KDF) `%s' not found in key `%s'",
        q, t.buf);
   }
 
-  dstr_reset(&d);
-  if ((q = key_getattr(0, k, "mac")) == 0) {
-    dstr_putf(&d, "%s-hmac", kk->h->name);
-    q = d.buf;
-  }
-  if ((kk->m = gmac_byname(q)) == 0) {
-    die(EXIT_FAILURE,
-       "message authentication code `%s' not found in key `%s'",
-       q, t.buf);
+  if (!balg)
+    bt = bulktab;
+  else {
+    for (bt = bulktab, bo = 0; bt->name; bt++) {
+      if (strcmp(balg, bt->name) == 0)
+       { balg = 0; goto b_found; }
+      n = strlen(bt->name);
+      if (strncmp(balg, bt->name, n) == 0 && balg[n] == '-')
+       { balg += n + 1; goto b_found; }
+    }
+    bt = bulktab;
+  b_found:;
   }
+  bo = wantpriv ? bt->decops : bt->encops;
+  *bc = bo->init(k, balg, kk->hc->name);
+  (*bc)->ops = bo;
 
   /* --- Tidy up --- */
 
@@ -564,39 +705,29 @@ k_found:;
  *
  * Arguments:  @kem *k@ = key-encapsulation thing
  *             @dstr *d@ = key-encapsulation data
- *             @gcipher **cx@ = key-expansion function (for IVs)
- *             @gcipher **c@ = where to put initialized encryption scheme
- *             @gmac **m@ = where to put initialized MAC
+ *             @bulk *bc@ = bulk crypto context to set up
  *
  * Returns:    Zero on success, nonzero on failure.
  *
  * Use:                Initializes all the various symmetric things from a KEM.
  */
 
-int setupkem(kem *k, dstr *d, gcipher **cx, gcipher **c, gmac **m)
+int setupkem(kem *k, dstr *d, bulk *bc)
 {
   octet *kd;
-  size_t n, cn, mn;
+  size_t n;
   ghash *h;
   int rc = -1;
 
-  h = GH_INIT(k->h);
+  h = GH_INIT(k->hc);
   if (k->ops->doit(k, d, h))
     goto done;
-  n = keysz(GH_CLASS(h)->hashsz, k->cx->keysz);
+  n = keysz(GH_CLASS(h)->hashsz, k->cxc->keysz);
   if (!n)
     goto done;
   kd = GH_DONE(h, 0);
-  *cx = GC_INIT(k->cx, kd, n);
-
-  cn = keysz(0, k->c->keysz); n = cn;
-  mn = keysz(0, k->m->keysz); if (mn > n) n = mn;
-  kd = xmalloc(n);
-  GC_ENCRYPT(*cx, 0, kd, cn);
-  *c = GC_INIT(k->c, kd, cn);
-  GC_ENCRYPT(*cx, 0, kd, mn);
-  *m = GM_KEY(k->m, kd, mn);
-  xfree(kd);
+  k->cx = GC_INIT(k->cxc, kd, n);
+  bc->ops->setup(bc, k->cx);
 
   rc = 0;
 done:
@@ -621,6 +752,7 @@ void freekem(kem *k)
     key_fetchdone(k->kp);
     xfree(k->kd);
   }
+  GC_DESTROY(k->cx);
   k->ops->destroy(k);
 }
 
index 610a765..8ab6d32 100644 (file)
@@ -47,6 +47,8 @@
 
 #include <mLib/dstr.h>
 
+#include "buf.h"
+#include "ct.h"
 #include "key.h"
 #include "gcipher.h"
 #include "ghash.h"
 
 /*----- Cryptographic object tables ---------------------------------------*/
 
+typedef struct bulk {
+  const struct bulkops *ops;
+} bulk;
+
+typedef struct bulkops {
+  bulk *(*init)(key */*k*/, const char */*calg*/, const char */*halg*/);
+  int (*setup)(bulk */*b*/, gcipher */*cx*/);
+  size_t (*overhead)(bulk */*b*/);
+  const char *(*doit)(bulk */*b*/, uint32 /*seq*/, buf */*bb*/,
+                     const void */*p*/, size_t /*sz*/);
+  void (*destroy)(bulk */*b*/);
+} bulkops;
+
+struct bulktab {
+  const char *name;
+  const bulkops *encops;
+  const bulkops *decops;
+};
+
+extern const struct bulktab bulktab[];
+
 /* --- Key encapsulation --- */
 
 typedef struct kem {
   const struct kemops *ops;
   key_packdef *kp;
   void *kd;
-  const gchash *h;
-  const gccipher *c, *cx;
-  const gcmac *m;
+  const gchash *hc;
+  const gccipher *cxc;
+  gcipher *cx;
 } kem;
 
 typedef struct kemops {
@@ -87,29 +110,28 @@ extern const struct kemtab kemtab[];
  * Arguments:  @key *k@ = the key to load
  *             @const char *app@ = application name
  *             @int wantpriv@ = nonzero if we want to decrypt
+ *             @bulk **bc@ = bulk crypto context to set up
  *
  * Returns:    A key-encapsulating thing.
  *
  * Use:                Loads a key.
  */
 
-extern kem *getkem(key */*k*/, const char */*app*/, int /*wantpriv*/);
+extern kem *getkem(key */*k*/, const char */*app*/, int /*wantpriv*/,
+                  bulk **/*bc*/);
 
 /* --- @setupkem@ --- *
  *
  * Arguments:  @kem *k@ = key-encapsulation thing
  *             @dstr *d@ = key-encapsulation data
- *             @gcipher **cx@ = key-expansion function (for IVs)
- *             @gcipher **c@ = where to put initialized encryption scheme
- *             @gmac **m@ = where to put initialized MAC
+ *             @bulk *bc@ = bulk crypto context to set up
  *
  * Returns:    Zero for success, nonzero on faliure.
  *
  * Use:                Initializes all the various symmetric things from a KEM.
  */
 
-extern int setupkem(kem */*k*/, dstr */*d*/,
-                   gcipher **/*cx*/, gcipher **/*c*/, gmac **/*m*/);
+extern int setupkem(kem */*k*/, dstr */*d*/, bulk */*bc*/);
 
 /* --- @freekem@ --- *
  *