blob: fff44335c8ba9f7a4a37a14c7079a35ea095c2a6 [file] [log] [blame]
Damien Miller1f0311c2014-05-15 14:24:09 +10001/* $OpenBSD: kex.c,v 1.99 2014/04/29 18:01:49 markus Exp $ */
Damien Millera664e872000-04-16 11:52:47 +10002/*
Ben Lindstrom44697232001-07-04 03:32:30 +00003 * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
Damien Millera664e872000-04-16 11:52:47 +10004 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
Damien Millera664e872000-04-16 11:52:47 +100013 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "includes.h"
Damien Millera664e872000-04-16 11:52:47 +100027
Damien Miller8dbffe72006-08-05 11:02:17 +100028#include <sys/param.h>
29
Damien Millerd7834352006-08-05 12:39:39 +100030#include <signal.h>
Damien Millerded319c2006-09-01 15:38:36 +100031#include <stdarg.h>
Damien Millera7a73ee2006-08-05 11:37:59 +100032#include <stdio.h>
Damien Millere7a1e5c2006-08-05 11:34:19 +100033#include <stdlib.h>
Damien Millere3476ed2006-07-24 14:13:33 +100034#include <string.h>
35
Damien Miller1f0311c2014-05-15 14:24:09 +100036#ifdef WITH_OPENSSL
Damien Millerd7834352006-08-05 12:39:39 +100037#include <openssl/crypto.h>
Damien Miller1f0311c2014-05-15 14:24:09 +100038#endif
Damien Millerd7834352006-08-05 12:39:39 +100039
Ben Lindstrom226cfa02001-01-22 05:34:40 +000040#include "xmalloc.h"
Damien Millerd7834352006-08-05 12:39:39 +100041#include "ssh2.h"
Ben Lindstrom226cfa02001-01-22 05:34:40 +000042#include "buffer.h"
Ben Lindstrom226cfa02001-01-22 05:34:40 +000043#include "packet.h"
44#include "compat.h"
45#include "cipher.h"
Damien Miller0bc1bd82000-11-13 22:57:25 +110046#include "key.h"
Damien Millerd7834352006-08-05 12:39:39 +100047#include "kex.h"
Ben Lindstrom226cfa02001-01-22 05:34:40 +000048#include "log.h"
Ben Lindstrom06b33aa2001-02-15 03:01:59 +000049#include "mac.h"
Ben Lindstromb9be60a2001-03-11 01:49:19 +000050#include "match.h"
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +000051#include "dispatch.h"
Ben Lindstrom7a2073c2002-03-22 02:30:41 +000052#include "monitor.h"
Darren Tucker36331b52010-01-08 16:50:41 +110053#include "roaming.h"
Damien Millerb3051d02014-01-10 10:58:53 +110054#include "digest.h"
Damien Millera664e872000-04-16 11:52:47 +100055
Damien Millerb3092032006-03-16 18:22:18 +110056#if OPENSSL_VERSION_NUMBER >= 0x00907000L
57# if defined(HAVE_EVP_SHA256)
Damien Milleraf87af12006-03-15 13:02:28 +110058# define evp_ssh_sha256 EVP_sha256
Damien Millerb3092032006-03-16 18:22:18 +110059# else
Damien Millera63128d2006-03-15 12:08:28 +110060extern const EVP_MD *evp_ssh_sha256(void);
Damien Millerb3092032006-03-16 18:22:18 +110061# endif
Tim Rice425a6882006-03-15 20:17:05 -080062#endif
Damien Millera63128d2006-03-15 12:08:28 +110063
Ben Lindstrombba81212001-06-25 05:01:22 +000064/* prototype */
65static void kex_kexinit_finish(Kex *);
66static void kex_choose_conf(Kex *);
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +000067
Damien Millerea111192013-04-23 19:24:32 +100068struct kexalg {
69 char *name;
70 int type;
71 int ec_nid;
Damien Millerb3051d02014-01-10 10:58:53 +110072 int hash_alg;
Damien Millerea111192013-04-23 19:24:32 +100073};
74static const struct kexalg kexalgs[] = {
Damien Miller1f0311c2014-05-15 14:24:09 +100075#ifdef WITH_OPENSSL
Damien Millerb3051d02014-01-10 10:58:53 +110076 { KEX_DH1, KEX_DH_GRP1_SHA1, 0, SSH_DIGEST_SHA1 },
77 { KEX_DH14, KEX_DH_GRP14_SHA1, 0, SSH_DIGEST_SHA1 },
78 { KEX_DHGEX_SHA1, KEX_DH_GEX_SHA1, 0, SSH_DIGEST_SHA1 },
Darren Tuckera75d2472013-05-10 18:11:55 +100079#ifdef HAVE_EVP_SHA256
Damien Millerb3051d02014-01-10 10:58:53 +110080 { KEX_DHGEX_SHA256, KEX_DH_GEX_SHA256, 0, SSH_DIGEST_SHA256 },
Damien Miller1f0311c2014-05-15 14:24:09 +100081#endif /* HAVE_EVP_SHA256 */
Darren Tuckera75d2472013-05-10 18:11:55 +100082#ifdef OPENSSL_HAS_ECC
Damien Millerb3051d02014-01-10 10:58:53 +110083 { KEX_ECDH_SHA2_NISTP256, KEX_ECDH_SHA2,
84 NID_X9_62_prime256v1, SSH_DIGEST_SHA256 },
85 { KEX_ECDH_SHA2_NISTP384, KEX_ECDH_SHA2, NID_secp384r1,
86 SSH_DIGEST_SHA384 },
Darren Tucker37bcef52013-11-09 18:39:25 +110087# ifdef OPENSSL_HAS_NISTP521
Damien Millerb3051d02014-01-10 10:58:53 +110088 { KEX_ECDH_SHA2_NISTP521, KEX_ECDH_SHA2, NID_secp521r1,
89 SSH_DIGEST_SHA512 },
Damien Miller1f0311c2014-05-15 14:24:09 +100090# endif /* OPENSSL_HAS_NISTP521 */
91#endif /* OPENSSL_HAS_ECC */
Damien Miller1f0311c2014-05-15 14:24:09 +100092#endif /* WITH_OPENSSL */
Darren Tucker1c8ce342013-11-08 19:50:32 +110093#ifdef HAVE_EVP_SHA256
Damien Millerb3051d02014-01-10 10:58:53 +110094 { KEX_CURVE25519_SHA256, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 },
Damien Miller1f0311c2014-05-15 14:24:09 +100095#endif /* HAVE_EVP_SHA256 */
Damien Millerb3051d02014-01-10 10:58:53 +110096 { NULL, -1, -1, -1},
Damien Millerea111192013-04-23 19:24:32 +100097};
98
99char *
Damien Miller690d9892013-11-08 12:16:49 +1100100kex_alg_list(char sep)
Damien Millerea111192013-04-23 19:24:32 +1000101{
102 char *ret = NULL;
103 size_t nlen, rlen = 0;
104 const struct kexalg *k;
105
106 for (k = kexalgs; k->name != NULL; k++) {
107 if (ret != NULL)
Damien Miller690d9892013-11-08 12:16:49 +1100108 ret[rlen++] = sep;
Damien Millerea111192013-04-23 19:24:32 +1000109 nlen = strlen(k->name);
110 ret = xrealloc(ret, 1, rlen + nlen + 2);
111 memcpy(ret + rlen, k->name, nlen + 1);
112 rlen += nlen;
113 }
114 return ret;
115}
116
117static const struct kexalg *
118kex_alg_by_name(const char *name)
119{
120 const struct kexalg *k;
121
122 for (k = kexalgs; k->name != NULL; k++) {
123 if (strcmp(k->name, name) == 0)
124 return k;
125 }
126 return NULL;
127}
128
Damien Millerd5f62bf2010-09-24 22:11:14 +1000129/* Validate KEX method name list */
130int
131kex_names_valid(const char *names)
132{
133 char *s, *cp, *p;
134
135 if (names == NULL || strcmp(names, "") == 0)
136 return 0;
137 s = cp = xstrdup(names);
138 for ((p = strsep(&cp, ",")); p && *p != '\0';
139 (p = strsep(&cp, ","))) {
Damien Millerea111192013-04-23 19:24:32 +1000140 if (kex_alg_by_name(p) == NULL) {
Damien Millerd5f62bf2010-09-24 22:11:14 +1000141 error("Unsupported KEX algorithm \"%.100s\"", p);
Darren Tuckera627d422013-06-02 07:31:17 +1000142 free(s);
Damien Millerd5f62bf2010-09-24 22:11:14 +1000143 return 0;
144 }
145 }
146 debug3("kex names ok: [%s]", names);
Darren Tuckera627d422013-06-02 07:31:17 +1000147 free(s);
Damien Millerd5f62bf2010-09-24 22:11:14 +1000148 return 1;
149}
150
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000151/* put algorithm proposal into buffer */
Ben Lindstrombba81212001-06-25 05:01:22 +0000152static void
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000153kex_prop2buf(Buffer *b, char *proposal[PROPOSAL_MAX])
Damien Millera664e872000-04-16 11:52:47 +1000154{
Damien Millereccb9de2005-06-17 12:59:34 +1000155 u_int i;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000156
157 buffer_clear(b);
Ben Lindstrom59971722002-03-27 17:42:57 +0000158 /*
159 * add a dummy cookie, the cookie will be overwritten by
160 * kex_send_kexinit(), each time a kexinit is set
161 */
162 for (i = 0; i < KEX_COOKIE_LEN; i++)
163 buffer_put_char(b, 0);
Damien Millera664e872000-04-16 11:52:47 +1000164 for (i = 0; i < PROPOSAL_MAX; i++)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000165 buffer_put_cstring(b, proposal[i]);
166 buffer_put_char(b, 0); /* first_kex_packet_follows */
167 buffer_put_int(b, 0); /* uint32 reserved */
Damien Millera664e872000-04-16 11:52:47 +1000168}
169
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000170/* parse buffer and return algorithm proposal */
Ben Lindstrombba81212001-06-25 05:01:22 +0000171static char **
Damien Millerbabb47a2003-02-24 11:53:32 +1100172kex_buf2prop(Buffer *raw, int *first_kex_follows)
Damien Millerb1715dc2000-05-30 13:44:51 +1000173{
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000174 Buffer b;
Darren Tucker0d0d1952007-06-05 18:23:28 +1000175 u_int i;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000176 char **proposal;
Damien Millerb1715dc2000-05-30 13:44:51 +1000177
Damien Miller07d86be2006-03-26 14:19:21 +1100178 proposal = xcalloc(PROPOSAL_MAX, sizeof(char *));
Damien Millerb1715dc2000-05-30 13:44:51 +1000179
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000180 buffer_init(&b);
181 buffer_append(&b, buffer_ptr(raw), buffer_len(raw));
Damien Millerb1715dc2000-05-30 13:44:51 +1000182 /* skip cookie */
183 for (i = 0; i < KEX_COOKIE_LEN; i++)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000184 buffer_get_char(&b);
Damien Millerb1715dc2000-05-30 13:44:51 +1000185 /* extract kex init proposal strings */
186 for (i = 0; i < PROPOSAL_MAX; i++) {
Damien Millerda108ec2010-08-31 22:36:39 +1000187 proposal[i] = buffer_get_cstring(&b,NULL);
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000188 debug2("kex_parse_kexinit: %s", proposal[i]);
Damien Millerb1715dc2000-05-30 13:44:51 +1000189 }
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000190 /* first kex follows / reserved */
191 i = buffer_get_char(&b);
Damien Millerbabb47a2003-02-24 11:53:32 +1100192 if (first_kex_follows != NULL)
193 *first_kex_follows = i;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000194 debug2("kex_parse_kexinit: first_kex_follows %d ", i);
195 i = buffer_get_int(&b);
Darren Tucker0d0d1952007-06-05 18:23:28 +1000196 debug2("kex_parse_kexinit: reserved %u ", i);
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000197 buffer_free(&b);
198 return proposal;
Damien Millerb1715dc2000-05-30 13:44:51 +1000199}
200
Ben Lindstrombba81212001-06-25 05:01:22 +0000201static void
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000202kex_prop_free(char **proposal)
Damien Millera664e872000-04-16 11:52:47 +1000203{
Damien Millereccb9de2005-06-17 12:59:34 +1000204 u_int i;
Damien Millera664e872000-04-16 11:52:47 +1000205
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000206 for (i = 0; i < PROPOSAL_MAX; i++)
Darren Tuckera627d422013-06-02 07:31:17 +1000207 free(proposal[i]);
208 free(proposal);
Damien Millera664e872000-04-16 11:52:47 +1000209}
210
Darren Tucker0d0d1952007-06-05 18:23:28 +1000211/* ARGSUSED */
Ben Lindstrombba81212001-06-25 05:01:22 +0000212static void
Damien Miller630d6f42002-01-22 23:17:30 +1100213kex_protocol_error(int type, u_int32_t seq, void *ctxt)
Damien Miller874d77b2000-10-14 16:23:11 +1100214{
Damien Miller630d6f42002-01-22 23:17:30 +1100215 error("Hm, kex protocol error: type %d seq %u", type, seq);
Damien Miller874d77b2000-10-14 16:23:11 +1100216}
217
Ben Lindstrombba81212001-06-25 05:01:22 +0000218static void
Damien Millerbc27d4e2002-02-13 13:54:06 +1100219kex_reset_dispatch(void)
Ben Lindstrom8ac91062001-04-04 17:57:54 +0000220{
Damien Miller7d053392002-01-22 23:24:13 +1100221 dispatch_range(SSH2_MSG_TRANSPORT_MIN,
222 SSH2_MSG_TRANSPORT_MAX, &kex_protocol_error);
Damien Millerbc27d4e2002-02-13 13:54:06 +1100223 dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit);
Ben Lindstrom8ac91062001-04-04 17:57:54 +0000224}
225
226void
Ben Lindstrom238abf62001-04-04 17:52:53 +0000227kex_finish(Kex *kex)
Damien Millera664e872000-04-16 11:52:47 +1000228{
Damien Millerbc27d4e2002-02-13 13:54:06 +1100229 kex_reset_dispatch();
Ben Lindstrom238abf62001-04-04 17:52:53 +0000230
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000231 packet_start(SSH2_MSG_NEWKEYS);
232 packet_send();
233 /* packet_write_wait(); */
234 debug("SSH2_MSG_NEWKEYS sent");
Damien Millera664e872000-04-16 11:52:47 +1000235
Ben Lindstrom064496f2002-12-23 02:04:22 +0000236 debug("expecting SSH2_MSG_NEWKEYS");
Damien Millerdff50992002-01-22 23:16:32 +1100237 packet_read_expect(SSH2_MSG_NEWKEYS);
Ben Lindstrom14519082002-02-26 17:58:29 +0000238 packet_check_eom();
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000239 debug("SSH2_MSG_NEWKEYS received");
Ben Lindstrom5ba23b32001-04-05 02:05:21 +0000240
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000241 kex->done = 1;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000242 buffer_clear(&kex->peer);
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000243 /* buffer_clear(&kex->my); */
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000244 kex->flags &= ~KEX_INIT_SENT;
Darren Tuckera627d422013-06-02 07:31:17 +1000245 free(kex->name);
Ben Lindstrom5ba23b32001-04-05 02:05:21 +0000246 kex->name = NULL;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000247}
Damien Millera664e872000-04-16 11:52:47 +1000248
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000249void
250kex_send_kexinit(Kex *kex)
251{
Darren Tucker3f9fdc72004-06-22 12:56:01 +1000252 u_int32_t rnd = 0;
Ben Lindstrom59971722002-03-27 17:42:57 +0000253 u_char *cookie;
Damien Millereccb9de2005-06-17 12:59:34 +1000254 u_int i;
Ben Lindstrom59971722002-03-27 17:42:57 +0000255
Ben Lindstrom8ac91062001-04-04 17:57:54 +0000256 if (kex == NULL) {
257 error("kex_send_kexinit: no kex, cannot rekey");
258 return;
259 }
Ben Lindstrom238abf62001-04-04 17:52:53 +0000260 if (kex->flags & KEX_INIT_SENT) {
261 debug("KEX_INIT_SENT");
262 return;
263 }
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000264 kex->done = 0;
Ben Lindstrom59971722002-03-27 17:42:57 +0000265
266 /* generate a random cookie */
267 if (buffer_len(&kex->my) < KEX_COOKIE_LEN)
268 fatal("kex_send_kexinit: kex proposal too short");
269 cookie = buffer_ptr(&kex->my);
270 for (i = 0; i < KEX_COOKIE_LEN; i++) {
271 if (i % 4 == 0)
Darren Tucker3f9fdc72004-06-22 12:56:01 +1000272 rnd = arc4random();
273 cookie[i] = rnd;
274 rnd >>= 8;
Ben Lindstrom59971722002-03-27 17:42:57 +0000275 }
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000276 packet_start(SSH2_MSG_KEXINIT);
277 packet_put_raw(buffer_ptr(&kex->my), buffer_len(&kex->my));
278 packet_send();
279 debug("SSH2_MSG_KEXINIT sent");
280 kex->flags |= KEX_INIT_SENT;
281}
282
Darren Tucker0d0d1952007-06-05 18:23:28 +1000283/* ARGSUSED */
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000284void
Damien Miller630d6f42002-01-22 23:17:30 +1100285kex_input_kexinit(int type, u_int32_t seq, void *ctxt)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000286{
287 char *ptr;
Damien Millereccb9de2005-06-17 12:59:34 +1000288 u_int i, dlen;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000289 Kex *kex = (Kex *)ctxt;
290
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000291 debug("SSH2_MSG_KEXINIT received");
Ben Lindstrom8ac91062001-04-04 17:57:54 +0000292 if (kex == NULL)
293 fatal("kex_input_kexinit: no kex, cannot rekey");
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000294
295 ptr = packet_get_raw(&dlen);
296 buffer_append(&kex->peer, ptr, dlen);
297
Ben Lindstrom8e312f32001-04-04 23:50:21 +0000298 /* discard packet */
299 for (i = 0; i < KEX_COOKIE_LEN; i++)
300 packet_get_char();
301 for (i = 0; i < PROPOSAL_MAX; i++)
Darren Tuckera627d422013-06-02 07:31:17 +1000302 free(packet_get_string(NULL));
Darren Tuckerae608bd2012-09-06 21:19:51 +1000303 /*
304 * XXX RFC4253 sec 7: "each side MAY guess" - currently no supported
305 * KEX method has the server move first, but a server might be using
306 * a custom method or one that we otherwise don't support. We should
307 * be prepared to remember first_kex_follows here so we can eat a
308 * packet later.
309 * XXX2 - RFC4253 is kind of ambiguous on what first_kex_follows means
310 * for cases where the server *doesn't* go first. I guess we should
311 * ignore it when it is set for these cases, which is what we do now.
312 */
313 (void) packet_get_char(); /* first_kex_follows */
314 (void) packet_get_int(); /* reserved */
Damien Miller48b03fc2002-01-22 23:11:40 +1100315 packet_check_eom();
Ben Lindstrom8e312f32001-04-04 23:50:21 +0000316
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000317 kex_kexinit_finish(kex);
318}
319
320Kex *
Ben Lindstrom238abf62001-04-04 17:52:53 +0000321kex_setup(char *proposal[PROPOSAL_MAX])
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000322{
323 Kex *kex;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000324
Damien Miller07d86be2006-03-26 14:19:21 +1100325 kex = xcalloc(1, sizeof(*kex));
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000326 buffer_init(&kex->peer);
327 buffer_init(&kex->my);
328 kex_prop2buf(&kex->my, proposal);
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000329 kex->done = 0;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000330
331 kex_send_kexinit(kex); /* we start */
Damien Millerbc27d4e2002-02-13 13:54:06 +1100332 kex_reset_dispatch();
Ben Lindstrom8ac91062001-04-04 17:57:54 +0000333
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000334 return kex;
335}
336
Ben Lindstrombba81212001-06-25 05:01:22 +0000337static void
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000338kex_kexinit_finish(Kex *kex)
339{
340 if (!(kex->flags & KEX_INIT_SENT))
341 kex_send_kexinit(kex);
342
343 kex_choose_conf(kex);
344
Damien Miller8e7fb332003-02-24 12:03:03 +1100345 if (kex->kex_type >= 0 && kex->kex_type < KEX_MAX &&
346 kex->kex[kex->kex_type] != NULL) {
347 (kex->kex[kex->kex_type])(kex);
348 } else {
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000349 fatal("Unsupported key exchange %d", kex->kex_type);
Damien Millera664e872000-04-16 11:52:47 +1000350 }
Damien Millera664e872000-04-16 11:52:47 +1000351}
352
Ben Lindstrombba81212001-06-25 05:01:22 +0000353static void
Damien Millera664e872000-04-16 11:52:47 +1000354choose_enc(Enc *enc, char *client, char *server)
355{
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000356 char *name = match_list(client, server, NULL);
Damien Millera664e872000-04-16 11:52:47 +1000357 if (name == NULL)
Darren Tucker0d0d1952007-06-05 18:23:28 +1000358 fatal("no matching cipher found: client %s server %s",
359 client, server);
Damien Miller963f6b22002-02-19 15:21:23 +1100360 if ((enc->cipher = cipher_by_name(name)) == NULL)
Damien Miller874d77b2000-10-14 16:23:11 +1100361 fatal("matching cipher is not supported: %s", name);
Damien Millera664e872000-04-16 11:52:47 +1000362 enc->name = name;
363 enc->enabled = 0;
364 enc->iv = NULL;
Damien Miller1d75abf2013-01-09 16:12:19 +1100365 enc->iv_len = cipher_ivlen(enc->cipher);
Damien Millera664e872000-04-16 11:52:47 +1000366 enc->key = NULL;
Damien Miller963f6b22002-02-19 15:21:23 +1100367 enc->key_len = cipher_keylen(enc->cipher);
368 enc->block_size = cipher_blocksize(enc->cipher);
Damien Millera664e872000-04-16 11:52:47 +1000369}
Damien Miller4f7becb2006-03-26 14:10:14 +1100370
Ben Lindstrombba81212001-06-25 05:01:22 +0000371static void
Damien Millera664e872000-04-16 11:52:47 +1000372choose_mac(Mac *mac, char *client, char *server)
373{
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000374 char *name = match_list(client, server, NULL);
Damien Millera664e872000-04-16 11:52:47 +1000375 if (name == NULL)
Darren Tucker0d0d1952007-06-05 18:23:28 +1000376 fatal("no matching mac found: client %s server %s",
377 client, server);
Darren Tucker5f3d5be2007-06-05 18:30:18 +1000378 if (mac_setup(mac, name) < 0)
Damien Millera664e872000-04-16 11:52:47 +1000379 fatal("unsupported mac %s", name);
Ben Lindstrom06b33aa2001-02-15 03:01:59 +0000380 /* truncate the key */
381 if (datafellows & SSH_BUG_HMAC)
382 mac->key_len = 16;
Damien Millera664e872000-04-16 11:52:47 +1000383 mac->name = name;
Damien Millera664e872000-04-16 11:52:47 +1000384 mac->key = NULL;
385 mac->enabled = 0;
386}
Damien Miller4f7becb2006-03-26 14:10:14 +1100387
Ben Lindstrombba81212001-06-25 05:01:22 +0000388static void
Damien Millera664e872000-04-16 11:52:47 +1000389choose_comp(Comp *comp, char *client, char *server)
390{
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000391 char *name = match_list(client, server, NULL);
Damien Millera664e872000-04-16 11:52:47 +1000392 if (name == NULL)
393 fatal("no matching comp found: client %s server %s", client, server);
Damien Miller9786e6e2005-07-26 21:54:56 +1000394 if (strcmp(name, "zlib@openssh.com") == 0) {
395 comp->type = COMP_DELAYED;
396 } else if (strcmp(name, "zlib") == 0) {
397 comp->type = COMP_ZLIB;
Damien Millera664e872000-04-16 11:52:47 +1000398 } else if (strcmp(name, "none") == 0) {
Damien Miller9786e6e2005-07-26 21:54:56 +1000399 comp->type = COMP_NONE;
Damien Millera664e872000-04-16 11:52:47 +1000400 } else {
401 fatal("unsupported comp %s", name);
402 }
403 comp->name = name;
404}
Damien Miller4f7becb2006-03-26 14:10:14 +1100405
Ben Lindstrombba81212001-06-25 05:01:22 +0000406static void
Damien Millera664e872000-04-16 11:52:47 +1000407choose_kex(Kex *k, char *client, char *server)
408{
Damien Millerea111192013-04-23 19:24:32 +1000409 const struct kexalg *kexalg;
410
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000411 k->name = match_list(client, server, NULL);
Damien Millera664e872000-04-16 11:52:47 +1000412 if (k->name == NULL)
Darren Tucker0d0d1952007-06-05 18:23:28 +1000413 fatal("Unable to negotiate a key exchange method");
Damien Millerea111192013-04-23 19:24:32 +1000414 if ((kexalg = kex_alg_by_name(k->name)) == NULL)
415 fatal("unsupported kex alg %s", k->name);
416 k->kex_type = kexalg->type;
Damien Millerb3051d02014-01-10 10:58:53 +1100417 k->hash_alg = kexalg->hash_alg;
Damien Millerea111192013-04-23 19:24:32 +1000418 k->ec_nid = kexalg->ec_nid;
Damien Millera664e872000-04-16 11:52:47 +1000419}
Damien Miller19bb3a52005-11-05 15:19:35 +1100420
Ben Lindstrombba81212001-06-25 05:01:22 +0000421static void
Damien Millera664e872000-04-16 11:52:47 +1000422choose_hostkeyalg(Kex *k, char *client, char *server)
423{
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000424 char *hostkeyalg = match_list(client, server, NULL);
Damien Miller0bc1bd82000-11-13 22:57:25 +1100425 if (hostkeyalg == NULL)
Damien Millera664e872000-04-16 11:52:47 +1000426 fatal("no hostkey alg");
Damien Miller0bc1bd82000-11-13 22:57:25 +1100427 k->hostkey_type = key_type_from_name(hostkeyalg);
428 if (k->hostkey_type == KEY_UNSPEC)
429 fatal("bad hostkey alg '%s'", hostkeyalg);
Darren Tuckera627d422013-06-02 07:31:17 +1000430 free(hostkeyalg);
Damien Millera664e872000-04-16 11:52:47 +1000431}
432
Damien Millera8e06ce2003-11-21 23:48:55 +1100433static int
Damien Millerbabb47a2003-02-24 11:53:32 +1100434proposals_match(char *my[PROPOSAL_MAX], char *peer[PROPOSAL_MAX])
435{
436 static int check[] = {
437 PROPOSAL_KEX_ALGS, PROPOSAL_SERVER_HOST_KEY_ALGS, -1
438 };
439 int *idx;
440 char *p;
441
442 for (idx = &check[0]; *idx != -1; idx++) {
443 if ((p = strchr(my[*idx], ',')) != NULL)
444 *p = '\0';
445 if ((p = strchr(peer[*idx], ',')) != NULL)
446 *p = '\0';
447 if (strcmp(my[*idx], peer[*idx]) != 0) {
448 debug2("proposal mismatch: my %s peer %s",
449 my[*idx], peer[*idx]);
450 return (0);
451 }
452 }
453 debug2("proposals match");
454 return (1);
455}
456
Ben Lindstrombba81212001-06-25 05:01:22 +0000457static void
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000458kex_choose_conf(Kex *kex)
Damien Millera664e872000-04-16 11:52:47 +1000459{
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000460 Newkeys *newkeys;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000461 char **my, **peer;
462 char **cprop, **sprop;
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000463 int nenc, nmac, ncomp;
Damien Miller76eea4a2014-01-26 09:37:25 +1100464 u_int mode, ctos, need, dh_need, authlen;
Damien Millerbabb47a2003-02-24 11:53:32 +1100465 int first_kex_follows, type;
Damien Millera664e872000-04-16 11:52:47 +1000466
Damien Millerbabb47a2003-02-24 11:53:32 +1100467 my = kex_buf2prop(&kex->my, NULL);
468 peer = kex_buf2prop(&kex->peer, &first_kex_follows);
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000469
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000470 if (kex->server) {
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000471 cprop=peer;
472 sprop=my;
473 } else {
474 cprop=my;
475 sprop=peer;
476 }
Damien Millera664e872000-04-16 11:52:47 +1000477
Darren Tucker36331b52010-01-08 16:50:41 +1100478 /* Check whether server offers roaming */
479 if (!kex->server) {
480 char *roaming;
481 roaming = match_list(KEX_RESUME, peer[PROPOSAL_KEX_ALGS], NULL);
482 if (roaming) {
483 kex->roaming = 1;
Darren Tuckera627d422013-06-02 07:31:17 +1000484 free(roaming);
Darren Tucker36331b52010-01-08 16:50:41 +1100485 }
486 }
487
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000488 /* Algorithm Negotiation */
Damien Millera664e872000-04-16 11:52:47 +1000489 for (mode = 0; mode < MODE_MAX; mode++) {
Damien Miller07d86be2006-03-26 14:19:21 +1100490 newkeys = xcalloc(1, sizeof(*newkeys));
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000491 kex->newkeys[mode] = newkeys;
Darren Tucker0d0d1952007-06-05 18:23:28 +1000492 ctos = (!kex->server && mode == MODE_OUT) ||
493 (kex->server && mode == MODE_IN);
Damien Millera664e872000-04-16 11:52:47 +1000494 nenc = ctos ? PROPOSAL_ENC_ALGS_CTOS : PROPOSAL_ENC_ALGS_STOC;
495 nmac = ctos ? PROPOSAL_MAC_ALGS_CTOS : PROPOSAL_MAC_ALGS_STOC;
496 ncomp = ctos ? PROPOSAL_COMP_ALGS_CTOS : PROPOSAL_COMP_ALGS_STOC;
Damien Miller1d75abf2013-01-09 16:12:19 +1100497 choose_enc(&newkeys->enc, cprop[nenc], sprop[nenc]);
498 /* ignore mac for authenticated encryption */
499 authlen = cipher_authlen(newkeys->enc.cipher);
500 if (authlen == 0)
501 choose_mac(&newkeys->mac, cprop[nmac], sprop[nmac]);
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000502 choose_comp(&newkeys->comp, cprop[ncomp], sprop[ncomp]);
Damien Millera664e872000-04-16 11:52:47 +1000503 debug("kex: %s %s %s %s",
504 ctos ? "client->server" : "server->client",
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000505 newkeys->enc.name,
Damien Miller1d75abf2013-01-09 16:12:19 +1100506 authlen == 0 ? newkeys->mac.name : "<implicit>",
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000507 newkeys->comp.name);
Damien Millera664e872000-04-16 11:52:47 +1000508 }
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000509 choose_kex(kex, cprop[PROPOSAL_KEX_ALGS], sprop[PROPOSAL_KEX_ALGS]);
510 choose_hostkeyalg(kex, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS],
Damien Millera664e872000-04-16 11:52:47 +1000511 sprop[PROPOSAL_SERVER_HOST_KEY_ALGS]);
Damien Miller76eea4a2014-01-26 09:37:25 +1100512 need = dh_need = 0;
Damien Millera664e872000-04-16 11:52:47 +1000513 for (mode = 0; mode < MODE_MAX; mode++) {
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000514 newkeys = kex->newkeys[mode];
Damien Millera92ac742014-01-26 09:38:03 +1100515 need = MAX(need, newkeys->enc.key_len);
516 need = MAX(need, newkeys->enc.block_size);
517 need = MAX(need, newkeys->enc.iv_len);
518 need = MAX(need, newkeys->mac.key_len);
519 dh_need = MAX(dh_need, cipher_seclen(newkeys->enc.cipher));
520 dh_need = MAX(dh_need, newkeys->enc.block_size);
521 dh_need = MAX(dh_need, newkeys->enc.iv_len);
522 dh_need = MAX(dh_need, newkeys->mac.key_len);
Damien Millera664e872000-04-16 11:52:47 +1000523 }
Damien Millerb1715dc2000-05-30 13:44:51 +1000524 /* XXX need runden? */
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000525 kex->we_need = need;
Damien Miller76eea4a2014-01-26 09:37:25 +1100526 kex->dh_need = dh_need;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000527
Damien Millerbabb47a2003-02-24 11:53:32 +1100528 /* ignore the next message if the proposals do not match */
Damien Millera8e06ce2003-11-21 23:48:55 +1100529 if (first_kex_follows && !proposals_match(my, peer) &&
Damien Miller0dc1bef2005-07-17 17:22:45 +1000530 !(datafellows & SSH_BUG_FIRSTKEX)) {
Damien Millerbabb47a2003-02-24 11:53:32 +1100531 type = packet_read();
532 debug2("skipping next packet (type %u)", type);
533 }
534
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000535 kex_prop_free(my);
536 kex_prop_free(peer);
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000537}
538
Ben Lindstrombba81212001-06-25 05:01:22 +0000539static u_char *
Damien Miller19bb3a52005-11-05 15:19:35 +1100540derive_key(Kex *kex, int id, u_int need, u_char *hash, u_int hashlen,
Damien Miller91b580e2014-01-12 19:21:22 +1100541 const u_char *shared_secret, u_int slen)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000542{
543 Buffer b;
Damien Millerb3051d02014-01-10 10:58:53 +1100544 struct ssh_digest_ctx *hashctx;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000545 char c = id;
Damien Millereccb9de2005-06-17 12:59:34 +1000546 u_int have;
Damien Millerb3051d02014-01-10 10:58:53 +1100547 size_t mdsz;
Damien Millereccb9de2005-06-17 12:59:34 +1000548 u_char *digest;
Damien Miller46d38de2005-07-17 17:02:09 +1000549
Damien Millerb3051d02014-01-10 10:58:53 +1100550 if ((mdsz = ssh_digest_bytes(kex->hash_alg)) == 0)
551 fatal("bad kex md size %zu", mdsz);
Damien Millerc91e5562006-03-26 13:58:55 +1100552 digest = xmalloc(roundup(need, mdsz));
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000553
554 buffer_init(&b);
Damien Miller91b580e2014-01-12 19:21:22 +1100555 buffer_append(&b, shared_secret, slen);
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000556
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000557 /* K1 = HASH(K || H || "A" || session_id) */
Damien Millerb3051d02014-01-10 10:58:53 +1100558 if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL)
559 fatal("%s: ssh_digest_start failed", __func__);
560 if (ssh_digest_update_buffer(hashctx, &b) != 0 ||
561 ssh_digest_update(hashctx, hash, hashlen) != 0 ||
562 ssh_digest_update(hashctx, &c, 1) != 0 ||
563 ssh_digest_update(hashctx, kex->session_id,
564 kex->session_id_len) != 0)
565 fatal("%s: ssh_digest_update failed", __func__);
566 if (ssh_digest_final(hashctx, digest, mdsz) != 0)
567 fatal("%s: ssh_digest_final failed", __func__);
568 ssh_digest_free(hashctx);
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000569
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000570 /*
571 * expand key:
572 * Kn = HASH(K || H || K1 || K2 || ... || Kn-1)
573 * Key = K1 || K2 || ... || Kn
574 */
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000575 for (have = mdsz; need > have; have += mdsz) {
Damien Millerb3051d02014-01-10 10:58:53 +1100576 if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL)
577 fatal("%s: ssh_digest_start failed", __func__);
578 if (ssh_digest_update_buffer(hashctx, &b) != 0 ||
579 ssh_digest_update(hashctx, hash, hashlen) != 0 ||
580 ssh_digest_update(hashctx, digest, have) != 0)
581 fatal("%s: ssh_digest_update failed", __func__);
582 if (ssh_digest_final(hashctx, digest + have, mdsz) != 0)
583 fatal("%s: ssh_digest_final failed", __func__);
584 ssh_digest_free(hashctx);
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000585 }
586 buffer_free(&b);
587#ifdef DEBUG_KEX
588 fprintf(stderr, "key '%c'== ", c);
589 dump_digest("key", digest, need);
590#endif
591 return digest;
Damien Millera664e872000-04-16 11:52:47 +1000592}
593
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000594Newkeys *current_keys[MODE_MAX];
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000595
Ben Lindstromb9be60a2001-03-11 01:49:19 +0000596#define NKEYS 6
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000597void
Damien Miller91b580e2014-01-12 19:21:22 +1100598kex_derive_keys(Kex *kex, u_char *hash, u_int hashlen,
599 const u_char *shared_secret, u_int slen)
Damien Millera664e872000-04-16 11:52:47 +1000600{
Ben Lindstrom46c16222000-12-22 01:43:59 +0000601 u_char *keys[NKEYS];
Damien Millereccb9de2005-06-17 12:59:34 +1000602 u_int i, mode, ctos;
Damien Millera664e872000-04-16 11:52:47 +1000603
Damien Miller19bb3a52005-11-05 15:19:35 +1100604 for (i = 0; i < NKEYS; i++) {
605 keys[i] = derive_key(kex, 'A'+i, kex->we_need, hash, hashlen,
Damien Miller91b580e2014-01-12 19:21:22 +1100606 shared_secret, slen);
Damien Miller19bb3a52005-11-05 15:19:35 +1100607 }
Damien Millera664e872000-04-16 11:52:47 +1000608
Ben Lindstrom064496f2002-12-23 02:04:22 +0000609 debug2("kex_derive_keys");
Damien Millera664e872000-04-16 11:52:47 +1000610 for (mode = 0; mode < MODE_MAX; mode++) {
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000611 current_keys[mode] = kex->newkeys[mode];
612 kex->newkeys[mode] = NULL;
Damien Miller4f7becb2006-03-26 14:10:14 +1100613 ctos = (!kex->server && mode == MODE_OUT) ||
614 (kex->server && mode == MODE_IN);
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000615 current_keys[mode]->enc.iv = keys[ctos ? 0 : 1];
616 current_keys[mode]->enc.key = keys[ctos ? 2 : 3];
617 current_keys[mode]->mac.key = keys[ctos ? 4 : 5];
Damien Millera664e872000-04-16 11:52:47 +1000618 }
Damien Millera664e872000-04-16 11:52:47 +1000619}
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000620
Damien Miller1f0311c2014-05-15 14:24:09 +1000621#ifdef WITH_OPENSSL
Damien Miller91b580e2014-01-12 19:21:22 +1100622void
623kex_derive_keys_bn(Kex *kex, u_char *hash, u_int hashlen, const BIGNUM *secret)
624{
625 Buffer shared_secret;
626
627 buffer_init(&shared_secret);
628 buffer_put_bignum2(&shared_secret, secret);
629 kex_derive_keys(kex, hash, hashlen,
630 buffer_ptr(&shared_secret), buffer_len(&shared_secret));
631 buffer_free(&shared_secret);
632}
Damien Miller1f0311c2014-05-15 14:24:09 +1000633#endif
Damien Miller91b580e2014-01-12 19:21:22 +1100634
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000635Newkeys *
636kex_get_newkeys(int mode)
637{
Ben Lindstrombe2cc432001-04-04 23:46:07 +0000638 Newkeys *ret;
639
640 ret = current_keys[mode];
641 current_keys[mode] = NULL;
642 return ret;
Ben Lindstrom2d90e002001-04-04 02:00:54 +0000643}
644
Damien Miller1f0311c2014-05-15 14:24:09 +1000645#ifdef WITH_SSH1
Darren Tuckere14e0052004-05-13 16:30:44 +1000646void
647derive_ssh1_session_id(BIGNUM *host_modulus, BIGNUM *server_modulus,
648 u_int8_t cookie[8], u_int8_t id[16])
649{
Damien Millerb3051d02014-01-10 10:58:53 +1100650 u_int8_t nbuf[2048], obuf[SSH_DIGEST_MAX_LENGTH];
Darren Tuckere14e0052004-05-13 16:30:44 +1000651 int len;
Damien Millerb3051d02014-01-10 10:58:53 +1100652 struct ssh_digest_ctx *hashctx;
Darren Tuckere14e0052004-05-13 16:30:44 +1000653
Damien Millerb3051d02014-01-10 10:58:53 +1100654 if ((hashctx = ssh_digest_start(SSH_DIGEST_MD5)) == NULL)
655 fatal("%s: ssh_digest_start", __func__);
Darren Tuckere14e0052004-05-13 16:30:44 +1000656
657 len = BN_num_bytes(host_modulus);
Damien Millereccb9de2005-06-17 12:59:34 +1000658 if (len < (512 / 8) || (u_int)len > sizeof(nbuf))
Darren Tuckere14e0052004-05-13 16:30:44 +1000659 fatal("%s: bad host modulus (len %d)", __func__, len);
660 BN_bn2bin(host_modulus, nbuf);
Damien Millerb3051d02014-01-10 10:58:53 +1100661 if (ssh_digest_update(hashctx, nbuf, len) != 0)
662 fatal("%s: ssh_digest_update failed", __func__);
Darren Tuckere14e0052004-05-13 16:30:44 +1000663
664 len = BN_num_bytes(server_modulus);
Damien Millereccb9de2005-06-17 12:59:34 +1000665 if (len < (512 / 8) || (u_int)len > sizeof(nbuf))
Darren Tuckere14e0052004-05-13 16:30:44 +1000666 fatal("%s: bad server modulus (len %d)", __func__, len);
667 BN_bn2bin(server_modulus, nbuf);
Damien Millerb3051d02014-01-10 10:58:53 +1100668 if (ssh_digest_update(hashctx, nbuf, len) != 0 ||
669 ssh_digest_update(hashctx, cookie, 8) != 0)
670 fatal("%s: ssh_digest_update failed", __func__);
671 if (ssh_digest_final(hashctx, obuf, sizeof(obuf)) != 0)
672 fatal("%s: ssh_digest_final failed", __func__);
673 memcpy(id, obuf, ssh_digest_bytes(SSH_DIGEST_MD5));
Darren Tuckere14e0052004-05-13 16:30:44 +1000674
Damien Millera5103f42014-02-04 11:20:14 +1100675 explicit_bzero(nbuf, sizeof(nbuf));
676 explicit_bzero(obuf, sizeof(obuf));
Darren Tuckere14e0052004-05-13 16:30:44 +1000677}
Damien Miller1f0311c2014-05-15 14:24:09 +1000678#endif
Darren Tuckere14e0052004-05-13 16:30:44 +1000679
Damien Millereb8b60e2010-08-31 22:41:14 +1000680#if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) || defined(DEBUG_KEXECDH)
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000681void
682dump_digest(char *msg, u_char *digest, int len)
683{
Damien Millereb8b60e2010-08-31 22:41:14 +1000684 int i;
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000685
686 fprintf(stderr, "%s\n", msg);
Darren Tucker82a3d2b2007-02-19 22:10:25 +1100687 for (i = 0; i < len; i++) {
Ben Lindstrom20d7c7b2001-04-04 01:56:17 +0000688 fprintf(stderr, "%02x", digest[i]);
689 if (i%32 == 31)
690 fprintf(stderr, "\n");
691 else if (i%8 == 7)
692 fprintf(stderr, " ");
693 }
694 fprintf(stderr, "\n");
695}
696#endif