From 8d057fea987e7dc72b9bf2111c5f75bdd541f496 Mon Sep 17 00:00:00 2001 From: Lars Schmertmann Date: Fri, 24 Jan 2020 13:59:04 +0100 Subject: Add test case for QSslSocket to show the wrong behavior Task-number: QTBUG-81661 Change-Id: I4ed2ad3a22bd5439751328d915e9984eb89397d1 Reviewed-by: Edward Welbourne Reviewed-by: Timur Pocheptsov --- .../ssl/qsslsocket/certs/selfsigned-client.crt | 18 ++ .../ssl/qsslsocket/certs/selfsigned-client.key | 27 ++ .../ssl/qsslsocket/certs/selfsigned-server.crt | 18 ++ .../ssl/qsslsocket/certs/selfsigned-server.key | 27 ++ .../auto/network/ssl/qsslsocket/tst_qsslsocket.cpp | 283 +++++++++++++++++++++ 5 files changed, 373 insertions(+) create mode 100644 tests/auto/network/ssl/qsslsocket/certs/selfsigned-client.crt create mode 100644 tests/auto/network/ssl/qsslsocket/certs/selfsigned-client.key create mode 100644 tests/auto/network/ssl/qsslsocket/certs/selfsigned-server.crt create mode 100644 tests/auto/network/ssl/qsslsocket/certs/selfsigned-server.key (limited to 'tests') diff --git a/tests/auto/network/ssl/qsslsocket/certs/selfsigned-client.crt b/tests/auto/network/ssl/qsslsocket/certs/selfsigned-client.crt new file mode 100644 index 0000000000..88da2db920 --- /dev/null +++ b/tests/auto/network/ssl/qsslsocket/certs/selfsigned-client.crt @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC6TCCAdECCC/r9KvmbWTKMA0GCSqGSIb3DQEBCwUAMDUxFDASBgNVBAMMC0F1 +c3dlaXNBcHAyMR0wGwYDVQQFExQxODIzNTE0MTY0NzI5NDg5NDM3MTAiGA8xOTcw +MDEwMTAwMDAwMFoYDzk5OTkxMjMxMjM1OTU5WjA1MRQwEgYDVQQDDAtBdXN3ZWlz +QXBwMjEdMBsGA1UEBRMUMTgyMzUxNDE2NDcyOTQ4OTQzNzEwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQCahBpcZyr+PJBCpolzQeFVvDKABwlpdRKGZ8qq +jD4sq2L7VlBJslgJGv5vsB5oJbnX1FFEu4Uw2kYb/LhnFCEXEFtGKRpWOEZOOqWb +4l4q2MCa82ZCoIDt8yoAt0sSShbtR6pjW+l0lwAOEpfGvMaMVo5JUyspRxhl1dSu +sS2Wf65zliqF5VSM2r4xMfJ6LVytxDZsGfTe/HFT2OYYrF+UQZg0mNL39rYWOK4R +xoOz8eLl3K5hKuHNfn5zPt5QtMhaIvebijBg23xJpl+BeoS37WzaK1f+NyWZKPFb +rttvSnFxpkyRHqJJ5piNGH6pkQ1+zhd7uh7eOIwxktjYBOFzAgMBAAEwDQYJKoZI +hvcNAQELBQADggEBADw3MYPft+X78OK/2HAltzsKjfxv/D5qVizm9hcyG1GYe5pS +qgFn0trCyJopYdbRr+hP7CuHwMmv62CZiHSog3CBPoUh19JENUDGbHXxTEFleB0i +Fd8I2+WvRjbQ+ehaeTJPx88v5kkJnB2tZUNZuhEws8emCwr1G0TQv1tRYCR1Lp9i +8/I3FSFpL1zyk47WfM/THa279MPw9WtrFGA6oi36gH9mYxek7n/zQTVi54xDx9GT +KigBYqavjFdNXryjLTCCtJpMTDePgP66NAUnxn0D/amI2vSbIN++PSTsBm+n4Ti5 +QW/ShFQDNb4bDiwjtTKCeKwvAp2/6GSHVkYy28M= +-----END CERTIFICATE----- diff --git a/tests/auto/network/ssl/qsslsocket/certs/selfsigned-client.key b/tests/auto/network/ssl/qsslsocket/certs/selfsigned-client.key new file mode 100644 index 0000000000..9e59342963 --- /dev/null +++ b/tests/auto/network/ssl/qsslsocket/certs/selfsigned-client.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAmoQaXGcq/jyQQqaJc0HhVbwygAcJaXUShmfKqow+LKti+1ZQ +SbJYCRr+b7AeaCW519RRRLuFMNpGG/y4ZxQhFxBbRikaVjhGTjqlm+JeKtjAmvNm +QqCA7fMqALdLEkoW7UeqY1vpdJcADhKXxrzGjFaOSVMrKUcYZdXUrrEtln+uc5Yq +heVUjNq+MTHyei1crcQ2bBn03vxxU9jmGKxflEGYNJjS9/a2FjiuEcaDs/Hi5dyu +YSrhzX5+cz7eULTIWiL3m4owYNt8SaZfgXqEt+1s2itX/jclmSjxW67bb0pxcaZM +kR6iSeaYjRh+qZENfs4Xe7oe3jiMMZLY2AThcwIDAQABAoIBAFjgvc0C5t8AdTZx +VsS+U2Aedang4lAPsE0xbIj3TFgjaTcLKfmKJUtvhIU39/WOJbz4+pofhvhXxVYZ +4vQfxvzeQrIzuFt52S7sWxA0gFgC/57hfKO8cQzt/u4UgJEPnupze5XVa47NwJFX +rof5U/erXgLdXQlMRMNm4QRvE7rp58E2MkSYNur0Xgy9L7cRcUQJ8iuMaxBpOzhS +fbNFi5zT7RCGcQSIDcb1JFlgs5tMUs6jzLoDSVD2+vvsN4i4LAAPkJSGTGed5vY1 +xn4G8KPR4HHrnBYEb0SGu4ZTznOnQ+JSKhQrbnvEzXM4RTfjqn0YvF8x70+pWSMi +Fb4mlBECgYEAzW82O79HAlMm8LD7J4byPfVc/1M5/JOnE9H+RR5Vt4jZQGyjCmJu +cj4UeZyVim0xg30sSYrJ2Urd27CtHp+sMgHkvJt3/ZgcfMZJbMKNGq/OUtV8s/cA +nkU++/LgeW8r7wpaDjT7bfnOdcf16mYoXrmk0rTJvRqGXCBvCxtt5bsCgYEAwIxu +vZjPV4Vu/VX6sH2d31D9EFZuZKjGhqukFVtRqLbeosqT9mA+LhQ/wP5qoR2gLQbe +EwxJLJwGFjUhyhbHNlo6oHv3fWkzmHIMPwDRRI3Ktwi/50SwNSnyERUQcLaiwqKx +BqaxPYNnspUt0nKE0LFZsSlrfEyxajqAlUEgm6kCgYAV+uQumFScpxDvh8AXhpS8 +lFgS6XC22YVy1XEDLC+3p2i3P+hh4A45IvNF378QRIabrvTiGXtnSF9cdhbPw/3E +i/dRRsEb3P6PSxfoDxjR1iWZL0Zcav0h8f6/LkleNMralJz2EC0moye36mEhZzTC +jdJYyQccuI3PpZi7839aqQKBgGezOnEiO4kHdB88jyc+gCglliWWZx4PR9x/1H8s +D26uDnneYJHwg4yNm0h1vTfInNujNzdLBp3f6edL9kbAvcmoDqsgGMqSPhd8VNwZ +tJsXQnYRYElN1RjM3nIUxiXuNvpcZLsQS6S1gMPNVEBjLOS4n3WquRjYtTRhDZ9U +1BsBAoGAUFrIatOLFhcgaqENHyUbMx5uSx0lIfF6Xd5KIAgi+btdmugHe+NK8Cd2 +Rc2bQLQ9K1SvKFX6nFuEsGxnXkKuyhL/j0Kgm8nZin4uAcrtFnNdFumvCL6YgYSc +IvvM+uVfGEdbqm4pTuiLBfzOXIIy3kVlLGo402QG1pBzOtmsRMs= +-----END RSA PRIVATE KEY----- diff --git a/tests/auto/network/ssl/qsslsocket/certs/selfsigned-server.crt b/tests/auto/network/ssl/qsslsocket/certs/selfsigned-server.crt new file mode 100644 index 0000000000..c97d27721c --- /dev/null +++ b/tests/auto/network/ssl/qsslsocket/certs/selfsigned-server.crt @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC5TCCAc0CCAO22gNi0v20MA0GCSqGSIb3DQEBCwUAMDMxFDASBgNVBAMMC0F1 +c3dlaXNBcHAyMRswGQYDVQQFExIyNTIxMTE1NjY3NjM2MjExODgwIhgPMTk3MDAx +MDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowMzEUMBIGA1UEAwwLQXVzd2Vpc0Fw +cDIxGzAZBgNVBAUTEjI1MjExMTU2Njc2MzYyMTE4ODCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBAL+Fl6v5dcU7qk7vbINclWOhvCe/uklKnXV2QU382x7g +qpbYxJiJvz24C6tgDMmE0pwEz6PiCbh1dkc8+9cdp37eBcFLCOXYQb27gqVVyVtu +xO0LLVXPCv48bGSwljOz0FRC3FolzWxzrZogM/i2b/lmehHJ3D4ejmINmIgtFJ9P +JNNCH4Oh5YEbaFFlNf2m7lCoSuQkOlLZcGeLoipK2XvhZJff6c1uxValh/Mx5dNB +5Mgd5cOZSSEhwf7mcE8C3SHVfjeNfZGIqlkwdY8lvAOjirAtj6Yl88sJOUID/Q/N +hU9D8IZy6+Bk2cJQwI/Gzr590VYvlSTI+6lXr//oBBECAwEAATANBgkqhkiG9w0B +AQsFAAOCAQEArSMO88AYT+9tPCl5lXtSRa0OycqKNlW58GujxIDuR8WX1eFmGSHQ +uijo5KPYUnqydZzAewGC8NvC9WcLwFltNZ9igXikUHiAHc1JLfW7+7SgKpwOUb02 +rJkUkpPA/SmwkLSKYiR1prt5wgSulU1HPBESep05DfR8MCU5+KHkLyXDqtrbudJ4 +lQd9dSKJFn+cSjUC5JNxCPHoIISe7hfGFMLkd0/tVfSIXLVOAZG4K6zExUdjyPi8 +qEuPq6QCRyIJbYQc5HfnARgwK6GXHqkyLWlqK946Yz8VOba7Nan5uQ6xCjUMHw8Z +z/673o/3DCaQ9N6dWahNQ09a9ZH8U1X4iA== +-----END CERTIFICATE----- diff --git a/tests/auto/network/ssl/qsslsocket/certs/selfsigned-server.key b/tests/auto/network/ssl/qsslsocket/certs/selfsigned-server.key new file mode 100644 index 0000000000..b7be118cb9 --- /dev/null +++ b/tests/auto/network/ssl/qsslsocket/certs/selfsigned-server.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAv4WXq/l1xTuqTu9sg1yVY6G8J7+6SUqddXZBTfzbHuCqltjE +mIm/PbgLq2AMyYTSnATPo+IJuHV2Rzz71x2nft4FwUsI5dhBvbuCpVXJW27E7Qst +Vc8K/jxsZLCWM7PQVELcWiXNbHOtmiAz+LZv+WZ6EcncPh6OYg2YiC0Un08k00If +g6HlgRtoUWU1/abuUKhK5CQ6UtlwZ4uiKkrZe+Fkl9/pzW7FVqWH8zHl00HkyB3l +w5lJISHB/uZwTwLdIdV+N419kYiqWTB1jyW8A6OKsC2PpiXzywk5QgP9D82FT0Pw +hnLr4GTZwlDAj8bOvn3RVi+VJMj7qVev/+gEEQIDAQABAoIBADdoXsjSEtBMwqiz +e6FFV7LLR7P4M9ygSY2B+MKnNH1qYe/iJn4626jvZfDeiNSEKKoaejffXRCQaveR +HQrO+XYqpV+WZayZM+vAI7vRZb+d/DrX0PXSQEvtDy7SJ6Itk0fNUBKEfTmy/bZp +Op/pp9tvWkFrNNyD2o1jgY1j/WNY8g605m0oURJ9WQsMUu/Kzu+NMoaKTIoQGb3d +dP71F4KaTXHYxj3B0c+y0NedKbrvnBsP6XbEpgJBaXjtD9z+z/aMF6dmuvpkx7uY +qzwPMRw05QPyJ9x+1V/v4TytY5f596NgW2niVj77BunkZasTYIEX7bjByrlTeLdx +xvPRpAECgYEA5KkM/ORbhN1oaw9+tQxA48oG2DFqChBr+vc4NU4j5SNFn9ks5nHI +xdJNZ9k+bjVUkBP4m88Wd07SW9zXCL8Q5lczb+p5SWl/Pp7ltqaxpH17uzamsaIv +KIBkeJTOU5TuWdXiV5FY+ofK9ojyEaqX1tmylWnoVe4bIMRWXE5bMSkCgYEA1mvJ +snkNzPFG0RK7ikjsNxrhzE07+7RSnoM9WeW8y2lvQ9MjdR6eOgqnnlcdk2A7OVbf +culNgLc0qx/PxZ4BV+8yLLb1EBBGvuVG+x4a6H2mLHdFCJekByZHaQNs9ogVLvdv +3z8D59KknBUjtj9dCw90Z41yMM4kpWMG9yfSEKkCgYEAvuCvytwF2d/JrrV8nD3i +XUTkecymLEiRGysMbNMR+9F56XotlSEe7KQloa8kAnPaZ3uEaOxyYJ4X1D+B8fct +cFsSwTYGkVXTtr6GG/cDC8EEbL+uX1J382Nae54croEAh1WYYGkg0eJRd4PSLxUt +M1j/TuLd4/2j/7JmNR/j2CECgYBdB3MBHghgzKXe+/OmMbFazyz8SN4nfLsDzwkF +QenBj0MY+DhADkK0B/9lcYKBeJT5cbmMz7AykkolnK22nbETh9ILGG4GxCkNlchQ +F2WxTSKV1EF9Ut11xKPi6fuSksQuFmjRQTPelsOYfIt7/M3PiKsGapYKmsXHg8l3 +3i0D0QKBgQCi+HNOaYqduxwjrj8h4eUbiwjID8DCNJ+jXsuGVa6jcsfFpdpivx2c +ytYSXuTXLRq0I3c1ChUOGQQeztJ5GtCPnXjLHHMf3f6yr7Pk56AUmUsaIlR1Q2Zo +gqpFD8zYD5UFc2KM7Y38YTh4j82uDzDvHBBFpli7dEmSn2WpcmzFag== +-----END RSA PRIVATE KEY----- diff --git a/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp b/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp index 321eede9c9..e968f5b0f1 100644 --- a/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp +++ b/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp @@ -271,6 +271,10 @@ private slots: #if QT_CONFIG(openssl) void alertMissingCertificate(); void alertInvalidCertificate(); + void selfSignedCertificates_data(); + void selfSignedCertificates(); + void pskHandshake_data(); + void pskHandshake(); #endif // openssl void setEmptyDefaultConfiguration(); // this test should be last @@ -1232,7 +1236,9 @@ public: QList ciphers; signals: + void sslErrors(const QList &errors); void socketError(QAbstractSocket::SocketError); + void handshakeInterruptedOnError(const QSslError& rrror); void gotAlert(QSsl::AlertLevel level, QSsl::AlertType type, const QString &message); void alertSent(QSsl::AlertLevel level, QSsl::AlertType type, const QString &message); @@ -1245,9 +1251,13 @@ protected: configuration.setProtocol(protocol); if (ignoreSslErrors) connect(socket, SIGNAL(sslErrors(QList)), this, SLOT(ignoreErrorSlot())); + else + connect(socket, SIGNAL(sslErrors(QList)), this, SIGNAL(sslErrors(QList))); connect(socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), this, SIGNAL(socketError(QAbstractSocket::SocketError))); + connect(socket, &QSslSocket::handshakeInterruptedOnError, this, &SslServer::handshakeInterruptedOnError); connect(socket, &QSslSocket::alertReceived, this, &SslServer::gotAlert); connect(socket, &QSslSocket::alertSent, this, &SslServer::alertSent); + connect(socket, &QSslSocket::preSharedKeyAuthenticationRequired, this, &SslServer::preSharedKeyAuthenticationRequired); QFile file(m_keyFile); QVERIFY(file.open(QIODevice::ReadOnly)); @@ -1295,6 +1305,11 @@ protected: } protected slots: + void preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator *authenticator) + { + authenticator->setPreSharedKey("123456"); + } + void ignoreErrorSlot() { socket->ignoreSslErrors(); @@ -4418,6 +4433,274 @@ void tst_QSslSocket::alertInvalidCertificate() QVERIFY(!clientSocket.isEncrypted()); } +void tst_QSslSocket::selfSignedCertificates_data() +{ + QTest::addColumn("clientKnown"); + + QTest::newRow("Client known") << true; + QTest::newRow("Client unknown") << false; +} + +void tst_QSslSocket::selfSignedCertificates() +{ + // In this test we want to check the behavior of the client/server when + // self-signed certificates are used and the client is un/known to the server. + QFETCH(bool, clientKnown); + + QFETCH_GLOBAL(const bool, setProxy); + if (setProxy) // Not what we test here, bail out. + return; + + SslServer server(testDataDir + "certs/selfsigned-server.key", + testDataDir + "certs/selfsigned-server.crt"); + server.protocol = QSsl::TlsV1_2; + server.ignoreSslErrors = false; + server.peerVerifyMode = QSslSocket::VerifyPeer; + + if (!server.listen(QHostAddress::LocalHost)) + QSKIP("SslServer::listen() returned false"); + + QFile clientFile(testDataDir + "certs/selfsigned-client.key"); + QVERIFY(clientFile.open(QIODevice::ReadOnly)); + QSslKey clientKey(clientFile.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey); + QSslCertificate clientCert + = QSslCertificate::fromPath(testDataDir + "certs/selfsigned-client.crt").first(); + + server.config.setCiphers({QSslCipher("DHE-RSA-AES256-SHA256")}); + server.config.setHandshakeMustInterruptOnError(true); + server.config.setMissingCertificateIsFatal(true); + if (clientKnown) + server.config.setCaCertificates({clientCert}); + + connect(&server, &SslServer::sslErrors, + [&server](const QList &errors) { + QCOMPARE(errors.size(), 1); + QVERIFY(errors.first().error() == QSslError::SelfSignedCertificate); + } + ); + connect(&server, &SslServer::socketError, + [&server](QAbstractSocket::SocketError socketError) { + QVERIFY(socketError == QAbstractSocket::SslHandshakeFailedError); + } + ); + connect(&server, &SslServer::handshakeInterruptedOnError, + [&server](const QSslError& error) { + QVERIFY(error.error() == QSslError::SelfSignedCertificate); + server.socket->continueInterruptedHandshake(); + } + ); + + QSslSocket clientSocket; + auto configuration = QSslConfiguration::defaultConfiguration(); + configuration.setProtocol(QSsl::TlsV1_2); + configuration.setCiphers({QSslCipher("DHE-RSA-AES256-SHA256")}); + configuration.setPrivateKey(clientKey); + configuration.setLocalCertificate(clientCert); + configuration.setPeerVerifyMode(QSslSocket::VerifyPeer); + configuration.setHandshakeMustInterruptOnError(true); + configuration.setMissingCertificateIsFatal(true); + clientSocket.setSslConfiguration(configuration); + + connect(&clientSocket, &QSslSocket::sslErrors, + [&clientSocket](const QList &errors) { + for (const auto error : errors) { + if (error.error() == QSslError::HostNameMismatch) { + QVERIFY(errors.size() == 2); + clientSocket.ignoreSslErrors(errors); + } else { + QVERIFY(error.error() == QSslError::SelfSignedCertificate); + } + } + } + ); + connect(&clientSocket, &QAbstractSocket::errorOccurred, + [&clientSocket](QAbstractSocket::SocketError socketError) { + QVERIFY(socketError == QAbstractSocket::RemoteHostClosedError); + } + ); + connect(&clientSocket, &QSslSocket::handshakeInterruptedOnError, + [&clientSocket](const QSslError& error) { + QVERIFY(error.error() == QSslError::SelfSignedCertificate); + clientSocket.continueInterruptedHandshake(); + } + ); + + QSignalSpy serverSpy(&server, &SslServer::alertSent); + QSignalSpy clientSpy(&clientSocket, &QSslSocket::alertReceived); + + clientSocket.connectToHostEncrypted(server.serverAddress().toString(), server.serverPort()); + + QTestEventLoop runner; + QTimer::singleShot(500, + [&runner]() { + runner.exitLoop(); + } + ); + + int waitFor = 2; + auto earlyQuitter = [&runner, &waitFor](QAbstractSocket::SocketError) { + if (!--waitFor) + runner.exitLoop(); + }; + + // Presumably, RemoteHostClosedError for the client and SslHandshakeError + // for the server: + connect(&clientSocket, &QAbstractSocket::errorOccurred, earlyQuitter); + connect(&server, &SslServer::socketError, earlyQuitter); + + runner.enterLoopMSecs(1000); + + if (clientKnown) { + QCOMPARE(serverSpy.count(), 0); + QCOMPARE(clientSpy.count(), 0); + QVERIFY(server.socket && server.socket->isEncrypted()); + QVERIFY(clientSocket.isEncrypted()); + } else { + QVERIFY(serverSpy.count() > 0); + QEXPECT_FAIL("", "Failing to trigger signal, QTBUG-81661", Continue); + QVERIFY(clientSpy.count() > 0); + QVERIFY(server.socket && !server.socket->isEncrypted()); + QVERIFY(!clientSocket.isEncrypted()); + } +} + +void tst_QSslSocket::pskHandshake_data() +{ + QTest::addColumn("pskRight"); + + QTest::newRow("Psk right") << true; + QTest::newRow("Psk wrong") << false; +} + +void tst_QSslSocket::pskHandshake() +{ + // In this test we want to check the behavior of the + // client/server when a preshared key (right/wrong) is used. + QFETCH(bool, pskRight); + + QFETCH_GLOBAL(const bool, setProxy); + if (setProxy) // Not what we test here, bail out. + return; + + SslServer server(testDataDir + "certs/selfsigned-server.key", + testDataDir + "certs/selfsigned-server.crt"); + server.protocol = QSsl::TlsV1_2; + server.ignoreSslErrors = false; + server.peerVerifyMode = QSslSocket::VerifyPeer; + + if (!server.listen(QHostAddress::LocalHost)) + QSKIP("SslServer::listen() returned false"); + + QFile clientFile(testDataDir + "certs/selfsigned-client.key"); + QVERIFY(clientFile.open(QIODevice::ReadOnly)); + QSslKey clientKey(clientFile.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey); + QSslCertificate clientCert + = QSslCertificate::fromPath(testDataDir + "certs/selfsigned-client.crt").first(); + + server.config.setCiphers({QSslCipher("RSA-PSK-AES128-CBC-SHA256")}); + server.config.setHandshakeMustInterruptOnError(true); + server.config.setMissingCertificateIsFatal(true); + + connect(&server, &SslServer::sslErrors, + [&server](const QList &errors) { + QCOMPARE(errors.size(), 1); + QVERIFY(errors.first().error() == QSslError::SelfSignedCertificate); + server.socket->ignoreSslErrors(errors); + } + ); + connect(&server, &SslServer::socketError, + [&server](QAbstractSocket::SocketError socketError) { + QVERIFY(socketError == QAbstractSocket::SslHandshakeFailedError); + } + ); + connect(&server, &SslServer::handshakeInterruptedOnError, + [&server](const QSslError& error) { + QVERIFY(error.error() == QSslError::SelfSignedCertificate); + server.socket->continueInterruptedHandshake(); + } + ); + + QSslSocket clientSocket; + auto configuration = QSslConfiguration::defaultConfiguration(); + configuration.setProtocol(QSsl::TlsV1_2); + configuration.setCiphers({QSslCipher("RSA-PSK-AES128-CBC-SHA256")}); + configuration.setPrivateKey(clientKey); + configuration.setLocalCertificate(clientCert); + configuration.setPeerVerifyMode(QSslSocket::VerifyPeer); + configuration.setHandshakeMustInterruptOnError(true); + configuration.setMissingCertificateIsFatal(true); + clientSocket.setSslConfiguration(configuration); + + connect(&clientSocket, &QSslSocket::preSharedKeyAuthenticationRequired, + [&clientSocket, pskRight](QSslPreSharedKeyAuthenticator *authenticator) { + authenticator->setPreSharedKey(pskRight ? "123456": "654321"); + } + ); + + connect(&clientSocket, &QSslSocket::sslErrors, + [&clientSocket](const QList &errors) { + for (const auto error : errors) { + if (error.error() == QSslError::HostNameMismatch) { + QVERIFY(errors.size() == 2); + clientSocket.ignoreSslErrors(errors); + } else { + QVERIFY(error.error() == QSslError::SelfSignedCertificate); + } + } + } + ); + connect(&clientSocket, &QAbstractSocket::errorOccurred, + [&clientSocket](QAbstractSocket::SocketError socketError) { + QVERIFY(socketError == QAbstractSocket::SslHandshakeFailedError); + } + ); + connect(&clientSocket, &QSslSocket::handshakeInterruptedOnError, + [&clientSocket](const QSslError& error) { + QVERIFY(error.error() == QSslError::SelfSignedCertificate); + clientSocket.continueInterruptedHandshake(); + } + ); + + QSignalSpy serverSpy(&server, &SslServer::alertSent); + QSignalSpy clientSpy(&clientSocket, &QSslSocket::alertReceived); + + clientSocket.connectToHostEncrypted(server.serverAddress().toString(), server.serverPort()); + + QTestEventLoop runner; + QTimer::singleShot(500, [&runner]() { + runner.exitLoop(); + }); + + int waitFor = 2; + auto earlyQuitter = [&runner, &waitFor](QAbstractSocket::SocketError) { + if (!--waitFor) + runner.exitLoop(); + }; + + // Presumably, RemoteHostClosedError for the client and SslHandshakeError + // for the server: + connect(&clientSocket, &QAbstractSocket::errorOccurred, earlyQuitter); + connect(&server, &SslServer::socketError, earlyQuitter); + + runner.enterLoopMSecs(1000); + + if (pskRight) { + QCOMPARE(serverSpy.count(), 0); + QCOMPARE(clientSpy.count(), 0); + QVERIFY(server.socket && server.socket->isEncrypted()); + QVERIFY(clientSocket.isEncrypted()); + } else { + QVERIFY(serverSpy.count() > 0); + QCOMPARE(serverSpy.first().at(0).toInt(), static_cast(QSsl::AlertLevel::Fatal)); + QCOMPARE(serverSpy.first().at(1).toInt(), static_cast(QSsl::AlertType::BadRecordMac)); + QVERIFY(clientSpy.count() > 0); + QCOMPARE(clientSpy.first().at(0).toInt(), static_cast(QSsl::AlertLevel::Fatal)); + QCOMPARE(clientSpy.first().at(1).toInt(), static_cast(QSsl::AlertType::BadRecordMac)); + QVERIFY(server.socket && !server.socket->isEncrypted()); + QVERIFY(!clientSocket.isEncrypted()); + } +} + #endif // openssl QTEST_MAIN(tst_QSslSocket) -- cgit v1.2.3