summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'Swiften/TLS')
-rw-r--r--Swiften/TLS/CAPICertificate.cpp67
-rw-r--r--Swiften/TLS/CertificateFactory.cpp23
-rw-r--r--Swiften/TLS/CertificateFactory.h8
-rw-r--r--Swiften/TLS/OpenSSL/OpenSSLCertificate.cpp16
-rw-r--r--Swiften/TLS/OpenSSL/OpenSSLCertificate.h2
-rw-r--r--Swiften/TLS/OpenSSL/OpenSSLCertificateFactory.cpp56
-rw-r--r--Swiften/TLS/OpenSSL/OpenSSLCertificateFactory.h10
-rw-r--r--Swiften/TLS/OpenSSL/OpenSSLContext.cpp716
-rw-r--r--Swiften/TLS/OpenSSL/OpenSSLContext.h74
-rw-r--r--Swiften/TLS/OpenSSL/OpenSSLContextFactory.cpp36
-rw-r--r--Swiften/TLS/OpenSSL/OpenSSLContextFactory.h14
-rw-r--r--Swiften/TLS/PrivateKey.cpp22
-rw-r--r--Swiften/TLS/PrivateKey.h33
-rw-r--r--Swiften/TLS/SConscript4
-rw-r--r--Swiften/TLS/Schannel/SchannelContext.cpp29
-rw-r--r--Swiften/TLS/Schannel/SchannelContextFactory.cpp8
-rw-r--r--Swiften/TLS/Schannel/SchannelContextFactory.h7
-rw-r--r--Swiften/TLS/SecureTransport/SecureTransportCertificate.mm9
-rw-r--r--Swiften/TLS/SecureTransport/SecureTransportContext.mm70
-rw-r--r--Swiften/TLS/SecureTransport/SecureTransportContextFactory.cpp10
-rw-r--r--Swiften/TLS/SecureTransport/SecureTransportContextFactory.h6
-rw-r--r--Swiften/TLS/ServerIdentityVerifier.cpp12
-rw-r--r--Swiften/TLS/ServerIdentityVerifier.h3
-rw-r--r--Swiften/TLS/TLSContext.cpp36
-rw-r--r--Swiften/TLS/TLSContext.h23
-rw-r--r--Swiften/TLS/TLSContextFactory.cpp7
-rw-r--r--Swiften/TLS/TLSContextFactory.h12
-rw-r--r--Swiften/TLS/TLSError.h18
-rw-r--r--Swiften/TLS/TLSOptions.h54
-rw-r--r--Swiften/TLS/UnitTest/CertificateTest.cpp111
-rw-r--r--Swiften/TLS/UnitTest/ClientServerTest.cpp883
-rw-r--r--Swiften/TLS/UnitTest/ServerIdentityVerifierTest.cpp52
32 files changed, 2187 insertions, 244 deletions
diff --git a/Swiften/TLS/CAPICertificate.cpp b/Swiften/TLS/CAPICertificate.cpp
index a46b9f6..526b535 100644
--- a/Swiften/TLS/CAPICertificate.cpp
+++ b/Swiften/TLS/CAPICertificate.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2016 Isode Limited.
+ * Copyright (c) 2012-2019 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -14,6 +14,7 @@
#include <Swiften/Base/Log.h>
#include <Swiften/Network/TimerFactory.h>
#include <Swiften/StringCodecs/Hexify.h>
+#include <Swiften/TLS/Schannel/SchannelUtil.h>
// Size of the SHA1 hash
#define SHA1_HASH_LEN 20
@@ -21,7 +22,7 @@
#define DEBUG_SCARD_STATUS(function, status) \
{ \
std::shared_ptr<boost::system::error_code> errorCode = std::make_shared<boost::system::error_code>(status, boost::system::system_category()); \
- SWIFT_LOG(debug) << std::hex << function << ": status: 0x" << status << ": " << errorCode->message() << std::endl; \
+ SWIFT_LOG(debug) << std::hex << function << ": status: 0x" << status << ": " << errorCode->message(); \
}
namespace Swift {
@@ -43,6 +44,7 @@ CAPICertificate::CAPICertificate(const std::string& capiUri, TimerFactory* timer
}
CAPICertificate::~CAPICertificate() {
+ SWIFT_LOG(debug) << "Destroying the CAPICertificate";
if (smartCardTimer_) {
smartCardTimer_->stop();
smartCardTimer_->onTick.disconnect(boost::bind(&CAPICertificate::handleSmartCardTimerTick, this));
@@ -50,7 +52,9 @@ CAPICertificate::~CAPICertificate() {
}
if (certStoreHandle_) {
- CertCloseStore(certStoreHandle_, 0);
+ if (CertCloseStore(certStoreHandle_, 0) == FALSE) {
+ SWIFT_LOG(debug) << "Failed to close the certificate store handle";
+ }
}
if (cardHandle_) {
@@ -80,7 +84,7 @@ const std::string& CAPICertificate::getSmartCardReaderName() const {
return smartCardReaderName_;
}
-PCCERT_CONTEXT findCertificateInStore (HCERTSTORE certStoreHandle, const std::string &certName) {
+PCCERT_CONTEXT findCertificateInStore(HCERTSTORE certStoreHandle, const std::string &certName) {
if (!boost::iequals(certName.substr(0, 5), "sha1:")) {
// Find client certificate. Note that this sample just searches for a
@@ -105,8 +109,7 @@ PCCERT_CONTEXT findCertificateInStore (HCERTSTORE certStoreHandle, const std::st
}
-
-void CAPICertificate::setUri (const std::string& capiUri) {
+void CAPICertificate::setUri(const std::string& capiUri) {
valid_ = false;
/* Syntax: "certstore:" <cert_store> ":" <hash> ":" <hash_of_cert> */
@@ -118,7 +121,7 @@ void CAPICertificate::setUri (const std::string& capiUri) {
/* Substring of subject: uses "storename" */
std::string capiIdentity = capiUri.substr(10);
std::string newCertStoreName;
- size_t pos = capiIdentity.find_first_of (':');
+ size_t pos = capiIdentity.find_first_of(':');
if (pos == std::string::npos) {
/* Using the default certificate store */
@@ -146,49 +149,38 @@ void CAPICertificate::setUri (const std::string& capiUri) {
certStore_ = newCertStoreName;
- PCCERT_CONTEXT certContext = findCertificateInStore (certStoreHandle_, certName_);
-
+ ScopedCertContext certContext(findCertificateInStore(certStoreHandle_, certName_));
if (!certContext) {
return;
}
-
/* Now verify that we can have access to the corresponding private key */
DWORD len;
- CRYPT_KEY_PROV_INFO *pinfo;
- HCRYPTPROV hprov;
- HCRYPTKEY key;
-
if (!CertGetCertificateContextProperty(certContext,
CERT_KEY_PROV_INFO_PROP_ID,
NULL,
&len)) {
- CertFreeCertificateContext(certContext);
+ SWIFT_LOG(error) << "Error while retrieving context properties";
return;
}
- pinfo = static_cast<CRYPT_KEY_PROV_INFO *>(malloc(len));
+ std::shared_ptr<CRYPT_KEY_PROV_INFO> pinfo(static_cast<CRYPT_KEY_PROV_INFO *>(malloc(len)), free);
if (!pinfo) {
- CertFreeCertificateContext(certContext);
return;
}
- if (!CertGetCertificateContextProperty(certContext, CERT_KEY_PROV_INFO_PROP_ID, pinfo, &len)) {
- CertFreeCertificateContext(certContext);
- free(pinfo);
+ if (!CertGetCertificateContextProperty(certContext, CERT_KEY_PROV_INFO_PROP_ID, pinfo.get(), &len)) {
return;
}
+ certContext.FreeContext();
- CertFreeCertificateContext(certContext);
-
+ HCRYPTPROV hprov;
// Now verify if we have access to the private key
if (!CryptAcquireContextW(&hprov, pinfo->pwszContainerName, pinfo->pwszProvName, pinfo->dwProvType, 0)) {
- free(pinfo);
return;
}
-
char smartCardReader[1024];
DWORD bufferLength = sizeof(smartCardReader);
if (!CryptGetProvParam(hprov, PP_SMARTCARD_READER, (BYTE *)&smartCardReader, &bufferLength, 0)) {
@@ -205,19 +197,19 @@ void CAPICertificate::setUri (const std::string& capiUri) {
smartCardTimer_ = timerFactory_->createTimer(SMARTCARD_EJECTION_CHECK_FREQUENCY_MILLISECONDS);
}
else {
- ///Need to handle an error here
+ CryptReleaseContext(hprov, 0);
+ return;
}
}
+ HCRYPTKEY key;
if (!CryptGetUserKey(hprov, pinfo->dwKeySpec, &key)) {
CryptReleaseContext(hprov, 0);
- free(pinfo);
return;
}
CryptDestroyKey(key);
CryptReleaseContext(hprov, 0);
- free(pinfo);
if (smartCardTimer_) {
smartCardTimer_->onTick.connect(boost::bind(&CAPICertificate::handleSmartCardTimerTick, this));
@@ -227,7 +219,7 @@ void CAPICertificate::setUri (const std::string& capiUri) {
valid_ = true;
}
-static void smartcard_check_status (SCARDCONTEXT hContext,
+static void smartcard_check_status(SCARDCONTEXT hContext,
const char* pReader,
SCARDHANDLE hCardHandle, /* Can be 0 on the first call */
SCARDHANDLE* newCardHandle, /* The handle returned */
@@ -288,37 +280,35 @@ static void smartcard_check_status (SCARDCONTEXT hContext,
}
}
-bool CAPICertificate::checkIfSmartCardPresent () {
+bool CAPICertificate::checkIfSmartCardPresent() {
if (!smartCardReaderName_.empty()) {
DWORD dwState;
smartcard_check_status(scardContext_, smartCardReaderName_.c_str(), cardHandle_, &cardHandle_, &dwState);
switch (dwState) {
case SCARD_ABSENT:
- SWIFT_LOG(debug) << "Card absent." << std::endl;
+ SWIFT_LOG(debug) << "Card absent.";
break;
case SCARD_PRESENT:
- SWIFT_LOG(debug) << "Card present." << std::endl;
+ SWIFT_LOG(debug) << "Card present.";
break;
case SCARD_SWALLOWED:
- SWIFT_LOG(debug) << "Card swallowed." << std::endl;
+ SWIFT_LOG(debug) << "Card swallowed.";
break;
case SCARD_POWERED:
- SWIFT_LOG(debug) << "Card has power." << std::endl;
+ SWIFT_LOG(debug) << "Card has power.";
break;
case SCARD_NEGOTIABLE:
- SWIFT_LOG(debug) << "Card reset and waiting PTS negotiation." << std::endl;
+ SWIFT_LOG(debug) << "Card reset and waiting PTS negotiation.";
break;
case SCARD_SPECIFIC:
- SWIFT_LOG(debug) << "Card has specific communication protocols set." << std::endl;
+ SWIFT_LOG(debug) << "Card has specific communication protocols set.";
break;
default:
- SWIFT_LOG(debug) << "Unknown or unexpected card state." << std::endl;
+ SWIFT_LOG(debug) << "Unknown or unexpected card state.";
break;
}
-
-
switch (dwState) {
case SCARD_ABSENT:
return false;
@@ -342,6 +332,7 @@ bool CAPICertificate::checkIfSmartCardPresent () {
void CAPICertificate::handleSmartCardTimerTick() {
bool poll = checkIfSmartCardPresent();
if (lastPollingResult_ && !poll) {
+ SWIFT_LOG(debug) << "CAPI Certificate detected that the certificate card was removed";
onCertificateCardRemoved();
}
lastPollingResult_ = poll;
diff --git a/Swiften/TLS/CertificateFactory.cpp b/Swiften/TLS/CertificateFactory.cpp
index 487f7cd..d4db3f4 100644
--- a/Swiften/TLS/CertificateFactory.cpp
+++ b/Swiften/TLS/CertificateFactory.cpp
@@ -1,14 +1,35 @@
/*
- * Copyright (c) 2010 Isode Limited.
+ * Copyright (c) 2010-2018 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#include <Swiften/TLS/CertificateFactory.h>
+#include <cassert>
+#include <memory>
+#include <sstream>
+#include <string>
+
+#include <boost/algorithm/string/predicate.hpp>
+#include <boost/optional.hpp>
+
+#include <Swiften/Base/Log.h>
+#include <Swiften/StringCodecs/Base64.h>
+#include <Swiften/TLS/PrivateKey.h>
+
namespace Swift {
CertificateFactory::~CertificateFactory() {
}
+std::vector<std::shared_ptr<Certificate>> CertificateFactory::createCertificateChain(const ByteArray& /* data */) {
+ assert(false);
+ return std::vector<std::shared_ptr<Certificate>>();
+}
+
+PrivateKey::ref CertificateFactory::createPrivateKey(const SafeByteArray& data, boost::optional<SafeByteArray> password) {
+ return std::make_shared<PrivateKey>(data, password);
+}
+
}
diff --git a/Swiften/TLS/CertificateFactory.h b/Swiften/TLS/CertificateFactory.h
index 522a6e6..873c36b 100644
--- a/Swiften/TLS/CertificateFactory.h
+++ b/Swiften/TLS/CertificateFactory.h
@@ -1,13 +1,17 @@
/*
- * Copyright (c) 2010-2015 Isode Limited.
+ * Copyright (c) 2010-2018 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#pragma once
+#include <boost/optional.hpp>
+
#include <Swiften/Base/API.h>
+#include <Swiften/Base/SafeByteArray.h>
#include <Swiften/TLS/Certificate.h>
+#include <Swiften/TLS/PrivateKey.h>
namespace Swift {
class SWIFTEN_API CertificateFactory {
@@ -15,5 +19,7 @@ namespace Swift {
virtual ~CertificateFactory();
virtual Certificate* createCertificateFromDER(const ByteArray& der) = 0;
+ virtual std::vector<std::shared_ptr<Certificate>> createCertificateChain(const ByteArray& data);
+ PrivateKey::ref createPrivateKey(const SafeByteArray& data, boost::optional<SafeByteArray> password = boost::optional<SafeByteArray>());
};
}
diff --git a/Swiften/TLS/OpenSSL/OpenSSLCertificate.cpp b/Swiften/TLS/OpenSSL/OpenSSLCertificate.cpp
index 17ac8cc..66b650d 100644
--- a/Swiften/TLS/OpenSSL/OpenSSLCertificate.cpp
+++ b/Swiften/TLS/OpenSSL/OpenSSLCertificate.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2016 Isode Limited.
+ * Copyright (c) 2010-2019 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -30,19 +30,27 @@ OpenSSLCertificate::OpenSSLCertificate(const ByteArray& der) {
#else
const unsigned char* p = vecptr(der);
#endif
- cert = std::shared_ptr<X509>(d2i_X509(NULL, &p, der.size()), X509_free);
+ cert = std::shared_ptr<X509>(d2i_X509(nullptr, &p, der.size()), X509_free);
if (!cert) {
- SWIFT_LOG(warning) << "Error creating certificate from DER data" << std::endl;
+// SWIFT_LOG(warning) << "Error creating certificate from DER data";
}
parse();
}
+void OpenSSLCertificate::incrementReferenceCount() const {
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+ X509_up_ref(cert.get());
+#else
+ CRYPTO_add(&(cert.get()->references), 1, CRYPTO_LOCK_EVP_PKEY);
+#endif
+}
+
ByteArray OpenSSLCertificate::toDER() const {
ByteArray result;
if (!cert) {
return result;
}
- result.resize(i2d_X509(cert.get(), NULL));
+ result.resize(i2d_X509(cert.get(), nullptr));
unsigned char* p = vecptr(result);
i2d_X509(cert.get(), &p);
return result;
diff --git a/Swiften/TLS/OpenSSL/OpenSSLCertificate.h b/Swiften/TLS/OpenSSL/OpenSSLCertificate.h
index 186caea..64da82a 100644
--- a/Swiften/TLS/OpenSSL/OpenSSLCertificate.h
+++ b/Swiften/TLS/OpenSSL/OpenSSLCertificate.h
@@ -45,6 +45,8 @@ namespace Swift {
return cert;
}
+ void incrementReferenceCount() const;
+
private:
void parse();
diff --git a/Swiften/TLS/OpenSSL/OpenSSLCertificateFactory.cpp b/Swiften/TLS/OpenSSL/OpenSSLCertificateFactory.cpp
new file mode 100644
index 0000000..73058a5
--- /dev/null
+++ b/Swiften/TLS/OpenSSL/OpenSSLCertificateFactory.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2018 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swiften/TLS/OpenSSL/OpenSSLCertificateFactory.h>
+
+#include <openssl/pem.h>
+#include <openssl/err.h>
+
+namespace Swift {
+
+OpenSSLCertificateFactory::OpenSSLCertificateFactory() {
+}
+
+OpenSSLCertificateFactory::~OpenSSLCertificateFactory() {
+}
+
+Certificate* OpenSSLCertificateFactory::createCertificateFromDER(const ByteArray& der) {
+ return new OpenSSLCertificate(der);
+}
+
+std::vector<std::shared_ptr<Certificate>> OpenSSLCertificateFactory::createCertificateChain(const ByteArray& data) {
+ std::vector<std::shared_ptr<Certificate>> certificateChain;
+
+ if (data.size() > std::numeric_limits<int>::max()) {
+ return certificateChain;
+ }
+
+ auto bio = std::shared_ptr<BIO>(BIO_new(BIO_s_mem()), BIO_free);
+ BIO_write(bio.get(), vecptr(data), int(data.size()));
+
+ // Attempt parsing data as PEM
+ X509* openSSLCert = nullptr;
+ auto x509certFromPEM = PEM_read_bio_X509(bio.get(), &openSSLCert, nullptr, nullptr);
+ if (x509certFromPEM && openSSLCert) {
+ std::shared_ptr<X509> x509Cert(openSSLCert, X509_free);
+ certificateChain.emplace_back(std::make_shared<OpenSSLCertificate>(x509Cert));
+ openSSLCert = nullptr;
+ while ((x509certFromPEM = PEM_read_bio_X509(bio.get(), &openSSLCert, nullptr, nullptr)) != nullptr) {
+ std::shared_ptr<X509> x509Cert(openSSLCert, X509_free);
+ certificateChain.emplace_back(std::make_shared<OpenSSLCertificate>(x509Cert));
+ openSSLCert = nullptr;
+ }
+ }
+
+ // Clear any (expected) errors which resulted from PEM parsing
+ // If we don't do this, any existing TLS context will detect these
+ // spurious errors and fail to work
+ ERR_clear_error();
+
+ return certificateChain;
+}
+
+}
diff --git a/Swiften/TLS/OpenSSL/OpenSSLCertificateFactory.h b/Swiften/TLS/OpenSSL/OpenSSLCertificateFactory.h
index c996cd5..a6974c8 100644
--- a/Swiften/TLS/OpenSSL/OpenSSLCertificateFactory.h
+++ b/Swiften/TLS/OpenSSL/OpenSSLCertificateFactory.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2014 Isode Limited.
+ * Copyright (c) 2010-2018 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -12,8 +12,10 @@
namespace Swift {
class OpenSSLCertificateFactory : public CertificateFactory {
public:
- virtual Certificate* createCertificateFromDER(const ByteArray& der) {
- return new OpenSSLCertificate(der);
- }
+ OpenSSLCertificateFactory();
+ virtual ~OpenSSLCertificateFactory() override final;
+
+ virtual Certificate* createCertificateFromDER(const ByteArray& der) override final;
+ virtual std::vector<std::shared_ptr<Certificate>> createCertificateChain(const ByteArray& data) override final;
};
}
diff --git a/Swiften/TLS/OpenSSL/OpenSSLContext.cpp b/Swiften/TLS/OpenSSL/OpenSSLContext.cpp
index cd6b6bc..86b0504 100644
--- a/Swiften/TLS/OpenSSL/OpenSSLContext.cpp
+++ b/Swiften/TLS/OpenSSL/OpenSSLContext.cpp
@@ -1,8 +1,9 @@
/*
- * Copyright (c) 2010-2016 Isode Limited.
+ * Copyright (c) 2010-2019 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
+
#include <Swiften/Base/Platform.h>
#ifdef SWIFTEN_PLATFORM_WINDOWS
@@ -10,15 +11,21 @@
#include <wincrypt.h>
#endif
+#include <cassert>
+#include <memory>
#include <vector>
+
+
+#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/pkcs12.h>
-#include <memory>
#if defined(SWIFTEN_PLATFORM_MACOSX)
#include <Security/Security.h>
#endif
+#include <Swiften/Base/Log.h>
+#include <Swiften/Base/Algorithm.h>
#include <Swiften/TLS/OpenSSL/OpenSSLContext.h>
#include <Swiften/TLS/OpenSSL/OpenSSLCertificate.h>
#include <Swiften/TLS/CertificateWithKey.h>
@@ -35,14 +42,78 @@ namespace Swift {
static const int MAX_FINISHED_SIZE = 4096;
static const int SSL_READ_BUFFERSIZE = 8192;
+#define SSL_DEFAULT_VERIFY_DEPTH 5
+
+// Callback function declarations for certificate verification
+extern "C" {
+ static int certVerifyCallback(X509_STORE_CTX *store_ctx, void*);
+ static int verifyCallback(int preverify_ok, X509_STORE_CTX *ctx);
+}
+
static void freeX509Stack(STACK_OF(X509)* stack) {
sk_X509_free(stack);
}
-OpenSSLContext::OpenSSLContext() : state_(Start), context_(0), handle_(0), readBIO_(0), writeBIO_(0) {
+namespace {
+ class OpenSSLInitializerFinalizer {
+ public:
+ OpenSSLInitializerFinalizer() {
+ SSL_load_error_strings();
+ SSL_library_init();
+ OpenSSL_add_all_algorithms();
+
+ // Disable compression
+ /*
+ STACK_OF(SSL_COMP)* compressionMethods = SSL_COMP_get_compression_methods();
+ sk_SSL_COMP_zero(compressionMethods);*/
+ }
+
+ ~OpenSSLInitializerFinalizer() {
+ EVP_cleanup();
+ }
+
+ OpenSSLInitializerFinalizer(const OpenSSLInitializerFinalizer &) = delete;
+ };
+
+ std::unique_ptr<SSL_CTX> createSSL_CTX(OpenSSLContext::Mode mode) {
+ std::unique_ptr<SSL_CTX> sslCtx;
+ switch (mode) {
+ case OpenSSLContext::Mode::Client:
+ sslCtx = std::unique_ptr<SSL_CTX>(SSL_CTX_new(SSLv23_client_method()));
+ break;
+ case OpenSSLContext::Mode::Server:
+ sslCtx = std::unique_ptr<SSL_CTX>(SSL_CTX_new(SSLv23_server_method()));
+ break;
+ }
+ return sslCtx;
+ }
+
+ std::string openSSLInternalErrorToString() {
+ auto bio = std::shared_ptr<BIO>(BIO_new(BIO_s_mem()), BIO_free);
+ ERR_print_errors(bio.get());
+ std::string errorString;
+ errorString.resize(BIO_pending(bio.get()));
+ BIO_read(bio.get(), (void*)errorString.data(), errorString.size());
+ return errorString;
+ }
+ }
+
+OpenSSLContext::OpenSSLContext(const TLSOptions& options, Mode mode) : mode_(mode), state_(State::Start) {
ensureLibraryInitialized();
- context_ = SSL_CTX_new(SSLv23_client_method());
- SSL_CTX_set_options(context_, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
+ context_ = createSSL_CTX(mode_);
+ SSL_CTX_set_options(context_.get(), SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
+
+ if (mode_ == Mode::Server) {
+#if OPENSSL_VERSION_NUMBER < 0x1010
+ // Automatically select highest preference curve used for ECDH temporary keys used during
+ // key exchange if possible.
+ // Since version 1.1.0, this option is always enabled.
+ SSL_CTX_set_ecdh_auto(context_.get(), 1);
+#endif
+
+ SSL_CTX_set_tlsext_servername_arg(context_.get(), this);
+ SSL_CTX_set_tlsext_servername_callback(context_.get(), OpenSSLContext::handleServerNameCallback);
+ }
// TODO: implement CRL checking
// TODO: download CRL (HTTP transport)
@@ -50,105 +121,420 @@ OpenSSLContext::OpenSSLContext() : state_(Start), context_(0), handle_(0), readB
// TODO: implement OCSP support
// TODO: handle OCSP stapling see https://www.rfc-editor.org/rfc/rfc4366.txt
- // Load system certs
+
+ // Default for ignoreSystemTrustAnchors is false, i.e. load System TAs by default,
+ // to preserve previous behaviour
+ if (!options.ignoreSystemTrustAnchors) {
+ // Load system certs
#if defined(SWIFTEN_PLATFORM_WINDOWS)
- X509_STORE* store = SSL_CTX_get_cert_store(context_);
- HCERTSTORE systemStore = CertOpenSystemStore(0, "ROOT");
- if (systemStore) {
- PCCERT_CONTEXT certContext = NULL;
- while (true) {
- certContext = CertFindCertificateInStore(systemStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, certContext);
- if (!certContext) {
- break;
- }
- OpenSSLCertificate cert(createByteArray(certContext->pbCertEncoded, certContext->cbCertEncoded));
- if (store && cert.getInternalX509()) {
- X509_STORE_add_cert(store, cert.getInternalX509().get());
+ X509_STORE* store = SSL_CTX_get_cert_store(context_.get());
+ HCERTSTORE systemStore = CertOpenSystemStore(0, "ROOT");
+ if (systemStore) {
+ PCCERT_CONTEXT certContext = nullptr;
+ while (true) {
+ certContext = CertFindCertificateInStore(systemStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_ANY, nullptr, certContext);
+ if (!certContext) {
+ break;
+ }
+ OpenSSLCertificate cert(createByteArray(certContext->pbCertEncoded, certContext->cbCertEncoded));
+ if (store && cert.getInternalX509()) {
+ X509_STORE_add_cert(store, cert.getInternalX509().get());
+ }
}
}
- }
#elif !defined(SWIFTEN_PLATFORM_MACOSX)
- SSL_CTX_set_default_verify_paths(context_);
+ SSL_CTX_set_default_verify_paths(context_.get());
#elif defined(SWIFTEN_PLATFORM_MACOSX) && !defined(SWIFTEN_PLATFORM_IPHONE)
- // On Mac OS X 10.5 (OpenSSL < 0.9.8), OpenSSL does not automatically look in the system store.
- // On Mac OS X 10.6 (OpenSSL >= 0.9.8), OpenSSL *does* look in the system store to determine trust.
- // However, if there is a certificate error, it will always emit the "Invalid CA" error if we didn't add
- // the certificates first. See
- // http://opensource.apple.com/source/OpenSSL098/OpenSSL098-27/src/crypto/x509/x509_vfy_apple.c
- // to understand why. We therefore add all certs from the system store ourselves.
- X509_STORE* store = SSL_CTX_get_cert_store(context_);
- CFArrayRef anchorCertificates;
- if (SecTrustCopyAnchorCertificates(&anchorCertificates) == 0) {
- for (int i = 0; i < CFArrayGetCount(anchorCertificates); ++i) {
- SecCertificateRef cert = reinterpret_cast<SecCertificateRef>(const_cast<void*>(CFArrayGetValueAtIndex(anchorCertificates, i)));
- CSSM_DATA certCSSMData;
- if (SecCertificateGetData(cert, &certCSSMData) != 0 || certCSSMData.Length == 0) {
- continue;
- }
- std::vector<unsigned char> certData;
- certData.resize(certCSSMData.Length);
- memcpy(&certData[0], certCSSMData.Data, certCSSMData.Length);
- OpenSSLCertificate certificate(certData);
- if (store && certificate.getInternalX509()) {
- X509_STORE_add_cert(store, certificate.getInternalX509().get());
+ // On Mac OS X 10.5 (OpenSSL < 0.9.8), OpenSSL does not automatically look in the system store.
+ // On Mac OS X 10.6 (OpenSSL >= 0.9.8), OpenSSL *does* look in the system store to determine trust.
+ // However, if there is a certificate error, it will always emit the "Invalid CA" error if we didn't add
+ // the certificates first. See
+ // http://opensource.apple.com/source/OpenSSL098/OpenSSL098-27/src/crypto/x509/x509_vfy_apple.c
+ // to understand why. We therefore add all certs from the system store ourselves.
+ X509_STORE* store = SSL_CTX_get_cert_store(context_.get());
+ CFArrayRef anchorCertificates;
+ if (SecTrustCopyAnchorCertificates(&anchorCertificates) == 0) {
+ for (int i = 0; i < CFArrayGetCount(anchorCertificates); ++i) {
+ SecCertificateRef cert = reinterpret_cast<SecCertificateRef>(const_cast<void*>(CFArrayGetValueAtIndex(anchorCertificates, i)));
+ CSSM_DATA certCSSMData;
+ if (SecCertificateGetData(cert, &certCSSMData) != 0 || certCSSMData.Length == 0) {
+ continue;
+ }
+ std::vector<unsigned char> certData;
+ certData.resize(certCSSMData.Length);
+ memcpy(&certData[0], certCSSMData.Data, certCSSMData.Length);
+ OpenSSLCertificate certificate(certData);
+ if (store && certificate.getInternalX509()) {
+ X509_STORE_add_cert(store, certificate.getInternalX509().get());
+ }
}
+ CFRelease(anchorCertificates);
}
- CFRelease(anchorCertificates);
- }
#endif
+ }
+ configure(options);
}
OpenSSLContext::~OpenSSLContext() {
- SSL_free(handle_);
- SSL_CTX_free(context_);
}
void OpenSSLContext::ensureLibraryInitialized() {
- static bool isLibraryInitialized = false;
- if (!isLibraryInitialized) {
- SSL_load_error_strings();
- SSL_library_init();
- OpenSSL_add_all_algorithms();
+ static OpenSSLInitializerFinalizer openSSLInit;
+}
+
+void OpenSSLContext::initAndSetBIOs() {
+ // Ownership of BIOs is transferred
+ readBIO_ = BIO_new(BIO_s_mem());
+ writeBIO_ = BIO_new(BIO_s_mem());
+ SSL_set_bio(handle_.get(), readBIO_, writeBIO_);
+}
+
+// This callback is called by OpenSSL when a client certificate needs to be verified.
+// In turn, this calls the verification callback which the user
+// of this OpenSSLContext has configured (if any).
+static int certVerifyCallback(X509_STORE_CTX* store_ctx, void* arg)
+{
+ OpenSSLContext* context = static_cast<OpenSSLContext *>(arg);
+
+ // Need to stash store_ctx pointer for use within verification
+ context->setX509StoreContext(store_ctx);
+
+ int ret;
+
+ // This callback shouldn't have been set up if the context doesn't
+ // have a verifyCertCallback set, but it doesn't hurt to double check
+ std::function<int (const TLSContext *)> cb = context->getVerifyCertCallback();
+ if (cb != nullptr) {
+ ret = cb(static_cast<const OpenSSLContext*>(context));
+ } else {
+ SWIFT_LOG(debug) << "certVerifyCallback called but context.verifyCertCallback is unset";
+ ret = 0;
+ }
+
+ context->setX509StoreContext(nullptr);
+ return ret;
+}
+
+// Convenience function to generate a text representation
+// of an X509 Name. This information is only used for logging.
+static std::string X509_NAME_to_text(X509_NAME* name)
+{
+ std::string nameString;
+
+ if (!name) {
+ return nameString;
+ }
+
+ std::unique_ptr<BIO, decltype(&BIO_free)> io(BIO_new(BIO_s_mem()), &BIO_free);
+ int r = X509_NAME_print_ex(io.get(), name, 0, XN_FLAG_RFC2253);
+ BIO_write(io.get(), "\0", 1);
+
+ if (r > 0) {
+ BUF_MEM* ptr = nullptr;
+ BIO_get_mem_ptr(io.get(), &ptr);
+ nameString = ptr->data;
+ }
+
+ return nameString;
+}
+
+// Check depth of certificate chain
+static int verifyCallback(int preverifyOk, X509_STORE_CTX* ctx)
+{
+ // Retrieve the pointer to the SSL of the connection currently treated
+ // and the application specific data stored into the SSL object.
+
+ int err = X509_STORE_CTX_get_error(ctx);
+ int depth = X509_STORE_CTX_get_error_depth(ctx);
+
+ SSL* ssl = static_cast<SSL*>(X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()));
+ SSL_CTX* sslctx = ssl ? SSL_get_SSL_CTX(ssl) : nullptr;
+ if (!sslctx) {
+ SWIFT_LOG(debug) << "verifyCallback: internal error";
+ return preverifyOk;
+ }
+
+ if (SSL_CTX_get_verify_mode(sslctx) == SSL_VERIFY_NONE) {
+ SWIFT_LOG(debug) << "verifyCallback: no verification required";
+ // No verification requested
+ return 1;
+ }
+
+ X509* errCert = X509_STORE_CTX_get_current_cert(ctx);
+ std::string subjectString;
+ if (errCert) {
+ X509_NAME* subjectName = X509_get_subject_name(errCert);
+ subjectString = X509_NAME_to_text(subjectName);
+ }
+
+ // Catch a too long certificate chain. The depth limit set using
+ // SSL_CTX_set_verify_depth() is by purpose set to "limit+1" so
+ // that whenever the "depth>verify_depth" condition is met, we
+ // have violated the limit and want to log this error condition.
+ // We must do it here, because the CHAIN_TOO_LONG error would not
+ // be found explicitly; only errors introduced by cutting off the
+ // additional certificates would be logged.
+ if (depth >= SSL_CTX_get_verify_depth(sslctx)) {
+ preverifyOk = 0;
+ err = X509_V_ERR_CERT_CHAIN_TOO_LONG;
+ X509_STORE_CTX_set_error(ctx, err);
+ }
+
+ if (!preverifyOk) {
+ std::string issuerString;
+ if (errCert) {
+ X509_NAME* issuerName = X509_get_issuer_name(errCert);
+ issuerString = X509_NAME_to_text(issuerName);
+ }
+ SWIFT_LOG(debug) << "verifyCallback: verification error " <<
+ X509_verify_cert_error_string(err) << " depth: " <<
+ depth << " issuer: " << ((issuerString.length() > 0) ? issuerString : "<unknown>");
+ } else {
+ SWIFT_LOG(debug) << "verifyCallback: SSL depth: " << depth << " Subject: " <<
+ ((subjectString.length() > 0) ? subjectString : "<>");
+ }
+ // Always return "OK", as check on verification status
+ // will be performed once TLS handshake has completed,
+ // by calling OpenSSLContext::getVerificationErrorTypeForResult() to
+ // get the value set via X509_STORE_CTX_set_error() above.
+ return 1;
+}
+
+bool OpenSSLContext::configure(const TLSOptions &options)
+{
+ if (options.cipherSuites) {
+ std::string cipherSuites = *(options.cipherSuites);
+ if (SSL_CTX_set_cipher_list(context_.get(), cipherSuites.c_str()) != 1 ) {
+ SWIFT_LOG(debug) << "Failed to set cipher-suites";
+ return false;
+ }
+ }
+
+ if (options.context) {
+ const auto& contextId = *options.context;
+
+ if (SSL_CTX_set_session_id_context(context_.get(),
+ reinterpret_cast<const unsigned char *>(contextId.c_str()),
+ contextId.length()) != 1) {
+ SWIFT_LOG(debug) << "Failed to set context-id";
+ return false;
+ }
+ }
+
+ if (options.sessionCacheTimeout) {
+ int scto = *options.sessionCacheTimeout;
+ if (scto <= 0) {
+ SWIFT_LOG(debug) << "Invalid value for session-cache-timeout";
+ return false;
+ }
+ (void)SSL_CTX_set_timeout(context_.get(), scto);
+ if (SSL_CTX_get_timeout(context_.get()) != scto) {
+ SWIFT_LOG(debug) << "Failed to set session-cache-timeout";
+ return false;
+ }
+ }
+
+ if (options.verifyCertificateCallback) {
+ verifyCertCallback = *options.verifyCertificateCallback;
+ } else {
+ verifyCertCallback = nullptr;
+ }
+
+ if (options.verifyMode) {
+ TLSOptions::VerifyMode verify_mode = *options.verifyMode;
+ int mode;
+ switch (verify_mode) {
+ case TLSOptions::VerifyMode::None:
+ mode = SSL_VERIFY_NONE;
+ break;
+ case TLSOptions::VerifyMode::Required:
+ mode = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT | SSL_VERIFY_CLIENT_ONCE;
+ break;
+ case TLSOptions::VerifyMode::Optional:
+ mode = SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE;
+ break;
+ }
+
+ // Set up default certificate chain verification depth - may be overridden below
+ SSL_CTX_set_verify_depth(context_.get(), SSL_DEFAULT_VERIFY_DEPTH + 1);
+
+ // Set callbacks up
+ SSL_CTX_set_verify(context_.get(), mode, verifyCallback);
+
+ // Only set up certificate verification callback if a user callback has
+ // been configured via the TLSOptions
+ if (verifyCertCallback != nullptr) {
+ SSL_CTX_set_cert_verify_callback(context_.get(), certVerifyCallback, this);
+ }
+ }
+
+ if (options.verifyDepth) {
+ int depth = *options.verifyDepth;
+ if (depth <= 0) {
+ SWIFT_LOG(debug) << "Invalid value for verify-depth";
+ return false;
+ }
+
+ // Increase depth limit by one, so that verifyCallback() will log it
+ SSL_CTX_set_verify_depth(context_.get(), depth + 1);
+ }
+
+ auto updateOptionIfPresent = [this](boost::optional<bool> option, int flag) {
+ if (option) {
+ if (*option) {
+ SSL_CTX_set_options(context_.get(), flag);
+ }
+ else {
+ SSL_CTX_clear_options(context_.get(), flag);
+ }
+ }
+ };
+ updateOptionIfPresent(options.workaroundMicrosoftSessID, SSL_OP_MICROSOFT_SESS_ID_BUG);
+ updateOptionIfPresent(options.workaroundNetscapeChallenge, SSL_OP_NETSCAPE_CHALLENGE_BUG);
+ updateOptionIfPresent(options.workaroundNetscapeReuseCipherChange, SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG);
+ updateOptionIfPresent(options.workaroundSSLRef2ReuseCertType, SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG);
+ updateOptionIfPresent(options.workaroundMicrosoftBigSSLv3Buffer, SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER);
+ updateOptionIfPresent(options.workaroundSSLeay080ClientDH, SSL_OP_SSLEAY_080_CLIENT_DH_BUG);
+ updateOptionIfPresent(options.workaroundTLSD5, SSL_OP_TLS_D5_BUG);
+ updateOptionIfPresent(options.workaroundTLSBlockPadding, SSL_OP_TLS_BLOCK_PADDING_BUG);
+ updateOptionIfPresent(options.workaroundDontInsertEmptyFragments, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS);
+ updateOptionIfPresent(options.workaroundAll, SSL_OP_ALL);
+ updateOptionIfPresent(options.suppressSSLv2, SSL_OP_NO_SSLv2);
+ updateOptionIfPresent(options.suppressSSLv3, SSL_OP_NO_SSLv3);
+ updateOptionIfPresent(options.suppressTLSv1, SSL_OP_NO_TLSv1);
+ updateOptionIfPresent(options.disableTLSRollBackBug, SSL_OP_TLS_ROLLBACK_BUG);
+ updateOptionIfPresent(options.singleDHUse, SSL_OP_SINGLE_DH_USE);
+
+ if (options.trustAnchors) {
+ // Add any additional Trust Anchors which are present in the TLSOptions
+ X509_STORE* store = SSL_CTX_get_cert_store(context_.get());
+
+ if (store) {
+ for (auto& certificate : *options.trustAnchors) {
+ auto openSSLCert = dynamic_cast<OpenSSLCertificate*>(certificate.get());
+ if (openSSLCert && openSSLCert->getInternalX509()) {
+ X509_STORE_add_cert(store, openSSLCert->getInternalX509().get());
+ // Don't need to increment reference count as X509_STORE_add_cert does thiS
+ }
+ }
+ }
+ }
+
+ return true;
+}
- // Disable compression
- /*
- STACK_OF(SSL_COMP)* compressionMethods = SSL_COMP_get_compression_methods();
- sk_SSL_COMP_zero(compressionMethods);*/
- isLibraryInitialized = true;
+void OpenSSLContext::accept() {
+ assert(mode_ == Mode::Server);
+ handle_ = std::unique_ptr<SSL>(SSL_new(context_.get()));
+ if (!handle_) {
+ state_ = State::Error;
+ onError(std::make_shared<TLSError>(TLSError::AcceptFailed, openSSLInternalErrorToString()));
+ return;
}
+
+ initAndSetBIOs();
+
+ state_ = State::Accepting;
+ doAccept();
}
void OpenSSLContext::connect() {
- handle_ = SSL_new(context_);
- // Ownership of BIOs is ransferred
- readBIO_ = BIO_new(BIO_s_mem());
- writeBIO_ = BIO_new(BIO_s_mem());
- SSL_set_bio(handle_, readBIO_, writeBIO_);
+ connect(std::string());
+}
+
+void OpenSSLContext::connect(const std::string& requestedServerName) {
+ assert(mode_ == Mode::Client);
+ handle_ = std::unique_ptr<SSL>(SSL_new(context_.get()));
+ if (!handle_) {
+ state_ = State::Error;
+ onError(std::make_shared<TLSError>(TLSError::ConnectFailed, openSSLInternalErrorToString()));
+ return;
+ }
- state_ = Connecting;
+ if (!requestedServerName.empty()) {
+ if (SSL_set_tlsext_host_name(handle_.get(), const_cast<char*>(requestedServerName.c_str())) != 1) {
+ onError(std::make_shared<TLSError>(TLSError::ConnectFailed, "Failed to set Server Name Indication: " + openSSLInternalErrorToString()));\
+ return;
+ }
+ }
+
+ // Ownership of BIOs is transferred to the SSL_CTX instance in handle_.
+ initAndSetBIOs();
+
+ state_ = State::Connecting;
doConnect();
}
+void OpenSSLContext::doAccept() {
+ auto acceptResult = SSL_accept(handle_.get());
+ auto error = SSL_get_error(handle_.get(), acceptResult);
+ switch (error) {
+ case SSL_ERROR_NONE: {
+ state_ = State::Connected;
+ //std::cout << x->name << std::endl;
+ //const char* comp = SSL_get_current_compression(handle_.get());
+ //std::cout << "Compression: " << SSL_COMP_get_name(comp) << std::endl;
+ onConnected();
+ // The following call is important so the client knowns the handshake is finished.
+ sendPendingDataToNetwork();
+ sendPendingDataToApplication();
+ break;
+ }
+ case SSL_ERROR_WANT_READ:
+ sendPendingDataToNetwork();
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ sendPendingDataToNetwork();
+ break;
+ default:
+ state_ = State::Error;
+ onError(std::make_shared<TLSError>(TLSError::AcceptFailed, openSSLInternalErrorToString()));
+ sendPendingDataToNetwork();
+ }
+}
+
void OpenSSLContext::doConnect() {
- int connectResult = SSL_connect(handle_);
- int error = SSL_get_error(handle_, connectResult);
+ int connectResult = SSL_connect(handle_.get());
+ int error = SSL_get_error(handle_.get(), connectResult);
switch (error) {
case SSL_ERROR_NONE: {
- state_ = Connected;
+ state_ = State::Connected;
//std::cout << x->name << std::endl;
- //const char* comp = SSL_get_current_compression(handle_);
+ //const char* comp = SSL_get_current_compression(handle_.get());
//std::cout << "Compression: " << SSL_COMP_get_name(comp) << std::endl;
onConnected();
+ // The following is needed since OpenSSL 1.1.1 for the server to be able to calculate the
+ // TLS finish message.
+ sendPendingDataToNetwork();
break;
}
case SSL_ERROR_WANT_READ:
sendPendingDataToNetwork();
break;
default:
- state_ = Error;
+ state_ = State::Error;
onError(std::make_shared<TLSError>());
+ onError(std::make_shared<TLSError>(TLSError::ConnectFailed, openSSLInternalErrorToString()));
+ }
+}
+
+int OpenSSLContext::handleServerNameCallback(SSL* ssl, int*, void* arg) {
+ if (ssl == nullptr)
+ return SSL_TLSEXT_ERR_NOACK;
+
+ const char* servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
+ if (servername) {
+ auto serverNameString = std::string(servername);
+ auto context = reinterpret_cast<OpenSSLContext*>(arg);
+ context->onServerNameRequested(serverNameString);
+
+ if (context->abortTLSHandshake_) {
+ context->abortTLSHandshake_ = false;
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+ }
}
+ return SSL_TLSEXT_ERR_OK;
}
void OpenSSLContext::sendPendingDataToNetwork() {
@@ -164,41 +550,146 @@ void OpenSSLContext::sendPendingDataToNetwork() {
void OpenSSLContext::handleDataFromNetwork(const SafeByteArray& data) {
BIO_write(readBIO_, vecptr(data), data.size());
switch (state_) {
- case Connecting:
+ case State::Accepting:
+ doAccept();
+ break;
+ case State::Connecting:
doConnect();
break;
- case Connected:
+ case State::Connected:
sendPendingDataToApplication();
break;
- case Start: assert(false); break;
- case Error: /*assert(false);*/ break;
+ case State::Start: assert(false); break;
+ case State::Error: /*assert(false);*/ break;
}
}
void OpenSSLContext::handleDataFromApplication(const SafeByteArray& data) {
- if (SSL_write(handle_, vecptr(data), data.size()) >= 0) {
- sendPendingDataToNetwork();
+ auto ret = SSL_write(handle_.get(), vecptr(data), data.size());
+ if (ret > 0 || SSL_get_error(handle_.get(), ret) == SSL_ERROR_WANT_READ) {
+ sendPendingDataToNetwork();
}
else {
- state_ = Error;
- onError(std::make_shared<TLSError>());
+ state_ = State::Error;
+ onError(std::make_shared<TLSError>(TLSError::UnknownError, openSSLInternalErrorToString()));
}
}
void OpenSSLContext::sendPendingDataToApplication() {
SafeByteArray data;
data.resize(SSL_READ_BUFFERSIZE);
- int ret = SSL_read(handle_, vecptr(data), data.size());
+ int ret = SSL_read(handle_.get(), vecptr(data), data.size());
while (ret > 0) {
data.resize(ret);
onDataForApplication(data);
data.resize(SSL_READ_BUFFERSIZE);
- ret = SSL_read(handle_, vecptr(data), data.size());
+ ret = SSL_read(handle_.get(), vecptr(data), data.size());
+ }
+ if (ret < 0 && SSL_get_error(handle_.get(), ret) != SSL_ERROR_WANT_READ) {
+ state_ = State::Error;
+ onError(std::make_shared<TLSError>(TLSError::UnknownError, openSSLInternalErrorToString()));
+ }
+}
+
+bool OpenSSLContext::setCertificateChain(const std::vector<std::shared_ptr<Certificate>>& certificateChain) {
+ if (certificateChain.size() == 0) {
+ SWIFT_LOG(debug) << "Trying to load empty certificate chain.";
+ return false;
+ }
+
+ // load endpoint certificate
+ auto openSSLCert = dynamic_cast<OpenSSLCertificate*>(certificateChain[0].get());
+ if (!openSSLCert) {
+ return false;
+ }
+
+ // This increments the reference count on the X509 certificate automatically
+ if (SSL_CTX_use_certificate(context_.get(), openSSLCert->getInternalX509().get()) != 1) {
+ return false;
}
- if (ret < 0 && SSL_get_error(handle_, ret) != SSL_ERROR_WANT_READ) {
- state_ = Error;
- onError(std::make_shared<TLSError>());
+
+ if (certificateChain.size() > 1) {
+ for (auto certificate = certificateChain.begin() + 1; certificate != certificateChain.end(); ++certificate) {
+ auto openSSLCert = dynamic_cast<OpenSSLCertificate*>(certificate->get());
+ if (!openSSLCert) {
+ return false;
+ }
+
+ if (SSL_CTX_add_extra_chain_cert(context_.get(), openSSLCert->getInternalX509().get()) != 1) {
+ SWIFT_LOG(debug) << "Trying to load empty certificate chain.";
+ return false;
+ }
+ // Have to manually increment reference count as SSL_CTX_add_extra_chain_cert does not do so
+ openSSLCert->incrementReferenceCount();
+ }
}
+
+ if (handle_) {
+ // This workaround is needed as OpenSSL has a shortcut to not do anything
+ // if you set the SSL_CTX to the existing SSL_CTX and not reloading the
+ // certificates from the SSL_CTX.
+ auto dummyContext = createSSL_CTX(mode_);
+ SSL_set_SSL_CTX(handle_.get(), dummyContext.get());
+ SSL_set_SSL_CTX(handle_.get(), context_.get());
+ }
+
+ return true;
+}
+
+int empty_or_preset_password_cb(char* buf, int max_len, int flag, void* password);
+
+int empty_or_preset_password_cb(char* buf, int max_len, int /* flag */, void* password) {
+ char* charPassword = (char*)password;
+ if (charPassword == nullptr) {
+ return 0;
+ }
+ int len = strlen(charPassword);
+ if(len > max_len) {
+ return 0;
+ }
+ memcpy(buf, charPassword, len);
+ return len;
+}
+
+bool OpenSSLContext::setPrivateKey(const PrivateKey::ref& privateKey) {
+ if (privateKey->getData().size() > std::numeric_limits<int>::max()) {
+ return false;
+ }
+
+ auto bio = std::shared_ptr<BIO>(BIO_new(BIO_s_mem()), BIO_free);
+ BIO_write(bio.get(), vecptr(privateKey->getData()), int(privateKey->getData().size()));
+
+ SafeByteArray safePassword;
+ void* password = nullptr;
+ if (privateKey->getPassword()) {
+ safePassword = privateKey->getPassword().get();
+ safePassword.push_back(0);
+ password = safePassword.data();
+ }
+ // Make sure resultKey is tidied up by wrapping it in a shared_ptr
+ auto resultKey = std::shared_ptr<EVP_PKEY>(PEM_read_bio_PrivateKey(bio.get(), nullptr, empty_or_preset_password_cb, password), EVP_PKEY_free);
+ if (resultKey) {
+ if (handle_) {
+ auto result = SSL_use_PrivateKey(handle_.get(), resultKey.get());
+ if (result != 1) {
+ return false;
+ }
+ }
+ else {
+ auto result = SSL_CTX_use_PrivateKey(context_.get(), resultKey.get());
+ if (result != 1) {
+ return false;
+ }
+ }
+ }
+ else {
+ return false;
+ }
+ return true;
+}
+
+void OpenSSLContext::setAbortTLSHandshake(bool abort) {
+ abortTLSHandshake_ = abort;
}
bool OpenSSLContext::setClientCertificate(CertificateWithKey::ref certificate) {
@@ -210,16 +701,16 @@ bool OpenSSLContext::setClientCertificate(CertificateWithKey::ref certificate) {
// Create a PKCS12 structure
BIO* bio = BIO_new(BIO_s_mem());
BIO_write(bio, vecptr(pkcs12Certificate->getData()), pkcs12Certificate->getData().size());
- std::shared_ptr<PKCS12> pkcs12(d2i_PKCS12_bio(bio, NULL), PKCS12_free);
+ std::shared_ptr<PKCS12> pkcs12(d2i_PKCS12_bio(bio, nullptr), PKCS12_free);
BIO_free(bio);
if (!pkcs12) {
return false;
}
// Parse PKCS12
- X509 *certPtr = 0;
- EVP_PKEY* privateKeyPtr = 0;
- STACK_OF(X509)* caCertsPtr = 0;
+ X509 *certPtr = nullptr;
+ EVP_PKEY* privateKeyPtr = nullptr;
+ STACK_OF(X509)* caCertsPtr = nullptr;
SafeByteArray password(pkcs12Certificate->getPassword());
password.push_back(0);
int result = PKCS12_parse(pkcs12.get(), reinterpret_cast<const char*>(vecptr(password)), &privateKeyPtr, &certPtr, &caCertsPtr);
@@ -231,32 +722,71 @@ bool OpenSSLContext::setClientCertificate(CertificateWithKey::ref certificate) {
std::shared_ptr<STACK_OF(X509)> caCerts(caCertsPtr, freeX509Stack);
// Use the key & certificates
- if (SSL_CTX_use_certificate(context_, cert.get()) != 1) {
+ if (SSL_CTX_use_certificate(context_.get(), cert.get()) != 1) {
return false;
}
- if (SSL_CTX_use_PrivateKey(context_, privateKey.get()) != 1) {
+ if (SSL_CTX_use_PrivateKey(context_.get(), privateKey.get()) != 1) {
return false;
}
for (int i = 0; i < sk_X509_num(caCerts.get()); ++i) {
- SSL_CTX_add_extra_chain_cert(context_, sk_X509_value(caCerts.get(), i));
+ SSL_CTX_add_extra_chain_cert(context_.get(), sk_X509_value(caCerts.get(), i));
}
return true;
}
+bool OpenSSLContext::setDiffieHellmanParameters(const ByteArray& parametersInOpenSslDer) {
+ auto bio = std::unique_ptr<BIO, decltype(&BIO_free)>(BIO_new(BIO_s_mem()), BIO_free);
+ if (bio) {
+ BIO_write(bio.get(), vecptr(parametersInOpenSslDer), parametersInOpenSslDer.size());
+ auto result = 0L;
+ if (auto dhparams = d2i_DHparams_bio(bio.get(), nullptr)) {
+ if (handle_) {
+ result = SSL_set_tmp_dh(handle_.get(), dhparams);
+ }
+ else {
+ result = SSL_CTX_set_tmp_dh(context_.get(), dhparams);
+ }
+ DH_free(dhparams);
+ }
+ return result == 1;
+ }
+ return false;
+}
+
std::vector<Certificate::ref> OpenSSLContext::getPeerCertificateChain() const {
std::vector<Certificate::ref> result;
- STACK_OF(X509)* chain = SSL_get_peer_cert_chain(handle_);
+
+ // When this context is a server, the peer (client) certificate
+ // is obtained via SSL_get_peer_certificate, and any other
+ // certificates set by the peer are available via SSL_get_peer_cert_chain.
+ // When this context is a client, all of the server's certificates are
+ // obtained using SSL_get_peer_cert_chain
+ if (mode_ == Mode::Server) {
+ auto cert = SSL_get_peer_certificate(handle_.get());
+ if (cert) {
+ // Do not need to copy the returned cert as SSL_get_peer_certificate
+ // increments the reference count on the certificate
+ std::shared_ptr<X509> x509Cert(cert, X509_free);
+ Certificate::ref cert = std::make_shared<OpenSSLCertificate>(x509Cert);
+ result.push_back(cert);
+ }
+ }
+
+ STACK_OF(X509)* chain = SSL_get_peer_cert_chain(handle_.get());
for (int i = 0; i < sk_X509_num(chain); ++i) {
+ // Here we do need to copy the returned cert, since SSL_get_peer_cert_chain
+ // does not increment the reference count on each certificate
std::shared_ptr<X509> x509Cert(X509_dup(sk_X509_value(chain, i)), X509_free);
Certificate::ref cert = std::make_shared<OpenSSLCertificate>(x509Cert);
result.push_back(cert);
}
+
return result;
}
std::shared_ptr<CertificateVerificationError> OpenSSLContext::getPeerCertificateVerificationError() const {
- int verifyResult = SSL_get_verify_result(handle_);
+ int verifyResult = SSL_get_verify_result(handle_.get());
if (verifyResult != X509_V_OK) {
return std::make_shared<CertificateVerificationError>(getVerificationErrorTypeForResult(verifyResult));
}
@@ -268,11 +798,19 @@ std::shared_ptr<CertificateVerificationError> OpenSSLContext::getPeerCertificate
ByteArray OpenSSLContext::getFinishMessage() const {
ByteArray data;
data.resize(MAX_FINISHED_SIZE);
- size_t size = SSL_get_finished(handle_, vecptr(data), data.size());
+ auto size = SSL_get_finished(handle_.get(), vecptr(data), data.size());
data.resize(size);
return data;
}
+ByteArray OpenSSLContext::getPeerFinishMessage() const {
+ ByteArray data;
+ data.resize(MAX_FINISHED_SIZE);
+ auto size = SSL_get_peer_finished(handle_.get(), vecptr(data), data.size());
+ data.resize(size);
+ return data;
+ }
+
CertificateVerificationError::Type OpenSSLContext::getVerificationErrorTypeForResult(int result) {
assert(result != 0);
switch (result) {
diff --git a/Swiften/TLS/OpenSSL/OpenSSLContext.h b/Swiften/TLS/OpenSSL/OpenSSLContext.h
index e75b3c9..8eb5758 100644
--- a/Swiften/TLS/OpenSSL/OpenSSLContext.h
+++ b/Swiften/TLS/OpenSSL/OpenSSLContext.h
@@ -1,11 +1,13 @@
/*
- * Copyright (c) 2010-2016 Isode Limited.
+ * Copyright (c) 2010-2018 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#pragma once
+#include <memory>
+
#include <boost/noncopyable.hpp>
#include <boost/signals2.hpp>
@@ -14,41 +16,77 @@
#include <Swiften/Base/ByteArray.h>
#include <Swiften/TLS/CertificateWithKey.h>
#include <Swiften/TLS/TLSContext.h>
+#include <Swiften/TLS/TLSOptions.h>
-namespace Swift {
+namespace std {
+ template<>
+ class default_delete<SSL_CTX> {
+ public:
+ void operator()(SSL_CTX *ptr) {
+ SSL_CTX_free(ptr);
+ }
+ };
+
+ template<>
+ class default_delete<SSL> {
+ public:
+ void operator()(SSL *ptr) {
+ SSL_free(ptr);
+ }
+ };
+}
+namespace Swift {
class OpenSSLContext : public TLSContext, boost::noncopyable {
public:
- OpenSSLContext();
- virtual ~OpenSSLContext();
+ OpenSSLContext(const TLSOptions& options, Mode mode);
+ virtual ~OpenSSLContext() override final;
+
+ void accept() override final;
+ void connect() override final;
+ void connect(const std::string& requestHostname) override final;
+
+ bool setCertificateChain(const std::vector<std::shared_ptr<Certificate>>& certificateChain) override final;
+ bool setPrivateKey(const PrivateKey::ref& privateKey) override final;
+ bool setClientCertificate(CertificateWithKey::ref cert) override final;
+ void setAbortTLSHandshake(bool abort) override final;
+ bool setDiffieHellmanParameters(const ByteArray& parametersInOpenSslDer) override final;
- void connect();
- bool setClientCertificate(CertificateWithKey::ref cert);
+ void handleDataFromNetwork(const SafeByteArray&) override final;
+ void handleDataFromApplication(const SafeByteArray&) override final;
- void handleDataFromNetwork(const SafeByteArray&);
- void handleDataFromApplication(const SafeByteArray&);
+ std::vector<Certificate::ref> getPeerCertificateChain() const override final;
+ std::shared_ptr<CertificateVerificationError> getPeerCertificateVerificationError() const override final;
- std::vector<Certificate::ref> getPeerCertificateChain() const;
- std::shared_ptr<CertificateVerificationError> getPeerCertificateVerificationError() const;
+ virtual ByteArray getFinishMessage() const override final;
+ virtual ByteArray getPeerFinishMessage() const override final;
- virtual ByteArray getFinishMessage() const;
+ void setX509StoreContext(X509_STORE_CTX *ptr) { x509_store_ctx = ptr; }
+ std::function<int (const TLSContext *)> getVerifyCertCallback() { return verifyCertCallback; }
private:
+ bool configure(const TLSOptions& options);
static void ensureLibraryInitialized();
-
+ static int handleServerNameCallback(SSL *ssl, int *ad, void *arg);
static CertificateVerificationError::Type getVerificationErrorTypeForResult(int);
+ void initAndSetBIOs();
+ void doAccept();
void doConnect();
void sendPendingDataToNetwork();
void sendPendingDataToApplication();
private:
- enum State { Start, Connecting, Connected, Error };
+ enum class State { Start, Accepting, Connecting, Connected, Error };
+ const Mode mode_;
State state_;
- SSL_CTX* context_;
- SSL* handle_;
- BIO* readBIO_;
- BIO* writeBIO_;
- };
+ std::unique_ptr<SSL_CTX> context_;
+ std::unique_ptr<SSL> handle_;
+ BIO* readBIO_ = nullptr;
+ BIO* writeBIO_ = nullptr;
+ bool abortTLSHandshake_ = false;
+ X509_STORE_CTX *x509_store_ctx = nullptr;
+ std::function<int (const TLSContext *)> verifyCertCallback = nullptr;
+ };
}
diff --git a/Swiften/TLS/OpenSSL/OpenSSLContextFactory.cpp b/Swiften/TLS/OpenSSL/OpenSSLContextFactory.cpp
index 9f7b2aa..e332ca8 100644
--- a/Swiften/TLS/OpenSSL/OpenSSLContextFactory.cpp
+++ b/Swiften/TLS/OpenSSL/OpenSSLContextFactory.cpp
@@ -1,34 +1,60 @@
/*
- * Copyright (c) 2010-2016 Isode Limited.
+ * Copyright (c) 2010-2019 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#include <Swiften/TLS/OpenSSL/OpenSSLContextFactory.h>
+#include <openssl/bio.h>
+#include <openssl/dh.h>
+#include <openssl/pem.h>
+
#include <Swiften/Base/Log.h>
#include <Swiften/TLS/OpenSSL/OpenSSLContext.h>
+#pragma clang diagnostic ignored "-Wshorten-64-to-32"
+
namespace Swift {
bool OpenSSLContextFactory::canCreate() const {
return true;
}
-TLSContext* OpenSSLContextFactory::createTLSContext(const TLSOptions&) {
- return new OpenSSLContext();
+std::unique_ptr<TLSContext> OpenSSLContextFactory::createTLSContext(const TLSOptions& options, TLSContext::Mode mode) {
+ return std::make_unique<OpenSSLContext>(options, mode);
+}
+
+ByteArray OpenSSLContextFactory::convertDHParametersFromPEMToDER(const std::string& dhParametersInPEM) {
+ ByteArray dhParametersInDER;
+
+ auto bio = std::unique_ptr<BIO, decltype(&BIO_free)>(BIO_new(BIO_s_mem()), BIO_free);
+ if (bio) {
+ BIO_write(bio.get(), dhParametersInPEM.data(), dhParametersInPEM.size());
+ if (auto params = PEM_read_bio_DHparams(bio.get(), nullptr, nullptr, nullptr)) {
+ unsigned char* buffer = nullptr;
+ auto len = i2d_DHparams(params, &buffer);
+ if (len > 0) {
+ dhParametersInDER = createByteArray(buffer, static_cast<size_t>(len));
+ free(buffer);
+ }
+ DH_free(params);
+
+ }
+ }
+ return dhParametersInDER;
}
void OpenSSLContextFactory::setCheckCertificateRevocation(bool check) {
if (check) {
- SWIFT_LOG(warning) << "CRL Checking not supported for OpenSSL" << std::endl;
+ SWIFT_LOG(warning) << "CRL Checking not supported for OpenSSL";
assert(false);
}
}
void OpenSSLContextFactory::setDisconnectOnCardRemoval(bool check) {
if (check) {
- SWIFT_LOG(warning) << "Smart cards not supported for OpenSSL" << std::endl;
+ SWIFT_LOG(warning) << "Smart cards not supported for OpenSSL";
}
}
diff --git a/Swiften/TLS/OpenSSL/OpenSSLContextFactory.h b/Swiften/TLS/OpenSSL/OpenSSLContextFactory.h
index e121a1a..834e479 100644
--- a/Swiften/TLS/OpenSSL/OpenSSLContextFactory.h
+++ b/Swiften/TLS/OpenSSL/OpenSSLContextFactory.h
@@ -1,23 +1,25 @@
/*
- * Copyright (c) 2010-2016 Isode Limited.
+ * Copyright (c) 2010-2019 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#pragma once
-#include <cassert>
+#include <memory>
#include <Swiften/TLS/TLSContextFactory.h>
namespace Swift {
class OpenSSLContextFactory : public TLSContextFactory {
public:
- bool canCreate() const;
- virtual TLSContext* createTLSContext(const TLSOptions& tlsOptions);
+ bool canCreate() const override final;
+ virtual std::unique_ptr<TLSContext> createTLSContext(const TLSOptions& tlsOptions, TLSContext::Mode mode) override final;
+
+ virtual ByteArray convertDHParametersFromPEMToDER(const std::string& dhParametersInPEM) override final;
// Not supported
- virtual void setCheckCertificateRevocation(bool b);
- virtual void setDisconnectOnCardRemoval(bool b);
+ virtual void setCheckCertificateRevocation(bool b) override final;
+ virtual void setDisconnectOnCardRemoval(bool b) override final;
};
}
diff --git a/Swiften/TLS/PrivateKey.cpp b/Swiften/TLS/PrivateKey.cpp
new file mode 100644
index 0000000..31cac1d
--- /dev/null
+++ b/Swiften/TLS/PrivateKey.cpp
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2018 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <Swiften/TLS/PrivateKey.h>
+
+namespace Swift {
+
+PrivateKey::PrivateKey(const SafeByteArray& data, boost::optional<SafeByteArray> password) : data_(data), password_(password) {
+}
+
+const SafeByteArray& PrivateKey::getData() const {
+ return data_;
+}
+
+const boost::optional<SafeByteArray>& PrivateKey::getPassword() const {
+ return password_;
+}
+
+}
diff --git a/Swiften/TLS/PrivateKey.h b/Swiften/TLS/PrivateKey.h
new file mode 100644
index 0000000..332fc48
--- /dev/null
+++ b/Swiften/TLS/PrivateKey.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2018 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#pragma once
+
+#include <memory>
+
+#include <boost/optional.hpp>
+
+#include <Swiften/Base/SafeByteArray.h>
+#include <Swiften/TLS/PrivateKey.h>
+
+namespace Swift {
+
+class PrivateKey {
+public:
+ using ref = std::shared_ptr<PrivateKey>;
+
+public:
+ PrivateKey(const SafeByteArray& data, boost::optional<SafeByteArray> password = boost::optional<SafeByteArray>());
+
+ const SafeByteArray& getData() const;
+ const boost::optional<SafeByteArray>& getPassword() const;
+
+private:
+ SafeByteArray data_;
+ boost::optional<SafeByteArray> password_;
+};
+
+}
diff --git a/Swiften/TLS/SConscript b/Swiften/TLS/SConscript
index 68bf50b..a79e6a1 100644
--- a/Swiften/TLS/SConscript
+++ b/Swiften/TLS/SConscript
@@ -4,6 +4,7 @@ objects = swiften_env.SwiftenObject([
"Certificate.cpp",
"CertificateFactory.cpp",
"CertificateTrustChecker.cpp",
+ "PrivateKey.cpp",
"ServerIdentityVerifier.cpp",
"TLSContext.cpp",
"TLSContextFactory.cpp",
@@ -16,8 +17,11 @@ if myenv.get("HAVE_OPENSSL", 0) :
"OpenSSL/OpenSSLContext.cpp",
"OpenSSL/OpenSSLCertificate.cpp",
"OpenSSL/OpenSSLContextFactory.cpp",
+ "OpenSSL/OpenSSLCertificateFactory.cpp",
])
myenv.Append(CPPDEFINES = "HAVE_OPENSSL")
+ if myenv["PLATFORM"] == "win32" :
+ myenv.Append(CPPDEFINES = "NOMINMAX")
elif myenv.get("HAVE_SCHANNEL", 0) :
swiften_env.Append(LIBS = ["Winscard"])
objects += myenv.SwiftenObject([
diff --git a/Swiften/TLS/Schannel/SchannelContext.cpp b/Swiften/TLS/Schannel/SchannelContext.cpp
index 5799157..722fb4a 100644
--- a/Swiften/TLS/Schannel/SchannelContext.cpp
+++ b/Swiften/TLS/Schannel/SchannelContext.cpp
@@ -5,7 +5,7 @@
*/
/*
- * Copyright (c) 2012-2016 Isode Limited.
+ * Copyright (c) 2012-2019 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -16,6 +16,7 @@
#include <WinHTTP.h> /* For SECURITY_FLAG_IGNORE_CERT_CN_INVALID */
+#include <Swiften/Base/Log.h>
#include <Swiften/TLS/CAPICertificate.h>
#include <Swiften/TLS/Schannel/SchannelCertificate.h>
@@ -39,13 +40,20 @@ SchannelContext::SchannelContext(bool tls1_0Workaround) : state_(Start), secCont
//------------------------------------------------------------------------
SchannelContext::~SchannelContext() {
- if (myCertStore_) CertCloseStore(myCertStore_, 0);
+ SWIFT_LOG(debug) << "Destroying SchannelContext";
+ if (myCertStore_) {
+ if (CertCloseStore(myCertStore_, 0) == FALSE) {
+ SWIFT_LOG(debug) << "Failed to close the certificate store";
+ }
+ }
}
//------------------------------------------------------------------------
void SchannelContext::determineStreamSizes() {
- QueryContextAttributes(contextHandle_, SECPKG_ATTR_STREAM_SIZES, &streamSizes_);
+ if (QueryContextAttributes(contextHandle_, SECPKG_ATTR_STREAM_SIZES, &streamSizes_) != SEC_E_OK) {
+ SWIFT_LOG(debug) << "QueryContextAttributes failed to determinate the stream size";
+ }
}
//------------------------------------------------------------------------
@@ -267,8 +275,8 @@ void SchannelContext::continueHandshake(const SafeByteArray& data) {
SecBuffer inBuffers[2];
// Provide Schannel with the remote host's handshake data
- inBuffers[0].pvBuffer = (char*)(&receivedData_[0]);
- inBuffers[0].cbBuffer = (unsigned long)receivedData_.size();
+ inBuffers[0].pvBuffer = static_cast<char*>(&receivedData_[0]);
+ inBuffers[0].cbBuffer = static_cast<unsigned long>(receivedData_.size());
inBuffers[0].BufferType = SECBUFFER_TOKEN;
inBuffers[1].pvBuffer = NULL;
@@ -483,8 +491,8 @@ void SchannelContext::decryptAndProcessData(const SafeByteArray& data) {
// contexts. Additionally, a second SECBUFFER_TOKEN type buffer that contains a security token
// must also be supplied.
//
- inBuffers[0].pvBuffer = (char*)(&receivedData_[0]);
- inBuffers[0].cbBuffer = (unsigned long)receivedData_.size();
+ inBuffers[0].pvBuffer = static_cast<char*>(&receivedData_[0]);
+ inBuffers[0].cbBuffer = static_cast<unsigned long>(receivedData_.size());
inBuffers[0].BufferType = SECBUFFER_DATA;
inBuffers[1].BufferType = SECBUFFER_EMPTY;
@@ -578,7 +586,7 @@ void SchannelContext::encryptAndSendData(const SafeByteArray& data) {
outBuffers[0].BufferType = SECBUFFER_STREAM_HEADER;
outBuffers[1].pvBuffer = &sendBuffer[0] + streamSizes_.cbHeader;
- outBuffers[1].cbBuffer = (unsigned long)bytesToSend;
+ outBuffers[1].cbBuffer = static_cast<unsigned long>(bytesToSend);
outBuffers[1].BufferType = SECBUFFER_DATA;
outBuffers[2].pvBuffer = &sendBuffer[0] + streamSizes_.cbHeader + bytesToSend;
@@ -645,6 +653,7 @@ std::vector<Certificate::ref> SchannelContext::getPeerCertificateChain() const {
SECURITY_STATUS status = QueryContextAttributes(contextHandle_, SECPKG_ATTR_REMOTE_CERT_CONTEXT, pServerCert.Reset());
if (status != SEC_E_OK) {
+ SWIFT_LOG(debug) << "Error while Querying the Certificate Chain";
return certificateChain;
}
certificateChain.push_back(std::make_shared<SchannelCertificate>(pServerCert));
@@ -678,6 +687,10 @@ ByteArray SchannelContext::getFinishMessage() const {
if (ret == SEC_E_OK) {
return createByteArray(((unsigned char*) bindings.Bindings) + bindings.Bindings->dwApplicationDataOffset + 11 /* tls-unique:*/, bindings.Bindings->cbApplicationDataLength - 11);
}
+ else {
+ SWIFT_LOG(debug) << "Error while retrieving Finish Message";
+ }
+
return ByteArray();
}
diff --git a/Swiften/TLS/Schannel/SchannelContextFactory.cpp b/Swiften/TLS/Schannel/SchannelContextFactory.cpp
index f78d386..d029730 100644
--- a/Swiften/TLS/Schannel/SchannelContextFactory.cpp
+++ b/Swiften/TLS/Schannel/SchannelContextFactory.cpp
@@ -5,7 +5,7 @@
*/
/*
- * Copyright (c) 2015-2016 Isode Limited.
+ * Copyright (c) 2015-2018 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -23,11 +23,13 @@ bool SchannelContextFactory::canCreate() const {
return true;
}
-TLSContext* SchannelContextFactory::createTLSContext(const TLSOptions& tlsOptions) {
+std::unique_ptr<TLSContext> SchannelContextFactory::createTLSContext(const TLSOptions& tlsOptions, TLSContext::Mode mode) {
+ // TLS server mode is not supported for the SecureTransport backend yet.
+ assert(mode == TLSContext::Mode::Client);
SchannelContext* context = new SchannelContext(tlsOptions.schannelTLS1_0Workaround);
context->setCheckCertificateRevocation(checkCertificateRevocation);
context->setDisconnectOnCardRemoval(disconnectOnCardRemoval);
- return context;
+ return std::unique_ptr<TLSContext>(context);
}
void SchannelContextFactory::setCheckCertificateRevocation(bool b) {
diff --git a/Swiften/TLS/Schannel/SchannelContextFactory.h b/Swiften/TLS/Schannel/SchannelContextFactory.h
index 142f193..76ff365 100644
--- a/Swiften/TLS/Schannel/SchannelContextFactory.h
+++ b/Swiften/TLS/Schannel/SchannelContextFactory.h
@@ -5,13 +5,15 @@
*/
/*
- * Copyright (c) 2015 Isode Limited.
+ * Copyright (c) 2015-2018 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#pragma once
+#include <memory>
+
#include <Swiften/TLS/TLSContextFactory.h>
namespace Swift {
@@ -20,8 +22,7 @@ namespace Swift {
SchannelContextFactory();
bool canCreate() const;
- virtual TLSContext* createTLSContext(const TLSOptions& tlsOptions);
-
+ virtual std::unique_ptr<TLSContext> createTLSContext(const TLSOptions& tlsOptions, TLSContext::Mode mode = TLSContext::Mode::Client);
virtual void setCheckCertificateRevocation(bool b);
virtual void setDisconnectOnCardRemoval(bool b);
diff --git a/Swiften/TLS/SecureTransport/SecureTransportCertificate.mm b/Swiften/TLS/SecureTransport/SecureTransportCertificate.mm
index db0af89..fffb3ed 100644
--- a/Swiften/TLS/SecureTransport/SecureTransportCertificate.mm
+++ b/Swiften/TLS/SecureTransport/SecureTransportCertificate.mm
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2016 Isode Limited.
+ * Copyright (c) 2015-2017 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -93,13 +93,14 @@ void SecureTransportCertificate::parse() {
NSDictionary* subjectAltNamesDict = certDict[@"2.5.29.17"][@"value"];
for (NSDictionary* entry in subjectAltNamesDict) {
- if ([entry[@"label"] isEqualToString:static_cast<NSString * _Nonnull>([NSString stringWithUTF8String:ID_ON_XMPPADDR_OID])]) {
+ NSString* label = entry[@"label"];
+ if ([label isEqualToString:static_cast<NSString * _Nonnull>([NSString stringWithUTF8String:ID_ON_XMPPADDR_OID])]) {
xmppAddresses_.push_back(ns2StdString(entry[@"value"]));
}
- else if ([entry[@"label"] isEqualToString:static_cast<NSString * _Nonnull>([NSString stringWithUTF8String:ID_ON_DNSSRV_OID])]) {
+ else if ([label isEqualToString:static_cast<NSString * _Nonnull>([NSString stringWithUTF8String:ID_ON_DNSSRV_OID])]) {
srvNames_.push_back(ns2StdString(entry[@"value"]));
}
- else if ([entry[@"label"] isEqualToString:@"DNS Name"]) {
+ else if ([label isEqualToString:@"DNS Name"]) {
dnsNames_.push_back(ns2StdString(entry[@"value"]));
}
}
diff --git a/Swiften/TLS/SecureTransport/SecureTransportContext.mm b/Swiften/TLS/SecureTransport/SecureTransportContext.mm
index 1ed636b..b4f7842 100644
--- a/Swiften/TLS/SecureTransport/SecureTransportContext.mm
+++ b/Swiften/TLS/SecureTransport/SecureTransportContext.mm
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2016 Isode Limited.
+ * Copyright (c) 2015-2019 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -72,14 +72,14 @@ CFArrayRef CreateClientCertificateChainAsCFArrayRef(CertificateWithKey::ref key)
break;
case errSecAuthFailed:
// Password did not work for decoding the certificate.
- SWIFT_LOG(warning) << "Invalid password." << std::endl;
+ SWIFT_LOG(warning) << "Invalid password.";
break;
case errSecDecode:
// Other decoding error.
- SWIFT_LOG(warning) << "PKCS12 decoding error." << std::endl;
+ SWIFT_LOG(warning) << "PKCS12 decoding error.";
break;
default:
- SWIFT_LOG(warning) << "Unknown error." << std::endl;
+ SWIFT_LOG(warning) << "Unknown error.";
}
if (securityError != errSecSuccess) {
@@ -110,20 +110,20 @@ SecureTransportContext::SecureTransportContext(bool checkCertificateRevocation)
// set IO callbacks
error = SSLSetIOFuncs(sslContext_.get(), &SecureTransportContext::SSLSocketReadCallback, &SecureTransportContext::SSLSocketWriteCallback);
if (error != noErr) {
- SWIFT_LOG(error) << "Unable to set IO functions to SSL context." << std::endl;
+ SWIFT_LOG(error) << "Unable to set IO functions to SSL context.";
sslContext_.reset();
}
error = SSLSetConnection(sslContext_.get(), this);
if (error != noErr) {
- SWIFT_LOG(error) << "Unable to set connection to SSL context." << std::endl;
+ SWIFT_LOG(error) << "Unable to set connection to SSL context.";
sslContext_.reset();
}
error = SSLSetSessionOption(sslContext_.get(), kSSLSessionOptionBreakOnServerAuth, true);
if (error != noErr) {
- SWIFT_LOG(error) << "Unable to set kSSLSessionOptionBreakOnServerAuth on session." << std::endl;
+ SWIFT_LOG(error) << "Unable to set kSSLSessionOptionBreakOnServerAuth on session.";
sslContext_.reset();
}
}
@@ -154,19 +154,19 @@ std::string SecureTransportContext::stateToString(State state) {
}
void SecureTransportContext::setState(State newState) {
- SWIFT_LOG(debug) << "Switch state from " << stateToString(state_) << " to " << stateToString(newState) << "." << std::endl;
+ SWIFT_LOG(debug) << "Switch state from " << stateToString(state_) << " to " << stateToString(newState) << ".";
state_ = newState;
}
void SecureTransportContext::connect() {
- SWIFT_LOG_ASSERT(state_ == None, error) << "current state '" << stateToString(state_) << " invalid." << std::endl;
+ SWIFT_LOG_ASSERT(state_ == None, error) << "current state '" << stateToString(state_) << " invalid.";
if (clientCertificate_) {
CFArrayRef certs = CreateClientCertificateChainAsCFArrayRef(clientCertificate_);
if (certs) {
std::shared_ptr<CFArray> certRefs(certs, CFRelease);
OSStatus result = SSLSetCertificate(sslContext_.get(), certRefs.get());
if (result != noErr) {
- SWIFT_LOG(error) << "SSLSetCertificate failed with error " << result << "." << std::endl;
+ SWIFT_LOG(error) << "SSLSetCertificate failed with error " << result << ".";
}
}
}
@@ -174,23 +174,23 @@ void SecureTransportContext::connect() {
}
void SecureTransportContext::processHandshake() {
- SWIFT_LOG_ASSERT(state_ == None || state_ == Handshake, error) << "current state '" << stateToString(state_) << " invalid." << std::endl;
+ SWIFT_LOG_ASSERT(state_ == None || state_ == Handshake, error) << "current state '" << stateToString(state_) << " invalid.";
OSStatus error = SSLHandshake(sslContext_.get());
if (error == errSSLWouldBlock) {
setState(Handshake);
}
else if (error == noErr) {
- SWIFT_LOG(debug) << "TLS handshake successful." << std::endl;
+ SWIFT_LOG(debug) << "TLS handshake successful.";
setState(HandshakeDone);
onConnected();
}
else if (error == errSSLPeerAuthCompleted) {
- SWIFT_LOG(debug) << "Received server certificate. Start verification." << std::endl;
+ SWIFT_LOG(debug) << "Received server certificate. Start verification.";
setState(Handshake);
verifyServerCertificate();
}
else {
- SWIFT_LOG(debug) << "Error returned from SSLHandshake call is " << error << "." << std::endl;
+ SWIFT_LOG(debug) << "Error returned from SSLHandshake call is " << error << ".";
fatalError(nativeToTLSError(error), std::make_shared<CertificateVerificationError>());
}
}
@@ -226,13 +226,13 @@ void SecureTransportContext::verifyServerCertificate() {
OSStatus cssmResult = 0;
switch(trustResult) {
case kSecTrustResultUnspecified:
- SWIFT_LOG(warning) << "Successful implicit validation. Result unspecified." << std::endl;
+ SWIFT_LOG(warning) << "Successful implicit validation. Result unspecified.";
break;
case kSecTrustResultProceed:
- SWIFT_LOG(warning) << "Validation resulted in explicitly trusted." << std::endl;
+ SWIFT_LOG(warning) << "Validation resulted in explicitly trusted.";
break;
case kSecTrustResultRecoverableTrustFailure:
- SWIFT_LOG(warning) << "recoverable trust failure" << std::endl;
+ SWIFT_LOG(warning) << "recoverable trust failure";
error = SecTrustGetCssmResultCode(trust, &cssmResult);
if (error == errSecSuccess) {
verificationError_ = CSSMErrorToVerificationError(cssmResult);
@@ -304,8 +304,8 @@ bool SecureTransportContext::setClientCertificate(CertificateWithKey::ref cert)
}
void SecureTransportContext::handleDataFromNetwork(const SafeByteArray& data) {
- SWIFT_LOG(debug) << std::endl;
- SWIFT_LOG_ASSERT(state_ == HandshakeDone || state_ == Handshake, error) << "current state '" << stateToString(state_) << " invalid." << std::endl;
+ SWIFT_LOG(debug);
+ SWIFT_LOG_ASSERT(state_ == HandshakeDone || state_ == Handshake, error) << "current state '" << stateToString(state_) << " invalid.";
append(readingBuffer_, data);
@@ -332,7 +332,7 @@ void SecureTransportContext::handleDataFromNetwork(const SafeByteArray& data) {
break;
}
else {
- SWIFT_LOG(error) << "SSLRead failed with error " << error << ", read bytes: " << bytesRead << "." << std::endl;
+ SWIFT_LOG(error) << "SSLRead failed with error " << error << ", read bytes: " << bytesRead << ".";
fatalError(std::make_shared<TLSError>(), std::make_shared<CertificateVerificationError>());
return;
}
@@ -347,7 +347,7 @@ void SecureTransportContext::handleDataFromNetwork(const SafeByteArray& data) {
}
break;
case Error:
- SWIFT_LOG(debug) << "Igoring received data in error state." << std::endl;
+ SWIFT_LOG(debug) << "Igoring received data in error state.";
break;
}
}
@@ -358,13 +358,13 @@ void SecureTransportContext::handleDataFromApplication(const SafeByteArray& data
OSStatus error = SSLWrite(sslContext_.get(), data.data(), data.size(), &processedBytes);
switch(error) {
case errSSLWouldBlock:
- SWIFT_LOG(warning) << "Unexpected because the write callback does not block." << std::endl;
+ SWIFT_LOG(warning) << "Unexpected because the write callback does not block.";
return;
case errSSLClosedGraceful:
case noErr:
return;
default:
- SWIFT_LOG(warning) << "SSLWrite returned error code: " << error << ", processed bytes: " << processedBytes << std::endl;
+ SWIFT_LOG(warning) << "SSLWrite returned error code: " << error << ", processed bytes: " << processedBytes;
fatalError(std::make_shared<TLSError>(), std::shared_ptr<CertificateVerificationError>());
}
}
@@ -376,7 +376,7 @@ std::vector<Certificate::ref> SecureTransportContext::getPeerCertificateChain()
typedef boost::remove_pointer<SecTrustRef>::type SecTrust;
std::shared_ptr<SecTrust> securityTrust;
- SecTrustRef secTrust = nullptr;;
+ SecTrustRef secTrust = nullptr;
OSStatus error = SSLCopyPeerTrust(sslContext_.get(), &secTrust);
if (error == noErr) {
securityTrust = std::shared_ptr<SecTrust>(secTrust, CFRelease);
@@ -390,7 +390,7 @@ std::vector<Certificate::ref> SecureTransportContext::getPeerCertificateChain()
}
}
else {
- SWIFT_LOG(warning) << "Failed to obtain peer trust structure; error = " << error << "." << std::endl;
+ SWIFT_LOG(warning) << "Failed to obtain peer trust structure; error = " << error << ".";
}
}
@@ -402,7 +402,7 @@ CertificateVerificationError::ref SecureTransportContext::getPeerCertificateVeri
}
ByteArray SecureTransportContext::getFinishMessage() const {
- SWIFT_LOG(warning) << "Access to TLS handshake finish message is not part of OS X Secure Transport APIs." << std::endl;
+ SWIFT_LOG(warning) << "Access to TLS handshake finish message is not part of OS X Secure Transport APIs.";
return ByteArray();
}
@@ -453,42 +453,42 @@ std::shared_ptr<CertificateVerificationError> SecureTransportContext::CSSMErrorT
std::shared_ptr<CertificateVerificationError> error;
switch(resultCode) {
case CSSMERR_TP_NOT_TRUSTED:
- SWIFT_LOG(debug) << "CSSM result code: CSSMERR_TP_NOT_TRUSTED" << std::endl;
+ SWIFT_LOG(debug) << "CSSM result code: CSSMERR_TP_NOT_TRUSTED";
error = std::make_shared<CertificateVerificationError>(CertificateVerificationError::Untrusted);
break;
case CSSMERR_TP_CERT_NOT_VALID_YET:
- SWIFT_LOG(debug) << "CSSM result code: CSSMERR_TP_CERT_NOT_VALID_YET" << std::endl;
+ SWIFT_LOG(debug) << "CSSM result code: CSSMERR_TP_CERT_NOT_VALID_YET";
error = std::make_shared<CertificateVerificationError>(CertificateVerificationError::NotYetValid);
break;
case CSSMERR_TP_CERT_EXPIRED:
- SWIFT_LOG(debug) << "CSSM result code: CSSMERR_TP_CERT_EXPIRED" << std::endl;
+ SWIFT_LOG(debug) << "CSSM result code: CSSMERR_TP_CERT_EXPIRED";
error = std::make_shared<CertificateVerificationError>(CertificateVerificationError::Expired);
break;
case CSSMERR_TP_CERT_REVOKED:
- SWIFT_LOG(debug) << "CSSM result code: CSSMERR_TP_CERT_REVOKED" << std::endl;
+ SWIFT_LOG(debug) << "CSSM result code: CSSMERR_TP_CERT_REVOKED";
error = std::make_shared<CertificateVerificationError>(CertificateVerificationError::Revoked);
break;
case CSSMERR_TP_VERIFY_ACTION_FAILED:
- SWIFT_LOG(debug) << "CSSM result code: CSSMERR_TP_VERIFY_ACTION_FAILED" << std::endl;
+ SWIFT_LOG(debug) << "CSSM result code: CSSMERR_TP_VERIFY_ACTION_FAILED";
break;
case CSSMERR_APPLETP_INCOMPLETE_REVOCATION_CHECK:
- SWIFT_LOG(debug) << "CSSM result code: CSSMERR_APPLETP_INCOMPLETE_REVOCATION_CHECK" << std::endl;
+ SWIFT_LOG(debug) << "CSSM result code: CSSMERR_APPLETP_INCOMPLETE_REVOCATION_CHECK";
if (checkCertificateRevocation_) {
error = std::make_shared<CertificateVerificationError>(CertificateVerificationError::RevocationCheckFailed);
}
break;
case CSSMERR_APPLETP_OCSP_UNAVAILABLE:
- SWIFT_LOG(debug) << "CSSM result code: CSSMERR_APPLETP_OCSP_UNAVAILABLE" << std::endl;
+ SWIFT_LOG(debug) << "CSSM result code: CSSMERR_APPLETP_OCSP_UNAVAILABLE";
if (checkCertificateRevocation_) {
error = std::make_shared<CertificateVerificationError>(CertificateVerificationError::RevocationCheckFailed);
}
break;
case CSSMERR_APPLETP_SSL_BAD_EXT_KEY_USE:
- SWIFT_LOG(debug) << "CSSM result code: CSSMERR_APPLETP_SSL_BAD_EXT_KEY_USE" << std::endl;
+ SWIFT_LOG(debug) << "CSSM result code: CSSMERR_APPLETP_SSL_BAD_EXT_KEY_USE";
error = std::make_shared<CertificateVerificationError>(CertificateVerificationError::InvalidPurpose);
break;
default:
- SWIFT_LOG(warning) << "unhandled CSSM error: " << resultCode << ", CSSM_TP_BASE_TP_ERROR: " << CSSM_TP_BASE_TP_ERROR << std::endl;
+ SWIFT_LOG(warning) << "unhandled CSSM error: " << resultCode << ", CSSM_TP_BASE_TP_ERROR: " << CSSM_TP_BASE_TP_ERROR;
error = std::make_shared<CertificateVerificationError>(CertificateVerificationError::UnknownError);
break;
}
diff --git a/Swiften/TLS/SecureTransport/SecureTransportContextFactory.cpp b/Swiften/TLS/SecureTransport/SecureTransportContextFactory.cpp
index 1fac1fb..ac399e1 100644
--- a/Swiften/TLS/SecureTransport/SecureTransportContextFactory.cpp
+++ b/Swiften/TLS/SecureTransport/SecureTransportContextFactory.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2016 Isode Limited.
+ * Copyright (c) 2015-2019 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -26,8 +26,10 @@ bool SecureTransportContextFactory::canCreate() const {
return true;
}
-TLSContext* SecureTransportContextFactory::createTLSContext(const TLSOptions& /* tlsOptions */) {
- return new SecureTransportContext(checkCertificateRevocation_);
+std::unique_ptr<TLSContext> SecureTransportContextFactory::createTLSContext(const TLSOptions& /* tlsOptions */, TLSContext::Mode mode) {
+ // TLS server mode is not supported for the SecureTransport backend yet.
+ assert(mode == TLSContext::Mode::Client);
+ return std::unique_ptr<TLSContext>(new SecureTransportContext(checkCertificateRevocation_));
}
void SecureTransportContextFactory::setCheckCertificateRevocation(bool b) {
@@ -37,7 +39,7 @@ void SecureTransportContextFactory::setCheckCertificateRevocation(bool b) {
void SecureTransportContextFactory::setDisconnectOnCardRemoval(bool b) {
disconnectOnCardRemoval_ = b;
if (disconnectOnCardRemoval_) {
- SWIFT_LOG(warning) << "Smart cards have not been tested yet" << std::endl;
+ SWIFT_LOG(warning) << "Smart cards have not been tested yet";
}
}
diff --git a/Swiften/TLS/SecureTransport/SecureTransportContextFactory.h b/Swiften/TLS/SecureTransport/SecureTransportContextFactory.h
index 74c598f..b86639a 100644
--- a/Swiften/TLS/SecureTransport/SecureTransportContextFactory.h
+++ b/Swiften/TLS/SecureTransport/SecureTransportContextFactory.h
@@ -1,11 +1,13 @@
/*
- * Copyright (c) 2015 Isode Limited.
+ * Copyright (c) 2015-2018 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#pragma once
+#include <memory>
+
#include <Swiften/TLS/TLSContextFactory.h>
namespace Swift {
@@ -17,7 +19,7 @@ class SecureTransportContextFactory : public TLSContextFactory {
virtual bool canCreate() const;
- virtual TLSContext* createTLSContext(const TLSOptions& tlsOptions);
+ virtual std::unique_ptr<TLSContext> createTLSContext(const TLSOptions& tlsOptions, TLSContext::Mode mode = TLSContext::Mode::Client);
virtual void setCheckCertificateRevocation(bool b);
virtual void setDisconnectOnCardRemoval(bool b);
diff --git a/Swiften/TLS/ServerIdentityVerifier.cpp b/Swiften/TLS/ServerIdentityVerifier.cpp
index 226e94b..18ea2aa 100644
--- a/Swiften/TLS/ServerIdentityVerifier.cpp
+++ b/Swiften/TLS/ServerIdentityVerifier.cpp
@@ -12,7 +12,7 @@
namespace Swift {
-ServerIdentityVerifier::ServerIdentityVerifier(const JID& jid, IDNConverter* idnConverter) : domainValid(false) {
+ServerIdentityVerifier::ServerIdentityVerifier(const JID& jid, IDNConverter* idnConverter, bool checkServer) : domainValid(false), checkServer_(checkServer) {
domain = jid.getDomain();
boost::optional<std::string> domainResult = idnConverter->getIDNAEncoded(domain);
if (!!domainResult) {
@@ -36,12 +36,14 @@ bool ServerIdentityVerifier::certificateVerifies(Certificate::ref certificate) {
}
hasSAN |= !dnsNames.empty();
+ std::string prefix = (checkServer_) ? "_xmpp-server." : "_xmpp-client.";
+
// SRV names
std::vector<std::string> srvNames = certificate->getSRVNames();
for (const auto& srvName : srvNames) {
// Only match SRV names that begin with the service; this isn't required per
// spec, but we're being purist about this.
- if (boost::starts_with(srvName, "_xmpp-client.") && matchesDomain(srvName.substr(std::string("_xmpp-client.").size(), srvName.npos))) {
+ if (boost::starts_with(srvName, prefix) && matchesDomain(srvName.substr(prefix.size(), srvName.npos))) {
return true;
}
}
@@ -80,15 +82,15 @@ bool ServerIdentityVerifier::matchesDomain(const std::string& s) const {
if (dotIndex != matchDomain.npos) {
matchDomain = matchDomain.substr(dotIndex + 1, matchDomain.npos);
}
- return matchString == matchDomain;
+ return boost::iequals(matchString, matchDomain);
}
else {
- return s == encodedDomain;
+ return boost::iequals(s, encodedDomain);
}
}
bool ServerIdentityVerifier::matchesAddress(const std::string& s) const {
- return s == domain;
+ return boost::iequals(s, domain);
}
}
diff --git a/Swiften/TLS/ServerIdentityVerifier.h b/Swiften/TLS/ServerIdentityVerifier.h
index f40c683..f2cf46f 100644
--- a/Swiften/TLS/ServerIdentityVerifier.h
+++ b/Swiften/TLS/ServerIdentityVerifier.h
@@ -18,7 +18,7 @@ namespace Swift {
class SWIFTEN_API ServerIdentityVerifier {
public:
- ServerIdentityVerifier(const JID& jid, IDNConverter* idnConverter);
+ ServerIdentityVerifier(const JID& jid, IDNConverter* idnConverter, bool checkServer=false);
bool certificateVerifies(Certificate::ref);
@@ -30,5 +30,6 @@ namespace Swift {
std::string domain;
std::string encodedDomain;
bool domainValid;
+ bool checkServer_;
};
}
diff --git a/Swiften/TLS/TLSContext.cpp b/Swiften/TLS/TLSContext.cpp
index 2763547..fd31c2d 100644
--- a/Swiften/TLS/TLSContext.cpp
+++ b/Swiften/TLS/TLSContext.cpp
@@ -1,19 +1,53 @@
/*
- * Copyright (c) 2010 Isode Limited.
+ * Copyright (c) 2010-2018 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#include <Swiften/TLS/TLSContext.h>
+#include <cassert>
+
namespace Swift {
TLSContext::~TLSContext() {
}
+void TLSContext::accept() {
+ assert(false);
+}
+
+void TLSContext::connect(const std::string& /* serverName */) {
+ assert(false);
+}
+
+bool TLSContext::setCertificateChain(const std::vector<std::shared_ptr<Certificate>>& /* certificateChain */) {
+ assert(false);
+ return false;
+}
+
+bool TLSContext::setPrivateKey(const PrivateKey::ref& /* privateKey */) {
+ assert(false);
+ return false;
+}
+
+bool TLSContext::setDiffieHellmanParameters(const ByteArray& /*parametersInOpenSslDer*/) {
+ assert(false);
+ return false;
+}
+
+void TLSContext::setAbortTLSHandshake(bool /* abort */) {
+ assert(false);
+}
+
Certificate::ref TLSContext::getPeerCertificate() const {
std::vector<Certificate::ref> chain = getPeerCertificateChain();
return chain.empty() ? Certificate::ref() : chain[0];
}
+ByteArray TLSContext::getPeerFinishMessage() const {
+ assert(false);
+ return ByteArray();
+}
+
}
diff --git a/Swiften/TLS/TLSContext.h b/Swiften/TLS/TLSContext.h
index 79e3485..f2dbdce 100644
--- a/Swiften/TLS/TLSContext.h
+++ b/Swiften/TLS/TLSContext.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2016 Isode Limited.
+ * Copyright (c) 2010-2018 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -15,6 +15,7 @@
#include <Swiften/TLS/Certificate.h>
#include <Swiften/TLS/CertificateVerificationError.h>
#include <Swiften/TLS/CertificateWithKey.h>
+#include <Swiften/TLS/PrivateKey.h>
#include <Swiften/TLS/TLSError.h>
namespace Swift {
@@ -23,9 +24,21 @@ namespace Swift {
public:
virtual ~TLSContext();
+ virtual void accept();
virtual void connect() = 0;
+ virtual void connect(const std::string& serverName);
+
+ virtual bool setCertificateChain(const std::vector<std::shared_ptr<Certificate>>& /* certificateChain */);
+ virtual bool setPrivateKey(const PrivateKey::ref& /* privateKey */);
virtual bool setClientCertificate(CertificateWithKey::ref cert) = 0;
+ virtual bool setDiffieHellmanParameters(const ByteArray& parametersInOpenSslDer);
+
+ /**
+ * This method can be used during the \ref onServerNameRequested signal,
+ * to report an error about an unknown host back to the requesting client.
+ */
+ virtual void setAbortTLSHandshake(bool /* abort */);
virtual void handleDataFromNetwork(const SafeByteArray&) = 0;
virtual void handleDataFromApplication(const SafeByteArray&) = 0;
@@ -35,11 +48,19 @@ namespace Swift {
virtual CertificateVerificationError::ref getPeerCertificateVerificationError() const = 0;
virtual ByteArray getFinishMessage() const = 0;
+ virtual ByteArray getPeerFinishMessage() const;
+
+ public:
+ enum class Mode {
+ Client,
+ Server
+ };
public:
boost::signals2::signal<void (const SafeByteArray&)> onDataForNetwork;
boost::signals2::signal<void (const SafeByteArray&)> onDataForApplication;
boost::signals2::signal<void (std::shared_ptr<TLSError>)> onError;
boost::signals2::signal<void ()> onConnected;
+ boost::signals2::signal<void (const std::string&)> onServerNameRequested;
};
}
diff --git a/Swiften/TLS/TLSContextFactory.cpp b/Swiften/TLS/TLSContextFactory.cpp
index d196e15..91e60d6 100644
--- a/Swiften/TLS/TLSContextFactory.cpp
+++ b/Swiften/TLS/TLSContextFactory.cpp
@@ -6,9 +6,16 @@
#include <Swiften/TLS/TLSContextFactory.h>
+#include <cassert>
+
namespace Swift {
TLSContextFactory::~TLSContextFactory() {
}
+ByteArray TLSContextFactory::convertDHParametersFromPEMToDER(const std::string& /* pem */) {
+ assert(false);
+ return ByteArray();
+}
+
}
diff --git a/Swiften/TLS/TLSContextFactory.h b/Swiften/TLS/TLSContextFactory.h
index d2ffe15..5f70541 100644
--- a/Swiften/TLS/TLSContextFactory.h
+++ b/Swiften/TLS/TLSContextFactory.h
@@ -1,25 +1,29 @@
/*
- * Copyright (c) 2010-2015 Isode Limited.
+ * Copyright (c) 2010-2018 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
#pragma once
+#include <memory>
+
#include <Swiften/Base/API.h>
+#include <Swiften/Base/ByteArray.h>
+#include <Swiften/TLS/TLSContext.h>
#include <Swiften/TLS/TLSOptions.h>
namespace Swift {
- class TLSContext;
-
class SWIFTEN_API TLSContextFactory {
public:
virtual ~TLSContextFactory();
virtual bool canCreate() const = 0;
- virtual TLSContext* createTLSContext(const TLSOptions& tlsOptions) = 0;
+ virtual std::unique_ptr<TLSContext> createTLSContext(const TLSOptions& tlsOptions, TLSContext::Mode = TLSContext::Mode::Client) = 0;
virtual void setCheckCertificateRevocation(bool b) = 0;
virtual void setDisconnectOnCardRemoval(bool b) = 0;
+
+ virtual ByteArray convertDHParametersFromPEMToDER(const std::string& pem);
};
}
diff --git a/Swiften/TLS/TLSError.h b/Swiften/TLS/TLSError.h
index ae775e6..9e4af2f 100644
--- a/Swiften/TLS/TLSError.h
+++ b/Swiften/TLS/TLSError.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2016 Isode Limited.
+ * Copyright (c) 2012-2019 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -7,6 +7,7 @@
#pragma once
#include <memory>
+#include <string>
#include <Swiften/Base/API.h>
#include <Swiften/Base/Error.h>
@@ -18,16 +19,23 @@ namespace Swift {
enum Type {
UnknownError,
- CertificateCardRemoved
+ CertificateCardRemoved,
+ AcceptFailed,
+ ConnectFailed
};
- TLSError(Type type = UnknownError) : type(type) {}
+ TLSError(Type type = UnknownError, std::string message = "") : type_(type), message_(std::move(message)) {}
Type getType() const {
- return type;
+ return type_;
+ }
+
+ const std::string& getMessage() const {
+ return message_;
}
private:
- Type type;
+ Type type_;
+ std::string message_;
};
}
diff --git a/Swiften/TLS/TLSOptions.h b/Swiften/TLS/TLSOptions.h
index dd7e920..e3faaf9 100644
--- a/Swiften/TLS/TLSOptions.h
+++ b/Swiften/TLS/TLSOptions.h
@@ -7,8 +7,11 @@
#pragma once
namespace Swift {
+ class TLSContext;
+ class Certificate;
- struct TLSOptions {
+ class TLSOptions {
+ public:
TLSOptions() : schannelTLS1_0Workaround(false) {
}
@@ -21,5 +24,54 @@ namespace Swift {
*/
bool schannelTLS1_0Workaround;
+ /**
+ * OpenSSL configuration flags
+ */
+ boost::optional<bool> workaroundMicrosoftSessID;
+ boost::optional<bool> workaroundNetscapeChallenge;
+ boost::optional<bool> workaroundNetscapeReuseCipherChange;
+ boost::optional<bool> workaroundSSLRef2ReuseCertType;
+ boost::optional<bool> workaroundMicrosoftBigSSLv3Buffer;
+ boost::optional<bool> workaroundSSLeay080ClientDH;
+ boost::optional<bool> workaroundTLSD5;
+ boost::optional<bool> workaroundTLSBlockPadding;
+ boost::optional<bool> workaroundDontInsertEmptyFragments;
+ boost::optional<bool> workaroundAll;
+ boost::optional<bool> suppressSSLv2;
+ boost::optional<bool> suppressSSLv3;
+ boost::optional<bool> suppressTLSv1;
+ boost::optional<bool> disableTLSRollBackBug;
+ boost::optional<bool> singleDHUse;
+
+ /**
+ * Other OpenSSL configuration items
+ */
+ boost::optional<std::string> cipherSuites;
+ boost::optional<std::string> context;
+ boost::optional<int> sessionCacheTimeout;
+ boost::optional<int> verifyDepth;
+
+ enum class VerifyMode {
+ None,
+ Required,
+ Optional
+ };
+ boost::optional<VerifyMode> verifyMode;
+
+ /**
+ * Callback for certificate verification
+ */
+
+ boost::optional<std::function<int(const TLSContext *)>> verifyCertificateCallback;
+
+ /**
+ * Allows specification of application-specific Trust Anchors
+ */
+ boost::optional<std::vector<std::shared_ptr<Certificate>>> trustAnchors;
+
+ /**
+ * Turns off automatic loading of system Trust Anchors
+ */
+ bool ignoreSystemTrustAnchors = false;
};
}
diff --git a/Swiften/TLS/UnitTest/CertificateTest.cpp b/Swiften/TLS/UnitTest/CertificateTest.cpp
index 2483dae..d5bf210 100644
--- a/Swiften/TLS/UnitTest/CertificateTest.cpp
+++ b/Swiften/TLS/UnitTest/CertificateTest.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010-2016 Isode Limited.
+ * Copyright (c) 2010-2018 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -10,9 +10,12 @@
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <Swiften/Base/ByteArray.h>
+#include <Swiften/Base/Platform.h>
#include <Swiften/Crypto/CryptoProvider.h>
#include <Swiften/Crypto/PlatformCryptoProvider.h>
#include <Swiften/TLS/Certificate.h>
+#include <Swiften/TLS/CertificateFactory.h>
+#include <Swiften/TLS/PlatformTLSFactories.h>
#include <Swiften/TLS/SimpleCertificate.h>
using namespace Swift;
@@ -20,6 +23,10 @@ using namespace Swift;
class CertificateTest : public CppUnit::TestFixture {
CPPUNIT_TEST_SUITE(CertificateTest);
CPPUNIT_TEST(testGetSHA1Fingerprint);
+#ifdef HAVE_OPENSSL
+ CPPUNIT_TEST(testParsingPEMCertificate);
+#endif
+ CPPUNIT_TEST(testParsingPEMPrivateKey);
CPPUNIT_TEST_SUITE_END();
public:
@@ -29,6 +36,108 @@ class CertificateTest : public CppUnit::TestFixture {
CPPUNIT_ASSERT_EQUAL(std::string("2f:b5:e1:34:19:fc:89:24:68:65:e7:a3:24:f4:76:ec:62:4e:87:40"), Certificate::getSHA1Fingerprint(testling, std::shared_ptr<CryptoProvider>(PlatformCryptoProvider::create()).get()));
}
+
+ void testParsingPEMCertificate() {
+ PlatformTLSFactories tlsFactories;
+
+ auto PEMCertificate = R"(-----BEGIN CERTIFICATE-----
+MIIEsjCCApoCCQCbkjlQfUqPtTANBgkqhkiG9w0BAQsFADAbMRkwFwYDVQQDDBBt
+b250YWd1ZS5leGFtcGxlMB4XDTE4MDExODE2NTMxMloXDTQ1MDYwNDE2NTMxMlow
+GzEZMBcGA1UEAwwQbW9udGFndWUuZXhhbXBsZTCCAiIwDQYJKoZIhvcNAQEBBQAD
+ggIPADCCAgoCggIBALAx5xuEYOjDJ9QHMvwRuDzxbHl1zR97SJFPAkmD8xH0sC61
+DNRyUvRq6UXb4znhqeqrNuZ9PV47GyK2Dpy/c/MY5NE3m/c+Z1tUnrcqyCkxITIn
+jdSI/elc9yhtMXX6lRi68BdPJnj/9+6trW0cWHlKEgnaSTAgCVB+4Dg9mjTIroCa
+HLoAKhr2zS7Ihs28aWb0cSvZ+qFgQhr7FGP2kfssTTQYyRPn9uHhtyWH6RqSv5x9
+BBGZWN5GtjkJvupcYWLt8ftuQyFpwEeEz5cgtiyKgsfz9CYvS9/OcwdLQr4z5Zq6
+eKxsog9GdwIi1Us4KR0X6tKu9ze42iEWF+foEWFP9/bjrVK/tt5lTSKnenk0nA8I
+amkG0btNAGihDti3lv60bGpd3B2/uh4gzzT2buXdf0EaybCt52MIr8xkwMU0Tkri
+RAYchdS8U8sekWG5Lg9m3L2BDa8/TKS/WflJhGcZeInGQslgIx7fCgO1M7Zr50pO
+wSURPXpvqUkXNEBy639UQEUsnBhntEQwZWx/6x6Ma/U5a5dL6qbtEJjlwIvS+nl9
+3w26g3DvWydNMCtZIVhgdrl+dZs+Uw5eA3QkHkDTSfYvQk7X5SYL0J5ZxwBvU9r1
+ED054+TAEuX2euiRA37xLhxonj8BaKkPQGlAHCLZaZPmNJWkNxElJhMoCfqBAgMB
+AAEwDQYJKoZIhvcNAQELBQADggIBAF+FNyW3nVeQeugBMksIhj7EMJl1AEKi0+78
+ZPsYX3CDbc/8GRZoTg/EWSiwPCBYc9VsxuKtODEYABCZgk7LnSBYoEauJDKWqkgM
+UOKPJI2hu7mIK7FJpjvEZe2MnRRA63oI/NVDJm8T2clrv/vPkY+ppsVl0toC0SpH
+/3dF5c65vYI19rTJraRU6kIrrgxFBzxzpn07LGh2rrOCQfy2umTSRMwz3ORAFfmi
++Kek1Dt7c+JVJ0ivCwhhc8MKza3JS2MuDfVWGnXtDLb81Ai0t4tQfLKvZEcgW+lh
+Drz9gv22buwncWL/IxtuhzyILtDSDKAYFbhfG6IAQut9BjMgpMnKrBCDlOLJl08K
+tgj2h7vUKyNSt3ndcSAtXjr6FD7+xPExJuyn/MgLONGGAZoZHFB4QO90wQaXxMPh
+7rnjUtzfLR8qkDmX8ZB4f4VOWpDWo4hBpgjTk0gYfzEkrh+zTqE9reh7CZ1WzwXO
+KnIBU2dZOE+XsJe49lW106DLqGzKRuQMUAwFMb7C7Nlg9GKTnvi41o+g6YE+MgxR
+uPKu891pCBtnDxZiWPT+7Fa/9UXxdIOTVIHW6utSWiWYbeNwXSmIm2ShfmNfWj9m
+x1JgJrFB6daWGR9aDBeDVRhgL6Z35lH7xI62pLJ4o2d2Y/9dUWAJfz5O8opeCyrF
+zqCzpwGL
+-----END CERTIFICATE-----
+)";
+
+ auto certificateChain = tlsFactories.getCertificateFactory()->createCertificateChain(createByteArray(PEMCertificate));
+ CPPUNIT_ASSERT_EQUAL(size_t(1), certificateChain.size());
+
+ auto certificate = certificateChain[0].get();
+ CPPUNIT_ASSERT_EQUAL(std::string("/CN=montague.example"), certificate->getSubjectName());
+ }
+
+ void testParsingPEMPrivateKey() {
+ PlatformTLSFactories tlsFactories;
+
+ auto PEMPrivateKey = R"(-----BEGIN PRIVATE KEY-----
+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
+5IMXuORWuoq0gXiCdEyko+saXsyWlqw=
+-----END PRIVATE KEY-----
+)";
+
+ auto privateKey = tlsFactories.getCertificateFactory()->createPrivateKey(createSafeByteArray(PEMPrivateKey));
+
+ CPPUNIT_ASSERT(privateKey != nullptr);
+ }
};
CPPUNIT_TEST_SUITE_REGISTRATION(CertificateTest);
diff --git a/Swiften/TLS/UnitTest/ClientServerTest.cpp b/Swiften/TLS/UnitTest/ClientServerTest.cpp
new file mode 100644
index 0000000..a356dcf
--- /dev/null
+++ b/Swiften/TLS/UnitTest/ClientServerTest.cpp
@@ -0,0 +1,883 @@
+/*
+ * Copyright (c) 2010-2019 Isode Limited.
+ * All rights reserved.
+ * See the COPYING file for more information.
+ */
+
+#include <map>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include <boost/variant.hpp>
+
+#include <gtest/gtest.h>
+
+#include <Swiften/Base/Log.h>
+#include <Swiften/TLS/CertificateFactory.h>
+#include <Swiften/TLS/PlatformTLSFactories.h>
+#include <Swiften/TLS/TLSContext.h>
+#include <Swiften/TLS/TLSContextFactory.h>
+#include <Swiften/TLS/TLSOptions.h>
+
+using namespace Swift;
+namespace {
+
+
+std::map<std::string, std::string> certificatePEM = {
+ {"montague.example",
+R"(-----BEGIN CERTIFICATE-----
+MIIEsjCCApoCCQCbkjlQfUqPtTANBgkqhkiG9w0BAQsFADAbMRkwFwYDVQQDDBBt
+b250YWd1ZS5leGFtcGxlMB4XDTE4MDExODE2NTMxMloXDTQ1MDYwNDE2NTMxMlow
+GzEZMBcGA1UEAwwQbW9udGFndWUuZXhhbXBsZTCCAiIwDQYJKoZIhvcNAQEBBQAD
+ggIPADCCAgoCggIBALAx5xuEYOjDJ9QHMvwRuDzxbHl1zR97SJFPAkmD8xH0sC61
+DNRyUvRq6UXb4znhqeqrNuZ9PV47GyK2Dpy/c/MY5NE3m/c+Z1tUnrcqyCkxITIn
+jdSI/elc9yhtMXX6lRi68BdPJnj/9+6trW0cWHlKEgnaSTAgCVB+4Dg9mjTIroCa
+HLoAKhr2zS7Ihs28aWb0cSvZ+qFgQhr7FGP2kfssTTQYyRPn9uHhtyWH6RqSv5x9
+BBGZWN5GtjkJvupcYWLt8ftuQyFpwEeEz5cgtiyKgsfz9CYvS9/OcwdLQr4z5Zq6
+eKxsog9GdwIi1Us4KR0X6tKu9ze42iEWF+foEWFP9/bjrVK/tt5lTSKnenk0nA8I
+amkG0btNAGihDti3lv60bGpd3B2/uh4gzzT2buXdf0EaybCt52MIr8xkwMU0Tkri
+RAYchdS8U8sekWG5Lg9m3L2BDa8/TKS/WflJhGcZeInGQslgIx7fCgO1M7Zr50pO
+wSURPXpvqUkXNEBy639UQEUsnBhntEQwZWx/6x6Ma/U5a5dL6qbtEJjlwIvS+nl9
+3w26g3DvWydNMCtZIVhgdrl+dZs+Uw5eA3QkHkDTSfYvQk7X5SYL0J5ZxwBvU9r1
+ED054+TAEuX2euiRA37xLhxonj8BaKkPQGlAHCLZaZPmNJWkNxElJhMoCfqBAgMB
+AAEwDQYJKoZIhvcNAQELBQADggIBAF+FNyW3nVeQeugBMksIhj7EMJl1AEKi0+78
+ZPsYX3CDbc/8GRZoTg/EWSiwPCBYc9VsxuKtODEYABCZgk7LnSBYoEauJDKWqkgM
+UOKPJI2hu7mIK7FJpjvEZe2MnRRA63oI/NVDJm8T2clrv/vPkY+ppsVl0toC0SpH
+/3dF5c65vYI19rTJraRU6kIrrgxFBzxzpn07LGh2rrOCQfy2umTSRMwz3ORAFfmi
++Kek1Dt7c+JVJ0ivCwhhc8MKza3JS2MuDfVWGnXtDLb81Ai0t4tQfLKvZEcgW+lh
+Drz9gv22buwncWL/IxtuhzyILtDSDKAYFbhfG6IAQut9BjMgpMnKrBCDlOLJl08K
+tgj2h7vUKyNSt3ndcSAtXjr6FD7+xPExJuyn/MgLONGGAZoZHFB4QO90wQaXxMPh
+7rnjUtzfLR8qkDmX8ZB4f4VOWpDWo4hBpgjTk0gYfzEkrh+zTqE9reh7CZ1WzwXO
+KnIBU2dZOE+XsJe49lW106DLqGzKRuQMUAwFMb7C7Nlg9GKTnvi41o+g6YE+MgxR
+uPKu891pCBtnDxZiWPT+7Fa/9UXxdIOTVIHW6utSWiWYbeNwXSmIm2ShfmNfWj9m
+x1JgJrFB6daWGR9aDBeDVRhgL6Z35lH7xI62pLJ4o2d2Y/9dUWAJfz5O8opeCyrF
+zqCzpwGL
+-----END CERTIFICATE-----
+)"},
+ {"capulet.example",
+R"(-----BEGIN CERTIFICATE-----
+MIIEsDCCApgCCQDUGdmqHfGngTANBgkqhkiG9w0BAQsFADAaMRgwFgYDVQQDDA9j
+YXB1bGV0LmV4YW1wbGUwHhcNMTgwMTE4MTY1NjEyWhcNNDUwNjA0MTY1NjEyWjAa
+MRgwFgYDVQQDDA9jYXB1bGV0LmV4YW1wbGUwggIiMA0GCSqGSIb3DQEBAQUAA4IC
+DwAwggIKAoICAQCgA/CpV7UGqUdjhEVoMjFIZ6Ca/C7utrVPAqBvE14eiurIhQrQ
+AmYeC9zA4/uXCdqkGq/a/RbF3OOCKLRUejCcynb0EnxiHxHa48ZsvLbKCK6guoXE
+pWnaZsmRpvJrBB2z6ohmxRuaDuc5CJT+Oq8AFPp3StTAFDo3Cju3fsGZPkNpCGbj
+ELwk4ok9INtEuTrMEmHZTD+VfjpXauUfN4ygKaPXJanCuxuifyin403BWgd8igkD
+oSCWxoDFMy3HGbh/NU+nJPJ23FxVC39RdDG3elvjNFpSZnALbiMnyor7YjF5TN93
+1ZWwn3VTnl7DnFquEbKbYyVFUzRII8Cd1TzKOL48rVLrCAqMXwm6YFtST6qPb9TZ
+0SX8qZGCwBfHV5OeS0ZgiBMMlmUPYcw9MlyvZnYyDPCOoPWmhEqd2gQzn//7hzb5
+mriCEyfcMzAqohylBNHXUVZTx5KcazJz6oOYdWEs1jfSXNKefgSWlgeoG2fgTXPN
+1OkQVS+FOiI0VCAIwR+vxhG3hVTz3kzXWvEt7M51faaHWWlnSzOrbSuj3f0ibS5J
+cj6ClyzOQRQDwzofyZ7oPWh6No/XkepVIn3HTTlnj1/8e6VsH+EBKSzoX2XvWPkO
+GAZEGHKiKh944u6d6mW37BPD2oKyusP3uPL5j2Fdm+m0HkP3/7yw+5EFVQIDAQAB
+MA0GCSqGSIb3DQEBCwUAA4ICAQCfCGK4iDo8H0i12wBC0+6GB9NBmgn8P09AUN0l
+tQjZPaqZCtLtBBqOrAaZQBQMfI0QAm5/h/VkhMvz5HQjqmQhI2drSDOmarYzOGgY
+yVItkEwtLEVhiGoeJ+L3Smosoqq6QFjcDzH7UzPTrJNspnY+09+m53pJggh41zzt
+1TOU+QasaB5oGopFaFUUlwjHAc7FpZgEd+tV6j0cpdT3GabVkkoLg01Z+0mqkpLD
+OjRBKJX8XvZ38VESsy3gWpcXnDq03n+8OgZo4R9SEcyyxjWbyb+qg2dzbQvRB2Va
+QNoXp5EzemXvFSulhR+TfDk2K1h45BurikRQxDi8LpBTUsCMwiqXdem68HOlTwLi
+/kMWbnVBcdurYcWVSwlJU4EJcTEdk51JStO1V0nAA0nCwn/iEhY8I6BitnrcCJ5e
+4CGVWr+zAm8DBjaFMTzy46Q5NcT0hwnHGN6T6l4aMcRggIK9anRbXCn6dSzma1pd
+R5N/Do00FTpyZGcUlVPnSlIfZVl7y/9XEO1n6xDJURrefL1JrM7UMyB17jA8HMq3
+S05kF7XRpludRB4QkAJt5BNNv6BPP7HPIKyR/rq94ONvzVPAo7uASyFE2sMBsfwP
+pXAI1LVolPCoUC13jEkKdmc8kMSxU+XtsvFryNhkfQtZfSg+nBRFYptFE7GrZ9WY
+GMSL4g==
+-----END CERTIFICATE-----
+)"},
+ {"casigned.example.com",
+R"(-----BEGIN CERTIFICATE-----
+MIIDYTCCAkmgAwIBAgIJAPEpYlUWlJW8MA0GCSqGSIb3DQEBCwUAMEAxCzAJBgNV
+BAYTAkdCMRAwDgYDVQQIDAdFbmdsYW5kMQ4wDAYDVQQKDAVJc29kZTEPMA0GA1UE
+AwwGVGVzdENBMB4XDTE4MDExODAwMDAwMFoXDTQ5MDExODAwMDAwMFowTjELMAkG
+A1UEBhMCR0IxEDAOBgNVBAgMB0VuZ2xhbmQxDjAMBgNVBAoMBUlzb2RlMR0wGwYD
+VQQDDBRjYXNpZ25lZC5leGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP
+ADCCAQoCggEBAKw4iCscGIDCc55mwxgJsn/v2GEorpE9zHYsMfl6px2aRLGgB8ki
+xYyNiEPA/b/ilShRp7pp22LPb5O3aOVVWzAs5FTvPdFY2vS8YIxR6XSKXO3u4Q8/
+w8CR0AbKpeCtL0TwSl1u76nq6ORMep6QGsTjDLQ7wUwdShL4qV4nIGxJWon/5GI0
+nl4Xr/wzmysbMjAMSiWOR90DNZMvqQBBhQcJdqdirc+whR8gOhIMvJBn5jlMuFEB
+zL/bR+kG/zFuD0EMsMTy6TETefYuxeWmhuzQENyAV5+1v48G/1a9zzN9Y0xQ6T0N
+ppbLzq+/zhxT1eBN8/O0bAGUzzk1VSFvouECAwEAAaNQME4wHQYDVR0OBBYEFCnR
+umlxjdaUvcpf77zahrDmPnt1MB8GA1UdIwQYMBaAFAbbhPAKtD5YzxQqGH2Um4/f
+OmdFMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAEPrTTKS+7A4dJJh
+gSAS44ZYyR8/kt9Y3V4qxJM2xrOrW6rM8N15n2i/osSy0AYHiPuvgDG+D7hLt5Ct
+7jZKrp3JRsBaU8//9k6+euwVyWS+EKo/eb7qtSNibZOk7GwrXekJMbRXz1cHTKnW
+0R8UG+EMkOqKQh5dhWuEBXff9SYLTALf0/i+/Ixl3b9rQ6zpY/7UwBdlWLaliM1i
+odIgVu1XRE6Su+bGMa1L5ArniN0LMFq9Mxag2H0V7Kru7vIvbBkL7U6tbi7u26hv
+q4kUFROW2U2C02FTmJeSEBvWoDyMoVjKkmaTL0+vDSQRPGtCsvCgaCXgOwU2Po9s
+8jjHQxg=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDUzCCAjugAwIBAgIJAPEpYlUWlJW7MA0GCSqGSIb3DQEBCwUAMEAxCzAJBgNV
+BAYTAkdCMRAwDgYDVQQIDAdFbmdsYW5kMQ4wDAYDVQQKDAVJc29kZTEPMA0GA1UE
+AwwGVGVzdENBMB4XDTE4MDExODAwMDAwMFoXDTQ5MDExODAwMDAwMFowQDELMAkG
+A1UEBhMCR0IxEDAOBgNVBAgMB0VuZ2xhbmQxDjAMBgNVBAoMBUlzb2RlMQ8wDQYD
+VQQDDAZUZXN0Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCiCA+9
+bdSHFNPOgbsnhrKbLJP+1lxWTe06+ExwJA3QgCKsR13MsAK8rhmuNB0k8ZIFJL+c
+yUOUgw0370eP940QAtK+HinJpvaGEZKEwaB8VUCjzFTb7zUBio1Y7B45aawEV3qT
+Lzcnil3f7vPIxnAoaptzPriySdggIsimj+y6AWum5FUPyfZzF7EnYfF/VH8V/ZNX
+fku7PPbaeDHvu4EF/0s8P+/l6jddz8dqbDMjH3GDM8sjryWPQPNDjKZ1x80BIdyq
+s9m/kXqIIySNwdIa/X+nYTMchUa5y46n2N4n0RA4F+rf+Ni1Cxk9Ejmmz5hinV+t
+Yzh6jsbB7yZiqEnzAgMBAAGjUDBOMB0GA1UdDgQWBBQG24TwCrQ+WM8UKhh9lJuP
+3zpnRTAfBgNVHSMEGDAWgBQG24TwCrQ+WM8UKhh9lJuP3zpnRTAMBgNVHRMEBTAD
+AQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBMHQ3WMEhkEc0VoOEw3mbsngt1eyJz+nFM
+FeGdRb7JSwpJfe/yBxyGodGyHsQJPVole6sUdq8S1QGT547PkgDHc6Btoq96Mahq
+brgfx1JL9a1F8qaqcENebHc7ltW8kps4xtdlithfo4nKaciYZ5mKbug+AFv569mb
+LU3F282dyW1dLa7+L8dGbb8Ntpnp0BB0Cotm4GX3Xi6Y+fikouqcmXj7vX+uMhG4
+pQpXqb2ML1Gev4w6XUsnls+OJok97x99NsOcqtwqsLJS/sg0cpwrgZ9+50hQKlHO
+8H6hhNjGBkvpIiNsWb9UOc+Id86J+drs9Ed93Eyyvwnq2XcqPGF3
+-----END CERTIFICATE-----)"}};
+std::map<std::string, std::string> privateKeyPEM = {
+ {"montague.example",
+R"(-----BEGIN PRIVATE KEY-----
+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
+5IMXuORWuoq0gXiCdEyko+saXsyWlqw=
+-----END PRIVATE KEY-----
+)"},{"capulet.example",
+R"(-----BEGIN PRIVATE KEY-----
+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
+dwiNVl130YWaJLjiA9Poc2llKtypfQ8=
+-----END PRIVATE KEY-----
+)"},
+ {"casigned.example.com",
+R"(-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCsOIgrHBiAwnOe
+ZsMYCbJ/79hhKK6RPcx2LDH5eqcdmkSxoAfJIsWMjYhDwP2/4pUoUae6adtiz2+T
+t2jlVVswLORU7z3RWNr0vGCMUel0ilzt7uEPP8PAkdAGyqXgrS9E8Epdbu+p6ujk
+THqekBrE4wy0O8FMHUoS+KleJyBsSVqJ/+RiNJ5eF6/8M5srGzIwDEoljkfdAzWT
+L6kAQYUHCXanYq3PsIUfIDoSDLyQZ+Y5TLhRAcy/20fpBv8xbg9BDLDE8ukxE3n2
+LsXlpobs0BDcgFeftb+PBv9Wvc8zfWNMUOk9DaaWy86vv84cU9XgTfPztGwBlM85
+NVUhb6LhAgMBAAECggEAP5KesUstwFoHU/GbEsSBn7hpp3Pc/MBTOUwXJoCgL81C
+jk8EkLrFMZ0NL/WNtcduR/PcdnOdCFyKU3zgu8dGYgIwRyoJJ74lFTJ0cVfLVbm9
+lBcZ/QcMu4kXwWoM0WMcj9YjnVnWHmMYlS2mFeIb/5HQnrdwIyisr1QbMSZAUbgn
+HOLSjkd0KUcXxwP9HfE/8zUFbUsv/wPHE9T5eWqZz7lKZRvgUyk3jYElpewxqPvR
+HPOq9BN87ft7rPzmmZ4JCU2tM5MEQONo3jT5pljTU/iz4pfs1HArHS+/zZbGcoiu
+AhODErhLucodwZ07MZsE9KAdGPGUeob4SgMwmkIiAQKBgQDYZ33DHOgYd5Fa7Tqv
+TC77Nbpjmnk5lyoInNf+7pxBTgqIuKbpS6Ctq76nHI0qerNfd44LqojQEJ9S5rbL
+3s+HR+pypx3DyysE92uLaVsF4rfwZeEpJyQ1Q9NGbsKVb2nNrdGs3LZhG5PL+JQT
+8lS9h/sMIbh5Gd/Dkt8a2HlTawKBgQDLu3YdXWeWUOcq63YrLFDO+RUGsCe1cf+2
+3wTmMRgxfzpAPDZKymUPwrJNd1ddCQiRVKg91pLhlDl8B89Q4iWdOpYnE6vrnw3w
+6iEdEyA6IauvEvD3JPXHGtVHKBS/wz06wQVGzsgMvVun1w32SDzTGZ/somvkN1UE
+BTMRVqXB4wKBgCgkgnq4GQG45FjGmtU7v5dP7yRc2lHpjZ2NYq/imU+v51pXTncG
+VWcB7drX/AO+QRnbdpkJaFmYS8W4EH6e9UY1+/KKt4XDYVaJgwSYZY8g8FgCvhLB
+NQFAdKejEitgQHMr5DI5uwcmc3MpyGYu1LNXzyT/+FVWjGCeFQ7IyzDDAoGAXK9I
++ez/bkQaFs2OMe/0R2TL6wIC3qohMpndSbf2ELaX0pdR/856EaR6p4FujlXIvw0r
+iQ5wPg3H5SRMbbikchYwL1DkusnedsadD/QeijO9PmW4mEcJRW5wrvGOFvuEhJFB
+jEgQfLM82muc33lXqpYSyn3N5cJCfBS9edrYiA8CgYEA0Tsa4KvwzmBXzy2jexUT
+PPYtV9S7hS0D7Owf5SBI1wtWczOu3Y6DsCG4mbDi/tvo1dxC9d5Ns8RXHH9mtdyn
+8VUuSu24lk7VZ0OP5/byiAGqC2urDFGe0iBqePdb+3uS1g132LzAJm4RqBYw4Xjd
+P6n9b0V3iodcFLMTy1ShJ+I=
+-----END PRIVATE KEY-----
+)"}};
+
+auto montagueEncryptedPEM = R"(-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-256-CBC,6B0A69362920334824667C1B6207279B
+
+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
+-----END RSA PRIVATE KEY-----
+)";
+
+auto dhParamsOpenSslDer1024 = R"(-----BEGIN DH PARAMETERS-----
+MIGHAoGBANjw4f5+gu8b8X8O6ALyJA1tH9TQTWZEI3YjUKQ1m0dVBMJ6XDC7FLJn
+gqE4hIGcm1FAWwIiuo0uMufqyVwFT2c+G8j4JHWK5z1tEP+GaqiO34N0cUo86qHp
+THSkJN7LuHbYRqI9fHWDZocW/5yAsDq5RPUCjFZAoh1BWdfDFfrjAgEC
+-----END DH PARAMETERS-----
+)";
+auto dhParamsOpenSslDer2048 = R"(-----BEGIN DH PARAMETERS-----
+MIIBCAKCAQEA0Q6vD5qtrh3XEkVkbN29ord/k3sgo2Q3PiFmyFt89qqVbebEzYmt
+t8DwaFGMcGlyKs4Lb1s7vocm9y3M9C0FZm85Muvv8WCbLZVZ+wfEtMibOjgRoDqt
+p7Qqe7/iPgMVrSjWegVkg3V8K8dnPpohPClM0yOe4NpBjSVNgBVJRpEtH8gFiCor
+H7hw63HpN/MgFdkjZNeCN+erv8p673xH8LrN98gQpkdQ9vCqYt1dHxF2XZcxBp8x
+XganwPeGgQosofkA6nVB70hyjwjEyxnHJZIMlx6DPXWC7X6ed0SazgH0sQNdACvG
+uU1zHCVIv6/f0adKrJg0s1jrM3qWZ6HmUwIBAg==
+-----END DH PARAMETERS-----
+)";
+
+auto createTLSContext = [](TLSContext::Mode mode) {
+ auto tlsFactories = std::make_shared<PlatformTLSFactories>();
+
+ auto tlsContextFactory = tlsFactories->getTLSContextFactory();
+
+ auto tlsContext = std::unique_ptr<TLSContext>(tlsContextFactory->createTLSContext({}, mode));
+ return tlsContext;
+};
+
+// This connects a TLSContext to another TLSContext
+class ClientServerConnector {
+ public:
+ ClientServerConnector(TLSContext* client, TLSContext* server) : clientContext_(client), serverContext_(server) {
+ connections_.push_back(clientContext_->onDataForNetwork.connect([&](const SafeByteArray& data) {
+ serverContext_->handleDataFromNetwork(data);
+ }));
+ connections_.push_back(serverContext_->onDataForNetwork.connect([&](const SafeByteArray& data) {
+ clientContext_->handleDataFromNetwork(data);
+ }));
+ }
+
+ private:
+ TLSContext* clientContext_;
+ TLSContext* serverContext_;
+ std::vector<boost::signals2::connection> connections_;
+};
+
+struct TLSDataForNetwork {
+ SafeByteArray data;
+};
+
+struct TLSDataForApplication {
+ SafeByteArray data;
+};
+
+struct TLSFault {
+ std::shared_ptr<Swift::TLSError> error;
+};
+
+struct TLSConnected {
+ std::vector<Certificate::ref> chain;
+};
+
+struct TLSServerNameRequested {
+ std::string name;
+};
+
+using TLSEvent = boost::variant<TLSDataForNetwork, TLSDataForApplication, TLSFault, TLSConnected, TLSServerNameRequested>;
+
+class TLSEventToSafeByteArrayVisitor : public boost::static_visitor<SafeByteArray> {
+ public:
+ SafeByteArray operator()(const TLSDataForNetwork& tlsData) const {
+ return tlsData.data;
+ }
+
+ SafeByteArray operator()(const TLSDataForApplication& tlsData) const {
+ return tlsData.data;
+ }
+
+ SafeByteArray operator()(const TLSFault&) const {
+ return createSafeByteArray("");
+ }
+
+ SafeByteArray operator()(const TLSConnected&) const {
+ return createSafeByteArray("");
+ }
+
+ SafeByteArray operator()(const TLSServerNameRequested&) const {
+ return createSafeByteArray("");
+ }
+};
+
+class TLSEventToStringVisitor : public boost::static_visitor<std::string> {
+ public:
+ std::string operator()(const TLSDataForNetwork& event) const {
+ return std::string("TLSDataForNetwork(") + "size: " + std::to_string(event.data.size()) + ")";
+ }
+
+ std::string operator()(const TLSDataForApplication& event) const {
+ return std::string("TLSDataForApplication(") + "size: " + std::to_string(event.data.size()) + ")";
+ }
+
+ std::string operator()(const TLSFault&) const {
+ return "TLSFault()";
+ }
+
+ std::string operator()(const TLSConnected& event) const {
+ std::string certificates;
+ for (auto cert : event.chain) {
+ certificates += "\t" + cert->getSubjectName() + "\n";
+ }
+ return std::string("TLSConnected()") + "\n" + certificates;
+ }
+
+ std::string operator()(const TLSServerNameRequested& event) const {
+ return std::string("TLSServerNameRequested(") + "name: " + event.name + ")";
+ }
+};
+
+class TLSClientServerEventHistory {
+ public:
+ TLSClientServerEventHistory(TLSContext* client, TLSContext* server) {
+ connectContext(std::string("client"), client);
+ connectContext(std::string("server"), server);
+ }
+
+ __attribute__((unused))
+ void print() {
+ auto count = 0;
+ std::cout << "\n";
+ for (auto event : events) {
+ if (event.first == "server") {
+ std::cout << std::string(80, ' ');
+ }
+ std::cout << count << ". ";
+ std::cout << event.first << " : " << boost::apply_visitor(TLSEventToStringVisitor(), event.second) << std::endl;
+ count++;
+ }
+ }
+
+ template<class TLSEventType>
+ boost::optional<TLSEventType> getEvent(const std::string& peer, size_t number = 0) {
+ for (const auto& pair : events) {
+ if (pair.first == peer) {
+ if (pair.second.type() == typeid(TLSEventType)) {
+ if (number == 0) {
+ return boost::optional<TLSEventType>(boost::get<TLSEventType>(pair.second));
+ }
+ else {
+ number--;
+ }
+ }
+ }
+ }
+ return {};
+ }
+
+ private:
+ void connectContext(const std::string& name, TLSContext* context) {
+ connections_.push_back(context->onDataForNetwork.connect([=](const SafeByteArray& data) {
+ events.push_back(std::pair<std::string, TLSEvent>(name, TLSDataForNetwork{data}));
+ }));
+ connections_.push_back(context->onDataForApplication.connect([=](const SafeByteArray& data) {
+ events.push_back(std::pair<std::string, TLSEvent>(name, TLSDataForApplication{data}));
+ }));
+ connections_.push_back(context->onError.connect([=](std::shared_ptr<Swift::TLSError> error) {
+ events.push_back(std::pair<std::string, TLSEvent>(name, TLSFault{error}));
+ }));
+ connections_.push_back(context->onConnected.connect([=](){
+ events.push_back(std::pair<std::string, TLSEvent>(name, TLSConnected{context->getPeerCertificateChain()}));
+ }));
+ }
+
+ public:
+ std::vector<std::pair<std::string, TLSEvent>> events;
+
+ private:
+ std::vector<boost::signals2::connection> connections_;
+};
+
+}
+
+TEST(ClientServerTest, testInitAndFreeContext) {
+ auto tlsClientContext = createTLSContext(TLSContext::Mode::Client);
+ auto tlsServerContext = createTLSContext(TLSContext::Mode::Server);
+}
+
+TEST(ClientServerTest, testServerSetPrivateKey) {
+ auto tlsServerContext = createTLSContext(TLSContext::Mode::Server);
+}
+
+TEST(ClientServerTest, testServerSetCertificateChain) {
+ auto tlsServerContext = createTLSContext(TLSContext::Mode::Server);
+}
+
+TEST(ClientServerTest, testClientServerBasicCommunicationWithCertificateMissing) {
+ auto clientContext = createTLSContext(TLSContext::Mode::Client);
+ auto serverContext = createTLSContext(TLSContext::Mode::Server);
+
+ TLSClientServerEventHistory events(clientContext.get(), serverContext.get());
+
+ ClientServerConnector connector(clientContext.get(), serverContext.get());
+
+ auto tlsFactories = std::make_shared<PlatformTLSFactories>();
+
+ auto privateKey = tlsFactories->getCertificateFactory()->createPrivateKey(createSafeByteArray(privateKeyPEM["capulet.example"]));
+ ASSERT_NE(nullptr, privateKey.get());
+ ASSERT_EQ(true, serverContext->setPrivateKey(privateKey));
+
+ serverContext->accept();
+ clientContext->connect();
+
+ ASSERT_EQ("server", events.events[1].first);
+ ASSERT_EQ("TLSFault()", boost::apply_visitor(TLSEventToStringVisitor(), events.events[1].second));
+}
+
+TEST(ClientServerTest, testClientServerBasicCommunicationWithPrivateKeyMissing) {
+ auto clientContext = createTLSContext(TLSContext::Mode::Client);
+ auto serverContext = createTLSContext(TLSContext::Mode::Server);
+
+ TLSClientServerEventHistory events(clientContext.get(), serverContext.get());
+
+ ClientServerConnector connector(clientContext.get(), serverContext.get());
+
+ auto tlsFactories = std::make_shared<PlatformTLSFactories>();
+
+ ASSERT_EQ(true, serverContext->setCertificateChain(tlsFactories->getCertificateFactory()->createCertificateChain(createByteArray(certificatePEM["capulet.example"]))));
+
+ serverContext->accept();
+ clientContext->connect();
+
+ ASSERT_EQ("server", events.events[1].first);
+ ASSERT_EQ("TLSFault()", boost::apply_visitor(TLSEventToStringVisitor(), events.events[1].second));
+}
+
+TEST(ClientServerTest, testWrongPrivateKeyAfterCertificate) {
+ auto clientContext = createTLSContext(TLSContext::Mode::Client);
+ auto serverContext = createTLSContext(TLSContext::Mode::Server);
+
+ TLSClientServerEventHistory events(clientContext.get(), serverContext.get());
+
+ ClientServerConnector connector(clientContext.get(), serverContext.get());
+
+ auto tlsFactories = std::make_shared<PlatformTLSFactories>();
+
+ ASSERT_EQ(true, serverContext->setCertificateChain(tlsFactories->getCertificateFactory()->createCertificateChain(createByteArray(certificatePEM["capulet.example"]))));
+
+ auto privateKey = tlsFactories->getCertificateFactory()->createPrivateKey(createSafeByteArray(privateKeyPEM["montague.example"]));
+ ASSERT_NE(nullptr, privateKey.get());
+ ASSERT_EQ(false, serverContext->setPrivateKey(privateKey));
+
+ serverContext->accept();
+ clientContext->connect();
+}
+
+TEST(ClientServerTest, testWrongCertificateAfterPrivateKey) {
+ auto clientContext = createTLSContext(TLSContext::Mode::Client);
+ auto serverContext = createTLSContext(TLSContext::Mode::Server);
+
+ TLSClientServerEventHistory events(clientContext.get(), serverContext.get());
+
+ ClientServerConnector connector(clientContext.get(), serverContext.get());
+
+ auto tlsFactories = std::make_shared<PlatformTLSFactories>();
+
+ auto privateKey = tlsFactories->getCertificateFactory()->createPrivateKey(createSafeByteArray(privateKeyPEM["montague.example"]));
+ ASSERT_NE(nullptr, privateKey.get());
+ ASSERT_EQ(true, serverContext->setPrivateKey(privateKey));
+
+ ASSERT_EQ(true, serverContext->setCertificateChain(tlsFactories->getCertificateFactory()->createCertificateChain(createByteArray(certificatePEM["capulet.example"]))));
+
+ serverContext->accept();
+ clientContext->connect();
+
+ ASSERT_EQ("server", events.events[1].first);
+ ASSERT_EQ("TLSFault()", boost::apply_visitor(TLSEventToStringVisitor(), events.events[1].second));
+}
+
+TEST(ClientServerTest, testClientServerBasicCommunication) {
+ auto clientContext = createTLSContext(TLSContext::Mode::Client);
+ auto serverContext = createTLSContext(TLSContext::Mode::Server);
+
+ TLSClientServerEventHistory events(clientContext.get(), serverContext.get());
+
+ ClientServerConnector connector(clientContext.get(), serverContext.get());
+
+ auto tlsFactories = std::make_shared<PlatformTLSFactories>();
+
+ ASSERT_EQ(true, serverContext->setCertificateChain(tlsFactories->getCertificateFactory()->createCertificateChain(createByteArray(certificatePEM["capulet.example"]))));
+
+ auto privateKey = tlsFactories->getCertificateFactory()->createPrivateKey(createSafeByteArray(privateKeyPEM["capulet.example"]));
+ ASSERT_NE(nullptr, privateKey.get());
+ ASSERT_EQ(true, serverContext->setPrivateKey(privateKey));
+
+ serverContext->accept();
+ clientContext->connect();
+
+ clientContext->handleDataFromApplication(createSafeByteArray("This is a test message from the client."));
+ serverContext->handleDataFromApplication(createSafeByteArray("This is a test message from the server."));
+
+ auto firstMessageFromClient = events.getEvent<TLSDataForApplication>("server");
+ ASSERT_EQ(true, firstMessageFromClient.is_initialized());
+ ASSERT_EQ(safeByteArrayToString(createSafeByteArray("This is a test message from the client.")), safeByteArrayToString(firstMessageFromClient->data));
+ auto firstMessageFromServer = events.getEvent<TLSDataForApplication>("client");
+ ASSERT_EQ(true, firstMessageFromServer.is_initialized());
+ ASSERT_EQ(safeByteArrayToString(createSafeByteArray("This is a test message from the server.")), safeByteArrayToString(firstMessageFromServer->data));
+}
+
+TEST(ClientServerTest, testClientServerBasicCommunicationEncryptedPrivateKeyRightPassword) {
+ auto clientContext = createTLSContext(TLSContext::Mode::Client);
+ auto serverContext = createTLSContext(TLSContext::Mode::Server);
+
+ TLSClientServerEventHistory events(clientContext.get(), serverContext.get());
+
+ ClientServerConnector connector(clientContext.get(), serverContext.get());
+
+ auto tlsFactories = std::make_shared<PlatformTLSFactories>();
+
+ ASSERT_EQ(true, serverContext->setCertificateChain(tlsFactories->getCertificateFactory()->createCertificateChain(createByteArray(certificatePEM["montague.example"]))));
+
+ auto privateKey = tlsFactories->getCertificateFactory()->createPrivateKey(createSafeByteArray(montagueEncryptedPEM), createSafeByteArray("test"));
+ ASSERT_NE(nullptr, privateKey.get());
+ ASSERT_EQ(true, serverContext->setPrivateKey(privateKey));
+
+ serverContext->accept();
+ clientContext->connect();
+
+ clientContext->handleDataFromApplication(createSafeByteArray("This is a test message from the client."));
+ serverContext->handleDataFromApplication(createSafeByteArray("This is a test message from the server."));
+
+ auto firstMessageFromClient = events.getEvent<TLSDataForApplication>("server");
+ ASSERT_EQ(true, firstMessageFromClient.is_initialized());
+ ASSERT_EQ(safeByteArrayToString(createSafeByteArray("This is a test message from the client.")), safeByteArrayToString(firstMessageFromClient->data));
+ auto firstMessageFromServer = events.getEvent<TLSDataForApplication>("client");
+ ASSERT_EQ(true, firstMessageFromServer.is_initialized());
+ ASSERT_EQ(safeByteArrayToString(createSafeByteArray("This is a test message from the server.")), safeByteArrayToString(firstMessageFromServer->data));
+}
+
+TEST(ClientServerTest, testClientServerBasicCommunicationWithChainedCert) {
+ auto clientContext = createTLSContext(TLSContext::Mode::Client);
+ auto serverContext = createTLSContext(TLSContext::Mode::Server);
+
+ TLSClientServerEventHistory events(clientContext.get(), serverContext.get());
+
+ ClientServerConnector connector(clientContext.get(), serverContext.get());
+
+ auto tlsFactories = std::make_shared<PlatformTLSFactories>();
+
+ ASSERT_TRUE(serverContext->setCertificateChain(tlsFactories->getCertificateFactory()->createCertificateChain(createByteArray(certificatePEM["casigned.example.com"]))));
+
+ auto privateKey = tlsFactories->getCertificateFactory()->createPrivateKey(createSafeByteArray(privateKeyPEM["casigned.example.com"]));
+ ASSERT_NE(nullptr, privateKey.get());
+ ASSERT_TRUE(serverContext->setPrivateKey(privateKey));
+
+ serverContext->accept();
+ clientContext->connect();
+
+ clientContext->handleDataFromApplication(createSafeByteArray("This is a test message from the client."));
+ serverContext->handleDataFromApplication(createSafeByteArray("This is a test message from the server."));
+
+ auto serverEvent = std::find_if(events.events.begin(), events.events.end(), [](std::pair<std::string, TLSEvent>& event){
+ return event.first == "server" && (event.second.type() == typeid(TLSDataForApplication));
+ });
+ ASSERT_NE(events.events.end(), serverEvent);
+ ASSERT_EQ(safeByteArrayToString(createSafeByteArray("This is a test message from the client.")), safeByteArrayToString(boost::apply_visitor(TLSEventToSafeByteArrayVisitor(), serverEvent->second)));
+ auto clientEvent = std::find_if(events.events.begin(), events.events.end(), [](std::pair<std::string, TLSEvent>& event){
+ return event.first == "client" && (event.second.type() == typeid(TLSDataForApplication));
+ });
+ ASSERT_NE(events.events.end(), clientEvent);
+ ASSERT_EQ(safeByteArrayToString(createSafeByteArray("This is a test message from the server.")), safeByteArrayToString(boost::apply_visitor(TLSEventToSafeByteArrayVisitor(), clientEvent->second)));
+}
+
+TEST(ClientServerTest, testSettingPrivateKeyWithWrongPassword) {
+ auto clientContext = createTLSContext(TLSContext::Mode::Client);
+ auto serverContext = createTLSContext(TLSContext::Mode::Server);
+
+ TLSClientServerEventHistory events(clientContext.get(), serverContext.get());
+
+ ClientServerConnector connector(clientContext.get(), serverContext.get());
+
+ auto tlsFactories = std::make_shared<PlatformTLSFactories>();
+
+ ASSERT_EQ(true, serverContext->setCertificateChain(tlsFactories->getCertificateFactory()->createCertificateChain(createByteArray(certificatePEM["montague.example"]))));
+
+ auto privateKey = tlsFactories->getCertificateFactory()->createPrivateKey(createSafeByteArray(montagueEncryptedPEM), createSafeByteArray("foo"));
+ ASSERT_NE(nullptr, privateKey.get());
+ ASSERT_EQ(false, serverContext->setPrivateKey(privateKey));
+}
+
+TEST(ClientServerTest, testSettingPrivateKeyWithoutRequiredPassword) {
+ auto clientContext = createTLSContext(TLSContext::Mode::Client);
+ auto serverContext = createTLSContext(TLSContext::Mode::Server);
+
+ TLSClientServerEventHistory events(clientContext.get(), serverContext.get());
+
+ ClientServerConnector connector(clientContext.get(), serverContext.get());
+
+ auto tlsFactories = std::make_shared<PlatformTLSFactories>();
+
+ ASSERT_EQ(true, serverContext->setCertificateChain(tlsFactories->getCertificateFactory()->createCertificateChain(createByteArray(certificatePEM["montague.example"]))));
+
+ auto privateKey = tlsFactories->getCertificateFactory()->createPrivateKey(createSafeByteArray(montagueEncryptedPEM));
+ ASSERT_NE(nullptr, privateKey.get());
+ ASSERT_EQ(false, serverContext->setPrivateKey(privateKey));
+}
+
+TEST(ClientServerTest, testClientServerSNIRequestedHostAvailable) {
+ auto tlsFactories = std::make_shared<PlatformTLSFactories>();
+ auto clientContext = createTLSContext(TLSContext::Mode::Client);
+ auto serverContext = createTLSContext(TLSContext::Mode::Server);
+
+ serverContext->onServerNameRequested.connect([&](const std::string& requestedName) {
+ if (certificatePEM.find(requestedName) != certificatePEM.end() && privateKeyPEM.find(requestedName) != privateKeyPEM.end()) {
+ auto certChain = tlsFactories->getCertificateFactory()->createCertificateChain(createByteArray(certificatePEM[requestedName]));
+ ASSERT_EQ(true, serverContext->setCertificateChain(std::move(certChain)));
+
+ auto privateKey = tlsFactories->getCertificateFactory()->createPrivateKey(createSafeByteArray(privateKeyPEM[requestedName]));
+ ASSERT_NE(nullptr, privateKey.get());
+ ASSERT_EQ(true, serverContext->setPrivateKey(privateKey));
+ }
+ });
+
+ TLSClientServerEventHistory events(clientContext.get(), serverContext.get());
+
+ ClientServerConnector connector(clientContext.get(), serverContext.get());
+
+ ASSERT_EQ(true, serverContext->setCertificateChain(tlsFactories->getCertificateFactory()->createCertificateChain(createByteArray(certificatePEM["capulet.example"]))));
+
+ auto privateKey = tlsFactories->getCertificateFactory()->createPrivateKey(createSafeByteArray(privateKeyPEM["capulet.example"]));
+ ASSERT_NE(nullptr, privateKey.get());
+ ASSERT_EQ(true, serverContext->setPrivateKey(privateKey));
+
+ serverContext->accept();
+ clientContext->connect("montague.example");
+
+ clientContext->handleDataFromApplication(createSafeByteArray("This is a test message from the client."));
+ serverContext->handleDataFromApplication(createSafeByteArray("This is a test message from the server."));
+
+ auto firstMessageFromClient = events.getEvent<TLSDataForApplication>("server");
+ ASSERT_EQ(true, firstMessageFromClient.is_initialized());
+ ASSERT_EQ(safeByteArrayToString(createSafeByteArray("This is a test message from the client.")), safeByteArrayToString(firstMessageFromClient->data));
+ auto firstMessageFromServer = events.getEvent<TLSDataForApplication>("client");
+ ASSERT_EQ(true, firstMessageFromServer.is_initialized());
+ ASSERT_EQ(safeByteArrayToString(createSafeByteArray("This is a test message from the server.")), safeByteArrayToString(firstMessageFromServer->data));
+
+ ASSERT_EQ("/CN=montague.example", events.getEvent<TLSConnected>("client")->chain[0]->getSubjectName());
+}
+
+TEST(ClientServerTest, testClientServerSNIRequestedHostUnavailable) {
+ auto tlsFactories = std::make_shared<PlatformTLSFactories>();
+ auto clientContext = createTLSContext(TLSContext::Mode::Client);
+ auto serverContext = createTLSContext(TLSContext::Mode::Server);
+
+ serverContext->onServerNameRequested.connect([&](const std::string&) {
+ serverContext->setAbortTLSHandshake(true);
+ });
+
+ TLSClientServerEventHistory events(clientContext.get(), serverContext.get());
+
+ ClientServerConnector connector(clientContext.get(), serverContext.get());
+
+ ASSERT_EQ(true, serverContext->setCertificateChain(tlsFactories->getCertificateFactory()->createCertificateChain(createByteArray(certificatePEM["capulet.example"]))));
+
+ auto privateKey = tlsFactories->getCertificateFactory()->createPrivateKey(createSafeByteArray(privateKeyPEM["capulet.example"]));
+ ASSERT_NE(nullptr, privateKey.get());
+ ASSERT_EQ(true, serverContext->setPrivateKey(privateKey));
+
+ serverContext->accept();
+ clientContext->connect("montague.example");
+
+ ASSERT_EQ("server", events.events[1].first);
+ ASSERT_EQ("TLSFault()", boost::apply_visitor(TLSEventToStringVisitor(), events.events[1].second));
+
+ ASSERT_EQ("client", events.events[3].first);
+ ASSERT_EQ("TLSFault()", boost::apply_visitor(TLSEventToStringVisitor(), events.events[3].second));
+}
+
+TEST(ClientServerTest, testClientServerEqualFinishedMessage) {
+ auto clientContext = createTLSContext(TLSContext::Mode::Client);
+ auto serverContext = createTLSContext(TLSContext::Mode::Server);
+
+ TLSClientServerEventHistory events(clientContext.get(), serverContext.get());
+
+ ClientServerConnector connector(clientContext.get(), serverContext.get());
+
+ auto tlsFactories = std::make_shared<PlatformTLSFactories>();
+
+ ASSERT_EQ(true, serverContext->setCertificateChain(tlsFactories->getCertificateFactory()->createCertificateChain(createByteArray(certificatePEM["capulet.example"]))));
+
+ auto privateKey = tlsFactories->getCertificateFactory()->createPrivateKey(createSafeByteArray(privateKeyPEM["capulet.example"]));
+ ASSERT_NE(nullptr, privateKey.get());
+ ASSERT_EQ(true, serverContext->setPrivateKey(privateKey));
+
+ serverContext->accept();
+ clientContext->connect();
+
+ ASSERT_EQ(serverContext->getPeerFinishMessage(), clientContext->getFinishMessage());
+ ASSERT_EQ(clientContext->getPeerFinishMessage(), serverContext->getFinishMessage());
+}
+
+TEST(ClientServerTest, testClientServerBasicCommunicationWith2048BitDHParams) {
+ auto clientContext = createTLSContext(TLSContext::Mode::Client);
+ auto serverContext = createTLSContext(TLSContext::Mode::Server);
+
+ TLSClientServerEventHistory events(clientContext.get(), serverContext.get());
+
+ ClientServerConnector connector(clientContext.get(), serverContext.get());
+
+ auto tlsFactories = std::make_shared<PlatformTLSFactories>();
+
+ ASSERT_EQ(true, serverContext->setCertificateChain(tlsFactories->getCertificateFactory()->createCertificateChain(createByteArray(certificatePEM["capulet.example"]))));
+
+ auto privateKey = tlsFactories->getCertificateFactory()->createPrivateKey(createSafeByteArray(privateKeyPEM["capulet.example"]));
+ ASSERT_NE(nullptr, privateKey.get());
+ ASSERT_EQ(true, serverContext->setPrivateKey(privateKey));
+
+ ASSERT_EQ(true, serverContext->setDiffieHellmanParameters(tlsFactories->getTLSContextFactory()->convertDHParametersFromPEMToDER(dhParamsOpenSslDer2048)));
+
+ serverContext->accept();
+ clientContext->connect();
+
+ clientContext->handleDataFromApplication(createSafeByteArray("This is a test message from the client."));
+ serverContext->handleDataFromApplication(createSafeByteArray("This is a test message from the server."));
+
+ auto firstMessageFromClient = events.getEvent<TLSDataForApplication>("server");
+ ASSERT_EQ(true, firstMessageFromClient.is_initialized());
+ ASSERT_EQ(safeByteArrayToString(createSafeByteArray("This is a test message from the client.")), safeByteArrayToString(firstMessageFromClient->data));
+ auto firstMessageFromServer = events.getEvent<TLSDataForApplication>("client");
+ ASSERT_EQ(true, firstMessageFromServer.is_initialized());
+ ASSERT_EQ(safeByteArrayToString(createSafeByteArray("This is a test message from the server.")), safeByteArrayToString(firstMessageFromServer->data));
+}
+
+TEST(ClientServerTest, testClientServerBasicCommunicationWith1024BitDHParams) {
+ auto clientContext = createTLSContext(TLSContext::Mode::Client);
+ auto serverContext = createTLSContext(TLSContext::Mode::Server);
+
+ TLSClientServerEventHistory events(clientContext.get(), serverContext.get());
+
+ ClientServerConnector connector(clientContext.get(), serverContext.get());
+
+ auto tlsFactories = std::make_shared<PlatformTLSFactories>();
+
+ ASSERT_EQ(true, serverContext->setCertificateChain(tlsFactories->getCertificateFactory()->createCertificateChain(createByteArray(certificatePEM["capulet.example"]))));
+
+ auto privateKey = tlsFactories->getCertificateFactory()->createPrivateKey(createSafeByteArray(privateKeyPEM["capulet.example"]));
+ ASSERT_NE(nullptr, privateKey.get());
+ ASSERT_EQ(true, serverContext->setPrivateKey(privateKey));
+
+ ASSERT_EQ(true, serverContext->setDiffieHellmanParameters(tlsFactories->getTLSContextFactory()->convertDHParametersFromPEMToDER(dhParamsOpenSslDer1024)));
+
+ serverContext->accept();
+ clientContext->connect();
+
+ clientContext->handleDataFromApplication(createSafeByteArray("This is a test message from the client."));
+ serverContext->handleDataFromApplication(createSafeByteArray("This is a test message from the server."));
+
+ auto firstMessageFromClient = events.getEvent<TLSDataForApplication>("server");
+ ASSERT_EQ(true, firstMessageFromClient.is_initialized());
+ ASSERT_EQ(safeByteArrayToString(createSafeByteArray("This is a test message from the client.")), safeByteArrayToString(firstMessageFromClient->data));
+ auto firstMessageFromServer = events.getEvent<TLSDataForApplication>("client");
+ ASSERT_EQ(true, firstMessageFromServer.is_initialized());
+ ASSERT_EQ(safeByteArrayToString(createSafeByteArray("This is a test message from the server.")), safeByteArrayToString(firstMessageFromServer->data));
+}
diff --git a/Swiften/TLS/UnitTest/ServerIdentityVerifierTest.cpp b/Swiften/TLS/UnitTest/ServerIdentityVerifierTest.cpp
index 30fe423..47f3db2 100644
--- a/Swiften/TLS/UnitTest/ServerIdentityVerifierTest.cpp
+++ b/Swiften/TLS/UnitTest/ServerIdentityVerifierTest.cpp
@@ -35,6 +35,8 @@ class ServerIdentityVerifierTest : public CppUnit::TestFixture {
CPPUNIT_TEST(testCertificateVerifies_WithMatchingInternationalXmppAddr);
CPPUNIT_TEST(testCertificateVerifies_WithMatchingCNWithoutSAN);
CPPUNIT_TEST(testCertificateVerifies_WithMatchingCNWithSAN);
+ CPPUNIT_TEST(testCertificateVerifies_WithMatchingSRVNameWithServerExpected);
+ CPPUNIT_TEST(testCertificateVerifies_WithMatchingSRVNameWithClientUnexpected);
CPPUNIT_TEST_SUITE_END();
public:
@@ -58,6 +60,14 @@ class ServerIdentityVerifierTest : public CppUnit::TestFixture {
CPPUNIT_ASSERT(testling.certificateVerifies(certificate));
}
+ void testCertificateVerifies_WithMatchingDNSNameMixedCase() {
+ ServerIdentityVerifier testling(JID("foo@baR.com/baz"), idnConverter.get());
+ SimpleCertificate::ref certificate(new SimpleCertificate());
+ certificate->addDNSName("Bar.com");
+
+ CPPUNIT_ASSERT(testling.certificateVerifies(certificate));
+ }
+
void testCertificateVerifies_WithSecondMatchingDNSName() {
ServerIdentityVerifier testling(JID("foo@bar.com/baz"), idnConverter.get());
SimpleCertificate::ref certificate(new SimpleCertificate());
@@ -131,6 +141,24 @@ class ServerIdentityVerifierTest : public CppUnit::TestFixture {
CPPUNIT_ASSERT(!testling.certificateVerifies(certificate));
}
+ void testCertificateVerifies_WithMatchingSRVNameWithServerExpected() {
+ // Server-mode test which gets cert with "xmpp-server" SRV name
+ ServerIdentityVerifier testling(JID("foo@bar.com/baz"), idnConverter.get(), true);
+ SimpleCertificate::ref certificate(new SimpleCertificate());
+ certificate->addSRVName("_xmpp-server.bar.com");
+
+ CPPUNIT_ASSERT(testling.certificateVerifies(certificate));
+ }
+
+ void testCertificateVerifies_WithMatchingSRVNameWithClientUnexpected() {
+ // Server-mode test which gets cert with "xmpp-client" SRV name
+ ServerIdentityVerifier testling(JID("foo@bar.com/baz"), idnConverter.get(), true);
+ SimpleCertificate::ref certificate(new SimpleCertificate());
+ certificate->addSRVName("_xmpp-client.bar.com");
+
+ CPPUNIT_ASSERT(!testling.certificateVerifies(certificate));
+ }
+
void testCertificateVerifies_WithMatchingXmppAddr() {
ServerIdentityVerifier testling(JID("foo@bar.com/baz"), idnConverter.get());
SimpleCertificate::ref certificate(new SimpleCertificate());
@@ -139,6 +167,14 @@ class ServerIdentityVerifierTest : public CppUnit::TestFixture {
CPPUNIT_ASSERT(testling.certificateVerifies(certificate));
}
+ void testCertificateVerifies_WithMatchingXmppAddrMixedCase() {
+ ServerIdentityVerifier testling(JID("foo@baR.com/baz"), idnConverter.get());
+ SimpleCertificate::ref certificate(new SimpleCertificate());
+ certificate->addXMPPAddress("bAr.com");
+
+ CPPUNIT_ASSERT(testling.certificateVerifies(certificate));
+ }
+
void testCertificateVerifies_WithMatchingXmppAddrWithWildcard() {
ServerIdentityVerifier testling(JID("foo@im.bar.com/baz"), idnConverter.get());
SimpleCertificate::ref certificate(new SimpleCertificate());
@@ -147,6 +183,14 @@ class ServerIdentityVerifierTest : public CppUnit::TestFixture {
CPPUNIT_ASSERT(!testling.certificateVerifies(certificate));
}
+ void testCertificateVerifies_WithMatchingXmppAddrWithWildcardMixedCase() {
+ ServerIdentityVerifier testling(JID("foo@im.bAr.com/baz"), idnConverter.get());
+ SimpleCertificate::ref certificate(new SimpleCertificate());
+ certificate->addXMPPAddress("*.baR.com");
+
+ CPPUNIT_ASSERT(!testling.certificateVerifies(certificate));
+ }
+
void testCertificateVerifies_WithMatchingInternationalXmppAddr() {
ServerIdentityVerifier testling(JID("foo@tron\xc3\xa7.com/baz"), idnConverter.get());
SimpleCertificate::ref certificate(new SimpleCertificate());
@@ -155,6 +199,14 @@ class ServerIdentityVerifierTest : public CppUnit::TestFixture {
CPPUNIT_ASSERT(testling.certificateVerifies(certificate));
}
+ void testCertificateVerifies_WithMatchingInternationalXmppAddrMixedCase() {
+ ServerIdentityVerifier testling(JID("foo@tRon\xc3\xa7.com/baz"), idnConverter.get());
+ SimpleCertificate::ref certificate(new SimpleCertificate());
+ certificate->addXMPPAddress("trOn\xc3\xa7.com");
+
+ CPPUNIT_ASSERT(testling.certificateVerifies(certificate));
+ }
+
void testCertificateVerifies_WithMatchingCNWithoutSAN() {
ServerIdentityVerifier testling(JID("foo@bar.com/baz"), idnConverter.get());
SimpleCertificate::ref certificate(new SimpleCertificate());