X-Git-Url: https://git.distorted.org.uk/~mdw/catacomb/blobdiff_plain/373641eaacc86b56715a2ebf0b603fce25c16051..8f2287ef5c05d496fcb9b012629af007fe56f897:/progs/cc-kem.c diff --git a/progs/cc-kem.c b/progs/cc-kem.c index 5b0cbf79..9159ade7 100644 --- a/progs/cc-kem.c +++ b/progs/cc-kem.c @@ -36,6 +36,7 @@ #include #include +#include "gaead.h" #include "mprand.h" #include "rand.h" @@ -48,6 +49,7 @@ #include "rmd160.h" #include "blowfish-cbc.h" +#include "chacha20-poly1305.h" #include "poly1305.h" #include "salsa20.h" #include "chacha.h" @@ -56,113 +58,180 @@ /*----- Bulk crypto -------------------------------------------------------*/ -/* --- NaCl `secretbox' --- */ +/* --- Authenticated encryption schemes --- */ -typedef struct naclbox_encctx { +typedef struct aead_encctx { bulk b; - const gccipher *cc; - gcipher *c; -} naclbox_encctx; + const gcaead *aec; + gaead_key *key; + union { gaead_enc *enc; gaead_dec *dec; } ed; + octet *t; + size_t nsz, tsz; +} aead_encctx; -static bulk *naclbox_init(key *k, const char *calg, const char *halg) +static bulk *aead_internalinit(key *k, const gcaead *aec) { - naclbox_encctx *ctx = CREATE(naclbox_encctx); - dstr t = DSTR_INIT; + aead_encctx *ctx = CREATE(aead_encctx); + + ctx->key = 0; + ctx->aec = aec; + if ((ctx->nsz = keysz_pad(4, aec->noncesz)) == 0) + die(EXIT_FAILURE, "no suitable nonce size for `%s'", aec->name); + ctx->tsz = keysz(0, ctx->aec->tagsz); + + return (&ctx->b); +} + +static bulk *aead_init(key *k, const char *calg, const char *halg) +{ + const gcaead *aec; const char *q; + dstr t = DSTR_INIT; 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'", + if (!calg) aec = &chacha20_poly1305; + else if ((aec = gaead_byname(calg)) == 0) + die(EXIT_FAILURE, "AEAD scheme `%s' not found in key `%s'", calg, t.buf); - } dstr_destroy(&t); - return (&ctx->b); + return (aead_internalinit(k, aec)); } -static int naclbox_setup(bulk *b, gcipher *cx) +static int aead_commonsetup(aead_encctx *ctx, gcipher *cx) { - naclbox_encctx *ctx = (naclbox_encctx *)b; - octet k[SALSA20_KEYSZ]; + size_t ksz, n; - GC_ENCRYPT(cx, 0, k, sizeof(k)); - ctx->c = GC_INIT(ctx->cc, k, sizeof(k)); + n = ksz = keysz(0, ctx->aec->keysz); + if (n < ctx->nsz) n = ctx->nsz; + if (n < ctx->tsz) n = ctx->tsz; + ctx->t = xmalloc(n); + + GC_ENCRYPT(cx, 0, ctx->t, ksz); + ctx->key = GAEAD_KEY(ctx->aec, ctx->t, ksz); return (0); } -static size_t naclbox_overhead(bulk *b) { return (POLY1305_TAGSZ); } +static size_t aead_overhead(bulk *b) + { aead_encctx *ctx = (aead_encctx *)b; return (ctx->aec->ohd + ctx->tsz); } -static void naclbox_destroy(bulk *b) +static void aead_commondestroy(aead_encctx *ctx) { - naclbox_encctx *ctx = (naclbox_encctx *)b; - - GC_DESTROY(ctx->c); + if (ctx->key) GAEAD_DESTROY(ctx->key); + xfree(ctx->t); DESTROY(ctx); } -static const char *naclbox_encdoit(bulk *b, uint32 seq, buf *bb, - const void *p, size_t sz) +static int aead_encsetup(bulk *b, gcipher *cx) { - naclbox_encctx *ctx = (naclbox_encctx *)b; - octet t[32]; - poly1305_key ak; - poly1305_ctx a; - octet *tag, *ct; + aead_encctx *ctx = (aead_encctx *)b; + ctx->ed.enc = 0; return (aead_commonsetup(ctx, cx)); +} - 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); +static const char *aead_encdoit(bulk *b, uint32 seq, buf *bb, + const void *p, size_t sz) +{ + aead_encctx *ctx = (aead_encctx *)b; + octet *t; + int rc; - 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); + memset(ctx->t + 4, 0, ctx->nsz - 4); STORE32_B(ctx->t, seq); + if (!ctx->ed.enc) + ctx->ed.enc = GAEAD_ENC(ctx->key, ctx->t, ctx->nsz, 0, sz, ctx->tsz); + else + GAEAD_REINIT(ctx->ed.enc, ctx->t, ctx->nsz, 0, sz, ctx->tsz); + t = buf_get(bb, ctx->tsz); assert(t); + rc = GAEAD_ENCRYPT(ctx->ed.enc, p, sz, bb); assert(rc >= 0); + rc = GAEAD_DONE(ctx->ed.enc, 0, bb, t, ctx->tsz); assert(rc >= 0); return (0); } -static const char *naclbox_decdoit(bulk *b, uint32 seq, buf *bb, - const void *p, size_t sz) +static void aead_encdestroy(bulk *b) { - naclbox_encctx *ctx = (naclbox_encctx *)b; + aead_encctx *ctx = (aead_encctx *)b; + if (ctx->ed.enc) GAEAD_DESTROY(ctx->ed.enc); + aead_commondestroy(ctx); +} + +static int aead_decsetup(bulk *b, gcipher *cx) +{ + aead_encctx *ctx = (aead_encctx *)b; + ctx->ed.dec = 0; return (aead_commonsetup(ctx, cx)); +} + +static const char *aead_decdoit(bulk *b, uint32 seq, buf *bb, + const void *p, size_t sz) +{ + aead_encctx *ctx = (aead_encctx *)b; buf bin; - octet t[32]; - poly1305_key ak; - poly1305_ctx a; - octet *tag, *ct, *pt; + const octet *t; + int rc; - 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); + memset(ctx->t + 4, 0, ctx->nsz - 4); STORE32_B(ctx->t, seq); + if (!ctx->ed.dec) + ctx->ed.dec = GAEAD_DEC(ctx->key, ctx->t, ctx->nsz, 0, sz, ctx->tsz); + else + GAEAD_REINIT(ctx->ed.enc, ctx->t, ctx->nsz, 0, sz, ctx->tsz); 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); + t = buf_get(&bin, ctx->tsz); if (!t) return ("no tag"); + rc = GAEAD_DECRYPT(ctx->ed.dec, BCUR(&bin), BLEFT(&bin), bb); + assert(rc >= 0); + rc = GAEAD_DONE(ctx->ed.dec, 0, bb, t, ctx->tsz); assert(rc >= 0); + if (!rc) return ("authentication failure"); return (0); } +static void aead_decdestroy(bulk *b) +{ + aead_encctx *ctx = (aead_encctx *)b; + if (ctx->ed.dec) GAEAD_DESTROY(ctx->ed.dec); + aead_commondestroy(ctx); +} + +static const struct bulkops aead_encops = { + aead_init, aead_encsetup, aead_overhead, + aead_encdoit, aead_encdestroy +}, aead_decops = { + aead_init, aead_decsetup, aead_overhead, + aead_decdoit, aead_decdestroy +}; + +/* --- NaCl `secretbox' in terms of AEAD --- */ + +static bulk *naclbox_init(key *k, const char *calg, const char *halg) +{ + const gcaead *aec; + 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) aec = &salsa20_naclbox; + else if (strcmp(calg, "salsa20/12") == 0) aec = &salsa2012_naclbox; + else if (strcmp(calg, "salsa20/8") == 0) aec = &salsa208_naclbox; + else if (strcmp(calg, "chacha20") == 0) aec = &chacha20_naclbox; + else if (strcmp(calg, "chacha12") == 0) aec = &chacha12_naclbox; + else if (strcmp(calg, "chacha8") == 0) aec = &chacha8_naclbox; + else { + die(EXIT_FAILURE, + "unknown or inappropriate encryption scheme `%s' in key `%s'", + calg, t.buf); + } + + dstr_destroy(&t); + return (aead_internalinit(k, aec)); +} + static const bulkops naclbox_encops = { - naclbox_init, naclbox_setup, naclbox_overhead, - naclbox_encdoit, naclbox_destroy + naclbox_init, aead_encsetup, aead_overhead, + aead_encdoit, aead_encdestroy }, naclbox_decops = { - naclbox_init, naclbox_setup, naclbox_overhead, - naclbox_decdoit, naclbox_destroy + naclbox_init, aead_decsetup, aead_overhead, + aead_decdoit, aead_decdestroy }; /* --- Generic composition --- */ @@ -300,6 +369,7 @@ static const bulkops gencomp_encops = { const struct bulktab bulktab[] = { { "gencomp", &gencomp_encops, &gencomp_decops }, { "naclbox", &naclbox_encops, &naclbox_decops }, + { "aead", &aead_encops, &aead_decops }, { 0, 0, 0 } }; @@ -604,135 +674,79 @@ static const kemops ec_decops = { ec_decinit, dh_decdoit, dh_enccheck, dh_encdestroy }; -/* --- X25519 --- */ - -static kem *x25519_encinit(key *k, void *kd) { return (CREATE(kem)); } -static void x25519_encdestroy(kem *k) { DESTROY(k); } - -static const char *x25519_enccheck(kem *k) -{ - x25519_pub *kd = k->kd; - - if (kd->pub.sz != X25519_PUBSZ) - return ("incorrect X25519 public key length"); - return (0); -} - -static int x25519_encdoit(kem *k, dstr *d, ghash *h) -{ - octet t[X25519_KEYSZ], z[X25519_OUTSZ]; - x25519_pub *kd = k->kd; - - rand_get(RAND_GLOBAL, t, sizeof(t)); - dstr_ensure(d, X25519_PUBSZ); - x25519((octet *)d->buf, t, x25519_base); - x25519(z, t, kd->pub.k); - d->len += X25519_PUBSZ; - GH_HASH(h, d->buf, X25519_PUBSZ); - GH_HASH(h, z, X25519_OUTSZ); - return (0); -} - -static const char *x25519_deccheck(kem *k) -{ - x25519_priv *kd = k->kd; - - if (kd->priv.sz != X25519_KEYSZ) - return ("incorrect X25519 private key length"); - if (kd->pub.sz != X25519_PUBSZ) - return ("incorrect X25519 public key length"); - return (0); -} - -static int x25519_decdoit(kem *k, dstr *d, ghash *h) -{ - octet z[X25519_OUTSZ]; - x25519_priv *kd = k->kd; - int rc = -1; - - if (d->len != X25519_PUBSZ) goto done; - x25519(z, kd->priv.k, (const octet *)d->buf); - GH_HASH(h, d->buf, X25519_PUBSZ); - GH_HASH(h, z, X25519_OUTSZ); - rc = 0; -done: - return (rc); -} - -static const kemops x25519_encops = { - x25519_pubfetch, sizeof(x25519_pub), - x25519_encinit, x25519_encdoit, x25519_enccheck, x25519_encdestroy -}; - -static const kemops x25519_decops = { - x25519_privfetch, sizeof(x25519_priv), - x25519_encinit, x25519_decdoit, x25519_deccheck, x25519_encdestroy -}; - -/* --- X448 --- */ - -static kem *x448_encinit(key *k, void *kd) { return (CREATE(kem)); } -static void x448_encdestroy(kem *k) { DESTROY(k); } - -static const char *x448_enccheck(kem *k) -{ - x448_pub *kd = k->kd; - - if (kd->pub.sz != X448_PUBSZ) - return ("incorrect X448 public key length"); - return (0); -} - -static int x448_encdoit(kem *k, dstr *d, ghash *h) -{ - octet t[X448_KEYSZ], z[X448_OUTSZ]; - x448_pub *kd = k->kd; - - rand_get(RAND_GLOBAL, t, sizeof(t)); - dstr_ensure(d, X448_PUBSZ); - x448((octet *)d->buf, t, x448_base); - x448(z, t, kd->pub.k); - d->len += X448_PUBSZ; - GH_HASH(h, d->buf, X448_PUBSZ); - GH_HASH(h, z, X448_OUTSZ); - return (0); -} - -static const char *x448_deccheck(kem *k) -{ - x448_priv *kd = k->kd; - - if (kd->priv.sz != X448_KEYSZ) - return ("incorrect X448 private key length"); - if (kd->pub.sz != X448_PUBSZ) - return ("incorrect X448 public key length"); - return (0); -} - -static int x448_decdoit(kem *k, dstr *d, ghash *h) -{ - octet z[X448_OUTSZ]; - x448_priv *kd = k->kd; - int rc = -1; - - if (d->len != X448_PUBSZ) goto done; - x448(z, kd->priv.k, (const octet *)d->buf); - GH_HASH(h, d->buf, X448_PUBSZ); - GH_HASH(h, z, X448_OUTSZ); - rc = 0; -done: - return (rc); -} - -static const kemops x448_encops = { - x448_pubfetch, sizeof(x448_pub), - x448_encinit, x448_encdoit, x448_enccheck, x448_encdestroy -}; - -static const kemops x448_decops = { - x448_privfetch, sizeof(x448_priv), - x448_encinit, x448_decdoit, x448_deccheck, x448_encdestroy -}; +/* --- X25519 and similar schemes --- */ + +#define XDHS(_) \ + _(x25519, X25519) \ + _(x448, X448) + +#define XDHDEF(xdh, XDH) \ + \ + static kem *xdh##_encinit(key *k, void *kd) { return (CREATE(kem)); } \ + static void xdh##_encdestroy(kem *k) { DESTROY(k); } \ + \ + static const char *xdh##_enccheck(kem *k) \ + { \ + xdh##_pub *kd = k->kd; \ + \ + if (kd->pub.sz != XDH##_PUBSZ) \ + return ("incorrect " #XDH "public key length"); \ + return (0); \ + } \ + \ + static int xdh##_encdoit(kem *k, dstr *d, ghash *h) \ + { \ + octet t[XDH##_KEYSZ], z[XDH##_OUTSZ]; \ + xdh##_pub *kd = k->kd; \ + \ + rand_get(RAND_GLOBAL, t, sizeof(t)); \ + dstr_ensure(d, XDH##_PUBSZ); \ + xdh((octet *)d->buf, t, xdh##_base); \ + xdh(z, t, kd->pub.k); \ + d->len += XDH##_PUBSZ; \ + GH_HASH(h, d->buf, XDH##_PUBSZ); \ + GH_HASH(h, z, XDH##_OUTSZ); \ + return (0); \ + } \ + \ + static const char *xdh##_deccheck(kem *k) \ + { \ + xdh##_priv *kd = k->kd; \ + \ + if (kd->priv.sz != XDH##_KEYSZ) \ + return ("incorrect " #XDH " private key length"); \ + if (kd->pub.sz != XDH##_PUBSZ) \ + return ("incorrect " #XDH " public key length"); \ + return (0); \ + } \ + \ + static int xdh##_decdoit(kem *k, dstr *d, ghash *h) \ + { \ + octet z[XDH##_OUTSZ]; \ + xdh##_priv *kd = k->kd; \ + int rc = -1; \ + \ + if (d->len != XDH##_PUBSZ) goto done; \ + xdh(z, kd->priv.k, (const octet *)d->buf); \ + GH_HASH(h, d->buf, XDH##_PUBSZ); \ + GH_HASH(h, z, XDH##_OUTSZ); \ + rc = 0; \ + done: \ + return (rc); \ + } \ + \ + static const kemops xdh##_encops = { \ + xdh##_pubfetch, sizeof(xdh##_pub), \ + xdh##_encinit, xdh##_encdoit, xdh##_enccheck, xdh##_encdestroy \ + }; \ + \ + static const kemops xdh##_decops = { \ + xdh##_privfetch, sizeof(xdh##_priv), \ + xdh##_encinit, xdh##_decdoit, xdh##_deccheck, xdh##_encdestroy \ + }; + +XDHS(XDHDEF) +#undef XDHDEF /* --- Symmetric --- */ @@ -802,8 +816,10 @@ const struct kemtab kemtab[] = { { "dh", &dh_encops, &dh_decops }, { "bindh", &bindh_encops, &bindh_decops }, { "ec", &ec_encops, &ec_decops }, - { "x25519", &x25519_encops, &x25519_decops }, - { "x448", &x448_encops, &x448_decops }, +#define XDHTAB(xdh, XDH) \ + { #xdh, &xdh##_encops, &xdh##_decops }, + XDHS(XDHTAB) +#undef XDHTAB { "symm", &symm_encops, &symm_decops }, { 0, 0, 0 } }; @@ -914,16 +930,6 @@ k_found:; halg, t.buf); } - dstr_reset(&d); - if ((q = key_getattr(0, k, "kdf")) == 0) { - dstr_putf(&d, "%s-mgf", kk->hc->name); - q = d.buf; - } - if ((kk->cxc = gcipher_byname(q)) == 0) { - die(EXIT_FAILURE, "encryption scheme (KDF) `%s' not found in key `%s'", - q, t.buf); - } - if (!balg) bt = bulktab; else { @@ -941,6 +947,16 @@ k_found:; *bc = bo->init(k, balg, kk->hc->name); (*bc)->ops = bo; + dstr_reset(&d); + if ((q = key_getattr(0, k, "kdf")) == 0) { + dstr_putf(&d, "%s-mgf", kk->hc->name); + q = d.buf; + } + if ((kk->cxc = gcipher_byname(q)) == 0) { + die(EXIT_FAILURE, "encryption scheme (KDF) `%s' not found in key `%s'", + q, t.buf); + } + /* --- Tidy up --- */ dstr_destroy(&d);