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