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