Merge pull request 'WolfSSLization' (#1) from miodrag into dev

This commit is contained in:
Miodrag
2020-11-03 16:14:55 +01:00
14 changed files with 743 additions and 1043 deletions

View File

@@ -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], [libcrypto],,[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/openssl/crypto.h],,AC_MSG_ERROR(libcrypto headers missing))
AC_CHECK_LIB([crypto], [main],CRYPTO_LIBS=-lcrypto, AC_MSG_ERROR(libcrypto missing)) AC_CHECK_LIB([crypto], [main],CRYPTO_LIBS=-lcrypto, AC_MSG_ERROR(libcrypto missing))
AC_CHECK_HEADER([openssl/ssl.h],, AC_MSG_ERROR(libssl headers missing),) AC_CHECK_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),)

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,46 @@
package=wolfssl
$(package)_version=4.5.0
$(package)_download_path=https://github.com/wolfSSL/wolfssl/archive
$(package)_download_file=v$($(package)_version)-stable.tar.gz
$(package)_file_name=wolfssl-$($(package)_version).tar.gz
$(package)_sha256_hash=7de62300ce14daa0051bfefc7c4d6302f96cabc768b6ae49eda77523b118250c
define $(package)_set_vars
$(package)_config_env=AR="$($(package)_ar)" RANLIB="$($(package)_ranlib)" CC="$($(package)_cc)"
$(package)_config_opts=--prefix=$(host_prefix)
$(package)_config_opts+=--enable-static
$(package)_config_opts+=--disable-shared
$(package)_config_opts+=--disable-examples
$(package)_config_opts+=--disable-crypttests
$(package)_config_opts+=--enable-keygen
$(package)_config_opts+=--enable-certgen
$(package)_config_opts+=--enable-enckeys
$(package)_config_opts+=--enable-opensslall
$(package)_config_opts+=--enable-opensslextra
$(package)_config_opts+=--enable-harden
endef
define $(package)_preprocess_cmds
cd $($(package)_build_subdir); ./autogen.sh
endef
define $(package)_config_cmds
./configure $($(package)_config_opts)
endef
#define $(package)_config_cmds
# $($(package)_autoconf)
#endef
define $(package)_build_cmds
$(MAKE) -j1 src/libwolfssl.la
endef
define $(package)_stage_cmds
$(MAKE) DESTDIR=$($(package)_staging_dir) install-libLTLIBRARIES install-includeHEADERS install-nobase_includeHEADERS install-pkgconfigDATA
endef
#define $(package)_postprocess_cmds
# rm -rf bin share
#endef

View File

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

View File

@@ -1,60 +1,135 @@
// 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)
{ {
return 1; 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,
* @brief Wait for a given SSL connection event. 0xCB, 0xD1, 0x77, 0x30, 0xC2, 0x9A, 0x69, 0x01, 0xCF, 0x01,
* 0xDE, 0xD4, 0x67, 0xD4, 0xEE, 0x9A, 0x03, 0x1C, 0x27, 0x42,
* @param eRoutine a SSLConnectionRoutine value which determines the type of the event. 0x06, 0x3D, 0x1D, 0x91, 0x27, 0xCF, 0x1C, 0x17, 0xB3, 0xDC,
* @param hSocket 0x9F, 0x6F, 0x12, 0xC8, 0x03, 0x5C, 0x01, 0xF3, 0x27, 0x7F,
* @param ssl pointer to an SSL instance. 0x34, 0x58, 0xAE, 0xB9, 0xA7, 0xA9, 0xCE, 0x5E, 0x25, 0x7D,
* @param timeoutSec timeout in seconds. 0x46, 0x84, 0xDD, 0xEE, 0x55, 0xFB, 0xEA, 0x1C, 0xCD, 0x9B,
* @return int returns nError corresponding to the connection event. 0x96, 0xC4, 0x22, 0x8C, 0x33, 0x8B, 0xC7, 0xE6, 0xCC, 0x4C,
*/ 0x77, 0x1B, 0x7A, 0x46, 0xDE, 0x33, 0xAD, 0xBB, 0xFD, 0x2D,
int TLSManager::waitFor(SSLConnectionRoutine eRoutine, SOCKET hSocket, SSL* ssl, int timeoutSec) 0xAD, 0x26, 0xE1, 0x27, 0x48, 0x94, 0xA3, 0x59, 0xC5, 0x10,
{ 0x5A, 0x86, 0x71, 0x8D, 0xAA, 0x15, 0x8B, 0xB2, 0xCB, 0x70,
int nErr = 0; 0xBE, 0x1F, 0x17, 0xBD, 0xEB, 0x51, 0xB1, 0x76, 0x0E, 0x24,
ERR_clear_error(); // clear the error queue 0x43, 0xAA, 0x06, 0xC0, 0x97, 0x01, 0x25, 0x52, 0x30, 0x7A,
0x56, 0x92, 0x3D, 0x8A, 0x3A, 0xBC, 0xFA, 0x98, 0x51, 0x04,
0x1D, 0x9B, 0x05, 0xB8, 0x84, 0x8C, 0x2F, 0x7A, 0x94, 0x1E,
0xAA, 0x51, 0xF2, 0x5D, 0x48, 0x50, 0x58, 0x8D, 0x7E, 0xBA,
0xD3, 0xCC, 0xF2, 0x92, 0x28, 0xB1, 0x1C, 0x4B, 0x50, 0x10,
0xFA, 0x7E, 0xDF, 0x8D, 0x23, 0x1C, 0x8C, 0x65, 0xE3, 0x86,
0x16, 0x67, 0x88, 0x9E, 0xFC, 0x8B, 0xC8, 0x55, 0x38, 0x6E,
0x79, 0x06, 0x6A, 0x6D, 0x72, 0x75, 0xA6, 0xAC, 0x77, 0x98,
0xDD, 0xB2, 0x0B, 0xAA, 0x48, 0x54, 0xA9, 0x07, 0x7E, 0x8C,
0x4C, 0x39, 0x08, 0x26, 0x6D, 0x53, 0xC2, 0xDF, 0xE2, 0xF0,
0xD6, 0x8A, 0x4F, 0xB5, 0x7A, 0x32, 0xEE, 0x93, 0x0E, 0x2A,
0x81, 0x2F, 0x3B, 0x1E, 0xE6, 0x38, 0xF8, 0x3C, 0xF5, 0x84,
0xB4, 0xFB, 0x92, 0x12, 0x28, 0xA3
};
static unsigned char dhg_2048[] = {
0x02
};
WOLFSSL_DH *dh = wolfSSL_DH_new();
if (dh == NULL) {
return NULL;
}
if (wc_DhSetKey((DhKey*)dh->internal, dhp_2048, sizeof(dhp_2048), dhg_2048, sizeof(dhg_2048)) != 0) {
wolfSSL_DH_free(dh);
return NULL;
}
return dh;
}
DH *tmp_dh_callback(WOLFSSL *ssl, int is_export, int keylength)
{
LogPrint("tls", "TLS: %s: %s():%d - Using Diffie-Hellman param for PFS: is_export=%d, keylength=%d\n",
__FILE__, __func__, __LINE__, is_export, keylength);
return get_dh2048();
}
int TLSManager::waitFor(SSLConnectionRoutine eRoutine, SOCKET hSocket, WOLFSSL* ssl, int timeoutSec, unsigned long& err_code)
{
int retOp = 0;
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:
@@ -62,18 +137,35 @@ int TLSManager::waitFor(SSLConnectionRoutine eRoutine, SOCKET hSocket, SSL* ssl,
} }
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;
} else if (retOp == 1) {
LogPrint("tls", "TLS: %s: %s():%d - SSL_SHUTDOWN completed\n", __FILE__, __func__, __LINE__);
break; break;
} else { } else {
if (nErr == 1) LogPrint("tls", "TLS: %s: %s():%d - SSL_SHUTDOWN failed\n", __FILE__, __func__, __LINE__);
// the error will be read afterwards
}
} else {
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");
if (!mykey || !mycert) {
return NULL; return NULL;
bool bInitialized = false;
SSL_CTX* tlsCtx = NULL;
if ((tlsCtx = SSL_CTX_new(ctxType == SERVER_CONTEXT ? TLS_server_method() : TLS_client_method()))) {
SSL_CTX_set_mode(tlsCtx, SSL_MODE_AUTO_RETRY);
int rootCertsNum = LoadDefaultRootCertificates(tlsCtx);
int trustedPathsNum = 0;
for (boost::filesystem::path trustedDir : trustedDirs) {
if (SSL_CTX_load_verify_locations(tlsCtx, NULL, trustedDir.string().c_str()) == 1)
trustedPathsNum++;
} }
if (rootCertsNum == 0 && trustedPathsNum == 0) bool bInitialized = false;
LogPrintf("TLS: WARNING: %s: %s: failed to set up verified certificates. It will be impossible to verify peer certificates. \n", __FILE__, __func__); WOLFSSL_CTX* tlsCtx = NULL;
SSL_CTX_set_verify(tlsCtx, SSL_VERIFY_PEER, tlsCertVerificationCallback); byte *pem;
int plen = 0;
if (SSL_CTX_use_certificate_file(tlsCtx, certificateFile.string().c_str(), SSL_FILETYPE_PEM) > 0) { if ((tlsCtx = wolfSSL_CTX_new(ctxType == SERVER_CONTEXT ? wolfTLSv1_3_server_method() : wolfTLSv1_3_client_method()))) {
if (SSL_CTX_use_PrivateKey_file(tlsCtx, privateKeyFile.string().c_str(), SSL_FILETYPE_PEM) > 0) { wolfSSL_CTX_set_mode(tlsCtx, SSL_MODE_AUTO_RETRY);
if (SSL_CTX_check_private_key(tlsCtx)) {
// Disable TLS < 1.3 ... imho redundant, because v1.3 is required via method
int ret = wolfSSL_CTX_set_min_proto_version(tlsCtx, TLS1_3_VERSION);
if (ret == 0) {
LogPrintf("TLS: WARNING: %s: %s():%d - failed to set min TLS version\n", __FILE__, __func__, __LINE__);
}
LogPrintf("TLS: %s: %s():%d - setting cipher list\n", __FILE__, __func__, __LINE__);
// Default TLSv1.3 cipher list is "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256"
// Nodes will randomly choose to prefer first cipher or the second, to create diversity on the network
// and not be in the situation where all nodes have the same list so the first is always used
if(GetRand(100) > 50) {
if (wolfSSL_CTX_set_cipher_list(tlsCtx, "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256")) {
LogPrintf("%s: Preferring TLS_AES256-GCM-SHA384\n", __func__);
} else {
LogPrintf("%s: Setting preferred cipher failed !!!\n", __func__);
}
} else {
if (wolfSSL_CTX_set_cipher_list(tlsCtx, "TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384")) {
LogPrintf("%s: Preferring TLS_AES256-GCM-SHA384\n", __func__);
} else {
LogPrintf("%s: Setting preferred cipher failed !!!\n", __func__);
}
}
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);
LogPrintf("TLS: %s: %s():%d - setting dh callback\n", __FILE__, __func__, __LINE__);
SSL_CTX_set_tmp_dh_callback(tlsCtx, tmp_dh_callback);
}
// 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; bInitialized = true;
} else { } else {
LogPrintf("TLS: ERROR: %s: %s: private key does not match the certificate public key\n", __FILE__, __func__); LogPrintf("TLS: ERROR: %s: %s: private key does not match the certificate public key\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 private key file\n", __FILE__, __func__);
}
} else { } else {
LogPrintf("TLS: ERROR: %s: %s: failed to use certificate file\n", __FILE__, __func__); LogPrintf("TLS: ERROR: %s: %s: failed to use certificate file\n", __FILE__, __func__);
ERR_print_errors_fp(stderr); wolfSSL_ERR_dump_errors_fp(stderr);
}
} }
} else { } 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);
CredentialsStatus credStatus = // Generating key and the self-signed certificate for it
VerifyCredentials(
boost::filesystem::path(GetArg("-tlskeypath", defaultKeyPath.string())),
boost::filesystem::path(GetArg("-tlscertpath", defaultCertPath.string())),
GetArg("-tlskeypwd", ""));
bool bPrepared = (credStatus == credOk);
if (!bPrepared) {
if (!mapArgs.count("-tlskeypath") && !mapArgs.count("-tlscertpath")) {
// Default paths were used
if (credStatus == credAbsent) {
// Generate new credentials (key and self-signed certificate on it) only if credentials were absent previously
// //
bPrepared = GenerateCredentials( mykey = GenerateEcKey();
defaultKeyPath, if (mykey) {
defaultCertPath, mycert = GenerateCertificate(mykey);
GetArg("-tlskeypwd", "")); if (mycert) {
if (CheckKeyCert()) {
LogPrintStr("TLS: New private key and self-signed certificate were generated successfully\n");
return true;
} }
//wolfSSL_X509_free(mycert);
} }
//wolfSSL_EVP_PKEY_free(mykey);
} }
return bPrepared; 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;
}
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__);

View File

@@ -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);
int waitFor(SSLConnectionRoutine eRoutine, SOCKET hSocket, WOLFSSL* ssl, int timeoutSec, unsigned long& err_code);
WOLFSSL* connect(SOCKET hSocket, const CAddress& addrConnect, unsigned long& err_code);
WOLFSSL_CTX* initCtx(TLSContextType ctxType);
bool prepareCredentials(); bool prepareCredentials();
SSL* accept(SOCKET hSocket, const CAddress& addr); WOLFSSL* accept(SOCKET hSocket, const CAddress& addr, unsigned long& err_code);
bool isNonTLSAddr(const string& strAddr, const vector<NODE_ADDR>& vPool, CCriticalSection& cs); bool isNonTLSAddr(const string& strAddr, const vector<NODE_ADDR>& vPool, CCriticalSection& cs);
void cleanNonTLSPool(std::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); int threadSocketHandler(CNode* pnode, fd_set& fdsetRecv, fd_set& fdsetSend, fd_set& fdsetError);
bool initialize(); bool initialize();
bool CheckKeyCert();
}; };
} }

View File

@@ -6,141 +6,65 @@
#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)
{
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; evpPrivKey = NULL;
} }
} }
} }
if(!evpPrivKey) // EVP_PKEY_assign_RSA uses the supplied key internally if(!evpPrivKey) {
RSA_free(privKey); wolfSSL_EC_KEY_free(privKey);
evpPrivKey = NULL;
} }
} }
BN_free(pubKey);
}
return evpPrivKey; return evpPrivKey;
} }
// 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)) {
{
X509_NAME *subjectName = X509_get_subject_name(cert);
if (subjectName)
{
// an issuer name is the same as a subject name, due to certificate is self-signed
if (X509_set_issuer_name(cert, subjectName))
{
// private key from keypair is used; signature will be set inside of the cert // private key from keypair is used; signature will be set inside of the cert
bCertSigned = X509_sign(cert, keypair, EVP_sha512()); bCertSigned = wolfSSL_X509_sign(cert, keypair, wolfSSL_EVP_sha512());
}
}
} }
} }
if (!bCertSigned) if (!bCertSigned) {
{ wolfSSL_X509_free(cert);
X509_free(cert);
cert = NULL; cert = NULL;
} }
} }
@@ -148,348 +72,4 @@ static X509* GenerateCertificate(EVP_PKEY *keypair)
return cert; return cert;
} }
// Stores key to file, specified by the 'filePath'
//
static bool StoreKey(EVP_PKEY *key, const boost::filesystem::path &filePath, const std::string &passphrase)
{
if (!key)
return false;
bool bStored = false;
FILE *keyfd = fopen(filePath.string().c_str(), "wb");
if (keyfd)
{
const EVP_CIPHER* pCipher = NULL;
if (passphrase.length() && (pCipher = EVP_aes_256_cbc()))
bStored = PEM_write_PrivateKey(keyfd, key, pCipher, NULL, 0, NULL, (void*)passphrase.c_str());
else
bStored = PEM_write_PrivateKey(keyfd, key, NULL, NULL, 0, NULL, NULL);
fclose(keyfd);
}
return bStored;
}
// Stores certificate to file, specified by the 'filePath'
//
static bool StoreCertificate(X509 *cert, const boost::filesystem::path &filePath)
{
if (!cert)
return false;
bool bStored = false;
FILE *certfd = fopen(filePath.string().c_str(), "wb");
if (certfd)
{
bStored = PEM_write_X509(certfd, cert);
fclose(certfd);
}
return bStored;
}
// Loads key from file, specified by the 'filePath'
//
static EVP_PKEY* LoadKey(const boost::filesystem::path &filePath, const std::string &passphrase)
{
if (!boost::filesystem::exists(filePath))
return NULL;
EVP_PKEY *key = NULL;
FILE *keyfd = fopen(filePath.string().c_str(), "rb");
if (keyfd)
{
key = PEM_read_PrivateKey(keyfd, NULL, NULL, passphrase.length() ? (void*)passphrase.c_str() : NULL);
fclose(keyfd);
}
return key;
}
// Loads certificate from file, specified by the 'filePath'
//
static X509* LoadCertificate(const boost::filesystem::path &filePath)
{
if (!boost::filesystem::exists(filePath))
return NULL;
X509 *cert = NULL;
FILE *certfd = fopen(filePath.string().c_str(), "rb");
if (certfd)
{
cert = PEM_read_X509(certfd, NULL, NULL, NULL);
fclose(certfd);
}
return cert;
}
// Verifies if the private key in 'key' matches the public key in 'cert'
// (Signs random bytes on 'key' and verifies signature correctness on public key from 'cert')
//
static bool IsMatching(EVP_PKEY *key, X509 *cert)
{
if (!key || !cert)
return false;
bool bIsMatching = false;
EVP_PKEY_CTX *ctxSign = EVP_PKEY_CTX_new(key, NULL);
if (ctxSign)
{
if (EVP_PKEY_sign_init(ctxSign) == 1 &&
EVP_PKEY_CTX_set_signature_md(ctxSign, EVP_sha512()) > 0)
{
unsigned char digest[SHA512_DIGEST_LENGTH] = { 0 };
size_t digestSize = sizeof digest, signatureSize = 0;
if (RAND_bytes((unsigned char*)&digest, digestSize) && // set random bytes as a digest
EVP_PKEY_sign(ctxSign, NULL, &signatureSize, digest, digestSize) == 1) // determine buffer length
{
unsigned char *signature = (unsigned char*)OPENSSL_malloc(signatureSize);
if (signature)
{
if (EVP_PKEY_sign(ctxSign, signature, &signatureSize, digest, digestSize) == 1)
{
EVP_PKEY *pubkey = X509_get_pubkey(cert);
if (pubkey)
{
EVP_PKEY_CTX *ctxVerif = EVP_PKEY_CTX_new(pubkey, NULL);
if (ctxVerif)
{
if (EVP_PKEY_verify_init(ctxVerif) == 1 &&
EVP_PKEY_CTX_set_signature_md(ctxVerif, EVP_sha512()) > 0)
{
bIsMatching = (EVP_PKEY_verify(ctxVerif, signature, signatureSize, digest, digestSize) == 1);
}
EVP_PKEY_CTX_free(ctxVerif);
}
EVP_PKEY_free(pubkey);
}
}
OPENSSL_free(signature);
}
}
}
EVP_PKEY_CTX_free(ctxSign);
}
return bIsMatching;
}
// Checks the correctness of a private-public key pair and the validity of a certificate using public key from key pair
//
static bool CheckCredentials(EVP_PKEY *key, X509 *cert)
{
if (!key || !cert)
return false;
bool bIsOk = false;
// Validating the correctness of a private-public key pair, depending on a key type
//
switch (EVP_PKEY_base_id(key))
{
case EVP_PKEY_RSA:
case EVP_PKEY_RSA2:
{
RSA *rsaKey = EVP_PKEY_get1_RSA(key);
if (rsaKey)
{
bIsOk = (RSA_check_key(rsaKey) == 1);
RSA_free(rsaKey);
}
break;
}
// Currently only RSA keys are supported.
// Other key types can be added here in further.
default:
bIsOk = false;
}
// Verifying if the private key matches the public key in certificate
if (bIsOk)
bIsOk = IsMatching(key, cert);
return bIsOk;
}
// Verifies credentials (a private key, a certificate for public key and a correspondence between the private and the public key)
//
CredentialsStatus VerifyCredentials(
const boost::filesystem::path &keyPath,
const boost::filesystem::path &certPath,
const std::string &passphrase)
{
CredentialsStatus status = credAbsent;
EVP_PKEY *key = NULL;
X509 *cert = NULL;
key = LoadKey(keyPath, passphrase);
cert = LoadCertificate(certPath);
if (key && cert)
status = CheckCredentials(key, cert) ? credOk : credNonConsistent;
else if (!key && !cert)
status = credAbsent;
else
status = credPartiallyAbsent;
if (key)
EVP_PKEY_free(key);
if (cert)
X509_free(cert);
return status;
}
// Generates public key pair and the self-signed certificate for it, and then stores them by the specified paths 'keyPath' and 'certPath' respectively.
//
bool GenerateCredentials(
const boost::filesystem::path &keyPath,
const boost::filesystem::path &certPath,
const std::string &passphrase)
{
bool bGenerated = false;
EVP_PKEY *key = NULL;
X509 *cert = NULL;
// Generating RSA key and the self-signed certificate for it
//
key = GenerateRsaKey(TLS_RSA_KEY_SIZE, RSA_F4);
if (key)
{
cert = GenerateCertificate(key);
if (cert)
{
if (StoreKey(key, keyPath, passphrase) &&
StoreCertificate(cert, certPath))
{
bGenerated = true;
LogPrintStr("TLS: New private key and self-signed certificate were generated successfully\n");
}
X509_free(cert);
}
EVP_PKEY_free(key);
}
return bGenerated;
}
// Checks if certificate of a peer is valid (by internal means of the TLS protocol)
//
// Validates peer certificate using a chain of CA certificates.
// If some of intermediate CA certificates are absent in the trusted certificates store, then validation status will be 'false')
//
bool ValidatePeerCertificate(SSL *ssl)
{
if (!ssl)
return false;
bool bIsOk = false;
X509 *cert = SSL_get_peer_certificate (ssl);
if (cert)
{
// NOTE: SSL_get_verify_result() is only useful in connection with SSL_get_peer_certificate (https://www.openssl.org/docs/man1.0.2/ssl/SSL_get_verify_result.html)
//
bIsOk = (SSL_get_verify_result(ssl) == X509_V_OK);
X509_free(cert);
}
else
{
LogPrint("net", "TLS: Peer does not have certificate\n");
bIsOk = false;
}
return bIsOk;
}
// Check if a given context is set up with a cert that can be validated by this context
//
bool ValidateCertificate(SSL_CTX *ssl_ctx)
{
if (!ssl_ctx)
return false;
bool bIsOk = false;
X509_STORE *store = SSL_CTX_get_cert_store(ssl_ctx);
if (store)
{
X509_STORE_CTX *ctx = X509_STORE_CTX_new();
if (ctx)
{
if (X509_STORE_CTX_init(ctx, store, SSL_CTX_get0_certificate(ssl_ctx), NULL) == 1)
bIsOk = X509_verify_cert(ctx) == 1;
X509_STORE_CTX_free(ctx);
}
}
return bIsOk;
}
// Creates the list of available OpenSSL default directories for trusted certificates storage
//
std::vector<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;
}
} }

View File

@@ -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; WOLFSSL_X509* GenerateCertificate(WOLFSSL_EVP_PKEY *keypair);
// 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.
// Returns the number of loaded certificates.
//
int LoadDefaultRootCertificates(SSL_CTX *ctx);
} }

View File

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

View File

@@ -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,29 +442,42 @@ 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); LOCK(cs_vNonTLSNodesOutbound);
LogPrint("tls", "%s():%d - handling connection to %s\n", __func__, __LINE__, addrConnect.ToString());
NODE_ADDR nodeAddr(addrConnect.ToStringIP()); NODE_ADDR nodeAddr(addrConnect.ToStringIP());
bool bUseTLS = ((GetBoolArg("-tls", true) || GetArg("-tls", "") == "only") && find(vNonTLSNodesOutbound.begin(), bool bUseTLS = ((GetBoolArg("-tls", true) || GetArg("-tls", "") == "only")
&& find(vNonTLSNodesOutbound.begin(),
vNonTLSNodesOutbound.end(), vNonTLSNodesOutbound.end(),
nodeAddr) == vNonTLSNodesOutbound.end()); nodeAddr) == vNonTLSNodesOutbound.end());
unsigned long err_code = 0;
if (bUseTLS) if (bUseTLS)
{ {
ssl = tlsmanager.connect(hSocket, addrConnect); ssl = tlsmanager.connect(hSocket, addrConnect, err_code);
if (!ssl) if (!ssl)
{ {
if (GetArg("-tls", "") != "only") if (err_code == TLSManager::SELECT_TIMEDOUT)
{ {
// Further reconnection will be made in non-TLS (unencrypted) mode if mandatory tls is not set // 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())); 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); CloseSocket(hSocket);
return NULL; return NULL;
@@ -474,14 +495,19 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest)
vNonTLSNodesOutbound.end()); vNonTLSNodesOutbound.end());
} }
} }
#else }
ssl = TLSManager::connect(hSocket, addrConnect); else
{
unsigned long err_code = 0;
ssl = tlsmanager.connect(hSocket, addrConnect, err_code);
if(!ssl) if(!ssl)
{ {
LogPrint("tls", "%s():%d - err_code %x, connection to %s failed)\n",
__func__, __LINE__, err_code, addrConnect.ToStringIP());
CloseSocket(hSocket); CloseSocket(hSocket);
return NULL; return NULL;
} }
#endif // COMPAT_NON_TLS }
#endif // USE_TLS #endif // USE_TLS
@@ -528,9 +554,9 @@ 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);
@@ -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);
} }
} }
@@ -823,9 +849,9 @@ void SocketSendData(CNode *pnode)
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,9 +879,9 @@ 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
@@ -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)
{ {
// Further reconnection will be made in non-TLS (unencrypted) mode if mandatory tls is not set // 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
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,7 +1223,7 @@ 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(
@@ -1196,14 +1234,19 @@ static void AcceptConnection(const ListenSocket& hListenSocket) {
vNonTLSNodesInbound.end()); vNonTLSNodesInbound.end());
} }
} }
#else else
ssl = TLSManager::accept( hSocket, addr); {
unsigned long err_code = 0;
ssl = tlsmanager.accept( hSocket, addr, err_code);
if(!ssl) if(!ssl)
{ {
LogPrint("tls", "%s():%d - err_code %x, failure accepting connection from %s\n",
__func__, __LINE__, err_code, addr.ToStringIP());
CloseSocket(hSocket); CloseSocket(hSocket);
return; 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,6 +1370,7 @@ 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;
@@ -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);
@@ -1725,8 +1774,9 @@ bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOu
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) if (!pnode)
{ {
string strDest; string strDest;
@@ -1744,6 +1794,7 @@ bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOu
boost::this_thread::interruption_point(); boost::this_thread::interruption_point();
} }
} }
}
#endif #endif
@@ -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());
@@ -2011,7 +2063,7 @@ void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler)
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;
} }
@@ -2051,9 +2103,12 @@ 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)
if (CNode::GetTlsFallbackNonTls())
{
// Clean pools of addresses for non-TLS connections // Clean pools of addresses for non-TLS connections
threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "poolscleaner", &ThreadNonTLSPoolsCleaner)); threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "poolscleaner", &ThreadNonTLSPoolsCleaner));
}
#endif #endif
// Dump network addresses // Dump network addresses
@@ -2076,17 +2131,11 @@ bool StopNode()
return true; return true;
} }
static class CNetCleanup void CNode::NetCleanup()
{
public:
CNetCleanup() {}
~CNetCleanup()
{ {
// Close sockets // Close sockets
BOOST_FOREACH(CNode* pnode, vNodes) BOOST_FOREACH(CNode* pnode, vNodes)
if (pnode->hSocket != INVALID_SOCKET) pnode->CloseSocketDisconnect();
CloseSocket(pnode->hSocket);
BOOST_FOREACH(ListenSocket& hListenSocket, vhListenSocket) BOOST_FOREACH(ListenSocket& hListenSocket, vhListenSocket)
if (hListenSocket.socket != INVALID_SOCKET) if (hListenSocket.socket != INVALID_SOCKET)
if (!CloseSocket(hListenSocket.socket)) if (!CloseSocket(hListenSocket.socket))
@@ -2105,13 +2154,11 @@ public:
delete pnodeLocalHost; delete pnodeLocalHost;
pnodeLocalHost = NULL; pnodeLocalHost = NULL;
#ifdef _WIN32 #ifdef WIN32
// Shutdown Windows Sockets // Shutdown Windows Sockets
WSACleanup(); 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,6 +2419,48 @@ 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.
@@ -2381,9 +2470,10 @@ CNode::~CNode()
{ {
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;
} }

View File

@@ -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;
@@ -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();
}; };

View File

@@ -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";
} }