summaryrefslogtreecommitdiff
path: root/common-kex.c
diff options
context:
space:
mode:
Diffstat (limited to 'common-kex.c')
-rw-r--r--common-kex.c186
1 files changed, 123 insertions, 63 deletions
diff --git a/common-kex.c b/common-kex.c
index a488877..a32ca6d 100644
--- a/common-kex.c
+++ b/common-kex.c
@@ -34,10 +34,11 @@
#include "bignum.h"
#include "random.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 @@ 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,
@@ -87,8 +87,9 @@ static void gen_new_zstream_trans();
#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 @@ void send_msg_kexinit() {
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,26 +280,28 @@ static void kexinitialise() {
* 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[hash_desc->hashsize];
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));
}
}
@@ -319,26 +322,26 @@ static void gen_new_keys() {
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 +349,14 @@ static void gen_new_keys() {
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 +382,16 @@ static void gen_new_keys() {
}
}
- 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 */
@@ -560,28 +561,23 @@ 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.
* 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() {
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);
+
+ struct kex_dh_param *param = m_malloc(sizeof(*param));
+ m_mp_init_multi(&param->pub, &param->priv, NULL);
/* read the prime and generator*/
load_dh_p(&dh_p);
@@ -592,33 +588,39 @@ void gen_kexdh_vals(mp_int *dh_pub, mp_int *dh_priv) {
/* 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 +633,8 @@ void kexdh_comb_key(mp_int *dh_pub_us, mp_int *dh_priv, mp_int *dh_pub_them,
}
/* 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 +644,11 @@ void kexdh_comb_key(mp_int *dh_pub_us, mp_int *dh_priv, mp_int *dh_pub_them,
/* 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 +662,70 @@ void kexdh_comb_key(mp_int *dh_pub_us, mp_int *dh_priv, mp_int *dh_pub_them,
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);
+
+ /* 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) {
+ Q_C = &param->key;
+ Q_S = Q_them;
+ } else {
+ Q_C = Q_them;
+ Q_S = &param->key;
+ }
+
+ /* 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);
+ /* 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
+
+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);
buf_burn(ses.kexhashbuf);
buf_free(ses.kexhashbuf);
@@ -674,9 +734,9 @@ void kexdh_comb_key(mp_int *dh_pub_us, mp_int *dh_priv, mp_int *dh_pub_them,
/* 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
@@ -719,7 +779,7 @@ static void read_kex_algos() {
}
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);