kex.c revision 1.9
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" 31RCSID("$OpenBSD: kex.c,v 1.9 2000/07/10 16:30:25 ho Exp $"); 32 33#include "ssh.h" 34#include "ssh2.h" 35#include "xmalloc.h" 36#include "buffer.h" 37#include "bufaux.h" 38#include "packet.h" 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 53#define KEX_COOKIE_LEN 16 54 55Buffer * 56kex_init(char *myproposal[PROPOSAL_MAX]) 57{ 58 int first_kex_packet_follows = 0; 59 unsigned char cookie[KEX_COOKIE_LEN]; 60 u_int32_t rand = 0; 61 int i; 62 Buffer *ki = xmalloc(sizeof(*ki)); 63 for (i = 0; i < KEX_COOKIE_LEN; i++) { 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]); 73 buffer_put_char(ki, first_kex_packet_follows); 74 buffer_put_int(ki, 0); /* uint32 reserved */ 75 return ki; 76} 77 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 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 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]; 290 char *c, *s, *p, *ret, *cp, *sp; 291 int i, j, nproposals; 292 293 c = cp = xstrdup(client); 294 s = sp = xstrdup(server); 295 296 for ((p = strsep(&sp, SEP)), i=0; p && *p != '\0'; 297 (p = strsep(&sp, SEP)), i++) { 298 if (i < MAX_PROP) 299 sproposals[i] = p; 300 else 301 break; 302 } 303 nproposals = i; 304 305 for ((p = strsep(&cp, SEP)), i=0; p && *p != '\0'; 306 (p = strsep(&cp, SEP)), i++) { 307 for (j = 0; j < nproposals; j++) { 308 if (strcmp(p, sproposals[j]) == 0) { 309 ret = xstrdup(p); 310 xfree(c); 311 xfree(s); 312 return ret; 313 } 314 } 315 } 316 xfree(c); 317 xfree(s); 318 return NULL; 319} 320void 321choose_enc(Enc *enc, char *client, char *server) 322{ 323 char *name = get_match(client, server); 324 if (name == NULL) 325 fatal("no matching cipher found: client %s server %s", client, server); 326 enc->type = cipher_number(name); 327 328 switch (enc->type) { 329 case SSH_CIPHER_3DES_CBC: 330 enc->key_len = 24; 331 enc->iv_len = 8; 332 enc->block_size = 8; 333 break; 334 case SSH_CIPHER_BLOWFISH_CBC: 335 case SSH_CIPHER_CAST128_CBC: 336 enc->key_len = 16; 337 enc->iv_len = 8; 338 enc->block_size = 8; 339 break; 340 case SSH_CIPHER_ARCFOUR: 341 enc->key_len = 16; 342 enc->iv_len = 0; 343 enc->block_size = 8; 344 break; 345 default: 346 fatal("unsupported cipher %s", name); 347 } 348 enc->name = name; 349 enc->enabled = 0; 350 enc->iv = NULL; 351 enc->key = NULL; 352} 353void 354choose_mac(Mac *mac, char *client, char *server) 355{ 356 char *name = get_match(client, server); 357 if (name == NULL) 358 fatal("no matching mac found: client %s server %s", client, server); 359 if (strcmp(name, "hmac-md5") == 0) { 360 mac->md = EVP_md5(); 361 } else if (strcmp(name, "hmac-sha1") == 0) { 362 mac->md = EVP_sha1(); 363 } else if (strcmp(name, "hmac-ripemd160@openssh.com") == 0) { 364 mac->md = EVP_ripemd160(); 365 } else { 366 fatal("unsupported mac %s", name); 367 } 368 mac->name = name; 369 mac->mac_len = mac->md->md_size; 370 mac->key_len = (datafellows & SSH_BUG_HMAC) ? 16 : mac->mac_len; 371 mac->key = NULL; 372 mac->enabled = 0; 373} 374void 375choose_comp(Comp *comp, char *client, char *server) 376{ 377 char *name = get_match(client, server); 378 if (name == NULL) 379 fatal("no matching comp found: client %s server %s", client, server); 380 if (strcmp(name, "zlib") == 0) { 381 comp->type = 1; 382 } else if (strcmp(name, "none") == 0) { 383 comp->type = 0; 384 } else { 385 fatal("unsupported comp %s", name); 386 } 387 comp->name = name; 388} 389void 390choose_kex(Kex *k, char *client, char *server) 391{ 392 k->name = get_match(client, server); 393 if (k->name == NULL) 394 fatal("no kex alg"); 395 if (strcmp(k->name, KEX_DH1) != 0) 396 fatal("bad kex alg %s", k->name); 397} 398void 399choose_hostkeyalg(Kex *k, char *client, char *server) 400{ 401 k->hostkeyalg = get_match(client, server); 402 if (k->hostkeyalg == NULL) 403 fatal("no hostkey alg"); 404 if (strcmp(k->hostkeyalg, KEX_DSS) != 0) 405 fatal("bad hostkey alg %s", k->hostkeyalg); 406} 407 408Kex * 409kex_choose_conf(char *cprop[PROPOSAL_MAX], char *sprop[PROPOSAL_MAX], int server) 410{ 411 int mode; 412 int ctos; /* direction: if true client-to-server */ 413 int need; 414 Kex *k; 415 416 k = xmalloc(sizeof(*k)); 417 memset(k, 0, sizeof(*k)); 418 k->server = server; 419 420 for (mode = 0; mode < MODE_MAX; mode++) { 421 int nenc, nmac, ncomp; 422 ctos = (!k->server && mode == MODE_OUT) || (k->server && mode == MODE_IN); 423 nenc = ctos ? PROPOSAL_ENC_ALGS_CTOS : PROPOSAL_ENC_ALGS_STOC; 424 nmac = ctos ? PROPOSAL_MAC_ALGS_CTOS : PROPOSAL_MAC_ALGS_STOC; 425 ncomp = ctos ? PROPOSAL_COMP_ALGS_CTOS : PROPOSAL_COMP_ALGS_STOC; 426 choose_enc (&k->enc [mode], cprop[nenc], sprop[nenc]); 427 choose_mac (&k->mac [mode], cprop[nmac], sprop[nmac]); 428 choose_comp(&k->comp[mode], cprop[ncomp], sprop[ncomp]); 429 debug("kex: %s %s %s %s", 430 ctos ? "client->server" : "server->client", 431 k->enc[mode].name, 432 k->mac[mode].name, 433 k->comp[mode].name); 434 } 435 choose_kex(k, cprop[PROPOSAL_KEX_ALGS], sprop[PROPOSAL_KEX_ALGS]); 436 choose_hostkeyalg(k, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS], 437 sprop[PROPOSAL_SERVER_HOST_KEY_ALGS]); 438 need = 0; 439 for (mode = 0; mode < MODE_MAX; mode++) { 440 if (need < k->enc[mode].key_len) 441 need = k->enc[mode].key_len; 442 if (need < k->enc[mode].iv_len) 443 need = k->enc[mode].iv_len; 444 if (need < k->mac[mode].key_len) 445 need = k->mac[mode].key_len; 446 } 447 /* XXX need runden? */ 448 k->we_need = need; 449 return k; 450} 451 452int 453kex_derive_keys(Kex *k, unsigned char *hash, BIGNUM *shared_secret) 454{ 455 int i; 456 int mode; 457 int ctos; 458 unsigned char *keys[NKEYS]; 459 460 for (i = 0; i < NKEYS; i++) 461 keys[i] = derive_key('A'+i, k->we_need, hash, shared_secret); 462 463 for (mode = 0; mode < MODE_MAX; mode++) { 464 ctos = (!k->server && mode == MODE_OUT) || (k->server && mode == MODE_IN); 465 k->enc[mode].iv = keys[ctos ? 0 : 1]; 466 k->enc[mode].key = keys[ctos ? 2 : 3]; 467 k->mac[mode].key = keys[ctos ? 4 : 5]; 468 } 469 return 0; 470} 471