chiark / gitweb /
Don't link the client against Catacomb.
[tripe] / keyexch.c
CommitLineData
410c8acf 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
95static 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
114static 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
132static 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
156static 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
254void 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
278void 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
306void 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
329static 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
410void 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);
420tidy:
421 mp_drop(m);
422}
423
424void 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);
438tidy:
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
452void 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
475tidy:
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
488void 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
508void 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
536int 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 -------------------------------------------------*/