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