blob: 68b9e522e73d96bfdc9b8c502ccb700ef7427888 [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.
Damien Millera664e872000-04-16 11:52:47 +100012 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25#include "includes.h"
Damien Miller874d77b2000-10-14 16:23:11 +110026RCSID("$OpenBSD: kex.c,v 1.12 2000/10/11 20:27:23 markus Exp $");
Damien Millera664e872000-04-16 11:52:47 +100027
28#include "ssh.h"
29#include "ssh2.h"
30#include "xmalloc.h"
31#include "buffer.h"
32#include "bufaux.h"
Damien Millerb1715dc2000-05-30 13:44:51 +100033#include "packet.h"
Damien Millera664e872000-04-16 11:52:47 +100034#include "compat.h"
35
36#include <openssl/bn.h>
37#include <openssl/dh.h>
38
39#include <openssl/crypto.h>
40#include <openssl/bio.h>
41#include <openssl/bn.h>
42#include <openssl/dh.h>
43#include <openssl/pem.h>
44
45#include "kex.h"
46
Damien Millerb1715dc2000-05-30 13:44:51 +100047#define KEX_COOKIE_LEN 16
48
Damien Millera664e872000-04-16 11:52:47 +100049Buffer *
50kex_init(char *myproposal[PROPOSAL_MAX])
51{
Damien Millerb1715dc2000-05-30 13:44:51 +100052 int first_kex_packet_follows = 0;
53 unsigned char cookie[KEX_COOKIE_LEN];
Damien Millera664e872000-04-16 11:52:47 +100054 u_int32_t rand = 0;
55 int i;
56 Buffer *ki = xmalloc(sizeof(*ki));
Damien Millerb1715dc2000-05-30 13:44:51 +100057 for (i = 0; i < KEX_COOKIE_LEN; i++) {
Damien Millera664e872000-04-16 11:52:47 +100058 if (i % 4 == 0)
59 rand = arc4random();
60 cookie[i] = rand & 0xff;
61 rand >>= 8;
62 }
63 buffer_init(ki);
64 buffer_append(ki, (char *)cookie, sizeof cookie);
65 for (i = 0; i < PROPOSAL_MAX; i++)
66 buffer_put_cstring(ki, myproposal[i]);
Damien Millerb1715dc2000-05-30 13:44:51 +100067 buffer_put_char(ki, first_kex_packet_follows);
68 buffer_put_int(ki, 0); /* uint32 reserved */
Damien Millera664e872000-04-16 11:52:47 +100069 return ki;
70}
71
Damien Millerb1715dc2000-05-30 13:44:51 +100072/* send kexinit, parse and save reply */
73void
74kex_exchange_kexinit(
75 Buffer *my_kexinit, Buffer *peer_kexint,
76 char *peer_proposal[PROPOSAL_MAX])
77{
78 int i;
79 char *ptr;
80 int plen;
81
82 debug("send KEXINIT");
83 packet_start(SSH2_MSG_KEXINIT);
84 packet_put_raw(buffer_ptr(my_kexinit), buffer_len(my_kexinit));
85 packet_send();
86 packet_write_wait();
87 debug("done");
88
89 /*
90 * read and save raw KEXINIT payload in buffer. this is used during
91 * computation of the session_id and the session keys.
92 */
93 debug("wait KEXINIT");
94 packet_read_expect(&plen, SSH2_MSG_KEXINIT);
95 ptr = packet_get_raw(&plen);
96 buffer_append(peer_kexint, ptr, plen);
97
98 /* parse packet and save algorithm proposal */
99 /* skip cookie */
100 for (i = 0; i < KEX_COOKIE_LEN; i++)
101 packet_get_char();
102 /* extract kex init proposal strings */
103 for (i = 0; i < PROPOSAL_MAX; i++) {
104 peer_proposal[i] = packet_get_string(NULL);
105 debug("got kexinit: %s", peer_proposal[i]);
106 }
107 /* first kex follow / reserved */
108 i = packet_get_char();
109 debug("first kex follow: %d ", i);
110 i = packet_get_int();
111 debug("reserved: %d ", i);
112 packet_done();
113 debug("done");
114}
115
Damien Millera664e872000-04-16 11:52:47 +1000116/* diffie-hellman-group1-sha1 */
117
118int
119dh_pub_is_valid(DH *dh, BIGNUM *dh_pub)
120{
121 int i;
122 int n = BN_num_bits(dh_pub);
123 int bits_set = 0;
124
Damien Millera664e872000-04-16 11:52:47 +1000125 if (dh_pub->neg) {
126 log("invalid public DH value: negativ");
127 return 0;
128 }
129 for (i = 0; i <= n; i++)
130 if (BN_is_bit_set(dh_pub, i))
131 bits_set++;
132 debug("bits set: %d/%d", bits_set, BN_num_bits(dh->p));
133
134 /* if g==2 and bits_set==1 then computing log_g(dh_pub) is trivial */
135 if (bits_set > 1 && (BN_cmp(dh_pub, dh->p) == -1))
136 return 1;
137 log("invalid public DH value (%d/%d)", bits_set, BN_num_bits(dh->p));
138 return 0;
139}
140
141DH *
Damien Miller874d77b2000-10-14 16:23:11 +1100142dh_gen_key(DH *dh)
Damien Millera664e872000-04-16 11:52:47 +1000143{
Damien Miller874d77b2000-10-14 16:23:11 +1100144 int tries = 0;
145
Damien Millera664e872000-04-16 11:52:47 +1000146 do {
147 if (DH_generate_key(dh) == 0)
148 fatal("DH_generate_key");
149 if (tries++ > 10)
150 fatal("dh_new_group1: too many bad keys: giving up");
151 } while (!dh_pub_is_valid(dh, dh->pub_key));
152 return dh;
153}
154
Damien Miller874d77b2000-10-14 16:23:11 +1100155DH *
156dh_new_group_asc(const char *gen, const char *modulus)
157{
158 DH *dh;
159 int ret;
160
161 dh = DH_new();
162 if (dh == NULL)
163 fatal("DH_new");
164
165 if ((ret = BN_hex2bn(&dh->p, modulus)) < 0)
166 fatal("BN_hex2bn p");
167 if ((ret = BN_hex2bn(&dh->g, gen)) < 0)
168 fatal("BN_hex2bn g");
169
170 return (dh_gen_key(dh));
171}
172
173DH *
174dh_new_group(BIGNUM *gen, BIGNUM *modulus)
175{
176 DH *dh;
177
178 dh = DH_new();
179 if (dh == NULL)
180 fatal("DH_new");
181 dh->p = modulus;
182 dh->g = gen;
183
184 return (dh_gen_key(dh));
185}
186
187DH *
188dh_new_group1()
189{
190 static char *gen = "2", *group1 =
191 "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
192 "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
193 "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
194 "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
195 "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381"
196 "FFFFFFFF" "FFFFFFFF";
197
198 return (dh_new_group_asc(gen, group1));
199}
200
Damien Millera664e872000-04-16 11:52:47 +1000201void
Damien Millera664e872000-04-16 11:52:47 +1000202dump_digest(unsigned char *digest, int len)
203{
204 int i;
205 for (i = 0; i< len; i++){
206 fprintf(stderr, "%02x", digest[i]);
207 if(i%2!=0)
208 fprintf(stderr, " ");
209 }
210 fprintf(stderr, "\n");
211}
212
213unsigned char *
214kex_hash(
215 char *client_version_string,
216 char *server_version_string,
217 char *ckexinit, int ckexinitlen,
218 char *skexinit, int skexinitlen,
219 char *serverhostkeyblob, int sbloblen,
220 BIGNUM *client_dh_pub,
221 BIGNUM *server_dh_pub,
222 BIGNUM *shared_secret)
223{
224 Buffer b;
225 static unsigned char digest[EVP_MAX_MD_SIZE];
226 EVP_MD *evp_md = EVP_sha1();
227 EVP_MD_CTX md;
228
229 buffer_init(&b);
230 buffer_put_string(&b, client_version_string, strlen(client_version_string));
231 buffer_put_string(&b, server_version_string, strlen(server_version_string));
232
233 /* kexinit messages: fake header: len+SSH2_MSG_KEXINIT */
234 buffer_put_int(&b, ckexinitlen+1);
235 buffer_put_char(&b, SSH2_MSG_KEXINIT);
236 buffer_append(&b, ckexinit, ckexinitlen);
237 buffer_put_int(&b, skexinitlen+1);
238 buffer_put_char(&b, SSH2_MSG_KEXINIT);
239 buffer_append(&b, skexinit, skexinitlen);
240
241 buffer_put_string(&b, serverhostkeyblob, sbloblen);
242 buffer_put_bignum2(&b, client_dh_pub);
243 buffer_put_bignum2(&b, server_dh_pub);
244 buffer_put_bignum2(&b, shared_secret);
245
246#ifdef DEBUG_KEX
247 buffer_dump(&b);
248#endif
249
250 EVP_DigestInit(&md, evp_md);
251 EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b));
252 EVP_DigestFinal(&md, digest, NULL);
253
254 buffer_free(&b);
255
256#ifdef DEBUG_KEX
257 dump_digest(digest, evp_md->md_size);
258#endif
259 return digest;
260}
261
262unsigned char *
Damien Miller874d77b2000-10-14 16:23:11 +1100263kex_hash_gex(
264 char *client_version_string,
265 char *server_version_string,
266 char *ckexinit, int ckexinitlen,
267 char *skexinit, int skexinitlen,
268 char *serverhostkeyblob, int sbloblen,
269 int minbits, BIGNUM *prime, BIGNUM *gen,
270 BIGNUM *client_dh_pub,
271 BIGNUM *server_dh_pub,
272 BIGNUM *shared_secret)
273{
274 Buffer b;
275 static unsigned char digest[EVP_MAX_MD_SIZE];
276 EVP_MD *evp_md = EVP_sha1();
277 EVP_MD_CTX md;
278
279 buffer_init(&b);
280 buffer_put_string(&b, client_version_string, strlen(client_version_string));
281 buffer_put_string(&b, server_version_string, strlen(server_version_string));
282
283 /* kexinit messages: fake header: len+SSH2_MSG_KEXINIT */
284 buffer_put_int(&b, ckexinitlen+1);
285 buffer_put_char(&b, SSH2_MSG_KEXINIT);
286 buffer_append(&b, ckexinit, ckexinitlen);
287 buffer_put_int(&b, skexinitlen+1);
288 buffer_put_char(&b, SSH2_MSG_KEXINIT);
289 buffer_append(&b, skexinit, skexinitlen);
290
291 buffer_put_string(&b, serverhostkeyblob, sbloblen);
292 buffer_put_int(&b, minbits);
293 buffer_put_bignum2(&b, prime);
294 buffer_put_bignum2(&b, gen);
295 buffer_put_bignum2(&b, client_dh_pub);
296 buffer_put_bignum2(&b, server_dh_pub);
297 buffer_put_bignum2(&b, shared_secret);
298
299#ifdef DEBUG_KEX
300 buffer_dump(&b);
301#endif
302
303 EVP_DigestInit(&md, evp_md);
304 EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b));
305 EVP_DigestFinal(&md, digest, NULL);
306
307 buffer_free(&b);
308
309#ifdef DEBUG_KEX
310 dump_digest(digest, evp_md->md_size);
311#endif
312 return digest;
313}
314
315unsigned char *
Damien Millera664e872000-04-16 11:52:47 +1000316derive_key(int id, int need, char unsigned *hash, BIGNUM *shared_secret)
317{
318 Buffer b;
319 EVP_MD *evp_md = EVP_sha1();
320 EVP_MD_CTX md;
321 char c = id;
322 int have;
323 int mdsz = evp_md->md_size;
324 unsigned char *digest = xmalloc(((need+mdsz-1)/mdsz)*mdsz);
325
326 buffer_init(&b);
327 buffer_put_bignum2(&b, shared_secret);
328
329 EVP_DigestInit(&md, evp_md);
330 EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b)); /* shared_secret K */
331 EVP_DigestUpdate(&md, hash, mdsz); /* transport-06 */
332 EVP_DigestUpdate(&md, &c, 1); /* key id */
333 EVP_DigestUpdate(&md, hash, mdsz); /* session id */
334 EVP_DigestFinal(&md, digest, NULL);
335
336 /* expand */
337 for (have = mdsz; need > have; have += mdsz) {
338 EVP_DigestInit(&md, evp_md);
339 EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b));
340 EVP_DigestUpdate(&md, hash, mdsz);
341 EVP_DigestUpdate(&md, digest, have);
342 EVP_DigestFinal(&md, digest + have, NULL);
343 }
344 buffer_free(&b);
345#ifdef DEBUG_KEX
346 fprintf(stderr, "Digest '%c'== ", c);
347 dump_digest(digest, need);
348#endif
349 return digest;
350}
351
352#define NKEYS 6
353
354#define MAX_PROP 20
355#define SEP ","
356
357char *
358get_match(char *client, char *server)
359{
360 char *sproposals[MAX_PROP];
Damien Miller37023962000-07-11 17:31:38 +1000361 char *c, *s, *p, *ret, *cp, *sp;
Damien Millera664e872000-04-16 11:52:47 +1000362 int i, j, nproposals;
363
Damien Miller37023962000-07-11 17:31:38 +1000364 c = cp = xstrdup(client);
365 s = sp = xstrdup(server);
Damien Millerb1715dc2000-05-30 13:44:51 +1000366
Damien Miller37023962000-07-11 17:31:38 +1000367 for ((p = strsep(&sp, SEP)), i=0; p && *p != '\0';
368 (p = strsep(&sp, SEP)), i++) {
Damien Millera664e872000-04-16 11:52:47 +1000369 if (i < MAX_PROP)
370 sproposals[i] = p;
371 else
372 break;
373 }
374 nproposals = i;
375
Damien Miller37023962000-07-11 17:31:38 +1000376 for ((p = strsep(&cp, SEP)), i=0; p && *p != '\0';
377 (p = strsep(&cp, SEP)), i++) {
Damien Millerb1715dc2000-05-30 13:44:51 +1000378 for (j = 0; j < nproposals; j++) {
379 if (strcmp(p, sproposals[j]) == 0) {
380 ret = xstrdup(p);
381 xfree(c);
382 xfree(s);
383 return ret;
384 }
385 }
Damien Millera664e872000-04-16 11:52:47 +1000386 }
Damien Millerb1715dc2000-05-30 13:44:51 +1000387 xfree(c);
388 xfree(s);
Damien Millera664e872000-04-16 11:52:47 +1000389 return NULL;
390}
391void
392choose_enc(Enc *enc, char *client, char *server)
393{
394 char *name = get_match(client, server);
395 if (name == NULL)
396 fatal("no matching cipher found: client %s server %s", client, server);
Damien Miller874d77b2000-10-14 16:23:11 +1100397 enc->cipher = cipher_by_name(name);
398 if (enc->cipher == NULL)
399 fatal("matching cipher is not supported: %s", name);
Damien Millera664e872000-04-16 11:52:47 +1000400 enc->name = name;
401 enc->enabled = 0;
402 enc->iv = NULL;
403 enc->key = NULL;
404}
405void
406choose_mac(Mac *mac, char *client, char *server)
407{
408 char *name = get_match(client, server);
409 if (name == NULL)
410 fatal("no matching mac found: client %s server %s", client, server);
411 if (strcmp(name, "hmac-md5") == 0) {
412 mac->md = EVP_md5();
413 } else if (strcmp(name, "hmac-sha1") == 0) {
414 mac->md = EVP_sha1();
415 } else if (strcmp(name, "hmac-ripemd160@openssh.com") == 0) {
416 mac->md = EVP_ripemd160();
417 } else {
418 fatal("unsupported mac %s", name);
419 }
420 mac->name = name;
421 mac->mac_len = mac->md->md_size;
Damien Miller30c3d422000-05-09 11:02:59 +1000422 mac->key_len = (datafellows & SSH_BUG_HMAC) ? 16 : mac->mac_len;
Damien Millera664e872000-04-16 11:52:47 +1000423 mac->key = NULL;
424 mac->enabled = 0;
425}
426void
427choose_comp(Comp *comp, char *client, char *server)
428{
429 char *name = get_match(client, server);
430 if (name == NULL)
431 fatal("no matching comp found: client %s server %s", client, server);
432 if (strcmp(name, "zlib") == 0) {
433 comp->type = 1;
434 } else if (strcmp(name, "none") == 0) {
435 comp->type = 0;
436 } else {
437 fatal("unsupported comp %s", name);
438 }
439 comp->name = name;
440}
441void
442choose_kex(Kex *k, char *client, char *server)
443{
444 k->name = get_match(client, server);
445 if (k->name == NULL)
446 fatal("no kex alg");
Damien Miller874d77b2000-10-14 16:23:11 +1100447 if (strcmp(k->name, KEX_DH1) == 0) {
448 k->kex_type = DH_GRP1_SHA1;
449 } else if (strcmp(k->name, KEX_DHGEX) == 0) {
450 k->kex_type = DH_GEX_SHA1;
451 } else
Damien Millera664e872000-04-16 11:52:47 +1000452 fatal("bad kex alg %s", k->name);
453}
454void
455choose_hostkeyalg(Kex *k, char *client, char *server)
456{
457 k->hostkeyalg = get_match(client, server);
458 if (k->hostkeyalg == NULL)
459 fatal("no hostkey alg");
460 if (strcmp(k->hostkeyalg, KEX_DSS) != 0)
461 fatal("bad hostkey alg %s", k->hostkeyalg);
462}
463
464Kex *
465kex_choose_conf(char *cprop[PROPOSAL_MAX], char *sprop[PROPOSAL_MAX], int server)
466{
Damien Millera664e872000-04-16 11:52:47 +1000467 int mode;
468 int ctos; /* direction: if true client-to-server */
469 int need;
470 Kex *k;
471
472 k = xmalloc(sizeof(*k));
473 memset(k, 0, sizeof(*k));
474 k->server = server;
475
476 for (mode = 0; mode < MODE_MAX; mode++) {
477 int nenc, nmac, ncomp;
478 ctos = (!k->server && mode == MODE_OUT) || (k->server && mode == MODE_IN);
479 nenc = ctos ? PROPOSAL_ENC_ALGS_CTOS : PROPOSAL_ENC_ALGS_STOC;
480 nmac = ctos ? PROPOSAL_MAC_ALGS_CTOS : PROPOSAL_MAC_ALGS_STOC;
481 ncomp = ctos ? PROPOSAL_COMP_ALGS_CTOS : PROPOSAL_COMP_ALGS_STOC;
482 choose_enc (&k->enc [mode], cprop[nenc], sprop[nenc]);
483 choose_mac (&k->mac [mode], cprop[nmac], sprop[nmac]);
484 choose_comp(&k->comp[mode], cprop[ncomp], sprop[ncomp]);
485 debug("kex: %s %s %s %s",
486 ctos ? "client->server" : "server->client",
487 k->enc[mode].name,
488 k->mac[mode].name,
489 k->comp[mode].name);
490 }
491 choose_kex(k, cprop[PROPOSAL_KEX_ALGS], sprop[PROPOSAL_KEX_ALGS]);
492 choose_hostkeyalg(k, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS],
493 sprop[PROPOSAL_SERVER_HOST_KEY_ALGS]);
Damien Millera664e872000-04-16 11:52:47 +1000494 need = 0;
495 for (mode = 0; mode < MODE_MAX; mode++) {
Damien Miller874d77b2000-10-14 16:23:11 +1100496 if (need < k->enc[mode].cipher->key_len)
497 need = k->enc[mode].cipher->key_len;
498 if (need < k->enc[mode].cipher->block_size)
499 need = k->enc[mode].cipher->block_size;
Damien Millera664e872000-04-16 11:52:47 +1000500 if (need < k->mac[mode].key_len)
501 need = k->mac[mode].key_len;
502 }
Damien Millerb1715dc2000-05-30 13:44:51 +1000503 /* XXX need runden? */
Damien Millera664e872000-04-16 11:52:47 +1000504 k->we_need = need;
505 return k;
506}
507
508int
509kex_derive_keys(Kex *k, unsigned char *hash, BIGNUM *shared_secret)
510{
511 int i;
512 int mode;
513 int ctos;
514 unsigned char *keys[NKEYS];
515
516 for (i = 0; i < NKEYS; i++)
517 keys[i] = derive_key('A'+i, k->we_need, hash, shared_secret);
518
519 for (mode = 0; mode < MODE_MAX; mode++) {
520 ctos = (!k->server && mode == MODE_OUT) || (k->server && mode == MODE_IN);
521 k->enc[mode].iv = keys[ctos ? 0 : 1];
522 k->enc[mode].key = keys[ctos ? 2 : 3];
523 k->mac[mode].key = keys[ctos ? 4 : 5];
524 }
525 return 0;
526}