diff options
author | Matt Johnston <matt@ucc.asn.au> | 2013-11-14 20:45:46 +0800 |
---|---|---|
committer | Matt Johnston <matt@ucc.asn.au> | 2013-11-14 20:45:46 +0800 |
commit | 8c8ecec3e96525d498077da81084302371bab4da (patch) | |
tree | 89363b5cdacf796c1ac7976086d567ad0ce64b33 | |
parent | cfac8435a73cddbc54a70ab07418b0bdb900fc10 (diff) | |
parent | b77864931bb577691c4bf59df3cfe4e5ba306bd0 (diff) |
merge ecc again
-rw-r--r-- | Makefile.in | 7 | ||||
-rw-r--r-- | algo.h | 22 | ||||
-rw-r--r-- | bignum.c | 2 | ||||
-rw-r--r-- | cli-kex.c | 116 | ||||
-rw-r--r-- | cli-runopts.c | 2 | ||||
-rw-r--r-- | common-algo.c | 23 | ||||
-rw-r--r-- | common-kex.c | 73 | ||||
-rw-r--r-- | curve25519-donna.c | 734 | ||||
-rw-r--r-- | debug.h | 3 | ||||
-rw-r--r-- | dropbearkey.c | 172 | ||||
-rw-r--r-- | ecc.c | 4 | ||||
-rw-r--r-- | ecdsa.c | 13 | ||||
-rw-r--r-- | ecdsa.h | 3 | ||||
-rw-r--r-- | gensignkey.c | 131 | ||||
-rw-r--r-- | gensignkey.h | 8 | ||||
-rw-r--r-- | kex.h | 19 | ||||
-rw-r--r-- | keyimport.c | 28 | ||||
-rw-r--r-- | options.h | 30 | ||||
-rw-r--r-- | runopts.h | 2 | ||||
-rw-r--r-- | session.h | 1 | ||||
-rw-r--r-- | signkey.c | 24 | ||||
-rw-r--r-- | svr-auth.c | 2 | ||||
-rw-r--r-- | svr-authpubkey.c | 6 | ||||
-rw-r--r-- | svr-kex.c | 148 | ||||
-rw-r--r-- | svr-runopts.c | 62 | ||||
-rw-r--r-- | sysoptions.h | 14 |
26 files changed, 1370 insertions, 279 deletions
diff --git a/Makefile.in b/Makefile.in index fa88f61..a644719 100644 --- a/Makefile.in +++ b/Makefile.in @@ -27,7 +27,8 @@ COMMONOBJS=dbutil.o buffer.o \ signkey.o rsa.o random.o \ queue.o \ atomicio.o compat.o fake-rfc2553.o \ - ltc_prng.o ecc.o ecdsa.o crypto_desc.o + ltc_prng.o ecc.o ecdsa.o crypto_desc.o \ + gensignkey.o gendss.o genrsa.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 \ @@ -42,9 +43,9 @@ CLIOBJS=cli-main.o cli-auth.o cli-authpasswd.o cli-kex.o \ CLISVROBJS=common-session.o packet.o common-algo.o common-kex.o \ common-channel.o common-chansession.o termcodes.o loginrec.o \ tcp-accept.o listener.o process-packet.o \ - common-runopts.o circbuffer.o + common-runopts.o circbuffer.o curve25519-donna.o -KEYOBJS=dropbearkey.o gendss.o genrsa.o +KEYOBJS=dropbearkey.o CONVERTOBJS=dropbearconvert.o keyimport.o @@ -81,17 +81,27 @@ struct dropbear_hash { const unsigned char hashsize; }; +enum dropbear_kex_mode { + DROPBEAR_KEX_NORMAL_DH, + DROPBEAR_KEX_ECDH, + DROPBEAR_KEX_CURVE25519, +}; + struct dropbear_kex { - // "normal" DH KEX + enum dropbear_kex_mode mode; + + /* "normal" DH KEX */ const unsigned char *dh_p_bytes; const int dh_p_len; - // elliptic curve DH KEX + /* elliptic curve DH KEX */ #ifdef DROPBEAR_ECDH const struct dropbear_ecc_curve *ecc_curve; +#else + const void* dummy; #endif - // both + /* both */ const struct ltc_hash_descriptor *hash_desc; }; @@ -117,12 +127,6 @@ int check_user_algos(const char* user_algo_list, algo_type * algos, 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, @@ -78,8 +78,6 @@ void bytes_to_mp(mp_int *mp, const unsigned char* bytes, unsigned int len) { /* hash the ssh representation of the mp_int mp */ void hash_process_mp(const struct ltc_hash_descriptor *hash_desc, hash_state *hs, mp_int *mp) { - - int i; buffer * buf; buf = buf_new(512 + 20); /* max buffer is a 4096 bit key, @@ -47,27 +47,43 @@ void send_msg_kexdh_init() { CHECKCLEARTOWRITE(); buf_putbyte(ses.writepayload, SSH_MSG_KEXDH_INIT); - 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); + switch (ses.newkeys->algo_kex->mode) { + case DROPBEAR_KEX_NORMAL_DH: + 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(); } - cli_ses.dh_param = gen_kexdh_param(); - } - buf_putmpint(ses.writepayload, &cli_ses.dh_param->pub); - } else { + buf_putmpint(ses.writepayload, &cli_ses.dh_param->pub); + break; + case DROPBEAR_KEX_ECDH: #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); + 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(); } - cli_ses.ecdh_param = gen_kexecdh_param(); - } - buf_put_ecc_raw_pubkey_string(ses.writepayload, &cli_ses.ecdh_param->key); + buf_put_ecc_raw_pubkey_string(ses.writepayload, &cli_ses.ecdh_param->key); +#endif + break; +#ifdef DROPBEAR_CURVE25519 + case DROPBEAR_KEX_CURVE25519: + if (ses.newkeys->algo_kex != cli_ses.param_kex_algo + || !cli_ses.curve25519_param) { + if (cli_ses.curve25519_param) { + free_kexcurve25519_param(cli_ses.curve25519_param); + } + cli_ses.curve25519_param = gen_kexcurve25519_param(); + } + buf_putstring(ses.writepayload, cli_ses.curve25519_param->pub, CURVE25519_LEN); #endif + break; } + cli_ses.param_kex_algo = ses.newkeys->algo_kex; encrypt_packet(); ses.requirenext[0] = SSH_MSG_KEXDH_REPLY; @@ -103,23 +119,38 @@ void recv_msg_kexdh_reply() { 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"); - } + switch (ses.newkeys->algo_kex->mode) { + case DROPBEAR_KEX_NORMAL_DH: + { + 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 { + kexdh_comb_key(cli_ses.dh_param, &dh_f, hostkey); + mp_clear(&dh_f); + } + break; + case DROPBEAR_KEX_ECDH: #ifdef DROPBEAR_ECDH - buffer *ecdh_qs = buf_getstringbuf(ses.payload); - kexecdh_comb_key(cli_ses.ecdh_param, ecdh_qs, hostkey); - buf_free(ecdh_qs); + { + buffer *ecdh_qs = buf_getstringbuf(ses.payload); + kexecdh_comb_key(cli_ses.ecdh_param, ecdh_qs, hostkey); + buf_free(ecdh_qs); + } +#endif + break; +#ifdef DROPBEAR_CURVE25519 + case DROPBEAR_KEX_CURVE25519: + { + buffer *ecdh_qs = buf_getstringbuf(ses.payload); + kexcurve25519_comb_key(cli_ses.curve25519_param, ecdh_qs, hostkey); + buf_free(ecdh_qs); + } #endif + break; } if (cli_ses.dh_param) { @@ -132,6 +163,12 @@ void recv_msg_kexdh_reply() { cli_ses.ecdh_param = NULL; } #endif +#ifdef DROPBEAR_CURVE25519 + if (cli_ses.curve25519_param) { + free_kexcurve25519_param(cli_ses.curve25519_param); + cli_ses.curve25519_param = NULL; + } +#endif cli_ses.param_kex_algo = NULL; if (buf_verify(ses.payload, hostkey, ses.hash) != DROPBEAR_SUCCESS) { @@ -147,7 +184,8 @@ void recv_msg_kexdh_reply() { TRACE(("leave recv_msg_kexdh_init")) } -static void ask_to_confirm(unsigned char* keyblob, unsigned int keybloblen) { +static void ask_to_confirm(unsigned char* keyblob, unsigned int keybloblen, + const char* algoname) { char* fp = NULL; FILE *tty = NULL; @@ -155,14 +193,16 @@ static void ask_to_confirm(unsigned char* keyblob, unsigned int keybloblen) { fp = sign_key_fingerprint(keyblob, keybloblen); if (cli_opts.always_accept_key) { - fprintf(stderr, "\nHost '%s' key accepted unconditionally.\n(fingerprint %s)\n", + fprintf(stderr, "\nHost '%s' key accepted unconditionally.\n(%s fingerprint %s)\n", cli_opts.remotehost, + algoname, fp); m_free(fp); return; } - fprintf(stderr, "\nHost '%s' is not in the trusted hosts file.\n(fingerprint %s)\nDo you want to continue connecting? (y/n) ", + fprintf(stderr, "\nHost '%s' is not in the trusted hosts file.\n(%s fingerprint %s)\nDo you want to continue connecting? (y/n) ", cli_opts.remotehost, + algoname, fp); m_free(fp); @@ -257,16 +297,17 @@ static void checkhostkey(unsigned char* keyblob, unsigned int keybloblen) { return; } + algoname = signkey_name_from_type(ses.newkeys->algo_hostkey, &algolen); + hostsfile = open_known_hosts_file(&readonly); if (!hostsfile) { - ask_to_confirm(keyblob, keybloblen); + ask_to_confirm(keyblob, keybloblen, algoname); /* ask_to_confirm will exit upon failure */ return; } line = buf_new(MAX_KNOWNHOSTS_LINE); hostlen = strlen(cli_opts.remotehost); - algoname = signkey_name_from_type(ses.newkeys->algo_hostkey, &algolen); do { if (buf_getline(line, hostsfile) == DROPBEAR_FAILURE) { @@ -319,17 +360,18 @@ static void checkhostkey(unsigned char* keyblob, unsigned int keybloblen) { /* The keys didn't match. eep. Note that we're "leaking" the fingerprint strings here, but we're exiting anyway */ - dropbear_exit("\n\nHost key mismatch for %s !\n" + dropbear_exit("\n\n%s host key mismatch for %s !\n" "Fingerprint is %s\n" "Expected %s\n" "If you know that the host key is correct you can\nremove the bad entry from ~/.ssh/known_hosts", + algoname, cli_opts.remotehost, sign_key_fingerprint(keyblob, keybloblen), fingerprint ? fingerprint : "UNKNOWN"); } while (1); /* keep going 'til something happens */ /* Key doesn't exist yet */ - ask_to_confirm(keyblob, keybloblen); + ask_to_confirm(keyblob, keybloblen, algoname); /* If we get here, they said yes */ diff --git a/cli-runopts.c b/cli-runopts.c index b8d304f..d20928b 100644 --- a/cli-runopts.c +++ b/cli-runopts.c @@ -450,7 +450,7 @@ void cli_getopts(int argc, char ** argv) { #ifdef ENABLE_CLI_PUBKEY_AUTH static void loadidentityfile(const char* filename) { sign_key *key; - int keytype; + enum signkey_type keytype; key = new_sign_key(); keytype = DROPBEAR_SIGNKEY_ANY; diff --git a/common-algo.c b/common-algo.c index 8076358..621a8cb 100644 --- a/common-algo.c +++ b/common-algo.c @@ -228,23 +228,32 @@ algo_type sshhostkey[] = { {NULL, 0, NULL, 0, NULL} }; -static struct dropbear_kex kex_dh_group1 = {dh_p_1, DH_P_1_LEN, NULL, &sha1_desc }; -static struct dropbear_kex kex_dh_group14 = {dh_p_14, DH_P_14_LEN, NULL, &sha1_desc }; +static const struct dropbear_kex kex_dh_group1 = {DROPBEAR_KEX_NORMAL_DH, dh_p_1, DH_P_1_LEN, NULL, &sha1_desc }; +static const struct dropbear_kex kex_dh_group14 = {DROPBEAR_KEX_NORMAL_DH, dh_p_14, DH_P_14_LEN, NULL, &sha1_desc }; +/* These can't be const since dropbear_ecc_fill_dp() fills out + ecc_curve at runtime */ #ifdef DROPBEAR_ECDH #ifdef DROPBEAR_ECC_256 -static struct dropbear_kex kex_ecdh_nistp256 = {NULL, 0, &ecc_curve_nistp256, &sha256_desc }; +static struct dropbear_kex kex_ecdh_nistp256 = {DROPBEAR_KEX_ECDH, 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 }; +static struct dropbear_kex kex_ecdh_nistp384 = {DROPBEAR_KEX_ECDH, 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 }; +static struct dropbear_kex kex_ecdh_nistp521 = {DROPBEAR_KEX_ECDH, NULL, 0, &ecc_curve_nistp521, &sha512_desc }; #endif -#endif // DROPBEAR_ECDH +#endif /* DROPBEAR_ECDH */ +#ifdef DROPBEAR_CURVE25519 +/* Referred to directly */ +static const struct dropbear_kex kex_curve25519 = {DROPBEAR_KEX_CURVE25519, NULL, 0, NULL, &sha256_desc }; +#endif algo_type sshkex[] = { +#ifdef DROPBEAR_CURVE25519 + {"curve25519-sha256@libssh.org", 0, &kex_curve25519, 1, NULL}, +#endif #ifdef DROPBEAR_ECDH #ifdef DROPBEAR_ECC_521 {"ecdh-sha2-nistp521", 0, &kex_ecdh_nistp521, 1, NULL}, @@ -289,7 +298,7 @@ void buf_put_algolist(buffer * buf, algo_type localalgos[]) { unsigned int donefirst = 0; buffer *algolist = NULL; - algolist = buf_new(160); + algolist = buf_new(200); for (i = 0; localalgos[i].name != NULL; i++) { if (localalgos[i].usable) { if (donefirst) diff --git a/common-kex.c b/common-kex.c index a32ca6d..a304d02 100644 --- a/common-kex.c +++ b/common-kex.c @@ -577,7 +577,7 @@ struct kex_dh_param *gen_kexdh_param() { TRACE(("enter gen_kexdh_vals")) struct kex_dh_param *param = m_malloc(sizeof(*param)); - m_mp_init_multi(¶m->pub, ¶m->priv, NULL); + m_mp_init_multi(¶m->pub, ¶m->priv, &dh_g, &dh_p, &dh_q, NULL); /* read the prime and generator*/ load_dh_p(&dh_p); @@ -690,8 +690,8 @@ void kexecdh_comb_key(struct kex_ecdh_param *param, buffer *pub_them, 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 */ + /* Create the remainder of the hash buffer, to generate the exchange hash + See RFC5656 section 4 page 7 */ if (IS_DROPBEAR_CLIENT) { Q_C = ¶m->key; Q_S = Q_them; @@ -700,7 +700,6 @@ void kexecdh_comb_key(struct kex_ecdh_param *param, buffer *pub_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 */ @@ -713,7 +712,71 @@ void kexecdh_comb_key(struct kex_ecdh_param *param, buffer *pub_them, /* calculate the hash H to sign */ finish_kexhashbuf(); } -#endif +#endif /* DROPBEAR_ECDH */ + +#ifdef DROPBEAR_CURVE25519 +struct kex_curve25519_param *gen_kexcurve25519_param () { + /* Per http://cr.yp.to/ecdh.html */ + struct kex_curve25519_param *param = m_malloc(sizeof(*param)); + const unsigned char basepoint[32] = {9}; + + genrandom(param->priv, CURVE25519_LEN); + param->priv[0] &= 248; + param->priv[31] &= 127; + param->priv[31] |= 64; + + curve25519_donna(param->pub, param->priv, basepoint); + + return param; +} + +void free_kexcurve25519_param(struct kex_curve25519_param *param) +{ + m_burn(param->priv, CURVE25519_LEN); + m_free(param); +} + +void kexcurve25519_comb_key(struct kex_curve25519_param *param, buffer *buf_pub_them, + sign_key *hostkey) { + unsigned char out[CURVE25519_LEN]; + const unsigned char* Q_C = NULL; + const unsigned char* Q_S = NULL; + + if (buf_pub_them->len != CURVE25519_LEN) + { + dropbear_exit("Bad curve25519"); + } + + curve25519_donna(out, param->priv, buf_pub_them->data); + m_mp_alloc_init_multi(&ses.dh_K, NULL); + bytes_to_mp(ses.dh_K, out, CURVE25519_LEN); + m_burn(out, sizeof(out)); + + /* Create the remainder of the hash buffer, to generate the exchange hash. + See RFC5656 section 4 page 7 */ + if (IS_DROPBEAR_CLIENT) { + Q_C = param->pub; + Q_S = buf_pub_them->data; + } else { + Q_S = param->pub; + Q_C = buf_pub_them->data; + } + + /* K_S, the host key */ + buf_put_pub_key(ses.kexhashbuf, hostkey, ses.newkeys->algo_hostkey); + /* Q_C, client's ephemeral public key octet string */ + buf_putstring(ses.kexhashbuf, Q_C, CURVE25519_LEN); + /* Q_S, server's ephemeral public key octet string */ + buf_putstring(ses.kexhashbuf, Q_S, CURVE25519_LEN); + /* K, the shared secret */ + buf_putmpint(ses.kexhashbuf, ses.dh_K); + + /* calculate the hash H to sign */ + finish_kexhashbuf(); +} +#endif /* DROPBEAR_CURVE25519 */ + + static void finish_kexhashbuf(void) { hash_state hs; diff --git a/curve25519-donna.c b/curve25519-donna.c new file mode 100644 index 0000000..bb1262e --- /dev/null +++ b/curve25519-donna.c @@ -0,0 +1,734 @@ +/* Copyright 2008, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * curve25519-donna: Curve25519 elliptic curve, public key function + * + * http://code.google.com/p/curve25519-donna/ + * + * Adam Langley <agl@imperialviolet.org> + * + * Derived from public domain C code by Daniel J. Bernstein <djb@cr.yp.to> + * + * More information about curve25519 can be found here + * http://cr.yp.to/ecdh.html + * + * djb's sample implementation of curve25519 is written in a special assembly + * language called qhasm and uses the floating point registers. + * + * This is, almost, a clean room reimplementation from the curve25519 paper. It + * uses many of the tricks described therein. Only the crecip function is taken + * from the sample implementation. + */ + +#include <string.h> +#include <stdint.h> + +#ifdef _MSC_VER +#define inline __inline +#endif + +typedef uint8_t u8; +typedef int32_t s32; +typedef int64_t limb; + +/* Field element representation: + * + * Field elements are written as an array of signed, 64-bit limbs, least + * significant first. The value of the field element is: + * x[0] + 2^26·x[1] + x^51·x[2] + 2^102·x[3] + ... + * + * i.e. the limbs are 26, 25, 26, 25, ... bits wide. + */ + +/* Sum two numbers: output += in */ +static void fsum(limb *output, const limb *in) { + unsigned i; + for (i = 0; i < 10; i += 2) { + output[0+i] = (output[0+i] + in[0+i]); + output[1+i] = (output[1+i] + in[1+i]); + } +} + +/* Find the difference of two numbers: output = in - output + * (note the order of the arguments!) + */ +static void fdifference(limb *output, const limb *in) { + unsigned i; + for (i = 0; i < 10; ++i) { + output[i] = (in[i] - output[i]); + } +} + +/* Multiply a number by a scalar: output = in * scalar */ +static void fscalar_product(limb *output, const limb *in, const limb scalar) { + unsigned i; + for (i = 0; i < 10; ++i) { + output[i] = in[i] * scalar; + } +} + +/* Multiply two numbers: output = in2 * in + * + * output must be distinct to both inputs. The inputs are reduced coefficient + * form, the output is not. + */ +static void fproduct(limb *output, const limb *in2, const limb *in) { + output[0] = ((limb) ((s32) in2[0])) * ((s32) in[0]); + output[1] = ((limb) ((s32) in2[0])) * ((s32) in[1]) + + ((limb) ((s32) in2[1])) * ((s32) in[0]); + output[2] = 2 * ((limb) ((s32) in2[1])) * ((s32) in[1]) + + ((limb) ((s32) in2[0])) * ((s32) in[2]) + + ((limb) ((s32) in2[2])) * ((s32) in[0]); + output[3] = ((limb) ((s32) in2[1])) * ((s32) in[2]) + + ((limb) ((s32) in2[2])) * ((s32) in[1]) + + ((limb) ((s32) in2[0])) * ((s32) in[3]) + + ((limb) ((s32) in2[3])) * ((s32) in[0]); + output[4] = ((limb) ((s32) in2[2])) * ((s32) in[2]) + + 2 * (((limb) ((s32) in2[1])) * ((s32) in[3]) + + ((limb) ((s32) in2[3])) * ((s32) in[1])) + + ((limb) ((s32) in2[0])) * ((s32) in[4]) + + ((limb) ((s32) in2[4])) * ((s32) in[0]); + output[5] = ((limb) ((s32) in2[2])) * ((s32) in[3]) + + ((limb) ((s32) in2[3])) * ((s32) in[2]) + + ((limb) ((s32) in2[1])) * ((s32) in[4]) + + ((limb) ((s32) in2[4])) * ((s32) in[1]) + + ((limb) ((s32) in2[0])) * ((s32) in[5]) + + ((limb) ((s32) in2[5])) * ((s32) in[0]); + output[6] = 2 * (((limb) ((s32) in2[3])) * ((s32) in[3]) + + ((limb) ((s32) in2[1])) * ((s32) in[5]) + + ((limb) ((s32) in2[5])) * ((s32) in[1])) + + ((limb) ((s32) in2[2])) * ((s32) in[4]) + + ((limb) ((s32) in2[4])) * ((s32) in[2]) + + ((limb) ((s32) in2[0])) * ((s32) in[6]) + + ((limb) ((s32) in2[6])) * ((s32) in[0]); + output[7] = ((limb) ((s32) in2[3])) * ((s32) in[4]) + + ((limb) ((s32) in2[4])) * ((s32) in[3]) + + ((limb) ((s32) in2[2])) * ((s32) in[5]) + + ((limb) ((s32) in2[5])) * ((s32) in[2]) + + ((limb) ((s32) in2[1])) * ((s32) in[6]) + + ((limb) ((s32) in2[6])) * ((s32) in[1]) + + ((limb) ((s32) in2[0])) * ((s32) in[7]) + + ((limb) ((s32) in2[7])) * ((s32) in[0]); + output[8] = ((limb) ((s32) in2[4])) * ((s32) in[4]) + + 2 * (((limb) ((s32) in2[3])) * ((s32) in[5]) + + ((limb) ((s32) in2[5])) * ((s32) in[3]) + + ((limb) ((s32) in2[1])) * ((s32) in[7]) + + ((limb) ((s32) in2[7])) * ((s32) in[1])) + + ((limb) ((s32) in2[2])) * ((s32) in[6]) + + ((limb) ((s32) in2[6])) * ((s32) in[2]) + + ((limb) ((s32) in2[0])) * ((s32) in[8]) + + ((limb) ((s32) in2[8])) * ((s32) in[0]); + output[9] = ((limb) ((s32) in2[4])) * ((s32) in[5]) + + ((limb) ((s32) in2[5])) * ((s32) in[4]) + + ((limb) ((s32) in2[3])) * ((s32) in[6]) + + ((limb) ((s32) in2[6])) * ((s32) in[3]) + + ((limb) ((s32) in2[2])) * ((s32) in[7]) + + ((limb) ((s32) in2[7])) * ((s32) in[2]) + + ((limb) ((s32) in2[1])) * ((s32) in[8]) + + ((limb) ((s32) in2[8])) * ((s32) in[1]) + + ((limb) ((s32) in2[0])) * ((s32) in[9]) + + ((limb) ((s32) in2[9])) * ((s32) in[0]); + output[10] = 2 * (((limb) ((s32) in2[5])) * ((s32) in[5]) + + ((limb) ((s32) in2[3])) * ((s32) in[7]) + + ((limb) ((s32) in2[7])) * ((s32) in[3]) + + ((limb) ((s32) in2[1])) * ((s32) in[9]) + + ((limb) ((s32) in2[9])) * ((s32) in[1])) + + ((limb) ((s32) in2[4])) * ((s32) in[6]) + + ((limb) ((s32) in2[6])) * ((s32) in[4]) + + ((limb) ((s32) in2[2])) * ((s32) in[8]) + + ((limb) ((s32) in2[8])) * ((s32) in[2]); + output[11] = ((limb) ((s32) in2[5])) * ((s32) in[6]) + + ((limb) ((s32) in2[6])) * ((s32) in[5]) + + ((limb) ((s32) in2[4])) * ((s32) in[7]) + + ((limb) ((s32) in2[7])) * ((s32) in[4]) + + ((limb) ((s32) in2[3])) * ((s32) in[8]) + + ((limb) ((s32) in2[8])) * ((s32) in[3]) + + ((limb) ((s32) in2[2])) * ((s32) in[9]) + + ((limb) ((s32) in2[9])) * ((s32) in[2]); + output[12] = ((limb) ((s32) in2[6])) * ((s32) in[6]) + + 2 * (((limb) ((s32) in2[5])) * ((s32) in[7]) + + ((limb) ((s32) in2[7])) * ((s32) in[5]) + + ((limb) ((s32) in2[3])) * ((s32) in[9]) + + ((limb) ((s32) in2[9])) * ((s32) in[3])) + + ((limb) ((s32) in2[4])) * ((s32) in[8]) + + ((limb) ((s32) in2[8])) * ((s32) in[4]); + output[13] = ((limb) ((s32) in2[6])) * ((s32) in[7]) + + ((limb) ((s32) in2[7])) * ((s32) in[6]) + + ((limb) ((s32) in2[5])) * ((s32) in[8]) + + ((limb) ((s32) in2[8])) * ((s32) in[5]) + + ((limb) ((s32) in2[4])) * ((s32) in[9]) + + ((limb) ((s32) in2[9])) * ((s32) in[4]); + output[14] = 2 * (((limb) ((s32) in2[7])) * ((s32) in[7]) + + ((limb) ((s32) in2[5])) * ((s32) in[9]) + + ((limb) ((s32) in2[9])) * ((s32) in[5])) + + ((limb) ((s32) in2[6])) * ((s32) in[8]) + + ((limb) ((s32) in2[8])) * ((s32) in[6]); + output[15] = ((limb) ((s32) in2[7])) * ((s32) in[8]) + + ((limb) ((s32) in2[8])) * ((s32) in[7]) + + ((limb) ((s32) in2[6])) * ((s32) in[9]) + + ((limb) ((s32) in2[9])) * ((s32) in[6]); + output[16] = ((limb) ((s32) in2[8])) * ((s32) in[8]) + + 2 * (((limb) ((s32) in2[7])) * ((s32) in[9]) + + ((limb) ((s32) in2[9])) * ((s32) in[7])); + output[17] = ((limb) ((s32) in2[8])) * ((s32) in[9]) + + ((limb) ((s32) in2[9])) * ((s32) in[8]); + output[18] = 2 * ((limb) ((s32) in2[9])) * ((s32) in[9]); +} + +/* Reduce a long form to a short form by taking the input mod 2^255 - 19. */ +static void freduce_degree(limb *output) { + /* Each of these shifts and adds ends up multiplying the value by 19. */ + output[8] += output[18] << 4; + output[8] += output[18] << 1; + output[8] += output[18]; + output[7] += output[17] << 4; + output[7] += output[17] << 1; + output[7] += output[17]; + output[6] += output[16] << 4; + output[6] += output[16] << 1; + output[6] += output[16]; + output[5] += output[15] << 4; + output[5] += output[15] << 1; + output[5] += output[15]; + output[4] += output[14] << 4; + output[4] += output[14] << 1; + output[4] += output[14]; + output[3] += output[13] << 4; + output[3] += output[13] << 1; + output[3] += output[13]; + output[2] += output[12] << 4; + output[2] += output[12] << 1; + output[2] += output[12]; + output[1] += output[11] << 4; + output[1] += output[11] << 1; + output[1] += output[11]; + output[0] += output[10] << 4; + output[0] += output[10] << 1; + output[0] += output[10]; +} + +#if (-1 & 3) != 3 +#error "This code only works on a two's complement system" +#endif + +/* return v / 2^26, using only shifts and adds. */ +static inline limb +div_by_2_26(const limb v) +{ + /* High word of v; no shift needed*/ + const uint32_t highword = (uint32_t) (((uint64_t) v) >> 32); + /* Set to all 1s if v was negative; else set to 0s. */ + const int32_t sign = ((int32_t) highword) >> 31; + /* Set to 0x3ffffff if v was negative; else set to 0. */ + const int32_t roundoff = ((uint32_t) sign) >> 6; + /* Should return v / (1<<26) */ + return (v + roundoff) >> 26; +} + +/* return v / (2^25), using only shifts and adds. */ +static inline limb +div_by_2_25(const limb v) +{ + /* High word of v; no shift needed*/ + const uint32_t highword = (uint32_t) (((uint64_t) v) >> 32); + /* Set to all 1s if v was negative; else set to 0s. */ + const int32_t sign = ((int32_t) highword) >> 31; + /* Set to 0x1ffffff if v was negative; else set to 0. */ + const int32_t roundoff = ((uint32_t) sign) >> 7; + /* Should return v / (1<<25) */ + return (v + roundoff) >> 25; +} + +static inline s32 +div_s32_by_2_25(const s32 v) +{ + const s32 roundoff = ((uint32_t)(v >> 31)) >> 7; + return (v + roundoff) >> 25; +} + +/* Reduce all coefficients of the short form input so that |x| < 2^26. + * + * On entry: |output[i]| < 2^62 + */ +static void freduce_coefficients(limb *output) { + unsigned i; + + output[10] = 0; + + for (i = 0; i < 10; i += 2) { + limb over = div_by_2_26(output[i]); + output[i] -= over << 26; + output[i+1] += over; + + over = div_by_2_25(output[i+1]); + output[i+1] -= over << 25; + output[i+2] += over; + } + /* Now |output[10]| < 2 ^ 38 and all other coefficients are reduced. */ + output[0] += output[10] << 4; + output[0] += output[10] << 1; + output[0] += output[10]; + + output[10] = 0; + + /* Now output[1..9] are reduced, and |output[0]| < 2^26 + 19 * 2^38 + * So |over| will be no more than 77825 */ + { + limb over = div_by_2_26(output[0]); + output[0] -= over << 26; + output[1] += over; + } + + /* Now output[0,2..9] are reduced, and |output[1]| < 2^25 + 77825 + * So |over| will be no more than 1. */ + { + /* output[1] fits in 32 bits, so we can use div_s32_by_2_25 here. */ + s32 over32 = div_s32_by_2_25((s32) output[1]); + output[1] -= over32 << 25; + output[2] += over32; + } + + /* Finally, output[0,1,3..9] are reduced, and output[2] is "nearly reduced": + * we have |output[2]| <= 2^26. This is good enough for all of our math, + * but it will require an extra freduce_coefficients before fcontract. */ +} + +/* A helpful wrapper around fproduct: output = in * in2. + * + * output must be distinct to both inputs. The output is reduced degree and + * reduced coefficient. + */ +static void +fmul(limb *output, const limb *in, const limb *in2) { + limb t[19]; + fproduct(t, in, in2); + freduce_degree(t); + freduce_coefficients(t); + memcpy(output, t, sizeof(limb) * 10); +} + +static void fsquare_inner(limb *output, const limb *in) { + output[0] = ((limb) ((s32) in[0])) * ((s32) in[0]); + output[1] = 2 * ((limb) ((s32) in[0])) * ((s32) in[1]); + output[2] = 2 * (((limb) ((s32) in[1])) * ((s32) in[1]) + + ((limb) ((s32) in[0])) * ((s32) in[2])); + output[3] = 2 * (((limb) ((s32) in[1])) * ((s32) in[2]) + + ((limb) ((s32) in[0])) * ((s32) in[3])); + output[4] = ((limb) ((s32) in[2])) * ((s32) in[2]) + + 4 * ((limb) ((s32) in[1])) * ((s32) in[3]) + + 2 * ((limb) ((s32) in[0])) * ((s32) in[4]); + output[5] = 2 * (((limb) ((s32) in[2])) * ((s32) in[3]) + + ((limb) ((s32) in[1])) * ((s32) in[4]) + + ((limb) ((s32) in[0])) * ((s32) in[5])); + output[6] = 2 * (((limb) ((s32) in[3])) * ((s32) in[3]) + + ((limb) ((s32) in[2])) * ((s32) in[4]) + + ((limb) ((s32) in[0])) * ((s32) in[6]) + + 2 * ((limb) ((s32) in[1])) * ((s32) in[5])); + output[7] = 2 * (((limb) ((s32) in[3])) * ((s32) in[4]) + + ((limb) ((s32) in[2])) * ((s32) in[5]) + + ((limb) ((s32) in[1])) * ((s32) in[6]) + + ((limb) ((s32) in[0])) * ((s32) in[7])); + output[8] = ((limb) ((s32) in[4])) * ((s32) in[4]) + + 2 * (((limb) ((s32) in[2])) * ((s32) in[6]) + + ((limb) ((s32) in[0])) * ((s32) in[8]) + + 2 * (((limb) ((s32) in[1])) * ((s32) in[7]) + + ((limb) ((s32) in[3])) * ((s32) in[5]))); + output[9] = 2 * (((limb) ((s32) in[4])) * ((s32) in[5]) + + ((limb) ((s32) in[3])) * ((s32) in[6]) + + ((limb) ((s32) in[2])) * ((s32) in[7]) + + ((limb) ((s32) in[1])) * ((s32) in[8]) + + ((limb) ((s32) in[0])) * ((s32) in[9])); + output[10] = 2 * (((limb) ((s32) in[5])) * ((s32) in[5]) + + ((limb) ((s32) in[4])) * ((s32) in[6]) + + ((limb) ((s32) in[2])) * ((s32) in[8]) + + 2 * (((limb) ((s32) in[3])) * ((s32) in[7]) + + ((limb) ((s32) in[1])) * ((s32) in[9]))); + output[11] = 2 * (((limb) ((s32) in[5])) * ((s32) in[6]) + + ((limb) ((s32) in[4])) * ((s32) in[7]) + + ((limb) ((s32) in[3])) * ((s32) in[8]) + + ((limb) ((s32) in[2])) * ((s32) in[9])); + output[12] = ((limb) ((s32) in[6])) * ((s32) in[6]) + + 2 * (((limb) ((s32) in[4])) * ((s32) in[8]) + + 2 * (((limb) ((s32) in[5])) * ((s32) in[7]) + + ((limb) ((s32) in[3])) * ((s32) in[9]))); + output[13] = 2 * (((limb) ((s32) in[6])) * ((s32) in[7]) + + ((limb) ((s32) in[5])) * ((s32) in[8]) + + ((limb) ((s32) in[4])) * ((s32) in[9])); + output[14] = 2 * (((limb) ((s32) in[7])) * ((s32) in[7]) + + ((limb) ((s32) in[6])) * ((s32) in[8]) + + 2 * ((limb) ((s32) in[5])) * ((s32) in[9])); + output[15] = 2 * (((limb) ((s32) in[7])) * ((s32) in[8]) + + ((limb) ((s32) in[6])) * ((s32) in[9])); + output[16] = ((limb) ((s32) in[8])) * ((s32) in[8]) + + 4 * ((limb) ((s32) in[7])) * ((s32) in[9]); + output[17] = 2 * ((limb) ((s32) in[8])) * ((s32) in[9]); + output[18] = 2 * ((limb) ((s32) in[9])) * ((s32) in[9]); +} + +static void +fsquare(limb *output, const limb *in) { + limb t[19]; + fsquare_inner(t, in); + freduce_degree(t); + freduce_coefficients(t); + memcpy(output, t, sizeof(limb) * 10); +} + +/* Take a little-endian, 32-byte number and expand it into polynomial form */ +static void +fexpand(limb *output, const u8 *input) { +#define F(n,start,shift,mask) \ + output[n] = ((((limb) input[start + 0]) | \ + ((limb) input[start + 1]) << 8 | \ + ((limb) input[start + 2]) << 16 | \ + ((limb) input[start + 3]) << 24) >> shift) & mask; + F(0, 0, 0, 0x3ffffff); + F(1, 3, 2, 0x1ffffff); + F(2, 6, 3, 0x3ffffff); + F(3, 9, 5, 0x1ffffff); + F(4, 12, 6, 0x3ffffff); + F(5, 16, 0, 0x1ffffff); + F(6, 19, 1, 0x3ffffff); + F(7, 22, 3, 0x1ffffff); + F(8, 25, 4, 0x3ffffff); + F(9, 28, 6, 0x3ffffff); +#undef F +} + +#if (-32 >> 1) != -16 +#error "This code only works when >> does sign-extension on negative numbers" +#endif + +/* Take a fully reduced polynomial form number and contract it into a + * little-endian, 32-byte array + */ +static void +fcontract(u8 *output, limb *input) { + int i; + int j; + + for (j = 0; j < 2; ++j) { + for (i = 0; i < 9; ++i) { + if ((i & 1) == 1) { + /* This calculation is a time-invariant way to make input[i] positive + by borrowing from the next-larger limb. + */ + const s32 mask = (s32)(input[i]) >> 31; + const s32 carry = -(((s32)(input[i]) & mask) >> 25); + input[i] = (s32)(input[i]) + (carry << 25); + input[i+1] = (s32)(input[i+1]) - carry; + } else { + const s32 mask = (s32)(input[i]) >> 31; + const s32 carry = -(((s32)(input[i]) & mask) >> 26); + input[i] = (s32)(input[i]) + (carry << 26); + input[i+1] = (s32)(input[i+1]) - carry; + } + } + { + const s32 mask = (s32)(input[9]) >> 31; + const s32 carry = -(((s32)(input[9]) & mask) >> 25); + input[9] = (s32)(input[9]) + (carry << 25); + input[0] = (s32)(input[0]) - (carry * 19); + } + } + + /* The first borrow-propagation pass above ended with every limb + except (possibly) input[0] non-negative. + + Since each input limb except input[0] is decreased by at most 1 + by a borrow-propagation pass, the second borrow-propagation pass + could only have wrapped around to decrease input[0] again if the + first pass left input[0] negative *and* input[1] through input[9] + were all zero. In that case, input[1] is now 2^25 - 1, and this + last borrow-propagation step will leave input[1] non-negative. + */ + { + const s32 mask = (s32)(input[0]) >> 31; + const s32 carry = -(((s32)(input[0]) & mask) >> 26); + input[0] = (s32)(input[0]) + (carry << 26); + input[1] = (s32)(input[1]) - carry; + } + + /* Both passes through the above loop, plus the last 0-to-1 step, are + necessary: if input[9] is -1 and input[0] through input[8] are 0, + negative values will remain in the array until the end. + */ + + input[1] <<= 2; + input[2] <<= 3; + input[3] <<= 5; + input[4] <<= 6; + input[6] <<= 1; + input[7] <<= 3; + input[8] <<= 4; + input[9] <<= 6; +#define F(i, s) \ + output[s+0] |= input[i] & 0xff; \ + output[s+1] = (input[i] >> 8) & 0xff; \ + output[s+2] = (input[i] >> 16) & 0xff; \ + output[s+3] = (input[i] >> 24) & 0xff; + output[0] = 0; + output[16] = 0; + F(0,0); + F(1,3); + F(2,6); + F(3,9); + F(4,12); + F(5,16); + F(6,19); + F(7,22); + F(8,25); + F(9,28); +#undef F +} + +/* Input: Q, Q', Q-Q' + * Output: 2Q, Q+Q' + * + * x2 z3: long form + * x3 z3: long form + * x z: short form, destroyed + * xprime zprime: short form, destroyed + * qmqp: short form, preserved + */ +static void fmonty(limb *x2, limb *z2, /* output 2Q */ + limb *x3, limb *z3, /* output Q + Q' */ + limb *x, limb *z, /* input Q */ + limb *xprime, limb *zprime, /* input Q' */ + const limb *qmqp /* input Q - Q' */) { + limb origx[10], origxprime[10], zzz[19], xx[19], zz[19], xxprime[19], + zzprime[19], zzzprime[19], xxxprime[19]; + + memcpy(origx, x, 10 * sizeof(limb)); + fsum(x, z); + fdifference(z, origx); // does x - z + + memcpy(origxprime, xprime, sizeof(limb) * 10); + fsum(xprime, zprime); + fdifference(zprime, origxprime); + fproduct(xxprime, xprime, z); + fproduct(zzprime, x, zprime); + freduce_degree(xxprime); + freduce_coefficients(xxprime); + freduce_degree(zzprime); + freduce_coefficients(zzprime); + memcpy(origxprime, xxprime, sizeof(limb) * 10); + fsum(xxprime, zzprime); + fdifference(zzprime, origxprime); + fsquare(xxxprime, xxprime); + fsquare(zzzprime, zzprime); + fproduct(zzprime, zzzprime, qmqp); + freduce_degree(zzprime); + freduce_coefficients(zzprime); + memcpy(x3, xxxprime, sizeof(limb) * 10); + memcpy(z3, zzprime, sizeof(limb) * 10); + + fsquare(xx, x); + fsquare(zz, z); + fproduct(x2, xx, zz); + freduce_degree(x2); + freduce_coefficients(x2); + fdifference(zz, xx); // does zz = xx - zz + memset(zzz + 10, 0, sizeof(limb) * 9); + fscalar_product(zzz, zz, 121665); + /* No need to call freduce_degree here: + fscalar_product doesn't increase the degree of its input. */ + freduce_coefficients(zzz); + fsum(zzz, xx); + fproduct(z2, zz, zzz); + freduce_degree(z2); + freduce_coefficients(z2); +} + +/* Conditionally swap two reduced-form limb arrays if 'iswap' is 1, but leave + * them unchanged if 'iswap' is 0. Runs in data-invariant time to avoid + * side-channel attacks. + * + * NOTE that this function requires that 'iswap' be 1 or 0; other values give + * wrong results. Also, the two limb arrays must be in reduced-coefficient, + * reduced-degree form: the values in a[10..19] or b[10..19] aren't swapped, + * and all all values in a[0..9],b[0..9] must have magnitude less than + * INT32_MAX. + */ +static void +swap_conditional(limb a[19], limb b[19], limb iswap) { + unsigned i; + const s32 swap = (s32) -iswap; + + for (i = 0; i < 10; ++i) { + const s32 x = swap & ( ((s32)a[i]) ^ ((s32)b[i]) ); + a[i] = ((s32)a[i]) ^ x; + b[i] = ((s32)b[i]) ^ x; + } +} + +/* Calculates nQ where Q is the x-coordinate of a point on the curve + * + * resultx/resultz: the x coordinate of the resulting curve point (short form) + * n: a little endian, 32-byte number + * q: a point of the curve (short form) + */ +static void +cmult(limb *resultx, limb *resultz, const u8 *n, const limb *q) { + limb a[19] = {0}, b[19] = {1}, c[19] = {1}, d[19] = {0}; + limb *nqpqx = a, *nqpqz = b, *nqx = c, *nqz = d, *t; + limb e[19] = {0}, f[19] = {1}, g[19] = {0}, h[19] = {1}; + limb *nqpqx2 = e, *nqpqz2 = f, *nqx2 = g, *nqz2 = h; + + unsigned i, j; + + memcpy(nqpqx, q, sizeof(limb) * 10); + + for (i = 0; i < 32; ++i) { + u8 byte = n[31 - i]; + for (j = 0; j < 8; ++j) { + const limb bit = byte >> 7; + + swap_conditional(nqx, nqpqx, bit); + swap_conditional(nqz, nqpqz, bit); + fmonty(nqx2, nqz2, + nqpqx2, nqpqz2, + nqx, nqz, + nqpqx, nqpqz, + q); + swap_conditional(nqx2, nqpqx2, bit); + swap_conditional(nqz2, nqpqz2, bit); + + t = nqx; + nqx = nqx2; + nqx2 = t; + t = nqz; + nqz = nqz2; + nqz2 = t; + t = nqpqx; + nqpqx = nqpqx2; + nqpqx2 = t; + t = nqpqz; + nqpqz = nqpqz2; + nqpqz2 = t; + + byte <<= 1; + } + } + + memcpy(resultx, nqx, sizeof(limb) * 10); + memcpy(resultz, nqz, sizeof(limb) * 10); +} + +// ----------------------------------------------------------------------------- +// Shamelessly copied from djb's code +// ----------------------------------------------------------------------------- +static void +crecip(limb *out, const limb *z) { + limb z2[10]; + limb z9[10]; + limb z11[10]; + limb z2_5_0[10]; + limb z2_10_0[10]; + limb z2_20_0[10]; + limb z2_50_0[10]; + limb z2_100_0[10]; + limb t0[10]; + limb t1[10]; + int i; + + /* 2 */ fsquare(z2,z); + /* 4 */ fsquare(t1,z2); + /* 8 */ fsquare(t0,t1); + /* 9 */ fmul(z9,t0,z); + /* 11 */ fmul(z11,z9,z2); + /* 22 */ fsquare(t0,z11); + /* 2^5 - 2^0 = 31 */ fmul(z2_5_0,t0,z9); + + /* 2^6 - 2^1 */ fsquare(t0,z2_5_0); + /* 2^7 - 2^2 */ fsquare(t1,t0); + /* 2^8 - 2^3 */ fsquare(t0,t1); + /* 2^9 - 2^4 */ fsquare(t1,t0); + /* 2^10 - 2^5 */ fsquare(t0,t1); + /* 2^10 - 2^0 */ fmul(z2_10_0,t0,z2_5_0); + + /* 2^11 - 2^1 */ fsquare(t0,z2_10_0); + /* 2^12 - 2^2 */ fsquare(t1,t0); + /* 2^20 - 2^10 */ for (i = 2;i < 10;i += 2) { fsquare(t0,t1); fsquare(t1,t0); } + /* 2^20 - 2^0 */ fmul(z2_20_0,t1,z2_10_0); + + /* 2^21 - 2^1 */ fsquare(t0,z2_20_0); + /* 2^22 - 2^2 */ fsquare(t1,t0); + /* 2^40 - 2^20 */ for (i = 2;i < 20;i += 2) { fsquare(t0,t1); fsquare(t1,t0); } + /* 2^40 - 2^0 */ fmul(t0,t1,z2_20_0); + + /* 2^41 - 2^1 */ fsquare(t1,t0); + /* 2^42 - 2^2 */ fsquare(t0,t1); + /* 2^50 - 2^10 */ for (i = 2;i < 10;i += 2) { fsquare(t1,t0); fsquare(t0,t1); } + /* 2^50 - 2^0 */ fmul(z2_50_0,t0,z2_10_0); + + /* 2^51 - 2^1 */ fsquare(t0,z2_50_0); + /* 2^52 - 2^2 */ fsquare(t1,t0); + /* 2^100 - 2^50 */ for (i = 2;i < 50;i += 2) { fsquare(t0,t1); fsquare(t1,t0); } + /* 2^100 - 2^0 */ fmul(z2_100_0,t1,z2_50_0); + + /* 2^101 - 2^1 */ fsquare(t1,z2_100_0); + /* 2^102 - 2^2 */ fsquare(t0,t1); + /* 2^200 - 2^100 */ for (i = 2;i < 100;i += 2) { fsquare(t1,t0); fsquare(t0,t1); } + /* 2^200 - 2^0 */ fmul(t1,t0,z2_100_0); + + /* 2^201 - 2^1 */ fsquare(t0,t1); + /* 2^202 - 2^2 */ fsquare(t1,t0); + /* 2^250 - 2^50 */ for (i = 2;i < 50;i += 2) { fsquare(t0,t1); fsquare(t1,t0); } + /* 2^250 - 2^0 */ fmul(t0,t1,z2_50_0); + + /* 2^251 - 2^1 */ fsquare(t1,t0); + /* 2^252 - 2^2 */ fsquare(t0,t1); + /* 2^253 - 2^3 */ fsquare(t1,t0); + /* 2^254 - 2^4 */ fsquare(t0,t1); + /* 2^255 - 2^5 */ fsquare(t1,t0); + /* 2^255 - 21 */ fmul(out,t1,z11); +} + +int curve25519_donna(u8 *, const u8 *, const u8 *); + +int +curve25519_donna(u8 *mypublic, const u8 *secret, const u8 *basepoint) { + limb bp[10], x[10], z[11], zmone[10]; + uint8_t e[32]; + int i; + + for (i = 0; i < 32; ++i) e[i] = secret[i]; + e[0] &= 248; + e[31] &= 127; + e[31] |= 64; + + fexpand(bp, basepoint); + cmult(x, z, e, bp); + crecip(zmone, z); + fmul(z, x, zmone); + freduce_coefficients(z); + fcontract(mypublic, z); + return 0; +} @@ -40,7 +40,6 @@ * 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 @@ -72,7 +71,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 --git a/dropbearkey.c b/dropbearkey.c index 57db3af..01385a1 100644 --- a/dropbearkey.c +++ b/dropbearkey.c @@ -54,15 +54,13 @@ #include "ecdsa.h" #include "crypto_desc.h" #include "random.h" +#include "gensignkey.h" static void printhelp(char * progname); -#define RSA_DEFAULT_SIZE 2048 -#define DSS_DEFAULT_SIZE 1024 -static void buf_writefile(buffer * buf, const char * filename); static void printpubkey(sign_key * key, int keytype); -static void justprintpub(const char* filename); +static int printpubfile(const char* filename); /* Print a help message */ static void printhelp(char * progname) { @@ -103,6 +101,30 @@ static void printhelp(char * progname) { ,progname); } +/* fails fatally */ +static void check_signkey_bits(enum signkey_type type, int bits) +{ + switch (type) { +#ifdef DROPBEAR_RSA + case DROPBEAR_SIGNKEY_RSA: + if (bits < 512 || bits > 4096 || (bits % 8 != 0)) { + dropbear_exit("Bits must satisfy 512 <= bits <= 4096, and be a" + " multiple of 8\n"); + } + break; +#endif +#ifdef DROPEAR_DSS + case DROPBEAR_SIGNKEY_DSS: + if (bits != 1024) { + dropbear_exit("DSS keys have a fixed size of 1024 bits\n"); + exit(EXIT_FAILURE); + } +#endif + default: + (void)0; /* quiet, compiler. ecdsa handles checks itself */ + } +} + #if defined(DBMULTI_dropbearkey) || !defined(DROPBEAR_MULTI) #if defined(DBMULTI_dropbearkey) && defined(DROPBEAR_MULTI) int dropbearkey_main(int argc, char ** argv) { @@ -112,13 +134,11 @@ int main(int argc, char ** argv) { int i; char ** next = 0; - sign_key *key = NULL; - buffer *buf = NULL; char * filename = NULL; enum signkey_type keytype = DROPBEAR_SIGNKEY_NONE; char * typetext = NULL; char * sizetext = NULL; - unsigned int bits; + unsigned int bits = 0; int printpub = 0; crypto_init(); @@ -174,8 +194,8 @@ int main(int argc, char ** argv) { } if (printpub) { - justprintpub(filename); - /* Not reached */ + int ret = printpubfile(filename); + exit(ret); } /* check/parse args */ @@ -216,106 +236,22 @@ int main(int argc, char ** argv) { 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 */ - } - - } else { - /* default key size */ - - 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", bits, - typetext, filename); - - /* don't want the file readable by others */ - umask(077); + check_signkey_bits(keytype, bits);; + } - /* now we can generate the key */ - key = new_sign_key(); - fprintf(stderr, "Generating key, this may take a while...\n"); - switch(keytype) { -#ifdef DROPBEAR_RSA - case DROPBEAR_SIGNKEY_RSA: - key->rsakey = gen_rsa_priv_key(bits); - break; -#endif -#ifdef DROPBEAR_DSS - case DROPBEAR_SIGNKEY_DSS: - 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_key_ptr(key, keytype) = ecckey; - } - break; -#endif - default: - fprintf(stderr, "Internal error, bad key type\n"); - exit(EXIT_FAILURE); - } + if (signkey_generate(keytype, bits, filename) == DROPBEAR_FAILURE) + { + dropbear_exit("Failed to generate key.\n"); + } - buf = buf_new(MAX_PRIVKEY_SIZE); - - buf_put_priv_key(buf, key, keytype); - buf_setpos(buf, 0); - buf_writefile(buf, filename); - - buf_burn(buf); - buf_free(buf); - - printpubkey(key, keytype); - - sign_key_free(key); + printpubfile(filename); return EXIT_SUCCESS; } #endif -static void justprintpub(const char* filename) { +static int printpubfile(const char* filename) { buffer *buf = NULL; sign_key *key = NULL; @@ -353,7 +289,7 @@ out: sign_key_free(key); key = NULL; } - exit(err); + return err; } static void printpubkey(sign_key * key, int keytype) { @@ -402,35 +338,3 @@ static void printpubkey(sign_key * key, int keytype) { m_free(fp); buf_free(buf); } - -/* Write a buffer to a file specified, failing if the file exists */ -static void buf_writefile(buffer * buf, const char * filename) { - - int fd; - int len; - - fd = open(filename, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); - if (fd < 0) { - fprintf(stderr, "Couldn't create new file %s\n", filename); - perror("Reason"); - buf_burn(buf); - exit(EXIT_FAILURE); - } - - /* write the file now */ - while (buf->pos != buf->len) { - len = write(fd, buf_getptr(buf, buf->len - buf->pos), - buf->len - buf->pos); - if (errno == EINTR) { - continue; - } - if (len <= 0) { - fprintf(stderr, "Failed writing file '%s'\n",filename); - perror("Reason"); - exit(EXIT_FAILURE); - } - buf_incrpos(buf, len); - } - - close(fd); -} @@ -6,7 +6,7 @@ #ifdef DROPBEAR_ECC -// .dp members are filled out by dropbear_ecc_fill_dp() at startup +/* .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, @@ -44,7 +44,7 @@ struct dropbear_ecc_curve *dropbear_ecc_curves[] = { void dropbear_ecc_fill_dp() { struct dropbear_ecc_curve **curve; - // libtomcrypt guarantees they're ordered by size + /* 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++) { @@ -8,6 +8,13 @@ #ifdef DROPBEAR_ECDSA +int signkey_is_ecdsa(enum signkey_type type) +{ + return type == DROPBEAR_SIGNKEY_ECDSA_NISTP256 + || type == DROPBEAR_SIGNKEY_ECDSA_NISTP384 + || type == DROPBEAR_SIGNKEY_ECDSA_NISTP521; +} + enum signkey_type ecdsa_signkey_type(ecc_key * key) { #ifdef DROPBEAR_ECC_256 if (key->dp == ecc_curve_nistp256.dp) { @@ -239,8 +246,8 @@ out: // 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) { +static int buf_get_ecdsa_verify_params(buffer *buf, + void *r, void* s) { int ret = DROPBEAR_FAILURE; unsigned int sig_len; unsigned int sig_pos; @@ -295,7 +302,7 @@ int buf_ecdsa_verify(buffer *buf, ecc_key *key, buffer *data_buf) { dropbear_exit("ECC error"); } - if (buf_get_ecdsa_verify_params(buf, curve, r, s) != DROPBEAR_SUCCESS) { + if (buf_get_ecdsa_verify_params(buf, r, s) != DROPBEAR_SUCCESS) { goto out; } @@ -7,6 +7,7 @@ #ifdef DROPBEAR_ECDSA +/* Prefer the larger size - it's fast anyway */ #if defined(DROPBEAR_ECC_521) #define ECDSA_DEFAULT_SIZE 521 #elif defined(DROPBEAR_ECC_384) @@ -26,6 +27,8 @@ 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); +/* Returns 1 on success */ +int signkey_is_ecdsa(enum signkey_type type); #endif diff --git a/gensignkey.c b/gensignkey.c new file mode 100644 index 0000000..88a3949 --- /dev/null +++ b/gensignkey.c @@ -0,0 +1,131 @@ +#include "includes.h" +#include "dbutil.h" +#include "buffer.h" +#include "ecdsa.h" +#include "genrsa.h" +#include "gendss.h" +#include "signkey.h" + +#define RSA_DEFAULT_SIZE 2048 +#define DSS_DEFAULT_SIZE 1024 + +// Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE +static int buf_writefile(buffer * buf, const char * filename) { + int ret = DROPBEAR_FAILURE; + int fd = -1; + + fd = open(filename, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); + if (fd < 0) { + dropbear_log(LOG_ERR, "Couldn't create new file %s: %s", + filename, strerror(errno)); + goto out; + } + + /* write the file now */ + while (buf->pos != buf->len) { + int len = write(fd, buf_getptr(buf, buf->len - buf->pos), + buf->len - buf->pos); + if (errno == EINTR) { + continue; + } + if (len <= 0) { + dropbear_log(LOG_ERR, "Failed writing file %s: %s", + filename, strerror(errno)); + goto out; + } + buf_incrpos(buf, len); + } + + ret = DROPBEAR_SUCCESS; + +out: + if (fd >= 0) { + m_close(fd); + } + return ret; +} + +/* returns 0 on failure */ +static int get_default_bits(enum signkey_type keytype) +{ + switch (keytype) { +#ifdef DROPBEAR_RSA + case DROPBEAR_SIGNKEY_RSA: + return RSA_DEFAULT_SIZE; +#endif +#ifdef DROPBEAR_DSS + case DROPBEAR_SIGNKEY_DSS: + return DSS_DEFAULT_SIZE; +#endif +#ifdef DROPBEAR_ECDSA + case DROPBEAR_SIGNKEY_ECDSA_KEYGEN: + return ECDSA_DEFAULT_SIZE; + case DROPBEAR_SIGNKEY_ECDSA_NISTP521: + return 521; + case DROPBEAR_SIGNKEY_ECDSA_NISTP384: + return 384; + case DROPBEAR_SIGNKEY_ECDSA_NISTP256: + return 256; +#endif + default: + return 0; + } +} + +int signkey_generate(enum signkey_type keytype, int bits, const char* filename) +{ + sign_key * key = NULL; + buffer *buf = NULL; + int ret = DROPBEAR_FAILURE; + if (bits == 0) + { + bits = get_default_bits(keytype); + } + + /* now we can generate the key */ + key = new_sign_key(); + + seedrandom(); + + switch(keytype) { +#ifdef DROPBEAR_RSA + case DROPBEAR_SIGNKEY_RSA: + key->rsakey = gen_rsa_priv_key(bits); + break; +#endif +#ifdef DROPBEAR_DSS + case DROPBEAR_SIGNKEY_DSS: + key->dsskey = gen_dss_priv_key(bits); + break; +#endif +#ifdef DROPBEAR_ECDSA + case DROPBEAR_SIGNKEY_ECDSA_KEYGEN: + case DROPBEAR_SIGNKEY_ECDSA_NISTP521: + case DROPBEAR_SIGNKEY_ECDSA_NISTP384: + case DROPBEAR_SIGNKEY_ECDSA_NISTP256: + { + ecc_key *ecckey = gen_ecdsa_priv_key(bits); + keytype = ecdsa_signkey_type(ecckey); + *signkey_key_ptr(key, keytype) = ecckey; + } + break; +#endif + default: + dropbear_exit("Internal error"); + } + + seedrandom(); + + buf = buf_new(MAX_PRIVKEY_SIZE); + + buf_put_priv_key(buf, key, keytype); + sign_key_free(key); + key = NULL; + buf_setpos(buf, 0); + ret = buf_writefile(buf, filename); + + buf_burn(buf); + buf_free(buf); + buf = NULL; + return ret; +} diff --git a/gensignkey.h b/gensignkey.h new file mode 100644 index 0000000..b463a42 --- /dev/null +++ b/gensignkey.h @@ -0,0 +1,8 @@ +#ifndef _GENSIGNKEY_H +#define _GENSIGNKEY_H + +#include "signkey.h" + +int signkey_generate(enum signkey_type type, int bits, const char* filename); + +#endif @@ -47,6 +47,13 @@ void kexecdh_comb_key(struct kex_ecdh_param *param, buffer *pub_them, sign_key *hostkey); #endif +#ifdef DROPBEAR_CURVE25519 +struct kex_curve25519_param *gen_kexcurve25519_param(); +void free_kexcurve25519_param(struct kex_curve25519_param *param); +void kexcurve25519_comb_key(struct kex_curve25519_param *param, buffer *pub_them, + sign_key *hostkey); +#endif + #ifndef DISABLE_ZLIB int is_compress_trans(); int is_compress_recv(); @@ -92,6 +99,18 @@ struct kex_ecdh_param { }; #endif +#ifdef DROPBEAR_CURVE25519 +#define CURVE25519_LEN 32 +struct kex_curve25519_param { + unsigned char priv[CURVE25519_LEN]; + unsigned char pub[CURVE25519_LEN]; +}; + +/* No header file for curve25519_donna */ +int curve25519_donna(unsigned char *out, const unsigned char *secret, const unsigned char *other); +#endif + + #define MAX_KEXHASHBUF 2000 #endif /* _KEX_H_ */ diff --git a/keyimport.c b/keyimport.c index f3fe96a..7098ae7 100644 --- a/keyimport.c +++ b/keyimport.c @@ -112,7 +112,7 @@ static sign_key *dropbear_read(const char* filename) { buffer * buf = NULL; sign_key *ret = NULL; - int type; + enum signkey_type type; buf = buf_new(MAX_PRIVKEY_SIZE); if (buf_readfile(buf, filename) == DROPBEAR_FAILURE) { @@ -501,7 +501,7 @@ static int openssh_encrypted(const char *filename) return ret; } -static sign_key *openssh_read(const char *filename, char *passphrase) +static sign_key *openssh_read(const char *filename, char * UNUSED(passphrase)) { struct openssh_key *key; unsigned char *p; @@ -511,7 +511,7 @@ static sign_key *openssh_read(const char *filename, char *passphrase) char *errmsg; char *modptr = NULL; int modlen = -9999; - int type; + enum signkey_type type; sign_key *retkey; buffer * blobbuf = NULL; @@ -709,19 +709,29 @@ static sign_key *openssh_read(const char *filename, char *passphrase) goto error; } - if (len == sizeof(OID_SEC256R1_BLOB) + if (0) {} +#ifdef DROPBEAR_ECC_256 + else 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) + } +#endif +#ifdef DROPBEAR_ECC_384 + 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) + } +#endif +#ifdef DROPBEAR_ECC_521 + 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 { + } +#endif + else { errmsg = "Unknown ECC key type"; goto error; } @@ -1018,8 +1028,8 @@ static int openssh_write(const char *filename, sign_key *key, } */ buffer *seq_buf = buf_new(400); - ecc_key **eck = signkey_ecc_key_ptr(key, key->type); - const unsigned long curve_size = (*eck)->dp->size; + ecc_key **eck = (ecc_key**)signkey_key_ptr(key, key->type); + const long curve_size = (*eck)->dp->size; int curve_oid_len = 0; const void* curve_oid = NULL; unsigned long pubkey_size = 2*curve_size+1; @@ -8,7 +8,7 @@ /* 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 */ +/* IMPORTANT: Many options will require "make clean" after changes */ #ifndef DROPBEAR_DEFPORT #define DROPBEAR_DEFPORT "22" @@ -129,7 +129,7 @@ much traffic. */ /* You can also disable integrity. Don't bother disabling this if you're * still using a cipher, it's relatively cheap. If you disable this it's dead - * simple to run arbitrary commands on the remote host. Beware. */ + * simple for an attacker to run arbitrary commands on the remote host. Beware. */ /* #define DROPBEAR_NONE_INTEGRITY */ /* Hostkey/public key algorithms - at least one required, these are used @@ -138,14 +138,26 @@ much traffic. */ * SSH2 RFC Draft requires dss, recommends rsa */ #define DROPBEAR_RSA #define DROPBEAR_DSS - -#define DROPBEAR_ECDH +/* ECDSA is significantly faster than RSA or DSS. Compiling in ECC + * code (either ECDSA or ECDH) increases binary size - around 30kB + * on x86-64 */ #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. */ -#define RSA_BLINDING +/* Generate hostkeys as-needed when the first connection using that key type occurs. + This avoids the need to otherwise run "dropbearkey" and avoids some problems + with badly seeded /dev/urandom when systems first boot. + This also requires a runtime flag "-R". This adds ~4kB to binary size (or hardly + anything if dropbearkey is linked in a "dropbearmulti" binary) */ +#define DROPBEAR_DELAY_HOSTKEY + +/* Enable Curve25519 for key exchange. This is another elliptic + * curve method with good security properties. Increases binary size + * by ~8kB on x86-64 */ +#define DROPBEAR_CURVE25519 + +/* Enable elliptic curve Diffie Hellman key exchange, see note about + * ECDSA above */ +#define DROPBEAR_ECDH /* Control the memory/performance/compression tradeoff for zlib. * Set windowBits=8 for least memory usage, see your system's @@ -182,7 +194,7 @@ much traffic. */ #define ENABLE_SVR_PASSWORD_AUTH /* PAM requires ./configure --enable-pam */ -//#define ENABLE_SVR_PAM_AUTH +/*#define ENABLE_SVR_PAM_AUTH */ #define ENABLE_SVR_PUBKEY_AUTH /* Whether to take public key options in @@ -100,6 +100,8 @@ typedef struct svr_runopts { sign_key *hostkey; + int delay_hostkey; + char *hostkey_files[MAX_HOSTKEYS]; int num_hostkey_files; @@ -244,6 +244,7 @@ struct clientsession { // XXX - move these to kexstate? struct kex_dh_param *dh_param; struct kex_ecdh_param *ecdh_param; + struct kex_curve25519_param *curve25519_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 */ @@ -39,8 +39,7 @@ static const char *signkey_names[DROPBEAR_SIGNKEY_NUM_NAMED] = { #ifdef DROPBEAR_ECDSA "ecdsa-sha2-nistp256", "ecdsa-sha2-nistp384", - "ecdsa-sha2-nistp521", - "ecdsa" // for keygen + "ecdsa-sha2-nistp521" #endif // DROPBEAR_ECDSA }; @@ -181,7 +180,7 @@ int buf_get_pub_key(buffer *buf, sign_key *key, enum signkey_type *type) { } #endif #ifdef DROPBEAR_ECDSA - { + if (signkey_is_ecdsa(keytype)) { ecc_key **eck = (ecc_key**)signkey_key_ptr(key, keytype); if (eck) { if (*eck) { @@ -249,7 +248,7 @@ int buf_get_priv_key(buffer *buf, sign_key *key, enum signkey_type *type) { } #endif #ifdef DROPBEAR_ECDSA - { + if (signkey_is_ecdsa(keytype)) { ecc_key **eck = (ecc_key**)signkey_key_ptr(key, keytype); if (eck) { if (*eck) { @@ -289,10 +288,7 @@ void buf_put_pub_key(buffer* buf, sign_key *key, enum signkey_type type) { } #endif #ifdef DROPBEAR_ECDSA - if (type == DROPBEAR_SIGNKEY_ECDSA_NISTP256 - || type == DROPBEAR_SIGNKEY_ECDSA_NISTP384 - || type == DROPBEAR_SIGNKEY_ECDSA_NISTP521) - { + if (signkey_is_ecdsa(type)) { ecc_key **eck = (ecc_key**)signkey_key_ptr(key, type); if (eck) { buf_put_ecdsa_pub_key(pubkeys, *eck); @@ -329,7 +325,7 @@ void buf_put_priv_key(buffer* buf, sign_key *key, enum signkey_type type) { } #endif #ifdef DROPBEAR_ECDSA - { + if (signkey_is_ecdsa(type)) { ecc_key **eck = (ecc_key**)signkey_key_ptr(key, type); if (eck) { buf_put_ecdsa_priv_key(buf, *eck); @@ -354,19 +350,25 @@ void sign_key_free(sign_key *key) { key->rsakey = NULL; #endif #ifdef DROPBEAR_ECDSA +#ifdef DROPBEAR_ECC_256 if (key->ecckey256) { ecc_free(key->ecckey256); key->ecckey256 = NULL; } +#endif +#ifdef DROPBEAR_ECC_384 if (key->ecckey384) { ecc_free(key->ecckey384); key->ecckey384 = NULL; } +#endif +#ifdef DROPBEAR_ECC_521 if (key->ecckey521) { ecc_free(key->ecckey521); key->ecckey521 = NULL; } #endif +#endif m_free(key->filename); @@ -484,7 +486,7 @@ void buf_put_sign(buffer* buf, sign_key *key, enum signkey_type type, } #endif #ifdef DROPBEAR_ECDSA - { + if (signkey_is_ecdsa(type)) { ecc_key **eck = (ecc_key**)signkey_key_ptr(key, type); if (eck) { buf_put_ecdsa_sign(sigblob, *eck, data_buf); @@ -535,7 +537,7 @@ int buf_verify(buffer * buf, sign_key *key, buffer *data_buf) { } #endif #ifdef DROPBEAR_ECDSA - { + if (signkey_is_ecdsa(type)) { ecc_key **eck = (ecc_key**)signkey_key_ptr(key, type); if (eck) { return buf_ecdsa_verify(buf, *eck, data_buf); @@ -231,7 +231,7 @@ static int checkusername(unsigned char *username, unsigned int userlen) { char* listshell = NULL; char* usershell = NULL; - int uid; + uid_t uid; TRACE(("enter checkusername")) if (userlen > MAX_USERNAME_LEN) { return DROPBEAR_FAILURE; diff --git a/svr-authpubkey.c b/svr-authpubkey.c index e0727de..66fe5e5 100644 --- a/svr-authpubkey.c +++ b/svr-authpubkey.c @@ -89,7 +89,7 @@ void svr_auth_pubkey() { buffer * signbuf = NULL; sign_key * key = NULL; char* fp = NULL; - int type = -1; + enum signkey_type type = -1; TRACE(("enter pubkeyauth")) @@ -294,8 +294,8 @@ static int checkpubkey(unsigned char* algo, unsigned int algolen, options_buf = buf_new(options_len); buf_putbytes(options_buf, options_start, options_len); - /* compare the algorithm */ - if (line->pos + algolen > line->len) { + /* compare the algorithm. +3 so we have enough bytes to read a space and some base64 characters too. */ + if (line->pos + algolen+3 > line->len) { continue; } if (strncmp(buf_getptr(line, algolen), algo, algolen) != 0) { @@ -35,6 +35,7 @@ #include "random.h" #include "runopts.h" #include "ecc.h" +#include "gensignkey.h" static void send_msg_kexdh_reply(mp_int *dh_e, buffer *ecdh_qs); @@ -52,15 +53,22 @@ void recv_msg_kexdh_init() { dropbear_exit("Premature kexdh_init message received"); } - 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); + switch (ses.newkeys->algo_kex->mode) { + case DROPBEAR_KEX_NORMAL_DH: + m_mp_init(&dh_e); + if (buf_getmpint(ses.payload, &dh_e) != DROPBEAR_SUCCESS) { + dropbear_exit("Bad kex value"); + } + break; + case DROPBEAR_KEX_ECDH: + case DROPBEAR_KEX_CURVE25519: +#if defined(DROPBEAR_ECDH) || defined(DROPBEAR_CURVE25519) + ecdh_qs = buf_getstringbuf(ses.payload); #endif + break; + } + if (ses.payload->pos != ses.payload->len) { + dropbear_exit("Bad kex value"); } send_msg_kexdh_reply(&dh_e, ecdh_qs); @@ -68,6 +76,7 @@ void recv_msg_kexdh_init() { mp_clear(&dh_e); if (ecdh_qs) { buf_free(ecdh_qs); + ecdh_qs = NULL; } send_msg_newkeys(); @@ -75,6 +84,79 @@ void recv_msg_kexdh_init() { ses.requirenext[1] = 0; TRACE(("leave recv_msg_kexdh_init")) } + +#ifdef DROPBEAR_DELAY_HOSTKEY +static void svr_ensure_hostkey() { + + const char* fn = NULL; + char *fn_temp = NULL; + enum signkey_type type = ses.newkeys->algo_hostkey; + void **hostkey = signkey_key_ptr(svr_opts.hostkey, type); + int ret = DROPBEAR_FAILURE; + + if (hostkey && *hostkey) { + return; + } + + switch (type) + { +#ifdef DROPBEAR_RSA + case DROPBEAR_SIGNKEY_RSA: + fn = RSA_PRIV_FILENAME; + break; +#endif +#ifdef DROPBEAR_DSS + case DROPBEAR_SIGNKEY_DSS: + fn = DSS_PRIV_FILENAME; + break; +#endif +#ifdef DROPBEAR_ECDSA + case DROPBEAR_SIGNKEY_ECDSA_NISTP256: + case DROPBEAR_SIGNKEY_ECDSA_NISTP384: + case DROPBEAR_SIGNKEY_ECDSA_NISTP521: + fn = ECDSA_PRIV_FILENAME; + break; +#endif + default: + (void)0; + } + + if (readhostkey(fn, svr_opts.hostkey, &type) == DROPBEAR_SUCCESS) { + return; + } + + fn_temp = m_malloc(strlen(fn) + 20); + snprintf(fn_temp, strlen(fn)+20, "%s.tmp%d", fn, getpid()); + + if (signkey_generate(type, 0, fn_temp) == DROPBEAR_FAILURE) { + goto out; + } + + if (link(fn_temp, fn) < 0) { + /* It's OK to get EEXIST - we probably just lost a race + with another connection to generate the key */ + if (errno != EEXIST) { + dropbear_log(LOG_ERR, "Failed moving key file to %s: %s", fn, + strerror(errno)); + /* XXX fallback to non-atomic copy for some filesystems? */ + goto out; + } + } + + ret = readhostkey(fn, svr_opts.hostkey, &type); + +out: + if (fn_temp) { + unlink(fn_temp); + m_free(fn_temp); + } + + if (ret == DROPBEAR_FAILURE) + { + dropbear_exit("Couldn't read or generate hostkey %s", fn); + } +} +#endif /* Generate our side of the diffie-hellman key exchange value (dh_f), and * calculate the session key using the diffie-hellman algorithm. Following @@ -88,26 +170,50 @@ static void send_msg_kexdh_reply(mp_int *dh_e, buffer *ecdh_qs) { /* we can start creating the kexdh_reply packet */ CHECKCLEARTOWRITE(); + +#ifdef DROPBEAR_DELAY_HOSTKEY + if (svr_opts.delay_hostkey) + { + svr_ensure_hostkey(); + } +#endif + buf_putbyte(ses.writepayload, SSH_MSG_KEXDH_REPLY); buf_put_pub_key(ses.writepayload, svr_opts.hostkey, ses.newkeys->algo_hostkey); - 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 { + switch (ses.newkeys->algo_kex->mode) { + case DROPBEAR_KEX_NORMAL_DH: + { + 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); + } + break; + case DROPBEAR_KEX_ECDH: #ifdef DROPBEAR_ECDH - struct kex_ecdh_param *ecdh_param = gen_kexecdh_param(); - kexecdh_comb_key(ecdh_param, ecdh_qs, svr_opts.hostkey); + { + 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); + buf_put_ecc_raw_pubkey_string(ses.writepayload, &ecdh_param->key); + free_kexecdh_param(ecdh_param); + } +#endif + break; + case DROPBEAR_KEX_CURVE25519: +#ifdef DROPBEAR_CURVE25519 + { + struct kex_curve25519_param *param = gen_kexcurve25519_param(); + kexcurve25519_comb_key(param, ecdh_qs, svr_opts.hostkey); + buf_putstring(ses.writepayload, param->pub, CURVE25519_LEN); + free_kexcurve25519_param(param); + } #endif + break; } /* calc the signature */ diff --git a/svr-runopts.c b/svr-runopts.c index 2db88c2..cbfd190 100644 --- a/svr-runopts.c +++ b/svr-runopts.c @@ -44,13 +44,19 @@ static void printhelp(const char * progname) { "-b bannerfile Display the contents of bannerfile" " before user login\n" " (default: none)\n" + "-r keyfile Specify hostkeys (repeatable)\n" + " defaults: \n" #ifdef DROPBEAR_DSS - "-d dsskeyfile Use dsskeyfile for the DSS host key\n" - " (default: %s)\n" + " dss %s\n" #endif #ifdef DROPBEAR_RSA - "-r rsakeyfile Use rsakeyfile for the RSA host key\n" - " (default: %s)\n" + " rsa %s\n" +#endif +#ifdef DROPBEAR_ECDSA + " ecdsa %s\n" +#endif +#ifdef DROPBEAR_DELAY_HOSTKEY + "-R Create hostkeys as required\n" #endif "-F Don't fork into background\n" #ifdef DISABLE_SYSLOG @@ -96,6 +102,9 @@ static void printhelp(const char * progname) { #ifdef DROPBEAR_RSA RSA_PRIV_FILENAME, #endif +#ifdef DROPBEAR_ECDSA + ECDSA_PRIV_FILENAME, +#endif DROPBEAR_MAX_PORTS, DROPBEAR_DEFPORT, DROPBEAR_PIDFILE, DEFAULT_RECV_WINDOW, DEFAULT_KEEPALIVE, DEFAULT_IDLE_TIMEOUT); } @@ -122,6 +131,7 @@ void svr_getopts(int argc, char ** argv) { svr_opts.inetdmode = 0; svr_opts.portcount = 0; svr_opts.hostkey = NULL; + svr_opts.delay_hostkey = 0; svr_opts.pidfile = DROPBEAR_PIDFILE; #ifdef ENABLE_SVR_LOCALTCPFWD svr_opts.nolocaltcp = 0; @@ -180,6 +190,9 @@ void svr_getopts(int argc, char ** argv) { case 'r': next = &keyfile; break; + case 'R': + svr_opts.delay_hostkey = 1; + break; case 'F': svr_opts.forkbg = 0; break; @@ -390,37 +403,37 @@ static void loadhostkey_helper(const char *name, void** src, void** dst, int fat /* Must be called after syslog/etc is working */ static void loadhostkey(const char *keyfile, int fatal_duplicate) { sign_key * read_key = new_sign_key(); - int type = DROPBEAR_SIGNKEY_ANY; + enum signkey_type 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); + loadhostkey_helper("RSA", (void**)&read_key->rsakey, (void**)&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); + loadhostkey_helper("DSS", (void**)&read_key->dsskey, (void**)&svr_opts.hostkey->dsskey, fatal_duplicate); } #endif #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); + loadhostkey_helper("ECDSA256", (void**)&read_key->ecckey256, (void**)&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); + loadhostkey_helper("ECDSA384", (void**)&read_key->ecckey384, (void**)&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); + loadhostkey_helper("ECDSA521", (void**)&read_key->ecckey521, (void**)&svr_opts.hostkey->ecckey521, fatal_duplicate); } #endif #endif // DROPBEAR_ECDSA @@ -438,6 +451,7 @@ static void addhostkey(const char *keyfile) { void load_all_hostkeys() { int i; + int disable_unset_keys = 1; svr_opts.hostkey = new_sign_key(); @@ -459,31 +473,47 @@ void load_all_hostkeys() { loadhostkey(ECDSA_PRIV_FILENAME, 0); #endif +#ifdef DROPBEAR_DELAY_HOSTKEY + if (svr_opts.delay_hostkey) + { + disable_unset_keys = 0; + } +#endif + #ifdef DROPBEAR_RSA - if (!svr_opts.hostkey->rsakey) { + if (disable_unset_keys && !svr_opts.hostkey->rsakey) { disablekey(DROPBEAR_SIGNKEY_RSA); } #endif + #ifdef DROPBEAR_DSS - if (!svr_opts.hostkey->dsskey) { + if (disable_unset_keys && !svr_opts.hostkey->dsskey) { disablekey(DROPBEAR_SIGNKEY_RSA); } #endif + + #ifdef DROPBEAR_ECDSA #ifdef DROPBEAR_ECC_256 - if (!svr_opts.hostkey->ecckey256) { + if ((disable_unset_keys || ECDSA_DEFAULT_SIZE != 256) + && !svr_opts.hostkey->ecckey256) { disablekey(DROPBEAR_SIGNKEY_ECDSA_NISTP256); } #endif + #ifdef DROPBEAR_ECC_384 - if (!svr_opts.hostkey->ecckey384) { + if ((disable_unset_keys || ECDSA_DEFAULT_SIZE != 384) + && !svr_opts.hostkey->ecckey384) { disablekey(DROPBEAR_SIGNKEY_ECDSA_NISTP384); } #endif + #ifdef DROPBEAR_ECC_521 - if (!svr_opts.hostkey->ecckey521) { + if ((disable_unset_keys || ECDSA_DEFAULT_SIZE != 521) + && !svr_opts.hostkey->ecckey521) { disablekey(DROPBEAR_SIGNKEY_ECDSA_NISTP521); } #endif -#endif +#endif /* DROPBEAR_ECDSA */ + } diff --git a/sysoptions.h b/sysoptions.h index 8d57375..8459eb6 100644 --- a/sysoptions.h +++ b/sysoptions.h @@ -104,21 +104,27 @@ #define DROPBEAR_LTC_PRNG #endif -// hashes which will be linked and registered -#if defined(DROPBEAR_SHA2_256_HMAC) || defined(DROPBEAR_ECC_256) +/* 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. */ +#define RSA_BLINDING + +/* hashes which will be linked and registered */ +#if defined(DROPBEAR_SHA2_256_HMAC) || defined(DROPBEAR_ECC_256) || defined(DROPBEAR_CURVE25519) #define DROPBEAR_SHA256 #endif #if defined(DROPBEAR_ECC_384) #define DROPBEAR_SHA384 #endif -#if defined(DROPBEAR_SHA2_512_HMAC) || defined(DROPBEAR_ECC_521) +/* LTC SHA384 depends on SHA512 */ +#if defined(DROPBEAR_SHA2_512_HMAC) || defined(DROPBEAR_ECC_521) || defined(DROPBEAR_ECC_384) #define DROPBEAR_SHA512 #endif #if defined(DROPBEAR_MD5_HMAC) #define DROPBEAR_MD5 #endif -// roughly 2x 521 bits +/* roughly 2x 521 bits */ #define MAX_ECC_SIZE 140 #define MAX_NAME_LEN 64 /* maximum length of a protocol name, isn't |