diff --git a/package/network/services/dropbear/Makefile b/package/network/services/dropbear/Makefile index 8de0739d56..8031a0c62a 100644 --- a/package/network/services/dropbear/Makefile +++ b/package/network/services/dropbear/Makefile @@ -8,14 +8,14 @@ include $(TOPDIR)/rules.mk PKG_NAME:=dropbear -PKG_VERSION:=2019.78 -PKG_RELEASE:=5 +PKG_VERSION:=2020.79 +PKG_RELEASE:=1 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.bz2 PKG_SOURCE_URL:= \ http://matt.ucc.asn.au/dropbear/releases/ \ https://dropbear.nl/mirror/releases/ -PKG_HASH:=525965971272270995364a0eb01f35180d793182e63dd0b0c3eb0292291644a4 +PKG_HASH:=084f00546b1610a3422a0773e2c04cbe1a220d984209e033b548b49f379cc441 PKG_LICENSE:=MIT PKG_LICENSE_FILES:=LICENSE libtomcrypt/LICENSE libtommath/LICENSE @@ -124,8 +124,7 @@ define Build/Configure # disable legacy/unsafe methods and unused functionality for OPTION in INETD_MODE DROPBEAR_CLI_NETCAT \ - DROPBEAR_3DES DROPBEAR_DSS DROPBEAR_ENABLE_CBC_MODE \ - DROPBEAR_SHA1_96_HMAC DROPBEAR_USE_PASSWORD_ENV; do \ + DROPBEAR_DSS DROPBEAR_USE_PASSWORD_ENV DO_MOTD ; do \ echo "#define $$$$OPTION 0" >> \ $(PKG_BUILD_DIR)/localoptions.h; \ done diff --git a/package/network/services/dropbear/patches/010-backport-change-address-logging.patch b/package/network/services/dropbear/patches/010-backport-change-address-logging.patch deleted file mode 100644 index 2b99f81ad5..0000000000 --- a/package/network/services/dropbear/patches/010-backport-change-address-logging.patch +++ /dev/null @@ -1,119 +0,0 @@ -From c153b3612b7c9f24a0f5af43618a646545ed6e22 Mon Sep 17 00:00:00 2001 -From: Kevin Darbyshire-Bryant -Date: Mon, 30 Sep 2019 12:42:13 +0100 -Subject: [PATCH] Improve address logging on early exit messages - -Change 'Early exit' and 'Exit before auth' messages to include the IP -address & port as part of the message. - -This allows log scanning utilities such as 'fail2ban' to obtain the -offending IP address as part of the failure event instead of extracting -the PID from the message and then scanning the log again for match -'child connection from' messages - -Signed-off-by: Kevin Darbyshire-Bryant ---- - svr-auth.c | 18 +++++++----------- - svr-session.c | 20 ++++++++++++++------ - 2 files changed, 21 insertions(+), 17 deletions(-) - ---- a/svr-auth.c -+++ b/svr-auth.c -@@ -241,8 +241,7 @@ static int checkusername(const char *use - } - - if (strlen(username) != userlen) { -- dropbear_exit("Attempted username with a null byte from %s", -- svr_ses.addrstring); -+ dropbear_exit("Attempted username with a null byte"); - } - - if (ses.authstate.username == NULL) { -@@ -252,8 +251,7 @@ static int checkusername(const char *use - } else { - /* check username hasn't changed */ - if (strcmp(username, ses.authstate.username) != 0) { -- dropbear_exit("Client trying multiple usernames from %s", -- svr_ses.addrstring); -+ dropbear_exit("Client trying multiple usernames"); - } - } - -@@ -268,8 +266,7 @@ static int checkusername(const char *use - if (!ses.authstate.pw_name) { - TRACE(("leave checkusername: user '%s' doesn't exist", username)) - dropbear_log(LOG_WARNING, -- "Login attempt for nonexistent user from %s", -- svr_ses.addrstring); -+ "Login attempt for nonexistent user"); - ses.authstate.checkusername_failed = 1; - return DROPBEAR_FAILURE; - } -@@ -279,9 +276,8 @@ static int checkusername(const char *use - if (!(DROPBEAR_SVR_MULTIUSER && uid == 0) && uid != ses.authstate.pw_uid) { - TRACE(("running as nonroot, only server uid is allowed")) - dropbear_log(LOG_WARNING, -- "Login attempt with wrong user %s from %s", -- ses.authstate.pw_name, -- svr_ses.addrstring); -+ "Login attempt with wrong user %s", -+ ses.authstate.pw_name); - ses.authstate.checkusername_failed = 1; - return DROPBEAR_FAILURE; - } -@@ -440,8 +436,8 @@ void send_msg_userauth_failure(int parti - } else { - userstr = ses.authstate.pw_name; - } -- dropbear_exit("Max auth tries reached - user '%s' from %s", -- userstr, svr_ses.addrstring); -+ dropbear_exit("Max auth tries reached - user '%s'", -+ userstr); - } - - TRACE(("leave send_msg_userauth_failure")) ---- a/svr-session.c -+++ b/svr-session.c -@@ -149,28 +149,36 @@ void svr_session(int sock, int childpipe - void svr_dropbear_exit(int exitcode, const char* format, va_list param) { - char exitmsg[150]; - char fullmsg[300]; -+ char fromaddr[60]; - int i; - - /* Render the formatted exit message */ - vsnprintf(exitmsg, sizeof(exitmsg), format, param); - -+ /* svr_ses.addrstring may not be set for some early exits, or for -+ the listener process */ -+ fromaddr[0] = '\0'; -+ if (svr_ses.addrstring) { -+ snprintf(fromaddr, sizeof(fromaddr), " from <%s>", svr_ses.addrstring); -+ } -+ - /* Add the prefix depending on session/auth state */ - if (!ses.init_done) { - /* before session init */ -- snprintf(fullmsg, sizeof(fullmsg), "Early exit: %s", exitmsg); -+ snprintf(fullmsg, sizeof(fullmsg), "Early exit%s: %s", fromaddr, exitmsg); - } else if (ses.authstate.authdone) { - /* user has authenticated */ - snprintf(fullmsg, sizeof(fullmsg), -- "Exit (%s): %s", -- ses.authstate.pw_name, exitmsg); -+ "Exit (%s)%s: %s", -+ ses.authstate.pw_name, fromaddr, exitmsg); - } else if (ses.authstate.pw_name) { - /* we have a potential user */ - snprintf(fullmsg, sizeof(fullmsg), -- "Exit before auth (user '%s', %u fails): %s", -- ses.authstate.pw_name, ses.authstate.failcount, exitmsg); -+ "Exit before auth%s: (user '%s', %u fails): %s", -+ fromaddr, ses.authstate.pw_name, ses.authstate.failcount, exitmsg); - } else { - /* before userauth */ -- snprintf(fullmsg, sizeof(fullmsg), "Exit before auth: %s", exitmsg); -+ snprintf(fullmsg, sizeof(fullmsg), "Exit before auth%s: %s", fromaddr, exitmsg); - } - - dropbear_log(LOG_INFO, "%s", fullmsg); diff --git a/package/network/services/dropbear/patches/010-backport-disable-toom-and-karatsuba.patch b/package/network/services/dropbear/patches/010-backport-disable-toom-and-karatsuba.patch new file mode 100644 index 0000000000..9af291eb01 --- /dev/null +++ b/package/network/services/dropbear/patches/010-backport-disable-toom-and-karatsuba.patch @@ -0,0 +1,17 @@ +From: Matt Johnston +Date: Thu, 18 Jun 2020 19:12:07 +0800 +Subject: Disable toom and karatsuba for new libtommath + +--- a/libtommath/tommath_class.h ++++ b/libtommath/tommath_class.h +@@ -1312,6 +1312,10 @@ + #undef BN_MP_KARATSUBA_SQR_C + #undef BN_MP_TOOM_MUL_C + #undef BN_MP_TOOM_SQR_C ++#undef BN_S_MP_KARATSUBA_MUL_C ++#undef BN_S_MP_KARATSUBA_SQR_C ++#undef BN_S_MP_TOOM_MUL_C ++#undef BN_S_MP_TOOM_SQR_C + + #include "dbmalloc.h" + #define MP_MALLOC m_malloc diff --git a/package/network/services/dropbear/patches/020-backport-ed25519-support.patch b/package/network/services/dropbear/patches/020-backport-ed25519-support.patch deleted file mode 100644 index 00a3bbbee1..0000000000 --- a/package/network/services/dropbear/patches/020-backport-ed25519-support.patch +++ /dev/null @@ -1,2890 +0,0 @@ -From 3d12521735e7ef7e48be217af0f27d68e23050a7 Mon Sep 17 00:00:00 2001 -From: Vladislav Grishenko -Date: Wed, 11 Mar 2020 21:09:45 +0500 -Subject: [PATCH] Add Ed25519 support (#91) - -* Add support for Ed25519 as a public key type - -Ed25519 is a elliptic curve signature scheme that offers -better security than ECDSA and DSA and good performance. It may be -used for both user and host keys. - -OpenSSH key import and fuzzer are not supported yet. - -Initially inspired by Peter Szabo. - -* Add curve25519 and ed25519 fuzzers - -* Add import and export of Ed25519 keys ---- - .travis.yml | 1 + - FUZZER-NOTES.md | 3 + - LICENSE | 71 ++-- - Makefile.in | 12 +- - README | 1 + - cli-kex.c | 2 +- - common-algo.c | 3 + - common-kex.c | 14 +- - curve25519-donna.c | 860 ----------------------------------------- - curve25519.c | 502 ++++++++++++++++++++++++ - curve25519.h | 37 ++ - default_options.h | 7 +- - dropbear.8 | 6 +- - dropbearkey.c | 25 ++ - ed25519.c | 184 +++++++++ - ed25519.h | 54 +++ - filelist.txt | 4 + - fuzz-common.c | 8 + - fuzz-hostkeys.c | 10 + - fuzzer-kexcurve25519.c | 72 ++++ - gened25519.c | 47 +++ - gened25519.h | 36 ++ - gensignkey.c | 10 + - keyimport.c | 158 +++++++- - signkey.c | 60 ++- - signkey.h | 7 + - ssh.h | 2 + - svr-kex.c | 8 +- - svr-runopts.c | 24 ++ - sysoptions.h | 7 +- - 30 files changed, 1289 insertions(+), 946 deletions(-) - delete mode 100644 curve25519-donna.c - create mode 100644 curve25519.c - create mode 100644 curve25519.h - create mode 100644 ed25519.c - create mode 100644 ed25519.h - create mode 100644 fuzzer-kexcurve25519.c - create mode 100644 gened25519.c - create mode 100644 gened25519.h - -diff --git a/.travis.yml b/.travis.yml -index 9bcbce4..99499c8 100644 ---- a/.travis.yml -+++ b/.travis.yml -@@ -57,6 +57,7 @@ script: - - ~/inst/bin/dropbearkey -t ecdsa -f testec256 -s 256 - - ~/inst/bin/dropbearkey -t ecdsa -f testec384 -s 384 - - ~/inst/bin/dropbearkey -t ecdsa -f testec521 -s 521 -+ - ~/inst/bin/dropbearkey -t ed25519 -f tested25519 - - test -z $DO_FUZZ || ./fuzzers_test.sh - - branches: -diff --git a/FUZZER-NOTES.md b/FUZZER-NOTES.md -index 7b88238..4967eba 100644 ---- a/FUZZER-NOTES.md -+++ b/FUZZER-NOTES.md -@@ -72,3 +72,6 @@ Current fuzzers are - - - [fuzzer-kexecdh](fuzzer-kexecdh.c) - test Elliptic Curve Diffie-Hellman key exchange like fuzzer-kexdh. - This is testing libtommath ECC routines. -+ -+- [fuzzer-kexcurve25519](fuzzer-kexcurve25519.c) - test Curve25519 Elliptic Curve Diffie-Hellman key exchange -+ like fuzzer-kexecdh. This is testing `dropbear_curve25519_scalarmult()` and other libtommath routines. -diff --git a/LICENSE b/LICENSE -index c400d94..a4849ff 100644 ---- a/LICENSE -+++ b/LICENSE -@@ -90,52 +90,25 @@ 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. -- */ -+crypto25519.c: -+crypto26619.h: -+ -+Modified TweetNaCl version 20140427, a self-contained public-domain C library. -+https://tweetnacl.cr.yp.to/ -+ -+Contributors (alphabetical order) -+Daniel J. Bernstein, University of Illinois at Chicago and Technische -+Universiteit Eindhoven -+Bernard van Gastel, Radboud Universiteit Nijmegen -+Wesley Janssen, Radboud Universiteit Nijmegen -+Tanja Lange, Technische Universiteit Eindhoven -+Peter Schwabe, Radboud Universiteit Nijmegen -+Sjaak Smetsers, Radboud Universiteit Nijmegen -+ -+Acknowledgments -+This work was supported by the U.S. National Science Foundation under grant -+1018836. "Any opinions, findings, and conclusions or recommendations expressed -+in this material are those of the author(s) and do not necessarily reflect the -+views of the National Science Foundation." -+This work was supported by the Netherlands Organisation for Scientific -+Research (NWO) under grant 639.073.005 and Veni 2013 project 13114. -diff --git a/Makefile.in b/Makefile.in -index bc55b7d..aaf7b3b 100644 ---- a/Makefile.in -+++ b/Makefile.in -@@ -36,8 +36,9 @@ COMMONOBJS=dbutil.o buffer.o dbhelpers.o \ - queue.o \ - atomicio.o compat.o fake-rfc2553.o \ - ltc_prng.o ecc.o ecdsa.o crypto_desc.o \ -+ curve25519.o ed25519.o \ - dbmalloc.o \ -- gensignkey.o gendss.o genrsa.o -+ gensignkey.o gendss.o genrsa.o gened25519.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 \ -@@ -52,7 +53,7 @@ CLIOBJS=cli-main.o cli-auth.o cli-authpasswd.o cli-kex.o \ - CLISVROBJS=common-session.o packet.o common-algo.o common-kex.o \ - common-channel.o common-chansession.o termcodes.o loginrec.o \ - tcp-accept.o listener.o process-packet.o dh_groups.o \ -- common-runopts.o circbuffer.o curve25519-donna.o list.o netio.o -+ common-runopts.o circbuffer.o list.o netio.o - - KEYOBJS=dropbearkey.o - -@@ -264,7 +265,7 @@ tidy: - ## Fuzzing targets - - # list of fuzz targets --FUZZ_TARGETS=fuzzer-preauth fuzzer-pubkey fuzzer-verify fuzzer-preauth_nomaths fuzzer-kexdh fuzzer-kexecdh -+FUZZ_TARGETS=fuzzer-preauth fuzzer-pubkey fuzzer-verify fuzzer-preauth_nomaths fuzzer-kexdh fuzzer-kexecdh fuzzer-kexcurve25519 - - FUZZER_OPTIONS = $(addsuffix .options, $(FUZZ_TARGETS)) - -@@ -303,6 +304,9 @@ fuzzer-kexdh: fuzzer-kexdh.o fuzz-harness.o - fuzzer-kexecdh: fuzzer-kexecdh.o fuzz-harness.o - $(CXX) $(CXXFLAGS) $@.o $(LDFLAGS) $(svrfuzzobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) $(FUZZLIB) @CRYPTLIB@ - -+fuzzer-kexcurve25519: fuzzer-kexcurve25519.o fuzz-harness.o -+ $(CXX) $(CXXFLAGS) $@.o $(LDFLAGS) $(svrfuzzobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) $(FUZZLIB) @CRYPTLIB@ -+ - fuzzer-%.options: Makefile - echo "[libfuzzer]" > $@ - echo "max_len = 50000" >> $@ -@@ -313,7 +317,9 @@ fuzz-hostkeys: - dropbearkey -t rsa -f keyr - dropbearkey -t dss -f keyd - dropbearkey -t ecdsa -size 256 -f keye -+ dropbearkey -t ed25519 -f keyed25519 - echo > hostkeys.c - /usr/bin/xxd -i -a keyr >> hostkeys.c - /usr/bin/xxd -i -a keye >> hostkeys.c - /usr/bin/xxd -i -a keyd >> hostkeys.c -+ /usr/bin/xxd -i -a keyed25519 >> hostkeys.c -diff --git a/README b/README -index b8a6fd2..d197ec7 100644 ---- a/README -+++ b/README -@@ -55,6 +55,7 @@ To run the server, you need to generate 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 -+./dropbearkey -t ed25519 -f dropbear_ed25519_host_key - - or alternatively convert OpenSSH keys to Dropbear: - ./dropbearconvert openssh dropbear /etc/ssh/ssh_host_dsa_key dropbear_dss_host_key -diff --git a/cli-kex.c b/cli-kex.c -index b02cfc7..7cefb5f 100644 ---- a/cli-kex.c -+++ b/cli-kex.c -@@ -81,7 +81,7 @@ void send_msg_kexdh_init() { - } - cli_ses.curve25519_param = gen_kexcurve25519_param(); - } -- buf_putstring(ses.writepayload, (const char*)cli_ses.curve25519_param->pub, CURVE25519_LEN); -+ buf_putstring(ses.writepayload, cli_ses.curve25519_param->pub, CURVE25519_LEN); - break; - #endif - } -diff --git a/common-algo.c b/common-algo.c -index 2f896ab..558aad2 100644 ---- a/common-algo.c -+++ b/common-algo.c -@@ -222,6 +222,9 @@ algo_type ssh_nocompress[] = { - }; - - algo_type sshhostkey[] = { -+#if DROPBEAR_ED25519 -+ {"ssh-ed25519", DROPBEAR_SIGNKEY_ED25519, NULL, 1, NULL}, -+#endif - #if DROPBEAR_ECDSA - #if DROPBEAR_ECC_256 - {"ecdsa-sha2-nistp256", DROPBEAR_SIGNKEY_ECDSA_NISTP256, NULL, 1, NULL}, -diff --git a/common-kex.c b/common-kex.c -index d4933dd..16b7e27 100644 ---- a/common-kex.c -+++ b/common-kex.c -@@ -36,6 +36,7 @@ - #include "dbrandom.h" - #include "runopts.h" - #include "ecc.h" -+#include "curve25519.h" - #include "crypto_desc.h" - - static void kexinitialise(void); -@@ -703,23 +704,18 @@ void kexecdh_comb_key(struct kex_ecdh_param *param, buffer *pub_them, - #endif /* DROPBEAR_ECDH */ - - #if DROPBEAR_CURVE25519 --struct kex_curve25519_param *gen_kexcurve25519_param () { -+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); -+ dropbear_curve25519_scalarmult(param->pub, param->priv, basepoint); - - return param; - } - --void free_kexcurve25519_param(struct kex_curve25519_param *param) --{ -+void free_kexcurve25519_param(struct kex_curve25519_param *param) { - m_burn(param->priv, CURVE25519_LEN); - m_free(param); - } -@@ -736,7 +732,7 @@ void kexcurve25519_comb_key(const struct kex_curve25519_param *param, const buff - dropbear_exit("Bad curve25519"); - } - -- curve25519_donna(out, param->priv, buf_pub_them->data); -+ dropbear_curve25519_scalarmult(out, param->priv, buf_pub_them->data); - - if (constant_time_memcmp(zeroes, out, CURVE25519_LEN) == 0) { - dropbear_exit("Bad curve25519"); -diff --git a/curve25519-donna.c b/curve25519-donna.c -deleted file mode 100644 -index ef0b6d1..0000000 ---- a/curve25519-donna.c -+++ /dev/null -@@ -1,860 +0,0 @@ --/* 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. -- * -- * output[x] <= 14 * the largest product of the input limbs. */ --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. -- * -- * On entry: |output[i]| < 14*2^54 -- * On exit: |output[0..8]| < 280*2^54 */ --static void freduce_degree(limb *output) { -- /* Each of these shifts and adds ends up multiplying the value by 19. -- * -- * For output[0..8], the absolute entry value is < 14*2^54 and we add, at -- * most, 19*14*2^54 thus, on exit, |output[0..8]| < 280*2^54. */ -- 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. -- * -- * On entry: v can take any value. */ --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. -- * -- * On entry: v can take any value. */ --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; --} -- --/* Reduce all coefficients of the short form input so that |x| < 2^26. -- * -- * On entry: |output[i]| < 280*2^54 */ --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]); -- /* The entry condition (that |output[i]| < 280*2^54) means that over is, at -- * most, 280*2^28 in the first iteration of this loop. This is added to the -- * next limb and we can approximate the resulting bound of that limb by -- * 281*2^54. */ -- output[i] -= over << 26; -- output[i+1] += over; -- -- /* For the first iteration, |output[i+1]| < 281*2^54, thus |over| < -- * 281*2^29. When this is added to the next limb, the resulting bound can -- * be approximated as 281*2^54. -- * -- * For subsequent iterations of the loop, 281*2^54 remains a conservative -- * bound and no overflow occurs. */ -- over = div_by_2_25(output[i+1]); -- output[i+1] -= over << 25; -- output[i+2] += over; -- } -- /* Now |output[10]| < 281*2^29 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*281*2^29 -- * So |over| will be no more than 2^16. */ -- { -- 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 + 2^16 < 2^26. The -- * bound on |output[1]| is sufficient to meet our needs. */ --} -- --/* A helpful wrapper around fproduct: output = in * in2. -- * -- * On entry: |in[i]| < 2^27 and |in2[i]| < 2^27. -- * -- * output must be distinct to both inputs. The output is reduced degree -- * (indeed, one need only provide storage for 10 limbs) and |output[i]| < 2^26. */ --static void --fmul(limb *output, const limb *in, const limb *in2) { -- limb t[19]; -- fproduct(t, in, in2); -- /* |t[i]| < 14*2^54 */ -- freduce_degree(t); -- freduce_coefficients(t); -- /* |t[i]| < 2^26 */ -- memcpy(output, t, sizeof(limb) * 10); --} -- --/* Square a number: output = in**2 -- * -- * output must be distinct from the input. The inputs are reduced coefficient -- * form, the output is not. -- * -- * output[x] <= 14 * the largest product of the input limbs. */ --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]); --} -- --/* fsquare sets output = in^2. -- * -- * On entry: The |in| argument is in reduced coefficients form and |in[i]| < -- * 2^27. -- * -- * On exit: The |output| argument is in reduced coefficients form (indeed, one -- * need only provide storage for 10 limbs) and |out[i]| < 2^26. */ --static void --fsquare(limb *output, const limb *in) { -- limb t[19]; -- fsquare_inner(t, in); -- /* |t[i]| < 14*2^54 because the largest product of two limbs will be < -- * 2^(27+27) and fsquare_inner adds together, at most, 14 of those -- * products. */ -- freduce_degree(t); -- freduce_coefficients(t); -- /* |t[i]| < 2^26 */ -- 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, 0x1ffffff); --#undef F --} -- --#if (-32 >> 1) != -16 --#error "This code only works when >> does sign-extension on negative numbers" --#endif -- --/* s32_eq returns 0xffffffff iff a == b and zero otherwise. */ --static s32 s32_eq(s32 a, s32 b) { -- a = ~(a ^ b); -- a &= a << 16; -- a &= a << 8; -- a &= a << 4; -- a &= a << 2; -- a &= a << 1; -- return a >> 31; --} -- --/* s32_gte returns 0xffffffff if a >= b and zero otherwise, where a and b are -- * both non-negative. */ --static s32 s32_gte(s32 a, s32 b) { -- a -= b; -- /* a >= 0 iff a >= b. */ -- return ~(a >> 31); --} -- --/* Take a fully reduced polynomial form number and contract it into a -- * little-endian, 32-byte array. -- * -- * On entry: |input_limbs[i]| < 2^26 */ --static void --fcontract(u8 *output, limb *input_limbs) { -- int i; -- int j; -- s32 input[10]; -- s32 mask; -- -- /* |input_limbs[i]| < 2^26, so it's valid to convert to an s32. */ -- for (i = 0; i < 10; i++) { -- input[i] = input_limbs[i]; -- } -- -- 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] -- * non-negative by borrowing from the next-larger limb. */ -- const s32 mask = input[i] >> 31; -- const s32 carry = -((input[i] & mask) >> 25); -- input[i] = input[i] + (carry << 25); -- input[i+1] = input[i+1] - carry; -- } else { -- const s32 mask = input[i] >> 31; -- const s32 carry = -((input[i] & mask) >> 26); -- input[i] = input[i] + (carry << 26); -- input[i+1] = input[i+1] - carry; -- } -- } -- -- /* There's no greater limb for input[9] to borrow from, but we can multiply -- * by 19 and borrow from input[0], which is valid mod 2^255-19. */ -- { -- const s32 mask = input[9] >> 31; -- const s32 carry = -((input[9] & mask) >> 25); -- input[9] = input[9] + (carry << 25); -- input[0] = input[0] - (carry * 19); -- } -- -- /* After the first iteration, input[1..9] are non-negative and fit within -- * 25 or 26 bits, depending on position. However, input[0] may be -- * negative. */ -- } -- -- /* The first borrow-propagation pass above ended with every limb -- except (possibly) input[0] non-negative. -- -- If input[0] was negative after the first pass, then it was because of a -- carry from input[9]. On entry, input[9] < 2^26 so the carry was, at most, -- one, since (2**26-1) >> 25 = 1. Thus input[0] >= -19. -- -- In the second pass, each limb is decreased by at most one. Thus 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 = input[0] >> 31; -- const s32 carry = -((input[0] & mask) >> 26); -- input[0] = input[0] + (carry << 26); -- input[1] = input[1] - carry; -- } -- -- /* All input[i] are now non-negative. However, there might be values between -- * 2^25 and 2^26 in a limb which is, nominally, 25 bits wide. */ -- for (j = 0; j < 2; j++) { -- for (i = 0; i < 9; i++) { -- if ((i & 1) == 1) { -- const s32 carry = input[i] >> 25; -- input[i] &= 0x1ffffff; -- input[i+1] += carry; -- } else { -- const s32 carry = input[i] >> 26; -- input[i] &= 0x3ffffff; -- input[i+1] += carry; -- } -- } -- -- { -- const s32 carry = input[9] >> 25; -- input[9] &= 0x1ffffff; -- input[0] += 19*carry; -- } -- } -- -- /* If the first carry-chain pass, just above, ended up with a carry from -- * input[9], and that caused input[0] to be out-of-bounds, then input[0] was -- * < 2^26 + 2*19, because the carry was, at most, two. -- * -- * If the second pass carried from input[9] again then input[0] is < 2*19 and -- * the input[9] -> input[0] carry didn't push input[0] out of bounds. */ -- -- /* It still remains the case that input might be between 2^255-19 and 2^255. -- * In this case, input[1..9] must take their maximum value and input[0] must -- * be >= (2^255-19) & 0x3ffffff, which is 0x3ffffed. */ -- mask = s32_gte(input[0], 0x3ffffed); -- for (i = 1; i < 10; i++) { -- if ((i & 1) == 1) { -- mask &= s32_eq(input[i], 0x1ffffff); -- } else { -- mask &= s32_eq(input[i], 0x3ffffff); -- } -- } -- -- /* mask is either 0xffffffff (if input >= 2^255-19) and zero otherwise. Thus -- * this conditionally subtracts 2^255-19. */ -- input[0] -= mask & 0x3ffffed; -- -- for (i = 1; i < 10; i++) { -- if ((i & 1) == 1) { -- input[i] -= mask & 0x1ffffff; -- } else { -- input[i] -= mask & 0x3ffffff; -- } -- } -- -- 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 -- * -- * On entry and exit, the absolute value of the limbs of all inputs and outputs -- * are < 2^26. */ --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); -- /* |x[i]| < 2^27 */ -- fdifference(z, origx); /* does x - z */ -- /* |z[i]| < 2^27 */ -- -- memcpy(origxprime, xprime, sizeof(limb) * 10); -- fsum(xprime, zprime); -- /* |xprime[i]| < 2^27 */ -- fdifference(zprime, origxprime); -- /* |zprime[i]| < 2^27 */ -- fproduct(xxprime, xprime, z); -- /* |xxprime[i]| < 14*2^54: the largest product of two limbs will be < -- * 2^(27+27) and fproduct adds together, at most, 14 of those products. -- * (Approximating that to 2^58 doesn't work out.) */ -- fproduct(zzprime, x, zprime); -- /* |zzprime[i]| < 14*2^54 */ -- freduce_degree(xxprime); -- freduce_coefficients(xxprime); -- /* |xxprime[i]| < 2^26 */ -- freduce_degree(zzprime); -- freduce_coefficients(zzprime); -- /* |zzprime[i]| < 2^26 */ -- memcpy(origxprime, xxprime, sizeof(limb) * 10); -- fsum(xxprime, zzprime); -- /* |xxprime[i]| < 2^27 */ -- fdifference(zzprime, origxprime); -- /* |zzprime[i]| < 2^27 */ -- fsquare(xxxprime, xxprime); -- /* |xxxprime[i]| < 2^26 */ -- fsquare(zzzprime, zzprime); -- /* |zzzprime[i]| < 2^26 */ -- fproduct(zzprime, zzzprime, qmqp); -- /* |zzprime[i]| < 14*2^52 */ -- freduce_degree(zzprime); -- freduce_coefficients(zzprime); -- /* |zzprime[i]| < 2^26 */ -- memcpy(x3, xxxprime, sizeof(limb) * 10); -- memcpy(z3, zzprime, sizeof(limb) * 10); -- -- fsquare(xx, x); -- /* |xx[i]| < 2^26 */ -- fsquare(zz, z); -- /* |zz[i]| < 2^26 */ -- fproduct(x2, xx, zz); -- /* |x2[i]| < 14*2^52 */ -- freduce_degree(x2); -- freduce_coefficients(x2); -- /* |x2[i]| < 2^26 */ -- fdifference(zz, xx); /* does zz = xx - zz */ -- /* |zz[i]| < 2^27 */ -- memset(zzz + 10, 0, sizeof(limb) * 9); -- fscalar_product(zzz, zz, 121665); -- /* |zzz[i]| < 2^(27+17) */ -- /* No need to call freduce_degree here: -- fscalar_product doesn't increase the degree of its input. */ -- freduce_coefficients(zzz); -- /* |zzz[i]| < 2^26 */ -- fsum(zzz, xx); -- /* |zzz[i]| < 2^27 */ -- fproduct(z2, zz, zzz); -- /* |z2[i]| < 14*2^(26+27) */ -- freduce_degree(z2); -- freduce_coefficients(z2); -- /* |z2|i| < 2^26 */ --} -- --/* 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 *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); -- fcontract(mypublic, z); -- return 0; --} -diff --git a/curve25519.c b/curve25519.c -new file mode 100644 -index 0000000..4b83776 ---- /dev/null -+++ b/curve25519.c -@@ -0,0 +1,502 @@ -+/* -+ * 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 "dbrandom.h" -+#include "curve25519.h" -+ -+#if DROPBEAR_CURVE25519 || DROPBEAR_ED25519 -+ -+/* Modified TweetNaCl version 20140427, a self-contained public-domain C library. -+ * https://tweetnacl.cr.yp.to/ */ -+ -+#define FOR(i,n) for (i = 0;i < n;++i) -+#define sv static void -+ -+typedef unsigned char u8; -+typedef unsigned long u32; -+typedef unsigned long long u64; -+typedef long long i64; -+typedef i64 gf[16]; -+ -+#if DROPBEAR_CURVE25519 -+static const gf -+ _121665 = {0xDB41,1}; -+#endif /* DROPBEAR_CURVE25519 */ -+#if DROPBEAR_ED25519 -+static const gf -+ gf0, -+ gf1 = {1}, -+ D2 = {0xf159, 0x26b2, 0x9b94, 0xebd6, 0xb156, 0x8283, 0x149a, 0x00e0, 0xd130, 0xeef3, 0x80f2, 0x198e, 0xfce7, 0x56df, 0xd9dc, 0x2406}, -+ X = {0xd51a, 0x8f25, 0x2d60, 0xc956, 0xa7b2, 0x9525, 0xc760, 0x692c, 0xdc5c, 0xfdd6, 0xe231, 0xc0a4, 0x53fe, 0xcd6e, 0x36d3, 0x2169}, -+ Y = {0x6658, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666}; -+#if DROPBEAR_SIGNKEY_VERIFY -+static const gf -+ D = {0x78a3, 0x1359, 0x4dca, 0x75eb, 0xd8ab, 0x4141, 0x0a4d, 0x0070, 0xe898, 0x7779, 0x4079, 0x8cc7, 0xfe73, 0x2b6f, 0x6cee, 0x5203}, -+ I = {0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, 0xe478, 0xad2f, 0x1806, 0x2f43, 0xd7a7, 0x3dfb, 0x0099, 0x2b4d, 0xdf0b, 0x4fc1, 0x2480, 0x2b83}; -+#endif /* DROPBEAR_SIGNKEY_VERIFY */ -+#endif /* DROPBEAR_ED25519 */ -+ -+#if DROPBEAR_ED25519 -+#if DROPBEAR_SIGNKEY_VERIFY -+static int vn(const u8 *x,const u8 *y,u32 n) -+{ -+ u32 i,d = 0; -+ FOR(i,n) d |= x[i]^y[i]; -+ return (1 & ((d - 1) >> 8)) - 1; -+} -+ -+static int crypto_verify_32(const u8 *x,const u8 *y) -+{ -+ return vn(x,y,32); -+} -+#endif /* DROPBEAR_SIGNKEY_VERIFY */ -+ -+sv set25519(gf r, const gf a) -+{ -+ int i; -+ FOR(i,16) r[i]=a[i]; -+} -+#endif /* DROPBEAR_ED25519 */ -+ -+sv car25519(gf o) -+{ -+ int i; -+ i64 c; -+ FOR(i,16) { -+ o[i]+=(1LL<<16); -+ c=o[i]>>16; -+ o[(i+1)*(i<15)]+=c-1+37*(c-1)*(i==15); -+ o[i]-=c<<16; -+ } -+} -+ -+sv sel25519(gf p,gf q,int b) -+{ -+ i64 t,i,c=~(b-1); -+ FOR(i,16) { -+ t= c&(p[i]^q[i]); -+ p[i]^=t; -+ q[i]^=t; -+ } -+} -+ -+sv pack25519(u8 *o,const gf n) -+{ -+ int i,j,b; -+ gf m,t; -+ FOR(i,16) t[i]=n[i]; -+ car25519(t); -+ car25519(t); -+ car25519(t); -+ FOR(j,2) { -+ m[0]=t[0]-0xffed; -+ for(i=1;i<15;i++) { -+ m[i]=t[i]-0xffff-((m[i-1]>>16)&1); -+ m[i-1]&=0xffff; -+ } -+ m[15]=t[15]-0x7fff-((m[14]>>16)&1); -+ b=(m[15]>>16)&1; -+ m[14]&=0xffff; -+ sel25519(t,m,1-b); -+ } -+ FOR(i,16) { -+ o[2*i]=t[i]&0xff; -+ o[2*i+1]=t[i]>>8; -+ } -+} -+ -+#if DROPBEAR_ED25519 -+#if DROPBEAR_SIGNKEY_VERIFY -+static int neq25519(const gf a, const gf b) -+{ -+ u8 c[32],d[32]; -+ pack25519(c,a); -+ pack25519(d,b); -+ return crypto_verify_32(c,d); -+} -+#endif /* DROPBEAR_SIGNKEY_VERIFY */ -+ -+static u8 par25519(const gf a) -+{ -+ u8 d[32]; -+ pack25519(d,a); -+ return d[0]&1; -+} -+#endif /* DROPBEAR_ED25519 */ -+ -+sv unpack25519(gf o, const u8 *n) -+{ -+ int i; -+ FOR(i,16) o[i]=n[2*i]+((i64)n[2*i+1]<<8); -+ o[15]&=0x7fff; -+} -+ -+sv A(gf o,const gf a,const gf b) -+{ -+ int i; -+ FOR(i,16) o[i]=a[i]+b[i]; -+} -+ -+sv Z(gf o,const gf a,const gf b) -+{ -+ int i; -+ FOR(i,16) o[i]=a[i]-b[i]; -+} -+ -+sv M(gf o,const gf a,const gf b) -+{ -+ i64 i,j,t[31]; -+ FOR(i,31) t[i]=0; -+ FOR(i,16) FOR(j,16) t[i+j]+=a[i]*b[j]; -+ FOR(i,15) t[i]+=38*t[i+16]; -+ FOR(i,16) o[i]=t[i]; -+ car25519(o); -+ car25519(o); -+} -+ -+sv S(gf o,const gf a) -+{ -+ M(o,a,a); -+} -+ -+sv inv25519(gf o,const gf i) -+{ -+ gf c; -+ int a; -+ FOR(a,16) c[a]=i[a]; -+ for(a=253;a>=0;a--) { -+ S(c,c); -+ if(a!=2&&a!=4) M(c,c,i); -+ } -+ FOR(a,16) o[a]=c[a]; -+} -+ -+#if DROPBEAR_ED25519 && DROPBEAR_SIGNKEY_VERIFY -+sv pow2523(gf o,const gf i) -+{ -+ gf c; -+ int a; -+ FOR(a,16) c[a]=i[a]; -+ for(a=250;a>=0;a--) { -+ S(c,c); -+ if(a!=1) M(c,c,i); -+ } -+ FOR(a,16) o[a]=c[a]; -+} -+#endif /* DROPBEAR_ED25519 && DROPBEAR_SIGNKEY_VERIFY */ -+ -+#if DROPBEAR_CURVE25519 -+int dropbear_curve25519_scalarmult(u8 *q,const u8 *n,const u8 *p) -+{ -+ u8 z[32]; -+ i64 x[80],r,i; -+ gf a,b,c,d,e,f; -+ FOR(i,31) z[i]=n[i]; -+ z[31]=(n[31]&127)|64; -+ z[0]&=248; -+ unpack25519(x,p); -+ FOR(i,16) { -+ b[i]=x[i]; -+ d[i]=a[i]=c[i]=0; -+ } -+ a[0]=d[0]=1; -+ for(i=254;i>=0;--i) { -+ r=(z[i>>3]>>(i&7))&1; -+ sel25519(a,b,r); -+ sel25519(c,d,r); -+ A(e,a,c); -+ Z(a,a,c); -+ A(c,b,d); -+ Z(b,b,d); -+ S(d,e); -+ S(f,a); -+ M(a,c,a); -+ M(c,b,e); -+ A(e,a,c); -+ Z(a,a,c); -+ S(b,a); -+ Z(c,d,f); -+ M(a,c,_121665); -+ A(a,a,d); -+ M(c,c,a); -+ M(a,d,f); -+ M(d,b,x); -+ S(b,e); -+ sel25519(a,b,r); -+ sel25519(c,d,r); -+ } -+ FOR(i,16) { -+ x[i+16]=a[i]; -+ x[i+32]=c[i]; -+ x[i+48]=b[i]; -+ x[i+64]=d[i]; -+ } -+ inv25519(x+32,x+32); -+ M(x+16,x+16,x+32); -+ pack25519(q,x+16); -+ return 0; -+} -+#endif /* DROPBEAR_CURVE25519 */ -+ -+#if DROPBEAR_ED25519 -+static int crypto_hash(u8 *out,const u8 *m,u64 n) -+{ -+ hash_state hs; -+ -+ sha512_init(&hs); -+ sha512_process(&hs, m, n); -+ return sha512_done(&hs, out); -+} -+ -+sv add(gf p[4],gf q[4]) -+{ -+ gf a,b,c,d,t,e,f,g,h; -+ -+ Z(a, p[1], p[0]); -+ Z(t, q[1], q[0]); -+ M(a, a, t); -+ A(b, p[0], p[1]); -+ A(t, q[0], q[1]); -+ M(b, b, t); -+ M(c, p[3], q[3]); -+ M(c, c, D2); -+ M(d, p[2], q[2]); -+ A(d, d, d); -+ Z(e, b, a); -+ Z(f, d, c); -+ A(g, d, c); -+ A(h, b, a); -+ -+ M(p[0], e, f); -+ M(p[1], h, g); -+ M(p[2], g, f); -+ M(p[3], e, h); -+} -+ -+sv cswap(gf p[4],gf q[4],u8 b) -+{ -+ int i; -+ FOR(i,4) -+ sel25519(p[i],q[i],b); -+} -+ -+sv pack(u8 *r,gf p[4]) -+{ -+ gf tx, ty, zi; -+ inv25519(zi, p[2]); -+ M(tx, p[0], zi); -+ M(ty, p[1], zi); -+ pack25519(r, ty); -+ r[31] ^= par25519(tx) << 7; -+} -+ -+sv scalarmult(gf p[4],gf q[4],const u8 *s) -+{ -+ int i; -+ set25519(p[0],gf0); -+ set25519(p[1],gf1); -+ set25519(p[2],gf1); -+ set25519(p[3],gf0); -+ for (i = 255;i >= 0;--i) { -+ u8 b = (s[i/8]>>(i&7))&1; -+ cswap(p,q,b); -+ add(q,p); -+ add(p,p); -+ cswap(p,q,b); -+ } -+} -+ -+sv scalarbase(gf p[4],const u8 *s) -+{ -+ gf q[4]; -+ set25519(q[0],X); -+ set25519(q[1],Y); -+ set25519(q[2],gf1); -+ M(q[3],X,Y); -+ scalarmult(p,q,s); -+} -+ -+int dropbear_ed25519_make_key(u8 *pk,u8 *sk) -+{ -+ u8 d[64]; -+ gf p[4]; -+ -+ genrandom(sk, 32); -+ -+ crypto_hash(d, sk, 32); -+ d[0] &= 248; -+ d[31] &= 127; -+ d[31] |= 64; -+ -+ scalarbase(p,d); -+ pack(pk,p); -+ -+ return 0; -+} -+ -+static const u64 L[32] = {0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x10}; -+ -+sv modL(u8 *r,i64 x[64]) -+{ -+ i64 carry,i,j; -+ for (i = 63;i >= 32;--i) { -+ carry = 0; -+ for (j = i - 32;j < i - 12;++j) { -+ x[j] += carry - 16 * x[i] * L[j - (i - 32)]; -+ carry = (x[j] + 128) >> 8; -+ x[j] -= carry << 8; -+ } -+ x[j] += carry; -+ x[i] = 0; -+ } -+ carry = 0; -+ FOR(j,32) { -+ x[j] += carry - (x[31] >> 4) * L[j]; -+ carry = x[j] >> 8; -+ x[j] &= 255; -+ } -+ FOR(j,32) x[j] -= carry * L[j]; -+ FOR(i,32) { -+ x[i+1] += x[i] >> 8; -+ r[i] = x[i] & 255; -+ } -+} -+ -+sv reduce(u8 *r) -+{ -+ i64 x[64],i; -+ FOR(i,64) x[i] = (u64) r[i]; -+ FOR(i,64) r[i] = 0; -+ modL(r,x); -+} -+ -+int dropbear_ed25519_sign(const u8 *m,u32 mlen,u8 *s,u32 *slen,const u8 *sk, const u8 *pk) -+{ -+ hash_state hs; -+ u8 d[64],h[64],r[64]; -+ i64 x[64]; -+ gf p[4]; -+ u32 i,j; -+ -+ crypto_hash(d, sk, 32); -+ d[0] &= 248; -+ d[31] &= 127; -+ d[31] |= 64; -+ -+ *slen = 64; -+ -+ sha512_init(&hs); -+ sha512_process(&hs,d + 32,32); -+ sha512_process(&hs,m,mlen); -+ sha512_done(&hs,r); -+ reduce(r); -+ scalarbase(p,r); -+ pack(s,p); -+ -+ sha512_init(&hs); -+ sha512_process(&hs,s,32); -+ sha512_process(&hs,pk,32); -+ sha512_process(&hs,m,mlen); -+ sha512_done(&hs,h); -+ reduce(h); -+ -+ FOR(i,64) x[i] = 0; -+ FOR(i,32) x[i] = (u64) r[i]; -+ FOR(i,32) FOR(j,32) x[i+j] += h[i] * (u64) d[j]; -+ modL(s + 32,x); -+ -+ return 0; -+} -+ -+#if DROPBEAR_SIGNKEY_VERIFY -+static int unpackneg(gf r[4],const u8 p[32]) -+{ -+ gf t, chk, num, den, den2, den4, den6; -+ set25519(r[2],gf1); -+ unpack25519(r[1],p); -+ S(num,r[1]); -+ M(den,num,D); -+ Z(num,num,r[2]); -+ A(den,r[2],den); -+ -+ S(den2,den); -+ S(den4,den2); -+ M(den6,den4,den2); -+ M(t,den6,num); -+ M(t,t,den); -+ -+ pow2523(t,t); -+ M(t,t,num); -+ M(t,t,den); -+ M(t,t,den); -+ M(r[0],t,den); -+ -+ S(chk,r[0]); -+ M(chk,chk,den); -+ if (neq25519(chk, num)) M(r[0],r[0],I); -+ -+ S(chk,r[0]); -+ M(chk,chk,den); -+ if (neq25519(chk, num)) return -1; -+ -+ if (par25519(r[0]) == (p[31]>>7)) Z(r[0],gf0,r[0]); -+ -+ M(r[3],r[0],r[1]); -+ return 0; -+} -+ -+int dropbear_ed25519_verify(const u8 *m,u32 mlen,const u8 *s,u32 slen,const u8 *pk) -+{ -+ hash_state hs; -+ u8 t[32],h[64]; -+ gf p[4],q[4]; -+ -+ if (slen < 64) return -1; -+ -+ if (unpackneg(q,pk)) return -1; -+ -+ sha512_init(&hs); -+ sha512_process(&hs,s,32); -+ sha512_process(&hs,pk,32); -+ sha512_process(&hs,m,mlen); -+ sha512_done(&hs,h); -+ -+ reduce(h); -+ scalarmult(p,q,h); -+ -+ scalarbase(q,s + 32); -+ add(p,q); -+ pack(t,p); -+ -+ if (crypto_verify_32(s, t)) -+ return -1; -+ -+ return 0; -+} -+#endif /* DROPBEAR_SIGNKEY_VERIFY */ -+ -+#endif /* DROPBEAR_ED25519 */ -+ -+#endif /* DROPBEAR_CURVE25519 || DROPBEAR_ED25519 */ -diff --git a/curve25519.h b/curve25519.h -new file mode 100644 -index 0000000..7f75aed ---- /dev/null -+++ b/curve25519.h -@@ -0,0 +1,37 @@ -+/* -+ * 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 DROPBEAR_CURVE25519_H -+#define DROPBEAR_CURVE25519_H -+ -+int dropbear_curve25519_scalarmult(unsigned char *q, const unsigned char *n, const unsigned char *p); -+int dropbear_ed25519_make_key(unsigned char *pk, unsigned char *sk); -+int dropbear_ed25519_sign(const unsigned char *m, unsigned long mlen, -+ unsigned char *s, unsigned long *slen, -+ const unsigned char *sk, const unsigned char *pk); -+int dropbear_ed25519_verify(const unsigned char *m, unsigned long mlen, -+ const unsigned char *s, unsigned long slen, -+ const unsigned char *pk); -+ -+#endif /* DROPBEAR_CURVE25519_H */ -diff --git a/default_options.h b/default_options.h -index 9000fcc..5b232dd 100644 ---- a/default_options.h -+++ b/default_options.h -@@ -22,6 +22,7 @@ IMPORTANT: Some options will require "make clean" after changes */ - #define DSS_PRIV_FILENAME "/etc/dropbear/dropbear_dss_host_key" - #define RSA_PRIV_FILENAME "/etc/dropbear/dropbear_rsa_host_key" - #define ECDSA_PRIV_FILENAME "/etc/dropbear/dropbear_ecdsa_host_key" -+#define ED25519_PRIV_FILENAME "/etc/dropbear/dropbear_ed25519_host_key" - - /* Set NON_INETD_MODE if you require daemon functionality (ie Dropbear listens - * on chosen ports and keeps accepting connections. This is the default. -@@ -116,11 +117,15 @@ IMPORTANT: Some options will require "make clean" after changes */ - * code (either ECDSA or ECDH) increases binary size - around 30kB - * on x86-64 */ - #define DROPBEAR_ECDSA 1 -+/* Ed25519 is faster than ECDSA. Compiling in Ed25519 code increases -+ binary size - around 7,5kB on x86-64 */ -+#define DROPBEAR_ED25519 1 - - /* RSA must be >=1024 */ - #define DROPBEAR_DEFAULT_RSA_SIZE 2048 - /* DSS is always 1024 */ - /* ECDSA defaults to largest size configured, usually 521 */ -+/* Ed25519 is always 256 */ - - /* Add runtime flag "-R" to generate hostkeys as-needed when the first - connection using that key type occurs. -@@ -143,7 +148,7 @@ IMPORTANT: Some options will require "make clean" after changes */ - * group14 is supported by most implementations. - * group16 provides a greater strength level but is slower and increases binary size - * curve25519 and ecdh algorithms are faster than non-elliptic curve methods -- * curve25519 increases binary size by ~8kB on x86-64 -+ * curve25519 increases binary size by ~2,5kB on x86-64 - * including either ECDH or ECDSA increases binary size by ~30kB on x86-64 - - * Small systems should generally include either curve25519 or ecdh for performance. -diff --git a/dropbear.8 b/dropbear.8 -index 71c955a..345954f 100644 ---- a/dropbear.8 -+++ b/dropbear.8 -@@ -107,7 +107,7 @@ Print the version - Authorized Keys - - ~/.ssh/authorized_keys can be set up to allow remote login with a RSA, --ECDSA, or DSS -+ECDSA, Ed25519 or DSS - key. Each line is of the form - .TP - [restrictions] ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIgAsp... [comment] -@@ -146,8 +146,8 @@ key authentication. - Host Key Files - - Host key files are read at startup from a standard location, by default --/etc/dropbear/dropbear_dss_host_key, /etc/dropbear/dropbear_rsa_host_key, and --/etc/dropbear/dropbear_ecdsa_host_key -+/etc/dropbear/dropbear_dss_host_key, /etc/dropbear/dropbear_rsa_host_key, -+/etc/dropbear/dropbear_ecdsa_host_key and /etc/dropbear/dropbear_ed25519_host_key - - If the -r command line option is specified the default files are not loaded. - Host key files are of the form generated by dropbearkey. -diff --git a/dropbearkey.c b/dropbearkey.c -index dd0e697..f881855 100644 ---- a/dropbearkey.c -+++ b/dropbearkey.c -@@ -43,6 +43,10 @@ - * mp_int y - * mp_int x - * -+ * Ed25519: -+ * string "ssh-ed25519" -+ * string k (32 bytes) + A (32 bytes) -+ * - */ - #include "includes.h" - #include "signkey.h" -@@ -51,6 +55,7 @@ - - #include "genrsa.h" - #include "gendss.h" -+#include "gened25519.h" - #include "ecdsa.h" - #include "crypto_desc.h" - #include "dbrandom.h" -@@ -75,6 +80,9 @@ static void printhelp(char * progname) { - #endif - #if DROPBEAR_ECDSA - " ecdsa\n" -+#endif -+#if DROPBEAR_ED25519 -+ " ed25519\n" - #endif - "-f filename Use filename for the secret key.\n" - " ~/.ssh/id_dropbear is recommended for client keys.\n" -@@ -94,6 +102,9 @@ static void printhelp(char * progname) { - "521 " - #endif - "\n" -+#endif -+#if DROPBEAR_ED25519 -+ " Ed25519 has a fixed size of 256 bits\n" - #endif - "-y Just print the publickey and fingerprint for the\n private key in .\n" - #if DEBUG_TRACE -@@ -106,6 +117,14 @@ static void printhelp(char * progname) { - static void check_signkey_bits(enum signkey_type type, int bits) - { - switch (type) { -+#if DROPBEAR_ED25519 -+ case DROPBEAR_SIGNKEY_ED25519: -+ if (bits != 256) { -+ dropbear_exit("Ed25519 keys have a fixed size of 256 bits\n"); -+ exit(EXIT_FAILURE); -+ } -+ break; -+#endif - #if DROPBEAR_RSA - case DROPBEAR_SIGNKEY_RSA: - if (bits < 512 || bits > 4096 || (bits % 8 != 0)) { -@@ -224,6 +243,12 @@ int main(int argc, char ** argv) { - keytype = DROPBEAR_SIGNKEY_ECDSA_KEYGEN; - } - #endif -+#if DROPBEAR_ED25519 -+ if (strcmp(typetext, "ed25519") == 0) -+ { -+ keytype = DROPBEAR_SIGNKEY_ED25519; -+ } -+#endif - - if (keytype == DROPBEAR_SIGNKEY_NONE) { - fprintf(stderr, "Unknown key type '%s'\n", typetext); -diff --git a/ed25519.c b/ed25519.c -new file mode 100644 -index 0000000..3fb544c ---- /dev/null -+++ b/ed25519.c -@@ -0,0 +1,184 @@ -+/* -+ * 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. */ -+ -+/* Perform Ed25519 operations on data, including reading keys, signing and -+ * verification. */ -+ -+#include "includes.h" -+#include "dbutil.h" -+#include "buffer.h" -+#include "ssh.h" -+#include "curve25519.h" -+#include "ed25519.h" -+ -+#if DROPBEAR_ED25519 -+ -+/* Load a public ed25519 key from a buffer, initialising the values. -+ * The key will have the same format as buf_put_ed25519_key. -+ * These should be freed with ed25519_key_free. -+ * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ -+int buf_get_ed25519_pub_key(buffer *buf, dropbear_ed25519_key *key) { -+ -+ unsigned int len; -+ -+ TRACE(("enter buf_get_ed25519_pub_key")) -+ dropbear_assert(key != NULL); -+ -+ buf_incrpos(buf, 4+SSH_SIGNKEY_ED25519_LEN); /* int + "ssh-ed25519" */ -+ -+ len = buf_getint(buf); -+ if (len != CURVE25519_LEN || buf->len - buf->pos < len) { -+ TRACE(("leave buf_get_ed25519_pub_key: failure")) -+ return DROPBEAR_FAILURE; -+ } -+ -+ m_burn(key->priv, CURVE25519_LEN); -+ memcpy(key->pub, buf_getptr(buf, CURVE25519_LEN), CURVE25519_LEN); -+ buf_incrpos(buf, CURVE25519_LEN); -+ -+ TRACE(("leave buf_get_ed25519_pub_key: success")) -+ return DROPBEAR_SUCCESS; -+} -+ -+/* Same as buf_get_ed25519_pub_key, but reads private key at the end. -+ * Loads a public and private ed25519 key from a buffer -+ * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ -+int buf_get_ed25519_priv_key(buffer *buf, dropbear_ed25519_key *key) { -+ -+ unsigned int len; -+ -+ TRACE(("enter buf_get_ed25519_priv_key")) -+ dropbear_assert(key != NULL); -+ -+ buf_incrpos(buf, 4+SSH_SIGNKEY_ED25519_LEN); /* int + "ssh-ed25519" */ -+ -+ len = buf_getint(buf); -+ if (len != CURVE25519_LEN*2 || buf->len - buf->pos < len) { -+ TRACE(("leave buf_get_ed25519_priv_key: failure")) -+ return DROPBEAR_FAILURE; -+ } -+ -+ memcpy(key->priv, buf_getptr(buf, CURVE25519_LEN), CURVE25519_LEN); -+ buf_incrpos(buf, CURVE25519_LEN); -+ memcpy(key->pub, buf_getptr(buf, CURVE25519_LEN), CURVE25519_LEN); -+ buf_incrpos(buf, CURVE25519_LEN); -+ -+ TRACE(("leave buf_get_ed25519_pub_key: success")) -+ return DROPBEAR_SUCCESS; -+} -+ -+/* Clear and free the memory used by a public or private key */ -+void ed25519_key_free(dropbear_ed25519_key *key) { -+ -+ TRACE2(("enter ed25519_key_free")) -+ -+ if (key == NULL) { -+ TRACE2(("leave ed25519_key_free: key == NULL")) -+ return; -+ } -+ m_burn(key->priv, CURVE25519_LEN); -+ m_free(key); -+ -+ TRACE2(("leave rsa_key_free")) -+} -+ -+/* Put the public ed25519 key into the buffer in the required format */ -+void buf_put_ed25519_pub_key(buffer *buf, const dropbear_ed25519_key *key) { -+ -+ TRACE(("enter buf_put_ed25519_pub_key")) -+ dropbear_assert(key != NULL); -+ -+ buf_putstring(buf, SSH_SIGNKEY_ED25519, SSH_SIGNKEY_ED25519_LEN); -+ buf_putstring(buf, key->pub, CURVE25519_LEN); -+ -+ TRACE(("leave buf_put_ed25519_pub_key")) -+} -+ -+/* Put the public and private ed25519 key into the buffer in the required format */ -+void buf_put_ed25519_priv_key(buffer *buf, const dropbear_ed25519_key *key) { -+ -+ TRACE(("enter buf_put_ed25519_priv_key")) -+ dropbear_assert(key != NULL); -+ -+ buf_putstring(buf, SSH_SIGNKEY_ED25519, SSH_SIGNKEY_ED25519_LEN); -+ buf_putint(buf, CURVE25519_LEN*2); -+ buf_putbytes(buf, key->priv, CURVE25519_LEN); -+ buf_putbytes(buf, key->pub, CURVE25519_LEN); -+ -+ TRACE(("leave buf_put_ed25519_priv_key")) -+} -+ -+/* Sign the data presented with key, writing the signature contents -+ * to the buffer */ -+void buf_put_ed25519_sign(buffer* buf, const dropbear_ed25519_key *key, const buffer *data_buf) { -+ -+ unsigned char s[64]; -+ unsigned long slen = sizeof(s); -+ -+ TRACE(("enter buf_put_ed25519_sign")) -+ dropbear_assert(key != NULL); -+ -+ if (dropbear_ed25519_sign(data_buf->data, data_buf->len, -+ s, &slen, key->priv, key->pub) == 0) { -+ buf_putstring(buf, SSH_SIGNKEY_ED25519, SSH_SIGNKEY_ED25519_LEN); -+ buf_putstring(buf, s, slen); -+ } -+ -+ TRACE(("leave buf_put_ed25519_sign")) -+} -+ -+#if DROPBEAR_SIGNKEY_VERIFY -+/* Verify a signature in buf, made on data by the key given. -+ * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ -+int buf_ed25519_verify(buffer *buf, const dropbear_ed25519_key *key, const buffer *data_buf) { -+ -+ int ret = DROPBEAR_FAILURE; -+ unsigned char *s; -+ unsigned long slen; -+ -+ TRACE(("enter buf_ed25519_verify")) -+ dropbear_assert(key != NULL); -+ -+ slen = buf_getint(buf); -+ if (slen != 64 || buf->len - buf->pos < slen) { -+ TRACE(("bad size")) -+ goto out; -+ } -+ s = buf_getptr(buf, slen); -+ -+ if (dropbear_ed25519_verify(data_buf->data, data_buf->len, -+ s, slen, key->pub) == 0) { -+ /* signature is valid */ -+ TRACE(("success!")) -+ ret = DROPBEAR_SUCCESS; -+ } -+ -+out: -+ TRACE(("leave buf_ed25519_verify: ret %d", ret)) -+ return ret; -+} -+ -+#endif /* DROPBEAR_SIGNKEY_VERIFY */ -+ -+#endif /* DROPBEAR_ED25519 */ -diff --git a/ed25519.h b/ed25519.h -new file mode 100644 -index 0000000..16c0d7b ---- /dev/null -+++ b/ed25519.h -@@ -0,0 +1,54 @@ -+/* -+ * 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 DROPBEAR_ED25519_H_ -+#define DROPBEAR_ED25519_H_ -+ -+#include "includes.h" -+#include "buffer.h" -+ -+#if DROPBEAR_ED25519 -+ -+#define CURVE25519_LEN 32 -+ -+typedef struct { -+ -+ unsigned char priv[CURVE25519_LEN]; -+ unsigned char pub[CURVE25519_LEN]; -+ -+} dropbear_ed25519_key; -+ -+void buf_put_ed25519_sign(buffer* buf, const dropbear_ed25519_key *key, const buffer *data_buf); -+#if DROPBEAR_SIGNKEY_VERIFY -+int buf_ed25519_verify(buffer * buf, const dropbear_ed25519_key *key, const buffer *data_buf); -+#endif -+int buf_get_ed25519_pub_key(buffer* buf, dropbear_ed25519_key *key); -+int buf_get_ed25519_priv_key(buffer* buf, dropbear_ed25519_key *key); -+void buf_put_ed25519_pub_key(buffer* buf, const dropbear_ed25519_key *key); -+void buf_put_ed25519_priv_key(buffer* buf, const dropbear_ed25519_key *key); -+void ed25519_key_free(dropbear_ed25519_key *key); -+ -+#endif /* DROPBEAR_ED25519 */ -+ -+#endif /* DROPBEAR_ED25519_H_ */ -diff --git a/filelist.txt b/filelist.txt -index 8281c14..3b9bb67 100644 ---- a/filelist.txt -+++ b/filelist.txt -@@ -99,6 +99,10 @@ rsa.c RSA asymmetric crypto routines - - dss.c DSS asymmetric crypto routines - -+ed25519.c Ed25519 asymmetric crypto routines -+ -+gened25519.c Ed25519 key generation -+ - gendss.c DSS key generation - - genrsa.c RSA key generation -diff --git a/fuzz-common.c b/fuzz-common.c -index 5c90c45..b1b00f6 100644 ---- a/fuzz-common.c -+++ b/fuzz-common.c -@@ -112,6 +112,14 @@ static void load_fixed_hostkeys(void) { - dropbear_exit("failed fixed ecdsa hostkey"); - } - -+ buf_setlen(b, 0); -+ buf_putbytes(b, keyed25519, keyed25519_len); -+ buf_setpos(b, 0); -+ type = DROPBEAR_SIGNKEY_ED25519; -+ if (buf_get_priv_key(b, svr_opts.hostkey, &type) == DROPBEAR_FAILURE) { -+ dropbear_exit("failed fixed ed25519 hostkey"); -+ } -+ - buf_free(b); - } - -diff --git a/fuzz-hostkeys.c b/fuzz-hostkeys.c -index dc1615d..3fff5cb 100644 ---- a/fuzz-hostkeys.c -+++ b/fuzz-hostkeys.c -@@ -127,3 +127,13 @@ unsigned char keyd[] = { - 0xf9, 0x39 - }; - unsigned int keyd_len = 458; -+unsigned char keyed25519[] = { -+ 0x00, 0x00, 0x00, 0x0b, 0x73, 0x73, 0x68, 0x2d, 0x65, 0x64, 0x32, 0x35, -+ 0x35, 0x31, 0x39, 0x00, 0x00, 0x00, 0x40, 0x10, 0xb3, 0x79, 0x06, 0xe5, -+ 0x9b, 0xe7, 0xe4, 0x6e, 0xec, 0xfe, 0xa5, 0x39, 0x21, 0x7c, 0xf6, 0x66, -+ 0x8c, 0x0b, 0x6a, 0x01, 0x09, 0x05, 0xc7, 0x4f, 0x64, 0xa8, 0x24, 0xd2, -+ 0x8d, 0xbd, 0xdd, 0xc6, 0x3c, 0x99, 0x1b, 0x2d, 0x3e, 0x33, 0x90, 0x19, -+ 0xa4, 0xd5, 0xe9, 0x23, 0xfe, 0x8e, 0xd6, 0xd4, 0xf9, 0xb1, 0x11, 0x69, -+ 0x7c, 0x57, 0x52, 0x0e, 0x41, 0xdb, 0x1b, 0x12, 0x87, 0xfa, 0xc9 -+}; -+unsigned int keyed25519_len = 83; -diff --git a/fuzzer-kexcurve25519.c b/fuzzer-kexcurve25519.c -new file mode 100644 -index 0000000..f2eab14 ---- /dev/null -+++ b/fuzzer-kexcurve25519.c -@@ -0,0 +1,72 @@ -+#include "fuzz.h" -+#include "session.h" -+#include "fuzz-wrapfd.h" -+#include "debug.h" -+#include "runopts.h" -+#include "algo.h" -+#include "bignum.h" -+ -+int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { -+ static int once = 0; -+ static struct key_context* keep_newkeys = NULL; -+ /* number of generated parameters is limited by the timeout for the first run. -+ TODO move this to the libfuzzer initialiser function instead if the timeout -+ doesn't apply there */ -+ #define NUM_PARAMS 20 -+ static struct kex_curve25519_param *curve25519_params[NUM_PARAMS]; -+ -+ if (!once) { -+ fuzz_common_setup(); -+ fuzz_svr_setup(); -+ -+ keep_newkeys = (struct key_context*)m_malloc(sizeof(struct key_context)); -+ keep_newkeys->algo_kex = fuzz_get_algo(sshkex, "curve25519-sha256"); -+ keep_newkeys->algo_hostkey = DROPBEAR_SIGNKEY_ED25519; -+ ses.newkeys = keep_newkeys; -+ -+ /* Pre-generate parameters */ -+ int i; -+ for (i = 0; i < NUM_PARAMS; i++) { -+ curve25519_params[i] = gen_kexcurve25519_param(); -+ } -+ -+ once = 1; -+ } -+ -+ if (fuzz_set_input(Data, Size) == DROPBEAR_FAILURE) { -+ return 0; -+ } -+ -+ m_malloc_set_epoch(1); -+ -+ if (setjmp(fuzz.jmp) == 0) { -+ /* Based on recv_msg_kexdh_init()/send_msg_kexdh_reply() -+ with DROPBEAR_KEX_CURVE25519 */ -+ ses.newkeys = keep_newkeys; -+ -+ /* Choose from the collection of curve25519 params */ -+ unsigned int e = buf_getint(fuzz.input); -+ struct kex_curve25519_param *curve25519_param = curve25519_params[e % NUM_PARAMS]; -+ -+ buffer * ecdh_qs = buf_getstringbuf(fuzz.input); -+ -+ ses.kexhashbuf = buf_new(KEXHASHBUF_MAX_INTS); -+ kexcurve25519_comb_key(curve25519_param, ecdh_qs, svr_opts.hostkey); -+ -+ mp_clear(ses.dh_K); -+ m_free(ses.dh_K); -+ buf_free(ecdh_qs); -+ -+ buf_free(ses.hash); -+ buf_free(ses.session_id); -+ /* kexhashbuf is freed in kexdh_comb_key */ -+ -+ m_malloc_free_epoch(1, 0); -+ } else { -+ m_malloc_free_epoch(1, 1); -+ TRACE(("dropbear_exit longjmped")) -+ /* dropbear_exit jumped here */ -+ } -+ -+ return 0; -+} -diff --git a/gened25519.c b/gened25519.c -new file mode 100644 -index 0000000..a027914 ---- /dev/null -+++ b/gened25519.c -@@ -0,0 +1,47 @@ -+/* -+ * 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 "dbutil.h" -+#include "dbrandom.h" -+#include "curve25519.h" -+#include "gened25519.h" -+ -+#if DROPBEAR_ED25519 -+ -+dropbear_ed25519_key * gen_ed25519_priv_key(unsigned int size) { -+ -+ dropbear_ed25519_key *key; -+ -+ if (size != 256) { -+ dropbear_exit("Ed25519 keys have a fixed size of 256 bits"); -+ } -+ -+ key = m_malloc(sizeof(*key)); -+ dropbear_ed25519_make_key(key->pub, key->priv); -+ -+ return key; -+} -+ -+#endif /* DROPBEAR_ED25519 */ -diff --git a/gened25519.h b/gened25519.h -new file mode 100644 -index 0000000..8058310 ---- /dev/null -+++ b/gened25519.h -@@ -0,0 +1,36 @@ -+/* -+ * 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 DROPBEAR_GENED25519_H_ -+#define DROPBEAR_GENED25519_H_ -+ -+#include "ed25519.h" -+ -+#if DROPBEAR_ED25519 -+ -+dropbear_ed25519_key * gen_ed25519_priv_key(unsigned int size); -+ -+#endif /* DROPBEAR_ED25519 */ -+ -+#endif /* DROPBEAR_GENED25519_H_ */ -diff --git a/gensignkey.c b/gensignkey.c -index 34b6f5a..674d81f 100644 ---- a/gensignkey.c -+++ b/gensignkey.c -@@ -4,6 +4,7 @@ - #include "ecdsa.h" - #include "genrsa.h" - #include "gendss.h" -+#include "gened25519.h" - #include "signkey.h" - #include "dbrandom.h" - -@@ -68,6 +69,10 @@ static int get_default_bits(enum signkey_type keytype) - return 384; - case DROPBEAR_SIGNKEY_ECDSA_NISTP256: - return 256; -+#endif -+#if DROPBEAR_ED25519 -+ case DROPBEAR_SIGNKEY_ED25519: -+ return 256; - #endif - default: - return 0; -@@ -118,6 +123,11 @@ int signkey_generate(enum signkey_type keytype, int bits, const char* filename, - *signkey_key_ptr(key, keytype) = ecckey; - } - break; -+#endif -+#if DROPBEAR_ED25519 -+ case DROPBEAR_SIGNKEY_ED25519: -+ key->ed25519key = gen_ed25519_priv_key(bits); -+ break; - #endif - default: - dropbear_exit("Internal error"); -diff --git a/keyimport.c b/keyimport.c -index 7304e58..ad0c530 100644 ---- a/keyimport.c -+++ b/keyimport.c -@@ -35,6 +35,15 @@ - #include "buffer.h" - #include "dbutil.h" - #include "ecc.h" -+#include "ssh.h" -+ -+static const unsigned char OSSH_PKEY_BLOB[] = -+ "openssh-key-v1\0" /* AUTH_MAGIC */ -+ "\0\0\0\4none" /* cipher name*/ -+ "\0\0\0\4none" /* kdf name */ -+ "\0\0\0\0" /* kdf */ -+ "\0\0\0\1"; /* key num */ -+#define OSSH_PKEY_BLOBLEN (sizeof(OSSH_PKEY_BLOB) - 1) - - #if DROPBEAR_ECDSA - static const unsigned char OID_SEC256R1_BLOB[] = {0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07}; -@@ -352,7 +361,7 @@ struct mpint_pos { void *start; int bytes; }; - * Code to read and write OpenSSH private keys. - */ - --enum { OSSH_DSA, OSSH_RSA, OSSH_EC }; -+enum { OSSH_DSA, OSSH_RSA, OSSH_EC, OSSH_PKEY }; - struct openssh_key { - int type; - int encrypted; -@@ -364,11 +373,12 @@ struct openssh_key { - static struct openssh_key *load_openssh_key(const char *filename) - { - struct openssh_key *ret; -+ buffer *buf = NULL; - FILE *fp = NULL; - char buffer[256]; - char *errmsg = NULL, *p = NULL; - int headers_done; -- unsigned long len, outlen; -+ unsigned long len; - - ret = (struct openssh_key*)m_malloc(sizeof(struct openssh_key)); - ret->keyblob = NULL; -@@ -397,12 +407,15 @@ static struct openssh_key *load_openssh_key(const char *filename) - ret->type = OSSH_DSA; - else if (!strcmp(buffer, "-----BEGIN EC PRIVATE KEY-----\n")) - ret->type = OSSH_EC; -+ else if (!strcmp(buffer, "-----BEGIN OPENSSH PRIVATE KEY-----\n")) -+ ret->type = OSSH_PKEY; - else { - errmsg = "Unrecognised key type"; - goto error; - } - - headers_done = 0; -+ buf = buf_new(0); - while (1) { - if (!fgets(buffer, sizeof(buffer), fp)) { - errmsg = "Unexpected end of file"; -@@ -448,20 +461,31 @@ static struct openssh_key *load_openssh_key(const char *filename) - } else { - headers_done = 1; - len = strlen(buffer); -- outlen = len*4/3; -- if (ret->keyblob_len + outlen > ret->keyblob_size) { -- ret->keyblob_size = ret->keyblob_len + outlen + 256; -- ret->keyblob = (unsigned char*)m_realloc(ret->keyblob, -- ret->keyblob_size); -- } -- outlen = ret->keyblob_size - ret->keyblob_len; -- if (base64_decode((const unsigned char *)buffer, len, -- ret->keyblob + ret->keyblob_len, &outlen) != CRYPT_OK){ -- errmsg = "Error decoding base64"; -- goto error; -- } -- ret->keyblob_len += outlen; -+ buf = buf_resize(buf, buf->size + len); -+ buf_putbytes(buf, buffer, len); -+ } -+ } -+ -+ if (buf && buf->len) { -+ ret->keyblob_size = ret->keyblob_len + buf->len*4/3 + 256; -+ ret->keyblob = (unsigned char*)m_realloc(ret->keyblob, ret->keyblob_size); -+ len = ret->keyblob_size; -+ if (base64_decode((const unsigned char *)buf->data, buf->len, -+ ret->keyblob, &len) != CRYPT_OK){ -+ errmsg = "Error decoding base64"; -+ goto error; -+ } -+ ret->keyblob_len = len; -+ } -+ -+ if (ret->type == OSSH_PKEY) { -+ if (ret->keyblob_len < OSSH_PKEY_BLOBLEN || -+ memcmp(ret->keyblob, OSSH_PKEY_BLOB, OSSH_PKEY_BLOBLEN)) { -+ errmsg = "Error decoding OpenSSH key"; -+ goto error; - } -+ ret->keyblob_len -= OSSH_PKEY_BLOBLEN; -+ memmove(ret->keyblob, ret->keyblob + OSSH_PKEY_BLOBLEN, ret->keyblob_len); - } - - if (ret->keyblob_len == 0 || !ret->keyblob) { -@@ -474,10 +498,18 @@ static struct openssh_key *load_openssh_key(const char *filename) - goto error; - } - -+ if (buf) { -+ buf_burn(buf); -+ buf_free(buf); -+ } - m_burn(buffer, sizeof(buffer)); - return ret; - - error: -+ if (buf) { -+ buf_burn(buf); -+ buf_free(buf); -+ } - m_burn(buffer, sizeof(buffer)); - if (ret) { - if (ret->keyblob) { -@@ -569,6 +601,57 @@ static sign_key *openssh_read(const char *filename, const char * UNUSED(passphra - #endif - } - -+ /* -+ * Now we have a decrypted key blob, which contains OpenSSH -+ * encoded private key. We must now untangle the OpenSSH format. -+ */ -+ if (key->type == OSSH_PKEY) { -+ blobbuf = buf_new(key->keyblob_len); -+ buf_putbytes(blobbuf, key->keyblob, key->keyblob_len); -+ buf_setpos(blobbuf, 0); -+ -+ /* limit length of private key blob */ -+ len = buf_getint(blobbuf); -+ buf_setlen(blobbuf, blobbuf->pos + len); -+ -+ type = DROPBEAR_SIGNKEY_ANY; -+ if (buf_get_pub_key(blobbuf, retkey, &type) -+ != DROPBEAR_SUCCESS) { -+ errmsg = "Error parsing OpenSSH key"; -+ goto ossh_error; -+ } -+ -+ /* restore full length */ -+ buf_setlen(blobbuf, key->keyblob_len); -+ -+ if (type != DROPBEAR_SIGNKEY_NONE) { -+ retkey->type = type; -+ /* limit length of private key blob */ -+ len = buf_getint(blobbuf); -+ buf_setlen(blobbuf, blobbuf->pos + len); -+#if DROPBEAR_ED25519 -+ if (type == DROPBEAR_SIGNKEY_ED25519) { -+ buf_incrpos(blobbuf, 8); -+ buf_eatstring(blobbuf); -+ buf_eatstring(blobbuf); -+ buf_incrpos(blobbuf, -SSH_SIGNKEY_ED25519_LEN-4); -+ if (buf_get_ed25519_priv_key(blobbuf, retkey->ed25519key) -+ == DROPBEAR_SUCCESS) { -+ errmsg = NULL; -+ retval = retkey; -+ goto error; -+ } -+ } -+#endif -+ } -+ -+ errmsg = "Unsupported OpenSSH key type"; -+ ossh_error: -+ sign_key_free(retkey); -+ retkey = NULL; -+ goto error; -+ } -+ - /* - * Now we have a decrypted key blob, which contains an ASN.1 - * encoded private key. We must now untangle the ASN.1. -@@ -1129,6 +1212,51 @@ static int openssh_write(const char *filename, sign_key *key, - } - #endif - -+#if DROPBEAR_ED25519 -+ if (key->type == DROPBEAR_SIGNKEY_ED25519) { -+ buffer *buf = buf_new(300); -+ keyblob = buf_new(100); -+ extrablob = buf_new(200); -+ -+ /* private key blob w/o header */ -+ buf_put_priv_key(keyblob, key, key->type); -+ buf_setpos(keyblob, 0); -+ buf_incrpos(keyblob, buf_getint(keyblob)); -+ len = buf_getint(keyblob); -+ -+ /* header */ -+ buf_putbytes(buf, OSSH_PKEY_BLOB, OSSH_PKEY_BLOBLEN); -+ -+ /* public key */ -+ buf_put_pub_key(buf, key, key->type); -+ -+ /* private key */ -+ buf_incrwritepos(extrablob, 4); -+ buf_put_pub_key(extrablob, key, key->type); -+ buf_putstring(extrablob, buf_getptr(keyblob, len), len); -+ /* comment */ -+ buf_putstring(extrablob, "", 0); -+ /* padding to cipher block length */ -+ len = (extrablob->len+8) & ~7; -+ for (i = 1; len - extrablob->len > 0; i++) -+ buf_putbyte(extrablob, i); -+ buf_setpos(extrablob, 0); -+ buf_putbytes(extrablob, "\0\0\0\0\0\0\0\0", 8); -+ buf_putbufstring(buf, extrablob); -+ -+ outlen = len = pos = buf->len; -+ outblob = (unsigned char*)m_malloc(outlen); -+ memcpy(outblob, buf->data, buf->len); -+ -+ buf_burn(buf); -+ buf_free(buf); -+ buf = NULL; -+ -+ header = "-----BEGIN OPENSSH PRIVATE KEY-----\n"; -+ footer = "-----END OPENSSH PRIVATE KEY-----\n"; -+ } -+#endif -+ - /* - * Padding on OpenSSH keys is deterministic. The number of - * padding bytes is always more than zero, and always at most -diff --git a/signkey.c b/signkey.c -index 88f06c7..a0af44a 100644 ---- a/signkey.c -+++ b/signkey.c -@@ -39,8 +39,11 @@ static const char * const signkey_names[DROPBEAR_SIGNKEY_NUM_NAMED] = { - #if DROPBEAR_ECDSA - "ecdsa-sha2-nistp256", - "ecdsa-sha2-nistp384", -- "ecdsa-sha2-nistp521" -+ "ecdsa-sha2-nistp521", - #endif /* DROPBEAR_ECDSA */ -+#if DROPBEAR_ED25519 -+ "ssh-ed25519", -+#endif /* DROPBEAR_ED25519 */ - }; - - /* malloc a new sign_key and set the dss and rsa keys to NULL */ -@@ -107,6 +110,10 @@ Be sure to check both (ret != NULL) and (*ret != NULL) */ - void ** - signkey_key_ptr(sign_key *key, enum signkey_type type) { - switch (type) { -+#if DROPBEAR_ED25519 -+ case DROPBEAR_SIGNKEY_ED25519: -+ return (void**)&key->ed25519key; -+#endif - #if DROPBEAR_ECDSA - #if DROPBEAR_ECC_256 - case DROPBEAR_SIGNKEY_ECDSA_NISTP256: -@@ -200,6 +207,17 @@ int buf_get_pub_key(buffer *buf, sign_key *key, enum signkey_type *type) { - } - } - #endif -+#if DROPBEAR_ED25519 -+ if (keytype == DROPBEAR_SIGNKEY_ED25519) { -+ ed25519_key_free(key->ed25519key); -+ key->ed25519key = m_malloc(sizeof(*key->ed25519key)); -+ ret = buf_get_ed25519_pub_key(buf, key->ed25519key); -+ if (ret == DROPBEAR_FAILURE) { -+ m_free(key->ed25519key); -+ key->ed25519key = NULL; -+ } -+ } -+#endif - - TRACE2(("leave buf_get_pub_key")) - -@@ -270,6 +288,17 @@ int buf_get_priv_key(buffer *buf, sign_key *key, enum signkey_type *type) { - } - } - #endif -+#if DROPBEAR_ED25519 -+ if (keytype == DROPBEAR_SIGNKEY_ED25519) { -+ ed25519_key_free(key->ed25519key); -+ key->ed25519key = m_malloc(sizeof(*key->ed25519key)); -+ ret = buf_get_ed25519_priv_key(buf, key->ed25519key); -+ if (ret == DROPBEAR_FAILURE) { -+ m_free(key->ed25519key); -+ key->ed25519key = NULL; -+ } -+ } -+#endif - - TRACE2(("leave buf_get_priv_key")) - -@@ -302,6 +331,11 @@ void buf_put_pub_key(buffer* buf, sign_key *key, enum signkey_type type) { - buf_put_ecdsa_pub_key(pubkeys, *eck); - } - } -+#endif -+#if DROPBEAR_ED25519 -+ if (type == DROPBEAR_SIGNKEY_ED25519) { -+ buf_put_ed25519_pub_key(pubkeys, key->ed25519key); -+ } - #endif - if (pubkeys->len == 0) { - dropbear_exit("Bad key types in buf_put_pub_key"); -@@ -341,6 +375,13 @@ void buf_put_priv_key(buffer* buf, sign_key *key, enum signkey_type type) { - return; - } - } -+#endif -+#if DROPBEAR_ED25519 -+ if (type == DROPBEAR_SIGNKEY_ED25519) { -+ buf_put_ed25519_priv_key(buf, key->ed25519key); -+ TRACE(("leave buf_put_priv_key: ed25519 done")) -+ return; -+ } - #endif - dropbear_exit("Bad key types in put pub key"); - } -@@ -379,6 +420,10 @@ void sign_key_free(sign_key *key) { - key->ecckey521 = NULL; - } - #endif -+#endif -+#if DROPBEAR_ED25519 -+ ed25519_key_free(key->ed25519key); -+ key->ed25519key = NULL; - #endif - - m_free(key->filename); -@@ -503,6 +548,11 @@ void buf_put_sign(buffer* buf, sign_key *key, enum signkey_type type, - buf_put_ecdsa_sign(sigblob, *eck, data_buf); - } - } -+#endif -+#if DROPBEAR_ED25519 -+ if (type == DROPBEAR_SIGNKEY_ED25519) { -+ buf_put_ed25519_sign(sigblob, key->ed25519key, data_buf); -+ } - #endif - if (sigblob->len == 0) { - dropbear_exit("Non-matching signing type"); -@@ -555,6 +605,14 @@ int buf_verify(buffer * buf, sign_key *key, const buffer *data_buf) { - } - } - #endif -+#if DROPBEAR_ED25519 -+ if (type == DROPBEAR_SIGNKEY_ED25519) { -+ if (key->ed25519key == NULL) { -+ dropbear_exit("No Ed25519 key to verify signature"); -+ } -+ return buf_ed25519_verify(buf, key->ed25519key, data_buf); -+ } -+#endif - - dropbear_exit("Non-matching signing type"); - return DROPBEAR_FAILURE; -diff --git a/signkey.h b/signkey.h -index 59df3ee..fa39a02 100644 ---- a/signkey.h -+++ b/signkey.h -@@ -28,6 +28,7 @@ - #include "buffer.h" - #include "dss.h" - #include "rsa.h" -+#include "ed25519.h" - - enum signkey_type { - #if DROPBEAR_RSA -@@ -41,6 +42,9 @@ enum signkey_type { - DROPBEAR_SIGNKEY_ECDSA_NISTP384, - DROPBEAR_SIGNKEY_ECDSA_NISTP521, - #endif /* DROPBEAR_ECDSA */ -+#if DROPBEAR_ED25519 -+ DROPBEAR_SIGNKEY_ED25519, -+#endif - DROPBEAR_SIGNKEY_NUM_NAMED, - DROPBEAR_SIGNKEY_ECDSA_KEYGEN = 70, /* just "ecdsa" for keygen */ - DROPBEAR_SIGNKEY_ANY = 80, -@@ -78,6 +82,9 @@ struct SIGN_key { - ecc_key * ecckey521; - #endif - #endif -+#if DROPBEAR_ED25519 -+ dropbear_ed25519_key * ed25519key; -+#endif - }; - - typedef struct SIGN_key sign_key; -diff --git a/ssh.h b/ssh.h -index f40b65a..db723b8 100644 ---- a/ssh.h -+++ b/ssh.h -@@ -105,6 +105,8 @@ - #define SSH_SIGNKEY_DSS_LEN 7 - #define SSH_SIGNKEY_RSA "ssh-rsa" - #define SSH_SIGNKEY_RSA_LEN 7 -+#define SSH_SIGNKEY_ED25519 "ssh-ed25519" -+#define SSH_SIGNKEY_ED25519_LEN 11 - - /* Agent commands. These aren't part of the spec, and are defined - * only on the openssh implementation. */ -diff --git a/svr-kex.c b/svr-kex.c -index 406ad97..af16d2f 100644 ---- a/svr-kex.c -+++ b/svr-kex.c -@@ -122,6 +122,11 @@ static void svr_ensure_hostkey() { - case DROPBEAR_SIGNKEY_ECDSA_NISTP521: - fn = ECDSA_PRIV_FILENAME; - break; -+#endif -+#if DROPBEAR_ED25519 -+ case DROPBEAR_SIGNKEY_ED25519: -+ fn = ED25519_PRIV_FILENAME; -+ break; - #endif - default: - dropbear_assert(0); -@@ -219,7 +224,8 @@ static void send_msg_kexdh_reply(mp_int *dh_e, buffer *ecdh_qs) { - { - struct kex_curve25519_param *param = gen_kexcurve25519_param(); - kexcurve25519_comb_key(param, ecdh_qs, svr_opts.hostkey); -- buf_putstring(ses.writepayload, (const char*)param->pub, CURVE25519_LEN); -+ -+ buf_putstring(ses.writepayload, param->pub, CURVE25519_LEN); - free_kexcurve25519_param(param); - } - break; -diff --git a/svr-runopts.c b/svr-runopts.c -index d7a0d5a..d430825 100644 ---- a/svr-runopts.c -+++ b/svr-runopts.c -@@ -57,6 +57,9 @@ static void printhelp(const char * progname) { - #if DROPBEAR_ECDSA - " ecdsa %s\n" - #endif -+#if DROPBEAR_ED25519 -+ " ed25519 %s\n" -+#endif - #if DROPBEAR_DELAY_HOSTKEY - "-R Create hostkeys as required\n" - #endif -@@ -116,6 +119,9 @@ static void printhelp(const char * progname) { - #endif - #if DROPBEAR_ECDSA - ECDSA_PRIV_FILENAME, -+#endif -+#if DROPBEAR_ED25519 -+ ED25519_PRIV_FILENAME, - #endif - MAX_AUTH_TRIES, - DROPBEAR_MAX_PORTS, DROPBEAR_DEFPORT, DROPBEAR_PIDFILE, -@@ -538,6 +544,13 @@ static void loadhostkey(const char *keyfile, int fatal_duplicate) { - } - #endif - #endif /* DROPBEAR_ECDSA */ -+ -+#if DROPBEAR_ED25519 -+ if (type == DROPBEAR_SIGNKEY_ED25519) { -+ loadhostkey_helper("ed25519", (void**)&read_key->ed25519key, (void**)&svr_opts.hostkey->ed25519key, fatal_duplicate); -+ } -+#endif -+ - sign_key_free(read_key); - TRACE(("leave loadhostkey")) - } -@@ -578,6 +591,9 @@ void load_all_hostkeys() { - - #if DROPBEAR_ECDSA - loadhostkey(ECDSA_PRIV_FILENAME, 0); -+#endif -+#if DROPBEAR_ED25519 -+ loadhostkey(ED25519_PRIV_FILENAME, 0); - #endif - } - -@@ -642,6 +658,14 @@ void load_all_hostkeys() { - #endif - #endif /* DROPBEAR_ECDSA */ - -+#if DROPBEAR_ED25519 -+ if (!svr_opts.delay_hostkey && !svr_opts.hostkey->ed25519key) { -+ disablekey(DROPBEAR_SIGNKEY_ED25519); -+ } else { -+ any_keys = 1; -+ } -+#endif -+ - if (!any_keys) { - dropbear_exit("No hostkeys available. 'dropbear -R' may be useful or run dropbearkey."); - } -diff --git a/sysoptions.h b/sysoptions.h -index cfd5469..2c27caf 100644 ---- a/sysoptions.h -+++ b/sysoptions.h -@@ -145,7 +145,8 @@ If you test it please contact the Dropbear author */ - #define DROPBEAR_SHA384 (DROPBEAR_ECC_384) - /* LTC SHA384 depends on SHA512 */ - #define DROPBEAR_SHA512 ((DROPBEAR_SHA2_512_HMAC) || (DROPBEAR_ECC_521) \ -- || (DROPBEAR_SHA384) || (DROPBEAR_DH_GROUP16)) -+ || (DROPBEAR_SHA384) || (DROPBEAR_DH_GROUP16) \ -+ || (DROPBEAR_ED25519)) - #define DROPBEAR_MD5 (DROPBEAR_MD5_HMAC) - - #define DROPBEAR_DH_GROUP14 ((DROPBEAR_DH_GROUP14_SHA256) || (DROPBEAR_DH_GROUP14_SHA1)) -@@ -186,7 +187,7 @@ If you test it please contact the Dropbear author */ - /* For a 4096 bit DSS key, empirically determined */ - #define MAX_PRIVKEY_SIZE 1700 - --#define MAX_HOSTKEYS 3 -+#define MAX_HOSTKEYS 4 - - /* The maximum size of the bignum portion of the kexhash buffer */ - /* Sect. 8 of the transport rfc 4253, K_S + e + f + K */ -@@ -252,7 +253,7 @@ If you test it please contact the Dropbear author */ - #error "At least one encryption algorithm must be enabled. AES128 is recommended." - #endif - --#if !(DROPBEAR_RSA || DROPBEAR_DSS || DROPBEAR_ECDSA) -+#if !(DROPBEAR_RSA || DROPBEAR_DSS || DROPBEAR_ECDSA || DROPBEAR_ED25519) - #error "At least one hostkey or public-key algorithm must be enabled; RSA is recommended." - #endif - --- -2.17.1 - diff --git a/package/network/services/dropbear/patches/021-backport-chacha20-poly1305-support.patch b/package/network/services/dropbear/patches/021-backport-chacha20-poly1305-support.patch deleted file mode 100644 index 2120b4593e..0000000000 --- a/package/network/services/dropbear/patches/021-backport-chacha20-poly1305-support.patch +++ /dev/null @@ -1,693 +0,0 @@ -From 3cdf9ec918b37c17e12b33e4c244944d1ee836ca Mon Sep 17 00:00:00 2001 -From: Vladislav Grishenko -Date: Mon, 6 Apr 2020 23:28:09 +0500 -Subject: [PATCH] Add Chacha20-Poly1305 authenticated encryption - -* Add general AEAD approach. -* Add chacha20-poly1305@openssh.com algo using LibTomCrypt chacha and - poly1305 routines. - -Chacha20-Poly1305 is generally faster than AES256 on CPU w/o dedicated -AES instructions, having the same key size. -Compiling in will add ~5,5kB to binary size on x86-64. ---- - Makefile.in | 2 +- - algo.h | 8 ++ - chachapoly.c | 148 ++++++++++++++++++++ - chachapoly.h | 44 ++++++ - common-algo.c | 11 +- - common-kex.c | 44 ++++-- - default_options.h | 6 + - libtomcrypt/src/headers/tomcrypt_dropbear.h | 4 + - packet.c | 145 +++++++++++++------ - session.h | 4 + - sysoptions.h | 8 +- - 11 files changed, 368 insertions(+), 56 deletions(-) - create mode 100644 chachapoly.c - create mode 100644 chachapoly.h - -diff --git a/Makefile.in b/Makefile.in -index aaf7b3b..3437cb2 100644 ---- a/Makefile.in -+++ b/Makefile.in -@@ -53,7 +53,7 @@ CLIOBJS=cli-main.o cli-auth.o cli-authpasswd.o cli-kex.o \ - CLISVROBJS=common-session.o packet.o common-algo.o common-kex.o \ - common-channel.o common-chansession.o termcodes.o loginrec.o \ - tcp-accept.o listener.o process-packet.o dh_groups.o \ -- common-runopts.o circbuffer.o list.o netio.o -+ common-runopts.o circbuffer.o list.o netio.o chachapoly.o - - KEYOBJS=dropbearkey.o - -diff --git a/algo.h b/algo.h -index b12fb94..efd0d73 100644 ---- a/algo.h -+++ b/algo.h -@@ -72,6 +72,14 @@ struct dropbear_cipher_mode { - unsigned long len, void *cipher_state); - int (*decrypt)(const unsigned char *ct, unsigned char *pt, - unsigned long len, void *cipher_state); -+ int (*aead_crypt)(unsigned int seq, -+ const unsigned char *in, unsigned char *out, -+ unsigned long len, unsigned long taglen, -+ void *cipher_state, int direction); -+ int (*aead_getlength)(unsigned int seq, -+ const unsigned char *in, unsigned int *outlen, -+ unsigned long len, void *cipher_state); -+ const struct dropbear_hash *aead_mac; - }; - - struct dropbear_hash { -diff --git a/chachapoly.c b/chachapoly.c -new file mode 100644 -index 0000000..8fb06c5 ---- /dev/null -+++ b/chachapoly.c -@@ -0,0 +1,148 @@ -+/* -+ * Dropbear SSH -+ * -+ * Copyright (c) 2002,2003 Matt Johnston -+ * Copyright (c) 2020 by Vladislav Grishenko -+ * 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 "algo.h" -+#include "dbutil.h" -+#include "chachapoly.h" -+ -+#if DROPBEAR_CHACHA20POLY1305 -+ -+#define CHACHA20_KEY_LEN 32 -+#define CHACHA20_BLOCKSIZE 8 -+#define POLY1305_KEY_LEN 32 -+#define POLY1305_TAG_LEN 16 -+ -+static const struct ltc_cipher_descriptor dummy = {.name = NULL}; -+ -+static const struct dropbear_hash dropbear_chachapoly_mac = -+ {NULL, POLY1305_KEY_LEN, POLY1305_TAG_LEN}; -+ -+const struct dropbear_cipher dropbear_chachapoly = -+ {&dummy, CHACHA20_KEY_LEN*2, CHACHA20_BLOCKSIZE}; -+ -+static int dropbear_chachapoly_start(int UNUSED(cipher), const unsigned char* UNUSED(IV), -+ const unsigned char *key, int keylen, -+ int UNUSED(num_rounds), dropbear_chachapoly_state *state) { -+ int err; -+ -+ TRACE2(("enter dropbear_chachapoly_start")) -+ -+ if (keylen != CHACHA20_KEY_LEN*2) { -+ return CRYPT_ERROR; -+ } -+ -+ if ((err = chacha_setup(&state->chacha, key, -+ CHACHA20_KEY_LEN, 20)) != CRYPT_OK) { -+ return err; -+ } -+ -+ if ((err = chacha_setup(&state->header, key + CHACHA20_KEY_LEN, -+ CHACHA20_KEY_LEN, 20) != CRYPT_OK)) { -+ return err; -+ } -+ -+ TRACE2(("leave dropbear_chachapoly_start")) -+ return CRYPT_OK; -+} -+ -+static int dropbear_chachapoly_crypt(unsigned int seq, -+ const unsigned char *in, unsigned char *out, -+ unsigned long len, unsigned long taglen, -+ dropbear_chachapoly_state *state, int direction) { -+ poly1305_state poly; -+ unsigned char seqbuf[8], key[POLY1305_KEY_LEN], tag[POLY1305_TAG_LEN]; -+ int err; -+ -+ TRACE2(("enter dropbear_chachapoly_crypt")) -+ -+ if (len < 4 || taglen != POLY1305_TAG_LEN) { -+ return CRYPT_ERROR; -+ } -+ -+ STORE64H((uint64_t)seq, seqbuf); -+ chacha_ivctr64(&state->chacha, seqbuf, sizeof(seqbuf), 0); -+ if ((err = chacha_keystream(&state->chacha, key, sizeof(key))) != CRYPT_OK) { -+ return err; -+ } -+ -+ poly1305_init(&poly, key, sizeof(key)); -+ if (direction == LTC_DECRYPT) { -+ poly1305_process(&poly, in, len); -+ poly1305_done(&poly, tag, &taglen); -+ if (constant_time_memcmp(in + len, tag, taglen) != 0) { -+ return CRYPT_ERROR; -+ } -+ } -+ -+ chacha_ivctr64(&state->header, seqbuf, sizeof(seqbuf), 0); -+ if ((err = chacha_crypt(&state->header, in, 4, out)) != CRYPT_OK) { -+ return err; -+ } -+ -+ chacha_ivctr64(&state->chacha, seqbuf, sizeof(seqbuf), 1); -+ if ((err = chacha_crypt(&state->chacha, in + 4, len - 4, out + 4)) != CRYPT_OK) { -+ return err; -+ } -+ -+ if (direction == LTC_ENCRYPT) { -+ poly1305_process(&poly, out, len); -+ poly1305_done(&poly, out + len, &taglen); -+ } -+ -+ TRACE2(("leave dropbear_chachapoly_crypt")) -+ return CRYPT_OK; -+} -+ -+static int dropbear_chachapoly_getlength(unsigned int seq, -+ const unsigned char *in, unsigned int *outlen, -+ unsigned long len, dropbear_chachapoly_state *state) { -+ unsigned char seqbuf[8], buf[4]; -+ int err; -+ -+ TRACE2(("enter dropbear_chachapoly_getlength")) -+ -+ if (len < sizeof(buf)) { -+ return CRYPT_ERROR; -+ } -+ -+ STORE64H((uint64_t)seq, seqbuf); -+ chacha_ivctr64(&state->header, seqbuf, sizeof(seqbuf), 0); -+ if ((err = chacha_crypt(&state->header, in, sizeof(buf), buf)) != CRYPT_OK) { -+ return err; -+ } -+ -+ LOAD32H(*outlen, buf); -+ -+ TRACE2(("leave dropbear_chachapoly_getlength")) -+ return CRYPT_OK; -+} -+ -+const struct dropbear_cipher_mode dropbear_mode_chachapoly = -+ {(void *)dropbear_chachapoly_start, NULL, NULL, -+ (void *)dropbear_chachapoly_crypt, -+ (void *)dropbear_chachapoly_getlength, &dropbear_chachapoly_mac}; -+ -+#endif /* DROPBEAR_CHACHA20POLY1305 */ -diff --git a/chachapoly.h b/chachapoly.h -new file mode 100644 -index 0000000..5a7c5b2 ---- /dev/null -+++ b/chachapoly.h -@@ -0,0 +1,44 @@ -+/* -+ * Dropbear SSH -+ * -+ * Copyright (c) 2002,2003 Matt Johnston -+ * Copyright (c) 2020 by Vladislav Grishenko -+ * 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 DROPBEAR_DROPBEAR_CHACHAPOLY_H_ -+#define DROPBEAR_DROPBEAR_CHACHAPOLY_H_ -+ -+#include "includes.h" -+#include "algo.h" -+ -+#if DROPBEAR_CHACHA20POLY1305 -+ -+typedef struct { -+ chacha_state chacha; -+ chacha_state header; -+} dropbear_chachapoly_state; -+ -+extern const struct dropbear_cipher dropbear_chachapoly; -+extern const struct dropbear_cipher_mode dropbear_mode_chachapoly; -+ -+#endif /* DROPBEAR_CHACHA20POLY1305 */ -+ -+#endif /* DROPBEAR_DROPBEAR_CHACHAPOLY_H_ */ -diff --git a/common-algo.c b/common-algo.c -index 558aad2..1436456 100644 ---- a/common-algo.c -+++ b/common-algo.c -@@ -30,6 +30,7 @@ - #include "dh_groups.h" - #include "ltc_prng.h" - #include "ecc.h" -+#include "chachapoly.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*/ -@@ -86,11 +87,11 @@ const struct dropbear_cipher dropbear_nocipher = - * about the symmetric_CBC vs symmetric_CTR cipher_state pointer */ - #if DROPBEAR_ENABLE_CBC_MODE - const struct dropbear_cipher_mode dropbear_mode_cbc = -- {(void*)cbc_start, (void*)cbc_encrypt, (void*)cbc_decrypt}; -+ {(void*)cbc_start, (void*)cbc_encrypt, (void*)cbc_decrypt, NULL, NULL, NULL}; - #endif /* DROPBEAR_ENABLE_CBC_MODE */ - - const struct dropbear_cipher_mode dropbear_mode_none = -- {void_start, void_cipher, void_cipher}; -+ {void_start, void_cipher, void_cipher, NULL, NULL, NULL}; - - #if DROPBEAR_ENABLE_CTR_MODE - /* a wrapper to make ctr_start and cbc_start look the same */ -@@ -101,7 +102,7 @@ static int dropbear_big_endian_ctr_start(int cipher, - return ctr_start(cipher, IV, key, keylen, num_rounds, CTR_COUNTER_BIG_ENDIAN, ctr); - } - const struct dropbear_cipher_mode dropbear_mode_ctr = -- {(void*)dropbear_big_endian_ctr_start, (void*)ctr_encrypt, (void*)ctr_decrypt}; -+ {(void*)dropbear_big_endian_ctr_start, (void*)ctr_encrypt, (void*)ctr_decrypt, NULL, NULL, NULL}; - #endif /* DROPBEAR_ENABLE_CTR_MODE */ - - /* Mapping of ssh hashes to libtomcrypt hashes, including keysize etc. -@@ -137,6 +138,10 @@ const struct dropbear_hash dropbear_nohash = - * that is also supported by the server will get used. */ - - algo_type sshciphers[] = { -+#if DROPBEAR_CHACHA20POLY1305 -+ {"chacha20-poly1305@openssh.com", 0, &dropbear_chachapoly, 1, &dropbear_mode_chachapoly}, -+#endif -+ - #if DROPBEAR_ENABLE_CTR_MODE - #if DROPBEAR_AES128 - {"aes128-ctr", 0, &dropbear_aes128, 1, &dropbear_mode_ctr}, -diff --git a/common-kex.c b/common-kex.c -index 16b7e27..5e2923f 100644 ---- a/common-kex.c -+++ b/common-kex.c -@@ -329,9 +329,12 @@ static void gen_new_keys() { - 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); -- if (recv_cipher < 0) -- dropbear_exit("Crypto error"); -+ int recv_cipher = -1; -+ if (ses.newkeys->recv.algo_crypt->cipherdesc->name != NULL) { -+ recv_cipher = find_cipher(ses.newkeys->recv.algo_crypt->cipherdesc->name); -+ if (recv_cipher < 0) -+ dropbear_exit("Crypto error"); -+ } - if (ses.newkeys->recv.crypt_mode->start(recv_cipher, - recv_IV, recv_key, - ses.newkeys->recv.algo_crypt->keysize, 0, -@@ -341,9 +344,12 @@ static void gen_new_keys() { - } - - if (ses.newkeys->trans.algo_crypt->cipherdesc != NULL) { -- int trans_cipher = find_cipher(ses.newkeys->trans.algo_crypt->cipherdesc->name); -- if (trans_cipher < 0) -- dropbear_exit("Crypto error"); -+ int trans_cipher = -1; -+ if (ses.newkeys->trans.algo_crypt->cipherdesc->name != NULL) { -+ trans_cipher = find_cipher(ses.newkeys->trans.algo_crypt->cipherdesc->name); -+ if (trans_cipher < 0) -+ dropbear_exit("Crypto error"); -+ } - if (ses.newkeys->trans.crypt_mode->start(trans_cipher, - trans_IV, trans_key, - ses.newkeys->trans.algo_crypt->keysize, 0, -@@ -868,19 +874,29 @@ static void read_kex_algos() { - - /* mac_algorithms_client_to_server */ - c2s_hash_algo = buf_match_algo(ses.payload, sshhashes, NULL, NULL); -+#if DROPBEAR_AEAD_MODE -+ if (((struct dropbear_cipher_mode*)c2s_cipher_algo->mode)->aead_crypt != NULL) { -+ c2s_hash_algo = NULL; -+ } else -+#endif - if (c2s_hash_algo == NULL) { - erralgo = "mac c->s"; - goto error; - } -- TRACE(("hash c2s is %s", c2s_hash_algo->name)) -+ TRACE(("hash c2s is %s", c2s_hash_algo ? c2s_hash_algo->name : "")) - - /* mac_algorithms_server_to_client */ - s2c_hash_algo = buf_match_algo(ses.payload, sshhashes, NULL, NULL); -+#if DROPBEAR_AEAD_MODE -+ if (((struct dropbear_cipher_mode*)s2c_cipher_algo->mode)->aead_crypt != NULL) { -+ s2c_hash_algo = NULL; -+ } else -+#endif - if (s2c_hash_algo == NULL) { - erralgo = "mac s->c"; - goto error; - } -- TRACE(("hash s2c is %s", s2c_hash_algo->name)) -+ TRACE(("hash s2c is %s", s2c_hash_algo ? s2c_hash_algo->name : "")) - - /* compression_algorithms_client_to_server */ - c2s_comp_algo = buf_match_algo(ses.payload, ses.compress_algos, NULL, NULL); -@@ -925,8 +941,14 @@ static void read_kex_algos() { - ses.newkeys->trans.crypt_mode = - (struct dropbear_cipher_mode*)c2s_cipher_algo->mode; - ses.newkeys->recv.algo_mac = -+#if DROPBEAR_AEAD_MODE -+ s2c_hash_algo == NULL ? ses.newkeys->recv.crypt_mode->aead_mac : -+#endif - (struct dropbear_hash*)s2c_hash_algo->data; - ses.newkeys->trans.algo_mac = -+#if DROPBEAR_AEAD_MODE -+ c2s_hash_algo == NULL ? ses.newkeys->trans.crypt_mode->aead_mac : -+#endif - (struct dropbear_hash*)c2s_hash_algo->data; - ses.newkeys->recv.algo_comp = s2c_comp_algo->val; - ses.newkeys->trans.algo_comp = c2s_comp_algo->val; -@@ -941,8 +963,14 @@ static void read_kex_algos() { - ses.newkeys->trans.crypt_mode = - (struct dropbear_cipher_mode*)s2c_cipher_algo->mode; - ses.newkeys->recv.algo_mac = -+#if DROPBEAR_AEAD_MODE -+ c2s_hash_algo == NULL ? ses.newkeys->recv.crypt_mode->aead_mac : -+#endif - (struct dropbear_hash*)c2s_hash_algo->data; - ses.newkeys->trans.algo_mac = -+#if DROPBEAR_AEAD_MODE -+ s2c_hash_algo == NULL ? ses.newkeys->trans.crypt_mode->aead_mac : -+#endif - (struct dropbear_hash*)s2c_hash_algo->data; - ses.newkeys->recv.algo_comp = c2s_comp_algo->val; - ses.newkeys->trans.algo_comp = s2c_comp_algo->val; -diff --git a/default_options.h b/default_options.h -index bafbb07..1a2ab10 100644 ---- a/default_options.h -+++ b/default_options.h -@@ -99,6 +99,12 @@ IMPORTANT: Some options will require "make clean" after changes */ - * and forwards compatibility */ - #define DROPBEAR_ENABLE_CTR_MODE 1 - -+/* Enable Chacha20-Poly1305 authenticated encryption mode. This is -+ * generally faster than AES256 on CPU w/o dedicated AES instructions, -+ * having the same key size. -+ * Compiling in will add ~5,5kB to binary size on x86-64 */ -+#define DROPBEAR_CHACHA20POLY1305 1 -+ - /* Message integrity. sha2-256 is recommended as a default, - sha1 for compatibility */ - #define DROPBEAR_SHA1_HMAC 1 -diff --git a/libtomcrypt/src/headers/tomcrypt_dropbear.h b/libtomcrypt/src/headers/tomcrypt_dropbear.h -index b0ce45b..59960e5 100644 ---- a/libtomcrypt/src/headers/tomcrypt_dropbear.h -+++ b/libtomcrypt/src/headers/tomcrypt_dropbear.h -@@ -35,6 +35,10 @@ - #define LTC_CTR_MODE - #endif - -+#if DROPBEAR_CHACHA20POLY1305 -+#define LTC_CHACHA -+#define LTC_POLY1305 -+#endif - - #if DROPBEAR_SHA512 - #define LTC_SHA512 -diff --git a/packet.c b/packet.c -index 9fda0d6..0454726 100644 ---- a/packet.c -+++ b/packet.c -@@ -215,7 +215,7 @@ static int read_packet_init() { - - unsigned int maxlen; - int slen; -- unsigned int len; -+ unsigned int len, plen; - unsigned int blocksize; - unsigned int macsize; - -@@ -254,21 +254,35 @@ static int read_packet_init() { - /* now we have the first block, need to get packet length, so we decrypt - * the first block (only need first 4 bytes) */ - buf_setpos(ses.readbuf, 0); -- if (ses.keys->recv.crypt_mode->decrypt(buf_getptr(ses.readbuf, blocksize), -- buf_getwriteptr(ses.readbuf, blocksize), -- blocksize, -- &ses.keys->recv.cipher_state) != CRYPT_OK) { -- dropbear_exit("Error decrypting"); -+#if DROPBEAR_AEAD_MODE -+ if (ses.keys->recv.crypt_mode->aead_crypt) { -+ if (ses.keys->recv.crypt_mode->aead_getlength(ses.recvseq, -+ buf_getptr(ses.readbuf, blocksize), &plen, -+ blocksize, -+ &ses.keys->recv.cipher_state) != CRYPT_OK) { -+ dropbear_exit("Error decrypting"); -+ } -+ len = plen + 4 + macsize; -+ } else -+#endif -+ { -+ if (ses.keys->recv.crypt_mode->decrypt(buf_getptr(ses.readbuf, blocksize), -+ buf_getwriteptr(ses.readbuf, blocksize), -+ blocksize, -+ &ses.keys->recv.cipher_state) != CRYPT_OK) { -+ dropbear_exit("Error decrypting"); -+ } -+ plen = buf_getint(ses.readbuf) + 4; -+ len = plen + macsize; - } -- len = buf_getint(ses.readbuf) + 4 + macsize; - - TRACE2(("packet size is %u, block %u mac %u", len, blocksize, macsize)) - - - /* check packet length */ - if ((len > RECV_MAX_PACKET_LEN) || -- (len < MIN_PACKET_LEN + macsize) || -- ((len - macsize) % blocksize != 0)) { -+ (plen < blocksize) || -+ (plen % blocksize != 0)) { - dropbear_exit("Integrity error (bad packet size %u)", len); - } - -@@ -294,23 +308,42 @@ void decrypt_packet() { - - ses.kexstate.datarecv += ses.readbuf->len; - -- /* we've already decrypted the first blocksize in read_packet_init */ -- buf_setpos(ses.readbuf, blocksize); -- -- /* decrypt it in-place */ -- len = ses.readbuf->len - macsize - ses.readbuf->pos; -- if (ses.keys->recv.crypt_mode->decrypt( -- buf_getptr(ses.readbuf, len), -- buf_getwriteptr(ses.readbuf, len), -- len, -- &ses.keys->recv.cipher_state) != CRYPT_OK) { -- dropbear_exit("Error decrypting"); -- } -- buf_incrpos(ses.readbuf, len); -+#if DROPBEAR_AEAD_MODE -+ if (ses.keys->recv.crypt_mode->aead_crypt) { -+ /* first blocksize is not decrypted yet */ -+ buf_setpos(ses.readbuf, 0); -+ -+ /* decrypt it in-place */ -+ len = ses.readbuf->len - macsize - ses.readbuf->pos; -+ if (ses.keys->recv.crypt_mode->aead_crypt(ses.recvseq, -+ buf_getptr(ses.readbuf, len + macsize), -+ buf_getwriteptr(ses.readbuf, len), -+ len, macsize, -+ &ses.keys->recv.cipher_state, LTC_DECRYPT) != CRYPT_OK) { -+ dropbear_exit("Error decrypting"); -+ } -+ buf_incrpos(ses.readbuf, len); -+ } else -+#endif -+ { -+ /* we've already decrypted the first blocksize in read_packet_init */ -+ buf_setpos(ses.readbuf, blocksize); -+ -+ /* decrypt it in-place */ -+ len = ses.readbuf->len - macsize - ses.readbuf->pos; -+ if (ses.keys->recv.crypt_mode->decrypt( -+ buf_getptr(ses.readbuf, len), -+ buf_getwriteptr(ses.readbuf, len), -+ len, -+ &ses.keys->recv.cipher_state) != CRYPT_OK) { -+ dropbear_exit("Error decrypting"); -+ } -+ buf_incrpos(ses.readbuf, len); - -- /* check the hmac */ -- if (checkmac() != DROPBEAR_SUCCESS) { -- dropbear_exit("Integrity error"); -+ /* check the hmac */ -+ if (checkmac() != DROPBEAR_SUCCESS) { -+ dropbear_exit("Integrity error"); -+ } - } - - /* get padding length */ -@@ -557,9 +590,16 @@ void encrypt_packet() { - buf_setpos(ses.writepayload, 0); - buf_setlen(ses.writepayload, 0); - -- /* length of padding - packet length must be a multiple of blocksize, -- * with a minimum of 4 bytes of padding */ -- padlen = blocksize - (writebuf->len) % blocksize; -+ /* length of padding - packet length excluding the packetlength uint32 -+ * field in aead mode must be a multiple of blocksize, with a minimum of -+ * 4 bytes of padding */ -+ len = writebuf->len; -+#if DROPBEAR_AEAD_MODE -+ if (ses.keys->trans.crypt_mode->aead_crypt) { -+ len -= 4; -+ } -+#endif -+ padlen = blocksize - len % blocksize; - if (padlen < 4) { - padlen += blocksize; - } -@@ -579,23 +619,42 @@ void encrypt_packet() { - buf_incrlen(writebuf, padlen); - genrandom(buf_getptr(writebuf, padlen), padlen); - -- make_mac(ses.transseq, &ses.keys->trans, writebuf, writebuf->len, mac_bytes); -+#if DROPBEAR_AEAD_MODE -+ if (ses.keys->trans.crypt_mode->aead_crypt) { -+ /* do the actual encryption, in-place */ -+ buf_setpos(writebuf, 0); -+ /* encrypt it in-place*/ -+ len = writebuf->len; -+ buf_incrlen(writebuf, mac_size); -+ if (ses.keys->trans.crypt_mode->aead_crypt(ses.transseq, -+ buf_getptr(writebuf, len), -+ buf_getwriteptr(writebuf, len + mac_size), -+ len, mac_size, -+ &ses.keys->trans.cipher_state, LTC_ENCRYPT) != CRYPT_OK) { -+ dropbear_exit("Error encrypting"); -+ } -+ buf_incrpos(writebuf, len + mac_size); -+ } else -+#endif -+ { -+ make_mac(ses.transseq, &ses.keys->trans, writebuf, writebuf->len, mac_bytes); -+ -+ /* do the actual encryption, in-place */ -+ buf_setpos(writebuf, 0); -+ /* encrypt it in-place*/ -+ len = writebuf->len; -+ if (ses.keys->trans.crypt_mode->encrypt( -+ buf_getptr(writebuf, len), -+ buf_getwriteptr(writebuf, len), -+ len, -+ &ses.keys->trans.cipher_state) != CRYPT_OK) { -+ dropbear_exit("Error encrypting"); -+ } -+ buf_incrpos(writebuf, len); - -- /* do the actual encryption, in-place */ -- buf_setpos(writebuf, 0); -- /* encrypt it in-place*/ -- len = writebuf->len; -- if (ses.keys->trans.crypt_mode->encrypt( -- buf_getptr(writebuf, len), -- buf_getwriteptr(writebuf, len), -- len, -- &ses.keys->trans.cipher_state) != CRYPT_OK) { -- dropbear_exit("Error encrypting"); -+ /* stick the MAC on it */ -+ buf_putbytes(writebuf, mac_bytes, mac_size); - } -- buf_incrpos(writebuf, len); -- -- /* stick the MAC on it */ -- buf_putbytes(writebuf, mac_bytes, mac_size); - - /* Update counts */ - ses.kexstate.datatrans += writebuf->len; -diff --git a/session.h b/session.h -index e436882..a8f8914 100644 ---- a/session.h -+++ b/session.h -@@ -41,6 +41,7 @@ - #include "chansession.h" - #include "dbutil.h" - #include "netio.h" -+#include "chachapoly.h" - - void common_session_init(int sock_in, int sock_out); - void session_loop(void(*loophandler)(void)) ATTRIB_NORETURN; -@@ -80,6 +81,9 @@ struct key_context_directional { - symmetric_CBC cbc; - #if DROPBEAR_ENABLE_CTR_MODE - symmetric_CTR ctr; -+#endif -+#if DROPBEAR_CHACHA20POLY1305 -+ dropbear_chachapoly_state chachapoly; - #endif - } cipher_state; - unsigned char mackey[MAX_MAC_LEN]; -diff --git a/sysoptions.h b/sysoptions.h -index 2c27caf..2432779 100644 ---- a/sysoptions.h -+++ b/sysoptions.h -@@ -92,7 +92,11 @@ - #define MD5_HASH_SIZE 16 - #define MAX_HASH_SIZE 64 /* sha512 */ - -+#if DROPBEAR_CHACHA20POLY1305 -+#define MAX_KEY_LEN 64 /* 2 x 256 bits for chacha20 */ -+#else - #define MAX_KEY_LEN 32 /* 256 bits for aes256 etc */ -+#endif - #define MAX_IV_LEN 20 /* must be same as max blocksize, */ - - #if DROPBEAR_SHA2_512_HMAC -@@ -207,6 +211,8 @@ If you test it please contact the Dropbear author */ - - #define DROPBEAR_TWOFISH ((DROPBEAR_TWOFISH256) || (DROPBEAR_TWOFISH128)) - -+#define DROPBEAR_AEAD_MODE ((DROPBEAR_CHACHA20POLY1305)) -+ - #define DROPBEAR_CLI_ANYTCPFWD ((DROPBEAR_CLI_REMOTETCPFWD) || (DROPBEAR_CLI_LOCALTCPFWD)) - - #define DROPBEAR_TCP_ACCEPT ((DROPBEAR_CLI_LOCALTCPFWD) || (DROPBEAR_SVR_REMOTETCPFWD)) -@@ -249,7 +255,7 @@ If you test it please contact the Dropbear author */ - #endif - - #if !(DROPBEAR_AES128 || DROPBEAR_3DES || DROPBEAR_AES256 || DROPBEAR_BLOWFISH \ -- || DROPBEAR_TWOFISH256 || DROPBEAR_TWOFISH128) -+ || DROPBEAR_TWOFISH256 || DROPBEAR_TWOFISH128 || DROPBEAR_CHACHA20POLY1305) - #error "At least one encryption algorithm must be enabled. AES128 is recommended." - #endif - --- -2.17.1 - diff --git a/package/network/services/dropbear/patches/100-pubkey_path.patch b/package/network/services/dropbear/patches/100-pubkey_path.patch index 732d84078f..af3fbb336b 100644 --- a/package/network/services/dropbear/patches/100-pubkey_path.patch +++ b/package/network/services/dropbear/patches/100-pubkey_path.patch @@ -1,6 +1,6 @@ --- a/svr-authpubkey.c +++ b/svr-authpubkey.c -@@ -338,14 +338,19 @@ static int checkpubkey(const char* algo, +@@ -386,14 +386,19 @@ static int checkpubkey(const char* keyal goto out; } @@ -28,7 +28,7 @@ #if DROPBEAR_SVR_MULTIUSER /* open the file as the authenticating user. */ -@@ -426,27 +431,36 @@ static int checkpubkeyperms() { +@@ -474,27 +479,36 @@ static int checkpubkeyperms() { goto out; } diff --git a/package/network/services/dropbear/patches/110-change_user.patch b/package/network/services/dropbear/patches/110-change_user.patch index 27e7fbaf4f..f66b319100 100644 --- a/package/network/services/dropbear/patches/110-change_user.patch +++ b/package/network/services/dropbear/patches/110-change_user.patch @@ -1,6 +1,6 @@ --- a/svr-chansession.c +++ b/svr-chansession.c -@@ -953,12 +953,12 @@ static void execchild(const void *user_d +@@ -950,12 +950,12 @@ static void execchild(const void *user_d /* We can only change uid/gid as root ... */ if (getuid() == 0) { diff --git a/package/network/services/dropbear/patches/160-lto-jobserver.patch b/package/network/services/dropbear/patches/160-lto-jobserver.patch index 02765335d3..dbba613ac3 100644 --- a/package/network/services/dropbear/patches/160-lto-jobserver.patch +++ b/package/network/services/dropbear/patches/160-lto-jobserver.patch @@ -1,11 +1,11 @@ --- a/Makefile.in +++ b/Makefile.in -@@ -189,17 +189,17 @@ dropbearkey: $(dropbearkeyobjs) +@@ -199,17 +199,17 @@ dropbearkey: $(dropbearkeyobjs) dropbearconvert: $(dropbearconvertobjs) dropbear: $(HEADERS) $(LIBTOM_DEPS) Makefile -- $(CC) $(LDFLAGS) -o $@$(EXEEXT) $($@objs) $(LIBTOM_LIBS) $(LIBS) @CRYPTLIB@ -+ +$(CC) $(LDFLAGS) -o $@$(EXEEXT) $($@objs) $(LIBTOM_LIBS) $(LIBS) @CRYPTLIB@ +- $(CC) $(LDFLAGS) -o $@$(EXEEXT) $($@objs) $(LIBTOM_LIBS) $(LIBS) @CRYPTLIB@ $(PLUGIN_LIBS) ++ +$(CC) $(LDFLAGS) -o $@$(EXEEXT) $($@objs) $(LIBTOM_LIBS) $(LIBS) @CRYPTLIB@ $(PLUGIN_LIBS) dbclient: $(HEADERS) $(LIBTOM_DEPS) Makefile - $(CC) $(LDFLAGS) -o $@$(EXEEXT) $($@objs) $(LIBTOM_LIBS) $(LIBS) @@ -22,7 +22,7 @@ # multi-binary compilation. -@@ -210,7 +210,7 @@ ifeq ($(MULTI),1) +@@ -220,7 +220,7 @@ ifeq ($(MULTI),1) endif dropbearmulti$(EXEEXT): $(HEADERS) $(MULTIOBJS) $(LIBTOM_DEPS) Makefile diff --git a/package/network/services/dropbear/patches/901-bundled-libs-cflags.patch b/package/network/services/dropbear/patches/901-bundled-libs-cflags.patch index 033aee3a06..2432b4ef72 100644 --- a/package/network/services/dropbear/patches/901-bundled-libs-cflags.patch +++ b/package/network/services/dropbear/patches/901-bundled-libs-cflags.patch @@ -1,6 +1,6 @@ --- a/libtomcrypt/makefile_include.mk +++ b/libtomcrypt/makefile_include.mk -@@ -75,6 +75,13 @@ endif +@@ -94,6 +94,13 @@ endif LTC_CFLAGS += -Wno-type-limits @@ -14,7 +14,7 @@ ifdef LTC_DEBUG $(info Debug build) # compile for DEBUGGING (required for ccmalloc checking!!!) -@@ -102,6 +109,9 @@ endif +@@ -121,6 +128,9 @@ endif endif # COMPILE_SMALL endif # COMPILE_DEBUG @@ -26,8 +26,8 @@ LTC_CFLAGS += -Wno-typedef-redefinition -Wno-tautological-compare -Wno-builtin-requires-header -Wno-missing-field-initializers --- a/libtommath/makefile_include.mk +++ b/libtommath/makefile_include.mk -@@ -37,6 +37,9 @@ CFLAGS += -Wsystem-headers -Wdeclaration - CFLAGS += -Wstrict-prototypes -Wpointer-arith +@@ -70,6 +70,9 @@ else + LTM_CFLAGS += -Wsystem-headers endif +ifndef OPENWRT_BUILD @@ -35,14 +35,14 @@ + ifdef COMPILE_DEBUG #debug - CFLAGS += -g3 -@@ -58,6 +61,9 @@ endif + LTM_CFLAGS += -g3 +@@ -90,6 +93,9 @@ endif + endif # COMPILE_SIZE - endif # COMPILE_DEBUG + ### ! OPENWRT_BUILD +endif + ifneq ($(findstring clang,$(CC)),) - CFLAGS += -Wno-typedef-redefinition -Wno-tautological-compare -Wno-builtin-requires-header + LTM_CFLAGS += -Wno-typedef-redefinition -Wno-tautological-compare -Wno-builtin-requires-header endif