Commit 73e22c11 authored by Matt Johnston's avatar Matt Johnston
Browse files

refactor kexdh code a bit, start working on ecdh etc

--HG--
branch : ecc
parent 9be0d6b5
......@@ -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
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 \
......
......@@ -94,5 +94,23 @@ int check_user_algos(const char* user_algo_list, algo_type * algos,
char * algolist_string(algo_type algos[]);
#endif
enum {
DROPBEAR_KEX_DH_GROUP1,
DROPBEAR_KEX_DH_GROUP14,
DROPBEAR_KEX_ECDH_SECP256R1,
};
#ifdef DROPBEAR_ECDH
#define IS_NORMAL_DH(algo) ((algo) == DROPBEAR_KEX_DH_GROUP1 || (algo) == DROPBEAR_KEX_DH_GROUP14)
#else
#define IS_NORMAL_DH(algo) 1
#endif
enum {
DROPBEAR_COMP_NONE,
DROPBEAR_COMP_ZLIB,
DROPBEAR_COMP_ZLIB_DELAY,
};
#endif /* _ALGO_H_ */
......@@ -42,16 +42,16 @@ static void checkhostkey(unsigned char* keyblob, unsigned int keybloblen);
#define MAX_KNOWNHOSTS_LINE 4500
void send_msg_kexdh_init() {
cli_ses.dh_e = (mp_int*)m_malloc(sizeof(mp_int));
cli_ses.dh_x = (mp_int*)m_malloc(sizeof(mp_int));
m_mp_init_multi(cli_ses.dh_e, cli_ses.dh_x, NULL);
gen_kexdh_vals(cli_ses.dh_e, cli_ses.dh_x);
CHECKCLEARTOWRITE();
buf_putbyte(ses.writepayload, SSH_MSG_KEXDH_INIT);
buf_putmpint(ses.writepayload, cli_ses.dh_e);
if (IS_NORMAL_DH(ses.newkeys->algo_kex)) {
cli_ses.dh_param = gen_kexdh_param();
buf_putmpint(ses.writepayload, &cli_ses.dh_param->pub);
} else {
#ifdef DROPBEAR_ECDH
cli_ses.ecdh_param =
#endif
}
encrypt_packet();
ses.requirenext = SSH_MSG_KEXDH_REPLY;
}
......@@ -59,18 +59,15 @@ void send_msg_kexdh_init() {
/* Handle a diffie-hellman key exchange reply. */
void recv_msg_kexdh_reply() {
DEF_MP_INT(dh_f);
sign_key *hostkey = NULL;
unsigned int type, keybloblen;
unsigned char* keyblob = NULL;
TRACE(("enter recv_msg_kexdh_reply"))
if (cli_ses.kex_state != KEXDH_INIT_SENT) {
dropbear_exit("Received out-of-order kexdhreply");
}
m_mp_init(&dh_f);
type = ses.newkeys->algo_hostkey;
TRACE(("type is %d", type))
......@@ -88,16 +85,23 @@ void recv_msg_kexdh_reply() {
dropbear_exit("Bad KEX packet");
}
if (buf_getmpint(ses.payload, &dh_f) != DROPBEAR_SUCCESS) {
TRACE(("failed getting mpint"))
dropbear_exit("Bad KEX packet");
}
if (IS_NORMAL_DH(ses.newkeys->algo_kex)) {
// Normal diffie-hellman
DEF_MP_INT(dh_f);
m_mp_init(&dh_f);
if (buf_getmpint(ses.payload, &dh_f) != DROPBEAR_SUCCESS) {
TRACE(("failed getting mpint"))
dropbear_exit("Bad KEX packet");
}
kexdh_comb_key(cli_ses.dh_e, cli_ses.dh_x, &dh_f, hostkey);
mp_clear(&dh_f);
mp_clear_multi(cli_ses.dh_e, cli_ses.dh_x, NULL);
m_free(cli_ses.dh_e);
m_free(cli_ses.dh_x);
kexdh_comb_key(cli_ses.dh_param, &dh_f, hostkey);
mp_clear(&dh_f);
free_kexdh_param(cli_ses.dh_param);
cli_ses.dh_param = NULL;
} else {
#ifdef DROPBEAR_ECDH
#endif
}
if (buf_verify(ses.payload, hostkey, ses.hash, SHA1_HASH_SIZE)
!= DROPBEAR_SUCCESS) {
......
......@@ -213,6 +213,7 @@ algo_type sshhostkey[] = {
};
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},
{NULL, 0, NULL, 0, NULL}
......
......@@ -549,15 +549,16 @@ static void load_dh_p(mp_int * dh_p)
/* 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 send_msg_kexdh_reply"))
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);
......@@ -568,32 +569,40 @@ 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*/
......@@ -609,7 +618,7 @@ 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) {
if (mp_exptmod(dh_pub_them, &param->priv, &dh_p, ses.dh_K) != MP_OKAY) {
dropbear_exit("Diffie-Hellman error");
}
......@@ -619,11 +628,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 */
......@@ -655,6 +664,16 @@ void kexdh_comb_key(mp_int *dh_pub_us, mp_int *dh_priv, 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
}
void free_kexecdh_param(struct kex_ecdh_param *param);
void kexecdh_comb_key(struct kex_ecdh_param *param, buffer *pub_them,
sign_key *hostkey);
#endif
/* read the other side's algo list. buf_match_algo is a callback to match
* algos for the client or server. */
static void read_kex_algos() {
......
......@@ -685,7 +685,7 @@ AS_MKDIR_P(libtomcrypt/src/pk/dsa)
AS_MKDIR_P(libtomcrypt/src/pk/ecc)
AS_MKDIR_P(libtomcrypt/src/pk/pkcs1)
AS_MKDIR_P(libtomcrypt/src/pk/rsa)
AS_MKDIR_P(libtomcrypt/src/prng)
AS_MKDIR_P(libtomcrypt/src/prngs)
AC_CONFIG_HEADER(config.h)
AC_OUTPUT(Makefile)
AC_OUTPUT(libtomcrypt/Makefile)
......
#ifdef DROPBEAR_ECC
void buf_put_ecc_key_string(buffer *buf, ecc_key *key) {
int len = key->dp->size*2 + 1;
buf_putint(len);
int err = ecc_ansi_x963_export(key, buf_getwriteptr(buf, len), &len);
if (err != CRYPT_OK) {
dropbear_exit("ECC error");
}
buf_incrwritepos(buf, len);
}
int buf_get_ecc_key_string(buffer *buf, ecc_key *key) {
}
#endif
......@@ -33,10 +33,19 @@ void recv_msg_kexinit();
void send_msg_newkeys();
void recv_msg_newkeys();
void kexfirstinitialise();
void gen_kexdh_vals(mp_int *dh_pub, mp_int *dh_priv);
void kexdh_comb_key(mp_int *dh_pub_us, mp_int *dh_priv, mp_int *dh_pub_them,
struct kex_dh_param *gen_kexdh_param();
void free_kexdh_param(struct kex_dh_param *param);
void kexdh_comb_key(struct kex_dh_param *param, mp_int *dh_pub_them,
sign_key *hostkey);
#ifdef DROPBEAR_ECDH
struct kex_ecdh_param *gen_kexecdh_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);
#endif
#ifndef DISABLE_ZLIB
int is_compress_trans();
int is_compress_recv();
......@@ -64,6 +73,17 @@ struct KEXState {
};
struct kex_dh_param {
mp_int pub;
mp_int priv;
};
#ifdef DROPBEAR_ECDH
struct kex_ecdh_param {
ecc_key key;
};
#endif
#define MAX_KEXHASHBUF 2000
#endif /* _KEX_H_ */
......@@ -134,6 +134,13 @@
#define LTC_HMAC
#ifdef DROPBEAR_ECDH
#define MECC
#define ECC256
#define ECC384
#define ECC521
#endif
/* Various tidbits of modern neatoness */
#define BASE64
......
/* Copied from libtomcrypt/src/prngs/sprng.c and modified to
* use Dropbear's genrandom(). */
/* LibTomCrypt, modular cryptographic library -- Tom St Denis
*
* LibTomCrypt is a library that provides various cryptographic
* algorithms in a highly modular and flexible manner.
*
* The library is free for all purposes without any express
* guarantee it works.
*
* Tom St Denis, [email protected], http://libtomcrypt.com
*/
#include "options.h"
#include "includes.h"
#include "random.h"
/**
@file sprng.c
Secure PRNG, Tom St Denis
*/
/* A secure PRNG using the RNG functions. Basically this is a
* wrapper that allows you to use a secure RNG as a PRNG
* in the various other functions.
*/
#ifdef DROPBEAR_LTC_PRNG
/**
Start the PRNG
@param prng [out] The PRNG state to initialize
@return CRYPT_OK if successful
*/
int dropbear_prng_start(prng_state *prng)
{
return CRYPT_OK;
}
/**
Add entropy to the PRNG state
@param in The data to add
@param inlen Length of the data to add
@param prng PRNG state to update
@return CRYPT_OK if successful
*/
int dropbear_prng_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng)
{
return CRYPT_OK;
}
/**
Make the PRNG ready to read from
@param prng The PRNG to make active
@return CRYPT_OK if successful
*/
int dropbear_prng_ready(prng_state *prng)
{
return CRYPT_OK;
}
/**
Read from the PRNG
@param out Destination
@param outlen Length of output
@param prng The active PRNG to read from
@return Number of octets read
*/
unsigned long dropbear_prng_read(unsigned char *out, unsigned long outlen, prng_state *prng)
{
LTC_ARGCHK(out != NULL);
genrandom(out, outlen);
return CRYPT_OK;
}
/**
Terminate the PRNG
@param prng The PRNG to terminate
@return CRYPT_OK if successful
*/
int dropbear_prng_done(prng_state *prng)
{
return CRYPT_OK;
}
/**
Export the PRNG state
@param out [out] Destination
@param outlen [in/out] Max size and resulting size of the state
@param prng The PRNG to export
@return CRYPT_OK if successful
*/
int dropbear_prng_export(unsigned char *out, unsigned long *outlen, prng_state *prng)
{
LTC_ARGCHK(outlen != NULL);
*outlen = 0;
return CRYPT_OK;
}
/**
Import a PRNG state
@param in The PRNG state
@param inlen Size of the state
@param prng The PRNG to import
@return CRYPT_OK if successful
*/
int dropbear_prng_import(const unsigned char *in, unsigned long inlen, prng_state *prng)
{
return CRYPT_OK;
}
/**
PRNG self-test
@return CRYPT_OK if successful, CRYPT_NOP if self-testing has been disabled
*/
int dropbear_prng_test(void)
{
return CRYPT_OK;
}
const struct ltc_prng_descriptor dropbear_prng_desc =
{
"dropbear_prng", 0,
&dropbear_prng_start,
&dropbear_prng_add_entropy,
&dropbear_prng_ready,
&dropbear_prng_read,
&dropbear_prng_done,
&dropbear_prng_export,
&dropbear_prng_import,
&dropbear_prng_test
};
#endif // DROPBEAR_LTC_PRNG
......@@ -136,6 +136,8 @@ much traffic. */
#define DROPBEAR_RSA
#define DROPBEAR_DSS
#define DROPBEAR_ECDH
/* RSA can be vulnerable to timing attacks which use the time required for
* signing to guess the private key. Blinding avoids this attack, though makes
* signing operations slightly slower. */
......
......@@ -36,6 +36,8 @@ static uint32_t counter = 0;
static unsigned char hashpool[SHA1_HASH_SIZE] = {0};
static int donerandinit = 0;
int dropbear_ltc_prng = -1;
#define INIT_SEED_SIZE 32 /* 256 bits */
/* The basic setup is we read some data from /dev/(u)random or prngd and hash it
......@@ -231,6 +233,13 @@ void seedrandom() {
sha1_done(&hs, hashpool);
#ifdef DROPBEAR_LTC_PRNG
if (dropbear_ltc_prng == -1) {
dropbear_ltc_prng = register_prng(&dropbear_prng_desc);
dropbear_assert(dropbear_ltc_prng != -1);
}
#endif
counter = 0;
donerandinit = 1;
......
......@@ -32,4 +32,6 @@ void genrandom(unsigned char* buf, unsigned int len);
void addrandom(char * buf, unsigned int len);
void gen_random_mpint(mp_int *max, mp_int *rand);
extern int dropbear_ltc_prng;
#endif /* _RANDOM_H_ */
......@@ -67,7 +67,7 @@ struct key_context_directional {
const struct dropbear_cipher_mode *crypt_mode;
const struct dropbear_hash *algo_mac;
int hash_index; /* lookup for libtomcrypt */
char algo_comp; /* compression */
int algo_comp; /* compression */
#ifndef DISABLE_ZLIB
z_streamp zstream;
#endif
......@@ -86,8 +86,8 @@ struct key_context {
struct key_context_directional recv;
struct key_context_directional trans;
char algo_kex;
char algo_hostkey;
int algo_kex;
int algo_hostkey;
int allow_compress; /* whether compression has started (useful in
[email protected] delayed compression case) */
......@@ -244,8 +244,8 @@ typedef enum {
} cli_state;
struct clientsession {
mp_int *dh_e, *dh_x; /* Used during KEX */
struct kex_dh_param *dh_param;
struct kex_ecdh_param *ecdh_param;
cli_kex_state kex_state; /* Used for progressing KEX */
cli_state state; /* Used to progress auth/channelsession etc */
unsigned donefirstkex : 1; /* Set when we set sentnewkeys, never reset */
......
......@@ -36,7 +36,7 @@
#include "runopts.h"
static void send_msg_kexdh_reply(mp_int *dh_e);
static void send_msg_kexdh_reply(mp_int *dh_e, buffer *ecdh_qs);
/* Handle a diffie-hellman key exchange initialisation. This involves
* calculating a session key reply value, and corresponding hash. These
......@@ -45,20 +45,29 @@ static void send_msg_kexdh_reply(mp_int *dh_e);
void recv_msg_kexdh_init() {
DEF_MP_INT(dh_e);
buffer *ecdh_qs = NULL;
TRACE(("enter recv_msg_kexdh_init"))
if (!ses.kexstate.recvkexinit) {
dropbear_exit("Premature kexdh_init message received");
}
m_mp_init(&dh_e);
if (buf_getmpint(ses.payload, &dh_e) != DROPBEAR_SUCCESS) {
dropbear_exit("Failed to get kex value");
if (IS_NORMAL_DH(ses.newkeys->algo_kex)) {
m_mp_init(&dh_e);
if (buf_getmpint(ses.payload, &dh_e) != DROPBEAR_SUCCESS) {
dropbear_exit("Failed to get kex value");
}
} else {
#ifdef DROPBEAR_ECDH
#endif
}
send_msg_kexdh_reply(&dh_e);
send_msg_kexdh_reply(&dh_e, ecdh_qs);
mp_clear(&dh_e);
if (ecdh_qs) {
buf_free(ecdh_qs);
}
send_msg_newkeys();
ses.requirenext = SSH_MSG_NEWKEYS;
......@@ -70,19 +79,10 @@ void recv_msg_kexdh_init() {
* that, the session hash is calculated, and signed with RSA or DSS. The
* result is sent to the client.
*
* See the transport rfc 4253 section 8 for details */
static void send_msg_kexdh_reply(mp_int *dh_e) {
DEF_MP_INT(dh_y);
DEF_MP_INT(dh_f);
* See the transport RFC4253 section 8 for details
* or RFC5656 section 4 for elliptic curve variant. */
static void send_msg_kexdh_reply(mp_int *dh_e, buffer *ecdh_qs) {
TRACE(("enter send_msg_kexdh_reply"))
m_mp_init_multi(&dh_y, &dh_f, NULL);
gen_kexdh_vals(&dh_f, &dh_y);
kexdh_comb_key(&dh_f, &dh_y, dh_e, svr_opts.hostkey);
mp_clear(&dh_y);
/* we can start creating the kexdh_reply packet */
CHECKCLEARTOWRITE();
......@@ -90,9 +90,23 @@ static void send_msg_kexdh_reply(mp_int *dh_e) {
buf_put_pub_key(ses.writepayload, svr_opts.hostkey,
ses.newkeys->algo_hostkey);
/* put f */
buf_putmpint(ses.writepayload, &dh_f);
mp_clear(&dh_f);
if (IS_NORMAL_DH(ses.newkeys->algo_kex)) {
// Normal diffie-hellman
struct kex_dh_param * dh_param = gen_kexdh_param();
kexdh_comb_key(dh_param, dh_e, svr_opts.hostkey);
/* put f */
buf_putmpint(ses.writepayload, &dh_param->pub);
free_kexdh_param(dh_param);
} else {
#ifdef DROPBEAR_ECDH
struct kex_ecdh_param *ecdh_param = gen_kexecdh_param();
kexecdh_comb_key(ecdh_param, ecdh_qs, svr_opts.hostkey);
buf_put_ecc_pub(ses.writepayload, &ecdh_param->key);
free_kexecdh_param(ecdh_param);
#endif
}
/* calc the signature */
buf_put_sign(ses.writepayload, svr_opts.hostkey,
......
......@@ -60,24 +60,20 @@
#define DROPBEAR_SUCCESS 0
#define DROPBEAR_FAILURE -1
/* various algorithm identifiers */
#define DROPBEAR_KEX_DH_GROUP1 0
#define DROPBEAR_KEX_DH_GROUP14 1
#define DROPBEAR_SIGNKEY_ANY 0
#define DROPBEAR_SIGNKEY_RSA 1
#define DROPBEAR_SIGNKEY_DSS 2
#define DROPBEAR_SIGNKEY_NONE 3
#define DROPBEAR_COMP_NONE 0
#define DROPBEAR_COMP_ZLIB 1
#define DROPBEAR_COMP_ZLIB_DELAY 2
/* Required for pubkey auth */
#if defined(ENABLE_SVR_PUBKEY_AUTH) || defined(DROPBEAR_CLIENT)
#define DROPBEAR_SIGNKEY_VERIFY
#endif
#ifdef DROPBEAR_ECDH
#define DROPBEAR_LTC_PRNG
#endif
#define SHA1_HASH_SIZE 20
#define MD5_HASH_SIZE 16
......@@ -93,6 +89,13 @@
#define MAX_MAC_LEN 20
#endif