"hash-sz=%lu", (unsigned long)algs->h->hashsz,
A_END);
a_info(a,
- "bulk-transform=%s", algs->bulk->name,
- "bulk-overhead=%lu", (unsigned long)algs->bulk->overhead(algs),
+ "bulk-transform=%s", algs->bulk->ops->name,
+ "bulk-overhead=%lu",
+ (unsigned long)algs->bulk->ops->overhead(algs->bulk),
A_END);
- if (algs->c) {
- a_info(a,
- "cipher=%s", algs->c->name,
- "cipher-keysz=%lu", (unsigned long)algs->cksz,
- "cipher-blksz=%lu", (unsigned long)algs->c->blksz,
- A_END);
- }
+ algs->bulk->ops->alginfo(algs->bulk, a);
a_info(a,
- "cipher-data-limit=%lu", (unsigned long)algs->expsz,
+ "cipher-data-limit=%lu",
+ (unsigned long)algs->bulk->ops->expsz(algs->bulk),
A_END);
- if (algs->m) {
- a_info(a,
- "mac=%s", algs->m->name,
- "mac-keysz=%lu", (unsigned long)algs->mksz,
- "mac-tagsz=%lu", (unsigned long)algs->tagsz,
- A_END);
- }
- if (algs->b) {
- a_info(a,
- "blkc=%.*s", strlen(algs->b->name) - 4, algs->b->name,
- "blkc-keysz=%lu", (unsigned long)algs->bksz,
- "blkc-blksz=%lu", (unsigned long)algs->b->blksz,
- A_END);
- }
a_ok(a);
}
trace_block(T_CRYPTO, "crypto: expected MAC", (pmac), (tagsz)); \
}) } while (0)
+/*----- Common functionality for generic-composition transforms -----------*/
+
#define CHECK_MAC(h, pmac, tagsz) do { \
ghash *_h = (h); \
const octet *_pmac = (pmac); \
} \
} while (0)
+typedef struct gencomp_algs {
+ const gccipher *c; size_t cksz;
+ const gcmac *m; size_t mksz; size_t tagsz;
+} gencomp_algs;
+
+typedef struct gencomp_chal {
+ bulkchal _b;
+ gmac *m; size_t tagsz;
+} gencomp_chal;
+
+static int gencomp_getalgs(gencomp_algs *a, const algswitch *asw,
+ dstr *e, key_file *kf, key *k)
+{
+ const char *p;
+ char *q, *qq;
+ unsigned long n;
+ dstr d = DSTR_INIT;
+ int rc = -1;
+
+ /* --- Symmetric encryption --- */
+
+ if ((p = key_getattr(kf, k, "cipher")) == 0) p = "blowfish-cbc";
+ if ((a->c = gcipher_byname(p)) == 0) {
+ a_format(e, "unknown-cipher", "%s", p, A_END);
+ goto done;
+ }
+
+ /* --- Message authentication --- */
+
+ if ((p = key_getattr(kf, k, "mac")) != 0) {
+ dstr_reset(&d);
+ dstr_puts(&d, p);
+ if ((q = strchr(d.buf, '/')) != 0)
+ *q++ = 0;
+ if ((a->m = gmac_byname(d.buf)) == 0) {
+ a_format(e, "unknown-mac", "%s", d.buf, A_END);
+ goto done;
+ }
+ if (!q)
+ a->tagsz = a->m->hashsz;
+ else {
+ n = strtoul(q, &qq, 0);
+ if (*qq) {
+ a_format(e, "bad-tag-length-string", "%s", q, A_END);
+ goto done;
+ }
+ if (n%8 || n/8 > a->m->hashsz) {
+ a_format(e, "bad-tag-length", "%lu", n, A_END);
+ goto done;
+ }
+ a->tagsz = n/8;
+ }
+ } else {
+ dstr_reset(&d);
+ dstr_putf(&d, "%s-hmac", asw->h->name);
+ if ((a->m = gmac_byname(d.buf)) == 0) {
+ a_format(e, "no-hmac-for-hash", "%s", asw->h->name, A_END);
+ goto done;
+ }
+ a->tagsz = asw->h->hashsz/2;
+ }
+
+ rc = 0;
+done:
+ dstr_destroy(&d);
+ return (rc);
+}
+
+#ifndef NTRACE
+static void gencomp_tracealgs(const gencomp_algs *a)
+{
+ trace(T_CRYPTO, "crypto: cipher = %s", a->c->name);
+ trace(T_CRYPTO, "crypto: mac = %s/%lu",
+ a->m->name, (unsigned long)a->tagsz * 8);
+}
+#endif
+
+static int gencomp_checkalgs(gencomp_algs *a, const algswitch *asw, dstr *e)
+{
+ /* --- Derive the key sizes --- *
+ *
+ * Must ensure that we have non-empty keys. This isn't ideal, but it
+ * provides a handy sanity check. Also must be based on a 64- or 128-bit
+ * block cipher or we can't do the data expiry properly.
+ */
+
+ if ((a->cksz = keysz(asw->hashsz, a->c->keysz)) == 0) {
+ a_format(e, "cipher", "%s", a->c->name,
+ "no-key-size", "%lu", (unsigned long)asw->hashsz,
+ A_END);
+ return (-1);
+ }
+ if ((a->mksz = keysz(asw->hashsz, a->m->keysz)) == 0) {
+ a_format(e, "mac", "%s", a->m->name,
+ "no-key-size", "%lu", (unsigned long)asw->hashsz,
+ A_END);
+ return (-1);
+ }
+
+ return (0);
+}
+
+static void gencomp_alginfo(const gencomp_algs *a, admin *adm)
+{
+ a_info(adm,
+ "cipher=%s", a->c->name,
+ "cipher-keysz=%lu", (unsigned long)a->cksz,
+ "cipher-blksz=%lu", (unsigned long)a->c->blksz,
+ A_END);
+ a_info(adm,
+ "mac=%s", a->m->name,
+ "mac-keysz=%lu", (unsigned long)a->mksz,
+ "mac-tagsz=%lu", (unsigned long)a->tagsz,
+ A_END);
+}
+
+static int gencomp_samealgsp(const gencomp_algs *a, const gencomp_algs *aa)
+{
+ return (a->c == aa->c &&
+ a->m == aa->m && a->tagsz == aa->tagsz);
+}
+
+static size_t gencomp_expsz(const gencomp_algs *a)
+ { return (a->c->blksz < 16 ? MEG(64) : MEG(2048)); }
+
+static bulkchal *gencomp_genchal(const gencomp_algs *a)
+{
+ gencomp_chal *gc = CREATE(gencomp_chal);
+
+ rand_get(RAND_GLOBAL, buf_t, a->mksz);
+ gc->m = GM_KEY(a->m, buf_t, a->mksz);
+ gc->_b.tagsz = a->tagsz;
+ IF_TRACING(T_CHAL, {
+ trace(T_CHAL, "chal: generated new challenge key");
+ trace_block(T_CRYPTO, "chal: new key", buf_t, a->mksz);
+ })
+ return (&gc->_b);
+}
+
+static int gencomp_chaltag(bulkchal *bc, const void *m, size_t msz, void *t)
+{
+ gencomp_chal *gc = (gencomp_chal *)bc;
+ ghash *h = GM_INIT(gc->m);
+
+ GH_HASH(h, m, msz);
+ memcpy(t, GH_DONE(h, 0), bc->tagsz);
+ GH_DESTROY(h);
+ return (0);
+}
+
+static int gencomp_chalvrf(bulkchal *bc, const void *m, size_t msz,
+ const void *t)
+{
+ gencomp_chal *gc = (gencomp_chal *)bc;
+ ghash *h = GM_INIT(gc->m);
+ int ok;
+
+ GH_HASH(h, m, msz);
+ ok = ct_memeq(GH_DONE(h, 0), t, gc->_b.tagsz);
+ GH_DESTROY(h);
+ return (ok ? 0 : -1);
+}
+
+static void gencomp_freechal(bulkchal *bc)
+ { gencomp_chal *gc = (gencomp_chal *)bc; GM_DESTROY(gc->m); DESTROY(gc); }
+
/*----- The original transform --------------------------------------------*
*
* We generate a random initialization vector (if the cipher needs one). We
* ciphertext and extracts the sequence number.
*/
-static int v0_check(const algswitch *a, dstr *e)
- { return (0); }
+typedef struct v0_algs {
+ bulkalgs _b;
+ gencomp_algs ga;
+} v0_algs;
+
+typedef struct v0_ctx {
+ bulkctx _b;
+ size_t tagsz;
+ struct {
+ gcipher *c;
+ gmac *m;
+ } d[NDIR];
+} v0_ctx;
+
+static bulkalgs *v0_getalgs(const algswitch *asw, dstr *e,
+ key_file *kf, key *k)
+{
+ v0_algs *a = CREATE(v0_algs);
+ if (gencomp_getalgs(&a->ga, asw, e, kf, k)) { DESTROY(a); return (0); }
+ return (&a->_b);
+}
+
+#ifndef NTRACE
+static void v0_tracealgs(const bulkalgs *aa)
+ { const v0_algs *a = (const v0_algs *)aa; gencomp_tracealgs(&a->ga); }
+#endif
+
+static int v0_checkalgs(bulkalgs *aa, const algswitch *asw, dstr *e)
+{
+ v0_algs *a = (v0_algs *)aa;
+ if (gencomp_checkalgs(&a->ga, asw, e)) return (-1);
+ return (0);
+}
+
+static int v0_samealgsp(const bulkalgs *aa, const bulkalgs *bb)
+{
+ const v0_algs *a = (const v0_algs *)aa, *b = (const v0_algs *)bb;
+ return (gencomp_samealgsp(&a->ga, &b->ga));
+}
+
+static void v0_alginfo(const bulkalgs *aa, admin *adm)
+ { const v0_algs *a = (const v0_algs *)aa; gencomp_alginfo(&a->ga, adm); }
+
+static size_t v0_overhead(const bulkalgs *aa)
+{
+ const v0_algs *a = (const v0_algs *)aa;
+ return (a->ga.tagsz + SEQSZ + a->ga.c->blksz);
+}
-static size_t v0_overhead(const algswitch *a)
- { return a->tagsz + SEQSZ + a->c->blksz; }
+static size_t v0_expsz(const bulkalgs *aa)
+ { const v0_algs *a = (const v0_algs *)aa; return (gencomp_expsz(&a->ga)); }
-static int v0_encrypt(keyset *ks, unsigned ty, buf *b, buf *bb)
+static bulkctx *v0_genkeys(const bulkalgs *aa, const struct rawkey *rk)
{
+ const v0_algs *a = (const v0_algs *)aa;
+ v0_ctx *bc = CREATE(v0_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->ga.mksz, rk, i, "integrity");
+ bc->d[i].m = GM_KEY(a->ga.m, k, a->ga.mksz);
+ }
+ return (&bc->_b);
+}
+
+static bulkchal *v0_genchal(const bulkalgs *aa)
+{
+ const v0_algs *a = (const v0_algs *)aa;
+ return (gencomp_genchal(&a->ga));
+}
+#define v0_chaltag gencomp_chaltag
+#define v0_chalvrf gencomp_chalvrf
+#define v0_freechal gencomp_freechal
+
+static void v0_freealgs(bulkalgs *aa)
+ { v0_algs *a = (v0_algs *)aa; DESTROY(a); }
+
+static void v0_freectx(bulkctx *bbc)
+{
+ v0_ctx *bc = (v0_ctx *)bbc;
+ int i;
+
+ for (i = 0; i < NDIR; i++) {
+ GC_DESTROY(bc->d[i].c);
+ GM_DESTROY(bc->d[i].m);
+ }
+ DESTROY(bc);
+}
+
+static int v0_encrypt(bulkctx *bbc, unsigned ty,
+ buf *b, buf *bb, uint32 seq)
+{
+ v0_ctx *bc = (v0_ctx *)bbc;
ghash *h;
- gcipher *c = ks->out.c;
+ gcipher *c = bc->d[DIR_OUT].c;
const octet *p = BCUR(b);
size_t sz = BLEFT(b);
octet *qmac, *qseq, *qiv, *qpk;
- uint32 oseq;
size_t ivsz = GC_CLASS(c)->blksz;
- size_t tagsz = ks->tagsz;
+ size_t tagsz = bc->tagsz;
octet t[4];
/* --- Determine the ciphertext layout --- */
/* --- Store the sequence number --- */
- oseq = ks->oseq++;
- STORE32(qseq, oseq);
+ STORE32(qseq, seq);
/* --- Establish an initialization vector if necessary --- */
/* --- Compute a MAC over type, sequence number, IV, and ciphertext --- */
if (tagsz) {
- h = GM_INIT(ks->out.m);
+ h = GM_INIT(bc->d[DIR_OUT].m);
GH_HASH(h, t, sizeof(t));
GH_HASH(h, qseq, SEQSZ + ivsz + sz);
memcpy(qmac, GH_DONE(h, 0), tagsz);
return (0);
}
-static int v0_decrypt(keyset *ks, unsigned ty, buf *b, buf *bb, uint32 *seq)
+static int v0_decrypt(bulkctx *bbc, unsigned ty,
+ buf *b, buf *bb, uint32 *seq)
{
+ v0_ctx *bc = (v0_ctx *)bbc;
const octet *pmac, *piv, *pseq, *ppk;
size_t psz = BLEFT(b);
size_t sz;
octet *q = BCUR(bb);
ghash *h;
- gcipher *c = ks->in.c;
+ gcipher *c = bc->d[DIR_IN].c;
size_t ivsz = GC_CLASS(c)->blksz;
- size_t tagsz = ks->tagsz;
+ size_t tagsz = bc->tagsz;
octet t[4];
/* --- Break up the packet into its components --- */
if (psz < ivsz + SEQSZ + tagsz) {
- T( trace(T_KEYSET, "keyset: block too small for keyset %u", ks->seq); )
+ T( trace(T_KEYSET, "keyset: block too small for keyset"); )
return (KSERR_MALFORMED);
}
sz = psz - ivsz - SEQSZ - tagsz;
/* --- Verify the MAC on the packet --- */
if (tagsz) {
- h = GM_INIT(ks->in.m);
+ h = GM_INIT(bc->d[DIR_IN].m);
GH_HASH(h, t, sizeof(t));
GH_HASH(h, pseq, SEQSZ + ivsz + sz);
CHECK_MAC(h, pmac, tagsz);
* tagsz 32 sz
*/
-static int iiv_check(const algswitch *a, dstr *e)
+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)
{
- if (a->b->blksz < a->c->blksz) {
+ 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 size_t iiv_overhead(const algswitch *a)
- { return a->tagsz + SEQSZ; }
+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(keyset *ks, unsigned ty, buf *b, buf *bb)
+static int iiv_encrypt(bulkctx *bbc, unsigned ty,
+ buf *b, buf *bb, uint32 seq)
{
+ iiv_ctx *bc = (iiv_ctx *)bbc;
ghash *h;
- gcipher *c = ks->out.c, *blkc = ks->out.b;
+ 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;
- uint32 oseq;
size_t ivsz = GC_CLASS(c)->blksz, blkcsz = GC_CLASS(blkc)->blksz;
- size_t tagsz = ks->tagsz;
+ size_t tagsz = bc->tagsz;
octet t[4];
/* --- Determine the ciphertext layout --- */
/* --- Store the sequence number --- */
- oseq = ks->oseq++;
- STORE32(qseq, oseq);
+ STORE32(qseq, seq);
/* --- Establish an initialization vector if necessary --- */
/* --- Compute a MAC over type, sequence number, and ciphertext --- */
if (tagsz) {
- h = GM_INIT(ks->out.m);
+ 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);
return (0);
}
-static int iiv_decrypt(keyset *ks, unsigned ty, buf *b, buf *bb, uint32 *seq)
+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 = ks->in.c, *blkc = ks->in.b;
+ 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 = ks->tagsz;
+ 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 %u", ks->seq); )
+ T( trace(T_KEYSET, "keyset: block too small for keyset"); )
return (KSERR_MALFORMED);
}
sz = psz - SEQSZ - tagsz;
/* --- Verify the MAC on the packet --- */
if (tagsz) {
- h = GM_INIT(ks->in.m);
+ 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);
const bulkops bulktab[] = {
-#define BULK(name, pre, prim) \
- { name, prim, pre##_check, pre##_overhead, pre##_encrypt, pre##_decrypt }
+#define COMMA ,
+
+#define BULK(name, pre) \
+ { name, pre##_getalgs, T( pre##_tracealgs COMMA ) \
+ pre##_checkalgs, pre##_samealgsp, \
+ pre##_alginfo, pre##_overhead, pre##_expsz, \
+ pre##_genkeys, pre##_genchal, pre##_freealgs, \
+ pre##_encrypt, pre##_decrypt, pre##_freectx, \
+ pre##_chaltag, pre##_chalvrf, pre##_freechal }
- BULK("v0", v0, BCP_CIPHER | BCP_MAC),
- BULK("iiv", iiv, BCP_CIPHER | BCP_MAC | BCP_BLKC),
+ BULK("v0", v0),
+ BULK("iiv", iiv),
#undef BULK
{ 0 }
/*----- Static variables --------------------------------------------------*/
-static gmac *mac;
+static bulkchal *bulk;
static uint32 oseq;
static seqwin iseq;
static void c_genkey(void)
{
- if (mac && GM_CLASS(mac) == master->algs.m && oseq < 0x07ffffff) return;
- if (mac) GM_DESTROY(mac);
- assert(master->algs.mksz < sizeof(buf_t));
- rand_get(RAND_GLOBAL, buf_t, master->algs.mksz);
- mac = GM_KEY(master->algs.m, buf_t, master->algs.mksz);
+ if (bulk && bulk->ops == master->algs.bulk->ops && oseq < 0x07ffffff)
+ return;
+ if (bulk) bulk->ops->freechal(bulk);
+ bulk = master->algs.bulk->ops->genchal(master->algs.bulk);
+ bulk->ops = master->algs.bulk->ops;
oseq = 0;
seq_reset(&iseq);
- IF_TRACING(T_CHAL, {
- trace(T_CHAL, "chal: generated new challenge key");
- trace_block(T_CRYPTO, "chal: new key", buf_t, master->algs.mksz);
- })
}
/* --- @c_new@ --- *
int c_new(buf *b)
{
octet *p;
- ghash *h;
c_genkey();
p = BCUR(b);
- if (buf_putu32(b, oseq++)) return (-1);
- h = GM_INIT(mac);
- GH_HASH(h, p, BCUR(b) - p);
- buf_put(b, GH_DONE(h, 0), master->algs.tagsz);
- GH_DESTROY(h);
- if (BBAD(b)) return (-1);
+ if (buf_putu32(b, oseq++) || !buf_get(b, bulk->tagsz)) return (-1);
+ if (bulk->ops->chaltag(bulk, p, 4, p + 4)) return (-1);
IF_TRACING(T_CHAL, {
trace(T_CHAL, "chal: issuing challenge %lu", (unsigned long)(oseq - 1));
trace_block(T_CRYPTO, "chal: challenge block", p, BCUR(b) - p);
int c_check(buf *b)
{
const octet *p;
- size_t sz = 4 + master->algs.tagsz;
+ size_t sz;
uint32 seq;
- ghash *h;
- int ok;
+ if (!bulk) {
+ a_warn("CHAL", "impossible-challenge", A_END);
+ goto fail;
+ }
+ sz = 4 + bulk->tagsz;
if ((p = buf_get(b, sz)) == 0) {
a_warn("CHAL", "invalid-challenge", A_END);
goto fail;
}
IF_TRACING(T_CHAL, trace_block(T_CRYPTO, "chal: check challenge", p, sz); )
- if (!mac) {
- a_warn("CHAL", "impossible-challenge", A_END);
- goto fail;
- }
- h = GM_INIT(mac);
- GH_HASH(h, p, 4);
- ok = ct_memeq(GH_DONE(h, 0), p + 4, master->algs.tagsz);
- GH_DESTROY(h);
- if (!ok) {
+ if (bulk->ops->chalvrf(bulk, p, 4, p + 4)) {
a_warn("CHAL", "incorrect-tag", A_END);
goto fail;
}
static int algs_get(algswitch *a, dstr *e, key_file *kf, key *k)
{
const char *p;
- const bulkops *bulk;
- char *q, *qq;
+ const bulkops *bops;
dstr d = DSTR_INIT, dd = DSTR_INIT;
int rc = -1;
/* --- Bulk crypto transform --- */
if ((p = key_getattr(kf, k, "bulk")) == 0) p = "v0";
- for (bulk = bulktab; bulk->name && strcmp(p, bulk->name) != 0; bulk++);
- if (!bulk->name) {
+ for (bops = bulktab; bops->name && strcmp(p, bops->name) != 0; bops++);
+ if (!bops->name) {
a_format(e, "unknown-bulk-transform", "%s", p, A_END);
goto done;
}
- a->bulk = bulk;
-
- /* --- Symmetric encryption for bulk data --- */
-
- if (!(a->bulk->prim & BCP_CIPHER))
- a->c = 0;
- else {
- if ((p = key_getattr(kf, k, "cipher")) == 0) p = "blowfish-cbc";
- if ((a->c = gcipher_byname(p)) == 0) {
- a_format(e, "unknown-cipher", "%s", p, A_END);
- goto done;
- }
- }
-
- /* --- Block cipher for miscellaneous use --- */
-
- if (!(a->bulk->prim & BCP_BLKC))
- a->b = 0;
- else {
- if ((p = key_getattr(kf, k, "blkc")) == 0) {
- dstr_reset(&dd);
- dstr_puts(&dd, a->c ? a->c->name : "rijndael-");
- if ((q = strrchr(dd.buf, '-')) != 0) *q = 0;
- p = dd.buf;
- }
- dstr_reset(&d);
- dstr_putf(&d, "%s-ecb", p);
- if ((a->b = gcipher_byname(d.buf)) == 0) {
- a_format(e, "unknown-blkc", "%s", p, A_END);
- goto done;
- }
- }
-
- /* --- Message authentication for bulk data --- */
-
- if (!(a->bulk->prim & BCP_MAC)) {
- a->m = 0;
- a->tagsz = 0;
- } else {
- if ((p = key_getattr(kf, k, "mac")) != 0) {
- dstr_reset(&d);
- dstr_puts(&d, p);
- if ((q = strchr(d.buf, '/')) != 0)
- *q++ = 0;
- if ((a->m = gmac_byname(d.buf)) == 0) {
- a_format(e, "unknown-mac", "%s", d.buf, A_END);
- goto done;
- }
- if (!q)
- a->tagsz = a->m->hashsz;
- else {
- unsigned long n = strtoul(q, &qq, 0);
- if (*qq) {
- a_format(e, "bad-tag-length-string", "%s", q, A_END);
- goto done;
- }
- if (n%8 || n/8 > a->m->hashsz) {
- a_format(e, "bad-tag-length", "%lu", n, A_END);
- goto done;
- }
- a->tagsz = n/8;
- }
- } else {
- dstr_reset(&d);
- dstr_putf(&d, "%s-hmac", a->h->name);
- if ((a->m = gmac_byname(d.buf)) == 0) {
- a_format(e, "no-hmac-for-hash", "%s", a->h->name, A_END);
- goto done;
- }
- a->tagsz = a->h->hashsz/2;
- }
- }
+ if ((a->bulk = bops->getalgs(a, e, kf, k)) == 0) goto done;
+ a->bulk->ops = bops;
/* --- All done --- */
static int algs_check(algswitch *a, dstr *e, const group *g)
{
- /* --- Check the bulk crypto transform --- */
-
- if (a->bulk->check(a, e)) return (-1);
-
- /* --- Derive the key sizes --- *
- *
- * Must ensure that we have non-empty keys. This isn't ideal, but it
- * provides a handy sanity check. Also must be based on a 64- or 128-bit
- * block cipher or we can't do the data expiry properly.
- */
-
a->hashsz = a->h->hashsz;
- if (a->c && (a->cksz = keysz(a->hashsz, a->c->keysz)) == 0) {
- a_format(e, "cipher", "%s", a->c->name,
- "no-key-size", "%lu", (unsigned long)a->hashsz,
- A_END);
- return (-1);
- }
- if (a->m && (a->mksz = keysz(a->hashsz, a->m->keysz)) == 0) {
- a_format(e, "mac", "%s", a->m->name,
- "no-key-size", "%lu", (unsigned long)a->hashsz,
- A_END);
- return (-1);
- }
- if (a->b && (a->bksz = keysz(a->hashsz, a->b->keysz)) == 0) {
- a_format(e, "blkc", "%.*s", strlen(a->b->name) - 4, a->b->name,
- "no-key-size", "%lu", (unsigned long)a->hashsz,
- A_END);
- return (-1);
- }
-
- /* --- Derive the data limit --- */
-
- if (a->c && a->c->blksz < 16) a->expsz = MEG(64);
- else a->expsz = MEG(2048);
-
- /* --- Ensure the MGF accepts hashes as keys --- */
if (keysz(a->hashsz, a->mgf->keysz) != a->hashsz) {
a_format(e, "mgf", "%s", a->mgf->name,
return (-1);
}
- /* --- All ship-shape and Bristol-fashion --- */
+ if (a->bulk->ops->checkalgs(a->bulk, a, e)) return (-1);
return (0);
}
const algswitch *a = &kdx->algs, *aa = &kdy->algs;
return (group_samep(kdx->g, kdy->g) &&
- a->bulk == aa->bulk &&
- a->c == aa->c && a->b == aa->b &&
a->mgf == aa->mgf && a->h == aa->h &&
- a->m == aa->m && a->tagsz == aa->tagsz);
+ a->bulk->ops == aa->bulk->ops &&
+ a->bulk->ops->samealgsp(a->bulk, aa->bulk));
}
/*----- Key data and key nodes --------------------------------------------*/
trace(T_CRYPTO, "crypto: h = %s", mpstr(kd->g->h));
if (kd->kpriv)
trace(T_CRYPTO, "crypto: x = %s", mpstr(kd->kpriv));
- trace(T_CRYPTO, "crypto: cipher = %s", kd->algs.c->name);
- trace(T_CRYPTO, "crypto: mgf = %s", kd->algs.mgf->name);
- trace(T_CRYPTO, "crypto: hash = %s", kd->algs.h->name);
- trace(T_CRYPTO, "crypto: mac = %s/%lu",
- kd->algs.m->name, (unsigned long)kd->algs.tagsz * 8);
+ kd->algs.bulk->ops->tracealgs(kd->algs.bulk);
})
})
/* --- Apply the bulk-crypto transformation --- */
- rc = ks->bulk->encrypt(ks, ty, b, bb);
+ rc = ks->bulk->ops->encrypt(ks->bulk, ty, b, bb, ks->oseq);
if (rc || !BOK(bb)) return (rc);
+ ks->oseq++;
/* --- Do the necessary accounting for data volume --- */
trace_block(T_CRYPTO, "crypto: ciphertext packet", BCUR(b), BLEFT(b));
})
- rc = ks->bulk->decrypt(ks, ty, b, bb, seq);
+ rc = ks->bulk->ops->decrypt(ks->bulk, ty, b, bb, seq);
if (rc) return (rc);
IF_TRACING(T_KEYSET, {
void ks_drop(keyset *ks)
{
- if (--ks->ref)
- return;
-
-#define DROP(dir, a, drop) do { if (ks->dir.a) drop(ks->dir.a); } while (0)
-#define DROP_DIR(dir) do { \
- DROP(dir, c, GC_DESTROY); \
- DROP(dir, m, GM_DESTROY); \
-} while (0)
+ if (--ks->ref) return;
+ ks->bulk->ops->freectx(ks->bulk);
+ DESTROY(ks);
+}
- DROP_DIR(in);
- DROP_DIR(out);
+/* --- @ks_derivekey@ --- *
+ *
+ * Arguments: @octet *k@ = pointer to an output buffer of at least
+ * @MAXHASHSZ@ bytes
+ * @size_t ksz@ = actual size wanted (for tracing)
+ * @const struct rawkey *rk@ = a raw key, as passed into
+ * @genkeys@
+ * @int dir@ = direction for the key (@DIR_IN@ or @DIR_OUT@)
+ * @const char *what@ = label for the key (input to derivation)
+ *
+ * Returns: ---
+ *
+ * Use: Derives a session key, for use on incoming or outgoing data.
+ * This function is part of a private protocol between @ks_gen@
+ * and the bulk crypto transform @genkeys@ operation.
+ */
-#undef DROP
-#undef DROP_DIR
+struct rawkey {
+ const gchash *hc;
+ const octet *k;
+ size_t x, y, z;
+};
- DESTROY(ks);
+void ks_derivekey(octet *k, size_t ksz, const struct rawkey *rk,
+ int dir, const char *what)
+{
+ const gchash *hc = rk->hc;
+ ghash *h;
+
+ assert(ksz <= hc->hashsz);
+ assert(hc->hashsz <= MAXHASHSZ);
+ h = GH_INIT(hc);
+ GH_HASH(h, "tripe-", 6); GH_HASH(h, what, strlen(what) + 1);
+ switch (dir) {
+ case DIR_IN:
+ GH_HASH(h, rk->k, rk->x);
+ GH_HASH(h, rk->k + rk->x, rk->y - rk->x);
+ break;
+ case DIR_OUT:
+ GH_HASH(h, rk->k + rk->x, rk->y - rk->x);
+ GH_HASH(h, rk->k, rk->x);
+ break;
+ default:
+ abort();
+ }
+ GH_HASH(h, rk->k + rk->y, rk->z - rk->y);
+ GH_DONE(h, k);
+ GH_DESTROY(h);
+ IF_TRACING(T_KEYSET, { IF_TRACING(T_CRYPTO, {
+ char _buf[32];
+ sprintf(_buf, "crypto: %s key %s", dir ? "incoming" : "outgoing", what);
+ trace_block(T_CRYPTO, _buf, k, ksz);
+ }) })
}
/* --- @ks_gen@ --- *
* calling @ks_encrypt@ directly.
*/
-static void gen_dir(const algswitch *algs, struct ksdir *ksd,
- const char *whichdir,
- const octet *from, size_t fromsz,
- const octet *to, size_t tosz,
- const octet *both, size_t bothsz)
-{
-#define SETKEY(what, a, init) do { \
- ghash *_h; \
- octet *_hh; \
- \
- if (!algs->a) \
- ksd->a = 0; \
- else { \
- _h = GH_INIT(algs->h); \
- HASH_STRING(_h, "tripe-" what); \
- GH_HASH(_h, from, fromsz); \
- GH_HASH(_h, to, tosz); \
- GH_HASH(_h, both, bothsz); \
- _hh = GH_DONE(_h, 0); \
- IF_TRACING(T_KEYSET, { IF_TRACING(T_CRYPTO, { \
- char _buf[32]; \
- sprintf(_buf, "crypto: %s key " what, whichdir); \
- trace_block(T_CRYPTO, _buf, _hh, algs->a##ksz); \
- }) }) \
- ksd->a = init(algs->a, _hh, algs->a##ksz); \
- GH_DESTROY(_h); \
- } \
-} while (0)
-
- SETKEY("encryption", c, GC_INIT);
- SETKEY("integrity", m, GM_KEY);
- SETKEY("blkc", b, GC_INIT);
-
-#undef SETKEY
-}
-
keyset *ks_gen(const void *k, size_t x, size_t y, size_t z, peer *p)
{
keyset *ks = CREATE(keyset);
time_t now = time(0);
- const octet *pp = k;
const algswitch *algs = &p->kx.kpriv->algs;
+ struct rawkey rk;
T( static unsigned seq = 0; )
T( trace(T_KEYSET, "keyset: adding new keyset %u", seq); )
- gen_dir(algs, &ks->in, "incoming", pp, x, pp + x, y - x, pp + y, z - y);
- gen_dir(algs, &ks->out, "outgoing", pp + x, y - x, pp, x, pp + y, z - y);
+ rk.hc = algs->h; rk.k = k; rk.x = x; rk.y = y; rk.z = z;
+ ks->bulk = algs->bulk->ops->genkeys(algs->bulk, &rk);
+ ks->bulk->ops = algs->bulk->ops;
T( ks->seq = seq++; )
- ks->bulk = algs->bulk;
ks->ref = 1;
ks->t_exp = now + T_EXP;
- ks->sz_exp = algs->expsz;
- ks->sz_regen = algs->expsz/2;
+ ks->sz_exp = algs->bulk->ops->expsz(algs->bulk);
+ ks->sz_regen = ks->sz_exp/2;
ks->oseq = 0;
seq_reset(&ks->iseq);
ks->next = 0;
ks->p = p;
ks->f = KSF_LISTEN;
- ks->tagsz = algs->tagsz;
return (ks);
}
hash=rmd160 mgf=rmd160-mgf hash-sz=20
bulk-transform=v0 bulk-overhead=22
cipher=blowfish-cbc cipher-keysz=20 cipher-blksz=8
-cipher-data-limit=67108864
mac=rmd160-hmac mac-keysz=20 mac-tagsz=10
+cipher-data-limit=67108864
])
AT_DATA([algs-beta-old], [dnl
hash=rmd160 mgf=rmd160-mgf hash-sz=20
bulk-transform=v0 bulk-overhead=22
cipher=blowfish-cbc cipher-keysz=20 cipher-blksz=8
-cipher-data-limit=67108864
mac=rmd160-hmac mac-keysz=20 mac-tagsz=10
+cipher-data-limit=67108864
])
AT_DATA([algs-beta-new], [dnl
hash=rmd160 mgf=rmd160-mgf hash-sz=20
bulk-transform=iiv bulk-overhead=14
cipher=blowfish-cbc cipher-keysz=20 cipher-blksz=8
-cipher-data-limit=67108864
mac=rmd160-hmac mac-keysz=20 mac-tagsz=10
blkc=blowfish blkc-keysz=20 blkc-blksz=8
+cipher-data-limit=67108864
])
cp algs-alpha expout; AT_CHECK([TRIPECTL -dalice ALGS],, [expout])
typedef struct keyset keyset;
typedef struct algswitch algswitch;
+typedef struct admin admin;
+
+typedef struct bulkalgs {
+ const struct bulkops *ops;
+} bulkalgs;
+
+typedef struct bulkctx {
+ const struct bulkops *ops;
+} bulkctx;
+
+typedef struct bulkchal {
+ const struct bulkops *ops;
+ size_t tagsz;
+} bulkchal;
+
+struct rawkey;
typedef struct bulkops {
const char *name;
- unsigned prim;
- int (*check)(const algswitch */*a*/, dstr */*e*/);
- size_t (*overhead)(const algswitch */*a*/);
- int (*encrypt)(keyset */*ks*/, unsigned /*ty*/, buf */*b*/, buf */*bb*/);
- int (*decrypt)(keyset */*ks*/, unsigned /*ty*/,
+
+ bulkalgs *(*getalgs)(const algswitch */*asw*/, dstr */*e*/,
+ key_file */*kf*/, key */*k*/);
+ /* Determine algorithms to use and return a @bulkalgs@ object
+ * representing the decision. On error, write tokens to @e@ and
+ * return null.
+ */
+
+ T( void (*tracealgs)(const bulkalgs */*a*/); )
+ /* Write trace information about the algorithm selection. */
+
+ int (*checkalgs)(bulkalgs */*a*/, const algswitch */*asw*/, dstr */*e*/);
+ /* Check that the algorithms in @a@ and @asw@ are acceptable. On
+ * error, write tokens to @e@ and return @-1@; otherwise return zero.
+ */
+
+ int (*samealgsp)(const bulkalgs */*a*/, const bulkalgs */*aa*/);
+ /* If @a@ and @aa@ represent the same algorithm selection, return
+ * nonzero; if not, return zero.
+ */
+
+ void (*alginfo)(const bulkalgs */*a*/, admin */*adm*/);
+ /* Report on the algorithm selection to an admin client: call
+ * @a_info@ with appropriate key-value pairs.
+ */
+
+ size_t (*overhead)(const bulkalgs */*a*/);
+ /* Return the per-packet overhead of the bulk transform, in bytes. */
+
+ size_t (*expsz)(const bulkalgs */*a*/);
+ /* Return the total size limit for the bulk transform, in bytes,
+ * after which the keys must no longer be used.
+ */
+
+ bulkctx *(*genkeys)(const bulkalgs */*a*/, const struct rawkey */*rk*/);
+ /* Generate session keys and construct and return an appropriate
+ * context for using them, by calling @ks_derive@.
+ */
+
+ bulkchal *(*genchal)(const bulkalgs */*a*/);
+ /* Construct and return a challenge issuing and verification
+ * context with a fresh random key.
+ */
+
+ void (*freealgs)(bulkalgs */*a*/);
+ /* Release an algorithm selection object. (Associated bulk
+ * encryption contexts and challenge contexts may still exist and
+ * must remain valid.)
+ */
+
+ int (*encrypt)(bulkctx */*bc*/, unsigned /*ty*/,
+ buf */*b*/, buf */*bb*/, uint32 /*seq*/);
+ /* Encrypt the packet in @b@, with type @ty@ (which doesn't need
+ * encoding separately) and sequence number @seq@ (which must be
+ * recoverable by @decrypt@), and write the result to @bb@. On
+ * error, return a @KSERR_...@ code and/or break the output buffer.
+ */
+
+ int (*decrypt)(bulkctx */*bc*/, unsigned /*ty*/,
buf */*b*/, buf */*bb*/, uint32 */*seq*/);
-} bulkops;
+ /* Decrypt the packet in @b@, with type @ty@, writing the result to
+ * @bb@ and storing the incoming (claimed) sequence number in @seq@.
+ * On error, return a @KSERR_...@ code.
+ */
+
+ void (*freectx)(bulkctx */*a*/);
+ /* Release a bulk encryption context and the resources it holds. */
+
+ int (*chaltag)(bulkchal */*bc*/, const void */*m*/, size_t /*msz*/,
+ void */*t*/);
+ /* Calculate a tag for the challenge in @m@, @msz@, and write it to
+ * @t@. Return @-1@ on error, zero on success.
+ */
+
+ int (*chalvrf)(bulkchal */*bc*/, const void */*m*/, size_t /*msz*/,
+ const void */*t*/);
+ /* Check the tag @t@ on @m@, @msz@: return zero if the tag is OK,
+ * nonzero if it's bad.
+ */
+
+ void (*freechal)(bulkchal */*bc*/);
+ /* Release a challenge context and the resources it holds. */
-#define BCP_CIPHER 1
-#define BCP_MAC 2
-#define BCP_BLKC 4
+} bulkops;
struct algswitch {
- const gchash *h; /* Hash function */
+ const gchash *h; size_t hashsz; /* Hash function */
const gccipher *mgf; /* Mask-generation function */
- const bulkops *bulk; /* Bulk crypto transformation */
- const gccipher *c; /* Symmetric encryption scheme */
- const gcmac *m; /* Message authentication code */
- const gccipher *b; /* Block cipher */
- size_t hashsz; /* Hash output size */
- size_t tagsz; /* Length to truncate MAC tags */
- size_t expsz; /* Size of data to process */
- size_t cksz, mksz, bksz; /* Key lengths for things */
+ bulkalgs *bulk; /* Bulk crypto algorithms */
};
typedef struct kdata {
* expiry.
*/
+enum { DIR_IN, DIR_OUT, NDIR };
+
struct keyset {
struct keyset *next; /* Next active keyset in the list */
unsigned ref; /* Reference count for keyset */
unsigned long sz_exp, sz_regen; /* Data limits for the keyset */
T( unsigned seq; ) /* Sequence number for tracing */
unsigned f; /* Various useful flags */
- const bulkops *bulk; /* Bulk crypto transform */
- size_t tagsz; /* Length to truncate MAC tags */
- struct ksdir {
- gcipher *c; /* Keyset cipher for encryption */
- gmac *m; /* Keyset MAC for integrity */
- gcipher *b; /* Block cipher, just in case */
- } in, out;
+ bulkctx *bulk; /* Bulk crypto transform */
uint32 oseq; /* Outbound sequence number */
seqwin iseq; /* Inbound sequence number */
};
admin_jobentry *v; /* And the big array of entries */
} admin_jobtable;
-typedef struct admin {
+struct admin {
struct admin *next, *prev; /* Links to next and previous */
unsigned f; /* Various useful flags */
unsigned ref; /* Reference counter */
admin_jobtable j; /* Table of outstanding jobs */
selbuf b; /* Line buffer for commands */
sel_file w; /* Selector for write buffering */
-} admin;
+};
#define AF_DEAD 1u /* Destroy this admin block */
#define AF_CLOSE 2u /* Client closed connection */
extern void ks_drop(keyset */*ks*/);
+/* --- @ks_derivekey@ --- *
+ *
+ * Arguments: @octet *k@ = pointer to an output buffer of at least
+ * @MAXHASHSZ@ bytes
+ * @size_t ksz@ = actual size wanted (for tracing)
+ * @const struct rawkey *rk@ = a raw key, as passed into
+ * @genkeys@
+ * @int dir@ = direction for the key (@DIR_IN@ or @DIR_OUT@)
+ * @const char *what@ = label for the key (input to derivation)
+ *
+ * Returns: ---
+ *
+ * Use: Derives a session key, for use on incoming or outgoing data.
+ * This function is part of a private protocol between @ks_gen@
+ * and the bulk crypto transform @genkeys@ operation.
+ */
+
+extern void ks_derivekey(octet */*k*/, size_t /*ksz*/,
+ const struct rawkey */*rk*/,
+ int /*dir*/, const char */*what*/);
+
/* --- @ks_gen@ --- *
*
* Arguments: @const void *k@ = pointer to key material