3 This file is part of qgpgme, the Qt API binding for gpgme
4 Copyright (c) 2017 by Bundesamt für Sicherheit in der Informationstechnik
5 Software engineering by Intevation GmbH
7 QGpgME is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the
10 License, or (at your option) any later version.
12 QGpgME is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 In addition, as a special exception, the copyright holders give
22 permission to link the code of this program with any edition of
23 the Qt library by Trolltech AS, Norway (or with modified versions
24 of Qt that use the same license as Qt), and distribute linked
25 combinations including the two. You must obey the GNU General
26 Public License in all respects for all of the code used other than
27 Qt. If you modify this file, you may extend this exception to
28 your version of the file, but you are not obligated to do so. If
29 you do not wish to do so, delete this exception statement from
40 #include <QTemporaryDir>
41 #include "keylistjob.h"
43 #include "keylistresult.h"
45 #include "engineinfo.h"
48 #include "dataprovider.h"
49 #include "signkeyjob.h"
51 #include "t-support.h"
53 using namespace QGpgME;
54 using namespace GpgME;
56 static const char aKey[] = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n"
58 "mDMEWG+w/hYJKwYBBAHaRw8BAQdAiq1oStvDYg8ZfFs5DgisYJo8dJxD+C/AA21O\n"
59 "K/aif0O0GXRvZnVfY29uZmxpY3RAZXhhbXBsZS5jb22IlgQTFggAPhYhBHoJBLaV\n"
60 "DamYAgoa1L5BwMOl/x88BQJYb7D+AhsDBQkDwmcABQsJCAcCBhUICQoLAgQWAgMB\n"
61 "Ah4BAheAAAoJEL5BwMOl/x88GvwA/0SxkbLyAcshGm2PRrPsFQsSVAfwaSYFVmS2\n"
62 "cMVIw1PfAQDclRH1Z4MpufK07ju4qI33o4s0UFpVRBuSxt7A4P2ZD7g4BFhvsP4S\n"
63 "CisGAQQBl1UBBQEBB0AmVrgaDNJ7K2BSalsRo2EkRJjHGqnp5bBB0tapnF81CQMB\n"
64 "CAeIeAQYFggAIBYhBHoJBLaVDamYAgoa1L5BwMOl/x88BQJYb7D+AhsMAAoJEL5B\n"
65 "wMOl/x88OR0BAMq4/vmJUORRTmzjHcv/DDrQB030DSq666rlckGIKTShAPoDXM9N\n"
66 "0gZK+YzvrinSKZXHmn0aSwmC1/hyPybJPEljBw==\n"
68 "-----END PGP PUBLIC KEY BLOCK-----\n";
70 class TestVarious: public QGpgMETest
80 DN dn(QStringLiteral("CN=Before\\0DAfter,OU=Test,DC=North America,DC=Fabrikam,DC=COM"));
81 QVERIFY(dn.dn() == QStringLiteral("CN=Before\rAfter,OU=Test,DC=North America,DC=Fabrikam,DC=COM"));
82 QStringList attrOrder;
83 attrOrder << QStringLiteral("DC") << QStringLiteral("OU") << QStringLiteral("CN");
84 dn.setAttributeOrder(attrOrder);
85 QVERIFY(dn.prettyDN() == QStringLiteral("DC=North America,DC=Fabrikam,DC=COM,OU=Test,CN=Before\rAfter"));
88 void testKeyFromFile()
90 if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() < "2.1.14") {
93 QGpgME::QByteArrayDataProvider dp(aKey);
95 const auto keys = data.toKeys();
96 QVERIFY(keys.size() == 1);
97 const auto key = keys[0];
98 QVERIFY(!key.isNull());
99 QVERIFY(key.primaryFingerprint() == QStringLiteral("7A0904B6950DA998020A1AD4BE41C0C3A5FF1F3C"));
102 void testDataRewind()
104 if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() < "2.1.14") {
107 QGpgME::QByteArrayDataProvider dp(aKey);
112 auto keys = data.toKeys();
113 QVERIFY(keys.size() == 0);
117 keys = data.toKeys();
118 QVERIFY(keys.size() == 1);
123 if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() < "2.1.13") {
126 KeyListJob *job = openpgp()->keyListJob(false, true, true);
127 std::vector<GpgME::Key> keys;
128 GpgME::KeyListResult result = job->exec(QStringList() << QStringLiteral("alfa@example.net"),
131 QVERIFY (!result.error());
132 QVERIFY (keys.size() == 1);
133 Key key = keys.front();
135 QVERIFY (key.numUserIDs() == 3);
136 const char uid[] = "Foo Bar (with comment) <foo@bar.baz>";
138 auto ctx = Context::createForProtocol(key.protocol());
140 TestPassphraseProvider provider;
141 ctx->setPassphraseProvider(&provider);
142 ctx->setPinentryMode(Context::PinentryLoopback);
144 QVERIFY(!ctx->addUid(key, uid));
148 QVERIFY (key.numUserIDs() == 4);
149 bool id_found = false;;
150 for (const auto &u: key.userIDs()) {
151 if (!strcmp (u.id(), uid)) {
152 QVERIFY (!u.isRevoked());
159 ctx = Context::createForProtocol(key.protocol());
160 QVERIFY (!ctx->revUid(key, uid));
164 bool id_revoked = false;;
165 for (const auto &u: key.userIDs()) {
166 if (!strcmp (u.id(), uid)) {
176 if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() < "2.1.22") {
179 KeyListJob *job = openpgp()->keyListJob(false, true, true);
180 std::vector<GpgME::Key> keys;
181 GpgME::KeyListResult result = job->exec(QStringList() << QStringLiteral("alfa@example.net"),
184 QVERIFY (!result.error());
185 QVERIFY (keys.size() == 1);
186 Key key = keys.front();
188 QVERIFY (key.subkey(0).expirationTime() == time_t(0));
189 QVERIFY (key.subkey(1).expirationTime() == time_t(0));
191 auto ctx = Context::createForProtocol(key.protocol());
193 TestPassphraseProvider provider;
194 ctx->setPassphraseProvider(&provider);
195 ctx->setPinentryMode(Context::PinentryLoopback);
197 // change expiration of the main key
198 QVERIFY(!ctx->setExpire(key, 1000));
202 QVERIFY (key.subkey(0).expirationTime() != time_t(0));
203 QVERIFY (key.subkey(1).expirationTime() == time_t(0));
204 time_t keyExpiration = key.subkey(0).expirationTime();
206 // change expiration of all subkeys
207 ctx = Context::createForProtocol(key.protocol());
208 QVERIFY(!ctx->setExpire(key, 2000, std::vector<Subkey>(), Context::SetExpireAllSubkeys));
212 QVERIFY (key.subkey(0).expirationTime() == keyExpiration);
213 QVERIFY (key.subkey(1).expirationTime() != time_t(0));
214 time_t subkeyExpiration = key.subkey(1).expirationTime();
216 // change expiration of specific subkey(s)
217 ctx = Context::createForProtocol(key.protocol());
218 std::vector<Subkey> specificSubkeys;
219 specificSubkeys.push_back(key.subkey(1));
220 QVERIFY(!ctx->setExpire(key, 3000, specificSubkeys));
224 QVERIFY (key.subkey(0).expirationTime() == keyExpiration);
225 QVERIFY (key.subkey(1).expirationTime() != subkeyExpiration);
227 // test error handling: calling setExpire() with the primary key as
228 // subkey should fail with "subkey <primary key fpr> not found"
229 ctx = Context::createForProtocol(key.protocol());
230 std::vector<Subkey> primaryKey;
231 primaryKey.push_back(key.subkey(0));
232 const auto err = ctx->setExpire(key, 3000, primaryKey);
233 QCOMPARE(err.code(), GPG_ERR_NOT_FOUND);
237 void testSignKeyWithoutExpiration()
241 if (!loopbackSupported()) {
245 auto ctx = Context::create(OpenPGP);
248 // Get the signing key (alfa@example.net)
249 auto seckey = ctx->key("A0FF4590BB6122EDEF6E3C542D727CC768697734", err, true);
251 QVERIFY(!seckey.isNull());
253 // Get the target key (Bob / Bravo Test)
254 auto target = ctx->key("D695676BDCEDCC2CDD6152BCFE180B1DA9E3B0B2", err, false);
256 QVERIFY(!target.isNull());
257 QVERIFY(target.numUserIDs() > 0);
260 auto job = std::unique_ptr<SignKeyJob>{openpgp()->signKeyJob()};
263 // Hack in the passphrase provider
264 auto jobCtx = Job::context(job.get());
265 TestPassphraseProvider provider;
266 jobCtx->setPassphraseProvider(&provider);
267 jobCtx->setPinentryMode(Context::PinentryLoopback);
270 job->setExportable(true);
271 job->setSigningKey(seckey);
272 job->setDupeOk(true);
274 connect(job.get(), &SignKeyJob::result,
275 this, [this] (const GpgME::Error &err2, const QString &, const GpgME::Error &) {
278 if (err2.code() == GPG_ERR_GENERAL) {
279 QFAIL(qPrintable(QString("The SignKeyJob failed with '%1'.\n"
280 "Hint: Run with GPGMEPP_INTERACTOR_DEBUG=stderr to debug the edit interaction.").arg(err2.asString())));
282 QFAIL(qPrintable(QString("The SignKeyJob failed with '%1'.").arg(err2.asString())));
288 QSignalSpy spy{this, &TestVarious::asyncDone};
289 QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT));
291 // At this point the signature should have been added.
293 const auto keySignature = target.userID(0).signature(target.userID(0).numSignatures() - 1);
294 QVERIFY(keySignature.neverExpires());
297 void testSignKeyWithExpiration()
301 if (!loopbackSupported()) {
305 auto ctx = Context::create(OpenPGP);
308 // Get the signing key (alfa@example.net)
309 auto seckey = ctx->key("A0FF4590BB6122EDEF6E3C542D727CC768697734", err, true);
311 QVERIFY(!seckey.isNull());
313 // Get the target key (Bob / Bravo Test)
314 auto target = ctx->key("D695676BDCEDCC2CDD6152BCFE180B1DA9E3B0B2", err, false);
316 QVERIFY(!target.isNull());
317 QVERIFY(target.numUserIDs() > 0);
320 auto job = std::unique_ptr<SignKeyJob>{openpgp()->signKeyJob()};
323 // Hack in the passphrase provider
324 auto jobCtx = Job::context(job.get());
325 TestPassphraseProvider provider;
326 jobCtx->setPassphraseProvider(&provider);
327 jobCtx->setPinentryMode(Context::PinentryLoopback);
330 job->setExportable(true);
331 job->setSigningKey(seckey);
332 job->setDupeOk(true);
333 job->setExpirationDate(QDate{2222, 2, 22});
335 connect(job.get(), &SignKeyJob::result,
336 this, [this] (const GpgME::Error &err2, const QString &, const GpgME::Error &) {
339 if (err2.code() == GPG_ERR_GENERAL) {
340 QFAIL(qPrintable(QString("The SignKeyJob failed with '%1'.\n"
341 "Hint: Run with GPGMEPP_INTERACTOR_DEBUG=stderr to debug the edit interaction.").arg(err2.asString())));
343 QFAIL(qPrintable(QString("The SignKeyJob failed with '%1'.").arg(err2.asString())));
348 QTest::ignoreMessage(QtWarningMsg, "Expiration of certification has been changed to QDate(\"2106-02-06\")");
351 QSignalSpy spy{this, &TestVarious::asyncDone};
352 QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT));
354 // At this point the signature should have been added.
356 const auto keySignature = target.userID(0).signature(target.userID(0).numSignatures() - 1);
357 QVERIFY(!keySignature.neverExpires());
358 const auto expirationDate = QDateTime::fromSecsSinceEpoch(keySignature.expirationTime()).date();
359 QCOMPARE(expirationDate, QDate(2106, 2, 6)); // expiration date is capped at 2106-02-06
364 QVERIFY(EngineInfo::Version("2.1.0") < EngineInfo::Version("2.1.1"));
365 QVERIFY(EngineInfo::Version("2.1.10") < EngineInfo::Version("2.1.11"));
366 QVERIFY(EngineInfo::Version("2.2.0") > EngineInfo::Version("2.1.19"));
367 QVERIFY(EngineInfo::Version("1.0.0") < EngineInfo::Version("2.0.0"));
368 QVERIFY(EngineInfo::Version("0.1.0") < EngineInfo::Version("1.0.0"));
369 QVERIFY(!(EngineInfo::Version("2.0.0") < EngineInfo::Version("2.0.0")));
370 QVERIFY(!(EngineInfo::Version("2.0.0") > EngineInfo::Version("2.0.0")));
371 QVERIFY(EngineInfo::Version("3.0.0") > EngineInfo::Version("2.3.20"));
372 QVERIFY(EngineInfo::Version("3.0.1") > EngineInfo::Version("3.0.0"));
373 QVERIFY(EngineInfo::Version("3.1.0") > EngineInfo::Version("3.0.20"));
375 QVERIFY(EngineInfo::Version("1.1.1") <= "2.0.0");
376 QVERIFY(EngineInfo::Version("1.1.1") <= "1.2.0");
377 QVERIFY(EngineInfo::Version("1.1.1") <= "1.1.2");
378 QVERIFY(EngineInfo::Version("1.1.1") <= "1.1.1");
379 QVERIFY(!(EngineInfo::Version("1.1.1") <= "1.1.0"));
380 QVERIFY(!(EngineInfo::Version("1.1.1") <= "1.0.9"));
381 QVERIFY(!(EngineInfo::Version("1.1.1") <= "0.9.9"));
383 QVERIFY(!(EngineInfo::Version("1.1.1") == "2.0.0"));
384 QVERIFY(!(EngineInfo::Version("1.1.1") == "1.2.0"));
385 QVERIFY(!(EngineInfo::Version("1.1.1") == "1.1.2"));
386 QVERIFY(EngineInfo::Version("1.1.1") == "1.1.1");
387 QVERIFY(!(EngineInfo::Version("1.1.1") == "1.1.0"));
388 QVERIFY(!(EngineInfo::Version("1.1.1") == "1.0.9"));
389 QVERIFY(!(EngineInfo::Version("1.1.1") == "0.9.9"));
391 QVERIFY(EngineInfo::Version("1.1.1") != "2.0.0");
392 QVERIFY(EngineInfo::Version("1.1.1") != "1.2.0");
393 QVERIFY(EngineInfo::Version("1.1.1") != "1.1.2");
394 QVERIFY(!(EngineInfo::Version("1.1.1") != "1.1.1"));
395 QVERIFY(EngineInfo::Version("1.1.1") != "1.1.0");
396 QVERIFY(EngineInfo::Version("1.1.1") != "1.0.9");
397 QVERIFY(EngineInfo::Version("1.1.1") != "0.9.9");
399 QVERIFY(!(EngineInfo::Version("1.1.1") >= "2.0.0"));
400 QVERIFY(!(EngineInfo::Version("1.1.1") >= "1.2.0"));
401 QVERIFY(!(EngineInfo::Version("1.1.1") >= "1.1.2"));
402 QVERIFY(EngineInfo::Version("1.1.1") >= "1.1.1");
403 QVERIFY(EngineInfo::Version("1.1.1") >= "1.1.0");
404 QVERIFY(EngineInfo::Version("1.1.1") >= "1.0.9");
405 QVERIFY(EngineInfo::Version("1.1.1") >= "0.9.9");
410 QGpgMETest::initTestCase();
411 const QString gpgHome = qgetenv("GNUPGHOME");
412 QVERIFY(copyKeyrings(gpgHome, mDir.path()));
413 qputenv("GNUPGHOME", mDir.path().toUtf8());
414 QFile conf(mDir.path() + QStringLiteral("/gpg.conf"));
415 QVERIFY(conf.open(QIODevice::WriteOnly));
416 if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() >= "2.2.18") {
417 conf.write("allow-weak-key-signatures");
426 QTEST_MAIN(TestVarious)
428 #include "t-various.moc"