| 1 | /**************************************************************************** | 
| 2 | ** | 
| 3 | ** Copyright (C) 2018 The Qt Company Ltd. | 
| 4 | ** Contact: https://www.qt.io/licensing/ | 
| 5 | ** | 
| 6 | ** This file is part of the test suite of the Qt Toolkit. | 
| 7 | ** | 
| 8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ | 
| 9 | ** Commercial License Usage | 
| 10 | ** Licensees holding valid commercial Qt licenses may use this file in | 
| 11 | ** accordance with the commercial license agreement provided with the | 
| 12 | ** Software or, alternatively, in accordance with the terms contained in | 
| 13 | ** a written agreement between you and The Qt Company. For licensing terms | 
| 14 | ** and conditions see https://www.qt.io/terms-conditions. For further | 
| 15 | ** information use the contact form at https://www.qt.io/contact-us. | 
| 16 | ** | 
| 17 | ** GNU General Public License Usage | 
| 18 | ** Alternatively, this file may be used under the terms of the GNU | 
| 19 | ** General Public License version 3 as published by the Free Software | 
| 20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT | 
| 21 | ** included in the packaging of this file. Please review the following | 
| 22 | ** information to ensure the GNU General Public License requirements will | 
| 23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. | 
| 24 | ** | 
| 25 | ** $QT_END_LICENSE$ | 
| 26 | ** | 
| 27 | ****************************************************************************/ | 
| 28 |  | 
| 29 | #include <QtTest/QtTest> | 
| 30 |  | 
| 31 | #include <QtNetwork/qsslpresharedkeyauthenticator.h> | 
| 32 | #include <QtNetwork/qsslconfiguration.h> | 
| 33 | #include <QtNetwork/qhostaddress.h> | 
| 34 | #include <QtNetwork/qsslsocket.h> | 
| 35 | #include <QtNetwork/qsslcipher.h> | 
| 36 | #include <QtNetwork/qudpsocket.h> | 
| 37 | #include <QtNetwork/qsslerror.h> | 
| 38 | #include <QtNetwork/qsslkey.h> | 
| 39 | #include <QtNetwork/qdtls.h> | 
| 40 | #include <QtNetwork/qssl.h> | 
| 41 |  | 
| 42 | #include <QtCore/qcryptographichash.h> | 
| 43 | #include <QtCore/qscopeguard.h> | 
| 44 | #include <QtCore/qbytearray.h> | 
| 45 | #include <QtCore/qvector.h> | 
| 46 | #include <QtCore/qobject.h> | 
| 47 | #include <QtCore/qstring.h> | 
| 48 | #include <QtCore/qobject.h> | 
| 49 | #include <QtCore/qlist.h> | 
| 50 |  | 
| 51 | #include <algorithm> | 
| 52 |  | 
| 53 | QT_BEGIN_NAMESPACE | 
| 54 |  | 
| 55 | namespace | 
| 56 | { | 
| 57 |  | 
| 58 | bool dtlsErrorIsCleared(const QDtls &dtls) | 
| 59 | { | 
| 60 |     return dtls.dtlsError() == QDtlsError::NoError && dtls.dtlsErrorString().isEmpty(); | 
| 61 | } | 
| 62 |  | 
| 63 | using DtlsPtr = QScopedPointer<QDtls>; | 
| 64 |  | 
| 65 | bool dtlsErrorIsCleared(DtlsPtr &dtls) | 
| 66 | { | 
| 67 |     return dtlsErrorIsCleared(dtls: *dtls); | 
| 68 | } | 
| 69 |  | 
| 70 | } // unnamed namespace | 
| 71 |  | 
| 72 | #define QDTLS_VERIFY_NO_ERROR(obj) QVERIFY(dtlsErrorIsCleared(obj)) | 
| 73 |  | 
| 74 | #define QDTLS_VERIFY_HANDSHAKE_SUCCESS(obj) \ | 
| 75 |     QVERIFY(obj->isConnectionEncrypted()); \ | 
| 76 |     QCOMPARE(obj->handshakeState(), QDtls::HandshakeComplete); \ | 
| 77 |     QDTLS_VERIFY_NO_ERROR(obj); \ | 
| 78 |     QCOMPARE(obj->peerVerificationErrors().size(), 0) | 
| 79 |  | 
| 80 | class tst_QDtls : public QObject | 
| 81 | { | 
| 82 |     Q_OBJECT | 
| 83 |  | 
| 84 | public slots: | 
| 85 |     void initTestCase(); | 
| 86 |     void init(); | 
| 87 |  | 
| 88 | private slots: | 
| 89 |     // Tests: | 
| 90 |     void construction_data(); | 
| 91 |     void construction(); | 
| 92 |     void configuration_data(); | 
| 93 |     void configuration(); | 
| 94 |     void invalidConfiguration(); | 
| 95 |     void setPeer_data(); | 
| 96 |     void setPeer(); | 
| 97 |     void handshake_data(); | 
| 98 |     void handshake(); | 
| 99 |     void handshakeWithRetransmission(); | 
| 100 |     void sessionCipher(); | 
| 101 |     void cipherPreferences_data(); | 
| 102 |     void cipherPreferences(); | 
| 103 |     void protocolVersionMatching_data(); | 
| 104 |     void protocolVersionMatching(); | 
| 105 |     void verificationErrors_data(); | 
| 106 |     void verificationErrors(); | 
| 107 |     void presetExpectedErrors_data(); | 
| 108 |     void presetExpectedErrors(); | 
| 109 |     void verifyServerCertificate_data(); | 
| 110 |     void verifyServerCertificate(); | 
| 111 |     void verifyClientCertificate_data(); | 
| 112 |     void verifyClientCertificate(); | 
| 113 |     void blacklistedCerificate(); | 
| 114 |     void readWriteEncrypted_data(); | 
| 115 |     void readWriteEncrypted(); | 
| 116 |     void datagramFragmentation(); | 
| 117 |  | 
| 118 | protected slots: | 
| 119 |     void handshakeReadyRead(); | 
| 120 |     void encryptedReadyRead(); | 
| 121 |     void pskRequested(QSslPreSharedKeyAuthenticator *auth); | 
| 122 |     void handleHandshakeTimeout(); | 
| 123 |  | 
| 124 | private: | 
| 125 |     void clientServerData(); | 
| 126 |     void connectHandshakeReadingSlots(); | 
| 127 |     void connectEncryptedReadingSlots(); | 
| 128 |     bool verificationErrorDetected(QSslError::SslError code) const; | 
| 129 |  | 
| 130 |     static QHostAddress toNonAny(const QHostAddress &addr); | 
| 131 |  | 
| 132 |     QUdpSocket serverSocket; | 
| 133 |     QHostAddress serverAddress; | 
| 134 |     quint16 serverPort = 0; | 
| 135 |     QSslConfiguration defaultServerConfig; | 
| 136 |     QSslCertificate selfSignedCert; | 
| 137 |     QString hostName; | 
| 138 |     QSslKey serverKeySS; | 
| 139 |     bool serverDropDgram = false; | 
| 140 |     const QByteArray serverExpectedPlainText = "Hello W ... hmm, I mean DTLS server!" ; | 
| 141 |     QByteArray serverReceivedPlainText; | 
| 142 |  | 
| 143 |     QUdpSocket clientSocket; | 
| 144 |     QHostAddress clientAddress; | 
| 145 |     quint16 clientPort = 0; | 
| 146 |     bool clientDropDgram = false; | 
| 147 |     const QByteArray clientExpectedPlainText = "Hello DTLS client." ; | 
| 148 |     QByteArray clientReceivedPlainText; | 
| 149 |  | 
| 150 |     DtlsPtr serverCrypto; | 
| 151 |     DtlsPtr clientCrypto; | 
| 152 |  | 
| 153 |     QTestEventLoop testLoop; | 
| 154 |     const int handshakeTimeoutMS = 5000; | 
| 155 |     const int dataExchangeTimeoutMS = 1000; | 
| 156 |  | 
| 157 |     const QByteArray presharedKey = "DEADBEEFDEADBEEF" ; | 
| 158 |     QString certDirPath; | 
| 159 | }; | 
| 160 |  | 
| 161 | QT_END_NAMESPACE | 
| 162 |  | 
| 163 | Q_DECLARE_METATYPE(QSsl::SslProtocol) | 
| 164 | Q_DECLARE_METATYPE(QSslSocket::SslMode) | 
| 165 | Q_DECLARE_METATYPE(QSslSocket::PeerVerifyMode) | 
| 166 | Q_DECLARE_METATYPE(QList<QSslCertificate>) | 
| 167 | Q_DECLARE_METATYPE(QSslKey) | 
| 168 | Q_DECLARE_METATYPE(QVector<QSslError>) | 
| 169 |  | 
| 170 | QT_BEGIN_NAMESPACE | 
| 171 |  | 
| 172 | void tst_QDtls::initTestCase() | 
| 173 | { | 
| 174 |     certDirPath = QFileInfo(QFINDTESTDATA("certs" )).absolutePath(); | 
| 175 |     QVERIFY(certDirPath.size() > 0); | 
| 176 |     certDirPath += QDir::separator() + QStringLiteral("certs" ) + QDir::separator(); | 
| 177 |  | 
| 178 |     QVERIFY(QSslSocket::supportsSsl()); | 
| 179 |  | 
| 180 |     QFile keyFile(certDirPath + QStringLiteral("ss-srv-key.pem" )); | 
| 181 |     QVERIFY(keyFile.open(QIODevice::ReadOnly)); | 
| 182 |     serverKeySS = QSslKey(keyFile.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, "foobar" ); | 
| 183 |     QVERIFY(!serverKeySS.isNull()); | 
| 184 |  | 
| 185 |     QList<QSslCertificate> certificates = QSslCertificate::fromPath(path: certDirPath + QStringLiteral("ss-srv-cert.pem" )); | 
| 186 |     QVERIFY(!certificates.isEmpty()); | 
| 187 |     QVERIFY(!certificates.first().isNull()); | 
| 188 |     selfSignedCert = certificates.first(); | 
| 189 |  | 
| 190 |     defaultServerConfig = QSslConfiguration::defaultDtlsConfiguration(); | 
| 191 |     defaultServerConfig.setPeerVerifyMode(QSslSocket::VerifyNone); | 
| 192 |     defaultServerConfig.setDtlsCookieVerificationEnabled(false); | 
| 193 |  | 
| 194 |     hostName = QStringLiteral("bob.org" ); | 
| 195 |  | 
| 196 |     void qt_ForceTlsSecurityLevel(); | 
| 197 |     qt_ForceTlsSecurityLevel(); | 
| 198 | } | 
| 199 |  | 
| 200 | void tst_QDtls::init() | 
| 201 | { | 
| 202 |     if (serverSocket.state() != QAbstractSocket::UnconnectedState) { | 
| 203 |         serverSocket.close(); | 
| 204 |         // disconnect signals/slots: | 
| 205 |         serverSocket.disconnect(); | 
| 206 |     } | 
| 207 |  | 
| 208 |     QVERIFY(serverSocket.bind()); | 
| 209 |     serverAddress = toNonAny(addr: serverSocket.localAddress()); | 
| 210 |     serverPort = serverSocket.localPort(); | 
| 211 |  | 
| 212 |     if (clientSocket.localPort()) { | 
| 213 |         clientSocket.close(); | 
| 214 |         // disconnect signals/slots: | 
| 215 |         clientSocket.disconnect(); | 
| 216 |     } | 
| 217 |  | 
| 218 |     clientAddress = {}; | 
| 219 |     clientPort = 0; | 
| 220 |  | 
| 221 |     serverCrypto.reset(other: new QDtls(QSslSocket::SslServerMode)); | 
| 222 |     serverDropDgram = false; | 
| 223 |     serverReceivedPlainText.clear(); | 
| 224 |  | 
| 225 |     clientCrypto.reset(other: new QDtls(QSslSocket::SslClientMode)); | 
| 226 |     clientDropDgram = false; | 
| 227 |     clientReceivedPlainText.clear(); | 
| 228 |  | 
| 229 |     connect(sender: clientCrypto.data(), signal: &QDtls::handshakeTimeout, | 
| 230 |             receiver: this, slot: &tst_QDtls::handleHandshakeTimeout); | 
| 231 |     connect(sender: serverCrypto.data(), signal: &QDtls::handshakeTimeout, | 
| 232 |             receiver: this, slot: &tst_QDtls::handleHandshakeTimeout); | 
| 233 | } | 
| 234 |  | 
| 235 | void tst_QDtls::construction_data() | 
| 236 | { | 
| 237 |     clientServerData(); | 
| 238 | } | 
| 239 |  | 
| 240 | void tst_QDtls::construction() | 
| 241 | { | 
| 242 |     QFETCH(const QSslSocket::SslMode, mode); | 
| 243 |  | 
| 244 |     QDtls dtls(mode); | 
| 245 |     QCOMPARE(dtls.peerAddress(), QHostAddress()); | 
| 246 |     QCOMPARE(dtls.peerPort(), quint16()); | 
| 247 |     QCOMPARE(dtls.peerVerificationName(), QString()); | 
| 248 |     QCOMPARE(dtls.sslMode(), mode); | 
| 249 |  | 
| 250 |     QCOMPARE(dtls.mtuHint(), quint16()); | 
| 251 |  | 
| 252 |     const auto params = dtls.cookieGeneratorParameters(); | 
| 253 |     QVERIFY(params.secret.size() > 0); | 
| 254 | #ifdef QT_CRYPTOGRAPHICHASH_ONLY_SHA1 | 
| 255 |     QCOMPARE(params.hash, QCryptographicHash::Sha1); | 
| 256 | #else | 
| 257 |     QCOMPARE(params.hash, QCryptographicHash::Sha256); | 
| 258 | #endif | 
| 259 |  | 
| 260 |     QCOMPARE(dtls.dtlsConfiguration(), QSslConfiguration::defaultDtlsConfiguration()); | 
| 261 |  | 
| 262 |     QCOMPARE(dtls.handshakeState(), QDtls::HandshakeNotStarted); | 
| 263 |     QCOMPARE(dtls.isConnectionEncrypted(), false); | 
| 264 |     QCOMPARE(dtls.sessionCipher(), QSslCipher()); | 
| 265 |     QCOMPARE(dtls.sessionProtocol(), QSsl::UnknownProtocol); | 
| 266 |  | 
| 267 |     QCOMPARE(dtls.dtlsError(), QDtlsError::NoError); | 
| 268 |     QCOMPARE(dtls.dtlsErrorString(), QString()); | 
| 269 |     QCOMPARE(dtls.peerVerificationErrors().size(), 0); | 
| 270 | } | 
| 271 |  | 
| 272 | void tst_QDtls::configuration_data() | 
| 273 | { | 
| 274 |     clientServerData(); | 
| 275 | } | 
| 276 |  | 
| 277 | void tst_QDtls::configuration() | 
| 278 | { | 
| 279 |     // There is a proper auto-test for QSslConfiguration in our TLS test suite, | 
| 280 |     // here we only test several DTLS-related details. | 
| 281 |     auto config = QSslConfiguration::defaultDtlsConfiguration(); | 
| 282 |     QCOMPARE(config.protocol(), QSsl::DtlsV1_2OrLater); | 
| 283 |  | 
| 284 |     const QList<QSslCipher> ciphers = config.ciphers(); | 
| 285 |     QVERIFY(ciphers.size() > 0); | 
| 286 |     for (const auto &cipher : ciphers) | 
| 287 |         QVERIFY(cipher.usedBits() >= 128); | 
| 288 |  | 
| 289 |     QCOMPARE(config.dtlsCookieVerificationEnabled(), true); | 
| 290 |  | 
| 291 |     QFETCH(const QSslSocket::SslMode, mode); | 
| 292 |     QDtls dtls(mode); | 
| 293 |     QCOMPARE(dtls.dtlsConfiguration(), config); | 
| 294 |     config.setProtocol(QSsl::DtlsV1_0OrLater); | 
| 295 |     config.setDtlsCookieVerificationEnabled(false); | 
| 296 |     QCOMPARE(config.dtlsCookieVerificationEnabled(), false); | 
| 297 |  | 
| 298 |     QVERIFY(dtls.setDtlsConfiguration(config)); | 
| 299 |     QDTLS_VERIFY_NO_ERROR(dtls); | 
| 300 |     QCOMPARE(dtls.dtlsConfiguration(), config); | 
| 301 |  | 
| 302 |     if (mode == QSslSocket::SslClientMode) { | 
| 303 |         // Testing a DTLS server would be more complicated, we'd need a DTLS | 
| 304 |         // client sending ClientHello(s), running an event loop etc. - way too | 
| 305 |         // much dancing for a simple  setter/getter test. | 
| 306 |         QVERIFY(dtls.setPeer(serverAddress, serverPort)); | 
| 307 |         QDTLS_VERIFY_NO_ERROR(dtls); | 
| 308 |  | 
| 309 |         QUdpSocket clientSocket; | 
| 310 |         QVERIFY(dtls.doHandshake(&clientSocket)); | 
| 311 |         QDTLS_VERIFY_NO_ERROR(dtls); | 
| 312 |         QCOMPARE(dtls.handshakeState(), QDtls::HandshakeInProgress); | 
| 313 |         // As soon as handshake started, it's not allowed to change configuration: | 
| 314 |         QVERIFY(!dtls.setDtlsConfiguration(QSslConfiguration::defaultDtlsConfiguration())); | 
| 315 |         QCOMPARE(dtls.dtlsError(), QDtlsError::InvalidOperation); | 
| 316 |         QCOMPARE(dtls.dtlsConfiguration(), config); | 
| 317 |     } | 
| 318 |  | 
| 319 |     static bool doneAlready = false; | 
| 320 |     if (!doneAlready) { | 
| 321 |         doneAlready = true; | 
| 322 |         QSslConfiguration nullConfig; | 
| 323 |         const auto defaultDtlsConfig = QSslConfiguration::defaultDtlsConfiguration(); | 
| 324 |         const auto restoreDefault = qScopeGuard(f: [&defaultDtlsConfig] { | 
| 325 |             QSslConfiguration::setDefaultDtlsConfiguration(defaultDtlsConfig); | 
| 326 |         }); | 
| 327 |         QSslConfiguration::setDefaultDtlsConfiguration(nullConfig); | 
| 328 |         QCOMPARE(QSslConfiguration::defaultDtlsConfiguration(), nullConfig); | 
| 329 |         QVERIFY(QSslConfiguration::defaultDtlsConfiguration() != defaultDtlsConfig); | 
| 330 |     } | 
| 331 | } | 
| 332 |  | 
| 333 | void tst_QDtls::invalidConfiguration() | 
| 334 | { | 
| 335 |     QUdpSocket socket; | 
| 336 |     QDtls crypto(QSslSocket::SslClientMode); | 
| 337 |     QVERIFY(crypto.setPeer(serverAddress, serverPort)); | 
| 338 |     // Note: not defaultDtlsConfiguration(), so the protocol is TLS (without D): | 
| 339 |     QVERIFY(crypto.setDtlsConfiguration(QSslConfiguration::defaultConfiguration())); | 
| 340 |     QDTLS_VERIFY_NO_ERROR(crypto); | 
| 341 |     QCOMPARE(crypto.dtlsConfiguration(), QSslConfiguration::defaultConfiguration()); | 
| 342 |     // Try to start the handshake: | 
| 343 |     QCOMPARE(crypto.doHandshake(&socket), false); | 
| 344 |     QCOMPARE(crypto.dtlsError(), QDtlsError::TlsInitializationError); | 
| 345 | } | 
| 346 |  | 
| 347 | void tst_QDtls::setPeer_data() | 
| 348 | { | 
| 349 |     clientServerData(); | 
| 350 | } | 
| 351 |  | 
| 352 | void tst_QDtls::setPeer() | 
| 353 | { | 
| 354 |     static const QHostAddress invalid[] = {QHostAddress(), | 
| 355 |                                            QHostAddress(QHostAddress::Broadcast), | 
| 356 |                                            QHostAddress(QStringLiteral("224.0.0.0" ))}; | 
| 357 |     static const QString peerName = QStringLiteral("does not matter actually" ); | 
| 358 |  | 
| 359 |     QFETCH(const QSslSocket::SslMode, mode); | 
| 360 |     QDtls dtls(mode); | 
| 361 |  | 
| 362 |     for (const auto &addr : invalid) { | 
| 363 |         QCOMPARE(dtls.setPeer(addr, 100, peerName), false); | 
| 364 |         QCOMPARE(dtls.dtlsError(), QDtlsError::InvalidInputParameters); | 
| 365 |         QCOMPARE(dtls.peerAddress(), QHostAddress()); | 
| 366 |         QCOMPARE(dtls.peerPort(), quint16()); | 
| 367 |         QCOMPARE(dtls.peerVerificationName(), QString()); | 
| 368 |     } | 
| 369 |  | 
| 370 |     QVERIFY(dtls.setPeer(serverAddress, serverPort, peerName)); | 
| 371 |     QDTLS_VERIFY_NO_ERROR(dtls); | 
| 372 |     QCOMPARE(dtls.peerAddress(), serverAddress); | 
| 373 |     QCOMPARE(dtls.peerPort(), serverPort); | 
| 374 |     QCOMPARE(dtls.peerVerificationName(), peerName); | 
| 375 |  | 
| 376 |     if (mode == QSslSocket::SslClientMode) { | 
| 377 |         // We test for client mode only, for server mode we'd have to run event | 
| 378 |         // loop etc. too much work for a simple setter/getter test. | 
| 379 |         QUdpSocket clientSocket; | 
| 380 |         QVERIFY(dtls.doHandshake(&clientSocket)); | 
| 381 |         QDTLS_VERIFY_NO_ERROR(dtls); | 
| 382 |         QCOMPARE(dtls.handshakeState(), QDtls::HandshakeInProgress); | 
| 383 |         QCOMPARE(dtls.setPeer(serverAddress, serverPort), false); | 
| 384 |         QCOMPARE(dtls.dtlsError(), QDtlsError::InvalidOperation); | 
| 385 |     } | 
| 386 | } | 
| 387 |  | 
| 388 | void tst_QDtls::handshake_data() | 
| 389 | { | 
| 390 |     QTest::addColumn<bool>(name: "withCertificate" ); | 
| 391 |  | 
| 392 |     QTest::addRow(format: "no-cert" ) << false; | 
| 393 |     QTest::addRow(format: "with-cert" ) << true; | 
| 394 | } | 
| 395 |  | 
| 396 | void tst_QDtls::handshake() | 
| 397 | { | 
| 398 |     connectHandshakeReadingSlots(); | 
| 399 |  | 
| 400 |     QFETCH(const bool, withCertificate); | 
| 401 |  | 
| 402 |     auto serverConfig = defaultServerConfig; | 
| 403 |     auto clientConfig = QSslConfiguration::defaultDtlsConfiguration(); | 
| 404 |  | 
| 405 |     if (!withCertificate) { | 
| 406 |         connect(sender: serverCrypto.data(), signal: &QDtls::pskRequired, receiver: this, slot: &tst_QDtls::pskRequested); | 
| 407 |         connect(sender: clientCrypto.data(), signal: &QDtls::pskRequired, receiver: this, slot: &tst_QDtls::pskRequested); | 
| 408 |         clientConfig.setPeerVerifyMode(QSslSocket::VerifyNone); | 
| 409 |         QVERIFY(clientConfig.peerCertificate().isNull()); | 
| 410 |     } else { | 
| 411 |         serverConfig.setPrivateKey(serverKeySS); | 
| 412 |         serverConfig.setLocalCertificate(selfSignedCert); | 
| 413 |         clientConfig.setCaCertificates({selfSignedCert}); | 
| 414 |     } | 
| 415 |  | 
| 416 |     QVERIFY(serverCrypto->setDtlsConfiguration(serverConfig)); | 
| 417 |     QVERIFY(clientCrypto->setDtlsConfiguration(clientConfig)); | 
| 418 |  | 
| 419 |     // Some early checks before we run event loop. | 
| 420 |     // Remote was not set yet: | 
| 421 |     QVERIFY(!clientCrypto->doHandshake(&clientSocket)); | 
| 422 |     QCOMPARE(clientCrypto->dtlsError(), QDtlsError::InvalidOperation); | 
| 423 |     QVERIFY(!serverCrypto->doHandshake(&serverSocket, QByteArray("ClientHello" ))); | 
| 424 |     QCOMPARE(serverCrypto->dtlsError(), QDtlsError::InvalidOperation); | 
| 425 |  | 
| 426 |     QVERIFY(clientCrypto->setPeer(serverAddress, serverPort, hostName)); | 
| 427 |  | 
| 428 |     // Invalid socket: | 
| 429 |     QVERIFY(!clientCrypto->doHandshake(nullptr)); | 
| 430 |     QCOMPARE(clientCrypto->dtlsError(), QDtlsError::InvalidInputParameters); | 
| 431 |  | 
| 432 |     // Now we are ready for handshake: | 
| 433 |     QVERIFY(clientCrypto->doHandshake(&clientSocket)); | 
| 434 |     QDTLS_VERIFY_NO_ERROR(clientCrypto); | 
| 435 |     QCOMPARE(clientCrypto->handshakeState(), QDtls::HandshakeInProgress); | 
| 436 |  | 
| 437 |     testLoop.enterLoopMSecs(ms: handshakeTimeoutMS); | 
| 438 |  | 
| 439 |     QVERIFY(!testLoop.timeout()); | 
| 440 |  | 
| 441 |     QVERIFY(serverCrypto->isConnectionEncrypted()); | 
| 442 |     QDTLS_VERIFY_NO_ERROR(serverCrypto); | 
| 443 |     QCOMPARE(serverCrypto->handshakeState(), QDtls::HandshakeComplete); | 
| 444 |     QCOMPARE(serverCrypto->peerVerificationErrors().size(), 0); | 
| 445 |  | 
| 446 |     QVERIFY(clientCrypto->isConnectionEncrypted()); | 
| 447 |     QDTLS_VERIFY_NO_ERROR(clientCrypto); | 
| 448 |     QCOMPARE(clientCrypto->handshakeState(), QDtls::HandshakeComplete); | 
| 449 |     QCOMPARE(clientCrypto->peerVerificationErrors().size(), 0); | 
| 450 |  | 
| 451 |     if (withCertificate) { | 
| 452 |         const auto serverCert = clientCrypto->dtlsConfiguration().peerCertificate(); | 
| 453 |         QVERIFY(!serverCert.isNull()); | 
| 454 |         QCOMPARE(serverCert, selfSignedCert); | 
| 455 |     } | 
| 456 |  | 
| 457 |     // Already in 'HandshakeComplete' state/encrypted. | 
| 458 |     QVERIFY(!clientCrypto->doHandshake(&clientSocket)); | 
| 459 |     QCOMPARE(clientCrypto->dtlsError(), QDtlsError::InvalidOperation); | 
| 460 |     QVERIFY(!serverCrypto->doHandshake(&serverSocket, {"ServerHello" })); | 
| 461 |     QCOMPARE(serverCrypto->dtlsError(), QDtlsError::InvalidOperation); | 
| 462 |     // Cannot change a remote without calling shutdown first. | 
| 463 |     QVERIFY(!clientCrypto->setPeer(serverAddress, serverPort)); | 
| 464 |     QCOMPARE(clientCrypto->dtlsError(), QDtlsError::InvalidOperation); | 
| 465 |     QVERIFY(!serverCrypto->setPeer(clientAddress, clientPort)); | 
| 466 |     QCOMPARE(serverCrypto->dtlsError(), QDtlsError::InvalidOperation); | 
| 467 | } | 
| 468 |  | 
| 469 | void tst_QDtls::handshakeWithRetransmission() | 
| 470 | { | 
| 471 |     connectHandshakeReadingSlots(); | 
| 472 |  | 
| 473 |     auto serverConfig = defaultServerConfig; | 
| 474 |     serverConfig.setPrivateKey(serverKeySS); | 
| 475 |     serverConfig.setLocalCertificate(selfSignedCert); | 
| 476 |     QVERIFY(serverCrypto->setDtlsConfiguration(serverConfig)); | 
| 477 |  | 
| 478 |     auto clientConfig = QSslConfiguration::defaultDtlsConfiguration(); | 
| 479 |     clientConfig.setCaCertificates({selfSignedCert}); | 
| 480 |     QVERIFY(clientCrypto->setDtlsConfiguration(clientConfig)); | 
| 481 |     QVERIFY(clientCrypto->setPeer(serverAddress, serverPort, hostName)); | 
| 482 |  | 
| 483 |     // Now we are ready for handshake: | 
| 484 |     QVERIFY(clientCrypto->doHandshake(&clientSocket)); | 
| 485 |     QDTLS_VERIFY_NO_ERROR(clientCrypto); | 
| 486 |     QCOMPARE(clientCrypto->handshakeState(), QDtls::HandshakeInProgress); | 
| 487 |  | 
| 488 |     serverDropDgram = true; | 
| 489 |     clientDropDgram = true; | 
| 490 |     // Every failed re-transmission doubles the next timeout. We don't want to | 
| 491 |     // slow down the test just to check the re-transmission ability, so we'll | 
| 492 |     // drop only the first 'ClientHello' and 'ServerHello' datagrams. The | 
| 493 |     // arithmetic is approximately this: the first ClientHello to be dropped - | 
| 494 |     // client will re-transmit in 1s., the first part of 'ServerHello' to be | 
| 495 |     // dropped, the client then will re-transmit after another 2 s. Thus it's ~3. | 
| 496 |     // We err on safe side and double our (already quite generous) 5s. | 
| 497 |     testLoop.enterLoopMSecs(ms: handshakeTimeoutMS * 2); | 
| 498 |  | 
| 499 |     QVERIFY(!testLoop.timeout()); | 
| 500 |     QDTLS_VERIFY_HANDSHAKE_SUCCESS(serverCrypto); | 
| 501 |     QDTLS_VERIFY_HANDSHAKE_SUCCESS(clientCrypto); | 
| 502 | } | 
| 503 |  | 
| 504 | void tst_QDtls::sessionCipher() | 
| 505 | { | 
| 506 |     connectHandshakeReadingSlots(); | 
| 507 |  | 
| 508 |     auto serverConfig = defaultServerConfig; | 
| 509 |     serverConfig.setPrivateKey(serverKeySS); | 
| 510 |     serverConfig.setLocalCertificate(selfSignedCert); | 
| 511 |     QVERIFY(serverCrypto->setDtlsConfiguration(serverConfig)); | 
| 512 |  | 
| 513 |     auto clientConfig = QSslConfiguration::defaultDtlsConfiguration(); | 
| 514 |     clientConfig.setCaCertificates({selfSignedCert}); | 
| 515 |     QVERIFY(clientCrypto->setDtlsConfiguration(clientConfig)); | 
| 516 |  | 
| 517 |     QVERIFY(clientCrypto->setPeer(serverAddress, serverPort, hostName)); | 
| 518 |     QVERIFY(clientCrypto->doHandshake(&clientSocket)); | 
| 519 |  | 
| 520 |     testLoop.enterLoopMSecs(ms: handshakeTimeoutMS); | 
| 521 |  | 
| 522 |     QVERIFY(!testLoop.timeout()); | 
| 523 |     QDTLS_VERIFY_HANDSHAKE_SUCCESS(clientCrypto); | 
| 524 |     QDTLS_VERIFY_HANDSHAKE_SUCCESS(serverCrypto); | 
| 525 |  | 
| 526 |     const auto defaultDtlsConfig = QSslConfiguration::defaultDtlsConfiguration(); | 
| 527 |  | 
| 528 |     const auto clCipher = clientCrypto->sessionCipher(); | 
| 529 |     QVERIFY(!clCipher.isNull()); | 
| 530 |     QVERIFY(defaultDtlsConfig.ciphers().contains(clCipher)); | 
| 531 |  | 
| 532 |     const auto srvCipher = serverCrypto->sessionCipher(); | 
| 533 |     QVERIFY(!srvCipher.isNull()); | 
| 534 |     QVERIFY(defaultDtlsConfig.ciphers().contains(srvCipher)); | 
| 535 |  | 
| 536 |     QCOMPARE(clCipher, srvCipher); | 
| 537 | } | 
| 538 |  | 
| 539 | void tst_QDtls::cipherPreferences_data() | 
| 540 | { | 
| 541 |     QTest::addColumn<bool>(name: "preferClient" ); | 
| 542 |  | 
| 543 |     QTest::addRow(format: "prefer-server" ) << true; | 
| 544 |     QTest::addRow(format: "prefer-client" ) << false; | 
| 545 | } | 
| 546 |  | 
| 547 | void tst_QDtls::cipherPreferences() | 
| 548 | { | 
| 549 |     // This test is based on the similar case in tst_QSslSocket. We test it for QDtls | 
| 550 |     // because it's possible to set ciphers and corresponding ('server preferred') | 
| 551 |     // options via QSslConfiguration. | 
| 552 |     const QSslCipher aes128(QStringLiteral("AES128-SHA" )); | 
| 553 |     const QSslCipher aes256(QStringLiteral("AES256-SHA" )); | 
| 554 |  | 
| 555 |     auto serverConfig = defaultServerConfig; | 
| 556 |     const QList<QSslCipher> ciphers = serverConfig.ciphers(); | 
| 557 |     if (!ciphers.contains(t: aes128) || !ciphers.contains(t: aes256)) | 
| 558 |         QSKIP("The ciphers needed by this test were not found in the default DTLS configuration" ); | 
| 559 |  | 
| 560 |     serverConfig.setCiphers({aes128, aes256}); | 
| 561 |     serverConfig.setLocalCertificate(selfSignedCert); | 
| 562 |     serverConfig.setPrivateKey(serverKeySS); | 
| 563 |  | 
| 564 |     QFETCH(const bool, preferClient); | 
| 565 |     if (preferClient) | 
| 566 |         serverConfig.setSslOption(option: QSsl::SslOptionDisableServerCipherPreference, on: true); | 
| 567 |  | 
| 568 |     QVERIFY(serverCrypto->setDtlsConfiguration(serverConfig)); | 
| 569 |     QDTLS_VERIFY_NO_ERROR(serverCrypto); | 
| 570 |  | 
| 571 |     auto clientConfig = QSslConfiguration::defaultDtlsConfiguration(); | 
| 572 |     clientConfig.setPeerVerifyMode(QSslSocket::VerifyNone); | 
| 573 |     clientConfig.setCiphers({aes256, aes128}); | 
| 574 |     QVERIFY(clientCrypto->setDtlsConfiguration(clientConfig)); | 
| 575 |     QVERIFY(clientCrypto->setPeer(serverAddress, serverPort)); | 
| 576 |     QDTLS_VERIFY_NO_ERROR(clientCrypto); | 
| 577 |  | 
| 578 |     connectHandshakeReadingSlots(); | 
| 579 |  | 
| 580 |     QVERIFY(clientCrypto->doHandshake(&clientSocket)); | 
| 581 |     QDTLS_VERIFY_NO_ERROR(clientCrypto); | 
| 582 |  | 
| 583 |     testLoop.enterLoopMSecs(ms: handshakeTimeoutMS); | 
| 584 |     QVERIFY(!testLoop.timeout()); | 
| 585 |     QDTLS_VERIFY_HANDSHAKE_SUCCESS(clientCrypto); | 
| 586 |     QDTLS_VERIFY_HANDSHAKE_SUCCESS(serverCrypto); | 
| 587 |  | 
| 588 |     if (preferClient) { | 
| 589 |         QCOMPARE(clientCrypto->sessionCipher(), aes256); | 
| 590 |         QCOMPARE(serverCrypto->sessionCipher(), aes256); | 
| 591 |     } else { | 
| 592 |         QCOMPARE(clientCrypto->sessionCipher(), aes128); | 
| 593 |         QCOMPARE(serverCrypto->sessionCipher(), aes128); | 
| 594 |     } | 
| 595 | } | 
| 596 |  | 
| 597 | void tst_QDtls::protocolVersionMatching_data() | 
| 598 | { | 
| 599 |     QTest::addColumn<QSsl::SslProtocol>(name: "serverProtocol" ); | 
| 600 |     QTest::addColumn<QSsl::SslProtocol>(name: "clientProtocol" ); | 
| 601 |     QTest::addColumn<bool>(name: "works" ); | 
| 602 |  | 
| 603 |     QTest::addRow(format: "DtlsV1_0 <-> DtlsV1_0" ) << QSsl::DtlsV1_0 << QSsl::DtlsV1_0 << true; | 
| 604 |     QTest::addRow(format: "DtlsV1_0OrLater <-> DtlsV1_0" ) << QSsl::DtlsV1_0OrLater << QSsl::DtlsV1_0 << true; | 
| 605 |     QTest::addRow(format: "DtlsV1_0 <-> DtlsV1_0OrLater" ) << QSsl::DtlsV1_0 << QSsl::DtlsV1_0OrLater << true; | 
| 606 |     QTest::addRow(format: "DtlsV1_0OrLater <-> DtlsV1_0OrLater" ) << QSsl::DtlsV1_0OrLater << QSsl::DtlsV1_0OrLater << true; | 
| 607 |  | 
| 608 |     QTest::addRow(format: "DtlsV1_2 <-> DtlsV1_2" ) << QSsl::DtlsV1_2 << QSsl::DtlsV1_2 << true; | 
| 609 |     QTest::addRow(format: "DtlsV1_2OrLater <-> DtlsV1_2" ) << QSsl::DtlsV1_2OrLater << QSsl::DtlsV1_2 << true; | 
| 610 |     QTest::addRow(format: "DtlsV1_2 <-> DtlsV1_2OrLater" ) << QSsl::DtlsV1_2 << QSsl::DtlsV1_2OrLater << true; | 
| 611 |     QTest::addRow(format: "DtlsV1_2OrLater <-> DtlsV1_2OrLater" ) << QSsl::DtlsV1_2OrLater << QSsl::DtlsV1_2OrLater << true; | 
| 612 |  | 
| 613 |     QTest::addRow(format: "DtlsV1_0 <-> DtlsV1_2" ) << QSsl::DtlsV1_0 << QSsl::DtlsV1_2 << false; | 
| 614 |     QTest::addRow(format: "DtlsV1_0 <-> DtlsV1_2OrLater" ) << QSsl::DtlsV1_0 << QSsl::DtlsV1_2OrLater << false; | 
| 615 |     QTest::addRow(format: "DtlsV1_2 <-> DtlsV1_0" ) << QSsl::DtlsV1_2 << QSsl::DtlsV1_0 << false; | 
| 616 |     QTest::addRow(format: "DtlsV1_2OrLater <-> DtlsV1_0" ) << QSsl::DtlsV1_2OrLater << QSsl::DtlsV1_0 << false; | 
| 617 | } | 
| 618 |  | 
| 619 | void tst_QDtls::protocolVersionMatching() | 
| 620 | { | 
| 621 |     QFETCH(const QSsl::SslProtocol, serverProtocol); | 
| 622 |     QFETCH(const QSsl::SslProtocol, clientProtocol); | 
| 623 |     QFETCH(const bool, works); | 
| 624 |  | 
| 625 |     connectHandshakeReadingSlots(); | 
| 626 |  | 
| 627 |     connect(sender: serverCrypto.data(), signal: &QDtls::pskRequired, receiver: this, slot: &tst_QDtls::pskRequested); | 
| 628 |     connect(sender: clientCrypto.data(), signal: &QDtls::pskRequired, receiver: this, slot: &tst_QDtls::pskRequested); | 
| 629 |  | 
| 630 |     auto serverConfig = defaultServerConfig; | 
| 631 |     serverConfig.setProtocol(serverProtocol); | 
| 632 |     QVERIFY(serverCrypto->setDtlsConfiguration(serverConfig)); | 
| 633 |  | 
| 634 |     auto clientConfig = QSslConfiguration::defaultDtlsConfiguration(); | 
| 635 |     clientConfig.setPeerVerifyMode(QSslSocket::VerifyNone); | 
| 636 |     clientConfig.setProtocol(clientProtocol); | 
| 637 |     QVERIFY(clientCrypto->setDtlsConfiguration(clientConfig)); | 
| 638 |  | 
| 639 |     QVERIFY(clientCrypto->setPeer(serverAddress, serverPort)); | 
| 640 |     QVERIFY(clientCrypto->doHandshake(&clientSocket)); | 
| 641 |  | 
| 642 |     testLoop.enterLoopMSecs(ms: handshakeTimeoutMS); | 
| 643 |  | 
| 644 |     if (works) { | 
| 645 |         QDTLS_VERIFY_HANDSHAKE_SUCCESS(serverCrypto); | 
| 646 |         QDTLS_VERIFY_HANDSHAKE_SUCCESS(clientCrypto); | 
| 647 |     } else { | 
| 648 |         QCOMPARE(serverCrypto->isConnectionEncrypted(), false); | 
| 649 |         QVERIFY(serverCrypto->handshakeState() != QDtls::HandshakeComplete); | 
| 650 |         QCOMPARE(clientCrypto->isConnectionEncrypted(), false); | 
| 651 |         QVERIFY(clientCrypto->handshakeState() != QDtls::HandshakeComplete); | 
| 652 |     } | 
| 653 | } | 
| 654 |  | 
| 655 | void tst_QDtls::verificationErrors_data() | 
| 656 | { | 
| 657 |     QTest::addColumn<bool>(name: "abortHandshake" ); | 
| 658 |  | 
| 659 |     QTest::addRow(format: "abort-handshake" ) << true; | 
| 660 |     QTest::addRow(format: "ignore-errors" ) << false; | 
| 661 | } | 
| 662 |  | 
| 663 | void tst_QDtls::verificationErrors() | 
| 664 | { | 
| 665 |     connectHandshakeReadingSlots(); | 
| 666 |  | 
| 667 |     auto serverConfig = defaultServerConfig; | 
| 668 |     serverConfig.setPrivateKey(serverKeySS); | 
| 669 |     serverConfig.setLocalCertificate(selfSignedCert); | 
| 670 |     QVERIFY(serverCrypto->setDtlsConfiguration(serverConfig)); | 
| 671 |     // And our client already has the default DTLS configuration. | 
| 672 |  | 
| 673 |     QVERIFY(clientCrypto->setPeer(serverAddress, serverPort)); | 
| 674 |     // Now we are ready for handshake: | 
| 675 |     QVERIFY(clientCrypto->doHandshake(&clientSocket)); | 
| 676 |  | 
| 677 |     testLoop.enterLoopMSecs(ms: handshakeTimeoutMS); | 
| 678 |  | 
| 679 |     QVERIFY(!testLoop.timeout()); | 
| 680 |     QDTLS_VERIFY_NO_ERROR(serverCrypto); | 
| 681 |  | 
| 682 |     QCOMPARE(clientCrypto->dtlsError(), QDtlsError::PeerVerificationError); | 
| 683 |     QCOMPARE(clientCrypto->handshakeState(), QDtls::PeerVerificationFailed); | 
| 684 |     QVERIFY(!clientCrypto->isConnectionEncrypted()); | 
| 685 |  | 
| 686 |     QVERIFY(verificationErrorDetected(QSslError::HostNameMismatch)); | 
| 687 |     QVERIFY(verificationErrorDetected(QSslError::SelfSignedCertificate)); | 
| 688 |  | 
| 689 |     const auto serverCert = clientCrypto->dtlsConfiguration().peerCertificate(); | 
| 690 |     QVERIFY(!serverCert.isNull()); | 
| 691 |     QCOMPARE(selfSignedCert, serverCert); | 
| 692 |  | 
| 693 |     QFETCH(const bool, abortHandshake); | 
| 694 |  | 
| 695 |     if (abortHandshake) { | 
| 696 |         QVERIFY(!clientCrypto->abortHandshake(nullptr)); | 
| 697 |         QCOMPARE(clientCrypto->dtlsError(), QDtlsError::InvalidInputParameters); | 
| 698 |         QVERIFY(clientCrypto->abortHandshake(&clientSocket)); | 
| 699 |         QDTLS_VERIFY_NO_ERROR(clientCrypto); | 
| 700 |         QVERIFY(!clientCrypto->isConnectionEncrypted()); | 
| 701 |         QCOMPARE(clientCrypto->handshakeState(), QDtls::HandshakeNotStarted); | 
| 702 |         QCOMPARE(clientCrypto->sessionCipher(), QSslCipher()); | 
| 703 |         QCOMPARE(clientCrypto->sessionProtocol(), QSsl::UnknownProtocol); | 
| 704 |         const auto config = clientCrypto->dtlsConfiguration(); | 
| 705 |         QVERIFY(config.peerCertificate().isNull()); | 
| 706 |         QCOMPARE(config.peerCertificateChain().size(), 0); | 
| 707 |         QCOMPARE(clientCrypto->peerVerificationErrors().size(), 0); | 
| 708 |     } else { | 
| 709 |         clientCrypto->ignoreVerificationErrors(errorsToIgnore: clientCrypto->peerVerificationErrors()); | 
| 710 |         QVERIFY(!clientCrypto->resumeHandshake(nullptr)); | 
| 711 |         QCOMPARE(clientCrypto->dtlsError(), QDtlsError::InvalidInputParameters); | 
| 712 |         QVERIFY(clientCrypto->resumeHandshake(&clientSocket)); | 
| 713 |         QDTLS_VERIFY_HANDSHAKE_SUCCESS(clientCrypto); | 
| 714 |         QVERIFY(clientCrypto->isConnectionEncrypted()); | 
| 715 |         QCOMPARE(clientCrypto->handshakeState(), QDtls::HandshakeComplete); | 
| 716 |         QCOMPARE(clientCrypto->peerVerificationErrors().size(), 0); | 
| 717 |     } | 
| 718 | } | 
| 719 |  | 
| 720 | void tst_QDtls::presetExpectedErrors_data() | 
| 721 | { | 
| 722 |     QTest::addColumn<QVector<QSslError>>(name: "expectedTlsErrors" ); | 
| 723 |     QTest::addColumn<bool>(name: "works" ); | 
| 724 |  | 
| 725 |     QVector<QSslError> expectedErrors{{QSslError::HostNameMismatch, selfSignedCert}}; | 
| 726 |     QTest::addRow(format: "unexpected-self-signed" ) << expectedErrors << false; | 
| 727 |     expectedErrors.push_back(t: {QSslError::SelfSignedCertificate, selfSignedCert}); | 
| 728 |     QTest::addRow(format: "all-errors-ignored" ) << expectedErrors << true; | 
| 729 | } | 
| 730 |  | 
| 731 | void tst_QDtls::presetExpectedErrors() | 
| 732 | { | 
| 733 |     QFETCH(const QVector<QSslError>, expectedTlsErrors); | 
| 734 |     QFETCH(const bool, works); | 
| 735 |  | 
| 736 |     connectHandshakeReadingSlots(); | 
| 737 |  | 
| 738 |     auto serverConfig = defaultServerConfig; | 
| 739 |     serverConfig.setPrivateKey(serverKeySS); | 
| 740 |     serverConfig.setLocalCertificate(selfSignedCert); | 
| 741 |     QVERIFY(serverCrypto->setDtlsConfiguration(serverConfig)); | 
| 742 |  | 
| 743 |     clientCrypto->ignoreVerificationErrors(errorsToIgnore: expectedTlsErrors); | 
| 744 |     QVERIFY(clientCrypto->setPeer(serverAddress, serverPort)); | 
| 745 |     QVERIFY(clientCrypto->doHandshake(&clientSocket)); | 
| 746 |  | 
| 747 |     testLoop.enterLoopMSecs(ms: handshakeTimeoutMS); | 
| 748 |  | 
| 749 |     QVERIFY(!testLoop.timeout()); | 
| 750 |  | 
| 751 |     if (works) { | 
| 752 |         QDTLS_VERIFY_HANDSHAKE_SUCCESS(serverCrypto); | 
| 753 |         QCOMPARE(clientCrypto->handshakeState(), QDtls::HandshakeComplete); | 
| 754 |         QVERIFY(clientCrypto->isConnectionEncrypted()); | 
| 755 |     } else { | 
| 756 |         QCOMPARE(clientCrypto->dtlsError(), QDtlsError::PeerVerificationError); | 
| 757 |         QVERIFY(!clientCrypto->isConnectionEncrypted()); | 
| 758 |         QCOMPARE(clientCrypto->handshakeState(), QDtls::PeerVerificationFailed); | 
| 759 |     } | 
| 760 | } | 
| 761 |  | 
| 762 | void tst_QDtls::verifyServerCertificate_data() | 
| 763 | { | 
| 764 |     QTest::addColumn<QSslSocket::PeerVerifyMode>(name: "verifyMode" ); | 
| 765 |     QTest::addColumn<QList<QSslCertificate>>(name: "serverCerts" ); | 
| 766 |     QTest::addColumn<QSslKey>(name: "serverKey" ); | 
| 767 |     QTest::addColumn<QString>(name: "peerName" ); | 
| 768 |     QTest::addColumn<bool>(name: "encrypted" ); | 
| 769 |  | 
| 770 |     { | 
| 771 |         // A special case - null key (but with certificate): | 
| 772 |         const auto chain = QSslCertificate::fromPath(path: certDirPath + QStringLiteral("bogus-server.crt" )); | 
| 773 |         QCOMPARE(chain.size(), 1); | 
| 774 |  | 
| 775 |         QSslKey nullKey; | 
| 776 |         // Only one row - server must fail to start handshake immediately. | 
| 777 |         QTest::newRow(dataTag: "valid-server-cert-no-key : VerifyPeer" ) << QSslSocket::VerifyPeer << chain << nullKey << QString() << false; | 
| 778 |     } | 
| 779 |     { | 
| 780 |         // Valid certificate: | 
| 781 |         auto chain = QSslCertificate::fromPath(path: certDirPath + QStringLiteral("bogus-server.crt" )); | 
| 782 |         QCOMPARE(chain.size(), 1); | 
| 783 |  | 
| 784 |         const auto caCert = QSslCertificate::fromPath(path: certDirPath + QStringLiteral("bogus-ca.crt" )); | 
| 785 |         QCOMPARE(caCert.size(), 1); | 
| 786 |         chain += caCert; | 
| 787 |  | 
| 788 |         QFile keyFile(certDirPath + QStringLiteral("bogus-server.key" )); | 
| 789 |         QVERIFY(keyFile.open(QIODevice::ReadOnly)); | 
| 790 |         const QSslKey key(keyFile.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey); | 
| 791 |         QVERIFY(!key.isNull()); | 
| 792 |  | 
| 793 |         auto cert = chain.first(); | 
| 794 |         const QString name(cert.subjectInfo(info: QSslCertificate::CommonName).first()); | 
| 795 |         QTest::newRow(dataTag: "valid-server-cert : AutoVerifyPeer" ) << QSslSocket::AutoVerifyPeer << chain << key << name << true; | 
| 796 |         QTest::newRow(dataTag: "valid-server-cert : QueryPeer" ) << QSslSocket::QueryPeer << chain << key << name << true; | 
| 797 |         QTest::newRow(dataTag: "valid-server-cert : VerifyNone" ) << QSslSocket::VerifyNone << chain << key << name << true; | 
| 798 |         QTest::newRow(dataTag: "valid-server-cert : VerifyPeer (add CA)" ) << QSslSocket::VerifyPeer << chain << key << name << true; | 
| 799 |         QTest::newRow(dataTag: "valid-server-cert : VerifyPeer (no CA)" ) << QSslSocket::VerifyPeer << chain << key << name << false; | 
| 800 |         QTest::newRow(dataTag: "valid-server-cert : VerifyPeer (name mismatch)" ) << QSslSocket::VerifyPeer << chain << key << QString() << false; | 
| 801 |     } | 
| 802 | } | 
| 803 |  | 
| 804 | void tst_QDtls::verifyServerCertificate() | 
| 805 | { | 
| 806 |     QFETCH(const QSslSocket::PeerVerifyMode, verifyMode); | 
| 807 |     QFETCH(const QList<QSslCertificate>, serverCerts); | 
| 808 |     QFETCH(const QSslKey, serverKey); | 
| 809 |     QFETCH(const QString, peerName); | 
| 810 |     QFETCH(const bool, encrypted); | 
| 811 |  | 
| 812 |     auto serverConfig = defaultServerConfig; | 
| 813 |     serverConfig.setLocalCertificateChain(serverCerts); | 
| 814 |     serverConfig.setPrivateKey(serverKey); | 
| 815 |     QVERIFY(serverCrypto->setDtlsConfiguration(serverConfig)); | 
| 816 |  | 
| 817 |     auto clientConfig = QSslConfiguration::defaultDtlsConfiguration(); | 
| 818 |  | 
| 819 |     if (serverCerts.size() == 2 && encrypted) { | 
| 820 |         auto caCerts = clientConfig.caCertificates(); | 
| 821 |         caCerts.append(t: serverCerts.at(i: 1)); | 
| 822 |         clientConfig.setCaCertificates(caCerts); | 
| 823 |     } | 
| 824 |  | 
| 825 |     clientConfig.setPeerVerifyMode(verifyMode); | 
| 826 |  | 
| 827 |     QVERIFY(clientCrypto->setDtlsConfiguration(clientConfig)); | 
| 828 |     QVERIFY(clientCrypto->setPeer(serverAddress, serverPort, peerName)); | 
| 829 |  | 
| 830 |     connectHandshakeReadingSlots(); | 
| 831 |  | 
| 832 |     QVERIFY(clientCrypto->doHandshake(&clientSocket)); | 
| 833 |  | 
| 834 |     testLoop.enterLoopMSecs(ms: handshakeTimeoutMS); | 
| 835 |     QVERIFY(!testLoop.timeout()); | 
| 836 |  | 
| 837 |     if (serverKey.isNull() && !serverCerts.isEmpty()) { | 
| 838 |         QDTLS_VERIFY_NO_ERROR(clientCrypto); | 
| 839 |         QCOMPARE(clientCrypto->handshakeState(), QDtls::HandshakeInProgress); | 
| 840 |         QCOMPARE(serverCrypto->dtlsError(), QDtlsError::TlsInitializationError); | 
| 841 |         QCOMPARE(serverCrypto->handshakeState(), QDtls::HandshakeNotStarted); | 
| 842 |         return; | 
| 843 |     } | 
| 844 |  | 
| 845 |     if (encrypted) { | 
| 846 |         QDTLS_VERIFY_HANDSHAKE_SUCCESS(serverCrypto); | 
| 847 |         QDTLS_VERIFY_HANDSHAKE_SUCCESS(clientCrypto); | 
| 848 |     } else { | 
| 849 |         QVERIFY(!clientCrypto->isConnectionEncrypted()); | 
| 850 |         QCOMPARE(clientCrypto->handshakeState(), QDtls::PeerVerificationFailed); | 
| 851 |         QVERIFY(clientCrypto->peerVerificationErrors().size()); | 
| 852 |         QVERIFY(clientCrypto->writeDatagramEncrypted(&clientSocket, "something" ) < 0); | 
| 853 |         QCOMPARE(clientCrypto->dtlsError(), QDtlsError::InvalidOperation); | 
| 854 |     } | 
| 855 | } | 
| 856 |  | 
| 857 | void tst_QDtls::verifyClientCertificate_data() | 
| 858 | { | 
| 859 |     QTest::addColumn<QSslSocket::PeerVerifyMode>(name: "verifyMode" ); | 
| 860 |     QTest::addColumn<QList<QSslCertificate>>(name: "clientCerts" ); | 
| 861 |     QTest::addColumn<QSslKey>(name: "clientKey" ); | 
| 862 |     QTest::addColumn<bool>(name: "encrypted" ); | 
| 863 |     { | 
| 864 |         // No certficates, no key: | 
| 865 |         QList<QSslCertificate> chain; | 
| 866 |         QSslKey key; | 
| 867 |         QTest::newRow(dataTag: "no-cert : AutoVerifyPeer" ) << QSslSocket::AutoVerifyPeer << chain << key << true; | 
| 868 |         QTest::newRow(dataTag: "no-cert : QueryPeer" ) << QSslSocket::QueryPeer << chain << key << true; | 
| 869 |         QTest::newRow(dataTag: "no-cert : VerifyNone" ) << QSslSocket::VerifyNone << chain << key << true; | 
| 870 |         QTest::newRow(dataTag: "no-cert : VerifyPeer" ) << QSslSocket::VerifyPeer << chain << key << false; | 
| 871 |     } | 
| 872 |     { | 
| 873 |         const auto chain = QSslCertificate::fromPath(path: certDirPath + QStringLiteral("fluke.cert" )); | 
| 874 |         QCOMPARE(chain.size(), 1); | 
| 875 |  | 
| 876 |         QFile keyFile(certDirPath + QStringLiteral("fluke.key" )); | 
| 877 |         QVERIFY(keyFile.open(QIODevice::ReadOnly)); | 
| 878 |         const QSslKey key(keyFile.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey); | 
| 879 |         QVERIFY(!key.isNull()); | 
| 880 |  | 
| 881 |         QTest::newRow(dataTag: "self-signed-cert : AutoVerifyPeer" ) << QSslSocket::AutoVerifyPeer << chain << key << true; | 
| 882 |         QTest::newRow(dataTag: "self-signed-cert : QueryPeer" ) << QSslSocket::QueryPeer << chain << key << true; | 
| 883 |         QTest::newRow(dataTag: "self-signed-cert : VerifyNone" ) << QSslSocket::VerifyNone << chain << key << true; | 
| 884 |         QTest::newRow(dataTag: "self-signed-cert : VerifyPeer" ) << QSslSocket::VerifyPeer << chain << key << false; | 
| 885 |     } | 
| 886 |     { | 
| 887 |         // Valid certificate, but wrong usage (server certificate): | 
| 888 |         const auto chain = QSslCertificate::fromPath(path: certDirPath + QStringLiteral("bogus-server.crt" )); | 
| 889 |         QCOMPARE(chain.size(), 1); | 
| 890 |  | 
| 891 |         QFile keyFile(certDirPath + QStringLiteral("bogus-server.key" )); | 
| 892 |         QVERIFY(keyFile.open(QIODevice::ReadOnly)); | 
| 893 |         const QSslKey key(keyFile.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey); | 
| 894 |         QVERIFY(!key.isNull()); | 
| 895 |  | 
| 896 |         QTest::newRow(dataTag: "valid-server-cert : AutoVerifyPeer" ) << QSslSocket::AutoVerifyPeer << chain << key << true; | 
| 897 |         QTest::newRow(dataTag: "valid-server-cert : QueryPeer" ) << QSslSocket::QueryPeer << chain << key << true; | 
| 898 |         QTest::newRow(dataTag: "valid-server-cert : VerifyNone" ) << QSslSocket::VerifyNone << chain << key << true; | 
| 899 |         QTest::newRow(dataTag: "valid-server-cert : VerifyPeer" ) << QSslSocket::VerifyPeer << chain << key << false; | 
| 900 |     } | 
| 901 |     { | 
| 902 |         // Valid certificate, correct usage (client certificate): | 
| 903 |         auto chain = QSslCertificate::fromPath(path: certDirPath + QStringLiteral("bogus-client.crt" )); | 
| 904 |         QCOMPARE(chain.size(), 1); | 
| 905 |  | 
| 906 |         QFile keyFile(certDirPath + QStringLiteral("bogus-client.key" )); | 
| 907 |         QVERIFY(keyFile.open(QIODevice::ReadOnly)); | 
| 908 |         const QSslKey key(keyFile.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey); | 
| 909 |         QVERIFY(!key.isNull()); | 
| 910 |  | 
| 911 |         QTest::newRow(dataTag: "valid-client-cert : AutoVerifyPeer" ) << QSslSocket::AutoVerifyPeer << chain << key << true; | 
| 912 |         QTest::newRow(dataTag: "valid-client-cert : QueryPeer" ) << QSslSocket::QueryPeer << chain << key << true; | 
| 913 |         QTest::newRow(dataTag: "valid-client-cert : VerifyNone" ) << QSslSocket::VerifyNone << chain << key << true; | 
| 914 |         QTest::newRow(dataTag: "valid-client-cert : VerifyPeer" ) << QSslSocket::VerifyPeer << chain << key << true; | 
| 915 |  | 
| 916 |         // Valid certificate, correct usage (client certificate), with chain: | 
| 917 |         chain += QSslCertificate::fromPath(path: certDirPath + QStringLiteral("bogus-ca.crt" )); | 
| 918 |         QCOMPARE(chain.size(), 2); | 
| 919 |  | 
| 920 |         QTest::newRow(dataTag: "valid-client-chain : AutoVerifyPeer" ) << QSslSocket::AutoVerifyPeer << chain << key << true; | 
| 921 |         QTest::newRow(dataTag: "valid-client-chain : QueryPeer" ) << QSslSocket::QueryPeer << chain << key << true; | 
| 922 |         QTest::newRow(dataTag: "valid-client-chain : VerifyNone" ) << QSslSocket::VerifyNone << chain << key << true; | 
| 923 |         QTest::newRow(dataTag: "valid-client-chain : VerifyPeer" ) << QSslSocket::VerifyPeer << chain << key << true; | 
| 924 |     } | 
| 925 | } | 
| 926 |  | 
| 927 | void tst_QDtls::verifyClientCertificate() | 
| 928 | { | 
| 929 |     connectHandshakeReadingSlots(); | 
| 930 |  | 
| 931 |     QFETCH(const QSslSocket::PeerVerifyMode, verifyMode); | 
| 932 |     QFETCH(const QList<QSslCertificate>, clientCerts); | 
| 933 |     QFETCH(const QSslKey, clientKey); | 
| 934 |     QFETCH(const bool, encrypted); | 
| 935 |  | 
| 936 |     QSslConfiguration serverConfig = defaultServerConfig; | 
| 937 |     serverConfig.setLocalCertificate(selfSignedCert); | 
| 938 |     serverConfig.setPrivateKey(serverKeySS); | 
| 939 |     serverConfig.setPeerVerifyMode(verifyMode); | 
| 940 |  | 
| 941 |     if (verifyMode == QSslSocket::VerifyPeer && clientCerts.size()) { | 
| 942 |         // Not always needed even if these conditions met, but does not hurt | 
| 943 |         // either. | 
| 944 |         const auto certs = QSslCertificate::fromPath(path: certDirPath + QStringLiteral("bogus-ca.crt" )); | 
| 945 |         QCOMPARE(certs.size(), 1); | 
| 946 |         serverConfig.setCaCertificates(serverConfig.caCertificates() + certs); | 
| 947 |     } | 
| 948 |  | 
| 949 |     QVERIFY(serverCrypto->setDtlsConfiguration(serverConfig)); | 
| 950 |     serverConfig = serverCrypto->dtlsConfiguration(); | 
| 951 |     QVERIFY(serverConfig.peerCertificate().isNull()); | 
| 952 |     QCOMPARE(serverConfig.peerCertificateChain().size(), 0); | 
| 953 |  | 
| 954 |     auto clientConfig = QSslConfiguration::defaultDtlsConfiguration(); | 
| 955 |     clientConfig.setLocalCertificateChain(clientCerts); | 
| 956 |     clientConfig.setPrivateKey(clientKey); | 
| 957 |     clientConfig.setPeerVerifyMode(QSslSocket::VerifyNone); | 
| 958 |     QVERIFY(clientCrypto->setDtlsConfiguration(clientConfig)); | 
| 959 |     QVERIFY(clientCrypto->setPeer(serverAddress, serverPort)); | 
| 960 |  | 
| 961 |     QVERIFY(clientCrypto->doHandshake(&clientSocket)); | 
| 962 |     QDTLS_VERIFY_NO_ERROR(clientCrypto); | 
| 963 |  | 
| 964 |     testLoop.enterLoopMSecs(ms: handshakeTimeoutMS); | 
| 965 |  | 
| 966 |     serverConfig = serverCrypto->dtlsConfiguration(); | 
| 967 |  | 
| 968 |     if (verifyMode == QSslSocket::VerifyNone || clientCerts.isEmpty()) { | 
| 969 |         QVERIFY(serverConfig.peerCertificate().isNull()); | 
| 970 |         QCOMPARE(serverConfig.peerCertificateChain().size(), 0); | 
| 971 |     } else { | 
| 972 |         QCOMPARE(serverConfig.peerCertificate(), clientCerts.first()); | 
| 973 |         QCOMPARE(serverConfig.peerCertificateChain(), clientCerts); | 
| 974 |     } | 
| 975 |  | 
| 976 |     if (encrypted) { | 
| 977 |         QDTLS_VERIFY_HANDSHAKE_SUCCESS(serverCrypto); | 
| 978 |         QDTLS_VERIFY_HANDSHAKE_SUCCESS(clientCrypto); | 
| 979 |     } else { | 
| 980 |         QVERIFY(!serverCrypto->isConnectionEncrypted()); | 
| 981 |         QCOMPARE(serverCrypto->handshakeState(), QDtls::PeerVerificationFailed); | 
| 982 |         QVERIFY(serverCrypto->dtlsErrorString().size() > 0); | 
| 983 |         QVERIFY(serverCrypto->peerVerificationErrors().size() > 0); | 
| 984 |  | 
| 985 |         QVERIFY(!clientCrypto->isConnectionEncrypted()); | 
| 986 |         QDTLS_VERIFY_NO_ERROR(clientCrypto); | 
| 987 |         QCOMPARE(clientCrypto->handshakeState(), QDtls::HandshakeInProgress); | 
| 988 |     } | 
| 989 | } | 
| 990 |  | 
| 991 | void tst_QDtls::blacklistedCerificate() | 
| 992 | { | 
| 993 |     const auto serverChain = QSslCertificate::fromPath(path: certDirPath + QStringLiteral("fake-login.live.com.pem" )); | 
| 994 |     QCOMPARE(serverChain.size(), 1); | 
| 995 |  | 
| 996 |     QFile keyFile(certDirPath + QStringLiteral("fake-login.live.com.key" )); | 
| 997 |     QVERIFY(keyFile.open(QIODevice::ReadOnly)); | 
| 998 |     const QSslKey key(keyFile.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey); | 
| 999 |     QVERIFY(!key.isNull()); | 
| 1000 |  | 
| 1001 |     auto serverConfig = defaultServerConfig; | 
| 1002 |     serverConfig.setLocalCertificateChain(serverChain); | 
| 1003 |     serverConfig.setPrivateKey(key); | 
| 1004 |     QVERIFY(serverCrypto->setDtlsConfiguration(serverConfig)); | 
| 1005 |  | 
| 1006 |     connectHandshakeReadingSlots(); | 
| 1007 |     const QString name(serverChain.first().subjectInfo(info: QSslCertificate::CommonName).first()); | 
| 1008 |     QVERIFY(clientCrypto->setPeer(serverAddress, serverPort, name)); | 
| 1009 |     QVERIFY(clientCrypto->doHandshake(&clientSocket)); | 
| 1010 |  | 
| 1011 |     testLoop.enterLoopMSecs(ms: handshakeTimeoutMS); | 
| 1012 |     QVERIFY(!testLoop.timeout()); | 
| 1013 |     QCOMPARE(clientCrypto->handshakeState(), QDtls::PeerVerificationFailed); | 
| 1014 |     QCOMPARE(clientCrypto->dtlsError(), QDtlsError::PeerVerificationError); | 
| 1015 |     QVERIFY(!clientCrypto->isConnectionEncrypted()); | 
| 1016 |     QVERIFY(verificationErrorDetected(QSslError::CertificateBlacklisted)); | 
| 1017 | } | 
| 1018 |  | 
| 1019 | void tst_QDtls::readWriteEncrypted_data() | 
| 1020 | { | 
| 1021 |     QTest::addColumn<bool>(name: "serverSideShutdown" ); | 
| 1022 |  | 
| 1023 |     QTest::addRow(format: "client-shutdown" ) << false; | 
| 1024 |     QTest::addRow(format: "server-shutdown" ) << true; | 
| 1025 | } | 
| 1026 |  | 
| 1027 | void tst_QDtls::readWriteEncrypted() | 
| 1028 | { | 
| 1029 |     connectHandshakeReadingSlots(); | 
| 1030 |  | 
| 1031 |     auto serverConfig = defaultServerConfig; | 
| 1032 |     serverConfig.setLocalCertificate(selfSignedCert); | 
| 1033 |     serverConfig.setPrivateKey(serverKeySS); | 
| 1034 |     QVERIFY(serverCrypto->setDtlsConfiguration(serverConfig)); | 
| 1035 |  | 
| 1036 |     auto clientConfig = QSslConfiguration::defaultDtlsConfiguration(); | 
| 1037 |     clientConfig.setCaCertificates({selfSignedCert}); | 
| 1038 |     QVERIFY(clientCrypto->setDtlsConfiguration(clientConfig)); | 
| 1039 |     QVERIFY(clientCrypto->setPeer(serverAddress, serverPort, hostName)); | 
| 1040 |  | 
| 1041 |     // 0. Verify we cannot write any encrypted message without handshake done | 
| 1042 |     QDTLS_VERIFY_NO_ERROR(clientCrypto); | 
| 1043 |     QVERIFY(clientCrypto->writeDatagramEncrypted(&clientSocket, serverExpectedPlainText) <= 0); | 
| 1044 |     QCOMPARE(clientCrypto->dtlsError(), QDtlsError::InvalidOperation); | 
| 1045 |     QVERIFY(!clientCrypto->shutdown(&clientSocket)); | 
| 1046 |     QCOMPARE(clientCrypto->dtlsError(), QDtlsError::InvalidOperation); | 
| 1047 |     QDTLS_VERIFY_NO_ERROR(serverCrypto); | 
| 1048 |     QVERIFY(serverCrypto->writeDatagramEncrypted(&serverSocket, clientExpectedPlainText) <= 0); | 
| 1049 |     QCOMPARE(serverCrypto->dtlsError(), QDtlsError::InvalidOperation); | 
| 1050 |     QVERIFY(!serverCrypto->shutdown(&serverSocket)); | 
| 1051 |     QCOMPARE(serverCrypto->dtlsError(), QDtlsError::InvalidOperation); | 
| 1052 |  | 
| 1053 |     // 1. Initiate a handshake: | 
| 1054 |     QVERIFY(clientCrypto->doHandshake(&clientSocket)); | 
| 1055 |     QDTLS_VERIFY_NO_ERROR(clientCrypto); | 
| 1056 |     // 1.1 Verify we cannot read yet. What the datagram is - not really important, | 
| 1057 |     // invalid state/operation - is what we verify: | 
| 1058 |     const QByteArray dummy = clientCrypto->decryptDatagram(socket: &clientSocket, dgram: "BS dgram" ); | 
| 1059 |     QCOMPARE(dummy.size(), 0); | 
| 1060 |     QCOMPARE(clientCrypto->dtlsError(), QDtlsError::InvalidOperation); | 
| 1061 |  | 
| 1062 |     // 1.2 Finish the handshake: | 
| 1063 |     testLoop.enterLoopMSecs(ms: handshakeTimeoutMS); | 
| 1064 |     QVERIFY(!testLoop.timeout()); | 
| 1065 |  | 
| 1066 |     QDTLS_VERIFY_HANDSHAKE_SUCCESS(clientCrypto); | 
| 1067 |     QDTLS_VERIFY_HANDSHAKE_SUCCESS(serverCrypto); | 
| 1068 |  | 
| 1069 |     // 2. Change reading slots: | 
| 1070 |     connectEncryptedReadingSlots(); | 
| 1071 |  | 
| 1072 |     // 3. Test parameter validation: | 
| 1073 |     QVERIFY(clientCrypto->writeDatagramEncrypted(nullptr, serverExpectedPlainText) <= 0); | 
| 1074 |     QCOMPARE(clientCrypto->dtlsError(), QDtlsError::InvalidInputParameters); | 
| 1075 |     // 4. Write the client's message: | 
| 1076 |     qint64 clientBytesWritten = clientCrypto->writeDatagramEncrypted(socket: &clientSocket, dgram: serverExpectedPlainText); | 
| 1077 |     QDTLS_VERIFY_NO_ERROR(clientCrypto); | 
| 1078 |     QVERIFY(clientBytesWritten > 0); | 
| 1079 |  | 
| 1080 |     // 5. Exchange client/server messages: | 
| 1081 |     testLoop.enterLoopMSecs(ms: dataExchangeTimeoutMS); | 
| 1082 |     QVERIFY(!testLoop.timeout()); | 
| 1083 |  | 
| 1084 |     QCOMPARE(serverExpectedPlainText, serverReceivedPlainText); | 
| 1085 |     QCOMPARE(clientExpectedPlainText, clientReceivedPlainText); | 
| 1086 |  | 
| 1087 |     QFETCH(const bool, serverSideShutdown); | 
| 1088 |     DtlsPtr &crypto = serverSideShutdown ? serverCrypto : clientCrypto; | 
| 1089 |     QUdpSocket *socket = serverSideShutdown ? &serverSocket : &clientSocket; | 
| 1090 |     // 6. Parameter validation: | 
| 1091 |     QVERIFY(!crypto->shutdown(nullptr)); | 
| 1092 |     QCOMPARE(crypto->dtlsError(), QDtlsError::InvalidInputParameters); | 
| 1093 |     // 7. Send shutdown alert: | 
| 1094 |     QVERIFY(crypto->shutdown(socket)); | 
| 1095 |     QDTLS_VERIFY_NO_ERROR(crypto); | 
| 1096 |     QCOMPARE(crypto->handshakeState(), QDtls::HandshakeNotStarted); | 
| 1097 |     QVERIFY(!crypto->isConnectionEncrypted()); | 
| 1098 |     // 8. Receive this read notification and handle it: | 
| 1099 |     testLoop.enterLoopMSecs(ms: dataExchangeTimeoutMS); | 
| 1100 |     QVERIFY(!testLoop.timeout()); | 
| 1101 |  | 
| 1102 |     DtlsPtr &peerCrypto = serverSideShutdown ? clientCrypto : serverCrypto; | 
| 1103 |     QVERIFY(!peerCrypto->isConnectionEncrypted()); | 
| 1104 |     QCOMPARE(peerCrypto->handshakeState(), QDtls::HandshakeNotStarted); | 
| 1105 |     QCOMPARE(peerCrypto->dtlsError(), QDtlsError::RemoteClosedConnectionError); | 
| 1106 | } | 
| 1107 |  | 
| 1108 | void tst_QDtls::datagramFragmentation() | 
| 1109 | { | 
| 1110 |     connectHandshakeReadingSlots(); | 
| 1111 |  | 
| 1112 |     auto serverConfig = defaultServerConfig; | 
| 1113 |     serverConfig.setLocalCertificate(selfSignedCert); | 
| 1114 |     serverConfig.setPrivateKey(serverKeySS); | 
| 1115 |     QVERIFY(serverCrypto->setDtlsConfiguration(serverConfig)); | 
| 1116 |  | 
| 1117 |     auto clientConfig = QSslConfiguration::defaultDtlsConfiguration(); | 
| 1118 |     clientConfig.setPeerVerifyMode(QSslSocket::VerifyNone); | 
| 1119 |     QVERIFY(clientCrypto->setDtlsConfiguration(clientConfig)); | 
| 1120 |     QVERIFY(clientCrypto->setPeer(serverAddress, serverPort)); | 
| 1121 |  | 
| 1122 |     QVERIFY(clientCrypto->doHandshake(&clientSocket)); | 
| 1123 |  | 
| 1124 |     testLoop.enterLoopMSecs(ms: handshakeTimeoutMS); | 
| 1125 |     QVERIFY(!testLoop.timeout()); | 
| 1126 |  | 
| 1127 |     QDTLS_VERIFY_HANDSHAKE_SUCCESS(clientCrypto); | 
| 1128 |     QDTLS_VERIFY_HANDSHAKE_SUCCESS(serverCrypto); | 
| 1129 |  | 
| 1130 |     // Done with handshake, reconnect readyRead: | 
| 1131 |     connectEncryptedReadingSlots(); | 
| 1132 |  | 
| 1133 |     // Verify our dgram is not fragmented and some error set (either UnderlyingSocketError | 
| 1134 |     // if OpenSSL somehow had attempted a write or TlsFatalError in case OpenSSL | 
| 1135 |     // noticed how big the chunk is). | 
| 1136 |     QVERIFY(clientCrypto->writeDatagramEncrypted(&clientSocket, QByteArray(1024 * 17, Qt::Uninitialized)) <= 0); | 
| 1137 |     QVERIFY(clientCrypto->dtlsError() != QDtlsError::NoError); | 
| 1138 |     // Error to write does not mean QDtls is broken: | 
| 1139 |     QVERIFY(clientCrypto->isConnectionEncrypted()); | 
| 1140 |     QVERIFY(clientCrypto->writeDatagramEncrypted(&clientSocket, "Hello, I'm a tiny datagram" ) > 0); | 
| 1141 |     QDTLS_VERIFY_NO_ERROR(clientCrypto); | 
| 1142 | } | 
| 1143 |  | 
| 1144 | void tst_QDtls::handshakeReadyRead() | 
| 1145 | { | 
| 1146 |     QUdpSocket *socket = qobject_cast<QUdpSocket *>(object: sender()); | 
| 1147 |     Q_ASSERT(socket); | 
| 1148 |  | 
| 1149 |     if (socket->pendingDatagramSize() <= 0) | 
| 1150 |         return; | 
| 1151 |  | 
| 1152 |     const bool isServer = socket == &serverSocket; | 
| 1153 |     DtlsPtr &crypto = isServer ? serverCrypto : clientCrypto; | 
| 1154 |     DtlsPtr &peerCrypto = isServer ? clientCrypto : serverCrypto; | 
| 1155 |     QHostAddress addr; | 
| 1156 |     quint16 port = 0; | 
| 1157 |  | 
| 1158 |     QByteArray dgram(socket->pendingDatagramSize(), Qt::Uninitialized); | 
| 1159 |     const qint64 size = socket->readDatagram(data: dgram.data(), maxlen: dgram.size(), host: &addr, port: &port); | 
| 1160 |     if (size != dgram.size()) | 
| 1161 |         return; | 
| 1162 |  | 
| 1163 |     if (isServer) { | 
| 1164 |         if (!clientPort) { | 
| 1165 |             // It's probably an initial 'ClientHello' message. Let's set remote's | 
| 1166 |             // address/port. But first we make sure it is, indeed, 'ClientHello'. | 
| 1167 |             if (int(dgram.constData()[0]) != 22) | 
| 1168 |                 return; | 
| 1169 |  | 
| 1170 |             if (addr.isNull() || addr.isBroadcast()) // Could never be us (client), bail out | 
| 1171 |                 return; | 
| 1172 |  | 
| 1173 |             if (!crypto->setPeer(address: addr, port)) | 
| 1174 |                 return testLoop.exitLoop(); | 
| 1175 |  | 
| 1176 |             // Check parameter validation: | 
| 1177 |             if (crypto->doHandshake(socket: nullptr, dgram) || crypto->dtlsError() != QDtlsError::InvalidInputParameters) | 
| 1178 |                 return testLoop.exitLoop(); | 
| 1179 |  | 
| 1180 |             if (crypto->doHandshake(socket: &serverSocket, dgram: {}) || crypto->dtlsError() != QDtlsError::InvalidInputParameters) | 
| 1181 |                 return testLoop.exitLoop(); | 
| 1182 |  | 
| 1183 |             // Make sure we cannot decrypt yet: | 
| 1184 |             const QByteArray dummyDgram = crypto->decryptDatagram(socket: &serverSocket, dgram); | 
| 1185 |             if (dummyDgram.size() > 0 || crypto->dtlsError() != QDtlsError::InvalidOperation) | 
| 1186 |                 return testLoop.exitLoop(); | 
| 1187 |  | 
| 1188 |             clientAddress = addr; | 
| 1189 |             clientPort = port; | 
| 1190 |         } else if (clientPort != port || clientAddress != addr) { | 
| 1191 |             return; | 
| 1192 |         } | 
| 1193 |  | 
| 1194 |         if (serverDropDgram) { | 
| 1195 |             serverDropDgram = false; | 
| 1196 |             return; | 
| 1197 |         } | 
| 1198 |     } else if (clientDropDgram) { | 
| 1199 |         clientDropDgram = false; | 
| 1200 |         return; | 
| 1201 |     } | 
| 1202 |  | 
| 1203 |     if (!crypto->doHandshake(socket, dgram)) | 
| 1204 |         return testLoop.exitLoop(); | 
| 1205 |  | 
| 1206 |     const auto state = crypto->handshakeState(); | 
| 1207 |     if (state != QDtls::HandshakeInProgress && state != QDtls::HandshakeComplete) | 
| 1208 |         return testLoop.exitLoop(); | 
| 1209 |  | 
| 1210 |     if (state == QDtls::HandshakeComplete && peerCrypto->handshakeState() == QDtls::HandshakeComplete) | 
| 1211 |         testLoop.exitLoop(); | 
| 1212 | } | 
| 1213 |  | 
| 1214 | void tst_QDtls::encryptedReadyRead() | 
| 1215 | { | 
| 1216 |     QUdpSocket *socket = qobject_cast<QUdpSocket *>(object: sender()); | 
| 1217 |     Q_ASSERT(socket); | 
| 1218 |  | 
| 1219 |     if (socket->pendingDatagramSize() <= 0) | 
| 1220 |         return; | 
| 1221 |  | 
| 1222 |     QByteArray dtlsMessage(int(socket->pendingDatagramSize()), Qt::Uninitialized); | 
| 1223 |     QHostAddress addr; | 
| 1224 |     quint16 port = 0; | 
| 1225 |     const qint64 bytesRead = socket->readDatagram(data: dtlsMessage.data(), maxlen: dtlsMessage.size(), host: &addr, port: &port); | 
| 1226 |     if (bytesRead <= 0) | 
| 1227 |         return; | 
| 1228 |  | 
| 1229 |     dtlsMessage.resize(size: int(bytesRead)); | 
| 1230 |  | 
| 1231 |     if (socket == &serverSocket) { | 
| 1232 |         if (addr != clientAddress || port != clientPort) | 
| 1233 |             return; | 
| 1234 |  | 
| 1235 |         if (serverExpectedPlainText == dtlsMessage) // No way it can happen! | 
| 1236 |             return testLoop.exitLoop(); | 
| 1237 |  | 
| 1238 |         serverReceivedPlainText = serverCrypto->decryptDatagram(socket: nullptr, dgram: dtlsMessage); | 
| 1239 |         if (serverReceivedPlainText.size() > 0 || serverCrypto->dtlsError() != QDtlsError::InvalidInputParameters) | 
| 1240 |             return testLoop.exitLoop(); | 
| 1241 |  | 
| 1242 |         serverReceivedPlainText = serverCrypto->decryptDatagram(socket: &serverSocket, dgram: dtlsMessage); | 
| 1243 |  | 
| 1244 |         const int messageType = dtlsMessage.data()[0]; | 
| 1245 |         if (serverReceivedPlainText != serverExpectedPlainText | 
| 1246 |             && (messageType == 23 || messageType == 21)) { | 
| 1247 |             // Type 23 is for application data, 21 is shutdown alert. Here we test | 
| 1248 |             // write/read operations and shutdown alerts, not expecting and thus | 
| 1249 |             // ignoring any other types of messages. | 
| 1250 |             return testLoop.exitLoop(); | 
| 1251 |         } | 
| 1252 |  | 
| 1253 |         if (serverCrypto->dtlsError() != QDtlsError::NoError) | 
| 1254 |             return testLoop.exitLoop(); | 
| 1255 |  | 
| 1256 |         // Verify it cannot be done twice: | 
| 1257 |         const QByteArray replayed = serverCrypto->decryptDatagram(socket: &serverSocket, dgram: dtlsMessage); | 
| 1258 |         if (replayed.size() > 0) | 
| 1259 |             return testLoop.exitLoop(); | 
| 1260 |  | 
| 1261 |         if (serverCrypto->writeDatagramEncrypted(socket: &serverSocket, dgram: clientExpectedPlainText) <= 0) | 
| 1262 |             testLoop.exitLoop(); | 
| 1263 |     } else { | 
| 1264 |         if (port != serverPort) | 
| 1265 |             return; | 
| 1266 |  | 
| 1267 |         if (clientExpectedPlainText == dtlsMessage) // What a disaster! | 
| 1268 |             return testLoop.exitLoop(); | 
| 1269 |  | 
| 1270 |         clientReceivedPlainText = clientCrypto->decryptDatagram(socket: &clientSocket, dgram: dtlsMessage); | 
| 1271 |         testLoop.exitLoop(); | 
| 1272 |     } | 
| 1273 | } | 
| 1274 |  | 
| 1275 | void tst_QDtls::pskRequested(QSslPreSharedKeyAuthenticator *auth) | 
| 1276 | { | 
| 1277 |     Q_ASSERT(auth); | 
| 1278 |  | 
| 1279 |     auth->setPreSharedKey(presharedKey); | 
| 1280 | } | 
| 1281 |  | 
| 1282 | void tst_QDtls::handleHandshakeTimeout() | 
| 1283 | { | 
| 1284 |     auto crypto = qobject_cast<QDtls *>(object: sender()); | 
| 1285 |     Q_ASSERT(crypto); | 
| 1286 |  | 
| 1287 |     if (!crypto->handleTimeout(socket: &clientSocket)) | 
| 1288 |         testLoop.exitLoop(); | 
| 1289 | } | 
| 1290 |  | 
| 1291 | void tst_QDtls::clientServerData() | 
| 1292 | { | 
| 1293 |     QTest::addColumn<QSslSocket::SslMode>(name: "mode" ); | 
| 1294 |  | 
| 1295 |     QTest::addRow(format: "client" ) << QSslSocket::SslClientMode; | 
| 1296 |     QTest::addRow(format: "server" ) << QSslSocket::SslServerMode; | 
| 1297 | } | 
| 1298 |  | 
| 1299 | void tst_QDtls::connectHandshakeReadingSlots() | 
| 1300 | { | 
| 1301 |     connect(sender: &serverSocket, signal: &QUdpSocket::readyRead, receiver: this, slot: &tst_QDtls::handshakeReadyRead); | 
| 1302 |     connect(sender: &clientSocket, signal: &QUdpSocket::readyRead, receiver: this, slot: &tst_QDtls::handshakeReadyRead); | 
| 1303 | } | 
| 1304 |  | 
| 1305 | void tst_QDtls::connectEncryptedReadingSlots() | 
| 1306 | { | 
| 1307 |     serverSocket.disconnect(); | 
| 1308 |     clientSocket.disconnect(); | 
| 1309 |     connect(sender: &serverSocket, signal: &QUdpSocket::readyRead, receiver: this, slot: &tst_QDtls::encryptedReadyRead); | 
| 1310 |     connect(sender: &clientSocket, signal: &QUdpSocket::readyRead, receiver: this, slot: &tst_QDtls::encryptedReadyRead); | 
| 1311 | } | 
| 1312 |  | 
| 1313 | bool tst_QDtls::verificationErrorDetected(QSslError::SslError code) const | 
| 1314 | { | 
| 1315 |     Q_ASSERT(clientCrypto.data()); | 
| 1316 |  | 
| 1317 |     const auto errors = clientCrypto->peerVerificationErrors(); | 
| 1318 |     for (const QSslError &error : errors) { | 
| 1319 |         if (error.error() == code) | 
| 1320 |             return true; | 
| 1321 |     } | 
| 1322 |  | 
| 1323 |     return false; | 
| 1324 | } | 
| 1325 |  | 
| 1326 | QHostAddress tst_QDtls::toNonAny(const QHostAddress &addr) | 
| 1327 | { | 
| 1328 |     if (addr == QHostAddress::Any || addr == QHostAddress::AnyIPv4) | 
| 1329 |         return QHostAddress::LocalHost; | 
| 1330 |     if (addr == QHostAddress::AnyIPv6) | 
| 1331 |         return QHostAddress::LocalHostIPv6; | 
| 1332 |     return addr; | 
| 1333 | } | 
| 1334 |  | 
| 1335 | QT_END_NAMESPACE | 
| 1336 |  | 
| 1337 | QTEST_MAIN(tst_QDtls) | 
| 1338 |  | 
| 1339 | #include "tst_qdtls.moc" | 
| 1340 |  |