chiark / gitweb /
Initial checkin.
[tripe] / keyexch.c
1 /* -*-c-*-
2  *
3  * $Id: keyexch.c,v 1.1 2001/02/03 20:26:37 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.1  2001/02/03 20:26:37  mdw
33  * Initial checkin.
34  *
35  */
36
37 /*----- Header files ------------------------------------------------------*/
38
39 #include "tripe.h"
40
41 /*----- Tunable parameters ------------------------------------------------*/
42
43 #define T_VALID MIN(2)
44 #define T_QUIET SEC(5)
45 #define T_RETRY SEC(10)
46 #define T_NEWCHAL SEC(5)
47
48 /*----- Handy macros ------------------------------------------------------*/
49
50 #define FREECTX(kx) do {                                                \
51   keyexch *_kkx = (kx);                                                 \
52   if (_kkx->f & KXF_INIT) {                                             \
53     mp_drop(_kkx->my_x); mp_drop(_kkx->my_gx); mp_drop(_kkx->my_gxy);   \
54     mp_drop(_kkx->your_gx); mp_drop(_kkx->your_gxy);                    \
55   }                                                                     \
56 } while (0)
57
58 #define INITCTX(kx, now) do {                                           \
59   keyexch *_kx = (kx);                                                  \
60   time_t _now = (now);                                                  \
61   FREECTX(_kx);                                                         \
62   kx->my_x = kx->my_gx = kx->my_gxy = 0;                                \
63   kx->your_gx = kx->your_gxy = 0;                                       \
64   kx->t_valid = _now + T_VALID;                                         \
65   kx->t_qchal = kx->t_qresp = 0;                                        \
66   kx->t_newchal = 0;                                                    \
67   kx->f = (kx->f | KXF_INIT) & ~(KXF_MYH | KXF_YOURH |                  \
68                                  KXF_REPLY | KXF_DONE);                 \
69 } while (0)
70
71 #define NEWCHAL(kx) do {                                                \
72   kx->f &= ~(KXF_YOURH | KXF_MYH);                                      \
73   mp_drop(kx->your_gx); kx->your_gx = 0;                                \
74   mp_drop(kx->your_gxy); kx->your_gxy = 0;                              \
75 } while (0)
76
77 #define ISVALID(kx, now) ((kx)->t_valid > (now))
78 #define ISQ_CHAL(kx, now) ((kx)->t_qchal > (now))
79 #define ISQ_RESP(kx, now) ((kx)->t_qresp > (now))
80 #define ISNEWCHAL(kx, now) ((kx)->t_newchal > (now))
81
82 /*----- Main code ---------------------------------------------------------*/
83
84 /* --- @hashmp@ --- *
85  *
86  * Arguments:   @rmd160_ctx *r@ = pointer to hash context
87  *              @mp *m@ = pointer to multiprecision integer
88  *
89  * Returns:     ---
90  *
91  * Use:         Adds the hash of a multiprecision integer to the context.
92  *              Corrupts @buf_o@.
93  */
94
95 static void hashmp(rmd160_ctx *r, mp *m)
96 {
97   buf b;
98   buf_init(&b, buf_o, sizeof(buf_o));
99   buf_putmp(&b, m);
100   assert(BOK(&b));
101   rmd160_hash(r, BBASE(&b), BLEN(&b));
102 }
103
104 /* --- @timer@ --- *
105  *
106  * Arguments:   @struct timeval *tv@ = the current time
107  *              @void *v@ = pointer to key exchange context
108  *
109  * Returns:     ---
110  *
111  * Use:         Acts when the key exchange timer goes off.
112  */
113
114 static void timer(struct timeval *tv, void *v)
115 {
116   keyexch *kx = v;
117   kx->f &= ~KXF_TIMER;
118   T( trace(T_KEYEXCH, "keyexch: timer has popped"); )
119   kx_start(kx);
120 }
121
122 /* --- @settimer@ --- *
123  *
124  * Arguments:   @keyexch *kx@ = pointer to key exchange context
125  *              @time_t t@ = when to set the timer for
126  *
127  * Returns:     ---
128  *
129  * Use:         Sets the timer for the next key exchange attempt.
130  */
131
132 static void settimer(keyexch *kx, time_t t)
133 {
134   struct timeval tv;
135   if (kx->f & KXF_TIMER)
136     sel_rmtimer(&kx->t);
137   tv.tv_sec = t;
138   tv.tv_usec = 0;
139   sel_addtimer(&sel, &kx->t, &tv, timer, kx);
140   kx->f |= KXF_TIMER;
141 }
142
143 /* --- @update@ --- *
144  *
145  * Arguments:   @keyexch *kx@ = pointer to key exchange context
146  *
147  * Returns:     ---
148  *
149  * Use:         Updates the information in the key exchange context.  Call
150  *              this after new information has arrived.  Expects that the
151  *              context is actually valid.  Doesn't send any packets.
152  *              Assumes that everything in the context is known to be
153  *              correct.
154  */
155
156 static void update(keyexch *kx)
157 {
158   rmd160_ctx r;
159   octet h[RMD160_HASHSZ];
160   mp *k_shared;
161   buf b;
162
163   /* --- Give up if there's nothing more to do --- */
164
165   if (kx->f & KXF_DONE)
166     return;
167
168   /* --- If we've just started, generate a new challenge --- */
169
170   if (!kx->my_x) {
171     T( trace(T_KEYEXCH, "keyexch: generating new challenge"); )
172     kx->my_x = mprand_range(MP_NEWSEC, kx->kpub.dp.q, &rand_global, 0);
173     kx->my_gx = mpmont_exp(&mg, MP_NEW, kx->kpub.dp.g, kx->my_x);
174     kx->my_gxy = mpmont_exp(&mg, MP_NEW, kx->kpub.y, kx->my_x);
175     IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
176       trace(T_CRYPTO, "crypto: secret = %s", mpstr(kx->my_x));
177       trace(T_CRYPTO, "crypto: public value = %s", mpstr(kx->my_gx));
178       trace(T_CRYPTO, "crypto: expected reply = %s", mpstr(kx->my_gxy));
179     }))
180   }
181
182   /* --- If I don't have your challenge, I can't do anything more --- */
183
184   if (!kx->your_gx)
185     return;
186
187   /* --- If I've not computed my hash, I should do that --- */
188
189   if (!(kx->f & KXF_MYH)) {    
190     T( trace(T_KEYEXCH, "keyexch: computing my hash"); )
191     rmd160_init(&r);
192     hashmp(&r, kx->my_gx);
193     hashmp(&r, kx->your_gx);
194     hashmp(&r, kx->my_gxy);
195     rmd160_done(&r, kx->my_h);
196     IF_TRACING(T_KEYEXCH, trace_block(T_CRYPTO, "crypto: my hash",
197                                       kx->my_h, sizeof(kx->my_h)); )
198     kx->f |= KXF_MYH;
199   }
200
201   /* --- If I've received a full challenge, answer it --- *
202    *
203    * If it turns out to be wrong, clear the appropriate bits of data.
204    */
205
206   if ((kx->f & KXF_YOURH) && !kx->your_gxy) {
207     kx->your_gxy = mpmont_exp(&mg, MP_NEW, kx->your_gx, kpriv.x);
208     rmd160_init(&r);
209     hashmp(&r, kx->your_gx);
210     hashmp(&r, kx->my_gx);
211     hashmp(&r, kx->your_gxy);
212     rmd160_done(&r, h);
213     IF_TRACING(T_KEYEXCH, trace_block(T_CRYPTO, "crypto: computed hash",
214                                       h, sizeof(h)); )
215     if (memcmp(h, kx->your_h, sizeof(h)) != 0) {
216       IF_TRACING(T_KEYEXCH, {
217         trace_block(T_CRYPTO, "crypto: expected hash",
218                     kx->your_h, sizeof(kx->your_h));
219         trace(T_KEYEXCH, "keyexch: hashes don't match: botched");
220       })
221       NEWCHAL(kx);
222       return;
223     }
224   }
225
226   /* --- If I have a good reply, compute a shared key --- */
227
228   if ((kx->f & KXF_YOURH) && (kx->f & KXF_REPLY)) {
229     k_shared = mpmont_exp(&mg, MP_NEW, kx->your_gx, kx->my_x);
230     IF_TRACING(T_KEYEXCH, {
231       trace(T_KEYEXCH, "keyexch: computed shared key");
232       trace(T_CRYPTO, "crypto: shared key = %s", mpstr(k_shared));
233     })
234     buf_init(&b, buf_o, sizeof(buf_o));
235     buf_putmp(&b, k_shared); assert(BOK(&b));
236     settimer(kx, ks_gen(kx->ks, BBASE(&b), BLEN(&b)));
237     mp_drop(k_shared);
238     BURN(buf_o);
239     kx->f |= KXF_DONE;
240   }
241 }
242
243 /* --- @resend_chal@, @resent_resp@ --- *
244  *
245  * Arguments:   @keyexch *kx@ = pointer to key exchange context
246  *              @time_t now@ = the time right now
247  *
248  * Returns:     ---
249  *
250  * Use:         Sends packets to the remote host, according to the various
251  *              timers and information available.
252  */
253
254 void resend_chal(keyexch *kx, time_t now)
255 {
256   buf *b;
257
258   if (kx->f & KXF_DONE)
259     return;
260   if (ISQ_CHAL(kx, now)) {
261     T( trace(T_KEYEXCH, "keyexch: not sending a new challenge yet"); )
262     return;
263   }
264   if (!kx->your_gx) {
265     T( trace(T_KEYEXCH, "keyexch: sending prechallenge"); )
266     b = p_txstart(kx->p, MSG_PRECHALLENGE);
267   } else {
268     T( trace(T_KEYEXCH, "keyexch: sending challenge"); )
269     b = p_txstart(kx->p, MSG_CHALLENGE);
270     buf_put(b, kx->my_h, sizeof(kx->my_h));
271   }
272   buf_putmp(b, kx->my_gx);
273   p_txend(kx->p);
274   kx->t_qchal = now + T_QUIET;
275   settimer(kx, now + T_RETRY);
276 }
277
278 void resend_resp(keyexch *kx, time_t now)
279 {
280   buf *b;
281
282   if (!kx->your_gxy)
283     return;
284   if (ISQ_RESP(kx, now)) {
285     T( trace(T_KEYEXCH, "keyexch: not sending a new response yet"); )
286     return;
287   }
288   T( trace(T_KEYEXCH, "keyexch: sending response"); )
289   b = p_txstart(kx->p, MSG_RESPONSE);
290   buf_putmp(b, kx->your_gxy);
291   p_txend(kx->p);
292   kx->t_qresp = now + T_QUIET;
293 }
294
295 /* --- @kx_start@ --- *
296  *
297  * Arguments:   @keyexch *kx@ = pointer to key exchange context
298  *
299  * Returns:     ---
300  *
301  * Use:         Stimulates a key exchange.  If a key exchage is in progress,
302  *              a new challenge is sent (unless the quiet timer forbids
303  *              this); if no exchange is in progress, one is commenced.
304  */
305
306 void kx_start(keyexch *kx)
307 {
308   time_t now = time(0);
309
310   if (!ISVALID(kx, now))
311     INITCTX(kx, now);
312   update(kx);
313   resend_chal(kx, now);
314 }
315
316 /* --- @dochallenge@ --- *
317  *
318  * Arguments:   @keyexch *kx@ = pointer to key exchange context
319  *              @time_t now@ = the current time
320  *              @mp *m@ = new challenge received
321  *              @const octet *h@ = challenge hash (if any)
322  *
323  * Returns:     ---
324  *
325  * Use:         Common code for handling challenge messages.  The caller
326  *              should have successfullly unpacked the challenge structure.
327  */
328
329 static void dochallenge(keyexch *kx, time_t now, mp *m, const octet *h)
330 {
331   unsigned f = 0;
332 #define f_newchal 1u
333 #define f_newhash 2u
334 #define f_new (f_newchal | f_newhash)
335 #define f_match 4u
336 #define f_good (f_new | f_match)
337 #define f_ignore 8u
338 #define f_reset 16u
339 #define f_change (f_new | f_reset)
340
341   /* --- Restart the process if necessary --- */
342
343   if (!ISVALID(kx, now))
344     INITCTX(kx, now);
345   if (!ISNEWCHAL(kx, now))
346     f |= f_ignore;
347
348   /* --- Sort out what to actually do --- */
349   
350   if (!kx->your_gx) {
351     f |= f_newchal;
352     if (h)
353       f |= f_newhash;
354   } else if (mp_eq(kx->your_gx, m)) {
355     if (!h || memcmp(h, kx->your_h, sizeof(kx->your_h)) == 0)
356       f |= f_match;
357     else if (!(kx->f & KXF_YOURH))
358       f |= f_newhash;
359   }
360
361   /* --- Update the values in the context --- */
362
363   if (f & f_good)
364     f &= ~f_ignore;
365   else if (!(f & f_ignore)) {
366     NEWCHAL(kx);
367     f |= f_reset;
368   }
369   if (f & (f_newchal | f_reset))
370     kx->your_gx = MP_COPY(m);
371   if (f & (f_newhash | f_reset)) {
372     memcpy(kx->your_h, h, sizeof(kx->your_h));
373     kx->f |= KXF_YOURH;  
374   }
375   if (f & f_new)
376     kx->t_qchal = 0;    
377
378   if (!(f & f_good)) {
379     a_warn("%s nonmatching challenge from `%s'",
380            (f & f_ignore) ? "rejecting" : "accepting", p_name(kx->p));
381   } else {
382     T( trace(T_KEYEXCH, "keyexch: good challenge (%s, %s) from `%s'",
383              (f & f_newchal) ? "new gxy" : "match gxy",
384              (f & f_newhash) ? "new hash" : "match hash", p_name(kx->p)); )
385   }
386   if (f & f_change)
387     update(kx);
388
389   if (f & f_new)
390     resend_chal(kx, now);
391 #undef f_newchal
392 #undef f_newhash
393 #undef f_new
394 #undef f_match
395 #undef f_good
396 #undef f_ignore
397 #undef f_change
398 }
399
400 /* --- @kx_prechallenge@, @kx_challenge@ --- *
401  *
402  * Arguments:   @keyexch *kx@ = pointer to key exhange context
403  *              @buf *b@ = pointer to buffer containing the packet
404  *
405  * Returns:     ---
406  *
407  * Use:         Handle prechallenges and challenges.
408  */
409
410 void kx_prechallenge(keyexch *kx, buf *b)
411 {
412   time_t now = time(0);
413   mp *m;
414
415   if ((m = buf_getmp(b, MP_NEW)) == 0 || BLEFT(b)) {
416     a_warn("malformed prechallenge from `%s'", p_name(kx->p));
417     goto tidy;
418   }
419   dochallenge(kx, now, m, 0);
420 tidy:
421   mp_drop(m);
422 }
423
424 void kx_challenge(keyexch *kx, buf *b)
425 {
426   time_t now = time(0);
427   mp *m = 0;
428   const octet *h;
429
430   if (buf_ensure(b, RMD160_HASHSZ) ||
431       (h = BCUR(b), BSTEP(b, RMD160_HASHSZ), 
432        (m = buf_getmp(b, MP_NEW)) == 0 || BLEFT(b))) {
433     a_warn("malformed challenge from `%s'", p_name(kx->p));
434     goto tidy;
435   }
436   dochallenge(kx, now, m, h);
437   resend_resp(kx, now);
438 tidy:
439   mp_drop(m);
440 }
441
442 /* --- @kx_response@ --- *
443  *
444  * Arguments:   @keyexch *kx@ = pointer to key exchange context
445  *              @buf *b@ = a buffer containing the packet to read
446  *
447  * Returns:     ---
448  *
449  * Use:         Reads a response from the buffer and handles it.
450  */
451
452 void kx_response(keyexch *kx, buf *b)
453 {
454   time_t now = time(0);
455   mp *m;
456
457   if ((m = buf_getmp(b, MP_NEW)) == 0 || BLEFT(b)) {
458     a_warn("malformed response from `%s'", p_name(kx->p));
459     goto tidy;
460   }
461   if (!ISVALID(kx, now))
462     INITCTX(kx, now);
463   if (!(kx->f & KXF_MYH)) {
464     a_warn("premature response from `%s'", p_name(kx->p));
465     goto tidy;
466   }
467   if (!mp_eq(m, kx->my_gxy)) {
468     a_warn("incorrect response from `%s'", p_name(kx->p));
469     goto tidy;
470   }
471   T( trace(T_KEYEXCH, "keyexch: valid response from `%s'", p_name(kx->p)); )
472   kx->f |= KXF_REPLY;
473   update(kx);
474
475 tidy:
476   mp_drop(m);
477 }
478
479 /* --- @kx_free@ --- *
480  *
481  * Arguments:   @keyexch *kx@ = pointer to key exchange context
482  *
483  * Returns:     ---
484  *
485  * Use:         Frees everything in a key exchange context.
486  */
487
488 void kx_free(keyexch *kx)
489 {
490   if (kx->f & KXF_TIMER)
491     sel_rmtimer(&kx->t);
492   FREECTX(kx);
493   dh_pubfree(&kx->kpub);
494 }
495
496 /* --- @kx_newkeys@ --- *
497  *
498  * Arguments:   @keyexch *kx@ = pointer to key exchange context
499  *
500  * Returns:     ---
501  *
502  * Use:         Informs the key exchange module that its keys may have
503  *              changed.  If fetching the new keys fails, the peer will be
504  *              destroyed, we log messages and struggle along with the old
505  *              keys.
506  */
507
508 void kx_newkeys(keyexch *kx)
509 {
510   dh_pub dp;
511
512   if (km_getpubkey(p_name(kx->p), &dp))
513     return;
514   dh_pubfree(&kx->kpub);
515   kx->kpub = dp;
516   if (!(kx->f & KXF_DONE)) {
517     T( trace(T_KEYEXCH, "keyexch: restarting key negotiation with `%s'",
518              p_name(kx->p)); )
519     INITCTX(kx, time(0));
520   }
521 }
522
523 /* --- @kx_init@ --- *
524  *
525  * Arguments:   @keyexch *kx@ = pointer to key exchange context
526  *              @peer *p@ = pointer to peer context
527  *              @keyset **ks@ = pointer to keyset list
528  *
529  * Returns:     Zero if OK, nonzero if it failed.
530  *
531  * Use:         Initializes a key exchange module.  The module currently
532  *              contains no keys, and will attempt to initiate a key
533  *              exchange.
534  */
535
536 int kx_init(keyexch *kx, peer *p, keyset **ks)
537 {
538   kx->ks = ks;
539   kx->p = p;
540   kx->f = 0;
541   if (km_getpubkey(p_name(p), &kx->kpub))
542     return (-1);
543   kx->t_valid = 0;
544   kx_start(kx);
545   return (0);
546 }
547
548 /*----- That's all, folks -------------------------------------------------*/