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