65fe735be795ae2f26e7b1d8c54b0e3139107209
[gpgme.git] / lang / qt / tests / t-encrypt.cpp
1 /* t-encrypt.cpp
2
3     This file is part of qgpgme, the Qt API binding for gpgme
4     Copyright (c) 2016 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 #ifdef HAVE_CONFIG_H
33  #include "config.h"
34 #endif
35
36 #include <QDebug>
37 #include <QTest>
38 #include <QTemporaryDir>
39 #include <QSignalSpy>
40 #include <QBuffer>
41 #include "keylistjob.h"
42 #include "encryptjob.h"
43 #include "signencryptjob.h"
44 #include "signingresult.h"
45 #include "qgpgmeencryptjob.h"
46 #include "encryptionresult.h"
47 #include "decryptionresult.h"
48 #include "qgpgmedecryptjob.h"
49 #include "qgpgmebackend.h"
50 #include "keylistresult.h"
51 #include "engineinfo.h"
52 #include "verifyopaquejob.h"
53 #include "t-support.h"
54
55 #define PROGRESS_TEST_SIZE 1 * 1024 * 1024
56
57 using namespace QGpgME;
58 using namespace GpgME;
59
60 static bool decryptSupported()
61 {
62     /* With GnuPG 2.0.x (at least 2.0.26 by default on jessie)
63      * the passphrase_cb does not work. So the test popped up
64      * a pinentry. So tests requiring decryption don't work. */
65     static auto version = GpgME::engineInfo(GpgME::GpgEngine).engineVersion();
66     if (version < "2.0.0") {
67         /* With 1.4 it just works */
68         return true;
69     }
70     if (version < "2.1.0") {
71         /* With 2.1 it works with loopback mode */
72         return false;
73     }
74     return true;
75 }
76
77 class EncryptionTest : public QGpgMETest
78 {
79     Q_OBJECT
80
81 Q_SIGNALS:
82     void asyncDone();
83
84 private Q_SLOTS:
85
86     void testSimpleEncryptDecrypt()
87     {
88         auto listjob = openpgp()->keyListJob(false, false, false);
89         std::vector<Key> keys;
90         auto keylistresult = listjob->exec(QStringList() << QStringLiteral("alfa@example.net"),
91                                           false, keys);
92         QVERIFY(!keylistresult.error());
93         QVERIFY(keys.size() == 1);
94         delete listjob;
95
96         auto job = openpgp()->encryptJob(/*ASCII Armor */true, /* Textmode */ true);
97         QVERIFY(job);
98         QByteArray cipherText;
99         auto result = job->exec(keys, QStringLiteral("Hello World").toUtf8(), Context::AlwaysTrust, cipherText);
100         delete job;
101         QVERIFY(!result.error());
102         const auto cipherString = QString::fromUtf8(cipherText);
103         QVERIFY(cipherString.startsWith("-----BEGIN PGP MESSAGE-----"));
104
105         /* Now decrypt */
106         if (!decryptSupported()) {
107             return;
108         }
109         auto ctx = Context::createForProtocol(OpenPGP);
110         TestPassphraseProvider provider;
111         ctx->setPassphraseProvider(&provider);
112         ctx->setPinentryMode(Context::PinentryLoopback);
113         auto decJob = new QGpgMEDecryptJob(ctx);
114         QByteArray plainText;
115         auto decResult = decJob->exec(cipherText, plainText);
116         QVERIFY(!decResult.error());
117         QVERIFY(QString::fromUtf8(plainText) == QStringLiteral("Hello World"));
118         delete decJob;
119     }
120
121     void testProgress()
122     {
123         if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() < "2.1.15") {
124             // We can only test the progress with 2.1.15 as this started to
125             // have total progress for memory callbacks
126             return;
127         }
128         auto listjob = openpgp()->keyListJob(false, false, false);
129         std::vector<Key> keys;
130         auto keylistresult = listjob->exec(QStringList() << QStringLiteral("alfa@example.net"),
131                                           false, keys);
132         QVERIFY(!keylistresult.error());
133         QVERIFY(keys.size() == 1);
134         delete listjob;
135
136         auto job = openpgp()->encryptJob(/*ASCII Armor */false, /* Textmode */ false);
137         QVERIFY(job);
138         QByteArray plainBa;
139         plainBa.fill('X', PROGRESS_TEST_SIZE);
140         QByteArray cipherText;
141
142         bool initSeen = false;
143         bool finishSeen = false;
144         connect(job, &Job::progress, this, [this, &initSeen, &finishSeen] (const QString&, int current, int total) {
145                 // We only check for progress 0 and max progress as the other progress
146                 // lines depend on the system speed and are as such unreliable to test.
147                 QVERIFY(total == PROGRESS_TEST_SIZE);
148                 if (current == 0) {
149                     initSeen = true;
150                 }
151                 if (current == total) {
152                     finishSeen = true;
153                 }
154                 QVERIFY(current >= 0 && current <= total);
155             });
156         connect(job, &EncryptJob::result, this, [this, &initSeen, &finishSeen] (const GpgME::EncryptionResult &,
157                                                                                 const QByteArray &,
158                                                                                 const QString,
159                                                                                 const GpgME::Error) {
160                 QVERIFY(initSeen);
161                 QVERIFY(finishSeen);
162                 Q_EMIT asyncDone();
163             });
164
165         auto inptr  = std::shared_ptr<QIODevice>(new QBuffer(&plainBa));
166         inptr->open(QIODevice::ReadOnly);
167         auto outptr = std::shared_ptr<QIODevice>(new QBuffer(&cipherText));
168         outptr->open(QIODevice::WriteOnly);
169
170         job->start(keys, inptr, outptr, Context::AlwaysTrust);
171         QSignalSpy spy (this, SIGNAL(asyncDone()));
172         QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT));
173     }
174
175     void testSymmetricEncryptDecrypt()
176     {
177         if (!decryptSupported()) {
178             return;
179         }
180         auto ctx = Context::createForProtocol(OpenPGP);
181         TestPassphraseProvider provider;
182         ctx->setPassphraseProvider(&provider);
183         ctx->setPinentryMode(Context::PinentryLoopback);
184         ctx->setArmor(true);
185         ctx->setTextMode(true);
186         auto job = new QGpgMEEncryptJob(ctx);
187         QByteArray cipherText;
188         auto result = job->exec(std::vector<Key>(), QStringLiteral("Hello symmetric World").toUtf8(), Context::AlwaysTrust, cipherText);
189         delete job;
190         QVERIFY(!result.error());
191         const auto cipherString = QString::fromUtf8(cipherText);
192         QVERIFY(cipherString.startsWith("-----BEGIN PGP MESSAGE-----"));
193
194         killAgent(mDir.path());
195
196         auto ctx2 = Context::createForProtocol(OpenPGP);
197         ctx2->setPassphraseProvider(&provider);
198         ctx2->setPinentryMode(Context::PinentryLoopback);
199         auto decJob = new QGpgMEDecryptJob(ctx2);
200         QByteArray plainText;
201         auto decResult = decJob->exec(cipherText, plainText);
202         QVERIFY(!result.error());
203         QVERIFY(QString::fromUtf8(plainText) == QStringLiteral("Hello symmetric World"));
204         delete decJob;
205     }
206
207     void testEncryptDecryptNowrap()
208     {
209         /* Now decrypt */
210         if (!decryptSupported()) {
211             return;
212         }
213         auto listjob = openpgp()->keyListJob(false, false, false);
214         std::vector<Key> keys;
215         auto keylistresult = listjob->exec(QStringList() << QStringLiteral("alfa@example.net"),
216                                           false, keys);
217         QVERIFY(!keylistresult.error());
218         QVERIFY(keys.size() == 1);
219         delete listjob;
220
221         auto job = openpgp()->signEncryptJob(/*ASCII Armor */true, /* Textmode */ true);
222
223         auto encSignCtx = Job::context(job);
224         TestPassphraseProvider provider1;
225         encSignCtx->setPassphraseProvider(&provider1);
226         encSignCtx->setPinentryMode(Context::PinentryLoopback);
227
228         QVERIFY(job);
229         QByteArray cipherText;
230         auto result = job->exec(keys, keys, QStringLiteral("Hello World").toUtf8(), Context::AlwaysTrust, cipherText);
231         delete job;
232         QVERIFY(!result.first.error());
233         QVERIFY(!result.second.error());
234         const auto cipherString = QString::fromUtf8(cipherText);
235         QVERIFY(cipherString.startsWith("-----BEGIN PGP MESSAGE-----"));
236
237         /* Now decrypt */
238         if (!decryptSupported()) {
239             return;
240         }
241         auto ctx = Context::createForProtocol(OpenPGP);
242         TestPassphraseProvider provider;
243         ctx->setPassphraseProvider(&provider);
244         ctx->setPinentryMode(Context::PinentryLoopback);
245         ctx->setDecryptionFlags(Context::DecryptUnwrap);
246
247         auto decJob = new QGpgMEDecryptJob(ctx);
248         QByteArray plainText;
249         auto decResult = decJob->exec(cipherText, plainText);
250
251         QVERIFY(!decResult.error());
252
253         delete decJob;
254
255         // Now verify the unwrapeped data.
256         auto verifyJob = openpgp()->verifyOpaqueJob(true);
257         QByteArray verified;
258
259         auto verResult = verifyJob->exec(plainText, verified);
260         QVERIFY(!verResult.error());
261         delete verifyJob;
262
263         QVERIFY(verResult.numSignatures() == 1);
264         auto sig = verResult.signatures()[0];
265
266         QVERIFY(verified == QStringLiteral("Hello World"));
267     }
268
269 private:
270     /* Loopback and passphrase provider don't work for mixed encryption.
271      * So this test is disabled until gnupg(?) is fixed for this. */
272     void testMixedEncryptDecrypt()
273     {
274         if (!decryptSupported()) {
275             return;
276         }
277         auto listjob = openpgp()->keyListJob(false, false, false);
278         std::vector<Key> keys;
279         auto keylistresult = listjob->exec(QStringList() << QStringLiteral("alfa@example.net"),
280                                           false, keys);
281         QVERIFY(!keylistresult.error());
282         QVERIFY(keys.size() == 1);
283         delete listjob;
284
285         auto ctx = Context::createForProtocol(OpenPGP);
286         ctx->setPassphraseProvider(new TestPassphraseProvider);
287         ctx->setPinentryMode(Context::PinentryLoopback);
288         ctx->setArmor(true);
289         ctx->setTextMode(true);
290         auto job = new QGpgMEEncryptJob(ctx);
291         QByteArray cipherText;
292         printf("Before exec, flags: %x\n", Context::Symmetric | Context::AlwaysTrust);
293         auto result = job->exec(keys, QStringLiteral("Hello symmetric World").toUtf8(),
294                                 static_cast<Context::EncryptionFlags>(Context::Symmetric | Context::AlwaysTrust),
295                                 cipherText);
296         printf("After exec\n");
297         delete job;
298         QVERIFY(!result.error());
299         printf("Cipher:\n%s\n", cipherText.constData());
300         const auto cipherString = QString::fromUtf8(cipherText);
301         QVERIFY(cipherString.startsWith("-----BEGIN PGP MESSAGE-----"));
302
303         killAgent(mDir.path());
304
305         /* Now create a new homedir which with we test symetric decrypt. */
306         QTemporaryDir tmp;
307         qputenv("GNUPGHOME", tmp.path().toUtf8());
308         QFile agentConf(tmp.path() + QStringLiteral("/gpg-agent.conf"));
309         QVERIFY(agentConf.open(QIODevice::WriteOnly));
310         agentConf.write("allow-loopback-pinentry");
311         agentConf.close();
312
313         auto ctx2 = Context::createForProtocol(OpenPGP);
314         ctx2->setPassphraseProvider(new TestPassphraseProvider);
315         ctx2->setPinentryMode(Context::PinentryLoopback);
316         ctx2->setTextMode(true);
317         auto decJob = new QGpgMEDecryptJob(ctx2);
318         QByteArray plainText;
319         auto decResult = decJob->exec(cipherText, plainText);
320         QVERIFY(!decResult.error());
321         qDebug() << "Plain: " << plainText;
322         QVERIFY(QString::fromUtf8(plainText) == QStringLiteral("Hello symmetric World"));
323         delete decJob;
324
325         killAgent(tmp.path());
326         qputenv("GNUPGHOME", mDir.path().toUtf8());
327     }
328
329 public Q_SLOT:
330
331     void initTestCase()
332     {
333         QGpgMETest::initTestCase();
334         const QString gpgHome = qgetenv("GNUPGHOME");
335         qputenv("GNUPGHOME", mDir.path().toUtf8());
336         QVERIFY(mDir.isValid());
337         QFile agentConf(mDir.path() + QStringLiteral("/gpg-agent.conf"));
338         QVERIFY(agentConf.open(QIODevice::WriteOnly));
339         agentConf.write("allow-loopback-pinentry");
340         agentConf.close();
341         QVERIFY(copyKeyrings(gpgHome, mDir.path()));
342     }
343
344 private:
345     QTemporaryDir mDir;
346 };
347
348 QTEST_MAIN(EncryptionTest)
349
350 #include "t-encrypt.moc"