| /**************************************************************************** |
| ** |
| ** Copyright (C) 2018 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** 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 The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU Lesser General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Lesser |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or any later version approved by the KDE Free |
| ** Qt Foundation. The licenses are as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qsslconfiguration.h" |
| #include "qdtls_openssl_p.h" |
| #include "qudpsocket.h" |
| #include "qdtls_p.h" |
| #include "qssl_p.h" |
| #include "qdtls.h" |
| |
| #include "qglobal.h" |
| |
| /*! |
| \class QDtlsClientVerifier |
| \brief This class implements server-side DTLS cookie generation and verification. |
| \since 5.12 |
| |
| \ingroup network |
| \ingroup ssl |
| \inmodule QtNetwork |
| |
| The QDtlsClientVerifier class implements server-side DTLS cookie generation |
| and verification. Datagram security protocols are highly susceptible to a |
| variety of Denial-of-Service attacks. According to \l {https://tools.ietf.org/html/rfc6347#section-4.2.1}{RFC 6347, section 4.2.1}, |
| these are two of the more common types of attack: |
| |
| \list |
| \li An attacker transmits a series of handshake initiation requests, causing |
| a server to allocate excessive resources and potentially perform expensive |
| cryptographic operations. |
| \li An attacker transmits a series of handshake initiation requests with |
| a forged source of the victim, making the server act as an amplifier. |
| Normally, the server would reply to the victim machine with a Certificate message, |
| which can be quite large, thus flooding the victim machine with datagrams. |
| \endlist |
| |
| As a countermeasure to these attacks, \l {https://tools.ietf.org/html/rfc6347#section-4.2.1}{RFC 6347, section 4.2.1} |
| proposes a stateless cookie technique that a server may deploy: |
| |
| \list |
| \li In response to the initial ClientHello message, the server sends a HelloVerifyRequest, |
| which contains a cookie. This cookie is a cryptographic hash and is generated using the |
| client's address, port number, and the server's secret (which is a cryptographically strong |
| pseudo-random sequence of bytes). |
| \li A reachable DTLS client is expected to reply with a new ClientHello message |
| containing this cookie. |
| \li When the server receives the ClientHello message with a cookie, it |
| generates a new cookie as described above. This new cookie is compared to the |
| one found in the ClientHello message. |
| \li In the cookies are equal, the client is considered to be real, and the |
| server can continue with a TLS handshake procedure. |
| \endlist |
| |
| \note A DTLS server is not required to use DTLS cookies. |
| |
| QDtlsClientVerifier is designed to work in pair with QUdpSocket, as shown in |
| the following code-excerpt: |
| |
| \snippet code/src_network_ssl_qdtlscookie.cpp 0 |
| |
| QDtlsClientVerifier does not impose any restrictions on how the application uses |
| QUdpSocket. For example, it is possible to have a server with a single QUdpSocket |
| in state QAbstractSocket::BoundState, handling multiple DTLS clients |
| simultaneously: |
| |
| \list |
| \li Testing if new clients are real DTLS-capable clients. |
| \li Completing TLS handshakes with the verified clients (see QDtls). |
| \li Decrypting datagrams coming from the connected clients (see QDtls). |
| \li Sending encrypted datagrams to the connected clients (see QDtls). |
| \endlist |
| |
| This implies that QDtlsClientVerifier does not read directly from a socket, |
| instead it expects the application to read an incoming datagram, extract the |
| sender's address, and port, and then pass this data to verifyClient(). |
| To send a HelloVerifyRequest message, verifyClient() can write to the QUdpSocket. |
| |
| \note QDtlsClientVerifier does not take ownership of the QUdpSocket object. |
| |
| By default QDtlsClientVerifier obtains its secret from a cryptographically |
| strong pseudorandom number generator. |
| |
| \note The default secret is shared by all objects of the classes QDtlsClientVerifier |
| and QDtls. Since this can impose security risks, RFC 6347 recommends to change |
| the server's secret frequently. Please see \l {https://tools.ietf.org/html/rfc6347}{RFC 6347, section 4.2.1} |
| for hints about possible server implementations. Cookie generator parameters |
| can be set using the class QDtlsClientVerifier::GeneratorParameters and |
| setCookieGeneratorParameters(): |
| |
| \snippet code/src_network_ssl_qdtlscookie.cpp 1 |
| |
| The \l{secureudpserver}{DTLS server} example illustrates how to use |
| QDtlsClientVerifier in a server application. |
| |
| \sa QUdpSocket, QAbstractSocket::BoundState, QDtls, verifyClient(), |
| GeneratorParameters, setCookieGeneratorParameters(), cookieGeneratorParameters(), |
| QDtls::setCookieGeneratorParameters(), |
| QDtls::cookieGeneratorParameters(), |
| QCryptographicHash::Algorithm, |
| QDtlsError, dtlsError(), dtlsErrorString() |
| */ |
| |
| /*! |
| \class QDtlsClientVerifier::GeneratorParameters |
| \brief This class defines parameters for DTLS cookie generator. |
| \since 5.12 |
| |
| \ingroup network |
| \ingroup ssl |
| \inmodule QtNetwork |
| |
| An object of this class provides the parameters that QDtlsClientVerifier |
| will use to generate DTLS cookies. They include a cryptographic hash |
| algorithm and a secret. |
| |
| \note An empty secret is considered to be invalid by |
| QDtlsClientVerifier::setCookieGeneratorParameters(). |
| |
| \sa QDtlsClientVerifier::setCookieGeneratorParameters(), |
| QDtlsClientVerifier::cookieGeneratorParameters(), |
| QDtls::setCookieGeneratorParameters(), |
| QDtls::cookieGeneratorParameters(), |
| QCryptographicHash::Algorithm |
| */ |
| |
| /*! |
| \enum QDtlsError |
| \brief Describes errors that can be found by QDtls and QDtlsClientVerifier. |
| \relates QDtls |
| \since 5.12 |
| |
| \ingroup network |
| \ingroup ssl |
| \inmodule QtNetwork |
| |
| This enum describes general and TLS-specific errors that can be encountered |
| by objects of the classes QDtlsClientVerifier and QDtls. |
| |
| \value NoError No error occurred, the last operation was successful. |
| \value InvalidInputParameters Input parameters provided by a caller were |
| invalid. |
| \value InvalidOperation An operation was attempted in a state that did not |
| permit it. |
| \value UnderlyingSocketError QUdpSocket::writeDatagram() failed, QUdpSocket::error() |
| and QUdpSocket::errorString() can provide more specific information. |
| \value RemoteClosedConnectionError TLS shutdown alert message was received. |
| \value PeerVerificationError Peer's identity could not be verified during the |
| TLS handshake. |
| \value TlsInitializationError An error occurred while initializing an underlying |
| TLS backend. |
| \value TlsFatalError A fatal error occurred during TLS handshake, other |
| than peer verification error or TLS initialization error. |
| \value TlsNonFatalError A failure to encrypt or decrypt a datagram, non-fatal, |
| meaning QDtls can continue working after this error. |
| */ |
| |
| /*! |
| \class QDtls |
| \brief This class provides encryption for UDP sockets. |
| \since 5.12 |
| |
| \ingroup network |
| \ingroup ssl |
| \inmodule QtNetwork |
| |
| The QDtls class can be used to establish a secure connection with a network |
| peer using User Datagram Protocol (UDP). DTLS connection over essentially |
| connectionless UDP means that two peers first have to successfully complete |
| a TLS handshake by calling doHandshake(). After the handshake has completed, |
| encrypted datagrams can be sent to the peer using writeDatagramEncrypted(). |
| Encrypted datagrams coming from the peer can be decrypted by decryptDatagram(). |
| |
| QDtls is designed to work with QUdpSocket. Since QUdpSocket can receive |
| datagrams coming from different peers, an application must implement |
| demultiplexing, forwarding datagrams coming from different peers to their |
| corresponding instances of QDtls. An association between a network peer |
| and its QDtls object can be established using the peer's address and port |
| number. Before starting a handshake, the application must set the peer's |
| address and port number using setPeer(). |
| |
| QDtls does not read datagrams from QUdpSocket, this is expected to be done by |
| the application, for example, in a slot attached to the QUdpSocket::readyRead() |
| signal. Then, these datagrams must be processed by QDtls. |
| |
| \note QDtls does \e not take ownership of the QUdpSocket object. |
| |
| Normally, several datagrams are to be received and sent by both peers during |
| the handshake phase. Upon reading datagrams, server and client must pass these |
| datagrams to doHandshake() until some error is found or handshakeState() |
| returns HandshakeComplete: |
| |
| \snippet code/src_network_ssl_qdtls.cpp 0 |
| |
| For a server, the first call to doHandshake() requires a non-empty datagram |
| containing a ClientHello message. If the server also deploys QDtlsClientVerifier, |
| the first ClientHello message is expected to be the one verified by QDtlsClientVerifier. |
| |
| In case the peer's identity cannot be validated during the handshake, the application |
| must inspect errors returned by peerVerificationErrors() and then either |
| ignore errors by calling ignoreVerificationErrors() or abort the handshake |
| by calling abortHandshake(). If errors were ignored, the handshake can be |
| resumed by calling resumeHandshake(). |
| |
| After the handshake has been completed, datagrams can be sent to and received |
| from the network peer securely: |
| |
| \snippet code/src_network_ssl_qdtls.cpp 2 |
| |
| A DTLS connection may be closed using shutdown(). |
| |
| \snippet code/src_network_ssl_qdtls.cpp 3 |
| |
| \warning It's recommended to call shutdown() before destroying the client's QDtls |
| object if you are planning to re-use the same port number to connect to the |
| server later. Otherwise, the server may drop incoming ClientHello messages, |
| see \l{https://tools.ietf.org/html/rfc6347#page-25}{RFC 6347, section 4.2.8} |
| for more details and implementation hints. |
| |
| If the server does not use QDtlsClientVerifier, it \e must configure its |
| QDtls objects to disable the cookie verification procedure: |
| |
| \snippet code/src_network_ssl_qdtls.cpp 4 |
| |
| A server that uses cookie verification with non-default generator parameters |
| \e must set the same parameters for its QDtls object before starting the handshake. |
| |
| \note The DTLS protocol leaves Path Maximum Transmission Unit (PMTU) discovery |
| to the application. The application may provide QDtls with the MTU using |
| setMtuHint(). This hint affects only the handshake phase, since only handshake |
| messages can be fragmented and reassembled by the DTLS. All other messages sent |
| by the application must fit into a single datagram. |
| \note DTLS-specific headers add some overhead to application data further |
| reducing the possible message size. |
| \warning A server configured to reply with HelloVerifyRequest will drop |
| all fragmented ClientHello messages, never starting a handshake. |
| |
| The \l{secureudpserver}{DTLS server} and \l{secureudpclient}{DTLS client} |
| examples illustrate how to use QDtls in applications. |
| |
| \sa QUdpSocket, QDtlsClientVerifier, HandshakeState, QDtlsError, QSslConfiguration |
| */ |
| |
| /*! |
| \typedef QDtls::GeneratorParameters |
| |
| This is a synonym for QDtlsClientVerifier::GeneratorParameters. |
| */ |
| |
| /*! |
| \fn void QDtls::handshakeTimeout() |
| |
| Packet loss can result in timeouts during the handshake phase. In this case |
| QDtls emits a handshakeTimeout() signal. Call handleTimeout() to retransmit |
| the handshake messages: |
| |
| \snippet code/src_network_ssl_qdtls.cpp 1 |
| |
| \sa handleTimeout() |
| */ |
| |
| /*! |
| \fn void QDtls::pskRequired(QSslPreSharedKeyAuthenticator *authenticator) |
| |
| QDtls 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 TLS 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 QDtls and must not be deleted |
| by the application. |
| |
| \sa QSslPreSharedKeyAuthenticator |
| */ |
| |
| /*! |
| \enum QDtls::HandshakeState |
| \brief Describes the current state of DTLS handshake. |
| \since 5.12 |
| |
| \ingroup network |
| \ingroup ssl |
| \inmodule QtNetwork |
| |
| This enum describes the current state of DTLS handshake for a QDtls |
| connection. |
| |
| \value HandshakeNotStarted Nothing done yet. |
| \value HandshakeInProgress Handshake was initiated and no errors were found so far. |
| \value PeerVerificationFailed The identity of the peer can't be established. |
| \value HandshakeComplete Handshake completed successfully and encrypted connection |
| was established. |
| |
| \sa QDtls::doHandshake(), QDtls::handshakeState() |
| */ |
| |
| |
| QT_BEGIN_NAMESPACE |
| |
| QSslConfiguration QDtlsBasePrivate::configuration() const |
| { |
| auto copyPrivate = new QSslConfigurationPrivate(dtlsConfiguration); |
| copyPrivate->ref.storeRelaxed(0); // the QSslConfiguration constructor refs up |
| QSslConfiguration copy(copyPrivate); |
| copyPrivate->sessionCipher = sessionCipher; |
| copyPrivate->sessionProtocol = sessionProtocol; |
| |
| return copy; |
| } |
| |
| void QDtlsBasePrivate::setConfiguration(const QSslConfiguration &configuration) |
| { |
| dtlsConfiguration.localCertificateChain = configuration.localCertificateChain(); |
| dtlsConfiguration.privateKey = configuration.privateKey(); |
| dtlsConfiguration.ciphers = configuration.ciphers(); |
| dtlsConfiguration.ellipticCurves = configuration.ellipticCurves(); |
| dtlsConfiguration.preSharedKeyIdentityHint = configuration.preSharedKeyIdentityHint(); |
| dtlsConfiguration.dhParams = configuration.diffieHellmanParameters(); |
| dtlsConfiguration.caCertificates = configuration.caCertificates(); |
| dtlsConfiguration.peerVerifyDepth = configuration.peerVerifyDepth(); |
| dtlsConfiguration.peerVerifyMode = configuration.peerVerifyMode(); |
| dtlsConfiguration.protocol = configuration.protocol(); |
| dtlsConfiguration.sslOptions = configuration.d->sslOptions; |
| dtlsConfiguration.sslSession = configuration.sessionTicket(); |
| dtlsConfiguration.sslSessionTicketLifeTimeHint = configuration.sessionTicketLifeTimeHint(); |
| dtlsConfiguration.nextAllowedProtocols = configuration.allowedNextProtocols(); |
| dtlsConfiguration.nextNegotiatedProtocol = configuration.nextNegotiatedProtocol(); |
| dtlsConfiguration.nextProtocolNegotiationStatus = configuration.nextProtocolNegotiationStatus(); |
| dtlsConfiguration.dtlsCookieEnabled = configuration.dtlsCookieVerificationEnabled(); |
| dtlsConfiguration.allowRootCertOnDemandLoading = configuration.d->allowRootCertOnDemandLoading; |
| dtlsConfiguration.backendConfig = configuration.backendConfiguration(); |
| |
| clearDtlsError(); |
| } |
| |
| bool QDtlsBasePrivate::setCookieGeneratorParameters(QCryptographicHash::Algorithm alg, |
| const QByteArray &key) |
| { |
| if (!key.size()) { |
| setDtlsError(QDtlsError::InvalidInputParameters, |
| QDtls::tr("Invalid (empty) secret")); |
| return false; |
| } |
| |
| clearDtlsError(); |
| |
| hashAlgorithm = alg; |
| secret = key; |
| |
| return true; |
| } |
| |
| bool QDtlsBasePrivate::isDtlsProtocol(QSsl::SslProtocol protocol) |
| { |
| switch (protocol) { |
| case QSsl::DtlsV1_0: |
| case QSsl::DtlsV1_0OrLater: |
| case QSsl::DtlsV1_2: |
| case QSsl::DtlsV1_2OrLater: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| static QString msgUnsupportedMulticastAddress() |
| { |
| return QDtls::tr("Multicast and broadcast addresses are not supported"); |
| } |
| |
| /*! |
| Default constructs GeneratorParameters object with QCryptographicHash::Sha1 |
| as its algorithm and an empty secret. |
| |
| \sa QDtlsClientVerifier::setCookieGeneratorParameters(), |
| QDtlsClientVerifier::cookieGeneratorParameters(), |
| QDtls::setCookieGeneratorParameters(), |
| QDtls::cookieGeneratorParameters() |
| */ |
| QDtlsClientVerifier::GeneratorParameters::GeneratorParameters() |
| { |
| } |
| |
| /*! |
| Constructs GeneratorParameters object from \a algorithm and \a secret. |
| |
| \sa QDtlsClientVerifier::setCookieGeneratorParameters(), |
| QDtlsClientVerifier::cookieGeneratorParameters(), |
| QDtls::setCookieGeneratorParameters(), |
| QDtls::cookieGeneratorParameters() |
| */ |
| QDtlsClientVerifier::GeneratorParameters::GeneratorParameters(QCryptographicHash::Algorithm algorithm, const QByteArray &secret) |
| : hash(algorithm), secret(secret) |
| { |
| } |
| |
| /*! |
| Constructs a QDtlsClientVerifier object, \a parent is passed to QObject's |
| constructor. |
| */ |
| QDtlsClientVerifier::QDtlsClientVerifier(QObject *parent) |
| : QObject(*new QDtlsClientVerifierOpenSSL, parent) |
| { |
| Q_D(QDtlsClientVerifier); |
| |
| d->mode = QSslSocket::SslServerMode; |
| // The default configuration suffices: verifier never does a full |
| // handshake and upon verifying a cookie in a client hello message, |
| // it reports success. |
| auto conf = QSslConfiguration::defaultDtlsConfiguration(); |
| conf.setPeerVerifyMode(QSslSocket::VerifyNone); |
| d->setConfiguration(conf); |
| } |
| |
| /*! |
| Destroys the QDtlsClientVerifier object. |
| */ |
| QDtlsClientVerifier::~QDtlsClientVerifier() |
| { |
| } |
| |
| /*! |
| Sets the secret and the cryptographic hash algorithm from \a params. This |
| QDtlsClientVerifier will use these to generate cookies. If the new secret |
| has size zero, this function returns \c false and does not change the |
| cookie generator parameters. |
| |
| \note The secret is supposed to be a cryptographically secure sequence of bytes. |
| |
| \sa QDtlsClientVerifier::GeneratorParameters, cookieGeneratorParameters(), |
| QCryptographicHash::Algorithm |
| */ |
| bool QDtlsClientVerifier::setCookieGeneratorParameters(const GeneratorParameters ¶ms) |
| { |
| Q_D(QDtlsClientVerifier); |
| |
| return d->setCookieGeneratorParameters(params.hash, params.secret); |
| } |
| |
| /*! |
| Returns the current secret and hash algorithm used to generate cookies. |
| The default hash algorithm is QCryptographicHash::Sha256 if Qt was configured |
| to support it, QCryptographicHash::Sha1 otherwise. The default secret is |
| obtained from the backend-specific cryptographically strong pseudorandom |
| number generator. |
| |
| \sa QCryptographicHash::Algorithm, QDtlsClientVerifier::GeneratorParameters, |
| setCookieGeneratorParameters() |
| */ |
| QDtlsClientVerifier::GeneratorParameters QDtlsClientVerifier::cookieGeneratorParameters() const |
| { |
| Q_D(const QDtlsClientVerifier); |
| |
| return {d->hashAlgorithm, d->secret}; |
| } |
| |
| /*! |
| \a socket must be a valid pointer, \a dgram must be a non-empty |
| datagram, \a address cannot be null, broadcast, or multicast. |
| \a port is the remote peer's port. This function returns \c true |
| if \a dgram contains a ClientHello message with a valid cookie. |
| If no matching cookie is found, verifyClient() will send a |
| HelloVerifyRequest message using \a socket and return \c false. |
| |
| The following snippet shows how a server application may check for errors: |
| |
| \snippet code/src_network_ssl_qdtlscookie.cpp 2 |
| |
| \sa QHostAddress::isNull(), QHostAddress::isBroadcast(), QHostAddress::isMulticast(), |
| setCookieGeneratorParameters(), cookieGeneratorParameters() |
| */ |
| bool QDtlsClientVerifier::verifyClient(QUdpSocket *socket, const QByteArray &dgram, |
| const QHostAddress &address, quint16 port) |
| { |
| Q_D(QDtlsClientVerifier); |
| |
| if (!socket || address.isNull() || !dgram.size()) { |
| d->setDtlsError(QDtlsError::InvalidInputParameters, |
| tr("A valid UDP socket, non-empty datagram, valid address/port were expected")); |
| return false; |
| } |
| |
| if (address.isBroadcast() || address.isMulticast()) { |
| d->setDtlsError(QDtlsError::InvalidInputParameters, |
| msgUnsupportedMulticastAddress()); |
| return false; |
| } |
| |
| return d->verifyClient(socket, dgram, address, port); |
| } |
| |
| /*! |
| Convenience function. Returns the last ClientHello message that was successfully |
| verified, or an empty QByteArray if no verification has completed. |
| |
| \sa verifyClient() |
| */ |
| QByteArray QDtlsClientVerifier::verifiedHello() const |
| { |
| Q_D(const QDtlsClientVerifier); |
| |
| return d->verifiedClientHello; |
| } |
| |
| /*! |
| Returns the last error that occurred or QDtlsError::NoError. |
| |
| \sa QDtlsError, dtlsErrorString() |
| */ |
| QDtlsError QDtlsClientVerifier::dtlsError() const |
| { |
| Q_D(const QDtlsClientVerifier); |
| |
| return d->errorCode; |
| } |
| |
| /*! |
| Returns a textual description of the last error, or an empty string. |
| |
| \sa dtlsError() |
| */ |
| QString QDtlsClientVerifier::dtlsErrorString() const |
| { |
| Q_D(const QDtlsBase); |
| |
| return d->errorDescription; |
| } |
| |
| /*! |
| Creates a QDtls object, \a parent is passed to the QObject constructor. |
| \a mode is QSslSocket::SslServerMode for a server-side DTLS connection or |
| QSslSocket::SslClientMode for a client. |
| |
| \sa sslMode(), QSslSocket::SslMode |
| */ |
| QDtls::QDtls(QSslSocket::SslMode mode, QObject *parent) |
| : QObject(*new QDtlsPrivateOpenSSL, parent) |
| { |
| Q_D(QDtls); |
| |
| d->mode = mode; |
| setDtlsConfiguration(QSslConfiguration::defaultDtlsConfiguration()); |
| } |
| |
| /*! |
| Destroys the QDtls object. |
| */ |
| QDtls::~QDtls() |
| { |
| } |
| |
| /*! |
| Sets the peer's address, \a port, and host name and returns \c true |
| if successful. \a address must not be null, multicast, or broadcast. |
| \a verificationName is the host name used for the certificate validation. |
| |
| \sa peerAddress(), peerPort(), peerVerificationName() |
| */ |
| bool QDtls::setPeer(const QHostAddress &address, quint16 port, |
| const QString &verificationName) |
| { |
| Q_D(QDtls); |
| |
| if (d->handshakeState != HandshakeNotStarted) { |
| d->setDtlsError(QDtlsError::InvalidOperation, |
| tr("Cannot set peer after handshake started")); |
| return false; |
| } |
| |
| if (address.isNull()) { |
| d->setDtlsError(QDtlsError::InvalidInputParameters, |
| tr("Invalid address")); |
| return false; |
| } |
| |
| if (address.isBroadcast() || address.isMulticast()) { |
| d->setDtlsError(QDtlsError::InvalidInputParameters, |
| msgUnsupportedMulticastAddress()); |
| return false; |
| } |
| |
| d->clearDtlsError(); |
| |
| d->remoteAddress = address; |
| d->remotePort = port; |
| d->peerVerificationName = verificationName; |
| |
| return true; |
| } |
| |
| /*! |
| Sets the host \a name that will be used for the certificate validation |
| and returns \c true if successful. |
| |
| \note This function must be called before the handshake starts. |
| |
| \sa peerVerificationName(), setPeer() |
| */ |
| bool QDtls::setPeerVerificationName(const QString &name) |
| { |
| Q_D(QDtls); |
| |
| if (d->handshakeState != HandshakeNotStarted) { |
| d->setDtlsError(QDtlsError::InvalidOperation, |
| tr("Cannot set verification name after handshake started")); |
| return false; |
| } |
| |
| d->clearDtlsError(); |
| d->peerVerificationName = name; |
| |
| return true; |
| } |
| |
| /*! |
| Returns the peer's address, set by setPeer(), or QHostAddress::Null. |
| |
| \sa setPeer() |
| */ |
| QHostAddress QDtls::peerAddress() const |
| { |
| Q_D(const QDtls); |
| |
| return d->remoteAddress; |
| } |
| |
| /*! |
| Returns the peer's port number, set by setPeer(), or 0. |
| |
| \sa setPeer() |
| */ |
| quint16 QDtls::peerPort() const |
| { |
| Q_D(const QDtlsBase); |
| |
| return d->remotePort; |
| } |
| |
| /*! |
| Returns the host name set by setPeer() or setPeerVerificationName(). |
| The default value is an empty string. |
| |
| \sa setPeerVerificationName(), setPeer() |
| */ |
| QString QDtls::peerVerificationName() const |
| { |
| Q_D(const QDtls); |
| |
| return d->peerVerificationName; |
| } |
| |
| /*! |
| Returns QSslSocket::SslServerMode for a server-side connection and |
| QSslSocket::SslClientMode for a client. |
| |
| \sa QDtls(), QSslSocket::SslMode |
| */ |
| QSslSocket::SslMode QDtls::sslMode() const |
| { |
| Q_D(const QDtls); |
| |
| return d->mode; |
| } |
| |
| /*! |
| \a mtuHint is the maximum transmission unit (MTU), either discovered or guessed |
| by the application. The application is not required to set this value. |
| |
| \sa mtuHint(), QAbstractSocket::PathMtuSocketOption |
| */ |
| void QDtls::setMtuHint(quint16 mtuHint) |
| { |
| Q_D(QDtls); |
| |
| d->mtuHint = mtuHint; |
| } |
| |
| /*! |
| Returns the value previously set by setMtuHint(). The default value is 0. |
| |
| \sa setMtuHint() |
| */ |
| quint16 QDtls::mtuHint() const |
| { |
| Q_D(const QDtls); |
| |
| return d->mtuHint; |
| } |
| |
| /*! |
| Sets the cryptographic hash algorithm and the secret from \a params. |
| This function is only needed for a server-side QDtls connection. |
| Returns \c true if successful. |
| |
| \note This function must be called before the handshake starts. |
| |
| \sa cookieGeneratorParameters(), doHandshake(), QDtlsClientVerifier, |
| QDtlsClientVerifier::cookieGeneratorParameters() |
| */ |
| bool QDtls::setCookieGeneratorParameters(const GeneratorParameters ¶ms) |
| { |
| Q_D(QDtls); |
| |
| return d->setCookieGeneratorParameters(params.hash, params.secret); |
| } |
| |
| /*! |
| Returns the current hash algorithm and secret, either default ones or previously |
| set by a call to setCookieGeneratorParameters(). |
| |
| The default hash algorithm is QCryptographicHash::Sha256 if Qt was |
| configured to support it, QCryptographicHash::Sha1 otherwise. The default |
| secret is obtained from the backend-specific cryptographically strong |
| pseudorandom number generator. |
| |
| \sa QDtlsClientVerifier, cookieGeneratorParameters() |
| */ |
| QDtls::GeneratorParameters QDtls::cookieGeneratorParameters() const |
| { |
| Q_D(const QDtls); |
| |
| return {d->hashAlgorithm, d->secret}; |
| } |
| |
| /*! |
| Sets the connection's TLS configuration from \a configuration |
| and returns \c true if successful. |
| |
| \note This function must be called before the handshake starts. |
| |
| \sa dtlsConfiguration(), doHandshake() |
| */ |
| bool QDtls::setDtlsConfiguration(const QSslConfiguration &configuration) |
| { |
| Q_D(QDtls); |
| |
| if (d->handshakeState != HandshakeNotStarted) { |
| d->setDtlsError(QDtlsError::InvalidOperation, |
| tr("Cannot set configuration after handshake started")); |
| return false; |
| } |
| |
| d->setConfiguration(configuration); |
| return true; |
| } |
| |
| /*! |
| Returns either the default DTLS configuration or the configuration set by an |
| earlier call to setDtlsConfiguration(). |
| |
| \sa setDtlsConfiguration(), QSslConfiguration::defaultDtlsConfiguration() |
| */ |
| QSslConfiguration QDtls::dtlsConfiguration() const |
| { |
| Q_D(const QDtls); |
| |
| return d->configuration(); |
| } |
| |
| /*! |
| Returns the current handshake state for this QDtls. |
| |
| \sa doHandshake(), QDtls::HandshakeState |
| */ |
| QDtls::HandshakeState QDtls::handshakeState()const |
| { |
| Q_D(const QDtls); |
| |
| return d->handshakeState; |
| } |
| |
| /*! |
| Starts or continues a DTLS handshake. \a socket must be a valid pointer. |
| When starting a server-side DTLS handshake, \a dgram must contain the initial |
| ClientHello message read from QUdpSocket. This function returns \c true if |
| no error was found. Handshake state can be tested using handshakeState(). |
| \c false return means some error occurred, use dtlsError() for more |
| detailed information. |
| |
| \note If the identity of the peer can't be established, the error is set to |
| QDtlsError::PeerVerificationError. If you want to ignore verification errors |
| and continue connecting, you must call ignoreVerificationErrors() and then |
| resumeHandshake(). If the errors cannot be ignored, you must call |
| abortHandshake(). |
| |
| \snippet code/src_network_ssl_qdtls.cpp 5 |
| |
| \sa handshakeState(), dtlsError(), ignoreVerificationErrors(), resumeHandshake(), |
| abortHandshake() |
| */ |
| bool QDtls::doHandshake(QUdpSocket *socket, const QByteArray &dgram) |
| { |
| Q_D(QDtls); |
| |
| if (d->handshakeState == HandshakeNotStarted) |
| return startHandshake(socket, dgram); |
| else if (d->handshakeState == HandshakeInProgress) |
| return continueHandshake(socket, dgram); |
| |
| d->setDtlsError(QDtlsError::InvalidOperation, |
| tr("Cannot start/continue handshake, invalid handshake state")); |
| return false; |
| } |
| |
| /*! |
| \internal |
| */ |
| bool QDtls::startHandshake(QUdpSocket *socket, const QByteArray &datagram) |
| { |
| Q_D(QDtls); |
| |
| if (!socket) { |
| d->setDtlsError(QDtlsError::InvalidInputParameters, tr("Invalid (nullptr) socket")); |
| return false; |
| } |
| |
| if (d->remoteAddress.isNull()) { |
| d->setDtlsError(QDtlsError::InvalidOperation, |
| tr("To start a handshake you must set peer's address and port first")); |
| return false; |
| } |
| |
| if (sslMode() == QSslSocket::SslServerMode && !datagram.size()) { |
| d->setDtlsError(QDtlsError::InvalidInputParameters, |
| tr("To start a handshake, DTLS server requires non-empty datagram (client hello)")); |
| return false; |
| } |
| |
| if (d->handshakeState != HandshakeNotStarted) { |
| d->setDtlsError(QDtlsError::InvalidOperation, |
| tr("Cannot start handshake, already done/in progress")); |
| return false; |
| } |
| |
| return d->startHandshake(socket, datagram); |
| } |
| |
| /*! |
| If a timeout occures during the handshake, the handshakeTimeout() signal |
| is emitted. The application must call handleTimeout() to retransmit handshake |
| messages; handleTimeout() returns \c true if a timeout has occurred, false |
| otherwise. \a socket must be a valid pointer. |
| |
| \sa handshakeTimeout() |
| */ |
| bool QDtls::handleTimeout(QUdpSocket *socket) |
| { |
| Q_D(QDtls); |
| |
| if (!socket) { |
| d->setDtlsError(QDtlsError::InvalidInputParameters, tr("Invalid (nullptr) socket")); |
| return false; |
| } |
| |
| return d->handleTimeout(socket); |
| } |
| |
| /*! |
| \internal |
| */ |
| bool QDtls::continueHandshake(QUdpSocket *socket, const QByteArray &datagram) |
| { |
| Q_D(QDtls); |
| |
| if (!socket || !datagram.size()) { |
| d->setDtlsError(QDtlsError::InvalidInputParameters, |
| tr("A valid QUdpSocket and non-empty datagram are needed to continue the handshake")); |
| return false; |
| } |
| |
| if (d->handshakeState != HandshakeInProgress) { |
| d->setDtlsError(QDtlsError::InvalidOperation, |
| tr("Cannot continue handshake, not in InProgress state")); |
| return false; |
| } |
| |
| return d->continueHandshake(socket, datagram); |
| } |
| |
| /*! |
| If peer verification errors were ignored during the handshake, |
| resumeHandshake() resumes and completes the handshake and returns |
| \c true. \a socket must be a valid pointer. Returns \c false if |
| the handshake could not be resumed. |
| |
| \sa doHandshake(), abortHandshake() peerVerificationErrors(), ignoreVerificationErrors() |
| */ |
| bool QDtls::resumeHandshake(QUdpSocket *socket) |
| { |
| Q_D(QDtls); |
| |
| if (!socket) { |
| d->setDtlsError(QDtlsError::InvalidInputParameters, tr("Invalid (nullptr) socket")); |
| return false; |
| } |
| |
| if (d->handshakeState != PeerVerificationFailed) { |
| d->setDtlsError(QDtlsError::InvalidOperation, |
| tr("Cannot resume, not in VerificationError state")); |
| return false; |
| } |
| |
| return d->resumeHandshake(socket); |
| } |
| |
| /*! |
| Aborts the ongoing handshake. Returns true if one was on-going on \a socket; |
| otherwise, sets a suitable error and returns false. |
| |
| \sa doHandshake(), resumeHandshake() |
| */ |
| bool QDtls::abortHandshake(QUdpSocket *socket) |
| { |
| Q_D(QDtls); |
| |
| if (!socket) { |
| d->setDtlsError(QDtlsError::InvalidInputParameters, tr("Invalid (nullptr) socket")); |
| return false; |
| } |
| |
| if (d->handshakeState != PeerVerificationFailed && d->handshakeState != HandshakeInProgress) { |
| d->setDtlsError(QDtlsError::InvalidOperation, |
| tr("No handshake in progress, nothing to abort")); |
| return false; |
| } |
| |
| d->abortHandshake(socket); |
| return true; |
| } |
| |
| /*! |
| Sends an encrypted shutdown alert message and closes the DTLS connection. |
| Handshake state changes to QDtls::HandshakeNotStarted. \a socket must be a |
| valid pointer. This function returns \c true on success. |
| |
| \sa doHandshake() |
| */ |
| bool QDtls::shutdown(QUdpSocket *socket) |
| { |
| Q_D(QDtls); |
| |
| if (!socket) { |
| d->setDtlsError(QDtlsError::InvalidInputParameters, |
| tr("Invalid (nullptr) socket")); |
| return false; |
| } |
| |
| if (!d->connectionEncrypted) { |
| d->setDtlsError(QDtlsError::InvalidOperation, |
| tr("Cannot send shutdown alert, not encrypted")); |
| return false; |
| } |
| |
| d->sendShutdownAlert(socket); |
| return true; |
| } |
| |
| /*! |
| Returns \c true if DTLS handshake completed successfully. |
| |
| \sa doHandshake(), handshakeState() |
| */ |
| bool QDtls::isConnectionEncrypted() const |
| { |
| Q_D(const QDtls); |
| |
| return d->connectionEncrypted; |
| } |
| |
| /*! |
| Returns the cryptographic \l {QSslCipher} {cipher} used by this connection, |
| or a null cipher if the connection isn't encrypted. The cipher for the |
| session is selected during the handshake phase. The cipher is used to encrypt |
| and decrypt data. |
| |
| QSslConfiguration provides functions for setting the ordered list of ciphers |
| from which the handshake phase will eventually select the session cipher. |
| This ordered list must be in place before the handshake phase begins. |
| |
| \sa QSslConfiguration, setDtlsConfiguration(), dtlsConfiguration() |
| */ |
| QSslCipher QDtls::sessionCipher() const |
| { |
| Q_D(const QDtls); |
| |
| return d->sessionCipher; |
| } |
| |
| /*! |
| Returns the DTLS protocol version used by this connection, or UnknownProtocol |
| if the connection isn't encrypted yet. The protocol for the connection is selected |
| during the handshake phase. |
| |
| setDtlsConfiguration() can set the preferred version before the handshake starts. |
| |
| \sa setDtlsConfiguration(), QSslConfiguration, QSslConfiguration::defaultDtlsConfiguration(), |
| QSslConfiguration::setProtocol() |
| */ |
| QSsl::SslProtocol QDtls::sessionProtocol() const |
| { |
| Q_D(const QDtls); |
| |
| return d->sessionProtocol; |
| } |
| |
| /*! |
| Encrypts \a dgram and writes the encrypted data into \a socket. Returns the |
| number of bytes written, or -1 in case of error. The handshake must be completed |
| before writing encrypted data. \a socket must be a valid |
| pointer. |
| |
| \sa doHandshake(), handshakeState(), isConnectionEncrypted(), dtlsError() |
| */ |
| qint64 QDtls::writeDatagramEncrypted(QUdpSocket *socket, const QByteArray &dgram) |
| { |
| Q_D(QDtls); |
| |
| if (!socket) { |
| d->setDtlsError(QDtlsError::InvalidInputParameters, tr("Invalid (nullptr) socket")); |
| return -1; |
| } |
| |
| if (!isConnectionEncrypted()) { |
| d->setDtlsError(QDtlsError::InvalidOperation, |
| tr("Cannot write a datagram, not in encrypted state")); |
| return -1; |
| } |
| |
| return d->writeDatagramEncrypted(socket, dgram); |
| } |
| |
| /*! |
| Decrypts \a dgram and returns its contents as plain text. The handshake must |
| be completed before datagrams can be decrypted. Depending on the type of the |
| TLS message the connection may write into \a socket, which must be a valid |
| pointer. |
| */ |
| QByteArray QDtls::decryptDatagram(QUdpSocket *socket, const QByteArray &dgram) |
| { |
| Q_D(QDtls); |
| |
| if (!socket) { |
| d->setDtlsError(QDtlsError::InvalidInputParameters, tr("Invalid (nullptr) socket")); |
| return {}; |
| } |
| |
| if (!isConnectionEncrypted()) { |
| d->setDtlsError(QDtlsError::InvalidOperation, |
| tr("Cannot read a datagram, not in encrypted state")); |
| return {}; |
| } |
| |
| if (!dgram.size()) |
| return {}; |
| |
| return d->decryptDatagram(socket, dgram); |
| } |
| |
| /*! |
| Returns the last error encountered by the connection or QDtlsError::NoError. |
| |
| \sa dtlsErrorString(), QDtlsError |
| */ |
| QDtlsError QDtls::dtlsError() const |
| { |
| Q_D(const QDtls); |
| |
| return d->errorCode; |
| } |
| |
| /*! |
| Returns a textual description for the last error encountered by the connection |
| or empty string. |
| |
| \sa dtlsError() |
| */ |
| QString QDtls::dtlsErrorString() const |
| { |
| Q_D(const QDtls); |
| |
| return d->errorDescription; |
| } |
| |
| /*! |
| Returns errors found while establishing the identity of the peer. |
| |
| If you want to continue connecting despite the errors that have occurred, |
| you must call ignoreVerificationErrors(). |
| */ |
| QVector<QSslError> QDtls::peerVerificationErrors() const |
| { |
| Q_D(const QDtls); |
| |
| return d->tlsErrors; |
| } |
| |
| /*! |
| This method tells QDtls to ignore only the errors given in \a errorsToIgnore. |
| |
| If, for instance, you want to connect to a server that uses a self-signed |
| certificate, consider the following snippet: |
| |
| \snippet code/src_network_ssl_qdtls.cpp 6 |
| |
| You can also call this function after doHandshake() encountered the |
| QDtlsError::PeerVerificationError error, and then resume the handshake by |
| calling resumeHandshake(). |
| |
| Later calls to this function will replace the list of errors that were |
| passed in previous calls. You can clear the list of errors you want to ignore |
| by calling this function with an empty list. |
| |
| \sa doHandshake(), resumeHandshake(), QSslError |
| */ |
| void QDtls::ignoreVerificationErrors(const QVector<QSslError> &errorsToIgnore) |
| { |
| Q_D(QDtls); |
| |
| d->tlsErrorsToIgnore = errorsToIgnore; |
| } |
| |
| QT_END_NAMESPACE |