chiark / gitweb /
Don't try to run bogus startup scripts.
[tripe] / keyexch.c
CommitLineData
410c8acf 1/* -*-c-*-
2 *
2de0ad0f 3 * $Id: keyexch.c,v 1.7 2003/05/17 11:01:28 mdw Exp $
410c8acf 4 *
5 * Key exchange protocol
6 *
7 * (c) 2001 Straylight/Edgeware
8 */
9
10/*----- Licensing notice --------------------------------------------------*
11 *
12 * This file is part of Trivial IP Encryption (TrIPE).
13 *
14 * TrIPE is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * TrIPE is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with TrIPE; if not, write to the Free Software Foundation,
26 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 */
28
29/*----- Revision history --------------------------------------------------*
30 *
31 * $Log: keyexch.c,v $
2de0ad0f 32 * Revision 1.7 2003/05/17 11:01:28 mdw
33 * Handle flags on challenge timers correctly to prevent confusing the event
34 * list.
35 *
9466fafa 36 * Revision 1.6 2003/04/06 10:26:35 mdw
37 * Report peer name on decrypt errors.
38 *
5d418e24 39 * Revision 1.5 2002/01/13 14:54:40 mdw
40 * Patch up zero-knowledge property by passing an encrypted log with a
41 * challenge, so that the prover can verify that the challenge is good.
42 *
00e64b67 43 * Revision 1.4 2001/06/22 19:40:36 mdw
44 * Support expiry of other peers' public keys.
45 *
56814747 46 * Revision 1.3 2001/06/19 22:07:09 mdw
47 * Cosmetic fixes.
48 *
0617b6e7 49 * Revision 1.2 2001/02/16 21:24:27 mdw
50 * Rewrite for new key exchange protocol.
51 *
410c8acf 52 * Revision 1.1 2001/02/03 20:26:37 mdw
53 * Initial checkin.
54 *
55 */
56
57/*----- Header files ------------------------------------------------------*/
58
59#include "tripe.h"
60
61/*----- Tunable parameters ------------------------------------------------*/
62
2de0ad0f 63#define T_VALID MIN(2) /* Challenge validity period */
64#define T_RETRY SEC(10) /* Challenge retransmit interval */
410c8acf 65
0617b6e7 66#define ISVALID(kx, now) ((now) < (kx)->t_valid)
67
68/*----- Various utilities -------------------------------------------------*/
410c8acf 69
70/* --- @hashmp@ --- *
71 *
0617b6e7 72 * Arguments: @HASH_CTX *r@ = pointer to hash context
410c8acf 73 * @mp *m@ = pointer to multiprecision integer
74 *
75 * Returns: ---
76 *
77 * Use: Adds the hash of a multiprecision integer to the context.
0617b6e7 78 * Corrupts @buf_t@.
410c8acf 79 */
80
0617b6e7 81static void hashmp(HASH_CTX *r, mp *m)
410c8acf 82{
83 buf b;
0617b6e7 84 buf_init(&b, buf_t, sizeof(buf_t));
410c8acf 85 buf_putmp(&b, m);
86 assert(BOK(&b));
0617b6e7 87 HASH(r, BBASE(&b), BLEN(&b));
410c8acf 88}
89
5d418e24 90/* --- @mpcrypt@ --- *
91 *
92 * Arguments: @mp *d@ = the destination integer
93 * @mp *x@ = the plaintext/ciphertext integer
94 * @size_t sz@ = the expected size of the plaintext
95 * @const octet *k@ = pointer to key material
96 * @size_t ksz@ = size of the key
97 *
98 * Returns: The encrypted/decrypted integer.
99 *
100 * Use: Encrypts (or decrypts) a multiprecision integer using another
101 * multiprecision integer as the key. This is a slightly grotty
102 * way to do this, but it's easier than the alternatives.
103 */
104
105static mp *mpcrypt(mp *d, mp *x, size_t sz, const octet *k, size_t ksz)
106{
107 MGF_CTX m;
108
109 MGF_INIT(&m, k, ksz, 0);
110 mp_storeb(x, buf_t, sz);
111 MGF_CRYPT(&m, buf_t, buf_t, sz);
112 return (mp_loadb(d, buf_t, sz));
113}
114
410c8acf 115/* --- @timer@ --- *
116 *
117 * Arguments: @struct timeval *tv@ = the current time
118 * @void *v@ = pointer to key exchange context
119 *
120 * Returns: ---
121 *
122 * Use: Acts when the key exchange timer goes off.
123 */
124
125static void timer(struct timeval *tv, void *v)
126{
127 keyexch *kx = v;
128 kx->f &= ~KXF_TIMER;
129 T( trace(T_KEYEXCH, "keyexch: timer has popped"); )
130 kx_start(kx);
131}
132
133/* --- @settimer@ --- *
134 *
135 * Arguments: @keyexch *kx@ = pointer to key exchange context
136 * @time_t t@ = when to set the timer for
137 *
138 * Returns: ---
139 *
140 * Use: Sets the timer for the next key exchange attempt.
141 */
142
143static void settimer(keyexch *kx, time_t t)
144{
145 struct timeval tv;
146 if (kx->f & KXF_TIMER)
147 sel_rmtimer(&kx->t);
148 tv.tv_sec = t;
149 tv.tv_usec = 0;
150 sel_addtimer(&sel, &kx->t, &tv, timer, kx);
151 kx->f |= KXF_TIMER;
152}
153
0617b6e7 154/*----- Challenge management ----------------------------------------------*/
155
156/* --- Notes on challenge management --- *
410c8acf 157 *
0617b6e7 158 * We may get multiple different replies to our key exchange; some will be
159 * correct, some inserted by attackers. Up until @KX_THRESH@, all challenges
160 * received will be added to the table and given a full response. After
161 * @KX_THRESH@ distinct challenges are received, we return only a `cookie':
162 * our existing challenge, followed by a hash of the sender's challenge. We
163 * do %%\emph{not}%% give a bare challenge a reply slot at this stage. All
164 * properly-formed cookies are assigned a table slot: if none is spare, a
165 * used slot is randomly selected and destroyed. A cookie always receives a
166 * full reply.
167 */
168
169/* --- @kxc_destroy@ --- *
170 *
171 * Arguments: @kxchal *kxc@ = pointer to the challenge block
410c8acf 172 *
173 * Returns: ---
174 *
0617b6e7 175 * Use: Disposes of a challenge block.
410c8acf 176 */
177
0617b6e7 178static void kxc_destroy(kxchal *kxc)
410c8acf 179{
0617b6e7 180 if (kxc->f & KXF_TIMER)
181 sel_rmtimer(&kxc->t);
182 mp_drop(kxc->c);
183 mp_drop(kxc->r);
5d418e24 184 mp_drop(kxc->ck);
0617b6e7 185 ks_drop(kxc->ks);
186 DESTROY(kxc);
187}
410c8acf 188
0617b6e7 189/* --- @kxc_stoptimer@ --- *
190 *
191 * Arguments: @kxchal *kxc@ = pointer to the challenge block
192 *
193 * Returns: ---
194 *
195 * Use: Stops the challenge's retry timer from sending messages.
196 * Useful when the state machine is in the endgame of the
197 * exchange.
198 */
410c8acf 199
0617b6e7 200static void kxc_stoptimer(kxchal *kxc)
201{
202 if (kxc->f & KXF_TIMER)
203 sel_rmtimer(&kxc->t);
2de0ad0f 204 kxc->f &= ~KXF_TIMER;
0617b6e7 205}
410c8acf 206
0617b6e7 207/* --- @kxc_new@ --- *
208 *
209 * Arguments: @keyexch *kx@ = pointer to key exchange block
0617b6e7 210 *
211 * Returns: A pointer to the challenge block.
212 *
213 * Use: Returns a pointer to a new challenge block to fill in.
214 */
410c8acf 215
0617b6e7 216static kxchal *kxc_new(keyexch *kx)
217{
218 kxchal *kxc;
219 unsigned i;
220
221 /* --- If we're over reply threshold, discard one at random --- */
222
223 if (kx->nr < KX_NCHAL)
224 i = kx->nr++;
225 else {
226 i = rand_global.ops->range(&rand_global, KX_NCHAL);
227 kxc_destroy(kx->r[i]);
410c8acf 228 }
229
0617b6e7 230 /* --- Fill in the new structure --- */
410c8acf 231
0617b6e7 232 kxc = CREATE(kxchal);
233 kxc->c = 0;
234 kxc->r = 0;
5d418e24 235 kxc->ck = 0;
0617b6e7 236 kxc->ks = 0;
237 kxc->kx = kx;
238 kxc->f = 0;
239 kx->r[i] = kxc;
240 return (kxc);
241}
410c8acf 242
0617b6e7 243/* --- @kxc_bychal@ --- *
244 *
245 * Arguments: @keyexch *kx@ = pointer to key exchange block
246 * @mp *c@ = challenge from remote host
247 *
248 * Returns: Pointer to the challenge block, or null.
249 *
250 * Use: Finds a challenge block, given its challenge.
251 */
252
253static kxchal *kxc_bychal(keyexch *kx, mp *c)
254{
255 unsigned i;
256
257 for (i = 0; i < kx->nr; i++) {
258 if (MP_EQ(c, kx->r[i]->c))
259 return (kx->r[i]);
260 }
261 return (0);
262}
263
264/* --- @kxc_byhc@ --- *
265 *
266 * Arguments: @keyexch *kx@ = pointer to key exchange block
267 * @const octet *hc@ = challenge hash from remote host
268 *
269 * Returns: Pointer to the challenge block, or null.
270 *
271 * Use: Finds a challenge block, given a hash of its challenge.
272 */
410c8acf 273
0617b6e7 274static kxchal *kxc_byhc(keyexch *kx, const octet *hc)
275{
276 unsigned i;
277
278 for (i = 0; i < kx->nr; i++) {
279 if (memcmp(hc, kx->r[i]->hc, HASHSZ) == 0)
280 return (kx->r[i]);
410c8acf 281 }
0617b6e7 282 return (0);
283}
284
285/* --- @kxc_answer@ --- *
286 *
287 * Arguments: @keyexch *kx@ = pointer to key exchange block
288 * @kxchal *kxc@ = pointer to challenge block
289 *
290 * Returns: ---
291 *
292 * Use: Sends a reply to the remote host, according to the data in
293 * this challenge block.
294 */
295
296static void kxc_answer(keyexch *kx, kxchal *kxc);
297
298static void kxc_timer(struct timeval *tv, void *v)
299{
300 kxchal *kxc = v;
301 kxc->f &= ~KXF_TIMER;
302 kxc_answer(kxc->kx, kxc);
303}
304
305static void kxc_answer(keyexch *kx, kxchal *kxc)
306{
307 stats *st = p_stats(kx->p);
308 buf *b = p_txstart(kx->p, MSG_KEYEXCH | (kxc->r ? KX_REPLY : KX_CHAL));
309 struct timeval tv;
310 buf bb;
311
312 /* --- Build the reply packet --- */
313
314 if (!kxc->r)
315 buf_putmp(b, kx->c);
316 else
317 buf_put(b, kx->hc, HASHSZ);
318 buf_put(b, kxc->hc, HASHSZ);
5d418e24 319 buf_putmp(b, kxc->ck);
0617b6e7 320
321 /* --- Maybe send an actual reply, if we have one --- */
410c8acf 322
0617b6e7 323 if (!kxc->r) {
324 T( trace(T_KEYEXCH, "keyexch: resending challenge to `%s'",
325 p_name(kx->p)); )
326 } else {
327 T( trace(T_KEYEXCH, "keyexch: sending reply to `%s'", p_name(kx->p)); )
328 buf_init(&bb, buf_i, sizeof(buf_i));
329 buf_putmp(&bb, kxc->r);
330 buf_flip(&bb);
331 ks_encrypt(kxc->ks, &bb, b);
332 }
333
334 /* --- Update the statistics --- */
335
336 if (BOK(b)) {
337 st->n_kxout++;
338 st->sz_kxout += BLEN(b);
339 p_txend(kx->p);
340 }
341
342 /* --- Schedule another resend --- */
343
344 if (kxc->f & KXF_TIMER)
345 sel_rmtimer(&kxc->t);
346 gettimeofday(&tv, 0);
347 tv.tv_sec += T_RETRY;
348 sel_addtimer(&sel, &kxc->t, &tv, kxc_timer, kxc);
349 kxc->f |= KXF_TIMER;
350}
351
352/*----- Individual message handlers ---------------------------------------*/
353
354/* --- @getreply@ --- *
355 *
356 * Arguments: @keyexch *kx@ = pointer to key exchange context
357 * @mp *c@ = a challenge
5d418e24 358 * @mp *ck@ = the supplied expected-reply check value
0617b6e7 359 *
360 * Returns: A pointer to the reply, or null if the reply-hash was wrong.
361 *
362 * Use: Computes replies to challenges.
363 */
364
5d418e24 365static mp *getreply(keyexch *kx, mp *c, mp *ck)
0617b6e7 366{
367 mp *r = mpmont_exp(&mg, MP_NEW, c, kpriv.x);
5d418e24 368 mp *a;
0617b6e7 369 HASH_CTX h;
5d418e24 370 octet buf[HASHSZ];
371 int ok;
0617b6e7 372
373 HASH_INIT(&h);
374 HASH_STRING(&h, "tripe-expected-reply");
375 hashmp(&h, c);
376 hashmp(&h, kx->c);
377 hashmp(&h, r);
378 HASH_DONE(&h, buf);
5d418e24 379
380 a = mpcrypt(MP_NEW, ck, mp_octets(kpriv.dp.q), buf, sizeof(buf));
0617b6e7 381 IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
382 trace(T_CRYPTO, "crypto: computed reply = %s", mpstr(r));
383 trace_block(T_CRYPTO, "crypto: computed reply hash", buf, HASHSZ);
5d418e24 384 trace(T_CRYPTO, "crypto: recovered log = %s", mpstr(a));
0617b6e7 385 }))
5d418e24 386 a = mpmont_exp(&mg, a, kpriv.dp.g, a);
387 ok = mp_eq(a, c);
388 if (!ok) {
389 a_warn("invalid expected-reply check from `%s'", p_name(kx->p));
390 IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
391 trace(T_CRYPTO, "crypto: computed challenge = %s", mpstr(a));
392 }))
0617b6e7 393 mp_drop(r);
0617b6e7 394 }
5d418e24 395 mp_drop(a);
396 return (ok ? r : 0);
0617b6e7 397}
398
399/* --- @dochallenge@ --- *
400 *
401 * Arguments: @keyexch *kx@ = pointer to key exchange block
402 * @unsigned msg@ = message code for the packet
403 * @buf *b@ = buffer containing the packet
404 *
405 * Returns: Zero if OK, nonzero if the packet was rejected.
406 *
407 * Use: Processes a packet containing a challenge.
408 */
409
410static int dochallenge(keyexch *kx, unsigned msg, buf *b)
411{
5d418e24 412 mp *c = 0, *ck = 0;
413 const octet *hc = 0;
0617b6e7 414 kxchal *kxc;
415 HASH_CTX h;
5d418e24 416 octet buf[HASHSZ];
0617b6e7 417
418 /* --- Ensure that we're in a sensible state --- */
419
420 if (kx->s != KXS_CHAL) {
421 a_warn("unexpected challenge from `%s'", p_name(kx->p));
422 goto bad;
423 }
424
425 /* --- Unpack the packet --- */
426
427 if ((c = buf_getmp(b)) == 0 ||
428 (msg >= KX_COOKIE && (hc = buf_get(b, HASHSZ)) == 0) ||
5d418e24 429 (msg >= KX_CHAL && (ck = buf_getmp(b)) == 0) ||
0617b6e7 430 BLEFT(b)) {
431 a_warn("malformed packet from `%s'", p_name(kx->p));
432 goto bad;
433 }
434
435 IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
436 trace(T_CRYPTO, "crypto: challenge = %s", mpstr(c));
437 if (hc) trace_block(T_CRYPTO, "crypto: cookie", hc, HASHSZ);
5d418e24 438 if (ck) trace(T_CRYPTO, "crypto: check value = %s", mpstr(ck));
0617b6e7 439 }))
440
441 /* --- First, handle a bare challenge --- *
442 *
443 * If the table is heavily loaded, just emit a cookie and return.
444 */
445
446 if (!hc && kx->nr >= KX_THRESH) {
447 T( trace(T_KEYEXCH, "keyexch: too many challenges -- sending cookie"); )
448 b = p_txstart(kx->p, MSG_KEYEXCH | KX_COOKIE);
449 buf_putmp(b, kx->c);
450 HASH_INIT(&h);
451 HASH_STRING(&h, "tripe-cookie");
452 hashmp(&h, c);
453 HASH_DONE(&h, buf_get(b, HASHSZ));
454 p_txend(kx->p);
455 goto tidy;
456 }
457
458 /* --- Discard a packet with an invalid cookie --- */
459
460 if (hc && memcmp(hc, kx->hc, HASHSZ) != 0) {
461 a_warn("incorrect cookie from `%s'", p_name(kx->p));
462 goto bad;
463 }
464
465 /* --- Find a challenge block for this packet --- *
410c8acf 466 *
0617b6e7 467 * If there isn't one already, create a new one.
410c8acf 468 */
469
0617b6e7 470 if ((kxc = kxc_bychal(kx, c)) == 0) {
471 size_t x, y, z;
472 mp *r;
473
474 /* --- Be careful here --- *
475 *
476 * If this is a full challenge, and it's the first time I've seen it, I
477 * want to be able to throw it away before committing a table entry to
478 * it.
479 */
480
5d418e24 481 if (!ck)
0617b6e7 482 kxc = kxc_new(kx);
483 else {
5d418e24 484 if ((r = getreply(kx, c, ck)) == 0)
0617b6e7 485 goto bad;
486 kxc = kxc_new(kx);
487 kxc->r = r;
410c8acf 488 }
0617b6e7 489 kxc->c = mp_copy(c);
490
491 /* --- Work out the cookie for this challenge --- */
492
493 HASH_INIT(&h);
494 HASH_STRING(&h, "tripe-cookie");
495 hashmp(&h, kxc->c);
496 HASH_DONE(&h, kxc->hc);
497
498 /* --- Compute the expected-reply hash --- */
499
500 HASH_INIT(&h);
501 HASH_STRING(&h, "tripe-expected-reply");
502 hashmp(&h, kx->c);
503 hashmp(&h, kxc->c);
504 hashmp(&h, kx->rx);
5d418e24 505 HASH_DONE(&h, buf);
506 kxc->ck = mpcrypt(MP_NEW, kx->alpha, mp_octets(kpriv.dp.q),
507 buf, sizeof(buf));
0617b6e7 508
509 /* --- Work out the shared key --- */
510
5d418e24 511 trace(T_CRYPTO, "debug: c = %s", mpstr(c));
512 trace(T_CRYPTO, "debug: alpha = %s", mpstr(kx->alpha));
0617b6e7 513 r = mpmont_exp(&mg, MP_NEW, c, kx->alpha);
5d418e24 514 trace(T_CRYPTO, "debug: r = %s", mpstr(r));
0617b6e7 515
516 /* --- Compute the switch messages --- */
517
518 HASH_INIT(&h); HASH_STRING(&h, "tripe-switch-request");
519 hashmp(&h, kx->c); hashmp(&h, kxc->c);
520 HASH_DONE(&h, kxc->hswrq_out);
521 HASH_INIT(&h); HASH_STRING(&h, "tripe-switch-confirm");
522 hashmp(&h, kx->c); hashmp(&h, kxc->c);
523 HASH_DONE(&h, kxc->hswok_out);
524
525 HASH_INIT(&h); HASH_STRING(&h, "tripe-switch-request");
526 hashmp(&h, kxc->c); hashmp(&h, kx->c);
527 HASH_DONE(&h, kxc->hswrq_in);
528 HASH_INIT(&h); HASH_STRING(&h, "tripe-switch-confirm");
529 hashmp(&h, kxc->c); hashmp(&h, kx->c);
530 HASH_DONE(&h, kxc->hswok_in);
531
532 IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
533 trace_block(T_CRYPTO, "crypto: computed cookie", kxc->hc, HASHSZ);
5d418e24 534 trace_block(T_CRYPTO, "crypto: expected-reply hash",
535 buf, HASHSZ);
536 trace(T_CRYPTO, "crypto: my reply check = %s", mpstr(kxc->ck));
0617b6e7 537 trace(T_CRYPTO, "crypto: shared secret = %s", mpstr(r));
538 trace_block(T_CRYPTO, "crypto: outbound switch request",
539 kxc->hswrq_out, HASHSZ);
540 trace_block(T_CRYPTO, "crypto: outbound switch confirm",
541 kxc->hswok_out, HASHSZ);
542 trace_block(T_CRYPTO, "crypto: inbound switch request",
543 kxc->hswrq_in, HASHSZ);
544 trace_block(T_CRYPTO, "crypto: inbound switch confirm",
545 kxc->hswok_in, HASHSZ);
546 }))
547
548 /* --- Create a new symmetric keyset --- */
549
550 buf_init(b, buf_o, sizeof(buf_o));
551 buf_putmp(b, kx->c); x = BLEN(b);
552 buf_putmp(b, kxc->c); y = BLEN(b);
553 buf_putmp(b, r); z = BLEN(b);
554 assert(BOK(b));
555
9466fafa 556 kxc->ks = ks_gen(BBASE(b), x, y, z, kx->p);
0617b6e7 557 mp_drop(r);
410c8acf 558 }
559
0617b6e7 560 /* --- Answer the challenge if we need to --- */
410c8acf 561
5d418e24 562 if (ck && !kxc->r) {
0617b6e7 563 mp *r;
5d418e24 564 if ((r = getreply(kx, c, ck)) == 0)
0617b6e7 565 goto bad;
566 kxc->r = r;
410c8acf 567 }
0617b6e7 568
569 kxc_answer(kx, kxc);
570
571 /* --- Tidy up and go home --- */
572
573tidy:
574 mp_drop(c);
5d418e24 575 mp_drop(ck);
0617b6e7 576 return (0);
577
578bad:
579 mp_drop(c);
5d418e24 580 mp_drop(ck);
0617b6e7 581 return (-1);
410c8acf 582}
583
0617b6e7 584/* --- @resend@ --- *
410c8acf 585 *
586 * Arguments: @keyexch *kx@ = pointer to key exchange context
410c8acf 587 *
588 * Returns: ---
589 *
0617b6e7 590 * Use: Sends the next message for a key exchange.
410c8acf 591 */
592
0617b6e7 593static void resend(keyexch *kx)
410c8acf 594{
0617b6e7 595 kxchal *kxc;
596 buf bb;
597 stats *st = p_stats(kx->p);
410c8acf 598 buf *b;
599
0617b6e7 600 switch (kx->s) {
601 case KXS_CHAL:
00e64b67 602 T( trace(T_KEYEXCH, "keyexch: sending prechallenge to `%s'",
603 p_name(kx->p)); )
0617b6e7 604 b = p_txstart(kx->p, MSG_KEYEXCH | KX_PRECHAL);
605 buf_putmp(b, kx->c);
606 break;
607 case KXS_COMMIT:
00e64b67 608 T( trace(T_KEYEXCH, "keyexch: sending switch request to `%s'",
609 p_name(kx->p)); )
0617b6e7 610 kxc = kx->r[0];
611 b = p_txstart(kx->p, MSG_KEYEXCH | KX_SWITCH);
612 buf_put(b, kx->hc, HASHSZ);
613 buf_put(b, kxc->hc, HASHSZ);
614 buf_init(&bb, buf_i, sizeof(buf_i));
615 buf_putmp(&bb, kxc->r);
616 buf_put(&bb, kxc->hswrq_out, HASHSZ);
617 buf_flip(&bb);
618 ks_encrypt(kxc->ks, &bb, b);
619 break;
620 case KXS_SWITCH:
00e64b67 621 T( trace(T_KEYEXCH, "keyexch: sending switch confirmation to `%s'",
0617b6e7 622 p_name(kx->p)); )
623 kxc = kx->r[0];
624 b = p_txstart(kx->p, MSG_KEYEXCH | KX_SWITCHOK);
625 buf_init(&bb, buf_i, sizeof(buf_i));
626 buf_put(&bb, kxc->hswok_out, HASHSZ);
627 buf_flip(&bb);
628 ks_encrypt(kxc->ks, &bb, b);
629 break;
630 default:
631 abort();
410c8acf 632 }
0617b6e7 633
634 if (BOK(b)) {
635 st->n_kxout++;
636 st->sz_kxout += BLEN(b);
637 p_txend(kx->p);
638 }
639
640 if (kx->s < KXS_SWITCH)
641 settimer(kx, time(0) + T_RETRY);
410c8acf 642}
643
0617b6e7 644/* --- @matchreply@ --- *
645 *
646 * Arguments: @keyexch *kx@ = pointer to key exchange context
647 * @const octet *hc_in@ = a hash of his challenge
648 * @const octet *hc_out@ = a hash of my challenge (cookie)
5d418e24 649 * @mp *ck@ = his expected-reply hash (optional)
0617b6e7 650 * @buf *b@ = encrypted remainder of the packet
651 *
652 * Returns: A pointer to the challenge block if OK, or null on failure.
653 *
654 * Use: Checks a reply or switch packet, ensuring that its contents
655 * are sensible and correct. If they are, @*b@ is set to point
656 * to the remainder of the encrypted data, and the correct
657 * challenge is returned.
658 */
659
660static kxchal *matchreply(keyexch *kx, const octet *hc_in,
5d418e24 661 const octet *hc_out, mp *ck, buf *b)
410c8acf 662{
0617b6e7 663 kxchal *kxc;
664 buf bb;
665 mp *r = 0;
666
667 /* --- Check the plaintext portions of the data --- */
668
669 IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
670 trace_block(T_CRYPTO, "crypto: challenge", hc_in, HASHSZ);
671 trace_block(T_CRYPTO, "crypto: cookie", hc_out, HASHSZ);
5d418e24 672 if (ck) trace(T_CRYPTO, "crypto: check value = %s", mpstr(ck));
0617b6e7 673 }))
674 if (memcmp(hc_out, kx->hc, HASHSZ) != 0) {
675 a_warn("incorrect cookie from `%s'", p_name(kx->p));
676 goto bad;
677 }
678 if ((kxc = kxc_byhc(kx, hc_in)) == 0) {
679 a_warn("received reply for unknown challenge from `%s'", p_name(kx->p));
680 goto bad;
681 }
410c8acf 682
0617b6e7 683 /* --- Maybe compute a reply for the challenge --- */
684
685 if (!kxc->r) {
5d418e24 686 if (!ck) {
0617b6e7 687 a_warn("unexpected switch request from `%s'", p_name(kx->p));
688 goto bad;
689 }
5d418e24 690 if ((r = getreply(kx, kxc->c, ck)) == 0)
0617b6e7 691 goto bad;
692 kxc->r = r;
693 r = 0;
694 }
695
696 /* --- Decrypt the rest of the packet --- */
697
698 buf_init(&bb, buf_o, sizeof(buf_o));
699 if (ks_decrypt(kxc->ks, b, &bb)) {
700 a_warn("failed to decrypt reply from `%s'", p_name(kx->p));
701 goto bad;
410c8acf 702 }
0617b6e7 703 buf_init(b, BBASE(&bb), BLEN(&bb));
704 if ((r = buf_getmp(b)) == 0) {
705 a_warn("invalid reply packet from `%s'", p_name(kx->p));
706 goto bad;
707 }
708 IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
709 trace(T_CRYPTO, "crypto: reply = %s", mpstr(r));
710 }))
711 if (!mp_eq(r, kx->rx)) {
712 a_warn("incorrect reply from `%s'", p_name(kx->p));
713 goto bad;
714 }
715
716 /* --- Done --- */
717
718 mp_drop(r);
719 return (kxc);
720
721bad:
722 mp_drop(r);
723 return (0);
410c8acf 724}
725
0617b6e7 726/* --- @commit@ --- *
410c8acf 727 *
728 * Arguments: @keyexch *kx@ = pointer to key exchange context
0617b6e7 729 * @kxchal *kxc@ = pointer to challenge to commit to
410c8acf 730 *
731 * Returns: ---
732 *
0617b6e7 733 * Use: Commits to a particular challenge as being the `right' one,
734 * since a reply has arrived for it.
410c8acf 735 */
736
0617b6e7 737static void commit(keyexch *kx, kxchal *kxc)
410c8acf 738{
0617b6e7 739 unsigned i;
410c8acf 740
0617b6e7 741 for (i = 0; i < kx->nr; i++) {
742 if (kx->r[i] != kxc)
743 kxc_destroy(kx->r[i]);
744 }
745 kx->r[0] = kxc;
746 kx->nr = 1;
747 kxc_stoptimer(kxc);
748 ksl_link(kx->ks, kxc->ks);
410c8acf 749}
750
0617b6e7 751/* --- @doreply@ --- *
410c8acf 752 *
753 * Arguments: @keyexch *kx@ = pointer to key exchange context
0617b6e7 754 * @buf *b@ = buffer containing packet
410c8acf 755 *
0617b6e7 756 * Returns: Zero if OK, nonzero if the packet was rejected.
410c8acf 757 *
0617b6e7 758 * Use: Handles a reply packet. This doesn't handle the various
759 * switch packets: they're rather too different.
410c8acf 760 */
761
0617b6e7 762static int doreply(keyexch *kx, buf *b)
410c8acf 763{
5d418e24 764 const octet *hc_in, *hc_out;
765 mp *ck = 0;
0617b6e7 766 kxchal *kxc;
767
768 if (kx->s != KXS_CHAL && kx->s != KXS_COMMIT) {
769 a_warn("unexpected reply from `%s'", p_name(kx->p));
770 goto bad;
771 }
772 if ((hc_in = buf_get(b, HASHSZ)) == 0 ||
773 (hc_out = buf_get(b, HASHSZ)) == 0 ||
5d418e24 774 (ck = buf_getmp(b)) == 0) {
0617b6e7 775 a_warn("invalid reply packet from `%s'", p_name(kx->p));
776 goto bad;
777 }
5d418e24 778 if ((kxc = matchreply(kx, hc_in, hc_out, ck, b)) == 0)
0617b6e7 779 goto bad;
780 if (BLEFT(b)) {
781 a_warn("invalid reply packet from `%s'", p_name(kx->p));
782 goto bad;
783 }
784 if (kx->s == KXS_CHAL) {
785 commit(kx, kxc);
786 kx->s = KXS_COMMIT;
787 }
788 resend(kx);
789 return (0);
790
791bad:
5d418e24 792 mp_drop(ck);
0617b6e7 793 return (-1);
410c8acf 794}
795
0617b6e7 796/* --- @doswitch@ --- *
410c8acf 797 *
0617b6e7 798 * Arguments: @keyexch *kx@ = pointer to key exchange block
799 * @buf *b@ = pointer to buffer containing packet
410c8acf 800 *
0617b6e7 801 * Returns: Zero if OK, nonzero if the packet was rejected.
410c8acf 802 *
0617b6e7 803 * Use: Handles a reply with a switch request bolted onto it.
410c8acf 804 */
805
0617b6e7 806static int doswitch(keyexch *kx, buf *b)
410c8acf 807{
0617b6e7 808 const octet *hc_in, *hc_out, *hswrq;
809 kxchal *kxc;
410c8acf 810
0617b6e7 811 if ((hc_in = buf_get(b, HASHSZ)) == 0 ||
812 (hc_out = buf_get(b, HASHSZ)) == 0) {
813 a_warn("invalid switch request from `%s'", p_name(kx->p));
814 goto bad;
410c8acf 815 }
0617b6e7 816 if ((kxc = matchreply(kx, hc_in, hc_out, 0, b)) == 0)
817 goto bad;
818 if ((hswrq = buf_get(b, HASHSZ)) == 0 || BLEFT(b)) {
819 a_warn("invalid switch request from `%s'", p_name(kx->p));
820 goto bad;
821 }
822 IF_TRACING(T_KEYEXCH, {
823 trace_block(T_CRYPTO, "crypto: switch request hash", hswrq, HASHSZ);
824 })
825 if (memcmp(hswrq, kxc->hswrq_in, HASHSZ) != 0) {
826 a_warn("incorrect switch request hash from `%s'", p_name(kx->p));
827 goto bad;
828 }
829 switch (kx->s) {
830 case KXS_CHAL:
831 commit(kx, kxc);
832 case KXS_COMMIT:
833 ks_activate(kxc->ks);
834 settimer(kx, ks_tregen(kxc->ks));
835 kx->s = KXS_SWITCH;
836 break;
837 }
838 resend(kx);
839 return (0);
840
841bad:
842 return (-1);
410c8acf 843}
844
0617b6e7 845/* --- @doswitchok@ --- *
846 *
847 * Arguments: @keyexch *kx@ = pointer to key exchange block
848 * @buf *b@ = pointer to buffer containing packet
849 *
850 * Returns: Zero if OK, nonzero if the packet was rejected.
851 *
852 * Use: Handles a reply with a switch request bolted onto it.
853 */
854
855static int doswitchok(keyexch *kx, buf *b)
410c8acf 856{
0617b6e7 857 const octet *hswok;
858 kxchal *kxc;
859 buf bb;
410c8acf 860
0617b6e7 861 if (kx->s < KXS_COMMIT) {
862 a_warn("unexpected switch confirmation from `%s'", p_name(kx->p));
863 goto bad;
410c8acf 864 }
0617b6e7 865 kxc = kx->r[0];
866 buf_init(&bb, buf_o, sizeof(buf_o));
867 if (ks_decrypt(kxc->ks, b, &bb)) {
868 a_warn("failed to decrypt switch confirmation from `%s'", p_name(kx->p));
869 goto bad;
870 }
871 buf_init(b, BBASE(&bb), BLEN(&bb));
872 if ((hswok = buf_get(b, HASHSZ)) == 0 || BLEFT(b)) {
873 a_warn("invalid switch confirmation from `%s'", p_name(kx->p));
874 goto bad;
875 }
876 IF_TRACING(T_KEYEXCH, {
877 trace_block(T_CRYPTO, "crypto: switch confirmation hash", hswok, HASHSZ);
878 })
879 if (memcmp(hswok, kxc->hswok_in, HASHSZ) != 0) {
880 a_warn("incorrect switch confirmation hash from `%s'", p_name(kx->p));
881 goto bad;
882 }
883 if (kx->s < KXS_SWITCH) {
884 ks_activate(kxc->ks);
885 settimer(kx, ks_tregen(kxc->ks));
886 kx->s = KXS_SWITCH;
887 }
888 return (0);
889
890bad:
891 return (-1);
892}
893
894/*----- Main code ---------------------------------------------------------*/
895
896/* --- @stop@ --- *
897 *
898 * Arguments: @keyexch *kx@ = pointer to key exchange context
899 *
900 * Returns: ---
901 *
902 * Use: Stops a key exchange dead in its tracks. Throws away all of
903 * the context information. The context is left in an
904 * inconsistent state. The only functions which understand this
905 * state are @kx_free@ and @kx_init@ (which cause it internally
906 * it), and @start@ (which expects it to be the prevailing
907 * state).
908 */
909
910static void stop(keyexch *kx)
911{
912 unsigned i;
913
00e64b67 914 if (kx->f & KXF_DEAD)
915 return;
916
0617b6e7 917 if (kx->f & KXF_TIMER)
918 sel_rmtimer(&kx->t);
919 for (i = 0; i < kx->nr; i++)
920 kxc_destroy(kx->r[i]);
921 mp_drop(kx->alpha);
922 mp_drop(kx->c);
923 mp_drop(kx->rx);
00e64b67 924 kx->t_valid = 0;
925 kx->f |= KXF_DEAD;
926 kx->f &= ~KXF_TIMER;
0617b6e7 927}
928
929/* --- @start@ --- *
930 *
931 * Arguments: @keyexch *kx@ = pointer to key exchange context
932 * @time_t now@ = the current time
933 *
934 * Returns: ---
935 *
936 * Use: Starts a new key exchange with the peer. The context must be
937 * in the bizarre state left by @stop@ or @kx_init@.
938 */
939
940static void start(keyexch *kx, time_t now)
941{
942 HASH_CTX h;
943
00e64b67 944 assert(kx->f & KXF_DEAD);
945
946 kx->f &= ~KXF_DEAD;
0617b6e7 947 kx->nr = 0;
0617b6e7 948 kx->alpha = mprand_range(MP_NEW, kpriv.dp.q, &rand_global, 0);
949 kx->c = mpmont_exp(&mg, MP_NEW, kpriv.dp.g, kx->alpha);
950 kx->rx = mpmont_exp(&mg, MP_NEW, kx->kpub.y, kx->alpha);
951 kx->s = KXS_CHAL;
952 kx->t_valid = now + T_VALID;
953
954 HASH_INIT(&h);
955 HASH_STRING(&h, "tripe-cookie");
956 hashmp(&h, kx->c);
957 HASH_DONE(&h, kx->hc);
958
959 IF_TRACING(T_KEYEXCH, {
960 trace(T_KEYEXCH, "keyexch: creating new challenge");
961 IF_TRACING(T_CRYPTO, {
962 trace(T_CRYPTO, "crypto: secret = %s", mpstr(kx->alpha));
963 trace(T_CRYPTO, "crypto: challenge = %s", mpstr(kx->c));
964 trace(T_CRYPTO, "crypto: expected response = %s", mpstr(kx->rx));
965 trace_block(T_CRYPTO, "crypto: challenge cookie", kx->hc, HASHSZ);
966 })
967 })
410c8acf 968}
969
00e64b67 970/* --- @checkpub@ --- *
971 *
972 * Arguments: @keyexch *kx@ = pointer to key exchange context
973 *
974 * Returns: Zero if OK, nonzero if the peer's public key has expired.
975 *
976 * Use: Deactivates the key-exchange until the peer acquires a new
977 * public key.
978 */
979
980static int checkpub(keyexch *kx)
981{
982 time_t now;
983 if (kx->f & KXF_DEAD)
984 return (-1);
985 now = time(0);
986 if (KEY_EXPIRED(now, kx->texp_kpub)) {
987 stop(kx);
988 a_warn("public key for `%s' has expired", p_name(kx->p));
989 dh_pubfree(&kx->kpub);
990 kx->f &= ~KXF_PUBKEY;
991 return (-1);
992 }
993 return (0);
994}
995
0617b6e7 996/* --- @kx_start@ --- *
410c8acf 997 *
998 * Arguments: @keyexch *kx@ = pointer to key exchange context
410c8acf 999 *
1000 * Returns: ---
1001 *
0617b6e7 1002 * Use: Stimulates a key exchange. If a key exchage is in progress,
1003 * a new challenge is sent (unless the quiet timer forbids
1004 * this); if no exchange is in progress, one is commenced.
410c8acf 1005 */
1006
0617b6e7 1007void kx_start(keyexch *kx)
410c8acf 1008{
1009 time_t now = time(0);
410c8acf 1010
00e64b67 1011 if (checkpub(kx))
1012 return;
0617b6e7 1013 if (!ISVALID(kx, now)) {
1014 stop(kx);
1015 start(kx, now);
410c8acf 1016 }
0617b6e7 1017 resend(kx);
1018}
1019
1020/* --- @kx_message@ --- *
1021 *
1022 * Arguments: @keyexch *kx@ = pointer to key exchange context
1023 * @unsigned msg@ = the message code
1024 * @buf *b@ = pointer to buffer containing the packet
1025 *
1026 * Returns: ---
1027 *
1028 * Use: Reads a packet containing key exchange messages and handles
1029 * it.
1030 */
1031
1032void kx_message(keyexch *kx, unsigned msg, buf *b)
1033{
1034 time_t now = time(0);
1035 stats *st = p_stats(kx->p);
1036 size_t sz = BSZ(b);
1037 int rc;
1038
1039#ifndef NTRACE
1040 static const char *const pkname[] = {
1041 "prechallenge", "cookie", "challenge",
1042 "reply", "switch request", "switch confirmation"
1043 };
1044#endif
1045
00e64b67 1046 if (checkpub(kx))
1047 return;
1048
0617b6e7 1049 if (!ISVALID(kx, now)) {
1050 stop(kx);
1051 start(kx, now);
410c8acf 1052 }
0617b6e7 1053
1054 T( trace(T_KEYEXCH, "keyexch: processing %s packet from `%s'",
1055 msg < KX_NMSG ? pkname[msg] : "unknown", p_name(kx->p)); )
1056
1057 switch (msg) {
1058 case KX_PRECHAL:
1059 case KX_COOKIE:
1060 case KX_CHAL:
1061 rc = dochallenge(kx, msg, b);
1062 break;
1063 case KX_REPLY:
1064 rc = doreply(kx, b);
1065 break;
1066 case KX_SWITCH:
1067 rc = doswitch(kx, b);
1068 break;
1069 case KX_SWITCHOK:
1070 rc = doswitchok(kx, b);
1071 break;
1072 default:
1073 a_warn("unexpected key exchange message type %u from `%p'",
1074 p_name(kx->p));
1075 rc = -1;
1076 break;
410c8acf 1077 }
410c8acf 1078
0617b6e7 1079 if (rc)
1080 st->n_reject++;
1081 else {
1082 st->n_kxin++;
1083 st->sz_kxin += sz;
1084 }
410c8acf 1085}
1086
1087/* --- @kx_free@ --- *
1088 *
1089 * Arguments: @keyexch *kx@ = pointer to key exchange context
1090 *
1091 * Returns: ---
1092 *
1093 * Use: Frees everything in a key exchange context.
1094 */
1095
1096void kx_free(keyexch *kx)
1097{
0617b6e7 1098 stop(kx);
00e64b67 1099 if (kx->f & KXF_PUBKEY)
1100 dh_pubfree(&kx->kpub);
410c8acf 1101}
1102
1103/* --- @kx_newkeys@ --- *
1104 *
1105 * Arguments: @keyexch *kx@ = pointer to key exchange context
1106 *
1107 * Returns: ---
1108 *
1109 * Use: Informs the key exchange module that its keys may have
1110 * changed. If fetching the new keys fails, the peer will be
1111 * destroyed, we log messages and struggle along with the old
1112 * keys.
1113 */
1114
1115void kx_newkeys(keyexch *kx)
1116{
1117 dh_pub dp;
1118
00e64b67 1119 if (km_getpubkey(p_name(kx->p), &dp, &kx->texp_kpub))
410c8acf 1120 return;
00e64b67 1121 if (kx->f & KXF_PUBKEY)
1122 dh_pubfree(&kx->kpub);
410c8acf 1123 kx->kpub = dp;
00e64b67 1124 kx->f |= KXF_PUBKEY;
1125 if ((kx->f & KXF_DEAD) || kx->s != KXS_SWITCH) {
410c8acf 1126 T( trace(T_KEYEXCH, "keyexch: restarting key negotiation with `%s'",
1127 p_name(kx->p)); )
00e64b67 1128 stop(kx);
1129 start(kx, time(0));
1130 resend(kx);
410c8acf 1131 }
1132}
1133
1134/* --- @kx_init@ --- *
1135 *
1136 * Arguments: @keyexch *kx@ = pointer to key exchange context
1137 * @peer *p@ = pointer to peer context
1138 * @keyset **ks@ = pointer to keyset list
1139 *
1140 * Returns: Zero if OK, nonzero if it failed.
1141 *
1142 * Use: Initializes a key exchange module. The module currently
1143 * contains no keys, and will attempt to initiate a key
1144 * exchange.
1145 */
1146
1147int kx_init(keyexch *kx, peer *p, keyset **ks)
1148{
1149 kx->ks = ks;
1150 kx->p = p;
00e64b67 1151 if (km_getpubkey(p_name(p), &kx->kpub, &kx->texp_kpub))
410c8acf 1152 return (-1);
00e64b67 1153 kx->f = KXF_DEAD | KXF_PUBKEY;
0617b6e7 1154 start(kx, time(0));
1155 resend(kx);
410c8acf 1156 return (0);
1157}
1158
1159/*----- That's all, folks -------------------------------------------------*/