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 | |
24 | using namespace QCA; |
25 | |
26 | namespace gpgQCAPlugin { |
27 | |
28 | MyMessageContext::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 | |
52 | Provider::Context *MyMessageContext::clone() const |
53 | { |
54 | return nullptr; |
55 | } |
56 | |
57 | bool MyMessageContext::canSignMultiple() const |
58 | { |
59 | return false; |
60 | } |
61 | |
62 | SecureMessage::Type MyMessageContext::type() const |
63 | { |
64 | return SecureMessage::OpenPGP; |
65 | } |
66 | |
67 | void MyMessageContext::reset() |
68 | { |
69 | wrote = 0; |
70 | ok = false; |
71 | wasSigned = false; |
72 | } |
73 | |
74 | void 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 | |
81 | void MyMessageContext::setupSign(const SecureMessageKeyList &keys, SecureMessage::SignMode m, bool, bool) |
82 | { |
83 | signerId = keys.first().pgpSecretKey().keyId(); |
84 | signMode = m; |
85 | } |
86 | |
87 | void MyMessageContext::setupVerify(const QByteArray &detachedSig) |
88 | { |
89 | sig = detachedSig; |
90 | } |
91 | |
92 | void 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 | |
129 | void MyMessageContext::update(const QByteArray &in) |
130 | { |
131 | gpg.write(in); |
132 | // this->in.append(in); |
133 | } |
134 | |
135 | QByteArray MyMessageContext::read() |
136 | { |
137 | const QByteArray a = out; |
138 | out.clear(); |
139 | return a; |
140 | } |
141 | |
142 | int MyMessageContext::written() |
143 | { |
144 | int x = wrote; |
145 | wrote = 0; |
146 | return x; |
147 | } |
148 | |
149 | void MyMessageContext::end() |
150 | { |
151 | gpg.endWrite(); |
152 | } |
153 | |
154 | void MyMessageContext::seterror() |
155 | { |
156 | gpg.reset(); |
157 | _finished = true; |
158 | ok = false; |
159 | op_err = GpgOp::ErrorUnknown; |
160 | } |
161 | |
162 | void 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 | |
212 | bool MyMessageContext::finished() const |
213 | { |
214 | return _finished; |
215 | } |
216 | |
217 | bool 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 | |
277 | bool MyMessageContext::success() const |
278 | { |
279 | return ok; |
280 | } |
281 | |
282 | SecureMessage::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 | |
312 | QByteArray MyMessageContext::signature() const |
313 | { |
314 | return sig; |
315 | } |
316 | |
317 | QString MyMessageContext::hashName() const |
318 | { |
319 | // TODO |
320 | return QStringLiteral("sha1" ); |
321 | } |
322 | |
323 | SecureMessageSignatureList MyMessageContext::signers() const |
324 | { |
325 | SecureMessageSignatureList list; |
326 | if (ok && wasSigned) |
327 | list += signer; |
328 | return list; |
329 | } |
330 | |
331 | QString MyMessageContext::diagnosticText() const |
332 | { |
333 | return dtext; |
334 | } |
335 | |
336 | void MyMessageContext::gpg_readyRead() |
337 | { |
338 | emit updated(); |
339 | } |
340 | |
341 | void MyMessageContext::gpg_bytesWritten(int bytes) |
342 | { |
343 | wrote += bytes; |
344 | } |
345 | |
346 | void MyMessageContext::gpg_finished() |
347 | { |
348 | complete(); |
349 | emit updated(); |
350 | } |
351 | |
352 | void 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 | |
380 | void 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 | |
387 | void MyMessageContext::gpg_readyReadDiagnosticText() |
388 | { |
389 | // TODO ? |
390 | } |
391 | |
392 | void 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 | |
404 | void 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 | |