tripe (1.0.0pre6) experimental; urgency=low
* Debianization!
+ * Don't report uninteresting errors when accepting connections.
+ * Support elliptic curve keys.
- -- Mark Wooding <mdw@nsict.org> Sat, 22 Nov 2003 18:35:50 +0000
+ -- Mark Wooding <mdw@nsict.org> Sat, 3 Apr 2004 11:23:40 +0100
Section: net
Priority: extra
Maintainer: Mark Wooding <mdw@nsict.org>
-Build-Depends: catacomb-dev (>= 2.0.1), mlib-dev (>= 2.0.2),
+Build-Depends: catacomb-dev (>= 2.1.0), mlib-dev (>= 2.0.3),
ethereal-dev, debhelper (>= 4.0.2)
Standards-Version: 3.1.1
.hP 7.
Congratulations. The two servers will exchange keys and begin sending
packets almost immediately. You've set up a virtual private network.
+.SS "Using elliptic curve keys"
+The
+.B tripe
+server can use elliptic curve Diffie-Hellman for key exchange, rather
+than traditional integer Diffie-Hellman. Given current public
+knowledge, elliptic curves can provide similar or better security to
+systems based on integer discrete log problems, faster, and with less
+transmitted data. It's a matter of controversy whether this will
+continue to be the case. The author uses elliptic curves.
+.PP
+The server works out which it
+should be doing based on the key type, which is either
+.B tripe\-dh
+for standard Diffie-Hellman, or
+.B tripe\-ec
+for elliptic curves. To create elliptic curve keys, say something like
+.VS
+key add \-aec\-param \-Cnist-p192 \-eforever \e
+ \-tparam tripe\-ec\-param
+.VE
+to construct a parameters key, using your preferred elliptic curve in
+the
+.B \-C
+option (see
+.BR key (1)
+for details); and create the private keys by
+.VS
+key add \-aec \-pparam \-talice \e
+ \-e"now + 1 year" tripe\-ec
+.VE
+Now start
+.B tripe
+with the
+.B \-ttripe\-ec
+option, and all should be well.
.SS "About the name"
The program's name is
.BR tripe ,
/* -*-c-*-
*
- * $Id: packet-tripe.c,v 1.1 2003/10/15 09:30:19 mdw Exp $
+ * $Id: packet-tripe.c,v 1.2 2004/04/03 12:35:13 mdw Exp $
*
* TrIPE protocol dissector for Ethereal
*
/*----- Revision history --------------------------------------------------*
*
* $Log: packet-tripe.c,v $
+ * Revision 1.2 2004/04/03 12:35:13 mdw
+ * Support elliptic curve key exchange.
+ *
* Revision 1.1 2003/10/15 09:30:19 mdw
* Add support for Ethereal protocol analysis.
*
static int proto_tripe = -1;
-typedef struct hfmp { int hf, hf_len, hf_val, tt; } hfmp;
+typedef struct hfmp {
+ int hf, hf_len, hf_val, tt;
+} hfmp;
+typedef struct hfge {
+ int hf, hf_len, hf_val, hfx_len, hfx_val, hfy_len, hfy_val, tt;
+} hfge;
static int hf_tripe_cat = -1;
static int hf_tripe_packet_type = -1;
static int hf_tripe_ct_cbc = -1;
static int hf_tripe_ct_mac = -1;
static int hf_tripe_kx_type = -1;
-static hfmp hf_tripe_kx_mychal = { -1, -1, -1, -1 };
+static hfge hf_tripe_kx_mychal = { -1, -1, -1, -1, -1, -1, -1, -1 };
static int hf_tripe_kx_mycookie = -1;
static int hf_tripe_kx_yourcookie = -1;
static hfmp hf_tripe_kx_check = { -1, -1, -1, -1 };
return (off + 2 + len);
}
+static gint getge(proto_tree *tt, const hfge *hf, tvbuff_t *b, gint off)
+{
+ guint16 len = tvb_get_ntohs(b, off), len2;
+ guint r;
+ proto_item *ti;
+ r = tvb_length_remaining(b, off);
+ if (r < 4 + len ||
+ (len2 = tvb_get_ntohs(b, off + 2 + len), r < 4 + len + len2)) {
+ ti = proto_tree_add_item(tt, hf->hf, b, off, len + 2, FALSE);
+ tt = proto_item_add_subtree(ti, hf->tt);
+ proto_tree_add_item(tt, hf->hf_len, b, off, 2, FALSE);
+ proto_tree_add_item(tt, hf->hf_val, b, off + 2, len, FALSE);
+ r = off + len + 2;
+ } else {
+ ti = proto_tree_add_item(tt, hf->hf, b, off, len + len2 + 4, FALSE);
+ tt = proto_item_add_subtree(ti, hf->tt);
+ proto_tree_add_item(tt, hf->hfx_len, b, off, 2, FALSE);
+ proto_tree_add_item(tt, hf->hfx_val, b, off + 2, len, FALSE);
+ proto_tree_add_item(tt, hf->hfy_len, b, off + 2 + len, 2, FALSE);
+ proto_tree_add_item(tt, hf->hfy_val, b, off + 4 + len, len2, FALSE);
+ r = off + len + len2 + 4;
+ }
+ return (r);
+}
+
static void dissect_tripe(tvbuff_t *b, packet_info *p, proto_tree *t)
{
proto_item *ti;
proto_tree_add_item(tt, hf_tripe_kx_type, b, 0, 1, FALSE);
switch (ty & MSG_TYPEMASK) {
case KX_PRECHAL:
- off = getmp(tt, &hf_tripe_kx_mychal, b, off);
+ off = getge(tt, &hf_tripe_kx_mychal, b, off);
goto tail;
case KX_COOKIE:
- off = getmp(tt, &hf_tripe_kx_mychal, b, off);
+ off = getge(tt, &hf_tripe_kx_mychal, b, off);
off = gethash(tt, hf_tripe_kx_yourcookie, b, off);
goto tail;
case KX_CHAL:
- off = getmp(tt, &hf_tripe_kx_mychal, b, off);
+ off = getge(tt, &hf_tripe_kx_mychal, b, off);
off = gethash(tt, hf_tripe_kx_yourcookie, b, off);
off = getmp(tt, &hf_tripe_kx_check, b, off);
goto tail;
&hf_tripe_kx_mychal.hf, {
"Sender's challenge data", "tripe.kx.mychal",
FT_BYTES, BASE_NONE, 0, 0,
- "This is the sender's challenge value."
+ "This is the sender's challenge."
},
&hf_tripe_kx_mychal.hf_len, {
"Challenge length", "tripe.kx.mychal.len",
FT_UINT16, BASE_DEC, 0, 0,
- "This is the length of the sender's challenge value."
+ "This is the length of the sender's challenge."
},
&hf_tripe_kx_mychal.hf_val, {
- "Challenge value", "tripe.kx.mychal.val",
+ "Challenge", "tripe.kx.mychal.val",
+ FT_BYTES, BASE_NONE, 0, 0,
+ "This is the value of the sender's challenge."
+ },
+ &hf_tripe_kx_mychal.hfx_len, {
+ "Challenge x length", "tripe.kx.mychal.x.len",
+ FT_UINT16, BASE_DEC, 0, 0,
+ "This is the length of the sender's challenge x-coordinate."
+ },
+ &hf_tripe_kx_mychal.hfy_val, {
+ "Challenge x value", "tripe.kx.mychal.x.val",
+ FT_BYTES, BASE_NONE, 0, 0,
+ "This is the value of the sender's challenge x-coordinate."
+ },
+ &hf_tripe_kx_mychal.hfy_len, {
+ "Challenge y length", "tripe.kx.mychal.y.len",
+ FT_UINT16, BASE_DEC, 0, 0,
+ "This is the length of the sender's challenge x-coordinate."
+ },
+ &hf_tripe_kx_mychal.hfx_val, {
+ "Challenge y value", "tripe.kx.mychal.y.val",
FT_BYTES, BASE_NONE, 0, 0,
- "This is the value of the sender's challenge value."
+ "This is the value of the sender's challenge x-coordinate."
},
&hf_tripe_kx_mycookie, {
"Sender's hashed cookie", "tripe.kx.mycookie",
/* -*-c-*-
*
- * $Id: keyexch.c,v 1.10 2003/10/15 09:29:38 mdw Exp $
+ * $Id: keyexch.c,v 1.11 2004/04/03 12:35:13 mdw Exp $
*
* Key exchange protocol
*
/*----- Revision history --------------------------------------------------*
*
* $Log: keyexch.c,v $
+ * Revision 1.11 2004/04/03 12:35:13 mdw
+ * Support elliptic curve key exchange.
+ *
* Revision 1.10 2003/10/15 09:29:38 mdw
* Cosmetic fix to changelog comment.
*
/*----- Various utilities -------------------------------------------------*/
-/* --- @hashmp@ --- *
+/* --- @hashge@ --- *
*
* Arguments: @HASH_CTX *r@ = pointer to hash context
- * @mp *m@ = pointer to multiprecision integer
+ * @ge *x@ = pointer to group element
*
* Returns: ---
*
- * Use: Adds the hash of a multiprecision integer to the context.
- * Corrupts @buf_t@.
+ * Use: Adds the hash of a group element to the context. Corrupts
+ * @buf_t@.
*/
-static void hashmp(HASH_CTX *r, mp *m)
+static void hashge(HASH_CTX *r, ge *x)
{
buf b;
buf_init(&b, buf_t, sizeof(buf_t));
- buf_putmp(&b, m);
+ G_TOBUF(gg, &b, x);
assert(BOK(&b));
HASH(r, BBASE(&b), BLEN(&b));
}
*
* Returns: The encrypted/decrypted integer.
*
- * Use: Encrypts (or decrypts) a multiprecision integer using another
- * multiprecision integer as the key. This is a slightly grotty
- * way to do this, but it's easier than the alternatives.
+ * Use: Encrypts (or decrypts) a multiprecision integer. In fact,
+ * the title is a bit of a misnomer: we actually compute
+ * %$x \xor H(k)$%, so it's a random oracle thing rather than an
+ * encryption thing.
*/
static mp *mpcrypt(mp *d, mp *x, size_t sz, const octet *k, size_t ksz)
{
if (kxc->f & KXF_TIMER)
sel_rmtimer(&kxc->t);
- mp_drop(kxc->c);
- mp_drop(kxc->r);
+ G_DESTROY(gg, kxc->c);
+ if (kxc->r) G_DESTROY(gg, kxc->r);
mp_drop(kxc->ck);
ks_drop(kxc->ks);
DESTROY(kxc);
/* --- Fill in the new structure --- */
kxc = CREATE(kxchal);
- kxc->c = 0;
+ kxc->c = G_CREATE(gg);
kxc->r = 0;
- kxc->ck = 0;
+ kxc->ck = MP_NEW;
kxc->ks = 0;
kxc->kx = kx;
kxc->f = 0;
/* --- @kxc_bychal@ --- *
*
* Arguments: @keyexch *kx@ = pointer to key exchange block
- * @mp *c@ = challenge from remote host
+ * @ge *c@ = challenge from remote host
*
* Returns: Pointer to the challenge block, or null.
*
* Use: Finds a challenge block, given its challenge.
*/
-static kxchal *kxc_bychal(keyexch *kx, mp *c)
+static kxchal *kxc_bychal(keyexch *kx, ge *c)
{
unsigned i;
for (i = 0; i < kx->nr; i++) {
- if (MP_EQ(c, kx->r[i]->c))
+ if (G_EQ(gg, c, kx->r[i]->c))
return (kx->r[i]);
}
return (0);
/* --- Build the reply packet --- */
if (!kxc->r)
- buf_putmp(b, kx->c);
+ G_TOBUF(gg, b, kx->c);
else
buf_put(b, kx->hc, HASHSZ);
buf_put(b, kxc->hc, HASHSZ);
} else {
T( trace(T_KEYEXCH, "keyexch: sending reply to `%s'", p_name(kx->p)); )
buf_init(&bb, buf_i, sizeof(buf_i));
- buf_putmp(&bb, kxc->r);
+ G_TOBUF(gg, &bb, kxc->r);
buf_flip(&bb);
ks_encrypt(kxc->ks, MSG_KEYEXCH | KX_REPLY, &bb, b);
}
/* --- @getreply@ --- *
*
* Arguments: @keyexch *kx@ = pointer to key exchange context
- * @mp *c@ = a challenge
+ * @ge *c@ = a challenge
* @mp *ck@ = the supplied expected-reply check value
*
* Returns: A pointer to the reply, or null if the reply-hash was wrong.
* Use: Computes replies to challenges.
*/
-static mp *getreply(keyexch *kx, mp *c, mp *ck)
+static ge *getreply(keyexch *kx, ge *c, mp *ck)
{
- mp *r = mpmont_exp(&mg, MP_NEW, c, kpriv.x);
- mp *a;
+ ge *r = G_CREATE(gg);
+ ge *y = G_CREATE(gg);
+ mp *a = MP_NEW;
HASH_CTX h;
octet buf[HASHSZ];
int ok;
+ G_EXP(gg, r, c, kpriv);
HASH_INIT(&h);
HASH_STRING(&h, "tripe-expected-reply");
- hashmp(&h, c);
- hashmp(&h, kx->c);
- hashmp(&h, r);
+ hashge(&h, c);
+ hashge(&h, kx->c);
+ hashge(&h, r);
HASH_DONE(&h, buf);
- a = mpcrypt(MP_NEW, ck, mp_octets(kpriv.dp.q), buf, sizeof(buf));
+ a = mpcrypt(MP_NEW, ck, mp_octets(gg->r), buf, sizeof(buf));
IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
- trace(T_CRYPTO, "crypto: computed reply = %s", mpstr(r));
+ trace(T_CRYPTO, "crypto: computed reply = %s", gestr(gg, r));
trace_block(T_CRYPTO, "crypto: computed reply hash", buf, HASHSZ);
trace(T_CRYPTO, "crypto: recovered log = %s", mpstr(a));
}))
- a = mpmont_exp(&mg, a, kpriv.dp.g, a);
- ok = mp_eq(a, c);
+ G_EXP(gg, y, gg->g, a);
+ ok = G_EQ(gg, y, c);
if (!ok) {
a_warn("invalid expected-reply check from `%s'", p_name(kx->p));
IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
- trace(T_CRYPTO, "crypto: computed challenge = %s", mpstr(a));
+ trace(T_CRYPTO, "crypto: computed challenge = %s", gestr(gg, y));
}))
- mp_drop(r);
+ G_DESTROY(gg, r);
+ r = 0;
}
mp_drop(a);
- return (ok ? r : 0);
+ G_DESTROY(gg, y);
+ return (r);
}
/* --- @dochallenge@ --- *
static int dochallenge(keyexch *kx, unsigned msg, buf *b)
{
- mp *c = 0, *ck = 0;
+ ge *c = G_CREATE(gg);
+ mp *ck = MP_NEW;
const octet *hc = 0;
kxchal *kxc;
HASH_CTX h;
/* --- Unpack the packet --- */
- if ((c = buf_getmp(b)) == 0 ||
+ if (G_FROMBUF(gg, b, c) ||
(msg >= KX_COOKIE && (hc = buf_get(b, HASHSZ)) == 0) ||
(msg >= KX_CHAL && (ck = buf_getmp(b)) == 0) ||
BLEFT(b)) {
}
IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
- trace(T_CRYPTO, "crypto: challenge = %s", mpstr(c));
+ trace(T_CRYPTO, "crypto: challenge = %s", gestr(gg, c));
if (hc) trace_block(T_CRYPTO, "crypto: cookie", hc, HASHSZ);
if (ck) trace(T_CRYPTO, "crypto: check value = %s", mpstr(ck));
}))
if (!hc && kx->nr >= KX_THRESH) {
T( trace(T_KEYEXCH, "keyexch: too many challenges -- sending cookie"); )
b = p_txstart(kx->p, MSG_KEYEXCH | KX_COOKIE);
- buf_putmp(b, kx->c);
+ G_TOBUF(gg, b, kx->c);
HASH_INIT(&h);
HASH_STRING(&h, "tripe-cookie");
- hashmp(&h, c);
+ hashge(&h, c);
HASH_DONE(&h, buf_get(b, HASHSZ));
p_txend(kx->p);
goto tidy;
if ((kxc = kxc_bychal(kx, c)) == 0) {
size_t x, y, z;
- mp *r;
+ ge *r;
/* --- Be careful here --- *
*
kxc = kxc_new(kx);
kxc->r = r;
}
- kxc->c = mp_copy(c);
+ kxc->c = G_CREATE(gg);
+ G_COPY(gg, kxc->c, c);
/* --- Work out the cookie for this challenge --- */
HASH_INIT(&h);
HASH_STRING(&h, "tripe-cookie");
- hashmp(&h, kxc->c);
+ hashge(&h, kxc->c);
HASH_DONE(&h, kxc->hc);
/* --- Compute the expected-reply hash --- */
HASH_INIT(&h);
HASH_STRING(&h, "tripe-expected-reply");
- hashmp(&h, kx->c);
- hashmp(&h, kxc->c);
- hashmp(&h, kx->rx);
+ 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(kpriv.dp.q),
+ kxc->ck = mpcrypt(MP_NEW, kx->alpha, mp_octets(gg->r),
buf, sizeof(buf));
/* --- Work out the shared key --- */
- trace(T_CRYPTO, "debug: c = %s", mpstr(c));
- trace(T_CRYPTO, "debug: alpha = %s", mpstr(kx->alpha));
- r = mpmont_exp(&mg, MP_NEW, c, kx->alpha);
- trace(T_CRYPTO, "debug: r = %s", mpstr(r));
+ r = G_CREATE(gg);
+ G_EXP(gg, r, c, kx->alpha);
/* --- Compute the switch messages --- */
HASH_INIT(&h); HASH_STRING(&h, "tripe-switch-request");
- hashmp(&h, kx->c); hashmp(&h, kxc->c);
+ hashge(&h, kx->c); hashge(&h, kxc->c);
HASH_DONE(&h, kxc->hswrq_out);
HASH_INIT(&h); HASH_STRING(&h, "tripe-switch-confirm");
- hashmp(&h, kx->c); hashmp(&h, kxc->c);
+ hashge(&h, kx->c); hashge(&h, kxc->c);
HASH_DONE(&h, kxc->hswok_out);
HASH_INIT(&h); HASH_STRING(&h, "tripe-switch-request");
- hashmp(&h, kxc->c); hashmp(&h, kx->c);
+ hashge(&h, kxc->c); hashge(&h, kx->c);
HASH_DONE(&h, kxc->hswrq_in);
HASH_INIT(&h); HASH_STRING(&h, "tripe-switch-confirm");
- hashmp(&h, kxc->c); hashmp(&h, kx->c);
+ hashge(&h, kxc->c); hashge(&h, kx->c);
HASH_DONE(&h, kxc->hswok_in);
IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
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", mpstr(r));
+ trace(T_CRYPTO, "crypto: shared secret = %s", gestr(gg, r));
trace_block(T_CRYPTO, "crypto: outbound switch request",
kxc->hswrq_out, HASHSZ);
trace_block(T_CRYPTO, "crypto: outbound switch confirm",
/* --- Create a new symmetric keyset --- */
buf_init(b, buf_o, sizeof(buf_o));
- buf_putmp(b, kx->c); x = BLEN(b);
- buf_putmp(b, kxc->c); y = BLEN(b);
- buf_putmp(b, r); z = BLEN(b);
+ G_TOBUF(gg, b, kx->c); x = BLEN(b);
+ G_TOBUF(gg, b, kxc->c); y = BLEN(b);
+ G_TOBUF(gg, b, r); z = BLEN(b);
assert(BOK(b));
kxc->ks = ks_gen(BBASE(b), x, y, z, kx->p);
- mp_drop(r);
+ G_DESTROY(gg, r);
}
/* --- Answer the challenge if we need to --- */
if (ck && !kxc->r) {
- mp *r;
+ ge *r;
if ((r = getreply(kx, c, ck)) == 0)
goto bad;
kxc->r = r;
/* --- Tidy up and go home --- */
tidy:
- mp_drop(c);
+ G_DESTROY(gg, c);
mp_drop(ck);
return (0);
bad:
- mp_drop(c);
+ G_DESTROY(gg, c);
mp_drop(ck);
return (-1);
}
T( trace(T_KEYEXCH, "keyexch: sending prechallenge to `%s'",
p_name(kx->p)); )
b = p_txstart(kx->p, MSG_KEYEXCH | KX_PRECHAL);
- buf_putmp(b, kx->c);
+ G_TOBUF(gg, b, kx->c);
break;
case KXS_COMMIT:
T( trace(T_KEYEXCH, "keyexch: sending switch request to `%s'",
buf_put(b, kx->hc, HASHSZ);
buf_put(b, kxc->hc, HASHSZ);
buf_init(&bb, buf_i, sizeof(buf_i));
- buf_putmp(&bb, kxc->r);
+ G_TOBUF(gg, &bb, kxc->r);
buf_put(&bb, kxc->hswrq_out, HASHSZ);
buf_flip(&bb);
ks_encrypt(kxc->ks, MSG_KEYEXCH | KX_SWITCH, &bb, b);
{
kxchal *kxc;
buf bb;
- mp *r = 0;
+ ge *r = 0;
/* --- Check the plaintext portions of the data --- */
goto bad;
}
buf_init(b, BBASE(&bb), BLEN(&bb));
- if ((r = buf_getmp(b)) == 0) {
+ r = G_CREATE(gg);
+ if (G_FROMBUF(gg, b, r)) {
a_warn("invalid reply packet from `%s'", p_name(kx->p));
goto bad;
}
IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
- trace(T_CRYPTO, "crypto: reply = %s", mpstr(r));
+ trace(T_CRYPTO, "crypto: reply = %s", gestr(gg, r));
}))
- if (!mp_eq(r, kx->rx)) {
+ if (!G_EQ(gg, r, kx->rx)) {
a_warn("incorrect reply from `%s'", p_name(kx->p));
goto bad;
}
/* --- Done --- */
- mp_drop(r);
+ G_DESTROY(gg, r);
return (kxc);
bad:
- mp_drop(r);
+ if (r) G_DESTROY(gg, r);
return (0);
}
for (i = 0; i < kx->nr; i++)
kxc_destroy(kx->r[i]);
mp_drop(kx->alpha);
- mp_drop(kx->c);
- mp_drop(kx->rx);
+ G_DESTROY(gg, kx->c);
+ G_DESTROY(gg, kx->rx);
kx->t_valid = 0;
kx->f |= KXF_DEAD;
kx->f &= ~KXF_TIMER;
kx->f &= ~KXF_DEAD;
kx->nr = 0;
- kx->alpha = mprand_range(MP_NEW, kpriv.dp.q, &rand_global, 0);
- kx->c = mpmont_exp(&mg, MP_NEW, kpriv.dp.g, kx->alpha);
- kx->rx = mpmont_exp(&mg, MP_NEW, kx->kpub.y, kx->alpha);
+ kx->alpha = mprand_range(MP_NEW, gg->r, &rand_global, 0);
+ kx->c = G_CREATE(gg); G_EXP(gg, kx->c, gg->g, kx->alpha);
+ kx->rx = G_CREATE(gg); G_EXP(gg, kx->rx, kx->kpub, kx->alpha);
kx->s = KXS_CHAL;
kx->t_valid = now + T_VALID;
HASH_INIT(&h);
HASH_STRING(&h, "tripe-cookie");
- hashmp(&h, kx->c);
+ hashge(&h, kx->c);
HASH_DONE(&h, kx->hc);
IF_TRACING(T_KEYEXCH, {
trace(T_KEYEXCH, "keyexch: creating new challenge");
IF_TRACING(T_CRYPTO, {
trace(T_CRYPTO, "crypto: secret = %s", mpstr(kx->alpha));
- trace(T_CRYPTO, "crypto: challenge = %s", mpstr(kx->c));
- trace(T_CRYPTO, "crypto: expected response = %s", mpstr(kx->rx));
+ 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);
})
})
if (KEY_EXPIRED(now, kx->texp_kpub)) {
stop(kx);
a_warn("public key for `%s' has expired", p_name(kx->p));
- dh_pubfree(&kx->kpub);
+ G_COPY(gg, kx->kpub, gg->i);
kx->f &= ~KXF_PUBKEY;
return (-1);
}
void kx_free(keyexch *kx)
{
stop(kx);
- if (kx->f & KXF_PUBKEY)
- dh_pubfree(&kx->kpub);
+ G_DESTROY(gg, kx->kpub);
}
/* --- @kx_newkeys@ --- *
void kx_newkeys(keyexch *kx)
{
- dh_pub dp;
-
- if (km_getpubkey(p_name(kx->p), &dp, &kx->texp_kpub))
+ if (km_getpubkey(p_name(kx->p), kx->kpub, &kx->texp_kpub))
return;
- if (kx->f & KXF_PUBKEY)
- dh_pubfree(&kx->kpub);
- kx->kpub = dp;
kx->f |= KXF_PUBKEY;
if ((kx->f & KXF_DEAD) || kx->s != KXS_SWITCH) {
T( trace(T_KEYEXCH, "keyexch: restarting key negotiation with `%s'",
{
kx->ks = ks;
kx->p = p;
- if (km_getpubkey(p_name(p), &kx->kpub, &kx->texp_kpub))
+ kx->kpub = G_CREATE(gg);
+ if (km_getpubkey(p_name(p), kx->kpub, &kx->texp_kpub)) {
+ G_DESTROY(gg, kx->kpub);
return (-1);
+ }
kx->f = KXF_DEAD | KXF_PUBKEY;
start(kx, time(0));
resend(kx);
/* -*-c-*-
*
- * $Id: keymgmt.c,v 1.3 2001/06/22 19:40:36 mdw Exp $
+ * $Id: keymgmt.c,v 1.4 2004/04/03 12:35:13 mdw Exp $
*
* Key loading and storing
*
/*----- Revision history --------------------------------------------------*
*
* $Log: keymgmt.c,v $
+ * Revision 1.4 2004/04/03 12:35:13 mdw
+ * Support elliptic curve key exchange.
+ *
* Revision 1.3 2001/06/22 19:40:36 mdw
* Support expiry of other peers' public keys.
*
/*----- Global variables --------------------------------------------------*/
-mpmont mg;
-dh_priv kpriv;
+group *gg;
+mp *kpriv;
/*----- Static variables --------------------------------------------------*/
static const char *kr_priv, *kr_pub, *tag_priv;
static fwatch w_priv, w_pub;
+/*----- Key groups --------------------------------------------------------*/
+
+typedef struct kgops {
+ const char *ty;
+ const char *(*loadpriv)(key_data *, group **, mp **, dstr *);
+ const char *(*loadpub)(key_data *, group **, ge **, dstr *);
+} kgops;
+
+/* --- Diffie-Hellman --- */
+
+static const char *kgdh_priv(key_data *kd, group **g, mp **x, dstr *t)
+{
+ key_packstruct kps[DH_PRIVFETCHSZ];
+ key_packdef *kp;
+ dh_priv dp;
+ const char *e;
+ int rc;
+
+ kp = key_fetchinit(dh_privfetch, kps, &dp);
+ if ((rc = key_unpack(kp, kd, t)) != 0) {
+ e = key_strerror(rc);
+ goto done;
+ }
+ *g = group_prime(&dp.dp);
+ *x = MP_COPY(dp.x);
+ e = 0;
+done:
+ key_fetchdone(kp);
+ return (e);
+}
+
+static const char *kgdh_pub(key_data *kd, group **g, ge **p, dstr *t)
+{
+ key_packstruct kps[DH_PUBFETCHSZ];
+ key_packdef *kp;
+ dh_pub dp;
+ const char *e;
+ int rc;
+
+ kp = key_fetchinit(dh_pubfetch, kps, &dp);
+ if ((rc = key_unpack(kp, kd, t)) != 0) {
+ e = key_strerror(rc);
+ goto done;
+ }
+ *g = group_prime(&dp.dp);
+ *p = G_CREATE(*g);
+ if (G_FROMINT(*g, *p, dp.y)) {
+ e = "bad public value";
+ goto done;
+ }
+ e = 0;
+done:
+ key_fetchdone(kp);
+ return (e);
+}
+
+static const kgops kgdh_ops = { "tripe-dh", kgdh_priv, kgdh_pub };
+
+/* --- Elliptic curve --- */
+
+static const char *kgec_priv(key_data *kd, group **g, mp **x, dstr *t)
+{
+ key_packstruct kps[EC_PRIVFETCHSZ];
+ key_packdef *kp;
+ ec_priv ep;
+ ec_info ei;
+ const char *e;
+ int rc;
+
+ kp = key_fetchinit(ec_privfetch, kps, &ep);
+ if ((rc = key_unpack(kp, kd, t)) != 0) {
+ e = key_strerror(rc);
+ goto done;
+ }
+ if ((e = ec_getinfo(&ei, ep.cstr)) != 0)
+ goto done;
+ *g = group_ec(&ei);
+ *x = MP_COPY(ep.x);
+ e = 0;
+done:
+ key_fetchdone(kp);
+ return (e);
+}
+
+static const char *kgec_pub(key_data *kd, group **g, ge **p, dstr *t)
+{
+ key_packstruct kps[EC_PUBFETCHSZ];
+ key_packdef *kp;
+ ec_pub ep;
+ ec_info ei;
+ const char *e;
+ int rc;
+
+ kp = key_fetchinit(ec_pubfetch, kps, &ep);
+ if ((rc = key_unpack(kp, kd, t)) != 0) {
+ e = key_strerror(rc);
+ goto done;
+ }
+ if ((e = ec_getinfo(&ei, ep.cstr)) != 0)
+ goto done;
+ *g = group_ec(&ei);
+ *p = G_CREATE(*g);
+ if (G_FROMEC(*g, *p, &ep.p)) {
+ e = "bad public point";
+ goto done;
+ }
+ e = 0;
+done:
+ key_fetchdone(kp);
+ return (e);
+}
+
+static const kgops kgec_ops = { "tripe-ec", kgec_priv, kgec_pub };
+
+/* --- Table of supported key types --- */
+
+static const kgops *kgtab[] = { &kgdh_ops, &kgec_ops, 0 };
+
/*----- Main code ---------------------------------------------------------*/
/* --- @keymoan@ --- *
*/
static void keymoan(const char *file, int line, const char *msg, void *p)
-{
- a_warn("%s:%i: error: %s", file, line, msg);
-}
+ { a_warn("%s:%i: error: %s", file, line, msg); }
/* --- @loadpriv@ --- *
*
* Arguments: @dstr *d@ = string to write errors in
- * @dh_priv *dh@ = where to store the key
*
* Returns: Zero if OK, nonzero on error.
*
* Use: Loads the private key from its keyfile.
*/
-static int loadpriv(dstr *d, dh_priv *dh)
+static int loadpriv(dstr *d)
{
key_file kf;
- key_packstruct kps[DH_PRIVFETCHSZ];
- key_packdef *kp;
- dh_priv mydh;
- int rc = 0;
- int e;
+ key *k;
+ key_data *kd;
+ dstr t = DSTR_INIT;
+ group *g = 0;
+ mp *x = 0;
+ int rc = -1;
+ const kgops **ko;
+ const char *e;
+
+ /* --- Open the private key file --- */
if (key_open(&kf, kr_priv, KOPEN_READ, keymoan, 0)) {
dstr_putf(d, "error reading private keyring `%s': %s",
kr_priv, strerror(errno));
- rc = -1;
- } else {
- T( trace(T_KEYMGMT, "keymgmt: loaded private keyring `%s'", kr_priv); )
- kp = key_fetchinit(dh_privfetch, kps, &mydh);
- if ((e = key_fetchbyname(kp, &kf, tag_priv)) != 0) {
- dstr_putf(d, "error loading private key `%s': %s",
- tag_priv, key_strerror(e));
- rc = -1;
- } else {
- dh->dp.p = MP_COPY(mydh.dp.p);
- dh->dp.q = MP_COPY(mydh.dp.q);
- dh->dp.g = MP_COPY(mydh.dp.g);
- dh->x = MP_COPY(mydh.x);
- dh->y = MP_COPY(mydh.y);
- IF_TRACING(T_KEYMGMT, {
- trace(T_KEYMGMT, "keymgmt: extracted private key `%s'", tag_priv);
- IF_TRACING(T_CRYPTO, {
- trace(T_CRYPTO, "crypto: p = %s", mpstr(kpriv.dp.p));
- trace(T_CRYPTO, "crypto: q = %s", mpstr(kpriv.dp.q));
- trace(T_CRYPTO, "crypto: g = %s", mpstr(kpriv.dp.g));
- trace(T_CRYPTO, "crypto: x = %s", mpstr(kpriv.x));
- trace(T_CRYPTO, "crypto: g^x = %s", mpstr(kpriv.y));
- })
- })
+ goto done_0;
+ }
+
+ /* --- Find the private key --- */
+
+ if (key_qtag(&kf, tag_priv, &t, &k, &kd)) {
+ dstr_putf(d, "private key `%s' not found in keyring `%s'",
+ tag_priv, kr_priv);
+ goto done_1;
+ }
+
+ /* --- Look up the key type in the table --- */
+
+ for (ko = kgtab; *ko; ko++) {
+ if (strcmp((*ko)->ty, k->type) == 0)
+ goto tymatch;
+ }
+ dstr_putf(d, "private key `%s' has unknown type `%s'", t.buf, k->type);
+ goto done_1;
+tymatch:;
+
+ /* --- Load the key --- */
+
+ if ((e = (*ko)->loadpriv(kd, &g, &x, &t)) != 0) {
+ dstr_putf(d, "error reading private key `%s': %s", t.buf, e);
+ goto done_1;
+ }
+
+ /* --- Check that the key is sensible --- */
+
+ if ((e = G_CHECK(g, &rand_global)) != 0) {
+ dstr_putf(d, "bad group 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
+ * elements. This assumes that the new group is basically the same as the
+ * old one, and will happily adopt the existing elements. If it isn't,
+ * then we lose badly. Check this, then.
+ */
+
+ if (gg) {
+ if (!group_samep(g, gg)) {
+ dstr_putf(d, "private key `%s' has different group", t.buf);
+ goto done_1;
}
- key_fetchdone(kp);
- key_close(&kf);
+ G_DESTROYGROUP(gg);
}
+ if (kpriv)
+ mp_drop(kpriv);
+
+ /* --- Dump out the group --- */
+
+ IF_TRACING(T_KEYMGMT, {
+ trace(T_KEYMGMT, "keymgmt: extracted private key `%s'", t.buf);
+ IF_TRACING(T_CRYPTO, {
+ 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));
+ })
+ })
+
+ /* --- Success! --- */
+
+ gg = g; g = 0;
+ kpriv = x; x = 0;
+ rc = 0;
+
+ /* --- Tidy up --- */
+
+done_1:
+ key_close(&kf);
+done_0:
+ dstr_destroy(&t);
+ if (x) mp_drop(x);
+ if (g) G_DESTROYGROUP(g);
return (rc);
}
int km_interval(void)
{
dstr d = DSTR_INIT;
- dh_priv dh;
key_file *kf;
int reload = 0;
if (fwatch_update(&w_priv, kr_priv)) {
T( trace(T_KEYMGMT, "keymgmt: private keyring updated: reloading..."); )
DRESET(&d);
- if (loadpriv(&d, &dh))
+ if (loadpriv(&d))
a_warn("%s -- ignoring changes", d.buf);
- else {
+ else
reload = 1;
- mpmont_destroy(&mg);
- dh_privfree(&kpriv);
- kpriv = dh;
- mpmont_create(&mg, kpriv.dp.p);
- }
}
/* --- Now check the public keys --- */
fwatch_init(&w_pub, kr_pub);
DRESET(&d);
- if (loadpriv(&d, &kpriv))
+ if (loadpriv(&d))
die(EXIT_FAILURE, "%s", d.buf);
- mpmont_create(&mg, kpriv.dp.p);
if (loadpub(&d))
die(EXIT_FAILURE, "%s", d.buf);
}
/* --- @km_getpubkey@ --- *
*
* Arguments: @const char *tag@ = public key tag to load
- * @dh_pub *kpub@ = where to put the public key
+ * @ge *kpub@ = where to put the public key
* @time_t *t_exp@ = where to put the expiry time
*
* Returns: Zero if OK, nonzero if it failed.
* Use: Fetches a public key from the keyring.
*/
-int km_getpubkey(const char *tag, dh_pub *kpub, time_t *t_exp)
+int km_getpubkey(const char *tag, ge *kpub, time_t *t_exp)
{
- key_packstruct kps[DH_PUBFETCHSZ];
- key_packdef *kp;
key *k;
- dh_pub dp;
- int e;
+ key_data *kd;
+ dstr t = DSTR_INIT;
+ const kgops **ko;
+ const char *e;
+ group *g = 0;
+ ge *p = 0;
+ 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);
+ goto done;
+ }
- kp = key_fetchinit(dh_pubfetch, kps, &dp);
- if ((k = key_bytag(kf_pub, tag)) == 0)
- e = KERR_NOTFOUND;
- else
- e = key_fetch(kp, k);
- key_fetchdone(kp);
- if (e) {
- a_warn("error loading public key `%s': %s", tag, key_strerror(e));
- return (-1);
+ /* --- Look up the key type in the table --- */
+
+ for (ko = kgtab; *ko; ko++) {
+ if (strcmp((*ko)->ty, k->type) == 0)
+ goto tymatch;
+ }
+ a_warn("public key `%s' has unknown type `%s'", t.buf, k->type);
+ goto done;
+tymatch:;
+
+ /* --- Load the key --- */
+
+ if ((e = (*ko)->loadpub(kd, &g, &p, &t)) != 0) {
+ a_warn("error reading public key `%s': %s", t.buf, e);
+ goto done;
+ }
+
+ /* --- Ensure that the group is correct --- *
+ *
+ * Dodginess! We assume that if this works, our global group is willing to
+ * adopt this public element. Probably reasonable.
+ */
+
+ if (!group_samep(gg, g)) {
+ a_warn("public key `%s' has incorrect group", t.buf);
+ goto done;
+ }
+
+ /* --- Check the public group element --- */
+
+ if (group_check(gg, p)) {
+ a_warn("public key `%s' has bad public group element", t.buf);
+ goto done;
}
+
+ /* --- Dump the public key --- */
+
IF_TRACING(T_KEYMGMT, {
- trace(T_KEYMGMT, "keymgmt: extracted public key `%s'", tag);
- IF_TRACING(T_CRYPTO, {
- trace(T_CRYPTO, "crypto: p = %s", mpstr(dp.dp.p));
- trace(T_CRYPTO, "crypto: q = %s", mpstr(dp.dp.q));
- trace(T_CRYPTO, "crypto: g = %s", mpstr(dp.dp.g));
- trace(T_CRYPTO, "crypto: g^x = %s", mpstr(dp.y));
- })
+ trace(T_KEYMGMT, "keymgmt: extracted public key `%s'", t.buf);
+ trace(T_CRYPTO, "crypto: p = %s", gestr(gg, p));
})
- if (!mp_eq(dp.dp.p, kpriv.dp.p) ||
- !mp_eq(dp.dp.q, kpriv.dp.q) ||
- !mp_eq(dp.dp.g, kpriv.dp.g)) {
- a_warn("public key `%s' has different group from private key", tag);
- return (-1);
- }
- kpub->dp.p = MP_COPY(dp.dp.p);
- kpub->dp.q = MP_COPY(dp.dp.q);
- kpub->dp.g = MP_COPY(dp.dp.g);
- kpub->y = MP_COPY(dp.y);
+
+ /* --- OK, accept the public key --- */
+
*t_exp = k->exp;
- return (0);
+ G_COPY(gg, kpub, p);
+ rc = 0;
+
+ /* --- Tidy up --- */
+
+done:
+ if (p) G_DESTROY(g, p);
+ if (g) G_DESTROYGROUP(g);
+ dstr_destroy(&t);
+ return (rc);
}
/*----- That's all, folks -------------------------------------------------*/
/* -*-c-*-
*
- * $Id: mallory.c,v 1.2 2003/10/15 09:31:06 mdw Exp $
+ * $Id: mallory.c,v 1.3 2004/04/03 12:35:13 mdw Exp $
*
* An evil proxy for TrIPE
*
/*----- Revision history --------------------------------------------------*
*
* $Log: mallory.c,v $
+ * Revision 1.3 2004/04/03 12:35:13 mdw
+ * Support elliptic curve key exchange.
+ *
* Revision 1.2 2003/10/15 09:31:06 mdw
* Make forking work properly.
*
#include <catacomb/key.h>
#include <catacomb/mp.h>
-#include <catacomb/mpmont.h>
#include <catacomb/mprand.h>
#include <catacomb/dh.h>
/* -*-c-*-
*
- * $Id: servutil.c,v 1.3 2001/06/19 22:08:11 mdw Exp $
+ * $Id: servutil.c,v 1.4 2004/04/03 12:35:13 mdw Exp $
*
* Various handy server-only utilities
*
/*----- Revision history --------------------------------------------------*
*
* $Log: servutil.c,v $
+ * Revision 1.4 2004/04/03 12:35:13 mdw
+ * Support elliptic curve key exchange.
+ *
* Revision 1.3 2001/06/19 22:08:11 mdw
* Use magic number for packet size.
*
return ((const char *)buf_t);
}
+/* --- @gestr@ --- *
+ *
+ * Arguments: @group *g@ = a group
+ * @ge *x@ = a group element
+ *
+ * Returns: A pointer to the element's textual representation.
+ *
+ * Use: Converts a group element to a string. Corrupts
+ * @buf_t@.
+ */
+
+const char *gestr(group *g, ge *x)
+{
+ if (group_writestring(g, x, (char *)buf_t, sizeof(buf_t)))
+ return ("<failed>");
+ return ((const char *)buf_t);
+}
+
/* --- @timestr@ --- *
*
* Arguments: @time_t t@ = a time to convert
$tripectl -D -s -p$tripe \
-f${logfile-@logfile@} \
-P${pidfile-@pidfile@} \
+ ${keytag+-S-t}$keytag \
${addr+-S-b}$addr \
${port+-S-p}${port} \
${user+-S-u}${user} \
# keyrings, and where its admin socket and logfile are kept.
# TRIPEDIR=/etc/tripe
+# The name of the private key to use. This is usually `tripe-dh' for
+# integer Diffie-Hellman keys (the default) or `tripe-ec' for elliptic
+# curve keys.
+# keytag=tripe-dh
+
# The address you want tripe to bind to. By default, tripe will accept
# packets to any address acceptable to the host, and send packets from the
# most appropriate address for the destination; setting this means it will
/* -*-c-*-
*
- * $Id: tripe.h,v 1.17 2003/10/15 09:30:18 mdw Exp $
+ * $Id: tripe.h,v 1.18 2004/04/03 12:35:13 mdw Exp $
*
* Main header file for TrIPE
*
/*----- Revision history --------------------------------------------------*
*
* $Log: tripe.h,v $
+ * Revision 1.18 2004/04/03 12:35:13 mdw
+ * Support elliptic curve key exchange.
+ *
* Revision 1.17 2003/10/15 09:30:18 mdw
* Add support for Ethereal protocol analysis.
*
#include <catacomb/rand.h>
#include <catacomb/mp.h>
-#include <catacomb/mpmont.h>
#include <catacomb/mprand.h>
#include <catacomb/dh.h>
+#include <catacomb/ec.h>
+#include <catacomb/ec-keys.h>
+#include <catacomb/group.h>
#include "tripe-protocol.h"
#include "util.h"
typedef struct kxchal {
struct keyexch *kx; /* Pointer back to key exchange */
- mp *c; /* Responder's challenge */
- mp *r; /* My reply to the challenge */
+ ge *c; /* Responder's challenge */
+ ge *r; /* My reply to the challenge */
keyset *ks; /* Pointer to temporary keyset */
unsigned f; /* Various useful flags */
sel_timer t; /* Response timer for challenge */
unsigned f; /* Various useful flags */
unsigned s; /* Current state in exchange */
sel_timer t; /* Timer for next exchange */
- dh_pub kpub; /* Peer's public key */
+ ge *kpub; /* Peer's public key */
time_t texp_kpub; /* Expiry time for public key */
mp *alpha; /* My temporary secret */
- mp *c; /* My challenge */
- mp *rx; /* The expected response */
+ ge *c; /* My challenge */
+ 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 */
/*----- Global variables --------------------------------------------------*/
extern sel_state sel; /* Global I/O event state */
-extern dh_priv kpriv; /* Our private key */
-extern mpmont mg; /* Montgomery context for DH group */
+extern group *gg; /* The group we work in */
+extern mp *kpriv; /* Our private key */
extern octet buf_i[PKBUFSZ], buf_o[PKBUFSZ], buf_t[PKBUFSZ];
#ifndef NTRACE
/* --- @km_getpubkey@ --- *
*
* Arguments: @const char *tag@ = public key tag to load
- * @dh_pub *kpub@ = where to put the public key
+ * @ge *kpub@ = where to put the public key
* @time_t *t_exp@ = where to put the expiry time
*
* Returns: Zero if OK, nonzero if it failed.
* Use: Fetches a public key from the keyring.
*/
-extern int km_getpubkey(const char */*tag*/, dh_pub */*kpub*/,
+extern int km_getpubkey(const char */*tag*/, ge */*kpub*/,
time_t */*t_exp*/);
/*----- Key exchange ------------------------------------------------------*/
extern const char *mpstr(mp */*m*/);
+/* --- @gestr@ --- *
+ *
+ * Arguments: @group *g@ = a group
+ * @ge *x@ = a group element
+ *
+ * Returns: A pointer to the element's textual representation.
+ *
+ * Use: Converts a group element to a string. Corrupts
+ * @buf_t@.
+ */
+
+extern const char *gestr(group */*g*/, ge */*x*/);
+
/* --- @timestr@ --- *
*
* Arguments: @time_t t@ = a time to convert