From 410a1bdf9459adc9d3061bd82c47e8690e033d59 Mon Sep 17 00:00:00 2001 From: Duke Leto Date: Fri, 25 Sep 2020 09:37:06 -0400 Subject: [PATCH 01/14] Add script to make linux bins --- contrib/devtools/gen-linux-binary-release.sh | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 contrib/devtools/gen-linux-binary-release.sh diff --git a/contrib/devtools/gen-linux-binary-release.sh b/contrib/devtools/gen-linux-binary-release.sh new file mode 100644 index 000000000..21d4d70b9 --- /dev/null +++ b/contrib/devtools/gen-linux-binary-release.sh @@ -0,0 +1,16 @@ +#!/bin/bash +# Copyright (c) 2019-2020 The Hush developers +# Released under the GPLv3 + + +#TODO: autodect version number, error handling +FILE="hush-3.5.0-linux-amd64.tar" + +mkdir build +cp sapling*.params build/ +cd src +cp komodod komodo-cli komodo-tx hushd hush-cli hush-tx hush-smart-chain ../build +cd ../build +tar -f $FILE -c * +gzip $FILE + From b33b1db4f1225c13e003d1241531433ff269487a Mon Sep 17 00:00:00 2001 From: Duke Leto Date: Sat, 26 Sep 2020 23:33:25 -0400 Subject: [PATCH 02/14] lol --- src/wallet/wallet.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 4ead8000c..452bfea89 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1018,7 +1018,7 @@ int CWallet::VerifyAndSetInitialWitness(const CBlockIndex* pindex, bool witnessO pblockindex = chainActive[wtxHeight]; ::ClearSingleNoteWitnessCache(nd); - LogPrintf("Setting Initial Sapling Witness for tx %s, %i of %i\n", wtxHash.ToString(), nWitnessTxIncrement, nWitnessTotalTxCount); + LogPrintf("%s: Setting Initial Sapling Witness for tx %s, %i of %i\n", __func__, wtxHash.ToString(), nWitnessTxIncrement, nWitnessTotalTxCount); SaplingMerkleTree saplingTree; blockRoot = pblockindex->pprev->hashFinalSaplingRoot; @@ -1063,6 +1063,8 @@ int CWallet::VerifyAndSetInitialWitness(const CBlockIndex* pindex, bool witnessO } } + if(fZdebug) + LogPrintf("%s: nMinimumHeight=%d\n",__func__, nMinimumHeight); return nMinimumHeight; } @@ -1077,10 +1079,11 @@ void CWallet::BuildWitnessCache(const CBlockIndex* pindex, bool witnessOnly) return; } - uint256 sproutRoot; uint256 saplingRoot; CBlockIndex* pblockindex = chainActive[startHeight]; int height = chainActive.Height(); + if(fZdebug) + LogPrintf("%s: height=%d, startHeight=%\n", __func__, height, startHeight); while (pblockindex) { From 3e81631dc93e5fe094d6d7f36698b9013305ef13 Mon Sep 17 00:00:00 2001 From: miodragpop Date: Mon, 28 Sep 2020 15:25:42 +0200 Subject: [PATCH 03/14] openssl updated from 1.1.1a to 1.1.1h Complete list of improvements and bugfixes can be found at the link below https://www.openssl.org/news/openssl-1.1.1-notes.html --- depends/packages/openssl.mk | 46 ++--- depends/patches/openssl/ssl_fix.patch | 273 -------------------------- 2 files changed, 24 insertions(+), 295 deletions(-) delete mode 100644 depends/patches/openssl/ssl_fix.patch diff --git a/depends/packages/openssl.mk b/depends/packages/openssl.mk index 276e887a7..5a2a70138 100644 --- a/depends/packages/openssl.mk +++ b/depends/packages/openssl.mk @@ -1,9 +1,8 @@ package=openssl -$(package)_version=1.1.1a -$(package)_download_path=https://www.openssl.org/source/old/1.1.1 +$(package)_version=1.1.1h +$(package)_download_path=https://www.openssl.org/source $(package)_file_name=$(package)-$($(package)_version).tar.gz -$(package)_sha256_hash=fc20130f8b7cbd2fb918b2f14e2f429e109c31ddd0fb38fc5d71d9ffed3f9f41 -$(package)_patches=ssl_fix.patch +$(package)_sha256_hash=5c9ca8774bd7b03e5784f26ae9e9e6d749c9da2438545077e6b3d755a06595d9 define $(package)_set_vars $(package)_config_env=AR="$($(package)_ar)" RANLIB="$($(package)_ranlib)" CC="$($(package)_cc)" @@ -14,39 +13,40 @@ $(package)_config_opts+=no-async $(package)_config_opts+=no-bf $(package)_config_opts+=no-blake2 $(package)_config_opts+=no-camellia -$(package)_config_opts+=no-capieng +#$(package)_config_opts+=no-capieng $(package)_config_opts+=no-cast $(package)_config_opts+=no-chacha $(package)_config_opts+=no-cmac $(package)_config_opts+=no-cms -$(package)_config_opts+=no-comp +#$(package)_config_opts+=no-comp $(package)_config_opts+=no-crypto-mdebug $(package)_config_opts+=no-crypto-mdebug-backtrace -$(package)_config_opts+=no-ct +#$(package)_config_opts+=no-ct +#$(package)_config_opts+=no-des $(package)_config_opts+=no-dgram -$(package)_config_opts+=no-dsa +#$(package)_config_opts+=no-dsa $(package)_config_opts+=no-dso $(package)_config_opts+=no-dtls $(package)_config_opts+=no-dtls1 $(package)_config_opts+=no-dtls1-method $(package)_config_opts+=no-dynamic-engine -$(package)_config_opts+=no-ec2m -$(package)_config_opts+=no-ec_nistp_64_gcc_128 +#$(package)_config_opts+=no-ec2m +#$(package)_config_opts+=no-ec_nistp_64_gcc_128 $(package)_config_opts+=no-egd $(package)_config_opts+=no-engine -$(package)_config_opts+=no-err +#$(package)_config_opts+=no-err $(package)_config_opts+=no-gost $(package)_config_opts+=no-heartbeats -$(package)_config_opts+=no-idea +#$(package)_config_opts+=no-idea $(package)_config_opts+=no-md2 $(package)_config_opts+=no-md4 $(package)_config_opts+=no-mdc2 $(package)_config_opts+=no-multiblock $(package)_config_opts+=no-nextprotoneg $(package)_config_opts+=no-ocb -$(package)_config_opts+=no-ocsp +#$(package)_config_opts+=no-ocsp $(package)_config_opts+=no-poly1305 -$(package)_config_opts+=no-posix-io +#$(package)_config_opts+=no-posix-io $(package)_config_opts+=no-psk $(package)_config_opts+=no-rc2 $(package)_config_opts+=no-rc4 @@ -58,21 +58,24 @@ $(package)_config_opts+=no-scrypt $(package)_config_opts+=no-sctp $(package)_config_opts+=no-seed $(package)_config_opts+=no-shared +#$(package)_config_opts+=no-sock $(package)_config_opts+=no-srp $(package)_config_opts+=no-srtp +$(package)_config_opts+=no-ssl $(package)_config_opts+=no-ssl3 $(package)_config_opts+=no-ssl3-method $(package)_config_opts+=no-ssl-trace -$(package)_config_opts+=no-stdio -$(package)_config_opts+=no-tls1 -$(package)_config_opts+=no-tls1-method +#$(package)_config_opts+=no-stdio +#$(package)_config_opts+=no-tls +#$(package)_config_opts+=no-tls1 +#$(package)_config_opts+=no-tls1-method $(package)_config_opts+=no-ts $(package)_config_opts+=no-ui $(package)_config_opts+=no-unit-test $(package)_config_opts+=no-weak-ssl-ciphers $(package)_config_opts+=no-whirlpool -$(package)_config_opts+=no-zlib -$(package)_config_opts+=no-zlib-dynamic +#$(package)_config_opts+=no-zlib +#$(package)_config_opts+=no-zlib-dynamic $(package)_config_opts+=$($(package)_cflags) $($(package)_cppflags) $(package)_config_opts+=-DPURIFY $(package)_config_opts_linux=-fPIC -Wa,--noexecstack @@ -89,9 +92,8 @@ $(package)_config_opts_i686_mingw32=mingw endef define $(package)_preprocess_cmds - sed -i.old 's/built on: $date/built on: not available/' util/mkbuildinf.pl && \ - sed -i.old "s|\"engines\", \"apps\", \"test\"|\"engines\"|" Configure && \ - patch -p1 < $($(package)_patch_dir)/ssl_fix.patch + sed -i.old 's/built on: $$$$date/built on: date not available/' util/mkbuildinf.pl && \ + sed -i.old "s|\"engines\", \"apps\", \"test\"|\"engines\"|" Configure endef define $(package)_config_cmds diff --git a/depends/patches/openssl/ssl_fix.patch b/depends/patches/openssl/ssl_fix.patch deleted file mode 100644 index d7f79fed5..000000000 --- a/depends/patches/openssl/ssl_fix.patch +++ /dev/null @@ -1,273 +0,0 @@ -From f725fe5b4b6504df08e30f5194d321c3025e2336 Mon Sep 17 00:00:00 2001 -From: Matt Caswell -Date: Tue, 20 Nov 2018 15:32:55 +0000 -Subject: [PATCH] Fix a RUN_ONCE bug - -We have a number of instances where there are multiple "init" functions for -a single CRYPTO_ONCE variable, e.g. to load config automatically or to not -load config automatically. Unfortunately the RUN_ONCE mechanism was not -correctly giving the right return value where an alternative init function -was being used. - -Reviewed-by: Tim Hudson -(Merged from https://github.com/openssl/openssl/pull/7983) ---- - crypto/init.c | 38 +++++++++----- - include/internal/thread_once.h | 92 ++++++++++++++++++++++++++++++++++ - ssl/ssl_init.c | 6 ++- - 3 files changed, 121 insertions(+), 15 deletions(-) - -diff --git a/crypto/init.c b/crypto/init.c -index 209d1a483da..f20a12f069a 100644 ---- a/crypto/init.c -+++ b/crypto/init.c -@@ -177,12 +177,6 @@ DEFINE_RUN_ONCE_STATIC(ossl_init_load_crypto_nodelete) - - static CRYPTO_ONCE load_crypto_strings = CRYPTO_ONCE_STATIC_INIT; - static int load_crypto_strings_inited = 0; --DEFINE_RUN_ONCE_STATIC(ossl_init_no_load_crypto_strings) --{ -- /* Do nothing in this case */ -- return 1; --} -- - DEFINE_RUN_ONCE_STATIC(ossl_init_load_crypto_strings) - { - int ret = 1; -@@ -201,6 +195,13 @@ DEFINE_RUN_ONCE_STATIC(ossl_init_load_crypto_strings) - return ret; - } - -+DEFINE_RUN_ONCE_STATIC_ALT(ossl_init_no_load_crypto_strings, -+ ossl_init_load_crypto_strings) -+{ -+ /* Do nothing in this case */ -+ return 1; -+} -+ - static CRYPTO_ONCE add_all_ciphers = CRYPTO_ONCE_STATIC_INIT; - DEFINE_RUN_ONCE_STATIC(ossl_init_add_all_ciphers) - { -@@ -218,6 +219,13 @@ DEFINE_RUN_ONCE_STATIC(ossl_init_add_all_ciphers) - return 1; - } - -+DEFINE_RUN_ONCE_STATIC_ALT(ossl_init_no_add_all_ciphers, -+ ossl_init_add_all_ciphers) -+{ -+ /* Do nothing */ -+ return 1; -+} -+ - static CRYPTO_ONCE add_all_digests = CRYPTO_ONCE_STATIC_INIT; - DEFINE_RUN_ONCE_STATIC(ossl_init_add_all_digests) - { -@@ -235,7 +243,8 @@ DEFINE_RUN_ONCE_STATIC(ossl_init_add_all_digests) - return 1; - } - --DEFINE_RUN_ONCE_STATIC(ossl_init_no_add_algs) -+DEFINE_RUN_ONCE_STATIC_ALT(ossl_init_no_add_all_digests, -+ ossl_init_add_all_digests) - { - /* Do nothing */ - return 1; -@@ -255,7 +264,7 @@ DEFINE_RUN_ONCE_STATIC(ossl_init_config) - config_inited = 1; - return 1; - } --DEFINE_RUN_ONCE_STATIC(ossl_init_no_config) -+DEFINE_RUN_ONCE_STATIC_ALT(ossl_init_no_config, ossl_init_config) - { - #ifdef OPENSSL_INIT_DEBUG - fprintf(stderr, -@@ -595,8 +604,9 @@ int OPENSSL_init_crypto(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings) - return 0; - - if ((opts & OPENSSL_INIT_NO_LOAD_CRYPTO_STRINGS) -- && !RUN_ONCE(&load_crypto_strings, -- ossl_init_no_load_crypto_strings)) -+ && !RUN_ONCE_ALT(&load_crypto_strings, -+ ossl_init_no_load_crypto_strings, -+ ossl_init_load_crypto_strings)) - return 0; - - if ((opts & OPENSSL_INIT_LOAD_CRYPTO_STRINGS) -@@ -604,7 +614,8 @@ int OPENSSL_init_crypto(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings) - return 0; - - if ((opts & OPENSSL_INIT_NO_ADD_ALL_CIPHERS) -- && !RUN_ONCE(&add_all_ciphers, ossl_init_no_add_algs)) -+ && !RUN_ONCE_ALT(&add_all_ciphers, ossl_init_no_add_all_ciphers, -+ ossl_init_add_all_ciphers)) - return 0; - - if ((opts & OPENSSL_INIT_ADD_ALL_CIPHERS) -@@ -612,7 +623,8 @@ int OPENSSL_init_crypto(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings) - return 0; - - if ((opts & OPENSSL_INIT_NO_ADD_ALL_DIGESTS) -- && !RUN_ONCE(&add_all_digests, ossl_init_no_add_algs)) -+ && !RUN_ONCE_ALT(&add_all_digests, ossl_init_no_add_all_digests, -+ ossl_init_add_all_digests)) - return 0; - - if ((opts & OPENSSL_INIT_ADD_ALL_DIGESTS) -@@ -624,7 +636,7 @@ int OPENSSL_init_crypto(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings) - return 0; - - if ((opts & OPENSSL_INIT_NO_LOAD_CONFIG) -- && !RUN_ONCE(&config, ossl_init_no_config)) -+ && !RUN_ONCE_ALT(&config, ossl_init_no_config, ossl_init_config)) - return 0; - - if (opts & OPENSSL_INIT_LOAD_CONFIG) { -diff --git a/include/internal/thread_once.h b/include/internal/thread_once.h -index 224244353ab..e268a959ef3 100644 ---- a/include/internal/thread_once.h -+++ b/include/internal/thread_once.h -@@ -9,6 +9,20 @@ - - #include - -+/* -+ * DEFINE_RUN_ONCE: Define an initialiser function that should be run exactly -+ * once. It takes no arguments and returns and int result (1 for success or -+ * 0 for failure). Typical usage might be: -+ * -+ * DEFINE_RUN_ONCE(myinitfunc) -+ * { -+ * do_some_initialisation(); -+ * if (init_is_successful()) -+ * return 1; -+ * -+ * return 0; -+ * } -+ */ - #define DEFINE_RUN_ONCE(init) \ - static int init(void); \ - int init##_ossl_ret_ = 0; \ -@@ -17,10 +31,30 @@ - init##_ossl_ret_ = init(); \ - } \ - static int init(void) -+ -+/* -+ * DECLARE_RUN_ONCE: Declare an initialiser function that should be run exactly -+ * once that has been defined in another file via DEFINE_RUN_ONCE(). -+ */ - #define DECLARE_RUN_ONCE(init) \ - extern int init##_ossl_ret_; \ - void init##_ossl_(void); - -+/* -+ * DEFINE_RUN_ONCE_STATIC: Define an initialiser function that should be run -+ * exactly once. This function will be declared as static within the file. It -+ * takes no arguments and returns and int result (1 for success or 0 for -+ * failure). Typical usage might be: -+ * -+ * DEFINE_RUN_ONCE_STATIC(myinitfunc) -+ * { -+ * do_some_initialisation(); -+ * if (init_is_successful()) -+ * return 1; -+ * -+ * return 0; -+ * } -+ */ - #define DEFINE_RUN_ONCE_STATIC(init) \ - static int init(void); \ - static int init##_ossl_ret_ = 0; \ -@@ -30,6 +64,46 @@ - } \ - static int init(void) - -+/* -+ * DEFINE_RUN_ONCE_STATIC_ALT: Define an alternative initialiser function. This -+ * function will be declared as static within the file. It takes no arguments -+ * and returns an int result (1 for success or 0 for failure). An alternative -+ * initialiser function is expected to be associated with a primary initialiser -+ * function defined via DEFINE_ONCE_STATIC where both functions use the same -+ * CRYPTO_ONCE object to synchronise. Where an alternative initialiser function -+ * is used only one of the primary or the alternative initialiser function will -+ * ever be called - and that function will be called exactly once. Definitition -+ * of an alternative initialiser function MUST occur AFTER the definition of the -+ * primary initialiser function. -+ * -+ * Typical usage might be: -+ * -+ * DEFINE_RUN_ONCE_STATIC(myinitfunc) -+ * { -+ * do_some_initialisation(); -+ * if (init_is_successful()) -+ * return 1; -+ * -+ * return 0; -+ * } -+ * -+ * DEFINE_RUN_ONCE_STATIC_ALT(myaltinitfunc, myinitfunc) -+ * { -+ * do_some_alternative_initialisation(); -+ * if (init_is_successful()) -+ * return 1; -+ * -+ * return 0; -+ * } -+ */ -+#define DEFINE_RUN_ONCE_STATIC_ALT(initalt, init) \ -+ static int initalt(void); \ -+ static void initalt##_ossl_(void) \ -+ { \ -+ init##_ossl_ret_ = initalt(); \ -+ } \ -+ static int initalt(void) -+ - /* - * RUN_ONCE - use CRYPTO_THREAD_run_once, and check if the init succeeded - * @once: pointer to static object of type CRYPTO_ONCE -@@ -43,3 +117,21 @@ - */ - #define RUN_ONCE(once, init) \ - (CRYPTO_THREAD_run_once(once, init##_ossl_) ? init##_ossl_ret_ : 0) -+ -+/* -+ * RUN_ONCE_ALT - use CRYPTO_THREAD_run_once, to run an alternative initialiser -+ * function and check if that initialisation succeeded -+ * @once: pointer to static object of type CRYPTO_ONCE -+ * @initalt: alternative initialiser function name that was previously given to -+ * DEFINE_RUN_ONCE_STATIC_ALT. This function must return 1 for -+ * success or 0 for failure. -+ * @init: primary initialiser function name that was previously given to -+ * DEFINE_RUN_ONCE_STATIC. This function must return 1 for success or -+ * 0 for failure. -+ * -+ * The return value is 1 on success (*) or 0 in case of error. -+ * -+ * (*) by convention, since the init function must return 1 on success. -+ */ -+#define RUN_ONCE_ALT(once, initalt, init) \ -+ (CRYPTO_THREAD_run_once(once, initalt##_ossl_) ? init##_ossl_ret_ : 0) -diff --git a/ssl/ssl_init.c b/ssl/ssl_init.c -index c0ccb9304a6..96526472c57 100644 ---- a/ssl/ssl_init.c -+++ b/ssl/ssl_init.c -@@ -134,7 +134,8 @@ DEFINE_RUN_ONCE_STATIC(ossl_init_load_ssl_strings) - return 1; - } - --DEFINE_RUN_ONCE_STATIC(ossl_init_no_load_ssl_strings) -+DEFINE_RUN_ONCE_STATIC_ALT(ossl_init_no_load_ssl_strings, -+ ossl_init_load_ssl_strings) - { - /* Do nothing in this case */ - return 1; -@@ -207,7 +208,8 @@ int OPENSSL_init_ssl(uint64_t opts, const OPENSSL_INIT_SETTINGS * settings) - return 0; - - if ((opts & OPENSSL_INIT_NO_LOAD_SSL_STRINGS) -- && !RUN_ONCE(&ssl_strings, ossl_init_no_load_ssl_strings)) -+ && !RUN_ONCE_ALT(&ssl_strings, ossl_init_no_load_ssl_strings, -+ ossl_init_load_ssl_strings)) - return 0; - - if ((opts & OPENSSL_INIT_LOAD_SSL_STRINGS) From 62f67821ecd801cc26b9f3aa14de8735d6563a0a Mon Sep 17 00:00:00 2001 From: miodragpop Date: Tue, 29 Sep 2020 13:08:45 +0200 Subject: [PATCH 04/14] tls implemented --- src/Makefile.am | 26 ++- src/hush/tlsenums.h | 8 + src/hush/tlsmanager.cpp | 484 +++++++++++++++++++++++++++++++++++++++ src/hush/tlsmanager.h | 58 +++++ src/hush/utiltls.cpp | 494 ++++++++++++++++++++++++++++++++++++++++ src/hush/utiltls.h | 56 +++++ src/init.cpp | 23 ++ src/net.cpp | 365 +++++++++++++++++++++++------ src/net.h | 28 ++- src/rpc/misc.cpp | 2 + src/rpc/net.cpp | 7 +- 11 files changed, 1479 insertions(+), 72 deletions(-) create mode 100644 src/hush/tlsenums.h create mode 100644 src/hush/tlsmanager.cpp create mode 100644 src/hush/tlsmanager.h create mode 100644 src/hush/utiltls.cpp create mode 100644 src/hush/utiltls.h diff --git a/src/Makefile.am b/src/Makefile.am index dc66cb4a8..6c4a62a8d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -50,6 +50,7 @@ LIBSECP256K1=secp256k1/libsecp256k1.la LIBCRYPTOCONDITIONS=cryptoconditions/libcryptoconditions_core.la LIBUNIVALUE=univalue/libunivalue.la LIBZCASH=libzcash.a +LIBHUSH=libhush.a if ENABLE_ZMQ LIBBITCOIN_ZMQ=libbitcoin_zmq.a @@ -78,7 +79,8 @@ EXTRA_LIBRARIES += \ $(LIBBITCOIN_COMMON) \ $(LIBBITCOIN_SERVER) \ $(LIBBITCOIN_CLI) \ - libzcash.a + libzcash.a \ + libhush.a if ENABLE_WALLET BITCOIN_INCLUDES += $(BDB_CPPFLAGS) EXTRA_LIBRARIES += $(LIBBITCOIN_WALLET) @@ -238,6 +240,8 @@ BITCOIN_CORE_H = \ zmq/zmqnotificationinterface.h \ zmq/zmqpublishnotifier.h +LIBHUSH_H = \ + hush/utiltls.h obj/build.h: FORCE @$(MKDIR_P) $(builddir)/obj @@ -314,7 +318,8 @@ libbitcoin_server_a_SOURCES = \ txmempool.cpp \ validationinterface.cpp \ $(BITCOIN_CORE_H) \ - $(LIBZCASH_H) + $(LIBZCASH_H) \ + $(LIBHUSH_H) if ENABLE_ZMQ libbitcoin_zmq_a_CPPFLAGS = $(BITCOIN_INCLUDES) $(ZMQ_CFLAGS) @@ -491,6 +496,7 @@ komodod_LDADD = \ $(LIBBITCOIN_ZMQ) \ $(LIBBITCOIN_CRYPTO) \ $(LIBZCASH) \ + $(LIBHUSH) \ $(LIBLEVELDB) \ $(LIBMEMENV) \ $(LIBSECP256K1) \ @@ -556,6 +562,7 @@ komodo_cli_LDADD = \ $(CRYPTO_LIBS) \ $(EVENT_LIBS) \ $(LIBZCASH) \ + $(LIBHUSH) \ $(LIBBITCOIN_CRYPTO) \ $(LIBZCASH_LIBS) @@ -590,6 +597,7 @@ komodo_tx_LDADD = \ $(LIBBITCOIN_UTIL) \ $(LIBSECP256K1) \ $(LIBZCASH) \ + $(LIBHUSH) \ $(LIBBITCOIN_CRYPTO) \ $(LIBZCASH_LIBS) \ $(LIBCRYPTOCONDITIONS) @@ -649,6 +657,20 @@ libzcashconsensus_la_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) endif # +# hush-specific sources # +libhush_a_SOURCES = \ + hush/utiltls.cpp\ + hush/tlsmanager.cpp + +libhush_a_CPPFLAGS = -DMULTICORE -fopenmp -fPIC -DBINARY_OUTPUT -DCURVE_ALT_BN128 -DBOOST_SPIRIT_THREADSAFE -DHAVE_BUILD_INFO -D__STDC_FORMAT_MACROS $(HARDENED_CPPFLAGS) -pipe -O1 -g -Wstack-protector -fstack-protector-all -fPIE -fvisibility=hidden -DSTATIC $(BITCOIN_INCLUDES) + +libhush_a_CXXFLAGS = $(HARDENED_CXXFLAGS) -fwrapv -fno-strict-aliasing + +libhush_a_LDFLAGS = $(HARDENED_LDFLAGS) + +libhush_a_CPPFLAGS += -DMONTGOMERY_OUTPUT +# + CLEANFILES = leveldb/libleveldb.a leveldb/libmemenv.a *.gcda *.gcno */*.gcno wallet/*/*.gcno DISTCLEANFILES = obj/build.h diff --git a/src/hush/tlsenums.h b/src/hush/tlsenums.h new file mode 100644 index 000000000..a7caf3a6c --- /dev/null +++ b/src/hush/tlsenums.h @@ -0,0 +1,8 @@ +namespace hush +{ +typedef enum { SSL_ACCEPT, + SSL_CONNECT, + SSL_SHUTDOWN } SSLConnectionRoutine; +typedef enum { CLIENT_CONTEXT, + SERVER_CONTEXT } TLSContextType; +} diff --git a/src/hush/tlsmanager.cpp b/src/hush/tlsmanager.cpp new file mode 100644 index 000000000..73bdd4407 --- /dev/null +++ b/src/hush/tlsmanager.cpp @@ -0,0 +1,484 @@ +#include +#include +#include +#include "utiltls.h" + +#include +#include +#include "../util.h" +#include "../protocol.h" + +#include +#include + +#include "tlsmanager.h" +using namespace std; +namespace hush +{ +/** +* @brief If verify_callback always returns 1, the TLS/SSL handshake will not be terminated with respect to verification failures and the connection will be established. +* +* @param preverify_ok +* @param chainContext +* @return int +*/ +int tlsCertVerificationCallback(int preverify_ok, X509_STORE_CTX* chainContext) +{ + return 1; +} +/** + * @brief Wait for a given SSL connection event. + * + * @param eRoutine a SSLConnectionRoutine value which determines the type of the event. + * @param hSocket + * @param ssl pointer to an SSL instance. + * @param timeoutSec timeout in seconds. + * @return int returns nError corresponding to the connection event. + */ +int TLSManager::waitFor(SSLConnectionRoutine eRoutine, SOCKET hSocket, SSL* ssl, int timeoutSec) +{ + int nErr = 0; + ERR_clear_error(); // clear the error queue + + while (true) { + switch (eRoutine) { + case SSL_CONNECT: + nErr = SSL_connect(ssl); + break; + + case SSL_ACCEPT: + nErr = SSL_accept(ssl); + break; + + case SSL_SHUTDOWN: + nErr = SSL_shutdown(ssl); + break; + + default: + return -1; + } + + if (eRoutine == SSL_SHUTDOWN) { + if (nErr >= 0) + break; + } else { + if (nErr == 1) + break; + } + + int sslErr = SSL_get_error(ssl, nErr); + + if (sslErr != SSL_ERROR_WANT_READ && sslErr != SSL_ERROR_WANT_WRITE) { + LogPrint("net", "TLS: WARNING: %s: %s: ssl_err_code: %s; errno: %s\n", __FILE__, __func__, ERR_error_string(sslErr, NULL), strerror(errno)); + nErr = -1; + break; + } + + fd_set socketSet; + FD_ZERO(&socketSet); + FD_SET(hSocket, &socketSet); + + struct timeval timeout = {timeoutSec, 0}; + + if (sslErr == SSL_ERROR_WANT_READ) { + int result = select(hSocket + 1, &socketSet, NULL, NULL, &timeout); + if (result == 0) { + LogPrint("net", "TLS: ERROR: %s: %s: WANT_READ timeout\n", __FILE__, __func__); + nErr = -1; + break; + } else if (result == -1) { + LogPrint("net", "TLS: ERROR: %s: %s: WANT_READ ssl_err_code: %s; errno: %s\n", __FILE__, __func__, ERR_error_string(sslErr, NULL), strerror(errno)); + nErr = -1; + break; + } + } else { + int result = select(hSocket + 1, NULL, &socketSet, NULL, &timeout); + if (result == 0) { + LogPrint("net", "TLS: ERROR: %s: %s: WANT_WRITE timeout\n", __FILE__, __func__); + nErr = -1; + break; + } else if (result == -1) { + LogPrint("net", "TLS: ERROR: %s: %s: WANT_WRITE ssl_err_code: %s; errno: %s\n", __FILE__, __func__, ERR_error_string(sslErr, NULL), strerror(errno)); + nErr = -1; + break; + } + } + } + + return nErr; +} +/** + * @brief establish TLS connection to an address + * + * @param hSocket socket + * @param addrConnect the outgoing address + * @param tls_ctx_client TLS Client context + * @return SSL* returns a ssl* if successful, otherwise returns NULL. + */ +SSL* TLSManager::connect(SOCKET hSocket, const CAddress& addrConnect) +{ + LogPrint("net", "TLS: establishing connection (tid = %X), (peerid = %s)\n", pthread_self(), addrConnect.ToString()); + + SSL* ssl = NULL; + bool bConnectedTLS = false; + + if ((ssl = SSL_new(tls_ctx_client))) { + if (SSL_set_fd(ssl, hSocket)) { + if (TLSManager::waitFor(SSL_CONNECT, hSocket, ssl, (DEFAULT_CONNECT_TIMEOUT / 1000)) == 1) + + bConnectedTLS = true; + } + } + + if (bConnectedTLS) { + LogPrintf("TLS: connection to %s has been established. Using cipher: %s\n", addrConnect.ToString(), SSL_get_cipher(ssl)); + } else { + LogPrintf("TLS: %s: %s: TLS connection to %s failed\n", __FILE__, __func__, addrConnect.ToString()); + + if (ssl) { + SSL_free(ssl); + ssl = NULL; + } + } + return ssl; +} +/** + * @brief Initialize TLS Context + * + * @param ctxType context type + * @param privateKeyFile private key file path + * @param certificateFile certificate key file path + * @param trustedDirs trusted directories + * @return SSL_CTX* returns the context. + */ +SSL_CTX* TLSManager::initCtx( + TLSContextType ctxType, + const boost::filesystem::path& privateKeyFile, + const boost::filesystem::path& certificateFile, + const std::vector& trustedDirs) +{ + if (!boost::filesystem::exists(privateKeyFile) || + !boost::filesystem::exists(certificateFile)) + return NULL; + + bool bInitialized = false; + SSL_CTX* tlsCtx = NULL; + + if ((tlsCtx = SSL_CTX_new(ctxType == SERVER_CONTEXT ? TLS_server_method() : TLS_client_method()))) { + SSL_CTX_set_mode(tlsCtx, SSL_MODE_AUTO_RETRY); + + int rootCertsNum = LoadDefaultRootCertificates(tlsCtx); + int trustedPathsNum = 0; + + for (boost::filesystem::path trustedDir : trustedDirs) { + if (SSL_CTX_load_verify_locations(tlsCtx, NULL, trustedDir.string().c_str()) == 1) + trustedPathsNum++; + } + + if (rootCertsNum == 0 && trustedPathsNum == 0) + LogPrintf("TLS: WARNING: %s: %s: failed to set up verified certificates. It will be impossible to verify peer certificates. \n", __FILE__, __func__); + + SSL_CTX_set_verify(tlsCtx, SSL_VERIFY_PEER, tlsCertVerificationCallback); + + if (SSL_CTX_use_certificate_file(tlsCtx, certificateFile.string().c_str(), SSL_FILETYPE_PEM) > 0) { + if (SSL_CTX_use_PrivateKey_file(tlsCtx, privateKeyFile.string().c_str(), SSL_FILETYPE_PEM) > 0) { + if (SSL_CTX_check_private_key(tlsCtx)) + bInitialized = true; + else + LogPrintf("TLS: ERROR: %s: %s: private key does not match the certificate public key\n", __FILE__, __func__); + } else + LogPrintf("TLS: ERROR: %s: %s: failed to use privateKey file\n", __FILE__, __func__); + } else { + LogPrintf("TLS: ERROR: %s: %s: failed to use certificate file\n", __FILE__, __func__); + ERR_print_errors_fp(stderr); + } + } else + LogPrintf("TLS: ERROR: %s: %s: failed to create TLS context\n", __FILE__, __func__); + + if (!bInitialized) { + if (tlsCtx) { + SSL_CTX_free(tlsCtx); + tlsCtx = NULL; + } + } + + return tlsCtx; +} +/** + * @brief load the certificate credentials from file. + * + * @return true returns true is successful. + * @return false returns false if an error has occured. + */ +bool TLSManager::prepareCredentials() +{ + boost::filesystem::path + defaultKeyPath(GetDataDir() / TLS_KEY_FILE_NAME), + defaultCertPath(GetDataDir() / TLS_CERT_FILE_NAME); + + CredentialsStatus credStatus = + VerifyCredentials( + boost::filesystem::path(GetArg("-tlskeypath", defaultKeyPath.string())), + boost::filesystem::path(GetArg("-tlscertpath", defaultCertPath.string())), + GetArg("-tlskeypwd", "")); + + bool bPrepared = (credStatus == credOk); + + if (!bPrepared) { + if (!mapArgs.count("-tlskeypath") && !mapArgs.count("-tlscertpath")) { + // Default paths were used + + if (credStatus == credAbsent) { + // Generate new credentials (key and self-signed certificate on it) only if credentials were absent previously + // + bPrepared = GenerateCredentials( + defaultKeyPath, + defaultCertPath, + GetArg("-tlskeypwd", "")); + } + } + } + + return bPrepared; +} +/** + * @brief accept a TLS connection + * + * @param hSocket the TLS socket. + * @param addr incoming address. + * @param tls_ctx_server TLS server context. + * @return SSL* returns pointer to the ssl object if successful, otherwise returns NULL + */ +SSL* TLSManager::accept(SOCKET hSocket, const CAddress& addr) +{ + LogPrint("net", "TLS: accepting connection from %s (tid = %X)\n", addr.ToString(), pthread_self()); + + SSL* ssl = NULL; + bool bAcceptedTLS = false; + + if ((ssl = SSL_new(tls_ctx_server))) { + if (SSL_set_fd(ssl, hSocket)) { + if (TLSManager::waitFor(SSL_ACCEPT, hSocket, ssl, (DEFAULT_CONNECT_TIMEOUT / 1000)) == 1) + bAcceptedTLS = true; + } + } + + if (bAcceptedTLS) { + LogPrintf("TLS: connection from %s has been accepted. Using cipher: %s\n", addr.ToString(), SSL_get_cipher(ssl)); + } else { + LogPrintf("TLS: ERROR: %s: %s: TLS connection from %s failed\n", __FILE__, __func__, addr.ToString()); + + if (ssl) { + SSL_free(ssl); + ssl = NULL; + } + } + + return ssl; +} +/** + * @brief Determines whether a string exists in the non-TLS address pool. + * + * @param strAddr The address. + * @param vPool Pool to search in. + * @param cs reference to the corresponding CCriticalSection. + * @return true returns true if address exists in the given pool. + * @return false returns false if address doesnt exist in the given pool. + */ +bool TLSManager::isNonTLSAddr(const string& strAddr, const vector& vPool, CCriticalSection& cs) +{ + LOCK(cs); + return (find(vPool.begin(), vPool.end(), NODE_ADDR(strAddr)) != vPool.end()); +} +/** + * @brief Removes non-TLS node addresses based on timeout. + * + * @param vPool + * @param cs + */ +void TLSManager::cleanNonTLSPool(std::vector& vPool, CCriticalSection& cs) +{ + LOCK(cs); + + vector vDeleted; + + BOOST_FOREACH (NODE_ADDR nodeAddr, vPool) { + if ((GetTimeMillis() - nodeAddr.time) >= 900000) { + vDeleted.push_back(nodeAddr); + LogPrint("net", "TLS: Node %s is deleted from the non-TLS pool\n", nodeAddr.ipAddr); + } + } + + BOOST_FOREACH (NODE_ADDR nodeAddrDeleted, vDeleted) { + vPool.erase( + remove( + vPool.begin(), + vPool.end(), + nodeAddrDeleted), + vPool.end()); + } +} + +/** + * @brief Handles send and recieve functionality in TLS Sockets. + * + * @param pnode reference to the CNode object. + * @param fdsetRecv + * @param fdsetSend + * @param fdsetError + * @return int returns -1 when socket is invalid. returns 0 otherwise. + */ +int TLSManager::threadSocketHandler(CNode* pnode, fd_set& fdsetRecv, fd_set& fdsetSend, fd_set& fdsetError) +{ + // + // Receive + // + bool recvSet = false, sendSet = false, errorSet = false; + + { + LOCK(pnode->cs_hSocket); + + if (pnode->hSocket == INVALID_SOCKET) + return -1; + + recvSet = FD_ISSET(pnode->hSocket, &fdsetRecv); + sendSet = FD_ISSET(pnode->hSocket, &fdsetSend); + errorSet = FD_ISSET(pnode->hSocket, &fdsetError); + } + + if (recvSet || errorSet) { + TRY_LOCK(pnode->cs_vRecvMsg, lockRecv); + if (lockRecv) { + { + // typical socket buffer is 8K-64K + // maximum record size is 16kB for SSLv3/TLSv1 + char pchBuf[0x10000]; + bool bIsSSL = false; + int nBytes = 0, nRet = 0; + + { + LOCK(pnode->cs_hSocket); + + if (pnode->hSocket == INVALID_SOCKET) { + LogPrint("net", "Receive: connection with %s is already closed\n", pnode->addr.ToString()); + return -1; + } + + bIsSSL = (pnode->ssl != NULL); + + if (bIsSSL) { + ERR_clear_error(); // clear the error queue, otherwise we may be reading an old error that occurred previously in the current thread + nBytes = SSL_read(pnode->ssl, pchBuf, sizeof(pchBuf)); + nRet = SSL_get_error(pnode->ssl, nBytes); + } else { + nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT); + nRet = WSAGetLastError(); + } + } + + if (nBytes > 0) { + if (!pnode->ReceiveMsgBytes(pchBuf, nBytes)) + pnode->CloseSocketDisconnect(); + pnode->nLastRecv = GetTime(); + pnode->nRecvBytes += nBytes; + pnode->RecordBytesRecv(nBytes); + } else if (nBytes == 0) { + // socket closed gracefully (peer disconnected) + // + if (!pnode->fDisconnect) + LogPrint("net", "socket closed (%s)\n", pnode->addr.ToString()); + pnode->CloseSocketDisconnect(); + } else if (nBytes < 0) { + // error + // + if (bIsSSL) { + if (nRet != SSL_ERROR_WANT_READ && nRet != SSL_ERROR_WANT_WRITE) // SSL_read() operation has to be repeated because of SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE (https://wiki.openssl.org/index.php/Manual:SSL_read(3)#NOTES) + { + if (!pnode->fDisconnect) + LogPrintf("ERROR: SSL_read %s\n", ERR_error_string(nRet, NULL)); + pnode->CloseSocketDisconnect(); + } else { + // preventive measure from exhausting CPU usage + // + MilliSleep(1); // 1 msec + } + } else { + if (nRet != WSAEWOULDBLOCK && nRet != WSAEMSGSIZE && nRet != WSAEINTR && nRet != WSAEINPROGRESS) { + if (!pnode->fDisconnect) + LogPrintf("ERROR: socket recv %s\n", NetworkErrorString(nRet)); + pnode->CloseSocketDisconnect(); + } + } + } + } + } + } + + // + // Send + // + if (sendSet) { + TRY_LOCK(pnode->cs_vSend, lockSend); + if (lockSend) + SocketSendData(pnode); + } + return 0; +} +/** + * @brief Initialization of the server and client contexts + * + * @return true returns True if successful. + * @return false returns False if an error has occured. + */ +bool TLSManager::initialize() +{ + bool bInitializationStatus = false; + + // Initialization routines for the OpenSSL library + // + SSL_load_error_strings(); + ERR_load_crypto_strings(); + OpenSSL_add_ssl_algorithms(); // OpenSSL_add_ssl_algorithms() always returns "1", so it is safe to discard the return value. + + namespace fs = boost::filesystem; + fs::path certFile = GetArg("-tlscertpath", ""); + if (!fs::exists(certFile)) + certFile = (GetDataDir() / TLS_CERT_FILE_NAME); + + fs::path privKeyFile = GetArg("-tlskeypath", ""); + if (!fs::exists(privKeyFile)) + privKeyFile = (GetDataDir() / TLS_KEY_FILE_NAME); + + std::vector trustedDirs; + fs::path trustedDir = GetArg("-tlstrustdir", ""); + if (fs::exists(trustedDir)) + // Use only the specified trusted directory + trustedDirs.push_back(trustedDir); + else + // If specified directory can't be used, then setting the default trusted directories + trustedDirs = GetDefaultTrustedDirectories(); + + for (fs::path dir : trustedDirs) + LogPrintf("TLS: trusted directory '%s' will be used\n", dir.string().c_str()); + + // Initialization of the server and client contexts + // + if ((tls_ctx_server = TLSManager::initCtx(SERVER_CONTEXT, privKeyFile, certFile, trustedDirs))) + { + if ((tls_ctx_client = TLSManager::initCtx(CLIENT_CONTEXT, privKeyFile, certFile, trustedDirs))) + { + LogPrint("net", "TLS: contexts are initialized\n"); + bInitializationStatus = true; + } + else + { + LogPrintf("TLS: ERROR: %s: %s: failed to initialize TLS client context\n", __FILE__, __func__); + SSL_CTX_free (tls_ctx_server); + } + } + else + LogPrintf("TLS: ERROR: %s: %s: failed to initialize TLS server context\n", __FILE__, __func__); + + return bInitializationStatus; +} +} diff --git a/src/hush/tlsmanager.h b/src/hush/tlsmanager.h new file mode 100644 index 000000000..da2c61b93 --- /dev/null +++ b/src/hush/tlsmanager.h @@ -0,0 +1,58 @@ +#include +#include +#include +#include "utiltls.h" +#include "tlsenums.h" +#include +#include +#include "../util.h" +#include "../protocol.h" +#include "../net.h" +#include "sync.h" +#include +#include +#include +#ifdef WIN32 +#include +#else +#include +#endif + +using namespace std; + +namespace hush +{ +typedef struct _NODE_ADDR { + std::string ipAddr; + int64_t time; // time in msec, of an attempt to connect via TLS + + _NODE_ADDR(std::string _ipAddr, int64_t _time = 0) : ipAddr(_ipAddr), time(_time) {} +bool operator==(const _NODE_ADDR b) const +{ + return (ipAddr == b.ipAddr); +} +} NODE_ADDR, *PNODE_ADDR; + +/** + * @brief A class to wrap some of hush specific TLS functionalities used in the net.cpp + * + */ +class TLSManager +{ +public: + int waitFor(SSLConnectionRoutine eRoutine, SOCKET hSocket, SSL* ssl, int timeoutSec); + SSL* connect(SOCKET hSocket, const CAddress& addrConnect); + SSL_CTX* initCtx( + TLSContextType ctxType, + const boost::filesystem::path& privateKeyFile, + const boost::filesystem::path& certificateFile, + const std::vector& trustedDirs); + + bool prepareCredentials(); + SSL* accept(SOCKET hSocket, const CAddress& addr); + bool isNonTLSAddr(const string& strAddr, const vector& vPool, CCriticalSection& cs); + void cleanNonTLSPool(std::vector& vPool, CCriticalSection& cs); + int threadSocketHandler(CNode* pnode, fd_set& fdsetRecv, fd_set& fdsetSend, fd_set& fdsetError); + bool initialize(); +}; +} diff --git a/src/hush/utiltls.cpp b/src/hush/utiltls.cpp new file mode 100644 index 000000000..b70fa9fd4 --- /dev/null +++ b/src/hush/utiltls.cpp @@ -0,0 +1,494 @@ +// Copyright (c) 2017 The Zen Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "util.h" +#include "utiltls.h" + +namespace hush { + +// Set of most common default trusted certificates directories used by OpenSSL +static const char* defaultTrustedDirs[] = +{ +#ifdef WIN32 + "" +#elif MAC_OSX + "/System/Library/OpenSSL/certs" +#else // Linux build + "/etc/ssl/certs", + "/usr/local/ssl/certs", + "/usr/lib/ssl/certs", + "/usr/share/ssl/certs", + "/etc/pki/tls/certs", + "/var/lib/ca-certificates" +#endif +}; + +// Default root certificates (PEM encoded) +static const char defaultRootCerts[] = +{ +// // Example of specifying a certificate +// // +// "-----BEGIN CERTIFICATE-----\n" +// "MIIDYDCCAkigAwIBAgIJAJMakdoBYY67MA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV\n" +// "BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX\n" +// "aWRnaXRzIFB0eSBMdGQwHhcNMTcwODE0MTc0MTMyWhcNNDQxMjMwMTc0MTMyWjBF\n" +// "MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50\n" +// "ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\n" +// "CgKCAQEAzNV+SPRCKSEGlntfpCRMVSfz99NoEo3K1SRyw6GTSb1LNSTQCn1EsCSH\n" +// "cVZTmyfjcTHpwz4aF14yw8lQC42f218AOsG1DV5suCaUXhSmZlajMkvEJVwfBOft\n" +// "xpcqE1fA9wovXlnJLXVgyJGMc896S8tcbrCU/l/BsqKh5QX8N60MQ3w376nSGvVP\n" +// "ussN8bVH3aKRwjhateqx1GRt0GPnM8/u7EkgF8Bc+m8WZYcUfkPC5Am2D0MO1HOA\n" +// "u3IKxXZMs/fYd6nF5DZBwg+D23EP/V8oqenn8ilvrSORq5PguOl1QoDyY66PhmjN\n" +// "L9c4Spxw8HXUDlrfuSQn2NJnw1XhdQIDAQABo1MwUTAdBgNVHQ4EFgQU/KD+n5Bz\n" +// "QLbp09qKzwwyNwOQU4swHwYDVR0jBBgwFoAU/KD+n5BzQLbp09qKzwwyNwOQU4sw\n" +// "DwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAVtprBxZD6O+WNYUM\n" +// "ksdKiVVoszEJXlt7wajuaPBPK/K3buxE9FLVxS+LiH1PUhPCc6V28guyKWwn109/\n" +// "4WnO51LQjygvd7SaePlbiO7iIatkOk4oETJQZ+tEJ7fv/NITY/GQUfgPNkANmPPz\n" +// "Mz9I6He8XhIpO6NGuDG+74aR1RhvR3PWJJYT0QpL0STVR4qTc/HfnymF5XnnjOYZ\n" +// "mwzT8jXX5dhLYwJmyPBS+uv+oa1quM/FitA63N9anYtRBiPaBtund9Ikjat1hM0h\n" +// "neo2tz7Mfsgjb0aiORtiyaH2OetvwR0QuCSVPnknkfGWPDINdUdkgKyA1PX58Smw\n" +// "vaXEcw==\n" +// "-----END CERTIFICATE-----" + + "" +}; + +// Generates RSA keypair (a private key of 'bits' length for a specified 'uPublicKey') +// +static EVP_PKEY* GenerateRsaKey(int bits, BN_ULONG uPublicKey) +{ + EVP_PKEY *evpPrivKey = NULL; + + BIGNUM *pubKey = BN_new(); + if (pubKey) + { + if (BN_set_word(pubKey, uPublicKey)) + { + RSA *privKey = RSA_new(); + if (privKey) + { + if (RAND_poll() && // The pseudo-random number generator must be seeded prior to calling RSA_generate_key_ex(). (https://www.openssl.org/docs/man1.1.0/crypto/RSA_generate_key.html) + RSA_generate_key_ex(privKey, bits, pubKey, NULL)) + { + if ((evpPrivKey = EVP_PKEY_new())) + { + if (!EVP_PKEY_assign_RSA(evpPrivKey, privKey)) + { + EVP_PKEY_free(evpPrivKey); + evpPrivKey = NULL; + } + } + } + + if(!evpPrivKey) // EVP_PKEY_assign_RSA uses the supplied key internally + RSA_free(privKey); + } + } + BN_free(pubKey); + } + + return evpPrivKey; +} + +// Generates certificate for a specified public key using a corresponding private key (both of them should be specified in the 'keypair'). +// +static X509* GenerateCertificate(EVP_PKEY *keypair) +{ + if (!keypair) + return NULL; + + X509 *cert = X509_new(); + if (cert) + { + bool bCertSigned = false; + long sn = 0; + + if (RAND_bytes((unsigned char*)&sn, sizeof sn) && + ASN1_INTEGER_set(X509_get_serialNumber(cert), sn)) + { + X509_gmtime_adj(X509_get_notBefore(cert), 0); + X509_gmtime_adj(X509_get_notAfter(cert), (60 * 60 * 24 * CERT_VALIDITY_DAYS)); + + // setting a public key from the keypair + if (X509_set_pubkey(cert, keypair)) + { + X509_NAME *subjectName = X509_get_subject_name(cert); + if (subjectName) + { + // an issuer name is the same as a subject name, due to certificate is self-signed + if (X509_set_issuer_name(cert, subjectName)) + { + // private key from keypair is used; signature will be set inside of the cert + bCertSigned = X509_sign(cert, keypair, EVP_sha512()); + } + } + } + } + + if (!bCertSigned) + { + X509_free(cert); + cert = NULL; + } + } + + return cert; +} + +// Stores key to file, specified by the 'filePath' +// +static bool StoreKey(EVP_PKEY *key, const boost::filesystem::path &filePath, const std::string &passphrase) +{ + if (!key) + return false; + + bool bStored = false; + + FILE *keyfd = fopen(filePath.string().c_str(), "wb"); + if (keyfd) + { + const EVP_CIPHER* pCipher = NULL; + + if (passphrase.length() && (pCipher = EVP_aes_256_cbc())) + bStored = PEM_write_PrivateKey(keyfd, key, pCipher, NULL, 0, NULL, (void*)passphrase.c_str()); + else + bStored = PEM_write_PrivateKey(keyfd, key, NULL, NULL, 0, NULL, NULL); + + fclose(keyfd); + } + + return bStored; +} + +// Stores certificate to file, specified by the 'filePath' +// +static bool StoreCertificate(X509 *cert, const boost::filesystem::path &filePath) +{ + if (!cert) + return false; + + bool bStored = false; + + FILE *certfd = fopen(filePath.string().c_str(), "wb"); + if (certfd) + { + bStored = PEM_write_X509(certfd, cert); + fclose(certfd); + } + + return bStored; +} + +// Loads key from file, specified by the 'filePath' +// +static EVP_PKEY* LoadKey(const boost::filesystem::path &filePath, const std::string &passphrase) +{ + if (!boost::filesystem::exists(filePath)) + return NULL; + + EVP_PKEY *key = NULL; + FILE *keyfd = fopen(filePath.string().c_str(), "rb"); + if (keyfd) + { + key = PEM_read_PrivateKey(keyfd, NULL, NULL, passphrase.length() ? (void*)passphrase.c_str() : NULL); + fclose(keyfd); + } + + return key; +} + +// Loads certificate from file, specified by the 'filePath' +// +static X509* LoadCertificate(const boost::filesystem::path &filePath) +{ + if (!boost::filesystem::exists(filePath)) + return NULL; + + X509 *cert = NULL; + FILE *certfd = fopen(filePath.string().c_str(), "rb"); + if (certfd) + { + cert = PEM_read_X509(certfd, NULL, NULL, NULL); + fclose(certfd); + } + + return cert; +} + +// Verifies if the private key in 'key' matches the public key in 'cert' +// (Signs random bytes on 'key' and verifies signature correctness on public key from 'cert') +// +static bool IsMatching(EVP_PKEY *key, X509 *cert) +{ + if (!key || !cert) + return false; + + bool bIsMatching = false; + + EVP_PKEY_CTX *ctxSign = EVP_PKEY_CTX_new(key, NULL); + if (ctxSign) + { + if (EVP_PKEY_sign_init(ctxSign) == 1 && + EVP_PKEY_CTX_set_signature_md(ctxSign, EVP_sha512()) > 0) + { + unsigned char digest[SHA512_DIGEST_LENGTH] = { 0 }; + size_t digestSize = sizeof digest, signatureSize = 0; + + if (RAND_bytes((unsigned char*)&digest, digestSize) && // set random bytes as a digest + EVP_PKEY_sign(ctxSign, NULL, &signatureSize, digest, digestSize) == 1) // determine buffer length + { + unsigned char *signature = (unsigned char*)OPENSSL_malloc(signatureSize); + if (signature) + { + if (EVP_PKEY_sign(ctxSign, signature, &signatureSize, digest, digestSize) == 1) + { + EVP_PKEY *pubkey = X509_get_pubkey(cert); + if (pubkey) + { + EVP_PKEY_CTX *ctxVerif = EVP_PKEY_CTX_new(pubkey, NULL); + if (ctxVerif) + { + if (EVP_PKEY_verify_init(ctxVerif) == 1 && + EVP_PKEY_CTX_set_signature_md(ctxVerif, EVP_sha512()) > 0) + { + bIsMatching = (EVP_PKEY_verify(ctxVerif, signature, signatureSize, digest, digestSize) == 1); + } + EVP_PKEY_CTX_free(ctxVerif); + } + EVP_PKEY_free(pubkey); + } + } + OPENSSL_free(signature); + } + } + } + EVP_PKEY_CTX_free(ctxSign); + } + + return bIsMatching; +} + +// Checks the correctness of a private-public key pair and the validity of a certificate using public key from key pair +// +static bool CheckCredentials(EVP_PKEY *key, X509 *cert) +{ + if (!key || !cert) + return false; + + bool bIsOk = false; + + // Validating the correctness of a private-public key pair, depending on a key type + // + switch (EVP_PKEY_base_id(key)) + { + case EVP_PKEY_RSA: + case EVP_PKEY_RSA2: + { + RSA *rsaKey = EVP_PKEY_get1_RSA(key); + if (rsaKey) + { + bIsOk = (RSA_check_key(rsaKey) == 1); + RSA_free(rsaKey); + } + break; + } + + // Currently only RSA keys are supported. + // Other key types can be added here in further. + + default: + bIsOk = false; + } + + // Verifying if the private key matches the public key in certificate + if (bIsOk) + bIsOk = IsMatching(key, cert); + + return bIsOk; +} + +// Verifies credentials (a private key, a certificate for public key and a correspondence between the private and the public key) +// +CredentialsStatus VerifyCredentials( + const boost::filesystem::path &keyPath, + const boost::filesystem::path &certPath, + const std::string &passphrase) +{ + CredentialsStatus status = credAbsent; + + EVP_PKEY *key = NULL; + X509 *cert = NULL; + + key = LoadKey(keyPath, passphrase); + cert = LoadCertificate(certPath); + + if (key && cert) + status = CheckCredentials(key, cert) ? credOk : credNonConsistent; + else if (!key && !cert) + status = credAbsent; + else + status = credPartiallyAbsent; + + if (key) + EVP_PKEY_free(key); + if (cert) + X509_free(cert); + + return status; +} + +// Generates public key pair and the self-signed certificate for it, and then stores them by the specified paths 'keyPath' and 'certPath' respectively. +// +bool GenerateCredentials( + const boost::filesystem::path &keyPath, + const boost::filesystem::path &certPath, + const std::string &passphrase) +{ + bool bGenerated = false; + + EVP_PKEY *key = NULL; + X509 *cert = NULL; + + // Generating RSA key and the self-signed certificate for it + // + key = GenerateRsaKey(TLS_RSA_KEY_SIZE, RSA_F4); + if (key) + { + cert = GenerateCertificate(key); + if (cert) + { + if (StoreKey(key, keyPath, passphrase) && + StoreCertificate(cert, certPath)) + { + bGenerated = true; + LogPrintStr("TLS: New private key and self-signed certificate were generated successfully\n"); + } + + X509_free(cert); + } + EVP_PKEY_free(key); + } + + return bGenerated; +} + +// Checks if certificate of a peer is valid (by internal means of the TLS protocol) +// +// Validates peer certificate using a chain of CA certificates. +// If some of intermediate CA certificates are absent in the trusted certificates store, then validation status will be 'false') +// +bool ValidatePeerCertificate(SSL *ssl) +{ + if (!ssl) + return false; + + bool bIsOk = false; + + X509 *cert = SSL_get_peer_certificate (ssl); + if (cert) + { + // NOTE: SSL_get_verify_result() is only useful in connection with SSL_get_peer_certificate (https://www.openssl.org/docs/man1.0.2/ssl/SSL_get_verify_result.html) + // + bIsOk = (SSL_get_verify_result(ssl) == X509_V_OK); + X509_free(cert); + } + else + { + LogPrint("net", "TLS: Peer does not have certificate\n"); + bIsOk = false; + } + return bIsOk; +} + +// Check if a given context is set up with a cert that can be validated by this context +// +bool ValidateCertificate(SSL_CTX *ssl_ctx) +{ + if (!ssl_ctx) + return false; + + bool bIsOk = false; + + X509_STORE *store = SSL_CTX_get_cert_store(ssl_ctx); + + if (store) + { + X509_STORE_CTX *ctx = X509_STORE_CTX_new(); + if (ctx) + { + if (X509_STORE_CTX_init(ctx, store, SSL_CTX_get0_certificate(ssl_ctx), NULL) == 1) + bIsOk = X509_verify_cert(ctx) == 1; + + X509_STORE_CTX_free(ctx); + } + } + + return bIsOk; +} + +// Creates the list of available OpenSSL default directories for trusted certificates storage +// +std::vector GetDefaultTrustedDirectories() +{ + namespace fs = boost::filesystem; + std::vector defaultDirectoriesList; + + // Default certificates directory specified in OpenSSL build + fs::path libDefaultDir = X509_get_default_cert_dir(); + + if (fs::exists(libDefaultDir)) + defaultDirectoriesList.push_back(libDefaultDir); + + // Check and set all possible standard default directories + for (const char *dir : defaultTrustedDirs) + { + fs::path defaultDir(dir); + + if (defaultDir != libDefaultDir && + fs::exists(defaultDir)) + defaultDirectoriesList.push_back(defaultDir); + } + + return defaultDirectoriesList; +} + +// Loads default root certificates (placed in the 'defaultRootCerts') into the specified context. +// Returns the number of loaded certificates. +// +int LoadDefaultRootCertificates(SSL_CTX *ctx) +{ + if (!ctx) + return 0; + + int certsLoaded = 0; + + // Certificate text buffer 'defaultRootCerts' is a C string with certificates in PEM format + BIO *memBuf = BIO_new_mem_buf(defaultRootCerts, -1); + if (memBuf) + { + X509 *cert = NULL; + while ((cert = PEM_read_bio_X509(memBuf, NULL, 0, NULL))) + { + if (X509_STORE_add_cert(SSL_CTX_get_cert_store(ctx), cert) > 0) + certsLoaded++; + + X509_free(cert); + } + BIO_free(memBuf); + } + + return certsLoaded; +} +} diff --git a/src/hush/utiltls.h b/src/hush/utiltls.h new file mode 100644 index 000000000..2dee400bd --- /dev/null +++ b/src/hush/utiltls.h @@ -0,0 +1,56 @@ +// Copyright (c) 2017 The Zen Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef UTILTLS_H +#define UTILTLS_H + +#include +namespace hush { + +#define TLS_KEY_FILE_NAME "key.pem" // default name of a private key +#define TLS_CERT_FILE_NAME "cert.pem" // default name of a certificate + +#define CERT_VALIDITY_DAYS (365 * 10) // period of validity, in days, for a self-signed certificate + +#define TLS_RSA_KEY_SIZE 2048 // size of a private RSA key, in bits, that will be generated, if no other key is specified + +typedef enum {credOk, credNonConsistent, credAbsent, credPartiallyAbsent} CredentialsStatus; + +// Verifies credentials (a private key, a certificate for public key and a correspondence between the private and the public key) +// +CredentialsStatus VerifyCredentials( + const boost::filesystem::path &keyPath, + const boost::filesystem::path &certPath, + const std::string &passphrase); + +// Generates public key pair and the self-signed certificate for it, and then stores them by the specified paths 'keyPath' and 'certPath' respectively. +// +bool GenerateCredentials( + const boost::filesystem::path &keyPath, + const boost::filesystem::path &certPath, + const std::string &passphrase); + +// Checks if certificate of a peer is valid (by internal means of the TLS protocol) +// +// Validates peer certificate using a chain of CA certificates. +// If some of intermediate CA certificates are absent in the trusted certificates store, then validation status will be 'false') +// +bool ValidatePeerCertificate(SSL *ssl); + +// Check if a given context is set up with a cert that can be validated by this context +// +bool ValidateCertificate(SSL_CTX *ssl_ctx); + +// Creates the list of available OpenSSL default directories for trusted certificates storage +// +std::vector GetDefaultTrustedDirectories(); + +// Loads default root certificates (placed in the 'defaultRootCerts') into the specified context. +// Returns the number of loaded certificates. +// +int LoadDefaultRootCertificates(SSL_CTX *ctx); + +} + +#endif // UTILTLS_H diff --git a/src/init.cpp b/src/init.cpp index b401079d6..6510424af 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -426,6 +426,11 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-timeout=", strprintf(_("Specify connection timeout in milliseconds (minimum: 1, default: %d)"), DEFAULT_CONNECT_TIMEOUT)); strUsage += HelpMessageOpt("-torcontrol=:", strprintf(_("Tor control port to use if onion listening enabled (default: %s)"), DEFAULT_TOR_CONTROL)); strUsage += HelpMessageOpt("-torpassword=", _("Tor control port password (default: empty)")); + strUsage += HelpMessageOpt("-tls=