[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [PATCH] diffie-hellman-group-exchange-sha256
[Thread Prev] | [Thread Next]
[Date Prev] | [Date Next]
- Subject: Re: [PATCH] diffie-hellman-group-exchange-sha256
- From: David Kedves <kedazo@xxxxxxxxx>
- Reply-to: libssh@xxxxxxxxxx
- Date: Tue, 31 May 2016 16:28:15 +0200
- To: libssh@xxxxxxxxxx
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