aboutsummaryrefslogtreecommitdiff
path: root/src/dh-gex.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/dh-gex.c')
-rw-r--r--src/dh-gex.c127
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;
+}