]> andersk Git - openssh.git/blobdiff - kex.c
- djm@cvs.openbsd.org 2010/01/30 02:54:53
[openssh.git] / kex.c
diff --git a/kex.c b/kex.c
index 576d4b56e774fa9a6772f256bb1d68095ea028d7..148cfee8046ad5af46be99c83f6b9affe0ec768a 100644 (file)
--- a/kex.c
+++ b/kex.c
@@ -1,5 +1,6 @@
+/* $OpenBSD: kex.c,v 1.82 2009/10/24 11:13:54 andreas Exp $ */
 /*
- * Copyright (c) 2000 Markus Friedl.  All rights reserved.
+ * Copyright (c) 2000, 2001 Markus Friedl.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: kex.c,v 1.25 2001/03/29 21:17:39 markus Exp $");
+
+#include <sys/param.h>
+
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 
 #include <openssl/crypto.h>
-#include <openssl/bio.h>
-#include <openssl/bn.h>
-#include <openssl/pem.h>
 
-#include "ssh2.h"
 #include "xmalloc.h"
+#include "ssh2.h"
 #include "buffer.h"
-#include "bufaux.h"
 #include "packet.h"
 #include "compat.h"
 #include "cipher.h"
-#include "kex.h"
 #include "key.h"
+#include "kex.h"
 #include "log.h"
 #include "mac.h"
 #include "match.h"
+#include "dispatch.h"
+#include "monitor.h"
+#include "roaming.h"
+
+#if OPENSSL_VERSION_NUMBER >= 0x00907000L
+# if defined(HAVE_EVP_SHA256)
+# define evp_ssh_sha256 EVP_sha256
+# else
+extern const EVP_MD *evp_ssh_sha256(void);
+# endif
+#endif
 
-#define KEX_COOKIE_LEN 16
+/* prototype */
+static void kex_kexinit_finish(Kex *);
+static void kex_choose_conf(Kex *);
 
-Buffer *
-kex_init(char *myproposal[PROPOSAL_MAX])
+/* put algorithm proposal into buffer */
+static void
+kex_prop2buf(Buffer *b, char *proposal[PROPOSAL_MAX])
 {
-       int first_kex_packet_follows = 0;
-       u_char cookie[KEX_COOKIE_LEN];
-       u_int32_t rand = 0;
-       int i;
-       Buffer *ki = xmalloc(sizeof(*ki));
-       for (i = 0; i < KEX_COOKIE_LEN; i++) {
-               if (i % 4 == 0)
-                       rand = arc4random();
-               cookie[i] = rand & 0xff;
-               rand >>= 8;
-       }
-       buffer_init(ki);
-       buffer_append(ki, (char *)cookie, sizeof cookie);
+       u_int i;
+
+       buffer_clear(b);
+       /*
+        * add a dummy cookie, the cookie will be overwritten by
+        * kex_send_kexinit(), each time a kexinit is set
+        */
+       for (i = 0; i < KEX_COOKIE_LEN; i++)
+               buffer_put_char(b, 0);
        for (i = 0; i < PROPOSAL_MAX; i++)
-               buffer_put_cstring(ki, myproposal[i]);
-       buffer_put_char(ki, first_kex_packet_follows);
-       buffer_put_int(ki, 0);                          /* uint32 reserved */
-       return ki;
+               buffer_put_cstring(b, proposal[i]);
+       buffer_put_char(b, 0);                  /* first_kex_packet_follows */
+       buffer_put_int(b, 0);                   /* uint32 reserved */
 }
 
-/* send kexinit, parse and save reply */
-void
-kex_exchange_kexinit(
-    Buffer *my_kexinit, Buffer *peer_kexint,
-    char *peer_proposal[PROPOSAL_MAX])
+/* parse buffer and return algorithm proposal */
+static char **
+kex_buf2prop(Buffer *raw, int *first_kex_follows)
 {
-       int i;
-       char *ptr;
-       int plen;
-
-       debug("send KEXINIT");
-       packet_start(SSH2_MSG_KEXINIT);
-       packet_put_raw(buffer_ptr(my_kexinit), buffer_len(my_kexinit));
-       packet_send();
-       packet_write_wait();
-       debug("done");
+       Buffer b;
+       u_int i;
+       char **proposal;
 
-       /*
-        * read and save raw KEXINIT payload in buffer. this is used during
-        * computation of the session_id and the session keys.
-        */
-       debug("wait KEXINIT");
-       packet_read_expect(&plen, SSH2_MSG_KEXINIT);
-       ptr = packet_get_raw(&plen);
-       buffer_append(peer_kexint, ptr, plen);
+       proposal = xcalloc(PROPOSAL_MAX, sizeof(char *));
 
-       /* parse packet and save algorithm proposal */
+       buffer_init(&b);
+       buffer_append(&b, buffer_ptr(raw), buffer_len(raw));
        /* skip cookie */
        for (i = 0; i < KEX_COOKIE_LEN; i++)
-               packet_get_char();
+               buffer_get_char(&b);
        /* extract kex init proposal strings */
        for (i = 0; i < PROPOSAL_MAX; i++) {
-               peer_proposal[i] = packet_get_string(NULL);
-               debug("got kexinit: %s", peer_proposal[i]);
+               proposal[i] = buffer_get_string(&b,NULL);
+               debug2("kex_parse_kexinit: %s", proposal[i]);
        }
-       /* first kex follow / reserved */
-       i = packet_get_char();
-       debug("first kex follow: %d ", i);
-       i = packet_get_int();
-       debug("reserved: %d ", i);
-       packet_done();
-       debug("done");
+       /* first kex follows / reserved */
+       i = buffer_get_char(&b);
+       if (first_kex_follows != NULL)
+               *first_kex_follows = i;
+       debug2("kex_parse_kexinit: first_kex_follows %d ", i);
+       i = buffer_get_int(&b);
+       debug2("kex_parse_kexinit: reserved %u ", i);
+       buffer_free(&b);
+       return proposal;
 }
 
-#ifdef DEBUG_KEX
-void
-dump_digest(u_char *digest, int len)
+static void
+kex_prop_free(char **proposal)
 {
-       int i;
-       for (i = 0; i< len; i++){
-               fprintf(stderr, "%02x", digest[i]);
-               if(i%2!=0)
-                       fprintf(stderr, " ");
-       }
-       fprintf(stderr, "\n");
+       u_int i;
+
+       for (i = 0; i < PROPOSAL_MAX; i++)
+               xfree(proposal[i]);
+       xfree(proposal);
 }
-#endif
 
-u_char *
-kex_hash(
-    char *client_version_string,
-    char *server_version_string,
-    char *ckexinit, int ckexinitlen,
-    char *skexinit, int skexinitlen,
-    char *serverhostkeyblob, int sbloblen,
-    BIGNUM *client_dh_pub,
-    BIGNUM *server_dh_pub,
-    BIGNUM *shared_secret)
+/* ARGSUSED */
+static void
+kex_protocol_error(int type, u_int32_t seq, void *ctxt)
 {
-       Buffer b;
-       static u_char digest[EVP_MAX_MD_SIZE];
-       EVP_MD *evp_md = EVP_sha1();
-       EVP_MD_CTX md;
-
-       buffer_init(&b);
-       buffer_put_string(&b, client_version_string, strlen(client_version_string));
-       buffer_put_string(&b, server_version_string, strlen(server_version_string));
-
-       /* kexinit messages: fake header: len+SSH2_MSG_KEXINIT */
-       buffer_put_int(&b, ckexinitlen+1);
-       buffer_put_char(&b, SSH2_MSG_KEXINIT);
-       buffer_append(&b, ckexinit, ckexinitlen);
-       buffer_put_int(&b, skexinitlen+1);
-       buffer_put_char(&b, SSH2_MSG_KEXINIT);
-       buffer_append(&b, skexinit, skexinitlen);
-
-       buffer_put_string(&b, serverhostkeyblob, sbloblen);
-       buffer_put_bignum2(&b, client_dh_pub);
-       buffer_put_bignum2(&b, server_dh_pub);
-       buffer_put_bignum2(&b, shared_secret);
-
-#ifdef DEBUG_KEX
-       buffer_dump(&b);
-#endif
+       error("Hm, kex protocol error: type %d seq %u", type, seq);
+}
 
-       EVP_DigestInit(&md, evp_md);
-       EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b));
-       EVP_DigestFinal(&md, digest, NULL);
+static void
+kex_reset_dispatch(void)
+{
+       dispatch_range(SSH2_MSG_TRANSPORT_MIN,
+           SSH2_MSG_TRANSPORT_MAX, &kex_protocol_error);
+       dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit);
+}
 
-       buffer_free(&b);
+void
+kex_finish(Kex *kex)
+{
+       kex_reset_dispatch();
 
-#ifdef DEBUG_KEX
-       dump_digest(digest, evp_md->md_size);
-#endif
-       return digest;
+       packet_start(SSH2_MSG_NEWKEYS);
+       packet_send();
+       /* packet_write_wait(); */
+       debug("SSH2_MSG_NEWKEYS sent");
+
+       debug("expecting SSH2_MSG_NEWKEYS");
+       packet_read_expect(SSH2_MSG_NEWKEYS);
+       packet_check_eom();
+       debug("SSH2_MSG_NEWKEYS received");
+
+       kex->done = 1;
+       buffer_clear(&kex->peer);
+       /* buffer_clear(&kex->my); */
+       kex->flags &= ~KEX_INIT_SENT;
+       xfree(kex->name);
+       kex->name = NULL;
 }
 
-u_char *
-kex_hash_gex(
-    char *client_version_string,
-    char *server_version_string,
-    char *ckexinit, int ckexinitlen,
-    char *skexinit, int skexinitlen,
-    char *serverhostkeyblob, int sbloblen,
-    int min, int wantbits, int max, BIGNUM *prime, BIGNUM *gen,
-    BIGNUM *client_dh_pub,
-    BIGNUM *server_dh_pub,
-    BIGNUM *shared_secret)
+void
+kex_send_kexinit(Kex *kex)
 {
-       Buffer b;
-       static u_char digest[EVP_MAX_MD_SIZE];
-       EVP_MD *evp_md = EVP_sha1();
-       EVP_MD_CTX md;
+       u_int32_t rnd = 0;
+       u_char *cookie;
+       u_int i;
 
-       buffer_init(&b);
-       buffer_put_string(&b, client_version_string, strlen(client_version_string));
-       buffer_put_string(&b, server_version_string, strlen(server_version_string));
-
-       /* kexinit messages: fake header: len+SSH2_MSG_KEXINIT */
-       buffer_put_int(&b, ckexinitlen+1);
-       buffer_put_char(&b, SSH2_MSG_KEXINIT);
-       buffer_append(&b, ckexinit, ckexinitlen);
-       buffer_put_int(&b, skexinitlen+1);
-       buffer_put_char(&b, SSH2_MSG_KEXINIT);
-       buffer_append(&b, skexinit, skexinitlen);
-
-       buffer_put_string(&b, serverhostkeyblob, sbloblen);
-       if (min == -1 || max == -1) 
-               buffer_put_int(&b, wantbits);
-       else {
-               buffer_put_int(&b, min);
-               buffer_put_int(&b, wantbits);
-               buffer_put_int(&b, max);
+       if (kex == NULL) {
+               error("kex_send_kexinit: no kex, cannot rekey");
+               return;
        }
-       buffer_put_bignum2(&b, prime);
-       buffer_put_bignum2(&b, gen);
-       buffer_put_bignum2(&b, client_dh_pub);
-       buffer_put_bignum2(&b, server_dh_pub);
-       buffer_put_bignum2(&b, shared_secret);
+       if (kex->flags & KEX_INIT_SENT) {
+               debug("KEX_INIT_SENT");
+               return;
+       }
+       kex->done = 0;
 
-#ifdef DEBUG_KEX
-       buffer_dump(&b);
-#endif
+       /* generate a random cookie */
+       if (buffer_len(&kex->my) < KEX_COOKIE_LEN)
+               fatal("kex_send_kexinit: kex proposal too short");
+       cookie = buffer_ptr(&kex->my);
+       for (i = 0; i < KEX_COOKIE_LEN; i++) {
+               if (i % 4 == 0)
+                       rnd = arc4random();
+               cookie[i] = rnd;
+               rnd >>= 8;
+       }
+       packet_start(SSH2_MSG_KEXINIT);
+       packet_put_raw(buffer_ptr(&kex->my), buffer_len(&kex->my));
+       packet_send();
+       debug("SSH2_MSG_KEXINIT sent");
+       kex->flags |= KEX_INIT_SENT;
+}
 
-       EVP_DigestInit(&md, evp_md);
-       EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b));
-       EVP_DigestFinal(&md, digest, NULL);
+/* ARGSUSED */
+void
+kex_input_kexinit(int type, u_int32_t seq, void *ctxt)
+{
+       char *ptr;
+       u_int i, dlen;
+       Kex *kex = (Kex *)ctxt;
 
-       buffer_free(&b);
+       debug("SSH2_MSG_KEXINIT received");
+       if (kex == NULL)
+               fatal("kex_input_kexinit: no kex, cannot rekey");
 
-#ifdef DEBUG_KEX
-       dump_digest(digest, evp_md->md_size);
-#endif
-       return digest;
+       ptr = packet_get_raw(&dlen);
+       buffer_append(&kex->peer, ptr, dlen);
+
+       /* discard packet */
+       for (i = 0; i < KEX_COOKIE_LEN; i++)
+               packet_get_char();
+       for (i = 0; i < PROPOSAL_MAX; i++)
+               xfree(packet_get_string(NULL));
+       (void) packet_get_char();
+       (void) packet_get_int();
+       packet_check_eom();
+
+       kex_kexinit_finish(kex);
 }
 
-u_char *
-derive_key(int id, int need, u_char *hash, BIGNUM *shared_secret)
+Kex *
+kex_setup(char *proposal[PROPOSAL_MAX])
 {
-       Buffer b;
-       EVP_MD *evp_md = EVP_sha1();
-       EVP_MD_CTX md;
-       char c = id;
-       int have;
-       int mdsz = evp_md->md_size;
-       u_char *digest = xmalloc(((need+mdsz-1)/mdsz)*mdsz);
+       Kex *kex;
 
-       buffer_init(&b);
-       buffer_put_bignum2(&b, shared_secret);
+       kex = xcalloc(1, sizeof(*kex));
+       buffer_init(&kex->peer);
+       buffer_init(&kex->my);
+       kex_prop2buf(&kex->my, proposal);
+       kex->done = 0;
 
-       EVP_DigestInit(&md, evp_md);
-       EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b));  /* shared_secret K */
-       EVP_DigestUpdate(&md, hash, mdsz);              /* transport-06 */
-       EVP_DigestUpdate(&md, &c, 1);                   /* key id */
-       EVP_DigestUpdate(&md, hash, mdsz);              /* session id */
-       EVP_DigestFinal(&md, digest, NULL);
+       kex_send_kexinit(kex);                                  /* we start */
+       kex_reset_dispatch();
 
-       /* expand */
-       for (have = mdsz; need > have; have += mdsz) {
-               EVP_DigestInit(&md, evp_md);
-               EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b));
-               EVP_DigestUpdate(&md, hash, mdsz);
-               EVP_DigestUpdate(&md, digest, have);
-               EVP_DigestFinal(&md, digest + have, NULL);
+       return kex;
+}
+
+static void
+kex_kexinit_finish(Kex *kex)
+{
+       if (!(kex->flags & KEX_INIT_SENT))
+               kex_send_kexinit(kex);
+
+       kex_choose_conf(kex);
+
+       if (kex->kex_type >= 0 && kex->kex_type < KEX_MAX &&
+           kex->kex[kex->kex_type] != NULL) {
+               (kex->kex[kex->kex_type])(kex);
+       } else {
+               fatal("Unsupported key exchange %d", kex->kex_type);
        }
-       buffer_free(&b);
-#ifdef DEBUG_KEX
-       fprintf(stderr, "Digest '%c'== ", c);
-       dump_digest(digest, need);
-#endif
-       return digest;
 }
 
-void
+static void
 choose_enc(Enc *enc, char *client, char *server)
 {
        char *name = match_list(client, server, NULL);
        if (name == NULL)
-               fatal("no matching cipher found: client %s server %s", client, server);
-       enc->cipher = cipher_by_name(name);
-       if (enc->cipher == NULL)
+               fatal("no matching cipher found: client %s server %s",
+                   client, server);
+       if ((enc->cipher = cipher_by_name(name)) == NULL)
                fatal("matching cipher is not supported: %s", name);
        enc->name = name;
        enc->enabled = 0;
        enc->iv = NULL;
        enc->key = NULL;
+       enc->key_len = cipher_keylen(enc->cipher);
+       enc->block_size = cipher_blocksize(enc->cipher);
 }
-void
+
+static void
 choose_mac(Mac *mac, char *client, char *server)
 {
        char *name = match_list(client, server, NULL);
        if (name == NULL)
-               fatal("no matching mac found: client %s server %s", client, server);
-       if (mac_init(mac, name) < 0)
+               fatal("no matching mac found: client %s server %s",
+                   client, server);
+       if (mac_setup(mac, name) < 0)
                fatal("unsupported mac %s", name);
        /* truncate the key */
        if (datafellows & SSH_BUG_HMAC)
@@ -300,35 +287,50 @@ choose_mac(Mac *mac, char *client, char *server)
        mac->key = NULL;
        mac->enabled = 0;
 }
-void
+
+static void
 choose_comp(Comp *comp, char *client, char *server)
 {
        char *name = match_list(client, server, NULL);
        if (name == NULL)
                fatal("no matching comp found: client %s server %s", client, server);
-       if (strcmp(name, "zlib") == 0) {
-               comp->type = 1;
+       if (strcmp(name, "zlib@openssh.com") == 0) {
+               comp->type = COMP_DELAYED;
+       } else if (strcmp(name, "zlib") == 0) {
+               comp->type = COMP_ZLIB;
        } else if (strcmp(name, "none") == 0) {
-               comp->type = 0;
+               comp->type = COMP_NONE;
        } else {
                fatal("unsupported comp %s", name);
        }
        comp->name = name;
 }
-void
+
+static void
 choose_kex(Kex *k, char *client, char *server)
 {
        k->name = match_list(client, server, NULL);
        if (k->name == NULL)
-               fatal("no kex alg");
+               fatal("Unable to negotiate a key exchange method");
        if (strcmp(k->name, KEX_DH1) == 0) {
-               k->kex_type = DH_GRP1_SHA1;
-       } else if (strcmp(k->name, KEX_DHGEX) == 0) {
-               k->kex_type = DH_GEX_SHA1;
+               k->kex_type = KEX_DH_GRP1_SHA1;
+               k->evp_md = EVP_sha1();
+       } else if (strcmp(k->name, KEX_DH14) == 0) {
+               k->kex_type = KEX_DH_GRP14_SHA1;
+               k->evp_md = EVP_sha1();
+       } else if (strcmp(k->name, KEX_DHGEX_SHA1) == 0) {
+               k->kex_type = KEX_DH_GEX_SHA1;
+               k->evp_md = EVP_sha1();
+#if OPENSSL_VERSION_NUMBER >= 0x00907000L
+       } else if (strcmp(k->name, KEX_DHGEX_SHA256) == 0) {
+               k->kex_type = KEX_DH_GEX_SHA256;
+               k->evp_md = evp_ssh_sha256();
+#endif
        } else
                fatal("bad kex alg %s", k->name);
 }
-void
+
+static void
 choose_hostkeyalg(Kex *k, char *client, char *server)
 {
        char *hostkeyalg = match_list(client, server, NULL);
@@ -340,67 +342,237 @@ choose_hostkeyalg(Kex *k, char *client, char *server)
        xfree(hostkeyalg);
 }
 
-Kex *
-kex_choose_conf(char *cprop[PROPOSAL_MAX], char *sprop[PROPOSAL_MAX], int server)
+static int
+proposals_match(char *my[PROPOSAL_MAX], char *peer[PROPOSAL_MAX])
 {
-       int mode;
-       int ctos;                               /* direction: if true client-to-server */
-       int need;
-       Kex *k;
+       static int check[] = {
+               PROPOSAL_KEX_ALGS, PROPOSAL_SERVER_HOST_KEY_ALGS, -1
+       };
+       int *idx;
+       char *p;
+
+       for (idx = &check[0]; *idx != -1; idx++) {
+               if ((p = strchr(my[*idx], ',')) != NULL)
+                       *p = '\0';
+               if ((p = strchr(peer[*idx], ',')) != NULL)
+                       *p = '\0';
+               if (strcmp(my[*idx], peer[*idx]) != 0) {
+                       debug2("proposal mismatch: my %s peer %s",
+                           my[*idx], peer[*idx]);
+                       return (0);
+               }
+       }
+       debug2("proposals match");
+       return (1);
+}
 
-       k = xmalloc(sizeof(*k));
-       memset(k, 0, sizeof(*k));
-       k->server = server;
+static void
+kex_choose_conf(Kex *kex)
+{
+       Newkeys *newkeys;
+       char **my, **peer;
+       char **cprop, **sprop;
+       int nenc, nmac, ncomp;
+       u_int mode, ctos, need;
+       int first_kex_follows, type;
+
+       my   = kex_buf2prop(&kex->my, NULL);
+       peer = kex_buf2prop(&kex->peer, &first_kex_follows);
+
+       if (kex->server) {
+               cprop=peer;
+               sprop=my;
+       } else {
+               cprop=my;
+               sprop=peer;
+       }
 
+       /* Check whether server offers roaming */
+       if (!kex->server) {
+               char *roaming;
+               roaming = match_list(KEX_RESUME, peer[PROPOSAL_KEX_ALGS], NULL);
+               if (roaming) {
+                       kex->roaming = 1;
+                       xfree(roaming);
+               }
+       }
+
+       /* Algorithm Negotiation */
        for (mode = 0; mode < MODE_MAX; mode++) {
-               int nenc, nmac, ncomp;
-               ctos = (!k->server && mode == MODE_OUT) || (k->server && mode == MODE_IN);
+               newkeys = xcalloc(1, sizeof(*newkeys));
+               kex->newkeys[mode] = newkeys;
+               ctos = (!kex->server && mode == MODE_OUT) ||
+                   (kex->server && mode == MODE_IN);
                nenc  = ctos ? PROPOSAL_ENC_ALGS_CTOS  : PROPOSAL_ENC_ALGS_STOC;
                nmac  = ctos ? PROPOSAL_MAC_ALGS_CTOS  : PROPOSAL_MAC_ALGS_STOC;
                ncomp = ctos ? PROPOSAL_COMP_ALGS_CTOS : PROPOSAL_COMP_ALGS_STOC;
-               choose_enc (&k->enc [mode], cprop[nenc],  sprop[nenc]);
-               choose_mac (&k->mac [mode], cprop[nmac],  sprop[nmac]);
-               choose_comp(&k->comp[mode], cprop[ncomp], sprop[ncomp]);
+               choose_enc (&newkeys->enc,  cprop[nenc],  sprop[nenc]);
+               choose_mac (&newkeys->mac,  cprop[nmac],  sprop[nmac]);
+               choose_comp(&newkeys->comp, cprop[ncomp], sprop[ncomp]);
                debug("kex: %s %s %s %s",
                    ctos ? "client->server" : "server->client",
-                   k->enc[mode].name,
-                   k->mac[mode].name,
-                   k->comp[mode].name);
+                   newkeys->enc.name,
+                   newkeys->mac.name,
+                   newkeys->comp.name);
        }
-       choose_kex(k, cprop[PROPOSAL_KEX_ALGS], sprop[PROPOSAL_KEX_ALGS]);
-       choose_hostkeyalg(k, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS],
+       choose_kex(kex, cprop[PROPOSAL_KEX_ALGS], sprop[PROPOSAL_KEX_ALGS]);
+       choose_hostkeyalg(kex, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS],
            sprop[PROPOSAL_SERVER_HOST_KEY_ALGS]);
        need = 0;
        for (mode = 0; mode < MODE_MAX; mode++) {
-           if (need < k->enc[mode].cipher->key_len)
-                   need = k->enc[mode].cipher->key_len;
-           if (need < k->enc[mode].cipher->block_size)
-                   need = k->enc[mode].cipher->block_size;
-           if (need < k->mac[mode].key_len)
-                   need = k->mac[mode].key_len;
+               newkeys = kex->newkeys[mode];
+               if (need < newkeys->enc.key_len)
+                       need = newkeys->enc.key_len;
+               if (need < newkeys->enc.block_size)
+                       need = newkeys->enc.block_size;
+               if (need < newkeys->mac.key_len)
+                       need = newkeys->mac.key_len;
        }
        /* XXX need runden? */
-       k->we_need = need;
-       return k;
+       kex->we_need = need;
+
+       /* ignore the next message if the proposals do not match */
+       if (first_kex_follows && !proposals_match(my, peer) &&
+           !(datafellows & SSH_BUG_FIRSTKEX)) {
+               type = packet_read();
+               debug2("skipping next packet (type %u)", type);
+       }
+
+       kex_prop_free(my);
+       kex_prop_free(peer);
+}
+
+static u_char *
+derive_key(Kex *kex, int id, u_int need, u_char *hash, u_int hashlen,
+    BIGNUM *shared_secret)
+{
+       Buffer b;
+       EVP_MD_CTX md;
+       char c = id;
+       u_int have;
+       int mdsz;
+       u_char *digest;
+
+       if ((mdsz = EVP_MD_size(kex->evp_md)) <= 0)
+               fatal("bad kex md size %d", mdsz);
+       digest = xmalloc(roundup(need, mdsz));
+
+       buffer_init(&b);
+       buffer_put_bignum2(&b, shared_secret);
+
+       /* K1 = HASH(K || H || "A" || session_id) */
+       EVP_DigestInit(&md, kex->evp_md);
+       if (!(datafellows & SSH_BUG_DERIVEKEY))
+               EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b));
+       EVP_DigestUpdate(&md, hash, hashlen);
+       EVP_DigestUpdate(&md, &c, 1);
+       EVP_DigestUpdate(&md, kex->session_id, kex->session_id_len);
+       EVP_DigestFinal(&md, digest, NULL);
+
+       /*
+        * expand key:
+        * Kn = HASH(K || H || K1 || K2 || ... || Kn-1)
+        * Key = K1 || K2 || ... || Kn
+        */
+       for (have = mdsz; need > have; have += mdsz) {
+               EVP_DigestInit(&md, kex->evp_md);
+               if (!(datafellows & SSH_BUG_DERIVEKEY))
+                       EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b));
+               EVP_DigestUpdate(&md, hash, hashlen);
+               EVP_DigestUpdate(&md, digest, have);
+               EVP_DigestFinal(&md, digest + have, NULL);
+       }
+       buffer_free(&b);
+#ifdef DEBUG_KEX
+       fprintf(stderr, "key '%c'== ", c);
+       dump_digest("key", digest, need);
+#endif
+       return digest;
 }
 
+Newkeys *current_keys[MODE_MAX];
+
 #define NKEYS  6
-int
-kex_derive_keys(Kex *k, u_char *hash, BIGNUM *shared_secret)
+void
+kex_derive_keys(Kex *kex, u_char *hash, u_int hashlen, BIGNUM *shared_secret)
 {
-       int i;
-       int mode;
-       int ctos;
        u_char *keys[NKEYS];
+       u_int i, mode, ctos;
 
-       for (i = 0; i < NKEYS; i++)
-               keys[i] = derive_key('A'+i, k->we_need, hash, shared_secret);
+       for (i = 0; i < NKEYS; i++) {
+               keys[i] = derive_key(kex, 'A'+i, kex->we_need, hash, hashlen,
+                   shared_secret);
+       }
 
+       debug2("kex_derive_keys");
        for (mode = 0; mode < MODE_MAX; mode++) {
-               ctos = (!k->server && mode == MODE_OUT) || (k->server && mode == MODE_IN);
-               k->enc[mode].iv  = keys[ctos ? 0 : 1];
-               k->enc[mode].key = keys[ctos ? 2 : 3];
-               k->mac[mode].key = keys[ctos ? 4 : 5];
+               current_keys[mode] = kex->newkeys[mode];
+               kex->newkeys[mode] = NULL;
+               ctos = (!kex->server && mode == MODE_OUT) ||
+                   (kex->server && mode == MODE_IN);
+               current_keys[mode]->enc.iv  = keys[ctos ? 0 : 1];
+               current_keys[mode]->enc.key = keys[ctos ? 2 : 3];
+               current_keys[mode]->mac.key = keys[ctos ? 4 : 5];
        }
-       return 0;
 }
+
+Newkeys *
+kex_get_newkeys(int mode)
+{
+       Newkeys *ret;
+
+       ret = current_keys[mode];
+       current_keys[mode] = NULL;
+       return ret;
+}
+
+void
+derive_ssh1_session_id(BIGNUM *host_modulus, BIGNUM *server_modulus,
+    u_int8_t cookie[8], u_int8_t id[16])
+{
+       const EVP_MD *evp_md = EVP_md5();
+       EVP_MD_CTX md;
+       u_int8_t nbuf[2048], obuf[EVP_MAX_MD_SIZE];
+       int len;
+
+       EVP_DigestInit(&md, evp_md);
+
+       len = BN_num_bytes(host_modulus);
+       if (len < (512 / 8) || (u_int)len > sizeof(nbuf))
+               fatal("%s: bad host modulus (len %d)", __func__, len);
+       BN_bn2bin(host_modulus, nbuf);
+       EVP_DigestUpdate(&md, nbuf, len);
+
+       len = BN_num_bytes(server_modulus);
+       if (len < (512 / 8) || (u_int)len > sizeof(nbuf))
+               fatal("%s: bad server modulus (len %d)", __func__, len);
+       BN_bn2bin(server_modulus, nbuf);
+       EVP_DigestUpdate(&md, nbuf, len);
+
+       EVP_DigestUpdate(&md, cookie, 8);
+
+       EVP_DigestFinal(&md, obuf, NULL);
+       memcpy(id, obuf, 16);
+
+       memset(nbuf, 0, sizeof(nbuf));
+       memset(obuf, 0, sizeof(obuf));
+       memset(&md, 0, sizeof(md));
+}
+
+#if defined(DEBUG_KEX) || defined(DEBUG_KEXDH)
+void
+dump_digest(char *msg, u_char *digest, int len)
+{
+       u_int i;
+
+       fprintf(stderr, "%s\n", msg);
+       for (i = 0; i < len; i++) {
+               fprintf(stderr, "%02x", digest[i]);
+               if (i%32 == 31)
+                       fprintf(stderr, "\n");
+               else if (i%8 == 7)
+                       fprintf(stderr, " ");
+       }
+       fprintf(stderr, "\n");
+}
+#endif
This page took 0.068998 seconds and 4 git commands to generate.