chiark / gitweb /
Don't try to run bogus startup scripts.
[tripe] / keyexch.c
1 /* -*-c-*-
2  *
3  * $Id: keyexch.c,v 1.7 2003/05/17 11:01:28 mdw Exp $
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 $
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  *
36  * Revision 1.6  2003/04/06 10:26:35  mdw
37  * Report peer name on decrypt errors.
38  *
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  *
43  * Revision 1.4  2001/06/22 19:40:36  mdw
44  * Support expiry of other peers' public keys.
45  *
46  * Revision 1.3  2001/06/19 22:07:09  mdw
47  * Cosmetic fixes.
48  *
49  * Revision 1.2  2001/02/16 21:24:27  mdw
50  * Rewrite for new key exchange protocol.
51  *
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
63 #define T_VALID MIN(2)                  /* Challenge validity period */
64 #define T_RETRY SEC(10)                 /* Challenge retransmit interval */
65
66 #define ISVALID(kx, now) ((now) < (kx)->t_valid)
67
68 /*----- Various utilities -------------------------------------------------*/
69
70 /* --- @hashmp@ --- *
71  *
72  * Arguments:   @HASH_CTX *r@ = pointer to hash context
73  *              @mp *m@ = pointer to multiprecision integer
74  *
75  * Returns:     ---
76  *
77  * Use:         Adds the hash of a multiprecision integer to the context.
78  *              Corrupts @buf_t@.
79  */
80
81 static void hashmp(HASH_CTX *r, mp *m)
82 {
83   buf b;
84   buf_init(&b, buf_t, sizeof(buf_t));
85   buf_putmp(&b, m);
86   assert(BOK(&b));
87   HASH(r, BBASE(&b), BLEN(&b));
88 }
89
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
105 static 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
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
125 static 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
143 static 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
154 /*----- Challenge management ----------------------------------------------*/
155
156 /* --- Notes on challenge management --- *
157  *
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
172  *
173  * Returns:     ---
174  *
175  * Use:         Disposes of a challenge block.
176  */
177
178 static void kxc_destroy(kxchal *kxc)
179 {
180   if (kxc->f & KXF_TIMER)
181     sel_rmtimer(&kxc->t);
182   mp_drop(kxc->c);
183   mp_drop(kxc->r);
184   mp_drop(kxc->ck);
185   ks_drop(kxc->ks);
186   DESTROY(kxc);
187 }
188
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  */
199
200 static void kxc_stoptimer(kxchal *kxc)
201 {
202   if (kxc->f & KXF_TIMER)
203     sel_rmtimer(&kxc->t);
204   kxc->f &= ~KXF_TIMER;
205 }
206
207 /* --- @kxc_new@ --- *
208  *
209  * Arguments:   @keyexch *kx@ = pointer to key exchange block
210  *
211  * Returns:     A pointer to the challenge block.
212  *
213  * Use:         Returns a pointer to a new challenge block to fill in.
214  */
215
216 static 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]);
228   }
229
230   /* --- Fill in the new structure --- */
231
232   kxc = CREATE(kxchal);
233   kxc->c = 0;
234   kxc->r = 0;
235   kxc->ck = 0;
236   kxc->ks = 0;
237   kxc->kx = kx;
238   kxc->f = 0;
239   kx->r[i] = kxc;
240   return (kxc);
241 }
242
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
253 static 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  */
273
274 static 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]);
281   }
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
296 static void kxc_answer(keyexch *kx, kxchal *kxc);
297
298 static 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
305 static 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);
319   buf_putmp(b, kxc->ck);
320
321   /* --- Maybe send an actual reply, if we have one --- */
322
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
358  *              @mp *ck@ = the supplied expected-reply check value
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
365 static mp *getreply(keyexch *kx, mp *c, mp *ck)
366 {
367   mp *r = mpmont_exp(&mg, MP_NEW, c, kpriv.x);
368   mp *a;
369   HASH_CTX h;
370   octet buf[HASHSZ];  
371   int ok;
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);
379
380   a = mpcrypt(MP_NEW, ck, mp_octets(kpriv.dp.q), buf, sizeof(buf));
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);
384     trace(T_CRYPTO, "crypto: recovered log = %s", mpstr(a));
385   }))
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     }))
393     mp_drop(r);
394   }
395   mp_drop(a);
396   return (ok ? r : 0);
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
410 static int dochallenge(keyexch *kx, unsigned msg, buf *b)
411 {
412   mp *c = 0, *ck = 0;
413   const octet *hc = 0;
414   kxchal *kxc;
415   HASH_CTX h;
416   octet buf[HASHSZ];
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) ||
429       (msg >= KX_CHAL && (ck = buf_getmp(b)) == 0) ||
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);
438     if (ck) trace(T_CRYPTO, "crypto: check value = %s", mpstr(ck));
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 --- *
466    *
467    * If there isn't one already, create a new one.
468    */
469
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
481     if (!ck)
482       kxc = kxc_new(kx);        
483     else {
484       if ((r = getreply(kx, c, ck)) == 0)
485         goto bad;
486       kxc = kxc_new(kx);
487       kxc->r = r;
488     }
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);
505     HASH_DONE(&h, buf);
506     kxc->ck = mpcrypt(MP_NEW, kx->alpha, mp_octets(kpriv.dp.q),
507                       buf, sizeof(buf));
508
509     /* --- Work out the shared key --- */
510
511     trace(T_CRYPTO, "debug: c = %s", mpstr(c));
512     trace(T_CRYPTO, "debug: alpha = %s", mpstr(kx->alpha));
513     r = mpmont_exp(&mg, MP_NEW, c, kx->alpha);
514     trace(T_CRYPTO, "debug: r = %s", mpstr(r));
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);
534       trace_block(T_CRYPTO, "crypto: expected-reply hash",
535                   buf, HASHSZ);
536       trace(T_CRYPTO, "crypto: my reply check = %s", mpstr(kxc->ck));
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
556     kxc->ks = ks_gen(BBASE(b), x, y, z, kx->p);
557     mp_drop(r);
558   }
559
560   /* --- Answer the challenge if we need to --- */
561
562   if (ck && !kxc->r) {
563     mp *r;
564     if ((r = getreply(kx, c, ck)) == 0)
565       goto bad;
566     kxc->r = r;
567   }
568
569   kxc_answer(kx, kxc);
570
571   /* --- Tidy up and go home --- */
572
573 tidy:
574   mp_drop(c);
575   mp_drop(ck);
576   return (0);
577
578 bad:
579   mp_drop(c);
580   mp_drop(ck);
581   return (-1);
582 }
583
584 /* --- @resend@ --- *
585  *
586  * Arguments:   @keyexch *kx@ = pointer to key exchange context
587  *
588  * Returns:     ---
589  *
590  * Use:         Sends the next message for a key exchange.
591  */
592
593 static void resend(keyexch *kx)
594 {
595   kxchal *kxc;
596   buf bb;
597   stats *st = p_stats(kx->p);
598   buf *b;
599
600   switch (kx->s) {
601     case KXS_CHAL:
602       T( trace(T_KEYEXCH, "keyexch: sending prechallenge to `%s'",
603                p_name(kx->p)); )
604       b = p_txstart(kx->p, MSG_KEYEXCH | KX_PRECHAL);
605       buf_putmp(b, kx->c);
606       break;
607     case KXS_COMMIT:
608       T( trace(T_KEYEXCH, "keyexch: sending switch request to `%s'",
609                p_name(kx->p)); )
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:
621       T( trace(T_KEYEXCH, "keyexch: sending switch confirmation to `%s'",
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();
632   }
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);
642 }
643
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)
649  *              @mp *ck@ = his expected-reply hash (optional)
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
660 static kxchal *matchreply(keyexch *kx, const octet *hc_in,
661                           const octet *hc_out, mp *ck, buf *b)
662 {
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);
672     if (ck) trace(T_CRYPTO, "crypto: check value = %s", mpstr(ck));
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   }
682
683   /* --- Maybe compute a reply for the challenge --- */
684
685   if (!kxc->r) {
686     if (!ck) {
687       a_warn("unexpected switch request from `%s'", p_name(kx->p));
688       goto bad;
689     }
690     if ((r = getreply(kx, kxc->c, ck)) == 0)
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;
702   }
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
721 bad:
722   mp_drop(r);
723   return (0);
724 }
725
726 /* --- @commit@ --- *
727  *
728  * Arguments:   @keyexch *kx@ = pointer to key exchange context
729  *              @kxchal *kxc@ = pointer to challenge to commit to
730  *
731  * Returns:     ---
732  *
733  * Use:         Commits to a particular challenge as being the `right' one,
734  *              since a reply has arrived for it.
735  */
736
737 static void commit(keyexch *kx, kxchal *kxc)
738 {
739   unsigned i;
740
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);  
749 }
750
751 /* --- @doreply@ --- *
752  *
753  * Arguments:   @keyexch *kx@ = pointer to key exchange context
754  *              @buf *b@ = buffer containing packet
755  *
756  * Returns:     Zero if OK, nonzero if the packet was rejected.
757  *
758  * Use:         Handles a reply packet.  This doesn't handle the various
759  *              switch packets: they're rather too different.
760  */
761
762 static int doreply(keyexch *kx, buf *b)
763 {
764   const octet *hc_in, *hc_out;
765   mp *ck = 0;
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 ||
774       (ck = buf_getmp(b)) == 0) {
775     a_warn("invalid reply packet from `%s'", p_name(kx->p));
776     goto bad;
777   }
778   if ((kxc = matchreply(kx, hc_in, hc_out, ck, b)) == 0)
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
791 bad:
792   mp_drop(ck);
793   return (-1);
794 }
795
796 /* --- @doswitch@ --- *
797  *
798  * Arguments:   @keyexch *kx@ = pointer to key exchange block
799  *              @buf *b@ = pointer to buffer containing packet
800  *
801  * Returns:     Zero if OK, nonzero if the packet was rejected.
802  *
803  * Use:         Handles a reply with a switch request bolted onto it.
804  */
805
806 static int doswitch(keyexch *kx, buf *b)
807 {
808   const octet *hc_in, *hc_out, *hswrq;
809   kxchal *kxc;
810
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;
815   }
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
841 bad:
842   return (-1);
843 }
844
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
855 static int doswitchok(keyexch *kx, buf *b)
856 {
857   const octet *hswok;
858   kxchal *kxc;
859   buf bb;
860
861   if (kx->s < KXS_COMMIT) {
862     a_warn("unexpected switch confirmation from `%s'", p_name(kx->p));
863     goto bad;
864   }
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
890 bad:
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
910 static void stop(keyexch *kx)
911 {
912   unsigned i;
913
914   if (kx->f & KXF_DEAD)
915     return;
916
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);
924   kx->t_valid = 0;
925   kx->f |= KXF_DEAD;
926   kx->f &= ~KXF_TIMER;
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
940 static void start(keyexch *kx, time_t now)
941 {
942   HASH_CTX h;
943
944   assert(kx->f & KXF_DEAD);
945
946   kx->f &= ~KXF_DEAD;
947   kx->nr = 0;
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   })
968 }
969
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
980 static 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
996 /* --- @kx_start@ --- *
997  *
998  * Arguments:   @keyexch *kx@ = pointer to key exchange context
999  *
1000  * Returns:     ---
1001  *
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.
1005  */
1006
1007 void kx_start(keyexch *kx)
1008 {
1009   time_t now = time(0);
1010
1011   if (checkpub(kx))
1012     return;
1013   if (!ISVALID(kx, now)) {
1014     stop(kx);
1015     start(kx, now);
1016   }
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
1032 void 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
1046   if (checkpub(kx))
1047     return;
1048
1049   if (!ISVALID(kx, now)) {
1050     stop(kx);
1051     start(kx, now);
1052   }
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;
1077   }
1078
1079   if (rc)
1080     st->n_reject++;
1081   else {
1082     st->n_kxin++;
1083     st->sz_kxin += sz;
1084   }
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
1096 void kx_free(keyexch *kx)
1097 {
1098   stop(kx);
1099   if (kx->f & KXF_PUBKEY)
1100     dh_pubfree(&kx->kpub);
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
1115 void kx_newkeys(keyexch *kx)
1116 {
1117   dh_pub dp;
1118
1119   if (km_getpubkey(p_name(kx->p), &dp, &kx->texp_kpub))
1120     return;
1121   if (kx->f & KXF_PUBKEY)
1122     dh_pubfree(&kx->kpub);
1123   kx->kpub = dp;
1124   kx->f |= KXF_PUBKEY;
1125   if ((kx->f & KXF_DEAD) || kx->s != KXS_SWITCH) {
1126     T( trace(T_KEYEXCH, "keyexch: restarting key negotiation with `%s'",
1127              p_name(kx->p)); )
1128     stop(kx);
1129     start(kx, time(0));
1130     resend(kx);
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
1147 int kx_init(keyexch *kx, peer *p, keyset **ks)
1148 {
1149   kx->ks = ks;
1150   kx->p = p;
1151   if (km_getpubkey(p_name(p), &kx->kpub, &kx->texp_kpub))
1152     return (-1);
1153   kx->f = KXF_DEAD | KXF_PUBKEY;
1154   start(kx, time(0));
1155   resend(kx);
1156   return (0);
1157 }
1158
1159 /*----- That's all, folks -------------------------------------------------*/