QSslSocket: introduce support for TLS PSK (client side)
[ChangeLog][QtNetwork][QSslSocket] It is now possible to use TLS PSK ciphersuites in client sockets. Task-number: QTBUG-39077 Change-Id: I5523a2be33d46230c6f4106c322fab8a5afa37b4 Reviewed-by: Richard J. Moore <rich@kde.org>
This commit is contained in:
parent
e267505d54
commit
bd26defd9b
291
src/network/ssl/qsslpresharedkeyauthenticator.cpp
Normal file
291
src/network/ssl/qsslpresharedkeyauthenticator.cpp
Normal file
@ -0,0 +1,291 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Governikus GmbH & Co. KG.
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** 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 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.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: 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
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qsslpresharedkeyauthenticator.h"
|
||||
#include "qsslpresharedkeyauthenticator_p.h"
|
||||
|
||||
#include <QSharedData>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
QSslPreSharedKeyAuthenticatorPrivate::QSslPreSharedKeyAuthenticatorPrivate()
|
||||
: maximumIdentityLength(0),
|
||||
maximumPreSharedKeyLength(0)
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
\class QSslPreSharedKeyAuthenticator
|
||||
|
||||
\brief The QSslPreSharedKeyAuthenticator class provides authentication data for pre
|
||||
shared keys (PSK) ciphersuites.
|
||||
|
||||
\inmodule QtNetwork
|
||||
|
||||
\reentrant
|
||||
|
||||
\ingroup network
|
||||
\ingroup ssl
|
||||
\ingroup shared
|
||||
|
||||
\since 5.5
|
||||
|
||||
The QSslPreSharedKeyAuthenticator class is used by an SSL socket to provide
|
||||
the required authentication data in a pre shared key (PSK) ciphersuite.
|
||||
|
||||
In a PSK handshake, the client must derive a key, which must match the key
|
||||
set on the server. The exact algorithm of deriving the key depends on the
|
||||
application; however, for this purpose, the server may send an \e{identity
|
||||
hint} to the client. This hint, combined with other information (for
|
||||
instance a passphrase), is then used by the client to construct the shared
|
||||
key.
|
||||
|
||||
The QSslPreSharedKeyAuthenticator provides means to client applications for
|
||||
completing the PSK handshake. The client application needs to connect a
|
||||
slot to the QSslSocket::preSharedKeyAuthenticationRequired() signal:
|
||||
|
||||
\code
|
||||
|
||||
connect(socket, &QSslSocket::preSharedKeyAuthenticationRequired,
|
||||
this, &AuthManager::handlePreSharedKeyAuthentication);
|
||||
|
||||
\endcode
|
||||
|
||||
The signal carries a QSslPreSharedKeyAuthenticator object containing the
|
||||
identity hint the server sent to the client, and which must be filled with the
|
||||
corresponding client identity and the derived key:
|
||||
|
||||
\code
|
||||
|
||||
void AuthManager::handlePreSharedKeyAuthentication(QSslPreSharedKeyAuthenticator *authenticator)
|
||||
{
|
||||
authenticator->setIdentity("My Qt App");
|
||||
|
||||
const QByteArray key = deriveKey(authenticator->identityHint(), passphrase);
|
||||
authenticator->setPreSharedKey(key);
|
||||
}
|
||||
|
||||
\endcode
|
||||
|
||||
\note PSK ciphersuites are supported only when using OpenSSL 1.0.1 (or
|
||||
greater) as the SSL backend.
|
||||
|
||||
\sa QSslSocket
|
||||
*/
|
||||
|
||||
/*!
|
||||
Constructs a default QSslPreSharedKeyAuthenticator object.
|
||||
|
||||
The identity hint, the identity and the key will be initialized to empty
|
||||
byte arrays; the maximum length for both the identity and the key will be
|
||||
initialized to 0.
|
||||
*/
|
||||
QSslPreSharedKeyAuthenticator::QSslPreSharedKeyAuthenticator()
|
||||
: d(new QSslPreSharedKeyAuthenticatorPrivate)
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Destroys the QSslPreSharedKeyAuthenticator object.
|
||||
*/
|
||||
QSslPreSharedKeyAuthenticator::~QSslPreSharedKeyAuthenticator()
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Constructs a QSslPreSharedKeyAuthenticator object as a copy of \a authenticator.
|
||||
|
||||
\sa operator=()
|
||||
*/
|
||||
QSslPreSharedKeyAuthenticator::QSslPreSharedKeyAuthenticator(const QSslPreSharedKeyAuthenticator &authenticator)
|
||||
: d(authenticator.d)
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Assigns the QSslPreSharedKeyAuthenticator object \a authenticator to this object,
|
||||
and returns a reference to the copy.
|
||||
*/
|
||||
QSslPreSharedKeyAuthenticator &QSslPreSharedKeyAuthenticator::operator=(const QSslPreSharedKeyAuthenticator &authenticator)
|
||||
{
|
||||
d = authenticator.d;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn QSslPreSharedKeyAuthenticator &QSslPreSharedKeyAuthenticator::operator=(QSslPreSharedKeyAuthenticator &&authenticator)
|
||||
|
||||
Move-assigns the the QSslPreSharedKeyAuthenticator object \a authenticator to this
|
||||
object, and returns a reference to the moved instance.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn void QSslPreSharedKeyAuthenticator::swap(QSslPreSharedKeyAuthenticator &authenticator)
|
||||
|
||||
Swaps the QSslPreSharedKeyAuthenticator object \a authenticator with this object.
|
||||
This operation is very fast and never fails.
|
||||
*/
|
||||
|
||||
/*!
|
||||
Returns the PSK identity hint as provided by the server. The interpretation
|
||||
of this hint is left to the application.
|
||||
*/
|
||||
QByteArray QSslPreSharedKeyAuthenticator::identityHint() const
|
||||
{
|
||||
return d->identityHint;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the PSK client identity (to be advised to the server) to \a identity.
|
||||
|
||||
\note it is possible to set an identity whose length is greater than
|
||||
maximumIdentityLength(); in this case, only the first maximumIdentityLength()
|
||||
bytes will be actually sent to the server.
|
||||
|
||||
\sa identity(), maximumIdentityLength()
|
||||
*/
|
||||
void QSslPreSharedKeyAuthenticator::setIdentity(const QByteArray &identity)
|
||||
{
|
||||
d->identity = identity;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the PSK client identity.
|
||||
|
||||
\sa setIdentity()
|
||||
*/
|
||||
QByteArray QSslPreSharedKeyAuthenticator::identity() const
|
||||
{
|
||||
return d->identity;
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
Returns the maximum length, in bytes, of the PSK client identity.
|
||||
|
||||
\note it is possible to set an identity whose length is greater than
|
||||
maximumIdentityLength(); in this case, only the first maximumIdentityLength()
|
||||
bytes will be actually sent to the server.
|
||||
|
||||
\sa setIdentity()
|
||||
*/
|
||||
int QSslPreSharedKeyAuthenticator::maximumIdentityLength() const
|
||||
{
|
||||
return d->maximumIdentityLength;
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
Sets the pre shared key to \a preSharedKey.
|
||||
|
||||
\note it is possible to set a key whose length is greater than the
|
||||
maximumPreSharedKeyLength(); in this case, only the first
|
||||
maximumPreSharedKeyLength() bytes will be actually sent to the server.
|
||||
|
||||
\sa preSharedKey(), maximumPreSharedKeyLength(), QByteArray::fromHex()
|
||||
*/
|
||||
void QSslPreSharedKeyAuthenticator::setPreSharedKey(const QByteArray &preSharedKey)
|
||||
{
|
||||
d->preSharedKey = preSharedKey;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the pre shared key.
|
||||
|
||||
\sa setPreSharedKey()
|
||||
*/
|
||||
QByteArray QSslPreSharedKeyAuthenticator::preSharedKey() const
|
||||
{
|
||||
return d->preSharedKey;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the maximum length, in bytes, of the pre shared key.
|
||||
|
||||
\note it is possible to set a key whose length is greater than the
|
||||
maximumPreSharedKeyLength(); in this case, only the first
|
||||
maximumPreSharedKeyLength() bytes will be actually sent to the server.
|
||||
|
||||
\sa setPreSharedKey()
|
||||
*/
|
||||
int QSslPreSharedKeyAuthenticator::maximumPreSharedKeyLength() const
|
||||
{
|
||||
return d->maximumPreSharedKeyLength;
|
||||
}
|
||||
|
||||
/*!
|
||||
\relates QSslPreSharedKeyAuthenticator
|
||||
\since 5.5
|
||||
|
||||
Returns true if the authenticator object \a lhs is equal to \a rhs; false
|
||||
otherwise.
|
||||
|
||||
Two authenticator objects are equal if and only if they have the same
|
||||
identity hint, identity, pre shared key, maximum length for the identity
|
||||
and maximum length for the pre shared key.
|
||||
|
||||
\sa operator!=(const QSslPreSharedKeyAuthenticator &lhs, const QSslPreSharedKeyAuthenticator &rhs)
|
||||
*/
|
||||
bool operator==(const QSslPreSharedKeyAuthenticator &lhs, const QSslPreSharedKeyAuthenticator &rhs)
|
||||
{
|
||||
return ((lhs.d == rhs.d) ||
|
||||
(lhs.d->identityHint == rhs.d->identityHint &&
|
||||
lhs.d->identity == rhs.d->identity &&
|
||||
lhs.d->maximumIdentityLength == rhs.d->maximumIdentityLength &&
|
||||
lhs.d->preSharedKey == rhs.d->preSharedKey &&
|
||||
lhs.d->maximumPreSharedKeyLength == rhs.d->maximumPreSharedKeyLength));
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn bool operator!=(const QSslPreSharedKeyAuthenticator &lhs, const QSslPreSharedKeyAuthenticator &rhs)
|
||||
\relates QSslPreSharedKeyAuthenticator
|
||||
\since 5.5
|
||||
|
||||
Returns true if the authenticator object \a lhs is different than \a rhs;
|
||||
false otherwise.
|
||||
|
||||
\sa operator==(const QSslPreSharedKeyAuthenticator &lhs, const QSslPreSharedKeyAuthenticator &rhs)
|
||||
*/
|
||||
|
||||
QT_END_NAMESPACE
|
101
src/network/ssl/qsslpresharedkeyauthenticator.h
Normal file
101
src/network/ssl/qsslpresharedkeyauthenticator.h
Normal file
@ -0,0 +1,101 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Governikus GmbH & Co. KG.
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** 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 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.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: 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
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QSSLPRESHAREDKEYAUTHENTICATOR_H
|
||||
#define QSSLPRESHAREDKEYAUTHENTICATOR_H
|
||||
|
||||
#include <QtCore/QtGlobal>
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QSharedDataPointer>
|
||||
#include <QtCore/QMetaType>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QSslPreSharedKeyAuthenticatorPrivate;
|
||||
|
||||
class Q_NETWORK_EXPORT QSslPreSharedKeyAuthenticator
|
||||
{
|
||||
public:
|
||||
QSslPreSharedKeyAuthenticator();
|
||||
~QSslPreSharedKeyAuthenticator();
|
||||
QSslPreSharedKeyAuthenticator(const QSslPreSharedKeyAuthenticator &authenticator);
|
||||
QSslPreSharedKeyAuthenticator &operator=(const QSslPreSharedKeyAuthenticator &authenticator);
|
||||
|
||||
#ifdef Q_COMPILER_RVALUE_REFS
|
||||
inline QSslPreSharedKeyAuthenticator &operator=(QSslPreSharedKeyAuthenticator &&authenticator)
|
||||
{ d.swap(authenticator.d); return *this; }
|
||||
#endif
|
||||
|
||||
void swap(QSslPreSharedKeyAuthenticator &authenticator)
|
||||
{
|
||||
d.swap(authenticator.d);
|
||||
}
|
||||
|
||||
QByteArray identityHint() const;
|
||||
|
||||
void setIdentity(const QByteArray &identity);
|
||||
QByteArray identity() const;
|
||||
int maximumIdentityLength() const;
|
||||
|
||||
void setPreSharedKey(const QByteArray &preSharedKey);
|
||||
QByteArray preSharedKey() const;
|
||||
int maximumPreSharedKeyLength() const;
|
||||
|
||||
private:
|
||||
friend Q_NETWORK_EXPORT bool operator==(const QSslPreSharedKeyAuthenticator &lhs, const QSslPreSharedKeyAuthenticator &rhs);
|
||||
friend class QSslSocketBackendPrivate;
|
||||
|
||||
QSharedDataPointer<QSslPreSharedKeyAuthenticatorPrivate> d;
|
||||
};
|
||||
|
||||
inline bool operator!=(const QSslPreSharedKeyAuthenticator &lhs, const QSslPreSharedKeyAuthenticator &rhs)
|
||||
{
|
||||
return !operator==(lhs, rhs);
|
||||
}
|
||||
|
||||
Q_DECLARE_SHARED(QSslPreSharedKeyAuthenticator)
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
Q_DECLARE_METATYPE(QSslPreSharedKeyAuthenticator)
|
||||
Q_DECLARE_METATYPE(QSslPreSharedKeyAuthenticator*)
|
||||
|
||||
#endif // QSSLPRESHAREDKEYAUTHENTICATOR_H
|
65
src/network/ssl/qsslpresharedkeyauthenticator_p.h
Normal file
65
src/network/ssl/qsslpresharedkeyauthenticator_p.h
Normal file
@ -0,0 +1,65 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Governikus GmbH & Co. KG.
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** 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 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.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: 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
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QSSLPRESHAREDKEYAUTHENTICATOR_P_H
|
||||
#define QSSLPRESHAREDKEYAUTHENTICATOR_P_H
|
||||
|
||||
#include <QSharedData>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QSslPreSharedKeyAuthenticatorPrivate : public QSharedData
|
||||
{
|
||||
public:
|
||||
QSslPreSharedKeyAuthenticatorPrivate();
|
||||
|
||||
QByteArray identityHint;
|
||||
|
||||
QByteArray identity;
|
||||
int maximumIdentityLength;
|
||||
|
||||
QByteArray preSharedKey;
|
||||
int maximumPreSharedKeyLength;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QSSLPRESHAREDKEYAUTHENTICATOR_P_H
|
@ -281,6 +281,28 @@
|
||||
\sa peerVerifyError()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn void QSslSocket::preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator *authenticator)
|
||||
\since 5.5
|
||||
|
||||
QSslSocket 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
|
||||
*/
|
||||
|
||||
#include "qssl_p.h"
|
||||
#include "qsslsocket.h"
|
||||
#include "qsslcipher.h"
|
||||
|
@ -52,6 +52,7 @@ class QSslCipher;
|
||||
class QSslCertificate;
|
||||
class QSslConfiguration;
|
||||
class QSslEllipticCurve;
|
||||
class QSslPreSharedKeyAuthenticator;
|
||||
|
||||
class QSslSocketPrivate;
|
||||
class Q_NETWORK_EXPORT QSslSocket : public QTcpSocket
|
||||
@ -199,6 +200,7 @@ Q_SIGNALS:
|
||||
void sslErrors(const QList<QSslError> &errors);
|
||||
void modeChanged(QSslSocket::SslMode newMode);
|
||||
void encryptedBytesWritten(qint64 totalBytes);
|
||||
void preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator *authenticator);
|
||||
|
||||
protected:
|
||||
qint64 readData(char *data, qint64 maxlen) Q_DECL_OVERRIDE;
|
||||
|
@ -58,6 +58,8 @@
|
||||
#include "qsslcipher_p.h"
|
||||
#include "qsslkey_p.h"
|
||||
#include "qsslellipticcurve.h"
|
||||
#include "qsslpresharedkeyauthenticator.h"
|
||||
#include "qsslpresharedkeyauthenticator_p.h"
|
||||
|
||||
#include <QtCore/qdatetime.h>
|
||||
#include <QtCore/qdebug.h>
|
||||
@ -72,6 +74,8 @@
|
||||
#include <QtCore/qvarlengtharray.h>
|
||||
#include <QLibrary> // for loading the security lib for the CA store
|
||||
|
||||
#include <string.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
#if defined(Q_OS_MACX)
|
||||
@ -89,6 +93,10 @@ bool QSslSocketPrivate::s_libraryLoaded = false;
|
||||
bool QSslSocketPrivate::s_loadedCiphersAndCerts = false;
|
||||
bool QSslSocketPrivate::s_loadRootCertsOnDemand = false;
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10001000L
|
||||
int QSslSocketBackendPrivate::s_indexForSSLExtraData = -1;
|
||||
#endif
|
||||
|
||||
/* \internal
|
||||
|
||||
From OpenSSL's thread(3) manual page:
|
||||
@ -181,6 +189,18 @@ static unsigned long id_function()
|
||||
{
|
||||
return (quintptr)QThread::currentThreadId();
|
||||
}
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10001000L && !defined(OPENSSL_NO_PSK)
|
||||
static unsigned int q_ssl_psk_client_callback(SSL *ssl,
|
||||
const char *hint,
|
||||
char *identity, unsigned int max_identity_len,
|
||||
unsigned char *psk, unsigned int max_psk_len)
|
||||
{
|
||||
QSslSocketBackendPrivate *d = reinterpret_cast<QSslSocketBackendPrivate *>(q_SSL_get_ex_data(ssl, QSslSocketBackendPrivate::s_indexForSSLExtraData));
|
||||
Q_ASSERT(d);
|
||||
return d->tlsPskClientCallback(hint, identity, max_identity_len, psk, max_psk_len);
|
||||
}
|
||||
#endif
|
||||
} // extern "C"
|
||||
|
||||
QSslSocketBackendPrivate::QSslSocketBackendPrivate()
|
||||
@ -390,6 +410,18 @@ bool QSslSocketBackendPrivate::initSslContext()
|
||||
else
|
||||
q_SSL_set_accept_state(ssl);
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10001000L
|
||||
// Save a pointer to this object into the SSL structure.
|
||||
if (q_SSLeay() >= 0x10001000L)
|
||||
q_SSL_set_ex_data(ssl, s_indexForSSLExtraData, this);
|
||||
#endif
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10001000L && !defined(OPENSSL_NO_PSK)
|
||||
// Set the client callback for PSK
|
||||
if (q_SSLeay() >= 0x10001000L && mode == QSslSocket::SslClientMode)
|
||||
q_SSL_set_psk_client_callback(ssl, &q_ssl_psk_client_callback);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -443,6 +475,11 @@ bool QSslSocketPrivate::ensureLibraryLoaded()
|
||||
q_SSL_load_error_strings();
|
||||
q_OpenSSL_add_all_algorithms();
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10001000L
|
||||
if (q_SSLeay() >= 0x10001000L)
|
||||
QSslSocketBackendPrivate::s_indexForSSLExtraData = q_SSL_get_ex_new_index(0L, NULL, NULL, NULL, NULL);
|
||||
#endif
|
||||
|
||||
// Initialize OpenSSL's random seed.
|
||||
if (!q_RAND_status()) {
|
||||
struct {
|
||||
@ -1262,6 +1299,37 @@ bool QSslSocketBackendPrivate::checkSslErrors()
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned int QSslSocketBackendPrivate::tlsPskClientCallback(const char *hint,
|
||||
char *identity, unsigned int max_identity_len,
|
||||
unsigned char *psk, unsigned int max_psk_len)
|
||||
{
|
||||
QSslPreSharedKeyAuthenticator authenticator;
|
||||
|
||||
// Fill in some read-only fields (for the user)
|
||||
if (hint)
|
||||
authenticator.d->identityHint = QByteArray::fromRawData(hint, int(::strlen(hint))); // it's NUL terminated, but do not include the NUL
|
||||
|
||||
authenticator.d->maximumIdentityLength = int(max_identity_len) - 1; // needs to be NUL terminated
|
||||
authenticator.d->maximumPreSharedKeyLength = int(max_psk_len);
|
||||
|
||||
// Let the client provide the remaining bits...
|
||||
Q_Q(QSslSocket);
|
||||
emit q->preSharedKeyAuthenticationRequired(&authenticator);
|
||||
|
||||
// No PSK set? Return now to make the handshake fail
|
||||
if (authenticator.preSharedKey().isEmpty())
|
||||
return 0;
|
||||
|
||||
// Copy data back into OpenSSL
|
||||
const int identityLength = qMin(authenticator.identity().length(), authenticator.maximumIdentityLength());
|
||||
::memcpy(identity, authenticator.identity().constData(), identityLength);
|
||||
identity[identityLength] = 0;
|
||||
|
||||
const int pskLength = qMin(authenticator.preSharedKey().length(), authenticator.maximumPreSharedKeyLength());
|
||||
::memcpy(psk, authenticator.preSharedKey().constData(), pskLength);
|
||||
return pskLength;
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
|
||||
void QSslSocketBackendPrivate::fetchCaRootForCert(const QSslCertificate &cert)
|
||||
|
@ -114,6 +114,9 @@ public:
|
||||
BIO *writeBio;
|
||||
SSL_SESSION *session;
|
||||
QList<QPair<int, int> > errorList;
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10001000L
|
||||
static int s_indexForSSLExtraData; // index used in SSL_get_ex_data to get the matching QSslSocketBackendPrivate
|
||||
#endif
|
||||
|
||||
// Platform specific functions
|
||||
void startClientEncryption() Q_DECL_OVERRIDE;
|
||||
@ -126,6 +129,7 @@ public:
|
||||
QSsl::SslProtocol sessionProtocol() const Q_DECL_OVERRIDE;
|
||||
void continueHandshake() Q_DECL_OVERRIDE;
|
||||
bool checkSslErrors();
|
||||
unsigned int tlsPskClientCallback(const char *hint, char *identity, unsigned int max_identity_len, unsigned char *psk, unsigned int max_psk_len);
|
||||
#ifdef Q_OS_WIN
|
||||
void fetchCaRootForCert(const QSslCertificate &cert);
|
||||
void _q_caRootLoaded(QSslCertificate,QSslCertificate);
|
||||
|
@ -290,6 +290,14 @@ DEFINEFUNC2(int, SSL_set_session, SSL* to, to, SSL_SESSION *session, session, re
|
||||
DEFINEFUNC(void, SSL_SESSION_free, SSL_SESSION *ses, ses, return, DUMMYARG)
|
||||
DEFINEFUNC(SSL_SESSION*, SSL_get1_session, SSL *ssl, ssl, return 0, return)
|
||||
DEFINEFUNC(SSL_SESSION*, SSL_get_session, const SSL *ssl, ssl, return 0, return)
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10001000L
|
||||
DEFINEFUNC5(int, SSL_get_ex_new_index, long argl, argl, void *argp, argp, CRYPTO_EX_new *new_func, new_func, CRYPTO_EX_dup *dup_func, dup_func, CRYPTO_EX_free *free_func, free_func, return -1, return)
|
||||
DEFINEFUNC3(int, SSL_set_ex_data, SSL *ssl, ssl, int idx, idx, void *arg, arg, return 0, return)
|
||||
DEFINEFUNC2(void *, SSL_get_ex_data, const SSL *ssl, ssl, int idx, idx, return NULL, return)
|
||||
#endif
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10001000L && !defined(OPENSSL_NO_PSK)
|
||||
DEFINEFUNC2(void, SSL_set_psk_client_callback, SSL* ssl, ssl, q_psk_client_callback_t callback, callback, return, DUMMYARG)
|
||||
#endif
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
|
||||
#ifndef OPENSSL_NO_SSL2
|
||||
DEFINEFUNC(const SSL_METHOD *, SSLv2_client_method, DUMMYARG, DUMMYARG, return 0, return)
|
||||
@ -854,6 +862,14 @@ bool q_resolveOpenSslSymbols()
|
||||
RESOLVEFUNC(SSL_SESSION_free)
|
||||
RESOLVEFUNC(SSL_get1_session)
|
||||
RESOLVEFUNC(SSL_get_session)
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10001000L
|
||||
RESOLVEFUNC(SSL_get_ex_new_index)
|
||||
RESOLVEFUNC(SSL_set_ex_data)
|
||||
RESOLVEFUNC(SSL_get_ex_data)
|
||||
#endif
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10001000L && !defined(OPENSSL_NO_PSK)
|
||||
RESOLVEFUNC(SSL_set_psk_client_callback)
|
||||
#endif
|
||||
RESOLVEFUNC(SSL_write)
|
||||
#ifndef OPENSSL_NO_SSL2
|
||||
RESOLVEFUNC(SSLv2_client_method)
|
||||
|
@ -374,6 +374,15 @@ int q_SSL_set_session(SSL *to, SSL_SESSION *session);
|
||||
void q_SSL_SESSION_free(SSL_SESSION *ses);
|
||||
SSL_SESSION *q_SSL_get1_session(SSL *ssl);
|
||||
SSL_SESSION *q_SSL_get_session(const SSL *ssl);
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10001000L
|
||||
int q_SSL_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func, CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func);
|
||||
int q_SSL_set_ex_data(SSL *ssl, int idx, void *arg);
|
||||
void *q_SSL_get_ex_data(const SSL *ssl, int idx);
|
||||
#endif
|
||||
#ifndef OPENSSL_NO_PSK
|
||||
typedef unsigned int (*q_psk_client_callback_t)(SSL *ssl, const char *hint, char *identity, unsigned int max_identity_len, unsigned char *psk, unsigned int max_psk_len);
|
||||
void q_SSL_set_psk_client_callback(SSL *ssl, q_psk_client_callback_t callback);
|
||||
#endif // OPENSSL_NO_PSK
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
|
||||
const SSL_METHOD *q_SSLv2_client_method();
|
||||
const SSL_METHOD *q_SSLv3_client_method();
|
||||
|
@ -15,6 +15,8 @@ contains(QT_CONFIG, ssl) | contains(QT_CONFIG, openssl) | contains(QT_CONFIG, op
|
||||
ssl/qsslkey_p.h \
|
||||
ssl/qsslsocket.h \
|
||||
ssl/qsslsocket_p.h \
|
||||
ssl/qsslpresharedkeyauthenticator.h \
|
||||
ssl/qsslpresharedkeyauthenticator_p.h \
|
||||
ssl/qsslcertificateextension.h \
|
||||
ssl/qsslcertificateextension_p.h
|
||||
SOURCES += ssl/qasn1element.cpp \
|
||||
@ -26,6 +28,7 @@ contains(QT_CONFIG, ssl) | contains(QT_CONFIG, openssl) | contains(QT_CONFIG, op
|
||||
ssl/qsslkey_p.cpp \
|
||||
ssl/qsslerror.cpp \
|
||||
ssl/qsslsocket.cpp \
|
||||
ssl/qsslpresharedkeyauthenticator.cpp \
|
||||
ssl/qsslcertificateextension.cpp
|
||||
|
||||
winrt {
|
||||
|
@ -1,6 +1,7 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Copyright (C) 2014 Governikus GmbH & Co. KG.
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the test suite of the Qt Toolkit.
|
||||
@ -41,6 +42,7 @@
|
||||
#include <QtNetwork/qsslkey.h>
|
||||
#include <QtNetwork/qsslsocket.h>
|
||||
#include <QtNetwork/qtcpserver.h>
|
||||
#include <QtNetwork/qsslpresharedkeyauthenticator.h>
|
||||
#include <QtTest/QtTest>
|
||||
|
||||
#include <QNetworkProxy>
|
||||
@ -81,6 +83,15 @@ typedef QSharedPointer<QSslSocket> QSslSocketPtr;
|
||||
#define QSSLSOCKET_CERTUNTRUSTED_WORKAROUND
|
||||
#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
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -104,6 +115,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:
|
||||
@ -199,6 +223,11 @@ private slots:
|
||||
void ecdhServer();
|
||||
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
|
||||
@ -231,6 +260,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()
|
||||
@ -240,6 +275,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
|
||||
}
|
||||
|
||||
@ -2762,6 +2802,320 @@ void tst_QSslSocket::setEmptyDefaultConfiguration() // this test should be last,
|
||||
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)
|
||||
|
Loading…
Reference in New Issue
Block a user