diff options
author | ubbo <ubbo@34ebc366-c3a9-4b3c-9f84-69acf7962910> | 2012-08-05 23:26:56 +0000 |
---|---|---|
committer | ubbo <ubbo@34ebc366-c3a9-4b3c-9f84-69acf7962910> | 2012-08-05 23:26:56 +0000 |
commit | 4db2fdfca8c9c5e226ff1589f8c8a88ebf879eca (patch) | |
tree | d6340ee2b354096b435e4fd41c2ff3926dab1e45 | |
parent | signing text works (diff) | |
download | gpg4usb-4db2fdfca8c9c5e226ff1589f8c8a88ebf879eca.tar.gz gpg4usb-4db2fdfca8c9c5e226ff1589f8c8a88ebf879eca.zip |
some work on verify
git-svn-id: http://cpunk.de/svn/src/gpg4usb/branches/0.3.2-mac@943 34ebc366-c3a9-4b3c-9f84-69acf7962910
-rw-r--r-- | TODO.kgpgport | 1 | ||||
-rw-r--r-- | gpg4usb.pro | 9 | ||||
-rw-r--r-- | kgpg/transactions/kgpgverify.cpp | 204 | ||||
-rw-r--r-- | kgpg/transactions/kgpgverify.h | 90 | ||||
-rw-r--r-- | mainwindow.cpp | 16 | ||||
-rw-r--r-- | verifynotification.cpp | 160 | ||||
-rw-r--r-- | verifynotification.h | 5 |
7 files changed, 466 insertions, 19 deletions
diff --git a/TODO.kgpgport b/TODO.kgpgport index b19fed3..1267f3f 100644 --- a/TODO.kgpgport +++ b/TODO.kgpgport @@ -3,3 +3,4 @@ TODO: -reactivate keyimportdetaildialog - sign should work for more than one secret keyid - why is listkeys called so often (twice?) +- signverify on one button like kgpg? diff --git a/gpg4usb.pro b/gpg4usb.pro index 5801afd..2953f79 100644 --- a/gpg4usb.pro +++ b/gpg4usb.pro @@ -71,7 +71,10 @@ HEADERS += attachments.h \ kgpg/transactions/kgpgimport.h \ kgpg/transactions/kgpgdelkey.h \ kgpg/transactions/kgpggeneratekey.h \ - kgpg/transactions/kgpgsigntext.h + kgpg/transactions/kgpgsigntext.h \ + kgpg/transactions/kgpgverify.h \ + #kgpg/model/kgpgitemmodel.h \ + #kgpg/model/kgpgitemnode.h SOURCES += attachments.cpp \ @@ -126,7 +129,9 @@ SOURCES += attachments.cpp \ kgpg/transactions/kgpgimport.cpp \ kgpg/transactions/kgpgdelkey.cpp \ kgpg/transactions/kgpggeneratekey.cpp \ - kgpg/transactions/kgpgsigntext.cpp + kgpg/transactions/kgpgsigntext.cpp \ + kgpg/transactions/kgpgverify.cpp \ + #kgpg/model/kgpgitemmodel.cpp RC_FILE = gpg4usb.rc diff --git a/kgpg/transactions/kgpgverify.cpp b/kgpg/transactions/kgpgverify.cpp new file mode 100644 index 0000000..e9dbb36 --- /dev/null +++ b/kgpg/transactions/kgpgverify.cpp @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2012 Rolf Eike Beer <[email protected]> + */ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "kgpgverify.h" + +#include "../gpgproc.h" +#include "../core/KGpgKeyNode.h" +//#include "../model/kgpgitemmodel.h" + +//#include <KGlobal> +//#include <KLocale> + +KGpgVerify::KGpgVerify(QObject *parent, const QString &text) + : KGpgTextOrFileTransaction(parent, text), + m_fileIndex(-1) +{ +} + +KGpgVerify::KGpgVerify(QObject *parent, const QList<QUrl> &files) + : KGpgTextOrFileTransaction(parent, files), + m_fileIndex(0) +{ +} + +KGpgVerify::~KGpgVerify() +{ +} + +QStringList +KGpgVerify::command() const +{ + QStringList ret(QLatin1String("--verify")); + + return ret; +} + +bool +KGpgVerify::nextLine(const QString &line) +{ + if (line.startsWith(QLatin1String("[GNUPG:] NO_PUBKEY "))) { + setSuccess(TS_MISSING_KEY); + m_missingId = line.mid(19).simplified(); + return false; + } + + if (line.startsWith(QLatin1String("[GNUPG:] ")) && + line.contains(QLatin1String("SIG"))) { + if (line.startsWith(QLatin1String("[GNUPG:] BADSIG"))) + setSuccess(KGpgVerify::TS_BAD_SIGNATURE); + else + setSuccess(KGpgTransaction::TS_OK); + } + + return KGpgTextOrFileTransaction::nextLine(line); +} + +void +KGpgVerify::finish() +{ + // GnuPG will return error code 2 if it wasn't able to verify the file. + // If it complained about a missing signature before that is fine. + if (((getProcess()->exitCode() == 2) && (getSuccess() == TS_MISSING_KEY)) || + ((getProcess()->exitCode() == 1) && (getSuccess() == TS_BAD_SIGNATURE))) + return; + + KGpgTextOrFileTransaction::finish(); +} + +static QString +sigTimeMessage(const QString &sigtime) +{ + QDateTime stamp; + if (sigtime.contains(QLatin1Char('T'))) { + stamp = QDateTime::fromString(sigtime, Qt::ISODate); + } else { + bool ok; + qint64 secs = sigtime.toLongLong(&ok); + if (ok) + stamp = QDateTime::fromMSecsSinceEpoch(secs * 1000); + } + + if (!stamp.isValid()) + return QString(); + +/* return tr("first argument is formatted date, second argument is formatted time", + "The signature was created at %1 %2", + KGlobal::locale()->formatDate(stamp.date(), KLocale::LongDate), + KGlobal::locale()->formatTime(stamp.time(), KLocale::LongDate)) + + QLatin1String("<br/>");*/ + return "todo"; +} + +/* +QString +KGpgVerify::getReport(const QStringList &log, const KGpgItemModel *model) +{ + QString result; + // newer versions of GnuPG emit both VALIDSIG and GOODSIG + // for a good signature. Since VALIDSIG has more information + // we use that. + const QRegExp validsig(QLatin1String("^\\[GNUPG:\\] VALIDSIG([ ]+[^ ]+){10,}.*$")); + const bool useGoodSig = (model != NULL) && (log.indexOf(validsig) == -1); + QString sigtime; // timestamp of signature creation + + foreach (const QString &line, log) { + if (!line.startsWith(QLatin1String("[GNUPG:] "))) + continue; + + const QString msg = line.mid(9); + + if (msg.startsWith(QLatin1String("VALIDSIG ")) && !useGoodSig) { + // from GnuPG source, doc/DETAILS: + // VALIDSIG <fingerprint in hex> <sig_creation_date> <sig-timestamp> + // <expire-timestamp> <sig-version> <reserved> <pubkey-algo> + // <hash-algo> <sig-class> <primary-key-fpr> + const QStringList vsig = msg.mid(9).split(QLatin1Char(' '), QString::SkipEmptyParts); + Q_ASSERT(vsig.count() >= 10); + + const KGpgKeyNode *node = model->findKeyNode(vsig[9]); + + if (node != NULL) { + // ignore for now if this is signed with the primary id (vsig[0] == vsig[9]) or not + if (node->getEmail().isEmpty()) + result += tr("<qt>Good signature from:<br /><b>%1</b><br />Key ID: %2<br /></qt>") + .arg(node->getName()).arg(vsig[9]); + else + result += tr("Good signature from: NAME <EMAIL>, Key ID: HEXID", + "<qt>Good signature from:<br /><b>%1 <%2></b><br />Key ID: %3<br /></qt>") + .arg(node->getName()).arg(node->getEmail()).arg(vsig[9]); + + result += sigTimeMessage(vsig[2]); + } else { + // this should normally never happen, but one could delete + // the key just after the verification. Brute force solution: + // do the whole report generation again, but this time make + // sure GOODSIG is used. + return getReport(log, NULL); + } + } else if (msg.startsWith(QLatin1String("UNEXPECTED")) || + msg.startsWith(QLatin1String("NODATA"))) { + result += tr("No signature found.") + QLatin1Char('\n'); + } else if (useGoodSig && msg.startsWith(QLatin1String("GOODSIG "))) { + int sigpos = msg.indexOf( ' ' , 8); + const QString keyid = msg.mid(8, sigpos - 8); + + // split the name/email pair to give translators more power to handle this + QString email; + QString name = msg.mid(sigpos + 1); + + int oPos = name.indexOf(QLatin1Char('<')); + int cPos = name.indexOf(QLatin1Char('>')); + if ((oPos >= 0) && (cPos >= 0)) { + email = name.mid(oPos + 1, cPos - oPos - 1); + name = name.left(oPos).simplified(); + } + + if (email.isEmpty()) + result += tr("<qt>Good signature from:<br /><b>%1</b><br />Key ID: %2<br /></qt>") + .arg(name).arg(keyid); + else + result += tr("Good signature from: NAME <EMAIL>, Key ID: HEXID", + "<qt>Good signature from:<br /><b>%1 <%2></b><br />Key ID: %3<br /></qt>") + .arg(name).arg(email).arg(keyid); + if (!sigtime.isEmpty()) { + result += sigTimeMessage(sigtime); + sigtime.clear(); + } + } else if (msg.startsWith(QLatin1String("SIG_ID "))) { + const QStringList parts = msg.simplified().split(QLatin1Char(' ')); + if (parts.count() > 2) + sigtime = parts[2]; + } else if (msg.startsWith(QLatin1String("BADSIG"))) { + int sigpos = msg.indexOf( ' ', 7); + result += tr("<qt><b>BAD signature</b> from:<br /> %1<br />Key id: %2<br /><br /><b>The file is corrupted</b><br /></qt>") + .arg(msg.mid(sigpos + 1).replace(QLatin1Char('<'), QLatin1String("<"))) + .arg(msg.mid(7, sigpos - 7)); + } else if (msg.startsWith(QLatin1String("TRUST_UNDEFINED"))) { + result += tr("<qt>The signature is valid, but the key is untrusted<br /></qt>"); + } else if (msg.startsWith(QLatin1String("TRUST_ULTIMATE"))) { + result += tr("<qt>The signature is valid, and the key is ultimately trusted<br /></qt>"); + } + } + + return result; +} +*/ + +QString +KGpgVerify::missingId() const +{ + return m_missingId; +} + +//#include "kgpgverify.moc" diff --git a/kgpg/transactions/kgpgverify.h b/kgpg/transactions/kgpgverify.h new file mode 100644 index 0000000..beba12f --- /dev/null +++ b/kgpg/transactions/kgpgverify.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2012 Rolf Eike Beer <[email protected]> + */ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KGPGVERIFY_H +#define KGPGVERIFY_H + +#include "kgpgtextorfiletransaction.h" + +#include <QObject> +#include <QString> +#include <QStringList> + +#include <QUrl> + +//class KGpgItemModel; +class QProcess; + +/** + * @brief verify the signature of the given text or files + */ +class KGpgVerify: public KGpgTextOrFileTransaction { + Q_OBJECT + + Q_DISABLE_COPY(KGpgVerify) + KGpgVerify(); // = delete C++0x +public: + enum ts_verify { + TS_MISSING_KEY = KGpgTransaction::TS_COMMON_END + 1, ///< signing key not in keyring + TS_BAD_SIGNATURE = TS_MISSING_KEY + 1 ///< the file is signed, but the signature is invalid + }; + + /** + * @brief verify signature of given text + * @param parent parent object + * @param text text to verify + */ + explicit KGpgVerify(QObject *parent, const QString &text = QString()); + + /** + * @brief verify signatures of file(s) + * @param parent parent object + * @param files list of file locations to verify + */ + KGpgVerify(QObject *parent, const QList<QUrl> &files); + + /** + * @brief destructor + */ + virtual ~KGpgVerify(); + + /** + * @brief get verification report + * @param log the log lines to scan + * @param model key model to use for key lookups + * @return verification report of GnuPG + */ + //static QString getReport(const QStringList &log, const KGpgItemModel *model = NULL); + + /** + * @brief get the missing key id + * @return key id that signed the message + * + * This is only valid if the transaction returned with result + * TS_MISSING_KEY. + */ + QString missingId() const; + +protected: + virtual QStringList command() const; + virtual bool nextLine(const QString &line); + virtual void finish(); + +private: + int m_fileIndex; + QString m_currentFile; + QStringList m_report; + QString m_missingId; +}; + +#endif // KGPGVERIFY_H diff --git a/mainwindow.cpp b/mainwindow.cpp index d8e6588..bda6a96 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -918,24 +918,10 @@ void MainWindow::slotSignDone(int result) //return; qDebug() << "Signing not possible: bad passphrase or missing key"; return; - } else { - edit->fillTextEditWithText(signt->signedText().join(QLatin1String("\n")) + QLatin1String("\n")); - } - - //signt->deleteLater(); - //const QString content = signt->signedText().join(QLatin1String("\n")) + QLatin1String("\n"); - //setPlainText(content); - //emit resetEncoding(false); - /*sender()->deleteLater(); - - if (text.isEmpty()) - { - qDebug() << "Signing not possible: bad passphrase or missing key"; - return; } edit->fillTextEditWithText(signt->signedText().join(QLatin1String("\n")) + QLatin1String("\n")); - */ + } void MainWindow::decrypt() diff --git a/verifynotification.cpp b/verifynotification.cpp index 3e056b9..9d9a0d9 100644 --- a/verifynotification.cpp +++ b/verifynotification.cpp @@ -98,8 +98,14 @@ bool VerifyNotification::refresh() mCtx->preventNoDataErr(&text); int textIsSigned = mCtx->textIsSigned(text); - gpgme_signature_t sign = mCtx->verify(text); + //gpgme_signature_t sign = mCtx->verify(text); + //KGpgVerify *verify = new KGpgVerify(this, message.mid(posstart, posend - posstart)); + KGpgVerify *verify = new KGpgVerify(this, text); + connect(verify, SIGNAL(done(int)), SLOT(slotVerifyDone(int))); + verify->start(); + + /* if (sign == NULL) { return false; } @@ -178,6 +184,156 @@ bool VerifyNotification::refresh() verifyLabelText.remove(verifyLabelText.length()-1,1); this->setVerifyLabel(verifyLabelText,verifyStatus); - + */ return true; } + +void VerifyNotification::slotVerifyDone(int result) +{ + const KGpgVerify * const verify = qobject_cast<KGpgVerify *>(sender()); + sender()->deleteLater(); + Q_ASSERT(verify != NULL); + + if (result == KGpgVerify::TS_MISSING_KEY) { + qDebug() << "missing keys" << verify->missingId(); + this->keysNotInList->append(verify->missingId()); + this->showImportAction(true); + } + + const QStringList messages = verify->getMessages(); + foreach(QString mess, messages) { + qDebug() << "vm: " << mess; + } + + getReport(messages); + /*emit verifyFinished(); + + if (result == KGpgVerify::TS_MISSING_KEY) { + verifyKeyNeeded(verify->missingId()); + return; + } + + const QStringList messages = verify->getMessages(); + + if (messages.isEmpty()) + return; + + QStringList msglist; + foreach (QString rawmsg, messages) + msglist << rawmsg.replace(QLatin1Char('<'), QLatin1String("<")); + + (void) new KgpgDetailedInfo(this, KGpgVerify::getReport(messages, m_model), + msglist.join(QLatin1String("<br/>")), + QStringList(), i18nc("Caption of message box", "Verification Finished")); + */ +} + +QString VerifyNotification::getReport(const QStringList &log) +{ + QString verifyLabelText; + verify_label_status verifyStatus=VERIFY_ERROR_OK; + + QString result; + // newer versions of GnuPG emit both VALIDSIG and GOODSIG + // for a good signature. Since VALIDSIG has more information + // we use that. + const QRegExp validsig(QLatin1String("^\\[GNUPG:\\] VALIDSIG([ ]+[^ ]+){10,}.*$")); + + +// const bool useGoodSig = (model != NULL) && (log.indexOf(validsig) == -1); + const bool useGoodSig = false; + + QString sigtime; // timestamp of signature creation + + foreach (const QString &line, log) { + if (!line.startsWith(QLatin1String("[GNUPG:] "))) + continue; + + const QString msg = line.mid(9); + + if (msg.startsWith(QLatin1String("VALIDSIG ")) && !useGoodSig) { + // from GnuPG source, doc/DETAILS: + // VALIDSIG <fingerprint in hex> <sig_creation_date> <sig-timestamp> + // <expire-timestamp> <sig-version> <reserved> <pubkey-algo> + // <hash-algo> <sig-class> <primary-key-fpr> + const QStringList vsig = msg.mid(9).split(QLatin1Char(' '), QString::SkipEmptyParts); + Q_ASSERT(vsig.count() >= 10); + + qDebug() << "sig: " << vsig[9]; + + verifyStatus=VERIFY_ERROR_WARN; + verifyLabelText.append(tr("Error for key with fingerprint ")+mCtx->beautifyFingerprint(QString(vsig[9]))); + + + } + } + + this->setVerifyLabel(verifyLabelText,verifyStatus); + /*const KGpgKeyNode *node = model->findKeyNode(vsig[9]); + + if (node != NULL) { + // ignore for now if this is signed with the primary id (vsig[0] == vsig[9]) or not + if (node->getEmail().isEmpty()) + result += tr("<qt>Good signature from:<br /><b>%1</b><br />Key ID: %2<br /></qt>") + .arg(node->getName()).arg(vsig[9]); + else + result += tr("Good signature from: NAME <EMAIL>, Key ID: HEXID", + "<qt>Good signature from:<br /><b>%1 <%2></b><br />Key ID: %3<br /></qt>") + .arg(node->getName()).arg(node->getEmail()).arg(vsig[9]); + + result += sigTimeMessage(vsig[2]); + } else { + // this should normally never happen, but one could delete + // the key just after the verification. Brute force solution: + // do the whole report generation again, but this time make + // sure GOODSIG is used. + return getReport(log, NULL); + } + } else if (msg.startsWith(QLatin1String("UNEXPECTED")) || + msg.startsWith(QLatin1String("NODATA"))) { + result += tr("No signature found.") + QLatin1Char('\n'); + } else if (useGoodSig && msg.startsWith(QLatin1String("GOODSIG "))) { + int sigpos = msg.indexOf( ' ' , 8); + const QString keyid = msg.mid(8, sigpos - 8); + + // split the name/email pair to give translators more power to handle this + QString email; + QString name = msg.mid(sigpos + 1); + + int oPos = name.indexOf(QLatin1Char('<')); + int cPos = name.indexOf(QLatin1Char('>')); + if ((oPos >= 0) && (cPos >= 0)) { + email = name.mid(oPos + 1, cPos - oPos - 1); + name = name.left(oPos).simplified(); + } + + if (email.isEmpty()) + result += tr("<qt>Good signature from:<br /><b>%1</b><br />Key ID: %2<br /></qt>") + .arg(name).arg(keyid); + else + result += tr("Good signature from: NAME <EMAIL>, Key ID: HEXID", + "<qt>Good signature from:<br /><b>%1 <%2></b><br />Key ID: %3<br /></qt>") + .arg(name).arg(email).arg(keyid); + if (!sigtime.isEmpty()) { + result += sigTimeMessage(sigtime); + sigtime.clear(); + } + } else if (msg.startsWith(QLatin1String("SIG_ID "))) { + const QStringList parts = msg.simplified().split(QLatin1Char(' ')); + if (parts.count() > 2) + sigtime = parts[2]; + } else if (msg.startsWith(QLatin1String("BADSIG"))) { + int sigpos = msg.indexOf( ' ', 7); + result += tr("<qt><b>BAD signature</b> from:<br /> %1<br />Key id: %2<br /><br /><b>The file is corrupted</b><br /></qt>") + .arg(msg.mid(sigpos + 1).replace(QLatin1Char('<'), QLatin1String("<"))) + .arg(msg.mid(7, sigpos - 7)); + } else if (msg.startsWith(QLatin1String("TRUST_UNDEFINED"))) { + result += tr("<qt>The signature is valid, but the key is untrusted<br /></qt>"); + } else if (msg.startsWith(QLatin1String("TRUST_ULTIMATE"))) { + result += tr("<qt>The signature is valid, and the key is ultimately trusted<br /></qt>"); + } + } + */ + return result; +} + diff --git a/verifynotification.h b/verifynotification.h index e1b4f1b..6f34f47 100644 --- a/verifynotification.h +++ b/verifynotification.h @@ -24,6 +24,7 @@ #include "editorpage.h" #include "verifydetailsdialog.h" +#include "kgpg/transactions/kgpgverify.h" #include <gpgme.h> #include <QWidget> @@ -75,6 +76,8 @@ public: QStringList *keysNotInList; /** List with keys, which are in signature but not in keylist */ + QString getReport(const QStringList &log); + public slots: /** @@ -93,6 +96,8 @@ public slots: */ bool refresh(); + void slotVerifyDone(int result); + private: QMenu *detailMenu; /** Menu for te Button in verfiyNotification */ QAction *importFromKeyserverAct; /** Action for importing keys from keyserver which are notin keylist */ |