Browse Source

Merge remote-tracking branch 'mio/dev' into dev

pull/305/head
Duke Leto 4 years ago
parent
commit
e48ecee294
  1. 14
      configure.ac
  2. 8
      depends/packages/libcurl.mk
  3. 113
      depends/packages/openssl.mk
  4. 4
      depends/packages/packages.mk
  5. 47
      depends/packages/wolfssl.mk
  6. 2
      src/cc/import.cpp
  7. 560
      src/hush/tlsmanager.cpp
  8. 35
      src/hush/tlsmanager.h
  9. 490
      src/hush/utiltls.cpp
  10. 42
      src/hush/utiltls.h
  11. 11
      src/init.cpp
  12. 392
      src/net.cpp
  13. 32
      src/net.h
  14. 28
      src/support/cleanse.cpp
  15. 2
      src/support/cleanse.h
  16. 45
      src/util.cpp
  17. 4
      src/wallet/crypter.cpp

14
configure.ac

@ -664,8 +664,8 @@ if test x$use_pkgconfig = xyes; then
m4_ifdef( m4_ifdef(
[PKG_CHECK_MODULES], [PKG_CHECK_MODULES],
[ [
PKG_CHECK_MODULES([SSL], [libssl],, [AC_MSG_ERROR(openssl not found.)]) PKG_CHECK_MODULES([SSL], [wolfssl],, [AC_MSG_ERROR(WolfSSL not found.)])
PKG_CHECK_MODULES([CRYPTO], [libcrypto],,[AC_MSG_ERROR(libcrypto not found.)]) PKG_CHECK_MODULES([CRYPTO], [wolfssl],,[AC_MSG_ERROR(libcrypto not found.)])
if test x$build_bitcoin_utils$build_bitcoind$bitcoin_enable_qt$use_tests != xnononono; then 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.)]) PKG_CHECK_MODULES([EVENT], [libevent],, [AC_MSG_ERROR(libevent not found.)])
if test x$TARGET_OS != xwindows; then if test x$TARGET_OS != xwindows; then
@ -688,11 +688,11 @@ else
# BUG: Fix this: # BUG: Fix this:
echo 'BUG: configure does not yet check for the following dependencies if pkg-config is not on the system: libcrypto++, gmp' 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/wolfcrypt/sha512.h],,AC_MSG_ERROR(libwolfssl headers missing))
AC_CHECK_LIB([crypto], [main],CRYPTO_LIBS=-lcrypto, AC_MSG_ERROR(libcrypto missing)) AC_CHECK_LIB([wolfssl], [wc_InitSha512],CRYPTO_LIBS=-lwolfssl, AC_MSG_ERROR(libwolfssl missing))
AC_CHECK_HEADER([openssl/ssl.h],, AC_MSG_ERROR(libssl headers missing),) AC_CHECK_HEADER([wolfssl/ssl.h],, AC_MSG_ERROR(libssl headers missing),)
AC_CHECK_LIB([ssl], [main],SSL_LIBS=-lssl, AC_MSG_ERROR(libssl 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 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),) AC_CHECK_HEADER([event2/event.h],, AC_MSG_ERROR(libevent headers missing),)
@ -764,7 +764,7 @@ AX_CHECK_COMPILE_FLAG([-fwrapv],[CXXFLAGS="$CXXFLAGS -fwrapv"])
AX_CHECK_COMPILE_FLAG([-fno-strict-aliasing],[CXXFLAGS="$CXXFLAGS -fno-strict-aliasing"]) AX_CHECK_COMPILE_FLAG([-fno-strict-aliasing],[CXXFLAGS="$CXXFLAGS -fno-strict-aliasing"])
AX_CHECK_COMPILE_FLAG([-Wno-builtin-declaration-mismatch],[CXXFLAGS="$CXXFLAGS -Wno-builtin-declaration-mismatch"],,[[$CXXFLAG_WERROR]]) AX_CHECK_COMPILE_FLAG([-Wno-builtin-declaration-mismatch],[CXXFLAGS="$CXXFLAGS -Wno-builtin-declaration-mismatch"],,[[$CXXFLAG_WERROR]])
LIBZCASH_LIBS="-lgmp -lgmpxx $BOOST_SYSTEM_LIB -lcrypto -lsodium $RUST_LIBS" LIBZCASH_LIBS="-lgmp -lgmpxx $BOOST_SYSTEM_LIB -lwolfssl -lsodium $RUST_LIBS"
AC_MSG_CHECKING([whether to build komodod]) AC_MSG_CHECKING([whether to build komodod])
AM_CONDITIONAL([BUILD_BITCOIND], [test x$build_bitcoind = xyes]) AM_CONDITIONAL([BUILD_BITCOIND], [test x$build_bitcoind = xyes])

8
depends/packages/libcurl.mk

@ -1,12 +1,12 @@
package=libcurl package=libcurl
$(package)_version=7.67.0 $(package)_version=7.67.0
$(package)_dependencies=openssl $(package)_dependencies=wolfssl
$(package)_download_path=https://curl.haxx.se/download $(package)_download_path=https://curl.haxx.se/download
$(package)_file_name=curl-$($(package)_version).tar.gz $(package)_file_name=curl-$($(package)_version).tar.gz
$(package)_sha256_hash=52af3361cf806330b88b4fe6f483b6844209d47ae196ac46da4de59bb361ab02 $(package)_sha256_hash=52af3361cf806330b88b4fe6f483b6844209d47ae196ac46da4de59bb361ab02
$(package)_config_opts_linux=--disable-shared --enable-static --prefix=$(host_prefix) --host=$(host) $(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 --prefix=$(host_prefix) --host=x86_64-w64-mingw32 $(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 --prefix=$(host_prefix) $(package)_config_opts_darwin=--disable-shared --enable-static --with-wolfssl --without-ssl --prefix=$(host_prefix)
$(package)_cflags_darwin=-mmacosx-version-min=10.9 $(package)_cflags_darwin=-mmacosx-version-min=10.9
$(package)_conf_tool=./configure $(package)_conf_tool=./configure

113
depends/packages/openssl.mk

@ -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

4
depends/packages/packages.mk

@ -39,8 +39,8 @@ native_packages := native_ccache
wallet_packages=bdb wallet_packages=bdb
ifeq ($(host_os),linux) 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 else
packages := boost openssl libevent zeromq $(zcash_packages) libcurl googletest #googlemock packages := boost wolfssl libevent zeromq $(zcash_packages) libcurl googletest #googlemock
endif endif

47
depends/packages/wolfssl.mk

@ -0,0 +1,47 @@
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+=--host=$(host)
$(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

2
src/cc/import.cpp

@ -19,7 +19,7 @@
#include "crosschain.h" #include "crosschain.h"
#include "primitives/transaction.h" #include "primitives/transaction.h"
#include "cc/CCinclude.h" #include "cc/CCinclude.h"
#include <openssl/sha.h> #include <wolfssl/wolfcrypt/sha.h>
#include "cc/CCtokens.h" #include "cc/CCtokens.h"
#include "key_io.h" #include "key_io.h"

560
src/hush/tlsmanager.cpp

@ -1,79 +1,171 @@
// Copyright (c) 2019-2020 The Hush developers // Copyright (c) 2019-2020 The Hush developers
// Distributed under the GPLv3 software license, see the accompanying // Distributed under the GPLv3 software license, see the accompanying
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html // file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
#include <openssl/conf.h> #include <wolfssl/options.h>
#include <openssl/ssl.h> #include <wolfssl/ssl.h>
#include <openssl/err.h> #include <wolfssl/openssl/dh.h>
#include "utiltls.h" #include <wolfssl/wolfcrypt/asn.h>
#include <boost/filesystem.hpp>
#include <boost/thread.hpp>
#include "../util.h"
#include "../protocol.h"
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <boost/thread.hpp> #include <boost/thread.hpp>
#include "tlsmanager.h" #include "tlsmanager.h"
#include "utiltls.h"
using namespace std; using namespace std;
namespace hush namespace hush
{ {
/** static WOLFSSL_EVP_PKEY *mykey;
* @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. static WOLFSSL_X509 *mycert;
*
* @param preverify_ok // this is the 'dh crypto environment' to be shared between two peers and it is meant to be public, therefore
* @param chainContext // it is OK to hard code it (or as an alternative to read it from a file)
* @return int // ----
*/ // generated via: openssl dhparam -C 2048
int tlsCertVerificationCallback(int preverify_ok, X509_STORE_CTX* chainContext) 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)
{ {
return 1; 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();
} }
/**
* @brief Wait for a given SSL connection event. int TLSManager::waitFor(SSLConnectionRoutine eRoutine, SOCKET hSocket, WOLFSSL* ssl, int timeoutSec, unsigned long& err_code)
*
* @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; int retOp = 0;
ERR_clear_error(); // clear the error queue err_code = 0;
char err_buffer[1024];
while (true)
{
// clear the current thread's error queue
wolfSSL_ERR_clear_error();
while (true) {
switch (eRoutine) { switch (eRoutine) {
case SSL_CONNECT: case SSL_CONNECT:
nErr = SSL_connect(ssl); {
retOp = wolfSSL_connect(ssl);
if (retOp == 0) {
err_code = wolfSSL_ERR_get_error();
const char* error_str = wolfSSL_ERR_error_string(err_code, err_buffer);
LogPrint("tls", "TLS: WARNING: %s: %s():%d - SSL_CONNECT err: %s\n",
__FILE__, __func__, __LINE__, err_buffer);
return -1;
}
}
break; break;
case SSL_ACCEPT: case SSL_ACCEPT:
nErr = SSL_accept(ssl); {
retOp = wolfSSL_accept(ssl);
if (retOp == 0) {
err_code = wolfSSL_ERR_get_error();
const char* error_str = wolfSSL_ERR_error_string(err_code, err_buffer);
LogPrint("tls", "TLS: WARNING: %s: %s():%d - SSL_ACCEPT err: %s\n",
__FILE__, __func__, __LINE__, err_buffer);
return -1;
}
}
break; break;
case SSL_SHUTDOWN: case SSL_SHUTDOWN:
nErr = SSL_shutdown(ssl); {
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; break;
default: default:
return -1; return -1;
} }
if (eRoutine == SSL_SHUTDOWN) { 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; 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 { } 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; 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) { if (sslErr != WOLFSSL_ERROR_WANT_READ && sslErr != WOLFSSL_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)); err_code = wolfSSL_ERR_get_error();
nErr = -1; const char* error_str = wolfSSL_ERR_error_string(err_code, err_buffer);
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), err_buffer);
retOp = -1;
break; break;
} }
@ -83,66 +175,89 @@ int TLSManager::waitFor(SSLConnectionRoutine eRoutine, SOCKET hSocket, SSL* ssl,
struct timeval timeout = {timeoutSec, 0}; 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); int result = select(hSocket + 1, &socketSet, NULL, NULL, &timeout);
if (result == 0) { if (result == 0) {
LogPrint("net", "TLS: ERROR: %s: %s: WANT_READ timeout\n", __FILE__, __func__); LogPrint("tls", "TLS: ERROR: %s: %s():%d - WANT_READ timeout on %s\n", __FILE__, __func__, __LINE__,
nErr = -1; (eRoutine == SSL_CONNECT ? "SSL_CONNECT" :
(eRoutine == SSL_ACCEPT ? "SSL_ACCEPT" : "SSL_SHUTDOWN" )));
err_code = SELECT_TIMEDOUT;
retOp = -1;
break; break;
} else if (result == -1) { } 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)); LogPrint("tls", "TLS: ERROR: %s: %s: WANT_READ ssl_err_code: 0x%x; errno: %s\n",
nErr = -1; __FILE__, __func__, sslErr, strerror(errno));
retOp = -1;
break; break;
} }
} else { } else {
int result = select(hSocket + 1, NULL, &socketSet, NULL, &timeout); int result = select(hSocket + 1, NULL, &socketSet, NULL, &timeout);
if (result == 0) { if (result == 0) {
LogPrint("net", "TLS: ERROR: %s: %s: WANT_WRITE timeout\n", __FILE__, __func__); LogPrint("tls", "TLS: ERROR: %s: %s():%d - WANT_WRITE timeout on %s\n", __FILE__, __func__, __LINE__,
nErr = -1; (eRoutine == SSL_CONNECT ? "SSL_CONNECT" :
(eRoutine == SSL_ACCEPT ? "SSL_ACCEPT" : "SSL_SHUTDOWN" )));
err_code = SELECT_TIMEDOUT;
retOp = -1;
break; break;
} else if (result == -1) { } 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)); LogPrint("tls", "TLS: ERROR: %s: %s: WANT_WRITE ssl_err_code: 0x%x; errno: %s\n",
nErr = -1; __FILE__, __func__, sslErr, strerror(errno));
retOp = -1;
break; break;
} }
} }
} }
return nErr; return retOp;
} }
/** /**
* @brief establish TLS connection to an address * @brief establish TLS connection to an address
* *
* @param hSocket socket * @param hSocket socket
* @param addrConnect the outgoing address * @param addrConnect the outgoing address
* @param tls_ctx_client TLS Client context * @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;
char err_buffer[1024];
WOLFSSL* ssl = NULL;
bool bConnectedTLS = false; bool bConnectedTLS = false;
if ((ssl = SSL_new(tls_ctx_client))) { if ((ssl = wolfSSL_new(tls_ctx_client))) {
if (SSL_set_fd(ssl, hSocket)) { if (wolfSSL_set_fd(ssl, hSocket)) {
if (TLSManager::waitFor(SSL_CONNECT, hSocket, ssl, (DEFAULT_CONNECT_TIMEOUT / 1000)) == 1) int ret = TLSManager::waitFor(SSL_CONNECT, hSocket, ssl, (DEFAULT_CONNECT_TIMEOUT / 1000), err_code);
if (ret == 1)
{
bConnectedTLS = true; bConnectedTLS = true;
}
} }
} }
else
{
err_code = wolfSSL_ERR_get_error();
const char* error_str = wolfSSL_ERR_error_string(err_code, err_buffer);
LogPrint("tls", "TLS: %s: %s():%d - SSL_new failed err: %s\n",
__FILE__, __func__, __LINE__, err_buffer);
}
if (bConnectedTLS) { 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 { } 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) { if (ssl) {
SSL_free(ssl); wolfSSL_free(ssl);
ssl = NULL; ssl = NULL;
} }
} }
return ssl; return ssl;
} }
/** /**
@ -152,49 +267,84 @@ SSL* TLSManager::connect(SOCKET hSocket, const CAddress& addrConnect)
* @param privateKeyFile private key file path * @param privateKeyFile private key file path
* @param certificateFile certificate key file path * @param certificateFile certificate key file path
* @param trustedDirs trusted directories * @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)
TLSContextType ctxType,
const boost::filesystem::path& privateKeyFile,
const boost::filesystem::path& certificateFile,
const std::vector<boost::filesystem::path>& trustedDirs)
{ {
if (!boost::filesystem::exists(privateKeyFile) || LogPrintf("TLS: %s: %s():%d - Initializing %s context\n",
!boost::filesystem::exists(certificateFile)) __FILE__, __func__, __LINE__, ctxType == SERVER_CONTEXT ? "server" : "client");
return NULL;
if (!mykey || !mycert) {
return NULL;
}
bool bInitialized = false; 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()))) { byte *pem;
SSL_CTX_set_mode(tlsCtx, SSL_MODE_AUTO_RETRY); int plen = 0;
int rootCertsNum = LoadDefaultRootCertificates(tlsCtx); if ((tlsCtx = wolfSSL_CTX_new(ctxType == SERVER_CONTEXT ? wolfTLSv1_3_server_method() : wolfTLSv1_3_client_method()))) {
int trustedPathsNum = 0; wolfSSL_CTX_set_mode(tlsCtx, SSL_MODE_AUTO_RETRY);
for (boost::filesystem::path trustedDir : trustedDirs) { // Disable TLS < 1.3 ... imho redundant, because v1.3 is required via method
if (SSL_CTX_load_verify_locations(tlsCtx, NULL, trustedDir.string().c_str()) == 1) int ret = wolfSSL_CTX_set_min_proto_version(tlsCtx, TLS1_3_VERSION);
trustedPathsNum++; if (ret == 0) {
LogPrintf("TLS: WARNING: %s: %s():%d - failed to set min TLS version\n", __FILE__, __func__, __LINE__);
} }
if (rootCertsNum == 0 && trustedPathsNum == 0) LogPrintf("TLS: %s: %s():%d - setting cipher list\n", __FILE__, __func__, __LINE__);
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); // 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__);
}
}
if (ctxType == SERVER_CONTEXT) {
// In case server and client prefered ciphers are different, server preference has priority
wolfSSL_CTX_set_options(tlsCtx, SSL_OP_CIPHER_SERVER_PREFERENCE);
if (SSL_CTX_use_certificate_file(tlsCtx, certificateFile.string().c_str(), SSL_FILETYPE_PEM) > 0) { LogPrintf("TLS: %s: %s():%d - setting dh callback\n", __FILE__, __func__, __LINE__);
if (SSL_CTX_use_PrivateKey_file(tlsCtx, privateKeyFile.string().c_str(), SSL_FILETYPE_PEM) > 0) { SSL_CTX_set_tmp_dh_callback(tlsCtx, tmp_dh_callback);
if (SSL_CTX_check_private_key(tlsCtx)) { }
bInitialized = true;
// No certificate verification, all should be self-signed
wolfSSL_CTX_set_verify(tlsCtx, WOLFSSL_VERIFY_NONE, NULL);
WOLFSSL_EC_KEY *ec_key = NULL;
ec_key = wolfSSL_EVP_PKEY_get0_EC_KEY(mykey);
if (ec_key != NULL && wolfSSL_PEM_write_mem_ECPrivateKey(ec_key, NULL, NULL, 0, &pem, &plen)) {
if (wolfSSL_CTX_use_certificate(tlsCtx, mycert) > 0) {
if (wolfSSL_CTX_use_PrivateKey_buffer(tlsCtx, pem, plen, SSL_FILETYPE_PEM) > 0) {
free(pem);
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 { } else {
LogPrintf("TLS: ERROR: %s: %s: private key does not match the certificate public key\n", __FILE__, __func__); LogPrintf("TLS: ERROR: %s: %s: failed to use private key file\n", __FILE__, __func__);
} }
} else } else {
LogPrintf("TLS: ERROR: %s: %s: failed to use privateKey file\n", __FILE__, __func__); LogPrintf("TLS: ERROR: %s: %s: failed to use certificate file\n", __FILE__, __func__);
} else { wolfSSL_ERR_dump_errors_fp(stderr);
LogPrintf("TLS: ERROR: %s: %s: failed to use certificate file\n", __FILE__, __func__); }
ERR_print_errors_fp(stderr);
} }
} else { } else {
LogPrintf("TLS: ERROR: %s: %s: failed to create TLS context\n", __FILE__, __func__); LogPrintf("TLS: ERROR: %s: %s: failed to create TLS context\n", __FILE__, __func__);
@ -202,97 +352,119 @@ SSL_CTX* TLSManager::initCtx(
if (!bInitialized) { if (!bInitialized) {
if (tlsCtx) { if (tlsCtx) {
SSL_CTX_free(tlsCtx); wolfSSL_CTX_free(tlsCtx);
tlsCtx = NULL; 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; return tlsCtx;
} }
/** /**
* @brief load the certificate credentials from file. * @brief generates certificate credentials.
* *
* @return true returns true is successful. * @return true returns true is successful.
* @return false returns false if an error has occured. * @return false returns false if an error has occured.
*/ */
bool TLSManager::prepareCredentials() bool TLSManager::prepareCredentials()
{ {
boost::filesystem::path mykey = NULL;
defaultKeyPath(GetDataDir() / TLS_KEY_FILE_NAME), mycert = NULL;
defaultCertPath(GetDataDir() / TLS_CERT_FILE_NAME);
// Generating key and the self-signed certificate for it
CredentialsStatus credStatus = //
VerifyCredentials( mykey = GenerateEcKey();
boost::filesystem::path(GetArg("-tlskeypath", defaultKeyPath.string())), if (mykey) {
boost::filesystem::path(GetArg("-tlscertpath", defaultCertPath.string())), mycert = GenerateCertificate(mykey);
GetArg("-tlskeypwd", "")); if (mycert) {
if (CheckKeyCert()) {
bool bPrepared = (credStatus == credOk); LogPrintStr("TLS: New private key and self-signed certificate were generated successfully\n");
if (!bPrepared) { return true;
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", ""));
} }
//wolfSSL_X509_free(mycert);
} }
//wolfSSL_EVP_PKEY_free(mykey);
}
return false;
}
bool TLSManager::CheckKeyCert()
{
if (!mykey) {
LogPrintf("Key is not generated!!!\n");
return false;
}
if (!mycert) {
LogPrintf("Certificate is not generated!!!\n");
return false;
}
WOLFSSL_EC_KEY *eccKey = wolfSSL_EVP_PKEY_get1_EC_KEY(mykey);
if (eccKey && wc_ecc_check_key((ecc_key*)eccKey->internal) == 0) {
wolfSSL_EC_KEY_free(eccKey);
} else {
LogPrintf("Generated ECC key check failed!!!\n");
return false;
} }
return bPrepared; if (wolfSSL_X509_verify(mycert, mykey) == WOLFSSL_SUCCESS) {
return true;
}
LogPrintf("Generated key and certificate do not match!!!\n");
return false;
} }
/** /**
* @brief accept a TLS connection * @brief accept a TLS connection
* *
* @param hSocket the TLS socket. * @param hSocket the TLS socket.
* @param addr incoming address. * @param addr incoming address.
* @param tls_ctx_server TLS server context. * @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;
char err_buffer[1024];
WOLFSSL* ssl = NULL;
bool bAcceptedTLS = false; bool bAcceptedTLS = false;
if ((ssl = SSL_new(tls_ctx_server))) { if ((ssl = wolfSSL_new(tls_ctx_server))) {
if (SSL_set_fd(ssl, hSocket)) { if (wolfSSL_set_fd(ssl, hSocket)) {
if (TLSManager::waitFor(SSL_ACCEPT, hSocket, ssl, (DEFAULT_CONNECT_TIMEOUT / 1000)) == 1) int ret = TLSManager::waitFor(SSL_ACCEPT, hSocket, ssl, (DEFAULT_CONNECT_TIMEOUT / 1000), err_code);
if (ret == 1)
{
bAcceptedTLS = true; bAcceptedTLS = true;
}
} }
} }
else
{
err_code = wolfSSL_ERR_get_error();
const char* error_str = wolfSSL_ERR_error_string(err_code, err_buffer);
LogPrint("tls", "TLS: %s: %s():%d - SSL_new failed err: %s\n",
__FILE__, __func__, __LINE__, err_buffer);
}
if (bAcceptedTLS) { 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 { } 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) { if (ssl) {
SSL_free(ssl); SSL_free(ssl);
@ -302,6 +474,7 @@ SSL* TLSManager::accept(SOCKET hSocket, const CAddress& addr)
return ssl; return ssl;
} }
/** /**
* @brief Determines whether a string exists in the non-TLS address pool. * @brief Determines whether a string exists in the non-TLS address pool.
* *
@ -316,6 +489,7 @@ bool TLSManager::isNonTLSAddr(const string& strAddr, const vector<NODE_ADDR>& vP
LOCK(cs); LOCK(cs);
return (find(vPool.begin(), vPool.end(), NODE_ADDR(strAddr)) != vPool.end()); return (find(vPool.begin(), vPool.end(), NODE_ADDR(strAddr)) != vPool.end());
} }
/** /**
* @brief Removes non-TLS node addresses based on timeout. * @brief Removes non-TLS node addresses based on timeout.
* *
@ -331,7 +505,7 @@ void TLSManager::cleanNonTLSPool(std::vector<NODE_ADDR>& vPool, CCriticalSection
BOOST_FOREACH (NODE_ADDR nodeAddr, vPool) { BOOST_FOREACH (NODE_ADDR nodeAddr, vPool) {
if ((GetTimeMillis() - nodeAddr.time) >= 900000) { if ((GetTimeMillis() - nodeAddr.time) >= 900000) {
vDeleted.push_back(nodeAddr); 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 +560,16 @@ int TLSManager::threadSocketHandler(CNode* pnode, fd_set& fdsetRecv, fd_set& fds
LOCK(pnode->cs_hSocket); LOCK(pnode->cs_hSocket);
if (pnode->hSocket == INVALID_SOCKET) { 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; return -1;
} }
bIsSSL = (pnode->ssl != NULL); bIsSSL = (pnode->ssl != NULL);
if (bIsSSL) { if (bIsSSL) {
ERR_clear_error(); // clear the error queue, otherwise we may be reading an old error that occurred previously in the current thread wolfSSL_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)); nBytes = wolfSSL_read(pnode->ssl, pchBuf, sizeof(pchBuf));
nRet = SSL_get_error(pnode->ssl, nBytes); nRet = wolfSSL_get_error(pnode->ssl, nBytes);
} else { } else {
nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT); nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT);
nRet = WSAGetLastError(); nRet = WSAGetLastError();
@ -409,20 +583,35 @@ int TLSManager::threadSocketHandler(CNode* pnode, fd_set& fdsetRecv, fd_set& fds
pnode->nRecvBytes += nBytes; pnode->nRecvBytes += nBytes;
pnode->RecordBytesRecv(nBytes); pnode->RecordBytesRecv(nBytes);
} else if (nBytes == 0) { } 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) // socket closed gracefully (peer disconnected)
// //
if (!pnode->fDisconnect) if (!pnode->fDisconnect)
LogPrint("net", "socket closed (%s)\n", pnode->addr.ToString()); LogPrint("tls", "socket closed (%s)\n", pnode->addr.ToString());
pnode->CloseSocketDisconnect(); pnode->CloseSocketDisconnect();
} else if (nBytes < 0) { } else if (nBytes < 0) {
// error // error
// //
if (bIsSSL) { 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) 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(); 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 { } else {
// preventive measure from exhausting CPU usage // preventive measure from exhausting CPU usage
// //
@ -431,7 +620,7 @@ int TLSManager::threadSocketHandler(CNode* pnode, fd_set& fdsetRecv, fd_set& fds
} else { } else {
if (nRet != WSAEWOULDBLOCK && nRet != WSAEMSGSIZE && nRet != WSAEINTR && nRet != WSAEINPROGRESS) { if (nRet != WSAEWOULDBLOCK && nRet != WSAEMSGSIZE && nRet != WSAEINTR && nRet != WSAEINPROGRESS) {
if (!pnode->fDisconnect) if (!pnode->fDisconnect)
LogPrintf("ERROR: socket recv %s\n", NetworkErrorString(nRet)); LogPrintf("TSL: ERROR: socket recv %s\n", NetworkErrorString(nRet));
pnode->CloseSocketDisconnect(); pnode->CloseSocketDisconnect();
} }
} }
@ -448,8 +637,10 @@ int TLSManager::threadSocketHandler(CNode* pnode, fd_set& fdsetRecv, fd_set& fds
if (lockSend) if (lockSend)
SocketSendData(pnode); SocketSendData(pnode);
} }
return 0; return 0;
} }
/** /**
* @brief Initialization of the server and client contexts * @brief Initialization of the server and client contexts
* *
@ -460,44 +651,23 @@ bool TLSManager::initialize()
{ {
bool bInitializationStatus = false; bool bInitializationStatus = false;
// Initialization routines for the OpenSSL library // Initialization routines for the WolfSSL library
SSL_load_error_strings(); //
ERR_load_crypto_strings(); wolfSSL_load_error_strings();
OpenSSL_add_ssl_algorithms(); // OpenSSL_add_ssl_algorithms() always returns "1", so it is safe to discard the return value. 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);
fs::path privKeyFile = GetArg("-tlskeypath", "");
if (!fs::exists(privKeyFile)) {
privKeyFile = (GetDataDir() / TLS_KEY_FILE_NAME);
}
std::vector<fs::path> 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 // Initialization of the server and client contexts
if ((tls_ctx_server = TLSManager::initCtx(SERVER_CONTEXT, privKeyFile, certFile, trustedDirs))) //
if ((tls_ctx_server = TLSManager::initCtx(SERVER_CONTEXT)))
{ {
if ((tls_ctx_client = TLSManager::initCtx(CLIENT_CONTEXT, privKeyFile, certFile, trustedDirs))) if ((tls_ctx_client = TLSManager::initCtx(CLIENT_CONTEXT)))
{ {
LogPrint("net", "TLS: contexts are initialized\n"); LogPrint("tls", "TLS: contexts are initialized\n");
bInitializationStatus = true; bInitializationStatus = true;
} else { } else {
LogPrintf("TLS: ERROR: %s: %s: failed to initialize TLS client context\n", __FILE__, __func__); 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 { } else {
LogPrintf("TLS: ERROR: %s: %s: failed to initialize TLS server context\n", __FILE__, __func__); LogPrintf("TLS: ERROR: %s: %s: failed to initialize TLS server context\n", __FILE__, __func__);

35
src/hush/tlsmanager.h

@ -1,15 +1,12 @@
// Copyright (c) 2019-2020 The Hush developers // Copyright (c) 2019-2020 The Hush developers
// Distributed under the GPLv3 software license, see the accompanying // Distributed under the GPLv3 software license, see the accompanying
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html // file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
#include <openssl/conf.h> #include <wolfssl/options.h>
#include <openssl/ssl.h> #include <wolfssl/ssl.h>
#include <openssl/err.h>
#include "utiltls.h"
#include "tlsenums.h" #include "tlsenums.h"
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <boost/thread.hpp> #include <boost/thread.hpp>
#include "../util.h" #include "../util.h"
#include "../protocol.h"
#include "../net.h" #include "../net.h"
#include "sync.h" #include "sync.h"
#include <boost/filesystem/path.hpp> #include <boost/filesystem/path.hpp>
@ -43,19 +40,21 @@ bool operator==(const _NODE_ADDR b) const
class TLSManager class TLSManager
{ {
public: public:
int waitFor(SSLConnectionRoutine eRoutine, SOCKET hSocket, SSL* ssl, int timeoutSec); /* This is set as a custom error number which is not an error in SSL protocol.
SSL* connect(SOCKET hSocket, const CAddress& addrConnect); A true (not null) SSL error returned by ERR_get_error() consists of a library number,
SSL_CTX* initCtx( function code and reason code. */
TLSContextType ctxType, static const long SELECT_TIMEDOUT = 0xFFFFFFFF;
const boost::filesystem::path& privateKeyFile,
const boost::filesystem::path& certificateFile,
const std::vector<boost::filesystem::path>& trustedDirs);
bool prepareCredentials(); int waitFor(SSLConnectionRoutine eRoutine, SOCKET hSocket, WOLFSSL* ssl, int timeoutSec, unsigned long& err_code);
SSL* accept(SOCKET hSocket, const CAddress& addr);
bool isNonTLSAddr(const string& strAddr, const vector<NODE_ADDR>& vPool, CCriticalSection& cs); WOLFSSL* connect(SOCKET hSocket, const CAddress& addrConnect, unsigned long& err_code);
void cleanNonTLSPool(std::vector<NODE_ADDR>& vPool, CCriticalSection& cs); WOLFSSL_CTX* initCtx(TLSContextType ctxType);
int threadSocketHandler(CNode* pnode, fd_set& fdsetRecv, fd_set& fdsetSend, fd_set& fdsetError); bool prepareCredentials();
bool initialize(); WOLFSSL* accept(SOCKET hSocket, const CAddress& addr, unsigned long& err_code);
bool isNonTLSAddr(const string& strAddr, const vector<NODE_ADDR>& vPool, CCriticalSection& cs);
void cleanNonTLSPool(std::vector<NODE_ADDR>& vPool, CCriticalSection& cs);
int threadSocketHandler(CNode* pnode, fd_set& fdsetRecv, fd_set& fdsetSend, fd_set& fdsetError);
bool initialize();
bool CheckKeyCert();
}; };
} }

490
src/hush/utiltls.cpp

@ -6,98 +6,34 @@
#include <stdio.h> #include <stdio.h>
#include <vector> #include <vector>
#include <openssl/rsa.h> #include <wolfssl/options.h>
#include <openssl/x509.h> #include <wolfssl/ssl.h>
#include <openssl/pem.h> #include "../util.h"
#include <openssl/rand.h>
#include <openssl/ssl.h>
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/operations.hpp>
#include "util.h"
#include "utiltls.h" #include "utiltls.h"
namespace hush { namespace hush {
// Set of most common default trusted certificates directories used by OpenSSL // Generates EC keypair
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) WOLFSSL_EVP_PKEY* GenerateEcKey(int nid)
{ {
EVP_PKEY *evpPrivKey = NULL; WOLFSSL_EVP_PKEY *evpPrivKey = NULL;
WOLFSSL_EC_KEY *privKey = wolfSSL_EC_KEY_new_by_curve_name(nid);
BIGNUM *pubKey = BN_new(); if (privKey) {
if (pubKey) wolfSSL_EC_KEY_set_asn1_flag(privKey, OPENSSL_EC_NAMED_CURVE);
{ if (wolfSSL_EC_KEY_generate_key(privKey)) {
if (BN_set_word(pubKey, uPublicKey)) if ((evpPrivKey = wolfSSL_EVP_PKEY_new())) {
{ if (!wolfSSL_EVP_PKEY_assign_EC_KEY(evpPrivKey, privKey)) {
RSA *privKey = RSA_new(); wolfSSL_EVP_PKEY_free(evpPrivKey);
if (privKey) evpPrivKey = NULL;
{
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);
if(!evpPrivKey) {
wolfSSL_EC_KEY_free(privKey);
evpPrivKey = NULL;
}
} }
return evpPrivKey; return evpPrivKey;
@ -105,391 +41,35 @@ 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'). // 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) WOLFSSL_X509* GenerateCertificate(WOLFSSL_EVP_PKEY *keypair)
{ {
if (!keypair) if (!keypair) {
return NULL; return NULL;
}
X509 *cert = X509_new(); WOLFSSL_X509 *cert = wolfSSL_X509_new();
if (cert) if (cert) {
{
bool bCertSigned = false; bool bCertSigned = false;
long sn = 0; long sn = 0;
if (RAND_bytes((unsigned char*)&sn, sizeof sn) && if (wolfSSL_RAND_bytes((unsigned char*)&sn, sizeof(sn)) &&wolfSSL_ASN1_INTEGER_set(wolfSSL_X509_get_serialNumber(cert), sn)) {
ASN1_INTEGER_set(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));
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 // setting a public key from the keypair
if (X509_set_pubkey(cert, keypair)) if (wolfSSL_X509_set_pubkey(cert, keypair)) {
{ // private key from keypair is used; signature will be set inside of the cert
X509_NAME *subjectName = X509_get_subject_name(cert); bCertSigned = wolfSSL_X509_sign(cert, keypair, wolfSSL_EVP_sha512());
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) if (!bCertSigned) {
{ wolfSSL_X509_free(cert);
X509_free(cert);
cert = NULL; 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) return 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<boost::filesystem::path> GetDefaultTrustedDirectories()
{
namespace fs = boost::filesystem;
std::vector<fs::path> 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;
}
} }

42
src/hush/utiltls.h

@ -6,51 +6,13 @@
#ifndef UTILTLS_H #ifndef UTILTLS_H
#define UTILTLS_H #define UTILTLS_H
#include <boost/filesystem/path.hpp>
namespace hush { 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 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 WOLFSSL_EVP_PKEY* GenerateEcKey(int nid = NID_X9_62_prime256v1);
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<boost::filesystem::path> GetDefaultTrustedDirectories();
// Loads default root certificates (placed in the 'defaultRootCerts') into the specified context. WOLFSSL_X509* GenerateCertificate(WOLFSSL_EVP_PKEY *keypair);
// Returns the number of loaded certificates.
//
int LoadDefaultRootCertificates(SSL_CTX *ctx);
} }

11
src/init.cpp

@ -77,7 +77,8 @@
#include <boost/interprocess/sync/file_lock.hpp> #include <boost/interprocess/sync/file_lock.hpp>
#include <boost/thread.hpp> #include <boost/thread.hpp>
#include <chrono> #include <chrono>
#include <openssl/crypto.h> #include <wolfssl/options.h>
#include <wolfssl/ssl.h>
#include <thread> #include <thread>
#if ENABLE_ZMQ #if ENABLE_ZMQ
@ -296,6 +297,7 @@ void Shutdown()
//pzcashParams = NULL; //pzcashParams = NULL;
globalVerifyHandle.reset(); globalVerifyHandle.reset();
ECC_Stop(); ECC_Stop();
CNode::NetCleanup();
LogPrintf("%s: done\n", __func__); LogPrintf("%s: done\n", __func__);
} }
@ -431,6 +433,8 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-torcontrol=<ip>:<port>", strprintf(_("Tor control port to use if onion listening enabled (default: %s)"), DEFAULT_TOR_CONTROL)); strUsage += HelpMessageOpt("-torcontrol=<ip>:<port>", strprintf(_("Tor control port to use if onion listening enabled (default: %s)"), DEFAULT_TOR_CONTROL));
strUsage += HelpMessageOpt("-torpassword=<pass>", _("Tor control port password (default: empty)")); strUsage += HelpMessageOpt("-torpassword=<pass>", _("Tor control port password (default: empty)"));
strUsage += HelpMessageOpt("-tls=<option>", _("Specify TLS usage (default: 1 => enabled and preferred, yet compatible); other options are -tls=0 to disable TLS and -tls=only to enforce it")); strUsage += HelpMessageOpt("-tls=<option>", _("Specify TLS usage (default: 1 => enabled and preferred, yet compatible); other options are -tls=0 to disable TLS and -tls=only to enforce it"));
strUsage += HelpMessageOpt("-tlsfallbacknontls=<0 or 1>", _("If a TLS connection fails, the next connection attempt of the same peer (based on IP address) takes place without TLS (default: 1)"));
strUsage += HelpMessageOpt("-tlsvalidate=<0 or 1>", _("Connect to peers only with valid certificates (default: 0)"));
strUsage += HelpMessageOpt("-tlskeypath=<path>", _("Full path to a private key")); strUsage += HelpMessageOpt("-tlskeypath=<path>", _("Full path to a private key"));
strUsage += HelpMessageOpt("-tlskeypwd=<password>", _("Password for a private key encryption (default: not set, i.e. private key will be stored unencrypted)")); strUsage += HelpMessageOpt("-tlskeypwd=<password>", _("Password for a private key encryption (default: not set, i.e. private key will be stored unencrypted)"));
strUsage += HelpMessageOpt("-tlscertpath=<path>", _("Full path to a certificate")); strUsage += HelpMessageOpt("-tlscertpath=<path>", _("Full path to a certificate"));
@ -493,8 +497,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-stopafterblockimport", strprintf("Stop running after importing blocks from disk (default: %u)", 0)); strUsage += HelpMessageOpt("-stopafterblockimport", strprintf("Stop running after importing blocks from disk (default: %u)", 0));
strUsage += HelpMessageOpt("-nuparams=hexBranchId:activationHeight", "Use given activation height for specified network upgrade (regtest-only)"); strUsage += HelpMessageOpt("-nuparams=hexBranchId:activationHeight", "Use given activation height for specified network upgrade (regtest-only)");
} }
string debugCategories = "addrman, alert, bench, coindb, db, deletetx, estimatefee, http, libevent, lock, mempool, net, partitioncheck, pow, proxy, prune, " string debugCategories = "addrman, alert, bench, coindb, db, deletetx, estimatefee, http, libevent, lock, mempool, net, tls, partitioncheck, pow, proxy, prune, rand, reindex, rpc, selectcoins, tor, zmq, zrpc, zrpcunsafe (implies zrpc)"; // Don't translate these
"rand, reindex, rpc, selectcoins, tor, zmq, zrpc, zrpcunsafe (implies zrpc)"; // Don't translate these
strUsage += HelpMessageOpt("-debug=<category>", strprintf(_("Output debugging information (default: %u, supplying <category> is optional)"), 0) + ". " + strUsage += HelpMessageOpt("-debug=<category>", strprintf(_("Output debugging information (default: %u, supplying <category> is optional)"), 0) + ". " +
_("If <category> is not supplied or if <category> = 1, output all debugging information.") + " " + _("<category> can be:") + " " + debugCategories + "."); _("If <category> is not supplied or if <category> = 1, output all debugging information.") + " " + _("<category> can be:") + " " + debugCategories + ".");
strUsage += HelpMessageOpt("-experimentalfeatures", _("Enable use of experimental features")); strUsage += HelpMessageOpt("-experimentalfeatures", _("Enable use of experimental features"));
@ -1433,7 +1436,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
if (fPrintToDebugLog) if (fPrintToDebugLog)
OpenDebugLog(); OpenDebugLog();
LogPrintf("Using OpenSSL version %s\n", SSLeay_version(SSLEAY_VERSION)); LogPrintf("Using WolfSSL version %s\n", wolfSSL_lib_version());
#ifdef ENABLE_WALLET #ifdef ENABLE_WALLET
LogPrintf("Using BerkeleyDB version %s\n", DbEnv::version(0, 0, 0)); LogPrintf("Using BerkeleyDB version %s\n", DbEnv::version(0, 0, 0));
#endif #endif

392
src/net.cpp

@ -44,9 +44,8 @@
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <boost/thread.hpp> #include <boost/thread.hpp>
#include <openssl/conf.h> #include <wolfssl/options.h>
#include <openssl/ssl.h> #include <wolfssl/ssl.h>
#include <openssl/err.h>
#include <hush/tlsmanager.cpp> #include <hush/tlsmanager.cpp>
using namespace hush; using namespace hush;
@ -69,7 +68,13 @@ using namespace hush;
#endif #endif
#define USE_TLS #define USE_TLS
#define COMPAT_NON_TLS // enables compatibility with nodes, that still doesn't support TLS connections
#if defined(USE_TLS) && !defined(TLS1_3_VERSION)
// minimum secure protocol is 1.3
// TLS1_3_VERSION is defined in openssl/tls1.h
#error "ERROR: Your WolfSSL version does not support TLS v1.3"
#endif
using namespace std; using namespace std;
@ -133,8 +138,8 @@ static boost::condition_variable messageHandlerCondition;
static CNodeSignals g_signals; static CNodeSignals g_signals;
CNodeSignals& GetNodeSignals() { return g_signals; } CNodeSignals& GetNodeSignals() { return g_signals; }
// OpenSSL server and client contexts // WolfSSL server and client contexts
SSL_CTX *tls_ctx_server, *tls_ctx_client; WOLFSSL_CTX *tls_ctx_server, *tls_ctx_client;
static bool operator==(_NODE_ADDR a, _NODE_ADDR b) static bool operator==(_NODE_ADDR a, _NODE_ADDR b)
{ {
@ -359,6 +364,9 @@ void AddressCurrentlyConnected(const CService& addr)
} }
CNode::eTlsOption CNode::tlsFallbackNonTls = CNode::eTlsOption::FALLBACK_UNSET;
CNode::eTlsOption CNode::tlsValidate = CNode::eTlsOption::FALLBACK_UNSET;
uint64_t CNode::nTotalBytesRecv = 0; uint64_t CNode::nTotalBytesRecv = 0;
uint64_t CNode::nTotalBytesSent = 0; uint64_t CNode::nTotalBytesSent = 0;
CCriticalSection CNode::cs_totalBytesRecv; CCriticalSection CNode::cs_totalBytesRecv;
@ -434,55 +442,73 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest)
addrman.Attempt(addrConnect); addrman.Attempt(addrConnect);
SSL *ssl = NULL; WOLFSSL *ssl = NULL;
#ifdef USE_TLS #ifdef USE_TLS
/* TCP connection is ready. Do client side SSL. */ /* TCP connection is ready. Do client side SSL. */
#ifdef COMPAT_NON_TLS if (CNode::GetTlsFallbackNonTls())
{ {
LOCK(cs_vNonTLSNodesOutbound);
NODE_ADDR nodeAddr(addrConnect.ToStringIP());
bool bUseTLS = ((GetBoolArg("-tls", true) || GetArg("-tls", "") == "only") && find(vNonTLSNodesOutbound.begin(),
vNonTLSNodesOutbound.end(),
nodeAddr) == vNonTLSNodesOutbound.end());
if (bUseTLS)
{ {
ssl = tlsmanager.connect(hSocket, addrConnect); LOCK(cs_vNonTLSNodesOutbound);
if (!ssl)
LogPrint("tls", "%s():%d - handling connection to %s\n", __func__, __LINE__, addrConnect.ToString());
NODE_ADDR nodeAddr(addrConnect.ToStringIP());
bool bUseTLS = ((GetBoolArg("-tls", true) || GetArg("-tls", "") == "only")
&& find(vNonTLSNodesOutbound.begin(),
vNonTLSNodesOutbound.end(),
nodeAddr) == vNonTLSNodesOutbound.end());
unsigned long err_code = 0;
if (bUseTLS)
{ {
if (GetArg("-tls", "") != "only") ssl = tlsmanager.connect(hSocket, addrConnect, err_code);
if (!ssl)
{ {
// Further reconnection will be made in non-TLS (unencrypted) mode if mandatory tls is not set if (err_code == TLSManager::SELECT_TIMEDOUT)
vNonTLSNodesOutbound.push_back(NODE_ADDR(addrConnect.ToStringIP(), GetTimeMillis())); {
// can fail for timeout in select on fd, that is not a ssl error and we should not
// consider this node as non TLS
LogPrint("tls", "%s():%d - Connection to %s timedout\n",
__func__, __LINE__, addrConnect.ToStringIP());
}
else
{
// Further reconnection will be made in non-TLS (unencrypted) mode
vNonTLSNodesOutbound.push_back(NODE_ADDR(addrConnect.ToStringIP(), GetTimeMillis()));
LogPrint("tls", "%s():%d - err_code %x, adding connection to %s vNonTLSNodesOutbound list (sz=%d)\n",
__func__, __LINE__, err_code, addrConnect.ToStringIP(), vNonTLSNodesOutbound.size());
}
CloseSocket(hSocket);
return NULL;
} }
CloseSocket(hSocket);
return NULL;
} }
} else
else {
{ LogPrintf ("Connection to %s will be unencrypted\n", addrConnect.ToString());
LogPrintf ("Connection to %s will be unencrypted\n", addrConnect.ToString());
vNonTLSNodesOutbound.erase(
vNonTLSNodesOutbound.erase( remove(
remove( vNonTLSNodesOutbound.begin(),
vNonTLSNodesOutbound.begin(), vNonTLSNodesOutbound.end(),
vNonTLSNodesOutbound.end(), nodeAddr),
nodeAddr), vNonTLSNodesOutbound.end());
vNonTLSNodesOutbound.end()); }
} }
} }
#else else
ssl = TLSManager::connect(hSocket, addrConnect);
if(!ssl)
{ {
CloseSocket(hSocket); unsigned long err_code = 0;
return NULL; ssl = tlsmanager.connect(hSocket, addrConnect, err_code);
if(!ssl)
{
LogPrint("tls", "%s():%d - err_code %x, connection to %s failed)\n",
__func__, __LINE__, err_code, addrConnect.ToStringIP());
CloseSocket(hSocket);
return NULL;
}
} }
#endif // COMPAT_NON_TLS
#endif // USE_TLS #endif // USE_TLS
// Add node // Add node
@ -509,15 +535,15 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest)
void CNode::CloseSocketDisconnect() void CNode::CloseSocketDisconnect()
{ {
fDisconnect = true; fDisconnect = true;
{ {
LOCK(cs_hSocket); LOCK(cs_hSocket);
if (hSocket != INVALID_SOCKET) if (hSocket != INVALID_SOCKET)
{ {
try try
{ {
LogPrint("net", "disconnecting peer=%d\n", id); LogPrint("net", "disconnecting peer=%d\n", id);
} }
catch(std::bad_alloc&) catch(std::bad_alloc&)
{ {
@ -528,13 +554,13 @@ void CNode::CloseSocketDisconnect()
if (ssl) if (ssl)
{ {
unsigned long err_code = 0;
tlsmanager.waitFor(SSL_SHUTDOWN, hSocket, ssl, (DEFAULT_CONNECT_TIMEOUT / 1000)); tlsmanager.waitFor(SSL_SHUTDOWN, hSocket, ssl, (DEFAULT_CONNECT_TIMEOUT / 1000), err_code);
SSL_free(ssl); wolfSSL_free(ssl);
ssl = NULL; ssl = NULL;
} }
CloseSocket(hSocket); CloseSocket(hSocket);
} }
} }
// in case this fails, we'll empty the recv buffer when the CNode is deleted // in case this fails, we'll empty the recv buffer when the CNode is deleted
@ -707,7 +733,7 @@ void CNode::copyStats(CNodeStats &stats, const std::vector<bool> &m_asmap)
// If ssl != NULL it means TLS connection was established successfully // If ssl != NULL it means TLS connection was established successfully
{ {
LOCK(cs_hSocket); LOCK(cs_hSocket);
stats.fTLSEstablished = (ssl != NULL) && (SSL_get_state(ssl) == TLS_ST_OK); stats.fTLSEstablished = (ssl != NULL) && (wolfSSL_is_init_finished(ssl) == 1);
} }
} }
@ -812,20 +838,20 @@ void SocketSendData(CNode *pnode)
int nBytes = 0, nRet = 0; int nBytes = 0, nRet = 0;
{ {
LOCK(pnode->cs_hSocket); LOCK(pnode->cs_hSocket);
if (pnode->hSocket == INVALID_SOCKET) if (pnode->hSocket == INVALID_SOCKET)
{ {
LogPrint("net", "Send: connection with %s is already closed\n", pnode->addr.ToString()); LogPrint("net", "Send: connection with %s is already closed\n", pnode->addr.ToString());
break; break;
} }
bIsSSL = (pnode->ssl != NULL); bIsSSL = (pnode->ssl != NULL);
if (bIsSSL) if (bIsSSL)
{ {
ERR_clear_error(); // clear the error queue, otherwise we may be reading an old error that occurred previously in the current thread wolfSSL_ERR_clear_error(); // clear the error queue, otherwise we may be reading an old error that occurred previously in the current thread
nBytes = SSL_write(pnode->ssl, &data[pnode->nSendOffset], data.size() - pnode->nSendOffset); nBytes = wolfSSL_write(pnode->ssl, &data[pnode->nSendOffset], data.size() - pnode->nSendOffset);
nRet = SSL_get_error(pnode->ssl, nBytes); nRet = wolfSSL_get_error(pnode->ssl, nBytes);
} }
else else
{ {
@ -853,18 +879,18 @@ void SocketSendData(CNode *pnode)
// //
if (bIsSSL) if (bIsSSL)
{ {
if (nRet != SSL_ERROR_WANT_READ && nRet != SSL_ERROR_WANT_WRITE) if (nRet != WOLFSSL_ERROR_WANT_READ && nRet != WOLFSSL_ERROR_WANT_WRITE)
{ {
LogPrintf("ERROR: SSL_write %s; closing connection\n", ERR_error_string(nRet, NULL)); LogPrintf("ERROR: SSL_write %s; closing connection\n", wolfSSL_ERR_error_string(nRet, NULL));
pnode->CloseSocketDisconnect(); pnode->CloseSocketDisconnect();
} }
else else
{ {
// preventive measure from exhausting CPU usage // preventive measure from exhausting CPU usage
// //
MilliSleep(1); // 1 msec MilliSleep(1); // 1 msec
} }
} }
else else
{ {
if (nRet != WSAEWOULDBLOCK && nRet != WSAEMSGSIZE && nRet != WSAEINTR && nRet != WSAEINPROGRESS) if (nRet != WSAEWOULDBLOCK && nRet != WSAEMSGSIZE && nRet != WSAEINTR && nRet != WSAEINPROGRESS)
@ -1154,30 +1180,42 @@ static void AcceptConnection(const ListenSocket& hListenSocket) {
setsockopt(hSocket, IPPROTO_TCP, TCP_NODELAY, (void*)&set, sizeof(int)); setsockopt(hSocket, IPPROTO_TCP, TCP_NODELAY, (void*)&set, sizeof(int));
#endif #endif
SSL *ssl = NULL; WOLFSSL *ssl = NULL;
SetSocketNonBlocking(hSocket, true); SetSocketNonBlocking(hSocket, true);
#ifdef USE_TLS #ifdef USE_TLS
/* TCP connection is ready. Do server side SSL. */ /* TCP connection is ready. Do server side SSL. */
#ifdef COMPAT_NON_TLS if (CNode::GetTlsFallbackNonTls())
{ {
LOCK(cs_vNonTLSNodesInbound); LOCK(cs_vNonTLSNodesInbound);
LogPrint("tls", "%s():%d - handling connection from %s\n", __func__, __LINE__, addr.ToString());
NODE_ADDR nodeAddr(addr.ToStringIP()); NODE_ADDR nodeAddr(addr.ToStringIP());
bool bUseTLS = ((GetBoolArg("-tls", true) || GetArg("-tls", "") == "only") && find(vNonTLSNodesInbound.begin(), bool bUseTLS = ((GetBoolArg("-tls", true) || GetArg("-tls", "") == "only")
&& find(vNonTLSNodesInbound.begin(),
vNonTLSNodesInbound.end(), vNonTLSNodesInbound.end(),
nodeAddr) == vNonTLSNodesInbound.end()); nodeAddr) == vNonTLSNodesInbound.end());
unsigned long err_code = 0;
if (bUseTLS) if (bUseTLS)
{ {
ssl = tlsmanager.accept( hSocket, addr); ssl = tlsmanager.accept( hSocket, addr, err_code);
if(!ssl) if(!ssl)
{ {
if (GetArg("-tls", "") != "only") if (err_code == TLSManager::SELECT_TIMEDOUT)
{
// can fail also for timeout in select on fd, that is not a ssl error and we should not
// consider this node as non TLS
LogPrint("tls", "%s():%d - Connection from %s timedout\n", __func__, __LINE__, addr.ToStringIP());
}
else
{ {
// Further reconnection will be made in non-TLS (unencrypted) mode if mandatory tls is not set // Further reconnection will be made in non-TLS (unencrypted) mode
vNonTLSNodesInbound.push_back(NODE_ADDR(addr.ToStringIP(), GetTimeMillis())); vNonTLSNodesInbound.push_back(NODE_ADDR(addr.ToStringIP(), GetTimeMillis()));
LogPrint("tls", "%s():%d - err_code %x, adding connection from %s vNonTLSNodesInbound list (sz=%d)\n",
__func__, __LINE__, err_code, addr.ToStringIP(), vNonTLSNodesInbound.size());
} }
CloseSocket(hSocket); CloseSocket(hSocket);
return; return;
@ -1185,8 +1223,8 @@ static void AcceptConnection(const ListenSocket& hListenSocket) {
} }
else else
{ {
LogPrintf ("TLS: Connection from %s will be unencrypted\n", addr.ToString()); LogPrintf ("TLS: Connection from %s will be unencrypted\n", addr.ToStringIP());
vNonTLSNodesInbound.erase( vNonTLSNodesInbound.erase(
remove( remove(
vNonTLSNodesInbound.begin(), vNonTLSNodesInbound.begin(),
@ -1196,14 +1234,19 @@ static void AcceptConnection(const ListenSocket& hListenSocket) {
vNonTLSNodesInbound.end()); vNonTLSNodesInbound.end());
} }
} }
#else else
ssl = TLSManager::accept( hSocket, addr);
if(!ssl)
{ {
CloseSocket(hSocket); unsigned long err_code = 0;
return; ssl = tlsmanager.accept( hSocket, addr, err_code);
if(!ssl)
{
LogPrint("tls", "%s():%d - err_code %x, failure accepting connection from %s\n",
__func__, __LINE__, err_code, addr.ToStringIP());
CloseSocket(hSocket);
return;
}
} }
#endif // COMPAT_NON_TLS
#endif // USE_TLS #endif // USE_TLS
CNode* pnode = new CNode(hSocket, addr, "", true, ssl); CNode* pnode = new CNode(hSocket, addr, "", true, ssl);
@ -1218,7 +1261,7 @@ static void AcceptConnection(const ListenSocket& hListenSocket) {
} }
} }
#if defined(USE_TLS) && defined(COMPAT_NON_TLS) #if defined(USE_TLS)
void ThreadNonTLSPoolsCleaner() void ThreadNonTLSPoolsCleaner()
{ {
while (true) while (true)
@ -1228,7 +1271,9 @@ void ThreadNonTLSPoolsCleaner()
MilliSleep(DEFAULT_CONNECT_TIMEOUT); // sleep and sleep_for are interruption points, which will throw boost::thread_interrupted MilliSleep(DEFAULT_CONNECT_TIMEOUT); // sleep and sleep_for are interruption points, which will throw boost::thread_interrupted
} }
} }
#endif // USE_TLS && COMPAT_NON_TLS
#endif // USE_TLS
void ThreadSocketHandler() void ThreadSocketHandler()
{ {
@ -1325,9 +1370,10 @@ void ThreadSocketHandler()
BOOST_FOREACH(CNode* pnode, vNodes) BOOST_FOREACH(CNode* pnode, vNodes)
{ {
LOCK(pnode->cs_hSocket); LOCK(pnode->cs_hSocket);
if (pnode->hSocket == INVALID_SOCKET) if (pnode->hSocket == INVALID_SOCKET)
continue; continue;
FD_SET(pnode->hSocket, &fdsetError); FD_SET(pnode->hSocket, &fdsetError);
hSocketMax = max(hSocketMax, pnode->hSocket); hSocketMax = max(hSocketMax, pnode->hSocket);
have_fds = true; have_fds = true;
@ -1347,6 +1393,7 @@ void ThreadSocketHandler()
// * We send some data. // * We send some data.
// * We wait for data to be received (and disconnect after timeout). // * We wait for data to be received (and disconnect after timeout).
// * We process a message in the buffer (message handler thread). // * We process a message in the buffer (message handler thread).
{ {
TRY_LOCK(pnode->cs_vSend, lockSend); TRY_LOCK(pnode->cs_vSend, lockSend);
if (lockSend && !pnode->vSendMsg.empty()) { if (lockSend && !pnode->vSendMsg.empty()) {
@ -1407,8 +1454,9 @@ void ThreadSocketHandler()
{ {
boost::this_thread::interruption_point(); boost::this_thread::interruption_point();
if (tlsmanager.threadSocketHandler(pnode,fdsetRecv,fdsetSend,fdsetError)==-1) if (tlsmanager.threadSocketHandler(pnode,fdsetRecv,fdsetSend,fdsetError)==-1){
continue; continue;
}
// //
// Inactivity checking // Inactivity checking
@ -1538,6 +1586,7 @@ void ThreadOpenConnections()
{ {
CAddress addr; CAddress addr;
OpenNetworkConnection(addr, NULL, strAddr.c_str()); OpenNetworkConnection(addr, NULL, strAddr.c_str());
for (int i = 0; i < 10 && i < nLoop; i++) for (int i = 0; i < 10 && i < nLoop; i++)
{ {
MilliSleep(500); MilliSleep(500);
@ -1721,30 +1770,32 @@ bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOu
return false; return false;
} else if (FindNode(std::string(pszDest))) } else if (FindNode(std::string(pszDest)))
return false; return false;
CNode* pnode = ConnectNode(addrConnect, pszDest); CNode* pnode = ConnectNode(addrConnect, pszDest);
boost::this_thread::interruption_point(); boost::this_thread::interruption_point();
#if defined(USE_TLS) && defined(COMPAT_NON_TLS) #if defined(USE_TLS)
if (CNode::GetTlsFallbackNonTls())
if (!pnode)
{ {
string strDest; if (!pnode)
int port;
if (!pszDest)
strDest = addrConnect.ToStringIP();
else
SplitHostPort(string(pszDest), port, strDest);
if (tlsmanager.isNonTLSAddr(strDest, vNonTLSNodesOutbound, cs_vNonTLSNodesOutbound))
{ {
// Attempt to reconnect in non-TLS mode string strDest;
pnode = ConnectNode(addrConnect, pszDest); int port;
boost::this_thread::interruption_point();
if (!pszDest)
strDest = addrConnect.ToStringIP();
else
SplitHostPort(string(pszDest), port, strDest);
if (tlsmanager.isNonTLSAddr(strDest, vNonTLSNodesOutbound, cs_vNonTLSNodesOutbound))
{
// Attempt to reconnect in non-TLS mode
pnode = ConnectNode(addrConnect, pszDest);
boost::this_thread::interruption_point();
}
} }
} }
#endif #endif
if (!pnode) if (!pnode)
@ -1836,6 +1887,7 @@ bool BindListenPort(const CService &addrBind, string& strError, bool fWhiteliste
// Create socket for listening for incoming connections // Create socket for listening for incoming connections
struct sockaddr_storage sockaddr; struct sockaddr_storage sockaddr;
socklen_t len = sizeof(sockaddr); socklen_t len = sizeof(sockaddr);
if (!addrBind.GetSockAddr((struct sockaddr*)&sockaddr, &len)) if (!addrBind.GetSockAddr((struct sockaddr*)&sockaddr, &len))
{ {
strError = strprintf("Error: Bind address family for %s not supported", addrBind.ToString()); strError = strprintf("Error: Bind address family for %s not supported", addrBind.ToString());
@ -2008,13 +2060,13 @@ void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler)
Discover(threadGroup); Discover(threadGroup);
#ifdef USE_TLS #ifdef USE_TLS
if (!tlsmanager.prepareCredentials()) if (!tlsmanager.prepareCredentials())
{ {
LogPrintf("TLS: ERROR: %s: %s: Credentials weren't loaded. Node can't be started.\n", __FILE__, __func__); LogPrintf("TLS: ERROR: %s: %s: Credentials weren't generated. Node can't be started.\n", __FILE__, __func__);
return; return;
} }
if (!tlsmanager.initialize()) if (!tlsmanager.initialize())
{ {
LogPrintf("TLS: ERROR: %s: %s: TLS initialization failed. Node can't be started.\n", __FILE__, __func__); LogPrintf("TLS: ERROR: %s: %s: TLS initialization failed. Node can't be started.\n", __FILE__, __func__);
@ -2051,11 +2103,14 @@ void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler)
// Process messages // Process messages
threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "msghand", &ThreadMessageHandler)); threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "msghand", &ThreadMessageHandler));
#if defined(USE_TLS) && defined(COMPAT_NON_TLS) #if defined(USE_TLS)
// Clean pools of addresses for non-TLS connections if (CNode::GetTlsFallbackNonTls())
threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "poolscleaner", &ThreadNonTLSPoolsCleaner)); {
// Clean pools of addresses for non-TLS connections
threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "poolscleaner", &ThreadNonTLSPoolsCleaner));
}
#endif #endif
// Dump network addresses // Dump network addresses
scheduler.scheduleEvery(&DumpAddresses, DUMP_ADDRESSES_INTERVAL); scheduler.scheduleEvery(&DumpAddresses, DUMP_ADDRESSES_INTERVAL);
} }
@ -2076,42 +2131,34 @@ bool StopNode()
return true; return true;
} }
static class CNetCleanup void CNode::NetCleanup()
{ {
public: // Close sockets
CNetCleanup() {} BOOST_FOREACH(CNode* pnode, vNodes)
pnode->CloseSocketDisconnect();
~CNetCleanup() BOOST_FOREACH(ListenSocket& hListenSocket, vhListenSocket)
{ if (hListenSocket.socket != INVALID_SOCKET)
// Close sockets if (!CloseSocket(hListenSocket.socket))
BOOST_FOREACH(CNode* pnode, vNodes) LogPrintf("CloseSocket(hListenSocket) failed with error %s\n", NetworkErrorString(WSAGetLastError()));
if (pnode->hSocket != INVALID_SOCKET)
CloseSocket(pnode->hSocket); // clean up some globals (to help leak detection)
BOOST_FOREACH(ListenSocket& hListenSocket, vhListenSocket) BOOST_FOREACH(CNode *pnode, vNodes)
if (hListenSocket.socket != INVALID_SOCKET) delete pnode;
if (!CloseSocket(hListenSocket.socket)) BOOST_FOREACH(CNode *pnode, vNodesDisconnected)
LogPrintf("CloseSocket(hListenSocket) failed with error %s\n", NetworkErrorString(WSAGetLastError())); delete pnode;
vNodes.clear();
// clean up some globals (to help leak detection) vNodesDisconnected.clear();
BOOST_FOREACH(CNode *pnode, vNodes) vhListenSocket.clear();
delete pnode; delete semOutbound;
BOOST_FOREACH(CNode *pnode, vNodesDisconnected) semOutbound = NULL;
delete pnode; delete pnodeLocalHost;
vNodes.clear(); pnodeLocalHost = NULL;
vNodesDisconnected.clear();
vhListenSocket.clear(); #ifdef WIN32
delete semOutbound; // Shutdown Windows Sockets
semOutbound = NULL; WSACleanup();
delete pnodeLocalHost;
pnodeLocalHost = NULL;
#ifdef _WIN32
// Shutdown Windows Sockets
WSACleanup();
#endif #endif
}
} }
instance_of_cnetcleanup;
void RelayTransaction(const CTransaction& tx) void RelayTransaction(const CTransaction& tx)
{ {
@ -2314,7 +2361,7 @@ bool CAddrDB::Read(CAddrMan& addr)
unsigned int ReceiveFloodSize() { return 1000*GetArg("-maxreceivebuffer", 5*1000); } unsigned int ReceiveFloodSize() { return 1000*GetArg("-maxreceivebuffer", 5*1000); }
unsigned int SendBufferSize() { return 1000*GetArg("-maxsendbuffer", 1*1000); } unsigned int SendBufferSize() { return 1000*GetArg("-maxsendbuffer", 1*1000); }
CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNameIn, bool fInboundIn, SSL *sslIn) : CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNameIn, bool fInboundIn, WOLFSSL *sslIn) :
ssSend(SER_NETWORK, INIT_PROTO_VERSION), ssSend(SER_NETWORK, INIT_PROTO_VERSION),
addrKnown(5000, 0.001), addrKnown(5000, 0.001),
setInventoryKnown(SendBufferSize() / 1000) setInventoryKnown(SendBufferSize() / 1000)
@ -2372,22 +2419,65 @@ CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNa
GetNodeSignals().InitializeNode(GetId(), this); GetNodeSignals().InitializeNode(GetId(), this);
} }
bool CNode::GetTlsFallbackNonTls()
{
if (tlsFallbackNonTls == eTlsOption::FALLBACK_UNSET)
{
// one time only setting of static class attribute
if ( GetArg("-tls", "") != "only" )
{
LogPrint("tls", "%s():%d - Non-TLS connections will be used in case of failure of TLS\n",
__func__, __LINE__);
tlsFallbackNonTls = eTlsOption::FALLBACK_TRUE;
}
else
{
LogPrint("tls", "%s():%d - Non-TLS connections will NOT be used in case of failure of TLS\n",
__func__, __LINE__);
tlsFallbackNonTls = eTlsOption::FALLBACK_FALSE;
}
}
return (tlsFallbackNonTls == eTlsOption::FALLBACK_TRUE);
}
bool CNode::GetTlsValidate()
{
if (tlsValidate == eTlsOption::FALLBACK_UNSET)
{
// one time only setting of static class attribute
if ( GetBoolArg("-tlsvalidate", false))
{
LogPrint("tls", "%s():%d - TLS certificates will be validated\n",
__func__, __LINE__);
tlsValidate = eTlsOption::FALLBACK_TRUE;
}
else
{
LogPrint("tls", "%s():%d - TLS certificates will NOT be validated\n",
__func__, __LINE__);
tlsValidate = eTlsOption::FALLBACK_FALSE;
}
}
return (tlsValidate == eTlsOption::FALLBACK_TRUE);
}
CNode::~CNode() CNode::~CNode()
{ {
// No need to make a lock on cs_hSocket, because before deletion CNode object is removed from the vNodes vector, so any other thread hasn't access to it. // No need to make a lock on cs_hSocket, because before deletion CNode object is removed from the vNodes vector, so any other thread hasn't access to it.
// Removal is synchronized with read and write routines, so all of them will be completed to this moment. // Removal is synchronized with read and write routines, so all of them will be completed to this moment.
if (hSocket != INVALID_SOCKET) if (hSocket != INVALID_SOCKET)
{ {
if (ssl) if (ssl)
{ {
tlsmanager.waitFor(SSL_SHUTDOWN, hSocket, ssl, (DEFAULT_CONNECT_TIMEOUT / 1000)); unsigned long err_code = 0;
tlsmanager.waitFor(SSL_SHUTDOWN, hSocket, ssl, (DEFAULT_CONNECT_TIMEOUT / 1000), err_code);
SSL_free(ssl);
wolfSSL_free(ssl);
ssl = NULL; ssl = NULL;
} }
CloseSocket(hSocket); CloseSocket(hSocket);
} }
if (pfilter) if (pfilter)

32
src/net.h

@ -46,9 +46,9 @@
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
#include <boost/signals2/signal.hpp> #include <boost/signals2/signal.hpp>
// Enable OpenSSL Support for Hush // Enable WolfSSL Support for Hush
#include <openssl/bio.h> #include <wolfssl/options.h>
#include <openssl/ssl.h> #include <wolfssl/ssl.h>
class CAddrMan; class CAddrMan;
class CBlockIndex; class CBlockIndex;
@ -103,14 +103,6 @@ EVP_PKEY *generate_key();
X509 *generate_x509(EVP_PKEY *pkey); X509 *generate_x509(EVP_PKEY *pkey);
bool write_to_disk(EVP_PKEY *pkey, X509 *x509); bool write_to_disk(EVP_PKEY *pkey, X509 *x509);
void configure_context(SSL_CTX *ctx, bool server_side); void configure_context(SSL_CTX *ctx, bool server_side);
static boost::filesystem::path tlsKeyPath;
static boost::filesystem::path tlsCertPath;
// OpenSSL related variables for metrics.cpp
static std::string routingsecrecy;
static std::string cipherdescription;
static std::string securitylevel;
static std::string validationdescription;
typedef int NodeId; typedef int NodeId;
@ -214,6 +206,7 @@ public:
NodeId nodeid; NodeId nodeid;
uint64_t nServices; uint64_t nServices;
bool fTLSEstablished; bool fTLSEstablished;
bool fTLSVerified;
int64_t nLastSend; int64_t nLastSend;
int64_t nLastRecv; int64_t nLastRecv;
int64_t nTimeConnected; int64_t nTimeConnected;
@ -359,6 +352,14 @@ protected:
// Basic fuzz-testing // Basic fuzz-testing
void Fuzz(int nChance); // modifies ssSend void Fuzz(int nChance); // modifies ssSend
enum class eTlsOption {
FALLBACK_UNSET = 0,
FALLBACK_FALSE = 1,
FALLBACK_TRUE = 2
};
static eTlsOption tlsFallbackNonTls;
static eTlsOption tlsValidate;
public: public:
uint256 hashContinue; uint256 hashContinue;
int nStartingHeight; int nStartingHeight;
@ -459,7 +460,7 @@ public:
if (addr.IsValid() && !addrKnown.contains(addr.GetKey())) { if (addr.IsValid() && !addrKnown.contains(addr.GetKey())) {
if (vAddrToSend.size() >= MAX_ADDR_TO_SEND) { if (vAddrToSend.size() >= MAX_ADDR_TO_SEND) {
vAddrToSend[insecure_rand() % vAddrToSend.size()] = addr; vAddrToSend[insecure_rand() % vAddrToSend.size()] = addr;
} else { } else {
vAddrToSend.push_back(addr); vAddrToSend.push_back(addr);
} }
} }
@ -693,6 +694,13 @@ public:
static uint64_t GetTotalBytesRecv(); static uint64_t GetTotalBytesRecv();
static uint64_t GetTotalBytesSent(); static uint64_t GetTotalBytesSent();
// resource deallocation on cleanup, called at node shutdown
static void NetCleanup();
// returns the value of the tlsfallbacknontls and tlsvalidate flags set at zend startup (see init.cpp)
static bool GetTlsFallbackNonTls();
static bool GetTlsValidate();
}; };

28
src/support/cleanse.cpp

@ -3,11 +3,33 @@
// Distributed under the GPLv3 software license, see the accompanying // Distributed under the GPLv3 software license, see the accompanying
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html // file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
#include "cleanse.h" #include <support/cleanse.h>
#include <openssl/crypto.h> #include <cstring>
#if defined(_MSC_VER)
#include <Windows.h> // For SecureZeroMemory.
#endif
void memory_cleanse(void *ptr, size_t len) void memory_cleanse(void *ptr, size_t len)
{ {
OPENSSL_cleanse(ptr, len); #if defined(_MSC_VER)
/* SecureZeroMemory is guaranteed not to be optimized out by MSVC. */
SecureZeroMemory(ptr, len);
#else
std::memset(ptr, 0, len);
/* Memory barrier that scares the compiler away from optimizing out the memset.
*
* Quoting Adam Langley <agl@google.com> in commit ad1907fe73334d6c696c8539646c21b11178f20f
* in BoringSSL (ISC License):
* As best as we can tell, this is sufficient to break any optimisations that
* might try to eliminate "superfluous" memsets.
* This method is used in memzero_explicit() the Linux kernel, too. Its advantage is that it
* is pretty efficient because the compiler can still implement the memset() efficiently,
* just not remove it entirely. See "Dead Store Elimination (Still) Considered Harmful" by
* Yang et al. (USENIX Security 2017) for more background.
*/
__asm__ __volatile__("" : : "r"(ptr) : "memory");
#endif
} }

2
src/support/cleanse.h

@ -8,6 +8,8 @@
#include <stdlib.h> #include <stdlib.h>
/** Secure overwrite a buffer (possibly containing secret data) with zero-bytes. The write
* operation will not be optimized out by the compiler. */
void memory_cleanse(void *ptr, size_t len); void memory_cleanse(void *ptr, size_t len);
#endif // BITCOIN_SUPPORT_CLEANSE_H #endif // BITCOIN_SUPPORT_CLEANSE_H

45
src/util.cpp

@ -101,8 +101,6 @@
#include <boost/program_options/detail/config_file.hpp> #include <boost/program_options/detail/config_file.hpp>
#include <boost/program_options/parsers.hpp> #include <boost/program_options/parsers.hpp>
#include <boost/thread.hpp> #include <boost/thread.hpp>
#include <openssl/crypto.h>
#include <openssl/conf.h>
// Work around clang compilation problem in Boost 1.46: // Work around clang compilation problem in Boost 1.46:
// /usr/include/boost/program_options/detail/config_file.hpp:163:17: error: call to function 'to_internal' that is neither visible in the template definition nor found by argument-dependent lookup // /usr/include/boost/program_options/detail/config_file.hpp:163:17: error: call to function 'to_internal' that is neither visible in the template definition nor found by argument-dependent lookup
@ -133,47 +131,6 @@ bool fLogIPs = DEFAULT_LOGIPS;
std::atomic<bool> fReopenDebugLog(false); std::atomic<bool> fReopenDebugLog(false);
CTranslationInterface translationInterface; CTranslationInterface translationInterface;
/** Init OpenSSL library multithreading support */
static CCriticalSection** ppmutexOpenSSL;
void locking_callback(int mode, int i, const char* file, int line) NO_THREAD_SAFETY_ANALYSIS
{
if (mode & CRYPTO_LOCK) {
ENTER_CRITICAL_SECTION(*ppmutexOpenSSL[i]);
} else {
LEAVE_CRITICAL_SECTION(*ppmutexOpenSSL[i]);
}
}
// Init
static class CInit
{
public:
CInit()
{
// Init OpenSSL library multithreading support
ppmutexOpenSSL = (CCriticalSection**)OPENSSL_malloc(CRYPTO_num_locks() * sizeof(CCriticalSection*));
for (int i = 0; i < CRYPTO_num_locks(); i++)
ppmutexOpenSSL[i] = new CCriticalSection();
CRYPTO_set_locking_callback(locking_callback);
// OpenSSL can optionally load a config file which lists optional loadable modules and engines.
// We don't use them so we don't require the config. However some of our libs may call functions
// which attempt to load the config file, possibly resulting in an exit() or crash if it is missing
// or corrupt. Explicitly tell OpenSSL not to try to load the file. The result for our libs will be
// that the config appears to have been loaded and there are no modules/engines available.
OPENSSL_no_config();
}
~CInit()
{
// Shutdown OpenSSL library multithreading support
CRYPTO_set_locking_callback(NULL);
for (int i = 0; i < CRYPTO_num_locks(); i++)
delete ppmutexOpenSSL[i];
OPENSSL_free(ppmutexOpenSSL);
}
}
instance_of_cinit;
/** /**
* LogPrintf() has been broken a couple of times now * LogPrintf() has been broken a couple of times now
* by well-meaning people adding mutexes in the most straightforward way. * by well-meaning people adding mutexes in the most straightforward way.
@ -1037,8 +994,6 @@ std::string LicenseInfo()
FormatParagraph(_("This is experimental software!!!")) + "\n" + FormatParagraph(_("This is experimental software!!!")) + "\n" +
"\n" + "\n" +
FormatParagraph(_("Distributed under the GPLv3 software license, see the accompanying file COPYING or <https://www.gnu.org/licenses/gpl-3.0.en.html>.")) + "\n" + FormatParagraph(_("Distributed under the GPLv3 software license, see the accompanying file COPYING or <https://www.gnu.org/licenses/gpl-3.0.en.html>.")) + "\n" +
"\n" +
FormatParagraph(_("This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit <https://www.openssl.org/> and cryptographic software written by Eric Young.")) +
"\n"; "\n";
} }

4
src/wallet/crypter.cpp

@ -28,8 +28,8 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
#include <openssl/aes.h> #include <wolfssl/openssl/aes.h>
#include <openssl/evp.h> #include <wolfssl/openssl/evp.h>
using namespace libzcash; using namespace libzcash;

Loading…
Cancel
Save