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