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
53QT_BEGIN_NAMESPACE
54
55namespace
56{
57
58bool dtlsErrorIsCleared(const QDtls &dtls)
59{
60 return dtls.dtlsError() == QDtlsError::NoError && dtls.dtlsErrorString().isEmpty();
61}
62
63using DtlsPtr = QScopedPointer<QDtls>;
64
65bool 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
80class tst_QDtls : public QObject
81{
82 Q_OBJECT
83
84public slots:
85 void initTestCase();
86 void init();
87
88private 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
118protected slots:
119 void handshakeReadyRead();
120 void encryptedReadyRead();
121 void pskRequested(QSslPreSharedKeyAuthenticator *auth);
122 void handleHandshakeTimeout();
123
124private:
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
161QT_END_NAMESPACE
162
163Q_DECLARE_METATYPE(QSsl::SslProtocol)
164Q_DECLARE_METATYPE(QSslSocket::SslMode)
165Q_DECLARE_METATYPE(QSslSocket::PeerVerifyMode)
166Q_DECLARE_METATYPE(QList<QSslCertificate>)
167Q_DECLARE_METATYPE(QSslKey)
168Q_DECLARE_METATYPE(QVector<QSslError>)
169
170QT_BEGIN_NAMESPACE
171
172void 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
200void 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
235void tst_QDtls::construction_data()
236{
237 clientServerData();
238}
239
240void 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
272void tst_QDtls::configuration_data()
273{
274 clientServerData();
275}
276
277void 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
333void 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
347void tst_QDtls::setPeer_data()
348{
349 clientServerData();
350}
351
352void 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
388void 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
396void 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
469void 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
504void 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
539void 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
547void 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
597void 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
619void 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
655void 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
663void 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
720void 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
731void 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
762void 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
804void 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
857void 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
927void 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
991void 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
1019void 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
1027void 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
1108void 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
1144void 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
1214void 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
1275void tst_QDtls::pskRequested(QSslPreSharedKeyAuthenticator *auth)
1276{
1277 Q_ASSERT(auth);
1278
1279 auth->setPreSharedKey(presharedKey);
1280}
1281
1282void 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
1291void 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
1299void 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
1305void 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
1313bool 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
1326QHostAddress 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
1335QT_END_NAMESPACE
1336
1337QTEST_MAIN(tst_QDtls)
1338
1339#include "tst_qdtls.moc"
1340

source code of qtbase/tests/auto/network/ssl/qdtls/tst_qdtls.cpp