# HG changeset patch # User Matt Johnston # Date 1382367441 -28800 # Node ID 33207ed1174b704998c60f49fdd7ab5ee0a9ed67 # Parent e378da7eae5d86af73c5396653fe223794b5d0fc# Parent 4095b6d7c9fce985fe87d2d8cde0c77fe0caec37 Merge in ECC diff -r e378da7eae5d -r 33207ed1174b Makefile.in --- a/Makefile.in Wed Oct 16 22:55:03 2013 +0800 +++ b/Makefile.in Mon Oct 21 22:57:21 2013 +0800 @@ -17,16 +17,17 @@ LTM=libtommath/libtommath.a ifeq (@BUNDLED_LIBTOM@, 1) -LIBTOM_DEPS=$(LTC) $(LTM) -CFLAGS+=-I$(srcdir)/libtomcrypt/src/headers/ -LIBS+=$(LTC) $(LTM) +LIBTOM_DEPS=$(LTC) $(LTM) +CFLAGS+=-I$(srcdir)/libtomcrypt/src/headers/ +LIBS+=$(LTC) $(LTM) endif 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 ecdsa.o crypto_desc.o SVROBJS=svr-kex.o svr-auth.o sshpty.o \ svr-authpasswd.o svr-authpubkey.o svr-authpubkeyoptions.o svr-session.o svr-service.o \ @@ -54,7 +55,7 @@ debug.h channel.h chansession.h config.h queue.h sshpty.h \ termcodes.h gendss.h genrsa.h runopts.h includes.h \ loginrec.h atomicio.h x11fwd.h agentfwd.h tcpfwd.h compat.h \ - listener.h fake-rfc2553.h + listener.h fake-rfc2553.h ecc.h ecdsa.h dropbearobjs=$(COMMONOBJS) $(CLISVROBJS) $(SVROBJS) @CRYPTLIB@ dbclientobjs=$(COMMONOBJS) $(CLISVROBJS) $(CLIOBJS) @@ -185,7 +186,7 @@ -ln -s dropbearmulti$(EXEEXT) $*$(EXEEXT) $(LTC): options.h - cd libtomcrypt && $(MAKE) clean && $(MAKE) + cd libtomcrypt && $(MAKE) $(LTM): options.h cd libtommath && $(MAKE) diff -r e378da7eae5d -r 33207ed1174b agentfwd.h --- a/agentfwd.h Wed Oct 16 22:55:03 2013 +0800 +++ b/agentfwd.h Mon Oct 21 22:57:21 2013 +0800 @@ -40,7 +40,7 @@ /* client functions */ void cli_load_agent_keys(m_list * ret_list); void agent_buf_sign(buffer *sigblob, sign_key *key, - const unsigned char *data, unsigned int len); + buffer *data_buf); void cli_setup_agent(struct Channel *channel); #ifdef __hpux diff -r e378da7eae5d -r 33207ed1174b algo.h --- a/algo.h Wed Oct 16 22:55:03 2013 +0800 +++ b/algo.h Mon Oct 21 22:57:21 2013 +0800 @@ -35,7 +35,7 @@ struct Algo_Type { - unsigned char *name; /* identifying name */ + const unsigned char *name; /* identifying name */ char val; /* a value for this cipher, or -1 for invalid */ const void *data; /* algorithm specific data */ char usable; /* whether we can use this algorithm */ @@ -59,8 +59,8 @@ struct dropbear_cipher { const struct ltc_cipher_descriptor *cipherdesc; - unsigned long keysize; - unsigned char blocksize; + const unsigned long keysize; + const unsigned char blocksize; }; struct dropbear_cipher_mode { @@ -74,12 +74,27 @@ }; struct dropbear_hash { - const struct ltc_hash_descriptor *hashdesc; - unsigned long keysize; - unsigned char hashsize; + const struct ltc_hash_descriptor *hash_desc; + const unsigned long keysize; + // hashsize may be truncated from the size returned by hash_desc, + // eg sha1-96 + const unsigned char hashsize; }; -void crypto_init(); +struct dropbear_kex { + // "normal" DH KEX + const unsigned char *dh_p_bytes; + const 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 *hash_desc; +}; + int have_algo(char* algo, size_t algolen, algo_type algos[]); void buf_put_algolist(buffer * buf, algo_type localalgos[]); @@ -102,5 +117,16 @@ char * algolist_string(algo_type algos[]); #endif +#ifdef DROPBEAR_ECDH +#define IS_NORMAL_DH(algo) ((algo)->dh_p_bytes != NULL) +#else +#define IS_NORMAL_DH(algo) 1 +#endif + +enum { + DROPBEAR_COMP_NONE, + DROPBEAR_COMP_ZLIB, + DROPBEAR_COMP_ZLIB_DELAY, +}; #endif /* _ALGO_H_ */ diff -r e378da7eae5d -r 33207ed1174b bignum.c --- a/bignum.c Wed Oct 16 22:55:03 2013 +0800 +++ b/bignum.c Mon Oct 21 22:57:21 2013 +0800 @@ -52,6 +52,22 @@ va_end(args); } +void m_mp_alloc_init_multi(mp_int **mp, ...) +{ + mp_int** cur_arg = mp; + va_list args; + + va_start(args, mp); /* init args to next argument from caller */ + while (cur_arg != NULL) { + *cur_arg = m_malloc(sizeof(mp_int)); + if (mp_init(*cur_arg) != MP_OKAY) { + dropbear_exit("Mem alloc error"); + } + cur_arg = va_arg(args, mp_int**); + } + va_end(args); +} + void bytes_to_mp(mp_int *mp, const unsigned char* bytes, unsigned int len) { if (mp_read_unsigned_bin(mp, (unsigned char*)bytes, len) != MP_OKAY) { @@ -60,7 +76,8 @@ } /* hash the ssh representation of the mp_int mp */ -void sha1_process_mp(hash_state *hs, mp_int *mp) { +void hash_process_mp(const struct ltc_hash_descriptor *hash_desc, + hash_state *hs, mp_int *mp) { int i; buffer * buf; @@ -68,8 +85,6 @@ buf = buf_new(512 + 20); /* max buffer is a 4096 bit key, plus header + some leeway*/ buf_putmpint(buf, mp); - i = buf->pos; - buf_setpos(buf, 0); - sha1_process(hs, buf_getptr(buf, i), i); + hash_desc->process(hs, buf->data, buf->len); buf_free(buf); } diff -r e378da7eae5d -r 33207ed1174b bignum.h --- a/bignum.h Wed Oct 16 22:55:03 2013 +0800 +++ b/bignum.h Mon Oct 21 22:57:21 2013 +0800 @@ -30,7 +30,9 @@ void m_mp_init(mp_int *mp); void m_mp_init_multi(mp_int *mp, ...) ATTRIB_SENTINEL; +void m_mp_alloc_init_multi(mp_int **mp, ...) ATTRIB_SENTINEL; void bytes_to_mp(mp_int *mp, const unsigned char* bytes, unsigned int len); -void sha1_process_mp(hash_state *hs, mp_int *mp); +void hash_process_mp(const struct ltc_hash_descriptor *hash_desc, + hash_state *hs, mp_int *mp); #endif /* _BIGNUM_H_ */ diff -r e378da7eae5d -r 33207ed1174b buffer.c --- a/buffer.c Wed Oct 16 22:55:03 2013 +0800 +++ b/buffer.c Mon Oct 21 22:57:21 2013 +0800 @@ -269,6 +269,11 @@ } +/* puts an entire buffer as a SSH string. ignore pos of buf_str. */ +void buf_putbufstring(buffer *buf, const buffer* buf_str) { + buf_putstring(buf, buf_str->data, buf_str->len); +} + /* put the set of len bytes into the buffer, incrementing the pos, increasing * len if required */ void buf_putbytes(buffer *buf, const unsigned char *bytes, unsigned int len) { diff -r e378da7eae5d -r 33207ed1174b buffer.h --- a/buffer.h Wed Oct 16 22:55:03 2013 +0800 +++ b/buffer.h Mon Oct 21 22:57:21 2013 +0800 @@ -59,6 +59,7 @@ void buf_eatstring(buffer *buf); void buf_putint(buffer* buf, unsigned int val); void buf_putstring(buffer* buf, const unsigned char* str, unsigned int len); +void buf_putbufstring(buffer *buf, const buffer* buf_str); void buf_putbytes(buffer *buf, const unsigned char *bytes, unsigned int len); void buf_putmpint(buffer* buf, mp_int * mp); int buf_getmpint(buffer* buf, mp_int* mp); diff -r e378da7eae5d -r 33207ed1174b cli-agentfwd.c --- a/cli-agentfwd.c Wed Oct 16 22:55:03 2013 +0800 +++ b/cli-agentfwd.c Mon Oct 21 22:57:21 2013 +0800 @@ -254,7 +254,7 @@ } void agent_buf_sign(buffer *sigblob, sign_key *key, - const unsigned char *data, unsigned int len) { + buffer *data_buf) { buffer *request_data = NULL; buffer *response = NULL; unsigned int siglen; @@ -266,10 +266,10 @@ string data uint32 flags */ - request_data = buf_new(MAX_PUBKEY_SIZE + len + 12); + request_data = buf_new(MAX_PUBKEY_SIZE + data_buf->len + 12); buf_put_pub_key(request_data, key, key->type); - buf_putstring(request_data, data, len); + buf_putbufstring(request_data, data_buf); buf_putint(request_data, 0); response = agent_request(SSH2_AGENTC_SIGN_REQUEST, request_data); diff -r e378da7eae5d -r 33207ed1174b cli-authpubkey.c --- a/cli-authpubkey.c Wed Oct 16 22:55:03 2013 +0800 +++ b/cli-authpubkey.c Mon Oct 21 22:57:21 2013 +0800 @@ -121,23 +121,19 @@ } void cli_buf_put_sign(buffer* buf, sign_key *key, int type, - const unsigned char *data, unsigned int len) -{ + buffer *data_buf) { #ifdef ENABLE_CLI_AGENTFWD if (key->source == SIGNKEY_SOURCE_AGENT) { /* Format the agent signature ourselves, as buf_put_sign would. */ buffer *sigblob; sigblob = buf_new(MAX_PUBKEY_SIZE); - agent_buf_sign(sigblob, key, data, len); - buf_setpos(sigblob, 0); - buf_putstring(buf, buf_getptr(sigblob, sigblob->len), - sigblob->len); - + agent_buf_sign(sigblob, key, data_buf); + buf_putbufstring(buf, sigblob); buf_free(sigblob); } else #endif /* ENABLE_CLI_AGENTFWD */ { - buf_put_sign(buf, key, type, data, len); + buf_put_sign(buf, key, type, data_buf); } } @@ -173,10 +169,10 @@ TRACE(("realsign")) /* We put the signature as well - this contains string(session id), then * the contents of the write payload to this point */ - sigbuf = buf_new(4 + SHA1_HASH_SIZE + ses.writepayload->len); - buf_putstring(sigbuf, ses.session_id, SHA1_HASH_SIZE); + sigbuf = buf_new(4 + ses.session_id->len + ses.writepayload->len); + buf_putbufstring(sigbuf, ses.session_id); buf_putbytes(sigbuf, ses.writepayload->data, ses.writepayload->len); - cli_buf_put_sign(ses.writepayload, key, type, sigbuf->data, sigbuf->len); + cli_buf_put_sign(ses.writepayload, key, type, sigbuf); buf_free(sigbuf); /* Nothing confidential in the buffer */ } diff -r e378da7eae5d -r 33207ed1174b cli-chansession.c --- a/cli-chansession.c Wed Oct 16 22:55:03 2013 +0800 +++ b/cli-chansession.c Mon Oct 21 22:57:21 2013 +0800 @@ -455,7 +455,7 @@ } static -void cli_escape_handler(struct Channel *channel, unsigned char* buf, int *len) { +void cli_escape_handler(struct Channel* UNUSED(channel), unsigned char* buf, int *len) { char c; int skip_char = 0; diff -r e378da7eae5d -r 33207ed1174b cli-kex.c --- a/cli-kex.c Wed Oct 16 22:55:03 2013 +0800 +++ b/cli-kex.c Mon Oct 21 22:57:21 2013 +0800 @@ -36,6 +36,7 @@ #include "random.h" #include "runopts.h" #include "signkey.h" +#include "ecc.h" static void checkhostkey(unsigned char* keyblob, unsigned int keybloblen); @@ -43,23 +44,31 @@ void send_msg_kexdh_init() { TRACE(("send_msg_kexdh_init()")) - if ((cli_ses.dh_e && cli_ses.dh_x - && cli_ses.dh_val_algo == ses.newkeys->algo_kex)) { - TRACE(("reusing existing dh_e from first_kex_packet_follows")) - } else { - if (!cli_ses.dh_e || !cli_ses.dh_e) { - 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); - cli_ses.dh_val_algo = ses.newkeys->algo_kex; - } 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)) { + if (ses.newkeys->algo_kex != cli_ses.param_kex_algo + || !cli_ses.dh_param) { + if (cli_ses.dh_param) { + free_kexdh_param(cli_ses.dh_param); + } + cli_ses.dh_param = gen_kexdh_param(); + } + buf_putmpint(ses.writepayload, &cli_ses.dh_param->pub); + } else { +#ifdef DROPBEAR_ECDH + if (ses.newkeys->algo_kex != cli_ses.param_kex_algo + || !cli_ses.ecdh_param) { + if (cli_ses.ecdh_param) { + free_kexecdh_param(cli_ses.ecdh_param); + } + cli_ses.ecdh_param = gen_kexecdh_param(); + } + buf_put_ecc_raw_pubkey_string(ses.writepayload, &cli_ses.ecdh_param->key); +#endif + } + cli_ses.param_kex_algo = ses.newkeys->algo_kex; encrypt_packet(); ses.requirenext[0] = SSH_MSG_KEXDH_REPLY; ses.requirenext[1] = SSH_MSG_KEXINIT; @@ -68,18 +77,15 @@ /* 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)) @@ -97,20 +103,38 @@ 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_param, &dh_f, hostkey); + mp_clear(&dh_f); + } else { +#ifdef DROPBEAR_ECDH + buffer *ecdh_qs = buf_getstringbuf(ses.payload); + kexecdh_comb_key(cli_ses.ecdh_param, ecdh_qs, hostkey); + buf_free(ecdh_qs); +#endif } - 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); - cli_ses.dh_val_algo = DROPBEAR_KEX_NONE; + if (cli_ses.dh_param) { + free_kexdh_param(cli_ses.dh_param); + cli_ses.dh_param = NULL; + } +#ifdef DROPBEAR_ECDH + if (cli_ses.ecdh_param) { + free_kexecdh_param(cli_ses.ecdh_param); + cli_ses.ecdh_param = NULL; + } +#endif - if (buf_verify(ses.payload, hostkey, ses.hash, SHA1_HASH_SIZE) - != DROPBEAR_SUCCESS) { + cli_ses.param_kex_algo = NULL; + if (buf_verify(ses.payload, hostkey, ses.hash) != DROPBEAR_SUCCESS) { dropbear_exit("Bad hostkey signature"); } diff -r e378da7eae5d -r 33207ed1174b cli-main.c --- a/cli-main.c Wed Oct 16 22:55:03 2013 +0800 +++ b/cli-main.c Mon Oct 21 22:57:21 2013 +0800 @@ -28,6 +28,8 @@ #include "dbutil.h" #include "runopts.h" #include "session.h" +#include "random.h" +#include "crypto_desc.h" static void cli_dropbear_exit(int exitcode, const char* format, va_list param) ATTRIB_NORETURN; static void cli_dropbear_log(int priority, const char* format, va_list param); @@ -51,6 +53,9 @@ disallow_core(); + seedrandom(); + crypto_init(); + cli_getopts(argc, argv); TRACE(("user='%s' host='%s' port='%s'", cli_opts.username, diff -r e378da7eae5d -r 33207ed1174b cli-session.c --- a/cli-session.c Wed Oct 16 22:55:03 2013 +0800 +++ b/cli-session.c Mon Oct 21 22:57:21 2013 +0800 @@ -36,6 +36,7 @@ #include "runopts.h" #include "chansession.h" #include "agentfwd.h" +#include "crypto_desc.h" static void cli_remoteclosed(); static void cli_sessionloop(); @@ -86,10 +87,6 @@ void cli_session(int sock_in, int sock_out) { - seedrandom(); - - crypto_init(); - common_session_init(sock_in, sock_out); chaninitialise(cli_chantypes); diff -r e378da7eae5d -r 33207ed1174b common-algo.c --- a/common-algo.c Wed Oct 16 22:55:03 2013 +0800 +++ b/common-algo.c Mon Oct 21 22:57:21 2013 +0800 @@ -23,24 +23,28 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ +#include "includes.h" #include "algo.h" #include "session.h" #include "dbutil.h" +#include "kex.h" +#include "ltc_prng.h" +#include "ecc.h" /* This file (algo.c) organises the ciphers which can be used, and is used to * decide which ciphers/hashes/compression/signing to use during key exchange*/ static int void_cipher(const unsigned char* in, unsigned char* out, - unsigned long len, void *cipher_state) { + unsigned long len, void* UNUSED(cipher_state)) { if (in != out) { memmove(out, in, len); } return CRYPT_OK; } -static int void_start(int cipher, const unsigned char *IV, - const unsigned char *key, - int keylen, int num_rounds, void *cipher_state) { +static int void_start(int UNUSED(cipher), const unsigned char* UNUSED(IV), + const unsigned char* UNUSED(key), + int UNUSED(keylen), int UNUSED(num_rounds), void* UNUSED(cipher_state)) { return CRYPT_OK; } @@ -204,6 +208,17 @@ }; algo_type sshhostkey[] = { +#ifdef DROPBEAR_ECDSA +#ifdef DROPBEAR_ECC_256 + {"ecdsa-sha2-nistp256", DROPBEAR_SIGNKEY_ECDSA_NISTP256, NULL, 1, NULL}, +#endif +#ifdef DROPBEAR_ECC_384 + {"ecdsa-sha2-nistp384", DROPBEAR_SIGNKEY_ECDSA_NISTP384, NULL, 1, NULL}, +#endif +#ifdef DROPBEAR_ECC_521 + {"ecdsa-sha2-nistp521", DROPBEAR_SIGNKEY_ECDSA_NISTP521, NULL, 1, NULL}, +#endif +#endif #ifdef DROPBEAR_RSA {"ssh-rsa", DROPBEAR_SIGNKEY_RSA, NULL, 1, NULL}, #endif @@ -213,65 +228,42 @@ {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_ECDH +#ifdef DROPBEAR_ECC_256 +static struct dropbear_kex kex_ecdh_nistp256 = {NULL, 0, &ecc_curve_nistp256, &sha256_desc }; +#endif +#ifdef DROPBEAR_ECC_384 +static struct dropbear_kex kex_ecdh_nistp384 = {NULL, 0, &ecc_curve_nistp384, &sha384_desc }; +#endif +#ifdef DROPBEAR_ECC_521 +static struct dropbear_kex kex_ecdh_nistp521 = {NULL, 0, &ecc_curve_nistp521, &sha512_desc }; +#endif +#endif // DROPBEAR_ECDH + + algo_type sshkex[] = { - {"diffie-hellman-group1-sha1", DROPBEAR_KEX_DH_GROUP1, NULL, 1, NULL}, - {"diffie-hellman-group14-sha1", DROPBEAR_KEX_DH_GROUP14, NULL, 1, NULL}, +#ifdef DROPBEAR_ECDH +#ifdef DROPBEAR_ECC_256 + {"ecdh-sha2-nistp256", 0, &kex_ecdh_nistp256, 1, NULL}, +#endif +#ifdef DROPBEAR_ECC_384 + {"ecdh-sha2-nistp384", 0, &kex_ecdh_nistp384, 1, NULL}, +#endif +#ifdef DROPBEAR_ECC_521 + {"ecdh-sha2-nistp521", 0, &kex_ecdh_nistp521, 1, NULL}, +#endif +#endif + {"diffie-hellman-group1-sha1", 0, &kex_dh_group1, 1, NULL}, + {"diffie-hellman-group14-sha1", 0, &kex_dh_group14, 1, NULL}, #ifdef USE_KEXGUESS2 {KEXGUESS2_ALGO_NAME, KEXGUESS2_ALGO_ID, NULL, 1, NULL}, #endif {NULL, 0, NULL, 0, NULL} }; - -/* Register the compiled in ciphers. - * This should be run before using any of the ciphers/hashes */ -void crypto_init() { - - const struct ltc_cipher_descriptor *regciphers[] = { -#ifdef DROPBEAR_AES - &aes_desc, -#endif -#ifdef DROPBEAR_BLOWFISH - &blowfish_desc, -#endif -#ifdef DROPBEAR_TWOFISH - &twofish_desc, -#endif -#ifdef DROPBEAR_3DES - &des3_desc, -#endif - NULL - }; - - const struct ltc_hash_descriptor *reghashes[] = { - /* we need sha1 for hostkey stuff regardless */ - &sha1_desc, -#ifdef DROPBEAR_MD5_HMAC - &md5_desc, -#endif -#ifdef DROPBEAR_SHA2_256_HMAC - &sha256_desc, -#endif -#ifdef DROPBEAR_SHA2_512_HMAC - &sha512_desc, -#endif - NULL - }; - int i; - - for (i = 0; regciphers[i] != NULL; i++) { - if (register_cipher(regciphers[i]) == -1) { - dropbear_exit("Error registering crypto"); - } - } - - for (i = 0; reghashes[i] != NULL; i++) { - if (register_hash(reghashes[i]) == -1) { - dropbear_exit("Error registering crypto"); - } - } -} - /* algolen specifies the length of algo, algos is our local list to match * against. * Returns DROPBEAR_SUCCESS if we have a match for algo, DROPBEAR_FAILURE diff -r e378da7eae5d -r 33207ed1174b common-kex.c --- a/common-kex.c Wed Oct 16 22:55:03 2013 +0800 +++ b/common-kex.c Mon Oct 21 22:57:21 2013 +0800 @@ -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 @@ 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,26 +280,28 @@ * 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 @@ 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 @@ 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 @@ } } - 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 @@ 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(¶m->pub, ¶m->priv, NULL); /* read the prime and generator*/ load_dh_p(&dh_p); @@ -592,33 +588,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, ¶m->priv) != MP_OKAY) { dropbear_exit("Diffie-Hellman error"); } - if (mp_div_2(dh_priv, &dh_q) != MP_OKAY) { + if (mp_div_2(¶m->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, ¶m->priv); /* f = g^y mod p */ - if (mp_exptmod(&dh_g, dh_priv, &dh_p, dh_pub) != MP_OKAY) { + if (mp_exptmod(&dh_g, ¶m->priv, &dh_p, ¶m->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(¶m->pub, ¶m->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 @@ } /* 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, ¶m->priv, &dh_p, ses.dh_K) != MP_OKAY) { dropbear_exit("Diffie-Hellman error"); } @@ -643,11 +644,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 = ¶m->pub; dh_f = dh_pub_them; } else { dh_e = dh_pub_them; - dh_f = dh_pub_us; + dh_f = ¶m->pub; } /* Create the remainder of the hash buffer, to generate the exchange hash */ @@ -661,11 +662,70 @@ 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, + ¶m->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(¶m->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, ¶m->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 = ¶m->key; + Q_S = Q_them; + } else { + Q_C = Q_them; + Q_S = ¶m->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 @@ /* 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 @@ } 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); diff -r e378da7eae5d -r 33207ed1174b common-session.c --- a/common-session.c Wed Oct 16 22:55:03 2013 +0800 +++ b/common-session.c Mon Oct 21 22:57:21 2013 +0800 @@ -101,7 +101,7 @@ ses.keys->recv.algo_mac = &dropbear_nohash; ses.keys->trans.algo_mac = &dropbear_nohash; - ses.keys->algo_kex = -1; + ses.keys->algo_kex = NULL; ses.keys->algo_hostkey = -1; ses.keys->recv.algo_comp = DROPBEAR_COMP_NONE; ses.keys->trans.algo_comp = DROPBEAR_COMP_NONE; @@ -245,7 +245,16 @@ ses.extra_session_cleanup(); } - m_free(ses.session_id); + if (ses.session_id) { + buf_burn(ses.session_id); + buf_free(ses.session_id); + ses.session_id = NULL; + } + if (ses.hash) { + buf_burn(ses.hash); + buf_free(ses.hash); + ses.hash = NULL; + } m_burn(ses.keys, sizeof(struct key_context)); m_free(ses.keys); diff -r e378da7eae5d -r 33207ed1174b configure.ac --- a/configure.ac Wed Oct 16 22:55:03 2013 +0800 +++ b/configure.ac Mon Oct 21 22:57:21 2013 +0800 @@ -695,7 +695,7 @@ 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) diff -r e378da7eae5d -r 33207ed1174b crypto_desc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/crypto_desc.c Mon Oct 21 22:57:21 2013 +0800 @@ -0,0 +1,75 @@ +#include "includes.h" +#include "dbutil.h" +#include "crypto_desc.h" +#include "ltc_prng.h" +#include "ecc.h" + +#ifdef DROPBEAR_LTC_PRNG + int dropbear_ltc_prng = -1; +#endif + + +/* Register the compiled in ciphers. + * This should be run before using any of the ciphers/hashes */ +void crypto_init() { + + const struct ltc_cipher_descriptor *regciphers[] = { +#ifdef DROPBEAR_AES + &aes_desc, +#endif +#ifdef DROPBEAR_BLOWFISH + &blowfish_desc, +#endif +#ifdef DROPBEAR_TWOFISH + &twofish_desc, +#endif +#ifdef DROPBEAR_3DES + &des3_desc, +#endif + NULL + }; + + const struct ltc_hash_descriptor *reghashes[] = { + /* we need sha1 for hostkey stuff regardless */ + &sha1_desc, +#ifdef DROPBEAR_MD5_HMAC + &md5_desc, +#endif +#ifdef DROPBEAR_SHA256 + &sha256_desc, +#endif +#ifdef DROPBEAR_SHA384 + &sha384_desc, +#endif +#ifdef DROPBEAR_SHA512 + &sha512_desc, +#endif + NULL + }; + int i; + + for (i = 0; regciphers[i] != NULL; i++) { + if (register_cipher(regciphers[i]) == -1) { + dropbear_exit("Error registering crypto"); + } + } + + for (i = 0; reghashes[i] != NULL; i++) { + if (register_hash(reghashes[i]) == -1) { + dropbear_exit("Error registering crypto"); + } + } + +#ifdef DROPBEAR_LTC_PRNG + dropbear_ltc_prng = register_prng(&dropbear_prng_desc); + if (dropbear_ltc_prng == -1) { + dropbear_exit("Error registering crypto"); + } +#endif + +#ifdef DROPBEAR_ECC + ltc_mp = ltm_desc; + dropbear_ecc_fill_dp(); +#endif +} + diff -r e378da7eae5d -r 33207ed1174b crypto_desc.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/crypto_desc.h Mon Oct 21 22:57:21 2013 +0800 @@ -0,0 +1,9 @@ +#ifndef _CRYPTO_DESC_H +#define _CRYPTO_DESC_H + +void crypto_init(); + +extern int dropbear_ltc_prng; + +#endif // _CRYPTO_DESC_H + diff -r e378da7eae5d -r 33207ed1174b dbutil.c --- a/dbutil.c Wed Oct 16 22:55:03 2013 +0800 +++ b/dbutil.c Mon Oct 21 22:57:21 2013 +0800 @@ -675,6 +675,14 @@ } fprintf(stderr, "\n"); } + +void printmpint(const char *label, mp_int *mp) { + buffer *buf = buf_new(1000); + buf_putmpint(buf, mp); + printhex(label, buf->data, buf->len); + buf_free(buf); + +} #endif /* Strip all control characters from text (a null-terminated string), except diff -r e378da7eae5d -r 33207ed1174b dbutil.h --- a/dbutil.h Wed Oct 16 22:55:03 2013 +0800 +++ b/dbutil.h Mon Oct 21 22:57:21 2013 +0800 @@ -57,6 +57,7 @@ void dropbear_trace(const char* format, ...) ATTRIB_PRINTF(1,2); void dropbear_trace2(const char* format, ...) ATTRIB_PRINTF(1,2); void printhex(const char * label, const unsigned char * buf, int len); +void printmpint(const char *label, mp_int *mp); extern int debug_trace; #endif diff -r e378da7eae5d -r 33207ed1174b debug.h --- a/debug.h Wed Oct 16 22:55:03 2013 +0800 +++ b/debug.h Mon Oct 21 22:57:21 2013 +0800 @@ -40,6 +40,7 @@ * since the printing may not sanitise strings etc. This will add a reasonable * amount to your executable size. */ /* #define DEBUG_TRACE */ +#define DEBUG_TRACE /* All functions writing to the cleartext payload buffer call * CHECKCLEARTOWRITE() before writing. This is only really useful if you're @@ -71,7 +72,7 @@ /* To debug with GDB it is easier to run with no forking of child processes. You will need to pass "-F" as well. */ -/* #define DEBUG_NOFORK */ +#define DEBUG_NOFORK /* For testing as non-root on shadowed systems, include the crypt of a password diff -r e378da7eae5d -r 33207ed1174b dropbearconvert.c --- a/dropbearconvert.c Wed Oct 16 22:55:03 2013 +0800 +++ b/dropbearconvert.c Mon Oct 21 22:57:21 2013 +0800 @@ -62,6 +62,9 @@ const char* infile; const char* outfile; + crypto_init(); + seedrandom(); + #ifdef DEBUG_TRACE /* It's hard for it to get in the way _too_ much */ debug_trace = 1; diff -r e378da7eae5d -r 33207ed1174b dropbearkey.c --- a/dropbearkey.c Wed Oct 16 22:55:03 2013 +0800 +++ b/dropbearkey.c Mon Oct 21 22:57:21 2013 +0800 @@ -51,11 +51,14 @@ #include "genrsa.h" #include "gendss.h" +#include "ecdsa.h" +#include "crypto_desc.h" +#include "random.h" static void printhelp(char * progname); -#define RSA_SIZE (1024/8) /* 1024 bit */ -#define DSS_SIZE (1024/8) /* 1024 bit */ +#define RSA_DEFAULT_SIZE 1024 +#define DSS_DEFAULT_SIZE 1024 static void buf_writefile(buffer * buf, const char * filename); static void printpubkey(sign_key * key, int keytype); @@ -72,9 +75,27 @@ #ifdef DROPBEAR_DSS " dss\n" #endif +#ifdef DROPBEAR_ECDSA + " ecdsa\n" +#endif "-f filename Use filename for the secret key\n" "-s bits Key size in bits, should be a multiple of 8 (optional)\n" - " (DSS has a fixed size of 1024 bits)\n" +#ifdef DROPBEAR_DSS + " DSS has a fixed size of 1024 bits\n" +#endif +#ifdef DROPBEAR_ECDSA + " ECDSA has sizes " +#ifdef DROPBEAR_ECC_256 + "256 " +#endif +#ifdef DROPBEAR_ECC_384 + "384 " +#endif +#ifdef DROPBEAR_ECC_521 + "521 " +#endif + "\n" +#endif "-y Just print the publickey and fingerprint for the\n private key in .\n" #ifdef DEBUG_TRACE "-v verbose\n" @@ -94,13 +115,15 @@ sign_key *key = NULL; buffer *buf = NULL; char * filename = NULL; - int keytype = -1; + enum signkey_type keytype = DROPBEAR_SIGNKEY_NONE; char * typetext = NULL; char * sizetext = NULL; unsigned int bits; - unsigned int keysize; int printpub = 0; + crypto_init(); + seedrandom(); + /* get the commandline options */ for (i = 1; i < argc; i++) { if (argv[i] == NULL) { @@ -162,21 +185,9 @@ exit(EXIT_FAILURE); } - if (strlen(typetext) == 3) { -#ifdef DROPBEAR_RSA - if (strncmp(typetext, "rsa", 3) == 0) { - keytype = DROPBEAR_SIGNKEY_RSA; - TRACE(("type is rsa")) - } -#endif -#ifdef DROPBEAR_DSS - if (strncmp(typetext, "dss", 3) == 0) { - keytype = DROPBEAR_SIGNKEY_DSS; - TRACE(("type is dss")) - } -#endif - } - if (keytype == -1) { + keytype = signkey_type_from_name(typetext, strlen(typetext)); + + if (keytype == DROPBEAR_SIGNKEY_NONE) { fprintf(stderr, "Unknown key type '%s'\n", typetext); printhelp(argv[0]); exit(EXIT_FAILURE); @@ -188,28 +199,51 @@ exit(EXIT_FAILURE); } - if (keytype == DROPBEAR_SIGNKEY_DSS && bits != 1024) { - fprintf(stderr, "DSS keys have a fixed size of 1024 bits\n"); - exit(EXIT_FAILURE); - } else if (bits < 512 || bits > 4096 || (bits % 8 != 0)) { - fprintf(stderr, "Bits must satisfy 512 <= bits <= 4096, and be a" - " multiple of 8\n"); - exit(EXIT_FAILURE); - } + // TODO: put RSA and DSS size checks into genrsa.c etc + switch (keytype) { +#ifdef DROPBEAR_RSA + case DROPBEAR_SIGNKEY_RSA: + if (bits < 512 || bits > 4096 || (bits % 8 != 0)) { + fprintf(stderr, "Bits must satisfy 512 <= bits <= 4096, and be a" + " multiple of 8\n"); + exit(EXIT_FAILURE); + } + break; +#endif +#ifdef DROPEAR_DSS + case DROPBEAR_SIGNKEY_DSS: + if (bits != 1024) { + fprintf(stderr, "DSS keys have a fixed size of 1024 bits\n"); + exit(EXIT_FAILURE); + } +#endif + default: + (void)0; /* quiet, compiler. ecdsa handles checks itself */ + } - keysize = bits / 8; - } else { - if (keytype == DROPBEAR_SIGNKEY_DSS) { - keysize = DSS_SIZE; - } else if (keytype == DROPBEAR_SIGNKEY_RSA) { - keysize = RSA_SIZE; - } else { - exit(EXIT_FAILURE); /* not reached */ + switch (keytype) { +#ifdef DROPBEAR_RSA + case DROPBEAR_SIGNKEY_RSA: + bits = RSA_DEFAULT_SIZE; + break; +#endif +#ifdef DROPBEAR_DSS + case DROPBEAR_SIGNKEY_DSS: + bits = DSS_DEFAULT_SIZE; + break; +#endif +#ifdef DROPBEAR_ECDSA + case DROPBEAR_SIGNKEY_ECDSA_KEYGEN: + bits = ECDSA_DEFAULT_SIZE; + break; +#endif + default: + exit(EXIT_FAILURE); /* not reached */ } } - fprintf(stderr, "Will output %d bit %s secret key to '%s'\n", keysize*8, + fprintf(stderr, "Will output %d bit %s secret key to '%s'\n", bits, typetext, filename); /* don't want the file readable by others */ @@ -222,12 +256,21 @@ switch(keytype) { #ifdef DROPBEAR_RSA case DROPBEAR_SIGNKEY_RSA: - key->rsakey = gen_rsa_priv_key(keysize); /* 128 bytes = 1024 bit */ + key->rsakey = gen_rsa_priv_key(bits); break; #endif #ifdef DROPBEAR_DSS case DROPBEAR_SIGNKEY_DSS: - key->dsskey = gen_dss_priv_key(keysize); /* 128 bytes = 1024 bit */ + key->dsskey = gen_dss_priv_key(bits); + break; +#endif +#ifdef DROPBEAR_ECDSA + case DROPBEAR_SIGNKEY_ECDSA_KEYGEN: + { + ecc_key *ecckey = gen_ecdsa_priv_key(bits); + keytype = ecdsa_signkey_type(ecckey); + *signkey_ecc_key_ptr(key, keytype) = ecckey; + } break; #endif default: @@ -319,7 +362,7 @@ fprintf(stderr, "base64 failed"); } - typestring = signkey_name_from_type(keytype, &err); + typestring = signkey_name_from_type(keytype, NULL); fp = sign_key_fingerprint(buf_getptr(buf, len), len); diff -r e378da7eae5d -r 33207ed1174b dss.c --- a/dss.c Wed Oct 16 22:55:03 2013 +0800 +++ b/dss.c Mon Oct 21 22:57:21 2013 +0800 @@ -47,11 +47,7 @@ TRACE(("enter buf_get_dss_pub_key")) dropbear_assert(key != NULL); - key->p = m_malloc(sizeof(mp_int)); - key->q = m_malloc(sizeof(mp_int)); - key->g = m_malloc(sizeof(mp_int)); - key->y = m_malloc(sizeof(mp_int)); - m_mp_init_multi(key->p, key->q, key->g, key->y, NULL); + m_mp_alloc_init_multi(&key->p, &key->q, &key->g, &key->y, NULL); key->x = NULL; buf_incrpos(buf, 4+SSH_SIGNKEY_DSS_LEN); /* int + "ssh-dss" */ @@ -87,8 +83,7 @@ return DROPBEAR_FAILURE; } - key->x = m_malloc(sizeof(mp_int)); - m_mp_init(key->x); + m_mp_alloc_init_multi(&key->x, NULL); ret = buf_getmpint(buf, key->x); if (ret == DROPBEAR_FAILURE) { m_free(key->x); @@ -161,9 +156,7 @@ #ifdef DROPBEAR_SIGNKEY_VERIFY /* Verify a DSS signature (in buf) made on data by the key given. * returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ -int buf_dss_verify(buffer* buf, dropbear_dss_key *key, const unsigned char* data, - unsigned int len) { - +int buf_dss_verify(buffer* buf, dropbear_dss_key *key, buffer *data_buf) { unsigned char msghash[SHA1_HASH_SIZE]; hash_state hs; int ret = DROPBEAR_FAILURE; @@ -187,7 +180,7 @@ /* hash the data */ sha1_init(&hs); - sha1_process(&hs, data, len); + sha1_process(&hs, data_buf->data, data_buf->len); sha1_done(&hs, msghash); /* create the signature - s' and r' are the received signatures in buf */ @@ -260,9 +253,7 @@ /* Sign the data presented with key, writing the signature contents * to the buffer */ -void buf_put_dss_sign(buffer* buf, dropbear_dss_key *key, const unsigned char* data, - unsigned int len) { - +void buf_put_dss_sign(buffer* buf, dropbear_dss_key *key, buffer *data_buf) { unsigned char msghash[SHA1_HASH_SIZE]; unsigned int writelen; unsigned int i; @@ -279,7 +270,7 @@ /* hash the data */ sha1_init(&hs); - sha1_process(&hs, data, len); + sha1_process(&hs, data_buf->data, data_buf->len); sha1_done(&hs, msghash); m_mp_init_multi(&dss_k, &dss_temp1, &dss_temp2, &dss_r, &dss_s, diff -r e378da7eae5d -r 33207ed1174b dss.h --- a/dss.h Wed Oct 16 22:55:03 2013 +0800 +++ b/dss.h Mon Oct 21 22:57:21 2013 +0800 @@ -30,8 +30,6 @@ #ifdef DROPBEAR_DSS -#define DSS_SIGNATURE_SIZE 4+SSH_SIGNKEY_DSS_LEN+4+2*SHA1_HASH_SIZE - typedef struct { mp_int* p; @@ -43,11 +41,9 @@ } dropbear_dss_key; -void buf_put_dss_sign(buffer* buf, dropbear_dss_key *key, const unsigned char* data, - unsigned int len); +void buf_put_dss_sign(buffer* buf, dropbear_dss_key *key, buffer *data_buf); #ifdef DROPBEAR_SIGNKEY_VERIFY -int buf_dss_verify(buffer* buf, dropbear_dss_key *key, const unsigned char* data, - unsigned int len); +int buf_dss_verify(buffer* buf, dropbear_dss_key *key, buffer *data_buf); #endif int buf_get_dss_pub_key(buffer* buf, dropbear_dss_key *key); int buf_get_dss_priv_key(buffer* buf, dropbear_dss_key *key); diff -r e378da7eae5d -r 33207ed1174b ecc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ecc.c Mon Oct 21 22:57:21 2013 +0800 @@ -0,0 +1,271 @@ +#include "includes.h" +#include "options.h" +#include "ecc.h" +#include "dbutil.h" +#include "bignum.h" + +#ifdef DROPBEAR_ECC + +// .dp members are filled out by dropbear_ecc_fill_dp() at startup +#ifdef DROPBEAR_ECC_256 +struct dropbear_ecc_curve ecc_curve_nistp256 = { + .ltc_size = 32, + .hash_desc = &sha256_desc, + .name = "nistp256" +}; +#endif +#ifdef DROPBEAR_ECC_384 +struct dropbear_ecc_curve ecc_curve_nistp384 = { + .ltc_size = 48, + .hash_desc = &sha384_desc, + .name = "nistp384" +}; +#endif +#ifdef DROPBEAR_ECC_521 +struct dropbear_ecc_curve ecc_curve_nistp521 = { + .ltc_size = 66, + .hash_desc = &sha512_desc, + .name = "nistp521" +}; +#endif + +struct dropbear_ecc_curve *dropbear_ecc_curves[] = { +#ifdef DROPBEAR_ECC_256 + &ecc_curve_nistp256, +#endif +#ifdef DROPBEAR_ECC_384 + &ecc_curve_nistp384, +#endif +#ifdef DROPBEAR_ECC_521 + &ecc_curve_nistp521, +#endif + NULL +}; + +void dropbear_ecc_fill_dp() { + struct dropbear_ecc_curve **curve; + // libtomcrypt guarantees they're ordered by size + const ltc_ecc_set_type *dp = ltc_ecc_sets; + for (curve = dropbear_ecc_curves; *curve; curve++) { + for (;dp->size > 0; dp++) { + if (dp->size == (*curve)->ltc_size) { + (*curve)->dp = dp; + break; + } + } + if (!(*curve)->dp) { + dropbear_exit("Missing ECC params %s", (*curve)->name); + } + } +} + +struct dropbear_ecc_curve* curve_for_dp(const ltc_ecc_set_type *dp) { + struct dropbear_ecc_curve **curve = NULL; + for (curve = dropbear_ecc_curves; *curve; curve++) { + if ((*curve)->dp == dp) { + break; + } + } + assert(*curve); + return *curve; +} + +ecc_key * new_ecc_key(void) { + ecc_key *key = m_malloc(sizeof(*key)); + m_mp_alloc_init_multi(&key->pubkey.x, &key->pubkey.y, + &key->pubkey.z, &key->k, NULL); + return key; +} + +// Copied from libtomcrypt ecc_import.c (version there is static), modified +// for different mp_int pointer without LTC_SOURCE +static int ecc_is_point(ecc_key *key) +{ + mp_int *prime, *b, *t1, *t2; + int err; + + prime = m_malloc(sizeof(mp_int)); + b = m_malloc(sizeof(mp_int)); + t1 = m_malloc(sizeof(mp_int)); + t2 = m_malloc(sizeof(mp_int)); + + m_mp_alloc_init_multi(&prime, &b, &t1, &t2, NULL); + + /* load prime and b */ + if ((err = mp_read_radix(prime, key->dp->prime, 16)) != CRYPT_OK) { goto error; } + if ((err = mp_read_radix(b, key->dp->B, 16)) != CRYPT_OK) { goto error; } + + /* compute y^2 */ + if ((err = mp_sqr(key->pubkey.y, t1)) != CRYPT_OK) { goto error; } + + /* compute x^3 */ + if ((err = mp_sqr(key->pubkey.x, t2)) != CRYPT_OK) { goto error; } + if ((err = mp_mod(t2, prime, t2)) != CRYPT_OK) { goto error; } + if ((err = mp_mul(key->pubkey.x, t2, t2)) != CRYPT_OK) { goto error; } + + /* compute y^2 - x^3 */ + if ((err = mp_sub(t1, t2, t1)) != CRYPT_OK) { goto error; } + + /* compute y^2 - x^3 + 3x */ + if ((err = mp_add(t1, key->pubkey.x, t1)) != CRYPT_OK) { goto error; } + if ((err = mp_add(t1, key->pubkey.x, t1)) != CRYPT_OK) { goto error; } + if ((err = mp_add(t1, key->pubkey.x, t1)) != CRYPT_OK) { goto error; } + if ((err = mp_mod(t1, prime, t1)) != CRYPT_OK) { goto error; } + while (mp_cmp_d(t1, 0) == LTC_MP_LT) { + if ((err = mp_add(t1, prime, t1)) != CRYPT_OK) { goto error; } + } + while (mp_cmp(t1, prime) != LTC_MP_LT) { + if ((err = mp_sub(t1, prime, t1)) != CRYPT_OK) { goto error; } + } + + /* compare to b */ + if (mp_cmp(t1, b) != LTC_MP_EQ) { + err = CRYPT_INVALID_PACKET; + } else { + err = CRYPT_OK; + } + + error: + mp_clear_multi(prime, b, t1, t2, NULL); + m_free(prime); + m_free(b); + m_free(t1); + m_free(t2); + return err; +} + +/* For the "ephemeral public key octet string" in ECDH (rfc5656 section 4) */ +void buf_put_ecc_raw_pubkey_string(buffer *buf, ecc_key *key) { + unsigned long len = key->dp->size*2 + 1; + buf_putint(buf, 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); +} + +/* For the "ephemeral public key octet string" in ECDH (rfc5656 section 4) */ +ecc_key * buf_get_ecc_raw_pubkey(buffer *buf, const struct dropbear_ecc_curve *curve) { + ecc_key *key = NULL; + int ret = DROPBEAR_FAILURE; + const unsigned int size = curve->dp->size; + unsigned char first; + + TRACE(("enter buf_get_ecc_raw_pubkey")) + + buf_setpos(buf, 0); + first = buf_getbyte(buf); + if (first == 2 || first == 3) { + dropbear_log(LOG_WARNING, "Dropbear doesn't support ECC point compression"); + return NULL; + } + if (first != 4 || buf->len != 1+2*size) { + TRACE(("leave, wrong size")) + return NULL; + } + + key = new_ecc_key(); + key->dp = curve->dp; + + if (mp_read_unsigned_bin(key->pubkey.x, buf_getptr(buf, size), size) != MP_OKAY) { + TRACE(("failed to read x")) + goto out; + } + buf_incrpos(buf, size); + + if (mp_read_unsigned_bin(key->pubkey.y, buf_getptr(buf, size), size) != MP_OKAY) { + TRACE(("failed to read y")) + goto out; + } + buf_incrpos(buf, size); + + mp_set(key->pubkey.z, 1); + + if (ecc_is_point(key) != CRYPT_OK) { + TRACE(("failed, not a point")) + goto out; + } + + // SEC1 3.2.3.1 Check that Q != 0 + if (mp_cmp_d(key->pubkey.x, 0) == LTC_MP_EQ) { + TRACE(("failed, x == 0")) + goto out; + } + if (mp_cmp_d(key->pubkey.y, 0) == LTC_MP_EQ) { + TRACE(("failed, y == 0")) + goto out; + } + + ret = DROPBEAR_SUCCESS; + + out: + if (ret == DROPBEAR_FAILURE) { + if (key) { + ecc_free(key); + m_free(key); + key = NULL; + } + } + + return 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 err = DROPBEAR_FAILURE; + + /* type valid? */ + if (private_key->type != PK_PRIVATE) { + goto done; + } + + if (private_key->dp != public_key->dp) { + goto done; + } + + /* 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 = m_malloc(sizeof(*shared_secret)); + m_mp_init(shared_secret); + mp_copy(result->x, shared_secret); + } + + if (prime) { + mp_clear(prime); + m_free(prime); + } + if (result) + { + ltc_ecc_del_point(result); + } + + if (err == DROPBEAR_FAILURE) { + dropbear_exit("ECC error"); + } + return shared_secret; +} + +#endif diff -r e378da7eae5d -r 33207ed1174b ecc.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ecc.h Mon Oct 21 22:57:21 2013 +0800 @@ -0,0 +1,36 @@ +#ifndef _DROPBEAR_ECC_H +#define _DROPBEAR_ECC_H + +#include "includes.h" +#include "options.h" + +#include "buffer.h" + +#ifdef DROPBEAR_ECC + +struct dropbear_ecc_curve { + int ltc_size; // to match the byte sizes in ltc_ecc_sets[] + const ltc_ecc_set_type *dp; // curve domain parameters + const struct ltc_hash_descriptor *hash_desc; + const unsigned char *name; +}; + +extern struct dropbear_ecc_curve ecc_curve_nistp256; +extern struct dropbear_ecc_curve ecc_curve_nistp384; +extern struct dropbear_ecc_curve ecc_curve_nistp521; +extern struct dropbear_ecc_curve *dropbear_ecc_curves[]; + +void dropbear_ecc_fill_dp(); +struct dropbear_ecc_curve* curve_for_dp(const ltc_ecc_set_type *dp); + +// "pubkey" refers to a point, but LTC uses ecc_key structure for both public +// and private keys +void buf_put_ecc_raw_pubkey_string(buffer *buf, ecc_key *key); +ecc_key * buf_get_ecc_raw_pubkey(buffer *buf, const struct dropbear_ecc_curve *curve); +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); + +#endif + +#endif // _DROPBEAR_ECC_H \ No newline at end of file diff -r e378da7eae5d -r 33207ed1174b ecdsa.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ecdsa.c Mon Oct 21 22:57:21 2013 +0800 @@ -0,0 +1,412 @@ +#include "options.h" +#include "includes.h" +#include "dbutil.h" +#include "crypto_desc.h" +#include "ecc.h" +#include "ecdsa.h" +#include "signkey.h" + +#ifdef DROPBEAR_ECDSA + +enum signkey_type ecdsa_signkey_type(ecc_key * key) { +#ifdef DROPBEAR_ECC_256 + if (key->dp == ecc_curve_nistp256.dp) { + return DROPBEAR_SIGNKEY_ECDSA_NISTP256; + } +#endif +#ifdef DROPBEAR_ECC_384 + if (key->dp == ecc_curve_nistp384.dp) { + return DROPBEAR_SIGNKEY_ECDSA_NISTP384; + } +#endif +#ifdef DROPBEAR_ECC_521 + if (key->dp == ecc_curve_nistp521.dp) { + return DROPBEAR_SIGNKEY_ECDSA_NISTP521; + } +#endif + return DROPBEAR_SIGNKEY_NONE; +} + +ecc_key *gen_ecdsa_priv_key(unsigned int bit_size) { + const ltc_ecc_set_type *dp = NULL; // curve domain parameters + switch (bit_size) { +#ifdef DROPBEAR_ECC_256 + case 256: + dp = ecc_curve_nistp256.dp; + break; +#endif +#ifdef DROPBEAR_ECC_384 + case 384: + dp = ecc_curve_nistp384.dp; + break; +#endif +#ifdef DROPBEAR_ECC_521 + case 521: + dp = ecc_curve_nistp521.dp; + break; +#endif + } + if (!dp) { + dropbear_exit("Key size %d isn't valid. Try " +#ifdef DROPBEAR_ECC_256 + "256 " +#endif +#ifdef DROPBEAR_ECC_384 + "384 " +#endif +#ifdef DROPBEAR_ECC_521 + "521 " +#endif + , bit_size); + } + + ecc_key *new_key = m_malloc(sizeof(*new_key)); + if (ecc_make_key_ex(NULL, dropbear_ltc_prng, new_key, dp) != CRYPT_OK) { + dropbear_exit("ECC error"); + } + return new_key; +} + +ecc_key *buf_get_ecdsa_pub_key(buffer* buf) { + unsigned char *key_ident = NULL, *identifier = NULL; + unsigned int key_ident_len, identifier_len; + buffer *q_buf = NULL; + struct dropbear_ecc_curve **curve; + ecc_key *new_key = NULL; + + // string "ecdsa-sha2-[identifier]" + key_ident = buf_getstring(buf, &key_ident_len); + // string "[identifier]" + identifier = buf_getstring(buf, &identifier_len); + + if (key_ident_len != identifier_len + strlen("ecdsa-sha2-")) { + TRACE(("Bad identifier lengths")) + goto out; + } + if (memcmp(&key_ident[strlen("ecdsa-sha2-")], identifier, identifier_len) != 0) { + TRACE(("mismatching identifiers")) + goto out; + } + + for (curve = dropbear_ecc_curves; *curve; curve++) { + if (memcmp(identifier, (char*)(*curve)->name, strlen((char*)(*curve)->name)) == 0) { + break; + } + } + if (!*curve) { + TRACE(("couldn't match ecc curve")) + goto out; + } + + // string Q + q_buf = buf_getstringbuf(buf); + new_key = buf_get_ecc_raw_pubkey(q_buf, *curve); + +out: + m_free(key_ident); + m_free(identifier); + if (q_buf) { + buf_free(q_buf); + q_buf = NULL; + } + TRACE(("leave buf_get_ecdsa_pub_key")) + return new_key; +} + +ecc_key *buf_get_ecdsa_priv_key(buffer *buf) { + ecc_key *new_key = NULL; + TRACE(("enter buf_get_ecdsa_priv_key")) + new_key = buf_get_ecdsa_pub_key(buf); + if (!new_key) { + return NULL; + } + + if (buf_getmpint(buf, new_key->k) != DROPBEAR_SUCCESS) { + ecc_free(new_key); + return NULL; + } + + return new_key; +} + +void buf_put_ecdsa_pub_key(buffer *buf, ecc_key *key) { + struct dropbear_ecc_curve *curve = NULL; + unsigned char key_ident[30]; + + curve = curve_for_dp(key->dp); + snprintf((char*)key_ident, sizeof(key_ident), "ecdsa-sha2-%s", curve->name); + buf_putstring(buf, key_ident, strlen(key_ident)); + buf_putstring(buf, curve->name, strlen(curve->name)); + buf_put_ecc_raw_pubkey_string(buf, key); +} + +void buf_put_ecdsa_priv_key(buffer *buf, ecc_key *key) { + buf_put_ecdsa_pub_key(buf, key); + buf_putmpint(buf, key->k); +} + +void buf_put_ecdsa_sign(buffer *buf, ecc_key *key, buffer *data_buf) { + /* Based on libtomcrypt's ecc_sign_hash but without the asn1 */ + int err = DROPBEAR_FAILURE; + struct dropbear_ecc_curve *curve = NULL; + hash_state hs; + unsigned char hash[64]; + void *e = NULL, *p = NULL, *s = NULL, *r; + unsigned char key_ident[30]; + buffer *sigbuf = NULL; + + TRACE(("buf_put_ecdsa_sign")) + curve = curve_for_dp(key->dp); + + if (ltc_init_multi(&r, &s, &p, &e, NULL) != CRYPT_OK) { + goto out; + } + + curve->hash_desc->init(&hs); + curve->hash_desc->process(&hs, data_buf->data, data_buf->len); + curve->hash_desc->done(&hs, hash); + + if (ltc_mp.unsigned_read(e, hash, curve->hash_desc->hashsize) != CRYPT_OK) { + goto out; + } + + if (ltc_mp.read_radix(p, (char *)key->dp->order, 16) != CRYPT_OK) { + goto out; + } + + for (;;) { + ecc_key R_key; // ephemeral key + if (ecc_make_key_ex(NULL, dropbear_ltc_prng, &R_key, key->dp) != CRYPT_OK) { + goto out; + } + if (ltc_mp.mpdiv(R_key.pubkey.x, p, NULL, r) != CRYPT_OK) { + goto out; + } + if (ltc_mp.compare_d(r, 0) == LTC_MP_EQ) { + // try again + ecc_free(&R_key); + continue; + } + /* k = 1/k */ + if (ltc_mp.invmod(R_key.k, p, R_key.k) != CRYPT_OK) { + goto out; + } + /* s = xr */ + if (ltc_mp.mulmod(key->k, r, p, s) != CRYPT_OK) { + goto out; + } + /* s = e + xr */ + if (ltc_mp.add(e, s, s) != CRYPT_OK) { + goto out; + } + if (ltc_mp.mpdiv(s, p, NULL, s) != CRYPT_OK) { + goto out; + } + /* s = (e + xr)/k */ + if (ltc_mp.mulmod(s, R_key.k, p, s) != CRYPT_OK) { + goto out; + } + ecc_free(&R_key); + + if (ltc_mp.compare_d(s, 0) != LTC_MP_EQ) { + break; + } + } + + snprintf((char*)key_ident, sizeof(key_ident), "ecdsa-sha2-%s", curve->name); + buf_putstring(buf, key_ident, strlen(key_ident)); + // enough for nistp521 + sigbuf = buf_new(200); + buf_putmpint(sigbuf, (mp_int*)r); + buf_putmpint(sigbuf, (mp_int*)s); + buf_putbufstring(buf, sigbuf); + + err = DROPBEAR_SUCCESS; + +out: + if (r && s && p && e) { + ltc_deinit_multi(r, s, p, e, NULL); + } + + if (sigbuf) { + buf_free(sigbuf); + } + + if (err == DROPBEAR_FAILURE) { + dropbear_exit("ECC error"); + } +} + +// returns values in s and r +// returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE +static int buf_get_ecdsa_verify_params(buffer *buf, struct dropbear_ecc_curve *curve, + void *r, void* s) { + int ret = DROPBEAR_FAILURE; + unsigned int sig_len; + unsigned int sig_pos; + + sig_len = buf_getint(buf); + sig_pos = buf->pos; + if (buf_getmpint(buf, r) != DROPBEAR_SUCCESS) { + goto out; + } + if (buf_getmpint(buf, s) != DROPBEAR_SUCCESS) { + goto out; + } + if (buf->pos - sig_pos != sig_len) { + goto out; + } + ret = DROPBEAR_SUCCESS; + +out: + return ret; +} + + +int buf_ecdsa_verify(buffer *buf, ecc_key *key, buffer *data_buf) { + /* Based on libtomcrypt's ecc_verify_hash but without the asn1 */ + int ret = DROPBEAR_FAILURE; + hash_state hs; + struct dropbear_ecc_curve *curve = NULL; + unsigned char hash[64]; + ecc_point *mG = NULL, *mQ = NULL; + void *r = NULL, *s = NULL, *v = NULL, *w = NULL, *u1 = NULL, *u2 = NULL, + *e = NULL, *p = NULL, *m = NULL; + void *mp = NULL; + + /* verify + * + * w = s^-1 mod n + * u1 = xw + * u2 = rw + * X = u1*G + u2*Q + * v = X_x1 mod n + * accept if v == r + */ + + TRACE(("buf_ecdsa_verify")) + curve = curve_for_dp(key->dp); + + mG = ltc_ecc_new_point(); + mQ = ltc_ecc_new_point(); + if (ltc_init_multi(&r, &s, &v, &w, &u1, &u2, &p, &e, &m, NULL) != CRYPT_OK + || !mG + || !mQ) { + dropbear_exit("ECC error"); + } + + if (buf_get_ecdsa_verify_params(buf, curve, r, s) != DROPBEAR_SUCCESS) { + goto out; + } + + curve->hash_desc->init(&hs); + curve->hash_desc->process(&hs, data_buf->data, data_buf->len); + curve->hash_desc->done(&hs, hash); + + if (ltc_mp.unsigned_read(e, hash, curve->hash_desc->hashsize) != CRYPT_OK) { + goto out; + } + + /* get the order */ + if (ltc_mp.read_radix(p, (char *)key->dp->order, 16) != CRYPT_OK) { + goto out; + } + + /* get the modulus */ + if (ltc_mp.read_radix(m, (char *)key->dp->prime, 16) != CRYPT_OK) { + goto out; + } + + /* check for zero */ + if (ltc_mp.compare_d(r, 0) == LTC_MP_EQ + || ltc_mp.compare_d(s, 0) == LTC_MP_EQ + || ltc_mp.compare(r, p) != LTC_MP_LT + || ltc_mp.compare(s, p) != LTC_MP_LT) { + goto out; + } + + /* w = s^-1 mod n */ + if (ltc_mp.invmod(s, p, w) != CRYPT_OK) { + goto out; + } + + /* u1 = ew */ + if (ltc_mp.mulmod(e, w, p, u1) != CRYPT_OK) { + goto out; + } + + /* u2 = rw */ + if (ltc_mp.mulmod(r, w, p, u2) != CRYPT_OK) { + goto out; + } + + /* find mG and mQ */ + if (ltc_mp.read_radix(mG->x, (char *)key->dp->Gx, 16) != CRYPT_OK) { + goto out; + } + if (ltc_mp.read_radix(mG->y, (char *)key->dp->Gy, 16) != CRYPT_OK) { + goto out; + } + if (ltc_mp.set_int(mG->z, 1) != CRYPT_OK) { + goto out; + } + + if (ltc_mp.copy(key->pubkey.x, mQ->x) != CRYPT_OK + || ltc_mp.copy(key->pubkey.y, mQ->y) != CRYPT_OK + || ltc_mp.copy(key->pubkey.z, mQ->z) != CRYPT_OK) { + goto out; + } + + /* compute u1*mG + u2*mQ = mG */ + if (ltc_mp.ecc_mul2add == NULL) { + if (ltc_mp.ecc_ptmul(u1, mG, mG, m, 0) != CRYPT_OK) { + goto out; + } + if (ltc_mp.ecc_ptmul(u2, mQ, mQ, m, 0) != CRYPT_OK) { + goto out; + } + + /* find the montgomery mp */ + if (ltc_mp.montgomery_setup(m, &mp) != CRYPT_OK) { + goto out; + } + + /* add them */ + if (ltc_mp.ecc_ptadd(mQ, mG, mG, m, mp) != CRYPT_OK) { + goto out; + } + + /* reduce */ + if (ltc_mp.ecc_map(mG, m, mp) != CRYPT_OK) { + goto out; + } + } else { + /* use Shamir's trick to compute u1*mG + u2*mQ using half of the doubles */ + if (ltc_mp.ecc_mul2add(mG, u1, mQ, u2, mG, m) != CRYPT_OK) { + goto out; + } + } + + /* v = X_x1 mod n */ + if (ltc_mp.mpdiv(mG->x, p, NULL, v) != CRYPT_OK) { + goto out; + } + + /* does v == r */ + if (ltc_mp.compare(v, r) == LTC_MP_EQ) { + ret = DROPBEAR_SUCCESS; + } + +out: + ltc_ecc_del_point(mG); + ltc_ecc_del_point(mQ); + mp_clear_multi(r, s, v, w, u1, u2, p, e, m, NULL); + if (mp != NULL) { + ltc_mp.montgomery_deinit(mp); + } + return ret; +} + + + +#endif // DROPBEAR_ECDSA diff -r e378da7eae5d -r 33207ed1174b ecdsa.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ecdsa.h Mon Oct 21 22:57:21 2013 +0800 @@ -0,0 +1,32 @@ +#ifndef _ECDSA_H_ +#define _ECDSA_H_ + +#include "includes.h" +#include "buffer.h" +#include "signkey.h" + +#ifdef DROPBEAR_ECDSA + +#ifdef DROPBEAR_ECC_256 +#define ECDSA_DEFAULT_SIZE 256 +#elif DROPBEAR_ECC_384 +#define ECDSA_DEFAULT_SIZE 384 +#elif DROPBEAR_ECC_521 +#define ECDSA_DEFAULT_SIZE 521 +#else +#define ECDSA_DEFAULT_SIZE 0 +#endif + +ecc_key *gen_ecdsa_priv_key(unsigned int bit_size); +ecc_key *buf_get_ecdsa_pub_key(buffer* buf); +ecc_key *buf_get_ecdsa_priv_key(buffer *buf); +void buf_put_ecdsa_pub_key(buffer *buf, ecc_key *key); +void buf_put_ecdsa_priv_key(buffer *buf, ecc_key *key); +enum signkey_type ecdsa_signkey_type(ecc_key * key); + +void buf_put_ecdsa_sign(buffer *buf, ecc_key *key, buffer *data_buf); +int buf_ecdsa_verify(buffer *buf, ecc_key *key, buffer *data_buf); + +#endif + +#endif // _ECDSA_H_ diff -r e378da7eae5d -r 33207ed1174b gendss.c --- a/gendss.c Wed Oct 16 22:55:03 2013 +0800 +++ b/gendss.c Mon Oct 21 22:57:21 2013 +0800 @@ -47,19 +47,16 @@ dropbear_dss_key *key; + if (size != 1024) { + dropbear_exit("DSS keys have a fixed size of 1024 bits"); + } + key = m_malloc(sizeof(*key)); - key->p = (mp_int*)m_malloc(sizeof(mp_int)); - key->q = (mp_int*)m_malloc(sizeof(mp_int)); - key->g = (mp_int*)m_malloc(sizeof(mp_int)); - key->y = (mp_int*)m_malloc(sizeof(mp_int)); - key->x = (mp_int*)m_malloc(sizeof(mp_int)); - m_mp_init_multi(key->p, key->q, key->g, key->y, key->x, NULL); - - seedrandom(); + m_mp_alloc_init_multi(&key->p, &key->q, &key->g, &key->y, &key->x, NULL); getq(key); - getp(key, size); + getp(key, size/8); getg(key); getx(key); gety(key); diff -r e378da7eae5d -r 33207ed1174b genrsa.c --- a/genrsa.c Wed Oct 16 22:55:03 2013 +0800 +++ b/genrsa.c Mon Oct 21 22:57:21 2013 +0800 @@ -34,7 +34,7 @@ #ifdef DROPBEAR_RSA static void getrsaprime(mp_int* prime, mp_int *primeminus, - mp_int* rsa_e, unsigned int size); + mp_int* rsa_e, unsigned int size_bytes); /* mostly taken from libtomcrypt's rsa key generation routine */ dropbear_rsa_key * gen_rsa_priv_key(unsigned int size) { @@ -44,26 +44,22 @@ DEF_MP_INT(qminus); DEF_MP_INT(lcm); - key = m_malloc(sizeof(*key)); + if (size < 512 || size > 4096 || (size % 8 != 0)) { + dropbear_exit("Bits must satisfy 512 <= bits <= 4096, and be a" + " multiple of 8"); + } - key->e = (mp_int*)m_malloc(sizeof(mp_int)); - key->n = (mp_int*)m_malloc(sizeof(mp_int)); - key->d = (mp_int*)m_malloc(sizeof(mp_int)); - key->p = (mp_int*)m_malloc(sizeof(mp_int)); - key->q = (mp_int*)m_malloc(sizeof(mp_int)); - - m_mp_init_multi(key->e, key->n, key->d, key->p, key->q, - &pminus, &lcm, &qminus, NULL); - - seedrandom(); + key = m_malloc(sizeof(*key)); + m_mp_alloc_init_multi(&key->e, &key->n, &key->d, &key->p, &key->q, NULL); + m_mp_init_multi(&pminus, &lcm, &qminus, NULL); if (mp_set_int(key->e, RSA_E) != MP_OKAY) { fprintf(stderr, "RSA generation failed\n"); exit(1); } - getrsaprime(key->p, &pminus, key->e, size/2); - getrsaprime(key->q, &qminus, key->e, size/2); + getrsaprime(key->p, &pminus, key->e, size/16); + getrsaprime(key->q, &qminus, key->e, size/16); if (mp_mul(key->p, key->q, key->n) != MP_OKAY) { fprintf(stderr, "RSA generation failed\n"); @@ -90,21 +86,21 @@ /* return a prime suitable for p or q */ static void getrsaprime(mp_int* prime, mp_int *primeminus, - mp_int* rsa_e, unsigned int size) { + mp_int* rsa_e, unsigned int size_bytes) { unsigned char *buf; DEF_MP_INT(temp_gcd); - buf = (unsigned char*)m_malloc(size+1); + buf = (unsigned char*)m_malloc(size_bytes+1); m_mp_init(&temp_gcd); do { /* generate a random odd number with MSB set, then find the the next prime above it */ - genrandom(buf, size+1); + genrandom(buf, size_bytes+1); buf[0] |= 0x80; /* MSB set */ - bytes_to_mp(prime, buf, size+1); + bytes_to_mp(prime, buf, size_bytes+1); /* find the next integer which is prime, 8 round of miller-rabin */ if (mp_prime_next_prime(prime, 8, 0) != MP_OKAY) { @@ -126,7 +122,7 @@ /* now we have a good value for result */ mp_clear(&temp_gcd); - m_burn(buf, size+1); + m_burn(buf, size_bytes+1); m_free(buf); } diff -r e378da7eae5d -r 33207ed1174b kex.h --- a/kex.h Wed Oct 16 22:55:03 2013 +0800 +++ b/kex.h Mon Oct 21 22:57:21 2013 +0800 @@ -27,16 +27,26 @@ #include "includes.h" #include "algo.h" +#include "signkey.h" void send_msg_kexinit(); 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(); @@ -66,6 +76,21 @@ }; +#define DH_P_1_LEN 128 +extern const unsigned char dh_p_1[DH_P_1_LEN]; +#define DH_P_14_LEN 256 +extern const unsigned char dh_p_14[DH_P_14_LEN]; + +struct kex_dh_param { + mp_int pub; /* e */ + mp_int priv; /* x */ +}; + +#ifdef DROPBEAR_ECDH +struct kex_ecdh_param { + ecc_key key; +}; +#endif #define MAX_KEXHASHBUF 2000 diff -r e378da7eae5d -r 33207ed1174b keyimport.c --- a/keyimport.c Wed Oct 16 22:55:03 2013 +0800 +++ b/keyimport.c Mon Oct 21 22:57:21 2013 +0800 @@ -36,6 +36,11 @@ #include "bignum.h" #include "buffer.h" #include "dbutil.h" +#include "ecc.h" + +const unsigned char OID_SEC256R1_BLOB[] = {0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07}; +const unsigned char OID_SEC384R1_BLOB[] = {0x2b, 0x81, 0x04, 0x00, 0x22}; +const unsigned char OID_SEC521R1_BLOB[] = {0x2b, 0x81, 0x04, 0x00, 0x23}; #define PUT_32BIT(cp, value) do { \ (cp)[3] = (unsigned char)(value); \ @@ -158,7 +163,7 @@ #endif buf = buf_new(MAX_PRIVKEY_SIZE); - buf_put_priv_key(buf, key, keytype); + buf_put_priv_key(buf, key, key->type); fp = fopen(filename, "w"); if (!fp) { @@ -349,7 +354,7 @@ * Code to read and write OpenSSH private keys. */ -enum { OSSH_DSA, OSSH_RSA }; +enum { OSSH_DSA, OSSH_RSA, OSSH_EC }; struct openssh_key { int type; int encrypted; @@ -392,6 +397,8 @@ ret->type = OSSH_RSA; else if (!strcmp(buffer, "-----BEGIN DSA PRIVATE KEY-----\n")) ret->type = OSSH_DSA; + else if (!strcmp(buffer, "-----BEGIN EC PRIVATE KEY-----\n")) + ret->type = OSSH_EC; else { errmsg = "Unrecognised key type"; goto error; @@ -521,6 +528,8 @@ sign_key *retkey; buffer * blobbuf = NULL; + retkey = new_sign_key(); + key = load_openssh_key(filename); if (!key) @@ -597,6 +606,8 @@ num_integers = 9; else if (key->type == OSSH_DSA) num_integers = 6; + else if (key->type == OSSH_EC) + num_integers = 1; /* * Space to create key blob in. @@ -620,11 +631,18 @@ } if (i == 0) { - /* - * The first integer should be zero always (I think - * this is some sort of version indication). - */ - if (len != 1 || p[0] != 0) { + /* First integer is a version indicator */ + int expected; + switch (key->type) { + case OSSH_RSA: + case OSSH_DSA: + expected = 0; + break; + case OSSH_EC: + expected = 1; + break; + } + if (len != 1 || p[0] != expected) { errmsg = "Version number mismatch"; goto error; } @@ -655,21 +673,127 @@ p += len; } +#ifdef DROPBEAR_ECDSA + if (key->type == OSSH_EC) { + const char* ecdsa_name; + unsigned char* private_key_bytes = NULL; + int private_key_len = 0; + unsigned char* public_key_bytes = NULL; + int public_key_len = 0; + ecc_key *ecc = NULL; + const struct dropbear_ecc_curve *curve = NULL; + + // See SEC1 v2, Appendix C.4 + // OpenSSL (so OpenSSH) seems to include the optional parts. + + // privateKey OCTET STRING, + ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p, + &id, &len, &flags); + p += ret; + // id==4 for octet string + if (ret < 0 || id != 4 || + key->keyblob+key->keyblob_len-p < len) { + errmsg = "ASN.1 decoding failure"; + goto error; + } + private_key_bytes = p; + private_key_len = len; + p += len; + + // parameters [0] ECDomainParameters {{ SECGCurveNames }} OPTIONAL, + ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p, + &id, &len, &flags); + p += ret; + // id==0 + if (ret < 0 || id != 0) { + errmsg = "ASN.1 decoding failure"; + goto error; + } + + ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p, + &id, &len, &flags); + p += ret; + // id==6 for object + if (ret < 0 || id != 6 || + key->keyblob+key->keyblob_len-p < len) { + errmsg = "ASN.1 decoding failure"; + goto error; + } + + if (len == sizeof(OID_SEC256R1_BLOB) + && memcmp(p, OID_SEC256R1_BLOB, len) == 0) { + retkey->type = DROPBEAR_SIGNKEY_ECDSA_NISTP256; + curve = &ecc_curve_nistp256; + } else if (len == sizeof(OID_SEC384R1_BLOB) + && memcmp(p, OID_SEC384R1_BLOB, len) == 0) { + retkey->type = DROPBEAR_SIGNKEY_ECDSA_NISTP384; + curve = &ecc_curve_nistp384; + } else if (len == sizeof(OID_SEC521R1_BLOB) + && memcmp(p, OID_SEC521R1_BLOB, len) == 0) { + retkey->type = DROPBEAR_SIGNKEY_ECDSA_NISTP521; + curve = &ecc_curve_nistp521; + } else { + errmsg = "Unknown ECC key type"; + goto error; + } + p += len; + + // publicKey [1] BIT STRING OPTIONAL + ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p, + &id, &len, &flags); + p += ret; + // id==1 + if (ret < 0 || id != 1) { + errmsg = "ASN.1 decoding failure"; + goto error; + } + + ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p, + &id, &len, &flags); + p += ret; + // id==3 for bit string + if (ret < 0 || id != 3 || + key->keyblob+key->keyblob_len-p < len) { + errmsg = "ASN.1 decoding failure"; + goto error; + } + public_key_bytes = p+1; + public_key_len = len-1; + p += len; + + buf_putbytes(blobbuf, public_key_bytes, public_key_len); + ecc = buf_get_ecc_raw_pubkey(blobbuf, curve); + if (!ecc) { + errmsg = "Error parsing ECC key"; + goto error; + } + m_mp_alloc_init_multi((mp_int**)&ecc->k, NULL); + if (mp_read_unsigned_bin(ecc->k, private_key_bytes, private_key_len) + != MP_OKAY) { + errmsg = "Error parsing ECC key"; + goto error; + } + + *signkey_ecc_key_ptr(retkey, retkey->type) = ecc; + } +#endif // DROPBEAR_ECDSA + /* * Now put together the actual key. Simplest way to do this is * to assemble our own key blobs and feed them to the createkey * functions; this is a bit faffy but it does mean we get all * the sanity checks for free. */ - retkey = new_sign_key(); - buf_setpos(blobbuf, 0); - type = DROPBEAR_SIGNKEY_ANY; - if (buf_get_priv_key(blobbuf, retkey, &type) - != DROPBEAR_SUCCESS) { - errmsg = "unable to create key structure"; - sign_key_free(retkey); - retkey = NULL; - goto error; + if (key->type == OSSH_RSA || key->type == OSSH_DSA) { + buf_setpos(blobbuf, 0); + type = DROPBEAR_SIGNKEY_ANY; + if (buf_get_priv_key(blobbuf, retkey, &type) + != DROPBEAR_SUCCESS) { + errmsg = "unable to create key structure"; + sign_key_free(retkey); + retkey = NULL; + goto error; + } } errmsg = NULL; /* no error */ diff -r e378da7eae5d -r 33207ed1174b libtomcrypt/Makefile.in --- a/libtomcrypt/Makefile.in Wed Oct 16 22:55:03 2013 +0800 +++ b/libtomcrypt/Makefile.in Mon Oct 21 22:57:21 2013 +0800 @@ -19,7 +19,7 @@ # Compilation flags. Note the += does not write over the user's CFLAGS! # The rest of the flags come from the parent Dropbear makefile -CFLAGS += -c -I$(srcdir)/src/headers/ -I$(srcdir)/../ +CFLAGS += -c -I$(srcdir)/src/headers/ -I$(srcdir)/../ -DLTC_SOURCE -I$(srcdir)/../libtommath/ # additional warnings (newer GCC 3.4 and higher) ifdef GCC_34 @@ -157,7 +157,53 @@ src/modes/lrw/lrw_getiv.o src/modes/lrw/lrw_process.o src/modes/lrw/lrw_setiv.o \ src/modes/lrw/lrw_start.o src/modes/lrw/lrw_test.o src/modes/ofb/ofb_decrypt.o src/modes/ofb/ofb_done.o \ src/modes/ofb/ofb_encrypt.o src/modes/ofb/ofb_getiv.o src/modes/ofb/ofb_setiv.o \ -src/modes/ofb/ofb_start.o +src/modes/ofb/ofb_start.o src/pk/asn1/der/bit/der_decode_bit_string.o \ +src/pk/asn1/der/bit/der_encode_bit_string.o src/pk/asn1/der/bit/der_length_bit_string.o \ +src/pk/asn1/der/boolean/der_decode_boolean.o src/pk/asn1/der/boolean/der_encode_boolean.o \ +src/pk/asn1/der/boolean/der_length_boolean.o src/pk/asn1/der/choice/der_decode_choice.o \ +src/pk/asn1/der/ia5/der_decode_ia5_string.o src/pk/asn1/der/ia5/der_encode_ia5_string.o \ +src/pk/asn1/der/ia5/der_length_ia5_string.o src/pk/asn1/der/integer/der_decode_integer.o \ +src/pk/asn1/der/integer/der_encode_integer.o src/pk/asn1/der/integer/der_length_integer.o \ +src/pk/asn1/der/object_identifier/der_decode_object_identifier.o \ +src/pk/asn1/der/object_identifier/der_encode_object_identifier.o \ +src/pk/asn1/der/object_identifier/der_length_object_identifier.o \ +src/pk/asn1/der/octet/der_decode_octet_string.o src/pk/asn1/der/octet/der_encode_octet_string.o \ +src/pk/asn1/der/octet/der_length_octet_string.o \ +src/pk/asn1/der/printable_string/der_decode_printable_string.o \ +src/pk/asn1/der/printable_string/der_encode_printable_string.o \ +src/pk/asn1/der/printable_string/der_length_printable_string.o \ +src/pk/asn1/der/sequence/der_decode_sequence_ex.o \ +src/pk/asn1/der/sequence/der_decode_sequence_flexi.o \ +src/pk/asn1/der/sequence/der_decode_sequence_multi.o \ +src/pk/asn1/der/sequence/der_encode_sequence_ex.o \ +src/pk/asn1/der/sequence/der_encode_sequence_multi.o src/pk/asn1/der/sequence/der_length_sequence.o \ +src/pk/asn1/der/sequence/der_sequence_free.o src/pk/asn1/der/set/der_encode_set.o \ +src/pk/asn1/der/set/der_encode_setof.o src/pk/asn1/der/short_integer/der_decode_short_integer.o \ +src/pk/asn1/der/short_integer/der_encode_short_integer.o \ +src/pk/asn1/der/short_integer/der_length_short_integer.o src/pk/asn1/der/utctime/der_decode_utctime.o \ +src/pk/asn1/der/utctime/der_encode_utctime.o src/pk/asn1/der/utctime/der_length_utctime.o \ +src/pk/asn1/der/utf8/der_decode_utf8_string.o src/pk/asn1/der/utf8/der_encode_utf8_string.o \ +src/pk/asn1/der/utf8/der_length_utf8_string.o src/pk/dsa/dsa_decrypt_key.o \ +src/pk/dsa/dsa_encrypt_key.o src/pk/dsa/dsa_export.o src/pk/dsa/dsa_free.o src/pk/dsa/dsa_import.o \ +src/pk/dsa/dsa_make_key.o src/pk/dsa/dsa_shared_secret.o src/pk/dsa/dsa_sign_hash.o \ +src/pk/dsa/dsa_verify_hash.o src/pk/dsa/dsa_verify_key.o src/pk/ecc/ecc.o \ +src/pk/ecc/ecc_ansi_x963_export.o src/pk/ecc/ecc_ansi_x963_import.o src/pk/ecc/ecc_decrypt_key.o \ +src/pk/ecc/ecc_encrypt_key.o src/pk/ecc/ecc_export.o src/pk/ecc/ecc_free.o src/pk/ecc/ecc_get_size.o \ +src/pk/ecc/ecc_import.o src/pk/ecc/ecc_make_key.o src/pk/ecc/ecc_shared_secret.o \ +src/pk/ecc/ecc_sign_hash.o src/pk/ecc/ecc_sizes.o src/pk/ecc/ecc_test.o src/pk/ecc/ecc_verify_hash.o \ +src/pk/ecc/ltc_ecc_is_valid_idx.o src/pk/ecc/ltc_ecc_map.o src/pk/ecc/ltc_ecc_mul2add.o \ +src/pk/ecc/ltc_ecc_mulmod.o src/pk/ecc/ltc_ecc_mulmod_timing.o src/pk/ecc/ltc_ecc_points.o \ +src/pk/ecc/ltc_ecc_projective_add_point.o src/pk/ecc/ltc_ecc_projective_dbl_point.o \ +src/pk/katja/katja_decrypt_key.o src/pk/katja/katja_encrypt_key.o src/pk/katja/katja_export.o \ +src/pk/katja/katja_exptmod.o src/pk/katja/katja_free.o src/pk/katja/katja_import.o \ +src/pk/katja/katja_make_key.o src/pk/pkcs1/pkcs_1_i2osp.o src/pk/pkcs1/pkcs_1_mgf1.o \ +src/pk/pkcs1/pkcs_1_oaep_decode.o src/pk/pkcs1/pkcs_1_oaep_encode.o src/pk/pkcs1/pkcs_1_os2ip.o \ +src/pk/pkcs1/pkcs_1_pss_decode.o src/pk/pkcs1/pkcs_1_pss_encode.o src/pk/pkcs1/pkcs_1_v1_5_decode.o \ +src/pk/pkcs1/pkcs_1_v1_5_encode.o src/pk/rsa/rsa_decrypt_key.o src/pk/rsa/rsa_encrypt_key.o \ +src/pk/rsa/rsa_export.o src/pk/rsa/rsa_exptmod.o src/pk/rsa/rsa_free.o src/pk/rsa/rsa_import.o \ +src/pk/rsa/rsa_make_key.o src/pk/rsa/rsa_sign_hash.o src/pk/rsa/rsa_verify_hash.o src/prngs/fortuna.o \ +src/prngs/rc4.o src/prngs/rng_get_bytes.o src/prngs/rng_make_prng.o src/prngs/sober128.o \ +src/prngs/sprng.o src/prngs/yarrow.o HEADERS=src/headers/tomcrypt_cfg.h src/headers/tomcrypt_mac.h src/headers/tomcrypt_macros.h \ src/headers/tomcrypt_custom.h src/headers/tomcrypt_argchk.h src/headers/tomcrypt_cipher.h \ diff -r e378da7eae5d -r 33207ed1174b libtomcrypt/src/headers/tomcrypt.h --- a/libtomcrypt/src/headers/tomcrypt.h Wed Oct 16 22:55:03 2013 +0800 +++ b/libtomcrypt/src/headers/tomcrypt.h Mon Oct 21 22:57:21 2013 +0800 @@ -24,7 +24,7 @@ /* descriptor table size */ /* Dropbear change - this should be smaller, saves some size */ -#define TAB_SIZE 4 +#define TAB_SIZE 5 /* error codes [will be expanded in future releases] */ enum { diff -r e378da7eae5d -r 33207ed1174b libtomcrypt/src/headers/tomcrypt_custom.h --- a/libtomcrypt/src/headers/tomcrypt_custom.h Wed Oct 16 22:55:03 2013 +0800 +++ b/libtomcrypt/src/headers/tomcrypt_custom.h Mon Oct 21 22:57:21 2013 +0800 @@ -127,13 +127,32 @@ #ifdef DROPBEAR_SHA256 #define SHA256 #endif - +#ifdef DROPBEAR_SHA384 +#define SHA384 +#endif #ifdef DROPBEAR_SHA512 #define SHA512 #endif #define LTC_HMAC +#ifdef DROPBEAR_ECC +#define MECC +#define LTC_ECC_SHAMIR +#define LTC_ECC_TIMING_RESISTANT +#define MPI +#define LTM_DESC +#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 -r e378da7eae5d -r 33207ed1174b libtomcrypt/src/headers/tomcrypt_math.h --- a/libtomcrypt/src/headers/tomcrypt_math.h Wed Oct 16 22:55:03 2013 +0800 +++ b/libtomcrypt/src/headers/tomcrypt_math.h Mon Oct 21 22:57:21 2013 +0800 @@ -11,12 +11,9 @@ typedef void ecc_point; #endif -/* Dropbear has its own rsa_key. We just comment this out. */ -#if 0 #ifndef MRSA typedef void rsa_key; #endif -#endif /** math descriptor */ typedef struct { @@ -389,8 +386,6 @@ ecc_point *C, void *modulus); -/* Dropbear has its own rsa code */ -#if 0 /* ---- (optional) rsa optimized math (for internal CRT) ---- */ /** RSA Key Generation @@ -416,7 +411,6 @@ int (*rsa_me)(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen, int which, rsa_key *key); -#endif } ltc_math_descriptor; extern ltc_math_descriptor ltc_mp; diff -r e378da7eae5d -r 33207ed1174b libtomcrypt/src/misc/crypt/crypt_ltc_mp_descriptor.c --- a/libtomcrypt/src/misc/crypt/crypt_ltc_mp_descriptor.c Wed Oct 16 22:55:03 2013 +0800 +++ b/libtomcrypt/src/misc/crypt/crypt_ltc_mp_descriptor.c Mon Oct 21 22:57:21 2013 +0800 @@ -10,4 +10,4 @@ */ #include "tomcrypt.h" -ltc_math_descriptor ltc_mp; +ltc_math_descriptor ltc_mp = {0}; diff -r e378da7eae5d -r 33207ed1174b libtomcrypt/src/pk/ecc/ecc_decrypt_key.c --- a/libtomcrypt/src/pk/ecc/ecc_decrypt_key.c Wed Oct 16 22:55:03 2013 +0800 +++ b/libtomcrypt/src/pk/ecc/ecc_decrypt_key.c Mon Oct 21 22:57:21 2013 +0800 @@ -21,7 +21,7 @@ ECC Crypto, Tom St Denis */ -#ifdef MECC +#if defined(MECC) && defined(LTC_DER) /** Decrypt an ECC encrypted key diff -r e378da7eae5d -r 33207ed1174b libtomcrypt/src/pk/ecc/ecc_encrypt_key.c --- a/libtomcrypt/src/pk/ecc/ecc_encrypt_key.c Wed Oct 16 22:55:03 2013 +0800 +++ b/libtomcrypt/src/pk/ecc/ecc_encrypt_key.c Mon Oct 21 22:57:21 2013 +0800 @@ -21,7 +21,7 @@ ECC Crypto, Tom St Denis */ -#ifdef MECC +#if defined(MECC) && defined(LTC_DER) /** Encrypt a symmetric key with ECC diff -r e378da7eae5d -r 33207ed1174b libtomcrypt/src/pk/ecc/ecc_export.c --- a/libtomcrypt/src/pk/ecc/ecc_export.c Wed Oct 16 22:55:03 2013 +0800 +++ b/libtomcrypt/src/pk/ecc/ecc_export.c Mon Oct 21 22:57:21 2013 +0800 @@ -21,7 +21,7 @@ ECC Crypto, Tom St Denis */ -#ifdef MECC +#if defined(MECC) && defined(LTC_DER) /** Export an ECC key as a binary packet diff -r e378da7eae5d -r 33207ed1174b libtomcrypt/src/pk/ecc/ecc_import.c --- a/libtomcrypt/src/pk/ecc/ecc_import.c Wed Oct 16 22:55:03 2013 +0800 +++ b/libtomcrypt/src/pk/ecc/ecc_import.c Mon Oct 21 22:57:21 2013 +0800 @@ -21,7 +21,7 @@ ECC Crypto, Tom St Denis */ -#ifdef MECC +#if defined(MECC) && defined(LTC_DER) static int is_point(ecc_key *key) { diff -r e378da7eae5d -r 33207ed1174b libtomcrypt/src/pk/ecc/ecc_sign_hash.c --- a/libtomcrypt/src/pk/ecc/ecc_sign_hash.c Wed Oct 16 22:55:03 2013 +0800 +++ b/libtomcrypt/src/pk/ecc/ecc_sign_hash.c Mon Oct 21 22:57:21 2013 +0800 @@ -21,7 +21,7 @@ ECC Crypto, Tom St Denis */ -#ifdef MECC +#if defined(MECC) && defined(LTC_DER) /** Sign a message digest diff -r e378da7eae5d -r 33207ed1174b libtomcrypt/src/pk/ecc/ecc_verify_hash.c --- a/libtomcrypt/src/pk/ecc/ecc_verify_hash.c Wed Oct 16 22:55:03 2013 +0800 +++ b/libtomcrypt/src/pk/ecc/ecc_verify_hash.c Mon Oct 21 22:57:21 2013 +0800 @@ -21,7 +21,7 @@ ECC Crypto, Tom St Denis */ -#ifdef MECC +#if defined(MECC) && defined(LTC_DER) /* verify * diff -r e378da7eae5d -r 33207ed1174b ltc_prng.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ltc_prng.c Mon Oct 21 22:57:21 2013 +0800 @@ -0,0 +1,137 @@ +/* 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, tomstdenis@gmail.com, http://libtomcrypt.com + */ +#include "options.h" +#include "includes.h" +#include "random.h" +#include "ltc_prng.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* UNUSED(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* UNUSED(in), unsigned long UNUSED(inlen), prng_state* UNUSED(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* UNUSED(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* UNUSED(prng)) +{ + LTC_ARGCHK(out != NULL); + genrandom(out, outlen); + return outlen; +} + +/** + Terminate the PRNG + @param prng The PRNG to terminate + @return CRYPT_OK if successful +*/ +int dropbear_prng_done(prng_state* UNUSED(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* UNUSED(out), unsigned long* outlen, prng_state* UNUSED(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* UNUSED(in), unsigned long UNUSED(inlen), prng_state* UNUSED(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 diff -r e378da7eae5d -r 33207ed1174b ltc_prng.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ltc_prng.h Mon Oct 21 22:57:21 2013 +0800 @@ -0,0 +1,13 @@ +#ifndef _LTC_PRNG_H_DROPBEAR +#define _LTC_PRNG_H_DROPBEAR + +#include "options.h" +#include "includes.h" + +#ifdef DROPBEAR_LTC_PRNG + +extern const struct ltc_prng_descriptor dropbear_prng_desc; + +#endif // DROPBEAR_LTC_PRNG + +#endif // _LTC_PRNG_H_DROPBEAR \ No newline at end of file diff -r e378da7eae5d -r 33207ed1174b options.h --- a/options.h Wed Oct 16 22:55:03 2013 +0800 +++ b/options.h Mon Oct 21 22:57:21 2013 +0800 @@ -5,10 +5,10 @@ #ifndef _OPTIONS_H_ #define _OPTIONS_H_ -/****************************************************************** - * Define compile-time options below - the "#ifndef DROPBEAR_XXX .... #endif" - * parts are to allow for commandline -DDROPBEAR_XXX options etc. - ******************************************************************/ +/* Define compile-time options below - the "#ifndef DROPBEAR_XXX .... #endif" + * parts are to allow for commandline -DDROPBEAR_XXX options etc. */ + +/* Important: Many options will require "make clean" after changes */ #ifndef DROPBEAR_DEFPORT #define DROPBEAR_DEFPORT "22" @@ -26,6 +26,9 @@ #ifndef RSA_PRIV_FILENAME #define RSA_PRIV_FILENAME "/etc/dropbear/dropbear_rsa_host_key" #endif +#ifndef ECDSA_PRIV_FILENAME +#define ECDSA_PRIV_FILENAME "/etc/dropbear/dropbear_ecdsa_host_key" +#endif /* Set NON_INETD_MODE if you require daemon functionality (ie Dropbear listens * on chosen ports and keeps accepting connections. This is the default. @@ -49,7 +52,7 @@ several kB in binary size however will make the symmetrical ciphers and hashes slower, perhaps by 50%. Recommended for small systems that aren't doing much traffic. */ -/*#define DROPBEAR_SMALL_CODE*/ +#define DROPBEAR_SMALL_CODE /* Enable X11 Forwarding - server only */ #define ENABLE_X11FWD @@ -136,6 +139,9 @@ #define DROPBEAR_RSA #define DROPBEAR_DSS +#define DROPBEAR_ECDH +#define DROPBEAR_ECDSA + /* 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. */ diff -r e378da7eae5d -r 33207ed1174b random.c --- a/random.c Wed Oct 16 22:55:03 2013 +0800 +++ b/random.c Mon Oct 21 22:57:21 2013 +0800 @@ -28,6 +28,7 @@ #include "bignum.h" #include "random.h" + /* this is used to generate unique output from the same hashpool */ static uint32_t counter = 0; /* the max value for the counter, so it won't integer overflow */ diff -r e378da7eae5d -r 33207ed1174b random.h diff -r e378da7eae5d -r 33207ed1174b rsa.c --- a/rsa.c Wed Oct 16 22:55:03 2013 +0800 +++ b/rsa.c Mon Oct 21 22:57:21 2013 +0800 @@ -39,8 +39,7 @@ #ifdef DROPBEAR_RSA static void rsa_pad_em(dropbear_rsa_key * key, - const unsigned char * data, unsigned int len, - mp_int * rsa_em); + buffer *data_buf, mp_int * rsa_em); /* Load a public rsa key from a buffer, initialising the values. * The key will have the same format as buf_put_rsa_key. @@ -51,9 +50,7 @@ int ret = DROPBEAR_FAILURE; TRACE(("enter buf_get_rsa_pub_key")) dropbear_assert(key != NULL); - key->e = m_malloc(sizeof(mp_int)); - key->n = m_malloc(sizeof(mp_int)); - m_mp_init_multi(key->e, key->n, NULL); + m_mp_alloc_init_multi(&key->e, &key->n, NULL); key->d = NULL; key->p = NULL; key->q = NULL; @@ -99,8 +96,7 @@ key->p = NULL; key->q = NULL; - key->d = m_malloc(sizeof(mp_int)); - m_mp_init(key->d); + m_mp_alloc_init_multi(&key->d, NULL); if (buf_getmpint(buf, key->d) == DROPBEAR_FAILURE) { TRACE(("leave buf_get_rsa_priv_key: d: ret == DROPBEAR_FAILURE")) goto out; @@ -109,9 +105,7 @@ if (buf->pos == buf->len) { /* old Dropbear private keys didn't keep p and q, so we will ignore them*/ } else { - key->p = m_malloc(sizeof(mp_int)); - key->q = m_malloc(sizeof(mp_int)); - m_mp_init_multi(key->p, key->q, NULL); + m_mp_alloc_init_multi(&key->p, &key->q, NULL); if (buf_getmpint(buf, key->p) == DROPBEAR_FAILURE) { TRACE(("leave buf_get_rsa_priv_key: p: ret == DROPBEAR_FAILURE")) @@ -213,9 +207,7 @@ #ifdef DROPBEAR_SIGNKEY_VERIFY /* Verify a signature in buf, made on data by the key given. * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ -int buf_rsa_verify(buffer * buf, dropbear_rsa_key *key, const unsigned char* data, - unsigned int len) { - +int buf_rsa_verify(buffer * buf, dropbear_rsa_key *key, buffer *data_buf) { unsigned int slen; DEF_MP_INT(rsa_s); DEF_MP_INT(rsa_mdash); @@ -247,7 +239,7 @@ } /* create the magic PKCS padded value */ - rsa_pad_em(key, data, len, &rsa_em); + rsa_pad_em(key, data_buf, &rsa_em); if (mp_exptmod(&rsa_s, key->e, key->n, &rsa_mdash) != MP_OKAY) { TRACE(("failed exptmod rsa_s")) @@ -270,9 +262,7 @@ /* Sign the data presented with key, writing the signature contents * to the buffer */ -void buf_put_rsa_sign(buffer* buf, dropbear_rsa_key *key, const unsigned char* data, - unsigned int len) { - +void buf_put_rsa_sign(buffer* buf, dropbear_rsa_key *key, buffer *data_buf) { unsigned int nsize, ssize; unsigned int i; DEF_MP_INT(rsa_s); @@ -285,7 +275,7 @@ m_mp_init_multi(&rsa_s, &rsa_tmp1, &rsa_tmp2, &rsa_tmp3, NULL); - rsa_pad_em(key, data, len, &rsa_tmp1); + rsa_pad_em(key, data_buf, &rsa_tmp1); /* the actual signing of the padded data */ @@ -377,8 +367,7 @@ * rsa_em must be a pointer to an initialised mp_int. */ static void rsa_pad_em(dropbear_rsa_key * key, - const unsigned char * data, unsigned int len, - mp_int * rsa_em) { + buffer *data_buf, mp_int * rsa_em) { /* ASN1 designator (including the 0x00 preceding) */ const unsigned char rsa_asn1_magic[] = @@ -391,7 +380,6 @@ unsigned int nsize; dropbear_assert(key != NULL); - dropbear_assert(data != NULL); nsize = mp_unsigned_bin_size(key->n); rsa_EM = buf_new(nsize-1); @@ -408,7 +396,7 @@ /* The hash of the data */ sha1_init(&hs); - sha1_process(&hs, data, len); + sha1_process(&hs, data_buf->data, data_buf->len); sha1_done(&hs, buf_getwriteptr(rsa_EM, SHA1_HASH_SIZE)); buf_incrwritepos(rsa_EM, SHA1_HASH_SIZE); diff -r e378da7eae5d -r 33207ed1174b rsa.h --- a/rsa.h Wed Oct 16 22:55:03 2013 +0800 +++ b/rsa.h Mon Oct 21 22:57:21 2013 +0800 @@ -43,11 +43,9 @@ } dropbear_rsa_key; -void buf_put_rsa_sign(buffer* buf, dropbear_rsa_key *key, const unsigned char* data, - unsigned int len); +void buf_put_rsa_sign(buffer* buf, dropbear_rsa_key *key, buffer *data_buf); #ifdef DROPBEAR_SIGNKEY_VERIFY -int buf_rsa_verify(buffer * buf, dropbear_rsa_key *key, const unsigned char* data, - unsigned int len); +int buf_rsa_verify(buffer * buf, dropbear_rsa_key *key, buffer *data_buf); #endif int buf_get_rsa_pub_key(buffer* buf, dropbear_rsa_key *key); int buf_get_rsa_priv_key(buffer* buf, dropbear_rsa_key *key); diff -r e378da7eae5d -r 33207ed1174b runopts.h --- a/runopts.h Wed Oct 16 22:55:03 2013 +0800 +++ b/runopts.h Mon Oct 21 22:57:21 2013 +0800 @@ -57,11 +57,10 @@ extern runopts opts; int readhostkey(const char * filename, sign_key * hostkey, int *type); +void load_all_hostkeys(); typedef struct svr_runopts { - char * rsakeyfile; - char * dsskeyfile; char * bannerfile; int forkbg; @@ -99,6 +98,10 @@ #endif sign_key *hostkey; + + char *hostkey_files[MAX_HOSTKEYS]; + int num_hostkey_files; + buffer * banner; char * pidfile; diff -r e378da7eae5d -r 33207ed1174b session.h --- a/session.h Wed Oct 16 22:55:03 2013 +0800 +++ b/session.h Mon Oct 21 22:57:21 2013 +0800 @@ -66,7 +66,7 @@ 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_directional recv; struct key_context_directional trans; - char algo_kex; - char algo_hostkey; + const struct dropbear_kex *algo_kex; + int algo_hostkey; int allow_compress; /* whether compression has started (useful in zlib@openssh.com delayed compression case) */ @@ -158,10 +158,10 @@ struct KEXState kexstate; struct key_context *keys; struct key_context *newkeys; - unsigned char *session_id; /* this is the hash from the first kex */ - /* The below are used temorarily during kex, are freed after use */ + buffer *session_id; /* this is the hash from the first kex */ + /* The below are used temporarily during kex, are freed after use */ mp_int * dh_K; /* SSH_MSG_KEXDH_REPLY and sending SSH_MSH_NEWKEYS */ - unsigned char hash[SHA1_HASH_SIZE]; /* the hash*/ + buffer *hash; /* the session hash */ buffer* kexhashbuf; /* session hash buffer calculated from various packets*/ buffer* transkexinit; /* the kexinit packet we send should be kept so we can add it to the hash when generating keys */ @@ -241,8 +241,11 @@ struct clientsession { - mp_int *dh_e, *dh_x; /* Used during KEX */ - int dh_val_algo; /* KEX algorithm corresponding to current dh_e and dh_x */ + // XXX - move these to kexstate? + struct kex_dh_param *dh_param; + struct kex_ecdh_param *ecdh_param; + const struct dropbear_kex *param_kex_algo; /* KEX algorithm corresponding to current dh_e and dh_x */ + 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 */ diff -r e378da7eae5d -r 33207ed1174b signkey.c --- a/signkey.c Wed Oct 16 22:55:03 2013 +0800 +++ b/signkey.c Mon Oct 21 22:57:21 2013 +0800 @@ -27,6 +27,22 @@ #include "signkey.h" #include "buffer.h" #include "ssh.h" +#include "ecdsa.h" + +static const char *signkey_names[DROPBEAR_SIGNKEY_NUM_NAMED] = { +#ifdef DROPBEAR_RSA + "ssh-rsa", +#endif +#ifdef DROPBEAR_DSS + "ssh-dss", +#endif +#ifdef DROPBEAR_ECDSA + "ecdsa-sha2-nistp256", + "ecdsa-sha2-nistp384", + "ecdsa-sha2-nistp521", + "ecdsa" // for keygen +#endif // DROPBEAR_ECDSA +}; /* malloc a new sign_key and set the dss and rsa keys to NULL */ sign_key * new_sign_key() { @@ -34,60 +50,75 @@ sign_key * ret; ret = (sign_key*)m_malloc(sizeof(sign_key)); -#ifdef DROPBEAR_DSS - ret->dsskey = NULL; -#endif -#ifdef DROPBEAR_RSA - ret->rsakey = NULL; -#endif - ret->filename = NULL; ret->type = DROPBEAR_SIGNKEY_NONE; ret->source = SIGNKEY_SOURCE_INVALID; return ret; } -/* Returns "ssh-dss" or "ssh-rsa" corresponding to the type. Exits fatally +/* Returns key name corresponding to the type. Exits fatally * if the type is invalid */ -const char* signkey_name_from_type(int type, int *namelen) { - -#ifdef DROPBEAR_RSA - if (type == DROPBEAR_SIGNKEY_RSA) { - *namelen = SSH_SIGNKEY_RSA_LEN; - return SSH_SIGNKEY_RSA; +const char* signkey_name_from_type(enum signkey_type type, unsigned int *namelen) { + if (type >= DROPBEAR_SIGNKEY_NUM_NAMED) { + dropbear_exit("Bad key type %d", type); } -#endif -#ifdef DROPBEAR_DSS - if (type == DROPBEAR_SIGNKEY_DSS) { - *namelen = SSH_SIGNKEY_DSS_LEN; - return SSH_SIGNKEY_DSS; + + if (namelen) { + *namelen = strlen(signkey_names[type]); } -#endif - dropbear_exit("Bad key type %d", type); - return NULL; /* notreached */ + return signkey_names[type]; } -/* Returns DROPBEAR_SIGNKEY_RSA, DROPBEAR_SIGNKEY_DSS, - * or DROPBEAR_SIGNKEY_NONE */ -int signkey_type_from_name(const char* name, int namelen) { +/* Returns DROPBEAR_SIGNKEY_NONE if none match */ +enum signkey_type signkey_type_from_name(const char* name, unsigned int namelen) { + int i; + for (i = 0; i < DROPBEAR_SIGNKEY_NUM_NAMED; i++) { + const char *fixed_name = signkey_names[i]; + if (namelen == strlen(fixed_name) + && memcmp(fixed_name, name, namelen) == 0) { -#ifdef DROPBEAR_RSA - if (namelen == SSH_SIGNKEY_RSA_LEN - && memcmp(name, SSH_SIGNKEY_RSA, SSH_SIGNKEY_RSA_LEN) == 0) { - return DROPBEAR_SIGNKEY_RSA; - } +#ifdef DROPBEAR_ECDSA + /* Some of the ECDSA key sizes are defined even if they're not compiled in */ + if (0 +#ifndef DROPBEAR_ECC_256 + || i == DROPBEAR_SIGNKEY_ECDSA_NISTP256 +#endif +#ifndef DROPBEAR_ECC_384 + || i == DROPBEAR_SIGNKEY_ECDSA_NISTP384 #endif -#ifdef DROPBEAR_DSS - if (namelen == SSH_SIGNKEY_DSS_LEN - && memcmp(name, SSH_SIGNKEY_DSS, SSH_SIGNKEY_DSS_LEN) == 0) { - return DROPBEAR_SIGNKEY_DSS; +#ifndef DROPBEAR_ECC_521 + || i == DROPBEAR_SIGNKEY_ECDSA_NISTP521 +#endif + ) { + TRACE(("attempt to use ecdsa type %d not compiled in", i)) + return DROPBEAR_SIGNKEY_NONE; + } +#endif + + return i; + } } -#endif TRACE(("signkey_type_from_name unexpected key type.")) return DROPBEAR_SIGNKEY_NONE; } +#ifdef DROPBEAR_ECDSA +ecc_key ** +signkey_ecc_key_ptr(sign_key *key, enum signkey_type ecc_type) { + switch (ecc_type) { + case DROPBEAR_SIGNKEY_ECDSA_NISTP256: + return &key->ecckey256; + case DROPBEAR_SIGNKEY_ECDSA_NISTP384: + return &key->ecckey384; + case DROPBEAR_SIGNKEY_ECDSA_NISTP521: + return &key->ecckey521; + default: + return NULL; + } +} +#endif + /* returns DROPBEAR_SUCCESS on success, DROPBEAR_FAILURE on fail. * type should be set by the caller to specify the type to read, and * on return is set to the type read (useful when type = _ANY) */ @@ -136,6 +167,21 @@ } } #endif +#ifdef DROPBEAR_ECDSA + { + ecc_key **eck = signkey_ecc_key_ptr(key, keytype); + if (eck) { + if (*eck) { + ecc_free(*eck); + *eck = NULL; + } + *eck = buf_get_ecdsa_pub_key(buf); + if (*eck) { + ret = DROPBEAR_SUCCESS; + } + } + } +#endif TRACE2(("leave buf_get_pub_key")) @@ -189,6 +235,21 @@ } } #endif +#ifdef DROPBEAR_ECDSA + { + ecc_key **eck = signkey_ecc_key_ptr(key, keytype); + if (eck) { + if (*eck) { + ecc_free(*eck); + *eck = NULL; + } + *eck = buf_get_ecdsa_priv_key(buf); + if (*eck) { + ret = DROPBEAR_SUCCESS; + } + } + } +#endif TRACE2(("leave buf_get_priv_key")) @@ -214,14 +275,19 @@ buf_put_rsa_pub_key(pubkeys, key->rsakey); } #endif +#ifdef DROPBEAR_ECDSA + { + ecc_key **eck = signkey_ecc_key_ptr(key, type); + if (eck) { + buf_put_ecdsa_pub_key(pubkeys, *eck); + } + } +#endif if (pubkeys->len == 0) { dropbear_exit("Bad key types in buf_put_pub_key"); } - buf_setpos(pubkeys, 0); - buf_putstring(buf, buf_getptr(pubkeys, pubkeys->len), - pubkeys->len); - + buf_putbufstring(buf, pubkeys); buf_free(pubkeys); TRACE2(("leave buf_put_pub_key")) } @@ -246,6 +312,16 @@ return; } #endif +#ifdef DROPBEAR_ECDSA + { + ecc_key **eck = signkey_ecc_key_ptr(key, type); + if (eck) { + buf_put_ecdsa_priv_key(buf, *eck); + TRACE(("leave buf_put_priv_key: ecdsa done")) + return; + } + } +#endif dropbear_exit("Bad key types in put pub key"); } @@ -261,6 +337,20 @@ rsa_key_free(key->rsakey); key->rsakey = NULL; #endif +#ifdef DROPBEAR_ECDSA + if (key->ecckey256) { + ecc_free(key->ecckey256); + key->ecckey256 = NULL; + } + if (key->ecckey384) { + ecc_free(key->ecckey384); + key->ecckey384 = NULL; + } + if (key->ecckey521) { + ecc_free(key->ecckey521); + key->ecckey521 = NULL; + } +#endif m_free(key->filename); @@ -269,7 +359,6 @@ } static char hexdig(unsigned char x) { - if (x > 0xf) return 'X'; @@ -333,14 +422,14 @@ sha1_done(&hs, hash); - /* "sha1 hexfingerprinthere\0", each hex digit is "AB:" etc */ - buflen = 5 + 3*SHA1_HASH_SIZE; + /* "sha1!! hexfingerprinthere\0", each hex digit is "AB:" etc */ + buflen = 7 + 3*SHA1_HASH_SIZE; ret = (char*)m_malloc(buflen); - strcpy(ret, "sha1 "); + strcpy(ret, "sha1!! "); for (i = 0; i < SHA1_HASH_SIZE; i++) { - unsigned int pos = 5 + 3*i; + unsigned int pos = 7 + 3*i; ret[pos] = hexdig(hash[i] >> 4); ret[pos+1] = hexdig(hash[i] & 0x0f); ret[pos+2] = ':'; @@ -364,28 +453,32 @@ } void buf_put_sign(buffer* buf, sign_key *key, int type, - const unsigned char *data, unsigned int len) { - + buffer *data_buf) { buffer *sigblob; sigblob = buf_new(MAX_PUBKEY_SIZE); #ifdef DROPBEAR_DSS if (type == DROPBEAR_SIGNKEY_DSS) { - buf_put_dss_sign(sigblob, key->dsskey, data, len); + buf_put_dss_sign(sigblob, key->dsskey, data_buf); } #endif #ifdef DROPBEAR_RSA if (type == DROPBEAR_SIGNKEY_RSA) { - buf_put_rsa_sign(sigblob, key->rsakey, data, len); + buf_put_rsa_sign(sigblob, key->rsakey, data_buf); + } +#endif +#ifdef DROPBEAR_ECDSA + { + ecc_key **eck = signkey_ecc_key_ptr(key, type); + if (eck) { + buf_put_ecdsa_sign(sigblob, *eck, data_buf); + } } #endif if (sigblob->len == 0) { dropbear_exit("Non-matching signing type"); } - buf_setpos(sigblob, 0); - buf_putstring(buf, buf_getptr(sigblob, sigblob->len), - sigblob->len); - + buf_putbufstring(buf, sigblob); buf_free(sigblob); } @@ -395,40 +488,45 @@ * If FAILURE is returned, the position of * buf is undefined. If SUCCESS is returned, buf will be positioned after the * signature blob */ -int buf_verify(buffer * buf, sign_key *key, const unsigned char *data, - unsigned int len) { +int buf_verify(buffer * buf, sign_key *key, buffer *data_buf) { unsigned int bloblen; - unsigned char * ident = NULL; - unsigned int identlen = 0; + unsigned char * type_name = NULL; + unsigned int type_name_len = 0; TRACE(("enter buf_verify")) bloblen = buf_getint(buf); - ident = buf_getstring(buf, &identlen); + type_name = buf_getstring(buf, &type_name_len); + enum signkey_type type = signkey_type_from_name(type_name, type_name_len); + m_free(type_name); #ifdef DROPBEAR_DSS - if (bloblen == DSS_SIGNATURE_SIZE && - memcmp(ident, SSH_SIGNKEY_DSS, identlen) == 0) { - m_free(ident); + if (type == DROPBEAR_SIGNKEY_DSS) { if (key->dsskey == NULL) { dropbear_exit("No DSS key to verify signature"); } - return buf_dss_verify(buf, key->dsskey, data, len); + return buf_dss_verify(buf, key->dsskey, data_buf); } #endif #ifdef DROPBEAR_RSA - if (memcmp(ident, SSH_SIGNKEY_RSA, identlen) == 0) { - m_free(ident); + if (type == DROPBEAR_SIGNKEY_RSA) { if (key->rsakey == NULL) { dropbear_exit("No RSA key to verify signature"); } - return buf_rsa_verify(buf, key->rsakey, data, len); + return buf_rsa_verify(buf, key->rsakey, data_buf); + } +#endif +#ifdef DROPBEAR_ECDSA + { + ecc_key **eck = signkey_ecc_key_ptr(key, type); + if (eck) { + return buf_ecdsa_verify(buf, *eck, data_buf); + } } #endif - m_free(ident); dropbear_exit("Non-matching signing type"); return DROPBEAR_FAILURE; } diff -r e378da7eae5d -r 33207ed1174b signkey.h --- a/signkey.h Wed Oct 16 22:55:03 2013 +0800 +++ b/signkey.h Mon Oct 21 22:57:21 2013 +0800 @@ -29,6 +29,24 @@ #include "dss.h" #include "rsa.h" +enum signkey_type { +#ifdef DROPBEAR_RSA + DROPBEAR_SIGNKEY_RSA, +#endif +#ifdef DROPBEAR_DSS + DROPBEAR_SIGNKEY_DSS, +#endif +#ifdef DROPBEAR_ECDSA + DROPBEAR_SIGNKEY_ECDSA_NISTP256, + DROPBEAR_SIGNKEY_ECDSA_NISTP384, + DROPBEAR_SIGNKEY_ECDSA_NISTP521, + DROPBEAR_SIGNKEY_ECDSA_KEYGEN, // just "ecdsa" for keygen +#endif // DROPBEAR_ECDSA + DROPBEAR_SIGNKEY_NUM_NAMED, + DROPBEAR_SIGNKEY_ANY = 80, + DROPBEAR_SIGNKEY_NONE = 90, +}; + /* Sources for signing keys */ typedef enum { @@ -39,11 +57,9 @@ struct SIGN_key { - int type; /* The type of key (dss or rsa) */ + enum signkey_type type; signkey_source source; char *filename; - /* the buffer? for encrypted keys, so we can later get - * the private key portion */ #ifdef DROPBEAR_DSS dropbear_dss_key * dsskey; @@ -51,27 +67,40 @@ #ifdef DROPBEAR_RSA dropbear_rsa_key * rsakey; #endif +#ifdef DROPBEAR_ECDSA +#ifdef DROPBEAR_ECC_256 + ecc_key * ecckey256; +#endif +#ifdef DROPBEAR_ECC_384 + ecc_key * ecckey384; +#endif +#ifdef DROPBEAR_ECC_521 + ecc_key * ecckey521; +#endif +#endif }; typedef struct SIGN_key sign_key; sign_key * new_sign_key(); -const char* signkey_name_from_type(int type, int *namelen); -int signkey_type_from_name(const char* name, int namelen); +const char* signkey_name_from_type(enum signkey_type type, unsigned int *namelen); +enum signkey_type signkey_type_from_name(const char* name, unsigned int namelen); int buf_get_pub_key(buffer *buf, sign_key *key, int *type); int buf_get_priv_key(buffer* buf, sign_key *key, int *type); void buf_put_pub_key(buffer* buf, sign_key *key, int type); void buf_put_priv_key(buffer* buf, sign_key *key, int type); void sign_key_free(sign_key *key); -void buf_put_sign(buffer* buf, sign_key *key, int type, - const unsigned char *data, unsigned int len); +void buf_put_sign(buffer* buf, sign_key *key, int type, buffer *data_buf); #ifdef DROPBEAR_SIGNKEY_VERIFY -int buf_verify(buffer * buf, sign_key *key, const unsigned char *data, - unsigned int len); +int buf_verify(buffer * buf, sign_key *key, buffer *data_buf); char * sign_key_fingerprint(unsigned char* keyblob, unsigned int keybloblen); #endif int cmp_base64_key(const unsigned char* keyblob, unsigned int keybloblen, const unsigned char* algoname, unsigned int algolen, buffer * line, char ** fingerprint); +#ifdef DROPBEAR_ECDSA +ecc_key ** signkey_ecc_key_ptr(sign_key *key, enum signkey_type ecc_type); +#endif + #endif /* _SIGNKEY_H_ */ diff -r e378da7eae5d -r 33207ed1174b svr-auth.c --- a/svr-auth.c Wed Oct 16 22:55:03 2013 +0800 +++ b/svr-auth.c Mon Oct 21 22:57:21 2013 +0800 @@ -88,8 +88,7 @@ CHECKCLEARTOWRITE(); buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_BANNER); - buf_putstring(ses.writepayload, buf_getptr(banner, banner->len), - banner->len); + buf_putbufstring(ses.writepayload, banner); buf_putstring(ses.writepayload, "en", 2); encrypt_packet(); @@ -344,12 +343,10 @@ buf_putbytes(typebuf, AUTH_METHOD_PASSWORD, AUTH_METHOD_PASSWORD_LEN); } - buf_setpos(typebuf, 0); - buf_putstring(ses.writepayload, buf_getptr(typebuf, typebuf->len), - typebuf->len); + buf_putbufstring(ses.writepayload, typebuf); - TRACE(("auth fail: methods %d, '%s'", ses.authstate.authtypes, - buf_getptr(typebuf, typebuf->len))); + TRACE(("auth fail: methods %d, '%.*s'", ses.authstate.authtypes, + typebuf->len, typebuf->data)) buf_free(typebuf); diff -r e378da7eae5d -r 33207ed1174b svr-authpubkey.c --- a/svr-authpubkey.c Wed Oct 16 22:55:03 2013 +0800 +++ b/svr-authpubkey.c Mon Oct 21 22:57:21 2013 +0800 @@ -125,15 +125,14 @@ /* create the data which has been signed - this a string containing * session_id, concatenated with the payload packet up to the signature */ - signbuf = buf_new(ses.payload->pos + 4 + SHA1_HASH_SIZE); - buf_putstring(signbuf, ses.session_id, SHA1_HASH_SIZE); + signbuf = buf_new(ses.payload->pos + 4 + ses.session_id->len); + buf_putbufstring(signbuf, ses.session_id); buf_putbytes(signbuf, ses.payload->data, ses.payload->pos); buf_setpos(signbuf, 0); /* ... and finally verify the signature */ fp = sign_key_fingerprint(keyblob, keybloblen); - if (buf_verify(ses.payload, key, buf_getptr(signbuf, signbuf->len), - signbuf->len) == DROPBEAR_SUCCESS) { + if (buf_verify(ses.payload, key, signbuf) == DROPBEAR_SUCCESS) { dropbear_log(LOG_NOTICE, "Pubkey auth succeeded for '%s' with key %s from %s", ses.authstate.pw_name, fp, svr_ses.addrstring); diff -r e378da7eae5d -r 33207ed1174b svr-kex.c --- a/svr-kex.c Wed Oct 16 22:55:03 2013 +0800 +++ b/svr-kex.c Mon Oct 21 22:57:21 2013 +0800 @@ -34,9 +34,9 @@ #include "bignum.h" #include "random.h" #include "runopts.h" - +#include "ecc.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,30 @@ 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 + ecdh_qs = buf_getstringbuf(ses.payload); +#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[0] = SSH_MSG_NEWKEYS; @@ -71,19 +81,10 @@ * 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(); @@ -91,13 +92,27 @@ 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_raw_pubkey_string(ses.writepayload, &ecdh_param->key); + free_kexecdh_param(ecdh_param); +#endif + } /* calc the signature */ buf_put_sign(ses.writepayload, svr_opts.hostkey, - ses.newkeys->algo_hostkey, ses.hash, SHA1_HASH_SIZE); + ses.newkeys->algo_hostkey, ses.hash); /* the SSH_MSG_KEXDH_REPLY is done */ encrypt_packet(); diff -r e378da7eae5d -r 33207ed1174b svr-main.c --- a/svr-main.c Wed Oct 16 22:55:03 2013 +0800 +++ b/svr-main.c Mon Oct 21 22:57:21 2013 +0800 @@ -29,6 +29,7 @@ #include "signkey.h" #include "runopts.h" #include "random.h" +#include "crypto_desc.h" static size_t listensockets(int *sock, size_t sockcount, int *maxfd); static void sigchld_handler(int dummy); @@ -383,9 +384,11 @@ dropbear_exit("signal() error"); } + crypto_init(); + /* Now we can setup the hostkeys - needs to be after logging is on, * otherwise we might end up blatting error messages to the socket */ - loadhostkeys(); + load_all_hostkeys(); seedrandom(); } diff -r e378da7eae5d -r 33207ed1174b svr-runopts.c --- a/svr-runopts.c Wed Oct 16 22:55:03 2013 +0800 +++ b/svr-runopts.c Mon Oct 21 22:57:21 2013 +0800 @@ -28,11 +28,14 @@ #include "buffer.h" #include "dbutil.h" #include "algo.h" +#include "ecdsa.h" svr_runopts svr_opts; /* GLOBAL */ static void printhelp(const char * progname); static void addportandaddress(char* spec); +static void loadhostkey(const char *keyfile, int fatal_duplicate); +static void addhostkey(const char *keyfile); static void printhelp(const char * progname) { @@ -105,10 +108,10 @@ char* recv_window_arg = NULL; char* keepalive_arg = NULL; char* idle_timeout_arg = NULL; + char* keyfile = NULL; + /* see printhelp() for options */ - svr_opts.rsakeyfile = NULL; - svr_opts.dsskeyfile = NULL; svr_opts.bannerfile = NULL; svr_opts.banner = NULL; svr_opts.forkbg = 1; @@ -160,6 +163,11 @@ dropbear_exit("Invalid null argument"); } next = 0x00; + + if (keyfile) { + addhostkey(keyfile); + keyfile = NULL; + } continue; } @@ -168,16 +176,10 @@ case 'b': next = &svr_opts.bannerfile; break; -#ifdef DROPBEAR_DSS case 'd': - next = &svr_opts.dsskeyfile; + case 'r': + next = &keyfile; break; -#endif -#ifdef DROPBEAR_RSA - case 'r': - next = &svr_opts.rsakeyfile; - break; -#endif case 'F': svr_opts.forkbg = 0; break; @@ -267,13 +269,6 @@ svr_opts.portcount = 1; } - if (svr_opts.dsskeyfile == NULL) { - svr_opts.dsskeyfile = DSS_PRIV_FILENAME; - } - if (svr_opts.rsakeyfile == NULL) { - svr_opts.rsakeyfile = RSA_PRIV_FILENAME; - } - if (svr_opts.bannerfile) { struct stat buf; if (stat(svr_opts.bannerfile, &buf) != 0) { @@ -292,7 +287,6 @@ svr_opts.bannerfile); } buf_setpos(svr_opts.banner, 0); - } if (recv_window_arg) { @@ -370,55 +364,126 @@ } } -static void disablekey(int type, const char* filename) { - +static void disablekey(int type) { int i; - + TRACE(("Disabling key type %d", type)) for (i = 0; sshhostkey[i].name != NULL; i++) { if (sshhostkey[i].val == type) { sshhostkey[i].usable = 0; break; } } - dropbear_log(LOG_WARNING, "Failed reading '%s', disabling %s", filename, - type == DROPBEAR_SIGNKEY_DSS ? "DSS" : "RSA"); +} + +static void loadhostkey_helper(const char *name, void** src, void** dst, int fatal_duplicate) { + if (*dst) { + if (fatal_duplicate) { + dropbear_exit("Only one %s key can be specified", name); + } + } else { + *dst = *src; + *src = NULL; + } + } /* Must be called after syslog/etc is working */ -void loadhostkeys() { +static void loadhostkey(const char *keyfile, int fatal_duplicate) { + sign_key * read_key = new_sign_key(); + int type = DROPBEAR_SIGNKEY_ANY; + if (readhostkey(keyfile, read_key, &type) == DROPBEAR_FAILURE) { + dropbear_log(LOG_WARNING, "Failed loading %s", keyfile); + } + +#ifdef DROPBEAR_RSA + if (type == DROPBEAR_SIGNKEY_RSA) { + loadhostkey_helper("RSA", &read_key->rsakey, &svr_opts.hostkey->rsakey, fatal_duplicate); + } +#endif + +#ifdef DROPBEAR_DSS + if (type == DROPBEAR_SIGNKEY_DSS) { + loadhostkey_helper("DSS", &read_key->dsskey, &svr_opts.hostkey->dsskey, fatal_duplicate); + } +#endif - int ret; - int type; +#ifdef DROPBEAR_ECDSA +#ifdef DROPBEAR_ECC_256 + if (type == DROPBEAR_SIGNKEY_ECDSA_NISTP256) { + loadhostkey_helper("ECDSA256", &read_key->ecckey256, &svr_opts.hostkey->ecckey256, fatal_duplicate); + } +#endif +#ifdef DROPBEAR_ECC_384 + if (type == DROPBEAR_SIGNKEY_ECDSA_NISTP384) { + loadhostkey_helper("ECDSA384", &read_key->ecckey384, &svr_opts.hostkey->ecckey384, fatal_duplicate); + } +#endif +#ifdef DROPBEAR_ECC_521 + if (type == DROPBEAR_SIGNKEY_ECDSA_NISTP521) { + loadhostkey_helper("ECDSA521", &read_key->ecckey521, &svr_opts.hostkey->ecckey521, fatal_duplicate); + } +#endif +#endif // DROPBEAR_ECDSA + sign_key_free(read_key); + TRACE(("leave loadhostkey")) +} - TRACE(("enter loadhostkeys")) +static void addhostkey(const char *keyfile) { + if (svr_opts.num_hostkey_files >= MAX_HOSTKEYS) { + dropbear_exit("Too many hostkeys"); + } + svr_opts.hostkey_files[svr_opts.num_hostkey_files] = m_strdup(keyfile); + svr_opts.num_hostkey_files++; +} + +void load_all_hostkeys() { + int i; svr_opts.hostkey = new_sign_key(); + for (i = 0; i < svr_opts.num_hostkey_files; i++) { + char *hostkey_file = svr_opts.hostkey_files[i]; + loadhostkey(hostkey_file, 1); + m_free(hostkey_file); + } + #ifdef DROPBEAR_RSA - type = DROPBEAR_SIGNKEY_RSA; - ret = readhostkey(svr_opts.rsakeyfile, svr_opts.hostkey, &type); - if (ret == DROPBEAR_FAILURE) { - disablekey(DROPBEAR_SIGNKEY_RSA, svr_opts.rsakeyfile); + loadhostkey(RSA_PRIV_FILENAME, 0); +#endif + +#ifdef DROPBEAR_DSS + loadhostkey(DSS_PRIV_FILENAME, 0); +#endif + +#ifdef DROPBEAR_ECDSA + loadhostkey(ECDSA_PRIV_FILENAME, 0); +#endif + +#ifdef DROPBEAR_RSA + if (!svr_opts.hostkey->rsakey) { + disablekey(DROPBEAR_SIGNKEY_RSA); } #endif #ifdef DROPBEAR_DSS - type = DROPBEAR_SIGNKEY_DSS; - ret = readhostkey(svr_opts.dsskeyfile, svr_opts.hostkey, &type); - if (ret == DROPBEAR_FAILURE) { - disablekey(DROPBEAR_SIGNKEY_DSS, svr_opts.dsskeyfile); + if (!svr_opts.hostkey->dsskey) { + disablekey(DROPBEAR_SIGNKEY_RSA); + } +#endif +#ifdef DROPBEAR_ECDSA +#ifdef DROPBEAR_ECC_256 + if (!svr_opts.hostkey->ecckey256) { + disablekey(DROPBEAR_SIGNKEY_ECDSA_NISTP256); } #endif - - if ( 1 -#ifdef DROPBEAR_DSS - && svr_opts.hostkey->dsskey == NULL +#ifdef DROPBEAR_ECC_384 + if (!svr_opts.hostkey->ecckey384) { + disablekey(DROPBEAR_SIGNKEY_ECDSA_NISTP384); + } #endif -#ifdef DROPBEAR_RSA - && svr_opts.hostkey->rsakey == NULL +#ifdef DROPBEAR_ECC_521 + if (!svr_opts.hostkey->ecckey521) { + disablekey(DROPBEAR_SIGNKEY_ECDSA_NISTP521); + } #endif - ) { - dropbear_exit("No hostkeys available"); - } - - TRACE(("leave loadhostkeys")) +#endif } diff -r e378da7eae5d -r 33207ed1174b svr-session.c --- a/svr-session.c Wed Oct 16 22:55:03 2013 +0800 +++ b/svr-session.c Mon Oct 21 22:57:21 2013 +0800 @@ -39,6 +39,7 @@ #include "service.h" #include "auth.h" #include "runopts.h" +#include "crypto_desc.h" static void svr_remoteclosed(); @@ -83,7 +84,6 @@ char *host, *port; size_t len; - crypto_init(); common_session_init(sock, sock); /* Initialise server specific parts of the session */ diff -r e378da7eae5d -r 33207ed1174b sysoptions.h --- a/sysoptions.h Wed Oct 16 22:55:03 2013 +0800 +++ b/sysoptions.h Mon Oct 21 22:57:21 2013 +0800 @@ -69,20 +69,6 @@ #define DROPBEAR_SUCCESS 0 #define DROPBEAR_FAILURE -1 -/* various algorithm identifiers */ -#define DROPBEAR_KEX_NONE 0 -#define DROPBEAR_KEX_DH_GROUP1 1 -#define DROPBEAR_KEX_DH_GROUP14 2 - -#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 @@ -92,8 +78,7 @@ #define MD5_HASH_SIZE 16 #define MAX_KEY_LEN 32 /* 256 bits for aes256 etc */ -#define MAX_IV_LEN 20 /* must be same as max blocksize, - and >= SHA1_HASH_SIZE */ +#define MAX_IV_LEN 20 /* must be same as max blocksize, */ #if defined(DROPBEAR_SHA2_512_HMAC) #define MAX_MAC_LEN 64 @@ -103,6 +88,37 @@ #define MAX_MAC_LEN 20 #endif +#if defined(DROPBEAR_ECDH) || defined (DROPBEAR_ECDSA) +#define DROPBEAR_ECC +#endif + +#ifdef DROPBEAR_ECC +#define DROPBEAR_ECC_256 +#define DROPBEAR_ECC_384 +#define DROPBEAR_ECC_521 +#endif + +#ifdef DROPBEAR_ECC +#define DROPBEAR_LTC_PRNG +#endif + +// hashes which will be linked and registered +#if defined(DROPBEAR_SHA2_256_HMAC) || defined(DROPBEAR_ECC_256) +#define DROPBEAR_SHA256 +#endif +#if defined(DROPBEAR_ECC_384) +#define DROPBEAR_SHA384 +#endif +#if defined(DROPBEAR_SHA2_512_HMAC) || defined(DROPBEAR_ECC_521) +#define DROPBEAR_SHA512 +#endif +#if defined(DROPBEAR_MD5_HMAC) +#define DROPBEAR_MD5 +#endif + +// roughly 2x 521 bits +#define MAX_ECC_SIZE 140 + #define MAX_NAME_LEN 64 /* maximum length of a protocol name, isn't explicitly specified for all protocols (just for algos) but seems valid */ @@ -134,6 +150,8 @@ /* For a 4096 bit DSS key, empirically determined */ #define MAX_PRIVKEY_SIZE 1700 +#define MAX_HOSTKEYS 3 + /* The maximum size of the bignum portion of the kexhash buffer */ /* Sect. 8 of the transport rfc 4253, K_S + e + f + K */ #define KEXHASHBUF_MAX_INTS (1700 + 130 + 130 + 130) @@ -155,19 +173,6 @@ #define DROPBEAR_TWOFISH #endif -#ifdef DROPBEAR_MD5_HMAC -#define DROPBEAR_MD5 -#endif - -#ifdef DROPBEAR_SHA2_256_HMAC -#define DROPBEAR_SHA256 -#endif - -#if (defined(DROPBEAR_DSS) && defined(DSS_PROTOK)) \ - || defined(DROPBEAR_SHA2_512_HMAC) -#define DROPBEAR_SHA512 -#endif - #ifndef ENABLE_X11FWD #define DISABLE_X11FWD #endif