3 * $Id: keyexch.c,v 1.6 2003/04/06 10:26:35 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.6 2003/04/06 10:26:35 mdw
33 * Report peer name on decrypt errors.
35 * Revision 1.5 2002/01/13 14:54:40 mdw
36 * Patch up zero-knowledge property by passing an encrypted log with a
37 * challenge, so that the prover can verify that the challenge is good.
39 * Revision 1.4 2001/06/22 19:40:36 mdw
40 * Support expiry of other peers' public keys.
42 * Revision 1.3 2001/06/19 22:07:09 mdw
45 * Revision 1.2 2001/02/16 21:24:27 mdw
46 * Rewrite for new key exchange protocol.
48 * Revision 1.1 2001/02/03 20:26:37 mdw
53 /*----- Header files ------------------------------------------------------*/
57 /*----- Tunable parameters ------------------------------------------------*/
59 #define T_VALID MIN(2)
60 #define T_RETRY SEC(10)
62 #define ISVALID(kx, now) ((now) < (kx)->t_valid)
64 /*----- Various utilities -------------------------------------------------*/
68 * Arguments: @HASH_CTX *r@ = pointer to hash context
69 * @mp *m@ = pointer to multiprecision integer
73 * Use: Adds the hash of a multiprecision integer to the context.
77 static void hashmp(HASH_CTX *r, mp *m)
80 buf_init(&b, buf_t, sizeof(buf_t));
83 HASH(r, BBASE(&b), BLEN(&b));
86 /* --- @mpcrypt@ --- *
88 * Arguments: @mp *d@ = the destination integer
89 * @mp *x@ = the plaintext/ciphertext integer
90 * @size_t sz@ = the expected size of the plaintext
91 * @const octet *k@ = pointer to key material
92 * @size_t ksz@ = size of the key
94 * Returns: The encrypted/decrypted integer.
96 * Use: Encrypts (or decrypts) a multiprecision integer using another
97 * multiprecision integer as the key. This is a slightly grotty
98 * way to do this, but it's easier than the alternatives.
101 static mp *mpcrypt(mp *d, mp *x, size_t sz, const octet *k, size_t ksz)
105 MGF_INIT(&m, k, ksz, 0);
106 mp_storeb(x, buf_t, sz);
107 MGF_CRYPT(&m, buf_t, buf_t, sz);
108 return (mp_loadb(d, buf_t, sz));
113 * Arguments: @struct timeval *tv@ = the current time
114 * @void *v@ = pointer to key exchange context
118 * Use: Acts when the key exchange timer goes off.
121 static void timer(struct timeval *tv, void *v)
125 T( trace(T_KEYEXCH, "keyexch: timer has popped"); )
129 /* --- @settimer@ --- *
131 * Arguments: @keyexch *kx@ = pointer to key exchange context
132 * @time_t t@ = when to set the timer for
136 * Use: Sets the timer for the next key exchange attempt.
139 static void settimer(keyexch *kx, time_t t)
142 if (kx->f & KXF_TIMER)
146 sel_addtimer(&sel, &kx->t, &tv, timer, kx);
150 /*----- Challenge management ----------------------------------------------*/
152 /* --- Notes on challenge management --- *
154 * We may get multiple different replies to our key exchange; some will be
155 * correct, some inserted by attackers. Up until @KX_THRESH@, all challenges
156 * received will be added to the table and given a full response. After
157 * @KX_THRESH@ distinct challenges are received, we return only a `cookie':
158 * our existing challenge, followed by a hash of the sender's challenge. We
159 * do %%\emph{not}%% give a bare challenge a reply slot at this stage. All
160 * properly-formed cookies are assigned a table slot: if none is spare, a
161 * used slot is randomly selected and destroyed. A cookie always receives a
165 /* --- @kxc_destroy@ --- *
167 * Arguments: @kxchal *kxc@ = pointer to the challenge block
171 * Use: Disposes of a challenge block.
174 static void kxc_destroy(kxchal *kxc)
176 if (kxc->f & KXF_TIMER)
177 sel_rmtimer(&kxc->t);
185 /* --- @kxc_stoptimer@ --- *
187 * Arguments: @kxchal *kxc@ = pointer to the challenge block
191 * Use: Stops the challenge's retry timer from sending messages.
192 * Useful when the state machine is in the endgame of the
196 static void kxc_stoptimer(kxchal *kxc)
198 if (kxc->f & KXF_TIMER)
199 sel_rmtimer(&kxc->t);
202 /* --- @kxc_new@ --- *
204 * Arguments: @keyexch *kx@ = pointer to key exchange block
206 * Returns: A pointer to the challenge block.
208 * Use: Returns a pointer to a new challenge block to fill in.
211 static kxchal *kxc_new(keyexch *kx)
216 /* --- If we're over reply threshold, discard one at random --- */
218 if (kx->nr < KX_NCHAL)
221 i = rand_global.ops->range(&rand_global, KX_NCHAL);
222 kxc_destroy(kx->r[i]);
225 /* --- Fill in the new structure --- */
227 kxc = CREATE(kxchal);
238 /* --- @kxc_bychal@ --- *
240 * Arguments: @keyexch *kx@ = pointer to key exchange block
241 * @mp *c@ = challenge from remote host
243 * Returns: Pointer to the challenge block, or null.
245 * Use: Finds a challenge block, given its challenge.
248 static kxchal *kxc_bychal(keyexch *kx, mp *c)
252 for (i = 0; i < kx->nr; i++) {
253 if (MP_EQ(c, kx->r[i]->c))
259 /* --- @kxc_byhc@ --- *
261 * Arguments: @keyexch *kx@ = pointer to key exchange block
262 * @const octet *hc@ = challenge hash from remote host
264 * Returns: Pointer to the challenge block, or null.
266 * Use: Finds a challenge block, given a hash of its challenge.
269 static kxchal *kxc_byhc(keyexch *kx, const octet *hc)
273 for (i = 0; i < kx->nr; i++) {
274 if (memcmp(hc, kx->r[i]->hc, HASHSZ) == 0)
280 /* --- @kxc_answer@ --- *
282 * Arguments: @keyexch *kx@ = pointer to key exchange block
283 * @kxchal *kxc@ = pointer to challenge block
287 * Use: Sends a reply to the remote host, according to the data in
288 * this challenge block.
291 static void kxc_answer(keyexch *kx, kxchal *kxc);
293 static void kxc_timer(struct timeval *tv, void *v)
296 kxc->f &= ~KXF_TIMER;
297 kxc_answer(kxc->kx, kxc);
300 static void kxc_answer(keyexch *kx, kxchal *kxc)
302 stats *st = p_stats(kx->p);
303 buf *b = p_txstart(kx->p, MSG_KEYEXCH | (kxc->r ? KX_REPLY : KX_CHAL));
307 /* --- Build the reply packet --- */
312 buf_put(b, kx->hc, HASHSZ);
313 buf_put(b, kxc->hc, HASHSZ);
314 buf_putmp(b, kxc->ck);
316 /* --- Maybe send an actual reply, if we have one --- */
319 T( trace(T_KEYEXCH, "keyexch: resending challenge to `%s'",
322 T( trace(T_KEYEXCH, "keyexch: sending reply to `%s'", p_name(kx->p)); )
323 buf_init(&bb, buf_i, sizeof(buf_i));
324 buf_putmp(&bb, kxc->r);
326 ks_encrypt(kxc->ks, &bb, b);
329 /* --- Update the statistics --- */
333 st->sz_kxout += BLEN(b);
337 /* --- Schedule another resend --- */
339 if (kxc->f & KXF_TIMER)
340 sel_rmtimer(&kxc->t);
341 gettimeofday(&tv, 0);
342 tv.tv_sec += T_RETRY;
343 sel_addtimer(&sel, &kxc->t, &tv, kxc_timer, kxc);
347 /*----- Individual message handlers ---------------------------------------*/
349 /* --- @getreply@ --- *
351 * Arguments: @keyexch *kx@ = pointer to key exchange context
352 * @mp *c@ = a challenge
353 * @mp *ck@ = the supplied expected-reply check value
355 * Returns: A pointer to the reply, or null if the reply-hash was wrong.
357 * Use: Computes replies to challenges.
360 static mp *getreply(keyexch *kx, mp *c, mp *ck)
362 mp *r = mpmont_exp(&mg, MP_NEW, c, kpriv.x);
369 HASH_STRING(&h, "tripe-expected-reply");
375 a = mpcrypt(MP_NEW, ck, mp_octets(kpriv.dp.q), buf, sizeof(buf));
376 IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
377 trace(T_CRYPTO, "crypto: computed reply = %s", mpstr(r));
378 trace_block(T_CRYPTO, "crypto: computed reply hash", buf, HASHSZ);
379 trace(T_CRYPTO, "crypto: recovered log = %s", mpstr(a));
381 a = mpmont_exp(&mg, a, kpriv.dp.g, a);
384 a_warn("invalid expected-reply check from `%s'", p_name(kx->p));
385 IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
386 trace(T_CRYPTO, "crypto: computed challenge = %s", mpstr(a));
394 /* --- @dochallenge@ --- *
396 * Arguments: @keyexch *kx@ = pointer to key exchange block
397 * @unsigned msg@ = message code for the packet
398 * @buf *b@ = buffer containing the packet
400 * Returns: Zero if OK, nonzero if the packet was rejected.
402 * Use: Processes a packet containing a challenge.
405 static int dochallenge(keyexch *kx, unsigned msg, buf *b)
413 /* --- Ensure that we're in a sensible state --- */
415 if (kx->s != KXS_CHAL) {
416 a_warn("unexpected challenge from `%s'", p_name(kx->p));
420 /* --- Unpack the packet --- */
422 if ((c = buf_getmp(b)) == 0 ||
423 (msg >= KX_COOKIE && (hc = buf_get(b, HASHSZ)) == 0) ||
424 (msg >= KX_CHAL && (ck = buf_getmp(b)) == 0) ||
426 a_warn("malformed packet from `%s'", p_name(kx->p));
430 IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
431 trace(T_CRYPTO, "crypto: challenge = %s", mpstr(c));
432 if (hc) trace_block(T_CRYPTO, "crypto: cookie", hc, HASHSZ);
433 if (ck) trace(T_CRYPTO, "crypto: check value = %s", mpstr(ck));
436 /* --- First, handle a bare challenge --- *
438 * If the table is heavily loaded, just emit a cookie and return.
441 if (!hc && kx->nr >= KX_THRESH) {
442 T( trace(T_KEYEXCH, "keyexch: too many challenges -- sending cookie"); )
443 b = p_txstart(kx->p, MSG_KEYEXCH | KX_COOKIE);
446 HASH_STRING(&h, "tripe-cookie");
448 HASH_DONE(&h, buf_get(b, HASHSZ));
453 /* --- Discard a packet with an invalid cookie --- */
455 if (hc && memcmp(hc, kx->hc, HASHSZ) != 0) {
456 a_warn("incorrect cookie from `%s'", p_name(kx->p));
460 /* --- Find a challenge block for this packet --- *
462 * If there isn't one already, create a new one.
465 if ((kxc = kxc_bychal(kx, c)) == 0) {
469 /* --- Be careful here --- *
471 * If this is a full challenge, and it's the first time I've seen it, I
472 * want to be able to throw it away before committing a table entry to
479 if ((r = getreply(kx, c, ck)) == 0)
486 /* --- Work out the cookie for this challenge --- */
489 HASH_STRING(&h, "tripe-cookie");
491 HASH_DONE(&h, kxc->hc);
493 /* --- Compute the expected-reply hash --- */
496 HASH_STRING(&h, "tripe-expected-reply");
501 kxc->ck = mpcrypt(MP_NEW, kx->alpha, mp_octets(kpriv.dp.q),
504 /* --- Work out the shared key --- */
506 trace(T_CRYPTO, "debug: c = %s", mpstr(c));
507 trace(T_CRYPTO, "debug: alpha = %s", mpstr(kx->alpha));
508 r = mpmont_exp(&mg, MP_NEW, c, kx->alpha);
509 trace(T_CRYPTO, "debug: r = %s", mpstr(r));
511 /* --- Compute the switch messages --- */
513 HASH_INIT(&h); HASH_STRING(&h, "tripe-switch-request");
514 hashmp(&h, kx->c); hashmp(&h, kxc->c);
515 HASH_DONE(&h, kxc->hswrq_out);
516 HASH_INIT(&h); HASH_STRING(&h, "tripe-switch-confirm");
517 hashmp(&h, kx->c); hashmp(&h, kxc->c);
518 HASH_DONE(&h, kxc->hswok_out);
520 HASH_INIT(&h); HASH_STRING(&h, "tripe-switch-request");
521 hashmp(&h, kxc->c); hashmp(&h, kx->c);
522 HASH_DONE(&h, kxc->hswrq_in);
523 HASH_INIT(&h); HASH_STRING(&h, "tripe-switch-confirm");
524 hashmp(&h, kxc->c); hashmp(&h, kx->c);
525 HASH_DONE(&h, kxc->hswok_in);
527 IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
528 trace_block(T_CRYPTO, "crypto: computed cookie", kxc->hc, HASHSZ);
529 trace_block(T_CRYPTO, "crypto: expected-reply hash",
531 trace(T_CRYPTO, "crypto: my reply check = %s", mpstr(kxc->ck));
532 trace(T_CRYPTO, "crypto: shared secret = %s", mpstr(r));
533 trace_block(T_CRYPTO, "crypto: outbound switch request",
534 kxc->hswrq_out, HASHSZ);
535 trace_block(T_CRYPTO, "crypto: outbound switch confirm",
536 kxc->hswok_out, HASHSZ);
537 trace_block(T_CRYPTO, "crypto: inbound switch request",
538 kxc->hswrq_in, HASHSZ);
539 trace_block(T_CRYPTO, "crypto: inbound switch confirm",
540 kxc->hswok_in, HASHSZ);
543 /* --- Create a new symmetric keyset --- */
545 buf_init(b, buf_o, sizeof(buf_o));
546 buf_putmp(b, kx->c); x = BLEN(b);
547 buf_putmp(b, kxc->c); y = BLEN(b);
548 buf_putmp(b, r); z = BLEN(b);
551 kxc->ks = ks_gen(BBASE(b), x, y, z, kx->p);
555 /* --- Answer the challenge if we need to --- */
559 if ((r = getreply(kx, c, ck)) == 0)
566 /* --- Tidy up and go home --- */
579 /* --- @resend@ --- *
581 * Arguments: @keyexch *kx@ = pointer to key exchange context
585 * Use: Sends the next message for a key exchange.
588 static void resend(keyexch *kx)
592 stats *st = p_stats(kx->p);
597 T( trace(T_KEYEXCH, "keyexch: sending prechallenge to `%s'",
599 b = p_txstart(kx->p, MSG_KEYEXCH | KX_PRECHAL);
603 T( trace(T_KEYEXCH, "keyexch: sending switch request to `%s'",
606 b = p_txstart(kx->p, MSG_KEYEXCH | KX_SWITCH);
607 buf_put(b, kx->hc, HASHSZ);
608 buf_put(b, kxc->hc, HASHSZ);
609 buf_init(&bb, buf_i, sizeof(buf_i));
610 buf_putmp(&bb, kxc->r);
611 buf_put(&bb, kxc->hswrq_out, HASHSZ);
613 ks_encrypt(kxc->ks, &bb, b);
616 T( trace(T_KEYEXCH, "keyexch: sending switch confirmation to `%s'",
619 b = p_txstart(kx->p, MSG_KEYEXCH | KX_SWITCHOK);
620 buf_init(&bb, buf_i, sizeof(buf_i));
621 buf_put(&bb, kxc->hswok_out, HASHSZ);
623 ks_encrypt(kxc->ks, &bb, b);
631 st->sz_kxout += BLEN(b);
635 if (kx->s < KXS_SWITCH)
636 settimer(kx, time(0) + T_RETRY);
639 /* --- @matchreply@ --- *
641 * Arguments: @keyexch *kx@ = pointer to key exchange context
642 * @const octet *hc_in@ = a hash of his challenge
643 * @const octet *hc_out@ = a hash of my challenge (cookie)
644 * @mp *ck@ = his expected-reply hash (optional)
645 * @buf *b@ = encrypted remainder of the packet
647 * Returns: A pointer to the challenge block if OK, or null on failure.
649 * Use: Checks a reply or switch packet, ensuring that its contents
650 * are sensible and correct. If they are, @*b@ is set to point
651 * to the remainder of the encrypted data, and the correct
652 * challenge is returned.
655 static kxchal *matchreply(keyexch *kx, const octet *hc_in,
656 const octet *hc_out, mp *ck, buf *b)
662 /* --- Check the plaintext portions of the data --- */
664 IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
665 trace_block(T_CRYPTO, "crypto: challenge", hc_in, HASHSZ);
666 trace_block(T_CRYPTO, "crypto: cookie", hc_out, HASHSZ);
667 if (ck) trace(T_CRYPTO, "crypto: check value = %s", mpstr(ck));
669 if (memcmp(hc_out, kx->hc, HASHSZ) != 0) {
670 a_warn("incorrect cookie from `%s'", p_name(kx->p));
673 if ((kxc = kxc_byhc(kx, hc_in)) == 0) {
674 a_warn("received reply for unknown challenge from `%s'", p_name(kx->p));
678 /* --- Maybe compute a reply for the challenge --- */
682 a_warn("unexpected switch request from `%s'", p_name(kx->p));
685 if ((r = getreply(kx, kxc->c, ck)) == 0)
691 /* --- Decrypt the rest of the packet --- */
693 buf_init(&bb, buf_o, sizeof(buf_o));
694 if (ks_decrypt(kxc->ks, b, &bb)) {
695 a_warn("failed to decrypt reply from `%s'", p_name(kx->p));
698 buf_init(b, BBASE(&bb), BLEN(&bb));
699 if ((r = buf_getmp(b)) == 0) {
700 a_warn("invalid reply packet from `%s'", p_name(kx->p));
703 IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
704 trace(T_CRYPTO, "crypto: reply = %s", mpstr(r));
706 if (!mp_eq(r, kx->rx)) {
707 a_warn("incorrect reply from `%s'", p_name(kx->p));
721 /* --- @commit@ --- *
723 * Arguments: @keyexch *kx@ = pointer to key exchange context
724 * @kxchal *kxc@ = pointer to challenge to commit to
728 * Use: Commits to a particular challenge as being the `right' one,
729 * since a reply has arrived for it.
732 static void commit(keyexch *kx, kxchal *kxc)
736 for (i = 0; i < kx->nr; i++) {
738 kxc_destroy(kx->r[i]);
743 ksl_link(kx->ks, kxc->ks);
746 /* --- @doreply@ --- *
748 * Arguments: @keyexch *kx@ = pointer to key exchange context
749 * @buf *b@ = buffer containing packet
751 * Returns: Zero if OK, nonzero if the packet was rejected.
753 * Use: Handles a reply packet. This doesn't handle the various
754 * switch packets: they're rather too different.
757 static int doreply(keyexch *kx, buf *b)
759 const octet *hc_in, *hc_out;
763 if (kx->s != KXS_CHAL && kx->s != KXS_COMMIT) {
764 a_warn("unexpected reply from `%s'", p_name(kx->p));
767 if ((hc_in = buf_get(b, HASHSZ)) == 0 ||
768 (hc_out = buf_get(b, HASHSZ)) == 0 ||
769 (ck = buf_getmp(b)) == 0) {
770 a_warn("invalid reply packet from `%s'", p_name(kx->p));
773 if ((kxc = matchreply(kx, hc_in, hc_out, ck, b)) == 0)
776 a_warn("invalid reply packet from `%s'", p_name(kx->p));
779 if (kx->s == KXS_CHAL) {
791 /* --- @doswitch@ --- *
793 * Arguments: @keyexch *kx@ = pointer to key exchange block
794 * @buf *b@ = pointer to buffer containing packet
796 * Returns: Zero if OK, nonzero if the packet was rejected.
798 * Use: Handles a reply with a switch request bolted onto it.
801 static int doswitch(keyexch *kx, buf *b)
803 const octet *hc_in, *hc_out, *hswrq;
806 if ((hc_in = buf_get(b, HASHSZ)) == 0 ||
807 (hc_out = buf_get(b, HASHSZ)) == 0) {
808 a_warn("invalid switch request from `%s'", p_name(kx->p));
811 if ((kxc = matchreply(kx, hc_in, hc_out, 0, b)) == 0)
813 if ((hswrq = buf_get(b, HASHSZ)) == 0 || BLEFT(b)) {
814 a_warn("invalid switch request from `%s'", p_name(kx->p));
817 IF_TRACING(T_KEYEXCH, {
818 trace_block(T_CRYPTO, "crypto: switch request hash", hswrq, HASHSZ);
820 if (memcmp(hswrq, kxc->hswrq_in, HASHSZ) != 0) {
821 a_warn("incorrect switch request hash from `%s'", p_name(kx->p));
828 ks_activate(kxc->ks);
829 settimer(kx, ks_tregen(kxc->ks));
840 /* --- @doswitchok@ --- *
842 * Arguments: @keyexch *kx@ = pointer to key exchange block
843 * @buf *b@ = pointer to buffer containing packet
845 * Returns: Zero if OK, nonzero if the packet was rejected.
847 * Use: Handles a reply with a switch request bolted onto it.
850 static int doswitchok(keyexch *kx, buf *b)
856 if (kx->s < KXS_COMMIT) {
857 a_warn("unexpected switch confirmation from `%s'", p_name(kx->p));
861 buf_init(&bb, buf_o, sizeof(buf_o));
862 if (ks_decrypt(kxc->ks, b, &bb)) {
863 a_warn("failed to decrypt switch confirmation from `%s'", p_name(kx->p));
866 buf_init(b, BBASE(&bb), BLEN(&bb));
867 if ((hswok = buf_get(b, HASHSZ)) == 0 || BLEFT(b)) {
868 a_warn("invalid switch confirmation from `%s'", p_name(kx->p));
871 IF_TRACING(T_KEYEXCH, {
872 trace_block(T_CRYPTO, "crypto: switch confirmation hash", hswok, HASHSZ);
874 if (memcmp(hswok, kxc->hswok_in, HASHSZ) != 0) {
875 a_warn("incorrect switch confirmation hash from `%s'", p_name(kx->p));
878 if (kx->s < KXS_SWITCH) {
879 ks_activate(kxc->ks);
880 settimer(kx, ks_tregen(kxc->ks));
889 /*----- Main code ---------------------------------------------------------*/
893 * Arguments: @keyexch *kx@ = pointer to key exchange context
897 * Use: Stops a key exchange dead in its tracks. Throws away all of
898 * the context information. The context is left in an
899 * inconsistent state. The only functions which understand this
900 * state are @kx_free@ and @kx_init@ (which cause it internally
901 * it), and @start@ (which expects it to be the prevailing
905 static void stop(keyexch *kx)
909 if (kx->f & KXF_DEAD)
912 if (kx->f & KXF_TIMER)
914 for (i = 0; i < kx->nr; i++)
915 kxc_destroy(kx->r[i]);
926 * Arguments: @keyexch *kx@ = pointer to key exchange context
927 * @time_t now@ = the current time
931 * Use: Starts a new key exchange with the peer. The context must be
932 * in the bizarre state left by @stop@ or @kx_init@.
935 static void start(keyexch *kx, time_t now)
939 assert(kx->f & KXF_DEAD);
943 kx->alpha = mprand_range(MP_NEW, kpriv.dp.q, &rand_global, 0);
944 kx->c = mpmont_exp(&mg, MP_NEW, kpriv.dp.g, kx->alpha);
945 kx->rx = mpmont_exp(&mg, MP_NEW, kx->kpub.y, kx->alpha);
947 kx->t_valid = now + T_VALID;
950 HASH_STRING(&h, "tripe-cookie");
952 HASH_DONE(&h, kx->hc);
954 IF_TRACING(T_KEYEXCH, {
955 trace(T_KEYEXCH, "keyexch: creating new challenge");
956 IF_TRACING(T_CRYPTO, {
957 trace(T_CRYPTO, "crypto: secret = %s", mpstr(kx->alpha));
958 trace(T_CRYPTO, "crypto: challenge = %s", mpstr(kx->c));
959 trace(T_CRYPTO, "crypto: expected response = %s", mpstr(kx->rx));
960 trace_block(T_CRYPTO, "crypto: challenge cookie", kx->hc, HASHSZ);
965 /* --- @checkpub@ --- *
967 * Arguments: @keyexch *kx@ = pointer to key exchange context
969 * Returns: Zero if OK, nonzero if the peer's public key has expired.
971 * Use: Deactivates the key-exchange until the peer acquires a new
975 static int checkpub(keyexch *kx)
978 if (kx->f & KXF_DEAD)
981 if (KEY_EXPIRED(now, kx->texp_kpub)) {
983 a_warn("public key for `%s' has expired", p_name(kx->p));
984 dh_pubfree(&kx->kpub);
985 kx->f &= ~KXF_PUBKEY;
991 /* --- @kx_start@ --- *
993 * Arguments: @keyexch *kx@ = pointer to key exchange context
997 * Use: Stimulates a key exchange. If a key exchage is in progress,
998 * a new challenge is sent (unless the quiet timer forbids
999 * this); if no exchange is in progress, one is commenced.
1002 void kx_start(keyexch *kx)
1004 time_t now = time(0);
1008 if (!ISVALID(kx, now)) {
1015 /* --- @kx_message@ --- *
1017 * Arguments: @keyexch *kx@ = pointer to key exchange context
1018 * @unsigned msg@ = the message code
1019 * @buf *b@ = pointer to buffer containing the packet
1023 * Use: Reads a packet containing key exchange messages and handles
1027 void kx_message(keyexch *kx, unsigned msg, buf *b)
1029 time_t now = time(0);
1030 stats *st = p_stats(kx->p);
1035 static const char *const pkname[] = {
1036 "prechallenge", "cookie", "challenge",
1037 "reply", "switch request", "switch confirmation"
1044 if (!ISVALID(kx, now)) {
1049 T( trace(T_KEYEXCH, "keyexch: processing %s packet from `%s'",
1050 msg < KX_NMSG ? pkname[msg] : "unknown", p_name(kx->p)); )
1056 rc = dochallenge(kx, msg, b);
1059 rc = doreply(kx, b);
1062 rc = doswitch(kx, b);
1065 rc = doswitchok(kx, b);
1068 a_warn("unexpected key exchange message type %u from `%p'",
1082 /* --- @kx_free@ --- *
1084 * Arguments: @keyexch *kx@ = pointer to key exchange context
1088 * Use: Frees everything in a key exchange context.
1091 void kx_free(keyexch *kx)
1094 if (kx->f & KXF_PUBKEY)
1095 dh_pubfree(&kx->kpub);
1098 /* --- @kx_newkeys@ --- *
1100 * Arguments: @keyexch *kx@ = pointer to key exchange context
1104 * Use: Informs the key exchange module that its keys may have
1105 * changed. If fetching the new keys fails, the peer will be
1106 * destroyed, we log messages and struggle along with the old
1110 void kx_newkeys(keyexch *kx)
1114 if (km_getpubkey(p_name(kx->p), &dp, &kx->texp_kpub))
1116 if (kx->f & KXF_PUBKEY)
1117 dh_pubfree(&kx->kpub);
1119 kx->f |= KXF_PUBKEY;
1120 if ((kx->f & KXF_DEAD) || kx->s != KXS_SWITCH) {
1121 T( trace(T_KEYEXCH, "keyexch: restarting key negotiation with `%s'",
1129 /* --- @kx_init@ --- *
1131 * Arguments: @keyexch *kx@ = pointer to key exchange context
1132 * @peer *p@ = pointer to peer context
1133 * @keyset **ks@ = pointer to keyset list
1135 * Returns: Zero if OK, nonzero if it failed.
1137 * Use: Initializes a key exchange module. The module currently
1138 * contains no keys, and will attempt to initiate a key
1142 int kx_init(keyexch *kx, peer *p, keyset **ks)
1146 if (km_getpubkey(p_name(p), &kx->kpub, &kx->texp_kpub))
1148 kx->f = KXF_DEAD | KXF_PUBKEY;
1154 /*----- That's all, folks -------------------------------------------------*/