summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndre Heinecke <[email protected]>2017-03-28 11:37:54 +0200
committerAndre Heinecke <[email protected]>2017-03-28 11:42:41 +0200
commitcf3385489036fe25e258d3f5f4cd61589a207b9f (patch)
tree64a0997fad4ab3eca63193e312282e36fb3ebc40
parenta56fc9587a51bf35947180b6051094982947d33b (diff)
Improve decrypt verify result display
This improves the look and information of the result status display when verifying files. The keys are now also fetched through GPGME and not over the keycache to ensure that tofu information is correct (if this is used). This also fixes a Bug because previously signings subkeys (like the one used to sign this commit) were not handled by kleopatra's sig key lookup. Although it's a bugfix it should stay in master because of the string changes. BUG: 340144
-rw-r--r--src/crypto/decryptverifytask.cpp140
-rw-r--r--src/crypto/decryptverifytask.h2
-rw-r--r--src/uiserver/decryptverifycommandemailbase.cpp3
-rw-r--r--src/uiserver/decryptverifycommandfilesbase.cpp3
4 files changed, 68 insertions, 80 deletions
diff --git a/src/crypto/decryptverifytask.cpp b/src/crypto/decryptverifytask.cpp
index a4de426..ee95f6b 100644
--- a/src/crypto/decryptverifytask.cpp
+++ b/src/crypto/decryptverifytask.cpp
@@ -71,6 +71,7 @@
#include <QDateTime>
#include <QStringList>
#include <QTextDocument> // Qt::escape
+#include <QRegularExpression>
#include <algorithm>
#include <cassert>
@@ -201,20 +202,14 @@ static QString signatureSummaryToString(int summary)
return i18n("Error: Signature not verified");
} else if (summary & Signature::Valid || summary & Signature::Green) {
return i18n("Good signature");
- } else if (summary & Signature::Red) {
- return i18n("Bad signature");
} else if (summary & Signature::KeyRevoked) {
- return i18n("Signing certificate revoked");
+ return i18n("Signing certificate was revoked");
} else if (summary & Signature::KeyExpired) {
- return i18n("Signing certificate expired");
+ return i18n("Signing certificate is expired");
} else if (summary & Signature::KeyMissing) {
- return i18n("No public certificate to verify the signature");
+ return i18n("Certificate is not available");
} else if (summary & Signature::SigExpired) {
return i18n("Signature expired");
-#if 0 //Duplicate entry
- } else if (summary & Signature::KeyMissing) {
- return i18n("Certificate missing");
-#endif
} else if (summary & Signature::CrlMissing) {
return i18n("CRL missing");
} else if (summary & Signature::CrlTooOld) {
@@ -223,8 +218,9 @@ static QString signatureSummaryToString(int summary)
return i18n("Bad policy");
} else if (summary & Signature::SysError) {
return i18n("System error"); //### retrieve system error details?
- }
- return QString();
+ } else if (summary & Signature::Red) {
+ return i18n("Bad signature");
+ }return QString();
}
static QString formatValidSignatureWithTrustLevel(const UserID &id)
@@ -254,7 +250,7 @@ static QString renderFingerprint(const char *fpr)
if (!fpr) {
return QString();
}
- return QStringLiteral("0x%1").arg(QString::fromLatin1(fpr).toUpper());
+ return QString::fromLatin1(fpr).toUpper().replace(QRegularExpression("(....)"), "\\1 ").trimmed();
}
static QString renderKeyLink(const QString &fpr, const QString &text)
@@ -267,7 +263,7 @@ static QString renderKey(const Key &key)
if (key.isNull()) {
return i18n("Unknown certificate");
}
- return renderKeyLink(QLatin1String(key.primaryFingerprint()), Formatting::prettyName(key));
+ return renderKeyLink(QLatin1String(key.primaryFingerprint()), renderFingerprint(key.primaryFingerprint()));
}
static QString renderKeyEMailOnlyNameAsFallback(const Key &key)
@@ -284,39 +280,21 @@ static QString formatDate(const QDateTime &dt)
{
return QLocale().toString(dt);
}
-static QString formatSigningInformation(const Signature &sig, const Key &key)
+static QString formatSigningInformation(const Signature &sig)
{
if (sig.isNull()) {
return QString();
}
const QDateTime dt = sig.creationTime() != 0 ? QDateTime::fromTime_t(sig.creationTime()) : QDateTime();
- const QString signer = key.isNull() ? QString() : renderKeyEMailOnlyNameAsFallback(key);
- const bool haveKey = !key.isNull();
- const bool haveSigner = !signer.isEmpty();
- const bool haveDate = dt.isValid();
- if (!haveKey) {
- if (haveDate) {
- return i18n("Signed on %1 with unknown certificate %2.", formatDate(dt), renderFingerprint(sig.fingerprint()));
- } else {
- return i18n("Signed with unknown certificate %1.", renderFingerprint(sig.fingerprint()));
- }
- }
- if (haveSigner) {
- if (haveDate)
- return i18nc("date, key owner, key ID",
- "Signed on %1 by %2 (Key ID: %3).",
- formatDate(dt),
- signer,
- renderFingerprint(key.shortKeyID()));
- else {
- return i18n("Signed by %1 with certificate %2.", signer, renderKey(key));
- }
+ QString text;
+ Key key = sig.key();
+ if (dt.isValid()) {
+ text = i18nc("1 is a date", "Signature created on %1", formatDate(dt)) + QStringLiteral("<br>");
}
- if (haveDate) {
- return i18n("Signed on %1 with certificate %2.", formatDate(dt), renderKey(key));
+ if (key.isNull()) {
+ return text += i18n("With unavilable certificate:") + QStringLiteral("<br>ID: 0x%1").arg(QString::fromLatin1(sig.fingerprint()).toUpper());
}
- return i18n("Signed with certificate %1.", renderKey(key));
-
+ return text += i18n("With certificate:") + QStringLiteral("<br>") + renderKey(key);
}
static QString strikeOut(const QString &str, bool strike)
@@ -363,6 +341,17 @@ static UserID findUserIDByMailbox(const Key &key, const Mailbox &mbox)
return UserID();
}
+static void updateKeys(const VerificationResult &result) {
+ // This little hack works around the problem that GnuPG / GpgME does not
+ // provide Key information in a verification result. The Key object is
+ // a dummy just holding the KeyID. This hack ensures that all available
+ // keys are fetched from the backend and are populated
+ for (const auto sig: result.signatures()) {
+ // Update key information
+ sig.key(true, true);
+ }
+}
+
}
class DecryptVerifyResult::SenderInfo
@@ -429,7 +418,6 @@ static QString formatVerificationResultOverview(const VerificationResult &res, c
}
const std::vector<Signature> sigs = res.signatures();
- const std::vector<Key> signers = info.signers;
if (sigs.empty()) {
return i18n("<b>No signatures found.</b>");
@@ -440,21 +428,17 @@ static QString formatVerificationResultOverview(const VerificationResult &res, c
return i18np("<b>Invalid signature.</b>", "<b>%1 invalid signatures.</b>", bad);
}
const uint warn = std::count_if(sigs.cbegin(), sigs.cend(), [](const Signature &sig) { return !IsGoodOrValid(sig); });
- if (warn > 0) {
- return i18np("<b>Not enough information to check signature validity.</b>", "<b>%1 signatures could not be verified.</b>", warn);
+ if (warn == sigs.size()) {
+ return i18np("<b>The data could not be verified.</b>", "<b>%1 signatures could not be verified.</b>", warn);
}
//Good signature:
QString text;
if (sigs.size() == 1) {
- const Key key = DecryptVerifyResult::keyForSignature(sigs[0], signers);
- if (key.isNull()) {
- return i18n("<b>Signature is valid.</b>");
- }
- text = i18n("<b>Signed by %1</b>", renderKeyEMailOnlyNameAsFallback(key));
+ text = i18n("<b>Valid signature by %1</b>", renderKeyEMailOnlyNameAsFallback(sigs[0].key()));
if (info.conflicts())
text += i18n("<br/><b>Warning:</b> The sender's mail address is not stored in the %1 used for signing.",
- renderKeyLink(QLatin1String(key.primaryFingerprint()), i18n("certificate")));
+ renderKeyLink(QLatin1String(sigs[0].key().primaryFingerprint()), i18n("certificate")));
} else {
text = i18np("<b>Valid signature.</b>", "<b>%1 valid signatures.</b>", sigs.size());
if (info.conflicts()) {
@@ -479,25 +463,40 @@ static QString formatDecryptionResultOverview(const DecryptionResult &result, co
return i18n("<b>Decryption succeeded.</b>");
}
-static QString formatSignature(const Signature &sig, const Key &key, const DecryptVerifyResult::SenderInfo &info)
+static QString formatSignature(const Signature &sig, const DecryptVerifyResult::SenderInfo &info)
{
if (sig.isNull()) {
return QString();
}
- const QString text = formatSigningInformation(sig, key) + QLatin1String("<br/>");
+ const QString text = formatSigningInformation(sig) + QLatin1String("<br/>");
+ const Key key = sig.key();
- const bool red = sig.summary() & Signature::Red;
+ // Green
if (sig.summary() & Signature::Valid) {
const UserID id = findUserIDByMailbox(key, info.informativeSender);
return text + formatValidSignatureWithTrustLevel(!id.isNull() ? id : key.userID(0));
}
- if (red) {
- return text + i18n("The signature is bad.");
+
+ // Red
+ if ((sig.summary() & Signature::Red)) {
+ return text + i18n("The signature is invalid: %1", signatureSummaryToString(sig.summary()));
}
- if (!sig.summary()) {
- return text + i18n("The validity of the signature cannot be verified.");
+
+ // Key missing
+ if ((sig.summary() & Signature::KeyMissing)) {
+ return text + i18n("You can search the certificate on a keyserver or import it from a file.");
+ }
+
+ // Yellow
+ if ((sig.validity() & Signature::Validity::Undefined) ||
+ (sig.validity() & Signature::Validity::Unknown) ||
+ (sig.summary() == Signature::Summary::None)) {
+ return text + (key.protocol() == OpenPGP ? i18n("The used key is not certified by you or any trusted person.") :
+ i18n("The used certificate is not certified by a trustworthy Certificate Authority or the Certificate Authority is unknown."));
}
+
+ // Catch all fall through
return text + i18n("The signature is invalid: %1", signatureSummaryToString(sig.summary()));
}
@@ -518,13 +517,12 @@ static QString formatVerificationResultDetails(const VerificationResult &res, co
}
const std::vector<Signature> sigs = res.signatures();
- const std::vector<Key> signers = KeyCache::instance()->findSigners(res);
QString details;
for (const Signature &sig : sigs) {
- details += formatSignature(sig, DecryptVerifyResult::keyForSignature(sig, signers), info) + QLatin1Char('\n');
+ details += formatSignature(sig, info) + QLatin1Char('\n');
}
details = details.trimmed();
- details.replace(QLatin1Char('\n'), QStringLiteral("<br/>"));
+ details.replace(QLatin1Char('\n'), QStringLiteral("<br/><br/>"));
if (info.conflicts()) {
details += i18n("<p>The sender's address %1 is not stored in the certificate. Stored: %2</p>", info.informativeSender.prettyAddress(), format(info.signerMailboxes()).join(i18nc("separator for a list of e-mail addresses", ", ")));
}
@@ -805,11 +803,15 @@ QString DecryptVerifyResult::overview() const
{
QString ov;
if (d->isDecryptOnly()) {
- ov = formatDecryptionResultOverview(d->m_decryptionResult);
+ ov += formatDecryptionResultOverview(d->m_decryptionResult);
} else if (d->isVerifyOnly()) {
- ov = formatVerificationResultOverview(d->m_verificationResult, d->makeSenderInfo());
+ ov += formatVerificationResultOverview(d->m_verificationResult, d->makeSenderInfo());
} else {
- ov = formatDecryptVerifyResultOverview(d->m_decryptionResult, d->m_verificationResult, d->makeSenderInfo());
+ ov += formatDecryptVerifyResultOverview(d->m_decryptionResult, d->m_verificationResult, d->makeSenderInfo());
+ }
+ if (ov.size() + d->label().size() > 120) {
+ // Avoid ugly breaks
+ ov = QStringLiteral("<br>") + ov;
}
return i18nc("label: result example: foo.sig: Verification failed. ", "%1: %2", d->label(), ov);
}
@@ -865,19 +867,6 @@ GpgME::VerificationResult DecryptVerifyResult::verificationResult() const
return d->m_verificationResult;
}
-const Key &DecryptVerifyResult::keyForSignature(const Signature &sig, const std::vector<Key> &keys)
-{
- if (const char *const fpr = sig.fingerprint()) {
- const std::vector<Key>::const_iterator it
- = std::lower_bound(keys.begin(), keys.end(), fpr, _detail::ByFingerprint<std::less>());
- if (it != keys.end() && _detail::ByFingerprint<std::equal_to>()(*it, fpr)) {
- return *it;
- }
- }
- static const Key null;
- return null;
-}
-
class AbstractDecryptVerifyTask::Private
{
public:
@@ -930,6 +919,7 @@ void DecryptVerifyTask::Private::emitResult(const std::shared_ptr<DecryptVerifyR
void DecryptVerifyTask::Private::slotResult(const DecryptionResult &dr, const VerificationResult &vr, const QByteArray &plainText)
{
+ updateKeys(vr);
{
std::stringstream ss;
ss << dr << '\n' << vr;
@@ -1250,6 +1240,7 @@ void VerifyOpaqueTask::Private::emitResult(const std::shared_ptr<DecryptVerifyRe
void VerifyOpaqueTask::Private::slotResult(const VerificationResult &result, const QByteArray &plainText)
{
+ updateKeys(result);
{
std::stringstream ss;
ss << result;
@@ -1404,6 +1395,7 @@ void VerifyDetachedTask::Private::emitResult(const std::shared_ptr<DecryptVerify
void VerifyDetachedTask::Private::slotResult(const VerificationResult &result)
{
+ updateKeys(result);
{
std::stringstream ss;
ss << result;
diff --git a/src/crypto/decryptverifytask.h b/src/crypto/decryptverifytask.h
index a7c05e1..eb79a06 100644
--- a/src/crypto/decryptverifytask.h
+++ b/src/crypto/decryptverifytask.h
@@ -247,8 +247,6 @@ public:
GpgME::VerificationResult verificationResult() const;
- static const GpgME::Key &keyForSignature(const GpgME::Signature &sig, const std::vector<GpgME::Key> &keys);
-
private:
static QString keyToString(const GpgME::Key &key);
diff --git a/src/uiserver/decryptverifycommandemailbase.cpp b/src/uiserver/decryptverifycommandemailbase.cpp
index 5e4544c..86eaf7d 100644
--- a/src/uiserver/decryptverifycommandemailbase.cpp
+++ b/src/uiserver/decryptverifycommandemailbase.cpp
@@ -225,9 +225,8 @@ void DecryptVerifyCommandEMailBase::Private::verificationResult(const Verificati
{
try {
const std::vector<Signature> sigs = vResult.signatures();
- const std::vector<Key> signers = KeyCache::instance()->findSigners(vResult);
Q_FOREACH (const Signature &sig, sigs) {
- const QString s = signatureToString(sig, DecryptVerifyResult::keyForSignature(sig, signers));
+ const QString s = signatureToString(sig, sig.key(true, true));
const char *color = summaryToString(sig.summary());
q->sendStatusEncoded("SIGSTATUS",
color + (' ' + hexencode(s.toUtf8().constData())));
diff --git a/src/uiserver/decryptverifycommandfilesbase.cpp b/src/uiserver/decryptverifycommandfilesbase.cpp
index 2f1f732..fae91db 100644
--- a/src/uiserver/decryptverifycommandfilesbase.cpp
+++ b/src/uiserver/decryptverifycommandfilesbase.cpp
@@ -205,9 +205,8 @@ void DecryptVerifyCommandFilesBase::Private::verificationResult(const Verificati
{
try {
const std::vector<Signature> sigs = vResult.signatures();
- const std::vector<Key> signers = KeyCache::instance()->findSigners(vResult);
for (const Signature &sig : sigs) {
- const QString s = signatureToString(sig, DecryptVerifyResult::keyForSignature(sig, signers));
+ const QString s = signatureToString(sig, sig.key(true, true));
const char *color = summaryToString(sig.summary());
q->sendStatusEncoded("SIGSTATUS",
color + (' ' + hexencode(s.toUtf8().constData())));