File indexing completed on 2024-05-12 05:20:02
0001 /* 0002 This file is part of Kleopatra's test suite. 0003 SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include <config-kleopatra.h> 0009 0010 #include "kleo_test.h" 0011 0012 #include <QGpgME/DecryptVerifyJob> 0013 #include <QGpgME/KeyListJob> 0014 #include <QGpgME/Protocol> 0015 #include <QGpgME/VerifyDetachedJob> 0016 0017 #include <QObject> 0018 #include <QSignalSpy> 0019 #include <QTimer> 0020 #include <gpgme++/decryptionresult.h> 0021 #include <gpgme++/error.h> 0022 #include <gpgme++/key.h> 0023 #include <gpgme++/verificationresult.h> 0024 0025 #include <chrono> 0026 using namespace std::chrono_literals; 0027 0028 // Replace this with a gpgme version check once GnuPG Bug #2092 0029 // ( https://bugs.gnupg.org/gnupg/issue2092 ) is fixed. 0030 #define GPGME_MULTITHREADED_KEYLIST_BROKEN 0031 0032 Q_DECLARE_METATYPE(GpgME::VerificationResult) 0033 0034 class VerifyTest : public QObject 0035 { 0036 Q_OBJECT 0037 private: 0038 // Data shared with all tests 0039 QByteArray mSignature; 0040 QByteArray mSignedData; 0041 const QGpgME::Protocol *mBackend; 0042 QEventLoop mEventLoop; 0043 0044 // Data for testParallelVerifyAndKeyListJobs() 0045 QList<QGpgME::VerifyDetachedJob *> mParallelVerifyJobs; 0046 QList<QGpgME::KeyListJob *> mParallelKeyListJobs; 0047 0048 // Data for testMixedParallelJobs() 0049 QList<QGpgME::Job *> mRunningJobs; 0050 int mJobsStarted; 0051 0052 public Q_SLOTS: 0053 void slotParallelKeyListJobFinished() 0054 { 0055 mParallelKeyListJobs.removeAll(static_cast<QGpgME::KeyListJob *>(sender())); 0056 0057 // When all jobs are done, quit the event loop 0058 if (mParallelVerifyJobs.isEmpty() && mParallelKeyListJobs.isEmpty()) { 0059 mEventLoop.quit(); 0060 } 0061 } 0062 0063 void slotParallelVerifyJobFinished(const GpgME::VerificationResult &result) 0064 { 0065 // Verify the result of the job is correct 0066 QVERIFY(mParallelVerifyJobs.contains(static_cast<QGpgME::VerifyDetachedJob *>(sender()))); 0067 QCOMPARE(result.signature(0).validity(), GpgME::Signature::Full); 0068 mParallelVerifyJobs.removeAll(static_cast<QGpgME::VerifyDetachedJob *>(sender())); 0069 0070 // Start a key list job 0071 QGpgME::KeyListJob *job = mBackend->keyListJob(); 0072 mParallelKeyListJobs.append(job); 0073 connect(job, &QGpgME::Job::done, this, &VerifyTest::slotParallelKeyListJobFinished); 0074 QVERIFY(!job->start(QStringList())); 0075 } 0076 0077 void someJobDone() 0078 { 0079 // Don't bother checking any results here 0080 mRunningJobs.removeAll(static_cast<QGpgME::Job *>(sender())); 0081 } 0082 0083 void startAnotherJob() 0084 { 0085 static int counter = 0; 0086 counter++; 0087 0088 // Randomly kill a running job 0089 if (counter % 10 == 0 && !mRunningJobs.isEmpty()) { 0090 mRunningJobs.at(counter % mRunningJobs.size())->slotCancel(); 0091 } 0092 0093 // Randomly either start a keylist or a verify job 0094 QGpgME::Job *job; 0095 if (counter % 2 == 0) { 0096 QGpgME::VerifyDetachedJob *vdj = mBackend->verifyDetachedJob(); 0097 QVERIFY(!vdj->start(mSignature, mSignedData)); 0098 job = vdj; 0099 } else { 0100 QGpgME::KeyListJob *klj = mBackend->keyListJob(); 0101 QVERIFY(!klj->start(QStringList())); 0102 job = klj; 0103 } 0104 mRunningJobs.append(job); 0105 connect(job, &QGpgME::Job::done, this, &VerifyTest::someJobDone); 0106 0107 // Quit after 2500 jobs, that should be enough 0108 mJobsStarted++; 0109 if (mJobsStarted >= 2500) { 0110 QTimer::singleShot(1s, &mEventLoop, &QEventLoop::quit); 0111 } else { 0112 QTimer::singleShot(0, this, &VerifyTest::startAnotherJob); 0113 } 0114 } 0115 0116 private Q_SLOTS: 0117 void initTestCase() 0118 { 0119 qRegisterMetaType<GpgME::VerificationResult>(); 0120 0121 const QString sigFileName = QLatin1StringView(KLEO_TEST_DATADIR) + QLatin1String("/test.data.sig"); 0122 const QString dataFileName = QLatin1StringView(KLEO_TEST_DATADIR) + QLatin1String("/test.data"); 0123 0124 QFile sigFile(sigFileName); 0125 QVERIFY(sigFile.open(QFile::ReadOnly)); 0126 QFile dataFile(dataFileName); 0127 QVERIFY(dataFile.open(QFile::ReadOnly)); 0128 0129 mSignature = sigFile.readAll(); 0130 mSignedData = dataFile.readAll(); 0131 0132 mBackend = QGpgME::openpgp(); 0133 } 0134 0135 void testVerify() 0136 { 0137 QGpgME::VerifyDetachedJob *job = mBackend->verifyDetachedJob(); 0138 QSignalSpy spy(job, &QGpgME::VerifyDetachedJob::result); 0139 QVERIFY(spy.isValid()); 0140 GpgME::Error err = job->start(mSignature, mSignedData); 0141 QVERIFY(!err); 0142 QTest::qWait(1000); // ### we need to enter the event loop, can be done nicer though 0143 0144 QCOMPARE(spy.count(), 1); 0145 auto result = spy.takeFirst().at(0).value<GpgME::VerificationResult>(); 0146 QCOMPARE(result.numSignatures(), 1U); 0147 0148 GpgME::Signature sig = result.signature(0); 0149 QCOMPARE(sig.summary() & GpgME::Signature::KeyMissing, 0); 0150 QCOMPARE((quint64)sig.creationTime(), Q_UINT64_C(1530524124)); 0151 QCOMPARE(sig.validity(), GpgME::Signature::Full); 0152 } 0153 0154 /* Test that the decrypt verify job also works with signed only, not 0155 * encrypted PGP messages */ 0156 void testDecryptVerifyOpaqueSigned() 0157 { 0158 const QString sigFileName = QLatin1StringView(KLEO_TEST_DATADIR) + QLatin1String("/test.data.signed-opaque.asc"); 0159 std::pair<GpgME::DecryptionResult, GpgME::VerificationResult> result; 0160 QByteArray plaintext; 0161 QFile sigFile(sigFileName); 0162 QVERIFY(sigFile.open(QFile::ReadOnly)); 0163 const QByteArray ciphertext = sigFile.readAll(); 0164 0165 QGpgME::DecryptVerifyJob *job = mBackend->decryptVerifyJob(); 0166 result = job->exec(ciphertext, plaintext); 0167 QVERIFY(result.first.error().code()); 0168 0169 QVERIFY(result.second.numSignatures()); 0170 GpgME::Signature sig = result.second.signature(0); 0171 QVERIFY(sig.validity() == GpgME::Signature::Validity::Full); 0172 QVERIFY(!sig.status().code()); 0173 QVERIFY(QString::fromUtf8(plaintext).startsWith(QLatin1StringView("/* -*- mode: c++; c-basic-offset:4 -*-"))); 0174 } 0175 0176 #ifndef GPGME_MULTITHREADED_KEYLIST_BROKEN 0177 // The following two tests are disabled because they trigger an 0178 // upstream bug in gpgme. See: https://bugs.gnupg.org/gnupg/issue2092 0179 // Which has a testcase attached that does similar things using gpgme 0180 // directly and triggers various problems. 0181 void testParallelVerifyAndKeyListJobs() 0182 { 0183 // ### Increasing 10 to 500 makes the verify jobs fail! 0184 // ^ This should also be reevaluated if the underlying bug in gpgme 0185 // is fixed. 0186 for (int i = 0; i < 10; ++i) { 0187 QGpgME::VerifyDetachedJob *job = mBackend->verifyDetachedJob(); 0188 mParallelVerifyJobs.append(job); 0189 QVERIFY(!job->start(mSignature, mSignedData)); 0190 connect(job, SIGNAL(result(GpgME::VerificationResult)), this, SLOT(slotParallelVerifyJobFinished(GpgME::VerificationResult))); 0191 } 0192 0193 mEventLoop.exec(); 0194 } 0195 0196 void testMixedParallelJobs() 0197 { 0198 mJobsStarted = 0; 0199 QTimer::singleShot(0, this, SLOT(startAnotherJob())); 0200 mEventLoop.exec(); 0201 } 0202 #endif 0203 }; 0204 0205 QTEST_KLEOMAIN(VerifyTest) 0206 0207 #include "test_verify.moc"