summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndré Klitzing <aklitzing@gmail.com>2016-04-28 17:13:57 +0200
committerLiang Qi <liang.qi@qt.io>2016-06-12 18:57:46 +0000
commit5bc48a4443b5b4a3ab2e20c6c839305f698946ed (patch)
treeed65116e1d33fbe3c4721594a66d023ab443cc72
parent5386af8d31b7b0211925de5cb9b9189889b7b2c1 (diff)
downloadqtwebsockets-5bc48a4443b5b4a3ab2e20c6c839305f698946ed.tar.gz
Add support for TLS PSK (client and server)
[ChangeLog][QWebSocket] It is now possible to use TLS PSK ciphersuites. Change-Id: I9e96669494cec5e6a4e076fe9f10fcd4ef6358a4 Reviewed-by: Liang Qi <liang.qi@qt.io>
-rw-r--r--src/websockets/qsslserver.cpp1
-rw-r--r--src/websockets/qsslserver_p.h2
-rw-r--r--src/websockets/qwebsocket.cpp22
-rw-r--r--src/websockets/qwebsocket.h1
-rw-r--r--src/websockets/qwebsocket_p.cpp3
-rw-r--r--src/websockets/qwebsocketserver.cpp23
-rw-r--r--src/websockets/qwebsocketserver.h1
-rw-r--r--src/websockets/qwebsocketserver_p.cpp2
-rw-r--r--tests/auto/websockets/qwebsocketserver/tst_qwebsocketserver.cpp127
9 files changed, 182 insertions, 0 deletions
diff --git a/src/websockets/qsslserver.cpp b/src/websockets/qsslserver.cpp
index 41db66a..5df59f7 100644
--- a/src/websockets/qsslserver.cpp
+++ b/src/websockets/qsslserver.cpp
@@ -119,6 +119,7 @@ void QSslServer::incomingConnection(qintptr socket)
connect(pSslSocket, static_cast<sslErrorsSignal>(&QSslSocket::sslErrors),
this, &QSslServer::sslErrors);
connect(pSslSocket, &QSslSocket::encrypted, this, &QSslServer::newEncryptedConnection);
+ connect(pSslSocket, &QSslSocket::preSharedKeyAuthenticationRequired, this, &QSslServer::preSharedKeyAuthenticationRequired);
addPendingConnection(pSslSocket);
diff --git a/src/websockets/qsslserver_p.h b/src/websockets/qsslserver_p.h
index 538e3b5..d5e581a 100644
--- a/src/websockets/qsslserver_p.h
+++ b/src/websockets/qsslserver_p.h
@@ -54,6 +54,7 @@
#include <QtNetwork/QTcpServer>
#include <QtNetwork/QSslError>
#include <QtNetwork/QSslConfiguration>
+#include <QtNetwork/QSslPreSharedKeyAuthenticator>
#include <QtCore/QList>
QT_BEGIN_NAMESPACE
@@ -74,6 +75,7 @@ Q_SIGNALS:
void sslErrors(const QList<QSslError> &errors);
void peerVerifyError(const QSslError &error);
void newEncryptedConnection();
+ void preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator *authenticator);
protected:
virtual void incomingConnection(qintptr socket);
diff --git a/src/websockets/qwebsocket.cpp b/src/websockets/qwebsocket.cpp
index 90973d6..ba343e4 100644
--- a/src/websockets/qwebsocket.cpp
+++ b/src/websockets/qwebsocket.cpp
@@ -239,6 +239,28 @@ not been filled in with new information when the signal returns.
QWebSocket::ignoreSslErrors() will have no effect.
*/
/*!
+ \fn void QWebSocket::preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator *authenticator)
+ \since 5.8
+
+ This signal is emitted if the SSL/TLS handshake negotiates a PSK
+ ciphersuite, and therefore a PSK authentication is then required.
+
+ When using PSK, the client must send to the server a valid identity and a
+ valid pre shared key, in order for the SSL handshake to continue.
+ Applications can provide this information in a slot connected to this
+ signal, by filling in the passed \a authenticator object according to their
+ needs.
+
+ \note Ignoring this signal, or failing to provide the required credentials,
+ will cause the handshake to fail, and therefore the connection to be aborted.
+
+ \note The \a authenticator object is owned by the websocket and must not be
+ deleted by the application.
+
+ \sa QSslPreSharedKeyAuthenticator
+ \sa QSslSocket::preSharedKeyAuthenticationRequired()
+*/
+/*!
\fn void QWebSocket::pong(quint64 elapsedTime, const QByteArray &payload)
Emitted when a pong message is received in reply to a previous ping.
diff --git a/src/websockets/qwebsocket.h b/src/websockets/qwebsocket.h
index e6c2473..cbe53ca 100644
--- a/src/websockets/qwebsocket.h
+++ b/src/websockets/qwebsocket.h
@@ -142,6 +142,7 @@ Q_SIGNALS:
#ifndef QT_NO_SSL
void sslErrors(const QList<QSslError> &errors);
+ void preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator *authenticator);
#endif
private:
diff --git a/src/websockets/qwebsocket_p.cpp b/src/websockets/qwebsocket_p.cpp
index 188df33..74945f4 100644
--- a/src/websockets/qwebsocket_p.cpp
+++ b/src/websockets/qwebsocket_p.cpp
@@ -60,6 +60,7 @@
#ifndef QT_NO_SSL
#include <QtNetwork/QSslConfiguration>
#include <QtNetwork/QSslError>
+#include <QtNetwork/QSslPreSharedKeyAuthenticator>
#endif
#include <QtCore/QDebug>
@@ -592,6 +593,8 @@ void QWebSocketPrivate::makeConnections(const QTcpSocket *pTcpSocket)
#ifndef QT_NO_SSL
const QSslSocket * const sslSocket = qobject_cast<const QSslSocket *>(pTcpSocket);
if (sslSocket) {
+ QObject::connect(sslSocket, &QSslSocket::preSharedKeyAuthenticationRequired, q,
+ &QWebSocket::preSharedKeyAuthenticationRequired);
QObject::connect(sslSocket, &QSslSocket::encryptedBytesWritten, q,
&QWebSocket::bytesWritten);
typedef void (QSslSocket:: *sslErrorSignalType)(const QList<QSslError> &);
diff --git a/src/websockets/qwebsocketserver.cpp b/src/websockets/qwebsocketserver.cpp
index ab5da31..f8ecdf2 100644
--- a/src/websockets/qwebsocketserver.cpp
+++ b/src/websockets/qwebsocketserver.cpp
@@ -193,6 +193,29 @@
*/
/*!
+ \fn void QWebSocketServer::preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator *authenticator)
+ \since 5.8
+
+ QWebSocketServer emits this signal when it negotiates a PSK ciphersuite, and
+ therefore a PSK authentication is then required.
+
+ When using PSK, the client must send to the server a valid identity and a
+ valid pre shared key, in order for the SSL handshake to continue.
+ Applications can provide this information in a slot connected to this
+ signal, by filling in the passed \a authenticator object according to their
+ needs.
+
+ \note Ignoring this signal, or failing to provide the required credentials,
+ will cause the handshake to fail, and therefore the connection to be aborted.
+
+ \note The \a authenticator object is owned by the socket and must not be
+ deleted by the application.
+
+ \sa QSslPreSharedKeyAuthenticator
+ \sa QSslSocket::preSharedKeyAuthenticationRequired()
+*/
+
+/*!
\enum QWebSocketServer::SslMode
Indicates whether the server operates over wss (SecureMode) or ws (NonSecureMode)
diff --git a/src/websockets/qwebsocketserver.h b/src/websockets/qwebsocketserver.h
index 58a3d54..47113e4 100644
--- a/src/websockets/qwebsocketserver.h
+++ b/src/websockets/qwebsocketserver.h
@@ -128,6 +128,7 @@ Q_SIGNALS:
#ifndef QT_NO_SSL
void peerVerifyError(const QSslError &error);
void sslErrors(const QList<QSslError> &errors);
+ void preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator *authenticator);
#endif
void closed();
};
diff --git a/src/websockets/qwebsocketserver_p.cpp b/src/websockets/qwebsocketserver_p.cpp
index 288c05b..91bfafc 100644
--- a/src/websockets/qwebsocketserver_p.cpp
+++ b/src/websockets/qwebsocketserver_p.cpp
@@ -104,6 +104,8 @@ void QWebSocketServerPrivate::init()
q_ptr, &QWebSocketServer::peerVerifyError);
QObject::connect(pSslServer, &QSslServer::sslErrors,
q_ptr, &QWebSocketServer::sslErrors);
+ QObject::connect(pSslServer, &QSslServer::preSharedKeyAuthenticationRequired,
+ q_ptr, &QWebSocketServer::preSharedKeyAuthenticationRequired);
}
#else
qFatal("SSL not supported on this platform.");
diff --git a/tests/auto/websockets/qwebsocketserver/tst_qwebsocketserver.cpp b/tests/auto/websockets/qwebsocketserver/tst_qwebsocketserver.cpp
index fb3d5b8..699939f 100644
--- a/tests/auto/websockets/qwebsocketserver/tst_qwebsocketserver.cpp
+++ b/tests/auto/websockets/qwebsocketserver/tst_qwebsocketserver.cpp
@@ -28,6 +28,10 @@
#include <QString>
#include <QtTest>
#include <QNetworkProxy>
+#ifndef QT_NO_OPENSSL
+#include <QtNetwork/qsslpresharedkeyauthenticator.h>
+#include <QtNetwork/qsslcipher.h>
+#endif
#include <QtWebSockets/QWebSocketServer>
#include <QtWebSockets/QWebSocket>
#include <QtWebSockets/QWebSocketCorsAuthenticator>
@@ -43,6 +47,47 @@ Q_DECLARE_METATYPE(QWebSocketCorsAuthenticator *)
Q_DECLARE_METATYPE(QSslError)
#endif
+#ifndef QT_NO_OPENSSL
+// Use this cipher to force PSK key sharing.
+static const QString PSK_CIPHER_WITHOUT_AUTH = QStringLiteral("PSK-AES256-CBC-SHA");
+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 PskProvider : public QObject
+{
+ Q_OBJECT
+
+public:
+ bool m_server = false;
+ QByteArray m_identity;
+ QByteArray m_psk;
+
+public slots:
+ void providePsk(QSslPreSharedKeyAuthenticator *authenticator)
+ {
+ QVERIFY(authenticator);
+ QCOMPARE(authenticator->identityHint(), PSK_SERVER_IDENTITY_HINT);
+ if (m_server)
+ QCOMPARE(authenticator->maximumIdentityLength(), 0);
+ else
+ 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);
+ }
+ }
+};
+#endif
+
class tst_QWebSocketServer : public QObject
{
Q_OBJECT
@@ -58,6 +103,7 @@ private Q_SLOTS:
void tst_settersAndGetters();
void tst_listening();
void tst_connectivity();
+ void tst_preSharedKey();
void tst_maxPendingConnections();
void tst_serverDestroyedWhileSocketConnected();
};
@@ -74,6 +120,9 @@ void tst_QWebSocketServer::init()
qRegisterMetaType<QWebSocketCorsAuthenticator *>("QWebSocketCorsAuthenticator *");
#ifndef QT_NO_SSL
qRegisterMetaType<QSslError>("QSslError");
+#ifndef QT_NO_OPENSSL
+ qRegisterMetaType<QSslPreSharedKeyAuthenticator *>();
+#endif
#endif
}
@@ -268,6 +317,84 @@ void tst_QWebSocketServer::tst_connectivity()
QCOMPARE(serverErrorSpy.count(), 0);
}
+void tst_QWebSocketServer::tst_preSharedKey()
+{
+#ifndef QT_NO_OPENSSL
+ QWebSocketServer server(QString(), QWebSocketServer::SecureMode);
+
+ bool cipherFound = false;
+ const QList<QSslCipher> supportedCiphers = QSslSocket::supportedCiphers();
+ for (const QSslCipher &cipher : supportedCiphers) {
+ if (cipher.name() == PSK_CIPHER_WITHOUT_AUTH) {
+ cipherFound = true;
+ break;
+ }
+ }
+
+ if (!cipherFound)
+ QSKIP("SSL implementation does not support the necessary cipher");
+
+ QSslCipher cipher(PSK_CIPHER_WITHOUT_AUTH);
+ QList<QSslCipher> list;
+ list << cipher;
+
+ QSslConfiguration config = QSslConfiguration::defaultConfiguration();
+ config.setCiphers(list);
+ config.setPeerVerifyMode(QSslSocket::VerifyNone);
+ config.setPreSharedKeyIdentityHint(PSK_SERVER_IDENTITY_HINT);
+ server.setSslConfiguration(config);
+
+ PskProvider providerServer;
+ providerServer.m_server = true;
+ providerServer.m_identity = PSK_CLIENT_IDENTITY;
+ providerServer.m_psk = PSK_CLIENT_PRESHAREDKEY;
+ connect(&server, &QWebSocketServer::preSharedKeyAuthenticationRequired, &providerServer, &PskProvider::providePsk);
+
+ QSignalSpy serverPskRequiredSpy(&server, &QWebSocketServer::preSharedKeyAuthenticationRequired);
+ QSignalSpy serverConnectionSpy(&server, &QWebSocketServer::newConnection);
+ QSignalSpy serverErrorSpy(&server,
+ SIGNAL(serverError(QWebSocketProtocol::CloseCode)));
+ QSignalSpy serverClosedSpy(&server, &QWebSocketServer::closed);
+ QSignalSpy sslErrorsSpy(&server, SIGNAL(sslErrors(QList<QSslError>)));
+
+ QWebSocket socket;
+ QSslConfiguration socketConfig = QSslConfiguration::defaultConfiguration();
+ socketConfig.setPeerVerifyMode(QSslSocket::VerifyNone);
+ socketConfig.setCiphers(list);
+ socket.setSslConfiguration(socketConfig);
+
+ PskProvider providerClient;
+ providerClient.m_identity = PSK_CLIENT_IDENTITY;
+ providerClient.m_psk = PSK_CLIENT_PRESHAREDKEY;
+ connect(&socket, &QWebSocket::preSharedKeyAuthenticationRequired, &providerClient, &PskProvider::providePsk);
+ QSignalSpy socketPskRequiredSpy(&socket, &QWebSocket::preSharedKeyAuthenticationRequired);
+ QSignalSpy socketConnectedSpy(&socket, &QWebSocket::connected);
+
+ QVERIFY(server.listen());
+ QCOMPARE(server.serverAddress(), QHostAddress(QHostAddress::Any));
+ QCOMPARE(server.serverUrl(), QUrl(QString::asprintf("wss://%ls:%d",
+ qUtf16Printable(QHostAddress(QHostAddress::LocalHost).toString()), server.serverPort())));
+
+ socket.open(server.serverUrl().toString());
+
+ if (socketConnectedSpy.count() == 0)
+ QVERIFY(socketConnectedSpy.wait());
+ QCOMPARE(socket.state(), QAbstractSocket::ConnectedState);
+ QCOMPARE(serverConnectionSpy.count(), 1);
+ QCOMPARE(serverPskRequiredSpy.count(), 1);
+ QCOMPARE(socketPskRequiredSpy.count(), 1);
+
+ QCOMPARE(serverClosedSpy.count(), 0);
+
+ server.close();
+
+ QVERIFY(serverClosedSpy.wait());
+ QCOMPARE(serverClosedSpy.count(), 1);
+ QCOMPARE(sslErrorsSpy.count(), 0);
+ QCOMPARE(serverErrorSpy.count(), 0);
+#endif
+}
+
void tst_QWebSocketServer::tst_maxPendingConnections()
{
//tests if maximum connections are respected