| 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 | |