[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