diff --git a/lang/qt/tests/t-changeexpiryjob.cpp b/lang/qt/tests/t-changeexpiryjob.cpp index 590a60bf..e20d1be2 100644 --- a/lang/qt/tests/t-changeexpiryjob.cpp +++ b/lang/qt/tests/t-changeexpiryjob.cpp @@ -1,424 +1,421 @@ /* 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 -Q_SIGNALS: - void asyncDone(); - 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); // 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); // 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); // 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); // 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); // 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 9ad10331..2249de3b 100644 --- a/lang/qt/tests/t-encrypt.cpp +++ b/lang/qt/tests/t-encrypt.cpp @@ -1,336 +1,333 @@ /* 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 -Q_SIGNALS: - void asyncDone(); - 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); 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(); 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); 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); 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(); 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(); 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(); 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-import.cpp b/lang/qt/tests/t-import.cpp index 06786bc0..456d7d64 100644 --- a/lang/qt/tests/t-import.cpp +++ b/lang/qt/tests/t-import.cpp @@ -1,172 +1,169 @@ /* t-import.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 "importjob.h" #include #include #include #include #include using namespace QGpgME; using namespace GpgME; class ImportTest : public QGpgMETest { Q_OBJECT private: QTemporaryDir tempGpgHome; -Q_SIGNALS: - void asyncDone(); - private Q_SLOTS: void initTestCase() { QGpgMETest::initTestCase(); QVERIFY2(tempGpgHome.isValid(), "Failed to create temporary GNUPGHOME"); qputenv("GNUPGHOME", tempGpgHome.path().toLocal8Bit()); } void testImportWithImportFilter() { if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() < "2.1.14") { QSKIP("gpg does not yet support the --import-filter option"); } // pub ed25519 2021-12-15 [SC] // E7A0841292ACC9465D3142652FB3A6F51FBF28A2 // uid [ultimate] importWithImportFilter@example.com // uid [ultimate] importWithImportFilter@example.net // sub cv25519 2021-12-15 [E] static const char keyFpr[] = "E7A0841292ACC9465D3142652FB3A6F51FBF28A2"; static const char keyData[] = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" "\n" "mDMEYbm2PhYJKwYBBAHaRw8BAQdACzxBWtNNsmJ6rzpZkjh1yBe+Ajsk9NR8umEu\n" "Da3HLgG0ImltcG9ydFdpdGhJbXBvcnRGaWx0ZXJAZXhhbXBsZS5uZXSIlAQTFgoA\n" "PBYhBOeghBKSrMlGXTFCZS+zpvUfvyiiBQJhubY+AhsDBQsJCAcCAyICAQYVCgkI\n" "CwIEFgIDAQIeBwIXgAAKCRAvs6b1H78oosRgAQCc/ke6q076nvzIE2UzT83JK/B6\n" "lxSV7Fb8bKltOMpvsAD+Phap3EzA8jdMyKoO0FM926bw5lX7QROfeZ/JBYqyPwC0\n" "ImltcG9ydFdpdGhJbXBvcnRGaWx0ZXJAZXhhbXBsZS5jb22IlAQTFgoAPBYhBOeg\n" "hBKSrMlGXTFCZS+zpvUfvyiiBQJhubZlAhsDBQsJCAcCAyICAQYVCgkICwIEFgID\n" "AQIeBwIXgAAKCRAvs6b1H78oohPWAQC/u9UXzkxRkrB2huaTZCsyimWEGZIMmxWd\n" "tE+vN9/IvQD/Yzia+xRS6yca3Yz6iW8xS844ZqRxvkUEHjtJXSOzagm4OARhubY+\n" "EgorBgEEAZdVAQUBAQdANQFjmDctY3N0/ELPZtj9tapwFs4vrmTVpx/SCfZmihkD\n" "AQgHiHgEGBYKACAWIQTnoIQSkqzJRl0xQmUvs6b1H78oogUCYbm2PgIbDAAKCRAv\n" "s6b1H78oovGyAP41ySzvvDpV7XDJBOAFxvWLmywa5IcO7Lrg7y1efoWj0AD+Kk/B\n" "s7jGLdoG51h670h50MMoYCANB6MwAdSP+qZUlQg=\n" "=/3O0\n" "-----END PGP PUBLIC KEY BLOCK-----\n"; auto *job = openpgp()->importJob(); job->setImportFilter(QLatin1String{"keep-uid=mbox = importWithImportFilter@example.net"}); connect(job, &ImportJob::result, this, [this](ImportResult result, QString, Error) { QVERIFY(!result.error()); QVERIFY(!result.imports().empty()); QVERIFY(result.numImported()); Q_EMIT asyncDone(); }); job->start(QByteArray{keyData}); QSignalSpy spy (this, SIGNAL(asyncDone())); QVERIFY(spy.wait()); auto ctx = Context::createForProtocol(GpgME::OpenPGP); GpgME::Error err; const auto key = ctx->key(keyFpr, err, false); QVERIFY(!key.isNull()); QCOMPARE(key.numUserIDs(), 1); QCOMPARE(key.userID(0).id(), "importWithImportFilter@example.net"); } void testImportWithKeyOrigin() { if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() < "2.1.22") { QSKIP("gpg does not yet support the --key-origin option"); } static const char keyFpr[] = "5C5C428FABCC20F6913464BCCA6FB442887289B3"; static const char keyData[] = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" "\n" "mDMEYbhuixYJKwYBBAHaRw8BAQdAulOM3IksCjdOJluEVlwalD8oZ5oa6wCw3EgW\n" "NswXXb60H2ltcG9ydFdpdGhLZXlPcmlnaW5AZXhhbXBsZS5uZXSIlAQTFgoAPBYh\n" "BFxcQo+rzCD2kTRkvMpvtEKIcomzBQJhuG6LAhsDBQsJCAcCAyICAQYVCgkICwIE\n" "FgIDAQIeBwIXgAAKCRDKb7RCiHKJs+cIAQDaeoOw1OCAGpZQb8xJmLJHul5dLLzU\n" "RBdHauMx9NROmQEA23QUVedc7walQjNKFzyIJA/YqRdbAKPiLonRBmxk9Ay4OARh\n" "uG6LEgorBgEEAZdVAQUBAQdAMVdO9mNWIP/q8PtNOnBGlPyhx/vs07sF5sXk50A+\n" "61QDAQgHiHgEGBYKACAWIQRcXEKPq8wg9pE0ZLzKb7RCiHKJswUCYbhuiwIbDAAK\n" "CRDKb7RCiHKJs/x6AP0SEbZqW4iLCz2i1JntQghK5qpSZOVqsBTcARd6pcJ/cwEA\n" "mrwskWazuS9+GVbHT5RATWOXnGaj+AICSDPE6qHtGgA=\n" "=putz\n" "-----END PGP PUBLIC KEY BLOCK-----\n"; auto *job = openpgp()->importJob(); job->setKeyOrigin(GpgME::Key::OriginWKD, "https://example.net"); connect(job, &ImportJob::result, this, [this](ImportResult result, QString, Error) { QVERIFY(!result.error()); QVERIFY(!result.imports().empty()); QVERIFY(result.numImported()); Q_EMIT asyncDone(); }); job->start(QByteArray{keyData}); QSignalSpy spy (this, SIGNAL(asyncDone())); QVERIFY(spy.wait()); auto ctx = Context::createForProtocol(GpgME::OpenPGP); GpgME::Error err; const auto key = ctx->key(keyFpr, err, false); QVERIFY(!key.isNull()); QVERIFY(key.origin() == Key::OriginWKD); // the origin URL is currently not available in GpgME } }; QTEST_MAIN(ImportTest) #include "t-import.moc" diff --git a/lang/qt/tests/t-keylist.cpp b/lang/qt/tests/t-keylist.cpp index dddcb738..e481dfea 100644 --- a/lang/qt/tests/t-keylist.cpp +++ b/lang/qt/tests/t-keylist.cpp @@ -1,209 +1,206 @@ /* t-keylist.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 "keylistjob.h" #include "listallkeysjob.h" #include "qgpgmebackend.h" #include "keylistresult.h" #include "context.h" #include "engineinfo.h" #include #include "t-support.h" using namespace QGpgME; using namespace GpgME; class KeyListTest : public QGpgMETest { Q_OBJECT -Q_SIGNALS: - void asyncDone(); - private Q_SLOTS: void testSingleKeyListSync() { KeyListJob *job = openpgp()->keyListJob(false, false, false); std::vector keys; GpgME::KeyListResult result = job->exec(QStringList() << QStringLiteral("alfa@example.net"), false, keys); delete job; QVERIFY (!result.error()); QVERIFY (keys.size() == 1); const QString kId = QLatin1String(keys.front().keyID()); QVERIFY (kId == QStringLiteral("2D727CC768697734")); QVERIFY (keys[0].subkeys().size() == 2); QVERIFY (keys[0].subkeys()[0].publicKeyAlgorithm() == Subkey::AlgoDSA); QVERIFY (keys[0].subkeys()[1].publicKeyAlgorithm() == Subkey::AlgoELG_E); } // This test can help with valgrind to check for memleaks when handling // keys void testGetKey() { GpgME::Key key; { auto ctx = std::unique_ptr (GpgME::Context::createForProtocol(GpgME::OpenPGP)); ctx->setKeyListMode (GpgME::KeyListMode::Local | GpgME::KeyListMode::Signatures | GpgME::KeyListMode::Validate | GpgME::KeyListMode::WithTofu); GpgME::Error err; key = ctx->key ("A0FF4590BB6122EDEF6E3C542D727CC768697734", err, false); } QVERIFY(key.primaryFingerprint()); QVERIFY(!strcmp(key.primaryFingerprint(), "A0FF4590BB6122EDEF6E3C542D727CC768697734")); { auto ctx = std::unique_ptr (GpgME::Context::createForProtocol(GpgME::OpenPGP)); ctx->setKeyListMode (GpgME::KeyListMode::Local | GpgME::KeyListMode::Signatures | GpgME::KeyListMode::Validate | GpgME::KeyListMode::WithTofu); GpgME::Error err; key = ctx->key ("A0FF4590BB6122EDEF6E3C542D727CC768697734", err, false); } QVERIFY(key.primaryFingerprint()); QVERIFY(!strcmp(key.primaryFingerprint(), "A0FF4590BB6122EDEF6E3C542D727CC768697734")); } void testPubkeyAlgoAsString() { static const QMap expected { { Subkey::AlgoRSA, QStringLiteral("RSA") }, { Subkey::AlgoRSA_E, QStringLiteral("RSA-E") }, { Subkey::AlgoRSA_S, QStringLiteral("RSA-S") }, { Subkey::AlgoELG_E, QStringLiteral("ELG-E") }, { Subkey::AlgoDSA, QStringLiteral("DSA") }, { Subkey::AlgoECC, QStringLiteral("ECC") }, { Subkey::AlgoELG, QStringLiteral("ELG") }, { Subkey::AlgoECDSA, QStringLiteral("ECDSA") }, { Subkey::AlgoECDH, QStringLiteral("ECDH") }, { Subkey::AlgoEDDSA, QStringLiteral("EdDSA") }, { Subkey::AlgoUnknown, QString() } }; Q_FOREACH (Subkey::PubkeyAlgo algo, expected.keys()) { QVERIFY(QString::fromUtf8(Subkey::publicKeyAlgorithmAsString(algo)) == expected.value(algo)); } } void testKeyListAsync() { KeyListJob *job = openpgp()->keyListJob(); connect(job, &KeyListJob::result, job, [this, job](KeyListResult, std::vector keys, QString, Error) { QVERIFY(keys.size() == 1); Q_EMIT asyncDone(); }); job->start(QStringList() << "alfa@example.net"); QSignalSpy spy (this, SIGNAL(asyncDone())); QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT)); } void testListAllKeysSync() { const auto accumulateFingerprints = [](std::vector &v, const Key &key) { v.push_back(std::string(key.primaryFingerprint())); return v; }; ListAllKeysJob *job = openpgp()->listAllKeysJob(/* includeSigs= */false, /* validate= */false); std::vector pubKeys, secKeys; GpgME::KeyListResult result = job->exec(pubKeys, secKeys, /* mergeKeys= */false); // mergeKeys is unused for GnuPG >= 2.1 delete job; QVERIFY(!result.error()); QCOMPARE(secKeys.size(), static_cast(2)); std::vector secKeyFingerprints = std::accumulate(secKeys.begin(), secKeys.end(), std::vector(), accumulateFingerprints); QCOMPARE(secKeyFingerprints, std::vector({ "23FD347A419429BACCD5E72D6BC4778054ACD246", "A0FF4590BB6122EDEF6E3C542D727CC768697734" })); QVERIFY(secKeys[0].hasSecret()); if (!(GpgME::engineInfo(GpgME::GpgEngine).engineVersion() < "2.1.0")) { QVERIFY(secKeys[0].subkeys()[0].keyGrip()); } QCOMPARE(pubKeys.size(), static_cast(26)); std::vector pubKeyFingerprints = std::accumulate(pubKeys.begin(), pubKeys.end(), std::vector(), accumulateFingerprints); QCOMPARE(pubKeyFingerprints, std::vector({ "045B2334ADD69FC221076841A5E67F7FA3AE3EA1", "04C1DF62EFA0EBB00519B06A8979A6C5567FB34A", "0DBCAD3F08843B9557C6C4D4A94C0F75653244D6", "1DDD28CEF714F5B03B8C246937CAB51FB79103F8", "23FD347A419429BACCD5E72D6BC4778054ACD246", "2686AA191A278013992C72EBBE794852BE5CF886", "3531152DE293E26A07F504BC318C1FAEFAEF6D1B", "38FBE1E4BF6A5E1242C8F6A13BDBEDB1777FBED3", "3FD11083779196C2ECDD9594AD1B0FAD43C2D0C7", "43929E89F8F79381678CAE515F6356BA6D9732AC", "56D33268F7FE693FBB594762D4BF57F37372E243", "5AB9D6D7BAA1C95B3BAA3D9425B00FD430CEC684", "61EE841A2A27EB983B3B3C26413F4AF31AFDAB6C", "6560C59C43D031C54D7C588EEBA9F240EB9DC9E6", "6FAA9C201E5E26DCBAEC39FD5D15E01D3FF13206", "9E91CBB11E4D4135583EF90513DB965534C6E3F1", "A0FF4590BB6122EDEF6E3C542D727CC768697734", "A7969DA1C3297AA96D49843F1C67EC133C661C84", "C9C07DCC6621B9FB8D071B1D168410A48FC282E6", "CD538D6CC9FB3D745ECDA5201FE8FC6F04259677", "D695676BDCEDCC2CDD6152BCFE180B1DA9E3B0B2", "E8143C489C8D41124DC40D0B47AF4B6961F04784", "E8D6C90B683B0982BD557A99DEF0F7B8EC67DBDE", "ECAC774F4EEEB0620767044A58CB9A4C85A81F38", "ED9B316F78644A58D042655A9EEF34CD4B11B25F", "F8F1EDC73995AB739AD54B380C820C71D2699313" })); if (!(GpgME::engineInfo(GpgME::GpgEngine).engineVersion() < "2.1.0")) { // with GnuPG >= 2.1 the job always lists keys with --with-keygrip and --with-secret, // i.e. the key grips and information about secret keys are always available QVERIFY(!pubKeys[0].hasSecret()); QVERIFY(pubKeys[0].subkeys()[0].keyGrip()); QVERIFY(pubKeys[4].hasSecret()); QVERIFY(pubKeys[4].subkeys()[0].keyGrip()); } } }; QTEST_MAIN(KeyListTest) #include "t-keylist.moc" diff --git a/lang/qt/tests/t-keylocate.cpp b/lang/qt/tests/t-keylocate.cpp index 6d00da3a..d84249c2 100644 --- a/lang/qt/tests/t-keylocate.cpp +++ b/lang/qt/tests/t-keylocate.cpp @@ -1,135 +1,132 @@ /* t-keylocate.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 "keylistjob.h" #include "protocol.h" #include "keylistresult.h" #include "engineinfo.h" #include "t-support.h" using namespace QGpgME; using namespace GpgME; class KeyLocateTest : public QGpgMETest { Q_OBJECT -Q_SIGNALS: - void asyncDone(); - private Q_SLOTS: #ifdef DO_ONLINE_TESTS void testDaneKeyLocate() { QTemporaryDir dir; const QString oldHome = qgetenv("GNUPGHOME"); qputenv("GNUPGHOME", dir.path().toUtf8()); /* Could do this with gpgconf but this is not a gpgconf test ;-) */ QFile conf(dir.path() + QStringLiteral("/gpg.conf")); QVERIFY(conf.open(QIODevice::WriteOnly)); conf.write("auto-key-locate dane"); conf.close(); auto *job = openpgp()->locateKeysJob(); mTestpattern = QStringLiteral("wk@gnupg.org"); connect(job, &KeyListJob::result, job, [this, job](KeyListResult result, std::vector keys, QString, Error) { QVERIFY(!result.error()); QVERIFY(keys.size() == 1); Key k = keys.front(); QVERIFY(k.numUserIDs()); bool found = false; Q_FOREACH (const UserID uid, k.userIDs()) { const QString mailBox = QString::fromUtf8(uid.email()); if (mTestpattern.toLower() == mailBox.toLower()) { found = true; } } QVERIFY(found); Q_EMIT asyncDone(); }); job->start(QStringList() << mTestpattern); QSignalSpy spy (this, SIGNAL(asyncDone())); QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT)); qputenv("GNUPGHOME", oldHome.toUtf8()); } #endif void testKeyLocateSingle() { if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() < "2.0.10") { return; } auto *job = openpgp()->locateKeysJob(); mTestpattern = QStringLiteral("alfa@example.net"); connect(job, &KeyListJob::result, job, [this, job](KeyListResult result, std::vector keys, QString, Error) { QVERIFY(!result.isNull()); QVERIFY(!result.isTruncated()); QVERIFY(!result.error()); QVERIFY(keys.size() == 1); Key k = keys.front(); QVERIFY(k.numUserIDs()); bool found = false; Q_FOREACH (const UserID uid, k.userIDs()) { const QString mailBox = QString::fromUtf8(uid.email()); if (mTestpattern.toLower() == mailBox.toLower()) { found = true; } } QVERIFY(found); Q_EMIT asyncDone(); }); job->start(QStringList() << mTestpattern); QSignalSpy spy (this, SIGNAL(asyncDone())); QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT)); } private: QString mTestpattern; }; QTEST_MAIN(KeyLocateTest) #include "t-keylocate.moc" diff --git a/lang/qt/tests/t-ownertrust.cpp b/lang/qt/tests/t-ownertrust.cpp index 093c21e4..31d22479 100644 --- a/lang/qt/tests/t-ownertrust.cpp +++ b/lang/qt/tests/t-ownertrust.cpp @@ -1,112 +1,109 @@ /* t-ownertrust.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 "keylistjob.h" #include "protocol.h" #include "keylistresult.h" #include "changeownertrustjob.h" #include "t-support.h" using namespace QGpgME; using namespace GpgME; class ChangeOwnerTrustTest: public QGpgMETest { Q_OBJECT -Q_SIGNALS: - void asyncDone(); - private Q_SLOTS: void testChangeOwnerTrust() { 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.ownerTrust() == Key::Unknown); ChangeOwnerTrustJob *job2 = openpgp()->changeOwnerTrustJob(); connect(job2, &ChangeOwnerTrustJob::result, this, [this](Error e) { if (e) { qDebug() << "Error in result: " << e.asString(); } QVERIFY(!e); Q_EMIT asyncDone(); }); job2->start(key, Key::Ultimate); QSignalSpy spy (this, SIGNAL(asyncDone())); QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT)); job = openpgp()->keyListJob(false, true, true); result = job->exec(QStringList() << QStringLiteral("alfa@example.net"), false, keys); delete job; key = keys.front(); QVERIFY (key.ownerTrust() == Key::Ultimate); ChangeOwnerTrustJob *job3 = openpgp()->changeOwnerTrustJob(); connect(job3, &ChangeOwnerTrustJob::result, this, [this](Error e) { QVERIFY(!e); Q_EMIT asyncDone(); }); job3->start(key, Key::Unknown); QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT)); job = openpgp()->keyListJob(false, true, true); result = job->exec(QStringList() << QStringLiteral("alfa@example.net"), false, keys); delete job; key = keys.front(); QVERIFY (key.ownerTrust() == Key::Unknown); } }; QTEST_MAIN(ChangeOwnerTrustTest) #include "t-ownertrust.moc" diff --git a/lang/qt/tests/t-remarks.cpp b/lang/qt/tests/t-remarks.cpp index c1880a7d..bba14ce9 100644 --- a/lang/qt/tests/t-remarks.cpp +++ b/lang/qt/tests/t-remarks.cpp @@ -1,519 +1,516 @@ /* 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 -Q_SIGNALS: - void asyncDone(); - 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); // 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); // 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); // 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); // 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); // 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); // 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); // 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); // 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); // 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); // 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.h b/lang/qt/tests/t-support.h index 8435de5b..0cff0c17 100644 --- a/lang/qt/tests/t-support.h +++ b/lang/qt/tests/t-support.h @@ -1,86 +1,90 @@ /* 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 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); public Q_SLOTS: void initTestCase(); void cleanupTestCase(); }; /* 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 2d881069..61543056 100644 --- a/lang/qt/tests/t-tofuinfo.cpp +++ b/lang/qt/tests/t-tofuinfo.cpp @@ -1,533 +1,531 @@ /* 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 -Q_SIGNALS: - void asyncDone(); 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); 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 14f8d9a9..f908c674 100644 --- a/lang/qt/tests/t-trustsignatures.cpp +++ b/lang/qt/tests/t-trustsignatures.cpp @@ -1,565 +1,562 @@ /* 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 -Q_SIGNALS: - void asyncDone(); - 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); // 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); // 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); // 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); // 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); // 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); // 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); // 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); // 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 dca34d30..1bb490f5 100644 --- a/lang/qt/tests/t-various.cpp +++ b/lang/qt/tests/t-various.cpp @@ -1,428 +1,425 @@ /* 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 -Q_SIGNALS: - void asyncDone(); - 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); 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); // 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); // 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); // 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" diff --git a/lang/qt/tests/t-wkdlookup.cpp b/lang/qt/tests/t-wkdlookup.cpp index a09591cb..5c2816cf 100644 --- a/lang/qt/tests/t-wkdlookup.cpp +++ b/lang/qt/tests/t-wkdlookup.cpp @@ -1,158 +1,155 @@ /* t-wkdlookup.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 "data.h" #include "engineinfo.h" #include "protocol.h" #include "wkdlookupjob.h" #include "wkdlookupresult.h" #include #include #include #include using namespace QGpgME; using namespace GpgME; static const char *requiredVersion = "2.1.12"; namespace { bool keyHasUserIDWithMatchingEmailAddress(const Key &key, const QString &expectedEmailAddress) { const auto email = expectedEmailAddress.toLower(); const auto userIds = key.userIDs(); return std::any_of( std::begin(userIds), std::end(userIds), [email](const UserID &uid) { return email == QString::fromUtf8(uid.email()).toLower(); }); } } class WKDLookupTest : public QGpgMETest { Q_OBJECT -Q_SIGNALS: - void asyncDone(); - private Q_SLOTS: void testWKDLookupAsync() { if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() < requiredVersion) { QSKIP("dirmngr does not yet support WKD lookup"); } if (!doOnlineTests()) { QSKIP("Set DO_ONLINE_TESTS environment variable to run this test."); } const QString email = QLatin1String{"wk@gnupg.org"}; WKDLookupResult result; auto *job = openpgp()->wkdLookupJob(); connect(job, &WKDLookupJob::result, job, [this, &result](const WKDLookupResult &result_, const QString &, const Error &) { result = result_; Q_EMIT asyncDone(); }); job->start(email); QSignalSpy spy (this, SIGNAL(asyncDone())); QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT)); QVERIFY(result.error().code() == GPG_ERR_NO_ERROR); QCOMPARE(result.pattern(), "wk@gnupg.org"); QCOMPARE(result.source(), "https://openpgpkey.gnupg.org"); const auto keys = result.keyData().toKeys(GpgME::OpenPGP); QVERIFY(keys.size() == 1); QVERIFY(keyHasUserIDWithMatchingEmailAddress(keys.front(), email)); } void testWKDLookupSync() { if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() < requiredVersion) { QSKIP("dirmngr does not yet support WKD lookup"); } if (!doOnlineTests()) { QSKIP("Set DO_ONLINE_TESTS environment variable to run this test."); } const QString email = QLatin1String{"wk@gnupg.org"}; auto *job = openpgp()->wkdLookupJob(); const auto result = job->exec(email); QVERIFY(result.error().code() == GPG_ERR_NO_ERROR); QCOMPARE(result.pattern(), "wk@gnupg.org"); QCOMPARE(result.source(), "https://openpgpkey.gnupg.org"); const auto keys = result.keyData().toKeys(GpgME::OpenPGP); QVERIFY(keys.size() == 1); QVERIFY(keyHasUserIDWithMatchingEmailAddress(keys.front(), email)); } void testLookupWithNoResultAsync() { if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() < requiredVersion) { QSKIP("dirmngr does not yet support WKD lookup"); } if (!doOnlineTests()) { QSKIP("Set DO_ONLINE_TESTS environment variable to run this test."); } const QString email = QLatin1String{"alfa@example.net"}; WKDLookupResult result; auto *job = openpgp()->wkdLookupJob(); connect(job, &WKDLookupJob::result, job, [this, &result](const WKDLookupResult &result_, const QString &, const Error &) { result = result_; Q_EMIT asyncDone(); }); job->start(email); QSignalSpy spy (this, SIGNAL(asyncDone())); QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT)); QVERIFY(result.error().code() == GPG_ERR_NO_ERROR); QCOMPARE(result.pattern(), "alfa@example.net"); QCOMPARE(result.source(), ""); QVERIFY(result.keyData().isNull()); } }; QTEST_MAIN(WKDLookupTest) #include "t-wkdlookup.moc" diff --git a/lang/qt/tests/t-wkspublish.cpp b/lang/qt/tests/t-wkspublish.cpp index b3891945..618f0b48 100644 --- a/lang/qt/tests/t-wkspublish.cpp +++ b/lang/qt/tests/t-wkspublish.cpp @@ -1,283 +1,280 @@ /* t-wkspublish.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 "wkspublishjob.h" #include "keygenerationjob.h" #include "keygenerationresult.h" #include "importjob.h" #include "importresult.h" #include "protocol.h" #include "engineinfo.h" #include "t-support.h" using namespace QGpgME; using namespace GpgME; //#define DO_ONLINE_TESTS #define TEST_ADDRESS "testuser2@test.gnupg.org" static const char *testSecKey = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" "\n" "lHgEV77hVhMJKyQDAwIIAQEHAgMEN3qKqBr9EecnfUnpw8RS8DHAjJqhwm2HAoEE\n" "3yfQQ9w8uB/bKm5dqW4HML3JWRH8YoJaKSVrJY2D1FZUY+vHlgABAKDwEAB0HND8\n" "5kbxiJmqKIuuNqCJ2jHgs9G0xk4GdKvZEdq0JlRlc3QgVXNlciAyIDx0ZXN0dXNl\n" "cjJAdGVzdC5nbnVwZy5vcmc+iHkEExMIACEFAle+4VYCGwMFCwkIBwIGFQgJCgsC\n" "BBYCAwECHgECF4AACgkQRVRoUEJO+6zgFQD7BF3pnS3w3A7J9y+Y3kyGfmscXFWJ\n" "Kme1PAsAlVSm1y4A+weReMvWFYHJH257v94yhStmV8egGoybsNDttNAW53cbnHwE\n" "V77hVhIJKyQDAwIIAQEHAgMEX+6cF0HEn4g3ztFvwHyr7uwXMVYUGL3lE3mjhnV3\n" "SbY6Dmy3OeFVnEVkawHqSv+HobpQTeEqNoQHAoIiXFCRlgMBCAcAAP9FykiyDspm\n" "T33XWRPD+LAOmaIU7CIhfv9+lVkeExlU1w+qiGEEGBMIAAkFAle+4VYCGwwACgkQ\n" "RVRoUEJO+6xjhgD/ZJ/MwYZJPk/xPYhTP8+wF+tErVNA8w3pP9D69dgUPdcA/izZ\n" "Pji6YetVhgsyaHc4PrKynsk5G6nM3KkAOehUQsX8\n" "=S/Wa\n" "-----END PGP PRIVATE KEY BLOCK-----\n"; static const char *testResponse = "From key-submission@test.gnupg.org Thu Aug 25 12:15:54 2016\n" "Return-Path: \n" "From: key-submission@test.gnupg.org\n" "To: testuser2@test.gnupg.org\n" "Subject: Confirm your key publication\n" "X-Wks-Loop: webkey.g10code.com\n" "MIME-Version: 1.0\n" "Content-Type: multipart/encrypted; protocol=\"application/pgp-encrypted\";\n" " boundary=\"=-=01-wbu5fr9nu6fix5tcojjo=-=\"\n" "Date: Thu, 25 Aug 2016 12:15:54 +0000\n" "Message-Id: \n" "Sender: \n" "X-Kolab-Scheduling-Message: FALSE\n" "\n" " \n" "\n" "--=-=01-wbu5fr9nu6fix5tcojjo=-=\n" "Content-Type: application/pgp-encrypted\n" "\n" "Version: 1\n" "\n" "--=-=01-wbu5fr9nu6fix5tcojjo=-=\n" "Content-Type: application/octet-stream\n" "\n" "-----BEGIN PGP MESSAGE-----\n" "Version: GnuPG v2\n" "\n" "hH4D8pSp7hUsFUASAgMEg0w39E6d0TkFYxLbT6n3YcoKTT+Ur/c7Sn1ECyL7Rnuk\n" "cmPO0adt3JxueK7Oz5COlk32SECFODdF3cQuDhkGxzC6Sfc4SfisdILmNhaT/MeW\n" "8a+yE4skSK70absif4kw5XkvxXNxHeIHfAteP50jPJLSwEsBTEceb9cRMoP7s8w0\n" "lYyi+RWQ7UKlKKywtcRCL4ow2H7spjx+a+3FzNOAoy7K0/thhLVRk8z+iuPi0/4n\n" "Z2Ql60USLLUlfV2ZIpXdCd+5GjTJsnGhDos1pas5TZcOOAxO12Cg5TcqHISOaqa8\n" "6BqxcKCU3NypIynOKHj375KArSs0WsEH8HWHyBBHB+NYtNpnTAuHNKxM+JtNxf+U\n" "NfD2zptS6kyiHLw+4zjL5pEV7RHS2PBwWBDS6vhnyybNwckleya96U04iYiGRYGE\n" "lUUR6Fl8H6x04dItFH1/jJA6Ppcu4FoYou04HADWCqJXPTgztjiW1/9QoCeXl5lm\n" "CcOCcuw7lXp+qTejuns=\n" "=SsWX\n" "-----END PGP MESSAGE-----\n" "\n" "--=-=01-wbu5fr9nu6fix5tcojjo=-=--\n"; class WKSPublishTest : public QGpgMETest { Q_OBJECT -Q_SIGNALS: - void asyncDone(); - private Q_SLOTS: void testUnsupported() { // First check if it is supported auto job = openpgp()->wksPublishJob(); connect(job, &WKSPublishJob::result, this, [this] (Error err, QByteArray, QByteArray, QString, Error) { QVERIFY(err); Q_EMIT asyncDone(); }); job->startCheck ("testuser1@localhost"); QSignalSpy spy (this, SIGNAL(asyncDone())); QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT)); } #ifdef DO_ONLINE_TESTS private Q_SLOTS: #else private: #endif void testWSKPublishSupport() { // First check if it is supported auto job = openpgp()->wksPublishJob(); connect(job, &WKSPublishJob::result, this, [this] (Error err, QByteArray, QByteArray, QString, Error) { if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() < "2.0.16") { std::cout << err; QVERIFY(err); } else { QVERIFY(!err); } Q_EMIT asyncDone(); }); job->startCheck ("testuser1@test.gnupg.org"); QSignalSpy spy (this, SIGNAL(asyncDone())); QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT)); } void testWKSPublishErrors() { if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() < "2.0.16") { /* Not supported */ return; } auto job = openpgp()->wksPublishJob(); connect(job, &WKSPublishJob::result, this, [this] (Error err, QByteArray, QByteArray, QString, Error) { QVERIFY(err); Q_EMIT asyncDone(); }); job->startCreate("AB874F24E98EBB8487EE7B170F8E3D97FE7011B7", QStringLiteral("Foo@bar.baz")); QSignalSpy spy (this, SIGNAL(asyncDone())); QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT)); } void testWKSPublishCreate() { if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() < "2.0.16") { /* Not supported */ return; } /* First generate a test key */ const QString args = QStringLiteral("\n" "%no-protection\n" "%transient-key\n" "key-type: ECDSA\n" "key-curve: brainpoolP256r1\n" "key-usage: sign\n" "subkey-type: ECDH\n" "subkey-curve: brainpoolP256r1\n" "subkey-usage: encrypt\n" "name-email: %1\n" "name-real: Test User\n" "").arg(TEST_ADDRESS); auto keygenjob = openpgp()->keyGenerationJob(); QByteArray fpr; connect(keygenjob, &KeyGenerationJob::result, this, [this, &fpr](KeyGenerationResult result, QByteArray, QString, Error) { QVERIFY(!result.error()); fpr = QByteArray(result.fingerprint()); QVERIFY(!fpr.isEmpty()); Q_EMIT asyncDone(); }); keygenjob->start(args); QSignalSpy spy (this, SIGNAL(asyncDone())); QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT)); /* Then try to create a request. */ auto job = openpgp()->wksPublishJob(); connect(job, &WKSPublishJob::result, this, [this] (Error err, QByteArray out, QByteArray, QString, Error) { QVERIFY(!err); Q_EMIT asyncDone(); const QString outstr = QString(out); QVERIFY(outstr.contains( QStringLiteral("-----BEGIN PGP PUBLIC KEY BLOCK-----"))); QVERIFY(outstr.contains( QStringLiteral("Content-Type: application/pgp-keys"))); QVERIFY(outstr.contains( QStringLiteral("From: " TEST_ADDRESS))); }); job->startCreate(fpr.constData(), QLatin1String(TEST_ADDRESS)); QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT)); } void testWKSPublishReceive() { if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() < "2.0.16") { /* Not supported */ return; } auto importjob = openpgp()->importJob(); connect(importjob, &ImportJob::result, this, [this](ImportResult result, QString, Error) { QVERIFY(!result.error()); QVERIFY(!result.imports().empty()); QVERIFY(result.numSecretKeysImported()); Q_EMIT asyncDone(); }); importjob->start(QByteArray(testSecKey)); QSignalSpy spy (this, SIGNAL(asyncDone())); QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT)); /* Get a response. */ auto job = openpgp()->wksPublishJob(); connect(job, &WKSPublishJob::result, this, [this] (Error err, QByteArray out, QByteArray, QString, Error) { QVERIFY(!err); Q_EMIT asyncDone(); const QString outstr = QString(out); QVERIFY(outstr.contains( QStringLiteral("-----BEGIN PGP MESSAGE-----"))); QVERIFY(outstr.contains( QStringLiteral("Content-Type: multipart/encrypted;"))); QVERIFY(outstr.contains( QStringLiteral("From: " TEST_ADDRESS))); }); job->startReceive(QByteArray(testResponse)); QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT)); } 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(); } private: QTemporaryDir mDir; }; QTEST_MAIN(WKSPublishTest) #include "t-wkspublish.moc"