summaryrefslogtreecommitdiffstats
path: root/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp')
-rw-r--r--tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp664
1 files changed, 584 insertions, 80 deletions
diff --git a/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp b/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp
index a355cfeb17..f4d3555531 100644
--- a/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp
+++ b/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp
@@ -1,7 +1,8 @@
/****************************************************************************
**
-** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
-** Contact: http://www.qt-project.org/legal
+** Copyright (C) 2015 The Qt Company Ltd.
+** Copyright (C) 2014 Governikus GmbH & Co. KG.
+** Contact: http://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
**
@@ -10,9 +11,9 @@
** 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 Digia. For licensing terms and
-** conditions see http://qt.digia.com/licensing. For further information
-** use the contact form at http://qt.digia.com/contact-us.
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
@@ -23,15 +24,15 @@
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
-** In addition, as a special exception, Digia gives you certain additional
-** rights. These rights are described in the Digia Qt LGPL Exception
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** $QT_END_LICENSE$
**
****************************************************************************/
-
+#include <QtCore/qglobal.h>
#include <QtCore/qthread.h>
#include <QtNetwork/qhostaddress.h>
#include <QtNetwork/qhostinfo.h>
@@ -41,27 +42,42 @@
#include <QtNetwork/qsslkey.h>
#include <QtNetwork/qsslsocket.h>
#include <QtNetwork/qtcpserver.h>
+#include <QtNetwork/qsslpresharedkeyauthenticator.h>
#include <QtTest/QtTest>
#include <QNetworkProxy>
#include <QAuthenticator>
#include "private/qhostinfo_p.h"
+#include "private/qiodevice_p.h" // for QIODEVICE_BUFFERSIZE
+
+#include "../../../network-settings.h"
+
#ifndef QT_NO_SSL
+#ifndef QT_NO_OPENSSL
#include "private/qsslsocket_openssl_p.h"
#include "private/qsslsocket_openssl_symbols_p.h"
-#include "private/qsslconfiguration_p.h"
#endif
+#include "private/qsslsocket_p.h"
+#include "private/qsslconfiguration_p.h"
-#include "../../../network-settings.h"
-
-#ifndef QT_NO_SSL
Q_DECLARE_METATYPE(QSslSocket::SslMode)
typedef QList<QSslError::SslError> SslErrorList;
Q_DECLARE_METATYPE(SslErrorList)
Q_DECLARE_METATYPE(QSslError)
+Q_DECLARE_METATYPE(QSslKey)
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 certificate for certificates.
+#ifndef QT_NO_OPENSSL
+#define FLUKE_CERTIFICATE_ERROR QSslError::SelfSignedCertificate
+#else
+#define FLUKE_CERTIFICATE_ERROR QSslError::CertificateUntrusted
#endif
+#endif // QT_NO_SSL
#if defined Q_OS_HPUX && defined Q_CC_GNU
// This error is delivered every time we try to use the fluke CA
@@ -69,9 +85,14 @@ Q_DECLARE_METATYPE(QSsl::SslProtocol)
#define QSSLSOCKET_CERTUNTRUSTED_WORKAROUND
#endif
-#ifndef QT_NO_SSL
-typedef QSharedPointer<QSslSocket> QSslSocketPtr;
-#endif
+// 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.
+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");
class tst_QSslSocket : public QObject
{
@@ -96,6 +117,19 @@ public:
#ifndef QT_NO_SSL
QSslSocketPtr newSocket();
+
+#ifndef QT_NO_OPENSSL
+ enum PskConnectTestType {
+ PskConnectDoNotHandlePsk,
+ PskConnectEmptyCredentials,
+ PskConnectWrongCredentials,
+ PskConnectWrongIdentity,
+ PskConnectWrongPreSharedKey,
+ PskConnectRightCredentialsPeerVerifyFailure,
+ PskConnectRightCredentialsVerifyPeer,
+ PskConnectRightCredentialsDoNotVerifyPeer,
+ };
+#endif
#endif
public slots:
@@ -168,7 +202,6 @@ private slots:
void waitForMinusOne();
void verifyMode();
void verifyDepth();
- void peerVerifyError();
void disconnectFromHostWhenConnecting();
void disconnectFromHostWhenConnected();
void resetProxy();
@@ -190,8 +223,15 @@ private slots:
void qtbug18498_peek2();
void dhServer();
void ecdhServer();
+ void verifyClientCertificate_data();
+ void verifyClientCertificate();
void setEmptyDefaultConfiguration(); // this test should be last
+#ifndef QT_NO_OPENSSL
+ void simplePskConnect_data();
+ void simplePskConnect();
+#endif
+
static void exitLoop()
{
// Safe exit - if we aren't in an event loop, don't
@@ -224,6 +264,12 @@ private:
static int loopLevel;
};
+#ifndef QT_NO_SSL
+#ifndef QT_NO_OPENSSL
+Q_DECLARE_METATYPE(tst_QSslSocket::PskConnectTestType)
+#endif
+#endif
+
int tst_QSslSocket::loopLevel = 0;
tst_QSslSocket::tst_QSslSocket()
@@ -233,6 +279,11 @@ tst_QSslSocket::tst_QSslSocket()
qRegisterMetaType<QSslError>("QSslError");
qRegisterMetaType<QAbstractSocket::SocketState>("QAbstractSocket::SocketState");
qRegisterMetaType<QAbstractSocket::SocketError>("QAbstractSocket::SocketError");
+
+#ifndef QT_NO_OPENSSL
+ qRegisterMetaType<QSslPreSharedKeyAuthenticator *>();
+ qRegisterMetaType<tst_QSslSocket::PskConnectTestType>();
+#endif
#endif
}
@@ -546,37 +597,53 @@ void tst_QSslSocket::sslErrors_data()
{
QTest::addColumn<QString>("host");
QTest::addColumn<int>("port");
- QTest::addColumn<SslErrorList>("expected");
- QTest::newRow(qPrintable(QtNetworkSettings::serverLocalName()))
- << QtNetworkSettings::serverLocalName()
- << 993
- << (SslErrorList() << QSslError::HostNameMismatch
- << QSslError::SelfSignedCertificate);
+ QString name = QtNetworkSettings::serverLocalName();
+ QTest::newRow(qPrintable(name)) << name << 993;
+
+ name = QHostInfo::fromName(QtNetworkSettings::serverName()).addresses().first().toString();
+ QTest::newRow(qPrintable(name)) << name << 443;
}
void tst_QSslSocket::sslErrors()
{
QFETCH(QString, host);
QFETCH(int, port);
- QFETCH(SslErrorList, expected);
QSslSocketPtr socket = newSocket();
+ QSignalSpy sslErrorsSpy(socket.data(), SIGNAL(sslErrors(QList<QSslError>)));
+ QSignalSpy peerVerifyErrorSpy(socket.data(), SIGNAL(peerVerifyError(QSslError)));
+
socket->connectToHostEncrypted(host, port);
if (!socket->waitForConnected())
- QEXPECT_FAIL("imap.trolltech.com", "server not open to internet", Continue);
- socket->waitForEncrypted(5000);
+ QSKIP("Skipping flaky test - See QTBUG-29941");
+ socket->waitForEncrypted(10000);
- SslErrorList output;
- foreach (QSslError error, socket->sslErrors()) {
- output << error.error();
- }
+ // check the SSL errors contain HostNameMismatch and an error due to
+ // the certificate being self-signed
+ SslErrorList sslErrors;
+ foreach (const QSslError &err, socket->sslErrors())
+ sslErrors << err.error();
+ qSort(sslErrors);
+ QVERIFY(sslErrors.contains(QSslError::HostNameMismatch));
+ QVERIFY(sslErrors.contains(FLUKE_CERTIFICATE_ERROR));
-#ifdef QSSLSOCKET_CERTUNTRUSTED_WORKAROUND
- if (output.count() && output.last() == QSslError::CertificateUntrusted)
- output.takeLast();
-#endif
- QCOMPARE(output, expected);
+ // check the same errors were emitted by sslErrors
+ QVERIFY(!sslErrorsSpy.isEmpty());
+ SslErrorList emittedErrors;
+ foreach (const QSslError &err, qvariant_cast<QList<QSslError> >(sslErrorsSpy.first().first()))
+ emittedErrors << err.error();
+ qSort(emittedErrors);
+ QCOMPARE(sslErrors, emittedErrors);
+
+ // check the same errors were emitted by peerVerifyError
+ QVERIFY(!peerVerifyErrorSpy.isEmpty());
+ SslErrorList peerErrors;
+ const QList<QVariantList> &peerVerifyList = peerVerifyErrorSpy;
+ foreach (const QVariantList &args, peerVerifyList)
+ peerErrors << qvariant_cast<QSslError>(args.first()).error();
+ qSort(peerErrors);
+ QCOMPARE(sslErrors, peerErrors);
}
void tst_QSslSocket::addCaCertificate()
@@ -929,7 +996,7 @@ void tst_QSslSocket::protocol()
socket->abort();
}
#endif
-#ifndef OPENSSL_NO_SSL2
+#if !defined(OPENSSL_NO_SSL2) && !defined(QT_SECURETRANSPORT)
{
// qt-test-server allows SSLV2.
socket->setProtocol(QSsl::SslV2);
@@ -996,12 +1063,17 @@ public:
const QString &certFile = SRCDIR "certs/fluke.cert",
const QString &interFile = QString())
: socket(0),
+ ignoreSslErrors(true),
+ peerVerifyMode(QSslSocket::AutoVerifyPeer),
protocol(QSsl::TlsV1_0),
m_keyFile(keyFile),
m_certFile(certFile),
m_interFile(interFile)
{ }
QSslSocket *socket;
+ QString addCaCertificates;
+ bool ignoreSslErrors;
+ QSslSocket::PeerVerifyMode peerVerifyMode;
QSsl::SslProtocol protocol;
QString m_keyFile;
QString m_certFile;
@@ -1012,8 +1084,10 @@ protected:
void incomingConnection(qintptr socketDescriptor)
{
socket = new QSslSocket(this);
+ socket->setPeerVerifyMode(peerVerifyMode);
socket->setProtocol(protocol);
- connect(socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(ignoreErrorSlot()));
+ if (ignoreSslErrors)
+ connect(socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(ignoreErrorSlot()));
QFile file(m_keyFile);
QVERIFY(file.open(QIODevice::ReadOnly));
@@ -1021,6 +1095,14 @@ protected:
QVERIFY(!key.isNull());
socket->setPrivateKey(key);
+ // Add CA certificates to verify client certificate
+ if (!addCaCertificates.isEmpty()) {
+ QList<QSslCertificate> caCert = QSslCertificate::fromPath(addCaCertificates);
+ QVERIFY(!caCert.isEmpty());
+ QVERIFY(!caCert.first().isNull());
+ socket->addCaCertificates(caCert);
+ }
+
// If we have a cert issued directly from the CA
if (m_interFile.isEmpty()) {
QList<QSslCertificate> localCert = QSslCertificate::fromPath(m_certFile);
@@ -1066,7 +1148,7 @@ void tst_QSslSocket::protocolServerSide_data()
QTest::addColumn<QSsl::SslProtocol>("clientProtocol");
QTest::addColumn<bool>("works");
-#ifndef OPENSSL_NO_SSL2
+#if !defined(OPENSSL_NO_SSL2) && !defined(QT_SECURETRANSPORT)
QTest::newRow("ssl2-ssl2") << QSsl::SslV2 << QSsl::SslV2 << false; // no idea why it does not work, but we don't care about SSL 2
#endif
QTest::newRow("ssl3-ssl3") << QSsl::SslV3 << QSsl::SslV3 << true;
@@ -1075,7 +1157,7 @@ void tst_QSslSocket::protocolServerSide_data()
QTest::newRow("any-any") << QSsl::AnyProtocol << QSsl::AnyProtocol << true;
QTest::newRow("secure-secure") << QSsl::SecureProtocols << QSsl::SecureProtocols << true;
-#ifndef OPENSSL_NO_SSL2
+#if !defined(OPENSSL_NO_SSL2) && !defined(QT_SECURETRANSPORT)
QTest::newRow("ssl2-ssl3") << QSsl::SslV2 << QSsl::SslV3 << false;
QTest::newRow("ssl2-tls1.0") << QSsl::SslV2 << QSsl::TlsV1_0 << false;
QTest::newRow("ssl2-tls1ssl3") << QSsl::SslV2 << QSsl::TlsV1SslV3 << false;
@@ -1083,33 +1165,33 @@ void tst_QSslSocket::protocolServerSide_data()
QTest::newRow("ssl2-any") << QSsl::SslV2 << QSsl::AnyProtocol << false; // no idea why it does not work, but we don't care about SSL 2
#endif
-#ifndef OPENSSL_NO_SSL2
+#if !defined(OPENSSL_NO_SSL2) && !defined(QT_SECURETRANSPORT)
QTest::newRow("ssl3-ssl2") << QSsl::SslV3 << QSsl::SslV2 << false;
#endif
QTest::newRow("ssl3-tls1.0") << QSsl::SslV3 << QSsl::TlsV1_0 << false;
QTest::newRow("ssl3-tls1ssl3") << QSsl::SslV3 << QSsl::TlsV1SslV3 << true;
QTest::newRow("ssl3-secure") << QSsl::SslV3 << QSsl::SecureProtocols << false;
-#ifndef OPENSSL_NO_SSL2
+#if !defined(OPENSSL_NO_SSL2) && !defined(QT_SECURETRANSPORT)
QTest::newRow("ssl3-any") << QSsl::SslV3 << QSsl::AnyProtocol << false; // we won't set a SNI header here because we connect to a
// numerical IP, so OpenSSL will send a SSL 2 handshake
#else
QTest::newRow("ssl3-any") << QSsl::SslV3 << QSsl::AnyProtocol << true;
#endif
-#ifndef OPENSSL_NO_SSL2
+#if !defined(OPENSSL_NO_SSL2) && !defined(QT_SECURETRANSPORT)
QTest::newRow("tls1.0-ssl2") << QSsl::TlsV1_0 << QSsl::SslV2 << false;
#endif
QTest::newRow("tls1.0-ssl3") << QSsl::TlsV1_0 << QSsl::SslV3 << false;
QTest::newRow("tls1-tls1ssl3") << QSsl::TlsV1_0 << QSsl::TlsV1SslV3 << true;
QTest::newRow("tls1.0-secure") << QSsl::TlsV1_0 << QSsl::SecureProtocols << true;
-#ifndef OPENSSL_NO_SSL2
+#if !defined(OPENSSL_NO_SSL2) && !defined(QT_SECURETRANSPORT)
QTest::newRow("tls1.0-any") << QSsl::TlsV1_0 << QSsl::AnyProtocol << false; // we won't set a SNI header here because we connect to a
// numerical IP, so OpenSSL will send a SSL 2 handshake
#else
QTest::newRow("tls1.0-any") << QSsl::TlsV1_0 << QSsl::AnyProtocol << true;
#endif
-#ifndef OPENSSL_NO_SSL2
+#if !defined(OPENSSL_NO_SSL2) && !defined(QT_SECURETRANSPORT)
QTest::newRow("tls1ssl3-ssl2") << QSsl::TlsV1SslV3 << QSsl::SslV2 << false;
#endif
QTest::newRow("tls1ssl3-ssl3") << QSsl::TlsV1SslV3 << QSsl::SslV3 << true;
@@ -1117,7 +1199,7 @@ void tst_QSslSocket::protocolServerSide_data()
QTest::newRow("tls1ssl3-secure") << QSsl::TlsV1SslV3 << QSsl::SecureProtocols << true;
QTest::newRow("tls1ssl3-any") << QSsl::TlsV1SslV3 << QSsl::AnyProtocol << true;
-#ifndef OPENSSL_NO_SSL2
+#if !defined(OPENSSL_NO_SSL2) && !defined(QT_SECURETRANSPORT)
QTest::newRow("secure-ssl2") << QSsl::SecureProtocols << QSsl::SslV2 << false;
#endif
QTest::newRow("secure-ssl3") << QSsl::SecureProtocols << QSsl::SslV3 << false;
@@ -1125,7 +1207,7 @@ void tst_QSslSocket::protocolServerSide_data()
QTest::newRow("secure-tls1ssl3") << QSsl::SecureProtocols << QSsl::TlsV1SslV3 << true;
QTest::newRow("secure-any") << QSsl::SecureProtocols << QSsl::AnyProtocol << true;
-#ifndef OPENSSL_NO_SSL2
+#if !defined(OPENSSL_NO_SSL2) && !defined(QT_SECURETRANSPORT)
QTest::newRow("any-ssl2") << QSsl::AnyProtocol << QSsl::SslV2 << false; // no idea why it does not work, but we don't care about SSL 2
#endif
QTest::newRow("any-ssl3") << QSsl::AnyProtocol << QSsl::SslV3 << true;
@@ -1950,7 +2032,7 @@ void tst_QSslSocket::verifyMode()
QSKIP("Skipping flaky test - See QTBUG-29941");
QList<QSslError> expectedErrors = QList<QSslError>()
- << QSslError(QSslError::SelfSignedCertificate, socket.peerCertificate());
+ << QSslError(FLUKE_CERTIFICATE_ERROR, socket.peerCertificate());
QCOMPARE(socket.sslErrors(), expectedErrors);
socket.abort();
@@ -1981,34 +2063,6 @@ void tst_QSslSocket::verifyDepth()
QCOMPARE(socket.peerVerifyDepth(), 1);
}
-void tst_QSslSocket::peerVerifyError()
-{
- QSslSocketPtr socket = newSocket();
- QSignalSpy sslErrorsSpy(socket.data(), SIGNAL(sslErrors(QList<QSslError>)));
- QSignalSpy peerVerifyErrorSpy(socket.data(), SIGNAL(peerVerifyError(QSslError)));
-
- socket->connectToHostEncrypted(QHostInfo::fromName(QtNetworkSettings::serverName()).addresses().first().toString(), 443);
- if (socket->waitForEncrypted(10000))
- QSKIP("Skipping flaky test - See QTBUG-29941");
-
- // check HostNameMismatch was emitted by peerVerifyError
- QVERIFY(!peerVerifyErrorSpy.isEmpty());
- SslErrorList peerErrors;
- const QList<QVariantList> &peerVerifyList = peerVerifyErrorSpy;
- foreach (const QVariantList &args, peerVerifyList)
- peerErrors << qvariant_cast<QSslError>(args.first()).error();
- QVERIFY(peerErrors.contains(QSslError::HostNameMismatch));
-
- // check HostNameMismatch was emitted by sslErrors
- QVERIFY(!sslErrorsSpy.isEmpty());
- SslErrorList sslErrors;
- foreach (const QSslError &err, qvariant_cast<QList<QSslError> >(sslErrorsSpy.first().first()))
- sslErrors << err.error();
- QVERIFY(peerErrors.contains(QSslError::HostNameMismatch));
-
- QCOMPARE(sslErrors.size(), peerErrors.size());
-}
-
void tst_QSslSocket::disconnectFromHostWhenConnecting()
{
QSslSocketPtr socket = newSocket();
@@ -2105,8 +2159,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(QLatin1String(SRCDIR "certs/qt-test-server-cacert.pem"));
- QSslError rightError(QSslError::SelfSignedCertificate, certs.at(0));
- QSslError wrongError(QSslError::SelfSignedCertificate);
+ QSslError rightError(FLUKE_CERTIFICATE_ERROR, certs.at(0));
+ QSslError wrongError(FLUKE_CERTIFICATE_ERROR);
QTest::newRow("SSL-failure-empty-list") << expectedSslErrors << 1;
@@ -2368,8 +2422,8 @@ void tst_QSslSocket::resume_data()
QTest::newRow("ignoreAllErrors") << true << QList<QSslError>() << true;
QList<QSslCertificate> certs = QSslCertificate::fromPath(QLatin1String(SRCDIR "certs/qt-test-server-cacert.pem"));
- QSslError rightError(QSslError::SelfSignedCertificate, certs.at(0));
- QSslError wrongError(QSslError::SelfSignedCertificate);
+ QSslError rightError(FLUKE_CERTIFICATE_ERROR, certs.at(0));
+ QSslError wrongError(FLUKE_CERTIFICATE_ERROR);
errorsList.append(wrongError);
QTest::newRow("ignoreSpecificErrors-Wrong") << true << errorsList << false;
errorsList.clear();
@@ -2747,6 +2801,140 @@ void tst_QSslSocket::ecdhServer()
QVERIFY(client->state() == QAbstractSocket::ConnectedState);
}
+void tst_QSslSocket::verifyClientCertificate_data()
+{
+ QTest::addColumn<QSslSocket::PeerVerifyMode>("peerVerifyMode");
+ QTest::addColumn<QList<QSslCertificate> >("clientCerts");
+ QTest::addColumn<QSslKey>("clientKey");
+ QTest::addColumn<bool>("works");
+
+ // no certificate
+ QList<QSslCertificate> noCerts;
+ QSslKey noKey;
+
+ QTest::newRow("NoCert:AutoVerifyPeer") << QSslSocket::AutoVerifyPeer << noCerts << noKey << true;
+ QTest::newRow("NoCert:QueryPeer") << QSslSocket::QueryPeer << noCerts << noKey << true;
+ QTest::newRow("NoCert:VerifyNone") << QSslSocket::VerifyNone << noCerts << noKey << true;
+ QTest::newRow("NoCert:VerifyPeer") << QSslSocket::VerifyPeer << noCerts << noKey << false;
+
+ // self-signed certificate
+ QList<QSslCertificate> flukeCerts = QSslCertificate::fromPath(SRCDIR "certs/fluke.cert");
+ QCOMPARE(flukeCerts.size(), 1);
+
+ QFile flukeFile(SRCDIR "certs/fluke.key");
+ QVERIFY(flukeFile.open(QIODevice::ReadOnly));
+ QSslKey flukeKey(flukeFile.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
+ QVERIFY(!flukeKey.isNull());
+
+ QTest::newRow("SelfSignedCert:AutoVerifyPeer") << QSslSocket::AutoVerifyPeer << flukeCerts << flukeKey << true;
+ QTest::newRow("SelfSignedCert:QueryPeer") << QSslSocket::QueryPeer << flukeCerts << flukeKey << true;
+ QTest::newRow("SelfSignedCert:VerifyNone") << QSslSocket::VerifyNone << flukeCerts << flukeKey << true;
+ QTest::newRow("SelfSignedCert:VerifyPeer") << QSslSocket::VerifyPeer << flukeCerts << flukeKey << false;
+
+ // valid certificate, but wrong usage (server certificate)
+ QList<QSslCertificate> serverCerts = QSslCertificate::fromPath(SRCDIR "certs/bogus-server.crt");
+ QCOMPARE(serverCerts.size(), 1);
+
+ QFile serverFile(SRCDIR "certs/bogus-server.key");
+ QVERIFY(serverFile.open(QIODevice::ReadOnly));
+ QSslKey serverKey(serverFile.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
+ QVERIFY(!serverKey.isNull());
+
+ QTest::newRow("ValidServerCert:AutoVerifyPeer") << QSslSocket::AutoVerifyPeer << serverCerts << serverKey << true;
+ QTest::newRow("ValidServerCert:QueryPeer") << QSslSocket::QueryPeer << serverCerts << serverKey << true;
+ QTest::newRow("ValidServerCert:VerifyNone") << QSslSocket::VerifyNone << serverCerts << serverKey << true;
+ QTest::newRow("ValidServerCert:VerifyPeer") << QSslSocket::VerifyPeer << serverCerts << serverKey << false;
+
+ // valid certificate, correct usage (client certificate)
+ QList<QSslCertificate> validCerts = QSslCertificate::fromPath(SRCDIR "certs/bogus-client.crt");
+ QCOMPARE(validCerts.size(), 1);
+
+ QFile validFile(SRCDIR "certs/bogus-client.key");
+ QVERIFY(validFile.open(QIODevice::ReadOnly));
+ QSslKey validKey(validFile.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
+ QVERIFY(!validKey.isNull());
+
+ QTest::newRow("ValidClientCert:AutoVerifyPeer") << QSslSocket::AutoVerifyPeer << validCerts << validKey << true;
+ QTest::newRow("ValidClientCert:QueryPeer") << QSslSocket::QueryPeer << validCerts << validKey << true;
+ QTest::newRow("ValidClientCert:VerifyNone") << QSslSocket::VerifyNone << validCerts << validKey << true;
+ QTest::newRow("ValidClientCert:VerifyPeer") << QSslSocket::VerifyPeer << validCerts << validKey << true;
+
+ // valid certificate, correct usage (client certificate), with chain
+ validCerts += QSslCertificate::fromPath(SRCDIR "certs/bogus-ca.crt");
+ QCOMPARE(validCerts.size(), 2);
+
+ QTest::newRow("ValidClientCert:AutoVerifyPeer") << QSslSocket::AutoVerifyPeer << validCerts << validKey << true;
+ QTest::newRow("ValidClientCert:QueryPeer") << QSslSocket::QueryPeer << validCerts << validKey << true;
+ QTest::newRow("ValidClientCert:VerifyNone") << QSslSocket::VerifyNone << validCerts << validKey << true;
+ QTest::newRow("ValidClientCert:VerifyPeer") << QSslSocket::VerifyPeer << validCerts << validKey << true;
+}
+
+void tst_QSslSocket::verifyClientCertificate()
+{
+#ifdef QT_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
+ if (!QSslSocket::supportsSsl()) {
+ qWarning("SSL not supported, skipping test");
+ return;
+ }
+
+ QFETCH_GLOBAL(bool, setProxy);
+ if (setProxy)
+ return;
+
+ QFETCH(QSslSocket::PeerVerifyMode, peerVerifyMode);
+ SslServer server;
+ server.addCaCertificates = QLatin1String(SRCDIR "certs/bogus-ca.crt");
+ server.ignoreSslErrors = false;
+ server.peerVerifyMode = peerVerifyMode;
+ QVERIFY(server.listen());
+
+ QEventLoop loop;
+ QTimer::singleShot(5000, &loop, SLOT(quit()));
+
+ QFETCH(QList<QSslCertificate>, clientCerts);
+ QFETCH(QSslKey, clientKey);
+ QSslSocketPtr client(new QSslSocket);
+ client->setLocalCertificateChain(clientCerts);
+ client->setPrivateKey(clientKey);
+ socket = client.data();
+
+ connect(socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(ignoreErrorSlot()));
+ connect(socket, SIGNAL(disconnected()), &loop, SLOT(quit()));
+ connect(socket, SIGNAL(encrypted()), &loop, SLOT(quit()));
+
+ client->connectToHostEncrypted(QHostAddress(QHostAddress::LocalHost).toString(), server.serverPort());
+
+ loop.exec();
+
+ QFETCH(bool, works);
+ QAbstractSocket::SocketState expectedState = (works) ? QAbstractSocket::ConnectedState : QAbstractSocket::UnconnectedState;
+
+ // check server socket
+ QVERIFY(server.socket);
+
+ QCOMPARE(int(server.socket->state()), int(expectedState));
+ QCOMPARE(server.socket->isEncrypted(), works);
+
+ if (peerVerifyMode == QSslSocket::VerifyNone || clientCerts.isEmpty()) {
+ QVERIFY(server.socket->peerCertificate().isNull());
+ QVERIFY(server.socket->peerCertificateChain().isEmpty());
+ } else {
+ QCOMPARE(server.socket->peerCertificate(), clientCerts.first());
+ QCOMPARE(server.socket->peerCertificateChain(), clientCerts);
+ }
+
+ // check client socket
+ QCOMPARE(int(client->state()), int(expectedState));
+ QCOMPARE(client->isEncrypted(), works);
+}
+
void tst_QSslSocket::setEmptyDefaultConfiguration() // this test should be last, as it has some side effects
{
// used to produce a crash in QSslConfigurationPrivate::deepCopyDefaultConfiguration, QTBUG-13265
@@ -2757,14 +2945,330 @@ void tst_QSslSocket::setEmptyDefaultConfiguration() // this test should be last,
QSslConfiguration emptyConf;
QSslConfiguration::setDefaultConfiguration(emptyConf);
- QSslSocketPtr socket = newSocket();
- connect(socket.data(), SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(ignoreErrorSlot()));
+ QSslSocketPtr client = newSocket();
+ socket = client.data();
+
+ connect(socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(ignoreErrorSlot()));
socket->connectToHostEncrypted(QtNetworkSettings::serverName(), 443);
QFETCH_GLOBAL(bool, setProxy);
if (setProxy && socket->waitForEncrypted(4000))
QSKIP("Skipping flaky test - See QTBUG-29941");
}
+#ifndef QT_NO_OPENSSL
+class PskProvider : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit PskProvider(QObject *parent = 0)
+ : QObject(parent)
+ {
+ }
+
+ void setIdentity(const QByteArray &identity)
+ {
+ m_identity = identity;
+ }
+
+ void setPreSharedKey(const QByteArray &psk)
+ {
+ m_psk = psk;
+ }
+
+public slots:
+ void providePsk(QSslPreSharedKeyAuthenticator *authenticator)
+ {
+ QVERIFY(authenticator);
+ QCOMPARE(authenticator->identityHint(), PSK_SERVER_IDENTITY_HINT);
+ QVERIFY(authenticator->maximumIdentityLength() > 0);
+ QVERIFY(authenticator->maximumPreSharedKeyLength() > 0);
+
+ if (!m_identity.isEmpty()) {
+ authenticator->setIdentity(m_identity);
+ QCOMPARE(authenticator->identity(), m_identity);
+ }
+
+ if (!m_psk.isEmpty()) {
+ authenticator->setPreSharedKey(m_psk);
+ QCOMPARE(authenticator->preSharedKey(), m_psk);
+ }
+ }
+
+private:
+ QByteArray m_identity;
+ QByteArray m_psk;
+};
+
+void tst_QSslSocket::simplePskConnect_data()
+{
+ QTest::addColumn<PskConnectTestType>("pskTestType");
+ QTest::newRow("PskConnectDoNotHandlePsk") << PskConnectDoNotHandlePsk;
+ QTest::newRow("PskConnectEmptyCredentials") << PskConnectEmptyCredentials;
+ QTest::newRow("PskConnectWrongCredentials") << PskConnectWrongCredentials;
+ QTest::newRow("PskConnectWrongIdentity") << PskConnectWrongIdentity;
+ QTest::newRow("PskConnectWrongPreSharedKey") << PskConnectWrongPreSharedKey;
+ QTest::newRow("PskConnectRightCredentialsPeerVerifyFailure") << PskConnectRightCredentialsPeerVerifyFailure;
+ QTest::newRow("PskConnectRightCredentialsVerifyPeer") << PskConnectRightCredentialsVerifyPeer;
+ QTest::newRow("PskConnectRightCredentialsDoNotVerifyPeer") << PskConnectRightCredentialsDoNotVerifyPeer;
+}
+
+void tst_QSslSocket::simplePskConnect()
+{
+ QFETCH(PskConnectTestType, pskTestType);
+ QSKIP("This test requires change 1f8cab2c3bcd91335684c95afa95ae71e00a94e4 on the network test server, QTQAINFRA-917");
+
+ if (!QSslSocket::supportsSsl())
+ QSKIP("No SSL support");
+
+ bool pskCipherFound = false;
+ const QList<QSslCipher> supportedCiphers = QSslSocket::supportedCiphers();
+ foreach (const QSslCipher &cipher, supportedCiphers) {
+ if (cipher.name() == PSK_CIPHER_WITHOUT_AUTH) {
+ pskCipherFound = true;
+ break;
+ }
+ }
+
+ if (!pskCipherFound)
+ QSKIP("SSL implementation does not support the necessary PSK cipher(s)");
+
+ QFETCH_GLOBAL(bool, setProxy);
+ if (setProxy)
+ QSKIP("This test must not be going through a proxy");
+
+ QSslSocket socket;
+ this->socket = &socket;
+
+ QSignalSpy connectedSpy(&socket, SIGNAL(connected()));
+ QVERIFY(connectedSpy.isValid());
+
+ QSignalSpy hostFoundSpy(&socket, SIGNAL(hostFound()));
+ QVERIFY(hostFoundSpy.isValid());
+
+ QSignalSpy disconnectedSpy(&socket, SIGNAL(disconnected()));
+ QVERIFY(disconnectedSpy.isValid());
+
+ QSignalSpy connectionEncryptedSpy(&socket, SIGNAL(encrypted()));
+ QVERIFY(connectionEncryptedSpy.isValid());
+
+ QSignalSpy sslErrorsSpy(&socket, SIGNAL(sslErrors(QList<QSslError>)));
+ QVERIFY(sslErrorsSpy.isValid());
+
+ QSignalSpy socketErrorsSpy(&socket, SIGNAL(error(QAbstractSocket::SocketError)));
+ QVERIFY(socketErrorsSpy.isValid());
+
+ QSignalSpy peerVerifyErrorSpy(&socket, SIGNAL(peerVerifyError(QSslError)));
+ QVERIFY(peerVerifyErrorSpy.isValid());
+
+ QSignalSpy pskAuthenticationRequiredSpy(&socket, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)));
+ QVERIFY(pskAuthenticationRequiredSpy.isValid());
+
+ connect(&socket, SIGNAL(connected()), this, SLOT(exitLoop()));
+ connect(&socket, SIGNAL(disconnected()), this, SLOT(exitLoop()));
+ connect(&socket, SIGNAL(modeChanged(QSslSocket::SslMode)), this, SLOT(exitLoop()));
+ connect(&socket, SIGNAL(encrypted()), this, SLOT(exitLoop()));
+ connect(&socket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(exitLoop()));
+ connect(&socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(exitLoop()));
+ connect(&socket, SIGNAL(peerVerifyError(QSslError)), this, SLOT(exitLoop()));
+ connect(&socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(exitLoop()));
+
+ // force a PSK cipher w/o auth
+ socket.setCiphers(PSK_CIPHER_WITHOUT_AUTH);
+
+ PskProvider provider;
+
+ switch (pskTestType) {
+ case PskConnectDoNotHandlePsk:
+ // don't connect to the provider
+ break;
+
+ case PskConnectEmptyCredentials:
+ // connect to the psk provider, but don't actually provide any PSK nor identity
+ connect(&socket, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)), &provider, SLOT(providePsk(QSslPreSharedKeyAuthenticator*)));
+ break;
+
+ 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));
+ 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.setPreSharedKey(PSK_CLIENT_PRESHAREDKEY);
+ connect(&socket, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)), &provider, SLOT(providePsk(QSslPreSharedKeyAuthenticator*)));
+ break;
+
+ case PskConnectWrongPreSharedKey:
+ // right identity, wrong PSK
+ provider.setIdentity(PSK_CLIENT_IDENTITY);
+ provider.setPreSharedKey(PSK_CLIENT_PRESHAREDKEY.left(PSK_CLIENT_PRESHAREDKEY.length() - 1));
+ connect(&socket, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)), &provider, SLOT(providePsk(QSslPreSharedKeyAuthenticator*)));
+ break;
+
+ case PskConnectRightCredentialsPeerVerifyFailure:
+ // right identity, right PSK, but since we can't verify the other peer, we'll fail
+ provider.setIdentity(PSK_CLIENT_IDENTITY);
+ provider.setPreSharedKey(PSK_CLIENT_PRESHAREDKEY);
+ connect(&socket, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)), &provider, SLOT(providePsk(QSslPreSharedKeyAuthenticator*)));
+ break;
+
+ case PskConnectRightCredentialsVerifyPeer:
+ // right identity, right PSK, verify the peer (but ignore the failure) and establish the connection
+ provider.setIdentity(PSK_CLIENT_IDENTITY);
+ provider.setPreSharedKey(PSK_CLIENT_PRESHAREDKEY);
+ connect(&socket, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)), &provider, SLOT(providePsk(QSslPreSharedKeyAuthenticator*)));
+ connect(&socket, SIGNAL(peerVerifyError(QSslError)), this, SLOT(ignoreErrorSlot()));
+ break;
+
+ case PskConnectRightCredentialsDoNotVerifyPeer:
+ // right identity, right PSK, do not verify the peer and establish the connection
+ provider.setIdentity(PSK_CLIENT_IDENTITY);
+ provider.setPreSharedKey(PSK_CLIENT_PRESHAREDKEY);
+ connect(&socket, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)), &provider, SLOT(providePsk(QSslPreSharedKeyAuthenticator*)));
+ socket.setPeerVerifyMode(QSslSocket::VerifyNone);
+ break;
+ }
+
+ // check the peer verification mode
+ switch (pskTestType) {
+ case PskConnectDoNotHandlePsk:
+ case PskConnectEmptyCredentials:
+ case PskConnectWrongCredentials:
+ case PskConnectWrongIdentity:
+ case PskConnectWrongPreSharedKey:
+ case PskConnectRightCredentialsPeerVerifyFailure:
+ case PskConnectRightCredentialsVerifyPeer:
+ QCOMPARE(socket.peerVerifyMode(), QSslSocket::AutoVerifyPeer);
+ break;
+
+ case PskConnectRightCredentialsDoNotVerifyPeer:
+ QCOMPARE(socket.peerVerifyMode(), QSslSocket::VerifyNone);
+ break;
+ }
+
+ // Start connecting
+ socket.connectToHost(QtNetworkSettings::serverName(), PSK_SERVER_PORT);
+ QCOMPARE(socket.state(), QAbstractSocket::HostLookupState);
+ enterLoop(10);
+
+ // Entered connecting state
+ QCOMPARE(socket.state(), QAbstractSocket::ConnectingState);
+ QCOMPARE(connectedSpy.count(), 0);
+ QCOMPARE(hostFoundSpy.count(), 1);
+ QCOMPARE(disconnectedSpy.count(), 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);
+
+ // 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);
+
+ // Start handshake.
+ enterLoop(10);
+
+ // We must get the PSK signal in all cases
+ QCOMPARE(pskAuthenticationRequiredSpy.count(), 1);
+
+ switch (pskTestType) {
+ case PskConnectDoNotHandlePsk:
+ case PskConnectEmptyCredentials:
+ case PskConnectWrongCredentials:
+ case PskConnectWrongIdentity:
+ case PskConnectWrongPreSharedKey:
+ // Handshake failure
+ QCOMPARE(socketErrorsSpy.count(), 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);
+ QVERIFY(!socket.isEncrypted());
+ break;
+
+ case PskConnectRightCredentialsPeerVerifyFailure:
+ // Peer verification failure
+ QCOMPARE(socketErrorsSpy.count(), 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);
+ 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);
+ 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);
+ QVERIFY(socket.isEncrypted());
+ QCOMPARE(socket.state(), QAbstractSocket::ConnectedState);
+ break;
+ }
+
+ // check writing
+ switch (pskTestType) {
+ case PskConnectDoNotHandlePsk:
+ case PskConnectEmptyCredentials:
+ case PskConnectWrongCredentials:
+ case PskConnectWrongIdentity:
+ case PskConnectWrongPreSharedKey:
+ case PskConnectRightCredentialsPeerVerifyFailure:
+ break;
+
+ case PskConnectRightCredentialsVerifyPeer:
+ case PskConnectRightCredentialsDoNotVerifyPeer:
+ socket.write("Hello from Qt TLS/PSK!");
+ QVERIFY(socket.waitForBytesWritten());
+ break;
+ }
+
+ // disconnect
+ switch (pskTestType) {
+ case PskConnectDoNotHandlePsk:
+ case PskConnectEmptyCredentials:
+ case PskConnectWrongCredentials:
+ case PskConnectWrongIdentity:
+ case PskConnectWrongPreSharedKey:
+ case PskConnectRightCredentialsPeerVerifyFailure:
+ break;
+
+ case PskConnectRightCredentialsVerifyPeer:
+ case PskConnectRightCredentialsDoNotVerifyPeer:
+ socket.disconnectFromHost();
+ enterLoop(10);
+ break;
+ }
+
+ QCOMPARE(socket.state(), QAbstractSocket::UnconnectedState);
+ QCOMPARE(disconnectedSpy.count(), 1);
+}
+#endif // QT_NO_OPENSSL
+
#endif // QT_NO_SSL
QTEST_MAIN(tst_QSslSocket)