blob: 221e030418221cdd99022e0c1b0199ba982e2488 [file] [log] [blame]
Damien Millera664e872000-04-16 11:52:47 +10001/*
2 * Copyright (c) 2000 Markus Friedl. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. All advertising materials mentioning features or use of this software
13 * must display the following acknowledgement:
14 * This product includes software developed by Markus Friedl.
15 * 4. The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30#include "includes.h"
Damien Miller30c3d422000-05-09 11:02:59 +100031RCSID("$Id: kex.c,v 1.8 2000/05/09 01:03:01 damien Exp $");
Damien Millera664e872000-04-16 11:52:47 +100032
33#include "ssh.h"
34#include "ssh2.h"
35#include "xmalloc.h"
36#include "buffer.h"
37#include "bufaux.h"
38#include "cipher.h"
39#include "compat.h"
40
41#include <openssl/bn.h>
42#include <openssl/dh.h>
43
44#include <openssl/crypto.h>
45#include <openssl/bio.h>
46#include <openssl/bn.h>
47#include <openssl/dh.h>
48#include <openssl/pem.h>
49
50#include "kex.h"
51
52Buffer *
53kex_init(char *myproposal[PROPOSAL_MAX])
54{
55 char c = 0;
56 unsigned char cookie[16];
57 u_int32_t rand = 0;
58 int i;
59 Buffer *ki = xmalloc(sizeof(*ki));
60 for (i = 0; i < 16; i++) {
61 if (i % 4 == 0)
62 rand = arc4random();
63 cookie[i] = rand & 0xff;
64 rand >>= 8;
65 }
66 buffer_init(ki);
67 buffer_append(ki, (char *)cookie, sizeof cookie);
68 for (i = 0; i < PROPOSAL_MAX; i++)
69 buffer_put_cstring(ki, myproposal[i]);
70 buffer_append(ki, &c, 1); /* boolean first_kex_packet_follows */
71 buffer_put_int(ki, 0); /* uint32 0 (reserved for future extension) */
72 return ki;
73}
74
75/* diffie-hellman-group1-sha1 */
76
77int
78dh_pub_is_valid(DH *dh, BIGNUM *dh_pub)
79{
80 int i;
81 int n = BN_num_bits(dh_pub);
82 int bits_set = 0;
83
84 /* we only accept g==2 */
85 if (!BN_is_word(dh->g, 2)) {
86 log("invalid DH base != 2");
87 return 0;
88 }
89 if (dh_pub->neg) {
90 log("invalid public DH value: negativ");
91 return 0;
92 }
93 for (i = 0; i <= n; i++)
94 if (BN_is_bit_set(dh_pub, i))
95 bits_set++;
96 debug("bits set: %d/%d", bits_set, BN_num_bits(dh->p));
97
98 /* if g==2 and bits_set==1 then computing log_g(dh_pub) is trivial */
99 if (bits_set > 1 && (BN_cmp(dh_pub, dh->p) == -1))
100 return 1;
101 log("invalid public DH value (%d/%d)", bits_set, BN_num_bits(dh->p));
102 return 0;
103}
104
105DH *
106dh_new_group1()
107{
108 static char *group1 =
109 "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
110 "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
111 "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
112 "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
113 "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381"
114 "FFFFFFFF" "FFFFFFFF";
115 DH *dh;
116 int ret, tries = 0;
117 dh = DH_new();
118 if(dh == NULL)
119 fatal("DH_new");
120 ret = BN_hex2bn(&dh->p, group1);
121 if(ret<0)
122 fatal("BN_hex2bn");
123 dh->g = BN_new();
124 if(dh->g == NULL)
125 fatal("DH_new g");
126 BN_set_word(dh->g, 2);
127 do {
128 if (DH_generate_key(dh) == 0)
129 fatal("DH_generate_key");
130 if (tries++ > 10)
131 fatal("dh_new_group1: too many bad keys: giving up");
132 } while (!dh_pub_is_valid(dh, dh->pub_key));
133 return dh;
134}
135
136void
137bignum_print(BIGNUM *b)
138{
139 BN_print_fp(stderr,b);
140}
141
142void
143dump_digest(unsigned char *digest, int len)
144{
145 int i;
146 for (i = 0; i< len; i++){
147 fprintf(stderr, "%02x", digest[i]);
148 if(i%2!=0)
149 fprintf(stderr, " ");
150 }
151 fprintf(stderr, "\n");
152}
153
154unsigned char *
155kex_hash(
156 char *client_version_string,
157 char *server_version_string,
158 char *ckexinit, int ckexinitlen,
159 char *skexinit, int skexinitlen,
160 char *serverhostkeyblob, int sbloblen,
161 BIGNUM *client_dh_pub,
162 BIGNUM *server_dh_pub,
163 BIGNUM *shared_secret)
164{
165 Buffer b;
166 static unsigned char digest[EVP_MAX_MD_SIZE];
167 EVP_MD *evp_md = EVP_sha1();
168 EVP_MD_CTX md;
169
170 buffer_init(&b);
171 buffer_put_string(&b, client_version_string, strlen(client_version_string));
172 buffer_put_string(&b, server_version_string, strlen(server_version_string));
173
174 /* kexinit messages: fake header: len+SSH2_MSG_KEXINIT */
175 buffer_put_int(&b, ckexinitlen+1);
176 buffer_put_char(&b, SSH2_MSG_KEXINIT);
177 buffer_append(&b, ckexinit, ckexinitlen);
178 buffer_put_int(&b, skexinitlen+1);
179 buffer_put_char(&b, SSH2_MSG_KEXINIT);
180 buffer_append(&b, skexinit, skexinitlen);
181
182 buffer_put_string(&b, serverhostkeyblob, sbloblen);
183 buffer_put_bignum2(&b, client_dh_pub);
184 buffer_put_bignum2(&b, server_dh_pub);
185 buffer_put_bignum2(&b, shared_secret);
186
187#ifdef DEBUG_KEX
188 buffer_dump(&b);
189#endif
190
191 EVP_DigestInit(&md, evp_md);
192 EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b));
193 EVP_DigestFinal(&md, digest, NULL);
194
195 buffer_free(&b);
196
197#ifdef DEBUG_KEX
198 dump_digest(digest, evp_md->md_size);
199#endif
200 return digest;
201}
202
203unsigned char *
204derive_key(int id, int need, char unsigned *hash, BIGNUM *shared_secret)
205{
206 Buffer b;
207 EVP_MD *evp_md = EVP_sha1();
208 EVP_MD_CTX md;
209 char c = id;
210 int have;
211 int mdsz = evp_md->md_size;
212 unsigned char *digest = xmalloc(((need+mdsz-1)/mdsz)*mdsz);
213
214 buffer_init(&b);
215 buffer_put_bignum2(&b, shared_secret);
216
217 EVP_DigestInit(&md, evp_md);
218 EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b)); /* shared_secret K */
219 EVP_DigestUpdate(&md, hash, mdsz); /* transport-06 */
220 EVP_DigestUpdate(&md, &c, 1); /* key id */
221 EVP_DigestUpdate(&md, hash, mdsz); /* session id */
222 EVP_DigestFinal(&md, digest, NULL);
223
224 /* expand */
225 for (have = mdsz; need > have; have += mdsz) {
226 EVP_DigestInit(&md, evp_md);
227 EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b));
228 EVP_DigestUpdate(&md, hash, mdsz);
229 EVP_DigestUpdate(&md, digest, have);
230 EVP_DigestFinal(&md, digest + have, NULL);
231 }
232 buffer_free(&b);
233#ifdef DEBUG_KEX
234 fprintf(stderr, "Digest '%c'== ", c);
235 dump_digest(digest, need);
236#endif
237 return digest;
238}
239
240#define NKEYS 6
241
242#define MAX_PROP 20
243#define SEP ","
244
245char *
246get_match(char *client, char *server)
247{
248 char *sproposals[MAX_PROP];
249 char *p;
250 int i, j, nproposals;
251
252 for ((p = strtok(server, SEP)), i=0; p; (p = strtok(NULL, SEP)), i++) {
253 if (i < MAX_PROP)
254 sproposals[i] = p;
255 else
256 break;
257 }
258 nproposals = i;
259
260 for ((p = strtok(client, SEP)), i=0; p; (p = strtok(NULL, SEP)), i++) {
261 for (j = 0; j < nproposals; j++)
262 if (strcmp(p, sproposals[j]) == 0)
263 return xstrdup(p);
264 }
265 return NULL;
266}
267void
268choose_enc(Enc *enc, char *client, char *server)
269{
270 char *name = get_match(client, server);
271 if (name == NULL)
272 fatal("no matching cipher found: client %s server %s", client, server);
273 enc->type = cipher_number(name);
274
275 switch (enc->type) {
276 case SSH_CIPHER_3DES_CBC:
277 enc->key_len = 24;
278 enc->iv_len = 8;
279 enc->block_size = 8;
280 break;
281 case SSH_CIPHER_BLOWFISH_CBC:
282 case SSH_CIPHER_CAST128_CBC:
283 enc->key_len = 16;
284 enc->iv_len = 8;
285 enc->block_size = 8;
286 break;
287 case SSH_CIPHER_ARCFOUR:
288 enc->key_len = 16;
289 enc->iv_len = 0;
290 enc->block_size = 8;
291 break;
292 default:
293 fatal("unsupported cipher %s", name);
294 }
295 enc->name = name;
296 enc->enabled = 0;
297 enc->iv = NULL;
298 enc->key = NULL;
299}
300void
301choose_mac(Mac *mac, char *client, char *server)
302{
303 char *name = get_match(client, server);
304 if (name == NULL)
305 fatal("no matching mac found: client %s server %s", client, server);
306 if (strcmp(name, "hmac-md5") == 0) {
307 mac->md = EVP_md5();
308 } else if (strcmp(name, "hmac-sha1") == 0) {
309 mac->md = EVP_sha1();
310 } else if (strcmp(name, "hmac-ripemd160@openssh.com") == 0) {
311 mac->md = EVP_ripemd160();
312 } else {
313 fatal("unsupported mac %s", name);
314 }
315 mac->name = name;
316 mac->mac_len = mac->md->md_size;
Damien Miller30c3d422000-05-09 11:02:59 +1000317 mac->key_len = (datafellows & SSH_BUG_HMAC) ? 16 : mac->mac_len;
Damien Millera664e872000-04-16 11:52:47 +1000318 mac->key = NULL;
319 mac->enabled = 0;
320}
321void
322choose_comp(Comp *comp, char *client, char *server)
323{
324 char *name = get_match(client, server);
325 if (name == NULL)
326 fatal("no matching comp found: client %s server %s", client, server);
327 if (strcmp(name, "zlib") == 0) {
328 comp->type = 1;
329 } else if (strcmp(name, "none") == 0) {
330 comp->type = 0;
331 } else {
332 fatal("unsupported comp %s", name);
333 }
334 comp->name = name;
335}
336void
337choose_kex(Kex *k, char *client, char *server)
338{
339 k->name = get_match(client, server);
340 if (k->name == NULL)
341 fatal("no kex alg");
342 if (strcmp(k->name, KEX_DH1) != 0)
343 fatal("bad kex alg %s", k->name);
344}
345void
346choose_hostkeyalg(Kex *k, char *client, char *server)
347{
348 k->hostkeyalg = get_match(client, server);
349 if (k->hostkeyalg == NULL)
350 fatal("no hostkey alg");
351 if (strcmp(k->hostkeyalg, KEX_DSS) != 0)
352 fatal("bad hostkey alg %s", k->hostkeyalg);
353}
354
355Kex *
356kex_choose_conf(char *cprop[PROPOSAL_MAX], char *sprop[PROPOSAL_MAX], int server)
357{
358 int i;
359 int mode;
360 int ctos; /* direction: if true client-to-server */
361 int need;
362 Kex *k;
363
364 k = xmalloc(sizeof(*k));
365 memset(k, 0, sizeof(*k));
366 k->server = server;
367
368 for (mode = 0; mode < MODE_MAX; mode++) {
369 int nenc, nmac, ncomp;
370 ctos = (!k->server && mode == MODE_OUT) || (k->server && mode == MODE_IN);
371 nenc = ctos ? PROPOSAL_ENC_ALGS_CTOS : PROPOSAL_ENC_ALGS_STOC;
372 nmac = ctos ? PROPOSAL_MAC_ALGS_CTOS : PROPOSAL_MAC_ALGS_STOC;
373 ncomp = ctos ? PROPOSAL_COMP_ALGS_CTOS : PROPOSAL_COMP_ALGS_STOC;
374 choose_enc (&k->enc [mode], cprop[nenc], sprop[nenc]);
375 choose_mac (&k->mac [mode], cprop[nmac], sprop[nmac]);
376 choose_comp(&k->comp[mode], cprop[ncomp], sprop[ncomp]);
377 debug("kex: %s %s %s %s",
378 ctos ? "client->server" : "server->client",
379 k->enc[mode].name,
380 k->mac[mode].name,
381 k->comp[mode].name);
382 }
383 choose_kex(k, cprop[PROPOSAL_KEX_ALGS], sprop[PROPOSAL_KEX_ALGS]);
384 choose_hostkeyalg(k, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS],
385 sprop[PROPOSAL_SERVER_HOST_KEY_ALGS]);
386 for (i = 0; i < PROPOSAL_MAX; i++) {
387 xfree(cprop[i]);
388 xfree(sprop[i]);
389 }
390 need = 0;
391 for (mode = 0; mode < MODE_MAX; mode++) {
392 if (need < k->enc[mode].key_len)
393 need = k->enc[mode].key_len;
394 if (need < k->enc[mode].iv_len)
395 need = k->enc[mode].iv_len;
396 if (need < k->mac[mode].key_len)
397 need = k->mac[mode].key_len;
398 }
399 /* need runden? */
400#define WE_NEED 32
401 k->we_need = WE_NEED;
402 k->we_need = need;
403 return k;
404}
405
406int
407kex_derive_keys(Kex *k, unsigned char *hash, BIGNUM *shared_secret)
408{
409 int i;
410 int mode;
411 int ctos;
412 unsigned char *keys[NKEYS];
413
414 for (i = 0; i < NKEYS; i++)
415 keys[i] = derive_key('A'+i, k->we_need, hash, shared_secret);
416
417 for (mode = 0; mode < MODE_MAX; mode++) {
418 ctos = (!k->server && mode == MODE_OUT) || (k->server && mode == MODE_IN);
419 k->enc[mode].iv = keys[ctos ? 0 : 1];
420 k->enc[mode].key = keys[ctos ? 2 : 3];
421 k->mac[mode].key = keys[ctos ? 4 : 5];
422 }
423 return 0;
424}