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 0 : 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 0 : static auto version = GpgME::engineInfo(GpgME::GpgEngine).engineVersion();
62 0 : if (version < "2.0.0") {
63 : /* With 1.4 it just works */
64 0 : return true;
65 : }
66 0 : if (version < "2.1.0") {
67 : /* With 2.1 it works with loopback mode */
68 0 : return false;
69 : }
70 0 : return true;
71 : }
72 :
73 0 : class EncryptionTest : public QGpgMETest
74 : {
75 : Q_OBJECT
76 :
77 : Q_SIGNALS:
78 : void asyncDone();
79 :
80 : private Q_SLOTS:
81 :
82 0 : void testSimpleEncryptDecrypt()
83 : {
84 0 : auto listjob = openpgp()->keyListJob(false, false, false);
85 0 : std::vector<Key> keys;
86 0 : auto keylistresult = listjob->exec(QStringList() << QStringLiteral("alfa@example.net"),
87 0 : false, keys);
88 0 : Q_ASSERT(!keylistresult.error());
89 0 : Q_ASSERT(keys.size() == 1);
90 0 : delete listjob;
91 :
92 0 : auto job = openpgp()->encryptJob(/*ASCII Armor */true, /* Textmode */ true);
93 0 : Q_ASSERT(job);
94 0 : QByteArray cipherText;
95 0 : auto result = job->exec(keys, QStringLiteral("Hello World").toUtf8(), Context::AlwaysTrust, cipherText);
96 0 : delete job;
97 0 : Q_ASSERT(!result.error());
98 0 : const auto cipherString = QString::fromUtf8(cipherText);
99 0 : Q_ASSERT(cipherString.startsWith("-----BEGIN PGP MESSAGE-----"));
100 :
101 : /* Now decrypt */
102 0 : if (!decryptSupported()) {
103 0 : return;
104 : }
105 0 : auto ctx = Context::createForProtocol(OpenPGP);
106 0 : TestPassphraseProvider provider;
107 0 : ctx->setPassphraseProvider(&provider);
108 0 : ctx->setPinentryMode(Context::PinentryLoopback);
109 0 : auto decJob = new QGpgMEDecryptJob(ctx);
110 0 : QByteArray plainText;
111 0 : auto decResult = decJob->exec(cipherText, plainText);
112 0 : Q_ASSERT(!result.error());
113 0 : Q_ASSERT(QString::fromUtf8(plainText) == QStringLiteral("Hello World"));
114 0 : delete decJob;
115 : }
116 :
117 0 : void testProgress()
118 : {
119 0 : 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 0 : return;
123 : }
124 0 : auto listjob = openpgp()->keyListJob(false, false, false);
125 0 : std::vector<Key> keys;
126 0 : auto keylistresult = listjob->exec(QStringList() << QStringLiteral("alfa@example.net"),
127 0 : false, keys);
128 0 : Q_ASSERT(!keylistresult.error());
129 0 : Q_ASSERT(keys.size() == 1);
130 0 : delete listjob;
131 :
132 0 : auto job = openpgp()->encryptJob(/*ASCII Armor */false, /* Textmode */ false);
133 0 : Q_ASSERT(job);
134 0 : QByteArray plainBa;
135 0 : plainBa.fill('X', PROGRESS_TEST_SIZE);
136 0 : QByteArray cipherText;
137 :
138 0 : bool initSeen = false;
139 0 : bool finishSeen = false;
140 0 : 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 0 : Q_ASSERT(total == PROGRESS_TEST_SIZE);
144 0 : if (current == 0) {
145 0 : initSeen = true;
146 : }
147 0 : if (current == total) {
148 0 : finishSeen = true;
149 : }
150 0 : Q_ASSERT(current >= 0 && current <= total);
151 0 : });
152 : connect(job, &EncryptJob::result, this, [this, &initSeen, &finishSeen] (const GpgME::EncryptionResult &,
153 : const QByteArray &,
154 : const QString,
155 0 : const GpgME::Error) {
156 0 : Q_ASSERT(initSeen);
157 0 : Q_ASSERT(finishSeen);
158 0 : Q_EMIT asyncDone();
159 0 : });
160 :
161 0 : auto inptr = std::shared_ptr<QIODevice>(new QBuffer(&plainBa));
162 0 : inptr->open(QIODevice::ReadOnly);
163 0 : auto outptr = std::shared_ptr<QIODevice>(new QBuffer(&cipherText));
164 0 : outptr->open(QIODevice::WriteOnly);
165 :
166 0 : job->start(keys, inptr, outptr, Context::AlwaysTrust);
167 0 : QSignalSpy spy (this, SIGNAL(asyncDone()));
168 0 : Q_ASSERT(spy.wait());
169 : }
170 :
171 0 : void testSymmetricEncryptDecrypt()
172 : {
173 0 : if (!decryptSupported()) {
174 0 : return;
175 : }
176 0 : auto ctx = Context::createForProtocol(OpenPGP);
177 0 : TestPassphraseProvider provider;
178 0 : ctx->setPassphraseProvider(&provider);
179 0 : ctx->setPinentryMode(Context::PinentryLoopback);
180 0 : ctx->setArmor(true);
181 0 : ctx->setTextMode(true);
182 0 : auto job = new QGpgMEEncryptJob(ctx);
183 0 : QByteArray cipherText;
184 0 : auto result = job->exec(std::vector<Key>(), QStringLiteral("Hello symmetric World").toUtf8(), Context::AlwaysTrust, cipherText);
185 0 : delete job;
186 0 : Q_ASSERT(!result.error());
187 0 : const auto cipherString = QString::fromUtf8(cipherText);
188 0 : Q_ASSERT(cipherString.startsWith("-----BEGIN PGP MESSAGE-----"));
189 :
190 0 : killAgent(mDir.path());
191 :
192 0 : auto ctx2 = Context::createForProtocol(OpenPGP);
193 0 : ctx2->setPassphraseProvider(&provider);
194 0 : ctx2->setPinentryMode(Context::PinentryLoopback);
195 0 : auto decJob = new QGpgMEDecryptJob(ctx2);
196 0 : QByteArray plainText;
197 0 : auto decResult = decJob->exec(cipherText, plainText);
198 0 : Q_ASSERT(!result.error());
199 0 : Q_ASSERT(QString::fromUtf8(plainText) == QStringLiteral("Hello symmetric World"));
200 0 : 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 0 : QTEST_MAIN(EncryptionTest)
283 :
284 : #include "t-encrypt.moc"
|