diff options
Diffstat (limited to 'tests/auto/network/ssl/qsslkey/tst_qsslkey.cpp')
-rw-r--r-- | tests/auto/network/ssl/qsslkey/tst_qsslkey.cpp | 184 |
1 files changed, 113 insertions, 71 deletions
diff --git a/tests/auto/network/ssl/qsslkey/tst_qsslkey.cpp b/tests/auto/network/ssl/qsslkey/tst_qsslkey.cpp index e86dcb83e8..79bae3c270 100644 --- a/tests/auto/network/ssl/qsslkey/tst_qsslkey.cpp +++ b/tests/auto/network/ssl/qsslkey/tst_qsslkey.cpp @@ -1,31 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #include <qsslkey.h> @@ -41,16 +15,22 @@ #include <QtCore/qdebug.h> #include <QtCore/qlist.h> +using namespace Qt::StringLiterals; + #ifdef QT_BUILD_INTERNAL #if QT_CONFIG(ssl) #include "private/qsslkey_p.h" #define TEST_CRYPTO #endif #ifndef QT_NO_OPENSSL - #include "private/qsslsocket_openssl_symbols_p.h" + #include "../shared/qopenssl_symbols.h" #endif #endif +#if QT_CONFIG(ssl) +#include <QtNetwork/qsslsocket.h> +#endif // QT_CONFIG(ssl) + #include <algorithm> class tst_QSslKey : public QObject @@ -112,12 +92,18 @@ private: QString testDataDir; bool fileContainsUnsupportedEllipticCurve(const QString &fileName) const; + bool algorithmsSupported(const QString &fileName) const; QVector<QString> unsupportedCurves; + + bool isOpenSsl = false; + bool isOpenSslResolved = false; + bool isSecureTransport = false; + bool isSchannel = false; }; tst_QSslKey::tst_QSslKey() { -#ifndef QT_NO_SSL +#if QT_CONFIG(ssl) const QString expectedCurves[] = { // See how we generate them in keys/genkey.sh. QStringLiteral("secp224r1"), @@ -140,6 +126,22 @@ tst_QSslKey::tst_QSslKey() unsupportedCurves.push_back(requestedEc); } } + // Alas, we don't use network-private (and why?). + const auto backendName = QSslSocket::activeBackend(); + isOpenSsl = backendName == QStringLiteral("openssl"); + + if (isOpenSsl) { +#if !defined(QT_NO_OPENSSL) && defined(QT_BUILD_INTERNAL) + isOpenSslResolved = qt_auto_test_resolve_OpenSSL_symbols(); +#else + isOpenSslResolved = false; // not 'unused variable' anymore. +#endif + } else { + isSecureTransport = backendName == QStringLiteral("securetransport"); + } + + if (!isOpenSsl && !isSecureTransport) + isSchannel = backendName == QStringLiteral("schannel"); #else unsupportedCurves = {}; // not unsued anymore. #endif @@ -154,6 +156,37 @@ bool tst_QSslKey::fileContainsUnsupportedEllipticCurve(const QString &fileName) return false; } +bool tst_QSslKey::algorithmsSupported(const QString &fileName) const +{ +#if QT_CONFIG(ssl) + if (isSchannel && fileName.contains("RC2-64")) // Schannel treats RC2 as 128 bit + return false; + + if (isSchannel || isSecureTransport) { + // No AES support in the generic back-end, PKCS#12 algorithms not supported either. + return !(fileName.contains(QRegularExpression("-aes\\d\\d\\d-")) || fileName.contains("pkcs8-pkcs12")); + } + + if (!isOpenSsl || QSslSocket::sslLibraryVersionNumber() >> 28 < 3) + return true; + + // OpenSSL v3 first introduced the notion of 'providers'. Many algorithms + // were moved into the 'legacy' provider. While they are still supported in theory, + // the 'legacy' provider is NOT loaded by default and we are not loading it either. + // Thus, some of the keys we are using in tst_QSslKey would fail the test. We + // have to filter them out. + const auto name = fileName.toLower(); + if (name.contains("-des.")) + return false; + + return !name.contains("-rc2-") && !name.contains("-rc4-"); +#else + Q_UNUSED(fileName); + return false; +#endif // QT_CONFIG(ssl) +} + + void tst_QSslKey::initTestCase() { testDataDir = QFileInfo(QFINDTESTDATA("rsa-without-passphrase.pem")).absolutePath(); @@ -188,7 +221,7 @@ static QByteArray readFile(const QString &absFilePath) { QFile file(absFilePath); if (!file.open(QIODevice::ReadOnly)) { - QWARN("failed to open file"); + qWarning("failed to open file"); return QByteArray(); } return file.readAll(); @@ -218,19 +251,12 @@ void tst_QSslKey::createPlainTestRows(bool pemOnly) QTest::addColumn<QSsl::KeyType>("type"); QTest::addColumn<int>("length"); QTest::addColumn<QSsl::EncodingFormat>("format"); - foreach (KeyInfo keyInfo, keyInfoList) { + for (const KeyInfo &keyInfo : std::as_const(keyInfoList)) { if (pemOnly && keyInfo.format != QSsl::EncodingFormat::Pem) continue; -#if QT_CONFIG(schannel) - if (keyInfo.fileInfo.fileName().contains("RC2-64")) - continue; // Schannel treats RC2 as 128 bit -#endif -#if QT_CONFIG(ssl) && defined(QT_NO_OPENSSL) // generic backend - if (keyInfo.fileInfo.fileName().contains(QRegularExpression("-aes\\d\\d\\d-"))) - continue; // No AES support in the generic back-end - if (keyInfo.fileInfo.fileName().contains("pkcs8-pkcs12")) - continue; // The generic back-end doesn't support PKCS#12 algorithms -#endif + + if (!algorithmsSupported(keyInfo.fileInfo.fileName())) + continue; QTest::newRow(keyInfo.fileInfo.fileName().toLatin1()) << keyInfo.fileInfo.absoluteFilePath() << keyInfo.algorithm << keyInfo.type @@ -273,7 +299,7 @@ void tst_QSslKey::constructorHandle() #ifndef QT_BUILD_INTERNAL QSKIP("This test requires -developer-build."); #else - if (!QSslSocket::supportsSsl()) + if (!isOpenSslResolved) return; QFETCH(QString, absFilePath); @@ -291,7 +317,7 @@ void tst_QSslKey::constructorHandle() passphrase = "1234"; BIO* bio = q_BIO_new(q_BIO_s_mem()); - q_BIO_write(bio, pem.constData(), pem.length()); + q_BIO_write(bio, pem.constData(), pem.size()); EVP_PKEY *origin = func(bio, nullptr, nullptr, static_cast<void *>(passphrase.data())); Q_ASSERT(origin); q_EVP_PKEY_up_ref(origin); @@ -419,13 +445,13 @@ void tst_QSslKey::toPemOrDer() QByteArray dataTag = QByteArray(QTest::currentDataTag()); if (dataTag.contains("-pkcs8-")) // these are encrypted QSKIP("Encrypted PKCS#8 keys gets decrypted when loaded. So we can't compare it to the encrypted version."); -#ifndef QT_NO_OPENSSL - if (dataTag.contains("pkcs8")) - QSKIP("OpenSSL converts PKCS#8 keys to other formats, invalidating comparisons."); -#else // !openssl - if (dataTag.contains("pkcs8") && dataTag.contains("rsa")) - QSKIP("PKCS#8 RSA keys are changed into a different format in the generic back-end, meaning the comparison fails."); -#endif // openssl + + if (dataTag.contains("pkcs8")) { + if (isOpenSsl) + QSKIP("OpenSSL converts PKCS#8 keys to other formats, invalidating comparisons."); + else if (dataTag.contains("rsa")) + QSKIP("PKCS#8 RSA keys are changed into a different format in the generic back-end, meaning the comparison fails."); + } QByteArray encoded = readFile(absFilePath); QSslKey key(encoded, algorithm, format, type); @@ -443,13 +469,18 @@ void tst_QSslKey::toEncryptedPemOrDer_data() QTest::addColumn<QSsl::EncodingFormat>("format"); QTest::addColumn<QString>("password"); - QStringList passwords; - passwords << " " << "foobar" << "foo bar" - << "aAzZ`1234567890-=~!@#$%^&*()_+[]{}\\|;:'\",.<>/?"; // ### add more (?) - foreach (KeyInfo keyInfo, keyInfoList) { + const QString passwords[] = { + u" "_s, + u"foobar"_s, + u"foo bar"_s, + u"aAzZ`1234567890-=~!@#$%^&*()_+[]{}\\|;:'\",.<>/?"_s, + // ### add more (?) + }; + + for (const KeyInfo &keyInfo : std::as_const(keyInfoList)) { if (keyInfo.fileInfo.fileName().contains("pkcs8")) continue; // pkcs8 keys are encrypted in a different way than the other keys - foreach (QString password, passwords) { + for (const QString &password : passwords) { const QByteArray testName = keyInfo.fileInfo.fileName().toLatin1() + '-' + (keyInfo.algorithm == QSsl::Rsa ? "RSA" : (keyInfo.algorithm == QSsl::Dsa ? "DSA" : "EC")) @@ -524,9 +555,15 @@ void tst_QSslKey::passphraseChecks_data() const QByteArray pass("123"); const QByteArray aesPass("1234"); - QTest::newRow("DES") << QString(testDataDir + "rsa-with-passphrase-des.pem") << pass; + if (!isOpenSsl || QSslSocket::sslLibraryVersionNumber() >> 28 < 3) { + // DES and RC2 are not provided by default in OpenSSL v3. + // This part is for either non-OpenSSL build, or OpenSSL v < 3.x. + QTest::newRow("DES") << QString(testDataDir + "rsa-with-passphrase-des.pem") << pass; + QTest::newRow("RC2") << QString(testDataDir + "rsa-with-passphrase-rc2.pem") << pass; + } + QTest::newRow("3DES") << QString(testDataDir + "rsa-with-passphrase-3des.pem") << pass; - QTest::newRow("RC2") << QString(testDataDir + "rsa-with-passphrase-rc2.pem") << pass; + #if defined(QT_NO_OPENSSL) || !defined(OPENSSL_NO_AES) QTest::newRow("AES128") << QString(testDataDir + "rsa-with-passphrase-aes128.pem") << aesPass; QTest::newRow("AES192") << QString(testDataDir + "rsa-with-passphrase-aes192.pem") << aesPass; @@ -543,7 +580,7 @@ void tst_QSslKey::passphraseChecks() QVERIFY(keyFile.exists()); { if (!keyFile.isOpen()) - keyFile.open(QIODevice::ReadOnly); + QVERIFY(keyFile.open(QIODevice::ReadOnly)); else keyFile.reset(); QSslKey key(&keyFile,QSsl::Rsa,QSsl::Pem, QSsl::PrivateKey); @@ -551,7 +588,7 @@ void tst_QSslKey::passphraseChecks() } { if (!keyFile.isOpen()) - keyFile.open(QIODevice::ReadOnly); + QVERIFY(keyFile.open(QIODevice::ReadOnly)); else keyFile.reset(); QSslKey key(&keyFile,QSsl::Rsa,QSsl::Pem, QSsl::PrivateKey, ""); @@ -559,7 +596,7 @@ void tst_QSslKey::passphraseChecks() } { if (!keyFile.isOpen()) - keyFile.open(QIODevice::ReadOnly); + QVERIFY(keyFile.open(QIODevice::ReadOnly)); else keyFile.reset(); QSslKey key(&keyFile,QSsl::Rsa,QSsl::Pem, QSsl::PrivateKey, "WRONG!"); @@ -567,7 +604,7 @@ void tst_QSslKey::passphraseChecks() } { if (!keyFile.isOpen()) - keyFile.open(QIODevice::ReadOnly); + QVERIFY(keyFile.open(QIODevice::ReadOnly)); else keyFile.reset(); QSslKey key(&keyFile,QSsl::Rsa,QSsl::Pem, QSsl::PrivateKey, passphrase); @@ -585,7 +622,7 @@ void tst_QSslKey::noPassphraseChecks() QFile keyFile(fileName); { if (!keyFile.isOpen()) - keyFile.open(QIODevice::ReadOnly); + QVERIFY(keyFile.open(QIODevice::ReadOnly)); else keyFile.reset(); QSslKey key(&keyFile,QSsl::Rsa,QSsl::Pem, QSsl::PrivateKey); @@ -593,7 +630,7 @@ void tst_QSslKey::noPassphraseChecks() } { if (!keyFile.isOpen()) - keyFile.open(QIODevice::ReadOnly); + QVERIFY(keyFile.open(QIODevice::ReadOnly)); else keyFile.reset(); QSslKey key(&keyFile,QSsl::Rsa,QSsl::Pem, QSsl::PrivateKey, ""); @@ -601,7 +638,7 @@ void tst_QSslKey::noPassphraseChecks() } { if (!keyFile.isOpen()) - keyFile.open(QIODevice::ReadOnly); + QVERIFY(keyFile.open(QIODevice::ReadOnly)); else keyFile.reset(); QSslKey key(&keyFile,QSsl::Rsa,QSsl::Pem, QSsl::PrivateKey, "xxx"); @@ -623,6 +660,9 @@ void tst_QSslKey::encrypt_data() QTest::addColumn<QByteArray>("iv"); QByteArray iv("abcdefgh"); +#if OPENSSL_VERSION_MAJOR < 3 + // Either non-OpenSSL build, or OpenSSL v < 3 + // (with DES and other legacy algorithms available by default) QTest::newRow("DES-CBC, length 0") << Cipher::DesCbc << QByteArray("01234567") << QByteArray() @@ -712,6 +752,7 @@ void tst_QSslKey::encrypt_data() << QByteArray(8, 'a') << QByteArray::fromHex("5AEC1A5B295660B02613454232F7DECE") << iv; +#endif // OPENSSL_VERSION_MAJOR #if defined(QT_NO_OPENSSL) || !defined(OPENSSL_NO_AES) // AES needs a longer IV @@ -759,12 +800,13 @@ void tst_QSslKey::encrypt() QFETCH(QByteArray, cipherText); QFETCH(QByteArray, iv); -#if QT_CONFIG(schannel) - QEXPECT_FAIL("RC2-40-CBC, length 0", "Schannel treats RC2 as 128-bit", Abort); - QEXPECT_FAIL("RC2-40-CBC, length 8", "Schannel treats RC2 as 128-bit", Abort); - QEXPECT_FAIL("RC2-64-CBC, length 0", "Schannel treats RC2 as 128-bit", Abort); - QEXPECT_FAIL("RC2-64-CBC, length 8", "Schannel treats RC2 as 128-bit", Abort); -#endif + if (isSchannel) { + QEXPECT_FAIL("RC2-40-CBC, length 0", "Schannel treats RC2 as 128-bit", Abort); + QEXPECT_FAIL("RC2-40-CBC, length 8", "Schannel treats RC2 as 128-bit", Abort); + QEXPECT_FAIL("RC2-64-CBC, length 0", "Schannel treats RC2 as 128-bit", Abort); + QEXPECT_FAIL("RC2-64-CBC, length 8", "Schannel treats RC2 as 128-bit", Abort); + } + QByteArray encrypted = QSslKeyPrivate::encrypt(cipher, plainText, key, iv); QCOMPARE(encrypted, cipherText); |