Annotation of src/usr.bin/ssh/kexdh.c, Revision 1.1
1.1 ! markus 1: /*
! 2: * Copyright (c) 2001 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: *
! 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"
! 26: RCSID("$OpenBSD: kex.c,v 1.24 2001/03/28 21:59:40 provos Exp $");
! 27:
! 28: #include <openssl/crypto.h>
! 29: #include <openssl/bn.h>
! 30:
! 31: #include "xmalloc.h"
! 32: #include "buffer.h"
! 33: #include "bufaux.h"
! 34: #include "key.h"
! 35: #include "kex.h"
! 36: #include "log.h"
! 37: #include "dispatch.h"
! 38: #include "packet.h"
! 39: #include "dh.h"
! 40: #include "ssh2.h"
! 41:
! 42: extern u_char *session_id2;
! 43: extern int session_id2_len;
! 44:
! 45: dispatch_fn kexdh_input_init; /* C -> S */
! 46: dispatch_fn kexdh_input_reply; /* S -> C */
! 47:
! 48: typedef struct State State;
! 49: struct State {
! 50: DH *dh;
! 51: };
! 52:
! 53: u_char *
! 54: kex_dh_hash(
! 55: char *client_version_string,
! 56: char *server_version_string,
! 57: char *ckexinit, int ckexinitlen,
! 58: char *skexinit, int skexinitlen,
! 59: char *serverhostkeyblob, int sbloblen,
! 60: BIGNUM *client_dh_pub,
! 61: BIGNUM *server_dh_pub,
! 62: BIGNUM *shared_secret)
! 63: {
! 64: Buffer b;
! 65: static u_char digest[EVP_MAX_MD_SIZE];
! 66: EVP_MD *evp_md = EVP_sha1();
! 67: EVP_MD_CTX md;
! 68:
! 69: buffer_init(&b);
! 70: buffer_put_string(&b, client_version_string, strlen(client_version_string));
! 71: buffer_put_string(&b, server_version_string, strlen(server_version_string));
! 72:
! 73: /* kexinit messages: fake header: len+SSH2_MSG_KEXINIT */
! 74: buffer_put_int(&b, ckexinitlen+1);
! 75: buffer_put_char(&b, SSH2_MSG_KEXINIT);
! 76: buffer_append(&b, ckexinit, ckexinitlen);
! 77: buffer_put_int(&b, skexinitlen+1);
! 78: buffer_put_char(&b, SSH2_MSG_KEXINIT);
! 79: buffer_append(&b, skexinit, skexinitlen);
! 80:
! 81: buffer_put_string(&b, serverhostkeyblob, sbloblen);
! 82: buffer_put_bignum2(&b, client_dh_pub);
! 83: buffer_put_bignum2(&b, server_dh_pub);
! 84: buffer_put_bignum2(&b, shared_secret);
! 85:
! 86: #ifdef DEBUG_KEX
! 87: buffer_dump(&b);
! 88: #endif
! 89: EVP_DigestInit(&md, evp_md);
! 90: EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b));
! 91: EVP_DigestFinal(&md, digest, NULL);
! 92:
! 93: buffer_free(&b);
! 94:
! 95: #ifdef DEBUG_KEX
! 96: dump_digest("hash", digest, evp_md->md_size);
! 97: #endif
! 98: return digest;
! 99: }
! 100:
! 101: /* client */
! 102:
! 103: void
! 104: kexdh_client(Kex *kex)
! 105: {
! 106: State *state;
! 107:
! 108: dispatch_set(SSH2_MSG_KEXDH_REPLY, &kexdh_input_reply);
! 109:
! 110: state = xmalloc(sizeof(State));
! 111: kex->state = state;
! 112:
! 113: /* generate and send 'e', client DH public key */
! 114: state->dh = dh_new_group1();
! 115: dh_gen_key(state->dh, kex->we_need * 8);
! 116: packet_start(SSH2_MSG_KEXDH_INIT);
! 117: packet_put_bignum2(state->dh->pub_key);
! 118: packet_send();
! 119:
! 120: debug("SSH2_MSG_KEXDH_INIT sent");
! 121: #ifdef DEBUG_KEXDH
! 122: DHparams_print_fp(stderr, state->dh);
! 123: fprintf(stderr, "pub= ");
! 124: BN_print_fp(stderr, state->dh->pub_key);
! 125: fprintf(stderr, "\n");
! 126: #endif
! 127: }
! 128:
! 129: void
! 130: kexdh_input_reply(int type, int plen, void *ctxt)
! 131: {
! 132: BIGNUM *dh_server_pub = NULL, *shared_secret = NULL;
! 133: Key *server_host_key;
! 134: char *server_host_key_blob = NULL, *signature = NULL;
! 135: u_char *kbuf, *hash;
! 136: u_int klen, kout, slen, sbloblen;
! 137: int dlen;
! 138: Kex *kex = (Kex *)ctxt;
! 139: State *state = (State *)kex->state;
! 140:
! 141: debug("SSH2_MSG_KEXDH_REPLY received");
! 142: dispatch_set(SSH2_MSG_KEXDH_REPLY, &kex_protocol_error);
! 143:
! 144: /* key, cert */
! 145: server_host_key_blob = packet_get_string(&sbloblen);
! 146: server_host_key = key_from_blob(server_host_key_blob, sbloblen);
! 147: if (server_host_key == NULL)
! 148: fatal("cannot decode server_host_key_blob");
! 149:
! 150: if (kex->check_host_key == NULL)
! 151: fatal("cannot check server_host_key");
! 152: kex->check_host_key(server_host_key);
! 153:
! 154: /* DH paramter f, server public DH key */
! 155: dh_server_pub = BN_new();
! 156: if (dh_server_pub == NULL)
! 157: fatal("dh_server_pub == NULL");
! 158: packet_get_bignum2(dh_server_pub, &dlen);
! 159:
! 160: #ifdef DEBUG_KEXDH
! 161: fprintf(stderr, "dh_server_pub= ");
! 162: BN_print_fp(stderr, dh_server_pub);
! 163: fprintf(stderr, "\n");
! 164: debug("bits %d", BN_num_bits(dh_server_pub));
! 165: #endif
! 166:
! 167: /* signed H */
! 168: signature = packet_get_string(&slen);
! 169: packet_done();
! 170:
! 171: if (!dh_pub_is_valid(state->dh, dh_server_pub))
! 172: packet_disconnect("bad server public DH value");
! 173:
! 174: klen = DH_size(state->dh);
! 175: kbuf = xmalloc(klen);
! 176: kout = DH_compute_key(kbuf, dh_server_pub, state->dh);
! 177: #ifdef DEBUG_KEXDH
! 178: dump_digest("shared secret", kbuf, kout);
! 179: #endif
! 180: shared_secret = BN_new();
! 181: BN_bin2bn(kbuf, kout, shared_secret);
! 182: memset(kbuf, 0, klen);
! 183: xfree(kbuf);
! 184:
! 185: /* calc and verify H */
! 186: hash = kex_dh_hash(
! 187: kex->client_version_string,
! 188: kex->server_version_string,
! 189: buffer_ptr(&kex->my), buffer_len(&kex->my),
! 190: buffer_ptr(&kex->peer), buffer_len(&kex->peer),
! 191: server_host_key_blob, sbloblen,
! 192: state->dh->pub_key,
! 193: dh_server_pub,
! 194: shared_secret
! 195: );
! 196: xfree(server_host_key_blob);
! 197: DH_free(state->dh);
! 198: BN_free(dh_server_pub);
! 199:
! 200: if (key_verify(server_host_key, (u_char *)signature, slen, hash, 20) != 1)
! 201: fatal("key_verify failed for server_host_key");
! 202: key_free(server_host_key);
! 203: xfree(signature);
! 204:
! 205: kex_derive_keys(kex, hash, shared_secret);
! 206: BN_clear_free(shared_secret);
! 207: packet_set_kex(kex);
! 208: kex_send_newkeys();
! 209:
! 210: /* save session id */
! 211: session_id2_len = 20;
! 212: session_id2 = xmalloc(session_id2_len);
! 213: memcpy(session_id2, hash, session_id2_len);
! 214:
! 215: xfree(state);
! 216: kex->state = NULL;
! 217: }
! 218:
! 219: /* server */
! 220:
! 221: void
! 222: kexdh_server(Kex *kex)
! 223: {
! 224: State *state;
! 225:
! 226: dispatch_set(SSH2_MSG_KEXDH_INIT, &kexdh_input_init);
! 227:
! 228: state = xmalloc(sizeof(*state));
! 229: kex->state = state;
! 230:
! 231: /* generate server DH public key */
! 232: state->dh = dh_new_group1();
! 233: dh_gen_key(state->dh, kex->we_need * 8);
! 234: }
! 235:
! 236: void
! 237: kexdh_input_init(int type, int plen, void *ctxt)
! 238: {
! 239: BIGNUM *shared_secret = NULL, *dh_client_pub = NULL;
! 240: Key *server_host_key;
! 241: u_char *kbuf, *hash, *signature = NULL, *server_host_key_blob = NULL;
! 242: u_int sbloblen, klen, kout;
! 243: int dlen, slen;
! 244: Kex *kex = (Kex*) ctxt;
! 245: State *state = (State*) kex->state;
! 246: DH *dh = state->dh;
! 247:
! 248: debug("SSH2_MSG_KEXDH_INIT received");
! 249: dispatch_set(SSH2_MSG_KEXDH_INIT, &kex_protocol_error);
! 250:
! 251: if (kex->load_host_key == NULL)
! 252: fatal("Cannot load hostkey");
! 253: server_host_key = kex->load_host_key(kex->hostkey_type);
! 254: if (server_host_key == NULL)
! 255: fatal("Unsupported hostkey type %d", kex->hostkey_type);
! 256:
! 257: /* key, cert */
! 258: dh_client_pub = BN_new();
! 259: if (dh_client_pub == NULL)
! 260: fatal("dh_client_pub == NULL");
! 261: packet_get_bignum2(dh_client_pub, &dlen);
! 262:
! 263: #ifdef DEBUG_KEXDH
! 264: fprintf(stderr, "dh_client_pub= ");
! 265: BN_print_fp(stderr, dh_client_pub);
! 266: fprintf(stderr, "\n");
! 267: debug("bits %d", BN_num_bits(dh_client_pub));
! 268: #endif
! 269:
! 270: #ifdef DEBUG_KEXDH
! 271: DHparams_print_fp(stderr, dh);
! 272: fprintf(stderr, "pub= ");
! 273: BN_print_fp(stderr, dh->pub_key);
! 274: fprintf(stderr, "\n");
! 275: #endif
! 276: if (!dh_pub_is_valid(dh, dh_client_pub))
! 277: packet_disconnect("bad client public DH value");
! 278:
! 279: klen = DH_size(dh);
! 280: kbuf = xmalloc(klen);
! 281: kout = DH_compute_key(kbuf, dh_client_pub, dh);
! 282: #ifdef DEBUG_KEXDH
! 283: dump_digest("shared secret", kbuf, kout);
! 284: #endif
! 285: shared_secret = BN_new();
! 286: BN_bin2bn(kbuf, kout, shared_secret);
! 287: memset(kbuf, 0, klen);
! 288: xfree(kbuf);
! 289:
! 290: key_to_blob(server_host_key, &server_host_key_blob, &sbloblen);
! 291:
! 292: /* calc H */
! 293: hash = kex_dh_hash(
! 294: kex->client_version_string,
! 295: kex->server_version_string,
! 296: buffer_ptr(&kex->peer), buffer_len(&kex->peer),
! 297: buffer_ptr(&kex->my), buffer_len(&kex->my),
! 298: (char *)server_host_key_blob, sbloblen,
! 299: dh_client_pub,
! 300: dh->pub_key,
! 301: shared_secret
! 302: );
! 303: BN_free(dh_client_pub);
! 304:
! 305: /* save session id := H */
! 306: /* XXX hashlen depends on KEX */
! 307: session_id2_len = 20;
! 308: session_id2 = xmalloc(session_id2_len);
! 309: memcpy(session_id2, hash, session_id2_len);
! 310:
! 311: /* sign H */
! 312: /* XXX hashlen depends on KEX */
! 313: key_sign(server_host_key, &signature, &slen, hash, 20);
! 314:
! 315: /* destroy_sensitive_data(); */
! 316:
! 317: /* send server hostkey, DH pubkey 'f' and singed H */
! 318: packet_start(SSH2_MSG_KEXDH_REPLY);
! 319: packet_put_string((char *)server_host_key_blob, sbloblen);
! 320: packet_put_bignum2(dh->pub_key); /* f */
! 321: packet_put_string((char *)signature, slen);
! 322: packet_send();
! 323: xfree(signature);
! 324: xfree(server_host_key_blob);
! 325:
! 326: kex_derive_keys(kex, hash, shared_secret);
! 327: BN_clear_free(shared_secret);
! 328: packet_set_kex(kex);
! 329: kex_send_newkeys();
! 330:
! 331: /* have keys, free DH */
! 332: DH_free(dh);
! 333: xfree(state);
! 334: kex->state = NULL;
! 335: }
! 336:
! 337: void
! 338: kexdh(Kex *kex)
! 339: {
! 340: if (kex->server)
! 341: kexdh_server(kex);
! 342: else
! 343: kexdh_client(kex);
! 344: }