Imported Upstream version 1.16.0
[platform/upstream/gpgme.git] / lang / qt / tests / t-various.cpp
1 /* t-various.cpp
2
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
6
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.
11
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.
16
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
20
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
30     your version.
31 */
32
33 #ifdef HAVE_CONFIG_H
34  #include "config.h"
35 #endif
36
37 #include <QDebug>
38 #include <QTest>
39 #include <QSignalSpy>
40 #include <QTemporaryDir>
41 #include "keylistjob.h"
42 #include "protocol.h"
43 #include "keylistresult.h"
44 #include "context.h"
45 #include "engineinfo.h"
46 #include "dn.h"
47 #include "data.h"
48 #include "dataprovider.h"
49 #include "signkeyjob.h"
50
51 #include "t-support.h"
52
53 using namespace QGpgME;
54 using namespace GpgME;
55
56 static const char aKey[] = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n"
57 "\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"
67 "=p2Oj\n"
68 "-----END PGP PUBLIC KEY BLOCK-----\n";
69
70 class TestVarious: public QGpgMETest
71 {
72     Q_OBJECT
73
74 Q_SIGNALS:
75     void asyncDone();
76
77 private Q_SLOTS:
78     void testDN()
79     {
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"));
86     }
87
88     void testKeyFromFile()
89     {
90         if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() < "2.1.14") {
91             return;
92         }
93         QGpgME::QByteArrayDataProvider dp(aKey);
94         Data data(&dp);
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"));
100     }
101
102     void testDataRewind()
103     {
104         if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() < "2.1.14") {
105             return;
106         }
107         QGpgME::QByteArrayDataProvider dp(aKey);
108         Data data(&dp);
109         char buf[20];
110         data.read(buf, 20);
111
112         auto keys = data.toKeys();
113         QVERIFY(keys.size() == 0);
114
115         data.rewind();
116
117         keys = data.toKeys();
118         QVERIFY(keys.size() == 1);
119     }
120
121     void testQuickUid()
122     {
123         if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() < "2.1.13") {
124             return;
125         }
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"),
129                                                 false, keys);
130         delete job;
131         QVERIFY (!result.error());
132         QVERIFY (keys.size() == 1);
133         Key key = keys.front();
134
135         QVERIFY (key.numUserIDs() == 3);
136         const char uid[] = "Foo Bar (with comment) <foo@bar.baz>";
137
138         auto ctx = Context::createForProtocol(key.protocol());
139         QVERIFY (ctx);
140         TestPassphraseProvider provider;
141         ctx->setPassphraseProvider(&provider);
142         ctx->setPinentryMode(Context::PinentryLoopback);
143
144         QVERIFY(!ctx->addUid(key, uid));
145         delete ctx;
146         key.update();
147
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());
153                 id_found = true;
154                 break;
155             }
156         }
157         QVERIFY (id_found);
158
159         ctx = Context::createForProtocol(key.protocol());
160         QVERIFY (!ctx->revUid(key, uid));
161         delete ctx;
162         key.update();
163
164         bool id_revoked = false;;
165         for (const auto &u: key.userIDs()) {
166             if (!strcmp (u.id(), uid)) {
167                 id_revoked = true;
168                 break;
169             }
170         }
171         QVERIFY(id_revoked);
172     }
173
174     void testSetExpire()
175     {
176         if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() < "2.1.22") {
177             return;
178         }
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"),
182                                                 false, keys);
183         delete job;
184         QVERIFY (!result.error());
185         QVERIFY (keys.size() == 1);
186         Key key = keys.front();
187
188         QVERIFY (key.subkey(0).expirationTime() == time_t(0));
189         QVERIFY (key.subkey(1).expirationTime() == time_t(0));
190
191         auto ctx = Context::createForProtocol(key.protocol());
192         QVERIFY (ctx);
193         TestPassphraseProvider provider;
194         ctx->setPassphraseProvider(&provider);
195         ctx->setPinentryMode(Context::PinentryLoopback);
196
197         // change expiration of the main key
198         QVERIFY(!ctx->setExpire(key, 1000));
199         delete ctx;
200         key.update();
201
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();
205
206         // change expiration of all subkeys
207         ctx = Context::createForProtocol(key.protocol());
208         QVERIFY(!ctx->setExpire(key, 2000, std::vector<Subkey>(), Context::SetExpireAllSubkeys));
209         delete ctx;
210         key.update();
211
212         QVERIFY (key.subkey(0).expirationTime() == keyExpiration);
213         QVERIFY (key.subkey(1).expirationTime() != time_t(0));
214         time_t subkeyExpiration = key.subkey(1).expirationTime();
215
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));
221         delete ctx;
222         key.update();
223
224         QVERIFY (key.subkey(0).expirationTime() == keyExpiration);
225         QVERIFY (key.subkey(1).expirationTime() != subkeyExpiration);
226
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);
234         delete ctx;
235     }
236
237     void testSignKeyWithoutExpiration()
238     {
239         Error err;
240
241         if (!loopbackSupported()) {
242             return;
243         }
244
245         auto ctx = Context::create(OpenPGP);
246         QVERIFY(ctx);
247
248         // Get the signing key (alfa@example.net)
249         auto seckey = ctx->key("A0FF4590BB6122EDEF6E3C542D727CC768697734", err, true);
250         QVERIFY(!err);
251         QVERIFY(!seckey.isNull());
252
253         // Get the target key (Bob / Bravo Test)
254         auto target = ctx->key("D695676BDCEDCC2CDD6152BCFE180B1DA9E3B0B2", err, false);
255         QVERIFY(!err);
256         QVERIFY(!target.isNull());
257         QVERIFY(target.numUserIDs() > 0);
258
259         // Create the job
260         auto job = std::unique_ptr<SignKeyJob>{openpgp()->signKeyJob()};
261         QVERIFY(job);
262
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);
268
269         // Setup the job
270         job->setExportable(true);
271         job->setSigningKey(seckey);
272         job->setDupeOk(true);
273
274         connect(job.get(), &SignKeyJob::result,
275                 this, [this] (const GpgME::Error &err2, const QString &, const GpgME::Error &) {
276                     Q_EMIT asyncDone();
277                     if (err2) {
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())));
281                         } else {
282                             QFAIL(qPrintable(QString("The SignKeyJob failed with '%1'.").arg(err2.asString())));
283                         }
284                     }
285                 });
286
287         job->start(target);
288         QSignalSpy spy{this, &TestVarious::asyncDone};
289         QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT));
290
291         // At this point the signature should have been added.
292         target.update();
293         const auto keySignature = target.userID(0).signature(target.userID(0).numSignatures() - 1);
294         QVERIFY(keySignature.neverExpires());
295     }
296
297     void testSignKeyWithExpiration()
298     {
299         Error err;
300
301         if (!loopbackSupported()) {
302             return;
303         }
304
305         auto ctx = Context::create(OpenPGP);
306         QVERIFY(ctx);
307
308         // Get the signing key (alfa@example.net)
309         auto seckey = ctx->key("A0FF4590BB6122EDEF6E3C542D727CC768697734", err, true);
310         QVERIFY(!err);
311         QVERIFY(!seckey.isNull());
312
313         // Get the target key (Bob / Bravo Test)
314         auto target = ctx->key("D695676BDCEDCC2CDD6152BCFE180B1DA9E3B0B2", err, false);
315         QVERIFY(!err);
316         QVERIFY(!target.isNull());
317         QVERIFY(target.numUserIDs() > 0);
318
319         // Create the job
320         auto job = std::unique_ptr<SignKeyJob>{openpgp()->signKeyJob()};
321         QVERIFY(job);
322
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);
328
329         // Setup the job
330         job->setExportable(true);
331         job->setSigningKey(seckey);
332         job->setDupeOk(true);
333         job->setExpirationDate(QDate{2222, 2, 22});
334
335         connect(job.get(), &SignKeyJob::result,
336                 this, [this] (const GpgME::Error &err2, const QString &, const GpgME::Error &) {
337                     Q_EMIT asyncDone();
338                     if (err2) {
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())));
342                         } else {
343                             QFAIL(qPrintable(QString("The SignKeyJob failed with '%1'.").arg(err2.asString())));
344                         }
345                     }
346                 });
347
348         QTest::ignoreMessage(QtWarningMsg, "Expiration of certification has been changed to QDate(\"2106-02-06\")");
349
350         job->start(target);
351         QSignalSpy spy{this, &TestVarious::asyncDone};
352         QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT));
353
354         // At this point the signature should have been added.
355         target.update();
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
360     }
361
362     void testVersion()
363     {
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"));
374
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"));
382
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"));
390
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");
398
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");
406     }
407
408     void initTestCase()
409     {
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");
418         }
419         conf.close();
420     }
421
422 private:
423     QTemporaryDir mDir;
424 };
425
426 QTEST_MAIN(TestVarious)
427
428 #include "t-various.moc"