# HG changeset patch # User Matt Johnston # Date 1393508158 -28800 # Node ID 89555751c489f8fe54ce0c1f4440a32b024b5857 # Parent e4b75744acab70f35bfbd349e126854f5aa081c3# Parent 4a74c58e11fc148d8a5fd56012941d82f82ae2a3 merge up to 2013.63, improve ASM makefile rules a bit diff -r e4b75744acab -r 89555751c489 .hgsigs --- a/.hgsigs Sun Oct 06 22:32:03 2013 +0800 +++ b/.hgsigs Thu Feb 27 21:35:58 2014 +0800 @@ -5,3 +5,8 @@ 095b46180bbc412b029420587736a6185afc17e1 0 iEYEABECAAYFAlFsCnkACgkQjPn4sExkf7xLrwCfeMWjUaSmfU/fvseT5TdrYRqBEVQAoLz5SFLEA40C5f8zE8Ma/vgVJVIC f168962bab857ca030829e4cd73d9b32c868c874 0 iEYEABECAAYFAlFwDNwACgkQjPn4sExkf7wJ6QCePVovn/avKXUyNwNBYCcov6JLYqkAnRCPQdkXgv20N3t10r6PRMBBo1/S deb211f75ca194e2fcf0d2e5f71c60474e42ec95 0 iEYEABECAAYFAlJO01cACgkQjPn4sExkf7yDqACaA/P+Yl/K2Cv3OC5G0b7ck2Kb75EAoIeW7qpCyclzJLWwk95koED+4lxD +025237c9f0a1a60a616f984d82fb2a9270d3b0ea 0 iEYEABECAAYFAlJeqDYACgkQjPn4sExkf7y5nQCfW6t+TJySBTTo+gCfDUBPRVxvNe8AoIn/15aWfqH/A2G9uikfoVtWK3pd +a50a1dc743317fad9b3737bc68fbca640659bb6d 0 iEYEABECAAYFAlJeqL0ACgkQjPn4sExkf7yVqACg6IP0fU29+Feh/TDeemDA+2XAzrIAoIdZfMDvVYlDoWotZD8ACFnf5H1P +9ec083a21adfcb099f21eb03704b66d14a4ba800 0 iEYEABECAAYFAlKE4JoACgkQjPn4sExkf7wLDgCghkVGwMjI138bEv+ORVzN7zIH7cEAoLckaxZc1k1aXlmlSCRlP8cuKH3o +3d1d7d151c0ce3a79da62e86463f5632fa2b144a 0 iEYEABECAAYFAlKd5AEACgkQjPn4sExkf7wzWgCfdvPEEIdlMPqcbOQMJ7b+eAyy164An2ip1lPh1eS5g26/gSfruvWBVym4 +277429102f1337bd10c89107d3e01de509cc1a7e 0 iEYEABECAAYFAlMEvF4ACgkQjPn4sExkf7xeVQCgtbxJ4G3hsFwUOM0K1WGr1J2vsbEAoMM8dEyr1mdrbgO1tzNLfD1nxbyn diff -r e4b75744acab -r 89555751c489 .hgtags --- a/.hgtags Sun Oct 06 22:32:03 2013 +0800 +++ b/.hgtags Thu Feb 27 21:35:58 2014 +0800 @@ -39,3 +39,7 @@ 96b8bcb88017815040949a417caa55686271e8a9 DROPBEAR_2013.57 e76614145aea67f66e4a4257685c771efba21aa1 DROPBEAR_2013.58 7b68e581985fd4ea50869f8608ab95cda5d17876 DROPBEAR_2013.59 +a50a1dc743317fad9b3737bc68fbca640659bb6d DROPBEAR_2013.60 +e894dbc015ba7ff4c3bf897ee20e28ca90c55a16 DROPBEAR_2013.61test +3d1d7d151c0ce3a79da62e86463f5632fa2b144a DROPBEAR_2013.62 +2351b2da8e0d08dcc6e64fcc328b53b9630bda68 DROPBEAR_2014.63 diff -r e4b75744acab -r 89555751c489 CHANGES --- a/CHANGES Sun Oct 06 22:32:03 2013 +0800 +++ b/CHANGES Thu Feb 27 21:35:58 2014 +0800 @@ -1,3 +1,89 @@ +2014.63 - Wednesday 19 February 2014 + +- Fix ~. to terminate a client interactive session after waking a laptop + from sleep. + +- Changed port separator syntax again, now using host^port. This is because + IPv6 link-local addresses use %. Reported by Gui Iribarren + +- Avoid constantly relinking dropbearmulti target, fix "make install" + for multi target, thanks to Mike Frysinger + +- Avoid getting stuck in a loop writing huge key files, reported by Bruno + Thomsen + +- Don't link dropbearkey or dropbearconvert to libz or libutil, + thanks to Nicolas Boos + +- Fix linking -lcrypt on systems without /usr/lib, thanks to Nicolas Boos + +- Avoid crash on exit due to cleaned up keys before last packets are sent, + debugged by Ronald Wahl + +- Fix a race condition in rekeying where Dropbear would exit if it received a + still-in-flight packet after initiating rekeying. Reported by Oliver Metz. + This is a longstanding bug but is triggered more easily since 2013.57 + +- Fix README for ecdsa keys, from Catalin Patulea + +- Ensure that generated RSA keys are always exactly the length + requested. Previously Dropbear always generated N+16 or N+15 bit keys. + Thanks to Unit 193 + +- Fix DROPBEAR_CLI_IMMEDIATE_AUTH mode which saves a network round trip if the + first public key succeeds. Still not enabled by default, needs more + compatibility testing with other implementations. + +- Fix for port 0 forwarding in the client and port forwarding with Apache MINA SSHD. Thanks to + +- Fix for bad system linux/pkt-sched.h header file with older Linux +kernels, from Steve Dover + +- Fix signal handlers so that errno is saved, thanks to Erik AhlĂ©n for a patch + and Mark Wickham for independently spotting the same problem. + +2013.62 - Tuesday 3 December 2013 + +- Disable "interactive" QoS connection options when a connection doesn't + have a PTY (eg scp, rsync). Thanks to Catalin Patulea for the patch. + +- Log when a hostkey is generated with -R, fix some bugs in handling server + hostkey commandline options + +- Fix crash in Dropbearconvert and 521 bit key, reported by NiLuJe + +- Update config.guess and config.sub again + +2013.61test - Thursday 14 November 2013 + +- ECC (elliptic curve) support. Supports ECDSA hostkeys (requires new keys to + be generated) and ECDH for setting up encryption keys (no intervention + required). This is significantly faster. + +- curve25519-sha256@libssh.org support for setting up encryption keys. This is + another elliptic curve mode with less potential of NSA interference in + algorithm parameters. curve25519-donna code thanks to Adam Langley + +- -R option to automatically generate hostkeys. This is recommended for + embedded platforms since it allows the system random number device + /dev/urandom a longer startup time to generate a secure seed before the + hostkey is required. + +- Compile fixes for old vendor compilers like Tru64 from Daniel Richard G. + +- Make authorized_keys handling more robust, don't exit encountering + malformed lines. Thanks to Lorin Hochstein and Mark Stillwell + +2013.60 - Wednesday 16 October 2013 + +- Fix "make install" so that it doesn't always install to /bin and /sbin + +- Fix "make install MULTI=1", installing manpages failed + +- Fix "make install" when scp is included since it has no manpage + +- Make --disable-bundled-libtom work + 2013.59 - Friday 4 October 2013 - Fix crash from -J command @@ -14,10 +100,10 @@ - Limit the size of decompressed payloads, avoids memory exhaustion denial of service - Thanks to Logan Lamb for reporting and investigating it + Thanks to Logan Lamb for reporting and investigating it. CVE-2013-4421 - Avoid disclosing existence of valid users through inconsistent delays - Thanks to Logan Lamb for reporting + Thanks to Logan Lamb for reporting. CVE-2013-4434 - Update config.guess and config.sub for newer architectures @@ -318,7 +404,7 @@ - Security: dbclient previously would prompt to confirm a mismatching hostkey but wouldn't warn loudly. It will now - exit upon a mismatch. + exit upon a mismatch. CVE-2007-1099 - Compile fixes, make sure that all variable definitions are at the start of a scope. @@ -380,7 +466,7 @@ (thanks to Tomas Vanek for helping track it down) - Implement per-IP pre-authentication connection limits - (after some poking from Pablo Fernandez) + (after some poking from Pablo Fernandez) CVE-2006-1206 - Exit gracefully if trying to connect to as SSH v1 server (reported by Rushi Lala) @@ -401,7 +487,7 @@ - SECURITY: fix for buffer allocation error in server code, could potentially allow authenticated users to gain elevated privileges. All multi-user systems running the server should upgrade (or apply the patch available on the - Dropbear webpage). + Dropbear webpage). CVE-2005-4178 - Fix channel handling code so that redirecting to /dev/null doesn't use 100% CPU. @@ -608,7 +694,7 @@ - SECURITY: Don't try to free() uninitialised variables in DSS verification code. Thanks to Arne Bernin for pointing out this bug. This is possibly exploitable, all users with DSS and pubkey-auth compiled in are advised to - upgrade. + upgrade. CVE-2004-2486 - Clean up agent forwarding socket files correctly, patch from Gerrit Pape. diff -r e4b75744acab -r 89555751c489 LICENSE --- a/LICENSE Sun Oct 06 22:32:03 2013 +0800 +++ b/LICENSE Thu Feb 27 21:35:58 2014 +0800 @@ -87,3 +87,55 @@ FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +===== + +curve25519-donna: + +/* 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 + * + * Derived from public domain C code by Daniel J. Bernstein + * + * 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. + */ diff -r e4b75744acab -r 89555751c489 MULTI --- a/MULTI Sun Oct 06 22:32:03 2013 +0800 +++ b/MULTI Thu Feb 27 21:35:58 2014 +0800 @@ -20,7 +20,3 @@ then execute as normal: ./dropbear - -"make install" doesn't currently work for multi-binary configuration, though -in most situations where it is being used, the target and build systems will -differ. diff -r e4b75744acab -r 89555751c489 Makefile.in --- a/Makefile.in Sun Oct 06 22:32:03 2013 +0800 +++ b/Makefile.in Thu Feb 27 21:35:58 2014 +0800 @@ -13,20 +13,32 @@ PROGRAMS=dropbear dbclient dropbearkey dropbearconvert endif -LTC=libtomcrypt/libtomcrypt.a -LTM=libtommath/libtommath.a +STATIC_LTC=libtomcrypt/libtomcrypt.a +STATIC_LTM=libtommath/libtommath.a + +LIBTOM_LIBS=@LIBTOM_LIBS@ ifeq (@BUNDLED_LIBTOM@, 1) -LIBTOM_DEPS=$(LTC) $(LTM) -CFLAGS+=-I$(srcdir)/libtomcrypt/src/headers/ -LIBS+=$(LTC) $(LTM) +LIBTOM_DEPS=$(STATIC_LTC) $(STATIC_LTM) +LIBTOM_LIBS=$(STATIC_LTC) $(STATIC_LTM) +CFLAGS+=-I$(srcdir)/libtomcrypt/src/headers/ +endif + +ifeq (@ASM_ARCH@, arm) +ASM_OBJS=aes-arm.o sha1-arm.o aes-asm-ltc.o sha1-asm-ltc.o +LIBTOM_DEPS += $(ASM_OBJS) +LIBTOM_LIBS += $(ASM_OBJS) +CFLAGS+=-DDROPBEAR_SHA1_ASM +CFLAGS+=-DDROPBEAR_AES_ASM endif COMMONOBJS=dbutil.o buffer.o \ dss.o bignum.o \ - signkey.o rsa.o random.o \ + signkey.o rsa.o dbrandom.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 \ + 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 \ @@ -41,22 +53,22 @@ 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 aes-arm.o + common-runopts.o circbuffer.o curve25519-donna.o -KEYOBJS=dropbearkey.o gendss.o genrsa.o +KEYOBJS=dropbearkey.o CONVERTOBJS=dropbearconvert.o keyimport.o SCPOBJS=scp.o progressmeter.o atomicio.o scpmisc.o compat.o HEADERS=options.h dbutil.h session.h packet.h algo.h ssh.h buffer.h kex.h \ - dss.h bignum.h signkey.h rsa.h random.h service.h auth.h \ + dss.h bignum.h signkey.h rsa.h dbrandom.h service.h auth.h \ 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@ +dropbearobjs=$(COMMONOBJS) $(CLISVROBJS) $(SVROBJS) dbclientobjs=$(COMMONOBJS) $(CLISVROBJS) $(CLIOBJS) dropbearkeyobjs=$(COMMONOBJS) $(KEYOBJS) dropbearconvertobjs=$(COMMONOBJS) $(CONVERTOBJS) @@ -66,6 +78,7 @@ srcdir=@srcdir@ prefix=@prefix@ +exec_prefix=@exec_prefix@ datarootdir = @datarootdir@ bindir=@bindir@ sbindir=@sbindir@ @@ -121,34 +134,34 @@ install: $(addprefix inst_, $(TARGETS)) -installdropbearmulti: insdbmulti $(addprefix insmulti, $(PROGRAMS)) - -insdbmulti: dropbearmulti - $(INSTALL) -d $(DESTDIR)$(bindir) - $(INSTALL) dropbearmulti$(EXEEXT) $(DESTDIR)$(bindir) - insmultidropbear: dropbearmulti $(INSTALL) -d $(DESTDIR)$(sbindir) -rm -f $(DESTDIR)$(sbindir)/dropbear$(EXEEXT) -ln -s $(bindir)/dropbearmulti$(EXEEXT) $(DESTDIR)$(sbindir)/dropbear$(EXEEXT) + $(INSTALL) -d $(DESTDIR)$(mandir)/man8 + $(INSTALL) -m 644 dropbear.8 $(DESTDIR)$(mandir)/man8/dropbear.8 insmulti%: dropbearmulti $(INSTALL) -d $(DESTDIR)$(bindir) -rm -f $(DESTDIR)$(bindir)/$*$(EXEEXT) -ln -s $(bindir)/dropbearmulti$(EXEEXT) $(DESTDIR)$(bindir)/$*$(EXEEXT) + $(INSTALL) -d $(DESTDIR)$(mandir)/man1 + if test -e $*.1; then $(INSTALL) -m 644 $*.1 $(DESTDIR)$(mandir)/man1/$*.1; fi # dropbear should go in sbin, so it needs a seperate rule inst_dropbear: dropbear $(INSTALL) -d $(DESTDIR)$(sbindir) $(INSTALL) dropbear$(EXEEXT) $(DESTDIR)$(sbindir) $(INSTALL) -d $(DESTDIR)$(mandir)/man8 - $(INSTALL) -m 644 dropbear.8 $(DESTDIR)$(mandir)/man8/dropbear.8 + $(INSTALL) -m 644 dropbear.8 $(DESTDIR)$(mandir)/man8/dropbear.8 inst_%: $* $(INSTALL) -d $(DESTDIR)$(bindir) $(INSTALL) $*$(EXEEXT) $(DESTDIR)$(bindir) $(INSTALL) -d $(DESTDIR)$(mandir)/man1 - $(INSTALL) -m 644 $*.1 $(DESTDIR)$(mandir)/man1/$*.1 + if test -e $*.1; then $(INSTALL) -m 644 $*.1 $(DESTDIR)$(mandir)/man1/$*.1; fi + +inst_dropbearmulti: $(addprefix insmulti, $(PROGRAMS)) # for some reason the rule further down doesn't like $($@objs) as a prereq. @@ -157,8 +170,14 @@ dropbearkey: $(dropbearkeyobjs) dropbearconvert: $(dropbearconvertobjs) -dropbear dbclient dropbearkey dropbearconvert: $(HEADERS) $(LIBTOM_DEPS) Makefile - $(CC) $(LDFLAGS) -o $@$(EXEEXT) $($@objs) $(LIBS) +dropbear: $(HEADERS) $(LIBTOM_DEPS) Makefile + $(CC) $(LDFLAGS) -o $@$(EXEEXT) $($@objs) $(LIBTOM_LIBS) $(LIBS) @CRYPTLIB@ + +dbclient: $(HEADERS) $(LIBTOM_DEPS) Makefile + $(CC) $(LDFLAGS) -o $@$(EXEEXT) $($@objs) $(LIBTOM_LIBS) $(LIBS) + +dropbearkey dropbearconvert: $(HEADERS) $(LIBTOM_DEPS) Makefile + $(CC) $(LDFLAGS) -o $@$(EXEEXT) $($@objs) $(LIBTOM_LIBS) # scp doesn't use the libs so is special. scp: $(SCPOBJS) $(HEADERS) Makefile @@ -168,14 +187,14 @@ # multi-binary compilation. MULTIOBJS= ifeq ($(MULTI),1) - MULTIOBJS=dbmulti.o $(sort $(foreach prog, $(PROGRAMS), $($(prog)objs))) @CRYPTLIB@ + MULTIOBJS=dbmulti.o $(sort $(foreach prog, $(PROGRAMS), $($(prog)objs))) CFLAGS+=$(addprefix -DDBMULTI_, $(PROGRAMS)) -DDROPBEAR_MULTI endif -dropbearmulti: multilink +dropbearmulti$(EXEEXT): $(HEADERS) $(MULTIOBJS) $(LIBTOM_DEPS) Makefile + $(CC) $(LDFLAGS) -o $@ $(MULTIOBJS) $(LIBTOM_LIBS) $(LIBS) @CRYPTLIB@ -multibinary: $(HEADERS) $(MULTIOBJS) $(LIBTOM_DEPS) Makefile - $(CC) $(LDFLAGS) -o dropbearmulti$(EXEEXT) $(MULTIOBJS) $(LIBS) +multibinary: dropbearmulti$(EXEEXT) multilink: multibinary $(addprefix link, $(PROGRAMS)) @@ -183,10 +202,10 @@ -rm -f $*$(EXEEXT) -ln -s dropbearmulti$(EXEEXT) $*$(EXEEXT) -$(LTC): options.h - cd libtomcrypt && $(MAKE) clean && $(MAKE) +$(STATIC_LTC): options.h + cd libtomcrypt && $(MAKE) -$(LTM): options.h +$(STATIC_LTM): options.h cd libtommath && $(MAKE) .PHONY : clean sizes thisclean distclean tidy ltc-clean ltm-clean diff -r e4b75744acab -r 89555751c489 README --- a/README Sun Oct 06 22:32:03 2013 +0800 +++ b/README Thu Feb 27 21:35:58 2014 +0800 @@ -42,8 +42,7 @@ dropbearconvert openssh dropbear ~/.ssh/id_rsa ~/.ssh/id_rsa.db dbclient -i ~/.ssh/id_rsa.db -Currently encrypted keys aren't supported, neither is agent forwarding. At some -stage both hopefully will be. +Dropbear does not support encrypted hostkeys though can connect to ssh-agent. ============================================================================ @@ -52,13 +51,18 @@ ============================================================================ -To run the server, you need to generate server keys, this is one-off: +To run the server, you need to server keys, this is one-off: ./dropbearkey -t rsa -f dropbear_rsa_host_key ./dropbearkey -t dss -f dropbear_dss_host_key +./dropbearkey -t ecdsa -f dropbear_ecdsa_host_key or alternatively convert OpenSSH keys to Dropbear: ./dropbearconvert openssh dropbear /etc/ssh/ssh_host_dsa_key dropbear_dss_host_key +You can also get Dropbear to create keys when the first connection is made - +this is preferable to generating keys when the system boots. Make sure +/etc/dropbear/ exists and then pass '-R' to the dropbear server. + ============================================================================ If the server is run as non-root, you most likely won't be able to allocate a diff -r e4b75744acab -r 89555751c489 agentfwd.h --- a/agentfwd.h Sun Oct 06 22:32:03 2013 +0800 +++ b/agentfwd.h Thu Feb 27 21:35:58 2014 +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 e4b75744acab -r 89555751c489 algo.h --- a/algo.h Sun Oct 06 22:32:03 2013 +0800 +++ b/algo.h Thu Feb 27 21:35:58 2014 +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,37 @@ }; 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; +}; + +enum dropbear_kex_mode { + DROPBEAR_KEX_NORMAL_DH, + DROPBEAR_KEX_ECDH, + DROPBEAR_KEX_CURVE25519, }; -void crypto_init(); +struct dropbear_kex { + enum dropbear_kex_mode mode; + + /* "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; +#else + const void* dummy; +#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 +127,10 @@ char * algolist_string(algo_type algos[]); #endif +enum { + DROPBEAR_COMP_NONE, + DROPBEAR_COMP_ZLIB, + DROPBEAR_COMP_ZLIB_DELAY, +}; #endif /* _ALGO_H_ */ diff -r e4b75744acab -r 89555751c489 bignum.c --- a/bignum.c Sun Oct 06 22:32:03 2013 +0800 +++ b/bignum.c Thu Feb 27 21:35:58 2014 +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,16 +76,13 @@ } /* hash the ssh representation of the mp_int mp */ -void sha1_process_mp(hash_state *hs, mp_int *mp) { - - int i; +void hash_process_mp(const struct ltc_hash_descriptor *hash_desc, + hash_state *hs, mp_int *mp) { buffer * buf; 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 e4b75744acab -r 89555751c489 bignum.h --- a/bignum.h Sun Oct 06 22:32:03 2013 +0800 +++ b/bignum.h Thu Feb 27 21:35:58 2014 +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 e4b75744acab -r 89555751c489 buffer.c --- a/buffer.c Sun Oct 06 22:32:03 2013 +0800 +++ b/buffer.c Thu Feb 27 21:35:58 2014 +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 e4b75744acab -r 89555751c489 buffer.h --- a/buffer.h Sun Oct 06 22:32:03 2013 +0800 +++ b/buffer.h Thu Feb 27 21:35:58 2014 +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 e4b75744acab -r 89555751c489 cli-agentfwd.c --- a/cli-agentfwd.c Sun Oct 06 22:32:03 2013 +0800 +++ b/cli-agentfwd.c Thu Feb 27 21:35:58 2014 +0800 @@ -34,7 +34,7 @@ #include "channel.h" #include "packet.h" #include "buffer.h" -#include "random.h" +#include "dbrandom.h" #include "listener.h" #include "runopts.h" #include "atomicio.h" @@ -73,8 +73,8 @@ return fd; } -// handle a request for a connection to the locally running ssh-agent -// or forward. +/* handle a request for a connection to the locally running ssh-agent + or forward. */ static int new_agent_chan(struct Channel * channel) { int fd = -1; @@ -94,7 +94,6 @@ channel->readfd = fd; channel->writefd = fd; - // success return 0; } @@ -202,7 +201,7 @@ num = buf_getint(inbuf); for (i = 0; i < num; i++) { sign_key * pubkey = NULL; - int key_type = DROPBEAR_SIGNKEY_ANY; + enum signkey_type key_type = DROPBEAR_SIGNKEY_ANY; buffer * key_buf; /* each public key is encoded as a string */ @@ -254,7 +253,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 +265,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 e4b75744acab -r 89555751c489 cli-auth.c --- a/cli-auth.c Sun Oct 06 22:32:03 2013 +0800 +++ b/cli-auth.c Thu Feb 27 21:35:58 2014 +0800 @@ -41,16 +41,6 @@ /* Send a "none" auth request to get available methods */ void cli_auth_getmethods() { TRACE(("enter cli_auth_getmethods")) -#ifdef CLI_IMMEDIATE_AUTH - ses.authstate.authtypes = AUTH_TYPE_PUBKEY; - if (getenv(DROPBEAR_PASSWORD_ENV)) { - ses.authstate.authtypes |= AUTH_TYPE_PASSWORD | AUTH_TYPE_INTERACT; - } - if (cli_auth_try() == DROPBEAR_SUCCESS) { - TRACE(("skipped initial none auth query")) - return; - } -#endif CHECKCLEARTOWRITE(); buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_REQUEST); buf_putstring(ses.writepayload, cli_opts.username, @@ -60,6 +50,26 @@ buf_putstring(ses.writepayload, "none", 4); /* 'none' method */ encrypt_packet(); + +#ifdef DROPBEAR_CLI_IMMEDIATE_AUTH + /* We can't haven't two auth requests in-flight with delayed zlib mode + since if the first one succeeds then the remote side will + expect the second one to be compressed. + Race described at + http://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/zlib-openssh.html + */ + if (ses.keys->trans.algo_comp != DROPBEAR_COMP_ZLIB_DELAY) { + ses.authstate.authtypes = AUTH_TYPE_PUBKEY; + if (getenv(DROPBEAR_PASSWORD_ENV)) { + ses.authstate.authtypes |= AUTH_TYPE_PASSWORD | AUTH_TYPE_INTERACT; + } + if (cli_auth_try() == DROPBEAR_SUCCESS) { + TRACE(("skipped initial none auth query")) + /* Note that there will be two auth responses in-flight */ + cli_ses.ignore_next_auth_response = 1; + } + } +#endif TRACE(("leave cli_auth_getmethods")) } @@ -150,31 +160,46 @@ TRACE(("<- MSG_USERAUTH_FAILURE")) TRACE(("enter recv_msg_userauth_failure")) + if (ses.authstate.authdone) { + TRACE(("leave recv_msg_userauth_failure, already authdone.")) + return; + } + if (cli_ses.state != USERAUTH_REQ_SENT) { /* Perhaps we should be more fatal? */ dropbear_exit("Unexpected userauth failure"); } + /* When DROPBEAR_CLI_IMMEDIATE_AUTH is set there will be an initial response for + the "none" auth request, and then a response to the immediate auth request. + We need to be careful handling them. */ + if (cli_ses.ignore_next_auth_response) { + TRACE(("ignore next response, state set to USERAUTH_REQ_SENT")) + cli_ses.state = USERAUTH_REQ_SENT; + } else { + cli_ses.state = USERAUTH_FAIL_RCVD; + cli_ses.lastauthtype = AUTH_TYPE_NONE; #ifdef ENABLE_CLI_PUBKEY_AUTH - /* If it was a pubkey auth request, we should cross that key - * off the list. */ - if (cli_ses.lastauthtype == AUTH_TYPE_PUBKEY) { - cli_pubkeyfail(); - } + /* If it was a pubkey auth request, we should cross that key + * off the list. */ + if (cli_ses.lastauthtype == AUTH_TYPE_PUBKEY) { + cli_pubkeyfail(); + } #endif #ifdef ENABLE_CLI_INTERACT_AUTH - /* If we get a failure message for keyboard interactive without - * receiving any request info packet, then we don't bother trying - * keyboard interactive again */ - if (cli_ses.lastauthtype == AUTH_TYPE_INTERACT - && !cli_ses.interact_request_received) { - TRACE(("setting auth_interact_failed = 1")) - cli_ses.auth_interact_failed = 1; + /* If we get a failure message for keyboard interactive without + * receiving any request info packet, then we don't bother trying + * keyboard interactive again */ + if (cli_ses.lastauthtype == AUTH_TYPE_INTERACT + && !cli_ses.interact_request_received) { + TRACE(("setting auth_interact_failed = 1")) + cli_ses.auth_interact_failed = 1; + } +#endif } -#endif - cli_ses.lastauthtype = AUTH_TYPE_NONE; + cli_ses.ignore_next_auth_response = 0; methods = buf_getstring(ses.payload, &methlen); @@ -227,13 +252,14 @@ } m_free(methods); - - cli_ses.state = USERAUTH_FAIL_RCVD; TRACE(("leave recv_msg_userauth_failure")) } void recv_msg_userauth_success() { + /* This function can validly get called multiple times + if DROPBEAR_CLI_IMMEDIATE_AUTH is set */ + TRACE(("received msg_userauth_success")) /* Note: in delayed-zlib mode, setting authdone here * will enable compression in the transport layer */ diff -r e4b75744acab -r 89555751c489 cli-authpubkey.c --- a/cli-authpubkey.c Sun Oct 06 22:32:03 2013 +0800 +++ b/cli-authpubkey.c Thu Feb 27 21:35:58 2014 +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 e4b75744acab -r 89555751c489 cli-chansession.c --- a/cli-chansession.c Sun Oct 06 22:32:03 2013 +0800 +++ b/cli-chansession.c Thu Feb 27 21:35:58 2014 +0800 @@ -369,6 +369,8 @@ if (cli_opts.wantpty) { send_chansess_pty_req(channel); + } else { + set_sock_priority(ses.sock_out, DROPBEAR_PRIO_BULK); } send_chansess_shell_req(channel); @@ -398,6 +400,7 @@ const unsigned char* source_host = "127.0.0.1"; const int source_port = 22; + TRACE(("enter cli_send_netcat_request")) cli_opts.wantpty = 0; if (send_msg_channel_open_init(STDIN_FILENO, &cli_chan_netcat) @@ -414,7 +417,7 @@ buf_putint(ses.writepayload, source_port); encrypt_packet(); - TRACE(("leave cli_send_chansess_request")) + TRACE(("leave cli_send_netcat_request")) } #endif @@ -433,7 +436,7 @@ } -// returns 1 if the character should be consumed, 0 to pass through +/* returns 1 if the character should be consumed, 0 to pass through */ static int do_escape(unsigned char c) { switch (c) { @@ -442,10 +445,10 @@ return 1; break; case 0x1a: - // ctrl-z + /* ctrl-z */ cli_tty_cleanup(); kill(getpid(), SIGTSTP); - // after continuation + /* after continuation */ cli_tty_setup(); cli_ses.winchange = 1; return 1; @@ -455,12 +458,12 @@ } 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; - // only handle escape characters if they are read one at a time. simplifies - // the code and avoids nasty people putting ~. at the start of a line to paste + /* only handle escape characters if they are read one at a time. simplifies + the code and avoids nasty people putting ~. at the start of a line to paste */ if (*len != 1) { cli_ses.last_char = 0x0; return; diff -r e4b75744acab -r 89555751c489 cli-kex.c --- a/cli-kex.c Sun Oct 06 22:32:03 2013 +0800 +++ b/cli-kex.c Thu Feb 27 21:35:58 2014 +0800 @@ -33,9 +33,10 @@ #include "ssh.h" #include "packet.h" #include "bignum.h" -#include "random.h" +#include "dbrandom.h" #include "runopts.h" #include "signkey.h" +#include "ecc.h" static void checkhostkey(unsigned char* keyblob, unsigned int keybloblen); @@ -43,43 +44,62 @@ 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); + 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(); + } + 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); + } + cli_ses.ecdh_param = gen_kexecdh_param(); + } + 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; - ses.requirenext[1] = SSH_MSG_KEXINIT; } /* 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 +117,59 @@ dropbear_exit("Bad KEX packet"); } - 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); + } + 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); + } +#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; } - 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 +#ifdef DROPBEAR_CURVE25519 + if (cli_ses.curve25519_param) { + free_kexcurve25519_param(cli_ses.curve25519_param); + cli_ses.curve25519_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"); } @@ -118,12 +177,12 @@ hostkey = NULL; send_msg_newkeys(); - ses.requirenext[0] = SSH_MSG_NEWKEYS; - ses.requirenext[1] = 0; + ses.requirenext = SSH_MSG_NEWKEYS; 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; @@ -131,14 +190,16 @@ 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); @@ -233,16 +294,17 @@ 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) { @@ -295,17 +357,18 @@ /* 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 -r e4b75744acab -r 89555751c489 cli-main.c --- a/cli-main.c Sun Oct 06 22:32:03 2013 +0800 +++ b/cli-main.c Thu Feb 27 21:35:58 2014 +0800 @@ -28,6 +28,8 @@ #include "dbutil.h" #include "runopts.h" #include "session.h" +#include "dbrandom.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, @@ -70,6 +75,9 @@ int sock = connect_remote(cli_opts.remotehost, cli_opts.remoteport, 0, &error); sock_in = sock_out = sock; + if (cli_opts.wantpty) { + set_sock_priority(sock, DROPBEAR_PRIO_LOWDELAY); + } } if (sock_in < 0) { @@ -138,4 +146,4 @@ *sock_in = *sock_out = -1; } } -#endif // ENABLE_CLI_PROXYCMD +#endif /* ENABLE_CLI_PROXYCMD */ diff -r e4b75744acab -r 89555751c489 cli-runopts.c --- a/cli-runopts.c Sun Oct 06 22:32:03 2013 +0800 +++ b/cli-runopts.c Thu Feb 27 21:35:58 2014 +0800 @@ -216,7 +216,7 @@ switch (argv[i][1]) { case 'y': /* always accept the remote hostkey */ if (cli_opts.always_accept_key) { - // twice means no checking at all + /* twice means no checking at all */ cli_opts.no_hostkey_check = 1; } cli_opts.always_accept_key = 1; @@ -450,7 +450,7 @@ #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; @@ -481,7 +481,7 @@ sign_key * key = (sign_key*)iter->item; len += 3 + strlen(key->filename); } - len += 30; // space for -W , terminator. + len += 30; /* space for -W , terminator. */ ret = m_malloc(len); total = 0; @@ -617,9 +617,9 @@ cli_opts.username = m_strdup(cli_opts.own_user); } - port = strchr(cli_opts.remotehost, '%'); + port = strchr(cli_opts.remotehost, '^'); if (!port) { - // legacy separator + /* legacy separator */ port = strchr(cli_opts.remotehost, '/'); } if (port) { diff -r e4b75744acab -r 89555751c489 cli-session.c --- a/cli-session.c Sun Oct 06 22:32:03 2013 +0800 +++ b/cli-session.c Thu Feb 27 21:35:58 2014 +0800 @@ -31,11 +31,12 @@ #include "packet.h" #include "tcpfwd.h" #include "channel.h" -#include "random.h" +#include "dbrandom.h" #include "service.h" #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); @@ -178,7 +175,7 @@ } static void recv_msg_service_accept(void) { - // do nothing, if it failed then the server MUST have disconnected + /* do nothing, if it failed then the server MUST have disconnected */ } /* This function drives the progress of the session - it initiates KEX, @@ -231,6 +228,10 @@ cli_ses.state = USERAUTH_REQ_SENT; TRACE(("leave cli_sessionloop: sent userauth methods req")) return; + + case USERAUTH_REQ_SENT: + TRACE(("leave cli_sessionloop: waiting, req_sent")) + return; case USERAUTH_FAIL_RCVD: if (cli_auth_try() == DROPBEAR_FAILURE) { diff -r e4b75744acab -r 89555751c489 cli-tcpfwd.c --- a/cli-tcpfwd.c Sun Oct 06 22:32:03 2013 +0800 +++ b/cli-tcpfwd.c Thu Feb 27 21:35:58 2014 +0800 @@ -161,9 +161,10 @@ if (!fwd->have_reply) { fwd->have_reply = 1; if (fwd->listenport == 0) { - /* The server should let us know which port was allocated if we requestd port 0 */ + /* The server should let us know which port was allocated if we requested port 0 */ int allocport = buf_getint(ses.payload); if (allocport > 0) { + fwd->listenport = allocport; dropbear_log(LOG_INFO, "Allocated port %d for remote forward to %s:%d", allocport, fwd->connectaddr, fwd->connectport); } @@ -193,8 +194,8 @@ struct TCPFwdEntry *fwd = (struct TCPFwdEntry*)iter->item; if (!fwd->listenaddr) { - // we store the addresses so that we can compare them - // when the server sends them back + /* we store the addresses so that we can compare them + when the server sends them back */ if (opts.listen_fwd_all) { fwd->listenaddr = m_strdup(""); } else { @@ -220,18 +221,33 @@ origaddr = buf_getstring(ses.payload, NULL); origport = buf_getint(ses.payload); - /* Find which port corresponds */ + /* Find which port corresponds. First try and match address as well as port, + in case they want to forward different ports separately ... */ for (iter = cli_opts.remotefwds->first; iter; iter = iter->next) { fwd = (struct TCPFwdEntry*)iter->item; if (origport == fwd->listenport - && (strcmp(origaddr, fwd->listenaddr) == 0)) { + && strcmp(origaddr, fwd->listenaddr) == 0) { break; } } + if (!iter) + { + /* ... otherwise try to generically match the only forwarded port + without address (also handles ::1 vs 127.0.0.1 vs localhost case). + rfc4254 is vague about the definition of "address that was connected" */ + for (iter = cli_opts.remotefwds->first; iter; iter = iter->next) { + fwd = (struct TCPFwdEntry*)iter->item; + if (origport == fwd->listenport) { + break; + } + } + } + + if (iter == NULL) { /* We didn't request forwarding on that port */ - cleantext(origaddr); + cleantext(origaddr); dropbear_log(LOG_INFO, "Server sent unrequested forward from \"%s:%d\"", origaddr, origport); goto out; diff -r e4b75744acab -r 89555751c489 common-algo.c --- a/common-algo.c Sun Oct 06 22:32:03 2013 +0800 +++ b/common-algo.c Thu Feb 27 21:35:58 2014 +0800 @@ -23,24 +23,29 @@ * 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" +#include "crypto_desc.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; } @@ -49,21 +54,6 @@ /* Remember to add new ciphers/hashes to regciphers/reghashes too */ -#ifdef DROPBEAR_AES_ASM -extern const struct ltc_cipher_descriptor aes_asm_desc; -#define DROPBEAR_AES_DESC (aes_asm_desc) -#else -#define DROPBEAR_AES_DESC (aes_desc) -#endif - -#ifdef DROPBEAR_SHA1_ASM -extern const struct ltc_hash_descriptor sha1_asm_desc; -#define DROPBEAR_SHA1_DESC (sha1_asm_desc) -#else -#define DROPBEAR_SHA1_DESC (sha1_desc) -#endif - - #ifdef DROPBEAR_AES256 static const struct dropbear_cipher dropbear_aes256 = {&DROPBEAR_AES_DESC, 32, 16}; @@ -219,6 +209,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 @@ -228,65 +229,51 @@ {NULL, 0, NULL, 0, NULL} }; +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 = {DROPBEAR_KEX_ECDH, NULL, 0, &ecc_curve_nistp256, &sha256_desc }; +#endif +#ifdef DROPBEAR_ECC_384 +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 = {DROPBEAR_KEX_ECDH, NULL, 0, &ecc_curve_nistp521, &sha512_desc }; +#endif +#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[] = { - {"diffie-hellman-group1-sha1", DROPBEAR_KEX_DH_GROUP1, NULL, 1, NULL}, - {"diffie-hellman-group14-sha1", DROPBEAR_KEX_DH_GROUP14, NULL, 1, NULL}, +#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}, +#endif +#ifdef DROPBEAR_ECC_384 + {"ecdh-sha2-nistp384", 0, &kex_ecdh_nistp384, 1, NULL}, +#endif +#ifdef DROPBEAR_ECC_256 + {"ecdh-sha2-nistp256", 0, &kex_ecdh_nistp256, 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 - &DROPBEAR_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 */ - &DROPBEAR_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 @@ -312,7 +299,7 @@ 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) @@ -409,7 +396,7 @@ for (i = 0; i < clicount; i++) { for (j = 0; j < servcount; j++) { if (!(servnames[j] && clinames[i])) { - // unusable algos are NULL + /* unusable algos are NULL */ continue; } if (strcmp(servnames[j], clinames[i]) == 0) { @@ -472,7 +459,7 @@ return 0; } -#endif // DROPBEAR_NONE_CIPHER +#endif /* DROPBEAR_NONE_CIPHER */ #ifdef ENABLE_USER_ALGO_LIST @@ -553,4 +540,4 @@ memcpy(algos, new_algos, sizeof(*new_algos) * (num_ret+1)); return num_ret; } -#endif // ENABLE_USER_ALGO_LIST +#endif /* ENABLE_USER_ALGO_LIST */ diff -r e4b75744acab -r 89555751c489 common-channel.c --- a/common-channel.c Sun Oct 06 22:32:03 2013 +0800 +++ b/common-channel.c Thu Feb 27 21:35:58 2014 +0800 @@ -59,6 +59,13 @@ #define ERRFD_IS_READ(channel) ((channel)->extrabuf == NULL) #define ERRFD_IS_WRITE(channel) (!ERRFD_IS_READ(channel)) +/* allow space for: + * 1 byte byte SSH_MSG_CHANNEL_DATA + * 4 bytes uint32 recipient channel + * 4 bytes string data + */ +#define RECV_MAX_CHANNEL_DATA_LEN (RECV_MAX_PAYLOAD_LEN-(1+4+4)) + /* Initialise all the channels */ void chaninitialise(const struct ChanType *chantypes[]) { @@ -165,7 +172,7 @@ newchan->extrabuf = NULL; /* The user code can set it up */ newchan->recvdonelen = 0; - newchan->recvmaxpacket = RECV_MAX_PAYLOAD_LEN; + newchan->recvmaxpacket = RECV_MAX_CHANNEL_DATA_LEN; ses.channels[i] = newchan; ses.chancount++; @@ -474,8 +481,13 @@ continue; } - /* Stuff to put over the wire */ - if (channel->transwindow > 0) { + /* Stuff to put over the wire. + Avoid queueing data to send if we're in the middle of a + key re-exchange (!dataallowed), but still read from the + FD if there's the possibility of "~."" to kill an + interactive session (the read_mangler) */ + if (channel->transwindow > 0 + && (ses.dataallowed || channel->read_mangler)) { if (channel->readfd >= 0) { FD_SET(channel->readfd, readfds); @@ -1023,7 +1035,7 @@ buf_putstring(ses.writepayload, type->name, strlen(type->name)); buf_putint(ses.writepayload, chan->index); buf_putint(ses.writepayload, opts.recv_window); - buf_putint(ses.writepayload, RECV_MAX_PAYLOAD_LEN); + buf_putint(ses.writepayload, RECV_MAX_CHANNEL_DATA_LEN); TRACE(("leave send_msg_channel_open_init()")) return DROPBEAR_SUCCESS; diff -r e4b75744acab -r 89555751c489 common-kex.c --- a/common-kex.c Sun Oct 06 22:32:03 2013 +0800 +++ b/common-kex.c Thu Feb 27 21:35:58 2014 +0800 @@ -32,12 +32,13 @@ #include "ssh.h" #include "packet.h" #include "bignum.h" -#include "random.h" +#include "dbrandom.h" #include "runopts.h" +#include "ecc.h" +#include "crypto_desc.h" /* diffie-hellman-group1-sha1 value for p */ -#define DH_P_1_LEN 128 -static const unsigned char dh_p_1[DH_P_1_LEN] = { +const unsigned char dh_p_1[DH_P_1_LEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, @@ -51,8 +52,7 @@ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; /* diffie-hellman-group14-sha1 value for p */ -#define DH_P_14_LEN 256 -static const unsigned char dh_p_14[DH_P_14_LEN] = { +const unsigned char dh_p_14[DH_P_14_LEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, @@ -87,8 +87,9 @@ #endif static void read_kex_algos(); /* helper function for gen_new_keys */ -static void hashkeys(unsigned char *out, int outlen, - const hash_state * hs, unsigned const char X); +static void hashkeys(unsigned char *out, unsigned int outlen, + const hash_state * hs, const unsigned char X); +static void finish_kexhashbuf(void); /* Send our list of algorithms we can use */ @@ -150,7 +151,7 @@ ses.newkeys = (struct key_context*)m_malloc(sizeof(struct key_context)); if (ses.send_kex_first_guess) { - ses.newkeys->algo_kex = sshkex[0].val; + ses.newkeys->algo_kex = sshkex[0].data; ses.newkeys->algo_hostkey = sshhostkey[0].val; ses.send_kex_first_guess(); } @@ -279,27 +280,30 @@ * out must have at least min(SHA1_HASH_SIZE, outlen) bytes allocated. * * See Section 7.2 of rfc4253 (ssh transport) for details */ -static void hashkeys(unsigned char *out, int outlen, +static void hashkeys(unsigned char *out, unsigned int outlen, const hash_state * hs, const unsigned char X) { + const struct ltc_hash_descriptor *hash_desc = ses.newkeys->algo_kex->hash_desc; hash_state hs2; - int offset; + unsigned int offset; + unsigned char tmpout[MAX_HASH_SIZE]; memcpy(&hs2, hs, sizeof(hash_state)); - sha1_process(&hs2, &X, 1); - sha1_process(&hs2, ses.session_id, SHA1_HASH_SIZE); - sha1_done(&hs2, out); - for (offset = SHA1_HASH_SIZE; + hash_desc->process(&hs2, &X, 1); + hash_desc->process(&hs2, ses.session_id->data, ses.session_id->len); + hash_desc->done(&hs2, tmpout); + memcpy(out, tmpout, MIN(hash_desc->hashsize, outlen)); + for (offset = hash_desc->hashsize; offset < outlen; - offset += SHA1_HASH_SIZE) + offset += hash_desc->hashsize) { /* need to extend */ - unsigned char k2[SHA1_HASH_SIZE]; memcpy(&hs2, hs, sizeof(hash_state)); - sha1_process(&hs2, out, offset); - sha1_done(&hs2, k2); - memcpy(&out[offset], k2, MIN(outlen - offset, SHA1_HASH_SIZE)); + hash_desc->process(&hs2, out, offset); + hash_desc->done(&hs2, tmpout); + memcpy(&out[offset], tmpout, MIN(outlen - offset, hash_desc->hashsize)); } + } /* Generate the actual encryption/integrity keys, using the results of the @@ -319,26 +323,26 @@ unsigned char *trans_IV, *trans_key, *recv_IV, *recv_key; hash_state hs; - unsigned int C2S_keysize, S2C_keysize; + const struct ltc_hash_descriptor *hash_desc = ses.newkeys->algo_kex->hash_desc; char mactransletter, macrecvletter; /* Client or server specific */ TRACE(("enter gen_new_keys")) /* the dh_K and hash are the start of all hashes, we make use of that */ - sha1_init(&hs); - sha1_process_mp(&hs, ses.dh_K); + hash_desc->init(&hs); + hash_process_mp(hash_desc, &hs, ses.dh_K); mp_clear(ses.dh_K); m_free(ses.dh_K); - sha1_process(&hs, ses.hash, SHA1_HASH_SIZE); - m_burn(ses.hash, SHA1_HASH_SIZE); + hash_desc->process(&hs, ses.hash->data, ses.hash->len); + buf_burn(ses.hash); + buf_free(ses.hash); + ses.hash = NULL; if (IS_DROPBEAR_CLIENT) { trans_IV = C2S_IV; recv_IV = S2C_IV; trans_key = C2S_key; recv_key = S2C_key; - C2S_keysize = ses.newkeys->trans.algo_crypt->keysize; - S2C_keysize = ses.newkeys->recv.algo_crypt->keysize; mactransletter = 'E'; macrecvletter = 'F'; } else { @@ -346,16 +350,14 @@ recv_IV = C2S_IV; trans_key = S2C_key; recv_key = C2S_key; - C2S_keysize = ses.newkeys->recv.algo_crypt->keysize; - S2C_keysize = ses.newkeys->trans.algo_crypt->keysize; mactransletter = 'F'; macrecvletter = 'E'; } - hashkeys(C2S_IV, SHA1_HASH_SIZE, &hs, 'A'); - hashkeys(S2C_IV, SHA1_HASH_SIZE, &hs, 'B'); - hashkeys(C2S_key, C2S_keysize, &hs, 'C'); - hashkeys(S2C_key, S2C_keysize, &hs, 'D'); + hashkeys(C2S_IV, sizeof(C2S_IV), &hs, 'A'); + hashkeys(S2C_IV, sizeof(S2C_IV), &hs, 'B'); + hashkeys(C2S_key, sizeof(C2S_key), &hs, 'C'); + hashkeys(S2C_key, sizeof(S2C_key), &hs, 'D'); if (ses.newkeys->recv.algo_crypt->cipherdesc != NULL) { int recv_cipher = find_cipher(ses.newkeys->recv.algo_crypt->cipherdesc->name); @@ -381,16 +383,16 @@ } } - if (ses.newkeys->trans.algo_mac->hashdesc != NULL) { + if (ses.newkeys->trans.algo_mac->hash_desc != NULL) { hashkeys(ses.newkeys->trans.mackey, ses.newkeys->trans.algo_mac->keysize, &hs, mactransletter); - ses.newkeys->trans.hash_index = find_hash(ses.newkeys->trans.algo_mac->hashdesc->name); + ses.newkeys->trans.hash_index = find_hash(ses.newkeys->trans.algo_mac->hash_desc->name); } - if (ses.newkeys->recv.algo_mac->hashdesc != NULL) { + if (ses.newkeys->recv.algo_mac->hash_desc != NULL) { hashkeys(ses.newkeys->recv.mackey, ses.newkeys->recv.algo_mac->keysize, &hs, macrecvletter); - ses.newkeys->recv.hash_index = find_hash(ses.newkeys->recv.algo_mac->hashdesc->name); + ses.newkeys->recv.hash_index = find_hash(ses.newkeys->recv.algo_mac->hash_desc->name); } /* Ready to switch over */ @@ -481,9 +483,6 @@ * and we calculate the first portion of the key-exchange-hash for used * later in the key exchange. No response is sent, as the client should * initiate the diffie-hellman key exchange */ - -/* Originally from kex.c, generalized for cli/svr mode --mihnea */ -/* Belongs in common_kex.c where it should be moved after review */ void recv_msg_kexinit() { unsigned int kexhashbuf_len = 0; @@ -526,7 +525,7 @@ /* I_S, the payload of the server's SSH_MSG_KEXINIT */ buf_setpos(ses.payload, 0); buf_putstring(ses.kexhashbuf, ses.payload->data, ses.payload->len); - + ses.requirenext = SSH_MSG_KEXDH_REPLY; } else { /* SERVER */ @@ -546,7 +545,7 @@ buf_putstring(ses.kexhashbuf, ses.transkexinit->data, ses.transkexinit->len); - ses.requirenext[0] = SSH_MSG_KEXDH_INIT; + ses.requirenext = SSH_MSG_KEXDH_INIT; } buf_free(ses.transkexinit); @@ -560,28 +559,24 @@ static void load_dh_p(mp_int * dh_p) { - switch (ses.newkeys->algo_kex) { - case DROPBEAR_KEX_DH_GROUP1: - bytes_to_mp(dh_p, dh_p_1, DH_P_1_LEN); - break; - case DROPBEAR_KEX_DH_GROUP14: - bytes_to_mp(dh_p, dh_p_14, DH_P_14_LEN); - break; - } + bytes_to_mp(dh_p, ses.newkeys->algo_kex->dh_p_bytes, + ses.newkeys->algo_kex->dh_p_len); } /* Initialises and generate one side of the diffie-hellman key exchange values. * See the transport rfc 4253 section 8 for details */ /* dh_pub and dh_priv MUST be already initialised */ -void gen_kexdh_vals(mp_int *dh_pub, mp_int *dh_priv) { +struct kex_dh_param *gen_kexdh_param() { + struct kex_dh_param *param = NULL; DEF_MP_INT(dh_p); DEF_MP_INT(dh_q); DEF_MP_INT(dh_g); TRACE(("enter gen_kexdh_vals")) - - m_mp_init_multi(&dh_g, &dh_p, &dh_q, NULL); + + param = m_malloc(sizeof(*param)); + m_mp_init_multi(¶m->pub, ¶m->priv, &dh_g, &dh_p, &dh_q, NULL); /* read the prime and generator*/ load_dh_p(&dh_p); @@ -592,33 +587,39 @@ /* calculate q = (p-1)/2 */ /* dh_priv is just a temp var here */ - if (mp_sub_d(&dh_p, 1, dh_priv) != MP_OKAY) { + if (mp_sub_d(&dh_p, 1, ¶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 +632,8 @@ } /* K = e^y mod p = f^x mod p */ - ses.dh_K = (mp_int*)m_malloc(sizeof(mp_int)); - m_mp_init(ses.dh_K); - if (mp_exptmod(dh_pub_them, dh_priv, &dh_p, ses.dh_K) != MP_OKAY) { + m_mp_alloc_init_multi(&ses.dh_K, NULL); + if (mp_exptmod(dh_pub_them, ¶m->priv, &dh_p, ses.dh_K) != MP_OKAY) { dropbear_exit("Diffie-Hellman error"); } @@ -643,11 +643,11 @@ /* From here on, the code needs to work with the _same_ vars on each side, * not vice-versaing for client/server */ if (IS_DROPBEAR_CLIENT) { - dh_e = dh_pub_us; + dh_e = ¶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 +661,140 @@ buf_putmpint(ses.kexhashbuf, ses.dh_K); /* calculate the hash H to sign */ - sha1_init(&hs); + finish_kexhashbuf(); +} + +#ifdef DROPBEAR_ECDH +struct kex_ecdh_param *gen_kexecdh_param() { + struct kex_ecdh_param *param = m_malloc(sizeof(*param)); + if (ecc_make_key_ex(NULL, dropbear_ltc_prng, + ¶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); + + /* 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; + } else { + Q_C = Q_them; + Q_S = ¶m->key; + } + + /* K_S, the host key */ + buf_put_pub_key(ses.kexhashbuf, hostkey, ses.newkeys->algo_hostkey); + /* Q_C, client's ephemeral public key octet string */ + buf_put_ecc_raw_pubkey_string(ses.kexhashbuf, Q_C); + /* Q_S, server's ephemeral public key octet string */ + buf_put_ecc_raw_pubkey_string(ses.kexhashbuf, Q_S); + /* K, the shared secret */ + buf_putmpint(ses.kexhashbuf, ses.dh_K); + + /* calculate the hash H to sign */ + finish_kexhashbuf(); +} +#endif /* DROPBEAR_ECDH */ + +#ifdef DROPBEAR_CURVE25519 +struct kex_curve25519_param *gen_kexcurve25519_param () { + /* Per http://cr.yp.to/ecdh.html */ + struct kex_curve25519_param *param = m_malloc(sizeof(*param)); + const unsigned char basepoint[32] = {9}; + + genrandom(param->priv, CURVE25519_LEN); + param->priv[0] &= 248; + param->priv[31] &= 127; + param->priv[31] |= 64; + + curve25519_donna(param->pub, param->priv, basepoint); + + return param; +} + +void free_kexcurve25519_param(struct kex_curve25519_param *param) +{ + m_burn(param->priv, CURVE25519_LEN); + m_free(param); +} + +void kexcurve25519_comb_key(struct kex_curve25519_param *param, buffer *buf_pub_them, + sign_key *hostkey) { + unsigned char out[CURVE25519_LEN]; + const unsigned char* Q_C = NULL; + const unsigned char* Q_S = NULL; + + if (buf_pub_them->len != CURVE25519_LEN) + { + dropbear_exit("Bad curve25519"); + } + + curve25519_donna(out, param->priv, buf_pub_them->data); + m_mp_alloc_init_multi(&ses.dh_K, NULL); + bytes_to_mp(ses.dh_K, out, CURVE25519_LEN); + m_burn(out, sizeof(out)); + + /* Create the remainder of the hash buffer, to generate the exchange hash. + See RFC5656 section 4 page 7 */ + if (IS_DROPBEAR_CLIENT) { + Q_C = param->pub; + Q_S = buf_pub_them->data; + } else { + Q_S = param->pub; + Q_C = buf_pub_them->data; + } + + /* K_S, the host key */ + buf_put_pub_key(ses.kexhashbuf, hostkey, ses.newkeys->algo_hostkey); + /* Q_C, client's ephemeral public key octet string */ + buf_putstring(ses.kexhashbuf, Q_C, CURVE25519_LEN); + /* Q_S, server's ephemeral public key octet string */ + buf_putstring(ses.kexhashbuf, Q_S, CURVE25519_LEN); + /* K, the shared secret */ + buf_putmpint(ses.kexhashbuf, ses.dh_K); + + /* calculate the hash H to sign */ + finish_kexhashbuf(); +} +#endif /* DROPBEAR_CURVE25519 */ + + + +static void finish_kexhashbuf(void) { + hash_state hs; + const struct ltc_hash_descriptor *hash_desc = ses.newkeys->algo_kex->hash_desc; + + hash_desc->init(&hs); buf_setpos(ses.kexhashbuf, 0); - sha1_process(&hs, buf_getptr(ses.kexhashbuf, ses.kexhashbuf->len), + hash_desc->process(&hs, buf_getptr(ses.kexhashbuf, ses.kexhashbuf->len), ses.kexhashbuf->len); - sha1_done(&hs, ses.hash); + ses.hash = buf_new(hash_desc->hashsize); + hash_desc->done(&hs, buf_getwriteptr(ses.hash, hash_desc->hashsize)); + buf_setlen(ses.hash, hash_desc->hashsize); + +#if defined(DEBUG_KEXHASH) && defined(DEBUG_TRACE) + if (!debug_trace) { + printhex("kexhashbuf", ses.kexhashbuf->data, ses.kexhashbuf->len); + printhex("kexhash", ses.hash->data, ses.hash->len); + } +#endif buf_burn(ses.kexhashbuf); buf_free(ses.kexhashbuf); @@ -674,9 +803,9 @@ /* first time around, we set the session_id to H */ if (ses.session_id == NULL) { /* create the session_id, this never needs freeing */ - ses.session_id = (unsigned char*)m_malloc(SHA1_HASH_SIZE); - memcpy(ses.session_id, ses.hash, SHA1_HASH_SIZE); + ses.session_id = buf_newcopy(ses.hash); } + } /* read the other side's algo list. buf_match_algo is a callback to match @@ -700,16 +829,16 @@ int allgood = 1; /* we AND this with each goodguess and see if its still true after */ - buf_incrpos(ses.payload, 16); /* start after the cookie */ - - memset(ses.newkeys, 0x0, sizeof(*ses.newkeys)); - #ifdef USE_KEXGUESS2 enum kexguess2_used kexguess2 = KEXGUESS2_LOOK; #else enum kexguess2_used kexguess2 = KEXGUESS2_NO; #endif + buf_incrpos(ses.payload, 16); /* start after the cookie */ + + memset(ses.newkeys, 0x0, sizeof(*ses.newkeys)); + /* kex_algorithms */ algo = buf_match_algo(ses.payload, sshkex, &kexguess2, &goodguess); allgood &= goodguess; @@ -719,7 +848,7 @@ } TRACE(("kexguess2 %d", kexguess2)) TRACE(("kex algo %s", algo->name)) - ses.newkeys->algo_kex = algo->val; + ses.newkeys->algo_kex = algo->data; /* server_host_key_algorithms */ algo = buf_match_algo(ses.payload, sshhostkey, &kexguess2, &goodguess); diff -r e4b75744acab -r 89555751c489 common-runopts.c --- a/common-runopts.c Sun Oct 06 22:32:03 2013 +0800 +++ b/common-runopts.c Thu Feb 27 21:35:58 2014 +0800 @@ -29,13 +29,14 @@ #include "dbutil.h" #include "auth.h" #include "algo.h" -#include "random.h" +#include "dbrandom.h" runopts opts; /* GLOBAL */ /* returns success or failure, and the keytype in *type. If we want * to restrict the type, type can contain a type to return */ -int readhostkey(const char * filename, sign_key * hostkey, int *type) { +int readhostkey(const char * filename, sign_key * hostkey, + enum signkey_type *type) { int ret = DROPBEAR_FAILURE; buffer *buf; diff -r e4b75744acab -r 89555751c489 common-session.c --- a/common-session.c Sun Oct 06 22:32:03 2013 +0800 +++ b/common-session.c Thu Feb 27 21:35:58 2014 +0800 @@ -30,7 +30,7 @@ #include "buffer.h" #include "dss.h" #include "ssh.h" -#include "random.h" +#include "dbrandom.h" #include "kex.h" #include "channel.h" #include "runopts.h" @@ -82,7 +82,7 @@ initqueue(&ses.writequeue); - ses.requirenext[0] = SSH_MSG_KEXINIT; + ses.requirenext = SSH_MSG_KEXINIT; ses.dataallowed = 1; /* we can send data until we actually send the SSH_MSG_KEXINIT */ ses.ignorenext = 0; @@ -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; @@ -153,10 +153,9 @@ SIGCHLD in svr-chansession is the only one currently. */ FD_SET(ses.signal_pipe[0], &readfd); - /* set up for channels which require reading/writing */ - if (ses.dataallowed) { - setchannelfds(&readfd, &writefd); - } + /* set up for channels which can be read/written */ + setchannelfds(&readfd, &writefd); + val = select(ses.maxfd+1, &readfd, &writefd, NULL, &timeout); if (exitflag) { @@ -217,9 +216,7 @@ /* process pipes etc for the channels, ses.dataallowed == 0 * during rekeying ) */ - if (ses.dataallowed) { - channelio(&readfd, &writefd); - } + channelio(&readfd, &writefd); if (loophandler) { loophandler(); @@ -244,20 +241,31 @@ if (ses.extra_session_cleanup) { ses.extra_session_cleanup(); } + + chancleanup(); - m_free(ses.session_id); + /* Cleaning up keys must happen after other cleanup + functions which might queue packets */ + 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); - chancleanup(); - TRACE(("leave session_cleanup")) } void send_session_identification() { buffer *writebuf = buf_new(strlen(LOCAL_IDENT "\r\n") + 1); buf_putbytes(writebuf, LOCAL_IDENT "\r\n", strlen(LOCAL_IDENT "\r\n")); - buf_putbyte(writebuf, 0x0); // packet type + buf_putbyte(writebuf, 0x0); /* packet type */ buf_setpos(writebuf, 0); enqueue(&ses.writequeue, writebuf); } diff -r e4b75744acab -r 89555751c489 config.guess --- a/config.guess Sun Oct 06 22:32:03 2013 +0800 +++ b/config.guess Thu Feb 27 21:35:58 2014 +0800 @@ -2,7 +2,7 @@ # Attempt to guess a canonical system name. # Copyright 1992-2013 Free Software Foundation, Inc. -timestamp='2013-04-24' +timestamp='2013-06-10' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by @@ -132,6 +132,27 @@ UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown +case "${UNAME_SYSTEM}" in +Linux|GNU|GNU/*) + # If the system lacks a compiler, then just pick glibc. + # We could probably try harder. + LIBC=gnu + + eval $set_cc_for_build + cat <<-EOF > $dummy.c + #include + #if defined(__UCLIBC__) + LIBC=uclibc + #elif defined(__dietlibc__) + LIBC=dietlibc + #else + LIBC=gnu + #endif + EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'` + ;; +esac + # Note: order is significant - the case branches are not exclusive. case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in @@ -853,21 +874,21 @@ exit ;; *:GNU:*:*) # the GNU system - echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` exit ;; *:GNU/*:*:*) # other systems with GNU libc and userland - echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu + echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC} exit ;; i*86:Minix:*:*) echo ${UNAME_MACHINE}-pc-minix exit ;; aarch64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; aarch64_be:Linux:*:*) UNAME_MACHINE=aarch64_be - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; alpha:Linux:*:*) case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in @@ -880,67 +901,54 @@ EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep -q ld.so.1 - if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi - echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} + if test "$?" = 0 ; then LIBC="gnulibc1" ; fi + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; arc:Linux:*:* | arceb:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; arm*:Linux:*:*) eval $set_cc_for_build if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} else if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then - echo ${UNAME_MACHINE}-unknown-linux-gnueabi + echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi else - echo ${UNAME_MACHINE}-unknown-linux-gnueabihf + echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf fi fi exit ;; avr32*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; cris:Linux:*:*) - echo ${UNAME_MACHINE}-axis-linux-gnu + echo ${UNAME_MACHINE}-axis-linux-${LIBC} exit ;; crisv32:Linux:*:*) - echo ${UNAME_MACHINE}-axis-linux-gnu + echo ${UNAME_MACHINE}-axis-linux-${LIBC} exit ;; frv:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; hexagon:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; i*86:Linux:*:*) - LIBC=gnu - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c - #ifdef __dietlibc__ - LIBC=dietlibc - #endif - #else - #include - #ifdef __UCLIBC__ - LIBC=uclibc - #endif -EOF - eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'` - echo "${UNAME_MACHINE}-pc-linux-${LIBC}" + echo ${UNAME_MACHINE}-pc-linux-${LIBC} exit ;; ia64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; m32r*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; m68*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; mips:Linux:*:* | mips64:Linux:*:*) eval $set_cc_for_build @@ -959,59 +967,63 @@ #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` - test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; } + test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; } ;; or1k:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; or32:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; padre:Linux:*:*) - echo sparc-unknown-linux-gnu + echo sparc-unknown-linux-${LIBC} exit ;; parisc64:Linux:*:* | hppa64:Linux:*:*) - echo hppa64-unknown-linux-gnu + echo hppa64-unknown-linux-${LIBC} exit ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in - PA7*) echo hppa1.1-unknown-linux-gnu ;; - PA8*) echo hppa2.0-unknown-linux-gnu ;; - *) echo hppa-unknown-linux-gnu ;; + PA7*) echo hppa1.1-unknown-linux-${LIBC} ;; + PA8*) echo hppa2.0-unknown-linux-${LIBC} ;; + *) echo hppa-unknown-linux-${LIBC} ;; esac exit ;; ppc64:Linux:*:*) - echo powerpc64-unknown-linux-gnu + echo powerpc64-unknown-linux-${LIBC} exit ;; ppc:Linux:*:*) - echo powerpc-unknown-linux-gnu + echo powerpc-unknown-linux-${LIBC} + exit ;; + ppc64le:Linux:*:*) + echo powerpc64le-unknown-linux-${LIBC} + exit ;; + ppcle:Linux:*:*) + echo powerpcle-unknown-linux-${LIBC} exit ;; s390:Linux:*:* | s390x:Linux:*:*) - echo ${UNAME_MACHINE}-ibm-linux + echo ${UNAME_MACHINE}-ibm-linux-${LIBC} exit ;; sh64*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; sh*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; sparc:Linux:*:* | sparc64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; tile*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; vax:Linux:*:*) - echo ${UNAME_MACHINE}-dec-linux-gnu + echo ${UNAME_MACHINE}-dec-linux-${LIBC} exit ;; x86_64:Linux:*:*) - LIBC=gnu - test -r /lib/libc.so && od -An -S13 /lib/libc.so | grep -q __uClibc_main && LIBC=uclibc echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; xtensa*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. @@ -1244,19 +1256,21 @@ exit ;; *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown - case $UNAME_PROCESSOR in - i386) - eval $set_cc_for_build - if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then - if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ - (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ - grep IS_64BIT_ARCH >/dev/null - then - UNAME_PROCESSOR="x86_64" - fi - fi ;; - unknown) UNAME_PROCESSOR=powerpc ;; - esac + eval $set_cc_for_build + if test "$UNAME_PROCESSOR" = unknown ; then + UNAME_PROCESSOR=powerpc + fi + if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then + if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + case $UNAME_PROCESSOR in + i386) UNAME_PROCESSOR=x86_64 ;; + powerpc) UNAME_PROCESSOR=powerpc64 ;; + esac + fi + fi echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} exit ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) diff -r e4b75744acab -r 89555751c489 config.sub --- a/config.sub Sun Oct 06 22:32:03 2013 +0800 +++ b/config.sub Thu Feb 27 21:35:58 2014 +0800 @@ -2,7 +2,7 @@ # Configuration validation subroutine script. # Copyright 1992-2013 Free Software Foundation, Inc. -timestamp='2013-04-24' +timestamp='2013-10-01' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by @@ -257,7 +257,7 @@ | avr | avr32 \ | be32 | be64 \ | bfin \ - | c4x | clipper \ + | c4x | c8051 | clipper \ | d10v | d30v | dlx | dsp16xx \ | epiphany \ | fido | fr30 | frv \ @@ -265,6 +265,7 @@ | hexagon \ | i370 | i860 | i960 | ia64 \ | ip2k | iq2000 \ + | k1om \ | le32 | le64 \ | lm32 \ | m32c | m32r | m32rle | m68000 | m68k | m88k \ @@ -324,7 +325,7 @@ c6x) basic_machine=tic6x-unknown ;; - m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | picochip) + m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip) basic_machine=$basic_machine-unknown os=-none ;; @@ -372,7 +373,7 @@ | be32-* | be64-* \ | bfin-* | bs2000-* \ | c[123]* | c30-* | [cjt]90-* | c4x-* \ - | clipper-* | craynv-* | cydra-* \ + | c8051-* | clipper-* | craynv-* | cydra-* \ | d10v-* | d30v-* | dlx-* \ | elxsi-* \ | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ @@ -381,6 +382,7 @@ | hexagon-* \ | i*86-* | i860-* | i960-* | ia64-* \ | ip2k-* | iq2000-* \ + | k1om-* \ | le32-* | le64-* \ | lm32-* \ | m32c-* | m32r-* | m32rle-* \ @@ -794,7 +796,7 @@ os=-mingw64 ;; mingw32) - basic_machine=i386-pc + basic_machine=i686-pc os=-mingw32 ;; mingw32ce) @@ -830,7 +832,7 @@ basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` ;; msys) - basic_machine=i386-pc + basic_machine=i686-pc os=-msys ;; mvs) @@ -1546,6 +1548,9 @@ c4x-* | tic4x-*) os=-coff ;; + c8051-*) + os=-elf + ;; hexagon-*) os=-elf ;; diff -r e4b75744acab -r 89555751c489 configure.ac --- a/configure.ac Sun Oct 06 22:32:03 2013 +0800 +++ b/configure.ac Thu Feb 27 21:35:58 2014 +0800 @@ -5,8 +5,9 @@ # of the platform checks have been taken straight from OpenSSH's configure.ac # Huge thanks to them for dealing with the horrible platform-specifics :) -AC_PREREQ(2.50) -AC_INIT(buffer.c) +AC_PREREQ(2.59) +AC_INIT +AC_CONFIG_SRCDIR(buffer.c) OLDCFLAGS=$CFLAGS # Checks for programs. @@ -20,7 +21,7 @@ if test -z "$OLDCFLAGS" && test "$GCC" = "yes"; then AC_MSG_NOTICE(No \$CFLAGS set... using "-Os -W -Wall" for GCC) - CFLAGS="-Os -W -Wall" + CFLAGS="-Os -W -Wall -Wno-pointer-sign" fi # large file support is useful for scp @@ -221,7 +222,8 @@ AC_TYPE_SIZE_T AC_HEADER_TIME -AC_CHECK_TYPES([uint16_t, u_int16_t, struct sockaddr_storage]) +AC_CHECK_TYPES([uint8_t, u_int8_t, uint16_t, u_int16_t, uint32_t, u_int32_t]) +AC_CHECK_TYPES([struct sockaddr_storage]) AC_CHECK_TYPE([socklen_t], ,[ AC_MSG_CHECKING([for socklen_t equivalent]) AC_CACHE_VAL([curl_cv_socklen_t_equiv], @@ -231,15 +233,15 @@ curl_cv_socklen_t_equiv= for arg2 in "struct sockaddr" void; do for t in int size_t unsigned long "unsigned long"; do - AC_TRY_COMPILE([ - #include - #include + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include +#include int getpeername (int, $arg2 *, $t *); - ],[ + ]],[[ $t len; getpeername(0,0,&len); - ],[ + ]])],[ curl_cv_socklen_t_equiv="$t" break ]) @@ -259,12 +261,11 @@ # for the fake-rfc2553 stuff - straight from OpenSSH AC_CACHE_CHECK([for struct sockaddr_storage], ac_cv_have_struct_sockaddr_storage, [ - AC_TRY_COMPILE( - [ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include - ], - [ struct sockaddr_storage s; ], + ]], + [[ struct sockaddr_storage s; ]])], [ ac_cv_have_struct_sockaddr_storage="yes" ], [ ac_cv_have_struct_sockaddr_storage="no" ] ) @@ -274,12 +275,11 @@ fi AC_CACHE_CHECK([for struct sockaddr_in6], ac_cv_have_struct_sockaddr_in6, [ - AC_TRY_COMPILE( - [ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include - ], - [ struct sockaddr_in6 s; s.sin6_family = 0; ], + ]], + [[ struct sockaddr_in6 s; s.sin6_family = 0; ]])], [ ac_cv_have_struct_sockaddr_in6="yes" ], [ ac_cv_have_struct_sockaddr_in6="no" ] ) @@ -289,12 +289,11 @@ fi AC_CACHE_CHECK([for struct in6_addr], ac_cv_have_struct_in6_addr, [ - AC_TRY_COMPILE( - [ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include - ], - [ struct in6_addr s; s.s6_addr[0] = 0; ], + ]], + [[ struct in6_addr s; s.s6_addr[0] = 0; ]])], [ ac_cv_have_struct_in6_addr="yes" ], [ ac_cv_have_struct_in6_addr="no" ] ) @@ -304,13 +303,12 @@ fi AC_CACHE_CHECK([for struct addrinfo], ac_cv_have_struct_addrinfo, [ - AC_TRY_COMPILE( - [ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include #include - ], - [ struct addrinfo s; s.ai_flags = AI_PASSIVE; ], + ]], + [[ struct addrinfo s; s.ai_flags = AI_PASSIVE; ]])], [ ac_cv_have_struct_addrinfo="yes" ], [ ac_cv_have_struct_addrinfo="no" ] ) @@ -323,15 +321,15 @@ # IRIX has a const char return value for gai_strerror() AC_CHECK_FUNCS(gai_strerror,[ AC_DEFINE(HAVE_GAI_STRERROR) - AC_TRY_COMPILE([ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include #include -const char *gai_strerror(int);],[ +const char *gai_strerror(int);]],[[ char *str; -str = gai_strerror(0);],[ +str = gai_strerror(0);]])],[ AC_DEFINE(HAVE_CONST_GAI_STRERROR_PROTO, 1, [Define if gai_strerror() returns const char *])])]) @@ -364,15 +362,25 @@ AC_CHECK_FUNCS(logout updwtmp logwtmp) AC_ARG_ENABLE(bundled-libtom, - [ --enable-bundled-libtom Use bundled libtomcrypt/libtommath even if a system version exists], - [ - BUNDLED_LIBTOM=1 - AC_MSG_NOTICE(Forcing bundled libtom*) +[ --enable-bundled-libtom Force using bundled libtomcrypt/libtommath even if a system version exists. + --disable-bundled-libtom Force using system libtomcrypt/libtommath, fail if it does not exist. + Default is to use system if available, otherwise bundled.], + [ + if test "x$enableval" = "xyes"; then + BUNDLED_LIBTOM=1 + AC_MSG_NOTICE(Forcing bundled libtom*) + else + BUNDLED_LIBTOM=0 + AC_CHECK_LIB(tommath, mp_exptmod, LIBTOM_LIBS="$LIBTOM_LIBS -ltommath", + [AC_MSG_ERROR([Missing system libtommath and --disable-bundled-libtom was specified])] ) + AC_CHECK_LIB(tomcrypt, register_cipher, LIBTOM_LIBS="$LIBTOM_LIBS -ltomcrypt", + [AC_MSG_ERROR([Missing system libtomcrypt and --disable-bundled-libtom was specified])] ) + fi ], [ BUNDLED_LIBTOM=0 - AC_CHECK_LIB(tomcrypt, register_cipher, , BUNDLED_LIBTOM=1) - AC_CHECK_LIB(tommath, mp_exptmod, , BUNDLED_LIBTOM=1) + AC_CHECK_LIB(tommath, mp_exptmod, LIBTOM_LIBS="$LIBTOM_LIBS -ltommath", BUNDLED_LIBTOM=1) + AC_CHECK_LIB(tomcrypt, register_cipher, LIBTOM_LIBS="$LIBTOM_LIBS -ltomcrypt", BUNDLED_LIBTOM=1) ] ) @@ -380,6 +388,7 @@ AC_DEFINE(BUNDLED_LIBTOM,,Use bundled libtom) fi +AC_SUBST(LIBTOM_LIBS) AC_SUBST(BUNDLED_LIBTOM) dnl Added from OpenSSH 3.6.1p2's configure.ac @@ -443,7 +452,7 @@ dnl lastlog detection dnl NOTE: the code itself will detect if lastlog is a directory AC_MSG_CHECKING([if your system defines LASTLOG_FILE]) -AC_TRY_COMPILE([ +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include #ifdef HAVE_LASTLOG_H @@ -455,13 +464,13 @@ #ifdef HAVE_LOGIN_H # include #endif - ], - [ char *lastlog = LASTLOG_FILE; ], + ]], + [[ char *lastlog = LASTLOG_FILE; ]])], [ AC_MSG_RESULT(yes) ], [ AC_MSG_RESULT(no) AC_MSG_CHECKING([if your system defines _PATH_LASTLOG]) - AC_TRY_COMPILE([ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include #ifdef HAVE_LASTLOG_H @@ -470,8 +479,8 @@ #ifdef HAVE_PATHS_H # include #endif - ], - [ char *lastlog = _PATH_LASTLOG; ], + ]], + [[ char *lastlog = _PATH_LASTLOG; ]])], [ AC_MSG_RESULT(yes) ], [ AC_MSG_RESULT(no) @@ -500,14 +509,14 @@ dnl utmp detection AC_MSG_CHECKING([if your system defines UTMP_FILE]) -AC_TRY_COMPILE([ +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include #ifdef HAVE_PATHS_H # include #endif - ], - [ char *utmp = UTMP_FILE; ], + ]], + [[ char *utmp = UTMP_FILE; ]])], [ AC_MSG_RESULT(yes) ], [ AC_MSG_RESULT(no) system_utmp_path=no ] @@ -530,14 +539,16 @@ dnl wtmp detection AC_MSG_CHECKING([if your system defines WTMP_FILE]) -AC_TRY_COMPILE([ +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include -#include +#ifdef HAVE_UTMP_H +# include +#endif #ifdef HAVE_PATHS_H # include #endif - ], - [ char *wtmp = WTMP_FILE; ], + ]], + [[ char *wtmp = WTMP_FILE; ]])], [ AC_MSG_RESULT(yes) ], [ AC_MSG_RESULT(no) system_wtmp_path=no ] @@ -563,7 +574,7 @@ dnl utmpx, but not define UTMPX_FILE (ditto wtmpx.) No doubt it's out dnl there, though. AC_MSG_CHECKING([if your system defines UTMPX_FILE]) -AC_TRY_COMPILE([ +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include #ifdef HAVE_UTMPX_H @@ -572,8 +583,8 @@ #ifdef HAVE_PATHS_H # include #endif - ], - [ char *utmpx = UTMPX_FILE; ], + ]], + [[ char *utmpx = UTMPX_FILE; ]])], [ AC_MSG_RESULT(yes) ], [ AC_MSG_RESULT(no) system_utmpx_path=no ] @@ -588,17 +599,19 @@ dnl wtmpx detection AC_MSG_CHECKING([if your system defines WTMPX_FILE]) -AC_TRY_COMPILE([ +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include -#include +#ifdef HAVE_UTMP_H +# include +#endif #ifdef HAVE_UTMPX_H -#include +# include #endif #ifdef HAVE_PATHS_H # include #endif - ], - [ char *wtmpx = WTMPX_FILE; ], + ]], + [[ char *wtmpx = WTMPX_FILE; ]])], [ AC_MSG_RESULT(yes) ], [ AC_MSG_RESULT(no) system_wtmpx_path=no ] @@ -615,7 +628,6 @@ AC_PROG_GCC_TRADITIONAL AC_FUNC_MEMCMP AC_FUNC_SELECT_ARGTYPES -AC_TYPE_SIGNAL AC_CHECK_FUNCS([dup2 getspnam getusershell memset putenv select socket strdup clearenv strlcpy strlcat daemon basename _getpty getaddrinfo freeaddrinfo getnameinfo fork writev]) AC_SEARCH_LIBS(basename, gen, AC_DEFINE(HAVE_BASENAME)) @@ -623,7 +635,9 @@ # Solaris needs ptmx if test -z "$no_ptmx_check" ; then if test x"$cross_compiling" = x"no" ; then - AC_CHECK_FILE("/dev/ptmx", AC_DEFINE(USE_DEV_PTMX,,Use /dev/ptmx)) + if test -e /dev/ptmx ; then + AC_DEFINE(USE_DEV_PTMX,,Use /dev/ptmx) + fi else AC_MSG_NOTICE([Not checking for /dev/ptmx, we're cross-compiling]) fi @@ -631,7 +645,9 @@ if test -z "$no_ptc_check" ; then if test x"$cross_compiling" = x"no" ; then - AC_CHECK_FILE("/dev/ptc", AC_DEFINE(HAVE_DEV_PTS_AND_PTC,,Use /dev/ptc & /dev/pts)) + if test -e /dev/ptc ; then + AC_DEFINE(HAVE_DEV_PTS_AND_PTC,,Use /dev/ptc & /dev/pts) + fi else AC_MSG_NOTICE([Not checking for /dev/ptc & /dev/pts since we're cross-compiling]) fi @@ -671,6 +687,7 @@ AS_MKDIR_P(libtomcrypt/src/modes/f8) AS_MKDIR_P(libtomcrypt/src/modes/lrw) AS_MKDIR_P(libtomcrypt/src/pk/asn1/der/bit) +AS_MKDIR_P(libtomcrypt/src/pk/asn1/der/boolean) AS_MKDIR_P(libtomcrypt/src/pk/asn1/der/choice) AS_MKDIR_P(libtomcrypt/src/pk/asn1/der/ia5) AS_MKDIR_P(libtomcrypt/src/pk/asn1/der/integer) @@ -678,18 +695,20 @@ AS_MKDIR_P(libtomcrypt/src/pk/asn1/der/octet) AS_MKDIR_P(libtomcrypt/src/pk/asn1/der/printable_string) AS_MKDIR_P(libtomcrypt/src/pk/asn1/der/sequence) +AS_MKDIR_P(libtomcrypt/src/pk/asn1/der/set) AS_MKDIR_P(libtomcrypt/src/pk/asn1/der/short_integer) AS_MKDIR_P(libtomcrypt/src/pk/asn1/der/utctime) +AS_MKDIR_P(libtomcrypt/src/pk/asn1/der/utf8) AS_MKDIR_P(libtomcrypt/src/pk/dh) AS_MKDIR_P(libtomcrypt/src/pk/dsa) AS_MKDIR_P(libtomcrypt/src/pk/ecc) +AS_MKDIR_P(libtomcrypt/src/pk/katja) 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) -AC_OUTPUT(libtommath/Makefile) +AC_CONFIG_FILES(Makefile libtomcrypt/Makefile libtommath/Makefile) +AC_OUTPUT AC_MSG_NOTICE() if test $BUNDLED_LIBTOM = 1 ; then diff -r e4b75744acab -r 89555751c489 crypto_desc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/crypto_desc.c Thu Feb 27 21:35:58 2014 +0800 @@ -0,0 +1,74 @@ +#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 + &DROPBEAR_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 */ + &DROPBEAR_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 e4b75744acab -r 89555751c489 crypto_desc.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/crypto_desc.h Thu Feb 27 21:35:58 2014 +0800 @@ -0,0 +1,28 @@ +#ifndef _CRYPTO_DESC_H +#define _CRYPTO_DESC_H + +#include "includes.h" + +void crypto_init(); + +extern int dropbear_ltc_prng; + +#ifdef DROPBEAR_AES_ASM +extern const struct ltc_cipher_descriptor aes_asm_desc; +#define DROPBEAR_AES_DESC (aes_asm_desc) +#else +#define DROPBEAR_AES_DESC (aes_desc) +#endif + +#ifdef DROPBEAR_SHA1_ASM +extern const struct ltc_hash_descriptor sha1_asm_desc; +#define DROPBEAR_SHA1_DESC (sha1_asm_desc) +#else +#define DROPBEAR_SHA1_DESC (sha1_desc) +#endif + + + + +#endif /* _CRYPTO_DESC_H */ + diff -r e4b75744acab -r 89555751c489 curve25519-donna.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/curve25519-donna.c Thu Feb 27 21:35:58 2014 +0800 @@ -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 + * + * Derived from public domain C code by Daniel J. Bernstein + * + * 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 +#include + +#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; +} diff -r e4b75744acab -r 89555751c489 dbclient.1 --- a/dbclient.1 Sun Oct 06 22:32:03 2013 +0800 +++ b/dbclient.1 Thu Feb 27 21:35:58 2014 +0800 @@ -15,7 +15,7 @@ .B dbclient [ .I args ] -.I [user1]@host1[%port1],[user2]@host2[%port2],... +.I [user1]@host1[^port1],[user2]@host2[^port2],... .SH DESCRIPTION .B dbclient @@ -26,7 +26,7 @@ .B \-p \fIport Connect to .I port -on the remote host. Alternatively a port can be specified as hostname%port. +on the remote host. Alternatively a port can be specified as hostname^port. Default is 22. .TP .B \-i \fIidfile @@ -127,7 +127,7 @@ this case a connection will be made to the first host, then a TCP forwarded connection will be made through that to the second host, and so on. Hosts other than the final destination will not see anything other than the encrypted SSH stream. -A port for a host can be specified with a hash (eg matt@martello%44 ). +A port for a host can be specified with a hash (eg matt@martello^44 ). This syntax can also be used with scp or rsync (specifying dbclient as the ssh/rsh command). A file can be "bounced" through multiple SSH hops, eg diff -r e4b75744acab -r 89555751c489 dbrandom.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dbrandom.c Thu Feb 27 21:35:58 2014 +0800 @@ -0,0 +1,312 @@ +/* + * Dropbear - a SSH2 server + * + * Copyright (c) 2002,2003 Matt Johnston + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + +#include "includes.h" +#include "buffer.h" +#include "dbutil.h" +#include "bignum.h" +#include "dbrandom.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 */ +#define MAX_COUNTER 1<<30 + +static unsigned char hashpool[SHA1_HASH_SIZE] = {0}; +static int donerandinit = 0; + +#define INIT_SEED_SIZE 32 /* 256 bits */ + +/* The basic setup is we read some data from /dev/(u)random or prngd and hash it + * into hashpool. To read data, we hash together current hashpool contents, + * and a counter. We feed more data in by hashing the current pool and new + * data into the pool. + * + * It is important to ensure that counter doesn't wrap around before we + * feed in new entropy. + * + */ + +/* Pass len=0 to hash an entire file */ +static int +process_file(hash_state *hs, const char *filename, + unsigned int len, int prngd) +{ + static int already_blocked = 0; + int readfd; + unsigned int readcount; + int ret = DROPBEAR_FAILURE; + +#ifdef DROPBEAR_PRNGD_SOCKET + if (prngd) + { + readfd = connect_unix(filename); + } + else +#endif + { + readfd = open(filename, O_RDONLY); + } + + if (readfd < 0) { + goto out; + } + + readcount = 0; + while (len == 0 || readcount < len) + { + int readlen, wantread; + unsigned char readbuf[4096]; + if (!already_blocked && !prngd) + { + int res; + struct timeval timeout; + fd_set read_fds; + + timeout.tv_sec = 2; + timeout.tv_usec = 0; + + FD_ZERO(&read_fds); + FD_SET(readfd, &read_fds); + res = select(readfd + 1, &read_fds, NULL, NULL, &timeout); + if (res == 0) + { + dropbear_log(LOG_WARNING, "Warning: Reading the randomness source '%s' seems to have blocked.\nYou may need to find a better entropy source.", filename); + already_blocked = 1; + } + } + + if (len == 0) + { + wantread = sizeof(readbuf); + } + else + { + wantread = MIN(sizeof(readbuf), len-readcount); + } + +#ifdef DROPBEAR_PRNGD_SOCKET + if (prngd) + { + char egdcmd[2]; + egdcmd[0] = 0x02; /* blocking read */ + egdcmd[1] = (unsigned char)wantread; + if (write(readfd, egdcmd, 2) < 0) + { + dropbear_exit("Can't send command to egd"); + } + } +#endif + + readlen = read(readfd, readbuf, wantread); + if (readlen <= 0) { + if (readlen < 0 && errno == EINTR) { + continue; + } + if (readlen == 0 && len == 0) + { + /* whole file was read as requested */ + break; + } + goto out; + } + sha1_process(hs, readbuf, readlen); + readcount += readlen; + } + ret = DROPBEAR_SUCCESS; +out: + close(readfd); + return ret; +} + +void addrandom(char * buf, unsigned int len) +{ + hash_state hs; + + /* hash in the new seed data */ + sha1_init(&hs); + /* existing state (zeroes on startup) */ + sha1_process(&hs, (void*)hashpool, sizeof(hashpool)); + + /* new */ + sha1_process(&hs, buf, len); + sha1_done(&hs, hashpool); +} + +static void write_urandom() +{ +#ifndef DROPBEAR_PRNGD_SOCKET + /* This is opportunistic, don't worry about failure */ + unsigned char buf[INIT_SEED_SIZE]; + FILE *f = fopen(DROPBEAR_URANDOM_DEV, "w"); + if (!f) { + return; + } + genrandom(buf, sizeof(buf)); + fwrite(buf, sizeof(buf), 1, f); + fclose(f); +#endif +} + +/* Initialise the prng from /dev/urandom or prngd. This function can + * be called multiple times */ +void seedrandom() { + + hash_state hs; + + pid_t pid; + struct timeval tv; + clock_t clockval; + + /* hash in the new seed data */ + sha1_init(&hs); + /* existing state */ + sha1_process(&hs, (void*)hashpool, sizeof(hashpool)); + +#ifdef DROPBEAR_PRNGD_SOCKET + if (process_file(&hs, DROPBEAR_PRNGD_SOCKET, INIT_SEED_SIZE, 1) + != DROPBEAR_SUCCESS) { + dropbear_exit("Failure reading random device %s", + DROPBEAR_PRNGD_SOCKET); + } +#else + /* non-blocking random source (probably /dev/urandom) */ + if (process_file(&hs, DROPBEAR_URANDOM_DEV, INIT_SEED_SIZE, 0) + != DROPBEAR_SUCCESS) { + dropbear_exit("Failure reading random device %s", + DROPBEAR_URANDOM_DEV); + } +#endif + + /* A few other sources to fall back on. + * Add more here for other platforms */ +#ifdef __linux__ + /* Seems to be a reasonable source of entropy from timers. Possibly hard + * for even local attackers to reproduce */ + process_file(&hs, "/proc/timer_list", 0, 0); + /* Might help on systems with wireless */ + process_file(&hs, "/proc/interrupts", 0, 0); + + process_file(&hs, "/proc/loadavg", 0, 0); + process_file(&hs, "/proc/sys/kernel/random/entropy_avail", 0, 0); + + /* Mostly network visible but useful in some situations. + * Limit size to avoid slowdowns on systems with lots of routes */ + process_file(&hs, "/proc/net/netstat", 4096, 0); + process_file(&hs, "/proc/net/dev", 4096, 0); + process_file(&hs, "/proc/net/tcp", 4096, 0); + /* Also includes interface lo */ + process_file(&hs, "/proc/net/rt_cache", 4096, 0); + process_file(&hs, "/proc/vmstat", 0, 0); +#endif + + pid = getpid(); + sha1_process(&hs, (void*)&pid, sizeof(pid)); + + /* gettimeofday() doesn't completely fill out struct timeval on + OS X (10.8.3), avoid valgrind warnings by clearing it first */ + memset(&tv, 0x0, sizeof(tv)); + gettimeofday(&tv, NULL); + sha1_process(&hs, (void*)&tv, sizeof(tv)); + + clockval = clock(); + sha1_process(&hs, (void*)&clockval, sizeof(clockval)); + + /* When a private key is read by the client or server it will + * be added to the hashpool - see runopts.c */ + + sha1_done(&hs, hashpool); + + counter = 0; + donerandinit = 1; + + /* Feed it all back into /dev/urandom - this might help if Dropbear + * is running from inetd and gets new state each time */ + write_urandom(); +} + +/* return len bytes of pseudo-random data */ +void genrandom(unsigned char* buf, unsigned int len) { + + hash_state hs; + unsigned char hash[SHA1_HASH_SIZE]; + unsigned int copylen; + + if (!donerandinit) { + dropbear_exit("seedrandom not done"); + } + + while (len > 0) { + sha1_init(&hs); + sha1_process(&hs, (void*)hashpool, sizeof(hashpool)); + sha1_process(&hs, (void*)&counter, sizeof(counter)); + sha1_done(&hs, hash); + + counter++; + if (counter > MAX_COUNTER) { + seedrandom(); + } + + copylen = MIN(len, SHA1_HASH_SIZE); + memcpy(buf, hash, copylen); + len -= copylen; + buf += copylen; + } + m_burn(hash, sizeof(hash)); +} + +/* Generates a random mp_int. + * max is a *mp_int specifying an upper bound. + * rand must be an initialised *mp_int for the result. + * the result rand satisfies: 0 < rand < max + * */ +void gen_random_mpint(mp_int *max, mp_int *rand) { + + unsigned char *randbuf = NULL; + unsigned int len = 0; + const unsigned char masks[] = {0xff, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f}; + + const int size_bits = mp_count_bits(max); + + len = size_bits / 8; + if ((size_bits % 8) != 0) { + len += 1; + } + + randbuf = (unsigned char*)m_malloc(len); + do { + genrandom(randbuf, len); + /* Mask out the unrequired bits - mp_read_unsigned_bin expects + * MSB first.*/ + randbuf[0] &= masks[size_bits % 8]; + + bytes_to_mp(rand, randbuf, len); + + /* keep regenerating until we get one satisfying + * 0 < rand < max */ + } while (mp_cmp(rand, max) != MP_LT); + m_burn(randbuf, len); + m_free(randbuf); +} diff -r e4b75744acab -r 89555751c489 dbrandom.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dbrandom.h Thu Feb 27 21:35:58 2014 +0800 @@ -0,0 +1,35 @@ +/* + * Dropbear - a SSH2 server + * + * Copyright (c) 2002,2003 Matt Johnston + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + +#ifndef _RANDOM_H_ +#define _RANDOM_H_ + +#include "includes.h" + +void seedrandom(); +void genrandom(unsigned char* buf, unsigned int len); +void addrandom(char * buf, unsigned int len); +void gen_random_mpint(mp_int *max, mp_int *rand); + +#endif /* _RANDOM_H_ */ diff -r e4b75744acab -r 89555751c489 dbutil.c --- a/dbutil.c Sun Oct 06 22:32:03 2013 +0800 +++ b/dbutil.c Thu Feb 27 21:35:58 2014 +0800 @@ -177,28 +177,48 @@ } #endif /* DEBUG_TRACE */ -static void set_sock_priority(int sock) { - +void set_sock_nodelay(int sock) { int val; /* disable nagle */ val = 1; setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&val, sizeof(val)); +} + +void set_sock_priority(int sock, enum dropbear_prio prio) { + + int iptos_val = 0, so_prio_val = 0, rc; /* set the TOS bit for either ipv4 or ipv6 */ #ifdef IPTOS_LOWDELAY - val = IPTOS_LOWDELAY; + if (prio == DROPBEAR_PRIO_LOWDELAY) { + iptos_val = IPTOS_LOWDELAY; + } else if (prio == DROPBEAR_PRIO_BULK) { + iptos_val = IPTOS_THROUGHPUT; + } #if defined(IPPROTO_IPV6) && defined(IPV6_TCLASS) - setsockopt(sock, IPPROTO_IPV6, IPV6_TCLASS, (void*)&val, sizeof(val)); + rc = setsockopt(sock, IPPROTO_IPV6, IPV6_TCLASS, (void*)&iptos_val, sizeof(iptos_val)); + if (rc < 0) { + TRACE(("Couldn't set IPV6_TCLASS (%s)", strerror(errno))); + } #endif - setsockopt(sock, IPPROTO_IP, IP_TOS, (void*)&val, sizeof(val)); + rc = setsockopt(sock, IPPROTO_IP, IP_TOS, (void*)&iptos_val, sizeof(iptos_val)); + if (rc < 0) { + TRACE(("Couldn't set IP_TOS (%s)", strerror(errno))); + } #endif #ifdef SO_PRIORITY - /* linux specific, sets QoS class. - * 6 looks to be optimal for interactive traffic (see tc-prio(8) ). */ - val = 6; - setsockopt(sock, SOL_SOCKET, SO_PRIORITY, (void*) &val, sizeof(val)); + if (prio == DROPBEAR_PRIO_LOWDELAY) { + so_prio_val = TC_PRIO_INTERACTIVE; + } else if (prio == DROPBEAR_PRIO_BULK) { + so_prio_val = TC_PRIO_BULK; + } + /* linux specific, sets QoS class. see tc-prio(8) */ + rc = setsockopt(sock, SOL_SOCKET, SO_PRIORITY, (void*) &so_prio_val, sizeof(so_prio_val)); + if (rc < 0) + dropbear_log(LOG_WARNING, "Couldn't set SO_PRIORITY (%s)", + strerror(errno)); #endif } @@ -290,7 +310,7 @@ } #endif - set_sock_priority(sock); + set_sock_nodelay(sock); if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) { err = errno; @@ -429,7 +449,7 @@ TRACE(("Error connecting: %s", strerror(err))) } else { /* Success */ - set_sock_priority(sock); + set_sock_nodelay(sock); } freeaddrinfo(res0); @@ -675,6 +695,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 @@ -784,6 +812,10 @@ /* make sure that the socket closes */ void m_close(int fd) { + if (fd == -1) { + return; + } + int val; do { val = close(fd); @@ -873,14 +905,17 @@ /* Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE, with the result in *val */ int m_str_to_uint(const char* str, unsigned int *val) { + unsigned long l; errno = 0; - *val = strtoul(str, NULL, 10); + l = strtoul(str, NULL, 10); /* The c99 spec doesn't actually seem to define EINVAL, but most platforms * I've looked at mention it in their manpage */ - if ((*val == 0 && errno == EINVAL) - || (*val == ULONG_MAX && errno == ERANGE)) { + if ((l == 0 && errno == EINVAL) + || (l == ULONG_MAX && errno == ERANGE) + || (l > UINT_MAX)) { return DROPBEAR_FAILURE; } else { + *val = l; return DROPBEAR_SUCCESS; } } diff -r e4b75744acab -r 89555751c489 dbutil.h --- a/dbutil.h Sun Oct 06 22:32:03 2013 +0800 +++ b/dbutil.h Thu Feb 27 21:35:58 2014 +0800 @@ -57,14 +57,23 @@ 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 +enum dropbear_prio { + DROPBEAR_PRIO_DEFAULT, + DROPBEAR_PRIO_LOWDELAY, + DROPBEAR_PRIO_BULK, +}; + char * stripcontrol(const char * text); void get_socket_address(int fd, char **local_host, char **local_port, char **remote_host, char **remote_port, int host_lookup); void getaddrstring(struct sockaddr_storage* addr, char **ret_host, char **ret_port, int host_lookup); +void set_sock_nodelay(int sock); +void set_sock_priority(int sock, enum dropbear_prio prio); int dropbear_listen(const char* address, const char* port, int *socks, unsigned int sockcount, char **errstring, int *maxfd); int spawn_command(void(*exec_fn)(void *user_data), void *exec_data, diff -r e4b75744acab -r 89555751c489 debian/changelog --- a/debian/changelog Sun Oct 06 22:32:03 2013 +0800 +++ b/debian/changelog Thu Feb 27 21:35:58 2014 +0800 @@ -1,3 +1,21 @@ +dropbear (2014.63-0.1) unstable; urgency=low + + * New upstream release. + + -- Matt Johnston Wed, 19 Feb 2014 22:54:00 +0800 + +dropbear (2013.62) unstable; urgency=low + + * New upstream release. + + -- Matt Johnston Tue, 7 Dec 2013 22:54:00 +0800 + +dropbear (2013.60-0.1) unstable; urgency=low + + * New upstream release. + + -- Matt Johnston Wed, 16 Oct 2013 22:54:00 +0800 + dropbear (2013.59-0.1) unstable; urgency=low * New upstream release. diff -r e4b75744acab -r 89555751c489 debug.h diff -r e4b75744acab -r 89555751c489 dropbear.8 --- a/dropbear.8 Sun Oct 06 22:32:03 2013 +0800 +++ b/dropbear.8 Thu Feb 27 21:35:58 2014 +0800 @@ -3,10 +3,10 @@ dropbear \- lightweight SSH server .SH SYNOPSIS .B dropbear -[\-FEmwsgjki] [\-b -.I banner\fR] [\-d -.I dsskey\fR] [\-r -.I rsakey\fR] [\-p +[\-RFEmwsgjki] [\-b +.I banner\fR] +[\-r +.I hostkeyfile\fR] [\-p .IR [address:]port ] .SH DESCRIPTION .B dropbear @@ -20,24 +20,16 @@ .I banner before user login (default: none). .TP -.B \-d \fIdsskey -dsskeyfile. +.B \-r \fIhostkey Use the contents of the file -.I dsskey -for the DSS host key (default: /etc/dropbear/dropbear_dss_host_key). -Note that -some SSH implementations -use the term "DSA" rather than "DSS", they mean the same thing. +.I hostkey +for the SSH hostkey. This file is generated with -.BR dropbearkey (1). +.BR dropbearkey (1) +or automatically with the '-R' option. See "Host Key Files" below. .TP -.B \-r \fIrsakey -rsakeyfile. -Use the contents of the file -.I rsakey -for the rsa host key (default: /etc/dropbear/dropbear_rsa_host_key). -This file is generated with -.BR dropbearkey (1). +.B \-R +Generate hostkeys automatically. See "Host Key Files" below. .TP .B \-F Don't fork into background. @@ -143,9 +135,13 @@ Host Key Files Host key files are read at startup from a standard location, by default -/etc/dropbear/dropbear_dss_host_key and /etc/dropbear/dropbear_rsa_host_key -or specified on the commandline with -d or -r. These are of the form generated -by dropbearkey. +/etc/dropbear/dropbear_dss_host_key, /etc/dropbear/dropbear_rsa_host_key, and +/etc/dropbear/dropbear-ecdsa_host_key +or specified on the commandline with -r. These are of the form generated +by dropbearkey. The -R option can be used to automatically generate keys +in the default location - keys will be generated after startup when the first +connection is established. This had the benefit that the system /dev/urandom +random number source has a better chance of being securely seeded. .TP Message Of The Day diff -r e4b75744acab -r 89555751c489 dropbearconvert.c --- a/dropbearconvert.c Sun Oct 06 22:32:03 2013 +0800 +++ b/dropbearconvert.c Thu Feb 27 21:35:58 2014 +0800 @@ -28,6 +28,8 @@ #include "buffer.h" #include "dbutil.h" #include "keyimport.h" +#include "crypto_desc.h" +#include "dbrandom.h" static int do_convert(int intype, const char* infile, int outtype, @@ -62,6 +64,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; @@ -111,7 +116,7 @@ const char* outfile) { sign_key * key = NULL; - char * keytype = NULL; + const char * keytype = NULL; int ret = 1; key = import_read(infile, NULL, intype); @@ -121,16 +126,7 @@ goto out; } -#ifdef DROPBEAR_RSA - if (key->rsakey != NULL) { - keytype = "RSA"; - } -#endif -#ifdef DROPBEAR_DSS - if (key->dsskey != NULL) { - keytype = "DSS"; - } -#endif + keytype = signkey_name_from_type(key->type, NULL); fprintf(stderr, "Key is a %s key\n", keytype); diff -r e4b75744acab -r 89555751c489 dropbearkey.1 --- a/dropbearkey.1 Sun Oct 06 22:32:03 2013 +0800 +++ b/dropbearkey.1 Thu Feb 27 21:35:58 2014 +0800 @@ -12,9 +12,10 @@ .SH DESCRIPTION .B dropbearkey generates a -.I RSA +.I RSA +.I DSS, or -.I DSS +.I ECDSA format SSH private key, and saves it to a file for the use with the Dropbear client or server. Note that @@ -26,6 +27,7 @@ Type of key to generate. Must be one of .I rsa +.I ecdsa or .IR dss . .TP @@ -36,7 +38,7 @@ .B \-s \fIbits Set the key size to .I bits -bits, should be multiple of 8 (optional). +bits, should be multiple of 8 (optional). .SH NOTES The program dropbearconvert(1) can be used to convert between Dropbear and OpenSSH key formats. .P diff -r e4b75744acab -r 89555751c489 dropbearkey.c --- a/dropbearkey.c Sun Oct 06 22:32:03 2013 +0800 +++ b/dropbearkey.c Thu Feb 27 21:35:58 2014 +0800 @@ -51,15 +51,16 @@ #include "genrsa.h" #include "gendss.h" +#include "ecdsa.h" +#include "crypto_desc.h" +#include "dbrandom.h" +#include "gensignkey.h" static void printhelp(char * progname); -#define RSA_SIZE (1024/8) /* 1024 bit */ -#define DSS_SIZE (1024/8) /* 1024 bit */ -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) { @@ -72,9 +73,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" @@ -82,6 +101,30 @@ ,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) { @@ -91,16 +134,16 @@ int i; char ** next = 0; - 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; + unsigned int bits = 0; int printpub = 0; + crypto_init(); + seedrandom(); + /* get the commandline options */ for (i = 1; i < argc; i++) { if (argv[i] == NULL) { @@ -151,8 +194,8 @@ } if (printpub) { - justprintpub(filename); - /* Not reached */ + int ret = printpubfile(filename); + exit(ret); } /* check/parse args */ @@ -162,21 +205,26 @@ exit(EXIT_FAILURE); } - if (strlen(typetext) == 3) { #ifdef DROPBEAR_RSA - if (strncmp(typetext, "rsa", 3) == 0) { - keytype = DROPBEAR_SIGNKEY_RSA; - TRACE(("type is rsa")) - } + if (strcmp(typetext, "rsa") == 0) + { + keytype = DROPBEAR_SIGNKEY_RSA; + } #endif #ifdef DROPBEAR_DSS - if (strncmp(typetext, "dss", 3) == 0) { - keytype = DROPBEAR_SIGNKEY_DSS; - TRACE(("type is dss")) - } + if (strcmp(typetext, "dss") == 0) + { + keytype = DROPBEAR_SIGNKEY_DSS; + } #endif +#ifdef DROPBEAR_ECDSA + if (strcmp(typetext, "ecdsa") == 0) + { + keytype = DROPBEAR_SIGNKEY_ECDSA_KEYGEN; } - if (keytype == -1) { +#endif + + if (keytype == DROPBEAR_SIGNKEY_NONE) { fprintf(stderr, "Unknown key type '%s'\n", typetext); printhelp(argv[0]); exit(EXIT_FAILURE); @@ -188,75 +236,26 @@ 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); - } - - 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 */ - } - } - - - fprintf(stderr, "Will output %d bit %s secret key to '%s'\n", keysize*8, - 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(keysize); /* 128 bytes = 1024 bit */ - break; -#endif -#ifdef DROPBEAR_DSS - case DROPBEAR_SIGNKEY_DSS: - key->dsskey = gen_dss_priv_key(keysize); /* 128 bytes = 1024 bit */ - 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; - int keytype; + enum signkey_type keytype; int ret; int err = DROPBEAR_FAILURE; @@ -290,7 +289,7 @@ sign_key_free(key); key = NULL; } - exit(err); + return err; } static void printpubkey(sign_key * key, int keytype) { @@ -319,7 +318,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); @@ -339,35 +338,3 @@ 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); -} diff -r e4b75744acab -r 89555751c489 dss.c --- a/dss.c Sun Oct 06 22:32:03 2013 +0800 +++ b/dss.c Thu Feb 27 21:35:58 2014 +0800 @@ -28,7 +28,7 @@ #include "dss.h" #include "buffer.h" #include "ssh.h" -#include "random.h" +#include "dbrandom.h" /* Handle DSS (Digital Signature Standard), aka DSA (D.S. Algorithm), * operations, such as key reading, signing, verification. Key generation @@ -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 e4b75744acab -r 89555751c489 dss.h --- a/dss.h Sun Oct 06 22:32:03 2013 +0800 +++ b/dss.h Thu Feb 27 21:35:58 2014 +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 e4b75744acab -r 89555751c489 ecc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ecc.c Thu Feb 27 21:35:58 2014 +0800 @@ -0,0 +1,275 @@ +#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 = { + 32, /* .ltc_size */ + NULL, /* .dp */ + &sha256_desc, /* .hash_desc */ + "nistp256" /* .name */ +}; +#endif +#ifdef DROPBEAR_ECC_384 +struct dropbear_ecc_curve ecc_curve_nistp384 = { + 48, /* .ltc_size */ + NULL, /* .dp */ + &sha384_desc, /* .hash_desc */ + "nistp384" /* .name */ +}; +#endif +#ifdef DROPBEAR_ECC_521 +struct dropbear_ecc_curve ecc_curve_nistp521 = { + 66, /* .ltc_size */ + NULL, /* .dp */ + &sha512_desc, /* .hash_desc */ + "nistp521" /* .name */ +}; +#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((mp_int**)&key->pubkey.x, (mp_int**)&key->pubkey.y, + (mp_int**)&key->pubkey.z, (mp_int**)&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; + int err; + buf_putint(buf, len); + 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 e4b75744acab -r 89555751c489 ecc.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ecc.h Thu Feb 27 21:35:58 2014 +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 */ diff -r e4b75744acab -r 89555751c489 ecdsa.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ecdsa.c Thu Feb 27 21:35:58 2014 +0800 @@ -0,0 +1,420 @@ +#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 + +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) { + 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 */ + ecc_key *new_key = NULL; + 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); + } + + 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, + 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, 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 e4b75744acab -r 89555751c489 ecdsa.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ecdsa.h Thu Feb 27 21:35:58 2014 +0800 @@ -0,0 +1,35 @@ +#ifndef _ECDSA_H_ +#define _ECDSA_H_ + +#include "includes.h" +#include "buffer.h" +#include "signkey.h" + +#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) +#define ECDSA_DEFAULT_SIZE 384 +#elif defined(DROPBEAR_ECC_256) +#define ECDSA_DEFAULT_SIZE 256 +#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); +/* Returns 1 on success */ +int signkey_is_ecdsa(enum signkey_type type); + +#endif + +#endif /* _ECDSA_H_ */ diff -r e4b75744acab -r 89555751c489 gendss.c --- a/gendss.c Sun Oct 06 22:32:03 2013 +0800 +++ b/gendss.c Thu Feb 27 21:35:58 2014 +0800 @@ -26,7 +26,7 @@ #include "dbutil.h" #include "signkey.h" #include "bignum.h" -#include "random.h" +#include "dbrandom.h" #include "buffer.h" #include "gendss.h" #include "dss.h" @@ -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 e4b75744acab -r 89555751c489 genrsa.c --- a/genrsa.c Sun Oct 06 22:32:03 2013 +0800 +++ b/genrsa.c Thu Feb 27 21:35:58 2014 +0800 @@ -25,7 +25,7 @@ #include "includes.h" #include "dbutil.h" #include "bignum.h" -#include "random.h" +#include "dbrandom.h" #include "rsa.h" #include "genrsa.h" @@ -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,30 +44,32 @@ 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); + while (1) { + 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"); - exit(1); + if (mp_mul(key->p, key->q, key->n) != MP_OKAY) { + fprintf(stderr, "RSA generation failed\n"); + exit(1); + } + + if ((unsigned int)mp_count_bits(key->n) == size) { + break; + } } /* lcm(p-1, q-1) */ @@ -90,21 +92,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); 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); - buf[0] |= 0x80; /* MSB set */ + genrandom(buf, size_bytes); + buf[0] |= 0x80; - bytes_to_mp(prime, buf, size+1); + bytes_to_mp(prime, buf, size_bytes); /* find the next integer which is prime, 8 round of miller-rabin */ if (mp_prime_next_prime(prime, 8, 0) != MP_OKAY) { @@ -126,7 +128,7 @@ /* now we have a good value for result */ mp_clear(&temp_gcd); - m_burn(buf, size+1); + m_burn(buf, size_bytes); m_free(buf); } diff -r e4b75744acab -r 89555751c489 gensignkey.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gensignkey.c Thu Feb 27 21:35:58 2014 +0800 @@ -0,0 +1,132 @@ +#include "includes.h" +#include "dbutil.h" +#include "buffer.h" +#include "ecdsa.h" +#include "genrsa.h" +#include "gendss.h" +#include "signkey.h" +#include "dbrandom.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 (len == -1 && 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 -r e4b75744acab -r 89555751c489 gensignkey.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gensignkey.h Thu Feb 27 21:35:58 2014 +0800 @@ -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 diff -r e4b75744acab -r 89555751c489 includes.h --- a/includes.h Sun Oct 06 22:32:03 2013 +0800 +++ b/includes.h Thu Feb 27 21:35:58 2014 +0800 @@ -134,15 +134,35 @@ #include "compat.h" -#include "fake-rfc2553.h" -#ifndef HAVE_UINT16_T +#ifndef HAVE_U_INT8_T +typedef unsigned char u_int8_t; +#endif /* HAVE_U_INT8_T */ +#ifndef HAVE_UINT8_T +typedef u_int8_t uint8_t; +#endif /* HAVE_UINT8_T */ + #ifndef HAVE_U_INT16_T typedef unsigned short u_int16_t; #endif /* HAVE_U_INT16_T */ +#ifndef HAVE_UINT16_T typedef u_int16_t uint16_t; #endif /* HAVE_UINT16_T */ +#ifndef HAVE_U_INT32_T +typedef unsigned int u_int32_t; +#endif /* HAVE_U_INT32_T */ +#ifndef HAVE_UINT32_T +typedef u_int32_t uint32_t; +#endif /* HAVE_UINT32_T */ + +#ifdef SO_PRIORITY +#include +#include +#endif + +#include "fake-rfc2553.h" + #ifndef LOG_AUTHPRIV #define LOG_AUTHPRIV LOG_AUTH #endif diff -r e4b75744acab -r 89555751c489 kex.h --- a/kex.h Sun Oct 06 22:32:03 2013 +0800 +++ b/kex.h Thu Feb 27 21:35:58 2014 +0800 @@ -27,16 +27,33 @@ #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 + +#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(); @@ -66,6 +83,33 @@ }; +#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 + +#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 diff -r e4b75744acab -r 89555751c489 keyimport.c --- a/keyimport.c Sun Oct 06 22:32:03 2013 +0800 +++ b/keyimport.c Thu Feb 27 21:35:58 2014 +0800 @@ -2,8 +2,6 @@ * Based on PuTTY's import.c for importing/exporting OpenSSH and SSH.com * keyfiles. * - * The horribleness of the code is probably mine (matt). - * * Modifications copyright 2003 Matt Johnston * * PuTTY is copyright 1997-2003 Simon Tatham. @@ -36,6 +34,11 @@ #include "bignum.h" #include "buffer.h" #include "dbutil.h" +#include "ecc.h" + +static const unsigned char OID_SEC256R1_BLOB[] = {0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07}; +static const unsigned char OID_SEC384R1_BLOB[] = {0x2b, 0x81, 0x04, 0x00, 0x22}; +static const unsigned char OID_SEC521R1_BLOB[] = {0x2b, 0x81, 0x04, 0x00, 0x23}; #define PUT_32BIT(cp, value) do { \ (cp)[3] = (unsigned char)(value); \ @@ -109,7 +112,7 @@ 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) { @@ -125,6 +128,8 @@ } buf_free(buf); + ret->type = type; + return ret; error: @@ -140,25 +145,13 @@ /* returns 0 on fail, 1 on success */ static int dropbear_write(const char*filename, sign_key * key) { - int keytype = -1; buffer * buf; FILE*fp; int len; int ret; -#ifdef DROPBEAR_RSA - if (key->rsakey != NULL) { - keytype = DROPBEAR_SIGNKEY_RSA; - } -#endif -#ifdef DROPBEAR_DSS - if (key->dsskey != NULL) { - keytype = DROPBEAR_SIGNKEY_DSS; - } -#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 +342,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 +385,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; @@ -506,7 +501,7 @@ 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; @@ -516,11 +511,13 @@ char *errmsg; char *modptr = NULL; int modlen = -9999; - int type; + enum signkey_type type; sign_key *retkey; buffer * blobbuf = NULL; + retkey = new_sign_key(); + key = load_openssh_key(filename); if (!key) @@ -597,6 +594,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. @@ -605,8 +604,10 @@ if (key->type == OSSH_DSA) { buf_putstring(blobbuf, "ssh-dss", 7); + retkey->type = DROPBEAR_SIGNKEY_DSS; } else if (key->type == OSSH_RSA) { buf_putstring(blobbuf, "ssh-rsa", 7); + retkey->type = DROPBEAR_SIGNKEY_RSA; } for (i = 0; i < num_integers; i++) { @@ -620,11 +621,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 +663,136 @@ p += len; } +#ifdef DROPBEAR_ECDSA + if (key->type == OSSH_EC) { + 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 (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; + } +#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; + } +#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; + } +#endif + 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_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 */ @@ -703,202 +826,284 @@ char zero[1]; int ret = 0; FILE *fp; - int keytype = -1; #ifdef DROPBEAR_RSA mp_int dmp1, dmq1, iqmp, tmpval; /* for rsa */ - - if (key->rsakey != NULL) { - keytype = DROPBEAR_SIGNKEY_RSA; - } -#endif -#ifdef DROPBEAR_DSS - if (key->dsskey != NULL) { - keytype = DROPBEAR_SIGNKEY_DSS; - } #endif - dropbear_assert(keytype != -1); + if (key->type == DROPBEAR_SIGNKEY_RSA || key->type == DROPBEAR_SIGNKEY_DSS) + { + /* + * Fetch the key blobs. + */ + keyblob = buf_new(3000); + buf_put_priv_key(keyblob, key, key->type); + + buf_setpos(keyblob, 0); + /* skip the "ssh-rsa" or "ssh-dss" header */ + buf_incrpos(keyblob, buf_getint(keyblob)); + + /* + * Find the sequence of integers to be encoded into the OpenSSH + * key blob, and also decide on the header line. + */ + numbers[0].start = zero; numbers[0].bytes = 1; zero[0] = '\0'; + + #ifdef DROPBEAR_RSA + if (key->type == DROPBEAR_SIGNKEY_RSA) { + + if (key->rsakey->p == NULL || key->rsakey->q == NULL) { + fprintf(stderr, "Pre-0.33 Dropbear keys cannot be converted to OpenSSH keys.\n"); + goto error; + } - /* - * Fetch the key blobs. - */ - keyblob = buf_new(3000); - buf_put_priv_key(keyblob, key, keytype); + /* e */ + numbers[2].bytes = buf_getint(keyblob); + numbers[2].start = buf_getptr(keyblob, numbers[2].bytes); + buf_incrpos(keyblob, numbers[2].bytes); + + /* n */ + numbers[1].bytes = buf_getint(keyblob); + numbers[1].start = buf_getptr(keyblob, numbers[1].bytes); + buf_incrpos(keyblob, numbers[1].bytes); + + /* d */ + numbers[3].bytes = buf_getint(keyblob); + numbers[3].start = buf_getptr(keyblob, numbers[3].bytes); + buf_incrpos(keyblob, numbers[3].bytes); + + /* p */ + numbers[4].bytes = buf_getint(keyblob); + numbers[4].start = buf_getptr(keyblob, numbers[4].bytes); + buf_incrpos(keyblob, numbers[4].bytes); + + /* q */ + numbers[5].bytes = buf_getint(keyblob); + numbers[5].start = buf_getptr(keyblob, numbers[5].bytes); + buf_incrpos(keyblob, numbers[5].bytes); - buf_setpos(keyblob, 0); - /* skip the "ssh-rsa" or "ssh-dss" header */ - buf_incrpos(keyblob, buf_getint(keyblob)); + /* now calculate some extra parameters: */ + m_mp_init(&tmpval); + m_mp_init(&dmp1); + m_mp_init(&dmq1); + m_mp_init(&iqmp); - /* - * Find the sequence of integers to be encoded into the OpenSSH - * key blob, and also decide on the header line. - */ - numbers[0].start = zero; numbers[0].bytes = 1; zero[0] = '\0'; + /* dmp1 = d mod (p-1) */ + if (mp_sub_d(key->rsakey->p, 1, &tmpval) != MP_OKAY) { + fprintf(stderr, "Bignum error for p-1\n"); + goto error; + } + if (mp_mod(key->rsakey->d, &tmpval, &dmp1) != MP_OKAY) { + fprintf(stderr, "Bignum error for dmp1\n"); + goto error; + } + + /* dmq1 = d mod (q-1) */ + if (mp_sub_d(key->rsakey->q, 1, &tmpval) != MP_OKAY) { + fprintf(stderr, "Bignum error for q-1\n"); + goto error; + } + if (mp_mod(key->rsakey->d, &tmpval, &dmq1) != MP_OKAY) { + fprintf(stderr, "Bignum error for dmq1\n"); + goto error; + } + + /* iqmp = (q^-1) mod p */ + if (mp_invmod(key->rsakey->q, key->rsakey->p, &iqmp) != MP_OKAY) { + fprintf(stderr, "Bignum error for iqmp\n"); + goto error; + } -#ifdef DROPBEAR_RSA - if (keytype == DROPBEAR_SIGNKEY_RSA) { + extrablob = buf_new(2000); + buf_putmpint(extrablob, &dmp1); + buf_putmpint(extrablob, &dmq1); + buf_putmpint(extrablob, &iqmp); + buf_setpos(extrablob, 0); + mp_clear(&dmp1); + mp_clear(&dmq1); + mp_clear(&iqmp); + mp_clear(&tmpval); + + /* dmp1 */ + numbers[6].bytes = buf_getint(extrablob); + numbers[6].start = buf_getptr(extrablob, numbers[6].bytes); + buf_incrpos(extrablob, numbers[6].bytes); + + /* dmq1 */ + numbers[7].bytes = buf_getint(extrablob); + numbers[7].start = buf_getptr(extrablob, numbers[7].bytes); + buf_incrpos(extrablob, numbers[7].bytes); + + /* iqmp */ + numbers[8].bytes = buf_getint(extrablob); + numbers[8].start = buf_getptr(extrablob, numbers[8].bytes); + buf_incrpos(extrablob, numbers[8].bytes); - if (key->rsakey->p == NULL || key->rsakey->q == NULL) { - fprintf(stderr, "Pre-0.33 Dropbear keys cannot be converted to OpenSSH keys.\n"); - goto error; + nnumbers = 9; + header = "-----BEGIN RSA PRIVATE KEY-----\n"; + footer = "-----END RSA PRIVATE KEY-----\n"; } + #endif /* DROPBEAR_RSA */ + + #ifdef DROPBEAR_DSS + if (key->type == DROPBEAR_SIGNKEY_DSS) { - /* e */ - numbers[2].bytes = buf_getint(keyblob); - numbers[2].start = buf_getptr(keyblob, numbers[2].bytes); - buf_incrpos(keyblob, numbers[2].bytes); - - /* n */ - numbers[1].bytes = buf_getint(keyblob); - numbers[1].start = buf_getptr(keyblob, numbers[1].bytes); - buf_incrpos(keyblob, numbers[1].bytes); - - /* d */ - numbers[3].bytes = buf_getint(keyblob); - numbers[3].start = buf_getptr(keyblob, numbers[3].bytes); - buf_incrpos(keyblob, numbers[3].bytes); - - /* p */ - numbers[4].bytes = buf_getint(keyblob); - numbers[4].start = buf_getptr(keyblob, numbers[4].bytes); - buf_incrpos(keyblob, numbers[4].bytes); - - /* q */ - numbers[5].bytes = buf_getint(keyblob); - numbers[5].start = buf_getptr(keyblob, numbers[5].bytes); - buf_incrpos(keyblob, numbers[5].bytes); + /* p */ + numbers[1].bytes = buf_getint(keyblob); + numbers[1].start = buf_getptr(keyblob, numbers[1].bytes); + buf_incrpos(keyblob, numbers[1].bytes); + + /* q */ + numbers[2].bytes = buf_getint(keyblob); + numbers[2].start = buf_getptr(keyblob, numbers[2].bytes); + buf_incrpos(keyblob, numbers[2].bytes); + + /* g */ + numbers[3].bytes = buf_getint(keyblob); + numbers[3].start = buf_getptr(keyblob, numbers[3].bytes); + buf_incrpos(keyblob, numbers[3].bytes); + + /* y */ + numbers[4].bytes = buf_getint(keyblob); + numbers[4].start = buf_getptr(keyblob, numbers[4].bytes); + buf_incrpos(keyblob, numbers[4].bytes); + + /* x */ + numbers[5].bytes = buf_getint(keyblob); + numbers[5].start = buf_getptr(keyblob, numbers[5].bytes); + buf_incrpos(keyblob, numbers[5].bytes); + + nnumbers = 6; + header = "-----BEGIN DSA PRIVATE KEY-----\n"; + footer = "-----END DSA PRIVATE KEY-----\n"; + } + #endif /* DROPBEAR_DSS */ + + /* + * Now count up the total size of the ASN.1 encoded integers, + * so as to determine the length of the containing SEQUENCE. + */ + len = 0; + for (i = 0; i < nnumbers; i++) { + len += ber_write_id_len(NULL, 2, numbers[i].bytes, 0); + len += numbers[i].bytes; + } + seqlen = len; + /* Now add on the SEQUENCE header. */ + len += ber_write_id_len(NULL, 16, seqlen, ASN1_CONSTRUCTED); + /* Round up to the cipher block size, ensuring we have at least one + * byte of padding (see below). */ + outlen = len; + if (passphrase) + outlen = (outlen+8) &~ 7; + + /* + * Now we know how big outblob needs to be. Allocate it. + */ + outblob = (unsigned char*)m_malloc(outlen); - /* now calculate some extra parameters: */ - m_mp_init(&tmpval); - m_mp_init(&dmp1); - m_mp_init(&dmq1); - m_mp_init(&iqmp); + /* + * And write the data into it. + */ + pos = 0; + pos += ber_write_id_len(outblob+pos, 16, seqlen, ASN1_CONSTRUCTED); + for (i = 0; i < nnumbers; i++) { + pos += ber_write_id_len(outblob+pos, 2, numbers[i].bytes, 0); + memcpy(outblob+pos, numbers[i].start, numbers[i].bytes); + pos += numbers[i].bytes; + } + } /* end RSA and DSS handling */ - /* dmp1 = d mod (p-1) */ - if (mp_sub_d(key->rsakey->p, 1, &tmpval) != MP_OKAY) { - fprintf(stderr, "Bignum error for p-1\n"); - goto error; - } - if (mp_mod(key->rsakey->d, &tmpval, &dmp1) != MP_OKAY) { - fprintf(stderr, "Bignum error for dmp1\n"); - goto error; +#ifdef DROPBEAR_ECDSA + if (key->type == DROPBEAR_SIGNKEY_ECDSA_NISTP256 + || key->type == DROPBEAR_SIGNKEY_ECDSA_NISTP384 + || key->type == DROPBEAR_SIGNKEY_ECDSA_NISTP521) { + + /* SEC1 V2 appendix c.4 + ECPrivateKey ::= SEQUENCE { + version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1), + privateKey OCTET STRING, + parameters [0] ECDomainParameters {{ SECGCurveNames }} OPTIONAL, + publicKey [1] BIT STRING OPTIONAL } + */ + buffer *seq_buf = buf_new(400); + 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; + unsigned int k_size; - /* dmq1 = d mod (q-1) */ - if (mp_sub_d(key->rsakey->q, 1, &tmpval) != MP_OKAY) { - fprintf(stderr, "Bignum error for q-1\n"); - goto error; - } - if (mp_mod(key->rsakey->d, &tmpval, &dmq1) != MP_OKAY) { - fprintf(stderr, "Bignum error for dmq1\n"); - goto error; - } + /* version. less than 10 bytes */ + buf_incrwritepos(seq_buf, + ber_write_id_len(buf_getwriteptr(seq_buf, 10), 2, 1, 0)); + buf_putbyte(seq_buf, 1); + + /* privateKey */ + k_size = mp_unsigned_bin_size((*eck)->k); + dropbear_assert(k_size <= curve_size); + buf_incrwritepos(seq_buf, + ber_write_id_len(buf_getwriteptr(seq_buf, 10), 4, k_size, 0)); + mp_to_unsigned_bin((*eck)->k, buf_getwriteptr(seq_buf, k_size)); + buf_incrwritepos(seq_buf, k_size); - /* iqmp = (q^-1) mod p */ - if (mp_invmod(key->rsakey->q, key->rsakey->p, &iqmp) != MP_OKAY) { - fprintf(stderr, "Bignum error for iqmp\n"); - goto error; + /* SECGCurveNames */ + switch (key->type) + { + case DROPBEAR_SIGNKEY_ECDSA_NISTP256: + curve_oid_len = sizeof(OID_SEC256R1_BLOB); + curve_oid = OID_SEC256R1_BLOB; + break; + case DROPBEAR_SIGNKEY_ECDSA_NISTP384: + curve_oid_len = sizeof(OID_SEC384R1_BLOB); + curve_oid = OID_SEC384R1_BLOB; + break; + case DROPBEAR_SIGNKEY_ECDSA_NISTP521: + curve_oid_len = sizeof(OID_SEC521R1_BLOB); + curve_oid = OID_SEC521R1_BLOB; + break; + default: + dropbear_exit("Internal error"); } - extrablob = buf_new(2000); - buf_putmpint(extrablob, &dmp1); - buf_putmpint(extrablob, &dmq1); - buf_putmpint(extrablob, &iqmp); - buf_setpos(extrablob, 0); - mp_clear(&dmp1); - mp_clear(&dmq1); - mp_clear(&iqmp); - mp_clear(&tmpval); - - /* dmp1 */ - numbers[6].bytes = buf_getint(extrablob); - numbers[6].start = buf_getptr(extrablob, numbers[6].bytes); - buf_incrpos(extrablob, numbers[6].bytes); - - /* dmq1 */ - numbers[7].bytes = buf_getint(extrablob); - numbers[7].start = buf_getptr(extrablob, numbers[7].bytes); - buf_incrpos(extrablob, numbers[7].bytes); - - /* iqmp */ - numbers[8].bytes = buf_getint(extrablob); - numbers[8].start = buf_getptr(extrablob, numbers[8].bytes); - buf_incrpos(extrablob, numbers[8].bytes); + buf_incrwritepos(seq_buf, + ber_write_id_len(buf_getwriteptr(seq_buf, 10), 0, 2+curve_oid_len, 0xa0)); + /* object == 6 */ + buf_incrwritepos(seq_buf, + ber_write_id_len(buf_getwriteptr(seq_buf, 10), 6, curve_oid_len, 0)); + buf_putbytes(seq_buf, curve_oid, curve_oid_len); - nnumbers = 9; - header = "-----BEGIN RSA PRIVATE KEY-----\n"; - footer = "-----END RSA PRIVATE KEY-----\n"; - } -#endif /* DROPBEAR_RSA */ - -#ifdef DROPBEAR_DSS - if (keytype == DROPBEAR_SIGNKEY_DSS) { - - /* p */ - numbers[1].bytes = buf_getint(keyblob); - numbers[1].start = buf_getptr(keyblob, numbers[1].bytes); - buf_incrpos(keyblob, numbers[1].bytes); - - /* q */ - numbers[2].bytes = buf_getint(keyblob); - numbers[2].start = buf_getptr(keyblob, numbers[2].bytes); - buf_incrpos(keyblob, numbers[2].bytes); - - /* g */ - numbers[3].bytes = buf_getint(keyblob); - numbers[3].start = buf_getptr(keyblob, numbers[3].bytes); - buf_incrpos(keyblob, numbers[3].bytes); + buf_incrwritepos(seq_buf, + ber_write_id_len(buf_getwriteptr(seq_buf, 10), 1, 2+1+pubkey_size, 0xa0)); + buf_incrwritepos(seq_buf, + ber_write_id_len(buf_getwriteptr(seq_buf, 10), 3, 1+pubkey_size, 0)); + buf_putbyte(seq_buf, 0); + int err = ecc_ansi_x963_export(*eck, buf_getwriteptr(seq_buf, pubkey_size), &pubkey_size); + if (err != CRYPT_OK) { + dropbear_exit("ECC error"); + } + buf_incrwritepos(seq_buf, pubkey_size); - /* y */ - numbers[4].bytes = buf_getint(keyblob); - numbers[4].start = buf_getptr(keyblob, numbers[4].bytes); - buf_incrpos(keyblob, numbers[4].bytes); - - /* x */ - numbers[5].bytes = buf_getint(keyblob); - numbers[5].start = buf_getptr(keyblob, numbers[5].bytes); - buf_incrpos(keyblob, numbers[5].bytes); + buf_setpos(seq_buf, 0); + + outblob = (unsigned char*)m_malloc(1000); - nnumbers = 6; - header = "-----BEGIN DSA PRIVATE KEY-----\n"; - footer = "-----END DSA PRIVATE KEY-----\n"; - } -#endif /* DROPBEAR_DSS */ + pos = 0; + pos += ber_write_id_len(outblob+pos, 16, seq_buf->len, ASN1_CONSTRUCTED); + memcpy(&outblob[pos], seq_buf->data, seq_buf->len); + pos += seq_buf->len; + len = pos; + outlen = len; - /* - * Now count up the total size of the ASN.1 encoded integers, - * so as to determine the length of the containing SEQUENCE. - */ - len = 0; - for (i = 0; i < nnumbers; i++) { - len += ber_write_id_len(NULL, 2, numbers[i].bytes, 0); - len += numbers[i].bytes; + buf_burn(seq_buf); + buf_free(seq_buf); + seq_buf = NULL; + + header = "-----BEGIN EC PRIVATE KEY-----\n"; + footer = "-----END EC PRIVATE KEY-----\n"; } - seqlen = len; - /* Now add on the SEQUENCE header. */ - len += ber_write_id_len(NULL, 16, seqlen, ASN1_CONSTRUCTED); - /* Round up to the cipher block size, ensuring we have at least one - * byte of padding (see below). */ - outlen = len; - if (passphrase) - outlen = (outlen+8) &~ 7; - - /* - * Now we know how big outblob needs to be. Allocate it. - */ - outblob = (unsigned char*)m_malloc(outlen); - - /* - * And write the data into it. - */ - pos = 0; - pos += ber_write_id_len(outblob+pos, 16, seqlen, ASN1_CONSTRUCTED); - for (i = 0; i < nnumbers; i++) { - pos += ber_write_id_len(outblob+pos, 2, numbers[i].bytes, 0); - memcpy(outblob+pos, numbers[i].start, numbers[i].bytes); - pos += numbers[i].bytes; - } +#endif /* * Padding on OpenSSH keys is deterministic. The number of diff -r e4b75744acab -r 89555751c489 libtomcrypt/Makefile.in --- a/libtomcrypt/Makefile.in Sun Oct 06 22:32:03 2013 +0800 +++ b/libtomcrypt/Makefile.in Thu Feb 27 21:35:58 2014 +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 e4b75744acab -r 89555751c489 libtomcrypt/src/headers/tomcrypt.h --- a/libtomcrypt/src/headers/tomcrypt.h Sun Oct 06 22:32:03 2013 +0800 +++ b/libtomcrypt/src/headers/tomcrypt.h Thu Feb 27 21:35:58 2014 +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 e4b75744acab -r 89555751c489 libtomcrypt/src/headers/tomcrypt_custom.h --- a/libtomcrypt/src/headers/tomcrypt_custom.h Sun Oct 06 22:32:03 2013 +0800 +++ b/libtomcrypt/src/headers/tomcrypt_custom.h Thu Feb 27 21:35:58 2014 +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 e4b75744acab -r 89555751c489 libtomcrypt/src/headers/tomcrypt_math.h --- a/libtomcrypt/src/headers/tomcrypt_math.h Sun Oct 06 22:32:03 2013 +0800 +++ b/libtomcrypt/src/headers/tomcrypt_math.h Thu Feb 27 21:35:58 2014 +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 e4b75744acab -r 89555751c489 libtomcrypt/src/misc/crypt/crypt_ltc_mp_descriptor.c --- a/libtomcrypt/src/misc/crypt/crypt_ltc_mp_descriptor.c Sun Oct 06 22:32:03 2013 +0800 +++ b/libtomcrypt/src/misc/crypt/crypt_ltc_mp_descriptor.c Thu Feb 27 21:35:58 2014 +0800 @@ -10,4 +10,4 @@ */ #include "tomcrypt.h" -ltc_math_descriptor ltc_mp; +ltc_math_descriptor ltc_mp = {0}; diff -r e4b75744acab -r 89555751c489 libtomcrypt/src/pk/ecc/ecc_decrypt_key.c --- a/libtomcrypt/src/pk/ecc/ecc_decrypt_key.c Sun Oct 06 22:32:03 2013 +0800 +++ b/libtomcrypt/src/pk/ecc/ecc_decrypt_key.c Thu Feb 27 21:35:58 2014 +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 e4b75744acab -r 89555751c489 libtomcrypt/src/pk/ecc/ecc_encrypt_key.c --- a/libtomcrypt/src/pk/ecc/ecc_encrypt_key.c Sun Oct 06 22:32:03 2013 +0800 +++ b/libtomcrypt/src/pk/ecc/ecc_encrypt_key.c Thu Feb 27 21:35:58 2014 +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 e4b75744acab -r 89555751c489 libtomcrypt/src/pk/ecc/ecc_export.c --- a/libtomcrypt/src/pk/ecc/ecc_export.c Sun Oct 06 22:32:03 2013 +0800 +++ b/libtomcrypt/src/pk/ecc/ecc_export.c Thu Feb 27 21:35:58 2014 +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 e4b75744acab -r 89555751c489 libtomcrypt/src/pk/ecc/ecc_import.c --- a/libtomcrypt/src/pk/ecc/ecc_import.c Sun Oct 06 22:32:03 2013 +0800 +++ b/libtomcrypt/src/pk/ecc/ecc_import.c Thu Feb 27 21:35:58 2014 +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 e4b75744acab -r 89555751c489 libtomcrypt/src/pk/ecc/ecc_sign_hash.c --- a/libtomcrypt/src/pk/ecc/ecc_sign_hash.c Sun Oct 06 22:32:03 2013 +0800 +++ b/libtomcrypt/src/pk/ecc/ecc_sign_hash.c Thu Feb 27 21:35:58 2014 +0800 @@ -21,7 +21,7 @@ ECC Crypto, Tom St Denis */ -#ifdef MECC +#if defined(MECC) && defined(LTC_DER) /** Sign a message digest diff -r e4b75744acab -r 89555751c489 libtomcrypt/src/pk/ecc/ecc_verify_hash.c --- a/libtomcrypt/src/pk/ecc/ecc_verify_hash.c Sun Oct 06 22:32:03 2013 +0800 +++ b/libtomcrypt/src/pk/ecc/ecc_verify_hash.c Thu Feb 27 21:35:58 2014 +0800 @@ -21,7 +21,7 @@ ECC Crypto, Tom St Denis */ -#ifdef MECC +#if defined(MECC) && defined(LTC_DER) /* verify * diff -r e4b75744acab -r 89555751c489 loginrec.h --- a/loginrec.h Sun Oct 06 22:32:03 2013 +0800 +++ b/loginrec.h Thu Feb 27 21:35:58 2014 +0800 @@ -73,16 +73,16 @@ #else /* Simply select your favourite login types. */ /* Can't do if-else because some systems use several... */ -# if defined(UTMPX_FILE) && !defined(DISABLE_UTMPX) +# if defined(HAVE_UTMPX_H) && defined(UTMPX_FILE) && !defined(DISABLE_UTMPX) # define USE_UTMPX # endif -# if defined(UTMP_FILE) && !defined(DISABLE_UTMP) +# if defined(HAVE_UTMP_H) && defined(UTMP_FILE) && !defined(DISABLE_UTMP) # define USE_UTMP # endif -# if defined(WTMPX_FILE) && !defined(DISABLE_WTMPX) +# if defined(HAVE_WTMPX_H) && defined(WTMPX_FILE) && !defined(DISABLE_WTMPX) # define USE_WTMPX # endif -# if defined(WTMP_FILE) && !defined(DISABLE_WTMP) +# if defined(HAVE_WTMP_H) && defined(WTMP_FILE) && !defined(DISABLE_WTMP) # define USE_WTMP # endif diff -r e4b75744acab -r 89555751c489 ltc_prng.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ltc_prng.c Thu Feb 27 21:35:58 2014 +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 "dbrandom.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 e4b75744acab -r 89555751c489 ltc_prng.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ltc_prng.h Thu Feb 27 21:35:58 2014 +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 */ diff -r e4b75744acab -r 89555751c489 options.h --- a/options.h Sun Oct 06 22:32:03 2013 +0800 +++ b/options.h Thu Feb 27 21:35:58 2014 +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 @@ -126,7 +129,7 @@ /* 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 @@ -135,11 +138,26 @@ * SSH2 RFC Draft requires dss, recommends rsa */ #define DROPBEAR_RSA #define DROPBEAR_DSS +/* 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 @@ -153,7 +171,7 @@ #endif /* Whether to do reverse DNS lookups. */ -//#define DO_HOST_LOOKUP +/*#define DO_HOST_LOOKUP */ /* Whether to print the message of the day (MOTD). This doesn't add much code * size */ @@ -176,7 +194,7 @@ #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 @@ -204,12 +222,11 @@ * return the password on standard output */ /*#define ENABLE_CLI_ASKPASS_HELPER*/ -/* Send a real auth request first rather than requesting a list of available methods. - * It saves a network round trip at login but prevents immediate login to - * accounts with no password, and might be rejected by some strict servers (none - * encountered yet) - hence it isn't enabled by default. */ -/* #define CLI_IMMEDIATE_AUTH */ - +/* Save a network roundtrip by sendng a real auth request immediately after + * sending a query for the available methods. It is at the expense of < 100 + * bytes of extra network traffic. This is not yet enabled by default since it + * could cause problems with non-compliant servers */ +/* #define DROPBEAR_CLI_IMMEDIATE_AUTH */ /* Source for randomness. This must be able to provide hundreds of bytes per SSH * connection without blocking. In addition /dev/random is used for seeding diff -r e4b75744acab -r 89555751c489 packet.c --- a/packet.c Sun Oct 06 22:32:03 2013 +0800 +++ b/packet.c Thu Feb 27 21:35:58 2014 +0800 @@ -30,7 +30,7 @@ #include "algo.h" #include "buffer.h" #include "kex.h" -#include "random.h" +#include "dbrandom.h" #include "service.h" #include "auth.h" #include "channel.h" @@ -41,7 +41,11 @@ unsigned char *output_mac); static int checkmac(); -#define ZLIB_COMPRESS_INCR 100 +/* For exact details see http://www.zlib.net/zlib_tech.html + * 5 bytes per 16kB block, plus 6 bytes for the stream. + * We might allocate 5 unnecessary bytes here if it's an + * exact multiple. */ +#define ZLIB_COMPRESS_EXPANSION (((RECV_MAX_PAYLOAD_LEN/16384)+1)*5 + 6) #define ZLIB_DECOMPRESS_INCR 1024 #ifndef DISABLE_ZLIB static buffer* buf_decompress(buffer* buf, unsigned int len); @@ -98,7 +102,7 @@ writebuf = (buffer*)examine(&ses.writequeue); len = writebuf->len - 1 - writebuf->pos; if (len > written) { - // partial buffer write + /* partial buffer write */ buf_incrpos(writebuf, written); written = 0; } else { @@ -333,7 +337,7 @@ /* payload length */ /* - 4 - 1 is for LEN and PADLEN values */ len = ses.readbuf->len - padlen - 4 - 1 - macsize; - if ((len > RECV_MAX_PAYLOAD_LEN) || (len < 1)) { + if ((len > RECV_MAX_PAYLOAD_LEN+ZLIB_COMPRESS_EXPANSION) || (len < 1)) { dropbear_exit("Bad packet size %d", len); } @@ -422,6 +426,8 @@ if (zstream->avail_out == 0) { int new_size = 0; if (ret->size >= RECV_MAX_PAYLOAD_LEN) { + /* Already been increased as large as it can go, + * yet didn't finish up the decompression */ dropbear_exit("bad packet, oversized decompressed"); } new_size = MIN(RECV_MAX_PAYLOAD_LEN, ret->size + ZLIB_DECOMPRESS_INCR); @@ -526,7 +532,7 @@ + mac_size #ifndef DISABLE_ZLIB /* some extra in case 'compression' makes it larger */ - + ZLIB_COMPRESS_INCR + + ZLIB_COMPRESS_EXPANSION #endif /* and an extra cleartext (stripped before transmission) byte for the * packet type */ @@ -539,14 +545,7 @@ #ifndef DISABLE_ZLIB /* compression */ if (is_compress_trans()) { - int compress_delta; buf_compress(writebuf, ses.writepayload, ses.writepayload->len); - compress_delta = (writebuf->len - PACKET_PAYLOAD_OFF) - ses.writepayload->len; - - /* Handle the case where 'compress' increased the size. */ - if (compress_delta > ZLIB_COMPRESS_INCR) { - buf_resize(writebuf, writebuf->size + compress_delta); - } } else #endif { @@ -694,7 +693,7 @@ /* the buffer has been filled, we must extend. This only happens in * unusual circumstances where the data grows in size after deflate(), * but it is possible */ - buf_resize(dest, dest->size + ZLIB_COMPRESS_INCR); + buf_resize(dest, dest->size + ZLIB_COMPRESS_EXPANSION); } TRACE2(("leave buf_compress")) diff -r e4b75744acab -r 89555751c489 process-packet.c --- a/process-packet.c Sun Oct 06 22:32:03 2013 +0800 +++ b/process-packet.c Thu Feb 27 21:35:58 2014 +0800 @@ -30,7 +30,7 @@ #include "algo.h" #include "buffer.h" #include "kex.h" -#include "random.h" +#include "dbrandom.h" #include "service.h" #include "auth.h" #include "channel.h" @@ -74,15 +74,32 @@ /* This applies for KEX, where the spec says the next packet MUST be * NEWKEYS */ - if (ses.requirenext[0] != 0) { - if (ses.requirenext[0] != type - && (ses.requirenext[1] == 0 || ses.requirenext[1] != type)) { - dropbear_exit("Unexpected packet type %d, expected [%d,%d]", type, - ses.requirenext[0], ses.requirenext[1]); - } else { + if (ses.requirenext != 0) { + if (ses.requirenext == type) + { /* Got what we expected */ - ses.requirenext[0] = 0; - ses.requirenext[1] = 0; + TRACE(("got expected packet %d during kexinit", type)) + } + else + { + /* RFC4253 7.1 - various messages are allowed at this point. + The only ones we know about have already been handled though, + so just return "unimplemented" */ + if (type >= 1 && type <= 49 + && type != SSH_MSG_SERVICE_REQUEST + && type != SSH_MSG_SERVICE_ACCEPT + && type != SSH_MSG_KEXINIT) + { + TRACE(("unknown allowed packet during kexinit")) + recv_unimplemented(); + goto out; + } + else + { + TRACE(("disallowed packet during kexinit")) + dropbear_exit("Unexpected packet type %d, expected %d", type, + ses.requirenext); + } } } @@ -94,6 +111,12 @@ goto out; } + /* Only clear the flag after we have checked ignorenext */ + if (ses.requirenext != 0 && ses.requirenext == type) + { + ses.requirenext = 0; + } + /* Kindly the protocol authors gave all the preauth packets type values * less-than-or-equal-to 60 ( == MAX_UNAUTH_PACKET_TYPE ). diff -r e4b75744acab -r 89555751c489 random.c --- a/random.c Sun Oct 06 22:32:03 2013 +0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,308 +0,0 @@ -/* - * Dropbear - a SSH2 server - * - * Copyright (c) 2002,2003 Matt Johnston - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ - -#include "includes.h" -#include "buffer.h" -#include "dbutil.h" -#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 */ -#define MAX_COUNTER 1<<30 - -static unsigned char hashpool[SHA1_HASH_SIZE] = {0}; -static int donerandinit = 0; - -#define INIT_SEED_SIZE 32 /* 256 bits */ - -/* The basic setup is we read some data from /dev/(u)random or prngd and hash it - * into hashpool. To read data, we hash together current hashpool contents, - * and a counter. We feed more data in by hashing the current pool and new - * data into the pool. - * - * It is important to ensure that counter doesn't wrap around before we - * feed in new entropy. - * - */ - -/* Pass len=0 to hash an entire file */ -static int -process_file(hash_state *hs, const char *filename, - unsigned int len, int prngd) -{ - static int already_blocked = 0; - int readfd; - unsigned int readcount; - int ret = DROPBEAR_FAILURE; - -#ifdef DROPBEAR_PRNGD_SOCKET - if (prngd) - { - readfd = connect_unix(filename); - } - else -#endif - { - readfd = open(filename, O_RDONLY); - } - - if (readfd < 0) { - goto out; - } - - readcount = 0; - while (len == 0 || readcount < len) - { - int readlen, wantread; - unsigned char readbuf[4096]; - if (!already_blocked) - { - int ret; - struct timeval timeout = { .tv_sec = 2, .tv_usec = 0}; - fd_set read_fds; - - FD_ZERO(&read_fds); - FD_SET(readfd, &read_fds); - ret = select(readfd + 1, &read_fds, NULL, NULL, &timeout); - if (ret == 0) - { - dropbear_log(LOG_WARNING, "Warning: Reading the randomness source '%s' seems to have blocked.\nYou may need to find a better entropy source.", filename); - already_blocked = 1; - } - } - - if (len == 0) - { - wantread = sizeof(readbuf); - } - else - { - wantread = MIN(sizeof(readbuf), len-readcount); - } - -#ifdef DROPBEAR_PRNGD_SOCKET - if (prngd) - { - char egdcmd[2]; - egdcmd[0] = 0x02; /* blocking read */ - egdcmd[1] = (unsigned char)wantread; - if (write(readfd, egdcmd, 2) < 0) - { - dropbear_exit("Can't send command to egd"); - } - } -#endif - - readlen = read(readfd, readbuf, wantread); - if (readlen <= 0) { - if (readlen < 0 && errno == EINTR) { - continue; - } - if (readlen == 0 && len == 0) - { - /* whole file was read as requested */ - break; - } - goto out; - } - sha1_process(hs, readbuf, readlen); - readcount += readlen; - } - ret = DROPBEAR_SUCCESS; -out: - close(readfd); - return ret; -} - -void addrandom(char * buf, unsigned int len) -{ - hash_state hs; - - /* hash in the new seed data */ - sha1_init(&hs); - /* existing state (zeroes on startup) */ - sha1_process(&hs, (void*)hashpool, sizeof(hashpool)); - - /* new */ - sha1_process(&hs, buf, len); - sha1_done(&hs, hashpool); -} - -static void write_urandom() -{ -#ifndef DROPBEAR_PRNGD_SOCKET - /* This is opportunistic, don't worry about failure */ - unsigned char buf[INIT_SEED_SIZE]; - FILE *f = fopen(DROPBEAR_URANDOM_DEV, "w"); - if (!f) { - return; - } - genrandom(buf, sizeof(buf)); - fwrite(buf, sizeof(buf), 1, f); - fclose(f); -#endif -} - -/* Initialise the prng from /dev/urandom or prngd. This function can - * be called multiple times */ -void seedrandom() { - - hash_state hs; - - pid_t pid; - struct timeval tv; - clock_t clockval; - - /* hash in the new seed data */ - sha1_init(&hs); - /* existing state */ - sha1_process(&hs, (void*)hashpool, sizeof(hashpool)); - -#ifdef DROPBEAR_PRNGD_SOCKET - if (process_file(&hs, DROPBEAR_PRNGD_SOCKET, INIT_SEED_SIZE, 1) - != DROPBEAR_SUCCESS) { - dropbear_exit("Failure reading random device %s", - DROPBEAR_PRNGD_SOCKET); - } -#else - /* non-blocking random source (probably /dev/urandom) */ - if (process_file(&hs, DROPBEAR_URANDOM_DEV, INIT_SEED_SIZE, 0) - != DROPBEAR_SUCCESS) { - dropbear_exit("Failure reading random device %s", - DROPBEAR_URANDOM_DEV); - } -#endif - - /* A few other sources to fall back on. - * Add more here for other platforms */ -#ifdef __linux__ - /* Seems to be a reasonable source of entropy from timers. Possibly hard - * for even local attackers to reproduce */ - process_file(&hs, "/proc/timer_list", 0, 0); - /* Might help on systems with wireless */ - process_file(&hs, "/proc/interrupts", 0, 0); - - process_file(&hs, "/proc/loadavg", 0, 0); - process_file(&hs, "/proc/sys/kernel/random/entropy_avail", 0, 0); - - /* Mostly network visible but useful in some situations. - * Limit size to avoid slowdowns on systems with lots of routes */ - process_file(&hs, "/proc/net/netstat", 4096, 0); - process_file(&hs, "/proc/net/dev", 4096, 0); - process_file(&hs, "/proc/net/tcp", 4096, 0); - /* Also includes interface lo */ - process_file(&hs, "/proc/net/rt_cache", 4096, 0); - process_file(&hs, "/proc/vmstat", 0, 0); -#endif - - pid = getpid(); - sha1_process(&hs, (void*)&pid, sizeof(pid)); - - // gettimeofday() doesn't completely fill out struct timeval on - // OS X (10.8.3), avoid valgrind warnings by clearing it first - memset(&tv, 0x0, sizeof(tv)); - gettimeofday(&tv, NULL); - sha1_process(&hs, (void*)&tv, sizeof(tv)); - - clockval = clock(); - sha1_process(&hs, (void*)&clockval, sizeof(clockval)); - - /* When a private key is read by the client or server it will - * be added to the hashpool - see runopts.c */ - - sha1_done(&hs, hashpool); - - counter = 0; - donerandinit = 1; - - /* Feed it all back into /dev/urandom - this might help if Dropbear - * is running from inetd and gets new state each time */ - write_urandom(); -} - -/* return len bytes of pseudo-random data */ -void genrandom(unsigned char* buf, unsigned int len) { - - hash_state hs; - unsigned char hash[SHA1_HASH_SIZE]; - unsigned int copylen; - - if (!donerandinit) { - dropbear_exit("seedrandom not done"); - } - - while (len > 0) { - sha1_init(&hs); - sha1_process(&hs, (void*)hashpool, sizeof(hashpool)); - sha1_process(&hs, (void*)&counter, sizeof(counter)); - sha1_done(&hs, hash); - - counter++; - if (counter > MAX_COUNTER) { - seedrandom(); - } - - copylen = MIN(len, SHA1_HASH_SIZE); - memcpy(buf, hash, copylen); - len -= copylen; - buf += copylen; - } - m_burn(hash, sizeof(hash)); -} - -/* Generates a random mp_int. - * max is a *mp_int specifying an upper bound. - * rand must be an initialised *mp_int for the result. - * the result rand satisfies: 0 < rand < max - * */ -void gen_random_mpint(mp_int *max, mp_int *rand) { - - unsigned char *randbuf = NULL; - unsigned int len = 0; - const unsigned char masks[] = {0xff, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f}; - - const int size_bits = mp_count_bits(max); - - len = size_bits / 8; - if ((size_bits % 8) != 0) { - len += 1; - } - - randbuf = (unsigned char*)m_malloc(len); - do { - genrandom(randbuf, len); - /* Mask out the unrequired bits - mp_read_unsigned_bin expects - * MSB first.*/ - randbuf[0] &= masks[size_bits % 8]; - - bytes_to_mp(rand, randbuf, len); - - /* keep regenerating until we get one satisfying - * 0 < rand < max */ - } while (mp_cmp(rand, max) != MP_LT); - m_burn(randbuf, len); - m_free(randbuf); -} diff -r e4b75744acab -r 89555751c489 random.h --- a/random.h Sun Oct 06 22:32:03 2013 +0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,35 +0,0 @@ -/* - * Dropbear - a SSH2 server - * - * Copyright (c) 2002,2003 Matt Johnston - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ - -#ifndef _RANDOM_H_ -#define _RANDOM_H_ - -struct mp_int; - -void seedrandom(); -void genrandom(unsigned char* buf, unsigned int len); -void addrandom(char * buf, unsigned int len); -void gen_random_mpint(mp_int *max, mp_int *rand); - -#endif /* _RANDOM_H_ */ diff -r e4b75744acab -r 89555751c489 rsa.c --- a/rsa.c Sun Oct 06 22:32:03 2013 +0800 +++ b/rsa.c Thu Feb 27 21:35:58 2014 +0800 @@ -34,13 +34,12 @@ #include "rsa.h" #include "buffer.h" #include "ssh.h" -#include "random.h" +#include "dbrandom.h" #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 */ @@ -357,7 +347,9 @@ mp_clear(&rsa_s); #if defined(DEBUG_RSA) && defined(DEBUG_TRACE) - printhex("RSA sig", buf->data, buf->len); + if (!debug_trace) { + printhex("RSA sig", buf->data, buf->len); + } #endif @@ -377,8 +369,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 +382,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 +398,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 e4b75744acab -r 89555751c489 rsa.h --- a/rsa.h Sun Oct 06 22:32:03 2013 +0800 +++ b/rsa.h Thu Feb 27 21:35:58 2014 +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 e4b75744acab -r 89555751c489 runopts.h --- a/runopts.h Sun Oct 06 22:32:03 2013 +0800 +++ b/runopts.h Thu Feb 27 21:35:58 2014 +0800 @@ -56,12 +56,12 @@ extern runopts opts; -int readhostkey(const char * filename, sign_key * hostkey, int *type); +int readhostkey(const char * filename, sign_key * hostkey, + enum signkey_type *type); +void load_all_hostkeys(); typedef struct svr_runopts { - char * rsakeyfile; - char * dsskeyfile; char * bannerfile; int forkbg; @@ -99,6 +99,12 @@ #endif sign_key *hostkey; + + int delay_hostkey; + + char *hostkey_files[MAX_HOSTKEYS]; + int num_hostkey_files; + buffer * banner; char * pidfile; diff -r e4b75744acab -r 89555751c489 scp.c --- a/scp.c Sun Oct 06 22:32:03 2013 +0800 +++ b/scp.c Thu Feb 27 21:35:58 2014 +0800 @@ -494,8 +494,8 @@ if (verbose_mode) addargs(&alist, "-v"); #if 0 - // Disabled since dbclient won't understand them - // and scp works fine without them. + /* Disabled since dbclient won't understand them + and scp works fine without them. */ addargs(&alist, "-x"); addargs(&alist, "-oClearAllForwardings yes"); addargs(&alist, "-n"); diff -r e4b75744acab -r 89555751c489 session.h --- a/session.h Sun Oct 06 22:32:03 2013 +0800 +++ b/session.h Thu Feb 27 21:35:58 2014 +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) */ @@ -135,9 +135,8 @@ unsigned dataallowed : 1; /* whether we can send data packets or we are in the middle of a KEX or something */ - unsigned char requirenext[2]; /* bytes indicating what packets we require next, - or 0x00 for any. Second option can only be - used if the first byte is also set */ + unsigned char requirenext; /* byte indicating what packets we require next, + or 0x00 for any. */ unsigned char ignorenext; /* whether to ignore the next packet, used for kex_follows stuff */ @@ -158,10 +157,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 */ @@ -233,6 +232,7 @@ typedef enum { STATE_NOTHING, + USERAUTH_WAIT, USERAUTH_REQ_SENT, USERAUTH_FAIL_RCVD, USERAUTH_SUCCESS_RCVD, @@ -241,8 +241,12 @@ 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; + 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 */ cli_state state; /* Used to progress auth/channelsession etc */ unsigned donefirstkex : 1; /* Set when we set sentnewkeys, never reset */ @@ -263,6 +267,7 @@ int lastauthtype; /* either AUTH_TYPE_PUBKEY or AUTH_TYPE_PASSWORD, for the last type of auth we tried */ + int ignore_next_auth_response; #ifdef ENABLE_CLI_INTERACT_AUTH int auth_interact_failed; /* flag whether interactive auth can still be used */ diff -r e4b75744acab -r 89555751c489 signkey.c --- a/signkey.c Sun Oct 06 22:32:03 2013 +0800 +++ b/signkey.c Thu Feb 27 21:35:58 2014 +0800 @@ -27,6 +27,21 @@ #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" +#endif /* DROPBEAR_ECDSA */ +}; /* malloc a new sign_key and set the dss and rsa keys to NULL */ sign_key * new_sign_key() { @@ -34,64 +49,92 @@ 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; } +/* Returns a pointer to the key part specific to "type" */ +void ** +signkey_key_ptr(sign_key *key, enum signkey_type type) { + switch (type) { +#ifdef DROPBEAR_ECC_256 + case DROPBEAR_SIGNKEY_ECDSA_NISTP256: + return (void**)&key->ecckey256; +#endif +#ifdef DROPBEAR_ECC_384 + case DROPBEAR_SIGNKEY_ECDSA_NISTP384: + return (void**)&key->ecckey384; +#endif +#ifdef DROPBEAR_ECC_521 + case DROPBEAR_SIGNKEY_ECDSA_NISTP521: + return (void**)&key->ecckey521; +#endif +#ifdef DROPBEAR_RSA + case DROPBEAR_SIGNKEY_RSA: + return (void**)&key->rsakey; +#endif +#ifdef DROPBEAR_DSS + case DROPBEAR_SIGNKEY_DSS: + return (void**)&key->dsskey; +#endif + default: + return NULL; + } +} + /* 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) */ -int buf_get_pub_key(buffer *buf, sign_key *key, int *type) { +int buf_get_pub_key(buffer *buf, sign_key *key, enum signkey_type *type) { unsigned char* ident; unsigned int len; @@ -136,6 +179,21 @@ } } #endif +#ifdef DROPBEAR_ECDSA + if (signkey_is_ecdsa(keytype)) { + ecc_key **eck = (ecc_key**)signkey_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")) @@ -146,7 +204,7 @@ /* 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) */ -int buf_get_priv_key(buffer *buf, sign_key *key, int *type) { +int buf_get_priv_key(buffer *buf, sign_key *key, enum signkey_type *type) { unsigned char* ident; unsigned int len; @@ -189,6 +247,21 @@ } } #endif +#ifdef DROPBEAR_ECDSA + if (signkey_is_ecdsa(keytype)) { + ecc_key **eck = (ecc_key**)signkey_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")) @@ -197,7 +270,7 @@ } /* type is either DROPBEAR_SIGNKEY_DSS or DROPBEAR_SIGNKEY_RSA */ -void buf_put_pub_key(buffer* buf, sign_key *key, int type) { +void buf_put_pub_key(buffer* buf, sign_key *key, enum signkey_type type) { buffer *pubkeys; @@ -214,20 +287,25 @@ buf_put_rsa_pub_key(pubkeys, key->rsakey); } #endif +#ifdef DROPBEAR_ECDSA + if (signkey_is_ecdsa(type)) { + ecc_key **eck = (ecc_key**)signkey_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")) } /* type is either DROPBEAR_SIGNKEY_DSS or DROPBEAR_SIGNKEY_RSA */ -void buf_put_priv_key(buffer* buf, sign_key *key, int type) { +void buf_put_priv_key(buffer* buf, sign_key *key, enum signkey_type type) { TRACE(("enter buf_put_priv_key")) TRACE(("type is %d", type)) @@ -246,6 +324,16 @@ return; } #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); + TRACE(("leave buf_put_priv_key: ecdsa done")) + return; + } + } +#endif dropbear_exit("Bad key types in put pub key"); } @@ -261,6 +349,26 @@ rsa_key_free(key->rsakey); 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); @@ -269,7 +377,6 @@ } static char hexdig(unsigned char x) { - if (x > 0xf) return 'X'; @@ -333,14 +440,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] = ':'; @@ -363,29 +470,33 @@ #endif } -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, enum signkey_type type, + 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 + 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); + } } #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 +506,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; + enum signkey_type type; TRACE(("enter buf_verify")) - bloblen = buf_getint(buf); - ident = buf_getstring(buf, &identlen); + buf_getint(buf); /* blob length */ + type_name = buf_getstring(buf, &type_name_len); + 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 + 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); + } } #endif - m_free(ident); dropbear_exit("Non-matching signing type"); return DROPBEAR_FAILURE; } diff -r e4b75744acab -r 89555751c489 signkey.h --- a/signkey.h Sun Oct 06 22:32:03 2013 +0800 +++ b/signkey.h Thu Feb 27 21:35:58 2014 +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, +#endif /* DROPBEAR_ECDSA */ + DROPBEAR_SIGNKEY_NUM_NAMED, + DROPBEAR_SIGNKEY_ECDSA_KEYGEN = 70, /* just "ecdsa" for keygen */ + 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,38 @@ #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); -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); +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, enum signkey_type *type); +int buf_get_priv_key(buffer* buf, sign_key *key, enum signkey_type *type); +void buf_put_pub_key(buffer* buf, sign_key *key, enum signkey_type type); +void buf_put_priv_key(buffer* buf, sign_key *key, enum signkey_type 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, enum signkey_type 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); +void** signkey_key_ptr(sign_key *key, enum signkey_type type); + #endif /* _SIGNKEY_H_ */ diff -r e4b75744acab -r 89555751c489 svr-agentfwd.c --- a/svr-agentfwd.c Sun Oct 06 22:32:03 2013 +0800 +++ b/svr-agentfwd.c Thu Feb 27 21:35:58 2014 +0800 @@ -37,7 +37,7 @@ #include "channel.h" #include "packet.h" #include "buffer.h" -#include "random.h" +#include "dbrandom.h" #include "listener.h" #include "auth.h" diff -r e4b75744acab -r 89555751c489 svr-auth.c --- a/svr-auth.c Sun Oct 06 22:32:03 2013 +0800 +++ b/svr-auth.c Thu Feb 27 21:35:58 2014 +0800 @@ -33,7 +33,7 @@ #include "packet.h" #include "auth.h" #include "runopts.h" -#include "random.h" +#include "dbrandom.h" static void authclear(); static int checkusername(unsigned char *username, unsigned int userlen); @@ -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(); @@ -232,7 +231,7 @@ char* listshell = NULL; char* usershell = NULL; - int uid; + uid_t uid; TRACE(("enter checkusername")) if (userlen > MAX_USERNAME_LEN) { return DROPBEAR_FAILURE; @@ -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 e4b75744acab -r 89555751c489 svr-authpubkey.c --- a/svr-authpubkey.c Sun Oct 06 22:32:03 2013 +0800 +++ b/svr-authpubkey.c Thu Feb 27 21:35:58 2014 +0800 @@ -89,7 +89,7 @@ buffer * signbuf = NULL; sign_key * key = NULL; char* fp = NULL; - int type = -1; + enum signkey_type type = -1; TRACE(("enter pubkeyauth")) @@ -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); @@ -295,8 +294,8 @@ 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) { diff -r e4b75744acab -r 89555751c489 svr-chansession.c --- a/svr-chansession.c Sun Oct 06 22:32:03 2013 +0800 +++ b/svr-chansession.c Thu Feb 27 21:35:58 2014 +0800 @@ -32,7 +32,7 @@ #include "sshpty.h" #include "termcodes.h" #include "ssh.h" -#include "random.h" +#include "dbrandom.h" #include "x11fwd.h" #include "agentfwd.h" #include "runopts.h" @@ -87,6 +87,8 @@ struct sigaction sa_chld; struct exitinfo *exit = NULL; + const int saved_errno = errno; + TRACE(("enter sigchld handler")) while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { TRACE(("sigchld handler: pid %d", pid)) @@ -140,6 +142,8 @@ sigemptyset(&sa_chld.sa_mask); sigaction(SIGCHLD, &sa_chld, NULL); TRACE(("leave sigchld handler")) + + errno = saved_errno; } /* send the exit status or the signal causing termination for a session */ @@ -664,6 +668,7 @@ if (chansess->term == NULL) { /* no pty */ + set_sock_priority(ses.sock_out, DROPBEAR_PRIO_BULK); ret = noptycommand(channel, chansess); } else { /* want pty */ diff -r e4b75744acab -r 89555751c489 svr-kex.c --- a/svr-kex.c Sun Oct 06 22:32:03 2013 +0800 +++ b/svr-kex.c Thu Feb 27 21:35:58 2014 +0800 @@ -32,11 +32,12 @@ #include "ssh.h" #include "packet.h" #include "bignum.h" -#include "random.h" +#include "dbrandom.h" #include "runopts.h" +#include "ecc.h" +#include "gensignkey.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,59 +46,192 @@ 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"); + 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); + + mp_clear(&dh_e); + if (ecdh_qs) { + buf_free(ecdh_qs); + ecdh_qs = NULL; + } + + send_msg_newkeys(); + ses.requirenext = SSH_MSG_NEWKEYS; + 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; } - send_msg_kexdh_reply(&dh_e); + 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; + } - mp_clear(&dh_e); + 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; + } - send_msg_newkeys(); - ses.requirenext[0] = SSH_MSG_NEWKEYS; - ses.requirenext[1] = 0; - TRACE(("leave recv_msg_kexdh_init")) + 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); + + if (ret == DROPBEAR_SUCCESS) { + char *fp = NULL; + unsigned int len; + buffer *key_buf = buf_new(MAX_PUBKEY_SIZE); + buf_put_pub_key(key_buf, svr_opts.hostkey, type); + buf_setpos(key_buf, 4); + len = key_buf->len - key_buf->pos; + fp = sign_key_fingerprint(buf_getptr(key_buf, len), len); + dropbear_log(LOG_INFO, "Generated hostkey %s, fingerprint is %s", + fn, fp); + m_free(fp); + buf_free(key_buf); + } + +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 * 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(); + +#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); - /* put f */ - buf_putmpint(ses.writepayload, &dh_f); - mp_clear(&dh_f); + 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); + + 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 */ 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 e4b75744acab -r 89555751c489 svr-main.c --- a/svr-main.c Sun Oct 06 22:32:03 2013 +0800 +++ b/svr-main.c Thu Feb 27 21:35:58 2014 +0800 @@ -28,7 +28,8 @@ #include "buffer.h" #include "signkey.h" #include "runopts.h" -#include "random.h" +#include "dbrandom.h" +#include "crypto_desc.h" static size_t listensockets(int *sock, size_t sockcount, int *maxfd); static void sigchld_handler(int dummy); @@ -136,6 +137,11 @@ dropbear_exit("No listening ports available."); } + for (i = 0; i < listensockcount; i++) { + set_sock_priority(listensocks[i], DROPBEAR_PRIO_LOWDELAY); + FD_SET(listensocks[i], &fds); + } + /* fork */ if (svr_opts.forkbg) { int closefds = 0; @@ -331,6 +337,8 @@ static void sigchld_handler(int UNUSED(unused)) { struct sigaction sa_chld; + const int saved_errno = errno; + while(waitpid(-1, NULL, WNOHANG) > 0); sa_chld.sa_handler = sigchld_handler; @@ -338,13 +346,14 @@ if (sigaction(SIGCHLD, &sa_chld, NULL) < 0) { dropbear_exit("signal() error"); } + errno = saved_errno; } /* catch any segvs */ static void sigsegv_handler(int UNUSED(unused)) { fprintf(stderr, "Aiee, segfault! You should probably report " "this as a bug to the developer\n"); - exit(EXIT_FAILURE); + _exit(EXIT_FAILURE); } /* catch ctrl-c or sigterm */ @@ -383,9 +392,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 e4b75744acab -r 89555751c489 svr-runopts.c --- a/svr-runopts.c Sun Oct 06 22:32:03 2013 +0800 +++ b/svr-runopts.c Thu Feb 27 21:35:58 2014 +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) { @@ -41,13 +44,19 @@ "-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 @@ -93,6 +102,9 @@ #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); } @@ -105,10 +117,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; @@ -119,6 +131,7 @@ 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; @@ -160,6 +173,11 @@ dropbear_exit("Invalid null argument"); } next = 0x00; + + if (keyfile) { + addhostkey(keyfile); + keyfile = NULL; + } continue; } @@ -168,16 +186,13 @@ 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; + case 'R': + svr_opts.delay_hostkey = 1; break; -#endif case 'F': svr_opts.forkbg = 0; break; @@ -267,13 +282,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 +300,6 @@ svr_opts.bannerfile); } buf_setpos(svr_opts.banner, 0); - } if (recv_window_arg) { @@ -370,55 +377,156 @@ } } -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(); + 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", (void**)&read_key->rsakey, (void**)&svr_opts.hostkey->rsakey, fatal_duplicate); + } +#endif + +#ifdef DROPBEAR_DSS + if (type == DROPBEAR_SIGNKEY_DSS) { + loadhostkey_helper("DSS", (void**)&read_key->dsskey, (void**)&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", (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", (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", (void**)&read_key->ecckey521, (void**)&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; + int disable_unset_keys = 1; + int any_keys = 0; 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_DELAY_HOSTKEY + if (svr_opts.delay_hostkey) { + disable_unset_keys = 0; } #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); + +#ifdef DROPBEAR_RSA + if (disable_unset_keys && !svr_opts.hostkey->rsakey) { + disablekey(DROPBEAR_SIGNKEY_RSA); + } else { + any_keys = 1; } #endif - if ( 1 #ifdef DROPBEAR_DSS - && svr_opts.hostkey->dsskey == NULL + if (disable_unset_keys && !svr_opts.hostkey->dsskey) { + disablekey(DROPBEAR_SIGNKEY_DSS); + } else { + any_keys = 1; + } +#endif + + +#ifdef DROPBEAR_ECDSA +#ifdef DROPBEAR_ECC_256 + if ((disable_unset_keys || ECDSA_DEFAULT_SIZE != 256) + && !svr_opts.hostkey->ecckey256) { + disablekey(DROPBEAR_SIGNKEY_ECDSA_NISTP256); + } else { + any_keys = 1; + } #endif -#ifdef DROPBEAR_RSA - && svr_opts.hostkey->rsakey == NULL + +#ifdef DROPBEAR_ECC_384 + if ((disable_unset_keys || ECDSA_DEFAULT_SIZE != 384) + && !svr_opts.hostkey->ecckey384) { + disablekey(DROPBEAR_SIGNKEY_ECDSA_NISTP384); + } else { + any_keys = 1; + } #endif - ) { + +#ifdef DROPBEAR_ECC_521 + if ((disable_unset_keys || ECDSA_DEFAULT_SIZE != 521) + && !svr_opts.hostkey->ecckey521) { + disablekey(DROPBEAR_SIGNKEY_ECDSA_NISTP521); + } else { + any_keys = 1; + } +#endif +#endif /* DROPBEAR_ECDSA */ + + if (!any_keys) { dropbear_exit("No hostkeys available"); } - - TRACE(("leave loadhostkeys")) } diff -r e4b75744acab -r 89555751c489 svr-session.c --- a/svr-session.c Sun Oct 06 22:32:03 2013 +0800 +++ b/svr-session.c Thu Feb 27 21:35:58 2014 +0800 @@ -30,7 +30,7 @@ #include "buffer.h" #include "dss.h" #include "ssh.h" -#include "random.h" +#include "dbrandom.h" #include "kex.h" #include "channel.h" #include "chansession.h" @@ -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 */ @@ -142,7 +142,7 @@ if (!sessinitdone) { /* before session init */ snprintf(fmtbuf, sizeof(fmtbuf), - "Premature exit: %s", format); + "Early exit: %s", format); } else if (ses.authstate.authdone) { /* user has authenticated */ snprintf(fmtbuf, sizeof(fmtbuf), diff -r e4b75744acab -r 89555751c489 svr-tcpfwd.c --- a/svr-tcpfwd.c Sun Oct 06 22:32:03 2013 +0800 +++ b/svr-tcpfwd.c Thu Feb 27 21:35:58 2014 +0800 @@ -211,7 +211,7 @@ tcpinfo->request_listenaddr = request_addr; if (!opts.listen_fwd_all || (strcmp(request_addr, "localhost") == 0) ) { - // NULL means "localhost only" + /* NULL means "localhost only" */ tcpinfo->listenaddr = NULL; } else diff -r e4b75744acab -r 89555751c489 sysoptions.h --- a/sysoptions.h Sun Oct 06 22:32:03 2013 +0800 +++ b/sysoptions.h Thu Feb 27 21:35:58 2014 +0800 @@ -4,7 +4,7 @@ *******************************************************************/ #ifndef DROPBEAR_VERSION -#define DROPBEAR_VERSION "2013.59" +#define DROPBEAR_VERSION "2014.63" #endif #define LOCAL_IDENT "SSH-2.0-dropbear_" DROPBEAR_VERSION @@ -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 @@ -90,10 +76,10 @@ #define SHA1_HASH_SIZE 20 #define MD5_HASH_SIZE 16 +#define MAX_HASH_SIZE 64 /* sha512 */ #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 +89,47 @@ #define MAX_MAC_LEN 20 #endif +#if defined(DROPBEAR_ECDH) || defined (DROPBEAR_ECDSA) +#define DROPBEAR_ECC +/* Debian doesn't define this in system headers */ +#ifndef LTM_DESC +#define LTM_DESC +#endif +#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 + +/* 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 +/* 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 */ +#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 +161,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) @@ -147,6 +176,7 @@ accept for keyb-interactive auth */ + #if defined(DROPBEAR_AES256) || defined(DROPBEAR_AES128) #define DROPBEAR_AES #endif @@ -155,19 +185,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 diff -r e4b75744acab -r 89555751c489 tcpfwd.h --- a/tcpfwd.h Sun Oct 06 22:32:03 2013 +0800 +++ b/tcpfwd.h Thu Feb 27 21:35:58 2014 +0800 @@ -40,7 +40,7 @@ unsigned char *listenaddr; unsigned int listenport; /* The address that the remote host asked to listen on */ - unsigned char *request_listenaddr;; + unsigned char *request_listenaddr; const struct ChanType *chantype; enum {direct, forwarded} tcp_type;