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