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 -------------------------------------------------*/