diff options
Diffstat (limited to 'src/dh-gex.c')
-rw-r--r-- | src/dh-gex.c | 127 |
1 files changed, 127 insertions, 0 deletions
diff --git a/src/dh-gex.c b/src/dh-gex.c new file mode 100644 index 00000000..f72684e6 --- /dev/null +++ b/src/dh-gex.c @@ -0,0 +1,127 @@ +/* + * dh-gex.c - diffie-hellman group exchange + * + * This file is part of the SSH Library + * + * Copyright (c) 2016 by Aris Adamantiadis <aris@0xbadc0de.be> + * + * 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 + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "libssh/dh-gex.h" +#include "libssh/libssh.h" +#include "libssh/ssh2.h" +#include "libssh/callbacks.h" +#include "libssh/dh.h" + +static SSH_PACKET_CALLBACK(ssh_packet_client_dhgex_group); +static SSH_PACKET_CALLBACK(ssh_packet_client_dhgex_reply); + +static ssh_packet_callback dhgex_client_callbacks[]= { + ssh_packet_client_dhgex_group, /* SSH_MSG_KEX_DH_GEX_GROUP */ + NULL, /* SSH_MSG_KEX_DH_GEX_INIT */ + ssh_packet_client_dhgex_reply /* SSH_MSG_KEX_DH_GEX_REPLY */ +}; + +static struct ssh_packet_callbacks_struct ssh_dh_client_callbacks = { + .start = SSH2_MSG_KEX_DH_GEX_GROUP, + .n_callbacks = 3, + .callbacks = dhgex_client_callbacks, + .user = NULL +}; + +/** @internal + * @brief initiates a diffie-hellman-group-exchange kex + */ +int ssh_client_dhgex_init(ssh_session session){ + int rc; + + rc = ssh_dh_init_common(session); + if (rc != SSH_OK){ + return rc; + } + + /* Minimum group size, preferred group size, maximum group size */ + rc = ssh_buffer_pack(session->out_buffer, "bddd", + SSH2_MSG_KEX_DH_GEX_REQUEST, 1534, 2048, 8192); + /* register the packet callbacks */ + ssh_packet_set_callbacks(session, &ssh_dh_client_callbacks); + session->dh_handshake_state = DH_STATE_REQUEST_SENT; + rc = packet_send(session); + return rc; +error: + ssh_dh_cleanup(session); + return SSH_ERROR; +} + +/** @internal + * @brief handle a DH_GEX_GROUP packet, client side. This packet contains + * the group parameters. + */ +SSH_PACKET_CALLBACK(ssh_packet_client_dhgex_group){ + int rc; + int blen; + bignum pmin1=NULL, one=NULL; + + if (session->dh_handshake_state != DH_STATE_REQUEST_SENT){ + ssh_set_error(session, SSH_FATAL, "Received DH_GEX_GROUP in invalid state"); + goto error; + } + one = bignum_new(); + pmin1 = bignum_new(); + if (one == NULL || pmin1 == NULL){ + ssh_error_oom(session); + goto error; + } + session->next_crypto->dh_group_is_mutable = 1; + rc = ssh_buffer_unpack(packet, "BB", &session->next_crypto->p, &session->next_crypto->g); + if (rc != SSH_OK){ + ssh_set_error(session, SSH_FATAL, "Invalid DH_GEX_GROUP packet"); + goto error; + } + /* basic checks */ + bignum_set_word(one, 1); + blen = bignum_num_bits(session->next_crypto->p); + if (blen < 1024 || blen > 8192){ + ssh_set_error(session, SSH_FATAL, "Invalid dh group parameter p: %d not in [1024:8192]", blen); + goto error; + } + if (bignum_cmp(session->next_crypto->p, one) <= 0){ + /* p must be positive and preferably bigger than one */ + ssh_set_error(session, SSH_FATAL, "Invalid dh group parameter p"); + } + if (!bignum_is_bit_set(session->next_crypto->p, 0)){ + /* p must be a prime and therefore not divisible by 2 */ + ssh_set_error(session, SSH_FATAL, "Invalid dh group parameter p"); + goto error; + } + bignum_sub(pmin1, session->next_crypto->p, one); + if (bignum_cmp(session->next_crypto->g, one) <= 0 || + bignum_cmp(session->next_crypto->g, pmin1) > 0){ + /* generator must be at least 2 and smaller than p-1*/ + ssh_set_error(session, SSH_FATAL, "Invalid dh group parameter g"); + goto error; + } + + session->dh_handshake_state = DH_STATE_INIT_SENT; + return SSH_PACKET_USED; +error: + bignum_safe_free(one); + bignum_safe_free(pmin1); + ssh_dh_cleanup(session); + session->session_state=SSH_SESSION_STATE_ERROR; + return SSH_PACKET_USED; +} |