X-Git-Url: https://git.distorted.org.uk/~mdw/catacomb/blobdiff_plain/0f00dc4c8eb47e67bc0f148c2dd109f73a451e0a..ee39a683a2b623a1da0747ec20f20b63470a2db6:/progs/cc-kem.c diff --git a/progs/cc-kem.c b/progs/cc-kem.c index e1653897..4a2cf192 100644 --- a/progs/cc-kem.c +++ b/progs/cc-kem.c @@ -46,9 +46,261 @@ #include "rmd160.h" #include "blowfish-cbc.h" +#include "poly1305.h" +#include "salsa20.h" +#include "chacha.h" #include "cc.h" +/*----- Bulk crypto -------------------------------------------------------*/ + +/* --- NaCl `secretbox' --- */ + +typedef struct naclbox_encctx { + bulk b; + const gccipher *cc; + gcipher *c; +} naclbox_encctx; + +static bulk *naclbox_init(key *k, const char *calg, const char *halg) +{ + naclbox_encctx *ctx = CREATE(naclbox_encctx); + dstr t = DSTR_INIT; + const char *q; + + key_fulltag(k, &t); + + if ((q = key_getattr(0, k, "cipher")) != 0) calg = q; + if (!calg || strcmp(calg, "salsa20") == 0) ctx->cc = &salsa20; + else if (strcmp(calg, "salsa20/12") == 0) ctx->cc = &salsa2012; + else if (strcmp(calg, "salsa20/8") == 0) ctx->cc = &salsa208; + else if (strcmp(calg, "chacha20") == 0) ctx->cc = &chacha20; + else if (strcmp(calg, "chacha12") == 0) ctx->cc = &chacha12; + else if (strcmp(calg, "chacha8") == 0) ctx->cc = &chacha8; + else { + die(EXIT_FAILURE, + "unknown or inappropriate encryption scheme `%s' in key `%s'", + calg, t.buf); + } + + dstr_destroy(&t); + return (&ctx->b); +} + +static int naclbox_setup(bulk *b, gcipher *cx) +{ + naclbox_encctx *ctx = (naclbox_encctx *)b; + octet k[SALSA20_KEYSZ]; + + GC_ENCRYPT(cx, 0, k, sizeof(k)); + ctx->c = GC_INIT(ctx->cc, k, sizeof(k)); + return (0); +} + +static size_t naclbox_overhead(bulk *b) { return (POLY1305_TAGSZ); } + +static void naclbox_destroy(bulk *b) +{ + naclbox_encctx *ctx = (naclbox_encctx *)b; + + GC_DESTROY(ctx->c); + DESTROY(ctx); +} + +static const char *naclbox_encdoit(bulk *b, uint32 seq, buf *bb, + const void *p, size_t sz) +{ + naclbox_encctx *ctx = (naclbox_encctx *)b; + octet t[32]; + poly1305_key ak; + poly1305_ctx a; + octet *tag, *ct; + + STORE32(t, seq); STORE32(t + 4, 0); GC_SETIV(ctx->c, t); + GC_ENCRYPT(ctx->c, 0, t, POLY1305_KEYSZ + POLY1305_MASKSZ); + poly1305_keyinit(&ak, t, POLY1305_KEYSZ); + poly1305_macinit(&a, &ak, t + POLY1305_KEYSZ); + + tag = buf_get(bb, POLY1305_TAGSZ); assert(tag); + ct = buf_get(bb, sz); assert(ct); + GC_ENCRYPT(ctx->c, p, ct, sz); + poly1305_hash(&a, ct, sz); + poly1305_done(&a, tag); + return (0); +} + +static const char *naclbox_decdoit(bulk *b, uint32 seq, buf *bb, + const void *p, size_t sz) +{ + naclbox_encctx *ctx = (naclbox_encctx *)b; + buf bin; + octet t[32]; + poly1305_key ak; + poly1305_ctx a; + octet *tag, *ct, *pt; + + STORE32(t, seq); STORE32(t + 4, 0); GC_SETIV(ctx->c, t); + GC_ENCRYPT(ctx->c, 0, t, POLY1305_KEYSZ + POLY1305_MASKSZ); + poly1305_keyinit(&ak, t, POLY1305_KEYSZ); + poly1305_macinit(&a, &ak, t + POLY1305_KEYSZ); + + buf_init(&bin, (/*unconst*/ void *)p, sz); + if ((tag = buf_get(&bin, POLY1305_TAGSZ)) == 0) return ("no tag"); + ct = BCUR(&bin); sz = BLEFT(&bin); + poly1305_hash(&a, ct, sz); + poly1305_done(&a, t); + if (!ct_memeq(t, tag, POLY1305_TAGSZ)) return ("authentication failure"); + pt = buf_get(bb, sz); assert(pt); + GC_DECRYPT(ctx->c, ct, pt, sz); + return (0); +} + +static const bulkops naclbox_encops = { + naclbox_init, naclbox_setup, naclbox_overhead, + naclbox_encdoit, naclbox_destroy +}, naclbox_decops = { + naclbox_init, naclbox_setup, naclbox_overhead, + naclbox_decdoit, naclbox_destroy +}; + +/* --- 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 }, + { "naclbox", &naclbox_encops, &naclbox_decops }, + { 0, 0, 0 } +}; + /*----- Key encapsulation -------------------------------------------------*/ /* --- RSA --- */ @@ -427,15 +679,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 +697,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 +723,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 +771,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 +818,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 +865,7 @@ void freekem(kem *k) key_fetchdone(k->kp); xfree(k->kd); } + GC_DESTROY(k->cx); k->ops->destroy(k); }