diff --git a/configure.ac b/configure.ac index bd51c7fcc..aab16b9be 100644 --- a/configure.ac +++ b/configure.ac @@ -664,8 +664,8 @@ if test x$use_pkgconfig = xyes; then m4_ifdef( [PKG_CHECK_MODULES], [ - PKG_CHECK_MODULES([SSL], [libssl],, [AC_MSG_ERROR(openssl not found.)]) - PKG_CHECK_MODULES([CRYPTO], [libcrypto],,[AC_MSG_ERROR(libcrypto not found.)]) + PKG_CHECK_MODULES([SSL], [wolfssl],, [AC_MSG_ERROR(WolfSSL not found.)]) + #PKG_CHECK_MODULES([CRYPTO], [libcrypto],,[AC_MSG_ERROR(libcrypto not found.)]) if test x$build_bitcoin_utils$build_bitcoind$bitcoin_enable_qt$use_tests != xnononono; then PKG_CHECK_MODULES([EVENT], [libevent],, [AC_MSG_ERROR(libevent not found.)]) if test x$TARGET_OS != xwindows; then @@ -688,11 +688,11 @@ else # BUG: Fix this: echo 'BUG: configure does not yet check for the following dependencies if pkg-config is not on the system: libcrypto++, gmp' - AC_CHECK_HEADER([openssl/crypto.h],,AC_MSG_ERROR(libcrypto headers missing)) + AC_CHECK_HEADER([wolfssl/openssl/crypto.h],,AC_MSG_ERROR(libcrypto headers missing)) AC_CHECK_LIB([crypto], [main],CRYPTO_LIBS=-lcrypto, AC_MSG_ERROR(libcrypto missing)) - AC_CHECK_HEADER([openssl/ssl.h],, AC_MSG_ERROR(libssl headers missing),) - AC_CHECK_LIB([ssl], [main],SSL_LIBS=-lssl, AC_MSG_ERROR(libssl missing)) + AC_CHECK_HEADER([wolfssl/ssl.h],, AC_MSG_ERROR(libssl headers missing),) + AC_CHECK_LIB([wolfssl], [main],SSL_LIBS=-lwolfssl, AC_MSG_ERROR(libwolfssl missing)) if test x$build_bitcoin_utils$build_bitcoind$bitcoin_enable_qt$use_tests != xnononono; then AC_CHECK_HEADER([event2/event.h],, AC_MSG_ERROR(libevent headers missing),) diff --git a/depends/packages/libcurl.mk b/depends/packages/libcurl.mk index 91ff1c0f1..ae2b07bd3 100644 --- a/depends/packages/libcurl.mk +++ b/depends/packages/libcurl.mk @@ -1,12 +1,12 @@ package=libcurl $(package)_version=7.67.0 -$(package)_dependencies=openssl +$(package)_dependencies=wolfssl $(package)_download_path=https://curl.haxx.se/download $(package)_file_name=curl-$($(package)_version).tar.gz $(package)_sha256_hash=52af3361cf806330b88b4fe6f483b6844209d47ae196ac46da4de59bb361ab02 -$(package)_config_opts_linux=--disable-shared --enable-static --prefix=$(host_prefix) --host=$(host) -$(package)_config_opts_mingw32=--enable-mingw --disable-shared --enable-static --prefix=$(host_prefix) --host=x86_64-w64-mingw32 -$(package)_config_opts_darwin=--disable-shared --enable-static --prefix=$(host_prefix) +$(package)_config_opts_linux=--disable-shared --enable-static --with-wolfssl --without-ssl --prefix=$(host_prefix) --host=$(host) +$(package)_config_opts_mingw32=--enable-mingw --disable-shared --enable-static --with-wolfssl --without-ssl --prefix=$(host_prefix) --host=x86_64-w64-mingw32 +$(package)_config_opts_darwin=--disable-shared --enable-static --with-wolfssl --without-ssl --prefix=$(host_prefix) $(package)_cflags_darwin=-mmacosx-version-min=10.9 $(package)_conf_tool=./configure diff --git a/depends/packages/openssl.mk b/depends/packages/openssl.mk deleted file mode 100644 index 6f939f0fa..000000000 --- a/depends/packages/openssl.mk +++ /dev/null @@ -1,113 +0,0 @@ -package=openssl -$(package)_version=1.1.1h -$(package)_download_path=https://www.openssl.org/source -$(package)_file_name=$(package)-$($(package)_version).tar.gz -$(package)_sha256_hash=5c9ca8774bd7b03e5784f26ae9e9e6d749c9da2438545077e6b3d755a06595d9 - -define $(package)_set_vars -$(package)_config_env=AR="$($(package)_ar)" RANLIB="$($(package)_ranlib)" CC="$($(package)_cc)" -$(package)_config_opts=--prefix=$(host_prefix) --openssldir=$(host_prefix)/etc/openssl -$(package)_config_opts+=no-afalgeng -$(package)_config_opts+=no-asm -$(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-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-crypto-mdebug -$(package)_config_opts+=no-crypto-mdebug-backtrace -#$(package)_config_opts+=no-ct -#$(package)_config_opts+=no-des -$(package)_config_opts+=no-dgram -#$(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-egd -$(package)_config_opts+=no-engine -#$(package)_config_opts+=no-err -$(package)_config_opts+=no-gost -$(package)_config_opts+=no-heartbeats -#$(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-poly1305 -#$(package)_config_opts+=no-posix-io -$(package)_config_opts+=no-psk -$(package)_config_opts+=no-rc2 -$(package)_config_opts+=no-rc4 -$(package)_config_opts+=no-rc5 -$(package)_config_opts+=no-rdrand -$(package)_config_opts+=no-rfc3779 -$(package)_config_opts+=no-rmd160 -$(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-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+=$($(package)_cflags) $($(package)_cppflags) -$(package)_config_opts+=-DPURIFY -$(package)_config_opts_linux=-fPIC -Wa,--noexecstack -$(package)_config_opts_x86_64_linux=linux-x86_64 -$(package)_config_opts_i686_linux=linux-generic32 -$(package)_config_opts_arm_linux=linux-generic32 -$(package)_config_opts_aarch64_linux=linux-generic64 -$(package)_config_opts_mipsel_linux=linux-generic32 -$(package)_config_opts_mips_linux=linux-generic32 -$(package)_config_opts_powerpc_linux=linux-generic32 -$(package)_config_opts_x86_64_darwin=darwin64-x86_64-cc -$(package)_config_opts_x86_64_mingw32=mingw64 -$(package)_config_opts_i686_mingw32=mingw -endef - -define $(package)_preprocess_cmds - 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 - ./Configure $($(package)_config_opts) -endef - -define $(package)_build_cmds - $(MAKE) -j1 build_libs libcrypto.pc libssl.pc openssl.pc -endef - -define $(package)_stage_cmds - $(MAKE) DESTDIR=$($(package)_staging_dir) -j1 install_sw -endef - -define $(package)_postprocess_cmds - rm -rf share bin etc -endef diff --git a/depends/packages/packages.mk b/depends/packages/packages.mk index 9c76166af..3dd823772 100644 --- a/depends/packages/packages.mk +++ b/depends/packages/packages.mk @@ -39,8 +39,8 @@ native_packages := native_ccache wallet_packages=bdb ifeq ($(host_os),linux) - packages := boost openssl libevent zeromq $(zcash_packages) googletest libcurl #googlemock + packages := boost wolfssl libevent zeromq $(zcash_packages) googletest libcurl #googlemock else - packages := boost openssl libevent zeromq $(zcash_packages) libcurl googletest #googlemock + packages := boost wolfssl libevent zeromq $(zcash_packages) libcurl googletest #googlemock endif diff --git a/depends/packages/wolfssl.mk b/depends/packages/wolfssl.mk new file mode 100644 index 000000000..7b59e6f74 --- /dev/null +++ b/depends/packages/wolfssl.mk @@ -0,0 +1,46 @@ +package=wolfssl +$(package)_version=4.5.0 +$(package)_download_path=https://github.com/wolfSSL/wolfssl/archive +$(package)_download_file=v$($(package)_version)-stable.tar.gz +$(package)_file_name=wolfssl-$($(package)_version).tar.gz +$(package)_sha256_hash=7de62300ce14daa0051bfefc7c4d6302f96cabc768b6ae49eda77523b118250c + +define $(package)_set_vars +$(package)_config_env=AR="$($(package)_ar)" RANLIB="$($(package)_ranlib)" CC="$($(package)_cc)" +$(package)_config_opts=--prefix=$(host_prefix) +$(package)_config_opts+=--enable-static +$(package)_config_opts+=--disable-shared +$(package)_config_opts+=--disable-examples +$(package)_config_opts+=--disable-crypttests +$(package)_config_opts+=--enable-keygen +$(package)_config_opts+=--enable-certgen +$(package)_config_opts+=--enable-enckeys +$(package)_config_opts+=--enable-opensslall +$(package)_config_opts+=--enable-opensslextra +$(package)_config_opts+=--enable-harden +endef + +define $(package)_preprocess_cmds + cd $($(package)_build_subdir); ./autogen.sh +endef + +define $(package)_config_cmds + ./configure $($(package)_config_opts) +endef + +#define $(package)_config_cmds +# $($(package)_autoconf) +#endef + +define $(package)_build_cmds + $(MAKE) -j1 src/libwolfssl.la +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) install-libLTLIBRARIES install-includeHEADERS install-nobase_includeHEADERS install-pkgconfigDATA +endef + +#define $(package)_postprocess_cmds +# rm -rf bin share +#endef + diff --git a/src/cc/import.cpp b/src/cc/import.cpp index 4a1978e8d..27fafdc68 100644 --- a/src/cc/import.cpp +++ b/src/cc/import.cpp @@ -19,7 +19,7 @@ #include "crosschain.h" #include "primitives/transaction.h" #include "cc/CCinclude.h" -#include +#include #include "cc/CCtokens.h" #include "key_io.h" diff --git a/src/hush/tlsmanager.cpp b/src/hush/tlsmanager.cpp index 55c0d0d54..ceb5dae31 100644 --- a/src/hush/tlsmanager.cpp +++ b/src/hush/tlsmanager.cpp @@ -1,23 +1,132 @@ // Copyright (c) 2019-2020 The Hush developers // Distributed under the GPLv3 software license, see the accompanying // file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html -#include -#include -#include -#include "utiltls.h" - -#include -#include -#include "../util.h" -#include "../protocol.h" +#include +#include +#include +#include #include #include #include "tlsmanager.h" +#include "utiltls.h" + using namespace std; namespace hush { + +// this is the 'dh crypto environment' to be shared between two peers and it is meant to be public, therefore +// it is OK to hard code it (or as an alternative to read it from a file) +// ---- +// generated via: openssl dhparam -C 2048 +static WOLFSSL_DH *get_dh2048(void) +{ + static unsigned char dhp_2048[] = { + 0xFF, 0x4A, 0xA8, 0x6C, 0x68, 0xD4, 0x4C, 0x41, 0x73, 0x8D, + 0xD8, 0x14, 0x57, 0xF9, 0x1C, 0x35, 0x72, 0x5F, 0xCD, 0x24, + 0xCB, 0xD1, 0x77, 0x30, 0xC2, 0x9A, 0x69, 0x01, 0xCF, 0x01, + 0xDE, 0xD4, 0x67, 0xD4, 0xEE, 0x9A, 0x03, 0x1C, 0x27, 0x42, + 0x06, 0x3D, 0x1D, 0x91, 0x27, 0xCF, 0x1C, 0x17, 0xB3, 0xDC, + 0x9F, 0x6F, 0x12, 0xC8, 0x03, 0x5C, 0x01, 0xF3, 0x27, 0x7F, + 0x34, 0x58, 0xAE, 0xB9, 0xA7, 0xA9, 0xCE, 0x5E, 0x25, 0x7D, + 0x46, 0x84, 0xDD, 0xEE, 0x55, 0xFB, 0xEA, 0x1C, 0xCD, 0x9B, + 0x96, 0xC4, 0x22, 0x8C, 0x33, 0x8B, 0xC7, 0xE6, 0xCC, 0x4C, + 0x77, 0x1B, 0x7A, 0x46, 0xDE, 0x33, 0xAD, 0xBB, 0xFD, 0x2D, + 0xAD, 0x26, 0xE1, 0x27, 0x48, 0x94, 0xA3, 0x59, 0xC5, 0x10, + 0x5A, 0x86, 0x71, 0x8D, 0xAA, 0x15, 0x8B, 0xB2, 0xCB, 0x70, + 0xBE, 0x1F, 0x17, 0xBD, 0xEB, 0x51, 0xB1, 0x76, 0x0E, 0x24, + 0x43, 0xAA, 0x06, 0xC0, 0x97, 0x01, 0x25, 0x52, 0x30, 0x7A, + 0x56, 0x92, 0x3D, 0x8A, 0x3A, 0xBC, 0xFA, 0x98, 0x51, 0x04, + 0x1D, 0x9B, 0x05, 0xB8, 0x84, 0x8C, 0x2F, 0x7A, 0x94, 0x1E, + 0xAA, 0x51, 0xF2, 0x5D, 0x48, 0x50, 0x58, 0x8D, 0x7E, 0xBA, + 0xD3, 0xCC, 0xF2, 0x92, 0x28, 0xB1, 0x1C, 0x4B, 0x50, 0x10, + 0xFA, 0x7E, 0xDF, 0x8D, 0x23, 0x1C, 0x8C, 0x65, 0xE3, 0x86, + 0x16, 0x67, 0x88, 0x9E, 0xFC, 0x8B, 0xC8, 0x55, 0x38, 0x6E, + 0x79, 0x06, 0x6A, 0x6D, 0x72, 0x75, 0xA6, 0xAC, 0x77, 0x98, + 0xDD, 0xB2, 0x0B, 0xAA, 0x48, 0x54, 0xA9, 0x07, 0x7E, 0x8C, + 0x4C, 0x39, 0x08, 0x26, 0x6D, 0x53, 0xC2, 0xDF, 0xE2, 0xF0, + 0xD6, 0x8A, 0x4F, 0xB5, 0x7A, 0x32, 0xEE, 0x93, 0x0E, 0x2A, + 0x81, 0x2F, 0x3B, 0x1E, 0xE6, 0x38, 0xF8, 0x3C, 0xF5, 0x84, + 0xB4, 0xFB, 0x92, 0x12, 0x28, 0xA3 + }; + static unsigned char dhg_2048[] = { + 0x02 + }; + + WOLFSSL_DH *dh = wolfSSL_DH_new(); + + if (dh == NULL) + return NULL; + + if (wc_DhSetKey((DhKey*)dh->internal, dhp_2048, sizeof(dhp_2048), dhg_2048, sizeof(dhg_2048)) != 0) { + wolfSSL_DH_free(dh); + return NULL; + } + return dh; +} + +DH *tmp_dh_callback(WOLFSSL *ssl, int is_export, int keylength) +{ + LogPrint("tls", "TLS: %s: %s():%d - Using Diffie-Hellman param for PFS: is_export=%d, keylength=%d\n", + __FILE__, __func__, __LINE__, is_export, keylength); + + return get_dh2048(); +} + +/** if 'tls' debug category is enabled, collect info about certificates relevant to the passed context and print them on logs */ +static void dumpCertificateDebugInfo(int preverify_ok, WOLFSSL_X509_STORE_CTX* chainContext) +{ + if (!LogAcceptCategory("tls")) { + return; + } + + char buf[256] = {}; + WOLFSSL_X509 *cert; + int err, depth; + + cert = wolfSSL_X509_STORE_CTX_get_current_cert(chainContext); + err = wolfSSL_X509_STORE_CTX_get_error(chainContext); + depth = wolfSSL_X509_STORE_CTX_get_error_depth(chainContext); + + LogPrintf("TLS: %s: %s():%d - preverify_ok=%d, errCode=%d, depth=%d\n", + __FILE__, __func__, __LINE__, preverify_ok, err, depth); + + // is not useful checking preverify_ok because, after the chain root verification, it is set accordingly + // to the return value of this callback, and we choose to always return 1 + if (err != X509_V_OK ) { + LogPrintf("TLS: %s: %s():%d - Certificate Verification ERROR=%d: [%s] at chain depth=%d\n", + __FILE__, __func__, __LINE__, err, wolfSSL_X509_verify_cert_error_string(err), depth); + + if (cert && err == X509_V_ERR_CERT_HAS_EXPIRED) { + char time_buf[MAX_TIME_STRING_SZ]; + ASN1_TIME * at = wolfSSL_X509_get_notAfter(cert); + if (wolfSSL_ASN1_TIME_to_string(at, time_buf, sizeof(time_buf)) != NULL) { + LogPrintf("TLS: %s: %s():%d - expired on=%s\n", + __FILE__, __func__, __LINE__, buf); + } + } + } else if (cert) { + wolfSSL_X509_NAME_oneline(wolfSSL_X509_get_subject_name(cert), buf, 256); + LogPrintf("TLS: %s: %s():%d - subj name=%s\n", + __FILE__, __func__, __LINE__, buf); + + wolfSSL_X509_NAME_oneline(wolfSSL_X509_get_issuer_name(cert), buf, 256); + LogPrintf("TLS: %s: %s():%d - issuer name=%s\n", + __FILE__, __func__, __LINE__, buf); + + char time_buf[MAX_TIME_STRING_SZ]; + WOLFSSL_ASN1_TIME * at = wolfSSL_X509_get_notAfter(cert); + if (wolfSSL_ASN1_TIME_to_string(at, time_buf, sizeof(time_buf)) != NULL) { + LogPrintf("TLS: %s: %s():%d - expiring on=%s\n", + __FILE__, __func__, __LINE__, buf); + } + } else { + // should never happen + LogPrintf("TLS: %s: %s():%d - invalid cert/err\n", __FILE__, __func__, __LINE__); + } +} + /** * @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. * @@ -25,8 +134,18 @@ namespace hush * @param chainContext * @return int */ -int tlsCertVerificationCallback(int preverify_ok, X509_STORE_CTX* chainContext) +int tlsCertVerificationCallback(int preverify_ok, WOLFSSL_X509_STORE_CTX* chainContext) { + dumpCertificateDebugInfo(preverify_ok, chainContext); + + /* The return value controls the strategy of the further verification process. If it returns 0 + * the verification process is immediately stopped with "verification failed" state. + * If SSL_VERIFY_PEER has been set in set_verify, a verification failure alert is sent to the peer and the TLS/SSL + * handshake is terminated. + * If it returns 1, the verification process is continued. + * Here we choose to continue the verification process by returning 1 and to leave the optional cert + * verification if we call ValidatePeerCertificate(). + */ return 1; } /** @@ -38,42 +157,93 @@ int tlsCertVerificationCallback(int preverify_ok, X509_STORE_CTX* chainContext) * @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 TLSManager::waitFor(SSLConnectionRoutine eRoutine, SOCKET hSocket, WOLFSSL* ssl, int timeoutSec, unsigned long& err_code) { - int nErr = 0; - ERR_clear_error(); // clear the error queue + int retOp = 0; + err_code = 0; + + while (true) + { + // clear the current thread's error queue + wolfSSL_ERR_clear_error(); - while (true) { switch (eRoutine) { - case SSL_CONNECT: - nErr = SSL_connect(ssl); + case SSL_CONNECT: + { + retOp = wolfSSL_connect(ssl); + if (retOp == 0) { + err_code = wolfSSL_ERR_get_error(); + const char* error_str = wolfSSL_ERR_error_string(err_code, NULL); + LogPrint("tls", "TLS: WARNING: %s: %s():%d - SSL_CONNECT err: %s\n", + __FILE__, __func__, __LINE__, error_str); + return -1; + } + } break; - - case SSL_ACCEPT: - nErr = SSL_accept(ssl); + + case SSL_ACCEPT: + { + retOp = wolfSSL_accept(ssl); + if (retOp == 0) { + err_code = wolfSSL_ERR_get_error(); + const char* error_str = wolfSSL_ERR_error_string(err_code, NULL); + LogPrint("tls", "TLS: WARNING: %s: %s():%d - SSL_ACCEPT err: %s\n", + __FILE__, __func__, __LINE__, error_str); + return -1; + } + } break; - - case SSL_SHUTDOWN: - nErr = SSL_shutdown(ssl); + + case SSL_SHUTDOWN: + { + if (hSocket != INVALID_SOCKET) { + std::string disconnectedPeer("no info"); + struct sockaddr_in addr; + socklen_t serv_len = sizeof(addr); + int ret = getpeername(hSocket, (struct sockaddr *)&addr, &serv_len); + if (ret == 0) { + disconnectedPeer = std::string(inet_ntoa(addr.sin_addr)) + ":" + std::to_string(ntohs(addr.sin_port)); + } + LogPrint("tls", "TLS: shutting down fd=%d, peer=%s\n", hSocket, disconnectedPeer); + } + retOp = wolfSSL_shutdown(ssl); + } break; - - default: - return -1; + + default: + return -1; } if (eRoutine == SSL_SHUTDOWN) { - if (nErr >= 0) + if (retOp == 0) { + LogPrint("tls", "TLS: WARNING: %s: %s():%d - SSL_SHUTDOWN: The close_notify was sent but the peer did not send it back yet.\n", + __FILE__, __func__, __LINE__); + // do not call SSL_get_error() because it may misleadingly indicate an error even though no error occurred. break; + } else if (retOp == 1) { + LogPrint("tls", "TLS: %s: %s():%d - SSL_SHUTDOWN completed\n", __FILE__, __func__, __LINE__); + break; + } else { + LogPrint("tls", "TLS: %s: %s():%d - SSL_SHUTDOWN failed\n", __FILE__, __func__, __LINE__); + // the error will be read afterwards + } } else { - if (nErr == 1) + if (retOp == 1) { + LogPrint("tls", "TLS: %s: %s():%d - %s completed\n", __FILE__, __func__, __LINE__, + eRoutine == SSL_CONNECT ? "SSL_CONNECT" : "SSL_ACCEPT"); break; + } } - int sslErr = SSL_get_error(ssl, nErr); + int sslErr = wolfSSL_get_error(ssl, retOp); - 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; + if (sslErr != WOLFSSL_ERROR_WANT_READ && sslErr != WOLFSSL_ERROR_WANT_WRITE) { + err_code = wolfSSL_ERR_get_error(); + const char* error_str = wolfSSL_ERR_error_string(err_code, NULL); + LogPrint("tls", "TLS: WARNING: %s: %s():%d - routine(%d), sslErr[0x%x], retOp[%d], errno[0x%x], lib[0x%x], func[0x%x], reas[0x%x]-> err: %s\n", + __FILE__, __func__, __LINE__, + eRoutine, sslErr, retOp, errno, wolfSSL_ERR_GET_LIB(err_code), ERR_GET_FUNC(err_code), wolfSSL_ERR_GET_REASON(err_code), error_str); + retOp = -1; break; } @@ -83,66 +253,88 @@ int TLSManager::waitFor(SSLConnectionRoutine eRoutine, SOCKET hSocket, SSL* ssl, struct timeval timeout = {timeoutSec, 0}; - if (sslErr == SSL_ERROR_WANT_READ) { + if (sslErr == WOLFSSL_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; + LogPrint("tls", "TLS: ERROR: %s: %s():%d - WANT_READ timeout on %s\n", __FILE__, __func__, __LINE__, + (eRoutine == SSL_CONNECT ? "SSL_CONNECT" : + (eRoutine == SSL_ACCEPT ? "SSL_ACCEPT" : "SSL_SHUTDOWN" ))); + err_code = SELECT_TIMEDOUT; + retOp = -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; + LogPrint("tls", "TLS: ERROR: %s: %s: WANT_READ ssl_err_code: 0x%x; errno: %s\n", + __FILE__, __func__, sslErr, strerror(errno)); + retOp = -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; + LogPrint("tls", "TLS: ERROR: %s: %s():%d - WANT_WRITE timeout on %s\n", __FILE__, __func__, __LINE__, + (eRoutine == SSL_CONNECT ? "SSL_CONNECT" : + (eRoutine == SSL_ACCEPT ? "SSL_ACCEPT" : "SSL_SHUTDOWN" ))); + err_code = SELECT_TIMEDOUT; + retOp = -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; + LogPrint("tls", "TLS: ERROR: %s: %s: WANT_WRITE ssl_err_code: 0x%x; errno: %s\n", + __FILE__, __func__, sslErr, strerror(errno)); + retOp = -1; break; } } } - return nErr; + return retOp; } + /** * @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. + * @return WOLFSSL* returns a ssl* if successful, otherwise returns NULL. */ -SSL* TLSManager::connect(SOCKET hSocket, const CAddress& addrConnect) +WOLFSSL* TLSManager::connect(SOCKET hSocket, const CAddress& addrConnect, unsigned long& err_code) { - LogPrint("net", "TLS: establishing connection tid=%X peerid=%s\n", pthread_self(), addrConnect.ToString()); + LogPrint("tls", "TLS: establishing connection (tid = %X), (peerid = %s)\n", pthread_self(), addrConnect.ToString()); - SSL* ssl = NULL; + err_code = 0; + WOLFSSL* 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) - + if ((ssl = wolfSSL_new(tls_ctx_client))) { + if (wolfSSL_set_fd(ssl, hSocket)) { + int ret = TLSManager::waitFor(SSL_CONNECT, hSocket, ssl, (DEFAULT_CONNECT_TIMEOUT / 1000), err_code); + if (ret == 1) + { bConnectedTLS = true; + } } } + else + { + err_code = wolfSSL_ERR_get_error(); + const char* error_str = wolfSSL_ERR_error_string(err_code, NULL); + LogPrint("tls", "TLS: %s: %s():%d - SSL_new failed err: %s\n", + __FILE__, __func__, __LINE__, error_str); + } if (bConnectedTLS) { - LogPrintf("TLS: connection to %s has been established. Using cipher: %s\n", addrConnect.ToString(), SSL_get_cipher(ssl)); + LogPrintf("TLS: connection to %s has been established (tlsv = %s 0x%04x / ssl = %s 0x%x ). Using cipher: %s\n", + addrConnect.ToString(), wolfSSL_get_version(ssl), wolfSSL_version(ssl), wolfSSL_OpenSSL_version(), wolfSSL_lib_version_hex(), wolfSSL_get_cipher_name(ssl)); } else { - LogPrintf("TLS: %s: TLS connection to %s failed\n", __func__, addrConnect.ToString()); + LogPrintf("TLS: %s: %s():%d - TLS connection to %s failed (err_code 0x%X)\n", + __FILE__, __func__, __LINE__, addrConnect.ToString(), err_code); if (ssl) { - SSL_free(ssl); + wolfSSL_free(ssl); ssl = NULL; } } + return ssl; } /** @@ -152,49 +344,98 @@ SSL* TLSManager::connect(SOCKET hSocket, const CAddress& addrConnect) * @param privateKeyFile private key file path * @param certificateFile certificate key file path * @param trustedDirs trusted directories - * @return SSL_CTX* returns the context. + * @return WOLSSL_CTX* returns the context. */ -SSL_CTX* TLSManager::initCtx( +WOLFSSL_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; + LogPrintf("TLS: %s: %s():%d - Initializing %s context\n", + __FILE__, __func__, __LINE__, ctxType == SERVER_CONTEXT ? "server" : "client"); + if (!boost::filesystem::exists(privateKeyFile) || !boost::filesystem::exists(certificateFile)) { + return NULL; + } + bool bInitialized = false; - SSL_CTX* tlsCtx = NULL; + WOLFSSL_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); + if ((tlsCtx = wolfSSL_CTX_new(ctxType == SERVER_CONTEXT ? wolfTLSv1_3_server_method() : wolfTLSv1_3_client_method()))) { + wolfSSL_CTX_set_mode(tlsCtx, SSL_MODE_AUTO_RETRY); + + // Disable TLS < 1.3 ... imho redundant, because v1.3 is required via method + int ret = wolfSSL_CTX_set_min_proto_version(tlsCtx, TLS1_3_VERSION); + if (ret == 0) { + LogPrintf("TLS: WARNING: %s: %s():%d - failed to set min TLS version\n", __FILE__, __func__, __LINE__); + } + + LogPrintf("TLS: %s: %s():%d - setting cipher list\n", __FILE__, __func__, __LINE__); + + // Default TLSv1.3 cipher list is "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256" + // Nodes will randomly choose to prefer first cipher or the second, to create diversity on the network + // and not be in the situation where all nodes have the same list so the first is always used + if(GetRand(100) > 50) { + if (wolfSSL_CTX_set_cipher_list(tlsCtx, "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256")) { + LogPrintf("%s: Preferring TLS_AES256-GCM-SHA384\n", __func__); + } else { + LogPrintf("%s: Setting preferred cipher failed !!!\n", __func__); + } + } else { + if (wolfSSL_CTX_set_cipher_list(tlsCtx, "TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384")) { + LogPrintf("%s: Preferring TLS_AES256-GCM-SHA384\n", __func__); + } else { + LogPrintf("%s: Setting preferred cipher failed !!!\n", __func__); + } + } + + // TLSv1.3 has ephemeral Diffie-Hellman as the only key exchange mechanism, so that perfect forward secrecy is ensured. + + if (ctxType == SERVER_CONTEXT) { + // amongst the Cl/Srv mutually-acceptable set, pick the one that the server prefers most instead of the one that + // the client prefers most + wolfSSL_CTX_set_options(tlsCtx, SSL_OP_CIPHER_SERVER_PREFERENCE); + + LogPrintf("TLS: %s: %s():%d - setting dh callback\n", __FILE__, __func__, __LINE__); + SSL_CTX_set_tmp_dh_callback(tlsCtx, tmp_dh_callback); + } + + // Fix for Secure Client-Initiated Renegotiation DoS threat: + // In WolfSSL library renegotiation is disabled by default build config + + // Support for TLSv1.3 should be the only one compiled + // ./configure --disable-oldtls --disable-tlsv12 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) + for (boost::filesystem::path trustedDir : trustedDirs) + { + if (wolfSSL_CTX_load_verify_locations(tlsCtx, NULL, trustedDir.string().c_str()) == 1) { trustedPathsNum++; + } } - if (rootCertsNum == 0 && trustedPathsNum == 0) + 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); + wolfSSL_CTX_set_verify(tlsCtx, WOLFSSL_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)) { + if (wolfSSL_CTX_use_certificate_file(tlsCtx, certificateFile.string().c_str(), WOLFSSL_FILETYPE_PEM) > 0) { + if (wolfSSL_CTX_use_PrivateKey_file(tlsCtx, privateKeyFile.string().c_str(), WOLFSSL_FILETYPE_PEM) > 0) { + if (wolfSSL_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 privateKey file\n", __FILE__, __func__); + } } else { LogPrintf("TLS: ERROR: %s: %s: failed to use certificate file\n", __FILE__, __func__); - ERR_print_errors_fp(stderr); + wolfSSL_ERR_dump_errors_fp(stderr); } } else { LogPrintf("TLS: ERROR: %s: %s: failed to create TLS context\n", __FILE__, __func__); @@ -202,32 +443,11 @@ SSL_CTX* TLSManager::initCtx( if (!bInitialized) { if (tlsCtx) { - SSL_CTX_free(tlsCtx); + wolfSSL_CTX_free(tlsCtx); tlsCtx = NULL; } } - SSL_CTX_set_cipher_list(tlsCtx, ""); // removes all <= TLS1.2 ciphers - // default is "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256" - // Nodes will randomly choose to prefer one suite or the other, to create diversity on the network - // and not be in the situation where all nodes have the same list so the first is always used - if(GetRand(100) > 50) { - LogPrintf("%s: Preferring TLS_AES256-GCM-SHA384\n", __func__); - SSL_CTX_set_ciphersuites(tlsCtx, "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256"); - } else { - LogPrintf("%s: Preferring TLS_CHACHA20-POLY1305\n", __func__); - SSL_CTX_set_ciphersuites(tlsCtx, "TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384"); - } - - /* - STACK_OF(SSL_CIPHER) *sk = SSL_CTX_get_ciphers(tlsCtx); - for (int i = 0; i < sk_SSL_CIPHER_num(sk); i++) - { - const SSL_CIPHER *c = sk_SSL_CIPHER_value(sk, i); - LogPrintf("%s: AVAILABLE CIPHER %s\n", __func__, SSL_CIPHER_get_name(c)); - } - */ - return tlsCtx; } /** @@ -273,26 +493,45 @@ bool TLSManager::prepareCredentials() * @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 + * @return WOLFSSL* returns pointer to the ssl object if successful, otherwise returns NULL */ -SSL* TLSManager::accept(SOCKET hSocket, const CAddress& addr) +WOLFSSL* TLSManager::accept(SOCKET hSocket, const CAddress& addr, unsigned long& err_code) { - LogPrint("net", "TLS: accepting connection from %s (tid = %X)\n", addr.ToString(), pthread_self()); + LogPrint("tls", "TLS: accepting connection from %s (tid = %X)\n", addr.ToString(), pthread_self()); - SSL* ssl = NULL; + err_code = 0; + WOLFSSL* 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) + if ((ssl = wolfSSL_new(tls_ctx_server))) { + if (wolfSSL_set_fd(ssl, hSocket)) { + int ret = TLSManager::waitFor(SSL_ACCEPT, hSocket, ssl, (DEFAULT_CONNECT_TIMEOUT / 1000), err_code); + if (ret == 1) + { bAcceptedTLS = true; + } } } + else + { + err_code = wolfSSL_ERR_get_error(); + const char* error_str = wolfSSL_ERR_error_string(err_code, NULL); + LogPrint("tls", "TLS: %s: %s():%d - SSL_new failed err: %s\n", + __FILE__, __func__, __LINE__, error_str); + } if (bAcceptedTLS) { - LogPrintf("TLS: connection from %s has been accepted. Using cipher: %s\n", addr.ToString(), SSL_get_cipher(ssl)); + LogPrintf("TLS: connection from %s has been accepted (tlsv = %s 0x%04x / ssl = %s 0x%x ). Using cipher: %s\n", + addr.ToString(), wolfSSL_get_version(ssl), wolfSSL_version(ssl), wolfSSL_OpenSSL_version(), wolfSSL_lib_version_hex(), wolfSSL_get_cipher(ssl)); + + WOLFSSL_STACK *sk = wolfSSL_get_ciphers_compat(ssl); + for (int i = 0; i < wolfSSL_sk_SSL_CIPHER_num(sk); i++) { + const WOLFSSL_CIPHER *c = wolfSSL_sk_SSL_CIPHER_value(sk, i); + LogPrint("tls", "TLS: supporting cipher: %s\n", wolfSSL_CIPHER_get_name(c)); + } } else { - LogPrintf("TLS: ERROR: %s: %s: TLS connection from %s failed\n", __FILE__, __func__, addr.ToString()); + LogPrintf("TLS: %s: %s():%d - TLS connection from %s failed (err_code 0x%X)\n", + __FILE__, __func__, __LINE__, addr.ToString(), err_code); if (ssl) { SSL_free(ssl); @@ -331,7 +570,7 @@ void TLSManager::cleanNonTLSPool(std::vector& vPool, CCriticalSection 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); + LogPrint("tls", "TLS: Node %s is deleted from the non-TLS pool\n", nodeAddr.ipAddr); } } @@ -386,16 +625,16 @@ int TLSManager::threadSocketHandler(CNode* pnode, fd_set& fdsetRecv, fd_set& fds LOCK(pnode->cs_hSocket); if (pnode->hSocket == INVALID_SOCKET) { - LogPrint("net", "Receive: connection with %s is already closed\n", pnode->addr.ToString()); + LogPrint("tls", "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); + wolfSSL_ERR_clear_error(); // clear the error queue, otherwise we may be reading an old error that occurred previously in the current thread + nBytes = wolfSSL_read(pnode->ssl, pchBuf, sizeof(pchBuf)); + nRet = wolfSSL_get_error(pnode->ssl, nBytes); } else { nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT); nRet = WSAGetLastError(); @@ -409,20 +648,35 @@ int TLSManager::threadSocketHandler(CNode* pnode, fd_set& fdsetRecv, fd_set& fds pnode->nRecvBytes += nBytes; pnode->RecordBytesRecv(nBytes); } else if (nBytes == 0) { + + if (bIsSSL) { + unsigned long error = ERR_get_error(); + const char* error_str = ERR_error_string(error, NULL); + LogPrint("tls", "TLS: WARNING: %s: %s():%d - SSL_read err: %s\n", + __FILE__, __func__, __LINE__, error_str); + } // socket closed gracefully (peer disconnected) // if (!pnode->fDisconnect) - LogPrint("net", "socket closed (%s)\n", pnode->addr.ToString()); + LogPrint("tls", "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 (nRet != WOLFSSL_ERROR_WANT_READ && nRet != WOLFSSL_ERROR_WANT_WRITE) { if (!pnode->fDisconnect) - LogPrintf("ERROR: SSL_read %s\n", ERR_error_string(nRet, NULL)); + LogPrintf("TSL: ERROR: SSL_read %s\n", ERR_error_string(nRet, NULL)); pnode->CloseSocketDisconnect(); + + unsigned long error = ERR_get_error(); + const char* error_str = ERR_error_string(error, NULL); + LogPrint("tls", "TLS: WARNING: %s: %s():%d - SSL_read - code[0x%x], err: %s\n", + __FILE__, __func__, __LINE__, nRet, error_str); + } else { // preventive measure from exhausting CPU usage // @@ -431,7 +685,7 @@ int TLSManager::threadSocketHandler(CNode* pnode, fd_set& fdsetRecv, fd_set& fds } else { if (nRet != WSAEWOULDBLOCK && nRet != WSAEMSGSIZE && nRet != WSAEINTR && nRet != WSAEINPROGRESS) { if (!pnode->fDisconnect) - LogPrintf("ERROR: socket recv %s\n", NetworkErrorString(nRet)); + LogPrintf("TSL: ERROR: socket recv %s\n", NetworkErrorString(nRet)); pnode->CloseSocketDisconnect(); } } @@ -461,14 +715,15 @@ 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. - + // + wolfSSL_load_error_strings(); + wolfSSL_ERR_load_crypto_strings(); + wolfSSL_library_init(); + namespace fs = boost::filesystem; fs::path certFile = GetArg("-tlscertpath", ""); if (!fs::exists(certFile)) - certFile = (GetDataDir() / TLS_CERT_FILE_NAME); + certFile = (GetDataDir() / TLS_CERT_FILE_NAME); fs::path privKeyFile = GetArg("-tlskeypath", ""); if (!fs::exists(privKeyFile)) { @@ -489,15 +744,16 @@ bool TLSManager::initialize() 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"); + LogPrint("tls", "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); + wolfSSL_CTX_free (tls_ctx_server); } } else { LogPrintf("TLS: ERROR: %s: %s: failed to initialize TLS server context\n", __FILE__, __func__); diff --git a/src/hush/tlsmanager.h b/src/hush/tlsmanager.h index ef677631e..f6add7e74 100644 --- a/src/hush/tlsmanager.h +++ b/src/hush/tlsmanager.h @@ -1,15 +1,12 @@ // Copyright (c) 2019-2020 The Hush developers // Distributed under the GPLv3 software license, see the accompanying // file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html -#include -#include -#include -#include "utiltls.h" +#include +#include #include "tlsenums.h" #include #include #include "../util.h" -#include "../protocol.h" #include "../net.h" #include "sync.h" #include @@ -43,16 +40,22 @@ bool operator==(const _NODE_ADDR b) const class TLSManager { public: - int waitFor(SSLConnectionRoutine eRoutine, SOCKET hSocket, SSL* ssl, int timeoutSec); - SSL* connect(SOCKET hSocket, const CAddress& addrConnect); - SSL_CTX* initCtx( + /* This is set as a custom error number which is not an error in OpenSSL protocol. + A true (not null) OpenSSL error returned by ERR_get_error() consists of a library number, + function code and reason code. */ + static const long SELECT_TIMEDOUT = 0xFFFFFFFF; + + int waitFor(SSLConnectionRoutine eRoutine, SOCKET hSocket, WOLFSSL* ssl, int timeoutSec, unsigned long& err_code); + + WOLFSSL* connect(SOCKET hSocket, const CAddress& addrConnect, unsigned long& err_code); + WOLFSSL_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); + WOLFSSL* accept(SOCKET hSocket, const CAddress& addr, unsigned long& err_code); 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); diff --git a/src/hush/utiltls.cpp b/src/hush/utiltls.cpp index 48fb9cc50..f6b7ba171 100644 --- a/src/hush/utiltls.cpp +++ b/src/hush/utiltls.cpp @@ -6,15 +6,13 @@ #include #include -#include -#include -#include -#include -#include +#include +#include +#include #include #include -#include "util.h" +#include "../util.h" #include "utiltls.h" namespace hush { @@ -67,37 +65,56 @@ static const char defaultRootCerts[] = }; // Generates RSA keypair (a private key of 'bits' length for a specified 'uPublicKey') -// -static EVP_PKEY* GenerateRsaKey(int bits, BN_ULONG uPublicKey) +// obsolete since we use EC instead of RSA +static WOLFSSL_EVP_PKEY* GenerateRsaKey(int bits, WOLFSSL_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); + WOLFSSL_EVP_PKEY *evpPrivKey = NULL; + + WOLFSSL_BIGNUM *pubKey = wolfSSL_BN_new(); + if (pubKey) { + if (wolfSSL_BN_set_word(pubKey, uPublicKey)) { + WOLFSSL_RSA *privKey = wolfSSL_RSA_new(); + if (privKey) { + if (wolfSSL_RAND_poll() && wolfSSL_RSA_generate_key_ex(privKey, bits, pubKey, NULL)) { + if ((evpPrivKey = wolfSSL_EVP_PKEY_new())) { + if (!wolfSSL_EVP_PKEY_assign_RSA(evpPrivKey, privKey)) { + wolfSSL_EVP_PKEY_free(evpPrivKey); evpPrivKey = NULL; } } } - if(!evpPrivKey) // EVP_PKEY_assign_RSA uses the supplied key internally - RSA_free(privKey); + if(!evpPrivKey) { + wolfSSL_RSA_free(privKey); + } } } - BN_free(pubKey); + wolfSSL_BN_free(pubKey); + } + + return evpPrivKey; +} + +// Generates EC keypair +// +static WOLFSSL_EVP_PKEY* GenerateEcKey(int nid = NID_X9_62_prime256v1) +{ + WOLFSSL_EVP_PKEY *evpPrivKey = NULL; + WOLFSSL_EC_KEY *privKey = wolfSSL_EC_KEY_new_by_curve_name(nid); + if (privKey) { + wolfSSL_EC_KEY_set_asn1_flag(privKey, OPENSSL_EC_NAMED_CURVE); + if (wolfSSL_EC_KEY_generate_key(privKey)) { + if ((evpPrivKey = wolfSSL_EVP_PKEY_new())) { + if (!wolfSSL_EVP_PKEY_assign_EC_KEY(evpPrivKey, privKey)) { + wolfSSL_EVP_PKEY_free(evpPrivKey); + evpPrivKey = NULL; + } + } + } + + if(!evpPrivKey) { + wolfSSL_EC_KEY_free(privKey); + } } return evpPrivKey; @@ -105,42 +122,30 @@ static EVP_PKEY* GenerateRsaKey(int bits, BN_ULONG uPublicKey) // 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) +static WOLFSSL_X509* GenerateCertificate(WOLFSSL_EVP_PKEY *keypair) { - if (!keypair) + if (!keypair) { return NULL; + } - X509 *cert = X509_new(); - if (cert) - { + WOLFSSL_X509 *cert = wolfSSL_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)); + + if (wolfSSL_RAND_bytes((unsigned char*)&sn, sizeof(sn)) &&wolfSSL_ASN1_INTEGER_set(wolfSSL_X509_get_serialNumber(cert), sn)) { + wolfSSL_X509_gmtime_adj(wolfSSL_X509_get_notBefore(cert), 0); + wolfSSL_X509_gmtime_adj(wolfSSL_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 (wolfSSL_X509_set_pubkey(cert, keypair)) { + // private key from keypair is used; signature will be set inside of the cert + bCertSigned = wolfSSL_X509_sign(cert, keypair, wolfSSL_EVP_sha512()); } } - if (!bCertSigned) - { - X509_free(cert); + if (!bCertSigned) { + wolfSSL_X509_free(cert); cert = NULL; } } @@ -150,7 +155,7 @@ static X509* GenerateCertificate(EVP_PKEY *keypair) // Stores key to file, specified by the 'filePath' // -static bool StoreKey(EVP_PKEY *key, const boost::filesystem::path &filePath, const std::string &passphrase) +static bool StoreKey(WOLFSSL_EVP_PKEY *key, const boost::filesystem::path &filePath, const std::string &passphrase) { if (!key) return false; @@ -160,12 +165,17 @@ static bool StoreKey(EVP_PKEY *key, const boost::filesystem::path &filePath, con FILE *keyfd = fopen(filePath.string().c_str(), "wb"); if (keyfd) { - const EVP_CIPHER* pCipher = NULL; + WOLFSSL_EC_KEY *ec_key = NULL; + ec_key = wolfSSL_EVP_PKEY_get0_EC_KEY(key); + if (ec_key != NULL) + { + const WOLFSSL_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); + if (passphrase.length() && (pCipher = wolfSSL_EVP_aes_256_cbc())) + bStored = wolfSSL_PEM_write_ECPrivateKey(keyfd, ec_key, pCipher, NULL, 0, NULL, (void*)passphrase.c_str()); + else + bStored = wolfSSL_PEM_write_ECPrivateKey(keyfd, ec_key, NULL, NULL, 0, NULL, NULL); + } fclose(keyfd); } @@ -175,7 +185,7 @@ static bool StoreKey(EVP_PKEY *key, const boost::filesystem::path &filePath, con // Stores certificate to file, specified by the 'filePath' // -static bool StoreCertificate(X509 *cert, const boost::filesystem::path &filePath) +static bool StoreCertificate(WOLFSSL_X509 *cert, const boost::filesystem::path &filePath) { if (!cert) return false; @@ -185,7 +195,7 @@ static bool StoreCertificate(X509 *cert, const boost::filesystem::path &filePath FILE *certfd = fopen(filePath.string().c_str(), "wb"); if (certfd) { - bStored = PEM_write_X509(certfd, cert); + bStored = wolfSSL_PEM_write_X509(certfd, cert); fclose(certfd); } @@ -194,34 +204,74 @@ static bool StoreCertificate(X509 *cert, const boost::filesystem::path &filePath // Loads key from file, specified by the 'filePath' // -static EVP_PKEY* LoadKey(const boost::filesystem::path &filePath, const std::string &passphrase) +static WOLFSSL_EVP_PKEY* old_LoadKey(const boost::filesystem::path &filePath, const std::string &passphrase) { if (!boost::filesystem::exists(filePath)) return NULL; - EVP_PKEY *key = NULL; + WOLFSSL_EVP_PKEY *key = wolfSSL_EVP_PKEY_new(); FILE *keyfd = fopen(filePath.string().c_str(), "rb"); if (keyfd) { - key = PEM_read_PrivateKey(keyfd, NULL, NULL, passphrase.length() ? (void*)passphrase.c_str() : NULL); + key = wolfSSL_PEM_read_PrivateKey(keyfd, NULL, NULL, passphrase.length() ? (void*)passphrase.c_str() : NULL); fclose(keyfd); } return key; } +// Loads key from file, specified by the 'filePath' +// +static WOLFSSL_EVP_PKEY* LoadKey(const boost::filesystem::path &filePath, const std::string &passphrase) +{ + if (!boost::filesystem::exists(filePath)) + return NULL; + + WOLFSSL_EVP_PKEY *key = NULL; + FILE *keyfd = fopen(filePath.string().c_str(), "rb"); + byte der[4096]; + byte pem[4096]; + WOLFSSL_EC_KEY *ecKey; + ecKey = wolfSSL_EC_KEY_new(); + word32 idx = 0; + + if (keyfd) + { + int fileSz = fread(pem, 1, 4096, keyfd); + fclose(keyfd); + + if (fileSz > 0) + { + if (ecKey) + { + int derSz = wc_KeyPemToDer(pem, fileSz, der, 4096, passphrase.c_str()); + int ret_decode = wc_EccPrivateKeyDecode(der, &idx, (ecc_key*)ecKey->internal, derSz); + if (ret_decode == 0) + { + if (key = wolfSSL_EVP_PKEY_new()) + { + wolfSSL_EVP_PKEY_assign_EC_KEY(key, ecKey); + } + } + } + } + } + + return key; +} + // Loads certificate from file, specified by the 'filePath' // -static X509* LoadCertificate(const boost::filesystem::path &filePath) +static WOLFSSL_X509* LoadCertificate(const boost::filesystem::path &filePath) { if (!boost::filesystem::exists(filePath)) return NULL; - X509 *cert = NULL; + WOLFSSL_X509 *cert = NULL; FILE *certfd = fopen(filePath.string().c_str(), "rb"); if (certfd) { - cert = PEM_read_X509(certfd, NULL, NULL, NULL); + cert = wolfSSL_PEM_read_X509(certfd, NULL, NULL, NULL); fclose(certfd); } @@ -231,59 +281,24 @@ static X509* LoadCertificate(const boost::filesystem::path &filePath) // 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) +static bool IsMatching(WOLFSSL_EVP_PKEY *key, WOLFSSL_X509 *cert) { if (!key || !cert) return false; - bool bIsMatching = false; - - EVP_PKEY_CTX *ctxSign = EVP_PKEY_CTX_new(key, NULL); - if (ctxSign) + if (wolfSSL_X509_verify(cert, key) == WOLFSSL_SUCCESS) { - 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 true; } - - return bIsMatching; + + LogPrintf("Loaded key and certificate do not match, delete them to generate new credentials!!!\n"); + + return false; } // 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) +static bool CheckCredentials(WOLFSSL_EVP_PKEY *key, WOLFSSL_X509 *cert) { if (!key || !cert) return false; @@ -292,22 +307,29 @@ static bool CheckCredentials(EVP_PKEY *key, X509 *cert) // Validating the correctness of a private-public key pair, depending on a key type // - switch (EVP_PKEY_base_id(key)) + switch (wolfSSL_EVP_PKEY_base_id(key)) { case EVP_PKEY_RSA: case EVP_PKEY_RSA2: { - RSA *rsaKey = EVP_PKEY_get1_RSA(key); + WOLFSSL_RSA *rsaKey = wolfSSL_EVP_PKEY_get1_RSA(key); if (rsaKey) { - bIsOk = (RSA_check_key(rsaKey) == 1); - RSA_free(rsaKey); + bIsOk = (wc_CheckRsaKey((RsaKey*)rsaKey->internal) == 0); + wolfSSL_RSA_free(rsaKey); + } + break; + } + case EVP_PKEY_EC: + { + WOLFSSL_EC_KEY *eccKey = wolfSSL_EVP_PKEY_get1_EC_KEY(key); + if (eccKey) + { + bIsOk = (wc_ecc_check_key((ecc_key*)eccKey->internal) == 0); + wolfSSL_EC_KEY_free(eccKey); } break; } - - // Currently only RSA keys are supported. - // Other key types can be added here in further. default: bIsOk = false; @@ -329,24 +351,28 @@ CredentialsStatus VerifyCredentials( { CredentialsStatus status = credAbsent; - EVP_PKEY *key = NULL; - X509 *cert = NULL; + WOLFSSL_EVP_PKEY *key = NULL; + WOLFSSL_X509 *cert = NULL; key = LoadKey(keyPath, passphrase); cert = LoadCertificate(certPath); - if (key && cert) + if (key && cert) { status = CheckCredentials(key, cert) ? credOk : credNonConsistent; - else if (!key && !cert) + } else if (!key && !cert) { status = credAbsent; - else + } else { status = credPartiallyAbsent; - - if (key) - EVP_PKEY_free(key); - if (cert) - X509_free(cert); - + } + + if (key) { + wolfSSL_EVP_PKEY_free(key); + } + + if (cert) { + wolfSSL_X509_free(cert); + } + return status; } @@ -359,27 +385,31 @@ bool GenerateCredentials( { bool bGenerated = false; - EVP_PKEY *key = NULL; - X509 *cert = NULL; + WOLFSSL_EVP_PKEY *key = NULL; + WOLFSSL_X509 *cert = NULL; - // Generating RSA key and the self-signed certificate for it + // Generating key and the self-signed certificate for it // - key = GenerateRsaKey(TLS_RSA_KEY_SIZE, RSA_F4); + //key = GenerateRsaKey(TLS_RSA_KEY_SIZE, RSA_F4); + //key = GenerateEcKey(NID_secp256k1); + key = GenerateEcKey(); if (key) { cert = GenerateCertificate(key); if (cert) { - if (StoreKey(key, keyPath, passphrase) && - StoreCertificate(cert, certPath)) + bool bKey = StoreKey(key, keyPath, passphrase); + bool bCert = StoreCertificate(cert, certPath); + + if ( bKey && bCert ) { bGenerated = true; LogPrintStr("TLS: New private key and self-signed certificate were generated successfully\n"); } - X509_free(cert); + wolfSSL_X509_free(cert); } - EVP_PKEY_free(key); + wolfSSL_EVP_PKEY_free(key); } return bGenerated; @@ -390,49 +420,58 @@ bool GenerateCredentials( // 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) +bool ValidatePeerCertificate(WOLFSSL *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; + WOLFSSL_X509 *cert = wolfSSL_get_peer_certificate (ssl); + if (cert) { + long errCode = wolfSSL_get_verify_result(ssl); + if (errCode != X509_V_OK) + { + LogPrint("tls", "TLS: %s: %s():%d - Certificate Verification ERROR=%d: [%s]\n", + __FILE__, __func__, __LINE__, errCode, wolfSSL_X509_verify_cert_error_string(errCode)); + } else { + bIsOk = true; + + char buf[256]; + wolfSSL_X509_NAME_oneline(wolfSSL_X509_get_subject_name(cert), buf, 256); + LogPrint("tls", "TLS: %s: %s():%d - subj name=%s\n", + __FILE__, __func__, __LINE__, buf); + + wolfSSL_X509_NAME_oneline(wolfSSL_X509_get_issuer_name(cert), buf, 256); + LogPrint("tls", "TLS: %s: %s():%d - issuer name=%s\n", + __FILE__, __func__, __LINE__, buf); + } + + wolfSSL_X509_free(cert); + } else { + LogPrint("tls", "TLS: %s: %s():%d - WARNING: Peer does not have certificate\n", + __FILE__, __func__, __LINE__); } 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) +bool ValidateCertificate(WOLFSSL_CTX *ssl_ctx) { - if (!ssl_ctx) + if (!ssl_ctx) { return false; - + } + bool bIsOk = false; - X509_STORE *store = SSL_CTX_get_cert_store(ssl_ctx); + WOLFSSL_X509_STORE *store = wolfSSL_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); + if (store) { + WOLFSSL_X509_STORE_CTX *ctx = wolfSSL_X509_STORE_CTX_new(); + if (ctx) { + bIsOk = (wolfSSL_X509_verify_cert(ctx) == WOLFSSL_SUCCESS); + wolfSSL_X509_STORE_CTX_free(ctx); } } @@ -446,20 +485,14 @@ 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)) + if (fs::exists(defaultDir)) { defaultDirectoriesList.push_back(defaultDir); + } } return defaultDirectoriesList; @@ -468,26 +501,27 @@ 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) +int LoadDefaultRootCertificates(WOLFSSL_CTX *ctx) { - if (!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))) + WOLFSSL_BIO *memBuf = wolfSSL_BIO_new_mem_buf(defaultRootCerts, -1); + if (memBuf) { + WOLFSSL_X509 *cert = NULL; + while ((cert = wolfSSL_PEM_read_bio_X509(memBuf, NULL, 0, NULL))) { - if (X509_STORE_add_cert(SSL_CTX_get_cert_store(ctx), cert) > 0) + if (wolfSSL_X509_STORE_add_cert(wolfSSL_CTX_get_cert_store(ctx), cert) > 0) { certsLoaded++; + } - X509_free(cert); + wolfSSL_X509_free(cert); } - BIO_free(memBuf); + wolfSSL_BIO_free(memBuf); } return certsLoaded; diff --git a/src/hush/utiltls.h b/src/hush/utiltls.h index 44581aa5a..c47308523 100644 --- a/src/hush/utiltls.h +++ b/src/hush/utiltls.h @@ -37,20 +37,20 @@ bool GenerateCredentials( // 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); +bool ValidatePeerCertificate(WOLFSSL *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); +bool ValidateCertificate(WOLFSSL_CTX *ssl_ctx); -// Creates the list of available OpenSSL default directories for trusted certificates storage +// Creates the list of available 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); +int LoadDefaultRootCertificates(WOLFSSL_CTX *ctx); } diff --git a/src/init.cpp b/src/init.cpp index 1dde8781e..415b7a680 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -77,7 +77,8 @@ #include #include #include -#include +#include +#include #include #if ENABLE_ZMQ @@ -296,6 +297,7 @@ void Shutdown() //pzcashParams = NULL; globalVerifyHandle.reset(); ECC_Stop(); + CNode::NetCleanup(); LogPrintf("%s: done\n", __func__); } @@ -431,6 +433,8 @@ std::string HelpMessage(HelpMessageMode mode) 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=