[pim/libkleo] /: Add more detailed explanations and guidance for not fully valid signatures
Ingo Klöcker <[email protected]>
| Newsgroups | gmane.comp.kde.cvs |
|---|---|
| Message-ID | <[email protected]> |
Git commit a9c1ad6b7ac93c1cbf3e8ee7057dd5fbaaf2bbf6 by Ingo Klöcker, on behalf of Ingo Klöcker.
Committed on 18/08/2026 at 09:29.
Pushed by kloecker into branch 'master'.
Add more detailed explanations and guidance for not fully valid signatures
The idea is to show these texts on demand to further explain the text
returned by prettyDataSignature.
GnuPG-bug-id: 8336
M +107 -0 autotests/formattingtest.cpp
M +80 -0 src/utils/formatting.cpp
M +15 -0 src/utils/formatting.h
https://invent.kde.org/pim/libkleo/-/commit/a9c1ad6b7ac93c1cbf3e8ee7057dd5fbaaf2bbf6
diff --git a/autotests/formattingtest.cpp b/autotests/formattingtest.cpp
index ef622f20..0413eebb 100644
--- a/autotests/formattingtest.cpp
+++ b/autotests/formattingtest.cpp
@@ -625,6 +625,113 @@ private Q_SLOTS:
expected.replace(u"COMPLIANCE"_s, DeVSCompliance::name(true));
QCOMPARE(maskDateAndTime(result), expected);
}
+
+ void test_explanationsForDataSignature_data()
+ {
+ QTest::addColumn<Kleo::SignatureStatus>("signatureStatus");
+ QTest::addColumn<QStringList>("expected");
+
+ QTest::newRow("NoSignature") << SignatureStatus::NoSignature << QStringList{};
+ QTest::newRow("KeyMissing") //
+ << SignatureStatus::KeyMissing
+ << QStringList{u"The signing certificate is not present in your certificate list, but it is needed to verify the data."_s};
+ QTest::newRow("ValidAndFullyTrusted") << SignatureStatus::ValidAndFullyTrusted << QStringList{};
+ QTest::newRow("ValidButNotFullyTrusted") //
+ << SignatureStatus::ValidButNotFullyTrusted
+ << QStringList{
+ u"Technically, signature and data match, but the signing certificate is not marked as trusted. "
+ "Therefore the data cannot be trusted to originate from the stated source."_s};
+ QTest::newRow("ValidButSignatureExpired") << SignatureStatus::ValidButSignatureExpired << QStringList{};
+ QTest::newRow("ValidButKeyExpired") //
+ << SignatureStatus::ValidButKeyExpired
+ << QStringList{
+ u"For an expired certificate, it cannot be evaluated whether the certificate can be trusted. "
+ "Therefore the data cannot be trusted. Technically, signature and data match."_s,
+ u"If the certificate was valid and trusted when you received the data, the data is likely valid."_s};
+ QTest::newRow("ValidButKeyRevoked") //
+ << SignatureStatus::ValidButKeyRevoked
+ << QStringList{
+ u"The certificate may have been revoked because it was compromised and it might now be used by a third party. "
+ "The data can therefore not be trusted. Technically, signature and data match."_s,
+ u"It is possible that you received the data at a time when the certificate was still valid and trusted. "
+ "If this is the case, the data may be valid."_s};
+ QTest::newRow("ValidButSignerUntrustworthy") << SignatureStatus::ValidButSignerUntrustworthy << QStringList{};
+ QTest::newRow("Invalid") //
+ << SignatureStatus::Invalid
+ << QStringList{
+ u"The data or the signature has been altered. This can happen accidentally (e.g. due to a transmission error), "
+ "unintentionally (e.g. due to a subsequent change to the data, possibly by an email client), or intentionally "
+ "(deliberate manipulation)."_s};
+ QTest::newRow("OtherError") << SignatureStatus::OtherError << QStringList{};
+ }
+
+ void test_explanationsForDataSignature()
+ {
+ QFETCH(Kleo::SignatureStatus, signatureStatus);
+ QFETCH(QStringList, expected);
+
+ const QStringList result = Formatting::explanationsForDataSignature(signatureStatus);
+ QCOMPARE(result, expected);
+ }
+
+ void test_guidanceForDataSignature_data()
+ {
+ QTest::addColumn<Kleo::SignatureStatus>("signatureStatus");
+ QTest::addColumn<GpgME::Protocol>("protocol");
+ QTest::addColumn<QString>("expected");
+
+ QTest::newRow("NoSignature OpenPGP") << SignatureStatus::NoSignature << GpgME::OpenPGP << QString{};
+ QTest::newRow("KeyMissing OpenPGP") //
+ << SignatureStatus::KeyMissing << GpgME::OpenPGP
+ << u"Ask the sender for the certificate or import it from a file or a keyserver. Then verify the data again."_s;
+ QTest::newRow("ValidAndFullyTrusted OpenPGP") << SignatureStatus::ValidAndFullyTrusted << GpgME::OpenPGP << QString{};
+ QTest::newRow("ValidButNotFullyTrusted OpenPGP") //
+ << SignatureStatus::ValidButNotFullyTrusted << GpgME::OpenPGP
+ << u"Verify the certificate’s fingerprint and certify it. Then verify the data again."_s;
+ QTest::newRow("ValidButSignatureExpired OpenPGP") << SignatureStatus::ValidButSignatureExpired << GpgME::OpenPGP << QString{};
+ QTest::newRow("ValidButKeyExpired OpenPGP") //
+ << SignatureStatus::ValidButKeyExpired << GpgME::OpenPGP
+ << u"You can look for an updated certificate on a keyserver, or ask the sender for it, then verify the data again after importing the certificate."_s;
+ QTest::newRow("ValidButKeyRevoked OpenPGP") //
+ << SignatureStatus::ValidButKeyRevoked << GpgME::OpenPGP
+ << u"If in doubt, contact the signer to clarify the situation and, if necessary, ask them to resend the data signed with a current certificate."_s;
+ QTest::newRow("ValidButSignerUntrustworthy OpenPGP") << SignatureStatus::ValidButSignerUntrustworthy << GpgME::OpenPGP << QString{};
+ QTest::newRow("Invalid OpenPGP") //
+ << SignatureStatus::Invalid << GpgME::OpenPGP //
+ << u"Ask the sender to resend the data."_s;
+ QTest::newRow("OtherError OpenPGP") << SignatureStatus::OtherError << GpgME::OpenPGP << QString{};
+
+ QTest::newRow("NoSignature S/MIME") << SignatureStatus::NoSignature << GpgME::CMS << QString{};
+ QTest::newRow("KeyMissing S/MIME") //
+ << SignatureStatus::KeyMissing << GpgME::CMS
+ << u"Ask the sender for the certificate or import it from a file or a keyserver. Then verify the data again."_s;
+ QTest::newRow("ValidAndFullyTrusted S/MIME") << SignatureStatus::ValidAndFullyTrusted << GpgME::CMS << QString{};
+ QTest::newRow("ValidButNotFullyTrusted S/MIME") //
+ << SignatureStatus::ValidButNotFullyTrusted << GpgME::CMS
+ << u"Verify the certificate’s Root-CA fingerprint and trust it. Then verify the data again."_s;
+ QTest::newRow("ValidButSignatureExpired S/MIME") << SignatureStatus::ValidButSignatureExpired << GpgME::CMS << QString{};
+ QTest::newRow("ValidButKeyExpired S/MIME") //
+ << SignatureStatus::ValidButKeyExpired << GpgME::CMS
+ << u"If in doubt, contact the signer to clarify the situation and, if necessary, ask them to resend the data with a current certificate."_s;
+ QTest::newRow("ValidButKeyRevoked S/MIME") //
+ << SignatureStatus::ValidButKeyRevoked << GpgME::CMS
+ << u"If in doubt, contact the signer to clarify the situation and, if necessary, ask them to resend the data signed with a current certificate."_s;
+ QTest::newRow("ValidButSignerUntrustworthy S/MIME") << SignatureStatus::ValidButSignerUntrustworthy << GpgME::CMS << QString{};
+ QTest::newRow("Invalid S/MIME") //
+ << SignatureStatus::Invalid << GpgME::CMS //
+ << u"Ask the sender to resend the data."_s;
+ QTest::newRow("OtherError S/MIME") << SignatureStatus::OtherError << GpgME::CMS << QString{};
+ }
+
+ void test_guidanceForDataSignature()
+ {
+ QFETCH(Kleo::SignatureStatus, signatureStatus);
+ QFETCH(GpgME::Protocol, protocol);
+ QFETCH(QString, expected);
+
+ const QString result = Formatting::guidanceForDataSignature(signatureStatus, protocol);
+ QCOMPARE(result, expected);
+ }
};
QTEST_MAIN(FormattingTest)
diff --git a/src/utils/formatting.cpp b/src/utils/formatting.cpp
index 5b1333a9..de92b747 100644
--- a/src/utils/formatting.cpp
+++ b/src/utils/formatting.cpp
@@ -1836,3 +1836,83 @@ QString Kleo::Formatting::prettyDataSignature(const Kleo::SignatureData &sigData
}
return text;
}
+
+QStringList Kleo::Formatting::explanationsForDataSignature(Kleo::SignatureStatus status)
+{
+ switch (status) {
+ case SignatureStatus::NoSignature:
+ return {};
+ case SignatureStatus::KeyMissing:
+ return {i18nc("@info", "The signing certificate is not present in your certificate list, but it is needed to verify the data.")};
+ case SignatureStatus::ValidAndFullyTrusted:
+ return {};
+ case SignatureStatus::ValidButNotFullyTrusted:
+ return {i18nc("@info",
+ "Technically, signature and data match, but the signing certificate is not marked as trusted. "
+ "Therefore the data cannot be trusted to originate from the stated source.")};
+ case SignatureStatus::ValidButSignatureExpired:
+ return {};
+ case SignatureStatus::ValidButKeyExpired:
+ return {i18nc("@info",
+ "For an expired certificate, it cannot be evaluated whether the certificate can be trusted. "
+ "Therefore the data cannot be trusted. Technically, signature and data match."),
+ i18nc("@info", "If the certificate was valid and trusted when you received the data, the data is likely valid.")};
+ case SignatureStatus::ValidButKeyRevoked:
+ return {i18nc("@info",
+ "The certificate may have been revoked because it was compromised and it might now be used by a third party. "
+ "The data can therefore not be trusted. Technically, signature and data match."),
+ i18nc("@info",
+ "It is possible that you received the data at a time when the certificate was still valid and trusted. "
+ "If this is the case, the data may be valid.")};
+ case SignatureStatus::ValidButSignerUntrustworthy:
+ return {};
+ case SignatureStatus::Invalid:
+ return {i18nc("@info",
+ "The data or the signature has been altered. This can happen accidentally (e.g. due to a transmission error), "
+ "unintentionally (e.g. due to a subsequent change to the data, possibly by an email client), or intentionally "
+ "(deliberate manipulation).")};
+ case SignatureStatus::OtherError:
+ // fall through
+ ;
+ }
+
+ return {};
+}
+
+QString Kleo::Formatting::guidanceForDataSignature(Kleo::SignatureStatus status, GpgME::Protocol protocol)
+{
+ switch (status) {
+ case SignatureStatus::NoSignature:
+ return {};
+ case SignatureStatus::KeyMissing:
+ return i18nc("@info", "Ask the sender for the certificate or import it from a file or a keyserver. Then verify the data again.");
+ case SignatureStatus::ValidAndFullyTrusted:
+ return {};
+ case SignatureStatus::ValidButNotFullyTrusted:
+ return (protocol == GpgME::OpenPGP) //
+ ? i18nc("@info", "Verify the certificate’s fingerprint and certify it. Then verify the data again.")
+ : i18nc("@info", "Verify the certificate’s Root-CA fingerprint and trust it. Then verify the data again.");
+ case SignatureStatus::ValidButSignatureExpired:
+ return {};
+ case SignatureStatus::ValidButKeyExpired:
+ return (protocol == GpgME::OpenPGP) //
+ ? i18nc("@info",
+ "You can look for an updated certificate on a keyserver, or ask the sender for it, then verify the data again after importing the "
+ "certificate.")
+ : i18nc("@info",
+ "If in doubt, contact the signer to clarify the situation and, if necessary, ask them to resend the data with a current certificate.");
+ case SignatureStatus::ValidButKeyRevoked:
+ return i18nc(
+ "@info",
+ "If in doubt, contact the signer to clarify the situation and, if necessary, ask them to resend the data signed with a current certificate.");
+ case SignatureStatus::ValidButSignerUntrustworthy:
+ return {};
+ case SignatureStatus::Invalid:
+ return i18nc("@info", "Ask the sender to resend the data.");
+ case SignatureStatus::OtherError:
+ // fall through
+ ;
+ }
+
+ return {};
+}
diff --git a/src/utils/formatting.h b/src/utils/formatting.h
index e5b67c7e..8799fd89 100644
--- a/src/utils/formatting.h
+++ b/src/utils/formatting.h
@@ -31,6 +31,7 @@ namespace Kleo
{
class KeyGroup;
struct SignatureData;
+enum class SignatureStatus;
namespace Formatting
{
@@ -117,6 +118,20 @@ KLEO_EXPORT QString prettyDataSignature(const GpgME::Signature &signature, const
*/
KLEO_EXPORT QString prettyDataSignature(const Kleo::SignatureData &signature);
+/*!
+ * Returns more detailed explanations for a signature that's not fully valid.
+ *
+ * Complements prettyDataSignature.
+ */
+KLEO_EXPORT QStringList explanationsForDataSignature(Kleo::SignatureStatus status);
+
+/*!
+ * Returns some guidance for what could be done in case a signature is not fully valid.
+ *
+ * Complements explanationsForDataSignature and prettyDataSignature.
+ */
+KLEO_EXPORT QString guidanceForDataSignature(Kleo::SignatureStatus status, GpgME::Protocol protocol);
+
// clang-format off
enum ToolTipOption {
KeyID = 0x001,