1/*
2 * Copyright (C) 2003-2008 Justin Karneges <justin@affinix.com>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17 */
18
19#include "mymessagecontext.h"
20#include "mykeystorelist.h"
21#include "mypgpkeycontext.h"
22#include "utils.h"
23
24using namespace QCA;
25
26namespace gpgQCAPlugin {
27
28MyMessageContext::MyMessageContext(MyOpenPGPContext *_sms, Provider *p)
29 : MessageContext(p, QStringLiteral("pgpmsg"))
30 , sms(_sms)
31 , op(Sign)
32 , signMode(SecureMessage::Detached)
33 , format(SecureMessage::Ascii)
34 , wrote(0)
35 , ok(false)
36 , wasSigned(false)
37 , op_err(GpgOp::ErrorUnknown)
38 , gpg(find_bin())
39 , _finished(false)
40{
41 connect(sender: &gpg, signal: &GpgOp::readyRead, context: this, slot: &MyMessageContext::gpg_readyRead);
42 connect(sender: &gpg, signal: &GpgOp::bytesWritten, context: this, slot: &MyMessageContext::gpg_bytesWritten);
43 connect(sender: &gpg, signal: &GpgOp::finished, context: this, slot: &MyMessageContext::gpg_finished);
44 connect(sender: &gpg, signal: &GpgOp::needPassphrase, context: this, slot: &MyMessageContext::gpg_needPassphrase);
45 connect(sender: &gpg, signal: &GpgOp::needCard, context: this, slot: &MyMessageContext::gpg_needCard);
46 connect(sender: &gpg, signal: &GpgOp::readyReadDiagnosticText, context: this, slot: &MyMessageContext::gpg_readyReadDiagnosticText);
47
48 connect(sender: &asker, signal: &QCA::PasswordAsker::responseReady, context: this, slot: &MyMessageContext::asker_responseReady);
49 connect(sender: &tokenAsker, signal: &QCA::TokenAsker::responseReady, context: this, slot: &MyMessageContext::tokenAsker_responseReady);
50}
51
52Provider::Context *MyMessageContext::clone() const
53{
54 return nullptr;
55}
56
57bool MyMessageContext::canSignMultiple() const
58{
59 return false;
60}
61
62SecureMessage::Type MyMessageContext::type() const
63{
64 return SecureMessage::OpenPGP;
65}
66
67void MyMessageContext::reset()
68{
69 wrote = 0;
70 ok = false;
71 wasSigned = false;
72}
73
74void MyMessageContext::setupEncrypt(const SecureMessageKeyList &keys)
75{
76 recipIds.clear();
77 for (int n = 0; n < keys.count(); ++n)
78 recipIds += keys[n].pgpPublicKey().keyId();
79}
80
81void MyMessageContext::setupSign(const SecureMessageKeyList &keys, SecureMessage::SignMode m, bool, bool)
82{
83 signerId = keys.first().pgpSecretKey().keyId();
84 signMode = m;
85}
86
87void MyMessageContext::setupVerify(const QByteArray &detachedSig)
88{
89 sig = detachedSig;
90}
91
92void MyMessageContext::start(SecureMessage::Format f, Operation op)
93{
94 _finished = false;
95 format = f;
96 this->op = op;
97
98 if (getProperty(QStringLiteral("pgp-always-trust")).toBool())
99 gpg.setAlwaysTrust(true);
100
101 if (format == SecureMessage::Ascii)
102 gpg.setAsciiFormat(true);
103 else
104 gpg.setAsciiFormat(false);
105
106 if (op == Encrypt) {
107 gpg.doEncrypt(recip_ids: recipIds);
108 } else if (op == Decrypt) {
109 gpg.doDecrypt();
110 } else if (op == Sign) {
111 if (signMode == SecureMessage::Message) {
112 gpg.doSign(signer_id: signerId);
113 } else if (signMode == SecureMessage::Clearsign) {
114 gpg.doSignClearsign(signer_id: signerId);
115 } else // SecureMessage::Detached
116 {
117 gpg.doSignDetached(signer_id: signerId);
118 }
119 } else if (op == Verify) {
120 if (!sig.isEmpty())
121 gpg.doVerifyDetached(sig);
122 else
123 gpg.doDecrypt();
124 } else if (op == SignAndEncrypt) {
125 gpg.doSignAndEncrypt(signer_id: signerId, recip_ids: recipIds);
126 }
127}
128
129void MyMessageContext::update(const QByteArray &in)
130{
131 gpg.write(in);
132 // this->in.append(in);
133}
134
135QByteArray MyMessageContext::read()
136{
137 const QByteArray a = out;
138 out.clear();
139 return a;
140}
141
142int MyMessageContext::written()
143{
144 int x = wrote;
145 wrote = 0;
146 return x;
147}
148
149void MyMessageContext::end()
150{
151 gpg.endWrite();
152}
153
154void MyMessageContext::seterror()
155{
156 gpg.reset();
157 _finished = true;
158 ok = false;
159 op_err = GpgOp::ErrorUnknown;
160}
161
162void MyMessageContext::complete()
163{
164 _finished = true;
165
166 dtext = gpg.readDiagnosticText();
167
168 ok = gpg.success();
169 if (ok) {
170 if (op == Sign && signMode == SecureMessage::Detached)
171 sig = gpg.read();
172 else
173 out = gpg.read();
174 }
175
176 if (ok) {
177 if (gpg.wasSigned()) {
178 const QString signerId = gpg.signerId();
179 const QDateTime ts = gpg.timestamp();
180 const GpgOp::VerifyResult vr = gpg.verifyResult();
181
182 SecureMessageSignature::IdentityResult ir;
183 Validity v;
184 if (vr == GpgOp::VerifyGood) {
185 ir = SecureMessageSignature::Valid;
186 v = ValidityGood;
187 } else if (vr == GpgOp::VerifyBad) {
188 ir = SecureMessageSignature::InvalidSignature;
189 v = ValidityGood; // good key, bad sig
190 } else // GpgOp::VerifyNoKey
191 {
192 ir = SecureMessageSignature::NoKey;
193 v = ErrorValidityUnknown;
194 }
195
196 SecureMessageKey key;
197 PGPKey pub = publicKeyFromId(id: signerId);
198 if (pub.isNull()) {
199 MyPGPKeyContext *kc = new MyPGPKeyContext(provider());
200 kc->_props.keyId = signerId;
201 pub.change(c: kc);
202 }
203 key.setPGPPublicKey(pub);
204
205 signer = SecureMessageSignature(ir, v, key, ts);
206 wasSigned = true;
207 }
208 } else
209 op_err = gpg.errorCode();
210}
211
212bool MyMessageContext::finished() const
213{
214 return _finished;
215}
216
217bool MyMessageContext::waitForFinished(int msecs)
218{
219 // FIXME
220 Q_UNUSED(msecs);
221 MyKeyStoreList *keyStoreList = MyKeyStoreList::instance();
222
223 while (true) {
224 // TODO: handle token prompt events
225
226 GpgOp::Event e = gpg.waitForEvent(msecs: -1);
227 if (e.type == GpgOp::Event::NeedPassphrase) {
228 // TODO
229
230 QString keyId;
231 PGPKey sec = secretKeyFromId(id: e.keyId);
232 if (!sec.isNull())
233 keyId = sec.keyId();
234 else
235 keyId = e.keyId;
236 QStringList out;
237 out += escape_string(QStringLiteral("qca-gnupg-1"));
238 out += escape_string(in: keyId);
239 QString serialized = out.join(QStringLiteral(":"));
240
241 KeyStoreEntry kse;
242 KeyStoreEntryContext *c = keyStoreList->entryPassive(serialized);
243 if (c)
244 kse.change(c);
245
246 asker.ask(pstyle: Event::StylePassphrase,
247 keyStoreInfo: KeyStoreInfo(KeyStore::PGPKeyring, keyStoreList->storeId(0), keyStoreList->name(0)),
248 keyStoreEntry: kse,
249 ptr: nullptr);
250 asker.waitForResponse();
251
252 if (!asker.accepted()) {
253 seterror();
254 return true;
255 }
256
257 gpg.submitPassphrase(a: asker.password());
258 } else if (e.type == GpgOp::Event::NeedCard) {
259 tokenAsker.ask(keyStoreInfo: KeyStoreInfo(KeyStore::PGPKeyring, keyStoreList->storeId(0), keyStoreList->name(0)),
260 keyStoreEntry: KeyStoreEntry(),
261 ptr: nullptr);
262
263 if (!tokenAsker.accepted()) {
264 seterror();
265 return true;
266 }
267
268 gpg.cardOkay();
269 } else if (e.type == GpgOp::Event::Finished)
270 break;
271 }
272
273 complete();
274 return true;
275}
276
277bool MyMessageContext::success() const
278{
279 return ok;
280}
281
282SecureMessage::Error MyMessageContext::errorCode() const
283{
284 SecureMessage::Error e = SecureMessage::ErrorUnknown;
285 if (op_err == GpgOp::ErrorProcess)
286 e = SecureMessage::ErrorUnknown;
287 else if (op_err == GpgOp::ErrorPassphrase)
288 e = SecureMessage::ErrorPassphrase;
289 else if (op_err == GpgOp::ErrorFormat)
290 e = SecureMessage::ErrorFormat;
291 else if (op_err == GpgOp::ErrorSignerExpired)
292 e = SecureMessage::ErrorSignerExpired;
293 else if (op_err == GpgOp::ErrorSignerRevoked)
294 e = SecureMessage::ErrorSignerRevoked;
295 else if (op_err == GpgOp::ErrorSignatureExpired)
296 e = SecureMessage::ErrorSignatureExpired;
297 else if (op_err == GpgOp::ErrorEncryptExpired)
298 e = SecureMessage::ErrorEncryptExpired;
299 else if (op_err == GpgOp::ErrorEncryptRevoked)
300 e = SecureMessage::ErrorEncryptRevoked;
301 else if (op_err == GpgOp::ErrorEncryptUntrusted)
302 e = SecureMessage::ErrorEncryptUntrusted;
303 else if (op_err == GpgOp::ErrorEncryptInvalid)
304 e = SecureMessage::ErrorEncryptInvalid;
305 else if (op_err == GpgOp::ErrorDecryptNoKey)
306 e = SecureMessage::ErrorUnknown;
307 else if (op_err == GpgOp::ErrorUnknown)
308 e = SecureMessage::ErrorUnknown;
309 return e;
310}
311
312QByteArray MyMessageContext::signature() const
313{
314 return sig;
315}
316
317QString MyMessageContext::hashName() const
318{
319 // TODO
320 return QStringLiteral("sha1");
321}
322
323SecureMessageSignatureList MyMessageContext::signers() const
324{
325 SecureMessageSignatureList list;
326 if (ok && wasSigned)
327 list += signer;
328 return list;
329}
330
331QString MyMessageContext::diagnosticText() const
332{
333 return dtext;
334}
335
336void MyMessageContext::gpg_readyRead()
337{
338 emit updated();
339}
340
341void MyMessageContext::gpg_bytesWritten(int bytes)
342{
343 wrote += bytes;
344}
345
346void MyMessageContext::gpg_finished()
347{
348 complete();
349 emit updated();
350}
351
352void MyMessageContext::gpg_needPassphrase(const QString &in_keyId)
353{
354 // FIXME: copied from above, clean up later
355
356 QString keyId;
357 PGPKey sec = secretKeyFromId(id: in_keyId);
358 if (!sec.isNull())
359 keyId = sec.keyId();
360 else
361 keyId = in_keyId;
362 // emit keyStoreList->storeNeedPassphrase(0, 0, keyId);
363 QStringList out;
364 out += escape_string(QStringLiteral("qca-gnupg-1"));
365 out += escape_string(in: keyId);
366 QString serialized = out.join(QStringLiteral(":"));
367
368 KeyStoreEntry kse;
369 MyKeyStoreList *keyStoreList = MyKeyStoreList::instance();
370 KeyStoreEntryContext *c = keyStoreList->entryPassive(serialized);
371 if (c)
372 kse.change(c);
373
374 asker.ask(pstyle: Event::StylePassphrase,
375 keyStoreInfo: KeyStoreInfo(KeyStore::PGPKeyring, keyStoreList->storeId(0), keyStoreList->name(0)),
376 keyStoreEntry: kse,
377 ptr: nullptr);
378}
379
380void MyMessageContext::gpg_needCard()
381{
382 MyKeyStoreList *keyStoreList = MyKeyStoreList::instance();
383 tokenAsker.ask(
384 keyStoreInfo: KeyStoreInfo(KeyStore::PGPKeyring, keyStoreList->storeId(0), keyStoreList->name(0)), keyStoreEntry: KeyStoreEntry(), ptr: nullptr);
385}
386
387void MyMessageContext::gpg_readyReadDiagnosticText()
388{
389 // TODO ?
390}
391
392void MyMessageContext::asker_responseReady()
393{
394 if (!asker.accepted()) {
395 seterror();
396 emit updated();
397 return;
398 }
399
400 const SecureArray a = asker.password();
401 gpg.submitPassphrase(a);
402}
403
404void MyMessageContext::tokenAsker_responseReady()
405{
406 if (!tokenAsker.accepted()) {
407 seterror();
408 emit updated();
409 return;
410 }
411
412 gpg.cardOkay();
413}
414
415} // end namespace gpgQCAPlugin
416

source code of qca/plugins/qca-gnupg/mymessagecontext.cpp