Annotation of src/usr.bin/ssh/kex.c, Revision 1.1
1.1 ! markus 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"
! 31: RCSID("$Id: kex.c,v 1.17 2000/04/03 18:30:40 markus Exp $");
! 32:
! 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 <ssl/bn.h>
! 42: #include <ssl/dh.h>
! 43:
! 44: #include <ssl/crypto.h>
! 45: #include <ssl/bio.h>
! 46: #include <ssl/bn.h>
! 47: #include <ssl/dh.h>
! 48: #include <ssl/pem.h>
! 49:
! 50: #include "kex.h"
! 51:
! 52: Buffer *
! 53: kex_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:
! 77: DH *
! 78: new_dh_group1()
! 79: {
! 80: static char *group1 =
! 81: "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
! 82: "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
! 83: "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
! 84: "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
! 85: "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381"
! 86: "FFFFFFFF" "FFFFFFFF";
! 87: DH *dh;
! 88: int ret;
! 89: dh = DH_new();
! 90: if(dh == NULL)
! 91: fatal("DH_new");
! 92: ret = BN_hex2bn(&dh->p,group1);
! 93: if(ret<0)
! 94: fatal("BN_hex2bn");
! 95: dh->g = BN_new();
! 96: if(dh->g == NULL)
! 97: fatal("DH_new g");
! 98: BN_set_word(dh->g,2);
! 99: if (DH_generate_key(dh) == 0)
! 100: fatal("DH_generate_key");
! 101: return dh;
! 102: }
! 103:
! 104: void
! 105: bignum_print(BIGNUM *b)
! 106: {
! 107: BN_print_fp(stderr,b);
! 108: }
! 109:
! 110: void
! 111: dump_digest(unsigned char *digest, int len)
! 112: {
! 113: int i;
! 114: for (i = 0; i< len; i++){
! 115: fprintf(stderr, "%02x", digest[i]);
! 116: if(i%2!=0)
! 117: fprintf(stderr, " ");
! 118: }
! 119: fprintf(stderr, "\n");
! 120: }
! 121:
! 122: unsigned char *
! 123: kex_hash(
! 124: char *client_version_string,
! 125: char *server_version_string,
! 126: char *ckexinit, int ckexinitlen,
! 127: char *skexinit, int skexinitlen,
! 128: char *serverhostkeyblob, int sbloblen,
! 129: BIGNUM *client_dh_pub,
! 130: BIGNUM *server_dh_pub,
! 131: BIGNUM *shared_secret)
! 132: {
! 133: Buffer b;
! 134: static unsigned char digest[EVP_MAX_MD_SIZE];
! 135: EVP_MD *evp_md = EVP_sha1();
! 136: EVP_MD_CTX md;
! 137:
! 138: buffer_init(&b);
! 139: buffer_put_string(&b, client_version_string, strlen(client_version_string));
! 140: buffer_put_string(&b, server_version_string, strlen(server_version_string));
! 141:
! 142: /* kexinit messages: fake header: len+SSH2_MSG_KEXINIT */
! 143: buffer_put_int(&b, ckexinitlen+1);
! 144: buffer_put_char(&b, SSH2_MSG_KEXINIT);
! 145: buffer_append(&b, ckexinit, ckexinitlen);
! 146: buffer_put_int(&b, skexinitlen+1);
! 147: buffer_put_char(&b, SSH2_MSG_KEXINIT);
! 148: buffer_append(&b, skexinit, skexinitlen);
! 149:
! 150: buffer_put_string(&b, serverhostkeyblob, sbloblen);
! 151: buffer_put_bignum2(&b, client_dh_pub);
! 152: buffer_put_bignum2(&b, server_dh_pub);
! 153: buffer_put_bignum2(&b, shared_secret);
! 154:
! 155: #ifdef DEBUG_KEX
! 156: buffer_dump(&b);
! 157: #endif
! 158:
! 159: EVP_DigestInit(&md, evp_md);
! 160: EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b));
! 161: EVP_DigestFinal(&md, digest, NULL);
! 162:
! 163: buffer_free(&b);
! 164:
! 165: #ifdef DEBUG_KEX
! 166: dump_digest(digest, evp_md->md_size);
! 167: #endif
! 168: return digest;
! 169: }
! 170:
! 171: unsigned char *
! 172: derive_key(int id, int need, char unsigned *hash, BIGNUM *shared_secret)
! 173: {
! 174: Buffer b;
! 175: EVP_MD *evp_md = EVP_sha1();
! 176: EVP_MD_CTX md;
! 177: char c = id;
! 178: int have;
! 179: int mdsz = evp_md->md_size;
! 180: unsigned char *digest = xmalloc(((need+mdsz-1)/mdsz)*mdsz);
! 181:
! 182: buffer_init(&b);
! 183: buffer_put_bignum2(&b, shared_secret);
! 184:
! 185: EVP_DigestInit(&md, evp_md);
! 186: EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b)); /* shared_secret K */
! 187: EVP_DigestUpdate(&md, hash, mdsz); /* transport-06 */
! 188: EVP_DigestUpdate(&md, &c, 1); /* key id */
! 189: EVP_DigestUpdate(&md, hash, mdsz); /* session id */
! 190: EVP_DigestFinal(&md, digest, NULL);
! 191:
! 192: /* expand */
! 193: for (have = mdsz; need > have; have += mdsz) {
! 194: EVP_DigestInit(&md, evp_md);
! 195: EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b));
! 196: EVP_DigestUpdate(&md, hash, mdsz);
! 197: EVP_DigestUpdate(&md, digest, have);
! 198: EVP_DigestFinal(&md, digest + have, NULL);
! 199: }
! 200: buffer_free(&b);
! 201: #ifdef DEBUG_KEX
! 202: fprintf(stderr, "Digest '%c'== ", c);
! 203: dump_digest(digest, need);
! 204: #endif
! 205: return digest;
! 206: }
! 207:
! 208: #define NKEYS 6
! 209:
! 210: #define MAX_PROP 20
! 211: #define SEP ","
! 212:
! 213: char *
! 214: get_match(char *client, char *server)
! 215: {
! 216: char *sproposals[MAX_PROP];
! 217: char *p;
! 218: int i, j, nproposals;
! 219:
! 220: for ((p = strtok(server, SEP)), i=0; p; (p = strtok(NULL, SEP)), i++) {
! 221: if (i < MAX_PROP)
! 222: sproposals[i] = p;
! 223: else
! 224: break;
! 225: }
! 226: nproposals = i;
! 227:
! 228: for ((p = strtok(client, SEP)), i=0; p; (p = strtok(NULL, SEP)), i++) {
! 229: for (j = 0; j < nproposals; j++)
! 230: if (strcmp(p, sproposals[j]) == 0)
! 231: return xstrdup(p);
! 232: }
! 233: return NULL;
! 234: }
! 235: void
! 236: choose_enc(Enc *enc, char *client, char *server)
! 237: {
! 238: char *name = get_match(client, server);
! 239: if (name == NULL)
! 240: fatal("no matching cipher found: client %s server %s", client, server);
! 241: enc->type = cipher_number(name);
! 242:
! 243: switch (enc->type) {
! 244: case SSH_CIPHER_3DES_CBC:
! 245: enc->key_len = 24;
! 246: enc->iv_len = 8;
! 247: enc->block_size = 8;
! 248: break;
! 249: case SSH_CIPHER_BLOWFISH_CBC:
! 250: case SSH_CIPHER_CAST128_CBC:
! 251: enc->key_len = 16;
! 252: enc->iv_len = 8;
! 253: enc->block_size = 8;
! 254: break;
! 255: case SSH_CIPHER_ARCFOUR:
! 256: enc->key_len = 16;
! 257: enc->iv_len = 0;
! 258: enc->block_size = 8;
! 259: break;
! 260: default:
! 261: fatal("unsupported cipher %s", name);
! 262: }
! 263: enc->name = name;
! 264: enc->enabled = 0;
! 265: enc->iv = NULL;
! 266: enc->key = NULL;
! 267: }
! 268: void
! 269: choose_mac(Mac *mac, char *client, char *server)
! 270: {
! 271: char *name = get_match(client, server);
! 272: if (name == NULL)
! 273: fatal("no matching mac found: client %s server %s", client, server);
! 274: if (strcmp(name, "hmac-md5") == 0) {
! 275: mac->md = EVP_md5();
! 276: } else if (strcmp(name, "hmac-sha1") == 0) {
! 277: mac->md = EVP_sha1();
! 278: } else if (strcmp(name, "hmac-ripemd160@openssh.com") == 0) {
! 279: mac->md = EVP_ripemd160();
! 280: } else {
! 281: fatal("unsupported mac %s", name);
! 282: }
! 283: mac->name = name;
! 284: mac->mac_len = mac->md->md_size;
! 285: mac->key_len = datafellows ? 16 : mac->mac_len;
! 286: mac->key = NULL;
! 287: mac->enabled = 0;
! 288: }
! 289: void
! 290: choose_comp(Comp *comp, char *client, char *server)
! 291: {
! 292: char *name = get_match(client, server);
! 293: if (name == NULL)
! 294: fatal("no matching comp found: client %s server %s", client, server);
! 295: if (strcmp(name, "zlib") == 0) {
! 296: comp->type = 1;
! 297: } else if (strcmp(name, "none") == 0) {
! 298: comp->type = 0;
! 299: } else {
! 300: fatal("unsupported comp %s", name);
! 301: }
! 302: comp->name = name;
! 303: }
! 304: void
! 305: choose_kex(Kex *k, char *client, char *server)
! 306: {
! 307: k->name = get_match(client, server);
! 308: if (k->name == NULL)
! 309: fatal("no kex alg");
! 310: if (strcmp(k->name, KEX_DH1) != 0)
! 311: fatal("bad kex alg %s", k->name);
! 312: }
! 313: void
! 314: choose_hostkeyalg(Kex *k, char *client, char *server)
! 315: {
! 316: k->hostkeyalg = get_match(client, server);
! 317: if (k->hostkeyalg == NULL)
! 318: fatal("no hostkey alg");
! 319: if (strcmp(k->hostkeyalg, KEX_DSS) != 0)
! 320: fatal("bad hostkey alg %s", k->hostkeyalg);
! 321: }
! 322:
! 323: Kex *
! 324: kex_choose_conf(char *cprop[PROPOSAL_MAX], char *sprop[PROPOSAL_MAX], int server)
! 325: {
! 326: int i;
! 327: int mode;
! 328: int ctos; /* direction: if true client-to-server */
! 329: int need;
! 330: Kex *k;
! 331:
! 332: k = xmalloc(sizeof(*k));
! 333: memset(k, 0, sizeof(*k));
! 334: k->server = server;
! 335:
! 336: for (mode = 0; mode < MODE_MAX; mode++) {
! 337: int nenc, nmac, ncomp;
! 338: ctos = (!k->server && mode == MODE_OUT) || (k->server && mode == MODE_IN);
! 339: nenc = ctos ? PROPOSAL_ENC_ALGS_CTOS : PROPOSAL_ENC_ALGS_STOC;
! 340: nmac = ctos ? PROPOSAL_MAC_ALGS_CTOS : PROPOSAL_MAC_ALGS_STOC;
! 341: ncomp = ctos ? PROPOSAL_COMP_ALGS_CTOS : PROPOSAL_COMP_ALGS_STOC;
! 342: choose_enc (&k->enc [mode], cprop[nenc], sprop[nenc]);
! 343: choose_mac (&k->mac [mode], cprop[nmac], sprop[nmac]);
! 344: choose_comp(&k->comp[mode], cprop[ncomp], sprop[ncomp]);
! 345: log("kex: %s %s %s %s",
! 346: ctos ? "client->server" : "server->client",
! 347: k->enc[mode].name,
! 348: k->mac[mode].name,
! 349: k->comp[mode].name);
! 350: }
! 351: choose_kex(k, cprop[PROPOSAL_KEX_ALGS], sprop[PROPOSAL_KEX_ALGS]);
! 352: choose_hostkeyalg(k, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS],
! 353: sprop[PROPOSAL_SERVER_HOST_KEY_ALGS]);
! 354: for (i = 0; i < PROPOSAL_MAX; i++) {
! 355: xfree(cprop[i]);
! 356: xfree(sprop[i]);
! 357: }
! 358: need = 0;
! 359: for (mode = 0; mode < MODE_MAX; mode++) {
! 360: if (need < k->enc[mode].key_len)
! 361: need = k->enc[mode].key_len;
! 362: if (need < k->enc[mode].iv_len)
! 363: need = k->enc[mode].iv_len;
! 364: if (need < k->mac[mode].key_len)
! 365: need = k->mac[mode].key_len;
! 366: }
! 367: /* need runden? */
! 368: #define WE_NEED 32
! 369: k->we_need = WE_NEED;
! 370: k->we_need = need;
! 371: return k;
! 372: }
! 373:
! 374: int
! 375: kex_derive_keys(Kex *k, unsigned char *hash, BIGNUM *shared_secret)
! 376: {
! 377: int i;
! 378: int mode;
! 379: int ctos;
! 380: unsigned char *keys[NKEYS];
! 381:
! 382: for (i = 0; i < NKEYS; i++)
! 383: keys[i] = derive_key('A'+i, k->we_need, hash, shared_secret);
! 384:
! 385: for (mode = 0; mode < MODE_MAX; mode++) {
! 386: ctos = (!k->server && mode == MODE_OUT) || (k->server && mode == MODE_IN);
! 387: k->enc[mode].iv = keys[ctos ? 0 : 1];
! 388: k->enc[mode].key = keys[ctos ? 2 : 3];
! 389: k->mac[mode].key = keys[ctos ? 4 : 5];
! 390: }
! 391: return 0;
! 392: }