kex: Added diffie-hellman-group-exchange-sha256 support

... and fixed HMAC_Init depricated usage

Closes #48
diff --git a/src/kex.c b/src/kex.c
index 1fdd626..73128ce 100644
--- a/src/kex.c
+++ b/src/kex.c
@@ -70,6 +70,35 @@
             }                                                           \
     }
 
+
+/* Helper macro called from kex_method_diffie_hellman_group1_sha256_key_exchange */
+#define LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA256_HASH(value, reqlen, version) \
+{                                                                       \
+    libssh2_sha256_ctx hash;                                            \
+    unsigned long len = 0;                                              \
+    if (!(value)) {                                                     \
+        value = LIBSSH2_ALLOC(session, reqlen + SHA256_DIGEST_LENGTH);  \
+    }                                                                   \
+    if (value)                                                          \
+        while (len < (unsigned long)reqlen) {                           \
+            libssh2_sha256_init(&hash);                                 \
+            libssh2_sha256_update(hash, exchange_state->k_value,        \
+                                    exchange_state->k_value_len);       \
+            libssh2_sha256_update(hash, exchange_state->h_sig_comp,     \
+                                    SHA256_DIGEST_LENGTH);              \
+        if (len > 0) {                                                  \
+            libssh2_sha256_update(hash, value, len);                    \
+        }    else {                                                     \
+            libssh2_sha256_update(hash, (version), 1);                  \
+            libssh2_sha256_update(hash, session->session_id,            \
+                                    session->session_id_len);           \
+        }                                                               \
+        libssh2_sha256_final(hash, (value) + len);                      \
+        len += SHA256_DIGEST_LENGTH;                                    \
+        }                                                               \
+}
+
+
 /*
  * diffie_hellman_sha1
  *
@@ -83,10 +112,11 @@
                                unsigned char packet_type_reply,
                                unsigned char *midhash,
                                unsigned long midhash_len,
-                               kmdhgGPsha1kex_state_t *exchange_state)
+                               kmdhgGPshakex_state_t *exchange_state)
 {
     int ret = 0;
     int rc;
+    libssh2_sha1_ctx exchange_hash_ctx;
 
     if (exchange_state->state == libssh2_NB_state_idle) {
         /* Setup initial values */
@@ -318,54 +348,56 @@
             _libssh2_bn_to_bin(exchange_state->k, exchange_state->k_value + 5);
         }
 
-        libssh2_sha1_init(&exchange_state->exchange_hash);
+        exchange_state->exchange_hash = (void*)&exchange_hash_ctx;
+        libssh2_sha1_init(&exchange_hash_ctx);
+
         if (session->local.banner) {
             _libssh2_htonu32(exchange_state->h_sig_comp,
                              strlen((char *) session->local.banner) - 2);
-            libssh2_sha1_update(exchange_state->exchange_hash,
+            libssh2_sha1_update(exchange_hash_ctx,
                                 exchange_state->h_sig_comp, 4);
-            libssh2_sha1_update(exchange_state->exchange_hash,
+            libssh2_sha1_update(exchange_hash_ctx,
                                 (char *) session->local.banner,
                                 strlen((char *) session->local.banner) - 2);
         } else {
             _libssh2_htonu32(exchange_state->h_sig_comp,
                              sizeof(LIBSSH2_SSH_DEFAULT_BANNER) - 1);
-            libssh2_sha1_update(exchange_state->exchange_hash,
+            libssh2_sha1_update(exchange_hash_ctx,
                                 exchange_state->h_sig_comp, 4);
-            libssh2_sha1_update(exchange_state->exchange_hash,
+            libssh2_sha1_update(exchange_hash_ctx,
                                 LIBSSH2_SSH_DEFAULT_BANNER,
                                 sizeof(LIBSSH2_SSH_DEFAULT_BANNER) - 1);
         }
 
         _libssh2_htonu32(exchange_state->h_sig_comp,
                          strlen((char *) session->remote.banner));
-        libssh2_sha1_update(exchange_state->exchange_hash,
+        libssh2_sha1_update(exchange_hash_ctx,
                             exchange_state->h_sig_comp, 4);
-        libssh2_sha1_update(exchange_state->exchange_hash,
+        libssh2_sha1_update(exchange_hash_ctx,
                             session->remote.banner,
                             strlen((char *) session->remote.banner));
 
         _libssh2_htonu32(exchange_state->h_sig_comp,
                          session->local.kexinit_len);
-        libssh2_sha1_update(exchange_state->exchange_hash,
+        libssh2_sha1_update(exchange_hash_ctx,
                             exchange_state->h_sig_comp, 4);
-        libssh2_sha1_update(exchange_state->exchange_hash,
+        libssh2_sha1_update(exchange_hash_ctx,
                             session->local.kexinit,
                             session->local.kexinit_len);
 
         _libssh2_htonu32(exchange_state->h_sig_comp,
                          session->remote.kexinit_len);
-        libssh2_sha1_update(exchange_state->exchange_hash,
+        libssh2_sha1_update(exchange_hash_ctx,
                             exchange_state->h_sig_comp, 4);
-        libssh2_sha1_update(exchange_state->exchange_hash,
+        libssh2_sha1_update(exchange_hash_ctx,
                             session->remote.kexinit,
                             session->remote.kexinit_len);
 
         _libssh2_htonu32(exchange_state->h_sig_comp,
                          session->server_hostkey_len);
-        libssh2_sha1_update(exchange_state->exchange_hash,
+        libssh2_sha1_update(exchange_hash_ctx,
                             exchange_state->h_sig_comp, 4);
-        libssh2_sha1_update(exchange_state->exchange_hash,
+        libssh2_sha1_update(exchange_hash_ctx,
                             session->server_hostkey,
                             session->server_hostkey_len);
 
@@ -378,38 +410,38 @@
                              LIBSSH2_DH_GEX_OPTGROUP);
             _libssh2_htonu32(exchange_state->h_sig_comp + 8,
                              LIBSSH2_DH_GEX_MAXGROUP);
-            libssh2_sha1_update(exchange_state->exchange_hash,
+            libssh2_sha1_update(exchange_hash_ctx,
                                 exchange_state->h_sig_comp, 12);
 #else
             _libssh2_htonu32(exchange_state->h_sig_comp,
                              LIBSSH2_DH_GEX_OPTGROUP);
-            libssh2_sha1_update(exchange_state->exchange_hash,
+            libssh2_sha1_update(exchange_hash_ctx,
                                 exchange_state->h_sig_comp, 4);
 #endif
         }
 
         if (midhash) {
-            libssh2_sha1_update(exchange_state->exchange_hash, midhash,
+            libssh2_sha1_update(exchange_hash_ctx, midhash,
                                 midhash_len);
         }
 
-        libssh2_sha1_update(exchange_state->exchange_hash,
+        libssh2_sha1_update(exchange_hash_ctx,
                             exchange_state->e_packet + 1,
                             exchange_state->e_packet_len - 1);
 
         _libssh2_htonu32(exchange_state->h_sig_comp,
                          exchange_state->f_value_len);
-        libssh2_sha1_update(exchange_state->exchange_hash,
+        libssh2_sha1_update(exchange_hash_ctx,
                             exchange_state->h_sig_comp, 4);
-        libssh2_sha1_update(exchange_state->exchange_hash,
+        libssh2_sha1_update(exchange_hash_ctx,
                             exchange_state->f_value,
                             exchange_state->f_value_len);
 
-        libssh2_sha1_update(exchange_state->exchange_hash,
+        libssh2_sha1_update(exchange_hash_ctx,
                             exchange_state->k_value,
                             exchange_state->k_value_len);
 
-        libssh2_sha1_final(exchange_state->exchange_hash,
+        libssh2_sha1_final(exchange_hash_ctx,
                            exchange_state->h_sig_comp);
 
         if (session->hostkey->
@@ -687,6 +719,628 @@
 }
 
 
+/*
+ * diffie_hellman_sha256
+ *
+ * Diffie Hellman Key Exchange, Group Agnostic
+ */
+static int diffie_hellman_sha256(LIBSSH2_SESSION *session,
+                                 _libssh2_bn *g,
+                                 _libssh2_bn *p,
+                                 int group_order,
+                                 unsigned char packet_type_init,
+                                 unsigned char packet_type_reply,
+                                 unsigned char *midhash,
+                                 unsigned long midhash_len,
+                                 kmdhgGPshakex_state_t *exchange_state)
+{
+    int ret = 0;
+    int rc;
+    libssh2_sha256_ctx exchange_hash_ctx;
+
+    if (exchange_state->state == libssh2_NB_state_idle) {
+        /* Setup initial values */
+        exchange_state->e_packet = NULL;
+        exchange_state->s_packet = NULL;
+        exchange_state->k_value = NULL;
+        exchange_state->ctx = _libssh2_bn_ctx_new();
+        exchange_state->x = _libssh2_bn_init(); /* Random from client */
+        exchange_state->e = _libssh2_bn_init(); /* g^x mod p */
+        exchange_state->f = _libssh2_bn_init_from_bin(); /* g^(Random from server) mod p */
+        exchange_state->k = _libssh2_bn_init(); /* The shared secret: f^x mod p */
+
+        /* Zero the whole thing out */
+        memset(&exchange_state->req_state, 0, sizeof(packet_require_state_t));
+
+        /* Generate x and e */
+        _libssh2_bn_rand(exchange_state->x, group_order, 0, -1);
+        _libssh2_bn_mod_exp(exchange_state->e, g, exchange_state->x, p,
+                            exchange_state->ctx);
+
+        /* Send KEX init */
+        /* packet_type(1) + String Length(4) + leading 0(1) */
+        exchange_state->e_packet_len =
+            _libssh2_bn_bytes(exchange_state->e) + 6;
+        if (_libssh2_bn_bits(exchange_state->e) % 8) {
+            /* Leading 00 not needed */
+            exchange_state->e_packet_len--;
+        }
+
+        exchange_state->e_packet =
+            LIBSSH2_ALLOC(session, exchange_state->e_packet_len);
+        if (!exchange_state->e_packet) {
+            ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+                                 "Out of memory error");
+            goto clean_exit;
+        }
+        exchange_state->e_packet[0] = packet_type_init;
+        _libssh2_htonu32(exchange_state->e_packet + 1,
+                         exchange_state->e_packet_len - 5);
+        if (_libssh2_bn_bits(exchange_state->e) % 8) {
+            _libssh2_bn_to_bin(exchange_state->e,
+                               exchange_state->e_packet + 5);
+        } else {
+            exchange_state->e_packet[5] = 0;
+            _libssh2_bn_to_bin(exchange_state->e,
+                               exchange_state->e_packet + 6);
+        }
+
+        _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Sending KEX packet %d",
+                       (int) packet_type_init);
+        exchange_state->state = libssh2_NB_state_created;
+    }
+
+    if (exchange_state->state == libssh2_NB_state_created) {
+        rc = _libssh2_transport_send(session, exchange_state->e_packet,
+                                     exchange_state->e_packet_len,
+                                     NULL, 0);
+        if (rc == LIBSSH2_ERROR_EAGAIN) {
+            return rc;
+        } else if (rc) {
+            ret = _libssh2_error(session, rc,
+                                 "Unable to send KEX init message");
+            goto clean_exit;
+        }
+        exchange_state->state = libssh2_NB_state_sent;
+    }
+
+    if (exchange_state->state == libssh2_NB_state_sent) {
+        if (session->burn_optimistic_kexinit) {
+            /* The first KEX packet to come along will be the guess initially
+             * sent by the server.  That guess turned out to be wrong so we
+             * need to silently ignore it */
+            int burn_type;
+
+            _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+                           "Waiting for badly guessed KEX packet (to be ignored)");
+            burn_type =
+                _libssh2_packet_burn(session, &exchange_state->burn_state);
+            if (burn_type == LIBSSH2_ERROR_EAGAIN) {
+                return burn_type;
+            } else if (burn_type <= 0) {
+                /* Failed to receive a packet */
+                ret = burn_type;
+                goto clean_exit;
+            }
+            session->burn_optimistic_kexinit = 0;
+
+            _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+                           "Burnt packet of type: %02x",
+                           (unsigned int) burn_type);
+        }
+
+        exchange_state->state = libssh2_NB_state_sent1;
+    }
+
+    if (exchange_state->state == libssh2_NB_state_sent1) {
+        /* Wait for KEX reply */
+        rc = _libssh2_packet_require(session, packet_type_reply,
+                                     &exchange_state->s_packet,
+                                     &exchange_state->s_packet_len, 0, NULL,
+                                     0, &exchange_state->req_state);
+        if (rc == LIBSSH2_ERROR_EAGAIN) {
+            return rc;
+        }
+        if (rc) {
+            ret = _libssh2_error(session, LIBSSH2_ERROR_TIMEOUT,
+                                 "Timed out waiting for KEX reply");
+            goto clean_exit;
+        }
+
+        /* Parse KEXDH_REPLY */
+        exchange_state->s = exchange_state->s_packet + 1;
+
+        session->server_hostkey_len = _libssh2_ntohu32(exchange_state->s);
+        exchange_state->s += 4;
+
+        if (session->server_hostkey)
+            LIBSSH2_FREE(session, session->server_hostkey);
+
+        session->server_hostkey =
+            LIBSSH2_ALLOC(session, session->server_hostkey_len);
+        if (!session->server_hostkey) {
+            ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+                                 "Unable to allocate memory for a copy "
+                                 "of the host key");
+            goto clean_exit;
+        }
+        memcpy(session->server_hostkey, exchange_state->s,
+               session->server_hostkey_len);
+        exchange_state->s += session->server_hostkey_len;
+
+#if LIBSSH2_MD5
+        {
+            libssh2_md5_ctx fingerprint_ctx;
+
+            if (libssh2_md5_init(&fingerprint_ctx)) {
+                libssh2_md5_update(fingerprint_ctx, session->server_hostkey,
+                                   session->server_hostkey_len);
+                libssh2_md5_final(fingerprint_ctx,
+                                  session->server_hostkey_md5);
+                session->server_hostkey_md5_valid = TRUE;
+            }
+            else {
+                session->server_hostkey_md5_valid = FALSE;
+            }
+        }
+#ifdef LIBSSH2DEBUG
+        {
+            char fingerprint[50], *fprint = fingerprint;
+            int i;
+            for(i = 0; i < 16; i++, fprint += 3) {
+                snprintf(fprint, 4, "%02x:", session->server_hostkey_md5[i]);
+            }
+            *(--fprint) = '\0';
+            _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+                           "Server's MD5 Fingerprint: %s", fingerprint);
+        }
+#endif /* LIBSSH2DEBUG */
+#endif /* ! LIBSSH2_MD5 */
+
+        {
+            libssh2_sha1_ctx fingerprint_ctx;
+
+            if (libssh2_sha1_init(&fingerprint_ctx)) {
+                libssh2_sha1_update(fingerprint_ctx, session->server_hostkey,
+                                    session->server_hostkey_len);
+                libssh2_sha1_final(fingerprint_ctx,
+                                   session->server_hostkey_sha1);
+                session->server_hostkey_sha1_valid = TRUE;
+            }
+            else {
+                session->server_hostkey_sha1_valid = FALSE;
+            }
+        }
+#ifdef LIBSSH2DEBUG
+        {
+            char fingerprint[64], *fprint = fingerprint;
+            int i;
+
+            for(i = 0; i < 20; i++, fprint += 3) {
+                snprintf(fprint, 4, "%02x:", session->server_hostkey_sha1[i]);
+            }
+            *(--fprint) = '\0';
+            _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+                           "Server's SHA1 Fingerprint: %s", fingerprint);
+        }
+#endif /* LIBSSH2DEBUG */
+
+        if (session->hostkey->init(session, session->server_hostkey,
+                                   session->server_hostkey_len,
+                                   &session->server_hostkey_abstract)) {
+            ret = _libssh2_error(session, LIBSSH2_ERROR_HOSTKEY_INIT,
+                                 "Unable to initialize hostkey importer");
+            goto clean_exit;
+        }
+
+        exchange_state->f_value_len = _libssh2_ntohu32(exchange_state->s);
+        exchange_state->s += 4;
+        exchange_state->f_value = exchange_state->s;
+        exchange_state->s += exchange_state->f_value_len;
+        _libssh2_bn_from_bin(exchange_state->f, exchange_state->f_value_len,
+                             exchange_state->f_value);
+
+        exchange_state->h_sig_len = _libssh2_ntohu32(exchange_state->s);
+        exchange_state->s += 4;
+        exchange_state->h_sig = exchange_state->s;
+
+        /* Compute the shared secret */
+        _libssh2_bn_mod_exp(exchange_state->k, exchange_state->f,
+                            exchange_state->x, p, exchange_state->ctx);
+        exchange_state->k_value_len = _libssh2_bn_bytes(exchange_state->k) + 5;
+        if (_libssh2_bn_bits(exchange_state->k) % 8) {
+            /* don't need leading 00 */
+            exchange_state->k_value_len--;
+        }
+        exchange_state->k_value =
+            LIBSSH2_ALLOC(session, exchange_state->k_value_len);
+        if (!exchange_state->k_value) {
+            ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+                                 "Unable to allocate buffer for K");
+            goto clean_exit;
+        }
+        _libssh2_htonu32(exchange_state->k_value,
+                         exchange_state->k_value_len - 4);
+        if (_libssh2_bn_bits(exchange_state->k) % 8) {
+            _libssh2_bn_to_bin(exchange_state->k, exchange_state->k_value + 4);
+        } else {
+            exchange_state->k_value[4] = 0;
+            _libssh2_bn_to_bin(exchange_state->k, exchange_state->k_value + 5);
+        }
+
+        exchange_state->exchange_hash = (void*)&exchange_hash_ctx;
+        libssh2_sha256_init(&exchange_hash_ctx);
+
+        if (session->local.banner) {
+            _libssh2_htonu32(exchange_state->h_sig_comp,
+                             strlen((char *) session->local.banner) - 2);
+            libssh2_sha256_update(exchange_hash_ctx,
+                                  exchange_state->h_sig_comp, 4);
+            libssh2_sha256_update(exchange_hash_ctx,
+                                  (char *) session->local.banner,
+                                  strlen((char *) session->local.banner) - 2);
+        } else {
+            _libssh2_htonu32(exchange_state->h_sig_comp,
+                             sizeof(LIBSSH2_SSH_DEFAULT_BANNER) - 1);
+            libssh2_sha256_update(exchange_hash_ctx,
+                                  exchange_state->h_sig_comp, 4);
+            libssh2_sha256_update(exchange_hash_ctx,
+                                  LIBSSH2_SSH_DEFAULT_BANNER,
+                                  sizeof(LIBSSH2_SSH_DEFAULT_BANNER) - 1);
+        }
+
+        _libssh2_htonu32(exchange_state->h_sig_comp,
+                         strlen((char *) session->remote.banner));
+        libssh2_sha256_update(exchange_hash_ctx,
+                              exchange_state->h_sig_comp, 4);
+        libssh2_sha256_update(exchange_hash_ctx,
+                              session->remote.banner,
+                              strlen((char *) session->remote.banner));
+
+        _libssh2_htonu32(exchange_state->h_sig_comp,
+                         session->local.kexinit_len);
+        libssh2_sha256_update(exchange_hash_ctx,
+                              exchange_state->h_sig_comp, 4);
+        libssh2_sha256_update(exchange_hash_ctx,
+                              session->local.kexinit,
+                              session->local.kexinit_len);
+
+        _libssh2_htonu32(exchange_state->h_sig_comp,
+                         session->remote.kexinit_len);
+        libssh2_sha256_update(exchange_hash_ctx,
+                              exchange_state->h_sig_comp, 4);
+        libssh2_sha256_update(exchange_hash_ctx,
+                              session->remote.kexinit,
+                              session->remote.kexinit_len);
+
+        _libssh2_htonu32(exchange_state->h_sig_comp,
+                         session->server_hostkey_len);
+        libssh2_sha256_update(exchange_hash_ctx,
+                              exchange_state->h_sig_comp, 4);
+        libssh2_sha256_update(exchange_hash_ctx,
+                              session->server_hostkey,
+                              session->server_hostkey_len);
+
+        if (packet_type_init == SSH_MSG_KEX_DH_GEX_INIT) {
+            /* diffie-hellman-group-exchange hashes additional fields */
+#ifdef LIBSSH2_DH_GEX_NEW
+            _libssh2_htonu32(exchange_state->h_sig_comp,
+                             LIBSSH2_DH_GEX_MINGROUP);
+            _libssh2_htonu32(exchange_state->h_sig_comp + 4,
+                             LIBSSH2_DH_GEX_OPTGROUP);
+            _libssh2_htonu32(exchange_state->h_sig_comp + 8,
+                             LIBSSH2_DH_GEX_MAXGROUP);
+            libssh2_sha256_update(exchange_hash_ctx,
+                                  exchange_state->h_sig_comp, 12);
+#else
+            _libssh2_htonu32(exchange_state->h_sig_comp,
+                             LIBSSH2_DH_GEX_OPTGROUP);
+            libssh2_sha256_update(exchange_hash_ctx,
+                                  exchange_state->h_sig_comp, 4);
+#endif
+        }
+
+        if (midhash) {
+            libssh2_sha256_update(exchange_hash_ctx, midhash,
+                                  midhash_len);
+        }
+
+        libssh2_sha256_update(exchange_hash_ctx,
+                              exchange_state->e_packet + 1,
+                              exchange_state->e_packet_len - 1);
+
+        _libssh2_htonu32(exchange_state->h_sig_comp,
+                         exchange_state->f_value_len);
+        libssh2_sha256_update(exchange_hash_ctx,
+                              exchange_state->h_sig_comp, 4);
+        libssh2_sha256_update(exchange_hash_ctx,
+                              exchange_state->f_value,
+                              exchange_state->f_value_len);
+
+        libssh2_sha256_update(exchange_hash_ctx,
+                              exchange_state->k_value,
+                              exchange_state->k_value_len);
+
+        libssh2_sha256_final(exchange_hash_ctx,
+                             exchange_state->h_sig_comp);
+
+        if (session->hostkey->
+            sig_verify(session, exchange_state->h_sig,
+                       exchange_state->h_sig_len, exchange_state->h_sig_comp,
+                       SHA256_DIGEST_LENGTH, &session->server_hostkey_abstract)) {
+            ret = _libssh2_error(session, LIBSSH2_ERROR_HOSTKEY_SIGN,
+                                 "Unable to verify hostkey signature");
+            goto clean_exit;
+        }
+
+
+
+        _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Sending NEWKEYS message");
+        exchange_state->c = SSH_MSG_NEWKEYS;
+
+        exchange_state->state = libssh2_NB_state_sent2;
+    }
+
+    if (exchange_state->state == libssh2_NB_state_sent2) {
+        rc = _libssh2_transport_send(session, &exchange_state->c, 1, NULL, 0);
+        if (rc == LIBSSH2_ERROR_EAGAIN) {
+            return rc;
+        } else if (rc) {
+            ret = _libssh2_error(session, rc, "Unable to send NEWKEYS message");
+            goto clean_exit;
+        }
+
+        exchange_state->state = libssh2_NB_state_sent3;
+    }
+
+    if (exchange_state->state == libssh2_NB_state_sent3) {
+        rc = _libssh2_packet_require(session, SSH_MSG_NEWKEYS,
+                                     &exchange_state->tmp,
+                                     &exchange_state->tmp_len, 0, NULL, 0,
+                                     &exchange_state->req_state);
+        if (rc == LIBSSH2_ERROR_EAGAIN) {
+            return rc;
+        } else if (rc) {
+            ret = _libssh2_error(session, rc, "Timed out waiting for NEWKEYS");
+            goto clean_exit;
+        }
+        /* The first key exchange has been performed,
+           switch to active crypt/comp/mac mode */
+        session->state |= LIBSSH2_STATE_NEWKEYS;
+        _libssh2_debug(session, LIBSSH2_TRACE_KEX, "Received NEWKEYS message");
+
+        /* This will actually end up being just packet_type(1)
+           for this packet type anyway */
+        LIBSSH2_FREE(session, exchange_state->tmp);
+
+        if (!session->session_id) {
+            session->session_id = LIBSSH2_ALLOC(session, SHA256_DIGEST_LENGTH);
+            if (!session->session_id) {
+                ret = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+                                     "Unable to allocate buffer for SHA digest");
+                goto clean_exit;
+            }
+            memcpy(session->session_id, exchange_state->h_sig_comp,
+                   SHA256_DIGEST_LENGTH);
+            session->session_id_len = SHA256_DIGEST_LENGTH;
+            _libssh2_debug(session, LIBSSH2_TRACE_KEX, "session_id calculated");
+        }
+
+        /* Cleanup any existing cipher */
+        if (session->local.crypt->dtor) {
+            session->local.crypt->dtor(session,
+                                       &session->local.crypt_abstract);
+        }
+
+        /* Calculate IV/Secret/Key for each direction */
+        if (session->local.crypt->init) {
+            unsigned char *iv = NULL, *secret = NULL;
+            int free_iv = 0, free_secret = 0;
+
+            LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA256_HASH(iv,
+                                                          session->local.crypt->
+                                                          iv_len, "A");
+            if (!iv) {
+                ret = -1;
+                goto clean_exit;
+            }
+            LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA256_HASH(secret,
+                                                          session->local.crypt->
+                                                          secret_len, "C");
+            if (!secret) {
+                LIBSSH2_FREE(session, iv);
+                ret = LIBSSH2_ERROR_KEX_FAILURE;
+                goto clean_exit;
+            }
+            if (session->local.crypt->
+                init(session, session->local.crypt, iv, &free_iv, secret,
+                     &free_secret, 1, &session->local.crypt_abstract)) {
+                LIBSSH2_FREE(session, iv);
+                LIBSSH2_FREE(session, secret);
+                ret = LIBSSH2_ERROR_KEX_FAILURE;
+                goto clean_exit;
+            }
+
+            if (free_iv) {
+                memset(iv, 0, session->local.crypt->iv_len);
+                LIBSSH2_FREE(session, iv);
+            }
+
+            if (free_secret) {
+                memset(secret, 0, session->local.crypt->secret_len);
+                LIBSSH2_FREE(session, secret);
+            }
+        }
+        _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+                       "Client to Server IV and Key calculated");
+
+        if (session->remote.crypt->dtor) {
+            /* Cleanup any existing cipher */
+            session->remote.crypt->dtor(session,
+                                        &session->remote.crypt_abstract);
+        }
+
+        if (session->remote.crypt->init) {
+            unsigned char *iv = NULL, *secret = NULL;
+            int free_iv = 0, free_secret = 0;
+
+            LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA256_HASH(iv,
+                                                          session->remote.crypt->
+                                                          iv_len, "B");
+            if (!iv) {
+                ret = LIBSSH2_ERROR_KEX_FAILURE;
+                goto clean_exit;
+            }
+            LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA256_HASH(secret,
+                                                          session->remote.crypt->
+                                                          secret_len, "D");
+            if (!secret) {
+                LIBSSH2_FREE(session, iv);
+                ret = LIBSSH2_ERROR_KEX_FAILURE;
+                goto clean_exit;
+            }
+            if (session->remote.crypt->
+                init(session, session->remote.crypt, iv, &free_iv, secret,
+                     &free_secret, 0, &session->remote.crypt_abstract)) {
+                LIBSSH2_FREE(session, iv);
+                LIBSSH2_FREE(session, secret);
+                ret = LIBSSH2_ERROR_KEX_FAILURE;
+                goto clean_exit;
+            }
+
+            if (free_iv) {
+                memset(iv, 0, session->remote.crypt->iv_len);
+                LIBSSH2_FREE(session, iv);
+            }
+
+            if (free_secret) {
+                memset(secret, 0, session->remote.crypt->secret_len);
+                LIBSSH2_FREE(session, secret);
+            }
+        }
+        _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+                       "Server to Client IV and Key calculated");
+
+        if (session->local.mac->dtor) {
+            session->local.mac->dtor(session, &session->local.mac_abstract);
+        }
+
+        if (session->local.mac->init) {
+            unsigned char *key = NULL;
+            int free_key = 0;
+
+            LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA256_HASH(key,
+                                                          session->local.mac->
+                                                          key_len, "E");
+            if (!key) {
+                ret = LIBSSH2_ERROR_KEX_FAILURE;
+                goto clean_exit;
+            }
+            session->local.mac->init(session, key, &free_key,
+                                     &session->local.mac_abstract);
+
+            if (free_key) {
+                memset(key, 0, session->local.mac->key_len);
+                LIBSSH2_FREE(session, key);
+            }
+        }
+        _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+                       "Client to Server HMAC Key calculated");
+
+        if (session->remote.mac->dtor) {
+            session->remote.mac->dtor(session, &session->remote.mac_abstract);
+        }
+
+        if (session->remote.mac->init) {
+            unsigned char *key = NULL;
+            int free_key = 0;
+
+            LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA256_HASH(key,
+                                                          session->remote.mac->
+                                                          key_len, "F");
+            if (!key) {
+                ret = LIBSSH2_ERROR_KEX_FAILURE;
+                goto clean_exit;
+            }
+            session->remote.mac->init(session, key, &free_key,
+                                      &session->remote.mac_abstract);
+
+            if (free_key) {
+                memset(key, 0, session->remote.mac->key_len);
+                LIBSSH2_FREE(session, key);
+            }
+        }
+        _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+                       "Server to Client HMAC Key calculated");
+
+        /* Initialize compression for each direction */
+
+        /* Cleanup any existing compression */
+        if (session->local.comp && session->local.comp->dtor) {
+            session->local.comp->dtor(session, 1,
+                                      &session->local.comp_abstract);
+        }
+
+        if (session->local.comp && session->local.comp->init) {
+            if (session->local.comp->init(session, 1,
+                                          &session->local.comp_abstract)) {
+                ret = LIBSSH2_ERROR_KEX_FAILURE;
+                goto clean_exit;
+            }
+        }
+        _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+                       "Client to Server compression initialized");
+
+        if (session->remote.comp && session->remote.comp->dtor) {
+            session->remote.comp->dtor(session, 0,
+                                       &session->remote.comp_abstract);
+        }
+
+        if (session->remote.comp && session->remote.comp->init) {
+            if (session->remote.comp->init(session, 0,
+                                           &session->remote.comp_abstract)) {
+                ret = LIBSSH2_ERROR_KEX_FAILURE;
+                goto clean_exit;
+            }
+        }
+        _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+                       "Server to Client compression initialized");
+
+    }
+
+  clean_exit:
+    _libssh2_bn_free(exchange_state->x);
+    exchange_state->x = NULL;
+    _libssh2_bn_free(exchange_state->e);
+    exchange_state->e = NULL;
+    _libssh2_bn_free(exchange_state->f);
+    exchange_state->f = NULL;
+    _libssh2_bn_free(exchange_state->k);
+    exchange_state->k = NULL;
+    _libssh2_bn_ctx_free(exchange_state->ctx);
+    exchange_state->ctx = NULL;
+
+    if (exchange_state->e_packet) {
+        LIBSSH2_FREE(session, exchange_state->e_packet);
+        exchange_state->e_packet = NULL;
+    }
+
+    if (exchange_state->s_packet) {
+        LIBSSH2_FREE(session, exchange_state->s_packet);
+        exchange_state->s_packet = NULL;
+    }
+
+    if (exchange_state->k_value) {
+        LIBSSH2_FREE(session, exchange_state->k_value);
+        exchange_state->k_value = NULL;
+    }
+
+    exchange_state->state = libssh2_NB_state_idle;
+
+    return ret;
+}
+
+
 
 /* kex_method_diffie_hellman_group1_sha1_key_exchange
  * Diffie-Hellman Group1 (Actually Group2) Key Exchange using SHA1
@@ -925,6 +1579,105 @@
 
 
 
+/* kex_method_diffie_hellman_group_exchange_sha256_key_exchange
+ * Diffie-Hellman Group Exchange Key Exchange using SHA256
+ * Negotiates random(ish) group for secret derivation
+ */
+static int
+kex_method_diffie_hellman_group_exchange_sha256_key_exchange
+(LIBSSH2_SESSION * session, key_exchange_state_low_t * key_state)
+{
+    unsigned long p_len, g_len;
+    int ret = 0;
+    int rc;
+
+    if (key_state->state == libssh2_NB_state_idle) {
+        key_state->p = _libssh2_bn_init();
+        key_state->g = _libssh2_bn_init();
+        /* Ask for a P and G pair */
+#ifdef LIBSSH2_DH_GEX_NEW
+        key_state->request[0] = SSH_MSG_KEX_DH_GEX_REQUEST;
+        _libssh2_htonu32(key_state->request + 1, LIBSSH2_DH_GEX_MINGROUP);
+        _libssh2_htonu32(key_state->request + 5, LIBSSH2_DH_GEX_OPTGROUP);
+        _libssh2_htonu32(key_state->request + 9, LIBSSH2_DH_GEX_MAXGROUP);
+        key_state->request_len = 13;
+        _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+                       "Initiating Diffie-Hellman Group-Exchange (New Method SHA256)");
+#else
+        key_state->request[0] = SSH_MSG_KEX_DH_GEX_REQUEST_OLD;
+        _libssh2_htonu32(key_state->request + 1, LIBSSH2_DH_GEX_OPTGROUP);
+        key_state->request_len = 5;
+        _libssh2_debug(session, LIBSSH2_TRACE_KEX,
+                       "Initiating Diffie-Hellman Group-Exchange (Old Method SHA256)");
+#endif
+
+        key_state->state = libssh2_NB_state_created;
+    }
+
+    if (key_state->state == libssh2_NB_state_created) {
+        rc = _libssh2_transport_send(session, key_state->request,
+                                     key_state->request_len, NULL, 0);
+        if (rc == LIBSSH2_ERROR_EAGAIN) {
+            return rc;
+        } else if (rc) {
+            ret = _libssh2_error(session, rc,
+                                 "Unable to send Group Exchange Request SHA256");
+            goto dh_gex_clean_exit;
+        }
+
+        key_state->state = libssh2_NB_state_sent;
+    }
+
+    if (key_state->state == libssh2_NB_state_sent) {
+        rc = _libssh2_packet_require(session, SSH_MSG_KEX_DH_GEX_GROUP,
+                                     &key_state->data, &key_state->data_len,
+                                     0, NULL, 0, &key_state->req_state);
+        if (rc == LIBSSH2_ERROR_EAGAIN) {
+            return rc;
+        } else if (rc) {
+            ret = _libssh2_error(session, rc,
+                                 "Timeout waiting for GEX_GROUP reply SHA256");
+            goto dh_gex_clean_exit;
+        }
+
+        key_state->state = libssh2_NB_state_sent1;
+    }
+
+    if (key_state->state == libssh2_NB_state_sent1) {
+        unsigned char *s = key_state->data + 1;
+        p_len = _libssh2_ntohu32(s);
+        s += 4;
+        _libssh2_bn_from_bin(key_state->p, p_len, s);
+        s += p_len;
+
+        g_len = _libssh2_ntohu32(s);
+        s += 4;
+        _libssh2_bn_from_bin(key_state->g, g_len, s);
+
+        ret = diffie_hellman_sha256(session, key_state->g, key_state->p, p_len,
+                                    SSH_MSG_KEX_DH_GEX_INIT,
+                                    SSH_MSG_KEX_DH_GEX_REPLY,
+                                    key_state->data + 1,
+                                    key_state->data_len - 1,
+                                    &key_state->exchange_state);
+        if (ret == LIBSSH2_ERROR_EAGAIN) {
+            return ret;
+        }
+
+        LIBSSH2_FREE(session, key_state->data);
+    }
+
+  dh_gex_clean_exit:
+    key_state->state = libssh2_NB_state_idle;
+    _libssh2_bn_free(key_state->g);
+    key_state->g = NULL;
+    _libssh2_bn_free(key_state->p);
+    key_state->p = NULL;
+
+    return ret;
+}
+
+
 #define LIBSSH2_KEX_METHOD_FLAG_REQ_ENC_HOSTKEY     0x0001
 #define LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY    0x0002
 
@@ -947,7 +1700,16 @@
     LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY,
 };
 
+static const LIBSSH2_KEX_METHOD
+kex_method_diffie_helman_group_exchange_sha256 = {
+    "diffie-hellman-group-exchange-sha256",
+    kex_method_diffie_hellman_group_exchange_sha256_key_exchange,
+    LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY,
+};
+
 static const LIBSSH2_KEX_METHOD *libssh2_kex_methods[] = {
+    &kex_method_diffie_helman_group_exchange_sha256,
+    &kex_method_diffie_helman_group_exchange_sha1,
     &kex_method_diffie_helman_group14_sha1,
     &kex_method_diffie_helman_group_exchange_sha1,
     &kex_method_diffie_helman_group1_sha1,
@@ -1604,21 +2366,21 @@
 
     /* Locate each string */
     if(kex_string_pair(&s, data, data_len, &kex_len, &kex))
-       return -1;
+        return -1;
     if(kex_string_pair(&s, data, data_len, &hostkey_len, &hostkey))
-       return -1;
+        return -1;
     if(kex_string_pair(&s, data, data_len, &crypt_cs_len, &crypt_cs))
-       return -1;
+        return -1;
     if(kex_string_pair(&s, data, data_len, &crypt_sc_len, &crypt_sc))
-       return -1;
+        return -1;
     if(kex_string_pair(&s, data, data_len, &mac_cs_len, &mac_cs))
-       return -1;
+        return -1;
     if(kex_string_pair(&s, data, data_len, &mac_sc_len, &mac_sc))
-       return -1;
+        return -1;
     if(kex_string_pair(&s, data, data_len, &comp_cs_len, &comp_cs))
-       return -1;
+        return -1;
     if(kex_string_pair(&s, data, data_len, &comp_sc_len, &comp_sc))
-       return -1;
+        return -1;
 
     /* If the server sent an optimistic packet, assume that it guessed wrong.
      * If the guess is determined to be right (by kex_agree_kex_hostkey)
@@ -1686,7 +2448,7 @@
  */
 int
 _libssh2_kex_exchange(LIBSSH2_SESSION * session, int reexchange,
-                     key_exchange_state_t * key_state)
+                      key_exchange_state_t * key_state)
 {
     int rc = 0;
     int retcode;
diff --git a/src/libgcrypt.h b/src/libgcrypt.h
index e2eb4d2..931a7a4 100644
--- a/src/libgcrypt.h
+++ b/src/libgcrypt.h
@@ -57,6 +57,7 @@
 
 #define MD5_DIGEST_LENGTH 16
 #define SHA_DIGEST_LENGTH 20
+#define SHA256_DIGEST_LENGTH 32
 
 #define _libssh2_random(buf, len)                \
   (gcry_randomize ((buf), (len), GCRY_STRONG_RANDOM), 1)
@@ -73,6 +74,15 @@
 #define libssh2_sha1(message, len, out) \
   gcry_md_hash_buffer (GCRY_MD_SHA1, out, message, len)
 
+#define libssh2_sha256_init(ctx) \
+  (GPG_ERR_NO_ERROR == gcry_md_open (ctx,  GCRY_MD_SHA256, 0))
+#define libssh2_sha256_update(ctx, data, len) \
+  gcry_md_write (ctx, (unsigned char *) data, len)
+#define libssh2_sha256_final(ctx, out) \
+  memcpy (out, gcry_md_read (ctx, 0), SHA256_DIGEST_LENGTH), gcry_md_close (ctx)
+#define libssh2_sha256(message, len, out) \
+  gcry_md_hash_buffer (GCRY_MD_SHA256, out, message, len)
+
 #define libssh2_md5_ctx gcry_md_hd_t
 
 /* returns 0 in case of failure */
diff --git a/src/libssh2_priv.h b/src/libssh2_priv.h
index 6c23b3d..0ef6e1a 100644
--- a/src/libssh2_priv.h
+++ b/src/libssh2_priv.h
@@ -149,6 +149,7 @@
  * padding length, payload, padding, and MAC.)."
  */
 #define MAX_SSH_PACKET_LEN 35000
+#define MAX_SHA_DIGEST_LEN SHA256_DIGEST_LENGTH
 
 #define LIBSSH2_ALLOC(session, count) \
   session->alloc((count), &(session)->abstract)
@@ -229,13 +230,13 @@
     time_t start;
 } packet_requirev_state_t;
 
-typedef struct kmdhgGPsha1kex_state_t
+typedef struct kmdhgGPshakex_state_t
 {
     libssh2_nonblocking_states state;
     unsigned char *e_packet;
     unsigned char *s_packet;
     unsigned char *tmp;
-    unsigned char h_sig_comp[SHA_DIGEST_LENGTH];
+    unsigned char h_sig_comp[MAX_SHA_DIGEST_LEN];
     unsigned char c;
     size_t e_packet_len;
     size_t s_packet_len;
@@ -252,16 +253,16 @@
     size_t f_value_len;
     size_t k_value_len;
     size_t h_sig_len;
-    libssh2_sha1_ctx exchange_hash;
+    void *exchange_hash;
     packet_require_state_t req_state;
     libssh2_nonblocking_states burn_state;
-} kmdhgGPsha1kex_state_t;
+} kmdhgGPshakex_state_t;
 
 typedef struct key_exchange_state_low_t
 {
     libssh2_nonblocking_states state;
     packet_require_state_t req_state;
-    kmdhgGPsha1kex_state_t exchange_state;
+    kmdhgGPshakex_state_t exchange_state;
     _libssh2_bn *p;             /* SSH2 defined value (p_value) */
     _libssh2_bn *g;             /* SSH2 defined value (2) */
     unsigned char request[13];
@@ -964,7 +965,7 @@
 #define SSH_MSG_KEXDH_INIT                          30
 #define SSH_MSG_KEXDH_REPLY                         31
 
-/* diffie-hellman-group-exchange-sha1 */
+/* diffie-hellman-group-exchange-sha1 and diffie-hellman-group-exchange-sha256 */
 #define SSH_MSG_KEX_DH_GEX_REQUEST_OLD              30
 #define SSH_MSG_KEX_DH_GEX_REQUEST                  34
 #define SSH_MSG_KEX_DH_GEX_GROUP                    31
diff --git a/src/openssl.c b/src/openssl.c
index d40c778..c3de2d1 100644
--- a/src/openssl.c
+++ b/src/openssl.c
@@ -434,7 +434,7 @@
     return (*key_ctx) ? 0 : -1;
 }
 
- int
+int
 _libssh2_rsa_new_private_frommemory(libssh2_rsa_ctx ** rsa,
                                     LIBSSH2_SESSION * session,
                                     const char *filedata, size_t filedata_len,
@@ -575,7 +575,7 @@
 
 int
 _libssh2_sha1(const unsigned char *message, unsigned long len,
-             unsigned char *out)
+              unsigned char *out)
 {
     EVP_MD_CTX ctx;
 
@@ -589,6 +589,28 @@
 }
 
 int
+_libssh2_sha256_init(libssh2_sha256_ctx *ctx)
+{
+    EVP_MD_CTX_init(ctx);
+    return EVP_DigestInit(ctx, EVP_get_digestbyname("sha256"));
+}
+
+int
+_libssh2_sha256(const unsigned char *message, unsigned long len,
+                unsigned char *out)
+{
+    EVP_MD_CTX ctx;
+
+    EVP_MD_CTX_init(&ctx);
+    if(EVP_DigestInit(&ctx, EVP_get_digestbyname("sha256"))) {
+        EVP_DigestUpdate(&ctx, message, len);
+        EVP_DigestFinal(&ctx, out, NULL);
+        return 0; /* success */
+    }
+    return 1; /* error */
+}
+
+int
 _libssh2_md5_init(libssh2_md5_ctx *ctx)
 {
     EVP_MD_CTX_init(ctx);
diff --git a/src/openssl.h b/src/openssl.h
index 4c3fe89..a2539fa 100644
--- a/src/openssl.h
+++ b/src/openssl.h
@@ -124,6 +124,17 @@
                   unsigned char *out);
 #define libssh2_sha1(x,y,z) _libssh2_sha1(x,y,z)
 
+#define libssh2_sha256_ctx EVP_MD_CTX
+
+/* returns 0 in case of failure */
+int _libssh2_sha256_init(libssh2_sha256_ctx *ctx);
+#define libssh2_sha256_init(x) _libssh2_sha256_init(x)
+#define libssh2_sha256_update(ctx, data, len) EVP_DigestUpdate(&(ctx), data, len)
+#define libssh2_sha256_final(ctx, out) EVP_DigestFinal(&(ctx), out, NULL)
+int _libssh2_sha256(const unsigned char *message, unsigned long len,
+                  unsigned char *out);
+#define libssh2_sha256(x,y,z) _libssh2_sha256(x,y,z)
+
 #define libssh2_md5_ctx EVP_MD_CTX
 
 /* returns 0 in case of failure */
@@ -136,21 +147,26 @@
 #define libssh2_hmac_ctx_init(ctx) \
   HMAC_CTX_init(&ctx)
 #define libssh2_hmac_sha1_init(ctx, key, keylen) \
-  HMAC_Init(ctx, key, keylen, EVP_sha1())
+  HMAC_Init_ex(ctx, key, keylen, EVP_sha1(), NULL)
 #define libssh2_hmac_md5_init(ctx, key, keylen) \
-  HMAC_Init(ctx, key, keylen, EVP_md5())
+  HMAC_Init_ex(ctx, key, keylen, EVP_md5(), NULL)
 #define libssh2_hmac_ripemd160_init(ctx, key, keylen) \
-  HMAC_Init(ctx, key, keylen, EVP_ripemd160())
+  HMAC_Init_ex(ctx, key, keylen, EVP_ripemd160(), NULL)
 #define libssh2_hmac_sha256_init(ctx, key, keylen) \
-  HMAC_Init(ctx, key, keylen, EVP_sha256())
+  HMAC_Init_ex(ctx, key, keylen, EVP_sha256(), NULL)
 #define libssh2_hmac_sha512_init(ctx, key, keylen) \
-  HMAC_Init(ctx, key, keylen, EVP_sha512())
+  HMAC_Init_ex(ctx, key, keylen, EVP_sha512(), NULL)
+
 #define libssh2_hmac_update(ctx, data, datalen) \
   HMAC_Update(&(ctx), data, datalen)
 #define libssh2_hmac_final(ctx, data) HMAC_Final(&(ctx), data, NULL)
 #define libssh2_hmac_cleanup(ctx) HMAC_cleanup(ctx)
 
-#define libssh2_crypto_init() OpenSSL_add_all_algorithms()
+#define libssh2_crypto_init() \
+  OpenSSL_add_all_algorithms(); \
+  ENGINE_load_builtin_engines(); \
+  ENGINE_register_all_complete()
+
 #define libssh2_crypto_exit()
 
 #define libssh2_rsa_ctx RSA