From: Mark Wooding Date: Sat, 24 May 2014 13:00:03 +0000 (+0100) Subject: server/: Prepare an interface for multiple bulk-crypto transforms. X-Git-Tag: 1.0.0pre16~20 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/tripe/commitdiff_plain/a93aacce200e0d68b614d8bfb05d9cbeba850b12?hp=494a7ac04de2a38bf6aade234602f831be314c55 server/: Prepare an interface for multiple bulk-crypto transforms. The current bulk-crypto transform is rather old-fashioned (though in most formal senses secure). It is neither as efficient as it could be (in terms of overhead); nor does it provide privacy properties which are as good as I'd like. So it would be good to be able to replace it with something better. There's now a table of named transforms. It only has one entry, `v0', which is compatible with previous versions, but we now have all of the machinery necessary to add new transforms later. There are also some minor improvements to the tracing of cryptographic details (notably: reporting the types of messages being encrypted and decrypted); and an interface change for @ks_encrypt@ and @ks_decrypt@, which are now allowed to corrupt @buf_u@ in the course of their operation. --- diff --git a/server/Makefile.am b/server/Makefile.am index e1b41e3b..ad5b4a98 100644 --- a/server/Makefile.am +++ b/server/Makefile.am @@ -44,6 +44,7 @@ tripe_SOURCES += tripe.h tripe_SOURCES += servutil.c tripe_SOURCES += addrmap.c tripe_SOURCES += keymgmt.c +tripe_SOURCES += bulkcrypto.c tripe_SOURCES += keyset.c tripe_SOURCES += keyexch.c tripe_SOURCES += chal.c diff --git a/server/admin.c b/server/admin.c index 16ab8a35..f3091be6 100644 --- a/server/admin.c +++ b/server/admin.c @@ -1731,18 +1731,26 @@ static void acmd_algs(admin *a, unsigned ac, char *av[]) "hash-sz=%lu", (unsigned long)algs->h->hashsz, A_END); a_info(a, - "cipher=%s", algs->c->name, - "cipher-keysz=%lu", (unsigned long)algs->cksz, - "cipher-blksz=%lu", (unsigned long)algs->c->blksz, + "bulk-transform=%s", algs->bulk->name, + "bulk-overhead=%lu", (unsigned long)algs->bulk->overhead(algs), 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); + } a_info(a, "cipher-data-limit=%lu", (unsigned long)algs->expsz, A_END); - 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->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); + } a_ok(a); } diff --git a/server/bulkcrypto.c b/server/bulkcrypto.c new file mode 100644 index 00000000..faaf8d71 --- /dev/null +++ b/server/bulkcrypto.c @@ -0,0 +1,215 @@ +/* -*-c-*- + * + * Bulk crypto transformations + * + * (c) 2014 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Trivial IP Encryption (TrIPE). + * + * TrIPE is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * TrIPE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with TrIPE; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Header files ------------------------------------------------------*/ + +#include "tripe.h" + +/*----- Utilities ---------------------------------------------------------*/ + +#define SEQSZ 4 /* Size of sequence number packet */ + +#define TRACE_IV(qiv, ivsz) do { IF_TRACING(T_KEYSET, { \ + trace_block(T_CRYPTO, "crypto: initialization vector", \ + (qiv), (ivsz)); \ +}) } while (0) + +#define TRACE_CT(qpk, sz) do { IF_TRACING(T_KEYSET, { \ + trace_block(T_CRYPTO, "crypto: encrypted packet", (qpk), (sz)); \ +}) } while (0) + +#define TRACE_MAC(qmac, tagsz) do { IF_TRACING(T_KEYSET, { \ + trace_block(T_CRYPTO, "crypto: computed MAC", (qmac), (tagsz)); \ +}) } while (0) + +#define CHECK_MAC(h, pmac, tagsz) do { \ + ghash *_h = (h); \ + const octet *_pmac = (pmac); \ + size_t _tagsz = (tagsz); \ + octet *_mac = GH_DONE(_h, 0); \ + int _eq = ct_memeq(_mac, _pmac, _tagsz); \ + TRACE_MAC(_mac, _tagsz); \ + GH_DESTROY(_h); \ + if (!_eq) { \ + IF_TRACING(T_KEYSET, { \ + trace(T_KEYSET, "keyset: incorrect MAC: decryption failed"); \ + trace_block(T_CRYPTO, "crypto: expected MAC", _pmac, _tagsz); \ + }) \ + return (KSERR_DECRYPT); \ + } \ +} while (0) + +/*----- The original transform --------------------------------------------* + * + * We generate a random initialization vector (if the cipher needs one). We + * encrypt the input message with the cipher, and format the type, sequence + * number, IV, and ciphertext as follows. + * + * +------+ +------+---...---+------...------+ + * | type | | seq | iv | ciphertext | + * +------+ +------+---...---+------...------+ + * 32 32 blksz sz + * + * All of this is fed into the MAC to compute a tag. The type is not + * transmitted: the other end knows what type of message it expects, and the + * type is only here to prevent us from being confused because some other + * kind of ciphertext has been substituted. The tag is prepended to the + * remainder, to yield the finished cryptogram, as follows. + * + * +---...---+------+---...---+------...------+ + * | tag | seq | iv | ciphertext | + * +---...---+------+---...---+------...------+ + * tagsz 32 blksz sz + * + * Decryption: checks the overall size, verifies the tag, then decrypts the + * ciphertext and extracts the sequence number. + */ + +static int v0_check(const algswitch *a, dstr *e) + { return (0); } + +static size_t v0_overhead(const algswitch *a) + { return a->tagsz + SEQSZ + a->c->blksz; } + +static int v0_encrypt(keyset *ks, unsigned ty, buf *b, buf *bb) +{ + ghash *h; + gcipher *c = ks->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; + octet t[4]; + + /* --- Determine the ciphertext layout --- */ + + if (buf_ensure(bb, tagsz + SEQSZ + ivsz + sz)) return (0); + qmac = BCUR(bb); qseq = qmac + tagsz; qiv = qseq + SEQSZ; qpk = qiv + ivsz; + BSTEP(bb, tagsz + SEQSZ + ivsz + sz); + + /* --- Store the type --- * + * + * This isn't transmitted, but it's covered by the MAC. + */ + + STORE32(t, ty); + + /* --- Store the sequence number --- */ + + oseq = ks->oseq++; + STORE32(qseq, oseq); + + /* --- Establish an initialization vector if necessary --- */ + + if (ivsz) { + rand_get(RAND_GLOBAL, qiv, ivsz); + GC_SETIV(c, qiv); + TRACE_IV(qiv, ivsz); + } + + /* --- Encrypt the packet --- */ + + GC_ENCRYPT(c, p, qpk, sz); + TRACE_CT(qpk, sz); + + /* --- Compute a MAC over type, sequence number, IV, and ciphertext --- */ + + if (tagsz) { + h = GM_INIT(ks->out.m); + GH_HASH(h, t, sizeof(t)); + GH_HASH(h, qseq, SEQSZ + ivsz + sz); + memcpy(qmac, GH_DONE(h, 0), tagsz); + GH_DESTROY(h); + TRACE_MAC(qmac, tagsz); + } + + /* --- We're done --- */ + + return (0); +} + +static int v0_decrypt(keyset *ks, unsigned ty, buf *b, buf *bb, uint32 *seq) +{ + 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; + size_t ivsz = GC_CLASS(c)->blksz; + size_t tagsz = ks->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); ) + return (KSERR_MALFORMED); + } + sz = psz - ivsz - SEQSZ - tagsz; + pmac = BCUR(b); pseq = pmac + tagsz; piv = pseq + SEQSZ; ppk = piv + ivsz; + STORE32(t, ty); + + /* --- Verify the MAC on the packet --- */ + + if (tagsz) { + h = GM_INIT(ks->in.m); + GH_HASH(h, t, sizeof(t)); + GH_HASH(h, pseq, SEQSZ + ivsz + sz); + CHECK_MAC(h, pmac, tagsz); + } + + /* --- Decrypt the packet --- */ + + if (ivsz) { + TRACE_IV(piv, ivsz); + GC_SETIV(c, piv); + } + GC_DECRYPT(c, ppk, q, sz); + + /* --- Finished --- */ + + *seq = LOAD32(pseq); + BSTEP(bb, sz); + return (0); +} + +/*----- Bulk crypto transform table ---------------------------------------*/ + +const bulkcrypto bulktab[] = { + +#define BULK(name, pre, prim) \ + { name, prim, pre##_check, pre##_overhead, pre##_encrypt, pre##_decrypt } + + BULK("v0", v0, BCP_CIPHER | BCP_MAC), + +#undef BULK + { 0 } +}; + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/server/keymgmt.c b/server/keymgmt.c index 6835968d..d28421fe 100644 --- a/server/keymgmt.c +++ b/server/keymgmt.c @@ -178,18 +178,11 @@ static const kgops *kgtab[] = { static int algs_get(algswitch *a, dstr *e, key_file *kf, key *k) { const char *p; + const bulkcrypto *bulk; char *q, *qq; dstr d = DSTR_INIT; int rc = -1; - /* --- Symmetric encryption for bulk data --- */ - - 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; - } - /* --- Hash function --- */ if ((p = key_getattr(kf, k, "hash")) == 0) p = "rmd160"; @@ -210,41 +203,70 @@ static int algs_get(algswitch *a, dstr *e, key_file *kf, key *k) goto done; } - /* --- Message authentication for bulk data --- */ + /* --- Bulk crypto transform --- */ - 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); + if ((p = key_getattr(kf, k, "bulk")) == 0) p = "v0"; + for (bulk = bulktab; bulk->name && strcmp(p, bulk->name) != 0; bulk++); + if (!bulk->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; } - 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); + } + + /* --- 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 (n%8 || n/8 > a->m->hashsz) { - a_format(e, "bad-tag-length", "%lu", n, A_END); + 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 = n/8; + a->tagsz = a->h->hashsz/2; } - } 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; } + /* --- All done --- */ + rc = 0; done: dstr_destroy(&d); @@ -267,6 +289,10 @@ 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 @@ -275,13 +301,13 @@ static int algs_check(algswitch *a, dstr *e, const group *g) */ a->hashsz = a->h->hashsz; - if ((a->cksz = keysz(a->hashsz, a->c->keysz)) == 0) { + 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->mksz = keysz(a->hashsz, a->m->keysz)) == 0) { + 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); @@ -290,7 +316,7 @@ static int algs_check(algswitch *a, dstr *e, const group *g) /* --- Derive the data limit --- */ - if (a->c->blksz < 16) a->expsz = MEG(64); + if (a->c && a->c->blksz < 16) a->expsz = MEG(64); else a->expsz = MEG(2048); /* --- Ensure the MGF accepts hashes as keys --- */ diff --git a/server/keyset.c b/server/keyset.c index 66a59618..6da6cc0f 100644 --- a/server/keyset.c +++ b/server/keyset.c @@ -32,8 +32,6 @@ #define KEYOK(ks, now) ((ks)->sz_exp > 0 && (ks)->t_exp > now) -#define SEQSZ 4 /* Size of sequence number packet */ - /*----- Low-level packet encryption and decryption ------------------------*/ /* --- Encrypted data format --- * @@ -77,73 +75,37 @@ static int doencrypt(keyset *ks, unsigned ty, buf *b, buf *bb) { - ghash *h; - gcipher *c = ks->cout; - const octet *p = BCUR(b); + int rc; 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 osz, nsz; - octet t[4]; - int rc = 0; - - /* --- Allocate the required buffer space --- */ - if (buf_ensure(bb, tagsz + SEQSZ + ivsz + sz)) - return (0); /* Caution! */ - qmac = BCUR(bb); qseq = qmac + tagsz; qiv = qseq + SEQSZ; qpk = qiv + ivsz; - BSTEP(bb, tagsz + SEQSZ + ivsz + sz); - STORE32(t, ty); + /* --- Initial tracing --- */ - oseq = ks->oseq++; STORE32(qseq, oseq); IF_TRACING(T_KEYSET, { - trace(T_KEYSET, "keyset: encrypting packet %lu using keyset %u", - (unsigned long)oseq, ks->seq); - trace_block(T_CRYPTO, "crypto: plaintext packet", p, sz); + trace(T_KEYSET, + "keyset: encrypting packet %lu (type %u) using keyset %u", + (unsigned long)ks->oseq, ty, ks->seq); + trace_block(T_CRYPTO, "crypto: plaintext packet", BCUR(b), sz); }) - /* --- Encrypt the packet --- */ - - if (ivsz) { - rand_get(RAND_GLOBAL, qiv, ivsz); - GC_SETIV(c, qiv); - IF_TRACING(T_KEYSET, { - trace_block(T_CRYPTO, "crypto: initialization vector", qiv, ivsz); - }) - } - GC_ENCRYPT(c, p, qpk, sz); - IF_TRACING(T_KEYSET, { - trace_block(T_CRYPTO, "crypto: encrypted packet", qpk, sz); - }) + /* --- Apply the bulk-crypto transformation --- */ - /* --- Now compute the MAC --- */ - - if (tagsz) { - h = GM_INIT(ks->mout); - GH_HASH(h, t, sizeof(t)); - GH_HASH(h, qseq, SEQSZ + ivsz + sz); - memcpy(qmac, GH_DONE(h, 0), tagsz); - GH_DESTROY(h); - IF_TRACING(T_KEYSET, { - trace_block(T_CRYPTO, "crypto: computed MAC", qmac, tagsz); - }) - } + rc = ks->bulk->encrypt(ks, ty, b, bb); + if (rc || !BOK(bb)) return (rc); - /* --- Deduct the packet size from the key's data life --- */ + /* --- Do the necessary accounting for data volume --- */ osz = ks->sz_exp; - if (osz > sz) - nsz = osz - sz; - else - nsz = 0; + nsz = osz > sz ? osz - sz : 0; if (osz >= ks->sz_regen && ks->sz_regen > nsz) { T( trace(T_KEYSET, "keyset: keyset %u data regen limit exceeded -- " "forcing exchange", ks->seq); ) rc = KSERR_REGEN; } ks->sz_exp = nsz; + + /* --- We're done --- */ + return (rc); } @@ -167,71 +129,24 @@ static int doencrypt(keyset *ks, unsigned ty, buf *b, buf *bb) static int dodecrypt(keyset *ks, unsigned ty, buf *b, buf *bb, uint32 *seq) { - const octet *pmac, *piv, *pseq, *ppk; - size_t psz = BLEFT(b); - size_t sz; - octet *q = BCUR(bb); - ghash *h; - gcipher *c = ks->cin; - size_t ivsz = GC_CLASS(c)->blksz; - size_t tagsz = ks->tagsz; - octet *mac; - int eq; - 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); ) - return (KSERR_MALFORMED); - } - sz = psz - ivsz - SEQSZ - tagsz; - pmac = BCUR(b); pseq = pmac + tagsz; piv = pseq + SEQSZ; ppk = piv + ivsz; - STORE32(t, ty); + const octet *q = BCUR(bb); + int rc; IF_TRACING(T_KEYSET, { - trace(T_KEYSET, "keyset: decrypting using keyset %u", ks->seq); - trace_block(T_CRYPTO, "crypto: ciphertext packet", ppk, sz); + trace(T_KEYSET, + "keyset: try decrypting packet (type %u) using keyset %u", + ty, ks->seq); + trace_block(T_CRYPTO, "crypto: ciphertext packet", BCUR(b), BLEFT(b)); }) - /* --- Verify the MAC on the packet --- */ - - if (tagsz) { - h = GM_INIT(ks->min); - GH_HASH(h, t, sizeof(t)); - GH_HASH(h, pseq, SEQSZ + ivsz + sz); - mac = GH_DONE(h, 0); - eq = ct_memeq(mac, pmac, tagsz); - IF_TRACING(T_KEYSET, { - trace_block(T_CRYPTO, "crypto: computed MAC", mac, tagsz); - }) - GH_DESTROY(h); - if (!eq) { - IF_TRACING(T_KEYSET, { - trace(T_KEYSET, "keyset: incorrect MAC: decryption failed"); - trace_block(T_CRYPTO, "crypto: expected MAC", pmac, tagsz); - }) - return (KSERR_DECRYPT); - } - } - - /* --- Decrypt the packet --- */ + rc = ks->bulk->decrypt(ks, ty, b, bb, seq); + if (rc) return (rc); - if (ivsz) { - GC_SETIV(c, piv); - IF_TRACING(T_KEYSET, { - trace_block(T_CRYPTO, "crypto: initialization vector", piv, ivsz); - }) - } - GC_DECRYPT(c, ppk, q, sz); - if (seq) - *seq = LOAD32(pseq); IF_TRACING(T_KEYSET, { trace(T_KEYSET, "keyset: decrypted OK (sequence = %lu)", - (unsigned long)LOAD32(pseq)); - trace_block(T_CRYPTO, "crypto: decrypted packet", q, sz); + (unsigned long)*seq); + trace_block(T_CRYPTO, "crypto: decrypted packet", q, BCUR(bb) - q); }) - BSTEP(bb, sz); return (0); } @@ -251,10 +166,19 @@ void ks_drop(keyset *ks) { if (--ks->ref) return; - GC_DESTROY(ks->cin); - GC_DESTROY(ks->cout); - GM_DESTROY(ks->min); - GM_DESTROY(ks->mout); + +#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) + + DROP_DIR(in); + DROP_DIR(out); + +#undef DROP +#undef DROP_DIR + DESTROY(ks); } @@ -281,10 +205,43 @@ void ks_drop(keyset *ks) * 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); + +#undef SETKEY +} + keyset *ks_gen(const void *k, size_t x, size_t y, size_t z, peer *p) { - ghash *h; - const octet *hh; keyset *ks = CREATE(keyset); time_t now = time(0); const octet *pp = k; @@ -293,54 +250,11 @@ keyset *ks_gen(const void *k, size_t x, size_t y, size_t z, peer *p) T( trace(T_KEYSET, "keyset: adding new keyset %u", seq); ) - /* --- Construct the various keys --- * - * - * This is done with macros, because it's quite tedious. - */ - -#define MINE GH_HASH(h, pp, x) -#define YOURS GH_HASH(h, pp + x, y - x) -#define OURS GH_HASH(h, pp + y, z - y) - -#define HASH_in MINE; YOURS; OURS -#define HASH_out YOURS; MINE; OURS -#define INIT_c(k) GC_INIT(algs->c, (k), algs->cksz) -#define INIT_m(k) GM_KEY(algs->m, (k), algs->mksz) -#define STR_c "encryption" -#define STR_m "integrity" -#define STR_in "incoming" -#define STR_out "outgoing" - -#define SETKEY(a, dir) do { \ - h = GH_INIT(algs->h); \ - HASH_STRING(h, "tripe-" STR_##a); \ - HASH_##dir; \ - hh = GH_DONE(h, 0); \ - IF_TRACING(T_KEYSET, { \ - trace_block(T_CRYPTO, "crypto: " STR_##dir " key " STR_##a, \ - hh, algs->a##ksz); \ - }) \ - ks->a##dir = INIT_##a(hh); \ - GH_DESTROY(h); \ -} while (0) - - SETKEY(c, in); SETKEY(c, out); - SETKEY(m, in); SETKEY(m, out); - -#undef MINE -#undef YOURS -#undef OURS -#undef STR_c -#undef STR_m -#undef STR_in -#undef STR_out -#undef INIT_c -#undef INIT_m -#undef HASH_in -#undef HASH_out -#undef SETKEY + 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); T( ks->seq = seq++; ) + ks->bulk = algs->bulk; ks->ref = 1; ks->t_exp = now + T_EXP; ks->sz_exp = algs->expsz; @@ -388,6 +302,11 @@ void ks_activate(keyset *ks) * ought to be replaced' notification is only ever given once * for each key. Also note that this call forces a keyset to be * used even if it's marked as not for data output. + * + * The encryption transform is permitted to corrupt @buf_u@ for + * its own purposes. Neither the source nor destination should + * be within @buf_u@; and callers mustn't expect anything stored + * in @buf_u@ to still */ int ks_encrypt(keyset *ks, unsigned ty, buf *b, buf *bb) @@ -415,6 +334,11 @@ int ks_encrypt(keyset *ks, unsigned ty, buf *b, buf *bb) * Use: Attempts to decrypt a message using a given key. Note that * requesting decryption with a key directly won't clear a * marking that it's not for encryption. + * + * The decryption transform is permitted to corrupt @buf_u@ for + * its own purposes. Neither the source nor destination should + * be within @buf_u@; and callers mustn't expect anything stored + * in @buf_u@ to still */ int ks_decrypt(keyset *ks, unsigned ty, buf *b, buf *bb) diff --git a/server/tests.at b/server/tests.at index 510fd4d5..e769aaa5 100644 --- a/server/tests.at +++ b/server/tests.at @@ -563,6 +563,7 @@ WITH_3TRIPES([alice], [bob], [carol], [-nslip -Tmx], AT_DATA([algs-alpha], [dnl kx-group=ec kx-group-order-bits=256 kx-group-elt-bits=512 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 @@ -571,6 +572,7 @@ mac=rmd160-hmac mac-keysz=20 mac-tagsz=10 AT_DATA([algs-beta-old], [dnl kx-group=prime kx-group-order-bits=160 kx-group-elt-bits=1023 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 @@ -579,6 +581,7 @@ mac=rmd160-hmac mac-keysz=20 mac-tagsz=10 AT_DATA([algs-beta-new], [dnl kx-group=ec kx-group-order-bits=161 kx-group-elt-bits=320 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 diff --git a/server/tripe-admin.5.in b/server/tripe-admin.5.in index a7aee7cf..aeafc63e 100644 --- a/server/tripe-admin.5.in +++ b/server/tripe-admin.5.in @@ -423,6 +423,12 @@ The mask-generating function in use, e.g., .B hashsz The size of the hash function's output, in octets. .TP +.B bulk-transform +The name of the bulk-crypto transform. +.TP +.B bulk-overhead +The amount of overhead, in bytes, caused by the crypto transform. +.TP .B cipher The name of the bulk data cipher in use, e.g., .BR blowfish-cbc . @@ -456,13 +462,12 @@ tunnel interface. If is the MTU of the path to the peer, then the tunnel MTU should be .IP .I MTU -\- 33 \- -.I cipher-blksz -\- -.I mac-tagsz +\- 29 \- +.I bulk-overhead .PP allowing 20 bytes of IP header, 8 bytes of UDP header, a packet type -octet, a four-octet sequence number, an IV, and a MAC tag. +octet, and the bulk-crypto transform overhead (which includes the +sequence number). .RE .SP .BI "BGCANCEL " tag @@ -1261,6 +1266,12 @@ exchange. .BI "KEYMGMT " which "-keyring " file " io-error " ecode " " message A system error occurred while opening or reading the keyring file. .SP +.BI "KEYMGMT " which "-keyring " file " key " tag " unknown-bulk-transform " bulk +The key specifies the use of an unknown bulk-crypto transform +.IR bulk . +Maybe the key was generated wrongly, or maybe the version of Catacomb +installed is too old. +.SP .BI "KEYMGMT " which "-keyring " file " key " tag " unknown-cipher " cipher The key specifies the use of an unknown symmetric encryption algorithm .IR cipher . diff --git a/server/tripe.8.in b/server/tripe.8.in index 14ee0ab7..ebbfc790 100644 --- a/server/tripe.8.in +++ b/server/tripe.8.in @@ -360,6 +360,10 @@ uses are Blowfish (by Schneier) for symmetric encryption, and RIPEMD-160 mode, designed by Bellare, Canetti and Krawczyk). These can all be overridden by setting attributes on your private key, as follows. .TP +.B bulk +Names the bulk-crypto transform to use. Currently the only choice is +.BR v0 . +.TP .B cipher Names the symmetric encryption scheme to use. The default is .BR blowfish\-cbc . diff --git a/server/tripe.h b/server/tripe.h index 60256f74..8cd66afa 100644 --- a/server/tripe.h +++ b/server/tripe.h @@ -156,16 +156,33 @@ /*----- Cipher selections -------------------------------------------------*/ -typedef struct algswitch { - const gccipher *c; /* Symmetric encryption scheme */ - const gccipher *mgf; /* Mask-generation function */ +typedef struct keyset keyset; +typedef struct algswitch algswitch; + +typedef struct bulkcrypto { + 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*/, + buf */*b*/, buf */*bb*/, uint32 */*seq*/); +} bulkcrypto; + +#define BCP_CIPHER 1 +#define BCP_MAC 2 + +struct algswitch { const gchash *h; /* Hash function */ + const gccipher *mgf; /* Mask-generation function */ + const struct bulkcrypto *bulk; /* Bulk crypto transformation */ + const gccipher *c; /* Symmetric encryption scheme */ const gcmac *m; /* Message authentication code */ 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; /* Key lengths for @c@ and @m@ */ -} algswitch; +}; typedef struct kdata { unsigned ref; /* Reference counter */ @@ -191,6 +208,8 @@ typedef struct knode { #define HASH_STRING(h, s) GH_HASH((h), (s), sizeof(s)) +extern const struct bulkcrypto bulktab[]; + /*----- Data structures ---------------------------------------------------*/ /* --- Socket addresses --- * @@ -237,7 +256,7 @@ typedef struct seqwin { * expiry. */ -typedef struct keyset { +struct keyset { struct keyset *next; /* Next active keyset in the list */ unsigned ref; /* Reference count for keyset */ struct peer *p; /* Pointer to peer structure */ @@ -245,12 +264,15 @@ typedef struct keyset { unsigned long sz_exp, sz_regen; /* Data limits for the keyset */ T( unsigned seq; ) /* Sequence number for tracing */ unsigned f; /* Various useful flags */ - gcipher *cin, *cout; /* Keyset ciphers for encryption */ + const bulkcrypto *bulk; /* Bulk crypto transform */ size_t tagsz; /* Length to truncate MAC tags */ - gmac *min, *mout; /* Keyset MACs for integrity */ + struct ksdir { + gcipher *c; /* Keyset cipher for encryption */ + gmac *m; /* Keyset MAC for integrity */ + } in, out; uint32 oseq; /* Outbound sequence number */ seqwin iseq; /* Inbound sequence number */ -} keyset; +}; #define KSF_LISTEN 1u /* Don't encrypt packets yet */ #define KSF_LINK 2u /* Key is in a linked list */ @@ -777,6 +799,11 @@ extern void ks_activate(keyset */*ks*/); * ought to be replaced' notification is only ever given once * for each key. Also note that this call forces a keyset to be * used even if it's marked as not for data output. + * + * The encryption transform is permitted to corrupt @buf_u@ for + * its own purposes. Neither the source nor destination should + * be within @buf_u@; and callers mustn't expect anything stored + * in @buf_u@ to still */ extern int ks_encrypt(keyset */*ks*/, unsigned /*ty*/, @@ -796,6 +823,11 @@ extern int ks_encrypt(keyset */*ks*/, unsigned /*ty*/, * Use: Attempts to decrypt a message using a given key. Note that * requesting decryption with a key directly won't clear a * marking that it's not for encryption. + * + * The decryption transform is permitted to corrupt @buf_u@ for + * its own purposes. Neither the source nor destination should + * be within @buf_u@; and callers mustn't expect anything stored + * in @buf_u@ to still */ extern int ks_decrypt(keyset */*ks*/, unsigned /*ty*/, diff --git a/svc/tripe-ifup.in b/svc/tripe-ifup.in index 5f70ca1b..79f5d469 100644 --- a/svc/tripe-ifup.in +++ b/svc/tripe-ifup.in @@ -118,7 +118,7 @@ case $haveaddr4,$haveaddr6 in mtu=$P_MTU;; *) pathmtu=$(pathmtu "$addr") - mtu=$(expr "$pathmtu" - 33 - $A_CIPHER_BLKSZ - $A_MAC_TAGSZ) + mtu=$(expr "$pathmtu" - 29 - $A_BULK_OVERHEAD) ;; esac ip link set dev "$ifname" up mtu "$mtu"