/* * Copyright (C) 2003-2008 Justin Karneges * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include "mymessagecontext.h" #include "mykeystorelist.h" #include "mypgpkeycontext.h" #include "utils.h" using namespace QCA; namespace gpgQCAPlugin { MyMessageContext::MyMessageContext(MyOpenPGPContext *_sms, Provider *p) : MessageContext(p, QStringLiteral("pgpmsg")) , sms(_sms) , op(Sign) , signMode(SecureMessage::Detached) , format(SecureMessage::Ascii) , wrote(0) , ok(false) , wasSigned(false) , op_err(GpgOp::ErrorUnknown) , gpg(find_bin()) , _finished(false) { connect(&gpg, &GpgOp::readyRead, this, &MyMessageContext::gpg_readyRead); connect(&gpg, &GpgOp::bytesWritten, this, &MyMessageContext::gpg_bytesWritten); connect(&gpg, &GpgOp::finished, this, &MyMessageContext::gpg_finished); connect(&gpg, &GpgOp::needPassphrase, this, &MyMessageContext::gpg_needPassphrase); connect(&gpg, &GpgOp::needCard, this, &MyMessageContext::gpg_needCard); connect(&gpg, &GpgOp::readyReadDiagnosticText, this, &MyMessageContext::gpg_readyReadDiagnosticText); connect(&asker, &QCA::PasswordAsker::responseReady, this, &MyMessageContext::asker_responseReady); connect(&tokenAsker, &QCA::TokenAsker::responseReady, this, &MyMessageContext::tokenAsker_responseReady); } Provider::Context *MyMessageContext::clone() const { return nullptr; } bool MyMessageContext::canSignMultiple() const { return false; } SecureMessage::Type MyMessageContext::type() const { return SecureMessage::OpenPGP; } void MyMessageContext::reset() { wrote = 0; ok = false; wasSigned = false; } void MyMessageContext::setupEncrypt(const SecureMessageKeyList &keys) { recipIds.clear(); for (int n = 0; n < keys.count(); ++n) recipIds += keys[n].pgpPublicKey().keyId(); } void MyMessageContext::setupSign(const SecureMessageKeyList &keys, SecureMessage::SignMode m, bool, bool) { signerId = keys.first().pgpSecretKey().keyId(); signMode = m; } void MyMessageContext::setupVerify(const QByteArray &detachedSig) { sig = detachedSig; } void MyMessageContext::start(SecureMessage::Format f, Operation op) { _finished = false; format = f; this->op = op; if (getProperty(QStringLiteral("pgp-always-trust")).toBool()) gpg.setAlwaysTrust(true); if (format == SecureMessage::Ascii) gpg.setAsciiFormat(true); else gpg.setAsciiFormat(false); if (op == Encrypt) { gpg.doEncrypt(recipIds); } else if (op == Decrypt) { gpg.doDecrypt(); } else if (op == Sign) { if (signMode == SecureMessage::Message) { gpg.doSign(signerId); } else if (signMode == SecureMessage::Clearsign) { gpg.doSignClearsign(signerId); } else // SecureMessage::Detached { gpg.doSignDetached(signerId); } } else if (op == Verify) { if (!sig.isEmpty()) gpg.doVerifyDetached(sig); else gpg.doDecrypt(); } else if (op == SignAndEncrypt) { gpg.doSignAndEncrypt(signerId, recipIds); } } void MyMessageContext::update(const QByteArray &in) { gpg.write(in); // this->in.append(in); } QByteArray MyMessageContext::read() { const QByteArray a = out; out.clear(); return a; } int MyMessageContext::written() { int x = wrote; wrote = 0; return x; } void MyMessageContext::end() { gpg.endWrite(); } void MyMessageContext::seterror() { gpg.reset(); _finished = true; ok = false; op_err = GpgOp::ErrorUnknown; } void MyMessageContext::complete() { _finished = true; dtext = gpg.readDiagnosticText(); ok = gpg.success(); if (ok) { if (op == Sign && signMode == SecureMessage::Detached) sig = gpg.read(); else out = gpg.read(); } if (ok) { if (gpg.wasSigned()) { const QString signerId = gpg.signerId(); const QDateTime ts = gpg.timestamp(); const GpgOp::VerifyResult vr = gpg.verifyResult(); SecureMessageSignature::IdentityResult ir; Validity v; if (vr == GpgOp::VerifyGood) { ir = SecureMessageSignature::Valid; v = ValidityGood; } else if (vr == GpgOp::VerifyBad) { ir = SecureMessageSignature::InvalidSignature; v = ValidityGood; // good key, bad sig } else // GpgOp::VerifyNoKey { ir = SecureMessageSignature::NoKey; v = ErrorValidityUnknown; } SecureMessageKey key; PGPKey pub = publicKeyFromId(signerId); if (pub.isNull()) { MyPGPKeyContext *kc = new MyPGPKeyContext(provider()); kc->_props.keyId = signerId; pub.change(kc); } key.setPGPPublicKey(pub); signer = SecureMessageSignature(ir, v, key, ts); wasSigned = true; } } else op_err = gpg.errorCode(); } bool MyMessageContext::finished() const { return _finished; } bool MyMessageContext::waitForFinished(int msecs) { // FIXME Q_UNUSED(msecs); MyKeyStoreList *keyStoreList = MyKeyStoreList::instance(); while (true) { // TODO: handle token prompt events GpgOp::Event e = gpg.waitForEvent(-1); if (e.type == GpgOp::Event::NeedPassphrase) { // TODO QString keyId; PGPKey sec = secretKeyFromId(e.keyId); if (!sec.isNull()) keyId = sec.keyId(); else keyId = e.keyId; QStringList out; out += escape_string(QStringLiteral("qca-gnupg-1")); out += escape_string(keyId); QString serialized = out.join(QStringLiteral(":")); KeyStoreEntry kse; KeyStoreEntryContext *c = keyStoreList->entryPassive(serialized); if (c) kse.change(c); asker.ask(Event::StylePassphrase, KeyStoreInfo(KeyStore::PGPKeyring, keyStoreList->storeId(0), keyStoreList->name(0)), kse, nullptr); asker.waitForResponse(); if (!asker.accepted()) { seterror(); return true; } gpg.submitPassphrase(asker.password()); } else if (e.type == GpgOp::Event::NeedCard) { tokenAsker.ask(KeyStoreInfo(KeyStore::PGPKeyring, keyStoreList->storeId(0), keyStoreList->name(0)), KeyStoreEntry(), nullptr); if (!tokenAsker.accepted()) { seterror(); return true; } gpg.cardOkay(); } else if (e.type == GpgOp::Event::Finished) break; } complete(); return true; } bool MyMessageContext::success() const { return ok; } SecureMessage::Error MyMessageContext::errorCode() const { SecureMessage::Error e = SecureMessage::ErrorUnknown; if (op_err == GpgOp::ErrorProcess) e = SecureMessage::ErrorUnknown; else if (op_err == GpgOp::ErrorPassphrase) e = SecureMessage::ErrorPassphrase; else if (op_err == GpgOp::ErrorFormat) e = SecureMessage::ErrorFormat; else if (op_err == GpgOp::ErrorSignerExpired) e = SecureMessage::ErrorSignerExpired; else if (op_err == GpgOp::ErrorSignerRevoked) e = SecureMessage::ErrorSignerRevoked; else if (op_err == GpgOp::ErrorSignatureExpired) e = SecureMessage::ErrorSignatureExpired; else if (op_err == GpgOp::ErrorEncryptExpired) e = SecureMessage::ErrorEncryptExpired; else if (op_err == GpgOp::ErrorEncryptRevoked) e = SecureMessage::ErrorEncryptRevoked; else if (op_err == GpgOp::ErrorEncryptUntrusted) e = SecureMessage::ErrorEncryptUntrusted; else if (op_err == GpgOp::ErrorEncryptInvalid) e = SecureMessage::ErrorEncryptInvalid; else if (op_err == GpgOp::ErrorDecryptNoKey) e = SecureMessage::ErrorUnknown; else if (op_err == GpgOp::ErrorUnknown) e = SecureMessage::ErrorUnknown; return e; } QByteArray MyMessageContext::signature() const { return sig; } QString MyMessageContext::hashName() const { // TODO return QStringLiteral("sha1"); } SecureMessageSignatureList MyMessageContext::signers() const { SecureMessageSignatureList list; if (ok && wasSigned) list += signer; return list; } QString MyMessageContext::diagnosticText() const { return dtext; } void MyMessageContext::gpg_readyRead() { emit updated(); } void MyMessageContext::gpg_bytesWritten(int bytes) { wrote += bytes; } void MyMessageContext::gpg_finished() { complete(); emit updated(); } void MyMessageContext::gpg_needPassphrase(const QString &in_keyId) { // FIXME: copied from above, clean up later QString keyId; PGPKey sec = secretKeyFromId(in_keyId); if (!sec.isNull()) keyId = sec.keyId(); else keyId = in_keyId; // emit keyStoreList->storeNeedPassphrase(0, 0, keyId); QStringList out; out += escape_string(QStringLiteral("qca-gnupg-1")); out += escape_string(keyId); QString serialized = out.join(QStringLiteral(":")); KeyStoreEntry kse; MyKeyStoreList *keyStoreList = MyKeyStoreList::instance(); KeyStoreEntryContext *c = keyStoreList->entryPassive(serialized); if (c) kse.change(c); asker.ask(Event::StylePassphrase, KeyStoreInfo(KeyStore::PGPKeyring, keyStoreList->storeId(0), keyStoreList->name(0)), kse, nullptr); } void MyMessageContext::gpg_needCard() { MyKeyStoreList *keyStoreList = MyKeyStoreList::instance(); tokenAsker.ask( KeyStoreInfo(KeyStore::PGPKeyring, keyStoreList->storeId(0), keyStoreList->name(0)), KeyStoreEntry(), nullptr); } void MyMessageContext::gpg_readyReadDiagnosticText() { // TODO ? } void MyMessageContext::asker_responseReady() { if (!asker.accepted()) { seterror(); emit updated(); return; } const SecureArray a = asker.password(); gpg.submitPassphrase(a); } void MyMessageContext::tokenAsker_responseReady() { if (!tokenAsker.accepted()) { seterror(); emit updated(); return; } gpg.cardOkay(); } } // end namespace gpgQCAPlugin