diff --git a/src/ssh/ssh-trans.c b/src/ssh/ssh-trans.c
index 5fa1ab01a2e60f9f94b1b3b251adc463f5d340a0..7f5ccb0a5eeca818dfb7f865a545c4263d817112 100644
--- a/src/ssh/ssh-trans.c
+++ b/src/ssh/ssh-trans.c
@@ -5,7 +5,22 @@
 #include "ssh.h"
 #include "ssh-trans.h"
 
+typedef struct deuce_ssh_transport_global_config {
+	atomic_bool used;
+	const char *software_version;
+	const char *version_comment;
+	size_t kex_entries;
+	char **kex_name;
+	deuce_ssh_kex_handler_t *kex_handler;
+	deuce_ssh_kex_cleanup_t *kex_cleanup;
+	int (*tx)(uint8_t *buf, size_t bufsz, atomic_bool *terminate, void *cbdata);
+	int (*rx)(uint8_t *buf, size_t bufsz, atomic_bool *terminate, void *cbdata);
+	int (*rx_line)(uint8_t *buf, size_t bufsz, size_t *bytes_received, atomic_bool *terminate, void *cbdata);
+	int (*extra_line_cb)(uint8_t *buf, size_t bufsz, void *cbdata);
+} *deuce_ssh_transport_global_config_t;
+
 static const char * const sw_ver = "DeuceSSH-0.0";
+static struct deuce_ssh_transport_global_config gconf;
 
 static inline bool
 has_nulls(uint8_t *buf, size_t buflen)
@@ -53,7 +68,7 @@ version_ex(deuce_ssh_session_t sess)
 	uint8_t line[256];
 
 	while (!sess->terminate) {
-		res = sess->rx_line(line, sizeof(line) - 1, &received, &sess->terminate, sess->rx_line_cbdata);
+		res = gconf.rx_line(line, sizeof(line) - 1, &received, &sess->terminate, sess->rx_line_cbdata);
 		if (res < 0) {
 			sess->terminate = true;
 			return res;
@@ -83,9 +98,9 @@ version_ex(deuce_ssh_session_t sess)
 			assert(res == thrd_success);
 			return 0;
 		}
-		if (sess->extra_line_cb) {
+		if (gconf.extra_line_cb) {
 			line[received] = 0;
-			res = sess->extra_line_cb(line, received, sess->extra_line_cbdata);
+			res = gconf.extra_line_cb(line, received, sess->extra_line_cbdata);
 			if (res < 0) {
 				sess->terminate = true;
 				return res;
@@ -122,38 +137,38 @@ tx_handshake(void *arg)
 {
 	deuce_ssh_session_t sess = arg;
 	int res;
+	uint8_t line[255];
+	size_t sz = 0;
 
 	/* Handshake */
-	res = sess->tx((uint8_t *)"SSH-2.0-", 8, &sess->terminate, sess->tx_cbdata);
-	if (res < 0) {
-		sess->terminate = true;
-		return res;
+	memcpy(line, "SSH-2.0-", 8);
+	sz += 8;
+	size_t asz = strlen(gconf.software_version);
+	memcpy(&line[sz], gconf.software_version, asz);
+	sz += asz;
+	if (gconf.version_comment != NULL) {
+		memcpy(&line[sz], " ", 1);
+		sz += 1;
+		asz = strlen(gconf.version_comment);
+		memcpy(&line[sz], gconf.version_comment, asz);
+		sz += asz;
 	}
-	size_t sz = strlen(sess->software_version);
-	res = sess->tx((uint8_t *)sess->software_version, sz, &sess->terminate, sess->tx_cbdata);
+	res = gconf.tx(line, sz, &sess->terminate, sess->tx_cbdata);
 	if (res < 0) {
 		sess->terminate = true;
 		return res;
 	}
-	if (sess->version_comment != NULL) {
-		res = sess->tx((uint8_t *)" ", 1, &sess->terminate, sess->tx_cbdata);
-		if (res < 0) {
-			sess->terminate = true;
-			return res;
-		}
-		sz = strlen(sess->version_comment);
-		res = sess->tx((uint8_t *)sess->software_version, sz, &sess->terminate, sess->tx_cbdata);
-		if (res < 0) {
-			sess->terminate = true;
-			return res;
-		}
-	}
 	return 0;
 }
 
 void
 deuce_ssh_transport_cleanup(deuce_ssh_session_t sess)
 {
+	if (sess->trans->kex_selected != SIZE_MAX) {
+		if (gconf.kex_cleanup[sess->trans->kex_selected] != NULL)
+			gconf.kex_cleanup[sess->trans->kex_selected](sess);
+		sess->trans->kex_selected = SIZE_MAX;
+	}
 	free(sess->remote_software_version);
 	sess->remote_software_version = NULL;
 	free(sess->remote_version_comment);
@@ -163,9 +178,13 @@ deuce_ssh_transport_cleanup(deuce_ssh_session_t sess)
 int
 deuce_ssh_transport_init(deuce_ssh_session_t sess)
 {
-	if (sess->software_version == NULL)
-		sess->software_version = sw_ver;
+	gconf.used = true;
+	if (gconf.software_version == NULL)
+		gconf.software_version = sw_ver;
 
+	sess->trans = calloc(1, sizeof(struct deuce_ssh_transport_state));
+	if (sess->trans == NULL)
+		return DEUCE_SSH_ERROR_ALLOC;
 	thrd_t thrd;
 	if (thrd_create(&thrd, rx_thread, sess) != thrd_success)
 		return DEUCE_SSH_ERROR_INIT;
@@ -176,7 +195,55 @@ deuce_ssh_transport_init(deuce_ssh_session_t sess)
 		thrd_join(thrd, &tres);
 		return res;
 	}
+	sess->trans->kex_selected = SIZE_MAX;
+	sess->trans->kex_state = 0;
+
+	sess->trans->transport_thread = thrd;
+	return 0;
+}
+
+int
+deuce_ssh_transport_register_kex(const char *name, deuce_ssh_kex_handler_t kex_handler, deuce_ssh_kex_cleanup_t kex_cleanup)
+{
+	if (gconf.used)
+		return DEUCE_SSH_ERROR_TOOLATE;
+	if (gconf.kex_entries + 1 == SIZE_MAX)
+		return DEUCE_SSH_ERROR_TOOMANY;
+	char **newnames = realloc(gconf.kex_name, sizeof(gconf.kex_name[0]) * (gconf.kex_entries + 1));
+	if (newnames == NULL)
+		return DEUCE_SSH_ERROR_ALLOC;
+	gconf.kex_name = newnames;
+	gconf.kex_name[gconf.kex_entries] = strdup(name);
+
+	deuce_ssh_kex_handler_t *newhandlers = realloc(gconf.kex_handler, sizeof(gconf.kex_handler[0]) * (gconf.kex_entries + 1));
+	if (newhandlers == NULL) {
+		free(gconf.kex_name[gconf.kex_entries]);
+		return DEUCE_SSH_ERROR_ALLOC;
+	}
+	gconf.kex_handler = newhandlers;
+	gconf.kex_handler[gconf.kex_entries] = kex_handler;
+
+	deuce_ssh_kex_cleanup_t *newcleanup = realloc(gconf.kex_cleanup, sizeof(gconf.kex_cleanup[0]) * (gconf.kex_entries + 1));
+	if (newcleanup == NULL) {
+		free(gconf.kex_name[gconf.kex_entries]);
+		return DEUCE_SSH_ERROR_ALLOC;
+	}
+	gconf.kex_cleanup = newcleanup;
+	gconf.kex_cleanup[gconf.kex_entries] = kex_cleanup;
+
+	gconf.kex_entries++;
+	return 0;
+}
+
+int
+deuce_ssh_transport_set_callbacks(deuce_ssh_transport_io_cb_t tx, deuce_ssh_transport_io_cb_t rx, deuce_ssh_transport_rxline_cb_t rx_line, deuce_ssh_transport_extra_line_cb_t extra_line_cb)
+{
+	if (gconf.used)
+		return DEUCE_SSH_ERROR_TOOLATE;
+	gconf.tx = tx;
+	gconf.rx = rx;
+	gconf.rx_line = rx_line;
+	gconf.extra_line_cb = extra_line_cb;
 
-	sess->transport_thread = thrd;
 	return 0;
 }
diff --git a/src/ssh/ssh-trans.h b/src/ssh/ssh-trans.h
index 11e68f23377db9920c204d7975f1623451af4e93..f388531ed0b2f34b95c78f30a186b6c244d7e811 100644
--- a/src/ssh/ssh-trans.h
+++ b/src/ssh/ssh-trans.h
@@ -1,5 +1,6 @@
 // RFC-4253
 
+#include "ssh.h"
 #include "ssh-arch.h"
 
 #ifndef DEUCE_SSH_TRANS_H
@@ -33,19 +34,41 @@
 #define SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE 14
 #define SSH_DISCONNECT_ILLEGAL_USER_NAME              15
 
-struct deuce_ssh_transport_packet {
+typedef struct deuce_ssh_transport_packet {
 	deuce_ssh_string_t payload;
 	deuce_ssh_string_t random_padding;
 	deuce_ssh_string_t mac;
 	deuce_ssh_uint32_t packet_length;
 	deuce_ssh_byte_t   padding;
-};
+} *deuce_ssh_transport_packet_t;
 
-struct deuce_ssh_transport_state {
+typedef int (*deuce_ssh_kex_handler_t)(deuce_ssh_transport_packet_t pkt, deuce_ssh_session_t sess);
+typedef void (*deuce_ssh_kex_cleanup_t)(deuce_ssh_session_t sess);
+
+typedef struct deuce_ssh_transport_state {
 	uint32_t    tx_seq;
 	uint32_t    rx_seq;
 	bool        client;
-};
+
+	/* Transport options */
+	thrd_t transport_thread;
+
+	/* KEX options */
+	void *kex_cbdata;
+	uint32_t kex_state;
+	size_t kex_selected;
+
+	/* KEX outputs */
+	size_t shared_secret_sz;
+	uint8_t *shared_secret;
+	size_t exchange_hash_sz;
+	uint8_t *exchange_hash;
+
+	size_t enc_selected;
+	size_t mac_selected;
+	size_t comp_selected;
+
+} *deuce_ssh_transport_state_t;
 
 int deuce_ssh_transport_init(deuce_ssh_session_t sess);
 void deuce_ssh_transport_cleanup(deuce_ssh_session_t sess);
diff --git a/src/ssh/ssh.c b/src/ssh/ssh.c
index 4b645ee0de20dd4ef3306a616ca8bbc77bf6ec25..c688776e9a64e1926a0923c92c3d29c3669459c8 100644
--- a/src/ssh/ssh.c
+++ b/src/ssh/ssh.c
@@ -9,9 +9,8 @@ deuce_ssh_session_init(deuce_ssh_session_t sess)
 		return DEUCE_SSH_ERROR_INIT;
 
 	res = deuce_ssh_transport_init(sess);
-	if (res < 0) {
+	if (res < 0)
 		return res;
-	}
 
 	sess->initialized = true;
 	return 0;
@@ -24,7 +23,7 @@ deuce_ssh_session_terminate(deuce_ssh_session_t sess)
 	if (atomic_compare_exchange_strong(&sess->initialized, &t, false)) {
 		sess->terminate = true;
 		int tres;
-		thrd_join(sess->transport_thread, &tres);
+		thrd_join(sess->trans->transport_thread, &tres);
 		sess->terminate = false;
 		return true;
 	}
diff --git a/src/ssh/ssh.h b/src/ssh/ssh.h
index 7036cbad02f0d7aa629115361a9605962b7783f9..aef1214f4be92d1e9698d631380a5d4ee72f75ba 100644
--- a/src/ssh/ssh.h
+++ b/src/ssh/ssh.h
@@ -20,6 +20,13 @@ _Static_assert(0, "threads.h support required");
 #define DEUCE_SSH_ERROR_ALLOC      -3
 #define DEUCE_SSH_ERROR_INIT       -4
 #define DEUCE_SSH_ERROR_TERMINATED -5
+#define DEUCE_SSH_ERROR_TOOLATE    -6
+#define DEUCE_SSH_ERROR_TOOMANY    -7
+
+typedef struct deuce_ssh_transport_state *deuce_ssh_transport_state_t;
+typedef int (*deuce_ssh_transport_io_cb_t)(uint8_t *buf, size_t bufsz, atomic_bool *terminate, void *cbdata);
+typedef int (*deuce_ssh_transport_rxline_cb_t)(uint8_t *buf, size_t bufsz, size_t *bytes_received, atomic_bool *terminate, void *cbdata);
+typedef int (*deuce_ssh_transport_extra_line_cb_t)(uint8_t *buf, size_t bufsz, void *cbdata);
 
 typedef struct deuce_ssh_session {
 	/* Global */
@@ -27,22 +34,16 @@ typedef struct deuce_ssh_session {
 	atomic_bool initialized;
 	atomic_bool terminate;
 
-	/* Transport options */
-	const char *software_version;
-	const char *version_comment;
+	/* Transport Remote information */
+	char *remote_software_version;
+	char *remote_version_comment;
+
 	void *tx_cbdata;
-	int (*tx)(uint8_t *buf, size_t bufsz, atomic_bool *terminate, void *cbdata);
 	void *rx_cbdata;
-	int (*rx)(uint8_t *buf, size_t bufsz, atomic_bool *terminate, void *cbdata);
 	void *rx_line_cbdata;
-	int (*rx_line)(uint8_t *buf, size_t bufsz, size_t *bytes_received, atomic_bool *terminate, void *cbdata);
 	void *extra_line_cbdata;
-	int (*extra_line_cb)(uint8_t *buf, size_t bufsz, void *cbdata);
-	thrd_t transport_thread;
 
-	/* Transport Remote information */
-	char *remote_software_version;
-	char *remote_version_comment;
+	deuce_ssh_transport_state_t trans;
 } *deuce_ssh_session_t;
 
 int deuce_ssh_session_init(deuce_ssh_session_t sess);