11e8db6e2SBrian Feldman /* 21e8db6e2SBrian Feldman * Copyright (c) 2001 Markus Friedl. All rights reserved. 31e8db6e2SBrian Feldman * 41e8db6e2SBrian Feldman * Redistribution and use in source and binary forms, with or without 51e8db6e2SBrian Feldman * modification, are permitted provided that the following conditions 61e8db6e2SBrian Feldman * are met: 71e8db6e2SBrian Feldman * 1. Redistributions of source code must retain the above copyright 81e8db6e2SBrian Feldman * notice, this list of conditions and the following disclaimer. 91e8db6e2SBrian Feldman * 2. Redistributions in binary form must reproduce the above copyright 101e8db6e2SBrian Feldman * notice, this list of conditions and the following disclaimer in the 111e8db6e2SBrian Feldman * documentation and/or other materials provided with the distribution. 121e8db6e2SBrian Feldman * 131e8db6e2SBrian Feldman * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 141e8db6e2SBrian Feldman * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 151e8db6e2SBrian Feldman * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 161e8db6e2SBrian Feldman * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 171e8db6e2SBrian Feldman * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 181e8db6e2SBrian Feldman * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 191e8db6e2SBrian Feldman * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 201e8db6e2SBrian Feldman * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 211e8db6e2SBrian Feldman * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 221e8db6e2SBrian Feldman * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 231e8db6e2SBrian Feldman */ 241e8db6e2SBrian Feldman 251e8db6e2SBrian Feldman #include "includes.h" 261e8db6e2SBrian Feldman RCSID("$OpenBSD: kexdh.c,v 1.3 2001/04/04 09:48:34 markus Exp $"); 271e8db6e2SBrian Feldman 281e8db6e2SBrian Feldman #include <openssl/crypto.h> 291e8db6e2SBrian Feldman #include <openssl/bn.h> 301e8db6e2SBrian Feldman 311e8db6e2SBrian Feldman #include "xmalloc.h" 321e8db6e2SBrian Feldman #include "buffer.h" 331e8db6e2SBrian Feldman #include "bufaux.h" 341e8db6e2SBrian Feldman #include "key.h" 351e8db6e2SBrian Feldman #include "kex.h" 361e8db6e2SBrian Feldman #include "log.h" 371e8db6e2SBrian Feldman #include "packet.h" 381e8db6e2SBrian Feldman #include "dh.h" 391e8db6e2SBrian Feldman #include "ssh2.h" 401e8db6e2SBrian Feldman 411e8db6e2SBrian Feldman u_char * 421e8db6e2SBrian Feldman kex_dh_hash( 431e8db6e2SBrian Feldman char *client_version_string, 441e8db6e2SBrian Feldman char *server_version_string, 451e8db6e2SBrian Feldman char *ckexinit, int ckexinitlen, 461e8db6e2SBrian Feldman char *skexinit, int skexinitlen, 471e8db6e2SBrian Feldman char *serverhostkeyblob, int sbloblen, 481e8db6e2SBrian Feldman BIGNUM *client_dh_pub, 491e8db6e2SBrian Feldman BIGNUM *server_dh_pub, 501e8db6e2SBrian Feldman BIGNUM *shared_secret) 511e8db6e2SBrian Feldman { 521e8db6e2SBrian Feldman Buffer b; 531e8db6e2SBrian Feldman static u_char digest[EVP_MAX_MD_SIZE]; 541e8db6e2SBrian Feldman EVP_MD *evp_md = EVP_sha1(); 551e8db6e2SBrian Feldman EVP_MD_CTX md; 561e8db6e2SBrian Feldman 571e8db6e2SBrian Feldman buffer_init(&b); 581e8db6e2SBrian Feldman buffer_put_string(&b, client_version_string, strlen(client_version_string)); 591e8db6e2SBrian Feldman buffer_put_string(&b, server_version_string, strlen(server_version_string)); 601e8db6e2SBrian Feldman 611e8db6e2SBrian Feldman /* kexinit messages: fake header: len+SSH2_MSG_KEXINIT */ 621e8db6e2SBrian Feldman buffer_put_int(&b, ckexinitlen+1); 631e8db6e2SBrian Feldman buffer_put_char(&b, SSH2_MSG_KEXINIT); 641e8db6e2SBrian Feldman buffer_append(&b, ckexinit, ckexinitlen); 651e8db6e2SBrian Feldman buffer_put_int(&b, skexinitlen+1); 661e8db6e2SBrian Feldman buffer_put_char(&b, SSH2_MSG_KEXINIT); 671e8db6e2SBrian Feldman buffer_append(&b, skexinit, skexinitlen); 681e8db6e2SBrian Feldman 691e8db6e2SBrian Feldman buffer_put_string(&b, serverhostkeyblob, sbloblen); 701e8db6e2SBrian Feldman buffer_put_bignum2(&b, client_dh_pub); 711e8db6e2SBrian Feldman buffer_put_bignum2(&b, server_dh_pub); 721e8db6e2SBrian Feldman buffer_put_bignum2(&b, shared_secret); 731e8db6e2SBrian Feldman 741e8db6e2SBrian Feldman #ifdef DEBUG_KEX 751e8db6e2SBrian Feldman buffer_dump(&b); 761e8db6e2SBrian Feldman #endif 771e8db6e2SBrian Feldman EVP_DigestInit(&md, evp_md); 781e8db6e2SBrian Feldman EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b)); 791e8db6e2SBrian Feldman EVP_DigestFinal(&md, digest, NULL); 801e8db6e2SBrian Feldman 811e8db6e2SBrian Feldman buffer_free(&b); 821e8db6e2SBrian Feldman 831e8db6e2SBrian Feldman #ifdef DEBUG_KEX 841e8db6e2SBrian Feldman dump_digest("hash", digest, evp_md->md_size); 851e8db6e2SBrian Feldman #endif 861e8db6e2SBrian Feldman return digest; 871e8db6e2SBrian Feldman } 881e8db6e2SBrian Feldman 891e8db6e2SBrian Feldman /* client */ 901e8db6e2SBrian Feldman 911e8db6e2SBrian Feldman void 921e8db6e2SBrian Feldman kexdh_client(Kex *kex) 931e8db6e2SBrian Feldman { 941e8db6e2SBrian Feldman BIGNUM *dh_server_pub = NULL, *shared_secret = NULL; 951e8db6e2SBrian Feldman DH *dh; 961e8db6e2SBrian Feldman Key *server_host_key; 971e8db6e2SBrian Feldman char *server_host_key_blob = NULL, *signature = NULL; 981e8db6e2SBrian Feldman u_char *kbuf, *hash; 991e8db6e2SBrian Feldman u_int klen, kout, slen, sbloblen; 1001e8db6e2SBrian Feldman int dlen, plen; 1011e8db6e2SBrian Feldman 1021e8db6e2SBrian Feldman /* generate and send 'e', client DH public key */ 1031e8db6e2SBrian Feldman dh = dh_new_group1(); 1041e8db6e2SBrian Feldman dh_gen_key(dh, kex->we_need * 8); 1051e8db6e2SBrian Feldman packet_start(SSH2_MSG_KEXDH_INIT); 1061e8db6e2SBrian Feldman packet_put_bignum2(dh->pub_key); 1071e8db6e2SBrian Feldman packet_send(); 1081e8db6e2SBrian Feldman 1091e8db6e2SBrian Feldman debug("sending SSH2_MSG_KEXDH_INIT"); 1101e8db6e2SBrian Feldman #ifdef DEBUG_KEXDH 1111e8db6e2SBrian Feldman DHparams_print_fp(stderr, dh); 1121e8db6e2SBrian Feldman fprintf(stderr, "pub= "); 1131e8db6e2SBrian Feldman BN_print_fp(stderr, dh->pub_key); 1141e8db6e2SBrian Feldman fprintf(stderr, "\n"); 1151e8db6e2SBrian Feldman #endif 1161e8db6e2SBrian Feldman 1171e8db6e2SBrian Feldman debug("expecting SSH2_MSG_KEXDH_REPLY"); 1181e8db6e2SBrian Feldman packet_read_expect(&plen, SSH2_MSG_KEXDH_REPLY); 1191e8db6e2SBrian Feldman 1201e8db6e2SBrian Feldman /* key, cert */ 1211e8db6e2SBrian Feldman server_host_key_blob = packet_get_string(&sbloblen); 1221e8db6e2SBrian Feldman server_host_key = key_from_blob(server_host_key_blob, sbloblen); 1231e8db6e2SBrian Feldman if (server_host_key == NULL) 1241e8db6e2SBrian Feldman fatal("cannot decode server_host_key_blob"); 1251e8db6e2SBrian Feldman 1261e8db6e2SBrian Feldman if (kex->check_host_key == NULL) 1271e8db6e2SBrian Feldman fatal("cannot check server_host_key"); 1281e8db6e2SBrian Feldman kex->check_host_key(server_host_key); 1291e8db6e2SBrian Feldman 1301e8db6e2SBrian Feldman /* DH paramter f, server public DH key */ 1311e8db6e2SBrian Feldman dh_server_pub = BN_new(); 1321e8db6e2SBrian Feldman if (dh_server_pub == NULL) 1331e8db6e2SBrian Feldman fatal("dh_server_pub == NULL"); 1341e8db6e2SBrian Feldman packet_get_bignum2(dh_server_pub, &dlen); 1351e8db6e2SBrian Feldman 1361e8db6e2SBrian Feldman #ifdef DEBUG_KEXDH 1371e8db6e2SBrian Feldman fprintf(stderr, "dh_server_pub= "); 1381e8db6e2SBrian Feldman BN_print_fp(stderr, dh_server_pub); 1391e8db6e2SBrian Feldman fprintf(stderr, "\n"); 1401e8db6e2SBrian Feldman debug("bits %d", BN_num_bits(dh_server_pub)); 1411e8db6e2SBrian Feldman #endif 1421e8db6e2SBrian Feldman 1431e8db6e2SBrian Feldman /* signed H */ 1441e8db6e2SBrian Feldman signature = packet_get_string(&slen); 1451e8db6e2SBrian Feldman packet_done(); 1461e8db6e2SBrian Feldman 1471e8db6e2SBrian Feldman if (!dh_pub_is_valid(dh, dh_server_pub)) 1481e8db6e2SBrian Feldman packet_disconnect("bad server public DH value"); 1491e8db6e2SBrian Feldman 1501e8db6e2SBrian Feldman klen = DH_size(dh); 1511e8db6e2SBrian Feldman kbuf = xmalloc(klen); 1521e8db6e2SBrian Feldman kout = DH_compute_key(kbuf, dh_server_pub, dh); 1531e8db6e2SBrian Feldman #ifdef DEBUG_KEXDH 1541e8db6e2SBrian Feldman dump_digest("shared secret", kbuf, kout); 1551e8db6e2SBrian Feldman #endif 1561e8db6e2SBrian Feldman shared_secret = BN_new(); 1571e8db6e2SBrian Feldman BN_bin2bn(kbuf, kout, shared_secret); 1581e8db6e2SBrian Feldman memset(kbuf, 0, klen); 1591e8db6e2SBrian Feldman xfree(kbuf); 1601e8db6e2SBrian Feldman 1611e8db6e2SBrian Feldman /* calc and verify H */ 1621e8db6e2SBrian Feldman hash = kex_dh_hash( 1631e8db6e2SBrian Feldman kex->client_version_string, 1641e8db6e2SBrian Feldman kex->server_version_string, 1651e8db6e2SBrian Feldman buffer_ptr(&kex->my), buffer_len(&kex->my), 1661e8db6e2SBrian Feldman buffer_ptr(&kex->peer), buffer_len(&kex->peer), 1671e8db6e2SBrian Feldman server_host_key_blob, sbloblen, 1681e8db6e2SBrian Feldman dh->pub_key, 1691e8db6e2SBrian Feldman dh_server_pub, 1701e8db6e2SBrian Feldman shared_secret 1711e8db6e2SBrian Feldman ); 1721e8db6e2SBrian Feldman xfree(server_host_key_blob); 1731e8db6e2SBrian Feldman BN_free(dh_server_pub); 1741e8db6e2SBrian Feldman DH_free(dh); 1751e8db6e2SBrian Feldman 1761e8db6e2SBrian Feldman if (key_verify(server_host_key, (u_char *)signature, slen, hash, 20) != 1) 1771e8db6e2SBrian Feldman fatal("key_verify failed for server_host_key"); 1781e8db6e2SBrian Feldman key_free(server_host_key); 1791e8db6e2SBrian Feldman xfree(signature); 1801e8db6e2SBrian Feldman 1811e8db6e2SBrian Feldman /* save session id */ 1821e8db6e2SBrian Feldman if (kex->session_id == NULL) { 1831e8db6e2SBrian Feldman kex->session_id_len = 20; 1841e8db6e2SBrian Feldman kex->session_id = xmalloc(kex->session_id_len); 1851e8db6e2SBrian Feldman memcpy(kex->session_id, hash, kex->session_id_len); 1861e8db6e2SBrian Feldman } 1871e8db6e2SBrian Feldman 1881e8db6e2SBrian Feldman kex_derive_keys(kex, hash, shared_secret); 1891e8db6e2SBrian Feldman BN_clear_free(shared_secret); 1901e8db6e2SBrian Feldman kex_finish(kex); 1911e8db6e2SBrian Feldman } 1921e8db6e2SBrian Feldman 1931e8db6e2SBrian Feldman /* server */ 1941e8db6e2SBrian Feldman 1951e8db6e2SBrian Feldman void 1961e8db6e2SBrian Feldman kexdh_server(Kex *kex) 1971e8db6e2SBrian Feldman { 1981e8db6e2SBrian Feldman BIGNUM *shared_secret = NULL, *dh_client_pub = NULL; 1991e8db6e2SBrian Feldman DH *dh; 2001e8db6e2SBrian Feldman Key *server_host_key; 2011e8db6e2SBrian Feldman u_char *kbuf, *hash, *signature = NULL, *server_host_key_blob = NULL; 2021e8db6e2SBrian Feldman u_int sbloblen, klen, kout; 2031e8db6e2SBrian Feldman int dlen, slen, plen; 2041e8db6e2SBrian Feldman 2051e8db6e2SBrian Feldman /* generate server DH public key */ 2061e8db6e2SBrian Feldman dh = dh_new_group1(); 2071e8db6e2SBrian Feldman dh_gen_key(dh, kex->we_need * 8); 2081e8db6e2SBrian Feldman 2091e8db6e2SBrian Feldman debug("expecting SSH2_MSG_KEXDH_INIT"); 2101e8db6e2SBrian Feldman packet_read_expect(&plen, SSH2_MSG_KEXDH_INIT); 2111e8db6e2SBrian Feldman 2121e8db6e2SBrian Feldman if (kex->load_host_key == NULL) 2131e8db6e2SBrian Feldman fatal("Cannot load hostkey"); 2141e8db6e2SBrian Feldman server_host_key = kex->load_host_key(kex->hostkey_type); 2151e8db6e2SBrian Feldman if (server_host_key == NULL) 2161e8db6e2SBrian Feldman fatal("Unsupported hostkey type %d", kex->hostkey_type); 2171e8db6e2SBrian Feldman 2181e8db6e2SBrian Feldman /* key, cert */ 2191e8db6e2SBrian Feldman dh_client_pub = BN_new(); 2201e8db6e2SBrian Feldman if (dh_client_pub == NULL) 2211e8db6e2SBrian Feldman fatal("dh_client_pub == NULL"); 2221e8db6e2SBrian Feldman packet_get_bignum2(dh_client_pub, &dlen); 2231e8db6e2SBrian Feldman 2241e8db6e2SBrian Feldman #ifdef DEBUG_KEXDH 2251e8db6e2SBrian Feldman fprintf(stderr, "dh_client_pub= "); 2261e8db6e2SBrian Feldman BN_print_fp(stderr, dh_client_pub); 2271e8db6e2SBrian Feldman fprintf(stderr, "\n"); 2281e8db6e2SBrian Feldman debug("bits %d", BN_num_bits(dh_client_pub)); 2291e8db6e2SBrian Feldman #endif 2301e8db6e2SBrian Feldman 2311e8db6e2SBrian Feldman #ifdef DEBUG_KEXDH 2321e8db6e2SBrian Feldman DHparams_print_fp(stderr, dh); 2331e8db6e2SBrian Feldman fprintf(stderr, "pub= "); 2341e8db6e2SBrian Feldman BN_print_fp(stderr, dh->pub_key); 2351e8db6e2SBrian Feldman fprintf(stderr, "\n"); 2361e8db6e2SBrian Feldman #endif 2371e8db6e2SBrian Feldman if (!dh_pub_is_valid(dh, dh_client_pub)) 2381e8db6e2SBrian Feldman packet_disconnect("bad client public DH value"); 2391e8db6e2SBrian Feldman 2401e8db6e2SBrian Feldman klen = DH_size(dh); 2411e8db6e2SBrian Feldman kbuf = xmalloc(klen); 2421e8db6e2SBrian Feldman kout = DH_compute_key(kbuf, dh_client_pub, dh); 2431e8db6e2SBrian Feldman #ifdef DEBUG_KEXDH 2441e8db6e2SBrian Feldman dump_digest("shared secret", kbuf, kout); 2451e8db6e2SBrian Feldman #endif 2461e8db6e2SBrian Feldman shared_secret = BN_new(); 2471e8db6e2SBrian Feldman BN_bin2bn(kbuf, kout, shared_secret); 2481e8db6e2SBrian Feldman memset(kbuf, 0, klen); 2491e8db6e2SBrian Feldman xfree(kbuf); 2501e8db6e2SBrian Feldman 2511e8db6e2SBrian Feldman key_to_blob(server_host_key, &server_host_key_blob, &sbloblen); 2521e8db6e2SBrian Feldman 2531e8db6e2SBrian Feldman /* calc H */ 2541e8db6e2SBrian Feldman hash = kex_dh_hash( 2551e8db6e2SBrian Feldman kex->client_version_string, 2561e8db6e2SBrian Feldman kex->server_version_string, 2571e8db6e2SBrian Feldman buffer_ptr(&kex->peer), buffer_len(&kex->peer), 2581e8db6e2SBrian Feldman buffer_ptr(&kex->my), buffer_len(&kex->my), 2591e8db6e2SBrian Feldman (char *)server_host_key_blob, sbloblen, 2601e8db6e2SBrian Feldman dh_client_pub, 2611e8db6e2SBrian Feldman dh->pub_key, 2621e8db6e2SBrian Feldman shared_secret 2631e8db6e2SBrian Feldman ); 2641e8db6e2SBrian Feldman BN_free(dh_client_pub); 2651e8db6e2SBrian Feldman 2661e8db6e2SBrian Feldman /* save session id := H */ 2671e8db6e2SBrian Feldman /* XXX hashlen depends on KEX */ 2681e8db6e2SBrian Feldman if (kex->session_id == NULL) { 2691e8db6e2SBrian Feldman kex->session_id_len = 20; 2701e8db6e2SBrian Feldman kex->session_id = xmalloc(kex->session_id_len); 2711e8db6e2SBrian Feldman memcpy(kex->session_id, hash, kex->session_id_len); 2721e8db6e2SBrian Feldman } 2731e8db6e2SBrian Feldman 2741e8db6e2SBrian Feldman /* sign H */ 2751e8db6e2SBrian Feldman /* XXX hashlen depends on KEX */ 2761e8db6e2SBrian Feldman key_sign(server_host_key, &signature, &slen, hash, 20); 2771e8db6e2SBrian Feldman 2781e8db6e2SBrian Feldman /* destroy_sensitive_data(); */ 2791e8db6e2SBrian Feldman 2801e8db6e2SBrian Feldman /* send server hostkey, DH pubkey 'f' and singed H */ 2811e8db6e2SBrian Feldman packet_start(SSH2_MSG_KEXDH_REPLY); 2821e8db6e2SBrian Feldman packet_put_string((char *)server_host_key_blob, sbloblen); 2831e8db6e2SBrian Feldman packet_put_bignum2(dh->pub_key); /* f */ 2841e8db6e2SBrian Feldman packet_put_string((char *)signature, slen); 2851e8db6e2SBrian Feldman packet_send(); 2861e8db6e2SBrian Feldman 2871e8db6e2SBrian Feldman xfree(signature); 2881e8db6e2SBrian Feldman xfree(server_host_key_blob); 2891e8db6e2SBrian Feldman /* have keys, free DH */ 2901e8db6e2SBrian Feldman DH_free(dh); 2911e8db6e2SBrian Feldman 2921e8db6e2SBrian Feldman kex_derive_keys(kex, hash, shared_secret); 2931e8db6e2SBrian Feldman BN_clear_free(shared_secret); 2941e8db6e2SBrian Feldman kex_finish(kex); 2951e8db6e2SBrian Feldman } 2961e8db6e2SBrian Feldman 2971e8db6e2SBrian Feldman void 2981e8db6e2SBrian Feldman kexdh(Kex *kex) 2991e8db6e2SBrian Feldman { 3001e8db6e2SBrian Feldman if (kex->server) 3011e8db6e2SBrian Feldman kexdh_server(kex); 3021e8db6e2SBrian Feldman else 3031e8db6e2SBrian Feldman kexdh_client(kex); 3041e8db6e2SBrian Feldman } 305