1d059297112922cabb0c674840589be8db821fd9aAdam Langley/* $OpenBSD: kex.c,v 1.105 2015/01/30 00:22:25 djm Exp $ */
2bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman/*
3bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * Copyright (c) 2000, 2001 Markus Friedl.  All rights reserved.
4bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman *
5bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * Redistribution and use in source and binary forms, with or without
6bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * modification, are permitted provided that the following conditions
7bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * are met:
8bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * 1. Redistributions of source code must retain the above copyright
9bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman *    notice, this list of conditions and the following disclaimer.
10bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * 2. Redistributions in binary form must reproduce the above copyright
11bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman *    notice, this list of conditions and the following disclaimer in the
12bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman *    documentation and/or other materials provided with the distribution.
13bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman *
14bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman */
25bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
26bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include "includes.h"
27bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
28d059297112922cabb0c674840589be8db821fd9aAdam Langley#include <sys/param.h>	/* MAX roundup */
29bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
30bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include <signal.h>
31bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include <stdarg.h>
32bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include <stdio.h>
33bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include <stdlib.h>
34bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include <string.h>
35bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
36d059297112922cabb0c674840589be8db821fd9aAdam Langley#ifdef WITH_OPENSSL
37bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include <openssl/crypto.h>
38d059297112922cabb0c674840589be8db821fd9aAdam Langley#include <openssl/dh.h>
39d059297112922cabb0c674840589be8db821fd9aAdam Langley#endif
40bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
41bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include "ssh2.h"
42bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include "packet.h"
43bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include "compat.h"
44bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include "cipher.h"
45d059297112922cabb0c674840589be8db821fd9aAdam Langley#include "sshkey.h"
46bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include "kex.h"
47bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include "log.h"
48bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include "mac.h"
49bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include "match.h"
50d059297112922cabb0c674840589be8db821fd9aAdam Langley#include "misc.h"
51bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include "dispatch.h"
52bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include "monitor.h"
53bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#include "roaming.h"
54bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
55d059297112922cabb0c674840589be8db821fd9aAdam Langley#include "ssherr.h"
56d059297112922cabb0c674840589be8db821fd9aAdam Langley#include "sshbuf.h"
57d059297112922cabb0c674840589be8db821fd9aAdam Langley#include "digest.h"
58d059297112922cabb0c674840589be8db821fd9aAdam Langley
59bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#if OPENSSL_VERSION_NUMBER >= 0x00907000L
60bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman# if defined(HAVE_EVP_SHA256)
61bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman# define evp_ssh_sha256 EVP_sha256
62bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman# else
63bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartmanextern const EVP_MD *evp_ssh_sha256(void);
64bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman# endif
65bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#endif
66bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
67bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman/* prototype */
68d059297112922cabb0c674840589be8db821fd9aAdam Langleystatic int kex_choose_conf(struct ssh *);
69d059297112922cabb0c674840589be8db821fd9aAdam Langleystatic int kex_input_newkeys(int, u_int32_t, void *);
70d059297112922cabb0c674840589be8db821fd9aAdam Langley
71d059297112922cabb0c674840589be8db821fd9aAdam Langleystruct kexalg {
72d059297112922cabb0c674840589be8db821fd9aAdam Langley	char *name;
73d059297112922cabb0c674840589be8db821fd9aAdam Langley	u_int type;
74d059297112922cabb0c674840589be8db821fd9aAdam Langley	int ec_nid;
75d059297112922cabb0c674840589be8db821fd9aAdam Langley	int hash_alg;
76d059297112922cabb0c674840589be8db821fd9aAdam Langley};
77d059297112922cabb0c674840589be8db821fd9aAdam Langleystatic const struct kexalg kexalgs[] = {
78d059297112922cabb0c674840589be8db821fd9aAdam Langley#ifdef WITH_OPENSSL
79d059297112922cabb0c674840589be8db821fd9aAdam Langley	{ KEX_DH1, KEX_DH_GRP1_SHA1, 0, SSH_DIGEST_SHA1 },
80d059297112922cabb0c674840589be8db821fd9aAdam Langley	{ KEX_DH14, KEX_DH_GRP14_SHA1, 0, SSH_DIGEST_SHA1 },
81d059297112922cabb0c674840589be8db821fd9aAdam Langley	{ KEX_DHGEX_SHA1, KEX_DH_GEX_SHA1, 0, SSH_DIGEST_SHA1 },
82d059297112922cabb0c674840589be8db821fd9aAdam Langley#ifdef HAVE_EVP_SHA256
83d059297112922cabb0c674840589be8db821fd9aAdam Langley	{ KEX_DHGEX_SHA256, KEX_DH_GEX_SHA256, 0, SSH_DIGEST_SHA256 },
84d059297112922cabb0c674840589be8db821fd9aAdam Langley#endif /* HAVE_EVP_SHA256 */
85d059297112922cabb0c674840589be8db821fd9aAdam Langley#ifdef OPENSSL_HAS_ECC
86d059297112922cabb0c674840589be8db821fd9aAdam Langley	{ KEX_ECDH_SHA2_NISTP256, KEX_ECDH_SHA2,
87d059297112922cabb0c674840589be8db821fd9aAdam Langley	    NID_X9_62_prime256v1, SSH_DIGEST_SHA256 },
88d059297112922cabb0c674840589be8db821fd9aAdam Langley	{ KEX_ECDH_SHA2_NISTP384, KEX_ECDH_SHA2, NID_secp384r1,
89d059297112922cabb0c674840589be8db821fd9aAdam Langley	    SSH_DIGEST_SHA384 },
90d059297112922cabb0c674840589be8db821fd9aAdam Langley# ifdef OPENSSL_HAS_NISTP521
91d059297112922cabb0c674840589be8db821fd9aAdam Langley	{ KEX_ECDH_SHA2_NISTP521, KEX_ECDH_SHA2, NID_secp521r1,
92d059297112922cabb0c674840589be8db821fd9aAdam Langley	    SSH_DIGEST_SHA512 },
93d059297112922cabb0c674840589be8db821fd9aAdam Langley# endif /* OPENSSL_HAS_NISTP521 */
94d059297112922cabb0c674840589be8db821fd9aAdam Langley#endif /* OPENSSL_HAS_ECC */
95d059297112922cabb0c674840589be8db821fd9aAdam Langley#endif /* WITH_OPENSSL */
96d059297112922cabb0c674840589be8db821fd9aAdam Langley#if defined(HAVE_EVP_SHA256) || !defined(WITH_OPENSSL)
97d059297112922cabb0c674840589be8db821fd9aAdam Langley	{ KEX_CURVE25519_SHA256, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 },
98d059297112922cabb0c674840589be8db821fd9aAdam Langley#endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */
99d059297112922cabb0c674840589be8db821fd9aAdam Langley	{ NULL, -1, -1, -1},
100d059297112922cabb0c674840589be8db821fd9aAdam Langley};
101d059297112922cabb0c674840589be8db821fd9aAdam Langley
102d059297112922cabb0c674840589be8db821fd9aAdam Langleychar *
103d059297112922cabb0c674840589be8db821fd9aAdam Langleykex_alg_list(char sep)
104d059297112922cabb0c674840589be8db821fd9aAdam Langley{
105d059297112922cabb0c674840589be8db821fd9aAdam Langley	char *ret = NULL, *tmp;
106d059297112922cabb0c674840589be8db821fd9aAdam Langley	size_t nlen, rlen = 0;
107d059297112922cabb0c674840589be8db821fd9aAdam Langley	const struct kexalg *k;
108d059297112922cabb0c674840589be8db821fd9aAdam Langley
109d059297112922cabb0c674840589be8db821fd9aAdam Langley	for (k = kexalgs; k->name != NULL; k++) {
110d059297112922cabb0c674840589be8db821fd9aAdam Langley		if (ret != NULL)
111d059297112922cabb0c674840589be8db821fd9aAdam Langley			ret[rlen++] = sep;
112d059297112922cabb0c674840589be8db821fd9aAdam Langley		nlen = strlen(k->name);
113d059297112922cabb0c674840589be8db821fd9aAdam Langley		if ((tmp = realloc(ret, rlen + nlen + 2)) == NULL) {
114d059297112922cabb0c674840589be8db821fd9aAdam Langley			free(ret);
115d059297112922cabb0c674840589be8db821fd9aAdam Langley			return NULL;
116d059297112922cabb0c674840589be8db821fd9aAdam Langley		}
117d059297112922cabb0c674840589be8db821fd9aAdam Langley		ret = tmp;
118d059297112922cabb0c674840589be8db821fd9aAdam Langley		memcpy(ret + rlen, k->name, nlen + 1);
119d059297112922cabb0c674840589be8db821fd9aAdam Langley		rlen += nlen;
120d059297112922cabb0c674840589be8db821fd9aAdam Langley	}
121d059297112922cabb0c674840589be8db821fd9aAdam Langley	return ret;
122d059297112922cabb0c674840589be8db821fd9aAdam Langley}
123d059297112922cabb0c674840589be8db821fd9aAdam Langley
124d059297112922cabb0c674840589be8db821fd9aAdam Langleystatic const struct kexalg *
125d059297112922cabb0c674840589be8db821fd9aAdam Langleykex_alg_by_name(const char *name)
126d059297112922cabb0c674840589be8db821fd9aAdam Langley{
127d059297112922cabb0c674840589be8db821fd9aAdam Langley	const struct kexalg *k;
128d059297112922cabb0c674840589be8db821fd9aAdam Langley
129d059297112922cabb0c674840589be8db821fd9aAdam Langley	for (k = kexalgs; k->name != NULL; k++) {
130d059297112922cabb0c674840589be8db821fd9aAdam Langley		if (strcmp(k->name, name) == 0)
131d059297112922cabb0c674840589be8db821fd9aAdam Langley			return k;
132d059297112922cabb0c674840589be8db821fd9aAdam Langley	}
133d059297112922cabb0c674840589be8db821fd9aAdam Langley	return NULL;
134d059297112922cabb0c674840589be8db821fd9aAdam Langley}
135bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
136bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman/* Validate KEX method name list */
137bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartmanint
138bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartmankex_names_valid(const char *names)
139bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman{
140bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	char *s, *cp, *p;
141bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
142bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	if (names == NULL || strcmp(names, "") == 0)
143bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		return 0;
144d059297112922cabb0c674840589be8db821fd9aAdam Langley	if ((s = cp = strdup(names)) == NULL)
145d059297112922cabb0c674840589be8db821fd9aAdam Langley		return 0;
146bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	for ((p = strsep(&cp, ",")); p && *p != '\0';
147bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	    (p = strsep(&cp, ","))) {
148d059297112922cabb0c674840589be8db821fd9aAdam Langley		if (kex_alg_by_name(p) == NULL) {
149bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			error("Unsupported KEX algorithm \"%.100s\"", p);
150d059297112922cabb0c674840589be8db821fd9aAdam Langley			free(s);
151bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			return 0;
152bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		}
153bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	}
154bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	debug3("kex names ok: [%s]", names);
155d059297112922cabb0c674840589be8db821fd9aAdam Langley	free(s);
156bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	return 1;
157bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman}
158bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
159bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman/* put algorithm proposal into buffer */
160d059297112922cabb0c674840589be8db821fd9aAdam Langleyint
161d059297112922cabb0c674840589be8db821fd9aAdam Langleykex_prop2buf(struct sshbuf *b, char *proposal[PROPOSAL_MAX])
162bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman{
163bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	u_int i;
164d059297112922cabb0c674840589be8db821fd9aAdam Langley	int r;
165d059297112922cabb0c674840589be8db821fd9aAdam Langley
166d059297112922cabb0c674840589be8db821fd9aAdam Langley	sshbuf_reset(b);
167bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
168bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	/*
169bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	 * add a dummy cookie, the cookie will be overwritten by
170bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	 * kex_send_kexinit(), each time a kexinit is set
171bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	 */
172d059297112922cabb0c674840589be8db821fd9aAdam Langley	for (i = 0; i < KEX_COOKIE_LEN; i++) {
173d059297112922cabb0c674840589be8db821fd9aAdam Langley		if ((r = sshbuf_put_u8(b, 0)) != 0)
174d059297112922cabb0c674840589be8db821fd9aAdam Langley			return r;
175d059297112922cabb0c674840589be8db821fd9aAdam Langley	}
176d059297112922cabb0c674840589be8db821fd9aAdam Langley	for (i = 0; i < PROPOSAL_MAX; i++) {
177d059297112922cabb0c674840589be8db821fd9aAdam Langley		if ((r = sshbuf_put_cstring(b, proposal[i])) != 0)
178d059297112922cabb0c674840589be8db821fd9aAdam Langley			return r;
179d059297112922cabb0c674840589be8db821fd9aAdam Langley	}
180d059297112922cabb0c674840589be8db821fd9aAdam Langley	if ((r = sshbuf_put_u8(b, 0)) != 0 ||	/* first_kex_packet_follows */
181d059297112922cabb0c674840589be8db821fd9aAdam Langley	    (r = sshbuf_put_u32(b, 0)) != 0)	/* uint32 reserved */
182d059297112922cabb0c674840589be8db821fd9aAdam Langley		return r;
183d059297112922cabb0c674840589be8db821fd9aAdam Langley	return 0;
184bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman}
185bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
186bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman/* parse buffer and return algorithm proposal */
187d059297112922cabb0c674840589be8db821fd9aAdam Langleyint
188d059297112922cabb0c674840589be8db821fd9aAdam Langleykex_buf2prop(struct sshbuf *raw, int *first_kex_follows, char ***propp)
189bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman{
190d059297112922cabb0c674840589be8db821fd9aAdam Langley	struct sshbuf *b = NULL;
191d059297112922cabb0c674840589be8db821fd9aAdam Langley	u_char v;
192bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	u_int i;
193d059297112922cabb0c674840589be8db821fd9aAdam Langley	char **proposal = NULL;
194d059297112922cabb0c674840589be8db821fd9aAdam Langley	int r;
195d059297112922cabb0c674840589be8db821fd9aAdam Langley
196d059297112922cabb0c674840589be8db821fd9aAdam Langley	*propp = NULL;
197d059297112922cabb0c674840589be8db821fd9aAdam Langley	if ((proposal = calloc(PROPOSAL_MAX, sizeof(char *))) == NULL)
198d059297112922cabb0c674840589be8db821fd9aAdam Langley		return SSH_ERR_ALLOC_FAIL;
199d059297112922cabb0c674840589be8db821fd9aAdam Langley	if ((b = sshbuf_fromb(raw)) == NULL) {
200d059297112922cabb0c674840589be8db821fd9aAdam Langley		r = SSH_ERR_ALLOC_FAIL;
201d059297112922cabb0c674840589be8db821fd9aAdam Langley		goto out;
202d059297112922cabb0c674840589be8db821fd9aAdam Langley	}
203d059297112922cabb0c674840589be8db821fd9aAdam Langley	if ((r = sshbuf_consume(b, KEX_COOKIE_LEN)) != 0) /* skip cookie */
204d059297112922cabb0c674840589be8db821fd9aAdam Langley		goto out;
205bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	/* extract kex init proposal strings */
206bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	for (i = 0; i < PROPOSAL_MAX; i++) {
207d059297112922cabb0c674840589be8db821fd9aAdam Langley		if ((r = sshbuf_get_cstring(b, &(proposal[i]), NULL)) != 0)
208d059297112922cabb0c674840589be8db821fd9aAdam Langley			goto out;
209bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		debug2("kex_parse_kexinit: %s", proposal[i]);
210bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	}
211bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	/* first kex follows / reserved */
212d059297112922cabb0c674840589be8db821fd9aAdam Langley	if ((r = sshbuf_get_u8(b, &v)) != 0 ||
213d059297112922cabb0c674840589be8db821fd9aAdam Langley	    (r = sshbuf_get_u32(b, &i)) != 0)
214d059297112922cabb0c674840589be8db821fd9aAdam Langley		goto out;
215bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	if (first_kex_follows != NULL)
216bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		*first_kex_follows = i;
217d059297112922cabb0c674840589be8db821fd9aAdam Langley	debug2("kex_parse_kexinit: first_kex_follows %d ", v);
218bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	debug2("kex_parse_kexinit: reserved %u ", i);
219d059297112922cabb0c674840589be8db821fd9aAdam Langley	r = 0;
220d059297112922cabb0c674840589be8db821fd9aAdam Langley	*propp = proposal;
221d059297112922cabb0c674840589be8db821fd9aAdam Langley out:
222d059297112922cabb0c674840589be8db821fd9aAdam Langley	if (r != 0 && proposal != NULL)
223d059297112922cabb0c674840589be8db821fd9aAdam Langley		kex_prop_free(proposal);
224d059297112922cabb0c674840589be8db821fd9aAdam Langley	sshbuf_free(b);
225d059297112922cabb0c674840589be8db821fd9aAdam Langley	return r;
226bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman}
227bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
228d059297112922cabb0c674840589be8db821fd9aAdam Langleyvoid
229bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartmankex_prop_free(char **proposal)
230bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman{
231bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	u_int i;
232bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
233bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	for (i = 0; i < PROPOSAL_MAX; i++)
234d059297112922cabb0c674840589be8db821fd9aAdam Langley		free(proposal[i]);
235d059297112922cabb0c674840589be8db821fd9aAdam Langley	free(proposal);
236bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman}
237bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
238bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman/* ARGSUSED */
239d059297112922cabb0c674840589be8db821fd9aAdam Langleystatic int
240bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartmankex_protocol_error(int type, u_int32_t seq, void *ctxt)
241bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman{
242bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	error("Hm, kex protocol error: type %d seq %u", type, seq);
243d059297112922cabb0c674840589be8db821fd9aAdam Langley	return 0;
244bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman}
245bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
246bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartmanstatic void
247d059297112922cabb0c674840589be8db821fd9aAdam Langleykex_reset_dispatch(struct ssh *ssh)
248bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman{
249d059297112922cabb0c674840589be8db821fd9aAdam Langley	ssh_dispatch_range(ssh, SSH2_MSG_TRANSPORT_MIN,
250bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	    SSH2_MSG_TRANSPORT_MAX, &kex_protocol_error);
251d059297112922cabb0c674840589be8db821fd9aAdam Langley	ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit);
252bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman}
253bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
254d059297112922cabb0c674840589be8db821fd9aAdam Langleyint
255d059297112922cabb0c674840589be8db821fd9aAdam Langleykex_send_newkeys(struct ssh *ssh)
256bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman{
257d059297112922cabb0c674840589be8db821fd9aAdam Langley	int r;
258bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
259d059297112922cabb0c674840589be8db821fd9aAdam Langley	kex_reset_dispatch(ssh);
260d059297112922cabb0c674840589be8db821fd9aAdam Langley	if ((r = sshpkt_start(ssh, SSH2_MSG_NEWKEYS)) != 0 ||
261d059297112922cabb0c674840589be8db821fd9aAdam Langley	    (r = sshpkt_send(ssh)) != 0)
262d059297112922cabb0c674840589be8db821fd9aAdam Langley		return r;
263bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	debug("SSH2_MSG_NEWKEYS sent");
264bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	debug("expecting SSH2_MSG_NEWKEYS");
265d059297112922cabb0c674840589be8db821fd9aAdam Langley	ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_input_newkeys);
266d059297112922cabb0c674840589be8db821fd9aAdam Langley	return 0;
267d059297112922cabb0c674840589be8db821fd9aAdam Langley}
268d059297112922cabb0c674840589be8db821fd9aAdam Langley
269d059297112922cabb0c674840589be8db821fd9aAdam Langleystatic int
270d059297112922cabb0c674840589be8db821fd9aAdam Langleykex_input_newkeys(int type, u_int32_t seq, void *ctxt)
271d059297112922cabb0c674840589be8db821fd9aAdam Langley{
272d059297112922cabb0c674840589be8db821fd9aAdam Langley	struct ssh *ssh = ctxt;
273d059297112922cabb0c674840589be8db821fd9aAdam Langley	struct kex *kex = ssh->kex;
274d059297112922cabb0c674840589be8db821fd9aAdam Langley	int r;
275bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
276d059297112922cabb0c674840589be8db821fd9aAdam Langley	debug("SSH2_MSG_NEWKEYS received");
277d059297112922cabb0c674840589be8db821fd9aAdam Langley	ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_protocol_error);
278d059297112922cabb0c674840589be8db821fd9aAdam Langley	if ((r = sshpkt_get_end(ssh)) != 0)
279d059297112922cabb0c674840589be8db821fd9aAdam Langley		return r;
280bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	kex->done = 1;
281d059297112922cabb0c674840589be8db821fd9aAdam Langley	sshbuf_reset(kex->peer);
282d059297112922cabb0c674840589be8db821fd9aAdam Langley	/* sshbuf_reset(kex->my); */
283bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	kex->flags &= ~KEX_INIT_SENT;
284d059297112922cabb0c674840589be8db821fd9aAdam Langley	free(kex->name);
285bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	kex->name = NULL;
286d059297112922cabb0c674840589be8db821fd9aAdam Langley	return 0;
287bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman}
288bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
289d059297112922cabb0c674840589be8db821fd9aAdam Langleyint
290d059297112922cabb0c674840589be8db821fd9aAdam Langleykex_send_kexinit(struct ssh *ssh)
291bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman{
292bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	u_char *cookie;
293d059297112922cabb0c674840589be8db821fd9aAdam Langley	struct kex *kex = ssh->kex;
294d059297112922cabb0c674840589be8db821fd9aAdam Langley	int r;
295bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
296d059297112922cabb0c674840589be8db821fd9aAdam Langley	if (kex == NULL)
297d059297112922cabb0c674840589be8db821fd9aAdam Langley		return SSH_ERR_INTERNAL_ERROR;
298d059297112922cabb0c674840589be8db821fd9aAdam Langley	if (kex->flags & KEX_INIT_SENT)
299d059297112922cabb0c674840589be8db821fd9aAdam Langley		return 0;
300bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	kex->done = 0;
301bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
302bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	/* generate a random cookie */
303d059297112922cabb0c674840589be8db821fd9aAdam Langley	if (sshbuf_len(kex->my) < KEX_COOKIE_LEN)
304d059297112922cabb0c674840589be8db821fd9aAdam Langley		return SSH_ERR_INVALID_FORMAT;
305d059297112922cabb0c674840589be8db821fd9aAdam Langley	if ((cookie = sshbuf_mutable_ptr(kex->my)) == NULL)
306d059297112922cabb0c674840589be8db821fd9aAdam Langley		return SSH_ERR_INTERNAL_ERROR;
307d059297112922cabb0c674840589be8db821fd9aAdam Langley	arc4random_buf(cookie, KEX_COOKIE_LEN);
308d059297112922cabb0c674840589be8db821fd9aAdam Langley
309d059297112922cabb0c674840589be8db821fd9aAdam Langley	if ((r = sshpkt_start(ssh, SSH2_MSG_KEXINIT)) != 0 ||
310d059297112922cabb0c674840589be8db821fd9aAdam Langley	    (r = sshpkt_putb(ssh, kex->my)) != 0 ||
311d059297112922cabb0c674840589be8db821fd9aAdam Langley	    (r = sshpkt_send(ssh)) != 0)
312d059297112922cabb0c674840589be8db821fd9aAdam Langley		return r;
313bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	debug("SSH2_MSG_KEXINIT sent");
314bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	kex->flags |= KEX_INIT_SENT;
315d059297112922cabb0c674840589be8db821fd9aAdam Langley	return 0;
316bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman}
317bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
318bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman/* ARGSUSED */
319d059297112922cabb0c674840589be8db821fd9aAdam Langleyint
320bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartmankex_input_kexinit(int type, u_int32_t seq, void *ctxt)
321bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman{
322d059297112922cabb0c674840589be8db821fd9aAdam Langley	struct ssh *ssh = ctxt;
323d059297112922cabb0c674840589be8db821fd9aAdam Langley	struct kex *kex = ssh->kex;
324d059297112922cabb0c674840589be8db821fd9aAdam Langley	const u_char *ptr;
325d059297112922cabb0c674840589be8db821fd9aAdam Langley	u_int i;
326d059297112922cabb0c674840589be8db821fd9aAdam Langley	size_t dlen;
327d059297112922cabb0c674840589be8db821fd9aAdam Langley	int r;
328bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
329bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	debug("SSH2_MSG_KEXINIT received");
330bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	if (kex == NULL)
331d059297112922cabb0c674840589be8db821fd9aAdam Langley		return SSH_ERR_INVALID_ARGUMENT;
332bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
333d059297112922cabb0c674840589be8db821fd9aAdam Langley	ptr = sshpkt_ptr(ssh, &dlen);
334d059297112922cabb0c674840589be8db821fd9aAdam Langley	if ((r = sshbuf_put(kex->peer, ptr, dlen)) != 0)
335d059297112922cabb0c674840589be8db821fd9aAdam Langley		return r;
336bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
337bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	/* discard packet */
338bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	for (i = 0; i < KEX_COOKIE_LEN; i++)
339d059297112922cabb0c674840589be8db821fd9aAdam Langley		if ((r = sshpkt_get_u8(ssh, NULL)) != 0)
340d059297112922cabb0c674840589be8db821fd9aAdam Langley			return r;
341bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	for (i = 0; i < PROPOSAL_MAX; i++)
342d059297112922cabb0c674840589be8db821fd9aAdam Langley		if ((r = sshpkt_get_string(ssh, NULL, NULL)) != 0)
343d059297112922cabb0c674840589be8db821fd9aAdam Langley			return r;
344d059297112922cabb0c674840589be8db821fd9aAdam Langley	/*
345d059297112922cabb0c674840589be8db821fd9aAdam Langley	 * XXX RFC4253 sec 7: "each side MAY guess" - currently no supported
346d059297112922cabb0c674840589be8db821fd9aAdam Langley	 * KEX method has the server move first, but a server might be using
347d059297112922cabb0c674840589be8db821fd9aAdam Langley	 * a custom method or one that we otherwise don't support. We should
348d059297112922cabb0c674840589be8db821fd9aAdam Langley	 * be prepared to remember first_kex_follows here so we can eat a
349d059297112922cabb0c674840589be8db821fd9aAdam Langley	 * packet later.
350d059297112922cabb0c674840589be8db821fd9aAdam Langley	 * XXX2 - RFC4253 is kind of ambiguous on what first_kex_follows means
351d059297112922cabb0c674840589be8db821fd9aAdam Langley	 * for cases where the server *doesn't* go first. I guess we should
352d059297112922cabb0c674840589be8db821fd9aAdam Langley	 * ignore it when it is set for these cases, which is what we do now.
353d059297112922cabb0c674840589be8db821fd9aAdam Langley	 */
354d059297112922cabb0c674840589be8db821fd9aAdam Langley	if ((r = sshpkt_get_u8(ssh, NULL)) != 0 ||	/* first_kex_follows */
355d059297112922cabb0c674840589be8db821fd9aAdam Langley	    (r = sshpkt_get_u32(ssh, NULL)) != 0 ||	/* reserved */
356d059297112922cabb0c674840589be8db821fd9aAdam Langley	    (r = sshpkt_get_end(ssh)) != 0)
357d059297112922cabb0c674840589be8db821fd9aAdam Langley			return r;
358d059297112922cabb0c674840589be8db821fd9aAdam Langley
359d059297112922cabb0c674840589be8db821fd9aAdam Langley	if (!(kex->flags & KEX_INIT_SENT))
360d059297112922cabb0c674840589be8db821fd9aAdam Langley		if ((r = kex_send_kexinit(ssh)) != 0)
361d059297112922cabb0c674840589be8db821fd9aAdam Langley			return r;
362d059297112922cabb0c674840589be8db821fd9aAdam Langley	if ((r = kex_choose_conf(ssh)) != 0)
363d059297112922cabb0c674840589be8db821fd9aAdam Langley		return r;
364d059297112922cabb0c674840589be8db821fd9aAdam Langley
365d059297112922cabb0c674840589be8db821fd9aAdam Langley	if (kex->kex_type < KEX_MAX && kex->kex[kex->kex_type] != NULL)
366d059297112922cabb0c674840589be8db821fd9aAdam Langley		return (kex->kex[kex->kex_type])(ssh);
367bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
368d059297112922cabb0c674840589be8db821fd9aAdam Langley	return SSH_ERR_INTERNAL_ERROR;
369bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman}
370bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
371d059297112922cabb0c674840589be8db821fd9aAdam Langleyint
372d059297112922cabb0c674840589be8db821fd9aAdam Langleykex_new(struct ssh *ssh, char *proposal[PROPOSAL_MAX], struct kex **kexp)
373bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman{
374d059297112922cabb0c674840589be8db821fd9aAdam Langley	struct kex *kex;
375d059297112922cabb0c674840589be8db821fd9aAdam Langley	int r;
376d059297112922cabb0c674840589be8db821fd9aAdam Langley
377d059297112922cabb0c674840589be8db821fd9aAdam Langley	*kexp = NULL;
378d059297112922cabb0c674840589be8db821fd9aAdam Langley	if ((kex = calloc(1, sizeof(*kex))) == NULL)
379d059297112922cabb0c674840589be8db821fd9aAdam Langley		return SSH_ERR_ALLOC_FAIL;
380d059297112922cabb0c674840589be8db821fd9aAdam Langley	if ((kex->peer = sshbuf_new()) == NULL ||
381d059297112922cabb0c674840589be8db821fd9aAdam Langley	    (kex->my = sshbuf_new()) == NULL) {
382d059297112922cabb0c674840589be8db821fd9aAdam Langley		r = SSH_ERR_ALLOC_FAIL;
383d059297112922cabb0c674840589be8db821fd9aAdam Langley		goto out;
384d059297112922cabb0c674840589be8db821fd9aAdam Langley	}
385d059297112922cabb0c674840589be8db821fd9aAdam Langley	if ((r = kex_prop2buf(kex->my, proposal)) != 0)
386d059297112922cabb0c674840589be8db821fd9aAdam Langley		goto out;
387bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	kex->done = 0;
388d059297112922cabb0c674840589be8db821fd9aAdam Langley	kex_reset_dispatch(ssh);
389d059297112922cabb0c674840589be8db821fd9aAdam Langley	r = 0;
390d059297112922cabb0c674840589be8db821fd9aAdam Langley	*kexp = kex;
391d059297112922cabb0c674840589be8db821fd9aAdam Langley out:
392d059297112922cabb0c674840589be8db821fd9aAdam Langley	if (r != 0)
393d059297112922cabb0c674840589be8db821fd9aAdam Langley		kex_free(kex);
394d059297112922cabb0c674840589be8db821fd9aAdam Langley	return r;
395bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman}
396bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
397d059297112922cabb0c674840589be8db821fd9aAdam Langleyvoid
398d059297112922cabb0c674840589be8db821fd9aAdam Langleykex_free_newkeys(struct newkeys *newkeys)
399bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman{
400d059297112922cabb0c674840589be8db821fd9aAdam Langley	if (newkeys == NULL)
401d059297112922cabb0c674840589be8db821fd9aAdam Langley		return;
402d059297112922cabb0c674840589be8db821fd9aAdam Langley	if (newkeys->enc.key) {
403d059297112922cabb0c674840589be8db821fd9aAdam Langley		explicit_bzero(newkeys->enc.key, newkeys->enc.key_len);
404d059297112922cabb0c674840589be8db821fd9aAdam Langley		free(newkeys->enc.key);
405d059297112922cabb0c674840589be8db821fd9aAdam Langley		newkeys->enc.key = NULL;
406d059297112922cabb0c674840589be8db821fd9aAdam Langley	}
407d059297112922cabb0c674840589be8db821fd9aAdam Langley	if (newkeys->enc.iv) {
408d059297112922cabb0c674840589be8db821fd9aAdam Langley		explicit_bzero(newkeys->enc.iv, newkeys->enc.block_size);
409d059297112922cabb0c674840589be8db821fd9aAdam Langley		free(newkeys->enc.iv);
410d059297112922cabb0c674840589be8db821fd9aAdam Langley		newkeys->enc.iv = NULL;
411d059297112922cabb0c674840589be8db821fd9aAdam Langley	}
412d059297112922cabb0c674840589be8db821fd9aAdam Langley	free(newkeys->enc.name);
413d059297112922cabb0c674840589be8db821fd9aAdam Langley	explicit_bzero(&newkeys->enc, sizeof(newkeys->enc));
414d059297112922cabb0c674840589be8db821fd9aAdam Langley	free(newkeys->comp.name);
415d059297112922cabb0c674840589be8db821fd9aAdam Langley	explicit_bzero(&newkeys->comp, sizeof(newkeys->comp));
416d059297112922cabb0c674840589be8db821fd9aAdam Langley	mac_clear(&newkeys->mac);
417d059297112922cabb0c674840589be8db821fd9aAdam Langley	if (newkeys->mac.key) {
418d059297112922cabb0c674840589be8db821fd9aAdam Langley		explicit_bzero(newkeys->mac.key, newkeys->mac.key_len);
419d059297112922cabb0c674840589be8db821fd9aAdam Langley		free(newkeys->mac.key);
420d059297112922cabb0c674840589be8db821fd9aAdam Langley		newkeys->mac.key = NULL;
421d059297112922cabb0c674840589be8db821fd9aAdam Langley	}
422d059297112922cabb0c674840589be8db821fd9aAdam Langley	free(newkeys->mac.name);
423d059297112922cabb0c674840589be8db821fd9aAdam Langley	explicit_bzero(&newkeys->mac, sizeof(newkeys->mac));
424d059297112922cabb0c674840589be8db821fd9aAdam Langley	explicit_bzero(newkeys, sizeof(*newkeys));
425d059297112922cabb0c674840589be8db821fd9aAdam Langley	free(newkeys);
426d059297112922cabb0c674840589be8db821fd9aAdam Langley}
427bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
428d059297112922cabb0c674840589be8db821fd9aAdam Langleyvoid
429d059297112922cabb0c674840589be8db821fd9aAdam Langleykex_free(struct kex *kex)
430d059297112922cabb0c674840589be8db821fd9aAdam Langley{
431d059297112922cabb0c674840589be8db821fd9aAdam Langley	u_int mode;
432d059297112922cabb0c674840589be8db821fd9aAdam Langley
433d059297112922cabb0c674840589be8db821fd9aAdam Langley#ifdef WITH_OPENSSL
434d059297112922cabb0c674840589be8db821fd9aAdam Langley	if (kex->dh)
435d059297112922cabb0c674840589be8db821fd9aAdam Langley		DH_free(kex->dh);
436d059297112922cabb0c674840589be8db821fd9aAdam Langley#ifdef OPENSSL_HAS_ECC
437d059297112922cabb0c674840589be8db821fd9aAdam Langley	if (kex->ec_client_key)
438d059297112922cabb0c674840589be8db821fd9aAdam Langley		EC_KEY_free(kex->ec_client_key);
439d059297112922cabb0c674840589be8db821fd9aAdam Langley#endif /* OPENSSL_HAS_ECC */
440d059297112922cabb0c674840589be8db821fd9aAdam Langley#endif /* WITH_OPENSSL */
441d059297112922cabb0c674840589be8db821fd9aAdam Langley	for (mode = 0; mode < MODE_MAX; mode++) {
442d059297112922cabb0c674840589be8db821fd9aAdam Langley		kex_free_newkeys(kex->newkeys[mode]);
443d059297112922cabb0c674840589be8db821fd9aAdam Langley		kex->newkeys[mode] = NULL;
444d059297112922cabb0c674840589be8db821fd9aAdam Langley	}
445d059297112922cabb0c674840589be8db821fd9aAdam Langley	sshbuf_free(kex->peer);
446d059297112922cabb0c674840589be8db821fd9aAdam Langley	sshbuf_free(kex->my);
447d059297112922cabb0c674840589be8db821fd9aAdam Langley	free(kex->session_id);
448d059297112922cabb0c674840589be8db821fd9aAdam Langley	free(kex->client_version_string);
449d059297112922cabb0c674840589be8db821fd9aAdam Langley	free(kex->server_version_string);
450d059297112922cabb0c674840589be8db821fd9aAdam Langley	free(kex);
451d059297112922cabb0c674840589be8db821fd9aAdam Langley}
452bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
453d059297112922cabb0c674840589be8db821fd9aAdam Langleyint
454d059297112922cabb0c674840589be8db821fd9aAdam Langleykex_setup(struct ssh *ssh, char *proposal[PROPOSAL_MAX])
455d059297112922cabb0c674840589be8db821fd9aAdam Langley{
456d059297112922cabb0c674840589be8db821fd9aAdam Langley	int r;
457d059297112922cabb0c674840589be8db821fd9aAdam Langley
458d059297112922cabb0c674840589be8db821fd9aAdam Langley	if ((r = kex_new(ssh, proposal, &ssh->kex)) != 0)
459d059297112922cabb0c674840589be8db821fd9aAdam Langley		return r;
460d059297112922cabb0c674840589be8db821fd9aAdam Langley	if ((r = kex_send_kexinit(ssh)) != 0) {		/* we start */
461d059297112922cabb0c674840589be8db821fd9aAdam Langley		kex_free(ssh->kex);
462d059297112922cabb0c674840589be8db821fd9aAdam Langley		ssh->kex = NULL;
463d059297112922cabb0c674840589be8db821fd9aAdam Langley		return r;
464bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	}
465d059297112922cabb0c674840589be8db821fd9aAdam Langley	return 0;
466bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman}
467bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
468d059297112922cabb0c674840589be8db821fd9aAdam Langleystatic int
469d059297112922cabb0c674840589be8db821fd9aAdam Langleychoose_enc(struct sshenc *enc, char *client, char *server)
470bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman{
471bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	char *name = match_list(client, server, NULL);
472d059297112922cabb0c674840589be8db821fd9aAdam Langley
473bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	if (name == NULL)
474d059297112922cabb0c674840589be8db821fd9aAdam Langley		return SSH_ERR_NO_CIPHER_ALG_MATCH;
475bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	if ((enc->cipher = cipher_by_name(name)) == NULL)
476d059297112922cabb0c674840589be8db821fd9aAdam Langley		return SSH_ERR_INTERNAL_ERROR;
477bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	enc->name = name;
478bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	enc->enabled = 0;
479bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	enc->iv = NULL;
480d059297112922cabb0c674840589be8db821fd9aAdam Langley	enc->iv_len = cipher_ivlen(enc->cipher);
481bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	enc->key = NULL;
482bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	enc->key_len = cipher_keylen(enc->cipher);
483bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	enc->block_size = cipher_blocksize(enc->cipher);
484d059297112922cabb0c674840589be8db821fd9aAdam Langley	return 0;
485bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman}
486bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
487d059297112922cabb0c674840589be8db821fd9aAdam Langleystatic int
488d059297112922cabb0c674840589be8db821fd9aAdam Langleychoose_mac(struct ssh *ssh, struct sshmac *mac, char *client, char *server)
489bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman{
490bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	char *name = match_list(client, server, NULL);
491d059297112922cabb0c674840589be8db821fd9aAdam Langley
492bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	if (name == NULL)
493d059297112922cabb0c674840589be8db821fd9aAdam Langley		return SSH_ERR_NO_MAC_ALG_MATCH;
494bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	if (mac_setup(mac, name) < 0)
495d059297112922cabb0c674840589be8db821fd9aAdam Langley		return SSH_ERR_INTERNAL_ERROR;
496bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	/* truncate the key */
497d059297112922cabb0c674840589be8db821fd9aAdam Langley	if (ssh->compat & SSH_BUG_HMAC)
498bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		mac->key_len = 16;
499bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	mac->name = name;
500bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	mac->key = NULL;
501bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	mac->enabled = 0;
502d059297112922cabb0c674840589be8db821fd9aAdam Langley	return 0;
503bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman}
504bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
505d059297112922cabb0c674840589be8db821fd9aAdam Langleystatic int
506d059297112922cabb0c674840589be8db821fd9aAdam Langleychoose_comp(struct sshcomp *comp, char *client, char *server)
507bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman{
508bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	char *name = match_list(client, server, NULL);
509d059297112922cabb0c674840589be8db821fd9aAdam Langley
510bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	if (name == NULL)
511d059297112922cabb0c674840589be8db821fd9aAdam Langley		return SSH_ERR_NO_COMPRESS_ALG_MATCH;
512bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	if (strcmp(name, "zlib@openssh.com") == 0) {
513bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		comp->type = COMP_DELAYED;
514bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	} else if (strcmp(name, "zlib") == 0) {
515bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		comp->type = COMP_ZLIB;
516bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	} else if (strcmp(name, "none") == 0) {
517bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		comp->type = COMP_NONE;
518bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	} else {
519d059297112922cabb0c674840589be8db821fd9aAdam Langley		return SSH_ERR_INTERNAL_ERROR;
520bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	}
521bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	comp->name = name;
522d059297112922cabb0c674840589be8db821fd9aAdam Langley	return 0;
523bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman}
524bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
525d059297112922cabb0c674840589be8db821fd9aAdam Langleystatic int
526d059297112922cabb0c674840589be8db821fd9aAdam Langleychoose_kex(struct kex *k, char *client, char *server)
527bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman{
528d059297112922cabb0c674840589be8db821fd9aAdam Langley	const struct kexalg *kexalg;
529d059297112922cabb0c674840589be8db821fd9aAdam Langley
530bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	k->name = match_list(client, server, NULL);
531d059297112922cabb0c674840589be8db821fd9aAdam Langley
532bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	if (k->name == NULL)
533d059297112922cabb0c674840589be8db821fd9aAdam Langley		return SSH_ERR_NO_KEX_ALG_MATCH;
534d059297112922cabb0c674840589be8db821fd9aAdam Langley	if ((kexalg = kex_alg_by_name(k->name)) == NULL)
535d059297112922cabb0c674840589be8db821fd9aAdam Langley		return SSH_ERR_INTERNAL_ERROR;
536d059297112922cabb0c674840589be8db821fd9aAdam Langley	k->kex_type = kexalg->type;
537d059297112922cabb0c674840589be8db821fd9aAdam Langley	k->hash_alg = kexalg->hash_alg;
538d059297112922cabb0c674840589be8db821fd9aAdam Langley	k->ec_nid = kexalg->ec_nid;
539d059297112922cabb0c674840589be8db821fd9aAdam Langley	return 0;
540bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman}
541bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
542d059297112922cabb0c674840589be8db821fd9aAdam Langleystatic int
543d059297112922cabb0c674840589be8db821fd9aAdam Langleychoose_hostkeyalg(struct kex *k, char *client, char *server)
544bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman{
545bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	char *hostkeyalg = match_list(client, server, NULL);
546d059297112922cabb0c674840589be8db821fd9aAdam Langley
547bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	if (hostkeyalg == NULL)
548d059297112922cabb0c674840589be8db821fd9aAdam Langley		return SSH_ERR_NO_HOSTKEY_ALG_MATCH;
549d059297112922cabb0c674840589be8db821fd9aAdam Langley	k->hostkey_type = sshkey_type_from_name(hostkeyalg);
550bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	if (k->hostkey_type == KEY_UNSPEC)
551d059297112922cabb0c674840589be8db821fd9aAdam Langley		return SSH_ERR_INTERNAL_ERROR;
552d059297112922cabb0c674840589be8db821fd9aAdam Langley	k->hostkey_nid = sshkey_ecdsa_nid_from_name(hostkeyalg);
553d059297112922cabb0c674840589be8db821fd9aAdam Langley	free(hostkeyalg);
554d059297112922cabb0c674840589be8db821fd9aAdam Langley	return 0;
555bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman}
556bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
557bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartmanstatic int
558bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartmanproposals_match(char *my[PROPOSAL_MAX], char *peer[PROPOSAL_MAX])
559bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman{
560bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	static int check[] = {
561bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		PROPOSAL_KEX_ALGS, PROPOSAL_SERVER_HOST_KEY_ALGS, -1
562bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	};
563bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	int *idx;
564bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	char *p;
565bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
566bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	for (idx = &check[0]; *idx != -1; idx++) {
567bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		if ((p = strchr(my[*idx], ',')) != NULL)
568bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			*p = '\0';
569bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		if ((p = strchr(peer[*idx], ',')) != NULL)
570bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			*p = '\0';
571bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		if (strcmp(my[*idx], peer[*idx]) != 0) {
572bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			debug2("proposal mismatch: my %s peer %s",
573bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			    my[*idx], peer[*idx]);
574bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			return (0);
575bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		}
576bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	}
577bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	debug2("proposals match");
578bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	return (1);
579bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman}
580bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
581d059297112922cabb0c674840589be8db821fd9aAdam Langleystatic int
582d059297112922cabb0c674840589be8db821fd9aAdam Langleykex_choose_conf(struct ssh *ssh)
583bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman{
584d059297112922cabb0c674840589be8db821fd9aAdam Langley	struct kex *kex = ssh->kex;
585d059297112922cabb0c674840589be8db821fd9aAdam Langley	struct newkeys *newkeys;
586d059297112922cabb0c674840589be8db821fd9aAdam Langley	char **my = NULL, **peer = NULL;
587bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	char **cprop, **sprop;
588bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	int nenc, nmac, ncomp;
589d059297112922cabb0c674840589be8db821fd9aAdam Langley	u_int mode, ctos, need, dh_need, authlen;
590d059297112922cabb0c674840589be8db821fd9aAdam Langley	int r, first_kex_follows;
591bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
592d059297112922cabb0c674840589be8db821fd9aAdam Langley	if ((r = kex_buf2prop(kex->my, NULL, &my)) != 0 ||
593d059297112922cabb0c674840589be8db821fd9aAdam Langley	    (r = kex_buf2prop(kex->peer, &first_kex_follows, &peer)) != 0)
594d059297112922cabb0c674840589be8db821fd9aAdam Langley		goto out;
595bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
596bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	if (kex->server) {
597bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		cprop=peer;
598bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		sprop=my;
599bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	} else {
600bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		cprop=my;
601bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		sprop=peer;
602bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	}
603bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
604bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	/* Check whether server offers roaming */
605bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	if (!kex->server) {
606d059297112922cabb0c674840589be8db821fd9aAdam Langley		char *roaming = match_list(KEX_RESUME,
607d059297112922cabb0c674840589be8db821fd9aAdam Langley		    peer[PROPOSAL_KEX_ALGS], NULL);
608d059297112922cabb0c674840589be8db821fd9aAdam Langley
609bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		if (roaming) {
610bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman			kex->roaming = 1;
611d059297112922cabb0c674840589be8db821fd9aAdam Langley			free(roaming);
612bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		}
613bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	}
614bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
615bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	/* Algorithm Negotiation */
616bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	for (mode = 0; mode < MODE_MAX; mode++) {
617d059297112922cabb0c674840589be8db821fd9aAdam Langley		if ((newkeys = calloc(1, sizeof(*newkeys))) == NULL) {
618d059297112922cabb0c674840589be8db821fd9aAdam Langley			r = SSH_ERR_ALLOC_FAIL;
619d059297112922cabb0c674840589be8db821fd9aAdam Langley			goto out;
620d059297112922cabb0c674840589be8db821fd9aAdam Langley		}
621bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		kex->newkeys[mode] = newkeys;
622bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		ctos = (!kex->server && mode == MODE_OUT) ||
623bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		    (kex->server && mode == MODE_IN);
624bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		nenc  = ctos ? PROPOSAL_ENC_ALGS_CTOS  : PROPOSAL_ENC_ALGS_STOC;
625bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		nmac  = ctos ? PROPOSAL_MAC_ALGS_CTOS  : PROPOSAL_MAC_ALGS_STOC;
626bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		ncomp = ctos ? PROPOSAL_COMP_ALGS_CTOS : PROPOSAL_COMP_ALGS_STOC;
627d059297112922cabb0c674840589be8db821fd9aAdam Langley		if ((r = choose_enc(&newkeys->enc, cprop[nenc],
628d059297112922cabb0c674840589be8db821fd9aAdam Langley		    sprop[nenc])) != 0)
629d059297112922cabb0c674840589be8db821fd9aAdam Langley			goto out;
630d059297112922cabb0c674840589be8db821fd9aAdam Langley		authlen = cipher_authlen(newkeys->enc.cipher);
631d059297112922cabb0c674840589be8db821fd9aAdam Langley		/* ignore mac for authenticated encryption */
632d059297112922cabb0c674840589be8db821fd9aAdam Langley		if (authlen == 0 &&
633d059297112922cabb0c674840589be8db821fd9aAdam Langley		    (r = choose_mac(ssh, &newkeys->mac, cprop[nmac],
634d059297112922cabb0c674840589be8db821fd9aAdam Langley		    sprop[nmac])) != 0)
635d059297112922cabb0c674840589be8db821fd9aAdam Langley			goto out;
636d059297112922cabb0c674840589be8db821fd9aAdam Langley		if ((r = choose_comp(&newkeys->comp, cprop[ncomp],
637d059297112922cabb0c674840589be8db821fd9aAdam Langley		    sprop[ncomp])) != 0)
638d059297112922cabb0c674840589be8db821fd9aAdam Langley			goto out;
639bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		debug("kex: %s %s %s %s",
640bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		    ctos ? "client->server" : "server->client",
641bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		    newkeys->enc.name,
642d059297112922cabb0c674840589be8db821fd9aAdam Langley		    authlen == 0 ? newkeys->mac.name : "<implicit>",
643bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		    newkeys->comp.name);
644bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	}
645d059297112922cabb0c674840589be8db821fd9aAdam Langley	if ((r = choose_kex(kex, cprop[PROPOSAL_KEX_ALGS],
646d059297112922cabb0c674840589be8db821fd9aAdam Langley	    sprop[PROPOSAL_KEX_ALGS])) != 0 ||
647d059297112922cabb0c674840589be8db821fd9aAdam Langley	    (r = choose_hostkeyalg(kex, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS],
648d059297112922cabb0c674840589be8db821fd9aAdam Langley	    sprop[PROPOSAL_SERVER_HOST_KEY_ALGS])) != 0)
649d059297112922cabb0c674840589be8db821fd9aAdam Langley		goto out;
650d059297112922cabb0c674840589be8db821fd9aAdam Langley	need = dh_need = 0;
651bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	for (mode = 0; mode < MODE_MAX; mode++) {
652bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		newkeys = kex->newkeys[mode];
653d059297112922cabb0c674840589be8db821fd9aAdam Langley		need = MAX(need, newkeys->enc.key_len);
654d059297112922cabb0c674840589be8db821fd9aAdam Langley		need = MAX(need, newkeys->enc.block_size);
655d059297112922cabb0c674840589be8db821fd9aAdam Langley		need = MAX(need, newkeys->enc.iv_len);
656d059297112922cabb0c674840589be8db821fd9aAdam Langley		need = MAX(need, newkeys->mac.key_len);
657d059297112922cabb0c674840589be8db821fd9aAdam Langley		dh_need = MAX(dh_need, cipher_seclen(newkeys->enc.cipher));
658d059297112922cabb0c674840589be8db821fd9aAdam Langley		dh_need = MAX(dh_need, newkeys->enc.block_size);
659d059297112922cabb0c674840589be8db821fd9aAdam Langley		dh_need = MAX(dh_need, newkeys->enc.iv_len);
660d059297112922cabb0c674840589be8db821fd9aAdam Langley		dh_need = MAX(dh_need, newkeys->mac.key_len);
661bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	}
662bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	/* XXX need runden? */
663bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	kex->we_need = need;
664d059297112922cabb0c674840589be8db821fd9aAdam Langley	kex->dh_need = dh_need;
665bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
666bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	/* ignore the next message if the proposals do not match */
667bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	if (first_kex_follows && !proposals_match(my, peer) &&
668d059297112922cabb0c674840589be8db821fd9aAdam Langley	    !(ssh->compat & SSH_BUG_FIRSTKEX))
669d059297112922cabb0c674840589be8db821fd9aAdam Langley		ssh->dispatch_skip_packets = 1;
670d059297112922cabb0c674840589be8db821fd9aAdam Langley	r = 0;
671d059297112922cabb0c674840589be8db821fd9aAdam Langley out:
672bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	kex_prop_free(my);
673bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	kex_prop_free(peer);
674d059297112922cabb0c674840589be8db821fd9aAdam Langley	return r;
675bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman}
676bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
677d059297112922cabb0c674840589be8db821fd9aAdam Langleystatic int
678d059297112922cabb0c674840589be8db821fd9aAdam Langleyderive_key(struct ssh *ssh, int id, u_int need, u_char *hash, u_int hashlen,
679d059297112922cabb0c674840589be8db821fd9aAdam Langley    const struct sshbuf *shared_secret, u_char **keyp)
680bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman{
681d059297112922cabb0c674840589be8db821fd9aAdam Langley	struct kex *kex = ssh->kex;
682d059297112922cabb0c674840589be8db821fd9aAdam Langley	struct ssh_digest_ctx *hashctx = NULL;
683bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	char c = id;
684bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	u_int have;
685d059297112922cabb0c674840589be8db821fd9aAdam Langley	size_t mdsz;
686bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	u_char *digest;
687d059297112922cabb0c674840589be8db821fd9aAdam Langley	int r;
688bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
689d059297112922cabb0c674840589be8db821fd9aAdam Langley	if ((mdsz = ssh_digest_bytes(kex->hash_alg)) == 0)
690d059297112922cabb0c674840589be8db821fd9aAdam Langley		return SSH_ERR_INVALID_ARGUMENT;
691d059297112922cabb0c674840589be8db821fd9aAdam Langley	if ((digest = calloc(1, roundup(need, mdsz))) == NULL) {
692d059297112922cabb0c674840589be8db821fd9aAdam Langley		r = SSH_ERR_ALLOC_FAIL;
693d059297112922cabb0c674840589be8db821fd9aAdam Langley		goto out;
694d059297112922cabb0c674840589be8db821fd9aAdam Langley	}
695bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
696bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	/* K1 = HASH(K || H || "A" || session_id) */
697d059297112922cabb0c674840589be8db821fd9aAdam Langley	if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL ||
698d059297112922cabb0c674840589be8db821fd9aAdam Langley	    ssh_digest_update_buffer(hashctx, shared_secret) != 0 ||
699d059297112922cabb0c674840589be8db821fd9aAdam Langley	    ssh_digest_update(hashctx, hash, hashlen) != 0 ||
700d059297112922cabb0c674840589be8db821fd9aAdam Langley	    ssh_digest_update(hashctx, &c, 1) != 0 ||
701d059297112922cabb0c674840589be8db821fd9aAdam Langley	    ssh_digest_update(hashctx, kex->session_id,
702d059297112922cabb0c674840589be8db821fd9aAdam Langley	    kex->session_id_len) != 0 ||
703d059297112922cabb0c674840589be8db821fd9aAdam Langley	    ssh_digest_final(hashctx, digest, mdsz) != 0) {
704d059297112922cabb0c674840589be8db821fd9aAdam Langley		r = SSH_ERR_LIBCRYPTO_ERROR;
705d059297112922cabb0c674840589be8db821fd9aAdam Langley		goto out;
706d059297112922cabb0c674840589be8db821fd9aAdam Langley	}
707d059297112922cabb0c674840589be8db821fd9aAdam Langley	ssh_digest_free(hashctx);
708d059297112922cabb0c674840589be8db821fd9aAdam Langley	hashctx = NULL;
709bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
710bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	/*
711bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	 * expand key:
712bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	 * Kn = HASH(K || H || K1 || K2 || ... || Kn-1)
713bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	 * Key = K1 || K2 || ... || Kn
714bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	 */
715bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	for (have = mdsz; need > have; have += mdsz) {
716d059297112922cabb0c674840589be8db821fd9aAdam Langley		if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL ||
717d059297112922cabb0c674840589be8db821fd9aAdam Langley		    ssh_digest_update_buffer(hashctx, shared_secret) != 0 ||
718d059297112922cabb0c674840589be8db821fd9aAdam Langley		    ssh_digest_update(hashctx, hash, hashlen) != 0 ||
719d059297112922cabb0c674840589be8db821fd9aAdam Langley		    ssh_digest_update(hashctx, digest, have) != 0 ||
720d059297112922cabb0c674840589be8db821fd9aAdam Langley		    ssh_digest_final(hashctx, digest + have, mdsz) != 0) {
721d059297112922cabb0c674840589be8db821fd9aAdam Langley			r = SSH_ERR_LIBCRYPTO_ERROR;
722d059297112922cabb0c674840589be8db821fd9aAdam Langley			goto out;
723d059297112922cabb0c674840589be8db821fd9aAdam Langley		}
724d059297112922cabb0c674840589be8db821fd9aAdam Langley		ssh_digest_free(hashctx);
725d059297112922cabb0c674840589be8db821fd9aAdam Langley		hashctx = NULL;
726bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	}
727bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#ifdef DEBUG_KEX
728bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	fprintf(stderr, "key '%c'== ", c);
729bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	dump_digest("key", digest, need);
730bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#endif
731d059297112922cabb0c674840589be8db821fd9aAdam Langley	*keyp = digest;
732d059297112922cabb0c674840589be8db821fd9aAdam Langley	digest = NULL;
733d059297112922cabb0c674840589be8db821fd9aAdam Langley	r = 0;
734d059297112922cabb0c674840589be8db821fd9aAdam Langley out:
735d059297112922cabb0c674840589be8db821fd9aAdam Langley	if (digest)
736d059297112922cabb0c674840589be8db821fd9aAdam Langley		free(digest);
737d059297112922cabb0c674840589be8db821fd9aAdam Langley	ssh_digest_free(hashctx);
738d059297112922cabb0c674840589be8db821fd9aAdam Langley	return r;
739bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman}
740bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
741bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#define NKEYS	6
742d059297112922cabb0c674840589be8db821fd9aAdam Langleyint
743d059297112922cabb0c674840589be8db821fd9aAdam Langleykex_derive_keys(struct ssh *ssh, u_char *hash, u_int hashlen,
744d059297112922cabb0c674840589be8db821fd9aAdam Langley    const struct sshbuf *shared_secret)
745bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman{
746d059297112922cabb0c674840589be8db821fd9aAdam Langley	struct kex *kex = ssh->kex;
747bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	u_char *keys[NKEYS];
748d059297112922cabb0c674840589be8db821fd9aAdam Langley	u_int i, j, mode, ctos;
749d059297112922cabb0c674840589be8db821fd9aAdam Langley	int r;
750bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
751bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	for (i = 0; i < NKEYS; i++) {
752d059297112922cabb0c674840589be8db821fd9aAdam Langley		if ((r = derive_key(ssh, 'A'+i, kex->we_need, hash, hashlen,
753d059297112922cabb0c674840589be8db821fd9aAdam Langley		    shared_secret, &keys[i])) != 0) {
754d059297112922cabb0c674840589be8db821fd9aAdam Langley			for (j = 0; j < i; j++)
755d059297112922cabb0c674840589be8db821fd9aAdam Langley				free(keys[j]);
756d059297112922cabb0c674840589be8db821fd9aAdam Langley			return r;
757d059297112922cabb0c674840589be8db821fd9aAdam Langley		}
758bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	}
759bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	for (mode = 0; mode < MODE_MAX; mode++) {
760bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		ctos = (!kex->server && mode == MODE_OUT) ||
761bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman		    (kex->server && mode == MODE_IN);
762d059297112922cabb0c674840589be8db821fd9aAdam Langley		kex->newkeys[mode]->enc.iv  = keys[ctos ? 0 : 1];
763d059297112922cabb0c674840589be8db821fd9aAdam Langley		kex->newkeys[mode]->enc.key = keys[ctos ? 2 : 3];
764d059297112922cabb0c674840589be8db821fd9aAdam Langley		kex->newkeys[mode]->mac.key = keys[ctos ? 4 : 5];
765bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	}
766d059297112922cabb0c674840589be8db821fd9aAdam Langley	return 0;
767bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman}
768bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
769d059297112922cabb0c674840589be8db821fd9aAdam Langley#ifdef WITH_OPENSSL
770d059297112922cabb0c674840589be8db821fd9aAdam Langleyint
771d059297112922cabb0c674840589be8db821fd9aAdam Langleykex_derive_keys_bn(struct ssh *ssh, u_char *hash, u_int hashlen,
772d059297112922cabb0c674840589be8db821fd9aAdam Langley    const BIGNUM *secret)
773bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman{
774d059297112922cabb0c674840589be8db821fd9aAdam Langley	struct sshbuf *shared_secret;
775d059297112922cabb0c674840589be8db821fd9aAdam Langley	int r;
776d059297112922cabb0c674840589be8db821fd9aAdam Langley
777d059297112922cabb0c674840589be8db821fd9aAdam Langley	if ((shared_secret = sshbuf_new()) == NULL)
778d059297112922cabb0c674840589be8db821fd9aAdam Langley		return SSH_ERR_ALLOC_FAIL;
779d059297112922cabb0c674840589be8db821fd9aAdam Langley	if ((r = sshbuf_put_bignum2(shared_secret, secret)) == 0)
780d059297112922cabb0c674840589be8db821fd9aAdam Langley		r = kex_derive_keys(ssh, hash, hashlen, shared_secret);
781d059297112922cabb0c674840589be8db821fd9aAdam Langley	sshbuf_free(shared_secret);
782d059297112922cabb0c674840589be8db821fd9aAdam Langley	return r;
783bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman}
784d059297112922cabb0c674840589be8db821fd9aAdam Langley#endif
785bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
786d059297112922cabb0c674840589be8db821fd9aAdam Langley#ifdef WITH_SSH1
787d059297112922cabb0c674840589be8db821fd9aAdam Langleyint
788bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartmanderive_ssh1_session_id(BIGNUM *host_modulus, BIGNUM *server_modulus,
789bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman    u_int8_t cookie[8], u_int8_t id[16])
790bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman{
791d059297112922cabb0c674840589be8db821fd9aAdam Langley	u_int8_t hbuf[2048], sbuf[2048], obuf[SSH_DIGEST_MAX_LENGTH];
792d059297112922cabb0c674840589be8db821fd9aAdam Langley	struct ssh_digest_ctx *hashctx = NULL;
793d059297112922cabb0c674840589be8db821fd9aAdam Langley	size_t hlen, slen;
794d059297112922cabb0c674840589be8db821fd9aAdam Langley	int r;
795d059297112922cabb0c674840589be8db821fd9aAdam Langley
796d059297112922cabb0c674840589be8db821fd9aAdam Langley	hlen = BN_num_bytes(host_modulus);
797d059297112922cabb0c674840589be8db821fd9aAdam Langley	slen = BN_num_bytes(server_modulus);
798d059297112922cabb0c674840589be8db821fd9aAdam Langley	if (hlen < (512 / 8) || (u_int)hlen > sizeof(hbuf) ||
799d059297112922cabb0c674840589be8db821fd9aAdam Langley	    slen < (512 / 8) || (u_int)slen > sizeof(sbuf))
800d059297112922cabb0c674840589be8db821fd9aAdam Langley		return SSH_ERR_KEY_BITS_MISMATCH;
801d059297112922cabb0c674840589be8db821fd9aAdam Langley	if (BN_bn2bin(host_modulus, hbuf) <= 0 ||
802d059297112922cabb0c674840589be8db821fd9aAdam Langley	    BN_bn2bin(server_modulus, sbuf) <= 0) {
803d059297112922cabb0c674840589be8db821fd9aAdam Langley		r = SSH_ERR_LIBCRYPTO_ERROR;
804d059297112922cabb0c674840589be8db821fd9aAdam Langley		goto out;
805d059297112922cabb0c674840589be8db821fd9aAdam Langley	}
806d059297112922cabb0c674840589be8db821fd9aAdam Langley	if ((hashctx = ssh_digest_start(SSH_DIGEST_MD5)) == NULL) {
807d059297112922cabb0c674840589be8db821fd9aAdam Langley		r = SSH_ERR_ALLOC_FAIL;
808d059297112922cabb0c674840589be8db821fd9aAdam Langley		goto out;
809d059297112922cabb0c674840589be8db821fd9aAdam Langley	}
810d059297112922cabb0c674840589be8db821fd9aAdam Langley	if (ssh_digest_update(hashctx, hbuf, hlen) != 0 ||
811d059297112922cabb0c674840589be8db821fd9aAdam Langley	    ssh_digest_update(hashctx, sbuf, slen) != 0 ||
812d059297112922cabb0c674840589be8db821fd9aAdam Langley	    ssh_digest_update(hashctx, cookie, 8) != 0 ||
813d059297112922cabb0c674840589be8db821fd9aAdam Langley	    ssh_digest_final(hashctx, obuf, sizeof(obuf)) != 0) {
814d059297112922cabb0c674840589be8db821fd9aAdam Langley		r = SSH_ERR_LIBCRYPTO_ERROR;
815d059297112922cabb0c674840589be8db821fd9aAdam Langley		goto out;
816d059297112922cabb0c674840589be8db821fd9aAdam Langley	}
817d059297112922cabb0c674840589be8db821fd9aAdam Langley	memcpy(id, obuf, ssh_digest_bytes(SSH_DIGEST_MD5));
818d059297112922cabb0c674840589be8db821fd9aAdam Langley	r = 0;
819d059297112922cabb0c674840589be8db821fd9aAdam Langley out:
820d059297112922cabb0c674840589be8db821fd9aAdam Langley	ssh_digest_free(hashctx);
821d059297112922cabb0c674840589be8db821fd9aAdam Langley	explicit_bzero(hbuf, sizeof(hbuf));
822d059297112922cabb0c674840589be8db821fd9aAdam Langley	explicit_bzero(sbuf, sizeof(sbuf));
823d059297112922cabb0c674840589be8db821fd9aAdam Langley	explicit_bzero(obuf, sizeof(obuf));
824d059297112922cabb0c674840589be8db821fd9aAdam Langley	return r;
825bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman}
826d059297112922cabb0c674840589be8db821fd9aAdam Langley#endif
827bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman
828bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) || defined(DEBUG_KEXECDH)
829bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartmanvoid
830bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartmandump_digest(char *msg, u_char *digest, int len)
831bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman{
832bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman	fprintf(stderr, "%s\n", msg);
833d059297112922cabb0c674840589be8db821fd9aAdam Langley	sshbuf_dump_data(digest, len, stderr);
834bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman}
835bd77cf78387b72b7b3ea870459077672bf75c3b5Greg Hartman#endif
836