3 * $Id: keyexch.c,v 1.1 2001/02/03 20:26:37 mdw Exp $
5 * Key exchange protocol
7 * (c) 2001 Straylight/Edgeware
10 /*----- Licensing notice --------------------------------------------------*
12 * This file is part of Trivial IP Encryption (TrIPE).
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.
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.
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.
29 /*----- Revision history --------------------------------------------------*
32 * Revision 1.1 2001/02/03 20:26:37 mdw
37 /*----- Header files ------------------------------------------------------*/
41 /*----- Tunable parameters ------------------------------------------------*/
43 #define T_VALID MIN(2)
44 #define T_QUIET SEC(5)
45 #define T_RETRY SEC(10)
46 #define T_NEWCHAL SEC(5)
48 /*----- Handy macros ------------------------------------------------------*/
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); \
58 #define INITCTX(kx, now) do { \
59 keyexch *_kx = (kx); \
60 time_t _now = (now); \
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; \
67 kx->f = (kx->f | KXF_INIT) & ~(KXF_MYH | KXF_YOURH | \
68 KXF_REPLY | KXF_DONE); \
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; \
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))
82 /*----- Main code ---------------------------------------------------------*/
86 * Arguments: @rmd160_ctx *r@ = pointer to hash context
87 * @mp *m@ = pointer to multiprecision integer
91 * Use: Adds the hash of a multiprecision integer to the context.
95 static void hashmp(rmd160_ctx *r, mp *m)
98 buf_init(&b, buf_o, sizeof(buf_o));
101 rmd160_hash(r, BBASE(&b), BLEN(&b));
106 * Arguments: @struct timeval *tv@ = the current time
107 * @void *v@ = pointer to key exchange context
111 * Use: Acts when the key exchange timer goes off.
114 static void timer(struct timeval *tv, void *v)
118 T( trace(T_KEYEXCH, "keyexch: timer has popped"); )
122 /* --- @settimer@ --- *
124 * Arguments: @keyexch *kx@ = pointer to key exchange context
125 * @time_t t@ = when to set the timer for
129 * Use: Sets the timer for the next key exchange attempt.
132 static void settimer(keyexch *kx, time_t t)
135 if (kx->f & KXF_TIMER)
139 sel_addtimer(&sel, &kx->t, &tv, timer, kx);
143 /* --- @update@ --- *
145 * Arguments: @keyexch *kx@ = pointer to key exchange context
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
156 static void update(keyexch *kx)
159 octet h[RMD160_HASHSZ];
163 /* --- Give up if there's nothing more to do --- */
165 if (kx->f & KXF_DONE)
168 /* --- If we've just started, generate a new challenge --- */
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));
182 /* --- If I don't have your challenge, I can't do anything more --- */
187 /* --- If I've not computed my hash, I should do that --- */
189 if (!(kx->f & KXF_MYH)) {
190 T( trace(T_KEYEXCH, "keyexch: computing my hash"); )
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)); )
201 /* --- If I've received a full challenge, answer it --- *
203 * If it turns out to be wrong, clear the appropriate bits of data.
206 if ((kx->f & KXF_YOURH) && !kx->your_gxy) {
207 kx->your_gxy = mpmont_exp(&mg, MP_NEW, kx->your_gx, kpriv.x);
209 hashmp(&r, kx->your_gx);
210 hashmp(&r, kx->my_gx);
211 hashmp(&r, kx->your_gxy);
213 IF_TRACING(T_KEYEXCH, trace_block(T_CRYPTO, "crypto: computed hash",
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");
226 /* --- If I have a good reply, compute a shared key --- */
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));
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)));
243 /* --- @resend_chal@, @resent_resp@ --- *
245 * Arguments: @keyexch *kx@ = pointer to key exchange context
246 * @time_t now@ = the time right now
250 * Use: Sends packets to the remote host, according to the various
251 * timers and information available.
254 void resend_chal(keyexch *kx, time_t now)
258 if (kx->f & KXF_DONE)
260 if (ISQ_CHAL(kx, now)) {
261 T( trace(T_KEYEXCH, "keyexch: not sending a new challenge yet"); )
265 T( trace(T_KEYEXCH, "keyexch: sending prechallenge"); )
266 b = p_txstart(kx->p, MSG_PRECHALLENGE);
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));
272 buf_putmp(b, kx->my_gx);
274 kx->t_qchal = now + T_QUIET;
275 settimer(kx, now + T_RETRY);
278 void resend_resp(keyexch *kx, time_t now)
284 if (ISQ_RESP(kx, now)) {
285 T( trace(T_KEYEXCH, "keyexch: not sending a new response yet"); )
288 T( trace(T_KEYEXCH, "keyexch: sending response"); )
289 b = p_txstart(kx->p, MSG_RESPONSE);
290 buf_putmp(b, kx->your_gxy);
292 kx->t_qresp = now + T_QUIET;
295 /* --- @kx_start@ --- *
297 * Arguments: @keyexch *kx@ = pointer to key exchange context
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.
306 void kx_start(keyexch *kx)
308 time_t now = time(0);
310 if (!ISVALID(kx, now))
313 resend_chal(kx, now);
316 /* --- @dochallenge@ --- *
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)
325 * Use: Common code for handling challenge messages. The caller
326 * should have successfullly unpacked the challenge structure.
329 static void dochallenge(keyexch *kx, time_t now, mp *m, const octet *h)
334 #define f_new (f_newchal | f_newhash)
336 #define f_good (f_new | f_match)
339 #define f_change (f_new | f_reset)
341 /* --- Restart the process if necessary --- */
343 if (!ISVALID(kx, now))
345 if (!ISNEWCHAL(kx, now))
348 /* --- Sort out what to actually do --- */
354 } else if (mp_eq(kx->your_gx, m)) {
355 if (!h || memcmp(h, kx->your_h, sizeof(kx->your_h)) == 0)
357 else if (!(kx->f & KXF_YOURH))
361 /* --- Update the values in the context --- */
365 else if (!(f & f_ignore)) {
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));
379 a_warn("%s nonmatching challenge from `%s'",
380 (f & f_ignore) ? "rejecting" : "accepting", p_name(kx->p));
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)); )
390 resend_chal(kx, now);
400 /* --- @kx_prechallenge@, @kx_challenge@ --- *
402 * Arguments: @keyexch *kx@ = pointer to key exhange context
403 * @buf *b@ = pointer to buffer containing the packet
407 * Use: Handle prechallenges and challenges.
410 void kx_prechallenge(keyexch *kx, buf *b)
412 time_t now = time(0);
415 if ((m = buf_getmp(b, MP_NEW)) == 0 || BLEFT(b)) {
416 a_warn("malformed prechallenge from `%s'", p_name(kx->p));
419 dochallenge(kx, now, m, 0);
424 void kx_challenge(keyexch *kx, buf *b)
426 time_t now = time(0);
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));
436 dochallenge(kx, now, m, h);
437 resend_resp(kx, now);
442 /* --- @kx_response@ --- *
444 * Arguments: @keyexch *kx@ = pointer to key exchange context
445 * @buf *b@ = a buffer containing the packet to read
449 * Use: Reads a response from the buffer and handles it.
452 void kx_response(keyexch *kx, buf *b)
454 time_t now = time(0);
457 if ((m = buf_getmp(b, MP_NEW)) == 0 || BLEFT(b)) {
458 a_warn("malformed response from `%s'", p_name(kx->p));
461 if (!ISVALID(kx, now))
463 if (!(kx->f & KXF_MYH)) {
464 a_warn("premature response from `%s'", p_name(kx->p));
467 if (!mp_eq(m, kx->my_gxy)) {
468 a_warn("incorrect response from `%s'", p_name(kx->p));
471 T( trace(T_KEYEXCH, "keyexch: valid response from `%s'", p_name(kx->p)); )
479 /* --- @kx_free@ --- *
481 * Arguments: @keyexch *kx@ = pointer to key exchange context
485 * Use: Frees everything in a key exchange context.
488 void kx_free(keyexch *kx)
490 if (kx->f & KXF_TIMER)
493 dh_pubfree(&kx->kpub);
496 /* --- @kx_newkeys@ --- *
498 * Arguments: @keyexch *kx@ = pointer to key exchange context
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
508 void kx_newkeys(keyexch *kx)
512 if (km_getpubkey(p_name(kx->p), &dp))
514 dh_pubfree(&kx->kpub);
516 if (!(kx->f & KXF_DONE)) {
517 T( trace(T_KEYEXCH, "keyexch: restarting key negotiation with `%s'",
519 INITCTX(kx, time(0));
523 /* --- @kx_init@ --- *
525 * Arguments: @keyexch *kx@ = pointer to key exchange context
526 * @peer *p@ = pointer to peer context
527 * @keyset **ks@ = pointer to keyset list
529 * Returns: Zero if OK, nonzero if it failed.
531 * Use: Initializes a key exchange module. The module currently
532 * contains no keys, and will attempt to initiate a key
536 int kx_init(keyexch *kx, peer *p, keyset **ks)
541 if (km_getpubkey(p_name(p), &kx->kpub))
548 /*----- That's all, folks -------------------------------------------------*/