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"