with the
.B \-ttripe\-ec
option, and all should be well.
+.SS "Using other symmetric algorithms"
+The default symmetric algorithms
+.B tripe
+uses are Blowfish (by Schneier) for symmetric encryption, and RIPEMD-160
+(by Dobbertin, Bosselaers and Preneel) for hashing and as a MAC (in HMAC
+mode, designed by Bellare, Canetti and Krawczyk). These can all be
+overridden by setting attributes on your private key, as follows.
+.TP
+.B cipher
+Names the symmetric encryption scheme to use. The default is
+.BR blowfish\-cbc .
+.TP
+.B hash
+Names the hash function to use. The default is
+.BR rmd160 .
+.TP
+.B mac
+Names the message authentication code to use. The name of the MAC may
+be followed by a
+.RB ` / '
+and the desired tag length in bits. The default is
+.IB hash \-hmac
+at half the underlying hash function's output length.
+.TP
+.B mgf
+A `mask-generation function', used in the key-exchange. The default is
+.IB hash \-mgf
+and there's no good reason to change it.
.SS "About the name"
The program's name is
.BR tripe ,
/* -*-c-*-
*
- * $Id: packet-tripe.c,v 1.3 2004/04/08 01:36:17 mdw Exp $
+ * $Id: packet-tripe.c,v 1.4 2004/04/18 18:08:11 mdw Exp $
*
* TrIPE protocol dissector for Ethereal
*
#include <glib.h>
#include <gmodule.h>
#include <epan/packet.h>
+#include <prefs.h>
#ifdef ETHEREAL_BUGGERED
# define plugin_address_table_t void
static int proto_tripe = -1;
+static guint hashsz = 20, tagsz = 10, ivsz = 8;
+
typedef struct hfmp {
int hf, hf_len, hf_val, tt;
} hfmp;
static int hf_tripe_ct = -1;
static int hf_tripe_ct_seq = -1;
static int hf_tripe_ct_iv = -1;
-static int hf_tripe_ct_cbc = -1;
-static int hf_tripe_ct_mac = -1;
+static int hf_tripe_ct_ct = -1;
+static int hf_tripe_ct_tag = -1;
static int hf_tripe_kx_type = -1;
static hfge hf_tripe_kx_mychal = { -1, -1, -1, -1, -1, -1, -1, -1 };
static int hf_tripe_kx_mycookie = -1;
/*----- Main code ---------------------------------------------------------*/
+static void prefcb(void) { }
+
static gint gethash(proto_tree *tt, int hf, tvbuff_t *b, gint off)
{
- proto_tree_add_item(tt, hf, b, off, 20, FALSE);
- return (off + 20);
+ proto_tree_add_item(tt, hf, b, off, hashsz, FALSE);
+ return (off + hashsz);
}
static gint getmp(proto_tree *tt, const hfmp *hf, tvbuff_t *b, gint off)
goto done;
ct:
ti = proto_tree_add_item(tt, hf_tripe_ct, b, off, -1, FALSE);
- seq = tvb_get_ntohl(b, off + 10);
+ seq = tvb_get_ntohl(b, off + tagsz);
proto_item_set_text(ti, "Encrypted ciphertext (sequence number %lu)",
(unsigned long)seq);
tt = proto_item_add_subtree(ti, tt_tripe_ct);
- proto_tree_add_item(tt, hf_tripe_ct_mac, b, off, 10, FALSE);
- off += 10;
+ if (tagsz) {
+ proto_tree_add_item(tt, hf_tripe_ct_tag, b, off, tagsz, FALSE);
+ off += tagsz;
+ }
proto_tree_add_item(tt, hf_tripe_ct_seq, b, off, 4, FALSE);
off += 4;
- proto_tree_add_item(tt, hf_tripe_ct_iv, b, off, 8, FALSE);
- off += 8;
- proto_tree_add_item(ti, hf_tripe_ct_cbc, b, off, -1, FALSE);
+ if (ivsz) {
+ proto_tree_add_item(tt, hf_tripe_ct_iv, b, off, ivsz, FALSE);
+ off += ivsz;
+ }
+ proto_tree_add_item(ti, hf_tripe_ct_ct, b, off, -1, FALSE);
goto done;
done:;
}
void proto_register_tripe(void)
{
+ module_t *mod;
+
static value_string vs_kxtype[] = {
{ KX_PRECHAL, "KX_PRECHAL (prechallenge)" },
{ KX_COOKIE, "KX_COOKIE (cookie)" },
FT_BYTES, BASE_NONE, 0, 0,
"This is the initialization vector used for the actual encryption."
},
- &hf_tripe_ct_cbc, {
- "CBC-encrypted data", "tripe.ct.cbc",
+ &hf_tripe_ct_ct, {
+ "Actual encrypted data", "tripe.ct.ct",
FT_BYTES, BASE_NONE, 0, 0,
- "This is the CBC-encrypted message. Reading it ought to be hard."
+ "This is the encrypted message. Reading it ought to be hard."
},
- &hf_tripe_ct_mac, {
- "Message authentication code", "tripe.ct.mac",
+ &hf_tripe_ct_tag, {
+ "Message authentication code", "tripe.ct.tag",
FT_BYTES, BASE_NONE, 0, 0,
- "This is the message authentication code for the ciphertext."
+ "This is the message authentication code tag for the ciphertext."
},
&hf_tripe_kx_type, {
"Key-exchange message type", "tripe.kx.type",
proto_tripe = proto_register_protocol("TrIPE", "TrIPE", "tripe");
proto_register_field_array(proto_tripe, hfs, array_length(hfs));
proto_register_subtree_array(tts, array_length(tts));
+
+ mod = prefs_register_protocol(proto_tripe, prefcb);
+ prefs_register_uint_preference(mod, "hashsz", "Hash length",
+ "hash function output length (in octets)",
+ 10, &hashsz);
+ prefs_register_uint_preference(mod, "tagsz", "MAC tag length",
+ "MAC tag length (in octets)", 10, &tagsz);
+ prefs_register_uint_preference(mod, "ivsz", "IV length",
+ "block cipher initialization vector length"
+ " (in octets)",
+ 10, &ivsz);
}
void proto_reg_handoff_tripe(void)
/* -*-c-*-
*
- * $Id: keyexch.c,v 1.12 2004/04/08 01:36:17 mdw Exp $
+ * $Id: keyexch.c,v 1.13 2004/04/18 18:08:11 mdw Exp $
*
* Key exchange protocol
*
/* --- @hashge@ --- *
*
- * Arguments: @HASH_CTX *r@ = pointer to hash context
+ * Arguments: @ghash *h@ = pointer to hash context
* @ge *x@ = pointer to group element
*
* Returns: ---
* @buf_t@.
*/
-static void hashge(HASH_CTX *r, ge *x)
+static void hashge(ghash *h, ge *x)
{
buf b;
buf_init(&b, buf_t, sizeof(buf_t));
G_TOBUF(gg, &b, x);
assert(BOK(&b));
- HASH(r, BBASE(&b), BLEN(&b));
+ GH_HASH(h, BBASE(&b), BLEN(&b));
}
-/* --- @mpcrypt@ --- *
+/* --- @mpencrypt@, @mpdecrypt@ --- *
*
* Arguments: @mp *d@ = the destination integer
* @mp *x@ = the plaintext/ciphertext integer
* @size_t sz@ = the expected size of the plaintext
* @const octet *k@ = pointer to key material
- * @size_t ksz@ = size of the key
*
* Returns: The encrypted/decrypted integer.
*
* encryption thing.
*/
-static mp *mpcrypt(mp *d, mp *x, size_t sz, const octet *k, size_t ksz)
+static mp *mpencrypt(mp *d, mp *x, size_t sz, const octet *k)
{
- MGF_CTX m;
+ gcipher *mgf;
- MGF_INIT(&m, k, ksz, 0);
+ mgf = GC_INIT(algs.mgf, k, algs.hashsz);
mp_storeb(x, buf_t, sz);
- MGF_CRYPT(&m, buf_t, buf_t, sz);
+ GC_ENCRYPT(mgf, buf_t, buf_t, sz);
+ GC_DESTROY(mgf);
+ return (mp_loadb(d, buf_t, sz));
+}
+
+static mp *mpdecrypt(mp *d, mp *x, size_t sz, const octet *k)
+{
+ gcipher *mgf;
+
+ mgf = GC_INIT(algs.mgf, k, algs.hashsz);
+ mp_storeb(x, buf_t, sz);
+ GC_DECRYPT(mgf, buf_t, buf_t, sz);
+ GC_DESTROY(mgf);
return (mp_loadb(d, buf_t, sz));
}
unsigned i;
for (i = 0; i < kx->nr; i++) {
- if (memcmp(hc, kx->r[i]->hc, HASHSZ) == 0)
+ if (memcmp(hc, kx->r[i]->hc, algs.hashsz) == 0)
return (kx->r[i]);
}
return (0);
if (!kxc->r)
G_TOBUF(gg, b, kx->c);
else
- buf_put(b, kx->hc, HASHSZ);
- buf_put(b, kxc->hc, HASHSZ);
+ buf_put(b, kx->hc, algs.hashsz);
+ buf_put(b, kxc->hc, algs.hashsz);
buf_putmp(b, kxc->ck);
/* --- Maybe send an actual reply, if we have one --- */
ge *r = G_CREATE(gg);
ge *y = G_CREATE(gg);
mp *a = MP_NEW;
- HASH_CTX h;
- octet buf[HASHSZ];
+ ghash *h;
+ const octet *hh;
int ok;
G_EXP(gg, r, c, kpriv);
- HASH_INIT(&h);
- HASH_STRING(&h, "tripe-expected-reply");
- hashge(&h, c);
- hashge(&h, kx->c);
- hashge(&h, r);
- HASH_DONE(&h, buf);
-
- a = mpcrypt(MP_NEW, ck, mp_octets(gg->r), buf, sizeof(buf));
+ h = GH_INIT(algs.h);
+ HASH_STRING(h, "tripe-expected-reply");
+ hashge(h, c);
+ hashge(h, kx->c);
+ hashge(h, r);
+ hh = GH_DONE(h, 0);
+
+ a = mpdecrypt(MP_NEW, ck, mp_octets(gg->r), hh);
IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
trace(T_CRYPTO, "crypto: computed reply = %s", gestr(gg, r));
- trace_block(T_CRYPTO, "crypto: computed reply hash", buf, HASHSZ);
+ trace_block(T_CRYPTO, "crypto: computed reply hash", hh, algs.hashsz);
trace(T_CRYPTO, "crypto: recovered log = %s", mpstr(a));
}))
+ GH_DESTROY(h);
G_EXP(gg, y, gg->g, a);
ok = G_EQ(gg, y, c);
if (!ok) {
mp *ck = MP_NEW;
const octet *hc = 0;
kxchal *kxc;
- HASH_CTX h;
- octet buf[HASHSZ];
+ ghash *h;
/* --- Ensure that we're in a sensible state --- */
/* --- Unpack the packet --- */
if (G_FROMBUF(gg, b, c) ||
- (msg >= KX_COOKIE && (hc = buf_get(b, HASHSZ)) == 0) ||
+ (msg >= KX_COOKIE && (hc = buf_get(b, algs.hashsz)) == 0) ||
(msg >= KX_CHAL && (ck = buf_getmp(b)) == 0) ||
BLEFT(b)) {
a_warn("malformed packet from `%s'", p_name(kx->p));
IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
trace(T_CRYPTO, "crypto: challenge = %s", gestr(gg, c));
- if (hc) trace_block(T_CRYPTO, "crypto: cookie", hc, HASHSZ);
+ if (hc) trace_block(T_CRYPTO, "crypto: cookie", hc, algs.hashsz);
if (ck) trace(T_CRYPTO, "crypto: check value = %s", mpstr(ck));
}))
T( trace(T_KEYEXCH, "keyexch: too many challenges -- sending cookie"); )
b = p_txstart(kx->p, MSG_KEYEXCH | KX_COOKIE);
G_TOBUF(gg, b, kx->c);
- HASH_INIT(&h);
- HASH_STRING(&h, "tripe-cookie");
- hashge(&h, c);
- HASH_DONE(&h, buf_get(b, HASHSZ));
+ h = GH_INIT(algs.h);
+ HASH_STRING(h, "tripe-cookie");
+ hashge(h, c);
+ GH_DONE(h, buf_get(b, algs.hashsz));
+ GH_DESTROY(h);
p_txend(kx->p);
goto tidy;
}
/* --- Discard a packet with an invalid cookie --- */
- if (hc && memcmp(hc, kx->hc, HASHSZ) != 0) {
+ if (hc && memcmp(hc, kx->hc, algs.hashsz) != 0) {
a_warn("incorrect cookie from `%s'", p_name(kx->p));
goto bad;
}
/* --- Work out the cookie for this challenge --- */
- HASH_INIT(&h);
- HASH_STRING(&h, "tripe-cookie");
- hashge(&h, kxc->c);
- HASH_DONE(&h, kxc->hc);
+ h = GH_INIT(algs.h);
+ HASH_STRING(h, "tripe-cookie");
+ hashge(h, kxc->c);
+ GH_DONE(h, kxc->hc);
+ GH_DESTROY(h);
+
+ IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
+ trace_block(T_CRYPTO, "crypto: computed cookie", kxc->hc, algs.hashsz);
+ }))
/* --- Compute the expected-reply hash --- */
- HASH_INIT(&h);
- HASH_STRING(&h, "tripe-expected-reply");
- hashge(&h, kx->c);
- hashge(&h, kxc->c);
- hashge(&h, kx->rx);
- HASH_DONE(&h, buf);
- kxc->ck = mpcrypt(MP_NEW, kx->alpha, mp_octets(gg->r),
- buf, sizeof(buf));
+ h = GH_INIT(algs.h);
+ HASH_STRING(h, "tripe-expected-reply");
+ hashge(h, kx->c);
+ hashge(h, kxc->c);
+ hashge(h, kx->rx);
+ hc = GH_DONE(h, 0);
+ kxc->ck = mpencrypt(MP_NEW, kx->alpha, mp_octets(gg->r), hc);
+ IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
+ trace_block(T_CRYPTO, "crypto: expected-reply hash", hc, algs.hashsz);
+ trace(T_CRYPTO, "crypto: my reply check = %s", mpstr(kxc->ck));
+ }))
+ GH_DESTROY(h);
/* --- Work out the shared key --- */
r = G_CREATE(gg);
G_EXP(gg, r, c, kx->alpha);
+ IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
+ trace(T_CRYPTO, "crypto: shared secret = %s", gestr(gg, r));
+ }))
/* --- Compute the switch messages --- */
- HASH_INIT(&h); HASH_STRING(&h, "tripe-switch-request");
- hashge(&h, kx->c); hashge(&h, kxc->c);
- HASH_DONE(&h, kxc->hswrq_out);
- HASH_INIT(&h); HASH_STRING(&h, "tripe-switch-confirm");
- hashge(&h, kx->c); hashge(&h, kxc->c);
- HASH_DONE(&h, kxc->hswok_out);
+ h = GH_INIT(algs.h); HASH_STRING(h, "tripe-switch-request");
+ hashge(h, kx->c); hashge(h, kxc->c);
+ GH_DONE(h, kxc->hswrq_out); GH_DESTROY(h);
+ h = GH_INIT(algs.h); HASH_STRING(h, "tripe-switch-confirm");
+ hashge(h, kx->c); hashge(h, kxc->c);
+ GH_DONE(h, kxc->hswok_out); GH_DESTROY(h);
- HASH_INIT(&h); HASH_STRING(&h, "tripe-switch-request");
- hashge(&h, kxc->c); hashge(&h, kx->c);
- HASH_DONE(&h, kxc->hswrq_in);
- HASH_INIT(&h); HASH_STRING(&h, "tripe-switch-confirm");
- hashge(&h, kxc->c); hashge(&h, kx->c);
- HASH_DONE(&h, kxc->hswok_in);
+ h = GH_INIT(algs.h); HASH_STRING(h, "tripe-switch-request");
+ hashge(h, kxc->c); hashge(h, kx->c);
+ GH_DONE(h, kxc->hswrq_in); GH_DESTROY(h);
+ h = GH_INIT(algs.h); HASH_STRING(h, "tripe-switch-confirm");
+ hashge(h, kxc->c); hashge(h, kx->c);
+ GH_DONE(h, kxc->hswok_in); GH_DESTROY(h);
IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
- trace_block(T_CRYPTO, "crypto: computed cookie", kxc->hc, HASHSZ);
- trace_block(T_CRYPTO, "crypto: expected-reply hash",
- buf, HASHSZ);
- trace(T_CRYPTO, "crypto: my reply check = %s", mpstr(kxc->ck));
- trace(T_CRYPTO, "crypto: shared secret = %s", gestr(gg, r));
trace_block(T_CRYPTO, "crypto: outbound switch request",
- kxc->hswrq_out, HASHSZ);
+ kxc->hswrq_out, algs.hashsz);
trace_block(T_CRYPTO, "crypto: outbound switch confirm",
- kxc->hswok_out, HASHSZ);
+ kxc->hswok_out, algs.hashsz);
trace_block(T_CRYPTO, "crypto: inbound switch request",
- kxc->hswrq_in, HASHSZ);
+ kxc->hswrq_in, algs.hashsz);
trace_block(T_CRYPTO, "crypto: inbound switch confirm",
- kxc->hswok_in, HASHSZ);
+ kxc->hswok_in, algs.hashsz);
}))
/* --- Create a new symmetric keyset --- */
p_name(kx->p)); )
kxc = kx->r[0];
b = p_txstart(kx->p, MSG_KEYEXCH | KX_SWITCH);
- buf_put(b, kx->hc, HASHSZ);
- buf_put(b, kxc->hc, HASHSZ);
+ buf_put(b, kx->hc, algs.hashsz);
+ buf_put(b, kxc->hc, algs.hashsz);
buf_init(&bb, buf_i, sizeof(buf_i));
G_TOBUF(gg, &bb, kxc->r);
- buf_put(&bb, kxc->hswrq_out, HASHSZ);
+ buf_put(&bb, kxc->hswrq_out, algs.hashsz);
buf_flip(&bb);
ks_encrypt(kxc->ks, MSG_KEYEXCH | KX_SWITCH, &bb, b);
break;
kxc = kx->r[0];
b = p_txstart(kx->p, MSG_KEYEXCH | KX_SWITCHOK);
buf_init(&bb, buf_i, sizeof(buf_i));
- buf_put(&bb, kxc->hswok_out, HASHSZ);
+ buf_put(&bb, kxc->hswok_out, algs.hashsz);
buf_flip(&bb);
ks_encrypt(kxc->ks, MSG_KEYEXCH | KX_SWITCHOK, &bb, b);
break;
/* --- Check the plaintext portions of the data --- */
IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
- trace_block(T_CRYPTO, "crypto: challenge", hc_in, HASHSZ);
- trace_block(T_CRYPTO, "crypto: cookie", hc_out, HASHSZ);
+ trace_block(T_CRYPTO, "crypto: challenge", hc_in, algs.hashsz);
+ trace_block(T_CRYPTO, "crypto: cookie", hc_out, algs.hashsz);
if (ck) trace(T_CRYPTO, "crypto: check value = %s", mpstr(ck));
}))
- if (memcmp(hc_out, kx->hc, HASHSZ) != 0) {
+ if (memcmp(hc_out, kx->hc, algs.hashsz) != 0) {
a_warn("incorrect cookie from `%s'", p_name(kx->p));
goto bad;
}
a_warn("unexpected reply from `%s'", p_name(kx->p));
goto bad;
}
- if ((hc_in = buf_get(b, HASHSZ)) == 0 ||
- (hc_out = buf_get(b, HASHSZ)) == 0 ||
+ if ((hc_in = buf_get(b, algs.hashsz)) == 0 ||
+ (hc_out = buf_get(b, algs.hashsz)) == 0 ||
(ck = buf_getmp(b)) == 0) {
a_warn("invalid reply packet from `%s'", p_name(kx->p));
goto bad;
const octet *hc_in, *hc_out, *hswrq;
kxchal *kxc;
- if ((hc_in = buf_get(b, HASHSZ)) == 0 ||
- (hc_out = buf_get(b, HASHSZ)) == 0) {
+ if ((hc_in = buf_get(b, algs.hashsz)) == 0 ||
+ (hc_out = buf_get(b, algs.hashsz)) == 0) {
a_warn("invalid switch request from `%s'", p_name(kx->p));
goto bad;
}
if ((kxc = matchreply(kx, MSG_KEYEXCH | KX_SWITCH,
hc_in, hc_out, 0, b)) == 0)
goto bad;
- if ((hswrq = buf_get(b, HASHSZ)) == 0 || BLEFT(b)) {
+ if ((hswrq = buf_get(b, algs.hashsz)) == 0 || BLEFT(b)) {
a_warn("invalid switch request from `%s'", p_name(kx->p));
goto bad;
}
IF_TRACING(T_KEYEXCH, {
- trace_block(T_CRYPTO, "crypto: switch request hash", hswrq, HASHSZ);
+ trace_block(T_CRYPTO, "crypto: switch request hash", hswrq, algs.hashsz);
})
- if (memcmp(hswrq, kxc->hswrq_in, HASHSZ) != 0) {
+ if (memcmp(hswrq, kxc->hswrq_in, algs.hashsz) != 0) {
a_warn("incorrect switch request hash from `%s'", p_name(kx->p));
goto bad;
}
goto bad;
}
buf_init(b, BBASE(&bb), BLEN(&bb));
- if ((hswok = buf_get(b, HASHSZ)) == 0 || BLEFT(b)) {
+ if ((hswok = buf_get(b, algs.hashsz)) == 0 || BLEFT(b)) {
a_warn("invalid switch confirmation from `%s'", p_name(kx->p));
goto bad;
}
IF_TRACING(T_KEYEXCH, {
- trace_block(T_CRYPTO, "crypto: switch confirmation hash", hswok, HASHSZ);
+ trace_block(T_CRYPTO, "crypto: switch confirmation hash",
+ hswok, algs.hashsz);
})
- if (memcmp(hswok, kxc->hswok_in, HASHSZ) != 0) {
+ if (memcmp(hswok, kxc->hswok_in, algs.hashsz) != 0) {
a_warn("incorrect switch confirmation hash from `%s'", p_name(kx->p));
goto bad;
}
static void start(keyexch *kx, time_t now)
{
- HASH_CTX h;
+ ghash *h;
assert(kx->f & KXF_DEAD);
kx->s = KXS_CHAL;
kx->t_valid = now + T_VALID;
- HASH_INIT(&h);
- HASH_STRING(&h, "tripe-cookie");
- hashge(&h, kx->c);
- HASH_DONE(&h, kx->hc);
+ h = GH_INIT(algs.h);
+ HASH_STRING(h, "tripe-cookie");
+ hashge(h, kx->c);
+ GH_DONE(h, kx->hc);
+ GH_DESTROY(h);
IF_TRACING(T_KEYEXCH, {
trace(T_KEYEXCH, "keyexch: creating new challenge");
trace(T_CRYPTO, "crypto: secret = %s", mpstr(kx->alpha));
trace(T_CRYPTO, "crypto: challenge = %s", gestr(gg, kx->c));
trace(T_CRYPTO, "crypto: expected response = %s", gestr(gg, kx->rx));
- trace_block(T_CRYPTO, "crypto: challenge cookie", kx->hc, HASHSZ);
+ trace_block(T_CRYPTO, "crypto: challenge cookie", kx->hc, algs.hashsz);
})
})
}
/* -*-c-*-
*
- * $Id: keymgmt.c,v 1.5 2004/04/08 01:36:17 mdw Exp $
+ * $Id: keymgmt.c,v 1.6 2004/04/18 18:08:11 mdw Exp $
*
* Key loading and storing
*
group *gg;
mp *kpriv;
+algswitch algs;
/*----- Static variables --------------------------------------------------*/
static const kgops *kgtab[] = { &kgdh_ops, &kgec_ops, 0 };
+/*----- Algswitch stuff ---------------------------------------------------*/
+
+/* --- @algs_get@ --- *
+ *
+ * Arguments: @algswitch *a@ = where to put the algorithms
+ * @key_file *kf@ = key file (for some stupid reason)
+ * @key *k@ = key to inspect
+ *
+ * Returns: Null if OK, or an error message.
+ *
+ * Use: Extracts an algorithm choice from a key.
+ */
+
+static const char *algs_get(algswitch *a, key_file *kf, key *k)
+{
+ const char *p;
+ char *q;
+ dstr d = DSTR_INIT;
+ const char *e;
+
+#define FAIL(msg) do { e = msg; goto done; } while (0)
+
+ if ((p = key_getattr(kf, k, "cipher")) == 0)
+ p = "blowfish-cbc";
+ if ((a->c = gcipher_byname(p)) == 0)
+ FAIL("unknown cipher");
+
+ if ((p = key_getattr(kf, k, "hash")) == 0)
+ p = "rmd160";
+ if ((a->h = ghash_byname(p)) == 0)
+ FAIL("unknown hash function");
+
+ if ((p = key_getattr(kf, k, "mgf")) != 0) {
+ dstr_reset(&d);
+ dstr_putf(&d, "%s-mgf");
+ p = d.buf;
+ }
+ if ((a->mgf = gcipher_byname(p)) == 0)
+ FAIL("unknown MGF cipher");
+
+ 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)
+ FAIL("unknown message authentication code");
+ if (!q)
+ a->tagsz = a->m->hashsz;
+ else {
+ unsigned long n = strtoul(q, &q, 0);
+ if (*q) FAIL("bad tag length string");
+ if (n%8 || n > ~(size_t)0) FAIL("bad tag length");
+ a->tagsz = n/8;
+ }
+ } else {
+ dstr_reset(&d);
+ dstr_putf(&d, "%s-hmac", a->h->name);
+ if ((a->m = gmac_byname(d.buf)) == 0)
+ FAIL("failed to derive HMAC from hash function");
+ a->tagsz = a->h->hashsz/2;
+ }
+
+ e = 0;
+done:
+ dstr_destroy(&d);
+ return (e);
+}
+
+/* --- @algs_check@ --- *
+ *
+ * Arguments: @algswitch *a@ = a choice of algorithms
+ * @const group *g@ = the group we're working in
+ *
+ * Returns: Null if OK, or an error message.
+ *
+ * Use: Checks an algorithm choice for sensibleness. This also
+ * derives some useful information from the choices, and you
+ * must call this before committing the algorithm selection
+ * for use by @keyset@ functions.
+ */
+
+static const char *algs_check(algswitch *a, const group *g)
+{
+ /* --- Derive the key sizes --- *
+ *
+ * Must ensure that we have non-empty keys. This isn't ideal, but it
+ * provides a handy sanity check.
+ */
+
+ a->hashsz = a->h->hashsz;
+ if ((a->cksz = keysz(a->hashsz, a->c->keysz)) == 0)
+ return ("no key size found for cipher");
+ if ((a->mksz = keysz(a->hashsz, a->m->keysz)) == 0)
+ return ("no key size found for MAC");
+
+ /* --- Ensure that the tag size is sane --- */
+
+ if (a->tagsz > a->m->hashsz) return ("tag length too large");
+
+ /* --- Ensure the MGF accepts hashes as keys --- */
+
+ if (keysz(a->hashsz, a->mgf->keysz) != a->hashsz)
+ return ("MGF not suitable -- restrictive key schedule");
+
+ /* --- All ship-shape and Bristol-fashion --- */
+
+ return (0);
+}
+
+/* --- @algs_samep@ --- *
+ *
+ * Arguments: @const algswitch *a, *aa@ = two algorithm selections
+ *
+ * Returns: Nonzero if the two selections are the same.
+ *
+ * Use: Checks sameness of algorithm selections: used to ensure that
+ * peers are using sensible algorithms.
+ */
+
+static int algs_samep(const algswitch *a, const algswitch *aa)
+{
+ return (a->c == aa->c && a->mgf == aa->mgf && a->h == aa->h &&
+ a->m == aa->m && a->tagsz == aa->tagsz);
+}
+
/*----- Main code ---------------------------------------------------------*/
/* --- @keymoan@ --- *
int rc = -1;
const kgops **ko;
const char *e;
+ algswitch a;
/* --- Open the private key file --- */
goto done_1;
}
+ /* --- Collect the algorithms --- */
+
+ if ((e = algs_get(&a, &kf, k)) != 0 ||
+ (e = algs_check(&a, g)) != 0) {
+ dstr_putf(d, "bad symmetric algorithm selection in private key `%s': %s",
+ t.buf, e);
+ goto done_1;
+ }
+
/* --- Good, we're happy --- *
*
* Dodginess! We change the group over here, but don't free any old group
trace(T_CRYPTO, "crypto: r = %s", mpstr(g->r));
trace(T_CRYPTO, "crypto: h = %s", mpstr(g->h));
trace(T_CRYPTO, "crypto: x = %s", mpstr(x));
+ trace(T_CRYPTO, "crypto: cipher = %s", a.c->name);
+ trace(T_CRYPTO, "crypto: mgf = %s", a.mgf->name);
+ trace(T_CRYPTO, "crypto: hash = %s", a.h->name);
+ trace(T_CRYPTO, "crypto: mac = %s/%lu",
+ a.m->name, (unsigned long)a.tagsz * 8);
})
})
/* --- Success! --- */
gg = g; g = 0;
+ algs = a;
kpriv = x; x = 0;
rc = 0;
void km_init(const char *priv, const char *pub, const char *tag)
{
dstr d = DSTR_INIT;
+ const gchash *const *hh;
kr_priv = priv;
kr_pub = pub;
fwatch_init(&w_priv, kr_priv);
fwatch_init(&w_pub, kr_pub);
+ for (hh = ghashtab; *hh; hh++) {
+ if ((*hh)->hashsz > MAXHASHSZ) {
+ die(EXIT_FAILURE, "INTERNAL ERROR: %s hash length %lu > MAXHASHSZ %d",
+ (*hh)->name, (unsigned long)(*hh)->hashsz, MAXHASHSZ);
+ }
+ }
+
DRESET(&d);
if (loadpriv(&d))
die(EXIT_FAILURE, "%s", d.buf);
const char *e;
group *g = 0;
ge *p = 0;
+ algswitch a;
int rc = -1;
/* --- Find the key --- */
if (key_qtag(kf_pub, tag, &t, &k, &kd)) {
- a_warn("private key `%s' not found in keyring `%s'", tag_priv, kr_priv);
+ a_warn("public key `%s' not found in keyring `%s'", tag, kr_pub);
goto done;
}
goto done;
}
+ /* --- Check the algorithms --- */
+
+ if ((e = algs_get(&a, kf_pub, k)) != 0) {
+ a_warn("public key `%s' has bad algorithm selection: %s", t.buf, e);
+ goto done;
+ }
+ if (!algs_samep(&a, &algs)) {
+ a_warn("public key `%s' specifies different algorithms", t.buf);
+ goto done;
+ }
+
/* --- Dump the public key --- */
IF_TRACING(T_KEYMGMT, {
/* -*-c-*-
*
- * $Id: keyset.c,v 1.10 2004/04/08 01:36:17 mdw Exp $
+ * $Id: keyset.c,v 1.11 2004/04/18 18:08:11 mdw Exp $
*
* Handling of symmetric keysets
*
*
* For a 64-bit block cipher (e.g., Blowfish), the probability of a collision
* occurring after 32 MB is less than %$2^{-21}$%, and the probability of a
- * collision occurring after 64 MB is less than %$2^{-19}$%.
+ * collision occurring after 64 MB is less than %$2^{-19}$%. These could be
+ * adjusted dependent on the encryption scheme, but it's too much pain.
*/
#define T_EXP MIN(60) /* Expiry time for a key */
#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 --- *
static int doencrypt(keyset *ks, unsigned ty, buf *b, buf *bb)
{
ghash *h;
- gcipher *c;
+ gcipher *c = ks->cout;
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 osz, nsz;
octet t[4];
int rc = 0;
/* --- Allocate the required buffer space --- */
- c = ks->cout;
- if (buf_ensure(bb, MACSZ + SEQSZ + IVSZ + sz))
+ if (buf_ensure(bb, tagsz + SEQSZ + ivsz + sz))
return (0); /* Caution! */
- qmac = BCUR(bb); qseq = qmac + MACSZ; qiv = qseq + SEQSZ; qpk = qiv + IVSZ;
- BSTEP(bb, MACSZ + SEQSZ + IVSZ + sz);
+ qmac = BCUR(bb); qseq = qmac + tagsz; qiv = qseq + SEQSZ; qpk = qiv + ivsz;
+ BSTEP(bb, tagsz + SEQSZ + ivsz + sz);
STORE32(t, ty);
- /* --- Encrypt the packet --- */
-
oseq = ks->oseq++; STORE32(qseq, oseq);
- rand_get(RAND_GLOBAL, qiv, IVSZ);
- c->ops->setiv(c, qiv);
- c->ops->encrypt(c, p, qpk, sz);
IF_TRACING(T_KEYSET, {
trace(T_KEYSET, "keyset: encrypting packet %lu using keyset %u",
(unsigned long)oseq, ks->seq);
- trace_block(T_CRYPTO, "crypto: encrypted packet", qpk, sz);
+ trace_block(T_CRYPTO, "crypto: plaintext packet", p, sz);
})
- /* --- Now compute the MAC --- */
+ /* --- Encrypt the packet --- */
- h = ks->mout->ops->init(ks->mout);
- h->ops->hash(h, t, sizeof(t));
- h->ops->hash(h, qseq, SEQSZ + IVSZ + sz);
- memcpy(qmac, h->ops->done(h, 0), MACSZ);
- h->ops->destroy(h);
+ 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: computed MAC", qmac, MACSZ);
+ trace_block(T_CRYPTO, "crypto: encrypted packet", qpk, sz);
})
+ /* --- 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);
+ })
+ }
+
/* --- Deduct the packet size from the key's data life --- */
osz = ks->sz_exp;
octet *q = BCUR(bb);
ghash *h;
gcipher *c = ks->cin;
- size_t ivsz = c->ops->c->blksz;
+ 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 + 4) {
+ if (psz < ivsz + SEQSZ + tagsz) {
T( trace(T_KEYSET, "keyset: block too small for keyset %u", ks->seq); )
return (-1);
}
- sz = psz - IVSZ - SEQSZ - MACSZ;
- pmac = BCUR(b); pseq = pmac + MACSZ; piv = pseq + SEQSZ; ppk = piv + IVSZ;
+ 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 --- */
-
- h = ks->min->ops->init(ks->min);
- h->ops->hash(h, t, sizeof(t));
- h->ops->hash(h, pseq, SEQSZ + IVSZ + sz);
- mac = h->ops->done(h, 0);
- eq = !memcmp(mac, pmac, MACSZ);
IF_TRACING(T_KEYSET, {
trace(T_KEYSET, "keyset: decrypting using keyset %u", ks->seq);
- trace_block(T_CRYPTO, "crypto: computed MAC", mac, MACSZ);
+ trace_block(T_CRYPTO, "crypto: ciphertext packet", ppk, sz);
})
- h->ops->destroy(h);
- if (!eq) {
+
+ /* --- 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 = !memcmp(mac, pmac, tagsz);
IF_TRACING(T_KEYSET, {
- trace(T_KEYSET, "keyset: incorrect MAC: decryption failed");
- trace_block(T_CRYPTO, "crypto: expected MAC", pmac, MACSZ);
+ trace_block(T_CRYPTO, "crypto: computed MAC", mac, tagsz);
})
- return (-1);
+ 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 (-1);
+ }
}
/* --- Decrypt the packet --- */
- c->ops->setiv(c, piv);
- c->ops->decrypt(c, ppk, q, sz);
+ 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, {
{
if (--ks->ref)
return;
- ks->cin->ops->destroy(ks->cin);
- ks->cout->ops->destroy(ks->cout);
- ks->min->ops->destroy(ks->min);
- ks->mout->ops->destroy(ks->mout);
+ GC_DESTROY(ks->cin);
+ GC_DESTROY(ks->cout);
+ GM_DESTROY(ks->min);
+ GM_DESTROY(ks->mout);
DESTROY(ks);
}
keyset *ks_gen(const void *k, size_t x, size_t y, size_t z, peer *p)
{
- HASH_CTX h;
- octet buf[HASHSZ];
+ ghash *h;
+ const octet *hh;
keyset *ks = CREATE(keyset);
time_t now = time(0);
const octet *pp = k;
* This is done with macros, because it's quite tedious.
*/
-#define MINE HASH(&h, pp, x)
-#define YOURS HASH(&h, pp + x, y - x)
-#define OURS HASH(&h, pp + y, z - y)
-
-#define IN MINE; YOURS; OURS
-#define OUT YOURS; MINE; OURS
-#define STR_IN "incoming"
-#define STR_OUT "outgoing"
-
-#define GETHASH(str, dir) do { \
- HASH_INIT(&h); \
- HASH_STRING(&h, "tripe-" str); \
- dir; \
- HASH_DONE(&h, buf); \
+#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, \
- buf, sizeof(buf)); \
+ 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)
- GETHASH("encryption", IN); ks->cin = CIPHER->init(buf, sizeof(buf));
- GETHASH("integrity", IN); ks->min = MAC->key(buf, sizeof(buf));
- GETHASH("encryption", OUT); ks->cout = CIPHER->init(buf, sizeof(buf));
- GETHASH("integrity", OUT); ks->mout = MAC->key(buf, sizeof(buf));
+ SETKEY(c, in); SETKEY(c, out);
+ SETKEY(m, in); SETKEY(m, out);
#undef MINE
#undef YOURS
#undef OURS
-#undef IN
-#undef OUT
-#undef STR_IN
-#undef STR_OUT
-#undef GETHASH
+#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
T( ks->seq = seq++; )
ks->ref = 1;
ks->next = 0;
ks->p = p;
ks->f = KSF_LISTEN;
- BURN(buf);
+ ks->tagsz = algs.tagsz;
return (ks);
}
/* -*-c-*-
*
- * $Id: tripe.c,v 1.13 2004/04/08 01:36:17 mdw Exp $
+ * $Id: tripe.c,v 1.14 2004/04/18 18:08:11 mdw Exp $
*
* Main program
*
{
struct timeval tvv;
T( trace(T_PEER, "peer: interval timer"); )
- rand_seed(RAND_GLOBAL, HASHSZ);
+ rand_seed(RAND_GLOBAL, MAXHASHSZ);
p_interval();
tvv = *tv;
tvv.tv_sec += T_INTERVAL;
sel_init(&sel);
sig_init(&sel);
rand_noisesrc(RAND_GLOBAL, &noise_source);
- rand_seed(RAND_GLOBAL, RMD160_HASHSZ);
+ rand_seed(RAND_GLOBAL, MAXHASHSZ);
signal(SIGPIPE, SIG_IGN);
tun_init();
p_init(baddr, port);
/* -*-c-*-
*
- * $Id: tripe.h,v 1.19 2004/04/08 01:36:17 mdw Exp $
+ * $Id: tripe.h,v 1.20 2004/04/18 18:08:11 mdw Exp $
*
* Main header file for TrIPE
*
/*----- Cipher selections -------------------------------------------------*/
-#include <catacomb/blowfish.h>
-#include <catacomb/blowfish-cbc.h>
-#include <catacomb/blowfish-counter.h>
-#include <catacomb/rmd160.h>
-#include <catacomb/rmd160-hmac.h>
+typedef struct algswitch {
+ const gccipher *c; /* Symmetric encryption scheme */
+ const gccipher *mgf; /* Mask-generation function */
+ const gchash *h; /* Hash function */
+ const gcmac *m; /* Message authentication code */
+ size_t hashsz; /* Hash output size */
+ size_t tagsz; /* Length to truncate MAC tags */
+ size_t cksz, mksz; /* Key lengths for @c@ and @m@ */
+} algswitch;
-#define CIPHER (&blowfish_cbc)
-#define MAC (&rmd160_hmac)
+extern algswitch algs;
-#define HASH_CTX rmd160_ctx
-#define HASH_INIT rmd160_init
-#define HASH rmd160_hash
-#define HASH_STRING(c, s) HASH((c), s, sizeof(s))
-#define HASH_DONE rmd160_done
-#define HASHSZ RMD160_HASHSZ
+#define MAXHASHSZ 64 /* Largest possible hash size */
-#define MGF_CTX blowfish_counterctx
-#define MGF_INIT blowfish_counterinit
-#define MGF_CRYPT blowfish_counterencrypt
-
-#define SEQSZ 4
-#define IVSZ BLOWFISH_BLKSZ
-#define MACSZ 10
+#define HASH_STRING(h, s) GH_HASH((h), (s), sizeof(s))
/*----- Data structures ---------------------------------------------------*/
T( unsigned seq; ) /* Sequence number for tracing */
unsigned f; /* Various useful flags */
gcipher *cin, *cout; /* Keyset ciphers for encryption */
+ size_t tagsz; /* Length to truncate MAC tags */
gmac *min, *mout; /* Keyset MACs for integrity */
uint32 oseq; /* Outbound sequence number */
uint32 iseq, iwin; /* Inbound sequence number */
keyset *ks; /* Pointer to temporary keyset */
unsigned f; /* Various useful flags */
sel_timer t; /* Response timer for challenge */
- octet hc[HASHSZ]; /* Hash of his challenge */
+ octet hc[MAXHASHSZ]; /* Hash of his challenge */
mp *ck; /* The check value */
- octet hswrq_in[HASHSZ]; /* Inbound switch request message */
- octet hswok_in[HASHSZ]; /* Inbound switch confirmation */
- octet hswrq_out[HASHSZ]; /* Outbound switch request message */
- octet hswok_out[HASHSZ]; /* Outbound switch confirmation */
+ octet hswrq_in[MAXHASHSZ]; /* Inbound switch request message */
+ octet hswok_in[MAXHASHSZ]; /* Inbound switch confirmation */
+ octet hswrq_out[MAXHASHSZ]; /* Outbound switch request message */
+ octet hswok_out[MAXHASHSZ]; /* Outbound switch confirmation */
} kxchal;
typedef struct keyexch {
ge *rx; /* The expected response */
unsigned nr; /* Number of extant responses */
time_t t_valid; /* When this exchange goes bad */
- octet hc[HASHSZ]; /* Hash of my challenge */
+ octet hc[MAXHASHSZ]; /* Hash of my challenge */
kxchal *r[KX_NCHAL]; /* Array of challenges */
} keyexch;