]> andersk Git - openssh.git/blame - kex.c
- (djm) Patch from Michael Stone <mstone@cs.loyola.edu> to add support for
[openssh.git] / kex.c
CommitLineData
4f8c6159 1/*
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"
74fc9186 31RCSID("$OpenBSD: kex.c,v 1.8 2000/06/20 01:39:41 markus Exp $");
4f8c6159 32
33#include "ssh.h"
34#include "ssh2.h"
35#include "xmalloc.h"
36#include "buffer.h"
37#include "bufaux.h"
71276795 38#include "packet.h"
4f8c6159 39#include "cipher.h"
40#include "compat.h"
41
42#include <openssl/bn.h>
43#include <openssl/dh.h>
44
45#include <openssl/crypto.h>
46#include <openssl/bio.h>
47#include <openssl/bn.h>
48#include <openssl/dh.h>
49#include <openssl/pem.h>
50
51#include "kex.h"
52
71276795 53#define KEX_COOKIE_LEN 16
54
4f8c6159 55Buffer *
56kex_init(char *myproposal[PROPOSAL_MAX])
57{
71276795 58 int first_kex_packet_follows = 0;
59 unsigned char cookie[KEX_COOKIE_LEN];
4f8c6159 60 u_int32_t rand = 0;
61 int i;
62 Buffer *ki = xmalloc(sizeof(*ki));
71276795 63 for (i = 0; i < KEX_COOKIE_LEN; i++) {
4f8c6159 64 if (i % 4 == 0)
65 rand = arc4random();
66 cookie[i] = rand & 0xff;
67 rand >>= 8;
68 }
69 buffer_init(ki);
70 buffer_append(ki, (char *)cookie, sizeof cookie);
71 for (i = 0; i < PROPOSAL_MAX; i++)
72 buffer_put_cstring(ki, myproposal[i]);
71276795 73 buffer_put_char(ki, first_kex_packet_follows);
74 buffer_put_int(ki, 0); /* uint32 reserved */
4f8c6159 75 return ki;
76}
77
71276795 78/* send kexinit, parse and save reply */
79void
80kex_exchange_kexinit(
81 Buffer *my_kexinit, Buffer *peer_kexint,
82 char *peer_proposal[PROPOSAL_MAX])
83{
84 int i;
85 char *ptr;
86 int plen;
87
88 debug("send KEXINIT");
89 packet_start(SSH2_MSG_KEXINIT);
90 packet_put_raw(buffer_ptr(my_kexinit), buffer_len(my_kexinit));
91 packet_send();
92 packet_write_wait();
93 debug("done");
94
95 /*
96 * read and save raw KEXINIT payload in buffer. this is used during
97 * computation of the session_id and the session keys.
98 */
99 debug("wait KEXINIT");
100 packet_read_expect(&plen, SSH2_MSG_KEXINIT);
101 ptr = packet_get_raw(&plen);
102 buffer_append(peer_kexint, ptr, plen);
103
104 /* parse packet and save algorithm proposal */
105 /* skip cookie */
106 for (i = 0; i < KEX_COOKIE_LEN; i++)
107 packet_get_char();
108 /* extract kex init proposal strings */
109 for (i = 0; i < PROPOSAL_MAX; i++) {
110 peer_proposal[i] = packet_get_string(NULL);
111 debug("got kexinit: %s", peer_proposal[i]);
112 }
113 /* first kex follow / reserved */
114 i = packet_get_char();
115 debug("first kex follow: %d ", i);
116 i = packet_get_int();
117 debug("reserved: %d ", i);
118 packet_done();
119 debug("done");
120}
121
4f8c6159 122/* diffie-hellman-group1-sha1 */
123
124int
125dh_pub_is_valid(DH *dh, BIGNUM *dh_pub)
126{
127 int i;
128 int n = BN_num_bits(dh_pub);
129 int bits_set = 0;
130
131 /* we only accept g==2 */
132 if (!BN_is_word(dh->g, 2)) {
133 log("invalid DH base != 2");
134 return 0;
135 }
136 if (dh_pub->neg) {
137 log("invalid public DH value: negativ");
138 return 0;
139 }
140 for (i = 0; i <= n; i++)
141 if (BN_is_bit_set(dh_pub, i))
142 bits_set++;
143 debug("bits set: %d/%d", bits_set, BN_num_bits(dh->p));
144
145 /* if g==2 and bits_set==1 then computing log_g(dh_pub) is trivial */
146 if (bits_set > 1 && (BN_cmp(dh_pub, dh->p) == -1))
147 return 1;
148 log("invalid public DH value (%d/%d)", bits_set, BN_num_bits(dh->p));
149 return 0;
150}
151
152DH *
153dh_new_group1()
154{
155 static char *group1 =
156 "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
157 "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
158 "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
159 "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
160 "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381"
161 "FFFFFFFF" "FFFFFFFF";
162 DH *dh;
163 int ret, tries = 0;
164 dh = DH_new();
165 if(dh == NULL)
166 fatal("DH_new");
167 ret = BN_hex2bn(&dh->p, group1);
168 if(ret<0)
169 fatal("BN_hex2bn");
170 dh->g = BN_new();
171 if(dh->g == NULL)
172 fatal("DH_new g");
173 BN_set_word(dh->g, 2);
174 do {
175 if (DH_generate_key(dh) == 0)
176 fatal("DH_generate_key");
177 if (tries++ > 10)
178 fatal("dh_new_group1: too many bad keys: giving up");
179 } while (!dh_pub_is_valid(dh, dh->pub_key));
180 return dh;
181}
182
4f8c6159 183void
184dump_digest(unsigned char *digest, int len)
185{
186 int i;
187 for (i = 0; i< len; i++){
188 fprintf(stderr, "%02x", digest[i]);
189 if(i%2!=0)
190 fprintf(stderr, " ");
191 }
192 fprintf(stderr, "\n");
193}
194
195unsigned char *
196kex_hash(
197 char *client_version_string,
198 char *server_version_string,
199 char *ckexinit, int ckexinitlen,
200 char *skexinit, int skexinitlen,
201 char *serverhostkeyblob, int sbloblen,
202 BIGNUM *client_dh_pub,
203 BIGNUM *server_dh_pub,
204 BIGNUM *shared_secret)
205{
206 Buffer b;
207 static unsigned char digest[EVP_MAX_MD_SIZE];
208 EVP_MD *evp_md = EVP_sha1();
209 EVP_MD_CTX md;
210
211 buffer_init(&b);
212 buffer_put_string(&b, client_version_string, strlen(client_version_string));
213 buffer_put_string(&b, server_version_string, strlen(server_version_string));
214
215 /* kexinit messages: fake header: len+SSH2_MSG_KEXINIT */
216 buffer_put_int(&b, ckexinitlen+1);
217 buffer_put_char(&b, SSH2_MSG_KEXINIT);
218 buffer_append(&b, ckexinit, ckexinitlen);
219 buffer_put_int(&b, skexinitlen+1);
220 buffer_put_char(&b, SSH2_MSG_KEXINIT);
221 buffer_append(&b, skexinit, skexinitlen);
222
223 buffer_put_string(&b, serverhostkeyblob, sbloblen);
224 buffer_put_bignum2(&b, client_dh_pub);
225 buffer_put_bignum2(&b, server_dh_pub);
226 buffer_put_bignum2(&b, shared_secret);
227
228#ifdef DEBUG_KEX
229 buffer_dump(&b);
230#endif
231
232 EVP_DigestInit(&md, evp_md);
233 EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b));
234 EVP_DigestFinal(&md, digest, NULL);
235
236 buffer_free(&b);
237
238#ifdef DEBUG_KEX
239 dump_digest(digest, evp_md->md_size);
240#endif
241 return digest;
242}
243
244unsigned char *
245derive_key(int id, int need, char unsigned *hash, BIGNUM *shared_secret)
246{
247 Buffer b;
248 EVP_MD *evp_md = EVP_sha1();
249 EVP_MD_CTX md;
250 char c = id;
251 int have;
252 int mdsz = evp_md->md_size;
253 unsigned char *digest = xmalloc(((need+mdsz-1)/mdsz)*mdsz);
254
255 buffer_init(&b);
256 buffer_put_bignum2(&b, shared_secret);
257
258 EVP_DigestInit(&md, evp_md);
259 EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b)); /* shared_secret K */
260 EVP_DigestUpdate(&md, hash, mdsz); /* transport-06 */
261 EVP_DigestUpdate(&md, &c, 1); /* key id */
262 EVP_DigestUpdate(&md, hash, mdsz); /* session id */
263 EVP_DigestFinal(&md, digest, NULL);
264
265 /* expand */
266 for (have = mdsz; need > have; have += mdsz) {
267 EVP_DigestInit(&md, evp_md);
268 EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b));
269 EVP_DigestUpdate(&md, hash, mdsz);
270 EVP_DigestUpdate(&md, digest, have);
271 EVP_DigestFinal(&md, digest + have, NULL);
272 }
273 buffer_free(&b);
274#ifdef DEBUG_KEX
275 fprintf(stderr, "Digest '%c'== ", c);
276 dump_digest(digest, need);
277#endif
278 return digest;
279}
280
281#define NKEYS 6
282
283#define MAX_PROP 20
284#define SEP ","
285
286char *
287get_match(char *client, char *server)
288{
289 char *sproposals[MAX_PROP];
71276795 290 char *c, *s, *p, *ret;
4f8c6159 291 int i, j, nproposals;
292
71276795 293 c = xstrdup(client);
294 s = xstrdup(server);
295
296 for ((p = strtok(s, SEP)), i=0; p; (p = strtok(NULL, SEP)), i++) {
4f8c6159 297 if (i < MAX_PROP)
298 sproposals[i] = p;
299 else
300 break;
301 }
302 nproposals = i;
303
71276795 304 for ((p = strtok(c, SEP)), i=0; p; (p = strtok(NULL, SEP)), i++) {
305 for (j = 0; j < nproposals; j++) {
306 if (strcmp(p, sproposals[j]) == 0) {
307 ret = xstrdup(p);
308 xfree(c);
309 xfree(s);
310 return ret;
311 }
312 }
4f8c6159 313 }
71276795 314 xfree(c);
315 xfree(s);
4f8c6159 316 return NULL;
317}
318void
319choose_enc(Enc *enc, char *client, char *server)
320{
321 char *name = get_match(client, server);
322 if (name == NULL)
323 fatal("no matching cipher found: client %s server %s", client, server);
324 enc->type = cipher_number(name);
325
326 switch (enc->type) {
327 case SSH_CIPHER_3DES_CBC:
328 enc->key_len = 24;
329 enc->iv_len = 8;
330 enc->block_size = 8;
331 break;
332 case SSH_CIPHER_BLOWFISH_CBC:
333 case SSH_CIPHER_CAST128_CBC:
334 enc->key_len = 16;
335 enc->iv_len = 8;
336 enc->block_size = 8;
337 break;
338 case SSH_CIPHER_ARCFOUR:
339 enc->key_len = 16;
340 enc->iv_len = 0;
341 enc->block_size = 8;
342 break;
343 default:
344 fatal("unsupported cipher %s", name);
345 }
346 enc->name = name;
347 enc->enabled = 0;
348 enc->iv = NULL;
349 enc->key = NULL;
350}
351void
352choose_mac(Mac *mac, char *client, char *server)
353{
354 char *name = get_match(client, server);
355 if (name == NULL)
356 fatal("no matching mac found: client %s server %s", client, server);
357 if (strcmp(name, "hmac-md5") == 0) {
358 mac->md = EVP_md5();
359 } else if (strcmp(name, "hmac-sha1") == 0) {
360 mac->md = EVP_sha1();
361 } else if (strcmp(name, "hmac-ripemd160@openssh.com") == 0) {
362 mac->md = EVP_ripemd160();
363 } else {
364 fatal("unsupported mac %s", name);
365 }
366 mac->name = name;
367 mac->mac_len = mac->md->md_size;
d0c832f3 368 mac->key_len = (datafellows & SSH_BUG_HMAC) ? 16 : mac->mac_len;
4f8c6159 369 mac->key = NULL;
370 mac->enabled = 0;
371}
372void
373choose_comp(Comp *comp, char *client, char *server)
374{
375 char *name = get_match(client, server);
376 if (name == NULL)
377 fatal("no matching comp found: client %s server %s", client, server);
378 if (strcmp(name, "zlib") == 0) {
379 comp->type = 1;
380 } else if (strcmp(name, "none") == 0) {
381 comp->type = 0;
382 } else {
383 fatal("unsupported comp %s", name);
384 }
385 comp->name = name;
386}
387void
388choose_kex(Kex *k, char *client, char *server)
389{
390 k->name = get_match(client, server);
391 if (k->name == NULL)
392 fatal("no kex alg");
393 if (strcmp(k->name, KEX_DH1) != 0)
394 fatal("bad kex alg %s", k->name);
395}
396void
397choose_hostkeyalg(Kex *k, char *client, char *server)
398{
399 k->hostkeyalg = get_match(client, server);
400 if (k->hostkeyalg == NULL)
401 fatal("no hostkey alg");
402 if (strcmp(k->hostkeyalg, KEX_DSS) != 0)
403 fatal("bad hostkey alg %s", k->hostkeyalg);
404}
405
406Kex *
407kex_choose_conf(char *cprop[PROPOSAL_MAX], char *sprop[PROPOSAL_MAX], int server)
408{
4f8c6159 409 int mode;
410 int ctos; /* direction: if true client-to-server */
411 int need;
412 Kex *k;
413
414 k = xmalloc(sizeof(*k));
415 memset(k, 0, sizeof(*k));
416 k->server = server;
417
418 for (mode = 0; mode < MODE_MAX; mode++) {
419 int nenc, nmac, ncomp;
420 ctos = (!k->server && mode == MODE_OUT) || (k->server && mode == MODE_IN);
421 nenc = ctos ? PROPOSAL_ENC_ALGS_CTOS : PROPOSAL_ENC_ALGS_STOC;
422 nmac = ctos ? PROPOSAL_MAC_ALGS_CTOS : PROPOSAL_MAC_ALGS_STOC;
423 ncomp = ctos ? PROPOSAL_COMP_ALGS_CTOS : PROPOSAL_COMP_ALGS_STOC;
424 choose_enc (&k->enc [mode], cprop[nenc], sprop[nenc]);
425 choose_mac (&k->mac [mode], cprop[nmac], sprop[nmac]);
426 choose_comp(&k->comp[mode], cprop[ncomp], sprop[ncomp]);
427 debug("kex: %s %s %s %s",
428 ctos ? "client->server" : "server->client",
429 k->enc[mode].name,
430 k->mac[mode].name,
431 k->comp[mode].name);
432 }
433 choose_kex(k, cprop[PROPOSAL_KEX_ALGS], sprop[PROPOSAL_KEX_ALGS]);
434 choose_hostkeyalg(k, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS],
435 sprop[PROPOSAL_SERVER_HOST_KEY_ALGS]);
4f8c6159 436 need = 0;
437 for (mode = 0; mode < MODE_MAX; mode++) {
438 if (need < k->enc[mode].key_len)
439 need = k->enc[mode].key_len;
440 if (need < k->enc[mode].iv_len)
441 need = k->enc[mode].iv_len;
442 if (need < k->mac[mode].key_len)
443 need = k->mac[mode].key_len;
444 }
71276795 445 /* XXX need runden? */
4f8c6159 446 k->we_need = need;
447 return k;
448}
449
450int
451kex_derive_keys(Kex *k, unsigned char *hash, BIGNUM *shared_secret)
452{
453 int i;
454 int mode;
455 int ctos;
456 unsigned char *keys[NKEYS];
457
458 for (i = 0; i < NKEYS; i++)
459 keys[i] = derive_key('A'+i, k->we_need, hash, shared_secret);
460
461 for (mode = 0; mode < MODE_MAX; mode++) {
462 ctos = (!k->server && mode == MODE_OUT) || (k->server && mode == MODE_IN);
463 k->enc[mode].iv = keys[ctos ? 0 : 1];
464 k->enc[mode].key = keys[ctos ? 2 : 3];
465 k->mac[mode].key = keys[ctos ? 4 : 5];
466 }
467 return 0;
468}
This page took 0.123896 seconds and 5 git commands to generate.