diff common-kex.c @ 910:89555751c489 asm

merge up to 2013.63, improve ASM makefile rules a bit
author Matt Johnston <matt@ucc.asn.au>
date Thu, 27 Feb 2014 21:35:58 +0800
parents 4a74c58e11fc
children 7cd89d4e0335
line wrap: on
line diff
--- a/common-kex.c	Sun Oct 06 22:32:03 2013 +0800
+++ b/common-kex.c	Thu Feb 27 21:35:58 2014 +0800
@@ -32,12 +32,13 @@
 #include "ssh.h"
 #include "packet.h"
 #include "bignum.h"
-#include "random.h"
+#include "dbrandom.h"
 #include "runopts.h"
+#include "ecc.h"
+#include "crypto_desc.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 +52,7 @@
 	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,
@@ -87,8 +87,9 @@
 #endif
 static void read_kex_algos();
 /* helper function for gen_new_keys */
-static void hashkeys(unsigned char *out, int outlen, 
-		const hash_state * hs, unsigned const char X);
+static void hashkeys(unsigned char *out, unsigned int outlen, 
+		const hash_state * hs, const unsigned char X);
+static void finish_kexhashbuf(void);
 
 
 /* Send our list of algorithms we can use */
@@ -150,7 +151,7 @@
 	ses.newkeys = (struct key_context*)m_malloc(sizeof(struct key_context));
 
 	if (ses.send_kex_first_guess) {
-		ses.newkeys->algo_kex = sshkex[0].val;
+		ses.newkeys->algo_kex = sshkex[0].data;
 		ses.newkeys->algo_hostkey = sshhostkey[0].val;
 		ses.send_kex_first_guess();
 	}
@@ -279,27 +280,30 @@
  * out must have at least min(SHA1_HASH_SIZE, outlen) bytes allocated.
  *
  * See Section 7.2 of rfc4253 (ssh transport) for details */
-static void hashkeys(unsigned char *out, int outlen, 
+static void hashkeys(unsigned char *out, unsigned int outlen, 
 		const hash_state * hs, const unsigned char X) {
 
+	const struct ltc_hash_descriptor *hash_desc = ses.newkeys->algo_kex->hash_desc;
 	hash_state hs2;
-	int offset;
+	unsigned int offset;
+	unsigned char tmpout[MAX_HASH_SIZE];
 
 	memcpy(&hs2, hs, sizeof(hash_state));
-	sha1_process(&hs2, &X, 1);
-	sha1_process(&hs2, ses.session_id, SHA1_HASH_SIZE);
-	sha1_done(&hs2, out);
-	for (offset = SHA1_HASH_SIZE; 
+	hash_desc->process(&hs2, &X, 1);
+	hash_desc->process(&hs2, ses.session_id->data, ses.session_id->len);
+	hash_desc->done(&hs2, tmpout);
+	memcpy(out, tmpout, MIN(hash_desc->hashsize, outlen));
+	for (offset = hash_desc->hashsize; 
 			offset < outlen; 
-			offset += SHA1_HASH_SIZE)
+			offset += hash_desc->hashsize)
 	{
 		/* need to extend */
-		unsigned char k2[SHA1_HASH_SIZE];
 		memcpy(&hs2, hs, sizeof(hash_state));
-		sha1_process(&hs2, out, offset);
-		sha1_done(&hs2, k2);
-		memcpy(&out[offset], k2, MIN(outlen - offset, SHA1_HASH_SIZE));
+		hash_desc->process(&hs2, out, offset);
+		hash_desc->done(&hs2, tmpout);
+		memcpy(&out[offset], tmpout, MIN(outlen - offset, hash_desc->hashsize));
 	}
+
 }
 
 /* Generate the actual encryption/integrity keys, using the results of the
@@ -319,26 +323,26 @@
 	unsigned char *trans_IV, *trans_key, *recv_IV, *recv_key;
 
 	hash_state hs;
-	unsigned int C2S_keysize, S2C_keysize;
+	const struct ltc_hash_descriptor *hash_desc = ses.newkeys->algo_kex->hash_desc;
 	char mactransletter, macrecvletter; /* Client or server specific */
 
 	TRACE(("enter gen_new_keys"))
 	/* the dh_K and hash are the start of all hashes, we make use of that */
 
-	sha1_init(&hs);
-	sha1_process_mp(&hs, ses.dh_K);
+	hash_desc->init(&hs);
+	hash_process_mp(hash_desc, &hs, ses.dh_K);
 	mp_clear(ses.dh_K);
 	m_free(ses.dh_K);
-	sha1_process(&hs, ses.hash, SHA1_HASH_SIZE);
-	m_burn(ses.hash, SHA1_HASH_SIZE);
+	hash_desc->process(&hs, ses.hash->data, ses.hash->len);
+	buf_burn(ses.hash);
+	buf_free(ses.hash);
+	ses.hash = NULL;
 
 	if (IS_DROPBEAR_CLIENT) {
 	    trans_IV	= C2S_IV;
 	    recv_IV		= S2C_IV;
 	    trans_key	= C2S_key;
 	    recv_key	= S2C_key;
-	    C2S_keysize = ses.newkeys->trans.algo_crypt->keysize;
-	    S2C_keysize = ses.newkeys->recv.algo_crypt->keysize;
 		mactransletter = 'E';
 		macrecvletter = 'F';
 	} else {
@@ -346,16 +350,14 @@
 	    recv_IV		= C2S_IV;
 	    trans_key	= S2C_key;
 	    recv_key	= C2S_key;
-	    C2S_keysize = ses.newkeys->recv.algo_crypt->keysize;
-	    S2C_keysize = ses.newkeys->trans.algo_crypt->keysize;
 		mactransletter = 'F';
 		macrecvletter = 'E';
 	}
 
-	hashkeys(C2S_IV, SHA1_HASH_SIZE, &hs, 'A');
-	hashkeys(S2C_IV, SHA1_HASH_SIZE, &hs, 'B');
-	hashkeys(C2S_key, C2S_keysize, &hs, 'C');
-	hashkeys(S2C_key, S2C_keysize, &hs, 'D');
+	hashkeys(C2S_IV, sizeof(C2S_IV), &hs, 'A');
+	hashkeys(S2C_IV, sizeof(S2C_IV), &hs, 'B');
+	hashkeys(C2S_key, sizeof(C2S_key), &hs, 'C');
+	hashkeys(S2C_key, sizeof(S2C_key), &hs, 'D');
 
 	if (ses.newkeys->recv.algo_crypt->cipherdesc != NULL) {
 		int recv_cipher = find_cipher(ses.newkeys->recv.algo_crypt->cipherdesc->name);
@@ -381,16 +383,16 @@
 		}
 	}
 
-	if (ses.newkeys->trans.algo_mac->hashdesc != NULL) {
+	if (ses.newkeys->trans.algo_mac->hash_desc != NULL) {
 		hashkeys(ses.newkeys->trans.mackey, 
 				ses.newkeys->trans.algo_mac->keysize, &hs, mactransletter);
-		ses.newkeys->trans.hash_index = find_hash(ses.newkeys->trans.algo_mac->hashdesc->name);
+		ses.newkeys->trans.hash_index = find_hash(ses.newkeys->trans.algo_mac->hash_desc->name);
 	}
 
-	if (ses.newkeys->recv.algo_mac->hashdesc != NULL) {
+	if (ses.newkeys->recv.algo_mac->hash_desc != NULL) {
 		hashkeys(ses.newkeys->recv.mackey, 
 				ses.newkeys->recv.algo_mac->keysize, &hs, macrecvletter);
-		ses.newkeys->recv.hash_index = find_hash(ses.newkeys->recv.algo_mac->hashdesc->name);
+		ses.newkeys->recv.hash_index = find_hash(ses.newkeys->recv.algo_mac->hash_desc->name);
 	}
 
 	/* Ready to switch over */
@@ -481,9 +483,6 @@
  * and we calculate the first portion of the key-exchange-hash for used
  * later in the key exchange. No response is sent, as the client should
  * initiate the diffie-hellman key exchange */
-
-/* Originally from kex.c, generalized for cli/svr mode --mihnea  */
-/* Belongs in common_kex.c where it should be moved after review */
 void recv_msg_kexinit() {
 	
 	unsigned int kexhashbuf_len = 0;
@@ -526,7 +525,7 @@
 		/* I_S, the payload of the server's SSH_MSG_KEXINIT */
 	    buf_setpos(ses.payload, 0);
 	    buf_putstring(ses.kexhashbuf, ses.payload->data, ses.payload->len);
-
+		ses.requirenext = SSH_MSG_KEXDH_REPLY;
 	} else {
 		/* SERVER */
 
@@ -546,7 +545,7 @@
 	    buf_putstring(ses.kexhashbuf,
 			ses.transkexinit->data, ses.transkexinit->len);
 
-		ses.requirenext[0] = SSH_MSG_KEXDH_INIT;
+		ses.requirenext = SSH_MSG_KEXDH_INIT;
 	}
 
 	buf_free(ses.transkexinit);
@@ -560,28 +559,24 @@
 
 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.
  * See the transport rfc 4253 section 8 for details */
 /* dh_pub and dh_priv MUST be already initialised */
-void gen_kexdh_vals(mp_int *dh_pub, mp_int *dh_priv) {
+struct kex_dh_param *gen_kexdh_param() {
+	struct kex_dh_param *param = NULL;
 
 	DEF_MP_INT(dh_p);
 	DEF_MP_INT(dh_q);
 	DEF_MP_INT(dh_g);
 
 	TRACE(("enter gen_kexdh_vals"))
-	
-	m_mp_init_multi(&dh_g, &dh_p, &dh_q, NULL);
+
+	param = m_malloc(sizeof(*param));
+	m_mp_init_multi(&param->pub, &param->priv, &dh_g, &dh_p, &dh_q, NULL);
 
 	/* read the prime and generator*/
 	load_dh_p(&dh_p);
@@ -592,33 +587,39 @@
 
 	/* calculate q = (p-1)/2 */
 	/* dh_priv is just a temp var here */
-	if (mp_sub_d(&dh_p, 1, dh_priv) != MP_OKAY) { 
+	if (mp_sub_d(&dh_p, 1, &param->priv) != MP_OKAY) { 
 		dropbear_exit("Diffie-Hellman error");
 	}
-	if (mp_div_2(dh_priv, &dh_q) != MP_OKAY) {
+	if (mp_div_2(&param->priv, &dh_q) != MP_OKAY) {
 		dropbear_exit("Diffie-Hellman error");
 	}
 
 	/* Generate a private portion 0 < dh_priv < dh_q */
-	gen_random_mpint(&dh_q, dh_priv);
+	gen_random_mpint(&dh_q, &param->priv);
 
 	/* f = g^y mod p */
-	if (mp_exptmod(&dh_g, dh_priv, &dh_p, dh_pub) != MP_OKAY) {
+	if (mp_exptmod(&dh_g, &param->priv, &dh_p, &param->pub) != MP_OKAY) {
 		dropbear_exit("Diffie-Hellman error");
 	}
 	mp_clear_multi(&dh_g, &dh_p, &dh_q, NULL);
+	return param;
+}
+
+void free_kexdh_param(struct kex_dh_param *param)
+{
+	mp_clear_multi(&param->pub, &param->priv, NULL);
+	m_free(param);
 }
 
 /* This function is fairly common between client/server, with some substitution
  * of dh_e/dh_f etc. Hence these arguments:
  * dh_pub_us is 'e' for the client, 'f' for the server. dh_pub_them is 
  * vice-versa. dh_priv is the x/y value corresponding to dh_pub_us */
-void kexdh_comb_key(mp_int *dh_pub_us, mp_int *dh_priv, mp_int *dh_pub_them,
+void kexdh_comb_key(struct kex_dh_param *param, mp_int *dh_pub_them,
 		sign_key *hostkey) {
 
 	mp_int dh_p;
 	mp_int *dh_e = NULL, *dh_f = NULL;
-	hash_state hs;
 
 	/* read the prime and generator*/
 	m_mp_init(&dh_p);
@@ -631,9 +632,8 @@
 	}
 	
 	/* 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, dh_priv, &dh_p, ses.dh_K) != MP_OKAY) {
+	m_mp_alloc_init_multi(&ses.dh_K, NULL);
+	if (mp_exptmod(dh_pub_them, &param->priv, &dh_p, ses.dh_K) != MP_OKAY) {
 		dropbear_exit("Diffie-Hellman error");
 	}
 
@@ -643,11 +643,11 @@
 	/* 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 = dh_pub_us;
+		dh_e = &param->pub;
 		dh_f = dh_pub_them;
 	} else {
 		dh_e = dh_pub_them;
-		dh_f = dh_pub_us;
+		dh_f = &param->pub;
 	} 
 
 	/* Create the remainder of the hash buffer, to generate the exchange hash */
@@ -661,11 +661,140 @@
 	buf_putmpint(ses.kexhashbuf, ses.dh_K);
 
 	/* calculate the hash H to sign */
-	sha1_init(&hs);
+	finish_kexhashbuf();
+}
+
+#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, ses.newkeys->algo_kex->ecc_curve->dp) != 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 kexecdh_comb_key(struct kex_ecdh_param *param, buffer *pub_them,
+		sign_key *hostkey) {
+	const struct dropbear_kex *algo_kex = ses.newkeys->algo_kex;
+	/* public keys from client and server */
+	ecc_key *Q_C, *Q_S, *Q_them;
+
+	Q_them = buf_get_ecc_raw_pubkey(pub_them, algo_kex->ecc_curve);
+
+	ses.dh_K = dropbear_ecc_shared_secret(Q_them, &param->key);
+
+	/* Create the remainder of the hash buffer, to generate the exchange hash
+	   See RFC5656 section 4 page 7 */
+	if (IS_DROPBEAR_CLIENT) {
+		Q_C = &param->key;
+		Q_S = Q_them;
+	} else {
+		Q_C = Q_them;
+		Q_S = &param->key;
+	} 
+
+	/* K_S, the host key */
+	buf_put_pub_key(ses.kexhashbuf, hostkey, ses.newkeys->algo_hostkey);
+	/* Q_C, client's ephemeral public key octet string */
+	buf_put_ecc_raw_pubkey_string(ses.kexhashbuf, Q_C);
+	/* Q_S, server's ephemeral public key octet string */
+	buf_put_ecc_raw_pubkey_string(ses.kexhashbuf, Q_S);
+	/* K, the shared secret */
+	buf_putmpint(ses.kexhashbuf, ses.dh_K);
+
+	/* calculate the hash H to sign */
+	finish_kexhashbuf();
+}
+#endif /* DROPBEAR_ECDH */
+
+#ifdef DROPBEAR_CURVE25519
+struct kex_curve25519_param *gen_kexcurve25519_param () {
+	/* Per http://cr.yp.to/ecdh.html */
+	struct kex_curve25519_param *param = m_malloc(sizeof(*param));
+	const unsigned char basepoint[32] = {9};
+
+	genrandom(param->priv, CURVE25519_LEN);
+	param->priv[0] &= 248;
+	param->priv[31] &= 127;
+	param->priv[31] |= 64;
+
+	curve25519_donna(param->pub, param->priv, basepoint);
+
+	return param;
+}
+
+void free_kexcurve25519_param(struct kex_curve25519_param *param)
+{
+	m_burn(param->priv, CURVE25519_LEN);
+	m_free(param);
+}
+
+void kexcurve25519_comb_key(struct kex_curve25519_param *param, buffer *buf_pub_them,
+	sign_key *hostkey) {
+	unsigned char out[CURVE25519_LEN];
+	const unsigned char* Q_C = NULL;
+	const unsigned char* Q_S = NULL;
+
+	if (buf_pub_them->len != CURVE25519_LEN)
+	{
+		dropbear_exit("Bad curve25519");
+	}
+
+	curve25519_donna(out, param->priv, buf_pub_them->data);
+	m_mp_alloc_init_multi(&ses.dh_K, NULL);
+	bytes_to_mp(ses.dh_K, out, CURVE25519_LEN);
+	m_burn(out, sizeof(out));
+
+	/* Create the remainder of the hash buffer, to generate the exchange hash.
+	   See RFC5656 section 4 page 7 */
+	if (IS_DROPBEAR_CLIENT) {
+		Q_C = param->pub;
+		Q_S = buf_pub_them->data;
+	} else {
+		Q_S = param->pub;
+		Q_C = buf_pub_them->data;
+	}
+
+	/* K_S, the host key */
+	buf_put_pub_key(ses.kexhashbuf, hostkey, ses.newkeys->algo_hostkey);
+	/* Q_C, client's ephemeral public key octet string */
+	buf_putstring(ses.kexhashbuf, Q_C, CURVE25519_LEN);
+	/* Q_S, server's ephemeral public key octet string */
+	buf_putstring(ses.kexhashbuf, Q_S, CURVE25519_LEN);
+	/* K, the shared secret */
+	buf_putmpint(ses.kexhashbuf, ses.dh_K);
+
+	/* calculate the hash H to sign */
+	finish_kexhashbuf();
+}
+#endif /* DROPBEAR_CURVE25519 */
+
+
+
+static void finish_kexhashbuf(void) {
+	hash_state hs;
+	const struct ltc_hash_descriptor *hash_desc = ses.newkeys->algo_kex->hash_desc;
+
+	hash_desc->init(&hs);
 	buf_setpos(ses.kexhashbuf, 0);
-	sha1_process(&hs, buf_getptr(ses.kexhashbuf, ses.kexhashbuf->len),
+	hash_desc->process(&hs, buf_getptr(ses.kexhashbuf, ses.kexhashbuf->len),
 			ses.kexhashbuf->len);
-	sha1_done(&hs, ses.hash);
+	ses.hash = buf_new(hash_desc->hashsize);
+	hash_desc->done(&hs, buf_getwriteptr(ses.hash, hash_desc->hashsize));
+	buf_setlen(ses.hash, hash_desc->hashsize);
+
+#if defined(DEBUG_KEXHASH) && defined(DEBUG_TRACE)
+	if (!debug_trace) {
+		printhex("kexhashbuf", ses.kexhashbuf->data, ses.kexhashbuf->len);
+		printhex("kexhash", ses.hash->data, ses.hash->len);
+	}
+#endif
 
 	buf_burn(ses.kexhashbuf);
 	buf_free(ses.kexhashbuf);
@@ -674,9 +803,9 @@
 	/* 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);
+		ses.session_id = buf_newcopy(ses.hash);
 	}
+
 }
 
 /* read the other side's algo list. buf_match_algo is a callback to match
@@ -700,16 +829,16 @@
 	int allgood = 1; /* we AND this with each goodguess and see if its still
 						true after */
 
-	buf_incrpos(ses.payload, 16); /* start after the cookie */
-
-	memset(ses.newkeys, 0x0, sizeof(*ses.newkeys));
-
 #ifdef USE_KEXGUESS2
 	enum kexguess2_used kexguess2 = KEXGUESS2_LOOK;
 #else
 	enum kexguess2_used kexguess2 = KEXGUESS2_NO;
 #endif
 
+	buf_incrpos(ses.payload, 16); /* start after the cookie */
+
+	memset(ses.newkeys, 0x0, sizeof(*ses.newkeys));
+
 	/* kex_algorithms */
 	algo = buf_match_algo(ses.payload, sshkex, &kexguess2, &goodguess);
 	allgood &= goodguess;
@@ -719,7 +848,7 @@
 	}
 	TRACE(("kexguess2 %d", kexguess2))
 	TRACE(("kex algo %s", algo->name))
-	ses.newkeys->algo_kex = algo->val;
+	ses.newkeys->algo_kex = algo->data;
 
 	/* server_host_key_algorithms */
 	algo = buf_match_algo(ses.payload, sshhostkey, &kexguess2, &goodguess);