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