From 66ff643c652defc000809488f58db090052ca75e Mon Sep 17 00:00:00 2001 Message-Id: <66ff643c652defc000809488f58db090052ca75e.1717994321.git.mdw@distorted.org.uk> From: Mark Wooding Date: Thu, 26 May 2016 09:26:09 +0100 Subject: [PATCH] progs/catcrypt.c, progs/cc-kem.c: Refactor bulk encryption. Organization: Straylight/Edgeware From: Mark Wooding 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 | 47 ++++++++-- progs/catcrypt.c | 103 +++++++--------------- progs/cc-kem.c | 220 +++++++++++++++++++++++++++++++++++++---------- progs/cc.h | 40 +++++++-- 4 files changed, 278 insertions(+), 132 deletions(-) diff --git a/progs/catcrypt.1 b/progs/catcrypt.1 index 597118f2..af5e6b3a 100644 --- a/progs/catcrypt.1 +++ b/progs/catcrypt.1 @@ -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 diff --git a/progs/catcrypt.c b/progs/catcrypt.c index 5879ecbe..72c77807 100644 --- a/progs/catcrypt.c +++ b/progs/catcrypt.c @@ -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, \ diff --git a/progs/cc-kem.c b/progs/cc-kem.c index e1653897..6c4d7f30 100644 --- a/progs/cc-kem.c +++ b/progs/cc-kem.c @@ -49,6 +49,145 @@ #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); } diff --git a/progs/cc.h b/progs/cc.h index 610a7658..8ab6d32f 100644 --- a/progs/cc.h +++ b/progs/cc.h @@ -47,6 +47,8 @@ #include +#include "buf.h" +#include "ct.h" #include "key.h" #include "gcipher.h" #include "ghash.h" @@ -54,15 +56,36 @@ /*----- 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@ --- * * -- [mdw]