[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH] diffie-hellman-group-exchange-sha256


Hey there!

I also had some interest to get these group-exchange algorithms working,
and found this old patch.

As it was not possible to apply this patch on 0.7.3 or neither on master
branch of libssh-git sources, I've rewritten (and reworked a bit) the patch.

Please find it attached...

And yes I know it is still big, needs to be split up, simplified.. etc...
I did some manual testing using sshd server on with a simple client using
libssh, and the group-exchange algorithms now seems to work.

Best regards,
David

On Mon, Mar 21, 2016 at 7:01 PM, Andreas Schneider <asn@xxxxxxxxxxxxxx>
wrote:

> On Wednesday 25 November 2015 19:02:51 Yanis Kurganov wrote:
> > Here is a fresh patch on 0.7.2
> > Successfully tested in our company.
> > Maybe this will help!
>
> Aris, didn't you work on that?
>
> >
> > 2015-09-16 17:14 GMT+03:00 Yanis Kurganov <yanis.kurganov@xxxxxxxxx>:
> > > Hi, Aris!
> > > I got it.
> > > OK, use my code as you wish!
> > > I'm waiting GEX in a future releases of libssh.
> > > And finally remove my own repository)))
> > >
> > > 2015-07-27 16:47 GMT+03:00 Aris Adamantiadis <aris@xxxxxxxxxxxx>:
> > >> Le 23/01/15 15:40, Yanis Kurganov a écrit :
> > >> > It's a final version with modern SSH_MSG_KEY_DH_GEX_REQUEST.
> > >> > Some clients (for example, Tera Term) use only this message.
> > >> >
> > >> > 2015-01-23 13:52 GMT+03:00 Yanis Kurganov <yanis.kurganov@xxxxxxxxx
> > >> >
> > >> > <mailto:yanis.kurganov@xxxxxxxxx>>:
> > >> >     Andreas, sorry, I missed something for server (for group1-14
> > >> >     algos).
> > >> >     Patch Diffie-Hellman Group Exchange - attempt 2
> > >>
> > >> Hi Yanis,
> > >>
> > >> Sorry it took me so much time, but I'm finally on holidays and have
> more
> > >> time to work :)
> > >> I have carefully reviewed your patch. Unfortunately, I cannot accept
> it
> > >> as-is. Here is my feedback:
> > >> - The patch is too big. Some new functionalities are introduced while
> > >> some core code is refactorized. It makes it hard to figure out what
> > >> really changed, and also harder to debug or git bisect.
> > >> - Some changes are too intrusive and should probably have been
> discussed
> > >> before. For example, the pre-initialization of group1 and group14
> > >> parameters that moved into a runtime operation.
> > >> - I really don't like the way the new packet handlers are implemented.
> > >> It's not your fault, the single function pointers array only works
> > >> correctly when the overlapping packets numbers have similar functions.
> > >> It's not the case with GEX anymore and it forced you to use a dirty
> hack
> > >> for the multiplexing. I'm implementing right now a way to have
> > >> independent callbacks for each key exchange method, so this problem
> goes
> > >> away.
> > >> - The current implementation will blindly accept any group that is
> > >> provided by the server. I want to check on what OpenSSH does but I'm
> > >> certain we should at least check the parameter size and maybe some
> basic
> > >> characteristics (is it a strong prime, is the generator/prime
> congruency
> > >> relation compliant with the RFC).
> > >> - I wasn't really expecting the server-side to serve group1 or group14
> > >> instead of groups in /etc/ssh/moduli. The whole point of
> > >> dh-group-exchange is to use different groups to discourage the use of
> > >> fixed groups like group1 & group14 that are more likely to be cracked
> > >> and have efficient attacks around. Serving these two groups will
> solve a
> > >> technical problem (client xxx won't connect) but at the cost of
> > >> introducing new security problems.
> > >>
> > >> Your code however correctly and neatly implements the packet parsing
> and
> > >> packet sending, together with the corner case of SSH_MSG_GEX_OLD_INIT.
> > >> My proposition now is that I'll work on a better way of decoupling the
> > >> different key exchange methods, and implement GEX by using as much of
> > >> your code as I can. I'll make sure you get your name as the commiter
> so
> > >> you're properly credited for you work.
> > >>
> > >> Best regards,
> > >>
> > >> Aris
>
> --
> Andreas Schneider                   GPG-ID: CC014E3D
> www.cryptomilk.org                asn@xxxxxxxxxxxxxx
>
>
From 71626a62eb7ba4807cedcc12dfad3a08625c81ff Mon Sep 17 00:00:00 2001
From: David Kedves <kedazo@xxxxxxxxxxxxxxxx>
Date: Tue, 31 May 2016 16:22:57 +0200
Subject: [PATCH] Rewritten Diffie-Hellman Group Exchange PATCH

---
 include/libssh/crypto.h           |   8 +-
 include/libssh/dh.h               |  10 +-
 include/libssh/libcrypto.h        |   1 +
 include/libssh/libgcrypt.h        |   1 +
 include/libssh/packet.h           |   3 +
 include/libssh/session.h          |   1 +
 src/client.c                      |  12 +-
 src/dh.c                          | 443 +++++++++++++++++++++++++++++++++-----
 src/kex.c                         |   7 +-
 src/packet.c                      |  14 +-
 src/packet_cb.c                   |  32 ++-
 src/server.c                      | 132 +++++++++++-
 src/session.c                     |   4 +
 src/wrapper.c                     |   2 +
 tests/client/torture_algorithms.c |  46 ++++
 tests/pkd/pkd_hello.c             |  12 +-
 16 files changed, 655 insertions(+), 73 deletions(-)

diff --git a/include/libssh/crypto.h b/include/libssh/crypto.h
index e370c74..a0da183 100644
--- a/include/libssh/crypto.h
+++ b/include/libssh/crypto.h
@@ -53,6 +53,10 @@ enum ssh_key_exchange_e {
   SSH_KEX_DH_GROUP1_SHA1=1,
   /* diffie-hellman-group14-sha1 */
   SSH_KEX_DH_GROUP14_SHA1,
+  /* diffie-hellman-group-exchange-sha1 */
+  SSH_KEX_DH_GROUPEX_SHA1,
+  /* diffie-hellman-group-exchange-sha256 */
+  SSH_KEX_DH_GROUPEX_SHA256,
   /* ecdh-sha2-nistp256 */
   SSH_KEX_ECDH_SHA2_NISTP256,
   /* curve25519-sha256@xxxxxxxxxx */
@@ -74,7 +78,9 @@ enum ssh_cipher_e {
 };
 
 struct ssh_crypto_struct {
-    bignum e,f,x,k,y;
+    bignum p,g,e,f,x,k,y;
+    unsigned int pmin,pbits,pmax; /* preferred size in bits of the group the server will send */
+    int old_gex; /* SSH_MSG_KEX_DH_GEX_REQUEST_OLD used */
 #ifdef HAVE_ECDH
     EC_KEY *ecdh_privkey;
     ssh_string ecdh_client_pubkey;
diff --git a/include/libssh/dh.h b/include/libssh/dh.h
index 8e0d5aa..4b18fec 100644
--- a/include/libssh/dh.h
+++ b/include/libssh/dh.h
@@ -30,17 +30,25 @@ int ssh_dh_generate_f(ssh_session session);
 int ssh_dh_generate_x(ssh_session session);
 int ssh_dh_generate_y(ssh_session session);
 
+int ssh_dh_generate_p_by_pbits(ssh_session session);
+int ssh_dh_generate_p_by_kex_type(ssh_session session);
+int ssh_dh_generate_g(ssh_session session);
+
 int ssh_crypto_init(void);
 void ssh_crypto_finalize(void);
 
 ssh_string ssh_dh_get_e(ssh_session session);
 ssh_string ssh_dh_get_f(ssh_session session);
-int ssh_dh_import_f(ssh_session session,ssh_string f_string);
+int ssh_dh_import_f(ssh_session session, ssh_string f_string);
 int ssh_dh_import_e(ssh_session session, ssh_string e_string);
+int ssh_dh_import_p(ssh_session session, ssh_string p_string);
+int ssh_dh_import_g(ssh_session session, ssh_string g_string);
 void ssh_dh_import_pubkey(ssh_session session,ssh_string pubkey_string);
 int ssh_dh_build_k(ssh_session session);
 int ssh_client_dh_init(ssh_session session);
 int ssh_client_dh_reply(ssh_session session, ssh_buffer packet);
+int ssh_client_dh_gex_init(ssh_session session);
+int ssh_client_dh_gex_reply(ssh_session session, ssh_buffer packet);
 
 int ssh_make_sessionid(ssh_session session);
 /* add data for the final cookie */
diff --git a/include/libssh/libcrypto.h b/include/libssh/libcrypto.h
index 6a08837..1b563d5 100644
--- a/include/libssh/libcrypto.h
+++ b/include/libssh/libcrypto.h
@@ -67,6 +67,7 @@ typedef BIGNUM*  bignum;
 typedef BN_CTX* bignum_CTX;
 
 #define bignum_new() BN_new()
+#define bignum_dup(num) BN_dup(num)
 #define bignum_free(num) BN_clear_free(num)
 #define bignum_set_word(bn,n) BN_set_word(bn,n)
 #define bignum_bin2bn(bn,datalen,data) BN_bin2bn(bn,datalen,data)
diff --git a/include/libssh/libgcrypt.h b/include/libssh/libgcrypt.h
index 7556aca..88f5dd0 100644
--- a/include/libssh/libgcrypt.h
+++ b/include/libssh/libgcrypt.h
@@ -56,6 +56,7 @@ int ssh_gcry_dec2bn(bignum *bn, const char *data);
 char *ssh_gcry_bn2dec(bignum bn);
 
 #define bignum_new() gcry_mpi_new(0)
+#define bignum_dup(num) gcry_mpi_copy(num)
 #define bignum_free(num) gcry_mpi_release(num)
 #define bignum_set_word(bn,n) gcry_mpi_set_ui(bn,n)
 #define bignum_bin2bn(bn,datalen,data) gcry_mpi_scan(data,GCRYMPI_FMT_USG,bn,datalen,NULL)
diff --git a/include/libssh/packet.h b/include/libssh/packet.h
index 3a84eb7..88e5421 100644
--- a/include/libssh/packet.h
+++ b/include/libssh/packet.h
@@ -60,11 +60,14 @@ SSH_PACKET_CALLBACK(ssh_packet_unimplemented);
 SSH_PACKET_CALLBACK(ssh_packet_disconnect_callback);
 SSH_PACKET_CALLBACK(ssh_packet_ignore_callback);
 SSH_PACKET_CALLBACK(ssh_packet_dh_reply);
+SSH_PACKET_CALLBACK(ssh_packet_dh_gex_reply);
 SSH_PACKET_CALLBACK(ssh_packet_newkeys);
 SSH_PACKET_CALLBACK(ssh_packet_service_accept);
 
 #ifdef WITH_SERVER
 SSH_PACKET_CALLBACK(ssh_packet_kexdh_init);
+SSH_PACKET_CALLBACK(ssh_packet_kexdh_gex_init);
+SSH_PACKET_CALLBACK(ssh_packet_kexdh_gex_request);
 #endif
 
 int ssh_packet_send_unimplemented(ssh_session session, uint32_t seqnum);
diff --git a/include/libssh/session.h b/include/libssh/session.h
index 60d7857..44c19a9 100644
--- a/include/libssh/session.h
+++ b/include/libssh/session.h
@@ -45,6 +45,7 @@ enum ssh_session_state_e {
 
 enum ssh_dh_state_e {
   DH_STATE_INIT=0,
+  DH_STATE_GEX_REQUEST_SENT,
   DH_STATE_INIT_SENT,
   DH_STATE_NEWKEYS_SENT,
   DH_STATE_FINISHED
diff --git a/src/client.c b/src/client.c
index 3b120bb..b0c72fb 100644
--- a/src/client.c
+++ b/src/client.c
@@ -250,6 +250,7 @@ end:
 static int dh_handshake(ssh_session session) {
 
   int rc = SSH_AGAIN;
+  enum ssh_dh_state_e next_state = DH_STATE_INIT_SENT;
 
   switch (session->dh_handshake_state) {
     case DH_STATE_INIT:
@@ -258,6 +259,11 @@ static int dh_handshake(ssh_session session) {
         case SSH_KEX_DH_GROUP14_SHA1:
           rc = ssh_client_dh_init(session);
           break;
+        case SSH_KEX_DH_GROUPEX_SHA1:
+        case SSH_KEX_DH_GROUPEX_SHA256:
+          next_state = DH_STATE_GEX_REQUEST_SENT;
+          rc = ssh_client_dh_gex_init(session);
+          break;
 #ifdef HAVE_ECDH
         case SSH_KEX_ECDH_SHA2_NISTP256:
           rc = ssh_client_ecdh_init(session);
@@ -276,10 +282,14 @@ static int dh_handshake(ssh_session session) {
           return SSH_ERROR;
       }
 
-      session->dh_handshake_state = DH_STATE_INIT_SENT;
+      session->dh_handshake_state = next_state;
+      break;
     case DH_STATE_INIT_SENT:
     	/* wait until ssh_packet_dh_reply is called */
     	break;
+    case DH_STATE_GEX_REQUEST_SENT:
+    	/* wait until ssh_packet_dh_reply is called */
+    	break;
     case DH_STATE_NEWKEYS_SENT:
     	/* wait until ssh_packet_newkeys is called */
     	break;
diff --git a/src/dh.c b/src/dh.c
index c54bb9f..1da45b1 100644
--- a/src/dh.c
+++ b/src/dh.c
@@ -6,6 +6,7 @@
  * Copyright (c) 2003-2013 by Aris Adamantiadis
  * Copyright (c) 2009-2013 by Andreas Schneider <asn@xxxxxxxxxxxxxx>
  * Copyright (c) 2012      by Dmitriy Kuznetsov <dk@xxxxxxxxx>
+ * Copyright (c) 2014-2015 by Yanis Kurganov <yanis.kurganov@xxxxxxxxx>
  *
  * The SSH Library is free software; you can redistribute it and/or modify
  * it under the terms of the GNU Lesser General Public License as published by
@@ -61,6 +62,7 @@
 #include "libssh/ssh2.h"
 #include "libssh/pki.h"
 #include "libssh/bignum.h"
+#include "libssh/kex.h"
 
 /* todo: remove it */
 #include "libssh/string.h"
@@ -111,16 +113,59 @@ static unsigned char p_group14_value[] = {
 
 #define P_GROUP14_LEN 256 /* Size in bytes of the p number for group 14 */
 
+static unsigned char p_groupex_value[] = {
+		0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2,
+		0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+		0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6,
+		0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+		0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D,
+		0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+		0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9,
+		0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+		0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11,
+		0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+		0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36,
+		0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+		0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56,
+		0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+		0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08,
+		0xCA, 0x18, 0x21, 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
+		0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, 0x9B, 0x27, 0x83, 0xA2,
+		0xEC, 0x07, 0xA2, 0x8F, 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
+		0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, 0x39, 0x95, 0x49, 0x7C,
+		0xEA, 0x95, 0x6A, 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
+		0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D, 0xAD, 0x33, 0x17, 0x0D,
+		0x04, 0x50, 0x7A, 0x33, 0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64,
+		0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A, 0x8A, 0xEA, 0x71, 0x57,
+		0x5D, 0x06, 0x0C, 0x7D, 0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7,
+		0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7, 0x1E, 0x8C, 0x94, 0xE0,
+		0x4A, 0x25, 0x61, 0x9D, 0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B,
+		0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64, 0xD8, 0x76, 0x02, 0x73,
+		0x3E, 0xC8, 0x6A, 0x64, 0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C,
+		0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C, 0x77, 0x09, 0x88, 0xC0,
+		0xBA, 0xD9, 0x46, 0xE2, 0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31,
+		0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E, 0x4B, 0x82, 0xD1, 0x20,
+		0xA9, 0x21, 0x08, 0x01, 0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, 0xD7,
+		0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, 0x26, 0x99, 0xC3, 0x27, 0x18,
+		0x6A, 0xF4, 0xE2, 0x3C, 0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, 0xDA,
+		0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, 0xE8, 0xDB, 0xBB, 0xC2, 0xDB,
+		0x04, 0xDE, 0x8E, 0xF9, 0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, 0xA6,
+		0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, 0x5D, 0x99, 0xB2, 0x96, 0x4F,
+		0xA0, 0x90, 0xC3, 0xA2, 0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, 0xED,
+		0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, 0xAF, 0xB8, 0x1B, 0xDD, 0x76,
+		0x21, 0x70, 0x48, 0x1C, 0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, 0xA9,
+		0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, 0xC1, 0x86, 0xFF, 0xB7, 0xDC,
+		0x90, 0xA6, 0xC0, 0x8F, 0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x06, 0x31, 0x99,
+		0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+
+#define P_GROUPEX_LEN 512 /* Size in bytes of the p number for group-exchange */
+
 static unsigned long g_int = 2 ;	/* G is defined as 2 by the ssh2 standards */
-static bignum g;
 static bignum p_group1;
 static bignum p_group14;
+static bignum p_groupex;
 static int ssh_crypto_initialized;
 
-static bignum select_p(enum ssh_key_exchange_e type) {
-    return type == SSH_KEX_DH_GROUP14_SHA1 ? p_group14 : p_group1;
-}
-
 int ssh_get_random(void *where, int len, int strong){
 
 #ifdef HAVE_LIBGCRYPT
@@ -144,7 +189,7 @@ int ssh_get_random(void *where, int len, int strong){
 
 
 /*
- * This inits the values g and p which are used for DH key agreement
+ * This inits the values p group values which are used for DH key agreement
  * FIXME: Make the function thread safe by adding a semaphore or mutex.
  */
 int ssh_crypto_init(void) {
@@ -157,47 +202,51 @@ int ssh_crypto_init(void) {
     }
 #endif
 
-    g = bignum_new();
-    if (g == NULL) {
-      return -1;
-    }
-    bignum_set_word(g,g_int);
-
 #ifdef HAVE_LIBGCRYPT
     bignum_bin2bn(p_group1_value, P_GROUP1_LEN, &p_group1);
     if (p_group1 == NULL) {
-      bignum_free(g);
-      g = NULL;
       return -1;
     }
     bignum_bin2bn(p_group14_value, P_GROUP14_LEN, &p_group14);
     if (p_group14 == NULL) {
-      bignum_free(g);
       bignum_free(p_group1);
-      g = NULL;
       p_group1 = NULL;
       return -1;
     }
+    bignum_bin2bn(p_groupex_value, P_GROUPEX_LEN, &p_groupex);
+    if (p_group == NULL) {
+      bignum_free(p_group1);
+      bignum_free(p_group14);
+      p_group1 = NULL;
+      p_group14 = NULL;
+      return -1;
+    }
 
 #elif defined HAVE_LIBCRYPTO
     p_group1 = bignum_new();
     if (p_group1 == NULL) {
-      bignum_free(g);
-      g = NULL;
       return -1;
     }
     bignum_bin2bn(p_group1_value, P_GROUP1_LEN, p_group1);
 
     p_group14 = bignum_new();
     if (p_group14 == NULL) {
-      bignum_free(g);
       bignum_free(p_group1);
-      g = NULL;
       p_group1 = NULL;
       return -1;
     }
     bignum_bin2bn(p_group14_value, P_GROUP14_LEN, p_group14);
 
+    p_groupex = bignum_new();
+    if (p_groupex == NULL) {
+      bignum_free(p_group1);
+      bignum_free(p_group14);
+      p_group1 = NULL;
+      p_group14 = NULL;
+      return -1;
+    }
+    bignum_bin2bn(p_groupex_value, P_GROUPEX_LEN, p_groupex);
+
     OpenSSL_add_all_algorithms();
 
 #endif
@@ -210,12 +259,12 @@ int ssh_crypto_init(void) {
 
 void ssh_crypto_finalize(void) {
   if (ssh_crypto_initialized) {
-    bignum_free(g);
-    g = NULL;
     bignum_free(p_group1);
     p_group1 = NULL;
     bignum_free(p_group14);
     p_group14 = NULL;
+    bignum_free(p_groupex);
+    p_groupex = NULL;
 #ifdef HAVE_LIBGCRYPT
     gcry_control(GCRYCTL_TERM_SECMEM);
 #elif defined HAVE_LIBCRYPTO
@@ -230,6 +279,9 @@ int ssh_dh_generate_x(ssh_session session) {
   int keysize;
   if (session->next_crypto->kex_type == SSH_KEX_DH_GROUP1_SHA1) {
     keysize = 1023;
+  } else if (session->next_crypto->kex_type == SSH_KEX_DH_GROUPEX_SHA1 ||
+	session->next_crypto->kex_type == SSH_KEX_DH_GROUPEX_SHA256) {
+        keysize = session->next_crypto->pbits / 2 - 1;
   } else {
     keysize = 2047;
   }
@@ -257,6 +309,9 @@ int ssh_dh_generate_y(ssh_session session) {
   int keysize;
   if (session->next_crypto->kex_type == SSH_KEX_DH_GROUP1_SHA1) {
     keysize = 1023;
+  } else if (session->next_crypto->kex_type == SSH_KEX_DH_GROUPEX_SHA1 ||
+		session->next_crypto->kex_type == SSH_KEX_DH_GROUPEX_SHA256) {
+        keysize = session->next_crypto->pbits / 2 - 1;
   } else {
     keysize = 2047;
   }
@@ -297,11 +352,11 @@ int ssh_dh_generate_e(ssh_session session) {
   }
 
 #ifdef HAVE_LIBGCRYPT
-  bignum_mod_exp(session->next_crypto->e, g, session->next_crypto->x,
-      select_p(session->next_crypto->kex_type));
+  bignum_mod_exp(session->next_crypto->e, session->next_crypto->g,
+      session->next_crypto->x, session->next_crypto->p);
 #elif defined HAVE_LIBCRYPTO
-  bignum_mod_exp(session->next_crypto->e, g, session->next_crypto->x,
-      select_p(session->next_crypto->kex_type), ctx);
+  bignum_mod_exp(session->next_crypto->e, session->next_crypto->g,
+      session->next_crypto->x, session->next_crypto->p, ctx);
 #endif
 
 #ifdef DEBUG_CRYPTO
@@ -332,11 +387,11 @@ int ssh_dh_generate_f(ssh_session session) {
   }
 
 #ifdef HAVE_LIBGCRYPT
-  bignum_mod_exp(session->next_crypto->f, g, session->next_crypto->y,
-      select_p(session->next_crypto->kex_type));
+  bignum_mod_exp(session->next_crypto->f, session->next_crypto->g,
+      session->next_crypto->y, session->next_crypto->p);
 #elif defined HAVE_LIBCRYPTO
-  bignum_mod_exp(session->next_crypto->f, g, session->next_crypto->y,
-      select_p(session->next_crypto->kex_type), ctx);
+  bignum_mod_exp(session->next_crypto->f, session->next_crypto->g,
+      session->next_crypto->y, session->next_crypto->p, ctx);
 #endif
 
 #ifdef DEBUG_CRYPTO
@@ -390,6 +445,103 @@ int ssh_dh_import_e(ssh_session session, ssh_string e_string) {
   return 0;
 }
 
+/* p number */
+int ssh_dh_import_p(ssh_session session, ssh_string p_string) {
+  session->next_crypto->p = ssh_make_string_bn(p_string);
+  if (session->next_crypto->p == NULL) {
+    return -1;
+  }
+
+  /* use the bits defined by server */
+  session->next_crypto->pbits = bignum_num_bits(session->next_crypto->p);
+  SSH_LOG(SSH_LOG_FUNCTIONS,
+	 "Received p from server, %dbits",
+         session->next_crypto->pbits);
+
+#ifdef DEBUG_CRYPTO
+    ssh_print_bignum("p",session->next_crypto->p);
+#endif
+
+  return 0;
+}
+
+int ssh_dh_generate_p_by_pbits(ssh_session session)
+{
+  unsigned int pbytes;
+
+  pbytes = session->next_crypto->pbits / 8;
+  if (pbytes >= P_GROUPEX_LEN)
+    session->next_crypto->p = bignum_dup(p_groupex);
+  else if (pbytes >= P_GROUP14_LEN)
+    session->next_crypto->p = bignum_dup(p_group14);
+  else // if (bytes == P_GROUP1_LEN)
+    session->next_crypto->p = bignum_dup(p_group1);
+
+  if (session->next_crypto->p == NULL) {
+    return SSH_ERROR;
+  }
+
+#ifdef DEBUG_CRYPTO
+  ssh_print_bignum("p",session->next_crypto->p);
+#endif
+
+  return SSH_OK;
+}
+
+int ssh_dh_generate_p_by_kex_type(ssh_session session)
+{
+  switch (session->next_crypto->kex_type)
+  {
+    case SSH_KEX_DH_GROUPEX_SHA1:
+    case SSH_KEX_DH_GROUPEX_SHA256:
+      session->next_crypto->p = bignum_dup(p_groupex);
+      break;
+    case SSH_KEX_DH_GROUP14_SHA1:
+      session->next_crypto->p = bignum_dup(p_group14);
+      break;
+    default:
+      session->next_crypto->p = bignum_dup(p_group1);
+    break;
+  }
+
+  if (session->next_crypto->p == NULL) {
+    return SSH_ERROR;
+  }
+
+#ifdef DEBUG_CRYPTO
+  ssh_print_bignum("p",session->next_crypto->p);
+#endif
+
+  return SSH_OK;
+}
+
+/* g number */
+int ssh_dh_import_g(ssh_session session, ssh_string g_string) {
+  session->next_crypto->g = ssh_make_string_bn(g_string);
+  if (session->next_crypto->g == NULL) {
+    return -1;
+  }
+
+#ifdef DEBUG_CRYPTO
+    ssh_print_bignum("g",session->next_crypto->g);
+#endif
+
+  return 0;
+}
+
+int ssh_dh_generate_g(ssh_session session)
+{
+  session->next_crypto->g = bignum_new();
+  if (session->next_crypto->g == NULL) {
+    return SSH_ERROR;
+  }
+  bignum_set_word(session->next_crypto->g, g_int);
+#ifdef DEBUG_CRYPTO
+  ssh_print_bignum("g",session->next_crypto->g);
+#endif
+  return SSH_OK;
+}
+
 int ssh_dh_build_k(ssh_session session) {
 #ifdef HAVE_LIBCRYPTO
   bignum_CTX ctx = bignum_ctx_new();
@@ -410,18 +562,18 @@ int ssh_dh_build_k(ssh_session session) {
 #ifdef HAVE_LIBGCRYPT
   if(session->client) {
     bignum_mod_exp(session->next_crypto->k, session->next_crypto->f,
-        session->next_crypto->x, select_p(session->next_crypto->kex_type));
+        session->next_crypto->x, session->next_crypto->p);
   } else {
     bignum_mod_exp(session->next_crypto->k, session->next_crypto->e,
-        session->next_crypto->y, select_p(session->next_crypto->kex_type));
+        session->next_crypto->y, session->next_crypto->p);
   }
 #elif defined HAVE_LIBCRYPTO
   if (session->client) {
     bignum_mod_exp(session->next_crypto->k, session->next_crypto->f,
-        session->next_crypto->x, select_p(session->next_crypto->kex_type), ctx);
+        session->next_crypto->x, session->next_crypto->p, ctx);
   } else {
     bignum_mod_exp(session->next_crypto->k, session->next_crypto->e,
-        session->next_crypto->y, select_p(session->next_crypto->kex_type), ctx);
+        session->next_crypto->y, session->next_crypto->p, ctx);
   }
 #endif
 
@@ -440,43 +592,193 @@ int ssh_dh_build_k(ssh_session session) {
   return 0;
 }
 
+/* returns 1 if a cipher is found in the list */
+static int ssh_check_cipher(const char *cipherlist, const char *name){
+    char *ptr;
+    ptr=ssh_find_matching(cipherlist,name);
+    if(ptr){
+        free(ptr);
+        return 1;
+    }   
+    return 0;
+}
+
+static int ssh_init_pbits(ssh_session session)
+{
+    struct ssh_kex_struct* kex = session->server ?
+        &session->next_crypto->server_kex :
+        &session->next_crypto->client_kex;
+    struct ssh_cipher_struct* ciphertab = ssh_get_ciphertab();
+    struct ssh_cipher_struct* cs_cipher = NULL;
+    struct ssh_cipher_struct* sc_cipher = NULL;
+    unsigned int nbits, hlen = session->next_crypto->kex_type ==
+        SSH_KEX_DH_GROUPEX_SHA256 ? SHA256_DIGEST_LENGTH : SHA_DIGEST_LENGTH;
+    size_t i;
+
+    /* FIXME: this algorithm negotiation must not be there... */
+    for (i = 0; ciphertab[i].name; ++i) {
+        if (cs_cipher == NULL) {
+            if (ssh_check_cipher(kex->methods[SSH_CRYPT_C_S], ciphertab[i].name)) {
+                cs_cipher = &ciphertab[i];
+            }
+        }
+        if (sc_cipher == NULL) {
+            if (ssh_check_cipher(kex->methods[SSH_CRYPT_S_C], ciphertab[i].name)) {
+                sc_cipher = &ciphertab[i];
+            }
+        }
+        if (cs_cipher && sc_cipher)
+           break;
+    }
+    if (cs_cipher == NULL) {
+        ssh_set_error(session, SSH_FATAL,
+            "Couldn't agree a client-to-server cipher (available: %s)",
+            kex->methods[SSH_CRYPT_C_S]);
+        return SSH_ERROR;
+    }
+    if (sc_cipher == NULL) {
+        ssh_set_error(session, SSH_FATAL,
+            "Couldn't agree a server-to-client cipher (available: %s)",
+            kex->methods[SSH_CRYPT_S_C]);
+        return SSH_ERROR;
+    }
+
+    /* Start with the maximum key length of either cipher */
+    nbits = cs_cipher->keysize > sc_cipher->keysize ?
+            cs_cipher->keysize : sc_cipher->keysize;
+
+    /* The keys are based on a hash. So cap the key size at hlen bits */
+    if (nbits > hlen * 8) {
+        nbits = hlen * 8;
+    }
+
+    /* min-max values */
+    session->next_crypto->pmin = 2048;
+    session->next_crypto->pmax = 8192;
+
+    /* DH group size (values from openssh/dh.c) */
+    if (nbits <= 112)
+        session->next_crypto->pbits = 2048;
+    else if (nbits <= 128)
+        session->next_crypto->pbits = 3072;
+    else if (nbits <= 192)
+        session->next_crypto->pbits = 7680;
+    else
+        session->next_crypto->pbits = 8192;
+
+    session->next_crypto->old_gex = 0;
+    return SSH_OK;
+}
+
 /** @internal
- * @brief Starts diffie-hellman-group1 key exchange
+ * @brief Starts diffie-hellman key exchange
  */
-int ssh_client_dh_init(ssh_session session){
-  ssh_string e = NULL;
+static int ssh_client_dh_init_by_type(ssh_session session, int type)
+{
   int rc;
-
-  if (ssh_dh_generate_x(session) < 0) {
-    goto error;
+  rc = ssh_dh_generate_x(session);
+  if (rc < 0) {
+    return SSH_ERROR;
   }
-  if (ssh_dh_generate_e(session) < 0) {
-    goto error;
+  rc = ssh_dh_generate_e(session);
+  if (rc < 0) {
+    return SSH_ERROR;
+  }
+  rc = ssh_buffer_pack(session->out_buffer, "bB",
+                       type, session->next_crypto->e);
+  if (rc != SSH_OK) {
+    return SSH_ERROR;
   }
+  rc = ssh_packet_send(session);
+  SSH_LOG(SSH_LOG_PROTOCOL, "%s sent.",
+	type == SSH2_MSG_KEX_DH_GEX_INIT ?
+	"SSH2_MSG_KEX_DH_GEX_INIT" : 
+        "SSH2_MSG_KEXDH_INIT");
+  return rc;
+}
 
-  e = ssh_dh_get_e(session);
-  if (e == NULL) {
-    goto error;
+int ssh_client_dh_init(ssh_session session)
+{
+  int rc;
+  rc = ssh_dh_generate_p_by_kex_type(session);
+  if (rc < 0) {
+	ssh_set_error(session, SSH_FATAL, "Cannot create p number");
+	return SSH_ERROR;
   }
+  rc = ssh_dh_generate_g(session);
+  if (rc < 0) {
+	ssh_set_error(session, SSH_FATAL, "Cannot create g number");
+	return SSH_ERROR;
+  }
+  return ssh_client_dh_init_by_type(session, SSH2_MSG_KEXDH_INIT);
+}
 
-  rc = ssh_buffer_pack(session->out_buffer, "bS", SSH2_MSG_KEXDH_INIT, e);
-  if (rc != SSH_OK) {
-    goto error;
+int ssh_client_dh_gex_init(ssh_session session)
+{
+  int rc;
+  rc = ssh_init_pbits(session);
+  if (rc < 0) {
+	ssh_set_error(session, SSH_FATAL, "Cannot init pbits");
+	return SSH_ERROR;
   }
 
-  ssh_string_burn(e);
-  ssh_string_free(e);
-  e=NULL;
+  #if 0
+  rc = ssh_buffer_pack(session->out_buffer, "bd",
+                       SSH2_MSG_KEX_DH_GEX_REQUEST_OLD,
+                       session->next_crypto->pbits);
+  #endif
 
+  rc = ssh_buffer_pack(session->out_buffer, "bddd",
+                       SSH2_MSG_KEX_DH_GEX_REQUEST,
+                       session->next_crypto->pmin,
+                       session->next_crypto->pbits,
+                       session->next_crypto->pmax);
+
+  if (rc != SSH_OK) {
+
+	return SSH_ERROR;
+  }
   rc = ssh_packet_send(session);
+  SSH_LOG(SSH_LOG_PROTOCOL,
+          "SSH2_MSG_KEX_DH_GEX_REQUEST(%d<%d<%d) sent",
+          session->next_crypto->pmin,
+          session->next_crypto->pbits,
+          session->next_crypto->pmax);
   return rc;
-  error:
-  if(e != NULL){
-    ssh_string_burn(e);
-    ssh_string_free(e);
-  }
+}
 
-  return SSH_ERROR;
+/* this method is called when server sent SSH_MSG_KEX_DH_GEX_GROUP */
+int ssh_client_dh_gex_reply(ssh_session session, ssh_buffer packet)
+{
+  ssh_string num;
+  int rc;
+  num = ssh_buffer_get_ssh_string(packet);
+  if (num == NULL) {
+	ssh_set_error(session,SSH_FATAL, "No p number in packet");
+	return SSH_ERROR;
+  }
+  rc = ssh_dh_import_p(session, num);
+  ssh_string_burn(num);
+  ssh_string_free(num);
+  if (rc < 0) {
+	ssh_set_error(session, SSH_FATAL, "Cannot import p number");
+	return SSH_ERROR;
+  }
+  num = ssh_buffer_get_ssh_string(packet);
+  if (num == NULL) {
+	ssh_set_error(session,SSH_FATAL, "No g number in packet");
+	return SSH_ERROR;
+  }
+  rc = ssh_dh_import_g(session, num);
+  ssh_string_burn(num);
+  ssh_string_free(num);
+  if (rc < 0) {
+	ssh_set_error(session, SSH_FATAL, "Cannot import g number");
+	return SSH_ERROR;
+  }
+  SSH_LOG(SSH_LOG_PROTOCOL,
+	  "SSH_MSG_KEX_DH_GEX_GROUP received (p,g)");
+  return ssh_client_dh_init_by_type(session, SSH2_MSG_KEX_DH_GEX_INIT);
 }
 
 int ssh_client_dh_reply(ssh_session session, ssh_buffer packet){
@@ -607,6 +909,31 @@ int ssh_make_sessionid(ssh_session session) {
             goto error;
         }
 
+    } else if (session->next_crypto->kex_type == SSH_KEX_DH_GROUPEX_SHA1 ||
+            session->next_crypto->kex_type == SSH_KEX_DH_GROUPEX_SHA256) {
+        if (session->next_crypto->old_gex) {
+            rc = ssh_buffer_pack(buf,
+                                "dBBBB",
+                                session->next_crypto->pbits,
+                                session->next_crypto->p,
+                                session->next_crypto->g,
+                                session->next_crypto->e,
+                                session->next_crypto->f);
+        } else {
+            rc = ssh_buffer_pack(buf,
+                                "dddBBBB",
+                                session->next_crypto->pmin,
+                                session->next_crypto->pbits,
+                                session->next_crypto->pmax,
+                                session->next_crypto->p,
+                                session->next_crypto->g,
+                                session->next_crypto->e,
+                                session->next_crypto->f);
+        }
+        if (rc != SSH_OK) {
+            goto error;
+        }
+
 #ifdef HAVE_ECDH
     } else if (session->next_crypto->kex_type == SSH_KEX_ECDH_SHA2_NISTP256) {
         if (session->next_crypto->ecdh_client_pubkey == NULL ||
@@ -648,6 +975,7 @@ int ssh_make_sessionid(ssh_session session) {
     switch (session->next_crypto->kex_type) {
     case SSH_KEX_DH_GROUP1_SHA1:
     case SSH_KEX_DH_GROUP14_SHA1:
+    case SSH_KEX_DH_GROUPEX_SHA1:
         session->next_crypto->digest_len = SHA_DIGEST_LENGTH;
         session->next_crypto->mac_type = SSH_MAC_SHA1;
         session->next_crypto->secret_hash = malloc(session->next_crypto->digest_len);
@@ -660,6 +988,7 @@ int ssh_make_sessionid(ssh_session session) {
         break;
     case SSH_KEX_ECDH_SHA2_NISTP256:
     case SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG:
+    case SSH_KEX_DH_GROUPEX_SHA256:
         session->next_crypto->digest_len = SHA256_DIGEST_LENGTH;
         session->next_crypto->mac_type = SSH_MAC_SHA256;
         session->next_crypto->secret_hash = malloc(session->next_crypto->digest_len);
diff --git a/src/kex.c b/src/kex.c
index f34728c..3bbdb38 100644
--- a/src/kex.c
+++ b/src/kex.c
@@ -85,7 +85,7 @@
 #define ECDH ""
 #endif
 
-#define KEY_EXCHANGE CURVE25519 ECDH "diffie-hellman-group14-sha1,diffie-hellman-group1-sha1"
+#define KEY_EXCHANGE CURVE25519 ECDH "diffie-hellman-group-exchange-sha256,diffie-hellman-group-exchange-sha1,diffie-hellman-group14-sha1,diffie-hellman-group1-sha1"
 #define KEX_METHODS_SIZE 10
 
 /* NOTE: This is a fixed API and the index is defined by ssh_kex_types_e */
@@ -583,10 +583,15 @@ int ssh_kex_select_methods (ssh_session session){
             session->next_crypto->kex_methods[i] = strdup("");
         }
     }
+
     if(strcmp(session->next_crypto->kex_methods[SSH_KEX], "diffie-hellman-group1-sha1") == 0){
       session->next_crypto->kex_type=SSH_KEX_DH_GROUP1_SHA1;
     } else if(strcmp(session->next_crypto->kex_methods[SSH_KEX], "diffie-hellman-group14-sha1") == 0){
       session->next_crypto->kex_type=SSH_KEX_DH_GROUP14_SHA1;
+    } else if(strcmp(session->next_crypto->kex_methods[SSH_KEX], "diffie-hellman-group-exchange-sha1") == 0){
+      session->next_crypto->kex_type=SSH_KEX_DH_GROUPEX_SHA1;
+    } else if(strcmp(session->next_crypto->kex_methods[SSH_KEX], "diffie-hellman-group-exchange-sha256") == 0){
+      session->next_crypto->kex_type=SSH_KEX_DH_GROUPEX_SHA256;
     } else if(strcmp(session->next_crypto->kex_methods[SSH_KEX], "ecdh-sha2-nistp256") == 0){
       session->next_crypto->kex_type=SSH_KEX_ECDH_SHA2_NISTP256;
     } else if(strcmp(session->next_crypto->kex_methods[SSH_KEX], "curve25519-sha256@xxxxxxxxxx") == 0){
diff --git a/src/packet.c b/src/packet.c
index 33943ae..f5a37e1 100644
--- a/src/packet.c
+++ b/src/packet.c
@@ -73,9 +73,17 @@ static ssh_packet_callback default_packet_handlers[]= {
 #endif
   ssh_packet_dh_reply,                     // SSH2_MSG_KEXDH_REPLY                31
                                            // SSH2_MSG_KEX_DH_GEX_GROUP           31
-  NULL,                                    // SSH2_MSG_KEX_DH_GEX_INIT            32
-  NULL,                                    // SSH2_MSG_KEX_DH_GEX_REPLY           33
-  NULL,                                    // SSH2_MSG_KEX_DH_GEX_REQUEST         34
+#if WITH_SERVER
+  ssh_packet_kexdh_gex_init,               // SSH2_MSG_KEX_DH_GEX_INIT            32
+#else
+  NULL,
+#endif
+  ssh_packet_dh_gex_reply,                 // SSH2_MSG_KEX_DH_GEX_REPLY           33
+#if WITH_SERVER
+  ssh_packet_kexdh_gex_request,            // SSH2_MSG_KEX_DH_GEX_REQUEST         34
+#else
+  NULL,
+#endif
   NULL, NULL, NULL, NULL, NULL, NULL,	NULL,
   NULL, NULL, NULL, NULL, NULL, NULL, NULL,
   NULL,                                    //                                     35-49
diff --git a/src/packet_cb.c b/src/packet_cb.c
index 472e8a7..a957881 100644
--- a/src/packet_cb.c
+++ b/src/packet_cb.c
@@ -94,11 +94,13 @@ SSH_PACKET_CALLBACK(ssh_packet_ignore_callback){
 
 SSH_PACKET_CALLBACK(ssh_packet_dh_reply){
   int rc;
+  enum ssh_dh_state_e next_state = DH_STATE_NEWKEYS_SENT;
   (void)type;
   (void)user;
   SSH_LOG(SSH_LOG_PROTOCOL,"Received SSH_KEXDH_REPLY");
   if (session->session_state != SSH_SESSION_STATE_DH ||
-		session->dh_handshake_state != DH_STATE_INIT_SENT){
+		(session->dh_handshake_state != DH_STATE_GEX_REQUEST_SENT &&
+        session->dh_handshake_state != DH_STATE_INIT_SENT)){
 	ssh_set_error(session,SSH_FATAL,"ssh_packet_dh_reply called in wrong state : %d:%d",
 			session->session_state,session->dh_handshake_state);
 	goto error;
@@ -108,6 +110,11 @@ SSH_PACKET_CALLBACK(ssh_packet_dh_reply){
     case SSH_KEX_DH_GROUP14_SHA1:
       rc=ssh_client_dh_reply(session, packet);
       break;
+    case SSH_KEX_DH_GROUPEX_SHA1:
+    case SSH_KEX_DH_GROUPEX_SHA256:
+      rc=ssh_client_dh_gex_reply(session, packet);
+      next_state = DH_STATE_INIT_SENT;
+      break;
 #ifdef HAVE_ECDH
     case SSH_KEX_ECDH_SHA2_NISTP256:
       rc = ssh_client_ecdh_reply(session, packet);
@@ -123,7 +130,7 @@ SSH_PACKET_CALLBACK(ssh_packet_dh_reply){
       goto error;
   }
   if(rc==SSH_OK) {
-    session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
+    session->dh_handshake_state = next_state;
     return SSH_PACKET_USED;
   }
 error:
@@ -131,6 +138,27 @@ error:
   return SSH_PACKET_USED;
 }
 
+SSH_PACKET_CALLBACK(ssh_packet_dh_gex_reply){
+  int rc;
+  (void)type;
+  (void)user;
+  SSH_LOG(SSH_LOG_PROTOCOL,"Received SSH2_MSG_KEX_DH_GEX_REPLY");
+  if (session->session_state!= SSH_SESSION_STATE_DH ||
+    session->dh_handshake_state != DH_STATE_INIT_SENT) {
+    ssh_set_error(session,SSH_FATAL,"ssh_packet_dh_gex_reply called in wrong state : %d:%d",
+    session->session_state,session->dh_handshake_state);
+   goto error;
+  }
+  rc = ssh_client_dh_reply(session, packet);
+  if (rc == SSH_OK) {
+    session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
+    return SSH_PACKET_USED;
+  }
+error:
+  session->session_state = SSH_SESSION_STATE_ERROR;
+  return SSH_PACKET_USED;
+}
+
 SSH_PACKET_CALLBACK(ssh_packet_newkeys){
   ssh_string sig_blob = NULL;
   int rc;
diff --git a/src/server.c b/src/server.c
index 017a4cc..6e5796b 100644
--- a/src/server.c
+++ b/src/server.c
@@ -65,7 +65,7 @@
             session->common.callbacks->connect_status_function(session->common.callbacks->userdata, status); \
     } while (0)
 
-static int dh_handshake_server(ssh_session session);
+static int ssh_dh_handshake_server(ssh_session session, int reply_type);
 
 
 /**
@@ -153,7 +153,7 @@ static int server_set_kex(ssh_session session) {
  * @brief parse an incoming SSH_MSG_KEXDH_INIT packet and complete
  *        key exchange
  **/
-static int ssh_server_kexdh_init(ssh_session session, ssh_buffer packet){
+static int ssh_server_kexdh_init(ssh_session session, ssh_buffer packet, int reply_type){
     ssh_string e;
     e = ssh_buffer_get_ssh_string(packet);
     if (e == NULL) {
@@ -165,12 +165,93 @@ static int ssh_server_kexdh_init(ssh_session session, ssh_buffer packet){
       session->session_state=SSH_SESSION_STATE_ERROR;
     } else {
       session->dh_handshake_state=DH_STATE_INIT_SENT;
-      dh_handshake_server(session);
+      ssh_dh_handshake_server(session, reply_type);
     }
     ssh_string_free(e);
     return SSH_OK;
 }
 
+static int ssh_server_kexdh_group_init(ssh_session session, ssh_buffer packet)
+{
+    int rc;
+    rc = ssh_dh_generate_p_by_kex_type(session);
+    if (rc < 0) {
+        ssh_set_error(session, SSH_FATAL, "Could not create p number");
+        return SSH_ERROR;
+    }
+    rc = ssh_dh_generate_g(session);
+    if (rc < 0) {
+        ssh_set_error(session, SSH_FATAL, "Could not create g number");
+        return SSH_ERROR;
+    }
+    return ssh_server_kexdh_init(session, packet, SSH2_MSG_KEXDH_REPLY);
+}
+
+static int ssh_server_kexdh_gex_send_group(ssh_session session)
+{
+    int rc;
+    SSH_LOG(SSH_LOG_PROTOCOL,
+        "Preferred size of the group in a client request: %u bits",
+        session->next_crypto->pbits);
+    rc = ssh_dh_generate_p_by_pbits(session);
+    if (rc < 0) {
+        ssh_set_error(session, SSH_FATAL, "Could not create p number");
+        return SSH_ERROR;
+    }
+    rc = ssh_dh_generate_g(session);
+    if (rc < 0) {
+        ssh_set_error(session, SSH_FATAL, "Could not create g number");
+        return SSH_ERROR;
+    }
+    rc = ssh_buffer_pack(session->out_buffer,
+                         "bBB",
+                         SSH2_MSG_KEX_DH_GEX_GROUP,
+                         session->next_crypto->p,
+                         session->next_crypto->g);
+    if (rc != SSH_OK) {
+        ssh_set_error_oom(session);
+        ssh_buffer_reinit(session->out_buffer);
+        return SSH_ERROR;
+    }
+    rc = ssh_packet_send(session);
+    if (rc != SSH_OK) {
+        return SSH_ERROR;
+    }
+    SSH_LOG(SSH_LOG_PACKET, "SSH2_MSG_KEX_DH_GEX_GROUP sent");
+    session->dh_handshake_state = DH_STATE_GEX_REQUEST_SENT;
+    return SSH_OK;
+}
+
+static int ssh_server_kexdh_gex_old_init(ssh_session session, ssh_buffer packet)
+{
+    uint32_t pbits;
+    int rc;
+    rc = ssh_buffer_unpack(packet, "d", &pbits);
+    if (rc != SSH_OK) {
+        ssh_set_error(session, SSH_FATAL, "No n in client request");
+        return SSH_ERROR;
+    }
+    session->next_crypto->pbits = pbits;
+    session->next_crypto->old_gex = 1;
+    return ssh_server_kexdh_gex_send_group(session);
+}
+
+static int ssh_server_kexdh_gex_new_init(ssh_session session, ssh_buffer packet)
+{
+    uint32_t pmin, pbits, pmax;
+    int rc;
+    rc = ssh_buffer_unpack(packet, "ddd", &pmin, &pbits, &pmax);
+    if (rc != SSH_OK) {
+        ssh_set_error(session, SSH_FATAL, "No min|n|max in client request");
+        return SSH_ERROR;
+    }
+    session->next_crypto->pmin = pmin;
+    session->next_crypto->pbits = pbits;
+    session->next_crypto->pmax = pmax;
+    session->next_crypto->old_gex = 0;
+    return ssh_server_kexdh_gex_send_group(session);
+}
+
 SSH_PACKET_CALLBACK(ssh_packet_kexdh_init){
   int rc = SSH_ERROR;
   (void)type;
@@ -194,8 +275,13 @@ SSH_PACKET_CALLBACK(ssh_packet_kexdh_init){
   switch(session->next_crypto->kex_type){
       case SSH_KEX_DH_GROUP1_SHA1:
       case SSH_KEX_DH_GROUP14_SHA1:
-        rc=ssh_server_kexdh_init(session, packet);
+        rc = ssh_server_kexdh_group_init(session, packet);
         break;
+      case SSH_KEX_DH_GROUPEX_SHA1:
+      case SSH_KEX_DH_GROUPEX_SHA256:
+        SSH_LOG(SSH_LOG_PACKET,"Received SSH_MSG_KEX_DH_GEX_REQUEST_OLD");
+        rc = ssh_server_kexdh_gex_old_init(session, packet);
+		break;
   #ifdef HAVE_ECDH
       case SSH_KEX_ECDH_SHA2_NISTP256:
         rc = ssh_server_ecdh_init(session, packet);
@@ -219,6 +305,40 @@ error:
   return SSH_PACKET_USED;
 }
 
+SSH_PACKET_CALLBACK(ssh_packet_kexdh_gex_init){
+    int rc;
+    (void)type;
+    (void)user;
+    SSH_LOG(SSH_LOG_PACKET,"Received SSH_MSG_KEX_DH_GEX_INIT");
+    if (session->dh_handshake_state != DH_STATE_GEX_REQUEST_SENT) {
+        SSH_LOG(SSH_LOG_RARE, "Invalid state for SSH_MSG_KEX_DH_GEX_INIT");
+        session->session_state = SSH_SESSION_STATE_ERROR;
+        return SSH_PACKET_USED;
+    }
+    rc = ssh_server_kexdh_init(session, packet, SSH2_MSG_KEX_DH_GEX_REPLY);
+    if (rc == SSH_ERROR) {
+        session->session_state = SSH_SESSION_STATE_ERROR;
+    }
+    return SSH_PACKET_USED;
+}
+
+SSH_PACKET_CALLBACK(ssh_packet_kexdh_gex_request){
+    int rc;
+    (void)type;
+    (void)user;
+    SSH_LOG(SSH_LOG_PACKET,"Received SSH_MSG_KEX_DH_GEX_REQUEST");
+    if (session->dh_handshake_state != DH_STATE_INIT) {
+        SSH_LOG(SSH_LOG_RARE,"Invalid state for SSH_MSG_KEX_DH_GEX_REQUEST");
+        session->session_state = SSH_SESSION_STATE_ERROR;
+        return SSH_PACKET_USED;
+    }
+    rc = ssh_server_kexdh_gex_new_init(session, packet);
+    if (rc == SSH_ERROR) {
+        session->session_state = SSH_SESSION_STATE_ERROR;
+    }
+    return SSH_PACKET_USED;
+}
+
 int ssh_get_key_params(ssh_session session, ssh_key *privkey){
     ssh_key pubkey;
     ssh_string pubkey_blob;
@@ -262,7 +382,7 @@ int ssh_get_key_params(ssh_session session, ssh_key *privkey){
     return SSH_OK;
 }
 
-static int dh_handshake_server(ssh_session session) {
+static int ssh_dh_handshake_server(ssh_session session, int reply_type) {
   ssh_key privkey;
   ssh_string sig_blob;
   ssh_string f;
@@ -309,7 +429,7 @@ static int dh_handshake_server(ssh_session session) {
 
   rc = ssh_buffer_pack(session->out_buffer,
                        "bSSS",
-                       SSH2_MSG_KEXDH_REPLY,
+                       reply_type,
                        session->next_crypto->server_pubkey,
                        f,
                        sig_blob);
diff --git a/src/session.c b/src/session.c
index 6885866..e63d063 100644
--- a/src/session.c
+++ b/src/session.c
@@ -346,6 +346,10 @@ const char* ssh_get_kex_algo(ssh_session session) {
             return "diffie-hellman-group1-sha1";
         case SSH_KEX_DH_GROUP14_SHA1:
             return "diffie-hellman-group14-sha1";
+        case SSH_KEX_DH_GROUPEX_SHA1:
+            return "diffie-hellman-group-exchange-sha1";
+        case SSH_KEX_DH_GROUPEX_SHA256:
+            return "diffie-hellman-group-exchange-sha256";
         case SSH_KEX_ECDH_SHA2_NISTP256:
             return "ecdh-sha2-nistp256";
         case SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG:
diff --git a/src/wrapper.c b/src/wrapper.c
index 7686937..f425a90 100644
--- a/src/wrapper.c
+++ b/src/wrapper.c
@@ -152,6 +152,8 @@ void crypto_free(struct ssh_crypto_struct *crypto){
   cipher_free(crypto->in_cipher);
   cipher_free(crypto->out_cipher);
 
+  bignum_free(crypto->p);
+  bignum_free(crypto->g);
   bignum_free(crypto->e);
   bignum_free(crypto->f);
   bignum_free(crypto->x);
diff --git a/tests/client/torture_algorithms.c b/tests/client/torture_algorithms.c
index 605772c..dc0e996 100644
--- a/tests/client/torture_algorithms.c
+++ b/tests/client/torture_algorithms.c
@@ -364,6 +364,45 @@ static void torture_algorithms_dh_group1(void **state) {
 
     ssh_disconnect(session);
 }
+
+static void torture_algorithms_dh_groupex_sha1(void **state) {
+    struct torture_state *s = *state;
+    ssh_session session = s->ssh.session;
+    int rc;
+
+    rc = ssh_options_set(session, SSH_OPTIONS_KEY_EXCHANGE, "diffie-hellman-group-exchange-sha1");
+    assert_int_equal(rc, SSH_OK);
+
+    rc = ssh_connect(session);
+    assert_int_equal(rc, SSH_OK);
+    rc = ssh_userauth_none(session, NULL);
+    if (rc != SSH_OK) {
+      rc = ssh_get_error_code(session);
+      assert_int_equal(rc, SSH_REQUEST_DENIED);
+    }
+
+    ssh_disconnect(session);
+}
+
+static void torture_algorithms_dh_groupex_sha256(void **state) {
+    struct torture_state *s = *state;
+    ssh_session session = s->ssh.session;
+    int rc;
+
+    rc = ssh_options_set(session, SSH_OPTIONS_KEY_EXCHANGE, "diffie-hellman-group-exchange-sha256");
+    assert_int_equal(rc, SSH_OK);
+
+    rc = ssh_connect(session);
+    assert_int_equal(rc, SSH_OK);
+    rc = ssh_userauth_none(session, NULL);
+    if (rc != SSH_OK) {
+      rc = ssh_get_error_code(session);
+      assert_int_equal(rc, SSH_REQUEST_DENIED);
+    }
+
+    ssh_disconnect(session);
+}
+
 int torture_run_tests(void) {
     int rc;
     struct CMUnitTest tests[] = {
@@ -448,6 +487,13 @@ int torture_run_tests(void) {
         cmocka_unit_test_setup_teardown(torture_algorithms_dh_group1,
                                         session_setup,
                                         session_teardown),
+        cmocka_unit_test_setup_teardown(torture_algorithms_dh_groupex_sha1,
+                                        session_setup,
+                                        session_teardown),
+        cmocka_unit_test_setup_teardown(torture_algorithms_dh_groupex_sha256,
+                                        session_setup,
+                                        session_teardown),
+
 #if defined(HAVE_LIBCRYPTO) && defined(HAVE_ECC)
         cmocka_unit_test_setup_teardown(torture_algorithms_ecdh_sha2_nistp256,
                                         session_setup,
diff --git a/tests/pkd/pkd_hello.c b/tests/pkd/pkd_hello.c
index f02cb5f..e39df78 100644
--- a/tests/pkd/pkd_hello.c
+++ b/tests/pkd/pkd_hello.c
@@ -185,22 +185,32 @@ static int torture_pkd_setup_ecdsa_521(void **state) {
     f(client, rsa_ecdh_sha2_nistp256,                 kexcmd("ecdh-sha2-nistp256 "),           setup_rsa,        teardown) \
     f(client, rsa_diffie_hellman_group14_sha1,        kexcmd("diffie-hellman-group14-sha1"),   setup_rsa,        teardown) \
     f(client, rsa_diffie_hellman_group1_sha1,         kexcmd("diffie-hellman-group1-sha1"),    setup_rsa,        teardown) \
+    f(client, rsa_diffie_hellman_groupex_sha1,        kexcmd("diffie-hellman-group-exchange-sha1"),      setup_rsa,        teardown) \
+    f(client, rsa_diffie_hellman_groupex_sha256,      kexcmd("diffie-hellman-group-exchange-sha256"),    setup_rsa,        teardown) \
     f(client, dsa_curve25519_sha256,                  kexcmd("curve25519-sha256@xxxxxxxxxx"),  setup_dsa,        teardown) \
     f(client, dsa_ecdh_sha2_nistp256,                 kexcmd("ecdh-sha2-nistp256 "),           setup_dsa,        teardown) \
     f(client, dsa_diffie_hellman_group14_sha1,        kexcmd("diffie-hellman-group14-sha1"),   setup_dsa,        teardown) \
     f(client, dsa_diffie_hellman_group1_sha1,         kexcmd("diffie-hellman-group1-sha1"),    setup_dsa,        teardown) \
+    f(client, dsa_diffie_hellman_groupex_sha1,        kexcmd("diffie-hellman-group-exchange-sha1"),      setup_dsa,        teardown) \
+    f(client, dsa_diffie_hellman_groupex_sha256,      kexcmd("diffie-hellman-group-exchange-sha256"),    setup_dsa,        teardown) \
     f(client, ecdsa_256_curve25519_sha256,            kexcmd("curve25519-sha256@xxxxxxxxxx"),  setup_ecdsa_256,  teardown) \
     f(client, ecdsa_256_ecdh_sha2_nistp256,           kexcmd("ecdh-sha2-nistp256 "),           setup_ecdsa_256,  teardown) \
     f(client, ecdsa_256_diffie_hellman_group14_sha1,  kexcmd("diffie-hellman-group14-sha1"),   setup_ecdsa_256,  teardown) \
     f(client, ecdsa_256_diffie_hellman_group1_sha1,   kexcmd("diffie-hellman-group1-sha1"),    setup_ecdsa_256,  teardown) \
+    f(client, ecdsa_diffie_hellman_groupex_sha1,      kexcmd("diffie-hellman-group-exchange-sha1"),      setup_ecdsa_256,  teardown) \
+    f(client, ecdsa_diffie_hellman_groupex_sha256,    kexcmd("diffie-hellman-group-exchange-sha256"),    setup_ecdsa_256,  teardown) \
     f(client, ecdsa_384_curve25519_sha256,            kexcmd("curve25519-sha256@xxxxxxxxxx"),  setup_ecdsa_384,  teardown) \
     f(client, ecdsa_384_ecdh_sha2_nistp256,           kexcmd("ecdh-sha2-nistp256 "),           setup_ecdsa_384,  teardown) \
     f(client, ecdsa_384_diffie_hellman_group14_sha1,  kexcmd("diffie-hellman-group14-sha1"),   setup_ecdsa_384,  teardown) \
     f(client, ecdsa_384_diffie_hellman_group1_sha1,   kexcmd("diffie-hellman-group1-sha1"),    setup_ecdsa_384,  teardown) \
+    f(client, ecdsa_384_diffie_hellman_groupex_sha1,   kexcmd("diffie-hellman-group-exchange-sha1"),      setup_ecdsa_384,  teardown) \
+    f(client, ecdsa_384_diffie_hellman_groupex_sha256, kexcmd("diffie-hellman-group-exchange-sha256"),    setup_ecdsa_384,  teardown) \
     f(client, ecdsa_521_curve25519_sha256,            kexcmd("curve25519-sha256@xxxxxxxxxx"),  setup_ecdsa_521,  teardown) \
     f(client, ecdsa_521_ecdh_sha2_nistp256,           kexcmd("ecdh-sha2-nistp256 "),           setup_ecdsa_521,  teardown) \
     f(client, ecdsa_521_diffie_hellman_group14_sha1,  kexcmd("diffie-hellman-group14-sha1"),   setup_ecdsa_521,  teardown) \
-    f(client, ecdsa_521_diffie_hellman_group1_sha1,   kexcmd("diffie-hellman-group1-sha1"),    setup_ecdsa_521,  teardown)
+    f(client, ecdsa_521_diffie_hellman_group1_sha1,   kexcmd("diffie-hellman-group1-sha1"),    setup_ecdsa_521,  teardown) \
+    f(client, ecdsa_521_diffie_hellman_groupex_sha1,   kexcmd("diffie-hellman-group-exchange-sha1"),      setup_ecdsa_521,  teardown) \
+    f(client, ecdsa_521_diffie_hellman_groupex_sha256, kexcmd("diffie-hellman-group-exchange-sha256"),    setup_ecdsa_521,  teardown) \
 
 #define PKDTESTS_CIPHER(f, client, ciphercmd) \
     /* Ciphers. */ \
-- 
2.7.4


Archive administrator: postmaster@lists.cynapses.org