diff --git a/Makefile.in b/Makefile.in
index 4eabd205c0d1609dbcf94403175a6a17e0bf753d..eef6fbee67dcaac8b6c9d2c55dbde25b22ace648 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -26,7 +26,7 @@ COMMONOBJS=dbutil.o buffer.o \
 		dss.o bignum.o \
 		signkey.o rsa.o random.o \
 		queue.o \
-		atomicio.o compat.o  fake-rfc2553.o ltc_prng.o ecc.o
+		atomicio.o compat.o fake-rfc2553.o ltc_prng.o ecc.o
 
 SVROBJS=svr-kex.o svr-algo.o svr-auth.o sshpty.o \
 		svr-authpasswd.o svr-authpubkey.o svr-authpubkeyoptions.o svr-session.o svr-service.o \
diff --git a/algo.h b/algo.h
index 89d05e3b60d15b8c7e9598ad71fdd2560570bea8..aeb56413b55a809c3df676f2f97168fcb20591fc 100644
--- a/algo.h
+++ b/algo.h
@@ -79,6 +79,20 @@ struct dropbear_hash {
 	unsigned char hashsize;
 };
 
+struct dropbear_kex {
+	// "normal" DH KEX
+	unsigned char *dh_p_bytes;
+	int dh_p_len;
+
+	// elliptic curve DH KEX
+#ifdef DROPBEAR_ECDH
+	const struct dropbear_ecc_curve *ecc_curve;
+#endif
+
+	// both
+	const struct ltc_hash_descriptor *hashdesc;
+};
+
 void crypto_init();
 int have_algo(char* algo, size_t algolen, algo_type algos[]);
 void buf_put_algolist(buffer * buf, algo_type localalgos[]);
@@ -94,14 +108,16 @@ int check_user_algos(const char* user_algo_list, algo_type * algos,
 char * algolist_string(algo_type algos[]);
 #endif
 
-enum {
+enum kex_type {
 	DROPBEAR_KEX_DH_GROUP1,
 	DROPBEAR_KEX_DH_GROUP14,
 	DROPBEAR_KEX_ECDH_SECP256R1,
+	DROPBEAR_KEX_ECDH_SECP384R1,
+	DROPBEAR_KEX_ECDH_SECP521R1,
 };
 
 #ifdef DROPBEAR_ECDH
-#define IS_NORMAL_DH(algo) ((algo) == DROPBEAR_KEX_DH_GROUP1 || (algo) == DROPBEAR_KEX_DH_GROUP14)
+#define IS_NORMAL_DH(algo) ((algo)->dh_p_bytes != NULL)
 #else
 #define IS_NORMAL_DH(algo) 1
 #endif
diff --git a/cli-kex.c b/cli-kex.c
index ddd2efcc62871996bc8cec59b2b219e41e5e5502..809075698a5eb2184aaf126b76a11bdc3d5c1a85 100644
--- a/cli-kex.c
+++ b/cli-kex.c
@@ -49,7 +49,7 @@ void send_msg_kexdh_init() {
 		buf_putmpint(ses.writepayload, &cli_ses.dh_param->pub);
 	} else {
 #ifdef DROPBEAR_ECDH
-		cli_ses.ecdh_param = 
+		cli_ses.ecdh_param = gen_kexecdh_param();
 #endif
 	}
 	encrypt_packet();
diff --git a/common-algo.c b/common-algo.c
index 91d27b2e76984b32b9e985568a803ecc06cfa9db..87592e13172fcf8e61df7dfea3e0c33ffe882da9 100644
--- a/common-algo.c
+++ b/common-algo.c
@@ -212,10 +212,36 @@ algo_type sshhostkey[] = {
 	{NULL, 0, NULL, 0, NULL}
 };
 
+static struct dropbear_kex kex_dh_group1 {dh_p_1, DH_P_1_LEN, NULL, sha1_desc };
+static struct dropbear_kex kex_dh_group14 {dh_p_14, DH_P_14_LEN, NULL, sha1_desc };
+
+#ifdef DROPBEAR_ECC_DH
+#ifdef DROPBEAR_ECC_256
+static struct dropbear_kex kex_ecdh_secp256r1 {NULL, 0, &ecc_curve_secp256r1, sha256_desc };
+#endif
+#ifdef DROPBEAR_ECC_384
+static struct dropbear_kex kex_ecdh_secp384r1 {NULL, 0, &ecc_curve_secp384r1, sha384_desc };
+#endif
+#ifdef DROPBEAR_ECC_521
+static struct dropbear_kex kex_ecdh_secp521r1 {NULL, 0, &ecc_curve_secp521r1, sha512_desc };
+#endif
+#endif // DROPBEAR_ECC_DH
+
+
 algo_type sshkex[] = {
-//	{"ecdh-sha2-secp256r1", DROPBEAR_KEX_ECDH_SECP256R1, NULL, 1, NULL},
-	{"diffie-hellman-group1-sha1", DROPBEAR_KEX_DH_GROUP1, NULL, 1, NULL},
-	{"diffie-hellman-group14-sha1", DROPBEAR_KEX_DH_GROUP14, NULL, 1, NULL},
+#ifdef DROPBEAR_ECC_DH
+#ifdef DROPBEAR_ECC_256
+	{"ecdh-sha2-secp256r1", 0, &kex_ecdh_descp256r1, 1, NULL},
+#endif
+#ifdef DROPBEAR_ECC_384
+	{"ecdh-sha2-secp384r1", 0, &kex_ecdh_descp384r1, 1, NULL},
+#endif
+#ifdef DROPBEAR_ECC_521
+	{"ecdh-sha2-secp521r1", 0, &kex_ecdh_descp521r1, 1, NULL},
+#endif
+#endif
+	{"diffie-hellman-group1-sha1", 0, &kex_dh_group1, 1, NULL},
+	{"diffie-hellman-group14-sha1", 0, &kex_dh_group14, 1, NULL},
 	{NULL, 0, NULL, 0, NULL}
 };
 
diff --git a/common-kex.c b/common-kex.c
index c153c8ff76aa8a0d943af90a4cbc37a9b8a00302..5d46f793cc40888451b7944a00223234caaded73 100644
--- a/common-kex.c
+++ b/common-kex.c
@@ -36,8 +36,7 @@
 #include "runopts.h"
 
 /* diffie-hellman-group1-sha1 value for p */
-#define DH_P_1_LEN 128
-static const unsigned char dh_p_1[DH_P_1_LEN] = {
+const unsigned char dh_p_1[DH_P_1_LEN] = {
 	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,
@@ -51,8 +50,7 @@ static const unsigned char dh_p_1[DH_P_1_LEN] = {
 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
 
 /* diffie-hellman-group14-sha1 value for p */
-#define DH_P_14_LEN 256
-static const unsigned char dh_p_14[DH_P_14_LEN] = {
+const unsigned char dh_p_14[DH_P_14_LEN] = {
 	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,
@@ -536,14 +534,8 @@ void recv_msg_kexinit() {
 
 static void load_dh_p(mp_int * dh_p)
 {
-	switch (ses.newkeys->algo_kex) {
-		case DROPBEAR_KEX_DH_GROUP1:
-			bytes_to_mp(dh_p, dh_p_1, DH_P_1_LEN);
-			break;
-		case DROPBEAR_KEX_DH_GROUP14:
-			bytes_to_mp(dh_p, dh_p_14, DH_P_14_LEN);
-			break;
-	}
+	bytes_to_mp(dh_p, ses.newkeys->algo_kex->dh_p_bytes, 
+		ses.newkeys->algo_kex->dh_p_len);
 }
 
 /* Initialises and generate one side of the diffie-hellman key exchange values.
@@ -667,11 +659,84 @@ void kexdh_comb_key(struct kex_dh_param *param, mp_int *dh_pub_them,
 #ifdef DROPBEAR_ECDH
 struct kex_ecdh_param *gen_kexecdh_param() {
 	struct kex_ecdh_param *param = m_malloc(sizeof(*param));
-	if (ecc_make_key_ex(NULL, dropbear_ltc_prng, &param->key
+	if (ecc_make_key_ex(NULL, dropbear_ltc_prng, 
+		&param->key, ses.newkeys->algo_kex->ecc_curve) != CRYPT_OK) {
+		dropbear_exit("ECC error")
+	}
+	return param;
+}
+
+void free_kexecdh_param(struct kex_ecdh_param *param) {
+	ecc_free(&param->key);
+	m_free(param);
+
 }
-void free_kexecdh_param(struct kex_ecdh_param *param);
 void kexecdh_comb_key(struct kex_ecdh_param *param, buffer *pub_them,
-		sign_key *hostkey);
+		sign_key *hostkey) {
+
+	hash_state hs;
+	// public keys from client and server
+	ecc_key *Q_C, *Q_S, *Q_them;
+
+	// XXX load Q_them
+
+	ses.dh_K = dropbear_ecc_shared_secret()
+	
+	/* Check that dh_pub_them (dh_e or dh_f) is in the range [1, p-1] */
+	if (mp_cmp(dh_pub_them, &dh_p) != MP_LT 
+			|| mp_cmp_d(dh_pub_them, 0) != MP_GT) {
+		dropbear_exit("Diffie-Hellman error");
+	}
+	
+	/* K = e^y mod p = f^x mod p */
+	ses.dh_K = (mp_int*)m_malloc(sizeof(mp_int));
+	m_mp_init(ses.dh_K);
+	if (mp_exptmod(dh_pub_them, &param->priv, &dh_p, ses.dh_K) != MP_OKAY) {
+		dropbear_exit("Diffie-Hellman error");
+	}
+
+	/* clear no longer needed vars */
+	mp_clear_multi(&dh_p, NULL);
+
+	/* From here on, the code needs to work with the _same_ vars on each side,
+	 * not vice-versaing for client/server */
+	if (IS_DROPBEAR_CLIENT) {
+		dh_e = &param->pub;
+		dh_f = dh_pub_them;
+	} else {
+		dh_e = dh_pub_them;
+		dh_f = &param->pub;
+	} 
+
+	/* Create the remainder of the hash buffer, to generate the exchange hash */
+	/* K_S, the host key */
+	buf_put_pub_key(ses.kexhashbuf, hostkey, ses.newkeys->algo_hostkey);
+	/* e, exchange value sent by the client */
+	buf_putmpint(ses.kexhashbuf, dh_e);
+	/* f, exchange value sent by the server */
+	buf_putmpint(ses.kexhashbuf, dh_f);
+	/* K, the shared secret */
+	buf_putmpint(ses.kexhashbuf, ses.dh_K);
+
+	/* calculate the hash H to sign */
+	sha1_init(&hs);
+	buf_setpos(ses.kexhashbuf, 0);
+	sha1_process(&hs, buf_getptr(ses.kexhashbuf, ses.kexhashbuf->len),
+			ses.kexhashbuf->len);
+	sha1_done(&hs, ses.hash);
+
+	buf_burn(ses.kexhashbuf);
+	buf_free(ses.kexhashbuf);
+	ses.kexhashbuf = NULL;
+	
+	/* first time around, we set the session_id to H */
+	if (ses.session_id == NULL) {
+		/* create the session_id, this never needs freeing */
+		ses.session_id = (unsigned char*)m_malloc(SHA1_HASH_SIZE);
+		memcpy(ses.session_id, ses.hash, SHA1_HASH_SIZE);
+	}
+
+}
 #endif
 
 /* read the other side's algo list. buf_match_algo is a callback to match
@@ -707,7 +772,7 @@ static void read_kex_algos() {
 		goto error;
 	}
 	TRACE(("kex algo %s", algo->name))
-	ses.newkeys->algo_kex = algo->val;
+	ses.newkeys->algo_kex = algo->data;
 
 	/* server_host_key_algorithms */
 	algo = ses.buf_match_algo(ses.payload, sshhostkey, &goodguess);
diff --git a/ecc.c b/ecc.c
index cf67ec09c8d95703cb6e0fb7c00fd4a229a603ec..873a92ac2187f1e437a461fdd1993531491faad5 100644
--- a/ecc.c
+++ b/ecc.c
@@ -1,6 +1,37 @@
+#include "includes.h"
+#include "options.h"
+#include "ecc.h"
+
 #ifdef DROPBEAR_ECC
 
+#ifdef DROPBEAR_ECC_256
+const struct ecc_curve_secp256r1 {
+	.ltc_set = &ltc_ecc_sets[0],
+	.hash_desc = sha256_desc,
+	.name = "secp256r1"
+};
+#endif
+
+
+#ifdef DROPBEAR_ECC_384
+const struct ecc_curve_secp384r1 {
+	.ltc_set = &ltc_ecc_sets[1],
+	.hash_desc = sha384_desc,
+	.name = "secp384r1"
+};
+#endif
+
+#ifdef DROPBEAR_ECC_256
+const struct ecc_curve_secp256r1 {
+	.ltc_set = &ltc_ecc_sets[0],
+	.hash_desc = sha256_desc,
+	.name = "secp256r1"
+};
+#endif
+
+
 void buf_put_ecc_key_string(buffer *buf, ecc_key *key) {
+	// XXX point compression
 	int len = key->dp->size*2 + 1;
 	buf_putint(len);
 	int err = ecc_ansi_x963_export(key, buf_getwriteptr(buf, len), &len);
@@ -13,5 +44,71 @@ void buf_put_ecc_key_string(buffer *buf, ecc_key *key) {
 int buf_get_ecc_key_string(buffer *buf, ecc_key *key) {
 }
 
+// a modified version of libtomcrypt's "ecc_shared_secret" to output
+// a mp_int instead.
+mp_int * dropbear_ecc_shared_secret(ecc_key *public_key, ecc_key *private_key)
+{
+   ecc_point *result = NULL
+   mp_int *prime = NULL, *shared_secret = NULL;
+   int ret = DROPBEAR_FAILURE;
+
+   /* type valid? */
+   if (private_key->type != PK_PRIVATE) {
+   	goto done;
+   }
+
+   if (private_key->dp != public_key->dp) {
+   	goto done;
+   }
+
+#if 0
+   // XXX - possibly not neccessary tests?
+   if (ltc_ecc_is_valid_idx(private_key->idx) == 0 || ltc_ecc_is_valid_idx(public_key->idx) == 0) {
+   	goto done;
+   }
+
+   if (XSTRCMP(private_key->dp->name, public_key->dp->name) != 0) {
+   	goto done;
+   }
+#endif
+
+   /* make new point */
+   result = ltc_ecc_new_point();
+   if (result == NULL) {
+      goto done;
+   }
+
+   prime = m_malloc(sizeof(*prime));
+   m_mp_init(prime);
+
+   if (mp_read_radix(prime, (char *)private_key->dp->prime, 16) != CRYPT_OK) { 
+	   	goto done; 
+   }
+   if (ltc_mp.ecc_ptmul(private_key->k, &public_key->pubkey, result, prime, 1) != CRYPT_OK) { 
+	   	goto done; 
+   }
+
+   err = DROPBEAR_SUCCESS;
+done:
+	if (err == DROPBEAR_SUCCESS) {
+		shared_secret = prime;
+		prime = NULL;
+	}
+
+	if (prime) {
+	   mp_clear(prime);
+	   m_free(prime);
+	}
+   ltc_ecc_del_point(result);
+
+   if (err == DROPBEAR_FAILURE) {
+   	 dropbear_exit("ECC error");
+   }
+
+   return shared_secret;
+   return err;
+}
+
+}
 
 #endif
diff --git a/ecc.h b/ecc.h
new file mode 100644
index 0000000000000000000000000000000000000000..426f698d9745bb19523a6a749f53ed057738ea4a
--- /dev/null
+++ b/ecc.h
@@ -0,0 +1,34 @@
+#ifndef _DROPBEAR_ECC_H
+#define _DROPBEAR_ECC_H
+
+#include "includes.h"
+#include "options.h"
+
+#include "buffer.h"
+
+#ifdef DROPBEAR_ECC
+
+struct dropbear_ecc_curve {
+	const ltc_ecc_set_type* ltc_set;
+	const struct ltc_hash_descriptor *hash_desc;
+	const char *name;
+};
+
+extern const struct dropbear_ecc_curve ecc_curve_secp256r1;
+extern const struct dropbear_ecc_curve ecc_curve_secp384r1;
+extern const struct dropbear_ecc_curve ecc_curve_secp521r1;
+
+// "pubkey" refers to a point, but LTC uses ecc_key structure for both public
+// and private keys
+void buf_put_ecc_pubkey_string(buffer *buf, ecc_key *key);
+int buf_get_ecc_pubkey_string(buffer *buf, ecc_key *key);
+int buf_get_ecc_privkey_string(buffer *buf, ecc_key *key);
+
+mp_int * dropbear_ecc_shared_secret(ecc_key *pub_key, ecc_key *priv_key);
+
+
+const ltc_ecc_set_type* get_ecc_curve(enum kex_type type);
+
+#endif
+
+#endif // _DROPBEAR_ECC_H
\ No newline at end of file
diff --git a/kex.h b/kex.h
index f9d70b84e4cfdad0573b8ee1a0c0848438c1377f..57019ffc25d40b8f79a98337fcdf4c64f6aa15f8 100644
--- a/kex.h
+++ b/kex.h
@@ -73,6 +73,11 @@ struct KEXState {
 
 };
 
+#define DH_P_1_LEN 128
+extern const const unsigned char dh_p_1[DH_P_1_LEN];
+#define DH_P_14_LEN 256
+const unsigned char dh_p_14[DH_P_14_LEN] = {
+
 struct kex_dh_param {
 	mp_int pub;
 	mp_int priv;
diff --git a/libtomcrypt/src/headers/tomcrypt_custom.h b/libtomcrypt/src/headers/tomcrypt_custom.h
index 1304ce754bfdda937dc41a9177ab7b7980e79d34..344835f5439f94254d6d2a505d3d685a2f1bb008 100644
--- a/libtomcrypt/src/headers/tomcrypt_custom.h
+++ b/libtomcrypt/src/headers/tomcrypt_custom.h
@@ -134,12 +134,18 @@
 
 #define LTC_HMAC
 
-#ifdef DROPBEAR_ECDH
+#ifdef DROPBEAR_ECC
 #define MECC
+#ifdef DROPBEAR_ECC_256
 #define ECC256
+#endif
+#ifdef DROPBEAR_ECC_384
 #define ECC384
+#endif
+#ifdef DROPBEAR_ECC_521
 #define ECC521
 #endif
+#endif
 
 /* Various tidbits of modern neatoness */
 #define BASE64
diff --git a/session.h b/session.h
index 145f695f87b2971dde4e3a9a9952fe339efafd51..6939536b588870335bb1f77c0fcd8b41ec57399d 100644
--- a/session.h
+++ b/session.h
@@ -86,7 +86,7 @@ struct key_context {
 	struct key_context_directional recv;
 	struct key_context_directional trans;
 
-	int algo_kex;
+	const struct dropbear_kex *algo_kex;
 	int algo_hostkey;
 
 	int allow_compress; /* whether compression has started (useful in 
diff --git a/sysoptions.h b/sysoptions.h
index 20c2e5738a4e8e7f3c0c64a6eeb29e1d82982879..8fa3ff02d47c7bb53b61aeeb08e6485161aed82a 100644
--- a/sysoptions.h
+++ b/sysoptions.h
@@ -93,6 +93,13 @@
 #define DROPBEAR_ECC
 #endif
 
+
+#ifdef DROPBEAR_ECC
+#define DROPBEAR_ECC_256
+#define DROPBEAR_ECC_384
+#define DROPBEAR_ECC_521
+#endif
+
 // roughly 2x 521 bits
 #define MAX_ECC_SIZE 140