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 Intevation GmbH
5 :
6 : QGpgME is free software; you can redistribute it and/or
7 : modify it under the terms of the GNU General Public License as
8 : published by the Free Software Foundation; either version 2 of the
9 : License, or (at your option) any later version.
10 :
11 : QGpgME is distributed in the hope that it will be useful,
12 : but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 : General Public License for more details.
15 :
16 : You should have received a copy of the GNU General Public License
17 : along with this program; if not, write to the Free Software
18 : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 :
20 : In addition, as a special exception, the copyright holders give
21 : permission to link the code of this program with any edition of
22 : the Qt library by Trolltech AS, Norway (or with modified versions
23 : of Qt that use the same license as Qt), and distribute linked
24 : combinations including the two. You must obey the GNU General
25 : Public License in all respects for all of the code used other than
26 : Qt. If you modify this file, you may extend this exception to
27 : your version of the file, but you are not obligated to do so. If
28 : you do not wish to do so, delete this exception statement from
29 : your version.
30 : */
31 : #ifdef HAVE_CONFIG_H
32 : #include "config.h"
33 : #endif
34 :
35 : #include <QDebug>
36 : #include <QTest>
37 : #include <QTemporaryDir>
38 : #include <QSignalSpy>
39 : #include <QBuffer>
40 : #include "keylistjob.h"
41 : #include "encryptjob.h"
42 : #include "qgpgmeencryptjob.h"
43 : #include "encryptionresult.h"
44 : #include "decryptionresult.h"
45 : #include "qgpgmedecryptjob.h"
46 : #include "qgpgmebackend.h"
47 : #include "keylistresult.h"
48 : #include "engineinfo.h"
49 : #include "t-support.h"
50 :
51 : #define PROGRESS_TEST_SIZE 1 * 1024 * 1024
52 :
53 : using namespace QGpgME;
54 : using namespace GpgME;
55 :
56 2 : static bool decryptSupported()
57 : {
58 : /* With GnuPG 2.0.x (at least 2.0.26 by default on jessie)
59 : * the passphrase_cb does not work. So the test popped up
60 : * a pinentry. So tests requiring decryption don't work. */
61 2 : static auto version = GpgME::engineInfo(GpgME::GpgEngine).engineVersion();
62 2 : if (version < "2.0.0") {
63 : /* With 1.4 it just works */
64 0 : return true;
65 : }
66 2 : if (version < "2.1.0") {
67 : /* With 2.1 it works with loopback mode */
68 0 : return false;
69 : }
70 2 : return true;
71 : }
72 :
73 2 : class EncryptionTest : public QGpgMETest
74 : {
75 : Q_OBJECT
76 :
77 : Q_SIGNALS:
78 : void asyncDone();
79 :
80 : private Q_SLOTS:
81 :
82 1 : void testSimpleEncryptDecrypt()
83 : {
84 1 : auto listjob = openpgp()->keyListJob(false, false, false);
85 1 : std::vector<Key> keys;
86 3 : auto keylistresult = listjob->exec(QStringList() << QStringLiteral("alfa@example.net"),
87 3 : false, keys);
88 1 : Q_ASSERT(!keylistresult.error());
89 1 : Q_ASSERT(keys.size() == 1);
90 1 : delete listjob;
91 :
92 1 : auto job = openpgp()->encryptJob(/*ASCII Armor */true, /* Textmode */ true);
93 1 : Q_ASSERT(job);
94 2 : QByteArray cipherText;
95 3 : auto result = job->exec(keys, QStringLiteral("Hello World").toUtf8(), Context::AlwaysTrust, cipherText);
96 1 : delete job;
97 1 : Q_ASSERT(!result.error());
98 2 : const auto cipherString = QString::fromUtf8(cipherText);
99 1 : Q_ASSERT(cipherString.startsWith("-----BEGIN PGP MESSAGE-----"));
100 :
101 : /* Now decrypt */
102 1 : if (!decryptSupported()) {
103 1 : return;
104 : }
105 1 : auto ctx = Context::createForProtocol(OpenPGP);
106 2 : TestPassphraseProvider provider;
107 1 : ctx->setPassphraseProvider(&provider);
108 1 : ctx->setPinentryMode(Context::PinentryLoopback);
109 1 : auto decJob = new QGpgMEDecryptJob(ctx);
110 2 : QByteArray plainText;
111 2 : auto decResult = decJob->exec(cipherText, plainText);
112 1 : Q_ASSERT(!result.error());
113 2 : Q_ASSERT(QString::fromUtf8(plainText) == QStringLiteral("Hello World"));
114 2 : delete decJob;
115 : }
116 :
117 1 : void testProgress()
118 : {
119 1 : if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() < "2.1.15") {
120 : // We can only test the progress with 2.1.15 as this started to
121 : // have total progress for memory callbacks
122 1 : return;
123 : }
124 1 : auto listjob = openpgp()->keyListJob(false, false, false);
125 1 : std::vector<Key> keys;
126 3 : auto keylistresult = listjob->exec(QStringList() << QStringLiteral("alfa@example.net"),
127 3 : false, keys);
128 1 : Q_ASSERT(!keylistresult.error());
129 1 : Q_ASSERT(keys.size() == 1);
130 1 : delete listjob;
131 :
132 1 : auto job = openpgp()->encryptJob(/*ASCII Armor */false, /* Textmode */ false);
133 1 : Q_ASSERT(job);
134 2 : QByteArray plainBa;
135 1 : plainBa.fill('X', PROGRESS_TEST_SIZE);
136 2 : QByteArray cipherText;
137 :
138 1 : bool initSeen = false;
139 1 : bool finishSeen = false;
140 2 : connect(job, &Job::progress, this, [this, &initSeen, &finishSeen] (const QString&, int current, int total) {
141 : // We only check for progress 0 and max progress as the other progress
142 : // lines depend on the system speed and are as such unreliable to test.
143 2 : Q_ASSERT(total == PROGRESS_TEST_SIZE);
144 2 : if (current == 0) {
145 1 : initSeen = true;
146 : }
147 2 : if (current == total) {
148 1 : finishSeen = true;
149 : }
150 2 : Q_ASSERT(current >= 0 && current <= total);
151 3 : });
152 : connect(job, &EncryptJob::result, this, [this, &initSeen, &finishSeen] (const GpgME::EncryptionResult &,
153 : const QByteArray &,
154 : const QString,
155 1 : const GpgME::Error) {
156 1 : Q_ASSERT(initSeen);
157 1 : Q_ASSERT(finishSeen);
158 1 : Q_EMIT asyncDone();
159 2 : });
160 :
161 2 : auto inptr = std::shared_ptr<QIODevice>(new QBuffer(&plainBa));
162 1 : inptr->open(QIODevice::ReadOnly);
163 2 : auto outptr = std::shared_ptr<QIODevice>(new QBuffer(&cipherText));
164 1 : outptr->open(QIODevice::WriteOnly);
165 :
166 1 : job->start(keys, inptr, outptr, Context::AlwaysTrust);
167 2 : QSignalSpy spy (this, SIGNAL(asyncDone()));
168 2 : Q_ASSERT(spy.wait());
169 : }
170 :
171 1 : void testSymmetricEncryptDecrypt()
172 : {
173 1 : if (!decryptSupported()) {
174 1 : return;
175 : }
176 1 : auto ctx = Context::createForProtocol(OpenPGP);
177 1 : TestPassphraseProvider provider;
178 1 : ctx->setPassphraseProvider(&provider);
179 1 : ctx->setPinentryMode(Context::PinentryLoopback);
180 1 : ctx->setArmor(true);
181 1 : ctx->setTextMode(true);
182 1 : auto job = new QGpgMEEncryptJob(ctx);
183 2 : QByteArray cipherText;
184 3 : auto result = job->exec(std::vector<Key>(), QStringLiteral("Hello symmetric World").toUtf8(), Context::AlwaysTrust, cipherText);
185 1 : delete job;
186 1 : Q_ASSERT(!result.error());
187 2 : const auto cipherString = QString::fromUtf8(cipherText);
188 1 : Q_ASSERT(cipherString.startsWith("-----BEGIN PGP MESSAGE-----"));
189 :
190 1 : killAgent(mDir.path());
191 :
192 1 : auto ctx2 = Context::createForProtocol(OpenPGP);
193 1 : ctx2->setPassphraseProvider(&provider);
194 1 : ctx2->setPinentryMode(Context::PinentryLoopback);
195 1 : auto decJob = new QGpgMEDecryptJob(ctx2);
196 2 : QByteArray plainText;
197 2 : auto decResult = decJob->exec(cipherText, plainText);
198 1 : Q_ASSERT(!result.error());
199 2 : Q_ASSERT(QString::fromUtf8(plainText) == QStringLiteral("Hello symmetric World"));
200 2 : delete decJob;
201 : }
202 :
203 : private:
204 : /* Loopback and passphrase provider don't work for mixed encryption.
205 : * So this test is disabled until gnupg(?) is fixed for this. */
206 : void testMixedEncryptDecrypt()
207 : {
208 : if (!decryptSupported()) {
209 : return;
210 : }
211 : auto listjob = openpgp()->keyListJob(false, false, false);
212 : std::vector<Key> keys;
213 : auto keylistresult = listjob->exec(QStringList() << QStringLiteral("alfa@example.net"),
214 : false, keys);
215 : Q_ASSERT(!keylistresult.error());
216 : Q_ASSERT(keys.size() == 1);
217 : delete listjob;
218 :
219 : auto ctx = Context::createForProtocol(OpenPGP);
220 : ctx->setPassphraseProvider(new TestPassphraseProvider);
221 : ctx->setPinentryMode(Context::PinentryLoopback);
222 : ctx->setArmor(true);
223 : ctx->setTextMode(true);
224 : auto job = new QGpgMEEncryptJob(ctx);
225 : QByteArray cipherText;
226 : printf("Before exec, flags: %x\n", Context::Symmetric | Context::AlwaysTrust);
227 : auto result = job->exec(keys, QStringLiteral("Hello symmetric World").toUtf8(),
228 : static_cast<Context::EncryptionFlags>(Context::Symmetric | Context::AlwaysTrust),
229 : cipherText);
230 : printf("After exec\n");
231 : delete job;
232 : Q_ASSERT(!result.error());
233 : printf("Cipher:\n%s\n", cipherText.constData());
234 : const auto cipherString = QString::fromUtf8(cipherText);
235 : Q_ASSERT(cipherString.startsWith("-----BEGIN PGP MESSAGE-----"));
236 :
237 : killAgent(mDir.path());
238 :
239 : /* Now create a new homedir which with we test symetric decrypt. */
240 : QTemporaryDir tmp;
241 : qputenv("GNUPGHOME", tmp.path().toUtf8());
242 : QFile agentConf(tmp.path() + QStringLiteral("/gpg-agent.conf"));
243 : Q_ASSERT(agentConf.open(QIODevice::WriteOnly));
244 : agentConf.write("allow-loopback-pinentry");
245 : agentConf.close();
246 :
247 : auto ctx2 = Context::createForProtocol(OpenPGP);
248 : ctx2->setPassphraseProvider(new TestPassphraseProvider);
249 : ctx2->setPinentryMode(Context::PinentryLoopback);
250 : ctx2->setTextMode(true);
251 : auto decJob = new QGpgMEDecryptJob(ctx2);
252 : QByteArray plainText;
253 : auto decResult = decJob->exec(cipherText, plainText);
254 : Q_ASSERT(!decResult.error());
255 : qDebug() << "Plain: " << plainText;
256 : Q_ASSERT(QString::fromUtf8(plainText) == QStringLiteral("Hello symmetric World"));
257 : delete decJob;
258 :
259 : killAgent(tmp.path());
260 : qputenv("GNUPGHOME", mDir.path().toUtf8());
261 : }
262 :
263 : public Q_SLOT:
264 :
265 : void initTestCase()
266 : {
267 : QGpgMETest::initTestCase();
268 : const QString gpgHome = qgetenv("GNUPGHOME");
269 : qputenv("GNUPGHOME", mDir.path().toUtf8());
270 : Q_ASSERT(mDir.isValid());
271 : QFile agentConf(mDir.path() + QStringLiteral("/gpg-agent.conf"));
272 : Q_ASSERT(agentConf.open(QIODevice::WriteOnly));
273 : agentConf.write("allow-loopback-pinentry");
274 : agentConf.close();
275 : Q_ASSERT(copyKeyrings(gpgHome, mDir.path()));
276 : }
277 :
278 : private:
279 : QTemporaryDir mDir;
280 : };
281 :
282 1 : QTEST_MAIN(EncryptionTest)
283 :
284 : #include "t-encrypt.moc"
|