diff --git a/lang/qt/tests/t-changeexpiryjob.cpp b/lang/qt/tests/t-changeexpiryjob.cpp index e20d1be2..090002f3 100644 --- a/lang/qt/tests/t-changeexpiryjob.cpp +++ b/lang/qt/tests/t-changeexpiryjob.cpp @@ -1,421 +1,396 @@ /* t-changeexpiryjob.cpp This file is part of qgpgme, the Qt API binding for gpgme Copyright (c) 2021 g10 Code GmbH Software engineering by Ingo Klöcker QGpgME is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. QGpgME is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "t-support.h" #include "changeexpiryjob.h" #include "context.h" #include "engineinfo.h" #include "protocol.h" #include #include #include using namespace QGpgME; using namespace GpgME; class TestChangeExpiryJob: public QGpgMETest { Q_OBJECT private Q_SLOTS: void test_change_expiration_default_without_subkeys() { Error err; if (!loopbackSupported()) { return; } auto ctx = Context::create(OpenPGP); QVERIFY(ctx); // Get the key (alfa@example.net) auto key = ctx->key("A0FF4590BB6122EDEF6E3C542D727CC768697734", err, true); QVERIFY(!err); QVERIFY(!key.isNull()); QVERIFY(!key.subkey(0).isNull()); QVERIFY(!key.subkey(1).isNull()); const auto subkeyExpiration = key.subkey(1).expirationTime(); { // Create the job auto job = std::unique_ptr{openpgp()->changeExpiryJob()}; QVERIFY(job); - - // Hack in the passphrase provider - auto jobCtx = Job::context(job.get()); - TestPassphraseProvider provider; - jobCtx->setPassphraseProvider(&provider); - jobCtx->setPinentryMode(Context::PinentryLoopback); + hookUpPassphraseProvider(job.get()); // Use defaults of job connect(job.get(), &ChangeExpiryJob::result, this, [this] (const GpgME::Error &err2, const QString &, const GpgME::Error &) { Q_EMIT asyncDone(); if (err2) { QFAIL(qPrintable(QString("The ChangeExpiryJob failed with '%1'.").arg(err2.asString()))); } }); const auto newExpirationDate = QDateTime::currentDateTime().addDays(1); job->start(key, newExpirationDate); QSignalSpy spy (this, SIGNAL(asyncDone())); QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT)); // At this point the expiration date should have been changed. key.update(); // allow a few seconds earlier expiration because job calculates "seconds from now" passed to gpg after it was started const auto expectedExpirationRange = std::make_pair( newExpirationDate.toSecsSinceEpoch() - 10, QDateTime::currentDateTime().addDays(1).toSecsSinceEpoch()); { const auto actualExpiration = key.subkey(0).expirationTime(); QVERIFY2(actualExpiration >= expectedExpirationRange.first, ("actual: " + std::to_string(actualExpiration) + "; expected: " + std::to_string(expectedExpirationRange.first)).c_str()); QVERIFY2(actualExpiration <= expectedExpirationRange.second, ("actual: " + std::to_string(actualExpiration) + "; expected: " + std::to_string(expectedExpirationRange.second)).c_str()); } { const auto actualExpiration = key.subkey(1).expirationTime(); QCOMPARE(actualExpiration, subkeyExpiration); // unchanged } } } void test_change_expiration_default_with_subkeys() { Error err; if (!loopbackSupported()) { return; } auto ctx = Context::create(OpenPGP); QVERIFY(ctx); // Get the key (alfa@example.net) auto key = ctx->key("A0FF4590BB6122EDEF6E3C542D727CC768697734", err, true); QVERIFY(!err); QVERIFY(!key.isNull()); QVERIFY(!key.subkey(0).isNull()); QVERIFY(!key.subkey(1).isNull()); const auto primaryKeyExpiration = key.subkey(0).expirationTime(); { // Create the job auto job = std::unique_ptr{openpgp()->changeExpiryJob()}; QVERIFY(job); - - // Hack in the passphrase provider - auto jobCtx = Job::context(job.get()); - TestPassphraseProvider provider; - jobCtx->setPassphraseProvider(&provider); - jobCtx->setPinentryMode(Context::PinentryLoopback); + hookUpPassphraseProvider(job.get()); // Use defaults of job connect(job.get(), &ChangeExpiryJob::result, this, [this] (const GpgME::Error &err2, const QString &, const GpgME::Error &) { Q_EMIT asyncDone(); if (err2) { QFAIL(qPrintable(QString("The ChangeExpiryJob failed with '%1'.").arg(err2.asString()))); } }); const auto newExpirationDate = QDateTime::currentDateTime().addDays(2); job->start(key, newExpirationDate, {key.subkey(1)}); QSignalSpy spy (this, SIGNAL(asyncDone())); QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT)); // At this point the expiration date should have been changed. key.update(); // allow a few seconds earlier expiration because job calculates "seconds from now" passed to gpg after it was started const auto expectedExpirationRange = std::make_pair( newExpirationDate.toSecsSinceEpoch() - 10, QDateTime::currentDateTime().addDays(2).toSecsSinceEpoch()); { const auto actualExpiration = key.subkey(0).expirationTime(); QCOMPARE(actualExpiration, primaryKeyExpiration); // unchanged } { const auto actualExpiration = key.subkey(1).expirationTime(); QVERIFY2(actualExpiration >= expectedExpirationRange.first, ("actual: " + std::to_string(actualExpiration) + "; expected: " + std::to_string(expectedExpirationRange.first)).c_str()); QVERIFY2(actualExpiration <= expectedExpirationRange.second, ("actual: " + std::to_string(actualExpiration) + "; expected: " + std::to_string(expectedExpirationRange.second)).c_str()); } } } void test_change_expiration_update_primary_key_without_subkeys() { Error err; if (!loopbackSupported()) { return; } auto ctx = Context::create(OpenPGP); QVERIFY(ctx); // Get the key (alfa@example.net) auto key = ctx->key("A0FF4590BB6122EDEF6E3C542D727CC768697734", err, true); QVERIFY(!err); QVERIFY(!key.isNull()); QVERIFY(!key.subkey(0).isNull()); QVERIFY(!key.subkey(1).isNull()); const auto subkeyExpiration = key.subkey(1).expirationTime(); { // Create the job auto job = std::unique_ptr{openpgp()->changeExpiryJob()}; QVERIFY(job); - - // Hack in the passphrase provider - auto jobCtx = Job::context(job.get()); - TestPassphraseProvider provider; - jobCtx->setPassphraseProvider(&provider); - jobCtx->setPinentryMode(Context::PinentryLoopback); + hookUpPassphraseProvider(job.get()); // Set up the job job->setOptions(ChangeExpiryJob::UpdatePrimaryKey); connect(job.get(), &ChangeExpiryJob::result, this, [this] (const GpgME::Error &err2, const QString &, const GpgME::Error &) { Q_EMIT asyncDone(); if (err2) { QFAIL(qPrintable(QString("The ChangeExpiryJob failed with '%1'.").arg(err2.asString()))); } }); const auto newExpirationDate = QDateTime::currentDateTime().addDays(3); job->start(key, newExpirationDate, {}); QSignalSpy spy (this, SIGNAL(asyncDone())); QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT)); // At this point the expiration date should have been changed. key.update(); // allow a few seconds earlier expiration because job calculates "seconds from now" passed to gpg after it was started const auto expectedExpirationRange = std::make_pair( newExpirationDate.toSecsSinceEpoch() - 10, QDateTime::currentDateTime().addDays(3).toSecsSinceEpoch()); { const auto actualExpiration = key.subkey(0).expirationTime(); QVERIFY2(actualExpiration >= expectedExpirationRange.first, ("actual: " + std::to_string(actualExpiration) + "; expected: " + std::to_string(expectedExpirationRange.first)).c_str()); QVERIFY2(actualExpiration <= expectedExpirationRange.second, ("actual: " + std::to_string(actualExpiration) + "; expected: " + std::to_string(expectedExpirationRange.second)).c_str()); } { const auto actualExpiration = key.subkey(1).expirationTime(); QCOMPARE(actualExpiration, subkeyExpiration); // unchanged } } } void test_change_expiration_update_primary_key_with_subkeys() { Error err; if (!loopbackSupported()) { return; } auto ctx = Context::create(OpenPGP); QVERIFY(ctx); // Get the key (alfa@example.net) auto key = ctx->key("A0FF4590BB6122EDEF6E3C542D727CC768697734", err, true); QVERIFY(!err); QVERIFY(!key.isNull()); QVERIFY(!key.subkey(0).isNull()); QVERIFY(!key.subkey(1).isNull()); { // Create the job auto job = std::unique_ptr{openpgp()->changeExpiryJob()}; QVERIFY(job); - - // Hack in the passphrase provider - auto jobCtx = Job::context(job.get()); - TestPassphraseProvider provider; - jobCtx->setPassphraseProvider(&provider); - jobCtx->setPinentryMode(Context::PinentryLoopback); + hookUpPassphraseProvider(job.get()); // Set up the job job->setOptions(ChangeExpiryJob::UpdatePrimaryKey); connect(job.get(), &ChangeExpiryJob::result, this, [this] (const GpgME::Error &err2, const QString &, const GpgME::Error &) { Q_EMIT asyncDone(); if (err2) { QFAIL(qPrintable(QString("The ChangeExpiryJob failed with '%1'.").arg(err2.asString()))); } }); const auto newExpirationDate = QDateTime::currentDateTime().addDays(4); job->start(key, newExpirationDate, {key.subkey(1)}); QSignalSpy spy (this, SIGNAL(asyncDone())); QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT)); // At this point the expiration date should have been changed. key.update(); // allow a few seconds earlier expiration because job calculates "seconds from now" passed to gpg after it was started const auto expectedExpirationRange = std::make_pair( newExpirationDate.toSecsSinceEpoch() - 10, QDateTime::currentDateTime().addDays(4).toSecsSinceEpoch()); { const auto actualExpiration = key.subkey(0).expirationTime(); QVERIFY2(actualExpiration >= expectedExpirationRange.first, ("actual: " + std::to_string(actualExpiration) + "; expected: " + std::to_string(expectedExpirationRange.first)).c_str()); QVERIFY2(actualExpiration <= expectedExpirationRange.second, ("actual: " + std::to_string(actualExpiration) + "; expected: " + std::to_string(expectedExpirationRange.second)).c_str()); } { const auto actualExpiration = key.subkey(1).expirationTime(); QVERIFY2(actualExpiration >= expectedExpirationRange.first, ("actual: " + std::to_string(actualExpiration) + "; expected: " + std::to_string(expectedExpirationRange.first)).c_str()); QVERIFY2(actualExpiration <= expectedExpirationRange.second, ("actual: " + std::to_string(actualExpiration) + "; expected: " + std::to_string(expectedExpirationRange.second)).c_str()); } } } void test_change_expiration_update_primary_key_and_all_subkeys() { Error err; if (!loopbackSupported()) { return; } auto ctx = Context::create(OpenPGP); QVERIFY(ctx); // Get the key (alfa@example.net) auto key = ctx->key("A0FF4590BB6122EDEF6E3C542D727CC768697734", err, true); QVERIFY(!err); QVERIFY(!key.isNull()); QVERIFY(!key.subkey(0).isNull()); QVERIFY(!key.subkey(1).isNull()); { // Create the job auto job = std::unique_ptr{openpgp()->changeExpiryJob()}; QVERIFY(job); - - // Hack in the passphrase provider - auto jobCtx = Job::context(job.get()); - TestPassphraseProvider provider; - jobCtx->setPassphraseProvider(&provider); - jobCtx->setPinentryMode(Context::PinentryLoopback); + hookUpPassphraseProvider(job.get()); // Set up the job job->setOptions(ChangeExpiryJob::UpdatePrimaryKey | ChangeExpiryJob::UpdateAllSubkeys); connect(job.get(), &ChangeExpiryJob::result, this, [this] (const GpgME::Error &err2, const QString &, const GpgME::Error &) { Q_EMIT asyncDone(); if (err2) { QFAIL(qPrintable(QString("The ChangeExpiryJob failed with '%1'.").arg(err2.asString()))); } }); const auto newExpirationDate = QDateTime::currentDateTime().addDays(5); job->start(key, newExpirationDate); QSignalSpy spy (this, SIGNAL(asyncDone())); QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT)); // At this point the expiration date should have been changed. key.update(); // allow a few seconds earlier expiration because job calculates "seconds from now" passed to gpg after it was started const auto expectedExpirationRange = std::make_pair( newExpirationDate.toSecsSinceEpoch() - 10, QDateTime::currentDateTime().addDays(5).toSecsSinceEpoch()); { const auto actualExpiration = key.subkey(0).expirationTime(); QVERIFY2(actualExpiration >= expectedExpirationRange.first, ("actual: " + std::to_string(actualExpiration) + "; expected: " + std::to_string(expectedExpirationRange.first)).c_str()); QVERIFY2(actualExpiration <= expectedExpirationRange.second, ("actual: " + std::to_string(actualExpiration) + "; expected: " + std::to_string(expectedExpirationRange.second)).c_str()); } { const auto actualExpiration = key.subkey(1).expirationTime(); QVERIFY2(actualExpiration >= expectedExpirationRange.first, ("actual: " + std::to_string(actualExpiration) + "; expected: " + std::to_string(expectedExpirationRange.first)).c_str()); QVERIFY2(actualExpiration <= expectedExpirationRange.second, ("actual: " + std::to_string(actualExpiration) + "; expected: " + std::to_string(expectedExpirationRange.second)).c_str()); } } } void initTestCase() { QGpgMETest::initTestCase(); const QString gpgHome = qgetenv("GNUPGHOME"); QVERIFY(copyKeyrings(gpgHome, mDir.path())); qputenv("GNUPGHOME", mDir.path().toUtf8()); } private: QTemporaryDir mDir; }; QTEST_MAIN(TestChangeExpiryJob) #include "t-changeexpiryjob.moc" diff --git a/lang/qt/tests/t-encrypt.cpp b/lang/qt/tests/t-encrypt.cpp index 2249de3b..6a4c68e9 100644 --- a/lang/qt/tests/t-encrypt.cpp +++ b/lang/qt/tests/t-encrypt.cpp @@ -1,333 +1,318 @@ /* t-encrypt.cpp This file is part of qgpgme, the Qt API binding for gpgme Copyright (c) 2016 by Bundesamt für Sicherheit in der Informationstechnik Software engineering by Intevation GmbH QGpgME is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. QGpgME is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include "keylistjob.h" #include "encryptjob.h" #include "signencryptjob.h" #include "signingresult.h" #include "encryptjob.h" #include "encryptionresult.h" #include "decryptionresult.h" #include "decryptjob.h" #include "qgpgmebackend.h" #include "keylistresult.h" #include "engineinfo.h" #include "verifyopaquejob.h" #include "t-support.h" #define PROGRESS_TEST_SIZE 1 * 1024 * 1024 using namespace QGpgME; using namespace GpgME; class EncryptionTest : public QGpgMETest { Q_OBJECT private Q_SLOTS: void testSimpleEncryptDecrypt() { auto listjob = openpgp()->keyListJob(false, false, false); std::vector keys; auto keylistresult = listjob->exec(QStringList() << QStringLiteral("alfa@example.net"), false, keys); QVERIFY(!keylistresult.error()); QVERIFY(keys.size() == 1); delete listjob; auto job = openpgp()->encryptJob(/*ASCII Armor */true, /* Textmode */ true); QVERIFY(job); QByteArray cipherText; auto result = job->exec(keys, QStringLiteral("Hello World").toUtf8(), Context::AlwaysTrust, cipherText); delete job; QVERIFY(!result.error()); const auto cipherString = QString::fromUtf8(cipherText); QVERIFY(cipherString.startsWith("-----BEGIN PGP MESSAGE-----")); /* Now decrypt */ if (!loopbackSupported()) { return; } auto decJob = openpgp()->decryptJob(); - auto ctx = Job::context(decJob); - TestPassphraseProvider provider; - ctx->setPassphraseProvider(&provider); - ctx->setPinentryMode(Context::PinentryLoopback); + hookUpPassphraseProvider(decJob); QByteArray plainText; auto decResult = decJob->exec(cipherText, plainText); QVERIFY(!decResult.error()); QVERIFY(QString::fromUtf8(plainText) == QStringLiteral("Hello World")); delete decJob; } void testProgress() { if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() < "2.1.15") { // We can only test the progress with 2.1.15 as this started to // have total progress for memory callbacks return; } auto listjob = openpgp()->keyListJob(false, false, false); std::vector keys; auto keylistresult = listjob->exec(QStringList() << QStringLiteral("alfa@example.net"), false, keys); QVERIFY(!keylistresult.error()); QVERIFY(keys.size() == 1); delete listjob; auto job = openpgp()->encryptJob(/*ASCII Armor */false, /* Textmode */ false); QVERIFY(job); QByteArray plainBa; plainBa.fill('X', PROGRESS_TEST_SIZE); QByteArray cipherText; bool initSeen = false; bool finishSeen = false; connect(job, &Job::progress, this, [this, &initSeen, &finishSeen] (const QString&, int current, int total) { // We only check for progress 0 and max progress as the other progress // lines depend on the system speed and are as such unreliable to test. QVERIFY(total == PROGRESS_TEST_SIZE); if (current == 0) { initSeen = true; } if (current == total) { finishSeen = true; } QVERIFY(current >= 0 && current <= total); }); connect(job, &EncryptJob::result, this, [this, &initSeen, &finishSeen] (const GpgME::EncryptionResult &, const QByteArray &, const QString, const GpgME::Error) { QVERIFY(initSeen); QVERIFY(finishSeen); Q_EMIT asyncDone(); }); auto inptr = std::shared_ptr(new QBuffer(&plainBa)); inptr->open(QIODevice::ReadOnly); auto outptr = std::shared_ptr(new QBuffer(&cipherText)); outptr->open(QIODevice::WriteOnly); job->start(keys, inptr, outptr, Context::AlwaysTrust); QSignalSpy spy (this, SIGNAL(asyncDone())); QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT)); } void testSymmetricEncryptDecrypt() { if (!loopbackSupported()) { return; } auto job = openpgp()->encryptJob(); + hookUpPassphraseProvider(job); auto ctx = Job::context(job); - TestPassphraseProvider provider; - ctx->setPassphraseProvider(&provider); - ctx->setPinentryMode(Context::PinentryLoopback); ctx->setArmor(true); ctx->setTextMode(true); QByteArray cipherText; auto result = job->exec(std::vector(), QStringLiteral("Hello symmetric World").toUtf8(), Context::AlwaysTrust, cipherText); delete job; QVERIFY(!result.error()); const auto cipherString = QString::fromUtf8(cipherText); QVERIFY(cipherString.startsWith("-----BEGIN PGP MESSAGE-----")); killAgent(mDir.path()); auto decJob = openpgp()->decryptJob(); - auto ctx2 = Job::context(decJob); - ctx2->setPassphraseProvider(&provider); - ctx2->setPinentryMode(Context::PinentryLoopback); + hookUpPassphraseProvider(decJob); QByteArray plainText; auto decResult = decJob->exec(cipherText, plainText); QVERIFY(!result.error()); QVERIFY(QString::fromUtf8(plainText) == QStringLiteral("Hello symmetric World")); delete decJob; } void testEncryptDecryptNowrap() { /* Now decrypt */ if (!loopbackSupported()) { return; } auto listjob = openpgp()->keyListJob(false, false, false); std::vector keys; auto keylistresult = listjob->exec(QStringList() << QStringLiteral("alfa@example.net"), false, keys); QVERIFY(!keylistresult.error()); QVERIFY(keys.size() == 1); delete listjob; auto job = openpgp()->signEncryptJob(/*ASCII Armor */true, /* Textmode */ true); - - auto encSignCtx = Job::context(job); - TestPassphraseProvider provider1; - encSignCtx->setPassphraseProvider(&provider1); - encSignCtx->setPinentryMode(Context::PinentryLoopback); - QVERIFY(job); + hookUpPassphraseProvider(job); + QByteArray cipherText; auto result = job->exec(keys, keys, QStringLiteral("Hello World").toUtf8(), Context::AlwaysTrust, cipherText); delete job; QVERIFY(!result.first.error()); QVERIFY(!result.second.error()); const auto cipherString = QString::fromUtf8(cipherText); QVERIFY(cipherString.startsWith("-----BEGIN PGP MESSAGE-----")); /* Now decrypt */ if (!loopbackSupported()) { return; } auto decJob = openpgp()->decryptJob(); + hookUpPassphraseProvider(decJob); auto ctx = Job::context(decJob); - TestPassphraseProvider provider; - ctx->setPassphraseProvider(&provider); - ctx->setPinentryMode(Context::PinentryLoopback); ctx->setDecryptionFlags(Context::DecryptUnwrap); QByteArray plainText; auto decResult = decJob->exec(cipherText, plainText); QVERIFY(!decResult.error()); delete decJob; // Now verify the unwrapeped data. auto verifyJob = openpgp()->verifyOpaqueJob(true); QByteArray verified; auto verResult = verifyJob->exec(plainText, verified); QVERIFY(!verResult.error()); delete verifyJob; QVERIFY(verResult.numSignatures() == 1); auto sig = verResult.signatures()[0]; QVERIFY(verified == QStringLiteral("Hello World")); } private: /* Loopback and passphrase provider don't work for mixed encryption. * So this test is disabled until gnupg(?) is fixed for this. */ void testMixedEncryptDecrypt() { if (!loopbackSupported()) { return; } auto listjob = openpgp()->keyListJob(false, false, false); std::vector keys; auto keylistresult = listjob->exec(QStringList() << QStringLiteral("alfa@example.net"), false, keys); QVERIFY(!keylistresult.error()); QVERIFY(keys.size() == 1); delete listjob; auto job = openpgp()->encryptJob(); + hookUpPassphraseProvider(job); auto ctx = Job::context(job); - ctx->setPassphraseProvider(new TestPassphraseProvider); - ctx->setPinentryMode(Context::PinentryLoopback); ctx->setArmor(true); ctx->setTextMode(true); QByteArray cipherText; printf("Before exec, flags: %x\n", Context::Symmetric | Context::AlwaysTrust); auto result = job->exec(keys, QStringLiteral("Hello symmetric World").toUtf8(), static_cast(Context::Symmetric | Context::AlwaysTrust), cipherText); printf("After exec\n"); delete job; QVERIFY(!result.error()); printf("Cipher:\n%s\n", cipherText.constData()); const auto cipherString = QString::fromUtf8(cipherText); QVERIFY(cipherString.startsWith("-----BEGIN PGP MESSAGE-----")); killAgent(mDir.path()); /* Now create a new homedir which with we test symmetric decrypt. */ QTemporaryDir tmp; qputenv("GNUPGHOME", tmp.path().toUtf8()); QFile agentConf(tmp.path() + QStringLiteral("/gpg-agent.conf")); QVERIFY(agentConf.open(QIODevice::WriteOnly)); agentConf.write("allow-loopback-pinentry"); agentConf.close(); auto decJob = openpgp()->decryptJob(); + hookUpPassphraseProvider(decJob); auto ctx2 = Job::context(decJob); - ctx2->setPassphraseProvider(new TestPassphraseProvider); - ctx2->setPinentryMode(Context::PinentryLoopback); ctx2->setTextMode(true); QByteArray plainText; auto decResult = decJob->exec(cipherText, plainText); QVERIFY(!decResult.error()); qDebug() << "Plain: " << plainText; QVERIFY(QString::fromUtf8(plainText) == QStringLiteral("Hello symmetric World")); delete decJob; killAgent(tmp.path()); qputenv("GNUPGHOME", mDir.path().toUtf8()); } public Q_SLOT: void initTestCase() { QGpgMETest::initTestCase(); const QString gpgHome = qgetenv("GNUPGHOME"); qputenv("GNUPGHOME", mDir.path().toUtf8()); QVERIFY(mDir.isValid()); QFile agentConf(mDir.path() + QStringLiteral("/gpg-agent.conf")); QVERIFY(agentConf.open(QIODevice::WriteOnly)); agentConf.write("allow-loopback-pinentry"); agentConf.close(); QVERIFY(copyKeyrings(gpgHome, mDir.path())); } private: QTemporaryDir mDir; }; QTEST_MAIN(EncryptionTest) #include "t-encrypt.moc" diff --git a/lang/qt/tests/t-remarks.cpp b/lang/qt/tests/t-remarks.cpp index bba14ce9..f9a901ab 100644 --- a/lang/qt/tests/t-remarks.cpp +++ b/lang/qt/tests/t-remarks.cpp @@ -1,516 +1,471 @@ /* t-remarks.cpp This file is part of qgpgme, the Qt API binding for gpgme Copyright (c) 2017 by Bundesamt für Sicherheit in der Informationstechnik Software engineering by Intevation GmbH QGpgME is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. QGpgME is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "keylistjob.h" #include "protocol.h" #include "signkeyjob.h" #include "context.h" #include "engineinfo.h" #include "t-support.h" using namespace QGpgME; using namespace GpgME; class TestRemarks: public QGpgMETest { Q_OBJECT public: // This test is disabled (no slot) because the behavior // is not clearly defined. Better to prevent that // case in the UI void testRemarkOwnKey() { if (!loopbackSupported()) { return; } // Get the signing key (alfa) auto ctx = Context::create(OpenPGP); QVERIFY (ctx); Error err; auto seckey = ctx->key("A0FF4590BB6122EDEF6E3C542D727CC768697734", err, true); QVERIFY (!seckey.isNull()); QVERIFY (!err); // Create the job auto job = openpgp()->signKeyJob(); QVERIFY (job); - - // Hack in the passphrase provider - auto jobCtx = Job::context(job); - TestPassphraseProvider provider; - jobCtx->setPassphraseProvider(&provider); - jobCtx->setPinentryMode(Context::PinentryLoopback); + hookUpPassphraseProvider(job); // Setup the job job->setExportable(false); std::vector uids; uids.push_back(0); job->setUserIDsToSign(uids); job->setSigningKey(seckey); job->setRemark(QStringLiteral("Just GNU it!")); job->setDupeOk(true); connect(job, &SignKeyJob::result, this, [this] (const GpgME::Error &err2, const QString, const GpgME::Error) { Q_EMIT asyncDone(); if (err2) { qDebug() << "Error: " << err2.asString(); } QVERIFY(err2); }); job->start(seckey); QSignalSpy spy (this, SIGNAL(asyncDone())); QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT)); } private Q_SLOTS: void testRemarkReplaceSingleUIDExportable() { if (!loopbackSupported()) { return; } // Get the signing key (alfa) auto ctx = Context::create(OpenPGP); QVERIFY (ctx); Error err; auto seckey = ctx->key("A0FF4590BB6122EDEF6E3C542D727CC768697734", err, true); QVERIFY (!seckey.isNull()); QVERIFY (!err); // Get the target key (tango) auto target = ctx->key("ECAC774F4EEEB0620767044A58CB9A4C85A81F38", err, false); QVERIFY (!target.isNull()); QVERIFY (!err); QVERIFY (target.numUserIDs()); // Create the job auto job = openpgp()->signKeyJob(); QVERIFY (job); - - // Hack in the passphrase provider - auto jobCtx = Job::context(job); - TestPassphraseProvider provider; - jobCtx->setPassphraseProvider(&provider); - jobCtx->setPinentryMode(Context::PinentryLoopback); + hookUpPassphraseProvider(job); // Setup the job job->setExportable(true); std::vector uids; uids.push_back(0); job->setUserIDsToSign(uids); job->setSigningKey(seckey); job->setRemark(QStringLiteral("The quick brown fox jumps over the lazy dog")); connect(job, &SignKeyJob::result, this, [this] (const GpgME::Error &err2, const QString, const GpgME::Error) { Q_EMIT asyncDone(); QVERIFY(!err2); }); job->start(target); QSignalSpy spy (this, SIGNAL(asyncDone())); QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT)); // At this point the remark should have been added. target.update(); const char *remark = target.userID(0).remark(seckey, err); QVERIFY(!err); QVERIFY(remark); QCOMPARE(QString::fromUtf8(remark), QStringLiteral("The quick brown fox " "jumps over the lazy dog")); // Now replace the remark auto job3 = openpgp()->signKeyJob(); QVERIFY (job3); - - // Hack in the passphrase provider - auto jobCtx3 = Job::context(job3); - jobCtx3->setPassphraseProvider(&provider); - jobCtx3->setPinentryMode(Context::PinentryLoopback); + hookUpPassphraseProvider(job3); // Setup the job job3->setExportable(false); job3->setUserIDsToSign(uids); job3->setSigningKey(seckey); job3->setDupeOk(true); job3->setRemark(QStringLiteral("The quick brown fox fails to jump over Frodo")); connect(job3, &SignKeyJob::result, this, [this] (const GpgME::Error &err2, const QString, const GpgME::Error) { Q_EMIT asyncDone(); QVERIFY(!err2); }); job3->start(target); QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT)); target.update(); remark = target.userID(0).remark(seckey, err); QVERIFY(!err); QVERIFY(remark); QCOMPARE(QString::fromUtf8(remark), QStringLiteral("The quick brown fox fails " "to jump over Frodo")); } void testMultipleRemarks() { if (!loopbackSupported()) { return; } // Get the signing key1 (alfa) auto ctx = Context::create(OpenPGP); QVERIFY (ctx); Error err; auto alpha = ctx->key("A0FF4590BB6122EDEF6E3C542D727CC768697734", err, true); QVERIFY (!alpha.isNull()); QVERIFY (!err); // Get the signing key2 (zulu) auto zulu = ctx->key("23FD347A419429BACCD5E72D6BC4778054ACD246", err, true); QVERIFY (!zulu.isNull()); QVERIFY (!err); // Get the target key (victor) auto target = ctx->key("E8143C489C8D41124DC40D0B47AF4B6961F04784", err, false); QVERIFY (!target.isNull()); QVERIFY (!err); QVERIFY (target.numUserIDs()); // Create the job auto job = openpgp()->signKeyJob(); QVERIFY (job); - - // Hack in the passphrase provider - auto jobCtx = Job::context(job); - TestPassphraseProvider provider; - jobCtx->setPassphraseProvider(&provider); - jobCtx->setPinentryMode(Context::PinentryLoopback); + hookUpPassphraseProvider(job); // Setup the first job job->setExportable(false); std::vector uids; uids.push_back(0); job->setUserIDsToSign(uids); job->setSigningKey(alpha); job->setRemark(QStringLiteral("String one")); connect(job, &SignKeyJob::result, this, [this] (const GpgME::Error &err2, const QString, const GpgME::Error) { Q_EMIT asyncDone(); QVERIFY(!err2); }); job->start(target); QSignalSpy spy (this, SIGNAL(asyncDone())); QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT)); // Now another remark from zulu auto job3 = openpgp()->signKeyJob(); QVERIFY (job3); - - // Hack in the passphrase provider - auto jobCtx3 = Job::context(job3); - jobCtx3->setPassphraseProvider(&provider); - jobCtx3->setPinentryMode(Context::PinentryLoopback); + hookUpPassphraseProvider(job3); // Setup the job job3->setExportable(false); job3->setUserIDsToSign(uids); job3->setSigningKey(zulu); job3->setDupeOk(true); job3->setRemark(QStringLiteral("String two")); connect(job3, &SignKeyJob::result, this, [this] (const GpgME::Error &err2, const QString, const GpgME::Error) { Q_EMIT asyncDone(); QVERIFY(!err2); }); job3->start(target); QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT)); target.update(); std::vector keys; keys.push_back(alpha); keys.push_back(zulu); const auto remarks = target.userID(0).remarks(keys, err); QVERIFY(!err); QVERIFY(remarks.size() == 2); QCOMPARE(remarks[0], std::string("String one")); QCOMPARE(remarks[1], std::string("String two")); } void testRemarkReplaceSingleUID() { if (!loopbackSupported()) { return; } // Get the signing key (alfa) auto ctx = Context::create(OpenPGP); QVERIFY (ctx); Error err; auto seckey = ctx->key("A0FF4590BB6122EDEF6E3C542D727CC768697734", err, true); QVERIFY (!seckey.isNull()); QVERIFY (!err); // Get the target key (xray) auto target = ctx->key("04C1DF62EFA0EBB00519B06A8979A6C5567FB34A", err, false); QVERIFY (!target.isNull()); QVERIFY (!err); QVERIFY (target.numUserIDs()); // Create the job auto job = openpgp()->signKeyJob(); QVERIFY (job); - - // Hack in the passphrase provider - auto jobCtx = Job::context(job); - TestPassphraseProvider provider; - jobCtx->setPassphraseProvider(&provider); - jobCtx->setPinentryMode(Context::PinentryLoopback); + hookUpPassphraseProvider(job); // Setup the job job->setExportable(false); std::vector uids; uids.push_back(0); job->setUserIDsToSign(uids); job->setSigningKey(seckey); job->setRemark(QStringLiteral("The quick brown fox jumps over the lazy dog")); connect(job, &SignKeyJob::result, this, [this] (const GpgME::Error &err2, const QString, const GpgME::Error) { Q_EMIT asyncDone(); QVERIFY(!err2); }); job->start(target); QSignalSpy spy (this, SIGNAL(asyncDone())); QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT)); // At this point the remark should have been added. target.update(); const char *remark = target.userID(0).remark(seckey, err); QVERIFY(!err); QVERIFY(remark); QCOMPARE(QString::fromUtf8(remark), QStringLiteral("The quick brown fox " "jumps over the lazy dog")); // Now replace the remark auto job3 = openpgp()->signKeyJob(); QVERIFY (job3); - - // Hack in the passphrase provider - auto jobCtx3 = Job::context(job3); - jobCtx3->setPassphraseProvider(&provider); - jobCtx3->setPinentryMode(Context::PinentryLoopback); + hookUpPassphraseProvider(job3); // Setup the job job3->setExportable(false); job3->setUserIDsToSign(uids); job3->setSigningKey(seckey); job3->setDupeOk(true); job3->setRemark(QStringLiteral("The quick brown fox jumps over Frodo")); connect(job3, &SignKeyJob::result, this, [this] (const GpgME::Error &err2, const QString, const GpgME::Error) { Q_EMIT asyncDone(); QVERIFY(!err2); }); job3->start(target); QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT)); target.update(); remark = target.userID(0).remark(seckey, err); QVERIFY(!err); QVERIFY(remark); QCOMPARE(QString::fromUtf8(remark), QStringLiteral("The quick brown fox " "jumps over Frodo")); } void testRemarkReplaceMultiUID() { if (!loopbackSupported()) { return; } // Get the signing key (alfa) auto ctx = Context::create(OpenPGP); QVERIFY (ctx); Error err; auto seckey = ctx->key("A0FF4590BB6122EDEF6E3C542D727CC768697734", err, true); QVERIFY (!seckey.isNull()); QVERIFY (!err); // Get the target key (mallory / mike) auto target = ctx->key("2686AA191A278013992C72EBBE794852BE5CF886", err, false); QVERIFY (!target.isNull()); QVERIFY (!err); QVERIFY (target.numUserIDs()); // Create the job auto job = openpgp()->signKeyJob(); QVERIFY (job); - - // Hack in the passphrase provider - auto jobCtx = Job::context(job); - TestPassphraseProvider provider; - jobCtx->setPassphraseProvider(&provider); - jobCtx->setPinentryMode(Context::PinentryLoopback); + hookUpPassphraseProvider(job); // Setup the job job->setExportable(false); std::vector uids; uids.push_back(0); job->setUserIDsToSign(uids); job->setSigningKey(seckey); job->setRemark(QStringLiteral("Mallory is evil 😠")); connect(job, &SignKeyJob::result, this, [this] (const GpgME::Error &err2, const QString, const GpgME::Error) { Q_EMIT asyncDone(); QVERIFY(!err2); }); job->start(target); QSignalSpy spy (this, SIGNAL(asyncDone())); QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT)); // At this point the remark should have been added. target.update(); const char *remark = target.userID(0).remark(seckey, err); QVERIFY(!err); QVERIFY(remark); QCOMPARE(QString::fromUtf8(remark), QStringLiteral("Mallory is evil 😠")); // Try to replace it without dupeOK auto job2 = openpgp()->signKeyJob(); QVERIFY (job2); - - // Hack in the passphrase provider - auto jobCtx2 = Job::context(job2); - jobCtx2->setPassphraseProvider(&provider); - jobCtx2->setPinentryMode(Context::PinentryLoopback); + hookUpPassphraseProvider(job2); // Setup the job job2->setExportable(false); job2->setUserIDsToSign(uids); job2->setSigningKey(seckey); job2->setRemark(QStringLiteral("Mallory is nice")); connect(job2, &SignKeyJob::result, this, [this] (const GpgME::Error &err2, const QString, const GpgME::Error) { Q_EMIT asyncDone(); QVERIFY(err2); }); job2->start(target); QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT)); // Now replace the remark auto job3 = openpgp()->signKeyJob(); QVERIFY (job3); - - // Hack in the passphrase provider - auto jobCtx3 = Job::context(job3); - jobCtx3->setPassphraseProvider(&provider); - jobCtx3->setPinentryMode(Context::PinentryLoopback); + hookUpPassphraseProvider(job3); // Setup the job job3->setExportable(false); job3->setUserIDsToSign(uids); job3->setSigningKey(seckey); job3->setDupeOk(true); job3->setRemark(QStringLiteral("Mallory is nice")); connect(job3, &SignKeyJob::result, this, [this] (const GpgME::Error &err2, const QString, const GpgME::Error) { Q_EMIT asyncDone(); QVERIFY(!err2); }); job3->start(target); QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT)); target.update(); remark = target.userID(0).remark(seckey, err); QVERIFY(!err); QVERIFY(remark); QCOMPARE(QString::fromUtf8(remark), QStringLiteral("Mallory is nice")); } void initTestCase() { QGpgMETest::initTestCase(); const QString gpgHome = qgetenv("GNUPGHOME"); QVERIFY(copyKeyrings(gpgHome, mDir.path())); qputenv("GNUPGHOME", mDir.path().toUtf8()); QFile conf(mDir.path() + QStringLiteral("/gpg.conf")); QVERIFY(conf.open(QIODevice::WriteOnly)); if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() >= "2.2.18") { conf.write("allow-weak-key-signatures"); } conf.close(); } private: QTemporaryDir mDir; }; QTEST_MAIN(TestRemarks) #include "t-remarks.moc" diff --git a/lang/qt/tests/t-support.cpp b/lang/qt/tests/t-support.cpp index 854dd7bd..e827b517 100644 --- a/lang/qt/tests/t-support.cpp +++ b/lang/qt/tests/t-support.cpp @@ -1,126 +1,142 @@ /* t-support.cpp This file is part of qgpgme, the Qt API binding for gpgme Copyright (c) 2016 by Bundesamt für Sicherheit in der Informationstechnik Software engineering by Intevation GmbH QGpgME is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. QGpgME is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "t-support.h" -#include "context.h" + +#include "job.h" #include #include #include #include #include +#include "context.h" #include "engineinfo.h" +using namespace GpgME; +using namespace QGpgME; + void QGpgMETest::initTestCase() { GpgME::initializeLibrary(); const QString gpgHome = qgetenv("GNUPGHOME"); QVERIFY2(!gpgHome.isEmpty(), "GNUPGHOME environment variable is not set."); } void QGpgMETest::cleanupTestCase() { QCoreApplication::sendPostedEvents(); killAgent(); } // static bool QGpgMETest::doOnlineTests() { return !qgetenv("DO_ONLINE_TESTS").isEmpty(); } bool QGpgMETest::copyKeyrings(const QString &src, const QString &dest) { bool is21dir = QFileInfo(src + QDir::separator() + QStringLiteral("pubring.kbx")).exists(); const QString name = is21dir ? QStringLiteral("pubring.kbx") : QStringLiteral("pubring.gpg"); if (!QFile::copy(src + QDir::separator() + name, dest + QDir::separator() + name)) { return false; } if (!is21dir) { return (QFile::copy(src + QDir::separator() + QStringLiteral("secring.gpg"), dest + QDir::separator() + QStringLiteral("secring.gpg"))); } QDir dir (src + QDir::separator() + QStringLiteral("private-keys-v1.d")); QDir target(dest); if (!target.mkdir("private-keys-v1.d")) { return false; } foreach (QString f, dir.entryList(QDir::Files)) { if (!QFile::copy(dir.path() + QDir::separator() + f, dest + QDir::separator() + QStringLiteral("private-keys-v1.d") + QDir::separator() + f)) { return false; } } return true; } +void QGpgMETest::hookUpPassphraseProvider(GpgME::Context *context) +{ + context->setPassphraseProvider(&mPassphraseProvider); + context->setPinentryMode(Context::PinentryLoopback); +} + +void QGpgMETest::hookUpPassphraseProvider(QGpgME::Job *job) +{ + hookUpPassphraseProvider(Job::context(job)); +} + void killAgent(const QString& dir) { QProcess proc; proc.setProgram(QStringLiteral("gpg-connect-agent")); QStringList arguments; arguments << "-S " << dir + "/S.gpg-agent"; proc.start(); proc.waitForStarted(); proc.write("KILLAGENT\n"); proc.write("BYE\n"); proc.closeWriteChannel(); proc.waitForFinished(); } bool loopbackSupported() { /* With GnuPG 2.0.x (at least 2.0.26 by default on jessie) * the passphrase_cb does not work. So the test popped up * a pinentry. So tests requiring decryption don't work. */ static auto version = GpgME::engineInfo(GpgME::GpgEngine).engineVersion(); if (version < "2.0.0") { /* With 1.4 it just works */ return true; } if (version < "2.1.0") { /* With 2.1 it works with loopback mode */ return false; } return true; } #include "t-support.hmoc" diff --git a/lang/qt/tests/t-support.h b/lang/qt/tests/t-support.h index 0cff0c17..0d5757c8 100644 --- a/lang/qt/tests/t-support.h +++ b/lang/qt/tests/t-support.h @@ -1,90 +1,106 @@ /* t-support.h This file is part of qgpgme, the Qt API binding for gpgme Copyright (c) 2016 by Bundesamt für Sicherheit in der Informationstechnik Software engineering by Intevation GmbH QGpgME is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. QGpgME is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #ifndef T_SUPPORT_H #define T_SUPPORT_H #include "interfaces/passphraseprovider.h" #include #include #include +namespace GpgME +{ +class Context; +} + +namespace QGpgME +{ +class Job; +} + namespace QTest { template <> inline char *toString(const std::string &s) { return QTest::toString(s.c_str()); } } namespace GpgME { class TestPassphraseProvider : public PassphraseProvider { public: char *getPassphrase(const char * /*useridHint*/, const char * /*description*/, bool /*previousWasBad*/, bool &/*canceled*/) Q_DECL_OVERRIDE { char *ret; gpgrt_asprintf(&ret, "abc"); return ret; } }; } // namespace GpgME void killAgent(const QString &dir = qgetenv("GNUPGHOME")); /* Is the passphrase Provider / loopback Supported */ bool loopbackSupported(); class QGpgMETest : public QObject { Q_OBJECT Q_SIGNALS: void asyncDone(); protected: static bool doOnlineTests(); bool copyKeyrings(const QString &from, const QString& to); + void hookUpPassphraseProvider(GpgME::Context *context); + void hookUpPassphraseProvider(QGpgME::Job *job); + public Q_SLOTS: void initTestCase(); void cleanupTestCase(); + +private: + GpgME::TestPassphraseProvider mPassphraseProvider; }; /* Timeout, in milliseconds, for use with QSignalSpy to wait on signals. */ #define QSIGNALSPY_TIMEOUT 60000 #endif // T_SUPPORT_H diff --git a/lang/qt/tests/t-tofuinfo.cpp b/lang/qt/tests/t-tofuinfo.cpp index 61543056..a1871c4f 100644 --- a/lang/qt/tests/t-tofuinfo.cpp +++ b/lang/qt/tests/t-tofuinfo.cpp @@ -1,531 +1,528 @@ /* t-tofuinfo.cpp This file is part of qgpgme, the Qt API binding for gpgme Copyright (c) 2016 by Bundesamt für Sicherheit in der Informationstechnik Software engineering by Intevation GmbH QGpgME is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. QGpgME is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "protocol.h" #include "tofuinfo.h" #include "tofupolicyjob.h" #include "verifyopaquejob.h" #include "verificationresult.h" #include "signingresult.h" #include "importjob.h" #include "importresult.h" #include "keylistjob.h" #include "keylistresult.h" #include "signjob.h" #include "key.h" #include "t-support.h" #include "engineinfo.h" #include "context.h" #include using namespace QGpgME; using namespace GpgME; static const char testMsg1[] = "-----BEGIN PGP MESSAGE-----\n" "\n" "owGbwMvMwCSoW1RzPCOz3IRxjXQSR0lqcYleSUWJTZOvjVdpcYmCu1+oQmaJIleH\n" "GwuDIBMDGysTSIqBi1MApi+nlGGuwDeHao53HBr+FoVGP3xX+kvuu9fCMJvl6IOf\n" "y1kvP4y+8D5a11ang0udywsA\n" "=Crq6\n" "-----END PGP MESSAGE-----\n"; static const char conflictKey1[] = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" "\n" "mDMEXDWgpxYJKwYBBAHaRw8BAQdAguVu4qkx8iw4eU+TQ4vvcKG7IdcZvbMhw3Zc\n" "npGf0+u0GXRvZnVfY29uZmxpY3RAZXhhbXBsZS5jb22IkAQTFggAOBYhBO6ovNDG\n" "nLzbR1TlMJYJ0fjlWbUrBQJcNaCnAhsDBQsJCAcCBhUKCQgLAgQWAgMBAh4BAheA\n" "AAoJEJYJ0fjlWbUrLaMBALegwkv2+sEcmKZqxt8JscYvFiEuycv2+rKHaZA0eDoN\n" "AP97W4XrJb5x49J5jDDdeko8k00uGqiiuAXJo27/i/phA7g4BFw1oKcSCisGAQQB\n" "l1UBBQEBB0Crhw24E2lPBhd/y+ZFotQ/2TrYqkUQqGPmff8ofLziNgMBCAeIeAQY\n" "FggAIBYhBO6ovNDGnLzbR1TlMJYJ0fjlWbUrBQJcNaCnAhsMAAoJEJYJ0fjlWbUr\n" "/K8BAJWsa+tOZsJw7w5fz6O0We6Xx4Rt17jHf563G6wMcz9+AQDRsedJ7w4zYzS9\n" "MFiJQ5aN0NDHMRtDFWAgCunVnJ3OBw==\n" "=fZa5\n" "-----END PGP PUBLIC KEY BLOCK-----\n"; static const char conflictKey2[] = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" "\n" "mDMEXDWgixYJKwYBBAHaRw8BAQdAMWOhumYspcvEOTuesOSN4rvnJVOj/6qOWFTu\n" "x+wPRra0GXRvZnVfY29uZmxpY3RAZXhhbXBsZS5jb22IkAQTFggAOBYhBA64G88Q\n" "NPXztj8ID/FhC7tiGbeRBQJcNaCLAhsDBQsJCAcCBhUKCQgLAgQWAgMBAh4BAheA\n" "AAoJEPFhC7tiGbeRUt4A/2hf4Zgz+TYyfeH/4/ZtyL1JuZggqR1s5UopEx2Aiw10\n" "AP405KiTd31TJQN8Ru+7bskPu0/mzLZMNkRvBNEdc5kbDLg4BFw1oIsSCisGAQQB\n" "l1UBBQEBB0B5NtSrx7wDDKgwUe5Rxz0vRkaWLtyE0KbfE77oPy5DGAMBCAeIeAQY\n" "FggAIBYhBA64G88QNPXztj8ID/FhC7tiGbeRBQJcNaCLAhsMAAoJEPFhC7tiGbeR\n" "km0BAP8TQwraipqb1pJlLsEgDXeM5Jocz4fuePD78BsOBtORAP9gpCyKXdyJYGlA\n" "qjmG356yG6pCK9aPckTZ9IViPiHWCw==\n" "=tn3Q\n" "-----END PGP PUBLIC KEY BLOCK-----\n"; static const char conflictMsg1[] = "-----BEGIN PGP MESSAGE-----\n" "\n" "owGbwMvMwCE2jfPij6eRW7UZTwsnMcSYLnT0Ki0uUXD3C1XILFHk6ihlYRDjYJAV\n" "U2R5t2LPhWNz9tx2D3lqANPEygTSwcDFKQAT+RjG8M9of873hQrMpinBVwKYv+rq\n" "XGmYW+ZcZJ+133KDq+itzlxGhg3L2X/6Khj+2Hd+He+KnXtunF2wNWxl7849e/Sy\n" "v6tc+8MBAA==\n" "=fZLe\n" "-----END PGP MESSAGE-----\n"; static const char conflictMsg2[] = "-----BEGIN PGP MESSAGE-----\n" "\n" "owGbwMvMwCH2MZF7d5Lk9omMp4WTGGJMFwZ4lRaXKLj7hSpklihydZSyMIhxMMiK\n" "KbLw7ZA+L2Dy9fM2ew5+mCZWJpAOBi5OAZhIUhIjw7bV+xS+cR0quqhmcY2Dl3WW\n" "8Ufr+rRNufOPyIdoO6nEXGH47/B+E1+oxS6e5f5n7MJ3aHBO+s345sipGV/4f665\n" "9mmiGjsA\n" "=8oJA\n" "-----END PGP MESSAGE-----\n"; class TofuInfoTest: public QGpgMETest { Q_OBJECT private: bool testSupported() { static bool initialized, supported; if (initialized) { return supported; } initialized = true; if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() < "2.1.16") { return false; } // If the keylist fails here this means that gnupg does not // support tofu at all. It can be disabled at compile time. So no // tests. auto *job = openpgp()->keyListJob(false, false, false); job->addMode(GpgME::WithTofu); std::vector keys; job->exec(QStringList() << QStringLiteral("zulu@example.net"), true, keys); delete job; supported = !keys.empty(); return supported; } void testTofuCopy(TofuInfo other, const TofuInfo &orig) { QVERIFY(!orig.isNull()); QVERIFY(!other.isNull()); QVERIFY(orig.signLast() == other.signLast()); QVERIFY(orig.signCount() == other.signCount()); QVERIFY(orig.validity() == other.validity()); QVERIFY(orig.policy() == other.policy()); } void signAndVerify(const QString &what, const GpgME::Key &key, int expected) { auto job = openpgp()->signJob(); - auto ctx = Job::context(job); - TestPassphraseProvider provider; - ctx->setPassphraseProvider(&provider); - ctx->setPinentryMode(Context::PinentryLoopback); + hookUpPassphraseProvider(job); std::vector keys; keys.push_back(key); QByteArray signedData; auto sigResult = job->exec(keys, what.toUtf8(), NormalSignatureMode, signedData); delete job; QVERIFY(!sigResult.error()); foreach (const auto uid, keys[0].userIDs()) { auto info = uid.tofuInfo(); QVERIFY(info.signCount() == expected - 1); } auto verifyJob = openpgp()->verifyOpaqueJob(); QByteArray verified; auto result = verifyJob->exec(signedData, verified); delete verifyJob; QVERIFY(!result.error()); QVERIFY(verified == what.toUtf8()); QVERIFY(result.numSignatures() == 1); auto sig = result.signatures()[0]; auto key2 = sig.key(); QVERIFY(!key.isNull()); QVERIFY(!strcmp (key2.primaryFingerprint(), key.primaryFingerprint())); QVERIFY(!strcmp (key.primaryFingerprint(), sig.fingerprint())); auto stats = key2.userID(0).tofuInfo(); QVERIFY(!stats.isNull()); if (stats.signCount() != expected) { std::cout << "################ Key before verify: " << key << "################ Key after verify: " << key2; } QVERIFY(stats.signCount() == expected); } private Q_SLOTS: void testTofuNull() { if (!testSupported()) { return; } TofuInfo tofu; QVERIFY(tofu.isNull()); QVERIFY(!tofu.description()); QVERIFY(!tofu.signCount()); QVERIFY(!tofu.signLast()); QVERIFY(!tofu.signFirst()); QVERIFY(tofu.validity() == TofuInfo::ValidityUnknown); QVERIFY(tofu.policy() == TofuInfo::PolicyUnknown); } void testTofuInfo() { if (!testSupported()) { return; } auto *job = openpgp()->verifyOpaqueJob(true); const QByteArray data1(testMsg1); QByteArray plaintext; auto ctx = Job::context(job); QVERIFY(ctx); ctx->setSender("alfa@example.net"); auto result = job->exec(data1, plaintext); delete job; QVERIFY(!result.isNull()); QVERIFY(!result.error()); QVERIFY(!strcmp(plaintext.constData(), "Just GNU it!\n")); QVERIFY(result.numSignatures() == 1); Signature sig = result.signatures()[0]; /* TOFU is always marginal */ QVERIFY(sig.validity() == Signature::Marginal); auto stats = sig.key().userID(0).tofuInfo(); QVERIFY(!stats.isNull()); QVERIFY(sig.key().primaryFingerprint()); QVERIFY(sig.fingerprint()); QVERIFY(!strcmp(sig.key().primaryFingerprint(), sig.fingerprint())); QVERIFY(stats.signFirst() == stats.signLast()); QVERIFY(stats.signCount() == 1); QVERIFY(stats.policy() == TofuInfo::PolicyAuto); QVERIFY(stats.validity() == TofuInfo::LittleHistory); testTofuCopy(stats, stats); /* Another verify */ job = openpgp()->verifyOpaqueJob(true); result = job->exec(data1, plaintext); delete job; QVERIFY(!result.isNull()); QVERIFY(!result.error()); QVERIFY(result.numSignatures() == 1); sig = result.signatures()[0]; /* TOFU is always marginal */ QVERIFY(sig.validity() == Signature::Marginal); stats = sig.key().userID(0).tofuInfo(); QVERIFY(!stats.isNull()); QVERIFY(!strcmp(sig.key().primaryFingerprint(), sig.fingerprint())); QVERIFY(stats.signFirst() == stats.signLast()); QVERIFY(stats.signCount() == 1); QVERIFY(stats.policy() == TofuInfo::PolicyAuto); QVERIFY(stats.validity() == TofuInfo::LittleHistory); /* Verify that another call yields the same result */ job = openpgp()->verifyOpaqueJob(true); result = job->exec(data1, plaintext); delete job; QVERIFY(!result.isNull()); QVERIFY(!result.error()); QVERIFY(result.numSignatures() == 1); sig = result.signatures()[0]; /* TOFU is always marginal */ QVERIFY(sig.validity() == Signature::Marginal); stats = sig.key().userID(0).tofuInfo(); QVERIFY(!stats.isNull()); QVERIFY(!strcmp(sig.key().primaryFingerprint(), sig.fingerprint())); QVERIFY(stats.signFirst() == stats.signLast()); QVERIFY(stats.signCount() == 1); QVERIFY(stats.policy() == TofuInfo::PolicyAuto); QVERIFY(stats.validity() == TofuInfo::LittleHistory); } void testTofuSignCount() { if (!testSupported()) { return; } auto *job = openpgp()->keyListJob(false, false, false); job->addMode(GpgME::WithTofu); std::vector keys; GpgME::KeyListResult result = job->exec(QStringList() << QStringLiteral("zulu@example.net"), true, keys); delete job; QVERIFY(!keys.empty()); Key key = keys[0]; QVERIFY(!key.isNull()); /* As we sign & verify quickly here we need different * messages to avoid having them treated as the same * message if they were created within the same second. * Alternatively we could use the same message and wait * a second between each call. But this would slow down * the testsuite. */ signAndVerify(QStringLiteral("Hello"), key, 1); key.update(); signAndVerify(QStringLiteral("Hello2"), key, 2); key.update(); signAndVerify(QStringLiteral("Hello3"), key, 3); key.update(); signAndVerify(QStringLiteral("Hello4"), key, 4); } void testTofuKeyList() { if (!testSupported()) { return; } /* First check that the key has no tofu info. */ auto *job = openpgp()->keyListJob(false, false, false); std::vector keys; auto result = job->exec(QStringList() << QStringLiteral("zulu@example.net"), true, keys); delete job; QVERIFY(!keys.empty()); auto key = keys[0]; QVERIFY(!key.isNull()); QVERIFY(key.userID(0).tofuInfo().isNull()); auto keyCopy = key; keyCopy.update(); auto sigCnt = keyCopy.userID(0).tofuInfo().signCount(); signAndVerify(QStringLiteral("Hello5"), keyCopy, sigCnt + 1); keyCopy.update(); signAndVerify(QStringLiteral("Hello6"), keyCopy, sigCnt + 2); /* Now another one but with tofu */ job = openpgp()->keyListJob(false, false, false); job->addMode(GpgME::WithTofu); result = job->exec(QStringList() << QStringLiteral("zulu@example.net"), true, keys); delete job; QVERIFY(!result.error()); QVERIFY(!keys.empty()); auto key2 = keys[0]; QVERIFY(!key2.isNull()); auto info = key2.userID(0).tofuInfo(); QVERIFY(!info.isNull()); QVERIFY(info.signCount()); } void testTofuPolicy() { if (!testSupported()) { return; } /* First check that the key has no tofu info. */ auto *job = openpgp()->keyListJob(false, false, false); std::vector keys; job->addMode(GpgME::WithTofu); auto result = job->exec(QStringList() << QStringLiteral("bravo@example.net"), false, keys); if (keys.empty()) { qDebug() << "bravo@example.net not found"; qDebug() << "Error: " << result.error().asString(); const auto homedir = QString::fromLocal8Bit(qgetenv("GNUPGHOME")); qDebug() << "Homedir is: " << homedir; QFileInfo fi(homedir + "/pubring.gpg"); qDebug () << "pubring exists: " << fi.exists() << " readable? " << fi.isReadable() << " size: " << fi.size(); QFileInfo fi2(homedir + "/pubring.kbx"); qDebug () << "keybox exists: " << fi2.exists() << " readable? " << fi2.isReadable() << " size: " << fi2.size(); result = job->exec(QStringList(), false, keys); foreach (const auto key, keys) { qDebug() << "Key: " << key.userID(0).name() << " <" << key.userID(0).email() << ">\n fpr: " << key.primaryFingerprint(); } } QVERIFY(!result.error()); QVERIFY(!keys.empty()); auto key = keys[0]; QVERIFY(!key.isNull()); QVERIFY(key.userID(0).tofuInfo().policy() != TofuInfo::PolicyBad); auto *tofuJob = openpgp()->tofuPolicyJob(); auto err = tofuJob->exec(key, TofuInfo::PolicyBad); QVERIFY(!err); result = job->exec(QStringList() << QStringLiteral("bravo@example.net"), false, keys); QVERIFY(!keys.empty()); key = keys[0]; QVERIFY(key.userID(0).tofuInfo().policy() == TofuInfo::PolicyBad); err = tofuJob->exec(key, TofuInfo::PolicyGood); result = job->exec(QStringList() << QStringLiteral("bravo@example.net"), false, keys); key = keys[0]; QVERIFY(key.userID(0).tofuInfo().policy() == TofuInfo::PolicyGood); delete tofuJob; delete job; } void testTofuConflict() { if (!testSupported()) { return; } if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() < "2.1.19") { return; } // Import key 1 auto importjob = openpgp()->importJob(); connect(importjob, &ImportJob::result, this, [this](ImportResult result, QString, Error) { QVERIFY(!result.error()); QVERIFY(!result.imports().empty()); QVERIFY(result.numImported()); Q_EMIT asyncDone(); }); importjob->start(QByteArray(conflictKey1)); QSignalSpy spy (this, SIGNAL(asyncDone())); QVERIFY(spy.wait()); // Verify Message 1 const QByteArray signedData(conflictMsg1); auto verifyJob = openpgp()->verifyOpaqueJob(true); QByteArray verified; auto result = verifyJob->exec(signedData, verified); delete verifyJob; QVERIFY(!result.isNull()); QVERIFY(!result.error()); QVERIFY(result.numSignatures() == 1); auto sig = result.signatures()[0]; QVERIFY(sig.validity() == Signature::Marginal); auto stats = sig.key().userID(0).tofuInfo(); QVERIFY(!stats.isNull()); QVERIFY(!strcmp(sig.key().primaryFingerprint(), sig.fingerprint())); QVERIFY(stats.signFirst() == stats.signLast()); QVERIFY(stats.signCount() == 1); QVERIFY(stats.policy() == TofuInfo::PolicyAuto); QVERIFY(stats.validity() == TofuInfo::LittleHistory); // Import key 2 importjob = openpgp()->importJob(); connect(importjob, &ImportJob::result, this, [this](ImportResult result, QString, Error) { QVERIFY(!result.error()); QVERIFY(!result.imports().empty()); QVERIFY(result.numImported()); Q_EMIT asyncDone(); }); importjob->start(QByteArray(conflictKey2)); QSignalSpy spy2 (this, SIGNAL(asyncDone())); QVERIFY(spy2.wait()); // Verify Message 2 const QByteArray signedData2(conflictMsg2); QByteArray verified2; verifyJob = openpgp()->verifyOpaqueJob(true); result = verifyJob->exec(signedData2, verified2); delete verifyJob; QVERIFY(!result.isNull()); QVERIFY(!result.error()); QVERIFY(result.numSignatures() == 1); sig = result.signatures()[0]; QVERIFY(sig.validity() == Signature::Unknown); // TODO activate when implemented // QVERIFY(sig.summary() == Signature::TofuConflict); stats = sig.key().userID(0).tofuInfo(); QVERIFY(!stats.isNull()); QVERIFY(!strcmp(sig.key().primaryFingerprint(), sig.fingerprint())); QVERIFY(stats.signFirst() == stats.signLast()); QVERIFY(stats.signCount() == 1); QVERIFY(stats.policy() == TofuInfo::PolicyAsk); QVERIFY(stats.validity() == TofuInfo::Conflict); } void initTestCase() { QGpgMETest::initTestCase(); const QString gpgHome = qgetenv("GNUPGHOME"); qputenv("GNUPGHOME", mDir.path().toUtf8()); QVERIFY(mDir.isValid()); QFile conf(mDir.path() + QStringLiteral("/gpg.conf")); QVERIFY(conf.open(QIODevice::WriteOnly)); conf.write("trust-model tofu+pgp"); conf.close(); QFile agentConf(mDir.path() + QStringLiteral("/gpg-agent.conf")); QVERIFY(agentConf.open(QIODevice::WriteOnly)); agentConf.write("allow-loopback-pinentry"); agentConf.close(); QVERIFY(copyKeyrings(gpgHome, mDir.path())); } private: QTemporaryDir mDir; }; QTEST_MAIN(TofuInfoTest) #include "t-tofuinfo.moc" diff --git a/lang/qt/tests/t-trustsignatures.cpp b/lang/qt/tests/t-trustsignatures.cpp index f908c674..6f7ad827 100644 --- a/lang/qt/tests/t-trustsignatures.cpp +++ b/lang/qt/tests/t-trustsignatures.cpp @@ -1,562 +1,522 @@ /* t-trustsignatures.cpp This file is part of qgpgme, the Qt API binding for gpgme Copyright (c) 2021 g10 Code GmbH Software engineering by Ingo Klöcker QGpgME is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. QGpgME is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "t-support.h" #include "context.h" #include "engineinfo.h" #include "protocol.h" #include "signkeyjob.h" #include #include #include using namespace QGpgME; using namespace GpgME; class TestTrustSignatures: public QGpgMETest { Q_OBJECT private Q_SLOTS: void test_tsign_single_uid_key_and_then_tsign_it_again() { Error err; if (!loopbackSupported()) { return; } auto ctx = Context::create(OpenPGP); QVERIFY(ctx); // Get the signing key (alfa@example.net) auto seckey = ctx->key("A0FF4590BB6122EDEF6E3C542D727CC768697734", err, true); QVERIFY(!err); QVERIFY(!seckey.isNull()); // Get the target key (victor@example.org) auto target = ctx->key("E8143C489C8D41124DC40D0B47AF4B6961F04784", err, false); QVERIFY(!err); QVERIFY(!target.isNull()); QVERIFY(target.numUserIDs() > 0); // Create first trust signature { // Create the job auto job = std::unique_ptr{openpgp()->signKeyJob()}; QVERIFY(job); - - // Hack in the passphrase provider - auto jobCtx = Job::context(job.get()); - TestPassphraseProvider provider; - jobCtx->setPassphraseProvider(&provider); - jobCtx->setPinentryMode(Context::PinentryLoopback); + hookUpPassphraseProvider(job.get()); // Setup the job job->setExportable(true); job->setSigningKey(seckey); job->setTrustSignature(TrustSignatureTrust::Complete, 1, QStringLiteral("example.org")); connect(job.get(), &SignKeyJob::result, this, [this] (const GpgME::Error &err2, const QString &, const GpgME::Error &) { Q_EMIT asyncDone(); if (err2) { if (err2.code() == GPG_ERR_GENERAL) { QFAIL(qPrintable(QString("The SignKeyJob failed with '%1'.\n" "Hint: Run with GPGMEPP_INTERACTOR_DEBUG=stderr to debug the edit interaction.").arg(err2.asString()))); } else { QFAIL(qPrintable(QString("The SignKeyJob failed with '%1'.").arg(err2.asString()))); } } }); job->start(target); QSignalSpy spy (this, SIGNAL(asyncDone())); QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT)); // At this point the trust signature should have been added. target.update(); const auto trustSignature = target.userID(0).signature(target.userID(0).numSignatures() - 1); QVERIFY(trustSignature.isTrustSignature()); QCOMPARE(trustSignature.trustDepth(), 1u); QCOMPARE(trustSignature.trustValue(), TrustSignatureTrust::Complete); QVERIFY(trustSignature.trustScope()); const auto trustScope = QString::fromUtf8(trustSignature.trustScope()); QVERIFY(!trustScope.isEmpty()); const QRegExp regex{trustScope}; QVERIFY(regex.isValid()); QVERIFY(regex.indexIn(QStringLiteral("Foo ")) != -1); } // Create second trust signature { // Create the job auto job = std::unique_ptr{openpgp()->signKeyJob()}; QVERIFY(job); - - // Hack in the passphrase provider - auto jobCtx = Job::context(job.get()); - TestPassphraseProvider provider; - jobCtx->setPassphraseProvider(&provider); - jobCtx->setPinentryMode(Context::PinentryLoopback); + hookUpPassphraseProvider(job.get()); // Setup the job job->setExportable(true); job->setSigningKey(seckey); job->setDupeOk(true); job->setTrustSignature(TrustSignatureTrust::Partial, 2, QStringLiteral("example.net")); connect(job.get(), &SignKeyJob::result, this, [this] (const GpgME::Error &err2, const QString &, const GpgME::Error &) { Q_EMIT asyncDone(); if (err2) { if (err2.code() == GPG_ERR_GENERAL) { QFAIL(qPrintable(QString("The SignKeyJob failed with '%1'.\n" "Hint: Run with GPGMEPP_INTERACTOR_DEBUG=stderr to debug the edit interaction.").arg(err2.asString()))); } else { QFAIL(qPrintable(QString("The SignKeyJob failed with '%1'.").arg(err2.asString()))); } } }); err = job->start(target); QVERIFY(!err); QSignalSpy spy (this, SIGNAL(asyncDone())); QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT)); // At this point the trust signature should have been added. target.update(); const auto trustSignature = target.userID(0).signature(target.userID(0).numSignatures() - 1); QVERIFY(trustSignature.isTrustSignature()); QCOMPARE(trustSignature.trustDepth(), 2u); QCOMPARE(trustSignature.trustValue(), TrustSignatureTrust::Partial); QVERIFY(trustSignature.trustScope()); const auto trustScope = QString::fromUtf8(trustSignature.trustScope()); QVERIFY(!trustScope.isEmpty()); const QRegExp regex{trustScope}; QVERIFY(regex.isValid()); QVERIFY(regex.indexIn(QStringLiteral("Foo ")) != -1); } } void test_tsign_multi_uid_key_and_then_tsign_it_again() { Error err; if (!loopbackSupported()) { return; } auto ctx = Context::create(OpenPGP); QVERIFY(ctx); // Get the signing key (alfa@example.net) auto seckey = ctx->key("A0FF4590BB6122EDEF6E3C542D727CC768697734", err, true); QVERIFY(!err); QVERIFY(!seckey.isNull()); // Get the target key (Bob / Bravo Test) auto target = ctx->key("D695676BDCEDCC2CDD6152BCFE180B1DA9E3B0B2", err, false); QVERIFY(!err); QVERIFY(!target.isNull()); QVERIFY(target.numUserIDs() > 0); // Create first trust signature { // Create the job auto job = openpgp()->signKeyJob();//std::unique_ptr{openpgp()->signKeyJob()}; QVERIFY(job); - - // Hack in the passphrase provider - auto jobCtx = Job::context(job); - TestPassphraseProvider provider; - jobCtx->setPassphraseProvider(&provider); - jobCtx->setPinentryMode(Context::PinentryLoopback); + hookUpPassphraseProvider(job); // Setup the job job->setExportable(true); job->setSigningKey(seckey); job->setTrustSignature(TrustSignatureTrust::Complete, 1, QStringLiteral("example.org")); connect(job, &SignKeyJob::result, this, [this] (const GpgME::Error &err2, const QString &, const GpgME::Error &) { Q_EMIT asyncDone(); if (err2) { if (err2.code() == GPG_ERR_GENERAL) { QFAIL(qPrintable(QString("The SignKeyJob failed with '%1'.\n" "Hint: Run with GPGMEPP_INTERACTOR_DEBUG=stderr to debug the edit interaction.").arg(err2.asString()))); } else { QFAIL(qPrintable(QString("The SignKeyJob failed with '%1'.").arg(err2.asString()))); } } }); job->start(target); QSignalSpy spy (this, SIGNAL(asyncDone())); QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT)); // At this point the trust signature should have been added. target.update(); const auto trustSignature = target.userID(0).signature(target.userID(0).numSignatures() - 1); QVERIFY(trustSignature.isTrustSignature()); QCOMPARE(trustSignature.trustDepth(), 1u); QCOMPARE(trustSignature.trustValue(), TrustSignatureTrust::Complete); QVERIFY(trustSignature.trustScope()); const auto trustScope = QString::fromUtf8(trustSignature.trustScope()); QVERIFY(!trustScope.isEmpty()); const QRegExp regex{trustScope}; QVERIFY(regex.isValid()); QVERIFY(regex.indexIn(QStringLiteral("Foo ")) != -1); } // Create second trust signature { // Create the job auto job = openpgp()->signKeyJob();//std::unique_ptr{openpgp()->signKeyJob()}; QVERIFY(job); - - // Hack in the passphrase provider - auto jobCtx = Job::context(job); - TestPassphraseProvider provider; - jobCtx->setPassphraseProvider(&provider); - jobCtx->setPinentryMode(Context::PinentryLoopback); + hookUpPassphraseProvider(job); // Setup the job job->setExportable(true); job->setSigningKey(seckey); job->setDupeOk(true); job->setTrustSignature(TrustSignatureTrust::Partial, 2, QStringLiteral("example.net")); connect(job, &SignKeyJob::result, this, [this] (const GpgME::Error &err2, const QString &, const GpgME::Error &) { Q_EMIT asyncDone(); if (err2) { if (err2.code() == GPG_ERR_GENERAL) { QFAIL(qPrintable(QString("The SignKeyJob failed with '%1'.\n" "Hint: Run with GPGMEPP_INTERACTOR_DEBUG=stderr to debug the edit interaction.").arg(err2.asString()))); } else { QFAIL(qPrintable(QString("The SignKeyJob failed with '%1'.").arg(err2.asString()))); } } }); err = job->start(target); QVERIFY(!err); QSignalSpy spy (this, SIGNAL(asyncDone())); QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT)); // At this point the trust signature should have been added. target.update(); const auto trustSignature = target.userID(0).signature(target.userID(0).numSignatures() - 1); QVERIFY(trustSignature.isTrustSignature()); QCOMPARE(trustSignature.trustDepth(), 2u); QCOMPARE(trustSignature.trustValue(), TrustSignatureTrust::Partial); QVERIFY(trustSignature.trustScope()); const auto trustScope = QString::fromUtf8(trustSignature.trustScope()); QVERIFY(!trustScope.isEmpty()); const QRegExp regex{trustScope}; QVERIFY(regex.isValid()); QVERIFY(regex.indexIn(QStringLiteral("Foo ")) != -1); } } void test_tsign_first_uid_and_then_tsign_both_uids() { Error err; if (!loopbackSupported()) { return; } auto ctx = Context::create(OpenPGP); QVERIFY(ctx); // Get the signing key (alfa@example.net) auto seckey = ctx->key("A0FF4590BB6122EDEF6E3C542D727CC768697734", err, true); QVERIFY(!err); QVERIFY(!seckey.isNull()); // Get the target key (Mallory / Mike Test) auto target = ctx->key("2686AA191A278013992C72EBBE794852BE5CF886", err, false); QVERIFY(!err); QVERIFY(!target.isNull()); QVERIFY(target.numUserIDs() > 0); // Create first trust signature { // Create the job auto job = openpgp()->signKeyJob();//std::unique_ptr{openpgp()->signKeyJob()}; QVERIFY(job); - - // Hack in the passphrase provider - auto jobCtx = Job::context(job); - TestPassphraseProvider provider; - jobCtx->setPassphraseProvider(&provider); - jobCtx->setPinentryMode(Context::PinentryLoopback); + hookUpPassphraseProvider(job); // Setup the job job->setExportable(true); job->setSigningKey(seckey); job->setUserIDsToSign({0}); job->setTrustSignature(TrustSignatureTrust::Complete, 1, QStringLiteral("example.org")); connect(job, &SignKeyJob::result, this, [this] (const GpgME::Error &err2, const QString &, const GpgME::Error &) { Q_EMIT asyncDone(); if (err2) { if (err2.code() == GPG_ERR_GENERAL) { QFAIL(qPrintable(QString("The SignKeyJob failed with '%1'.\n" "Hint: Run with GPGMEPP_INTERACTOR_DEBUG=stderr to debug the edit interaction.").arg(err2.asString()))); } else { QFAIL(qPrintable(QString("The SignKeyJob failed with '%1'.").arg(err2.asString()))); } } }); job->start(target); QSignalSpy spy (this, SIGNAL(asyncDone())); QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT)); // At this point the trust signature should have been added. target.update(); const auto trustSignature = target.userID(0).signature(target.userID(0).numSignatures() - 1); QVERIFY(trustSignature.isTrustSignature()); QCOMPARE(trustSignature.trustDepth(), 1u); QCOMPARE(trustSignature.trustValue(), TrustSignatureTrust::Complete); QVERIFY(trustSignature.trustScope()); const auto trustScope = QString::fromUtf8(trustSignature.trustScope()); QVERIFY(!trustScope.isEmpty()); const QRegExp regex{trustScope}; QVERIFY(regex.isValid()); QVERIFY(regex.indexIn(QStringLiteral("Foo ")) != -1); } // Create second trust signature { // Create the job auto job = openpgp()->signKeyJob();//std::unique_ptr{openpgp()->signKeyJob()}; QVERIFY(job); - - // Hack in the passphrase provider - auto jobCtx = Job::context(job); - TestPassphraseProvider provider; - jobCtx->setPassphraseProvider(&provider); - jobCtx->setPinentryMode(Context::PinentryLoopback); + hookUpPassphraseProvider(job); // Setup the job job->setExportable(true); job->setSigningKey(seckey); job->setDupeOk(true); job->setTrustSignature(TrustSignatureTrust::Partial, 2, QStringLiteral("example.net")); connect(job, &SignKeyJob::result, this, [this] (const GpgME::Error &err2, const QString &, const GpgME::Error &) { Q_EMIT asyncDone(); if (err2) { if (err2.code() == GPG_ERR_GENERAL) { QFAIL(qPrintable(QString("The SignKeyJob failed with '%1'.\n" "Hint: Run with GPGMEPP_INTERACTOR_DEBUG=stderr to debug the edit interaction.").arg(err2.asString()))); } else { QFAIL(qPrintable(QString("The SignKeyJob failed with '%1'.").arg(err2.asString()))); } } }); err = job->start(target); QVERIFY(!err); QSignalSpy spy (this, SIGNAL(asyncDone())); QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT)); // At this point the trust signature should have been added. target.update(); const auto trustSignature = target.userID(0).signature(target.userID(0).numSignatures() - 1); QVERIFY(trustSignature.isTrustSignature()); QCOMPARE(trustSignature.trustDepth(), 2u); QCOMPARE(trustSignature.trustValue(), TrustSignatureTrust::Partial); QVERIFY(trustSignature.trustScope()); const auto trustScope = QString::fromUtf8(trustSignature.trustScope()); QVERIFY(!trustScope.isEmpty()); const QRegExp regex{trustScope}; QVERIFY(regex.isValid()); QVERIFY(regex.indexIn(QStringLiteral("Foo ")) != -1); } } void test_tsign_all_uids_and_then_tsign_first_uid() { Error err; if (!loopbackSupported()) { return; } auto ctx = Context::create(OpenPGP); QVERIFY(ctx); // Get the signing key (alfa@example.net) auto seckey = ctx->key("A0FF4590BB6122EDEF6E3C542D727CC768697734", err, true); QVERIFY(!err); QVERIFY(!seckey.isNull()); // Get the target key (Echelon / Echo Test / Eve) auto target = ctx->key("3531152DE293E26A07F504BC318C1FAEFAEF6D1B", err, false); QVERIFY(!err); QVERIFY(!target.isNull()); QVERIFY(target.numUserIDs() > 0); // Create first trust signature { // Create the job auto job = openpgp()->signKeyJob();//std::unique_ptr{openpgp()->signKeyJob()}; QVERIFY(job); - - // Hack in the passphrase provider - auto jobCtx = Job::context(job); - TestPassphraseProvider provider; - jobCtx->setPassphraseProvider(&provider); - jobCtx->setPinentryMode(Context::PinentryLoopback); + hookUpPassphraseProvider(job); // Setup the job job->setExportable(true); job->setSigningKey(seckey); job->setTrustSignature(TrustSignatureTrust::Complete, 1, QStringLiteral("example.org")); connect(job, &SignKeyJob::result, this, [this] (const GpgME::Error &err2, const QString &, const GpgME::Error &) { Q_EMIT asyncDone(); if (err2) { if (err2.code() == GPG_ERR_GENERAL) { QFAIL(qPrintable(QString("The SignKeyJob failed with '%1'.\n" "Hint: Run with GPGMEPP_INTERACTOR_DEBUG=stderr to debug the edit interaction.").arg(err2.asString()))); } else { QFAIL(qPrintable(QString("The SignKeyJob failed with '%1'.").arg(err2.asString()))); } } }); job->start(target); QSignalSpy spy (this, SIGNAL(asyncDone())); QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT)); // At this point the trust signature should have been added. target.update(); const auto trustSignature = target.userID(0).signature(target.userID(0).numSignatures() - 1); QVERIFY(trustSignature.isTrustSignature()); QCOMPARE(trustSignature.trustDepth(), 1u); QCOMPARE(trustSignature.trustValue(), TrustSignatureTrust::Complete); QVERIFY(trustSignature.trustScope()); const auto trustScope = QString::fromUtf8(trustSignature.trustScope()); QVERIFY(!trustScope.isEmpty()); const QRegExp regex{trustScope}; QVERIFY(regex.isValid()); QVERIFY(regex.indexIn(QStringLiteral("Foo ")) != -1); } // Create second trust signature { // Create the job auto job = openpgp()->signKeyJob();//std::unique_ptr{openpgp()->signKeyJob()}; QVERIFY(job); - - // Hack in the passphrase provider - auto jobCtx = Job::context(job); - TestPassphraseProvider provider; - jobCtx->setPassphraseProvider(&provider); - jobCtx->setPinentryMode(Context::PinentryLoopback); + hookUpPassphraseProvider(job); // Setup the job job->setExportable(true); job->setSigningKey(seckey); job->setUserIDsToSign({0}); job->setDupeOk(true); job->setTrustSignature(TrustSignatureTrust::Partial, 2, QStringLiteral("example.net")); connect(job, &SignKeyJob::result, this, [this] (const GpgME::Error &err2, const QString &, const GpgME::Error &) { Q_EMIT asyncDone(); if (err2) { if (err2.code() == GPG_ERR_GENERAL) { QFAIL(qPrintable(QString("The SignKeyJob failed with '%1'.\n" "Hint: Run with GPGMEPP_INTERACTOR_DEBUG=stderr to debug the edit interaction.").arg(err2.asString()))); } else { QFAIL(qPrintable(QString("The SignKeyJob failed with '%1'.").arg(err2.asString()))); } } }); err = job->start(target); QVERIFY(!err); QSignalSpy spy (this, SIGNAL(asyncDone())); QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT)); // At this point the trust signature should have been added. target.update(); const auto trustSignature = target.userID(0).signature(target.userID(0).numSignatures() - 1); QVERIFY(trustSignature.isTrustSignature()); QCOMPARE(trustSignature.trustDepth(), 2u); QCOMPARE(trustSignature.trustValue(), TrustSignatureTrust::Partial); QVERIFY(trustSignature.trustScope()); const auto trustScope = QString::fromUtf8(trustSignature.trustScope()); QVERIFY(!trustScope.isEmpty()); const QRegExp regex{trustScope}; QVERIFY(regex.isValid()); QVERIFY(regex.indexIn(QStringLiteral("Foo ")) != -1); } } void initTestCase() { QGpgMETest::initTestCase(); const QString gpgHome = qgetenv("GNUPGHOME"); QVERIFY(copyKeyrings(gpgHome, mDir.path())); qputenv("GNUPGHOME", mDir.path().toUtf8()); QFile conf(mDir.path() + QStringLiteral("/gpg.conf")); QVERIFY(conf.open(QIODevice::WriteOnly)); if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() >= "2.2.18") { conf.write("allow-weak-key-signatures"); } conf.close(); } private: QTemporaryDir mDir; }; QTEST_MAIN(TestTrustSignatures) #include "t-trustsignatures.moc" diff --git a/lang/qt/tests/t-various.cpp b/lang/qt/tests/t-various.cpp index 1bb490f5..b630350c 100644 --- a/lang/qt/tests/t-various.cpp +++ b/lang/qt/tests/t-various.cpp @@ -1,425 +1,411 @@ /* t-various.cpp This file is part of qgpgme, the Qt API binding for gpgme Copyright (c) 2017 by Bundesamt für Sicherheit in der Informationstechnik Software engineering by Intevation GmbH QGpgME is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. QGpgME is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "keylistjob.h" #include "protocol.h" #include "keylistresult.h" #include "context.h" #include "engineinfo.h" #include "dn.h" #include "data.h" #include "dataprovider.h" #include "signkeyjob.h" #include "t-support.h" using namespace QGpgME; using namespace GpgME; static const char aKey[] = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" "\n" "mDMEWG+w/hYJKwYBBAHaRw8BAQdAiq1oStvDYg8ZfFs5DgisYJo8dJxD+C/AA21O\n" "K/aif0O0GXRvZnVfY29uZmxpY3RAZXhhbXBsZS5jb22IlgQTFggAPhYhBHoJBLaV\n" "DamYAgoa1L5BwMOl/x88BQJYb7D+AhsDBQkDwmcABQsJCAcCBhUICQoLAgQWAgMB\n" "Ah4BAheAAAoJEL5BwMOl/x88GvwA/0SxkbLyAcshGm2PRrPsFQsSVAfwaSYFVmS2\n" "cMVIw1PfAQDclRH1Z4MpufK07ju4qI33o4s0UFpVRBuSxt7A4P2ZD7g4BFhvsP4S\n" "CisGAQQBl1UBBQEBB0AmVrgaDNJ7K2BSalsRo2EkRJjHGqnp5bBB0tapnF81CQMB\n" "CAeIeAQYFggAIBYhBHoJBLaVDamYAgoa1L5BwMOl/x88BQJYb7D+AhsMAAoJEL5B\n" "wMOl/x88OR0BAMq4/vmJUORRTmzjHcv/DDrQB030DSq666rlckGIKTShAPoDXM9N\n" "0gZK+YzvrinSKZXHmn0aSwmC1/hyPybJPEljBw==\n" "=p2Oj\n" "-----END PGP PUBLIC KEY BLOCK-----\n"; class TestVarious: public QGpgMETest { Q_OBJECT private Q_SLOTS: void testDN() { DN dn(QStringLiteral("CN=Before\\0DAfter,OU=Test,DC=North America,DC=Fabrikam,DC=COM")); QVERIFY(dn.dn() == QStringLiteral("CN=Before\rAfter,OU=Test,DC=North America,DC=Fabrikam,DC=COM")); QStringList attrOrder; attrOrder << QStringLiteral("DC") << QStringLiteral("OU") << QStringLiteral("CN"); dn.setAttributeOrder(attrOrder); QVERIFY(dn.prettyDN() == QStringLiteral("DC=North America,DC=Fabrikam,DC=COM,OU=Test,CN=Before\rAfter")); } void testKeyFromFile() { if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() < "2.1.14") { return; } QGpgME::QByteArrayDataProvider dp(aKey); Data data(&dp); const auto keys = data.toKeys(); QVERIFY(keys.size() == 1); const auto key = keys[0]; QVERIFY(!key.isNull()); QVERIFY(key.primaryFingerprint() == QStringLiteral("7A0904B6950DA998020A1AD4BE41C0C3A5FF1F3C")); } void testDataRewind() { if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() < "2.1.14") { return; } QGpgME::QByteArrayDataProvider dp(aKey); Data data(&dp); char buf[20]; data.read(buf, 20); auto keys = data.toKeys(); QVERIFY(keys.size() == 0); data.rewind(); keys = data.toKeys(); QVERIFY(keys.size() == 1); } void testQuickUid() { if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() < "2.1.13") { return; } KeyListJob *job = openpgp()->keyListJob(false, true, true); std::vector keys; GpgME::KeyListResult result = job->exec(QStringList() << QStringLiteral("alfa@example.net"), false, keys); delete job; QVERIFY (!result.error()); QVERIFY (keys.size() == 1); Key key = keys.front(); QVERIFY (key.numUserIDs() == 3); const char uid[] = "Foo Bar (with comment) "; auto ctx = Context::createForProtocol(key.protocol()); QVERIFY (ctx); - TestPassphraseProvider provider; - ctx->setPassphraseProvider(&provider); - ctx->setPinentryMode(Context::PinentryLoopback); + hookUpPassphraseProvider(ctx); QVERIFY(!ctx->addUid(key, uid)); delete ctx; key.update(); QVERIFY (key.numUserIDs() == 4); bool id_found = false;; for (const auto &u: key.userIDs()) { if (!strcmp (u.id(), uid)) { QVERIFY (!u.isRevoked()); id_found = true; break; } } QVERIFY (id_found); ctx = Context::createForProtocol(key.protocol()); QVERIFY (!ctx->revUid(key, uid)); delete ctx; key.update(); bool id_revoked = false;; for (const auto &u: key.userIDs()) { if (!strcmp (u.id(), uid)) { id_revoked = true; break; } } QVERIFY(id_revoked); } void testSetExpire() { if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() < "2.1.22") { return; } KeyListJob *job = openpgp()->keyListJob(false, true, true); std::vector keys; GpgME::KeyListResult result = job->exec(QStringList() << QStringLiteral("alfa@example.net"), false, keys); delete job; QVERIFY (!result.error()); QVERIFY (keys.size() == 1); Key key = keys.front(); QVERIFY (key.subkey(0).expirationTime() == time_t(0)); QVERIFY (key.subkey(1).expirationTime() == time_t(0)); auto ctx = Context::createForProtocol(key.protocol()); QVERIFY (ctx); - TestPassphraseProvider provider; - ctx->setPassphraseProvider(&provider); - ctx->setPinentryMode(Context::PinentryLoopback); + hookUpPassphraseProvider(ctx); // change expiration of the main key QVERIFY(!ctx->setExpire(key, 1000)); delete ctx; key.update(); QVERIFY (key.subkey(0).expirationTime() != time_t(0)); QVERIFY (key.subkey(1).expirationTime() == time_t(0)); time_t keyExpiration = key.subkey(0).expirationTime(); // change expiration of all subkeys ctx = Context::createForProtocol(key.protocol()); QVERIFY(!ctx->setExpire(key, 2000, std::vector(), Context::SetExpireAllSubkeys)); delete ctx; key.update(); QVERIFY (key.subkey(0).expirationTime() == keyExpiration); QVERIFY (key.subkey(1).expirationTime() != time_t(0)); time_t subkeyExpiration = key.subkey(1).expirationTime(); // change expiration of specific subkey(s) ctx = Context::createForProtocol(key.protocol()); std::vector specificSubkeys; specificSubkeys.push_back(key.subkey(1)); QVERIFY(!ctx->setExpire(key, 3000, specificSubkeys)); delete ctx; key.update(); QVERIFY (key.subkey(0).expirationTime() == keyExpiration); QVERIFY (key.subkey(1).expirationTime() != subkeyExpiration); // test error handling: calling setExpire() with the primary key as // subkey should fail with "subkey not found" ctx = Context::createForProtocol(key.protocol()); std::vector primaryKey; primaryKey.push_back(key.subkey(0)); const auto err = ctx->setExpire(key, 3000, primaryKey); QCOMPARE(err.code(), static_cast(GPG_ERR_NOT_FOUND)); delete ctx; } void testSignKeyWithoutExpiration() { Error err; if (!loopbackSupported()) { return; } auto ctx = Context::create(OpenPGP); QVERIFY(ctx); // Get the signing key (alfa@example.net) auto seckey = ctx->key("A0FF4590BB6122EDEF6E3C542D727CC768697734", err, true); QVERIFY(!err); QVERIFY(!seckey.isNull()); // Get the target key (Bob / Bravo Test) auto target = ctx->key("D695676BDCEDCC2CDD6152BCFE180B1DA9E3B0B2", err, false); QVERIFY(!err); QVERIFY(!target.isNull()); QVERIFY(target.numUserIDs() > 0); // Create the job auto job = std::unique_ptr{openpgp()->signKeyJob()}; QVERIFY(job); - - // Hack in the passphrase provider - auto jobCtx = Job::context(job.get()); - TestPassphraseProvider provider; - jobCtx->setPassphraseProvider(&provider); - jobCtx->setPinentryMode(Context::PinentryLoopback); + hookUpPassphraseProvider(job.get()); // Setup the job job->setExportable(true); job->setSigningKey(seckey); job->setDupeOk(true); connect(job.get(), &SignKeyJob::result, this, [this] (const GpgME::Error &err2, const QString &, const GpgME::Error &) { Q_EMIT asyncDone(); if (err2) { if (err2.code() == GPG_ERR_GENERAL) { QFAIL(qPrintable(QString("The SignKeyJob failed with '%1'.\n" "Hint: Run with GPGMEPP_INTERACTOR_DEBUG=stderr to debug the edit interaction.").arg(err2.asString()))); } else { QFAIL(qPrintable(QString("The SignKeyJob failed with '%1'.").arg(err2.asString()))); } } }); job->start(target); QSignalSpy spy{this, &TestVarious::asyncDone}; QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT)); // At this point the signature should have been added. target.update(); const auto keySignature = target.userID(0).signature(target.userID(0).numSignatures() - 1); QVERIFY(keySignature.neverExpires()); } void testSignKeyWithExpiration() { Error err; if (!loopbackSupported()) { return; } auto ctx = Context::create(OpenPGP); QVERIFY(ctx); // Get the signing key (alfa@example.net) auto seckey = ctx->key("A0FF4590BB6122EDEF6E3C542D727CC768697734", err, true); QVERIFY(!err); QVERIFY(!seckey.isNull()); // Get the target key (Bob / Bravo Test) auto target = ctx->key("D695676BDCEDCC2CDD6152BCFE180B1DA9E3B0B2", err, false); QVERIFY(!err); QVERIFY(!target.isNull()); QVERIFY(target.numUserIDs() > 0); // Create the job auto job = std::unique_ptr{openpgp()->signKeyJob()}; QVERIFY(job); - - // Hack in the passphrase provider - auto jobCtx = Job::context(job.get()); - TestPassphraseProvider provider; - jobCtx->setPassphraseProvider(&provider); - jobCtx->setPinentryMode(Context::PinentryLoopback); + hookUpPassphraseProvider(job.get()); // Setup the job job->setExportable(true); job->setSigningKey(seckey); job->setDupeOk(true); job->setExpirationDate(QDate{2222, 2, 22}); connect(job.get(), &SignKeyJob::result, this, [this] (const GpgME::Error &err2, const QString &, const GpgME::Error &) { Q_EMIT asyncDone(); if (err2) { if (err2.code() == GPG_ERR_GENERAL) { QFAIL(qPrintable(QString("The SignKeyJob failed with '%1'.\n" "Hint: Run with GPGMEPP_INTERACTOR_DEBUG=stderr to debug the edit interaction.").arg(err2.asString()))); } else { QFAIL(qPrintable(QString("The SignKeyJob failed with '%1'.").arg(err2.asString()))); } } }); QTest::ignoreMessage(QtWarningMsg, "Expiration of certification has been changed to QDate(\"2106-02-06\")"); job->start(target); QSignalSpy spy{this, &TestVarious::asyncDone}; QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT)); // At this point the signature should have been added. target.update(); const auto keySignature = target.userID(0).signature(target.userID(0).numSignatures() - 1); QVERIFY(!keySignature.neverExpires()); const auto expirationDate = QDateTime::fromSecsSinceEpoch(uint_least32_t(keySignature.expirationTime())).date(); QCOMPARE(expirationDate, QDate(2106, 2, 6)); // expiration date is capped at 2106-02-06 } void testVersion() { QVERIFY(EngineInfo::Version("2.1.0") < EngineInfo::Version("2.1.1")); QVERIFY(EngineInfo::Version("2.1.10") < EngineInfo::Version("2.1.11")); QVERIFY(EngineInfo::Version("2.2.0") > EngineInfo::Version("2.1.19")); QVERIFY(EngineInfo::Version("1.0.0") < EngineInfo::Version("2.0.0")); QVERIFY(EngineInfo::Version("0.1.0") < EngineInfo::Version("1.0.0")); QVERIFY(!(EngineInfo::Version("2.0.0") < EngineInfo::Version("2.0.0"))); QVERIFY(!(EngineInfo::Version("2.0.0") > EngineInfo::Version("2.0.0"))); QVERIFY(EngineInfo::Version("3.0.0") > EngineInfo::Version("2.3.20")); QVERIFY(EngineInfo::Version("3.0.1") > EngineInfo::Version("3.0.0")); QVERIFY(EngineInfo::Version("3.1.0") > EngineInfo::Version("3.0.20")); QVERIFY(EngineInfo::Version("1.1.1") <= "2.0.0"); QVERIFY(EngineInfo::Version("1.1.1") <= "1.2.0"); QVERIFY(EngineInfo::Version("1.1.1") <= "1.1.2"); QVERIFY(EngineInfo::Version("1.1.1") <= "1.1.1"); QVERIFY(!(EngineInfo::Version("1.1.1") <= "1.1.0")); QVERIFY(!(EngineInfo::Version("1.1.1") <= "1.0.9")); QVERIFY(!(EngineInfo::Version("1.1.1") <= "0.9.9")); QVERIFY(!(EngineInfo::Version("1.1.1") == "2.0.0")); QVERIFY(!(EngineInfo::Version("1.1.1") == "1.2.0")); QVERIFY(!(EngineInfo::Version("1.1.1") == "1.1.2")); QVERIFY(EngineInfo::Version("1.1.1") == "1.1.1"); QVERIFY(!(EngineInfo::Version("1.1.1") == "1.1.0")); QVERIFY(!(EngineInfo::Version("1.1.1") == "1.0.9")); QVERIFY(!(EngineInfo::Version("1.1.1") == "0.9.9")); QVERIFY(EngineInfo::Version("1.1.1") != "2.0.0"); QVERIFY(EngineInfo::Version("1.1.1") != "1.2.0"); QVERIFY(EngineInfo::Version("1.1.1") != "1.1.2"); QVERIFY(!(EngineInfo::Version("1.1.1") != "1.1.1")); QVERIFY(EngineInfo::Version("1.1.1") != "1.1.0"); QVERIFY(EngineInfo::Version("1.1.1") != "1.0.9"); QVERIFY(EngineInfo::Version("1.1.1") != "0.9.9"); QVERIFY(!(EngineInfo::Version("1.1.1") >= "2.0.0")); QVERIFY(!(EngineInfo::Version("1.1.1") >= "1.2.0")); QVERIFY(!(EngineInfo::Version("1.1.1") >= "1.1.2")); QVERIFY(EngineInfo::Version("1.1.1") >= "1.1.1"); QVERIFY(EngineInfo::Version("1.1.1") >= "1.1.0"); QVERIFY(EngineInfo::Version("1.1.1") >= "1.0.9"); QVERIFY(EngineInfo::Version("1.1.1") >= "0.9.9"); } void initTestCase() { QGpgMETest::initTestCase(); const QString gpgHome = qgetenv("GNUPGHOME"); QVERIFY(copyKeyrings(gpgHome, mDir.path())); qputenv("GNUPGHOME", mDir.path().toUtf8()); QFile conf(mDir.path() + QStringLiteral("/gpg.conf")); QVERIFY(conf.open(QIODevice::WriteOnly)); if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() >= "2.2.18") { conf.write("allow-weak-key-signatures"); } conf.close(); } private: QTemporaryDir mDir; }; QTEST_MAIN(TestVarious) #include "t-various.moc"