diff options
Diffstat (limited to 'tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp')
-rw-r--r-- | tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp | 1309 |
1 files changed, 845 insertions, 464 deletions
diff --git a/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp b/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp index d5035c5266..b45d6b5d8f 100644 --- a/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp +++ b/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp @@ -1,36 +1,14 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Copyright (C) 2014 Governikus GmbH & Co. KG. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2014 Governikus GmbH & Co. KG. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QtNetwork/private/qtnetworkglobal_p.h> #include <QtCore/qglobal.h> #include <QtCore/qthread.h> #include <QtCore/qelapsedtimer.h> #include <QtCore/qrandom.h> +#include <QtCore/qscopeguard.h> #include <QtNetwork/qhostaddress.h> #include <QtNetwork/qhostinfo.h> #include <QtNetwork/qnetworkproxy.h> @@ -40,24 +18,48 @@ #include <QtNetwork/qsslsocket.h> #include <QtNetwork/qtcpserver.h> #include <QtNetwork/qsslpresharedkeyauthenticator.h> -#include <QtTest/QtTest> +#include <QtTest/private/qemulationdetector_p.h> + +#include <QTest> #include <QNetworkProxy> #include <QAuthenticator> +#include <QTestEventLoop> +#include <QSignalSpy> +#include <QSemaphore> #include "private/qhostinfo_p.h" #include "private/qiodevice_p.h" // for QIODEVICE_BUFFERSIZE #include "../../../network-settings.h" +#include "../shared/tlshelpers.h" -#ifndef QT_NO_SSL -#ifndef QT_NO_OPENSSL -#include "private/qsslsocket_openssl_p.h" -#include "private/qsslsocket_openssl_symbols_p.h" +#if QT_CONFIG(openssl) +#include "../shared/qopenssl_symbols.h" #endif + +#include "private/qtlsbackend_p.h" + #include "private/qsslsocket_p.h" #include "private/qsslconfiguration_p.h" +using namespace std::chrono_literals; + +QT_WARNING_PUSH +QT_WARNING_DISABLE_DEPRECATED +// make these enum values available without causing deprecation warnings: +namespace Test { +#define COPY(tag, v) \ + constexpr auto tag ## V ## v = QSsl:: tag ## V ## v ; \ + constexpr auto tag ## V ## v ## OrLater = QSsl:: tag ## V ## v ## OrLater ; \ + /* end */ +COPY(Tls, 1_0) +COPY(Dtls, 1_0) +COPY(Tls, 1_1) +#undef COPY +} // namespace Test +QT_WARNING_POP + Q_DECLARE_METATYPE(QSslSocket::SslMode) typedef QList<QSslError::SslError> SslErrorList; Q_DECLARE_METATYPE(SslErrorList) @@ -67,25 +69,6 @@ Q_DECLARE_METATYPE(QSsl::SslProtocol) Q_DECLARE_METATYPE(QSslSocket::PeerVerifyMode); typedef QSharedPointer<QSslSocket> QSslSocketPtr; -// Non-OpenSSL backends are not able to report a specific error code -// for self-signed certificates. -#ifndef QT_NO_OPENSSL -#define FLUKE_CERTIFICATE_ERROR QSslError::SelfSignedCertificate -#else -#define FLUKE_CERTIFICATE_ERROR QSslError::CertificateUntrusted -#endif -#endif // QT_NO_OPENSSL - -// Detect ALPN (Application-Layer Protocol Negotiation) support -#undef ALPN_SUPPORTED // Undef the variable first to be safe -#if defined(OPENSSL_VERSION_NUMBER) && !defined(OPENSSL_NO_TLSEXT) -#define ALPN_SUPPORTED 1 -#endif - -#if QT_CONFIG(schannel) && !defined(Q_CC_MINGW) -#define ALPN_SUPPORTED 1 -#endif - #if defined Q_OS_HPUX && defined Q_CC_GNU // This error is delivered every time we try to use the fluke CA // certificate. For now we work around this bug. Task 202317. @@ -95,19 +78,18 @@ typedef QSharedPointer<QSslSocket> QSslSocketPtr; // Use this cipher to force PSK key sharing. // Also, it's a cipher w/o auth, to check that we emit the signals warning // about the identity of the peer. -#ifndef QT_NO_OPENSSL +#if QT_CONFIG(openssl) static const QString PSK_CIPHER_WITHOUT_AUTH = QStringLiteral("PSK-AES256-CBC-SHA"); static const quint16 PSK_SERVER_PORT = 4433; static const QByteArray PSK_CLIENT_PRESHAREDKEY = QByteArrayLiteral("\x1a\x2b\x3c\x4d\x5e\x6f"); static const QByteArray PSK_SERVER_IDENTITY_HINT = QByteArrayLiteral("QtTestServerHint"); static const QByteArray PSK_CLIENT_IDENTITY = QByteArrayLiteral("Client_identity"); +#endif // QT_CONFIG(openssl) QT_BEGIN_NAMESPACE void qt_ForceTlsSecurityLevel(); QT_END_NAMESPACE -#endif // !QT_NO_OPENSSL - class tst_QSslSocket : public QObject { Q_OBJECT @@ -128,10 +110,10 @@ public: return QTestEventLoop::instance().timeout(); } -#ifndef QT_NO_SSL +#if QT_CONFIG(ssl) QSslSocketPtr newSocket(); -#ifndef QT_NO_OPENSSL +#if QT_CONFIG(openssl) enum PskConnectTestType { PskConnectDoNotHandlePsk, PskConnectEmptyCredentials, @@ -142,20 +124,22 @@ public: PskConnectRightCredentialsVerifyPeer, PskConnectRightCredentialsDoNotVerifyPeer, }; -#endif -#endif +#endif // QT_CONFIG(openssl) +#endif // QT_CONFIG(ssl) public slots: void initTestCase_data(); void initTestCase(); void init(); void cleanup(); -#ifndef QT_NO_NETWORKPROXY +#if QT_CONFIG(networkproxy) void proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *auth); #endif -#ifndef QT_NO_SSL +#if QT_CONFIG(ssl) private slots: + void activeBackend(); + void backends(); void constructing(); void configNoOnDemandLoad(); void simpleConnect(); @@ -164,34 +148,30 @@ private slots: // API tests void sslErrors_data(); void sslErrors(); - void addCaCertificate(); - void addCaCertificates(); - void addCaCertificates2(); void ciphers(); +#if QT_CONFIG(securetransport) + void tls13Ciphers(); +#endif // QT_CONFIG(securetransport) void connectToHostEncrypted(); void connectToHostEncryptedWithVerificationPeerName(); void sessionCipher(); - void flush(); - void isEncrypted(); void localCertificate(); void mode(); void peerCertificate(); void peerCertificateChain(); void privateKey(); -#ifndef QT_NO_OPENSSL +#if QT_CONFIG(openssl) void privateKeyOpaque(); #endif void protocol(); void protocolServerSide_data(); void protocolServerSide(); -#ifndef QT_NO_OPENSSL void serverCipherPreferences(); -#endif // QT_NO_OPENSSL void setCaCertificates(); void setLocalCertificate(); void localCertificateChain(); void setLocalCertificateChain(); - void setPrivateKey(); + void tlsConfiguration(); void setSocketDescriptor(); void setSslConfiguration_data(); void setSslConfiguration(); @@ -218,9 +198,10 @@ private slots: void waitForMinusOne(); void verifyMode(); void verifyDepth(); + void verifyAndDefaultConfiguration(); void disconnectFromHostWhenConnecting(); void disconnectFromHostWhenConnected(); -#ifndef QT_NO_OPENSSL +#if QT_CONFIG(openssl) void closeWhileEmittingSocketError(); #endif void resetProxy(); @@ -233,19 +214,16 @@ private slots: void writeBigChunk(); void blacklistedCertificates(); void versionAccessors(); -#ifndef QT_NO_OPENSSL - void sslOptions(); -#endif void encryptWithoutConnecting(); void resume_data(); void resume(); void qtbug18498_peek(); void qtbug18498_peek2(); void dhServer(); -#ifndef QT_NO_OPENSSL +#if QT_CONFIG(openssl) void dhServerCustomParamsNull(); void dhServerCustomParams(); -#endif +#endif // QT_CONFIG(openssl) void ecdhServer(); void verifyClientCertificate_data(); void verifyClientCertificate(); @@ -253,7 +231,7 @@ private slots: void allowedProtocolNegotiation(); -#ifndef QT_NO_OPENSSL +#if QT_CONFIG(openssl) void simplePskConnect_data(); void simplePskConnect(); void ephemeralServerKey_data(); @@ -262,7 +240,7 @@ private slots: void forwardReadChannelFinished(); void signatureAlgorithm_data(); void signatureAlgorithm(); -#endif +#endif // QT_CONFIG(openssl) void unsupportedProtocols_data(); void unsupportedProtocols(); @@ -312,19 +290,48 @@ protected slots: private: QSslSocket *socket; QList<QSslError> storedExpectedSslErrors; -#endif // QT_NO_SSL + bool isTestingOpenSsl = false; + bool isSecurityLevel0Required = false; + bool opensslResolved = false; + bool isTestingSecureTransport = false; + bool isTestingSchannel = false; + QSslError::SslError flukeCertificateError = QSslError::CertificateUntrusted; + bool hasServerAlpn = false; +#endif // QT_CONFIG(ssl) private: static int loopLevel; public: static QString testDataDir; + + bool supportsTls13() const + { + if (isTestingOpenSsl) { +#ifdef TLS1_3_VERSION + return true; +#endif + } + if (isTestingSchannel) { + // Copied from qtls_schannel.cpp #supportsTls13() + static bool supported = []() { + const auto current = QOperatingSystemVersion::current(); + const auto minimum = + QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10, 0, 20221); + return current >= minimum; + }(); + return supported; + } + + return false; + } + }; QString tst_QSslSocket::testDataDir; -#ifndef QT_NO_SSL -#ifndef QT_NO_OPENSSL +#if QT_CONFIG(ssl) +#if QT_CONFIG(openssl) Q_DECLARE_METATYPE(tst_QSslSocket::PskConnectTestType) -#endif -#endif +#endif // QT_CONFIG(openssl) +#endif // QT_CONFIG(ssl) int tst_QSslSocket::loopLevel = 0; @@ -341,39 +348,22 @@ QString httpServerCertChainPath() #endif // QT_TEST_SERVER } -bool supportsTls13() -{ -#ifdef TLS1_3_VERSION - return true; -#elif QT_CONFIG(schannel) - // Copied from qsslsocket_schannel.cpp #supportsTls13() - static bool supported = []() { - const auto current = QOperatingSystemVersion::current(); - const auto minimum = - QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10, 0, 20221); - return current >= minimum; - }(); - return supported; -#else - return false; -#endif -} - } // unnamed namespace tst_QSslSocket::tst_QSslSocket() { -#ifndef QT_NO_SSL +#if QT_CONFIG(ssl) qRegisterMetaType<QList<QSslError> >("QList<QSslError>"); qRegisterMetaType<QSslError>("QSslError"); qRegisterMetaType<QAbstractSocket::SocketState>("QAbstractSocket::SocketState"); qRegisterMetaType<QAbstractSocket::SocketError>("QAbstractSocket::SocketError"); -#ifndef QT_NO_OPENSSL +#if QT_CONFIG(openssl) qRegisterMetaType<QSslPreSharedKeyAuthenticator *>(); qRegisterMetaType<tst_QSslSocket::PskConnectTestType>(); -#endif -#endif +#endif // QT_CONFIG(openssl) + +#endif // QT_CONFIG(ssl) } enum ProxyTests { @@ -410,7 +400,31 @@ void tst_QSslSocket::initTestCase() testDataDir = QCoreApplication::applicationDirPath(); if (!testDataDir.endsWith(QLatin1String("/"))) testDataDir += QLatin1String("/"); -#ifndef QT_NO_SSL + + hasServerAlpn = QSslSocket::supportedFeatures().contains(QSsl::SupportedFeature::ServerSideAlpn); + // Several plugins (TLS-backends) can co-exist. QSslSocket would implicitly + // select 'openssl' if available, and if not: 'securetransport' (Darwin) or + // 'schannel' (Windows). Check what we actually have: + const auto &tlsBackends = QSslSocket::availableBackends(); + if (tlsBackends.contains(QTlsBackend::builtinBackendNames[QTlsBackend::nameIndexOpenSSL])) { + isTestingOpenSsl = true; + flukeCertificateError = QSslError::SelfSignedCertificate; +#if QT_CONFIG(openssl) + opensslResolved = qt_auto_test_resolve_OpenSSL_symbols(); + // This is where OpenSSL moved several protocols under + // non-default (0) security level (the default is 1). + isSecurityLevel0Required = OPENSSL_VERSION_NUMBER >= 0x30100010; +#else + opensslResolved = false; // Not 'unused variable' anymore. +#endif + } else if (tlsBackends.contains(QTlsBackend::builtinBackendNames[QTlsBackend::nameIndexSchannel])) { + isTestingSchannel = true; + } else { + QVERIFY(tlsBackends.contains(QTlsBackend::builtinBackendNames[QTlsBackend::nameIndexSecureTransport])); + isTestingSecureTransport = true; + } + +#if QT_CONFIG(ssl) qDebug("Using SSL library %s (%ld)", qPrintable(QSslSocket::sslLibraryVersionString()), QSslSocket::sslLibraryVersionNumber()); @@ -427,14 +441,14 @@ void tst_QSslSocket::initTestCase() if (!QtNetworkSettings::verifyTestNetworkSettings()) QSKIP("No network test server available"); #endif // QT_TEST_SERVER -#endif // QT_NO_SSL +#endif // QT_CONFIG(ssl) } void tst_QSslSocket::init() { QFETCH_GLOBAL(bool, setProxy); if (setProxy) { -#ifndef QT_NO_NETWORKPROXY +#if QT_CONFIG(networkproxy) QFETCH_GLOBAL(int, proxyType); const QString socksProxyAddr = QtNetworkSettings::socksProxyServerIp().toString(); const QString httpProxyAddr = QtNetworkSettings::httpProxyServerIp().toString(); @@ -462,26 +476,24 @@ void tst_QSslSocket::init() break; } QNetworkProxy::setApplicationProxy(proxy); -#else // !QT_NO_NETWORKPROXY +#else QSKIP("No proxy support"); -#endif // QT_NO_NETWORKPROXY +#endif // QT_CONFIG(networkproxy) } -#ifndef QT_NO_OPENSSL QT_PREPEND_NAMESPACE(qt_ForceTlsSecurityLevel)(); -#endif // QT_NO_OPENSSL qt_qhostinfo_clear_cache(); } void tst_QSslSocket::cleanup() { -#ifndef QT_NO_NETWORKPROXY +#if QT_CONFIG(networkproxy) QNetworkProxy::setApplicationProxy(QNetworkProxy::DefaultProxy); #endif } -#ifndef QT_NO_SSL +#if QT_CONFIG(ssl) QSslSocketPtr tst_QSslSocket::newSocket() { const auto socket = QSslSocketPtr::create(); @@ -493,18 +505,184 @@ QSslSocketPtr tst_QSslSocket::newSocket() return socket; } -#endif +#endif // QT_CONFIG(ssl) -#ifndef QT_NO_NETWORKPROXY +#if QT_CONFIG(networkproxy) void tst_QSslSocket::proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *auth) { ++proxyAuthCalled; auth->setUser("qsockstest"); auth->setPassword("password"); } -#endif // !QT_NO_NETWORKPROXY +#endif // QT_CONFIG(networkproxy) + +#if QT_CONFIG(ssl) -#ifndef QT_NO_SSL +void tst_QSslSocket::activeBackend() +{ + QFETCH_GLOBAL(const bool, setProxy); + if (setProxy) // Not interesting for backend test. + return; + + // We cannot set non-existing as active: + const QString nonExistingBackend = QStringLiteral("TheQtTLS"); + QCOMPARE(QSslSocket::setActiveBackend(nonExistingBackend), false); + QCOMPARE(QSslSocket::supportedProtocols(nonExistingBackend).size(), 0); + QCOMPARE(QSslSocket::supportedFeatures(nonExistingBackend), QList<QSsl::SupportedFeature>()); + QCOMPARE(QSslSocket::implementedClasses(nonExistingBackend), QList<QSsl::ImplementedClass>()); + + const QString backendName = QSslSocket::activeBackend(); + // Implemented by all our existing backends: + const auto implemented = QSsl::ImplementedClass::Socket; + const auto supportedFt = QSsl::SupportedFeature::ClientSideAlpn; + + QVERIFY(QSslSocket::availableBackends().contains(backendName)); + QCOMPARE(QSslSocket::setActiveBackend(backendName), true); + QCOMPARE(QSslSocket::activeBackend(), backendName); + QCOMPARE(QSslSocket::setActiveBackend(backendName), true); // We can do it again. + QCOMPARE(QSslSocket::activeBackend(), backendName); + + const auto protocols = QSslSocket::supportedProtocols(); + QVERIFY(protocols.size() > 0); + // 'Any' and 'Secure', since they are always present: + QVERIFY(protocols.contains(QSsl::AnyProtocol)); + QVERIFY(protocols.contains(QSsl::SecureProtocols)); + + const auto protocolsForNamed = QSslSocket::supportedProtocols(backendName); + QCOMPARE(protocols, protocolsForNamed); + // Any and secure, new versions are coming, old + // go away, nothing more specific. + QVERIFY(protocolsForNamed.contains(QSsl::AnyProtocol)); + QVERIFY(protocolsForNamed.contains(QSsl::SecureProtocols)); + QCOMPARE(QSslSocket::isProtocolSupported(QSsl::SecureProtocols), true); + QCOMPARE(QSslSocket::isProtocolSupported(QSsl::SecureProtocols, backendName), true); + + const auto classes = QSslSocket::implementedClasses(); + QVERIFY(classes.contains(implemented)); + QVERIFY(QSslSocket::isClassImplemented(implemented)); + QVERIFY(QSslSocket::isClassImplemented(implemented, backendName)); + + const auto features = QSslSocket::supportedFeatures(); + QVERIFY(features.contains(QSsl::SupportedFeature(supportedFt))); + QVERIFY(QSslSocket::isFeatureSupported(QSsl::SupportedFeature(supportedFt))); + QVERIFY(QSslSocket::isFeatureSupported(QSsl::SupportedFeature(supportedFt), backendName)); +} + +namespace { +struct MockTlsBackend : QTlsBackend +{ + MockTlsBackend(const QString &mockName) : name(mockName) + { + } + QString backendName() const override + { + return name; + } + + QList<QSsl::SupportedFeature> supportedFeatures() const override + { + return features; + } + QList<QSsl::SslProtocol> supportedProtocols() const override + { + return protocols; + } + QList<QSsl::ImplementedClass> implementedClasses() const override + { + return classes; + } + + QString name; + QList<QSsl::ImplementedClass> classes; + QList<QSsl::SupportedFeature> features; + QList<QSsl::SslProtocol> protocols; +}; + +} // Unnamed backend. + +void tst_QSslSocket::backends() +{ + QFETCH_GLOBAL(const bool, setProxy); + if (setProxy) // Not interesting for backend test. + return; + + // We are here, protected by QT_CONFIG(ssl). Some backend must be pre-existing. + // Let's test the 'real' backend: + auto backendNames = QTlsBackend::availableBackendNames(); + const auto sizeBefore = backendNames.size(); + QVERIFY(sizeBefore > 0); + + const QString tlsBackend = QSslSocket::activeBackend(); + QVERIFY(tlsBackend == QTlsBackend::builtinBackendNames[QTlsBackend::nameIndexOpenSSL] + || tlsBackend == QTlsBackend::builtinBackendNames[QTlsBackend::nameIndexSchannel] + || tlsBackend == QTlsBackend::builtinBackendNames[QTlsBackend::nameIndexSecureTransport]); + + const auto builtinProtocols = QSslSocket::supportedProtocols(tlsBackend); + QVERIFY(builtinProtocols.contains(QSsl::SecureProtocols)); + // Socket and ALPN are supported by all our backends: + const auto builtinClasses = QSslSocket::implementedClasses(tlsBackend); + QVERIFY(builtinClasses.contains(QSsl::ImplementedClass::Socket)); + const auto builtinFeatures = QSslSocket::supportedFeatures(tlsBackend); + QVERIFY(builtinFeatures.contains(QSsl::SupportedFeature::ClientSideAlpn)); + + // Verify that non-dummy backend can be found: + auto *systemBackend = QTlsBackend::findBackend(tlsBackend); + QVERIFY(systemBackend); + + const auto protocols = QList<QSsl::SslProtocol>{QSsl::SecureProtocols}; + const auto classes = QList<QSsl::ImplementedClass>{QSsl::ImplementedClass::Socket}; + const auto features = QList<QSsl::SupportedFeature>{QSsl::SupportedFeature::CertificateVerification}; + + const QString nameA = QStringLiteral("backend A"); + const QString nameB = QStringLiteral("backend B"); + const QString nonExisting = QStringLiteral("non-existing backend"); + + QVERIFY(!backendNames.contains(nameA)); + QVERIFY(!backendNames.contains(nameB)); + QVERIFY(!backendNames.contains(nonExisting)); + { + MockTlsBackend factoryA(nameA); + backendNames = QTlsBackend::availableBackendNames(); + QVERIFY(backendNames.contains(nameA)); + QVERIFY(!backendNames.contains(nameB)); + QVERIFY(!backendNames.contains(nonExisting)); + + const auto *backendA = QTlsBackend::findBackend(nameA); + QVERIFY(backendA); + QCOMPARE(backendA->backendName(), nameA); + + QCOMPARE(factoryA.supportedFeatures().size(), 0); + QCOMPARE(factoryA.supportedProtocols().size(), 0); + QCOMPARE(factoryA.implementedClasses().size(), 0); + + factoryA.protocols = protocols; + factoryA.classes = classes; + factoryA.features = features; + + // It's an overrider in some re-implemented factory: + QCOMPARE(factoryA.supportedProtocols(), protocols); + QCOMPARE(factoryA.supportedFeatures(), features); + QCOMPARE(factoryA.implementedClasses(), classes); + + // That's a helper function (static member function): + QCOMPARE(QTlsBackend::supportedProtocols(nameA), protocols); + QCOMPARE(QTlsBackend::supportedFeatures(nameA), features); + QCOMPARE(QTlsBackend::implementedClasses(nameA), classes); + + MockTlsBackend factoryB(nameB); + QVERIFY(QTlsBackend::availableBackendNames().contains(nameA)); + QVERIFY(QTlsBackend::availableBackendNames().contains(nameB)); + QVERIFY(!QTlsBackend::availableBackendNames().contains(nonExisting)); + + const auto *nullBackend = QTlsBackend::findBackend(nonExisting); + QCOMPARE(nullBackend, nullptr); + } + backendNames = QTlsBackend::availableBackendNames(); + QCOMPARE(backendNames.size(), sizeBefore); + // Check we cleaned up our factories: + QVERIFY(!backendNames.contains(nameA)); + QVERIFY(!backendNames.contains(nameB)); +} void tst_QSslSocket::constructing() { @@ -536,7 +714,7 @@ void tst_QSslSocket::constructing() QVERIFY(!socket.isTextModeEnabled()); QVERIFY(!socket.isWritable()); QCOMPARE(socket.openMode(), QIODevice::NotOpen); - QTest::ignoreMessage(QtWarningMsg, readNotOpenMessage); + QTest::ignoreMessage(QtWarningMsg, "QIODevice::peek (QSslSocket): device not open"); QVERIFY(socket.peek(2).isEmpty()); QCOMPARE(socket.pos(), qint64(0)); QTest::ignoreMessage(QtWarningMsg, writeNotOpenMessage); @@ -547,8 +725,7 @@ void tst_QSslSocket::constructing() QCOMPARE(socket.read(0, 0), qint64(-1)); QTest::ignoreMessage(QtWarningMsg, readNotOpenMessage); QVERIFY(socket.readAll().isEmpty()); - QTest::ignoreMessage(QtWarningMsg, "QIODevice::readLine (QSslSocket): Called with maxSize < 2"); - QCOMPARE(socket.readLine(0, 0), qint64(-1)); + QTest::ignoreMessage(QtWarningMsg, "QIODevice::readLine (QSslSocket): device not open"); char buf[10]; QCOMPARE(socket.readLine(buf, sizeof(buf)), qint64(-1)); QTest::ignoreMessage(QtWarningMsg, "QIODevice::seek (QSslSocket): Cannot call seek on a sequential device"); @@ -570,7 +747,7 @@ void tst_QSslSocket::constructing() QCOMPARE(socket.peerAddress(), QHostAddress()); QVERIFY(socket.peerName().isEmpty()); QCOMPARE(socket.peerPort(), quint16(0)); -#ifndef QT_NO_NETWORKPROXY +#if QT_CONFIG(networkproxy) QCOMPARE(socket.proxy().type(), QNetworkProxy::DefaultProxy); #endif QCOMPARE(socket.readBufferSize(), qint64(0)); @@ -621,16 +798,32 @@ void tst_QSslSocket::configNoOnDemandLoad() QCOMPARE(customConfig, socket.sslConfiguration()); } +static void downgrade_TLS_QTQAINFRA_4499(QSslSocket &socket) +{ + // Set TLS 1.0 or above because the server doesn't support TLS 1.2 or above + // QTQAINFRA-4499 + QSslConfiguration config = socket.sslConfiguration(); + config.setProtocol(Test::TlsV1_0OrLater); + socket.setSslConfiguration(config); +} + void tst_QSslSocket::simpleConnect() { if (!QSslSocket::supportsSsl()) return; + // Starting from OpenSSL v 3.1.1 deprecated protocol versions (we want to use when connecting) are not available by default. + if (isSecurityLevel0Required) + QSKIP("Testing with OpenSSL backend, but security level 0 is required for TLS v1.1 or earlier"); + QFETCH_GLOBAL(bool, setProxy); if (setProxy) return; QSslSocket socket; + + downgrade_TLS_QTQAINFRA_4499(socket); + QSignalSpy connectedSpy(&socket, SIGNAL(connected())); QSignalSpy hostFoundSpy(&socket, SIGNAL(hostFound())); QSignalSpy disconnectedSpy(&socket, SIGNAL(disconnected())); @@ -651,30 +844,30 @@ void tst_QSslSocket::simpleConnect() // Entered connecting state QCOMPARE(socket.state(), QAbstractSocket::ConnectingState); - QCOMPARE(connectedSpy.count(), 0); - QCOMPARE(hostFoundSpy.count(), 1); - QCOMPARE(disconnectedSpy.count(), 0); + QCOMPARE(connectedSpy.size(), 0); + QCOMPARE(hostFoundSpy.size(), 1); + QCOMPARE(disconnectedSpy.size(), 0); enterLoop(10); // Entered connected state QCOMPARE(socket.state(), QAbstractSocket::ConnectedState); QCOMPARE(socket.mode(), QSslSocket::UnencryptedMode); QVERIFY(!socket.isEncrypted()); - QCOMPARE(connectedSpy.count(), 1); - QCOMPARE(hostFoundSpy.count(), 1); - QCOMPARE(disconnectedSpy.count(), 0); + QCOMPARE(connectedSpy.size(), 1); + QCOMPARE(hostFoundSpy.size(), 1); + QCOMPARE(disconnectedSpy.size(), 0); // Enter encrypted mode socket.startClientEncryption(); QCOMPARE(socket.mode(), QSslSocket::SslClientMode); QVERIFY(!socket.isEncrypted()); - QCOMPARE(connectionEncryptedSpy.count(), 0); - QCOMPARE(sslErrorsSpy.count(), 0); + QCOMPARE(connectionEncryptedSpy.size(), 0); + QCOMPARE(sslErrorsSpy.size(), 0); // Starting handshake enterLoop(10); - QCOMPARE(sslErrorsSpy.count(), 1); - QCOMPARE(connectionEncryptedSpy.count(), 0); + QCOMPARE(sslErrorsSpy.size(), 1); + QCOMPARE(connectionEncryptedSpy.size(), 0); QVERIFY(!socket.isEncrypted()); QCOMPARE(socket.state(), QAbstractSocket::UnconnectedState); } @@ -684,6 +877,10 @@ void tst_QSslSocket::simpleConnectWithIgnore() if (!QSslSocket::supportsSsl()) return; + // Starting from OpenSSL v 3.1.1 deprecated protocol versions (we want to use when connecting) are not available by default. + if (isSecurityLevel0Required) + QSKIP("Testing with OpenSSL backend, but security level 0 is required for TLS v1.1 or earlier"); + QFETCH_GLOBAL(bool, setProxy); if (setProxy) return; @@ -693,6 +890,8 @@ void tst_QSslSocket::simpleConnectWithIgnore() QSignalSpy encryptedSpy(&socket, SIGNAL(encrypted())); QSignalSpy sslErrorsSpy(&socket, SIGNAL(sslErrors(QList<QSslError>))); + downgrade_TLS_QTQAINFRA_4499(socket); + connect(&socket, SIGNAL(readyRead()), this, SLOT(exitLoop())); connect(&socket, SIGNAL(encrypted()), this, SLOT(exitLoop())); connect(&socket, SIGNAL(connected()), this, SLOT(exitLoop())); @@ -710,10 +909,10 @@ void tst_QSslSocket::simpleConnectWithIgnore() enterLoop(10); // Done; encryption should be enabled. - QCOMPARE(sslErrorsSpy.count(), 1); + QCOMPARE(sslErrorsSpy.size(), 1); QVERIFY(socket.isEncrypted()); QCOMPARE(socket.state(), QAbstractSocket::ConnectedState); - QCOMPARE(encryptedSpy.count(), 1); + QCOMPARE(encryptedSpy.size(), 1); // Wait for incoming data if (!socket.canReadLine()) @@ -726,6 +925,10 @@ void tst_QSslSocket::simpleConnectWithIgnore() void tst_QSslSocket::sslErrors_data() { + // Starting from OpenSSL v 3.1.1 deprecated protocol versions (we want to use in 'sslErrors' test) are not available by default. + if (isSecurityLevel0Required) + QSKIP("Testing with OpenSSL backend, but security level 0 is required for TLS v1.1 or earlier"); + QTest::addColumn<QString>("host"); QTest::addColumn<int>("port"); @@ -742,10 +945,10 @@ void tst_QSslSocket::sslErrors() QFETCH(int, port); QSslSocketPtr socket = newSocket(); -#if QT_CONFIG(schannel) - // Needs to be < 1.2 because of the old certificate and <= 1.0 because of the mail server - socket->setProtocol(QSsl::SslProtocol::TlsV1_0); -#endif + + QVERIFY(socket); + downgrade_TLS_QTQAINFRA_4499(*socket); + QSignalSpy sslErrorsSpy(socket.data(), SIGNAL(sslErrors(QList<QSslError>))); QSignalSpy peerVerifyErrorSpy(socket.data(), SIGNAL(peerVerifyError(QSslError))); @@ -775,12 +978,15 @@ void tst_QSslSocket::sslErrors() sslErrors << err.error(); std::sort(sslErrors.begin(), sslErrors.end()); QVERIFY(sslErrors.contains(QSslError::HostNameMismatch)); - QVERIFY(sslErrors.contains(FLUKE_CERTIFICATE_ERROR)); + + // Non-OpenSSL backends are not able to report a specific error code + // for self-signed certificates. + QVERIFY(sslErrors.contains(flukeCertificateError)); // check the same errors were emitted by sslErrors QVERIFY(!sslErrorsSpy.isEmpty()); SslErrorList emittedErrors; - const auto sslErrorsSpyErrors = qvariant_cast<QList<QSslError> >(qAsConst(sslErrorsSpy).first().first()); + const auto sslErrorsSpyErrors = qvariant_cast<QList<QSslError> >(std::as_const(sslErrorsSpy).first().first()); for (const QSslError &err : sslErrorsSpyErrors) emittedErrors << err.error(); std::sort(emittedErrors.begin(), emittedErrors.end()); @@ -796,28 +1002,17 @@ void tst_QSslSocket::sslErrors() QCOMPARE(sslErrors, peerErrors); } -void tst_QSslSocket::addCaCertificate() -{ - if (!QSslSocket::supportsSsl()) - return; -} - -void tst_QSslSocket::addCaCertificates() -{ - if (!QSslSocket::supportsSsl()) - return; -} - -void tst_QSslSocket::addCaCertificates2() +void tst_QSslSocket::ciphers() { if (!QSslSocket::supportsSsl()) return; -} -void tst_QSslSocket::ciphers() -{ - if (!QSslSocket::supportsSsl()) + QFETCH_GLOBAL(const bool, setProxy); + if (setProxy) { + // KISS(mart), we don't connect, no need to test the same thing + // many times! return; + } QSslSocket socket; QCOMPARE(socket.sslConfiguration().ciphers(), QSslConfiguration::defaultConfiguration().ciphers()); @@ -840,7 +1035,7 @@ void tst_QSslSocket::ciphers() QString ciphersAsString; const auto &supported = sslConfig.supportedCiphers(); for (const auto &cipher : supported) { - if (cipher.isNull() || !cipher.name().length()) + if (cipher.isNull() || !cipher.name().size()) continue; if (ciphers.size() > 0) ciphersAsString += QStringLiteral(":"); @@ -853,17 +1048,65 @@ void tst_QSslSocket::ciphers() if (!ciphers.size()) QSKIP("No proper ciphersuite was found to test 'setCiphers'"); -#if QT_CONFIG(schannel) - qWarning("Schannel doesn't support setting ciphers from a cipher-string."); -#else - sslConfig.setCiphers(ciphersAsString); - socket.setSslConfiguration(sslConfig); - QCOMPARE(ciphers, socket.sslConfiguration().ciphers()); -#endif + + if (isTestingSchannel) { + qWarning("Schannel doesn't support setting ciphers from a cipher-string."); + } else { + sslConfig.setCiphers(ciphersAsString); + socket.setSslConfiguration(sslConfig); + QCOMPARE(ciphers, socket.sslConfiguration().ciphers()); + } + sslConfig.setCiphers(ciphers); socket.setSslConfiguration(sslConfig); QCOMPARE(ciphers, socket.sslConfiguration().ciphers()); + + if (isTestingOpenSsl) { + for (const auto &cipher : ciphers) { + if (cipher.name().size() && cipher.protocol() != QSsl::UnknownProtocol) { + const QSslCipher aCopy(cipher.name(), cipher.protocol()); + QCOMPARE(aCopy, cipher); + break; + } + } + } +} + +#if QT_CONFIG(securetransport) +void tst_QSslSocket::tls13Ciphers() +{ + // SecureTransport introduced several new ciphers under + // "TLS 1.3 ciphersuites" section. Since Qt 6 we respect + // the ciphers from QSslConfiguration. In case of default + // configuration, these are the same we report and we + // were failing (for historical reasons) to report those + // TLS 1.3 suites when creating default QSslConfiguration. + // Check we now have them. + if (!isTestingSecureTransport) + QSKIP("The feature 'securetransport' was enabled, but active backend is not \"securetransport\""); + + QFETCH_GLOBAL(const bool, setProxy); + if (setProxy) + return; + + const auto suites = QSslConfiguration::defaultConfiguration().ciphers(); + QSslCipher ciph; + // Check the one of reported and previously missed: + for (const auto &suite : suites) { + if (suite.encryptionMethod() == QStringLiteral("CHACHA20")) { + // There are several ciphesuites using CHACHA20, the first one + // is sufficient for the purpose of this test: + ciph = suite; + break; + } + } + + QVERIFY(!ciph.isNull()); + QCOMPARE(ciph.encryptionMethod(), QStringLiteral("CHACHA20")); + QCOMPARE(ciph.supportedBits(), 256); + QCOMPARE(ciph.usedBits(), 256); } +#endif // QT_CONFIG(securetransport) void tst_QSslSocket::connectToHostEncrypted() { @@ -871,9 +1114,7 @@ void tst_QSslSocket::connectToHostEncrypted() return; QSslSocketPtr socket = newSocket(); -#if QT_CONFIG(schannel) // old certificate not supported with TLS 1.2 - socket->setProtocol(QSsl::SslProtocol::TlsV1_1); -#endif + this->socket = socket.data(); auto config = socket->sslConfiguration(); QVERIFY(config.addCaCertificates(httpServerCertChainPath())); @@ -893,6 +1134,7 @@ void tst_QSslSocket::connectToHostEncrypted() socket->disconnectFromHost(); QVERIFY(socket->waitForDisconnected()); + QVERIFY(!socket->isEncrypted()); QCOMPARE(socket->mode(), QSslSocket::SslClientMode); @@ -909,9 +1151,7 @@ void tst_QSslSocket::connectToHostEncryptedWithVerificationPeerName() return; QSslSocketPtr socket = newSocket(); -#if QT_CONFIG(schannel) // old certificate not supported with TLS 1.2 - socket->setProtocol(QSsl::SslProtocol::TlsV1_1); -#endif + this->socket = socket.data(); auto config = socket->sslConfiguration(); @@ -966,14 +1206,6 @@ void tst_QSslSocket::sessionCipher() QVERIFY(socket->waitForDisconnected()); } -void tst_QSslSocket::flush() -{ -} - -void tst_QSslSocket::isEncrypted() -{ -} - void tst_QSslSocket::localCertificate() { if (!QSslSocket::supportsSsl()) @@ -1017,7 +1249,7 @@ void tst_QSslSocket::peerCertificateChain() QSslSocketPtr socket = newSocket(); this->socket = socket.data(); QList<QSslCertificate> caCertificates = QSslCertificate::fromPath(httpServerCertChainPath()); - QCOMPARE(caCertificates.count(), 1); + QCOMPARE(caCertificates.size(), 1); auto config = socket->sslConfiguration(); config.addCaCertificates(caCertificates); socket->setSslConfiguration(config); @@ -1034,7 +1266,7 @@ void tst_QSslSocket::peerCertificateChain() QSKIP("Skipping flaky test - See QTBUG-29941"); QList<QSslCertificate> certChain = socket->peerCertificateChain(); - QVERIFY(certChain.count() > 0); + QVERIFY(certChain.size() > 0); QCOMPARE(certChain.first(), socket->peerCertificate()); socket->disconnectFromHost(); @@ -1075,11 +1307,15 @@ void tst_QSslSocket::privateKey() { } -#ifndef QT_NO_OPENSSL +#if QT_CONFIG(openssl) void tst_QSslSocket::privateKeyOpaque() { - if (!QSslSocket::supportsSsl()) - return; +#ifndef OPENSSL_NO_DEPRECATED_3_0 + if (!isTestingOpenSsl) + QSKIP("The active TLS backend does not support private opaque keys"); + + if (!opensslResolved) + QSKIP("Failed to resolve OpenSSL symbols, required by this test"); QFile file(testDataDir + "certs/fluke.key"); QVERIFY(file.open(QIODevice::ReadOnly)); @@ -1108,8 +1344,9 @@ void tst_QSslSocket::privateKeyOpaque() QFETCH_GLOBAL(bool, setProxy); if (setProxy && !socket->waitForEncrypted(10000)) QSKIP("Skipping flaky test - See QTBUG-29941"); +#endif // OPENSSL_NO_DEPRECATED_3_0 } -#endif +#endif // Feature 'openssl'. void tst_QSslSocket::protocol() { @@ -1133,38 +1370,38 @@ void tst_QSslSocket::protocol() QFETCH_GLOBAL(bool, setProxy); { // qt-test-server allows TLSV1. - socket->setProtocol(QSsl::TlsV1_0); - QCOMPARE(socket->protocol(), QSsl::TlsV1_0); + socket->setProtocol(Test::TlsV1_0); + QCOMPARE(socket->protocol(), Test::TlsV1_0); socket->connectToHostEncrypted(QtNetworkSettings::httpServerName(), 443); if (setProxy && !socket->waitForEncrypted()) QSKIP("Skipping flaky test - See QTBUG-29941"); - QCOMPARE(socket->protocol(), QSsl::TlsV1_0); + QCOMPARE(socket->protocol(), Test::TlsV1_0); socket->abort(); - QCOMPARE(socket->protocol(), QSsl::TlsV1_0); + QCOMPARE(socket->protocol(), Test::TlsV1_0); socket->connectToHost(QtNetworkSettings::httpServerName(), 443); QVERIFY2(socket->waitForConnected(), qPrintable(socket->errorString())); socket->startClientEncryption(); if (setProxy && !socket->waitForEncrypted()) QSKIP("Skipping flaky test - See QTBUG-29941"); - QCOMPARE(socket->protocol(), QSsl::TlsV1_0); + QCOMPARE(socket->protocol(), Test::TlsV1_0); socket->abort(); } { // qt-test-server probably doesn't allow TLSV1.1 - socket->setProtocol(QSsl::TlsV1_1); - QCOMPARE(socket->protocol(), QSsl::TlsV1_1); + socket->setProtocol(Test::TlsV1_1); + QCOMPARE(socket->protocol(), Test::TlsV1_1); socket->connectToHostEncrypted(QtNetworkSettings::httpServerName(), 443); if (setProxy && !socket->waitForEncrypted()) QSKIP("Skipping flaky test - See QTBUG-29941"); - QCOMPARE(socket->protocol(), QSsl::TlsV1_1); + QCOMPARE(socket->protocol(), Test::TlsV1_1); socket->abort(); - QCOMPARE(socket->protocol(), QSsl::TlsV1_1); + QCOMPARE(socket->protocol(), Test::TlsV1_1); socket->connectToHost(QtNetworkSettings::httpServerName(), 443); QVERIFY2(socket->waitForConnected(), qPrintable(socket->errorString())); socket->startClientEncryption(); if (setProxy && !socket->waitForEncrypted()) QSKIP("Skipping flaky test - See QTBUG-29941"); - QCOMPARE(socket->protocol(), QSsl::TlsV1_1); + QCOMPARE(socket->protocol(), Test::TlsV1_1); socket->abort(); } { @@ -1235,7 +1472,7 @@ public: config(QSslConfiguration::defaultConfiguration()), ignoreSslErrors(true), peerVerifyMode(QSslSocket::AutoVerifyPeer), - protocol(QSsl::TlsV1_0), + protocol(QSsl::SecureProtocols), m_keyFile(keyFile), m_certFile(certFile), m_interFile(interFile) @@ -1257,6 +1494,7 @@ signals: void handshakeInterruptedOnError(const QSslError& rrror); void gotAlert(QSsl::AlertLevel level, QSsl::AlertType type, const QString &message); void alertSent(QSsl::AlertLevel level, QSsl::AlertType type, const QString &message); + void socketEncrypted(QSslSocket *); protected: void incomingConnection(qintptr socketDescriptor) override @@ -1274,6 +1512,7 @@ protected: connect(socket, &QSslSocket::alertReceived, this, &SslServer::gotAlert); connect(socket, &QSslSocket::alertSent, this, &SslServer::alertSent); connect(socket, &QSslSocket::preSharedKeyAuthenticationRequired, this, &SslServer::preSharedKeyAuthenticationRequired); + connect(socket, &QSslSocket::encrypted, this, [this](){ emit socketEncrypted(socket); }); QFile file(m_keyFile); QVERIFY(file.open(QIODevice::ReadOnly)); @@ -1338,41 +1577,34 @@ void tst_QSslSocket::protocolServerSide_data() QTest::addColumn<QSsl::SslProtocol>("clientProtocol"); QTest::addColumn<bool>("works"); - QTest::newRow("tls1.0-tls1.0") << QSsl::TlsV1_0 << QSsl::TlsV1_0 << true; QTest::newRow("any-any") << QSsl::AnyProtocol << QSsl::AnyProtocol << true; QTest::newRow("secure-secure") << QSsl::SecureProtocols << QSsl::SecureProtocols << true; - QTest::newRow("tls1.0-secure") << QSsl::TlsV1_0 << QSsl::SecureProtocols << true; - QTest::newRow("tls1.0-any") << QSsl::TlsV1_0 << QSsl::AnyProtocol << true; - - QTest::newRow("secure-tls1.0") << QSsl::SecureProtocols << QSsl::TlsV1_0 << true; + QTest::newRow("tls1.0-secure") << Test::TlsV1_0 << QSsl::SecureProtocols << false; + QTest::newRow("secure-tls1.0") << QSsl::SecureProtocols << Test::TlsV1_0 << false; QTest::newRow("secure-any") << QSsl::SecureProtocols << QSsl::AnyProtocol << true; - QTest::newRow("tls1.0orlater-tls1.0") << QSsl::TlsV1_0OrLater << QSsl::TlsV1_0 << true; - QTest::newRow("tls1.0orlater-tls1.1") << QSsl::TlsV1_0OrLater << QSsl::TlsV1_1 << true; - QTest::newRow("tls1.0orlater-tls1.2") << QSsl::TlsV1_0OrLater << QSsl::TlsV1_2 << true; + QTest::newRow("tls1.0orlater-tls1.2") << Test::TlsV1_0OrLater << QSsl::TlsV1_2 << true; if (supportsTls13()) - QTest::newRow("tls1.0orlater-tls1.3") << QSsl::TlsV1_0OrLater << QSsl::TlsV1_3 << true; + QTest::newRow("tls1.0orlater-tls1.3") << Test::TlsV1_0OrLater << QSsl::TlsV1_3 << true; - QTest::newRow("tls1.1orlater-tls1.0") << QSsl::TlsV1_1OrLater << QSsl::TlsV1_0 << false; - QTest::newRow("tls1.1orlater-tls1.1") << QSsl::TlsV1_1OrLater << QSsl::TlsV1_1 << true; - QTest::newRow("tls1.1orlater-tls1.2") << QSsl::TlsV1_1OrLater << QSsl::TlsV1_2 << true; + QTest::newRow("tls1.1orlater-tls1.0") << Test::TlsV1_1OrLater << Test::TlsV1_0 << false; + QTest::newRow("tls1.1orlater-tls1.2") << Test::TlsV1_1OrLater << QSsl::TlsV1_2 << true; if (supportsTls13()) - QTest::newRow("tls1.1orlater-tls1.3") << QSsl::TlsV1_1OrLater << QSsl::TlsV1_3 << true; + QTest::newRow("tls1.1orlater-tls1.3") << Test::TlsV1_1OrLater << QSsl::TlsV1_3 << true; - QTest::newRow("tls1.2orlater-tls1.0") << QSsl::TlsV1_2OrLater << QSsl::TlsV1_0 << false; - QTest::newRow("tls1.2orlater-tls1.1") << QSsl::TlsV1_2OrLater << QSsl::TlsV1_1 << false; + QTest::newRow("tls1.2orlater-tls1.0") << QSsl::TlsV1_2OrLater << Test::TlsV1_0 << false; + QTest::newRow("tls1.2orlater-tls1.1") << QSsl::TlsV1_2OrLater << Test::TlsV1_1 << false; QTest::newRow("tls1.2orlater-tls1.2") << QSsl::TlsV1_2OrLater << QSsl::TlsV1_2 << true; if (supportsTls13()) { QTest::newRow("tls1.2orlater-tls1.3") << QSsl::TlsV1_2OrLater << QSsl::TlsV1_3 << true; - QTest::newRow("tls1.3orlater-tls1.0") << QSsl::TlsV1_3OrLater << QSsl::TlsV1_0 << false; - QTest::newRow("tls1.3orlater-tls1.1") << QSsl::TlsV1_3OrLater << QSsl::TlsV1_1 << false; + QTest::newRow("tls1.3orlater-tls1.0") << QSsl::TlsV1_3OrLater << Test::TlsV1_0 << false; + QTest::newRow("tls1.3orlater-tls1.1") << QSsl::TlsV1_3OrLater << Test::TlsV1_1 << false; QTest::newRow("tls1.3orlater-tls1.2") << QSsl::TlsV1_3OrLater << QSsl::TlsV1_2 << false; QTest::newRow("tls1.3orlater-tls1.3") << QSsl::TlsV1_3OrLater << QSsl::TlsV1_3 << true; } - QTest::newRow("any-tls1.0") << QSsl::AnyProtocol << QSsl::TlsV1_0 << true; QTest::newRow("any-secure") << QSsl::AnyProtocol << QSsl::SecureProtocols << true; } @@ -1429,23 +1661,37 @@ void tst_QSslSocket::protocolServerSide() QCOMPARE(client.isEncrypted(), works); } -#ifndef QT_NO_OPENSSL - void tst_QSslSocket::serverCipherPreferences() { - if (!QSslSocket::supportsSsl()) { - qWarning("SSL not supported, skipping test"); - return; - } + if (!isTestingOpenSsl) + QSKIP("The active TLS backend does not support server-side cipher preferences"); QFETCH_GLOBAL(bool, setProxy); if (setProxy) return; - // First using the default (server preference) + QSslCipher testedCiphers[2]; { + // First using the default (server preference) + const auto supportedCiphers = QSslConfiguration::supportedCiphers(); + int nSet = 0; + for (const auto &cipher : supportedCiphers) { + // Ciphersuites from TLS 1.2 and 1.3 are set separately, + // let's select 1.3 or above explicitly. + if (cipher.protocol() < QSsl::TlsV1_3) + continue; + + testedCiphers[nSet++] = cipher; + if (nSet == 2) + break; + } + + if (nSet != 2) + QSKIP("Failed to find two proper ciphersuites to test, bailing out."); + SslServer server; - server.ciphers = {QSslCipher("AES128-SHA"), QSslCipher("AES256-SHA")}; + server.protocol = QSsl::TlsV1_2OrLater; + server.ciphers = {testedCiphers[0], testedCiphers[1]}; QVERIFY(server.listen()); QEventLoop loop; @@ -1455,7 +1701,8 @@ void tst_QSslSocket::serverCipherPreferences() socket = &client; auto sslConfig = socket->sslConfiguration(); - sslConfig.setCiphers({QSslCipher("AES256-SHA"), QSslCipher("AES128-SHA")}); + sslConfig.setProtocol(QSsl::TlsV1_2OrLater); + sslConfig.setCiphers({testedCiphers[1], testedCiphers[0]}); socket->setSslConfiguration(sslConfig); // upon SSL wrong version error, errorOccurred will be triggered, not sslErrors @@ -1468,16 +1715,19 @@ void tst_QSslSocket::serverCipherPreferences() loop.exec(); QVERIFY(client.isEncrypted()); - QCOMPARE(client.sessionCipher().name(), QString("AES128-SHA")); + QCOMPARE(client.sessionCipher().name(), testedCiphers[0].name()); } { + if (QTestPrivate::isRunningArmOnX86()) + QSKIP("This test is known to crash on QEMU emulation for no good reason."); // Now using the client preferences SslServer server; QSslConfiguration config = QSslConfiguration::defaultConfiguration(); config.setSslOption(QSsl::SslOptionDisableServerCipherPreference, true); server.config = config; - server.ciphers = {QSslCipher("AES128-SHA"), QSslCipher("AES256-SHA")}; + server.protocol = QSsl::TlsV1_2OrLater; + server.ciphers = {testedCiphers[0], testedCiphers[1]}; QVERIFY(server.listen()); QEventLoop loop; @@ -1487,7 +1737,8 @@ void tst_QSslSocket::serverCipherPreferences() socket = &client; auto sslConfig = socket->sslConfiguration(); - sslConfig.setCiphers({QSslCipher("AES256-SHA"), QSslCipher("AES128-SHA")}); + sslConfig.setProtocol(QSsl::TlsV1_2OrLater); + sslConfig.setCiphers({testedCiphers[1], testedCiphers[0]}); socket->setSslConfiguration(sslConfig); // upon SSL wrong version error, errorOccurred will be triggered, not sslErrors @@ -1500,12 +1751,10 @@ void tst_QSslSocket::serverCipherPreferences() loop.exec(); QVERIFY(client.isEncrypted()); - QCOMPARE(client.sessionCipher().name(), QString("AES256-SHA")); + QCOMPARE(client.sessionCipher().name(), testedCiphers[1].name()); } } -#endif // QT_NO_OPENSSL - void tst_QSslSocket::setCaCertificates() { @@ -1575,18 +1824,46 @@ void tst_QSslSocket::setLocalCertificateChain() loop.exec(); QList<QSslCertificate> chain = socket->peerCertificateChain(); -#if QT_CONFIG(schannel) - QEXPECT_FAIL("", "Schannel cannot send intermediate certificates not " - "located in a system certificate store", - Abort); -#endif + if (isTestingSchannel) { + QEXPECT_FAIL("", "Schannel cannot send intermediate certificates not " + "located in a system certificate store", + Abort); + } + QCOMPARE(chain.size(), 2); - QCOMPARE(chain[0].serialNumber(), QByteArray("10:a0:ad:77:58:f6:6e:ae:46:93:a3:43:f9:59:8a:9e")); - QCOMPARE(chain[1].serialNumber(), QByteArray("3b:eb:99:c5:ea:d8:0b:5d:0b:97:5d:4f:06:75:4b:e1")); + QCOMPARE(chain[0].serialNumber(), + QByteArray("58:df:33:c1:9b:bc:c5:51:7a:00:86:64:43:94:41:e2:26:ef:3f:89")); + QCOMPARE(chain[1].serialNumber(), + QByteArray("11:72:34:bc:21:e6:ca:04:24:13:f8:35:48:84:a6:e9:de:96:22:15")); } -void tst_QSslSocket::setPrivateKey() +void tst_QSslSocket::tlsConfiguration() { + QFETCH_GLOBAL(const bool, setProxy); + if (setProxy) + return; + // Test some things not covered by any other auto-test. + QSslSocket socket; + auto tlsConfig = socket.sslConfiguration(); + QVERIFY(tlsConfig.sessionCipher().isNull()); + QCOMPARE(tlsConfig.addCaCertificates(QStringLiteral("nonexisting/chain.crt")), false); + QCOMPARE(tlsConfig.sessionProtocol(), QSsl::UnknownProtocol); + QSslConfiguration nullConfig; + QVERIFY(nullConfig.isNull()); +#if QT_CONFIG(openssl) + nullConfig.setEllipticCurves(tlsConfig.ellipticCurves()); + QCOMPARE(nullConfig.ellipticCurves(), tlsConfig.ellipticCurves()); +#endif + QMap<QByteArray, QVariant> backendConfig; + backendConfig["DTLSMTU"] = QVariant::fromValue(1024); + backendConfig["DTLSTIMEOUTMS"] = QVariant::fromValue(1000); + nullConfig.setBackendConfiguration(backendConfig); + QCOMPARE(nullConfig.backendConfiguration(), backendConfig); + QTest::ignoreMessage(QtWarningMsg, "QSslConfiguration::setPeerVerifyDepth: cannot set negative depth of -1000"); + nullConfig.setPeerVerifyDepth(-1000); + QVERIFY(nullConfig.peerVerifyDepth() != -1000); + nullConfig.setPeerVerifyDepth(100); + QCOMPARE(nullConfig.peerVerifyDepth(), 100); } void tst_QSslSocket::setSocketDescriptor() @@ -1629,11 +1906,16 @@ void tst_QSslSocket::setSslConfiguration_data() QTest::newRow("empty") << QSslConfiguration() << false; QSslConfiguration conf = QSslConfiguration::defaultConfiguration(); QTest::newRow("default") << conf << false; // does not contain test server cert - QList<QSslCertificate> testServerCert = QSslCertificate::fromPath(httpServerCertChainPath()); - conf.setCaCertificates(testServerCert); - QTest::newRow("set-root-cert") << conf << true; - conf.setProtocol(QSsl::SecureProtocols); - QTest::newRow("secure") << conf << true; + + if (!isTestingSecureTransport) { + QList<QSslCertificate> testServerCert = QSslCertificate::fromPath(httpServerCertChainPath()); + conf.setCaCertificates(testServerCert); + QTest::newRow("set-root-cert") << conf << true; + conf.setProtocol(QSsl::SecureProtocols); + QTest::newRow("secure") << conf << true; + } else { + qWarning("Skipping the cases with certificate, SecureTransport does not like old certificate on the test server"); + } } void tst_QSslSocket::setSslConfiguration() @@ -1644,9 +1926,7 @@ void tst_QSslSocket::setSslConfiguration() QSslSocketPtr socket = newSocket(); QFETCH(QSslConfiguration, configuration); socket->setSslConfiguration(configuration); -#if QT_CONFIG(schannel) // old certificate not supported with TLS 1.2 - socket->setProtocol(QSsl::SslProtocol::TlsV1_1); -#endif + this->socket = socket.data(); socket->connectToHostEncrypted(QtNetworkSettings::httpServerName(), 443); QFETCH(bool, works); @@ -1699,9 +1979,19 @@ void tst_QSslSocket::waitForConnectedEncryptedReadyRead() if (!QSslSocket::supportsSsl()) return; + // Starting from OpenSSL v 3.1.1 deprecated protocol versions (we want to use here) are not available by default. + if (isSecurityLevel0Required) + QSKIP("Testing with OpenSSL backend, but security level 0 is required for TLS v1.1 or earlier"); + QSslSocketPtr socket = newSocket(); this->socket = socket.data(); + // Set TLS 1.0 or above because the server doesn't support TLS 1.2 or above + // QTQAINFRA-4499 + QSslConfiguration config = socket->sslConfiguration(); + config.setProtocol(Test::TlsV1_0OrLater); + socket->setSslConfiguration(config); + connect(this->socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(ignoreErrorSlot())); socket->connectToHostEncrypted(QtNetworkSettings::imapServerName(), 993); @@ -1980,8 +2270,17 @@ void tst_QSslSocket::spontaneousWrite() receiver->startClientEncryption(); // SSL handshake: - connect(receiver, SIGNAL(encrypted()), SLOT(exitLoop())); + // Need to wait for both sides to emit encrypted as the ordering of which + // ones emits encrypted() changes depending on whether we use TLS 1.2 or 1.3 + int waitFor = 2; + auto earlyQuitter = [&waitFor]() { + if (!--waitFor) + exitLoop(); + }; + connect(receiver, &QSslSocket::encrypted, this, earlyQuitter); + connect(sender, &QSslSocket::encrypted, this, earlyQuitter); enterLoop(1); + QVERIFY(!timeout()); QVERIFY(sender->isEncrypted()); QVERIFY(receiver->isEncrypted()); @@ -2024,9 +2323,21 @@ void tst_QSslSocket::setReadBufferSize() receiver->ignoreSslErrors(); receiver->startClientEncryption(); - // SSL handshake: - connect(receiver, SIGNAL(encrypted()), SLOT(exitLoop())); + // Need to wait for both sides to emit encrypted as the ordering of which + // ones emits encrypted() changes depending on whether we use TLS 1.2 or 1.3 + int waitFor = 2; + auto earlyQuitter = [&waitFor]() { + if (!--waitFor) + exitLoop(); + }; + connect(receiver, &QSslSocket::encrypted, this, earlyQuitter); + connect(sender, &QSslSocket::encrypted, this, earlyQuitter); + enterLoop(1); + if (!sender->isEncrypted()) { + connect(sender, &QSslSocket::encrypted, this, &tst_QSslSocket::exitLoop); + enterLoop(1); + } QVERIFY(!timeout()); QVERIFY(sender->isEncrypted()); QVERIFY(receiver->isEncrypted()); @@ -2346,9 +2657,7 @@ void tst_QSslSocket::verifyMode() return; QSslSocket socket; -#if QT_CONFIG(schannel) // old certificate not supported with TLS 1.2 - socket.setProtocol(QSsl::SslProtocol::TlsV1_1); -#endif + QCOMPARE(socket.peerVerifyMode(), QSslSocket::AutoVerifyPeer); socket.setPeerVerifyMode(QSslSocket::VerifyNone); QCOMPARE(socket.peerVerifyMode(), QSslSocket::VerifyNone); @@ -2361,7 +2670,7 @@ void tst_QSslSocket::verifyMode() QSKIP("Skipping flaky test - See QTBUG-29941"); QList<QSslError> expectedErrors = QList<QSslError>() - << QSslError(FLUKE_CERTIFICATE_ERROR, socket.peerCertificate()); + << QSslError(flukeCertificateError, socket.peerCertificate()); QCOMPARE(socket.sslHandshakeErrors(), expectedErrors); socket.abort(); @@ -2392,6 +2701,43 @@ void tst_QSslSocket::verifyDepth() QCOMPARE(socket.peerVerifyDepth(), 1); } +void tst_QSslSocket::verifyAndDefaultConfiguration() +{ + QFETCH_GLOBAL(const bool, setProxy); + if (setProxy) + return; + if (!QSslSocket::supportedFeatures().contains(QSsl::SupportedFeature::CertificateVerification)) + QSKIP("This backend doesn't support manual certificate verification"); + const auto defaultCACertificates = QSslConfiguration::defaultConfiguration().caCertificates(); + const auto chainGuard = qScopeGuard([&defaultCACertificates]{ + auto conf = QSslConfiguration::defaultConfiguration(); + conf.setCaCertificates(defaultCACertificates); + QSslConfiguration::setDefaultConfiguration(conf); + }); + + auto chain = QSslCertificate::fromPath(testDataDir + QStringLiteral("certs/qtiochain.crt"), QSsl::Pem); + QCOMPARE(chain.size(), 2); + QVERIFY(!chain.at(0).isNull()); + QVERIFY(!chain.at(1).isNull()); + auto errors = QSslCertificate::verify(chain); + // At least, test that 'verify' did not alter the default configuration: + QCOMPARE(defaultCACertificates, QSslConfiguration::defaultConfiguration().caCertificates()); + if (!errors.isEmpty()) + QSKIP("The certificate for qt.io could not be trusted, skipping the rest of the test"); +#ifdef Q_OS_WINDOWS + const auto fakeCaChain = QSslCertificate::fromPath(testDataDir + QStringLiteral("certs/fluke.cert")); + QCOMPARE(fakeCaChain.size(), 1); + const auto caCert = fakeCaChain.at(0); + QVERIFY(!caCert.isNull()); + auto conf = QSslConfiguration::defaultConfiguration(); + conf.setCaCertificates({caCert}); + QSslConfiguration::setDefaultConfiguration(conf); + errors = QSslCertificate::verify(chain); + QVERIFY(errors.size() > 0); + QCOMPARE(QSslConfiguration::defaultConfiguration().caCertificates(), QList{caCert}); +#endif +} + void tst_QSslSocket::disconnectFromHostWhenConnecting() { QSslSocketPtr socket = newSocket(); @@ -2436,7 +2782,7 @@ void tst_QSslSocket::disconnectFromHostWhenConnected() QCOMPARE(socket->bytesToWrite(), qint64(0)); } -#ifndef QT_NO_OPENSSL +#if QT_CONFIG(openssl) class BrokenPskHandshake : public QTcpServer { @@ -2467,6 +2813,9 @@ private: void tst_QSslSocket::closeWhileEmittingSocketError() { + if (!isTestingOpenSsl) + QSKIP("The active TLS backend does not support this test"); + QFETCH_GLOBAL(bool, setProxy); if (setProxy) return; @@ -2487,17 +2836,17 @@ void tst_QSslSocket::closeWhileEmittingSocketError() // Make sure we have some data buffered so that close will try to flush: clientSocket.write(QByteArray(1000000, Qt::Uninitialized)); - QTestEventLoop::instance().enterLoopMSecs(1000); + QTestEventLoop::instance().enterLoop(1s); QVERIFY(!QTestEventLoop::instance().timeout()); - QCOMPARE(socketErrorSpy.count(), 1); + QCOMPARE(socketErrorSpy.size(), 1); } -#endif // QT_NO_OPENSSL +#endif // Feature 'openssl'. void tst_QSslSocket::resetProxy() { -#ifndef QT_NO_NETWORKPROXY +#if QT_CONFIG(networkproxy) QFETCH_GLOBAL(bool, setProxy); if (setProxy) return; @@ -2541,7 +2890,7 @@ void tst_QSslSocket::resetProxy() socket2.setProxy(goodProxy); socket2.connectToHostEncrypted(QtNetworkSettings::httpServerName(), 443); QVERIFY2(socket2.waitForConnected(10000), qPrintable(socket.errorString())); -#endif // QT_NO_NETWORKPROXY +#endif // QT_CONFIG(networkproxy) } void tst_QSslSocket::ignoreSslErrorsList_data() @@ -2553,8 +2902,8 @@ void tst_QSslSocket::ignoreSslErrorsList_data() QList<QSslError> expectedSslErrors; // fromPath gives us a list of certs, but it actually only contains one QList<QSslCertificate> certs = QSslCertificate::fromPath(httpServerCertChainPath()); - QSslError rightError(FLUKE_CERTIFICATE_ERROR, certs.at(0)); - QSslError wrongError(FLUKE_CERTIFICATE_ERROR); + QSslError rightError(flukeCertificateError, certs.at(0)); + QSslError wrongError(flukeCertificateError); QTest::newRow("SSL-failure-empty-list") << expectedSslErrors << 1; @@ -2587,7 +2936,7 @@ void tst_QSslSocket::ignoreSslErrorsList() bool expectEncryptionSuccess = (expectedSslErrorSignalCount == 0); if (socket.waitForEncrypted(10000) != expectEncryptionSuccess) QSKIP("Skipping flaky test - See QTBUG-29941"); - QCOMPARE(sslErrorsSpy.count(), expectedSslErrorSignalCount); + QCOMPARE(sslErrorsSpy.size(), expectedSslErrorSignalCount); } void tst_QSslSocket::ignoreSslErrorsListWithSlot_data() @@ -2648,9 +2997,7 @@ void tst_QSslSocket::abortOnSslErrors() void tst_QSslSocket::readFromClosedSocket() { QSslSocketPtr socket = newSocket(); -#if QT_CONFIG(schannel) // old certificate not supported with TLS 1.2 - socket->setProtocol(QSsl::SslProtocol::TlsV1_1); -#endif + socket->ignoreSslErrors(); socket->connectToHostEncrypted(QtNetworkSettings::httpServerName(), 443); socket->ignoreSslErrors(); @@ -2747,9 +3094,16 @@ void tst_QSslSocket::blacklistedCertificates() connect(receiver, SIGNAL(encrypted()), SLOT(exitLoop())); enterLoop(1); QList<QSslError> sslErrors = receiver->sslHandshakeErrors(); - QVERIFY(sslErrors.count() > 0); + QVERIFY(sslErrors.size() > 0); // there are more errors (self signed cert and hostname mismatch), but we only care about the blacklist error - QCOMPARE(sslErrors.at(0).error(), QSslError::CertificateBlacklisted); + std::optional<QSslError> blacklistedError; + for (const QSslError &error : sslErrors) { + if (error.error() == QSslError::CertificateBlacklisted) { + blacklistedError = error; + break; + } + } + QVERIFY2(blacklistedError, "CertificateBlacklisted error not found!"); } void tst_QSslSocket::versionAccessors() @@ -2761,60 +3115,6 @@ void tst_QSslSocket::versionAccessors() qDebug() << QString::number(QSslSocket::sslLibraryVersionNumber(), 16); } -#ifndef QT_NO_OPENSSL -void tst_QSslSocket::sslOptions() -{ - if (!QSslSocket::supportsSsl()) - return; - -#ifdef SSL_OP_NO_COMPRESSION - QCOMPARE(QSslSocketBackendPrivate::setupOpenSslOptions(QSsl::SecureProtocols, - QSslConfigurationPrivate::defaultSslOptions), - long(SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_COMPRESSION|SSL_OP_CIPHER_SERVER_PREFERENCE)); -#else - QCOMPARE(QSslSocketBackendPrivate::setupOpenSslOptions(QSsl::SecureProtocols, - QSslConfigurationPrivate::defaultSslOptions), - long(SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_CIPHER_SERVER_PREFERENCE)); -#endif - - QCOMPARE(QSslSocketBackendPrivate::setupOpenSslOptions(QSsl::SecureProtocols, - QSsl::SslOptionDisableEmptyFragments - |QSsl::SslOptionDisableLegacyRenegotiation), - long(SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_CIPHER_SERVER_PREFERENCE)); - -#ifdef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION - QCOMPARE(QSslSocketBackendPrivate::setupOpenSslOptions(QSsl::SecureProtocols, - QSsl::SslOptionDisableEmptyFragments), - long((SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION|SSL_OP_CIPHER_SERVER_PREFERENCE))); -#endif - -#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS - QCOMPARE(QSslSocketBackendPrivate::setupOpenSslOptions(QSsl::SecureProtocols, - QSsl::SslOptionDisableLegacyRenegotiation), - long((SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_CIPHER_SERVER_PREFERENCE) & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS)); -#endif - -#ifdef SSL_OP_NO_TICKET - QCOMPARE(QSslSocketBackendPrivate::setupOpenSslOptions(QSsl::SecureProtocols, - QSsl::SslOptionDisableEmptyFragments - |QSsl::SslOptionDisableLegacyRenegotiation - |QSsl::SslOptionDisableSessionTickets), - long((SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_TICKET|SSL_OP_CIPHER_SERVER_PREFERENCE))); -#endif - -#ifdef SSL_OP_NO_TICKET -#ifdef SSL_OP_NO_COMPRESSION - QCOMPARE(QSslSocketBackendPrivate::setupOpenSslOptions(QSsl::SecureProtocols, - QSsl::SslOptionDisableEmptyFragments - |QSsl::SslOptionDisableLegacyRenegotiation - |QSsl::SslOptionDisableSessionTickets - |QSsl::SslOptionDisableCompression), - long((SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_TICKET|SSL_OP_NO_COMPRESSION|SSL_OP_CIPHER_SERVER_PREFERENCE))); -#endif -#endif -} -#endif - void tst_QSslSocket::encryptWithoutConnecting() { if (!QSslSocket::supportsSsl()) @@ -2829,6 +3129,10 @@ void tst_QSslSocket::encryptWithoutConnecting() void tst_QSslSocket::resume_data() { + // Starting from OpenSSL v 3.1.1 deprecated protocol versions (we want to use in 'resume' test) are not available by default. + if (isSecurityLevel0Required) + QSKIP("Testing with OpenSSL backend, but security level 0 is required for TLS v1.1 or earlier"); + QTest::addColumn<bool>("ignoreErrorsAfterPause"); QTest::addColumn<QList<QSslError> >("errorsToIgnore"); QTest::addColumn<bool>("expectSuccess"); @@ -2840,8 +3144,8 @@ void tst_QSslSocket::resume_data() // Note, httpServerCertChainPath() it's ... because we use the same certificate on // different services. We'll be actually connecting to IMAP server. QList<QSslCertificate> certs = QSslCertificate::fromPath(httpServerCertChainPath()); - QSslError rightError(FLUKE_CERTIFICATE_ERROR, certs.at(0)); - QSslError wrongError(FLUKE_CERTIFICATE_ERROR); + QSslError rightError(flukeCertificateError, certs.at(0)); + QSslError wrongError(flukeCertificateError); errorsList.append(wrongError); QTest::newRow("ignoreSpecificErrors-Wrong") << true << errorsList << false; errorsList.clear(); @@ -2864,6 +3168,12 @@ void tst_QSslSocket::resume() QSslSocket socket; socket.setPauseMode(QAbstractSocket::PauseOnSslErrors); + // Set TLS 1.0 or above because the server doesn't support TLS 1.2 or above + // QTQAINFRA-4499 + QSslConfiguration config = socket.sslConfiguration(); + config.setProtocol(Test::TlsV1_0OrLater); + socket.setSslConfiguration(config); + QSignalSpy sslErrorSpy(&socket, SIGNAL(sslErrors(QList<QSslError>))); QSignalSpy encryptedSpy(&socket, SIGNAL(encrypted())); QSignalSpy errorSpy(&socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError))); @@ -2879,9 +3189,9 @@ void tst_QSslSocket::resume() QFETCH_GLOBAL(bool, setProxy); if (setProxy && QTestEventLoop::instance().timeout()) QSKIP("Skipping flaky test - See QTBUG-29941"); - QCOMPARE(sslErrorSpy.count(), 1); - QCOMPARE(errorSpy.count(), 0); - QCOMPARE(encryptedSpy.count(), 0); + QCOMPARE(sslErrorSpy.size(), 1); + QCOMPARE(errorSpy.size(), 0); + QCOMPARE(encryptedSpy.size(), 0); QVERIFY(!socket.isEncrypted()); if (ignoreErrorsAfterPause) { if (errorsToIgnore.empty()) @@ -2893,15 +3203,15 @@ void tst_QSslSocket::resume() QTestEventLoop::instance().enterLoop(10); QVERIFY(!QTestEventLoop::instance().timeout()); // quit by encrypted() or error() signal if (expectSuccess) { - QCOMPARE(encryptedSpy.count(), 1); + QCOMPARE(encryptedSpy.size(), 1); QVERIFY(socket.isEncrypted()); - QCOMPARE(errorSpy.count(), 0); + QCOMPARE(errorSpy.size(), 0); socket.disconnectFromHost(); QVERIFY(socket.waitForDisconnected(10000)); } else { - QCOMPARE(encryptedSpy.count(), 0); + QCOMPARE(encryptedSpy.size(), 0); QVERIFY(!socket.isEncrypted()); - QCOMPARE(errorSpy.count(), 1); + QCOMPARE(errorSpy.size(), 1); QCOMPARE(socket.error(), QAbstractSocket::SslHandshakeFailedError); } } @@ -3088,17 +3398,17 @@ void tst_QSslSocket::qtbug18498_peek2() bigblock.fill('#', QIODEVICE_BUFFERSIZE + 1024); QVERIFY(client->write(QByteArray("head"))); QVERIFY(client->write(bigblock)); - QTRY_COMPARE(server->bytesAvailable(), bigblock.length() + 4); + QTRY_COMPARE(server->bytesAvailable(), bigblock.size() + 4); QCOMPARE(server->read(4), QByteArray("head")); - QCOMPARE(server->peek(bigblock.length()), bigblock); - b.reserve(bigblock.length()); - b.resize(server->peek(b.data(), bigblock.length())); + QCOMPARE(server->peek(bigblock.size()), bigblock); + b.reserve(bigblock.size()); + b.resize(server->peek(b.data(), bigblock.size())); QCOMPARE(b, bigblock); //check oversized peek - QCOMPARE(server->peek(bigblock.length() * 3), bigblock); - b.reserve(bigblock.length() * 3); - b.resize(server->peek(b.data(), bigblock.length() * 3)); + QCOMPARE(server->peek(bigblock.size() * 3), bigblock); + b.reserve(bigblock.size() * 3); + b.resize(server->peek(b.data(), bigblock.size() * 3)); QCOMPARE(b, bigblock); QCOMPARE(server->readAll(), bigblock); @@ -3166,7 +3476,13 @@ void tst_QSslSocket::dhServer() return; SslServer server; - server.ciphers = {QSslCipher("DHE-RSA-AES256-SHA"), QSslCipher("DHE-DSS-AES256-SHA")}; + QSslCipher rsaCipher("DHE-RSA-AES256-SHA"); + QSslCipher dssCipher("DHE-DSS-AES256-SHA"); + if (rsaCipher.isNull()) + QSKIP("The current backend doesn't support DHE-RSA-AES256-SHA"); + if (dssCipher.isNull()) + QSKIP("The current backend doesn't support DHE-DSS-AES256-SHA"); + server.ciphers = { rsaCipher, dssCipher }; QVERIFY(server.listen()); QEventLoop loop; @@ -3184,18 +3500,20 @@ void tst_QSslSocket::dhServer() QCOMPARE(client.state(), QAbstractSocket::ConnectedState); } -#ifndef QT_NO_OPENSSL +#if QT_CONFIG(openssl) void tst_QSslSocket::dhServerCustomParamsNull() { - if (!QSslSocket::supportsSsl()) - QSKIP("No SSL support"); + if (!isTestingOpenSsl) + QSKIP("This test requires OpenSSL as the active TLS backend"); QFETCH_GLOBAL(bool, setProxy); if (setProxy) return; + const QSslCipher cipherWithDH("DHE-RSA-AES256-SHA256"); SslServer server; - server.ciphers = {QSslCipher("DHE-RSA-AES256-SHA"), QSslCipher("DHE-DSS-AES256-SHA")}; + server.ciphers = {cipherWithDH}; + server.protocol = QSsl::TlsV1_2; QSslConfiguration cfg = server.config; cfg.setDiffieHellmanParameters(QSslDiffieHellmanParameters()); @@ -3207,6 +3525,8 @@ void tst_QSslSocket::dhServerCustomParamsNull() QTimer::singleShot(5000, &loop, SLOT(quit())); QSslSocket client; + QSslConfiguration config = client.sslConfiguration(); + client.setSslConfiguration(config); socket = &client; connect(socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), &loop, SLOT(quit())); connect(socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(ignoreErrorSlot())); @@ -3216,22 +3536,25 @@ void tst_QSslSocket::dhServerCustomParamsNull() loop.exec(); - QVERIFY(client.state() != QAbstractSocket::ConnectedState); + QCOMPARE(client.state(), QAbstractSocket::ConnectedState); + QCOMPARE(client.sessionCipher(), cipherWithDH); } -#endif // QT_NO_OPENSSL -#ifndef QT_NO_OPENSSL void tst_QSslSocket::dhServerCustomParams() { if (!QSslSocket::supportsSsl()) QSKIP("No SSL support"); + if (!QSslSocket::isClassImplemented(QSsl::ImplementedClass::DiffieHellman)) + QSKIP("The current backend doesn't support diffie hellman parameters"); QFETCH_GLOBAL(bool, setProxy); if (setProxy) return; SslServer server; - server.ciphers = {QSslCipher("DHE-RSA-AES256-SHA"), QSslCipher("DHE-DSS-AES256-SHA")}; + const QSslCipher cipherWithDH("DHE-RSA-AES256-SHA256"); + server.ciphers = {cipherWithDH}; + server.protocol = QSsl::TlsV1_2; QSslConfiguration cfg = server.config; @@ -3261,9 +3584,10 @@ void tst_QSslSocket::dhServerCustomParams() loop.exec(); - QVERIFY(client.state() == QAbstractSocket::ConnectedState); + QCOMPARE(client.state(), QAbstractSocket::ConnectedState); + QCOMPARE(client.sessionCipher(), cipherWithDH); } -#endif // QT_NO_OPENSSL +#endif // QT_CONFIG(openssl) void tst_QSslSocket::ecdhServer() { @@ -3277,7 +3601,10 @@ void tst_QSslSocket::ecdhServer() return; SslServer server; - server.ciphers = {QSslCipher("ECDHE-RSA-AES128-SHA")}; + QSslCipher cipher("ECDHE-RSA-AES128-SHA"); + if (cipher.isNull()) + QSKIP("The current backend doesn't support ECDHE-RSA-AES128-SHA"); + server.ciphers = {cipher}; QVERIFY(server.listen()); QEventLoop loop; @@ -3365,14 +3692,15 @@ void tst_QSslSocket::verifyClientCertificate_data() void tst_QSslSocket::verifyClientCertificate() { -#if QT_CONFIG(securetransport) - // We run both client and server on the same machine, - // this means, client can update keychain with client's certificates, - // and server later will use the same certificates from the same - // keychain thus making tests fail (wrong number of certificates, - // success instead of failure etc.). - QSKIP("This test can not work with Secure Transport"); -#endif // QT_CONFIG(securetransport) + if (isTestingSecureTransport) { + // We run both client and server on the same machine, + // this means, client can update keychain with client's certificates, + // and server later will use the same certificates from the same + // keychain thus making tests fail (wrong number of certificates, + // success instead of failure etc.). + QSKIP("This test can not work with Secure Transport"); + } + if (!QSslSocket::supportsSsl()) { qWarning("SSL not supported, skipping test"); return; @@ -3383,12 +3711,13 @@ void tst_QSslSocket::verifyClientCertificate() return; QFETCH(QSslSocket::PeerVerifyMode, peerVerifyMode); -#if QT_CONFIG(schannel) - if (peerVerifyMode == QSslSocket::QueryPeer || peerVerifyMode == QSslSocket::AutoVerifyPeer) - QSKIP("Schannel doesn't tackle requesting a certificate and not receiving one."); -#endif + if (isTestingSchannel) { + if (peerVerifyMode == QSslSocket::QueryPeer || peerVerifyMode == QSslSocket::AutoVerifyPeer) + QSKIP("Schannel doesn't tackle requesting a certificate and not receiving one."); + } SslServer server; + server.protocol = QSsl::TlsV1_2; server.addCaCertificates = testDataDir + "certs/bogus-ca.crt"; server.ignoreSslErrors = false; server.peerVerifyMode = peerVerifyMode; @@ -3402,6 +3731,9 @@ void tst_QSslSocket::verifyClientCertificate() QSslSocket client; client.setLocalCertificateChain(clientCerts); client.setPrivateKey(clientKey); + QSslConfiguration config = client.sslConfiguration(); + config.setProtocol(Test::TlsV1_0OrLater); + client.setSslConfiguration(config); socket = &client; connect(socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(ignoreErrorSlot())); @@ -3426,13 +3758,13 @@ void tst_QSslSocket::verifyClientCertificate() QVERIFY(server.socket->peerCertificateChain().isEmpty()); } else { QCOMPARE(server.socket->peerCertificate(), clientCerts.first()); -#if QT_CONFIG(schannel) - if (clientCerts.count() == 1 && server.socket->peerCertificateChain().count() == 2) { - QEXPECT_FAIL("", - "Schannel includes the entire chain, not just the leaf and intermediates", - Continue); + if (isTestingSchannel) { + if (clientCerts.size() == 1 && server.socket->peerCertificateChain().size() == 2) { + QEXPECT_FAIL("", + "Schannel includes the entire chain, not just the leaf and intermediates", + Continue); + } } -#endif QCOMPARE(server.socket->peerCertificateChain(), clientCerts); } @@ -3443,7 +3775,18 @@ void tst_QSslSocket::verifyClientCertificate() void tst_QSslSocket::readBufferMaxSize() { -#if QT_CONFIG(securetransport) || QT_CONFIG(schannel) + // QTBUG-94186: originally, only SecureTransport was + // running this test (since it was a bug in that backend, + // see comment below), after TLS plugins were introduced, + // we enabled this test on all backends, as a part of + // the clean up. This revealed the fact that this test + // is flaky, and it started to fail on Windows. + // TODO: this is a temporary solution, to be removed + // as soon as 94186 fixed for good. + if (!isTestingSecureTransport) + QSKIP("This test is flaky with TLS backend other than SecureTransport"); + + // QTBUG-55170: // SecureTransport back-end was ignoring read-buffer // size limit, resulting (potentially) in a constantly @@ -3468,11 +3811,19 @@ void tst_QSslSocket::readBufferMaxSize() socket = client.data(); connect(socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), &loop, SLOT(quit())); connect(socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(ignoreErrorSlot())); - connect(socket, SIGNAL(encrypted()), &loop, SLOT(quit())); client->connectToHostEncrypted(QHostAddress(QHostAddress::LocalHost).toString(), server.serverPort()); + int waitFor = 2; + auto earlyQuitter = [&loop, &waitFor]() { + if (!--waitFor) + loop.exit(); + }; + + connect(socket, &QSslSocket::encrypted, &loop, earlyQuitter); + connect(&server, &SslServer::socketEncrypted, &loop, earlyQuitter); + // Wait for 'encrypted' first: QTimer::singleShot(5000, &loop, SLOT(quit())); loop.exec(); @@ -3498,9 +3849,6 @@ void tst_QSslSocket::readBufferMaxSize() loop.exec(); QCOMPARE(client->bytesAvailable() + readSoFar, message.size()); -#else - // Not needed, QSslSocket works correctly with other back-ends. -#endif // QT_CONFIG(securetransport) || QT_CONFIG(schannel) } void tst_QSslSocket::setEmptyDefaultConfiguration() // this test should be last, as it has some side effects @@ -3525,20 +3873,13 @@ void tst_QSslSocket::setEmptyDefaultConfiguration() // this test should be last, void tst_QSslSocket::allowedProtocolNegotiation() { -#ifndef ALPN_SUPPORTED - QSKIP("ALPN is unsupported, skipping test"); -#endif - -#if QT_CONFIG(schannel) - if (QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows8_1) - QSKIP("ALPN is not supported on this version of Windows using Schannel."); -#endif + if (!hasServerAlpn) + QSKIP("Server-side ALPN is unsupported, skipping test"); QFETCH_GLOBAL(bool, setProxy); if (setProxy) return; - const QByteArray expectedNegotiated("cool-protocol"); QList<QByteArray> serverProtos; serverProtos << expectedNegotiated << "not-so-cool-protocol"; @@ -3560,15 +3901,25 @@ void tst_QSslSocket::allowedProtocolNegotiation() QEventLoop loop; QTimer::singleShot(5000, &loop, SLOT(quit())); - connect(&clientSocket, SIGNAL(encrypted()), &loop, SLOT(quit())); + + // Need to wait for both sides to emit encrypted as the ordering of which + // ones emits encrypted() changes depending on whether we use TLS 1.2 or 1.3 + int waitFor = 2; + auto earlyQuitter = [&loop, &waitFor]() { + if (!--waitFor) + loop.exit(); + }; + connect(&clientSocket, &QSslSocket::encrypted, &loop, earlyQuitter); + connect(&server, &SslServer::socketEncrypted, &loop, earlyQuitter); + loop.exec(); - QVERIFY(server.socket->sslConfiguration().nextNegotiatedProtocol() == - clientSocket.sslConfiguration().nextNegotiatedProtocol()); - QVERIFY(server.socket->sslConfiguration().nextNegotiatedProtocol() == expectedNegotiated); + QCOMPARE(server.socket->sslConfiguration().nextNegotiatedProtocol(), + clientSocket.sslConfiguration().nextNegotiatedProtocol()); + QCOMPARE(server.socket->sslConfiguration().nextNegotiatedProtocol(), expectedNegotiated); } -#ifndef QT_NO_OPENSSL +#if QT_CONFIG(openssl) class PskProvider : public QObject { Q_OBJECT @@ -3626,7 +3977,7 @@ public: config(QSslConfiguration::defaultConfiguration()), ignoreSslErrors(true), peerVerifyMode(QSslSocket::AutoVerifyPeer), - protocol(QSsl::TlsV1_0), + protocol(QSsl::TlsV1_2), m_pskProvider() { m_pskProvider.m_server = true; @@ -3672,8 +4023,12 @@ protected slots: socket->ignoreSslErrors(); } }; + void tst_QSslSocket::simplePskConnect_data() { + if (!isTestingOpenSsl) + QSKIP("The active TLS backend does support PSK"); + QTest::addColumn<PskConnectTestType>("pskTestType"); QTest::newRow("PskConnectDoNotHandlePsk") << PskConnectDoNotHandlePsk; QTest::newRow("PskConnectEmptyCredentials") << PskConnectEmptyCredentials; @@ -3764,14 +4119,14 @@ void tst_QSslSocket::simplePskConnect() case PskConnectWrongCredentials: // provide totally wrong credentials - provider.setIdentity(PSK_CLIENT_IDENTITY.left(PSK_CLIENT_IDENTITY.length() - 1)); - provider.setPreSharedKey(PSK_CLIENT_PRESHAREDKEY.left(PSK_CLIENT_PRESHAREDKEY.length() - 1)); + provider.setIdentity(PSK_CLIENT_IDENTITY.left(PSK_CLIENT_IDENTITY.size() - 1)); + provider.setPreSharedKey(PSK_CLIENT_PRESHAREDKEY.left(PSK_CLIENT_PRESHAREDKEY.size() - 1)); connect(&socket, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)), &provider, SLOT(providePsk(QSslPreSharedKeyAuthenticator*))); break; case PskConnectWrongIdentity: // right PSK, wrong identity - provider.setIdentity(PSK_CLIENT_IDENTITY.left(PSK_CLIENT_IDENTITY.length() - 1)); + provider.setIdentity(PSK_CLIENT_IDENTITY.left(PSK_CLIENT_IDENTITY.size() - 1)); provider.setPreSharedKey(PSK_CLIENT_PRESHAREDKEY); connect(&socket, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)), &provider, SLOT(providePsk(QSslPreSharedKeyAuthenticator*))); break; @@ -3779,7 +4134,7 @@ void tst_QSslSocket::simplePskConnect() case PskConnectWrongPreSharedKey: // right identity, wrong PSK provider.setIdentity(PSK_CLIENT_IDENTITY); - provider.setPreSharedKey(PSK_CLIENT_PRESHAREDKEY.left(PSK_CLIENT_PRESHAREDKEY.length() - 1)); + provider.setPreSharedKey(PSK_CLIENT_PRESHAREDKEY.left(PSK_CLIENT_PRESHAREDKEY.size() - 1)); connect(&socket, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)), &provider, SLOT(providePsk(QSslPreSharedKeyAuthenticator*))); break; @@ -3831,32 +4186,32 @@ void tst_QSslSocket::simplePskConnect() // Entered connecting state QCOMPARE(socket.state(), QAbstractSocket::ConnectingState); - QCOMPARE(connectedSpy.count(), 0); - QCOMPARE(hostFoundSpy.count(), 1); - QCOMPARE(disconnectedSpy.count(), 0); + QCOMPARE(connectedSpy.size(), 0); + QCOMPARE(hostFoundSpy.size(), 1); + QCOMPARE(disconnectedSpy.size(), 0); enterLoop(10); // Entered connected state QCOMPARE(socket.state(), QAbstractSocket::ConnectedState); QCOMPARE(socket.mode(), QSslSocket::UnencryptedMode); QVERIFY(!socket.isEncrypted()); - QCOMPARE(connectedSpy.count(), 1); - QCOMPARE(hostFoundSpy.count(), 1); - QCOMPARE(disconnectedSpy.count(), 0); + QCOMPARE(connectedSpy.size(), 1); + QCOMPARE(hostFoundSpy.size(), 1); + QCOMPARE(disconnectedSpy.size(), 0); // Enter encrypted mode socket.startClientEncryption(); QCOMPARE(socket.mode(), QSslSocket::SslClientMode); QVERIFY(!socket.isEncrypted()); - QCOMPARE(connectionEncryptedSpy.count(), 0); - QCOMPARE(sslErrorsSpy.count(), 0); - QCOMPARE(peerVerifyErrorSpy.count(), 0); + QCOMPARE(connectionEncryptedSpy.size(), 0); + QCOMPARE(sslErrorsSpy.size(), 0); + QCOMPARE(peerVerifyErrorSpy.size(), 0); // Start handshake. enterLoop(10); // We must get the PSK signal in all cases - QCOMPARE(pskAuthenticationRequiredSpy.count(), 1); + QCOMPARE(pskAuthenticationRequiredSpy.size(), 1); switch (pskTestType) { case PskConnectDoNotHandlePsk: @@ -3865,40 +4220,40 @@ void tst_QSslSocket::simplePskConnect() case PskConnectWrongIdentity: case PskConnectWrongPreSharedKey: // Handshake failure - QCOMPARE(socketErrorsSpy.count(), 1); + QCOMPARE(socketErrorsSpy.size(), 1); QCOMPARE(qvariant_cast<QAbstractSocket::SocketError>(socketErrorsSpy.at(0).at(0)), QAbstractSocket::SslHandshakeFailedError); - QCOMPARE(sslErrorsSpy.count(), 0); - QCOMPARE(peerVerifyErrorSpy.count(), 0); - QCOMPARE(connectionEncryptedSpy.count(), 0); + QCOMPARE(sslErrorsSpy.size(), 0); + QCOMPARE(peerVerifyErrorSpy.size(), 0); + QCOMPARE(connectionEncryptedSpy.size(), 0); QVERIFY(!socket.isEncrypted()); break; case PskConnectRightCredentialsPeerVerifyFailure: // Peer verification failure - QCOMPARE(socketErrorsSpy.count(), 1); + QCOMPARE(socketErrorsSpy.size(), 1); QCOMPARE(qvariant_cast<QAbstractSocket::SocketError>(socketErrorsSpy.at(0).at(0)), QAbstractSocket::SslHandshakeFailedError); - QCOMPARE(sslErrorsSpy.count(), 1); - QCOMPARE(peerVerifyErrorSpy.count(), 1); - QCOMPARE(connectionEncryptedSpy.count(), 0); + QCOMPARE(sslErrorsSpy.size(), 1); + QCOMPARE(peerVerifyErrorSpy.size(), 1); + QCOMPARE(connectionEncryptedSpy.size(), 0); QVERIFY(!socket.isEncrypted()); break; case PskConnectRightCredentialsVerifyPeer: // Peer verification failure, but ignore it and keep connecting - QCOMPARE(socketErrorsSpy.count(), 0); - QCOMPARE(sslErrorsSpy.count(), 1); - QCOMPARE(peerVerifyErrorSpy.count(), 1); - QCOMPARE(connectionEncryptedSpy.count(), 1); + QCOMPARE(socketErrorsSpy.size(), 0); + QCOMPARE(sslErrorsSpy.size(), 1); + QCOMPARE(peerVerifyErrorSpy.size(), 1); + QCOMPARE(connectionEncryptedSpy.size(), 1); QVERIFY(socket.isEncrypted()); QCOMPARE(socket.state(), QAbstractSocket::ConnectedState); break; case PskConnectRightCredentialsDoNotVerifyPeer: // No peer verification => no failure - QCOMPARE(socketErrorsSpy.count(), 0); - QCOMPARE(sslErrorsSpy.count(), 0); - QCOMPARE(peerVerifyErrorSpy.count(), 0); - QCOMPARE(connectionEncryptedSpy.count(), 1); + QCOMPARE(socketErrorsSpy.size(), 0); + QCOMPARE(sslErrorsSpy.size(), 0); + QCOMPARE(peerVerifyErrorSpy.size(), 0); + QCOMPARE(connectionEncryptedSpy.size(), 1); QVERIFY(socket.isEncrypted()); QCOMPARE(socket.state(), QAbstractSocket::ConnectedState); break; @@ -3939,11 +4294,14 @@ void tst_QSslSocket::simplePskConnect() } QCOMPARE(socket.state(), QAbstractSocket::UnconnectedState); - QCOMPARE(disconnectedSpy.count(), 1); + QCOMPARE(disconnectedSpy.size(), 1); } void tst_QSslSocket::ephemeralServerKey_data() { + if (!isTestingOpenSsl) + QSKIP("The active TLS backend does not support ephemeral keys"); + QTest::addColumn<QString>("cipher"); QTest::addColumn<bool>("emptyKey"); @@ -3969,16 +4327,16 @@ void tst_QSslSocket::ephemeralServerKey() client->connectToHostEncrypted(QHostAddress(QHostAddress::LocalHost).toString(), server.serverPort()); spy.wait(); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QVERIFY(server.config.ephemeralServerKey().isNull()); QCOMPARE(client->sslConfiguration().ephemeralServerKey().isNull(), emptyKey); } void tst_QSslSocket::pskServer() { -#if QT_CONFIG(schannel) - QSKIP("Schannel does not have PSK support implemented."); -#endif + if (!isTestingOpenSsl) + QSKIP("The active TLS-backend does not have PSK support implemented."); + QFETCH_GLOBAL(bool, setProxy); if (!QSslSocket::supportsSsl() || setProxy) return; @@ -4032,22 +4390,22 @@ void tst_QSslSocket::pskServer() QCOMPARE(socket.state(), QAbstractSocket::ConnectedState); QCOMPARE(socket.mode(), QSslSocket::UnencryptedMode); QVERIFY(!socket.isEncrypted()); - QCOMPARE(connectedSpy.count(), 1); - QCOMPARE(disconnectedSpy.count(), 0); + QCOMPARE(connectedSpy.size(), 1); + QCOMPARE(disconnectedSpy.size(), 0); // Enter encrypted mode socket.startClientEncryption(); QCOMPARE(socket.mode(), QSslSocket::SslClientMode); QVERIFY(!socket.isEncrypted()); - QCOMPARE(connectionEncryptedSpy.count(), 0); + QCOMPARE(connectionEncryptedSpy.size(), 0); // Start handshake. enterLoop(10); // We must get the PSK signal in all cases - QCOMPARE(pskAuthenticationRequiredSpy.count(), 1); + QCOMPARE(pskAuthenticationRequiredSpy.size(), 1); - QCOMPARE(connectionEncryptedSpy.count(), 1); + QCOMPARE(connectionEncryptedSpy.size(), 1); QVERIFY(socket.isEncrypted()); QCOMPARE(socket.state(), QAbstractSocket::ConnectedState); @@ -4060,13 +4418,13 @@ void tst_QSslSocket::pskServer() enterLoop(10); QCOMPARE(socket.state(), QAbstractSocket::UnconnectedState); - QCOMPARE(disconnectedSpy.count(), 1); + QCOMPARE(disconnectedSpy.size(), 1); } void tst_QSslSocket::signatureAlgorithm_data() { - if (!QSslSocket::supportsSsl()) - QSKIP("Signature algorithms cannot be tested without SSL support"); + if (!isTestingOpenSsl) + QSKIP("Signature algorithms cannot be tested with a non-OpenSSL TLS backend"); if (QSslSocket::sslLibraryVersionNumber() >= 0x10101000L) { // FIXME: investigate if this test makes any sense with TLS 1.3. @@ -4132,7 +4490,7 @@ void tst_QSslSocket::signatureAlgorithm_data() // signature algorithms do not match, but are ignored because the tls version is not v1.2 QTest::newRow("client_ignore_TlsV1_1") << QByteArrayList({rsaSha256}) - << QSsl::TlsV1_1 + << Test::TlsV1_1 << QByteArrayList({rsaSha512}) << QSsl::AnyProtocol << QAbstractSocket::ConnectedState; @@ -4140,11 +4498,11 @@ void tst_QSslSocket::signatureAlgorithm_data() << QByteArrayList({rsaSha256}) << QSsl::AnyProtocol << QByteArrayList({rsaSha512}) - << QSsl::TlsV1_1 + << Test::TlsV1_1 << QAbstractSocket::ConnectedState; QTest::newRow("client_ignore_TlsV1_0") << QByteArrayList({rsaSha256}) - << QSsl::TlsV1_0 + << Test::TlsV1_0 << QByteArrayList({rsaSha512}) << QSsl::AnyProtocol << QAbstractSocket::ConnectedState; @@ -4152,7 +4510,7 @@ void tst_QSslSocket::signatureAlgorithm_data() << QByteArrayList({rsaSha256}) << QSsl::AnyProtocol << QByteArrayList({rsaSha512}) - << QSsl::TlsV1_0 + << Test::TlsV1_0 << QAbstractSocket::ConnectedState; } @@ -4195,8 +4553,9 @@ void tst_QSslSocket::signatureAlgorithm() void tst_QSslSocket::forwardReadChannelFinished() { - if (!QSslSocket::supportsSsl()) - QSKIP("Needs SSL"); + if (!isTestingOpenSsl) + QSKIP("This test requires the OpenSSL backend"); + QFETCH_GLOBAL(bool, setProxy); if (setProxy) QSKIP("This test doesn't work via a proxy"); @@ -4212,17 +4571,17 @@ void tst_QSslSocket::forwardReadChannelFinished() &QTestEventLoop::instance(), &QTestEventLoop::exitLoop); socket.connectToHostEncrypted(QtNetworkSettings::httpServerName(), 443); enterLoop(10); - QVERIFY(readChannelFinishedSpy.count()); + QVERIFY(readChannelFinishedSpy.size()); } -#endif // QT_NO_OPENSSL +#endif // QT_CONFIG(openssl) void tst_QSslSocket::unsupportedProtocols_data() { QTest::addColumn<QSsl::SslProtocol>("unsupportedProtocol"); - QTest::newRow("DtlsV1_0") << QSsl::DtlsV1_0; + QTest::newRow("DtlsV1_0") << Test::DtlsV1_0; QTest::newRow("DtlsV1_2") << QSsl::DtlsV1_2; - QTest::newRow("DtlsV1_0OrLater") << QSsl::DtlsV1_0OrLater; + QTest::newRow("DtlsV1_0OrLater") << Test::DtlsV1_0OrLater; QTest::newRow("DtlsV1_2OrLater") << QSsl::DtlsV1_2OrLater; QTest::newRow("UnknownProtocol") << QSsl::UnknownProtocol; } @@ -4234,7 +4593,7 @@ void tst_QSslSocket::unsupportedProtocols() return; QFETCH(const QSsl::SslProtocol, unsupportedProtocol); - const int timeoutMS = 500; + constexpr auto timeout = 500ms; // Test a client socket. { // 0. connectToHostEncrypted: client-side, non-blocking API, error is discovered @@ -4256,7 +4615,7 @@ void tst_QSslSocket::unsupportedProtocols() QCOMPARE(socket.error(), QAbstractSocket::UnknownSocketError); socket.connectToHost(QHostAddress::LocalHost, server.serverPort()); - QVERIFY(socket.waitForConnected(timeoutMS)); + QVERIFY(socket.waitForConnected(int(timeout.count()))); socket.setProtocol(unsupportedProtocol); socket.startClientEncryption(); @@ -4281,7 +4640,7 @@ void tst_QSslSocket::unsupportedProtocols() QTcpSocket client; client.connectToHost(QHostAddress::LocalHost, server.serverPort()); - loop.enterLoopMSecs(timeoutMS); + loop.enterLoop(timeout); QVERIFY(!loop.timeout()); QVERIFY(server.socket); QCOMPARE(server.socket->error(), QAbstractSocket::SslInvalidUserDataError); @@ -4294,13 +4653,15 @@ void tst_QSslSocket::oldErrorsOnSocketReuse() if (setProxy) return; // not relevant SslServer server; - server.protocol = QSsl::TlsV1_1; + if (!isTestingOpenSsl) + server.protocol = Test::TlsV1_1; server.m_certFile = testDataDir + "certs/fluke.cert"; server.m_keyFile = testDataDir + "certs/fluke.key"; QVERIFY(server.listen(QHostAddress::SpecialAddress::LocalHost)); QSslSocket socket; - socket.setProtocol(QSsl::TlsV1_1); + if (!isTestingOpenSsl) + socket.setProtocol(Test::TlsV1_1); QList<QSslError> errorList; auto connection = connect(&socket, QOverload<const QList<QSslError> &>::of(&QSslSocket::sslErrors), [&socket, &errorList](const QList<QSslError> &errors) { @@ -4335,8 +4696,6 @@ void tst_QSslSocket::oldErrorsOnSocketReuse() } } -#endif // QT_NO_SSL - #if QT_CONFIG(openssl) void tst_QSslSocket::alertMissingCertificate() @@ -4344,6 +4703,8 @@ void tst_QSslSocket::alertMissingCertificate() // In this test we want a server to abort the connection due to the failing // client authentication. The server expected to send an alert before closing // the connection, and the client expected to receive this alert and report it. + if (!isTestingOpenSsl) + QSKIP("This test requires the OpenSSL backend"); QFETCH_GLOBAL(const bool, setProxy); if (setProxy) // Not what we test here, bail out. @@ -4386,10 +4747,19 @@ void tst_QSslSocket::alertMissingCertificate() connect(&clientSocket, &QAbstractSocket::errorOccurred, earlyQuitter); connect(&server, &SslServer::socketError, earlyQuitter); - runner.enterLoopMSecs(1000); + runner.enterLoop(1s); - QVERIFY(serverSpy.count() > 0); - QVERIFY(clientSpy.count() > 0); + if (clientSocket.isEncrypted()) { + // When using TLS 1.3 the client side thinks it is connected very + // quickly, before the server has finished processing. So wait for the + // inevitable disconnect. + QCOMPARE(clientSocket.sessionProtocol(), QSsl::TlsV1_3); + connect(&clientSocket, &QSslSocket::disconnected, &runner, &QTestEventLoop::exitLoop); + runner.enterLoop(10s); + } + + QVERIFY(serverSpy.size() > 0); + QVERIFY(clientSpy.size() > 0); QVERIFY(server.socket && !server.socket->isEncrypted()); QVERIFY(!clientSocket.isEncrypted()); } @@ -4400,6 +4770,9 @@ void tst_QSslSocket::alertInvalidCertificate() // it also will do 'early' checks, meaning the reported and // not ignored _during_ the hanshake, not after. This ensures // OpenSSL sends an alert. + if (!isTestingOpenSsl) + QSKIP("This test requires the OpenSSL backend"); + QFETCH_GLOBAL(const bool, setProxy); if (setProxy) // Not what we test here, bail out. return; @@ -4437,17 +4810,20 @@ void tst_QSslSocket::alertInvalidCertificate() connect(&clientSocket, &QAbstractSocket::errorOccurred, earlyQuitter); connect(&server, &SslServer::socketError, earlyQuitter); - runner.enterLoopMSecs(1000); + runner.enterLoop(1s); - QVERIFY(serverSpy.count() > 0); - QVERIFY(clientSpy.count() > 0); - QVERIFY(interruptedSpy.count() > 0); + QVERIFY(serverSpy.size() > 0); + QVERIFY(clientSpy.size() > 0); + QVERIFY(interruptedSpy.size() > 0); QVERIFY(server.socket && !server.socket->isEncrypted()); QVERIFY(!clientSocket.isEncrypted()); } void tst_QSslSocket::selfSignedCertificates_data() { + if (!isTestingOpenSsl) + QSKIP("The active TLS backend does not detect the required error"); + QTest::addColumn<bool>("clientKnown"); QTest::newRow("Client known") << true; @@ -4562,17 +4938,17 @@ void tst_QSslSocket::selfSignedCertificates() connect(&clientSocket, &QAbstractSocket::errorOccurred, earlyQuitter); connect(&server, &SslServer::socketError, earlyQuitter); - runner.enterLoopMSecs(1000); + runner.enterLoop(1s); if (clientKnown) { - QCOMPARE(serverSpy.count(), 0); - QCOMPARE(clientSpy.count(), 0); + QCOMPARE(serverSpy.size(), 0); + QCOMPARE(clientSpy.size(), 0); QVERIFY(server.socket && server.socket->isEncrypted()); QVERIFY(clientSocket.isEncrypted()); } else { - QVERIFY(serverSpy.count() > 0); + QVERIFY(serverSpy.size() > 0); QEXPECT_FAIL("", "Failing to trigger signal, QTBUG-81661", Continue); - QVERIFY(clientSpy.count() > 0); + QVERIFY(clientSpy.size() > 0); QVERIFY(server.socket && !server.socket->isEncrypted()); QVERIFY(!clientSocket.isEncrypted()); } @@ -4580,6 +4956,9 @@ void tst_QSslSocket::selfSignedCertificates() void tst_QSslSocket::pskHandshake_data() { + if (!isTestingOpenSsl) + QSKIP("The active TLS backend does not support PSK"); + QTest::addColumn<bool>("pskRight"); QTest::newRow("Psk right") << true; @@ -4697,18 +5076,18 @@ void tst_QSslSocket::pskHandshake() connect(&clientSocket, &QAbstractSocket::errorOccurred, earlyQuitter); connect(&server, &SslServer::socketError, earlyQuitter); - runner.enterLoopMSecs(1000); + runner.enterLoop(1s); if (pskRight) { - QCOMPARE(serverSpy.count(), 0); - QCOMPARE(clientSpy.count(), 0); + QCOMPARE(serverSpy.size(), 0); + QCOMPARE(clientSpy.size(), 0); QVERIFY(server.socket && server.socket->isEncrypted()); QVERIFY(clientSocket.isEncrypted()); } else { - QVERIFY(serverSpy.count() > 0); + QVERIFY(serverSpy.size() > 0); QCOMPARE(serverSpy.first().at(0).toInt(), static_cast<int>(QSsl::AlertLevel::Fatal)); QCOMPARE(serverSpy.first().at(1).toInt(), static_cast<int>(QSsl::AlertType::BadRecordMac)); - QVERIFY(clientSpy.count() > 0); + QVERIFY(clientSpy.size() > 0); QCOMPARE(clientSpy.first().at(0).toInt(), static_cast<int>(QSsl::AlertLevel::Fatal)); QCOMPARE(clientSpy.first().at(1).toInt(), static_cast<int>(QSsl::AlertType::BadRecordMac)); QVERIFY(server.socket && !server.socket->isEncrypted()); @@ -4716,7 +5095,9 @@ void tst_QSslSocket::pskHandshake() } } -#endif // openssl +#endif // QT_CONFIG(openssl) +#endif // QT_CONFIG(ssl) + QTEST_MAIN(tst_QSslSocket) |