Page MenuHome GnuPG

No OneTemporary

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 7375d524..5f465654 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,415 +1,418 @@
# SPDX-License-Identifier: CC0-1.0
# SPDX-FileCopyrightText: none
# target_include_directories does not handle empty include paths
include_directories(
${Boost_INCLUDE_DIRS}
${GPGME_INCLUDES}
)
add_definitions(-DTRANSLATION_DOMAIN=\"libkleopatra\")
#add_definitions( -DQT_NO_CAST_FROM_ASCII )
#add_definitions( -DQT_NO_CAST_TO_ASCII )
kde_enable_exceptions()
add_definitions( -DGPGMEPP_ERR_SOURCE_DEFAULT=13 ) # 13 is GPG_ERR_SOURCE_KLEO, even if gpg-error's too old to know about
add_subdirectory( pics )
add_library(KPim${KF_MAJOR_VERSION}Libkleo)
add_library(KPim${KF_MAJOR_VERSION}::Libkleo ALIAS KPim${KF_MAJOR_VERSION}Libkleo)
########### next target ###############
target_sources(KPim${KF_MAJOR_VERSION}Libkleo PRIVATE
kleo/auditlogentry.cpp
kleo/auditlogentry.h
kleo/checksumdefinition.cpp
kleo/checksumdefinition.h
kleo/debug.cpp
kleo/debug.h
kleo/defaultkeyfilter.cpp
kleo/defaultkeyfilter.h
kleo/defaultkeygenerationjob.cpp
kleo/defaultkeygenerationjob.h
kleo/docaction.cpp kleo/docaction.h
kleo/dn.cpp
kleo/dn.h
kleo/enum.cpp
kleo/enum.h
kleo/expirychecker.cpp
kleo/expirychecker.h
kleo/expirycheckerconfig.cpp
kleo/expirycheckerconfig.h
kleo/expirycheckersettings.cpp
kleo/expirycheckersettings.h
kleo/kconfigbasedkeyfilter.cpp
kleo/kconfigbasedkeyfilter.h
kleo/keyfilter.h
kleo/keyfiltermanager.cpp
kleo/keyfiltermanager.h
kleo/keygroup.cpp
kleo/keygroup.h
kleo/keygroupconfig.cpp
kleo/keygroupconfig.h
kleo/keygroupimportexport.cpp
kleo/keygroupimportexport.h
kleo/keyresolver.cpp
kleo/keyresolver.h
kleo/keyresolvercore.cpp
kleo/keyresolvercore.h
kleo/keyserverconfig.cpp
kleo/keyserverconfig.h
kleo/kleoexception.cpp
kleo/kleoexception.h
kleo/oidmap.cpp
kleo/oidmap.h
kleo/predicates.h
kleo/stl_util.h
models/keycache.cpp
models/keycache.h
models/keycache_p.h
models/keylist.h
models/keylistmodel.cpp
models/keylistmodel.h
models/keylistmodelinterface.cpp
models/keylistmodelinterface.h
models/keylistsortfilterproxymodel.cpp
models/keylistsortfilterproxymodel.h
models/keyrearrangecolumnsproxymodel.cpp
models/keyrearrangecolumnsproxymodel.h
models/subkeylistmodel.cpp
models/subkeylistmodel.h
models/useridlistmodel.cpp
models/useridlistmodel.h
+ models/useridproxymodel.cpp
+ models/useridproxymodel.h
utils/algorithm.h
utils/assuan.cpp
utils/assuan.h
utils/chrono.h
utils/classify.cpp
utils/classify.h
utils/compat.cpp
utils/compat.h
utils/compliance.cpp
utils/compliance.h
utils/cryptoconfig.cpp
utils/cryptoconfig.h
utils/cryptoconfig_p.h
utils/filesystemwatcher.cpp
utils/filesystemwatcher.h
utils/formatting.cpp
utils/formatting.h
utils/gnupg-registry.c
utils/gnupg-registry.h
utils/gnupg.cpp
utils/gnupg.h
utils/hex.cpp
utils/hex.h
utils/keyhelpers.cpp
utils/keyhelpers.h
utils/keyusage.h
utils/qtstlhelpers.cpp
utils/qtstlhelpers.h
utils/scdaemon.cpp
utils/scdaemon.h
utils/stringutils.cpp
utils/stringutils.h
utils/systeminfo.cpp
utils/systeminfo.h
utils/test.cpp
utils/test.h
utils/uniquelock.cpp
utils/uniquelock.h
)
ecm_qt_declare_logging_category(KPim${KF_MAJOR_VERSION}Libkleo HEADER libkleo_debug.h IDENTIFIER LIBKLEO_LOG CATEGORY_NAME org.kde.pim.libkleo
DESCRIPTION "libkleo (kleo_core)"
EXPORT LIBKLEO
)
target_sources(KPim${KF_MAJOR_VERSION}Libkleo PRIVATE
ui/adjustingscrollarea.cpp
ui/adjustingscrollarea.h
ui/auditlogviewer.cpp
ui/auditlogviewer.h
ui/cryptoconfigentryreaderport.cpp
ui/cryptoconfigentryreaderport_p.h
ui/cryptoconfigmodule.cpp
ui/cryptoconfigmodule.h
ui/cryptoconfigmodule_p.h
ui/directoryserviceswidget.cpp
ui/directoryserviceswidget.h
ui/dnattributeorderconfigwidget.cpp
ui/dnattributeorderconfigwidget.h
ui/editdirectoryservicedialog.cpp
ui/editdirectoryservicedialog.h
ui/filenamerequester.cpp
ui/filenamerequester.h
ui/messagebox.cpp
ui/messagebox.h
ui/navigatabletreeview.cpp
ui/navigatabletreeview.h
ui/navigatabletreewidget.cpp
ui/navigatabletreewidget.h
ui/progressbar.cpp
ui/progressbar.h
ui/progressdialog.cpp
ui/progressdialog.h
ui/readerportselection.cpp
ui/readerportselection.h
)
ecm_qt_declare_logging_category(KPim${KF_MAJOR_VERSION}Libkleo HEADER kleo_ui_debug.h IDENTIFIER KLEO_UI_LOG CATEGORY_NAME org.kde.pim.kleo_ui
DESCRIPTION "libkleo (kleo_ui)"
OLD_CATEGORY_NAMES log_kleo_ui
EXPORT LIBKLEO
)
target_sources(KPim${KF_MAJOR_VERSION}Libkleo PRIVATE # make this a separate lib.
ui/keyapprovaldialog.cpp
ui/keyapprovaldialog.h
ui/keylistview.cpp
ui/keylistview.h
ui/keyrequester.cpp
ui/keyrequester.h
ui/keyselectioncombo.cpp
ui/keyselectioncombo.h
ui/keyselectiondialog.cpp
ui/keyselectiondialog.h
ui/newkeyapprovaldialog.cpp
ui/newkeyapprovaldialog.h
)
if(MINGW)
# we do not care about different signedness of passed pointer arguments
set_source_files_properties(utils/gnupg-registry.c PROPERTIES COMPILE_OPTIONS "-Wno-pointer-sign")
endif()
target_link_libraries(KPim${KF_MAJOR_VERSION}Libkleo PUBLIC Gpgmepp PRIVATE Qt::Widgets
KF${KF_MAJOR_VERSION}::I18n
KF${KF_MAJOR_VERSION}::Completion
KF${KF_MAJOR_VERSION}::ConfigCore
KF${KF_MAJOR_VERSION}::ConfigWidgets
KF${KF_MAJOR_VERSION}::CoreAddons
KF${KF_MAJOR_VERSION}::WidgetsAddons
KF${KF_MAJOR_VERSION}::ItemModels
KF${KF_MAJOR_VERSION}::Codecs
LibGpgError::LibGpgError)
if (QT_MAJOR_VERSION STREQUAL "6")
target_link_libraries(KPim${KF_MAJOR_VERSION}Libkleo PRIVATE Qt6::Core5Compat PUBLIC QGpgmeQt6)
else()
target_link_libraries(KPim${KF_MAJOR_VERSION}Libkleo PUBLIC QGpgme)
endif()
# Boost::headers may not be available for old versions of Boost
if (TARGET Boost::headers)
target_link_libraries(KPim${KF_MAJOR_VERSION}Libkleo PRIVATE Boost::headers)
endif()
if (KPim${KF_MAJOR_VERSION}TextEdit_FOUND)
add_definitions(-DHAVE_PIMTEXTEDIT)
target_link_libraries(KPim${KF_MAJOR_VERSION}Libkleo PRIVATE KPim${KF_MAJOR_VERSION}::PimTextEdit)
endif()
if (COMPILE_WITH_UNITY_CMAKE_SUPPORT)
set_target_properties(KPim${KF_MAJOR_VERSION}Libkleo PROPERTIES UNITY_BUILD ON)
endif()
ecm_generate_export_header(KPim${KF_MAJOR_VERSION}Libkleo
BASE_NAME kleo
VERSION ${PIM_VERSION}
DEPRECATED_BASE_VERSION 0
DEPRECATION_VERSIONS 5.23
)
if(WIN32)
target_link_libraries(KPim${KF_MAJOR_VERSION}Libkleo ${GPGME_VANILLA_LIBRARIES} )
endif()
set_target_properties(KPim${KF_MAJOR_VERSION}Libkleo PROPERTIES
VERSION ${LIBKLEO_VERSION}
SOVERSION ${LIBKLEO_SOVERSION}
EXPORT_NAME Libkleo
)
install(TARGETS
KPim${KF_MAJOR_VERSION}Libkleo
EXPORT KPim${KF_MAJOR_VERSION}LibkleoTargets ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}
)
target_include_directories(KPim${KF_MAJOR_VERSION}Libkleo INTERFACE "$<INSTALL_INTERFACE:${KDE_INSTALL_INCLUDEDIR}/KPim${KF_MAJOR_VERSION}/Libkleo>")
target_include_directories(KPim${KF_MAJOR_VERSION}Libkleo PUBLIC "$<BUILD_INTERFACE:${libkleo_SOURCE_DIR}/src;${libkleo_BINARY_DIR}/src>")
ecm_generate_headers(libkleo_CamelCase_HEADERS
HEADER_NAMES
AuditLogEntry
ChecksumDefinition
Debug
DefaultKeyFilter
DefaultKeyGenerationJob
DocAction
Dn
Enum
ExpiryChecker
ExpiryCheckerConfig
ExpiryCheckerSettings
KConfigBasedKeyFilter
KeyFilter
KeyFilterManager
KeyGroup
KeyGroupConfig
KeyGroupImportExport
KeyResolver
KeyResolverCore
KeyserverConfig
KleoException
OidMap
Predicates
Stl_Util
REQUIRED_HEADERS libkleo_HEADERS
PREFIX Libkleo
RELATIVE kleo
)
ecm_generate_headers(libkleo_CamelCase_models_HEADERS
HEADER_NAMES
KeyCache
KeyList
KeyListModel
KeyListModelInterface
KeyListSortFilterProxyModel
KeyRearrangeColumnsProxyModel
SubkeyListModel
UserIDListModel
+ UserIDProxyModel
REQUIRED_HEADERS libkleo_models_HEADERS
PREFIX Libkleo
RELATIVE models
)
ecm_generate_headers(libkleo_CamelCase_utils_HEADERS
HEADER_NAMES
Algorithm
Assuan
Chrono
Classify
Compat
Compliance
CryptoConfig
FileSystemWatcher
Formatting
GnuPG
Hex
KeyHelpers
KeyUsage
QtStlHelpers
SCDaemon
StringUtils
SystemInfo
Test
UniqueLock
REQUIRED_HEADERS libkleo_utils_HEADERS
PREFIX Libkleo
RELATIVE utils
)
ecm_generate_headers(libkleo_CamelCase_ui_HEADERS
HEADER_NAMES
AdjustingScrollArea
AuditLogViewer
CryptoConfigModule
DNAttributeOrderConfigWidget
DirectoryServicesWidget
EditDirectoryServiceDialog
FileNameRequester
KeyApprovalDialog
KeyListView
KeyRequester
KeySelectionCombo
KeySelectionDialog
MessageBox
NavigatableTreeView
NavigatableTreeWidget
NewKeyApprovalDialog
ProgressDialog
ReaderPortSelection
REQUIRED_HEADERS libkleo_ui_HEADERS
PREFIX Libkleo
RELATIVE ui
)
if (QT_MAJOR_VERSION STREQUAL "6")
ecm_generate_pri_file(BASE_NAME Libkleo
LIB_NAME KPim${KF_MAJOR_VERSION}Libkleo
DEPS "QGpgme" FILENAME_VAR PRI_FILENAME INCLUDE_INSTALL_DIR ${KDE_INSTALL_INCLUDEDIR}/KPim${KF_MAJOR_VERSION}/Libkleo
)
else()
ecm_generate_pri_file(BASE_NAME Libkleo
LIB_NAME KPim${KF_MAJOR_VERSION}Libkleo
DEPS "QGpgmeQt6" FILENAME_VAR PRI_FILENAME INCLUDE_INSTALL_DIR ${KDE_INSTALL_INCLUDEDIR}/KPim${KF_MAJOR_VERSION}/Libkleo
)
endif()
kconfig_add_kcfg_files(KPim${KF_MAJOR_VERSION}Libkleo
kcfg/expirycheckerconfigbase.kcfgc
kcfg/classifyconfig.kcfgc
)
install(FILES
${libkleo_CamelCase_HEADERS}
${libkleo_CamelCase_models_HEADERS}
${libkleo_CamelCase_ui_HEADERS}
${libkleo_CamelCase_utils_HEADERS}
DESTINATION ${KDE_INSTALL_INCLUDEDIR}/KPim${KF_MAJOR_VERSION}/Libkleo/Libkleo
COMPONENT Devel
)
install(FILES
${CMAKE_CURRENT_BINARY_DIR}/expirycheckerconfigbase.h
${CMAKE_CURRENT_BINARY_DIR}/kleo_export.h
${libkleo_HEADERS}
${libkleo_models_HEADERS}
${libkleo_ui_HEADERS}
${libkleo_utils_HEADERS}
${CMAKE_CURRENT_BINARY_DIR}/classifyconfig.h
DESTINATION ${KDE_INSTALL_INCLUDEDIR}/KPim${KF_MAJOR_VERSION}/Libkleo/libkleo
COMPONENT Devel
)
install(FILES
${PRI_FILENAME}
DESTINATION ${ECM_MKSPECS_INSTALL_DIR})
if ( WIN32 )
install ( FILES libkleopatrarc-win32.desktop DESTINATION ${KDE_INSTALL_CONFDIR} RENAME libkleopatrarc )
else ()
install ( FILES libkleopatrarc.desktop DESTINATION ${KDE_INSTALL_CONFDIR} RENAME libkleopatrarc )
endif ()
if (BUILD_QCH)
ecm_add_qch(
KPim${KF_MAJOR_VERSION}Libkleo_QCH
NAME KPim${KF_MAJOR_VERSION}Libkleo
BASE_NAME KPim${KF_MAJOR_VERSION}Libkleo
VERSION ${PIM_VERSION}
ORG_DOMAIN org.kde
SOURCES # using only public headers, to cover only public API
${libkleo_HEADERS}
${libkleo_models_HEADERS}
${libkleo_ui_HEADERS}
${libkleo_utils_HEADERS}
#MD_MAINPAGE "${CMAKE_SOURCE_DIR}/README.md"
#IMAGE_DIRS "${CMAKE_SOURCE_DIR}/docs/pics"
LINK_QCHS
Qt${QT_MAJOR_VERSION}Core_QCH
Qt${QT_MAJOR_VERSION}Gui_QCH
Qt${QT_MAJOR_VERSION}Widgets_QCH
INCLUDE_DIRS
${CMAKE_CURRENT_BINARY_DIR}
BLANK_MACROS
KLEO_EXPORT
TAGFILE_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR}
QCH_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR}
COMPONENT Devel
)
endif()
diff --git a/src/kleo/defaultkeyfilter.cpp b/src/kleo/defaultkeyfilter.cpp
index 53aa867d..6878aaeb 100644
--- a/src/kleo/defaultkeyfilter.cpp
+++ b/src/kleo/defaultkeyfilter.cpp
@@ -1,590 +1,727 @@
/*
defaultkeyfilter.cpp
This file is part of libkleopatra, the KDE keymanagement library
SPDX-FileCopyrightText: 2004 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik
SPDX-FileContributor: Intevation GmbH
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-libkleo.h>
#include "defaultkeyfilter.h"
+#include "utils/compliance.h"
#if GPGMEPP_KEY_HAS_HASCERTIFY_SIGN_ENCRYPT_AUTHENTICATE
#else
#include <libkleo/compat.h>
#endif
#include <libkleo/compliance.h>
#include <libkleo/formatting.h>
#include <libkleo/keyhelpers.h>
#include <functional>
#include <memory>
using namespace GpgME;
using namespace Kleo;
static bool is_card_key(const Key &key)
{
const std::vector<Subkey> sks = key.subkeys();
return std::find_if(sks.begin(), sks.end(), std::mem_fn(&Subkey::isCardKey)) != sks.end();
}
class DefaultKeyFilter::Private
{
public:
Private()
{
}
QColor mFgColor;
QColor mBgColor;
QString mName;
QString mIcon;
QString mId;
MatchContexts mMatchContexts = AnyMatchContext;
unsigned int mSpecificity = 0;
bool mItalic = false;
bool mBold = false;
bool mStrikeOut = false;
bool mUseFullFont = false;
QFont mFont;
TriState mRevoked = DoesNotMatter;
TriState mExpired = DoesNotMatter;
TriState mInvalid = DoesNotMatter;
TriState mDisabled = DoesNotMatter;
TriState mRoot = DoesNotMatter;
TriState mCanEncrypt = DoesNotMatter;
TriState mCanSign = DoesNotMatter;
TriState mCanCertify = DoesNotMatter;
TriState mCanAuthenticate = DoesNotMatter;
TriState mHasEncrypt = DoesNotMatter;
TriState mHasSign = DoesNotMatter;
TriState mHasCertify = DoesNotMatter;
TriState mHasAuthenticate = DoesNotMatter;
TriState mQualified = DoesNotMatter;
TriState mCardKey = DoesNotMatter;
TriState mHasSecret = DoesNotMatter;
TriState mIsOpenPGP = DoesNotMatter;
TriState mWasValidated = DoesNotMatter;
TriState mIsDeVs = DoesNotMatter;
TriState mBad = DoesNotMatter;
TriState mValidIfSMIME = DoesNotMatter;
LevelState mOwnerTrust = LevelDoesNotMatter;
GpgME::Key::OwnerTrust mOwnerTrustReferenceLevel = Key::OwnerTrust::Unknown;
LevelState mValidity = LevelDoesNotMatter;
GpgME::UserID::Validity mValidityReferenceLevel = UserID::Validity::Unknown;
};
DefaultKeyFilter::DefaultKeyFilter()
: KeyFilter{}
, d{new Private}
{
}
DefaultKeyFilter::~DefaultKeyFilter() = default;
bool DefaultKeyFilter::matches(const Key &key, MatchContexts contexts) const
{
if (!(d->mMatchContexts & contexts)) {
return false;
}
#ifdef MATCH
#undef MATCH
#endif
#define MATCH(member, method) \
do { \
if (member != DoesNotMatter && key.method() != bool(member == Set)) { \
return false; \
} \
} while (false)
#define IS_MATCH(what) MATCH(d->m##what, is##what)
#define CAN_MATCH(what) MATCH(d->mCan##what, can##what)
#if GPGMEPP_KEY_HAS_HASCERTIFY_SIGN_ENCRYPT_AUTHENTICATE
#define HAS_MATCH(what) MATCH(d->mHas##what, has##what)
#else
#define HAS_MATCH(what) \
do { \
if (d->mHas##what != DoesNotMatter && Kleo::keyHas##what(key) != bool(d->mHas##what == Set)) { \
return false; \
} \
} while (false)
#endif
IS_MATCH(Revoked);
IS_MATCH(Expired);
IS_MATCH(Invalid);
IS_MATCH(Disabled);
IS_MATCH(Root);
CAN_MATCH(Encrypt);
CAN_MATCH(Sign);
CAN_MATCH(Certify);
CAN_MATCH(Authenticate);
HAS_MATCH(Encrypt);
HAS_MATCH(Sign);
HAS_MATCH(Certify);
HAS_MATCH(Authenticate);
IS_MATCH(Qualified);
if (d->mCardKey != DoesNotMatter) {
if ((d->mCardKey == Set && !is_card_key(key)) || (d->mCardKey == NotSet && is_card_key(key))) {
return false;
}
}
MATCH(d->mHasSecret, hasSecret);
#undef MATCH
if (d->mIsOpenPGP != DoesNotMatter && bool(key.protocol() == GpgME::OpenPGP) != bool(d->mIsOpenPGP == Set)) {
return false;
}
if (d->mWasValidated != DoesNotMatter && bool(key.keyListMode() & GpgME::Validate) != bool(d->mWasValidated == Set)) {
return false;
}
if (d->mIsDeVs != DoesNotMatter && bool(DeVSCompliance::keyIsCompliant(key)) != bool(d->mIsDeVs == Set)) {
return false;
}
if (d->mBad != DoesNotMatter &&
/* This is similar to GPGME::Key::isBad which was introduced in GPGME 1.13.0 */
bool(key.isNull() || key.isRevoked() || key.isExpired() || key.isDisabled() || key.isInvalid()) != bool(d->mBad == Set)) {
return false;
}
const UserID uid = key.userID(0);
if ((key.protocol() == GpgME::CMS) //
&& (d->mValidIfSMIME != DoesNotMatter) //
&& (bool(uid.validity() >= UserID::Full) != bool(d->mValidIfSMIME == Set))) {
return false;
}
switch (d->mOwnerTrust) {
default:
case LevelDoesNotMatter:
break;
case Is:
if (key.ownerTrust() != d->mOwnerTrustReferenceLevel) {
return false;
}
break;
case IsNot:
if (key.ownerTrust() == d->mOwnerTrustReferenceLevel) {
return false;
}
break;
case IsAtLeast:
if (static_cast<int>(key.ownerTrust()) < static_cast<int>(d->mOwnerTrustReferenceLevel)) {
return false;
}
break;
case IsAtMost:
if (static_cast<int>(key.ownerTrust()) > static_cast<int>(d->mOwnerTrustReferenceLevel)) {
return false;
}
break;
}
switch (d->mValidity) {
default:
case LevelDoesNotMatter:
break;
case Is:
if (uid.validity() != d->mValidityReferenceLevel) {
return false;
}
break;
case IsNot:
if (uid.validity() == d->mValidityReferenceLevel) {
return false;
}
break;
case IsAtLeast:
if (static_cast<int>(uid.validity()) < static_cast<int>(d->mValidityReferenceLevel)) {
return false;
}
break;
case IsAtMost:
if (static_cast<int>(uid.validity()) > static_cast<int>(d->mValidityReferenceLevel)) {
return false;
}
break;
}
return true;
}
+bool DefaultKeyFilter::matches(const UserID &userID, MatchContexts contexts) const
+{
+ if (!(d->mMatchContexts & contexts)) {
+ return false;
+ }
+#ifdef MATCH_KEY
+#undef MATCH_KEY
+#endif
+#define MATCH_KEY(member, method) \
+ do { \
+ if (member != DoesNotMatter && userID.parent().method() != bool(member == Set)) { \
+ return false; \
+ } \
+ } while (false)
+#define IS_MATCH_KEY(what) MATCH_KEY(d->m##what, is##what)
+#define CAN_MATCH_KEY(what) MATCH_KEY(d->mCan##what, can##what)
+#if GPGMEPP_KEY_HAS_HASCERTIFY_SIGN_ENCRYPT_AUTHENTICATE
+#define HAS_MATCH_KEY(what) MATCH_KEY(d->mHas##what, has##what)
+#else
+#define HAS_MATCH_KEY(what) \
+ do { \
+ if (d->mHas##what != DoesNotMatter && Kleo::keyHas##what(userID.parent()) != bool(d->mHas##what == Set)) { \
+ return false; \
+ } \
+ } while (false)
+#endif
+
+#ifdef MATCH
+#undef MATCH
+#endif
+#define MATCH(member, method) \
+ do { \
+ if (member != DoesNotMatter && (userID.parent().method() != bool(member == Set) || userID.method() != bool(member == Set))) { \
+ return false; \
+ } \
+ } while (false)
+#define IS_MATCH(what) MATCH(d->m##what, is##what)
+ IS_MATCH(Revoked);
+ IS_MATCH_KEY(Expired);
+ // We have to do this manually since there's no UserID::isExpired()
+ if (d->mExpired != DoesNotMatter && (userID.parent().isExpired() != bool(d->mExpired == Set) || isExpired(userID) != bool(d->mExpired == Set))) {
+ return false;
+ }
+ IS_MATCH(Invalid);
+ IS_MATCH_KEY(Disabled);
+ IS_MATCH_KEY(Root);
+ CAN_MATCH_KEY(Encrypt);
+ CAN_MATCH_KEY(Sign);
+ CAN_MATCH_KEY(Certify);
+ CAN_MATCH_KEY(Authenticate);
+ HAS_MATCH_KEY(Encrypt);
+ HAS_MATCH_KEY(Sign);
+ HAS_MATCH_KEY(Certify);
+ HAS_MATCH_KEY(Authenticate);
+ IS_MATCH_KEY(Qualified);
+ if (d->mCardKey != DoesNotMatter) {
+ if ((d->mCardKey == Set && !is_card_key(userID.parent())) || (d->mCardKey == NotSet && is_card_key(userID.parent()))) {
+ return false;
+ }
+ }
+ MATCH_KEY(d->mHasSecret, hasSecret);
+#undef MATCH
+ if (d->mIsOpenPGP != DoesNotMatter && bool(userID.parent().protocol() == GpgME::OpenPGP) != bool(d->mIsOpenPGP == Set)) {
+ return false;
+ }
+ if (d->mWasValidated != DoesNotMatter && bool(userID.parent().keyListMode() & GpgME::Validate) != bool(d->mWasValidated == Set)) {
+ return false;
+ }
+ if (d->mIsDeVs != DoesNotMatter && bool(DeVSCompliance::userIDIsCompliant(userID)) != bool(d->mIsDeVs == Set)) {
+ return false;
+ }
+ if (d->mBad != DoesNotMatter &&
+ /* This is similar to GPGME::Key::isBad which was introduced in GPGME 1.13.0 */
+ bool(userID.parent().isNull() || userID.isNull() || userID.parent().isRevoked() || userID.isRevoked() || userID.parent().isExpired()
+ || userID.parent().isDisabled() || userID.parent().isInvalid() || userID.isInvalid())
+ != bool(d->mBad == Set)) {
+ return false;
+ }
+ if ((userID.parent().protocol() == GpgME::CMS) //
+ && (d->mValidIfSMIME != DoesNotMatter) //
+ && (bool(userID.validity() >= UserID::Full) != bool(d->mValidIfSMIME == Set))) {
+ return false;
+ }
+ switch (d->mOwnerTrust) {
+ default:
+ case LevelDoesNotMatter:
+ break;
+ case Is:
+ if (userID.parent().ownerTrust() != d->mOwnerTrustReferenceLevel) {
+ return false;
+ }
+ break;
+ case IsNot:
+ if (userID.parent().ownerTrust() == d->mOwnerTrustReferenceLevel) {
+ return false;
+ }
+ break;
+ case IsAtLeast:
+ if (static_cast<int>(userID.parent().ownerTrust()) < static_cast<int>(d->mOwnerTrustReferenceLevel)) {
+ return false;
+ }
+ break;
+ case IsAtMost:
+ if (static_cast<int>(userID.parent().ownerTrust()) > static_cast<int>(d->mOwnerTrustReferenceLevel)) {
+ return false;
+ }
+ break;
+ }
+ switch (d->mValidity) {
+ default:
+ case LevelDoesNotMatter:
+ break;
+ case Is:
+ if (userID.validity() != d->mValidityReferenceLevel) {
+ return false;
+ }
+ break;
+ case IsNot:
+ if (userID.validity() == d->mValidityReferenceLevel) {
+ return false;
+ }
+ break;
+ case IsAtLeast:
+ if (static_cast<int>(userID.validity()) < static_cast<int>(d->mValidityReferenceLevel)) {
+ return false;
+ }
+ break;
+ case IsAtMost:
+ if (static_cast<int>(userID.validity()) > static_cast<int>(d->mValidityReferenceLevel)) {
+ return false;
+ }
+ break;
+ }
+ return true;
+}
+
KeyFilter::FontDescription DefaultKeyFilter::fontDescription() const
{
if (d->mUseFullFont) {
return FontDescription::create(font(), bold(), italic(), strikeOut());
} else {
return FontDescription::create(bold(), italic(), strikeOut());
}
}
void DefaultKeyFilter::setFgColor(const QColor &value)
{
d->mFgColor = value;
}
void DefaultKeyFilter::setBgColor(const QColor &value)
{
d->mBgColor = value;
}
void DefaultKeyFilter::setName(const QString &value)
{
d->mName = value;
}
void DefaultKeyFilter::setIcon(const QString &value)
{
d->mIcon = value;
}
void DefaultKeyFilter::setId(const QString &value)
{
d->mId = value;
}
void DefaultKeyFilter::setMatchContexts(MatchContexts value)
{
d->mMatchContexts = value;
}
void DefaultKeyFilter::setSpecificity(unsigned int value)
{
d->mSpecificity = value;
}
void DefaultKeyFilter::setItalic(bool value)
{
d->mItalic = value;
}
void DefaultKeyFilter::setBold(bool value)
{
d->mBold = value;
}
void DefaultKeyFilter::setStrikeOut(bool value)
{
d->mStrikeOut = value;
}
void DefaultKeyFilter::setUseFullFont(bool value)
{
d->mUseFullFont = value;
}
void DefaultKeyFilter::setFont(const QFont &value)
{
d->mFont = value;
}
void DefaultKeyFilter::setRevoked(DefaultKeyFilter::TriState value)
{
d->mRevoked = value;
}
void DefaultKeyFilter::setExpired(DefaultKeyFilter::TriState value)
{
d->mExpired = value;
}
void DefaultKeyFilter::setInvalid(DefaultKeyFilter::TriState value)
{
d->mInvalid = value;
}
void DefaultKeyFilter::setDisabled(DefaultKeyFilter::TriState value)
{
d->mDisabled = value;
}
void DefaultKeyFilter::setRoot(DefaultKeyFilter::TriState value)
{
d->mRoot = value;
}
void DefaultKeyFilter::setCanEncrypt(DefaultKeyFilter::TriState value)
{
d->mCanEncrypt = value;
}
void DefaultKeyFilter::setCanSign(DefaultKeyFilter::TriState value)
{
d->mCanSign = value;
}
void DefaultKeyFilter::setCanCertify(DefaultKeyFilter::TriState value)
{
d->mCanCertify = value;
}
void DefaultKeyFilter::setCanAuthenticate(DefaultKeyFilter::TriState value)
{
d->mCanAuthenticate = value;
}
void DefaultKeyFilter::setHasEncrypt(DefaultKeyFilter::TriState value)
{
d->mHasEncrypt = value;
}
void DefaultKeyFilter::setHasSign(DefaultKeyFilter::TriState value)
{
d->mHasSign = value;
}
void DefaultKeyFilter::setHasCertify(DefaultKeyFilter::TriState value)
{
d->mHasCertify = value;
}
void DefaultKeyFilter::setHasAuthenticate(DefaultKeyFilter::TriState value)
{
d->mHasAuthenticate = value;
}
void DefaultKeyFilter::setQualified(DefaultKeyFilter::TriState value)
{
d->mQualified = value;
}
void DefaultKeyFilter::setCardKey(DefaultKeyFilter::TriState value)
{
d->mCardKey = value;
}
void DefaultKeyFilter::setHasSecret(DefaultKeyFilter::TriState value)
{
d->mHasSecret = value;
}
void DefaultKeyFilter::setIsOpenPGP(DefaultKeyFilter::TriState value)
{
d->mIsOpenPGP = value;
}
void DefaultKeyFilter::setWasValidated(DefaultKeyFilter::TriState value)
{
d->mWasValidated = value;
}
void DefaultKeyFilter::setOwnerTrust(DefaultKeyFilter::LevelState value)
{
d->mOwnerTrust = value;
}
void DefaultKeyFilter::setOwnerTrustReferenceLevel(GpgME::Key::OwnerTrust value)
{
d->mOwnerTrustReferenceLevel = value;
}
void DefaultKeyFilter::setValidity(DefaultKeyFilter::LevelState value)
{
d->mValidity = value;
}
void DefaultKeyFilter::setValidityReferenceLevel(GpgME::UserID::Validity value)
{
d->mValidityReferenceLevel = value;
}
void DefaultKeyFilter::setIsDeVs(DefaultKeyFilter::TriState value)
{
d->mIsDeVs = value;
}
void DefaultKeyFilter::setIsBad(DefaultKeyFilter::TriState value)
{
d->mBad = value;
}
void DefaultKeyFilter::setValidIfSMIME(DefaultKeyFilter::TriState value)
{
d->mValidIfSMIME = value;
}
QColor DefaultKeyFilter::fgColor() const
{
return d->mFgColor;
}
QColor DefaultKeyFilter::bgColor() const
{
return d->mBgColor;
}
QString DefaultKeyFilter::name() const
{
return d->mName;
}
QString DefaultKeyFilter::icon() const
{
return d->mIcon;
}
QString DefaultKeyFilter::id() const
{
return d->mId;
}
QFont DefaultKeyFilter::font() const
{
return d->mFont;
}
KeyFilter::MatchContexts DefaultKeyFilter::availableMatchContexts() const
{
return d->mMatchContexts;
}
unsigned int DefaultKeyFilter::specificity() const
{
return d->mSpecificity;
}
bool DefaultKeyFilter::italic() const
{
return d->mItalic;
}
bool DefaultKeyFilter::bold() const
{
return d->mBold;
}
bool DefaultKeyFilter::strikeOut() const
{
return d->mStrikeOut;
}
bool DefaultKeyFilter::useFullFont() const
{
return d->mUseFullFont;
}
DefaultKeyFilter::TriState DefaultKeyFilter::revoked() const
{
return d->mRevoked;
}
DefaultKeyFilter::TriState DefaultKeyFilter::expired() const
{
return d->mExpired;
}
DefaultKeyFilter::TriState DefaultKeyFilter::invalid() const
{
return d->mInvalid;
}
DefaultKeyFilter::TriState DefaultKeyFilter::disabled() const
{
return d->mDisabled;
}
DefaultKeyFilter::TriState DefaultKeyFilter::root() const
{
return d->mRoot;
}
DefaultKeyFilter::TriState DefaultKeyFilter::canEncrypt() const
{
return d->mCanEncrypt;
}
DefaultKeyFilter::TriState DefaultKeyFilter::canSign() const
{
return d->mCanSign;
}
DefaultKeyFilter::TriState DefaultKeyFilter::canCertify() const
{
return d->mCanCertify;
}
DefaultKeyFilter::TriState DefaultKeyFilter::canAuthenticate() const
{
return d->mCanAuthenticate;
}
DefaultKeyFilter::TriState DefaultKeyFilter::hasEncrypt() const
{
return d->mHasEncrypt;
}
DefaultKeyFilter::TriState DefaultKeyFilter::hasSign() const
{
return d->mHasSign;
}
DefaultKeyFilter::TriState DefaultKeyFilter::hasCertify() const
{
return d->mHasCertify;
}
DefaultKeyFilter::TriState DefaultKeyFilter::hasAuthenticate() const
{
return d->mHasAuthenticate;
}
DefaultKeyFilter::TriState DefaultKeyFilter::qualified() const
{
return d->mQualified;
}
DefaultKeyFilter::TriState DefaultKeyFilter::cardKey() const
{
return d->mCardKey;
}
DefaultKeyFilter::TriState DefaultKeyFilter::hasSecret() const
{
return d->mHasSecret;
}
DefaultKeyFilter::TriState DefaultKeyFilter::isOpenPGP() const
{
return d->mIsOpenPGP;
}
DefaultKeyFilter::TriState DefaultKeyFilter::wasValidated() const
{
return d->mWasValidated;
}
DefaultKeyFilter::LevelState DefaultKeyFilter::ownerTrust() const
{
return d->mOwnerTrust;
}
GpgME::Key::OwnerTrust DefaultKeyFilter::ownerTrustReferenceLevel() const
{
return d->mOwnerTrustReferenceLevel;
}
DefaultKeyFilter::LevelState DefaultKeyFilter::validity() const
{
return d->mValidity;
}
GpgME::UserID::Validity DefaultKeyFilter::validityReferenceLevel() const
{
return d->mValidityReferenceLevel;
}
DefaultKeyFilter::TriState DefaultKeyFilter::isDeVS() const
{
return d->mIsDeVs;
}
DefaultKeyFilter::TriState DefaultKeyFilter::isBad() const
{
return d->mBad;
}
DefaultKeyFilter::TriState DefaultKeyFilter::validIfSMIME() const
{
return d->mValidIfSMIME;
}
diff --git a/src/kleo/defaultkeyfilter.h b/src/kleo/defaultkeyfilter.h
index 0cd0935d..f0ee1296 100644
--- a/src/kleo/defaultkeyfilter.h
+++ b/src/kleo/defaultkeyfilter.h
@@ -1,153 +1,154 @@
/*
defaultkeyfilter.h
This file is part of libkleopatra, the KDE keymanagement library
SPDX-FileCopyrightText: 2004 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik
SPDX-FileContributor: Intevation GmbH
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include "keyfilter.h"
#include "kleo_export.h"
#include <QColor>
#include <QFont>
#include <QString>
#include <gpgme++/key.h>
#include <memory>
namespace Kleo
{
/** Default implementation of key filter class. */
class KLEO_EXPORT DefaultKeyFilter : public KeyFilter
{
public:
DefaultKeyFilter();
~DefaultKeyFilter() override;
/** Used for bool checks */
enum TriState {
// clang-format off
DoesNotMatter = 0,
Set = 1,
NotSet = 2,
// clang-format on
};
/** Used for level checks */
enum LevelState {
// clang-format off
LevelDoesNotMatter = 0,
Is = 1,
IsNot = 2,
IsAtLeast = 3,
IsAtMost = 4,
// clang-format on
};
bool matches(const GpgME::Key &key, MatchContexts ctx) const override;
+ bool matches(const GpgME::UserID &userID, MatchContexts ctx) const override;
unsigned int specificity() const override;
void setSpecificity(unsigned int value);
QString id() const override;
void setId(const QString &value);
KeyFilter::MatchContexts availableMatchContexts() const override;
void setMatchContexts(KeyFilter::MatchContexts value);
QColor fgColor() const override;
void setFgColor(const QColor &value);
QColor bgColor() const override;
void setBgColor(const QColor &value);
FontDescription fontDescription() const override;
QString name() const override;
void setName(const QString &value);
QString icon() const override;
void setIcon(const QString &value);
QFont font() const;
void setFont(const QFont &value);
TriState revoked() const;
TriState expired() const;
TriState invalid() const;
TriState disabled() const;
TriState root() const;
TriState canEncrypt() const;
TriState canSign() const;
TriState canCertify() const;
TriState canAuthenticate() const;
TriState hasEncrypt() const;
TriState hasSign() const;
TriState hasCertify() const;
TriState hasAuthenticate() const;
TriState qualified() const;
TriState cardKey() const;
TriState hasSecret() const;
TriState isOpenPGP() const;
TriState wasValidated() const;
TriState isDeVS() const;
TriState isBad() const;
LevelState ownerTrust() const;
GpgME::Key::OwnerTrust ownerTrustReferenceLevel() const;
LevelState validity() const;
GpgME::UserID::Validity validityReferenceLevel() const;
bool italic() const;
bool bold() const;
bool strikeOut() const;
bool useFullFont() const;
void setRevoked(const TriState);
void setExpired(const TriState);
void setInvalid(const TriState);
void setDisabled(const TriState);
void setRoot(const TriState);
void setCanEncrypt(const TriState);
void setCanSign(const TriState);
void setCanCertify(const TriState);
void setCanAuthenticate(const TriState);
void setHasEncrypt(const TriState);
void setHasSign(const TriState);
void setHasCertify(const TriState);
void setHasAuthenticate(const TriState);
void setQualified(const TriState);
void setCardKey(const TriState);
void setHasSecret(const TriState);
void setIsOpenPGP(const TriState);
void setWasValidated(const TriState);
void setIsDeVs(const TriState);
void setIsBad(const TriState);
/**
* If \p value is \c Set, then invalid S/MIME certificates do not match.
* If \p value is \c NotSet, then valid S/MIME certificates do not match.
*/
void setValidIfSMIME(TriState value);
TriState validIfSMIME() const;
void setOwnerTrust(const LevelState);
void setOwnerTrustReferenceLevel(const GpgME::Key::OwnerTrust);
void setValidity(const LevelState);
void setValidityReferenceLevel(const GpgME::UserID::Validity);
void setItalic(bool value);
void setBold(bool value);
void setStrikeOut(bool value);
void setUseFullFont(bool value);
private:
class Private;
const std::unique_ptr<Private> d;
};
} // namespace Kleo
diff --git a/src/kleo/keyfilter.h b/src/kleo/keyfilter.h
index 6c5be645..ac14e1cc 100644
--- a/src/kleo/keyfilter.h
+++ b/src/kleo/keyfilter.h
@@ -1,105 +1,107 @@
/*
keyfilter.h
This file is part of libkleopatra, the KDE keymanagement library
SPDX-FileCopyrightText: 2004 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include "kleo_export.h"
#include <QFlags>
#include <algorithm>
#include <memory>
namespace GpgME
{
class Key;
+class UserID;
}
class QFont;
class QColor;
class QString;
namespace Kleo
{
/**
@short An abstract base class key filters
*/
class KLEO_EXPORT KeyFilter
{
public:
virtual ~KeyFilter()
{
}
enum MatchContext {
// clang-format off
NoMatchContext = 0x0,
Appearance = 0x1,
Filtering = 0x2,
AnyMatchContext = Appearance | Filtering
// clang-format on
};
Q_DECLARE_FLAGS(MatchContexts, MatchContext)
virtual bool matches(const GpgME::Key &key, MatchContexts ctx) const = 0;
+ virtual bool matches(const GpgME::UserID &userID, MatchContexts ctx) const = 0;
virtual unsigned int specificity() const = 0;
virtual QString id() const = 0;
virtual MatchContexts availableMatchContexts() const = 0;
// not sure if we want these here, but for the time being, it's
// the easiest way:
virtual QColor fgColor() const = 0;
virtual QColor bgColor() const = 0;
virtual QString name() const = 0;
virtual QString icon() const = 0;
class FontDescription
{
public:
FontDescription();
FontDescription(const FontDescription &other);
FontDescription &operator=(const FontDescription &other)
{
FontDescription copy(other);
swap(copy);
return *this;
}
~FontDescription();
static FontDescription create(bool bold, bool italic, bool strikeOut);
static FontDescription create(const QFont &font, bool bold, bool italic, bool strikeOut);
QFont font(const QFont &base) const;
FontDescription resolve(const FontDescription &other) const;
void swap(FontDescription &other)
{
std::swap(this->d, other.d);
}
struct Private;
private:
std::unique_ptr<Private> d;
};
virtual FontDescription fontDescription() const = 0;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(KeyFilter::MatchContexts)
}
#include <QObject>
Q_DECLARE_METATYPE(Kleo::KeyFilter::MatchContexts)
diff --git a/src/kleo/keyfiltermanager.cpp b/src/kleo/keyfiltermanager.cpp
index f16044b1..9be56d99 100644
--- a/src/kleo/keyfiltermanager.cpp
+++ b/src/kleo/keyfiltermanager.cpp
@@ -1,459 +1,481 @@
/*
keyfiltermanager.cpp
This file is part of libkleopatra, the KDE keymanagement library
SPDX-FileCopyrightText: 2004 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-libkleo.h>
#include "keyfiltermanager.h"
#include "defaultkeyfilter.h"
#include "kconfigbasedkeyfilter.h"
#include "stl_util.h"
#include <libkleo/algorithm.h>
#include <libkleo/compliance.h>
#include <libkleo/gnupg.h>
#include <libkleo/keyhelpers.h>
#include <libkleo_debug.h>
#include <KConfig>
#include <KConfigGroup>
#include <KLocalizedString>
#include <KSharedConfig>
#include <QAbstractListModel>
#include <QCoreApplication>
#include <QIcon>
#include <QModelIndex>
#include <QRegularExpression>
#include <QStringList>
#include <algorithm>
#include <climits>
#include <functional>
using namespace Kleo;
using namespace GpgME;
namespace
{
class Model : public QAbstractListModel
{
KeyFilterManager::Private *m_keyFilterManagerPrivate;
public:
explicit Model(KeyFilterManager::Private *p)
: QAbstractListModel(nullptr)
, m_keyFilterManagerPrivate(p)
{
}
int rowCount(const QModelIndex &) const override;
QVariant data(const QModelIndex &idx, int role) const override;
/* upgrade to public */ using QAbstractListModel::beginResetModel;
/* upgrade to public */ using QAbstractListModel::endResetModel;
};
class AllCertificatesKeyFilter : public DefaultKeyFilter
{
public:
AllCertificatesKeyFilter()
: DefaultKeyFilter()
{
setSpecificity(UINT_MAX); // overly high for ordering
setName(i18n("All Certificates"));
setId(QStringLiteral("all-certificates"));
setMatchContexts(Filtering);
}
};
class MyCertificatesKeyFilter : public DefaultKeyFilter
{
public:
MyCertificatesKeyFilter()
: DefaultKeyFilter()
{
setHasSecret(Set);
setSpecificity(UINT_MAX - 1); // overly high for ordering
setName(i18n("My Certificates"));
setId(QStringLiteral("my-certificates"));
setMatchContexts(AnyMatchContext);
setBold(true);
}
};
class TrustedCertificatesKeyFilter : public DefaultKeyFilter
{
public:
TrustedCertificatesKeyFilter()
: DefaultKeyFilter()
{
setRevoked(NotSet);
setValidity(IsAtLeast);
setValidityReferenceLevel(UserID::Marginal);
setSpecificity(UINT_MAX - 2); // overly high for ordering
setName(i18n("Trusted Certificates"));
setId(QStringLiteral("trusted-certificates"));
setMatchContexts(Filtering);
}
};
class FullCertificatesKeyFilter : public DefaultKeyFilter
{
public:
FullCertificatesKeyFilter()
: DefaultKeyFilter()
{
setRevoked(NotSet);
setValidity(IsAtLeast);
setValidityReferenceLevel(UserID::Full);
setSpecificity(UINT_MAX - 3);
setName(i18n("Fully Trusted Certificates"));
setId(QStringLiteral("full-certificates"));
setMatchContexts(Filtering);
}
};
class OtherCertificatesKeyFilter : public DefaultKeyFilter
{
public:
OtherCertificatesKeyFilter()
: DefaultKeyFilter()
{
setHasSecret(NotSet);
setValidity(IsAtMost);
setValidityReferenceLevel(UserID::Never);
setSpecificity(UINT_MAX - 4); // overly high for ordering
setName(i18n("Other Certificates"));
setId(QStringLiteral("other-certificates"));
setMatchContexts(Filtering);
}
};
/* This filter selects uncertified OpenPGP keys, i.e. "good" OpenPGP keys with
* unrevoked user IDs that are not fully valid. */
class UncertifiedOpenPGPKeysFilter : public DefaultKeyFilter
{
public:
UncertifiedOpenPGPKeysFilter()
: DefaultKeyFilter()
{
setSpecificity(UINT_MAX - 6); // overly high for ordering
setName(i18n("Not Certified Certificates"));
setId(QStringLiteral("not-certified-certificates"));
setMatchContexts(Filtering);
setIsOpenPGP(Set);
setIsBad(NotSet);
}
bool matches(const Key &key, MatchContexts contexts) const override
{
return DefaultKeyFilter::matches(key, contexts) && !Kleo::allUserIDsHaveFullValidity(key);
}
};
/* This filter selects only invalid keys (i.e. those where not all
* UIDs are at least fully valid). */
class KeyNotValidFilter : public DefaultKeyFilter
{
public:
KeyNotValidFilter()
: DefaultKeyFilter()
{
setSpecificity(UINT_MAX - 7); // overly high for ordering
setName(i18n("Not Validated Certificates"));
setId(QStringLiteral("not-validated-certificates"));
setMatchContexts(Filtering);
}
bool matches(const Key &key, MatchContexts contexts) const override
{
return DefaultKeyFilter::matches(key, contexts) && !Kleo::allUserIDsHaveFullValidity(key);
}
};
}
static std::vector<std::shared_ptr<KeyFilter>> defaultFilters()
{
std::vector<std::shared_ptr<KeyFilter>> result;
result.reserve(6);
result.push_back(std::shared_ptr<KeyFilter>(new MyCertificatesKeyFilter));
result.push_back(std::shared_ptr<KeyFilter>(new TrustedCertificatesKeyFilter));
result.push_back(std::shared_ptr<KeyFilter>(new FullCertificatesKeyFilter));
result.push_back(std::shared_ptr<KeyFilter>(new OtherCertificatesKeyFilter));
result.push_back(std::shared_ptr<KeyFilter>(new AllCertificatesKeyFilter));
result.push_back(std::shared_ptr<KeyFilter>(new UncertifiedOpenPGPKeysFilter));
result.push_back(std::shared_ptr<KeyFilter>(new KeyNotValidFilter));
return result;
}
class KeyFilterManager::Private
{
public:
Private()
: filters()
, model(this)
{
}
void clear()
{
model.beginResetModel();
filters.clear();
model.endResetModel();
}
std::vector<std::shared_ptr<KeyFilter>> filters;
Model model;
GpgME::Protocol protocol = GpgME::UnknownProtocol;
};
KeyFilterManager *KeyFilterManager::mSelf = nullptr;
KeyFilterManager::KeyFilterManager(QObject *parent)
: QObject(parent)
, d(new Private)
{
mSelf = this;
// ### DF: doesn't a KStaticDeleter work more reliably?
if (QCoreApplication *app = QCoreApplication::instance()) {
connect(app, &QCoreApplication::aboutToQuit, this, &QObject::deleteLater);
}
reload();
}
KeyFilterManager::~KeyFilterManager()
{
mSelf = nullptr;
if (d) {
d->clear();
}
}
KeyFilterManager *KeyFilterManager::instance()
{
if (!mSelf) {
mSelf = new KeyFilterManager();
}
return mSelf;
}
void KeyFilterManager::alwaysFilterByProtocol(GpgME::Protocol protocol)
{
if (protocol != d->protocol) {
d->protocol = protocol;
reload();
}
}
const std::shared_ptr<KeyFilter> &KeyFilterManager::filterMatching(const Key &key, KeyFilter::MatchContexts contexts) const
{
const auto it = std::find_if(d->filters.cbegin(), d->filters.cend(), [&key, contexts](const std::shared_ptr<KeyFilter> &filter) {
return filter->matches(key, contexts);
});
if (it != d->filters.cend()) {
return *it;
}
static const std::shared_ptr<KeyFilter> null;
return null;
}
std::vector<std::shared_ptr<KeyFilter>> KeyFilterManager::filtersMatching(const Key &key, KeyFilter::MatchContexts contexts) const
{
std::vector<std::shared_ptr<KeyFilter>> result;
result.reserve(d->filters.size());
std::remove_copy_if(d->filters.begin(), d->filters.end(), std::back_inserter(result), [&key, contexts](const std::shared_ptr<KeyFilter> &filter) {
return !filter->matches(key, contexts);
});
return result;
}
namespace
{
static const auto byDecreasingSpecificity = [](const std::shared_ptr<KeyFilter> &lhs, const std::shared_ptr<KeyFilter> &rhs) {
return lhs->specificity() > rhs->specificity();
};
}
void KeyFilterManager::reload()
{
d->clear();
d->filters = defaultFilters();
KSharedConfigPtr config = KSharedConfig::openConfig(QStringLiteral("libkleopatrarc"));
const QStringList groups = config->groupList().filter(QRegularExpression(QStringLiteral("^Key Filter #\\d+$")));
const bool ignoreDeVs = !DeVSCompliance::isCompliant();
for (QStringList::const_iterator it = groups.begin(); it != groups.end(); ++it) {
const KConfigGroup cfg(config, *it);
if (cfg.hasKey("is-de-vs") && ignoreDeVs) {
/* Don't show de-vs filters in other compliance modes */
continue;
}
d->filters.push_back(std::shared_ptr<KeyFilter>(new KConfigBasedKeyFilter(cfg)));
}
std::stable_sort(d->filters.begin(), d->filters.end(), byDecreasingSpecificity);
if (d->protocol != GpgME::UnknownProtocol) {
// remove filters with conflicting isOpenPGP rule
const auto conflictingValue = (d->protocol == GpgME::OpenPGP) ? DefaultKeyFilter::NotSet : DefaultKeyFilter::Set;
Kleo::erase_if(d->filters, [conflictingValue](const auto &f) {
const auto filter = std::dynamic_pointer_cast<DefaultKeyFilter>(f);
Q_ASSERT(filter);
return filter->isOpenPGP() == conflictingValue;
});
// add isOpenPGP rule to all filters
const auto isOpenPGPValue = (d->protocol == GpgME::OpenPGP) ? DefaultKeyFilter::Set : DefaultKeyFilter::NotSet;
std::for_each(std::begin(d->filters), std::end(d->filters), [isOpenPGPValue](auto &f) {
const auto filter = std::dynamic_pointer_cast<DefaultKeyFilter>(f);
Q_ASSERT(filter);
return filter->setIsOpenPGP(isOpenPGPValue);
});
}
qCDebug(LIBKLEO_LOG) << "KeyFilterManager::" << __func__ << "final filter count is" << d->filters.size();
}
QAbstractItemModel *KeyFilterManager::model() const
{
return &d->model;
}
const std::shared_ptr<KeyFilter> &KeyFilterManager::keyFilterByID(const QString &id) const
{
const auto it = std::find_if(d->filters.begin(), d->filters.end(), [id](const std::shared_ptr<KeyFilter> &filter) {
return filter->id() == id;
});
if (it != d->filters.end()) {
return *it;
}
static const std::shared_ptr<KeyFilter> null;
return null;
}
const std::shared_ptr<KeyFilter> &KeyFilterManager::fromModelIndex(const QModelIndex &idx) const
{
if (!idx.isValid() || idx.model() != &d->model || idx.row() < 0 || static_cast<unsigned>(idx.row()) >= d->filters.size()) {
static const std::shared_ptr<KeyFilter> null;
return null;
}
return d->filters[idx.row()];
}
QModelIndex KeyFilterManager::toModelIndex(const std::shared_ptr<KeyFilter> &kf) const
{
if (!kf) {
return {};
}
const auto pair = std::equal_range(d->filters.cbegin(), d->filters.cend(), kf, byDecreasingSpecificity);
const auto it = std::find(pair.first, pair.second, kf);
if (it != pair.second) {
return d->model.index(it - d->filters.begin());
} else {
return QModelIndex();
}
}
int Model::rowCount(const QModelIndex &) const
{
return m_keyFilterManagerPrivate->filters.size();
}
QVariant Model::data(const QModelIndex &idx, int role) const
{
if (!idx.isValid() || idx.model() != this || idx.row() < 0 || static_cast<unsigned>(idx.row()) > m_keyFilterManagerPrivate->filters.size()) {
return QVariant();
}
const auto filter = m_keyFilterManagerPrivate->filters[idx.row()];
switch (role) {
case Qt::DecorationRole:
return filter->icon();
case Qt::DisplayRole:
case Qt::EditRole:
case Qt::ToolTipRole: /* Most useless tooltip ever. */
return filter->name();
case KeyFilterManager::FilterIdRole:
return filter->id();
case KeyFilterManager::FilterMatchContextsRole:
return QVariant::fromValue(filter->availableMatchContexts());
default:
return QVariant();
}
}
static KeyFilter::FontDescription
get_fontdescription(const std::vector<std::shared_ptr<KeyFilter>> &filters, const Key &key, const KeyFilter::FontDescription &initial)
{
return kdtools::accumulate_if(
filters.begin(),
filters.end(),
[&key](const std::shared_ptr<KeyFilter> &filter) {
return filter->matches(key, KeyFilter::Appearance);
},
initial,
[](const KeyFilter::FontDescription &lhs, const std::shared_ptr<KeyFilter> &rhs) {
return lhs.resolve(rhs->fontDescription());
});
}
QFont KeyFilterManager::font(const Key &key, const QFont &baseFont) const
{
const KeyFilter::FontDescription fd = get_fontdescription(d->filters, key, KeyFilter::FontDescription());
return fd.font(baseFont);
}
static QColor get_color(const std::vector<std::shared_ptr<KeyFilter>> &filters, const Key &key, QColor (KeyFilter::*fun)() const)
{
const auto it = std::find_if(filters.cbegin(), filters.cend(), [&fun, &key](const std::shared_ptr<KeyFilter> &filter) {
return filter->matches(key, KeyFilter::Appearance) && (filter.get()->*fun)().isValid();
});
if (it == filters.cend()) {
return {};
} else {
return (it->get()->*fun)();
}
}
+static QColor get_color(const std::vector<std::shared_ptr<KeyFilter>> &filters, const UserID &userID, QColor (KeyFilter::*fun)() const)
+{
+ const auto it = std::find_if(filters.cbegin(), filters.cend(), [&fun, &userID](const std::shared_ptr<KeyFilter> &filter) {
+ return filter->matches(userID, KeyFilter::Appearance) && (filter.get()->*fun)().isValid();
+ });
+ if (it == filters.cend()) {
+ return {};
+ } else {
+ return (it->get()->*fun)();
+ }
+}
+
static QString get_string(const std::vector<std::shared_ptr<KeyFilter>> &filters, const Key &key, QString (KeyFilter::*fun)() const)
{
const auto it = std::find_if(filters.cbegin(), filters.cend(), [&fun, &key](const std::shared_ptr<KeyFilter> &filter) {
return filter->matches(key, KeyFilter::Appearance) && !(filter.get()->*fun)().isEmpty();
});
if (it == filters.cend()) {
return QString();
} else {
return (*it)->icon();
}
}
QColor KeyFilterManager::bgColor(const Key &key) const
{
return get_color(d->filters, key, &KeyFilter::bgColor);
}
QColor KeyFilterManager::fgColor(const Key &key) const
{
return get_color(d->filters, key, &KeyFilter::fgColor);
}
+QColor KeyFilterManager::bgColor(const UserID &userID) const
+{
+ return get_color(d->filters, userID, &KeyFilter::bgColor);
+}
+
+QColor KeyFilterManager::fgColor(const UserID &userID) const
+{
+ return get_color(d->filters, userID, &KeyFilter::fgColor);
+}
+
QIcon KeyFilterManager::icon(const Key &key) const
{
const QString icon = get_string(d->filters, key, &KeyFilter::icon);
return icon.isEmpty() ? QIcon() : QIcon::fromTheme(icon);
}
diff --git a/src/kleo/keyfiltermanager.h b/src/kleo/keyfiltermanager.h
index 533637c8..9892cf50 100644
--- a/src/kleo/keyfiltermanager.h
+++ b/src/kleo/keyfiltermanager.h
@@ -1,81 +1,84 @@
/*
keyfiltermanager.h
This file is part of libkleopatra, the KDE keymanagement library
SPDX-FileCopyrightText: 2004 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include "kleo_export.h"
#include <Libkleo/KeyFilter>
#include <QObject>
#include <gpgme++/global.h>
#include <memory>
#include <vector>
namespace GpgME
{
class Key;
+class UserID;
}
class QAbstractItemModel;
class QModelIndex;
class QFont;
class QColor;
class QIcon;
namespace Kleo
{
class KLEO_EXPORT KeyFilterManager : public QObject
{
Q_OBJECT
public:
enum ModelRoles {
FilterIdRole = Qt::UserRole,
FilterMatchContextsRole,
};
protected:
explicit KeyFilterManager(QObject *parent = nullptr);
~KeyFilterManager() override;
public:
static KeyFilterManager *instance();
/**
* Adds the rule that keys must match @p protocol to all filters.
*/
void alwaysFilterByProtocol(GpgME::Protocol protocol);
const std::shared_ptr<KeyFilter> &filterMatching(const GpgME::Key &key, KeyFilter::MatchContexts contexts) const;
std::vector<std::shared_ptr<KeyFilter>> filtersMatching(const GpgME::Key &key, KeyFilter::MatchContexts contexts) const;
QAbstractItemModel *model() const;
const std::shared_ptr<KeyFilter> &keyFilterByID(const QString &id) const;
const std::shared_ptr<KeyFilter> &fromModelIndex(const QModelIndex &mi) const;
QModelIndex toModelIndex(const std::shared_ptr<KeyFilter> &kf) const;
void reload();
QFont font(const GpgME::Key &key, const QFont &baseFont) const;
QColor bgColor(const GpgME::Key &key) const;
+ QColor bgColor(const GpgME::UserID &userID) const;
QColor fgColor(const GpgME::Key &key) const;
+ QColor fgColor(const GpgME::UserID &userID) const;
QIcon icon(const GpgME::Key &key) const;
class Private;
private:
std::unique_ptr<Private> d;
static KeyFilterManager *mSelf;
};
}
diff --git a/src/models/useridproxymodel.cpp b/src/models/useridproxymodel.cpp
new file mode 100644
index 00000000..3b7efd05
--- /dev/null
+++ b/src/models/useridproxymodel.cpp
@@ -0,0 +1,189 @@
+/*
+ SPDX-FileCopyrightText: 2024 g10 Code GmbH
+ SPDX-FileContributor: Tobias Fella <tobias.fella@gnupg.com>
+ SPDX-License-Identifier: GPL-2.0-or-later
+*/
+
+#include "useridproxymodel.h"
+
+#include "keylist.h"
+#include "keylistmodel.h"
+#include "kleo/keyfiltermanager.h"
+#include "utils/formatting.h"
+#include "utils/systeminfo.h"
+
+#include <global.h>
+
+#include <QColor>
+
+using namespace Kleo;
+
+UserIDProxyModel::UserIDProxyModel(QObject *parent)
+ : AbstractKeyListSortFilterProxyModel(parent)
+{
+}
+
+static QVariant returnIfValid(const QColor &t)
+{
+ if (t.isValid()) {
+ return t;
+ } else {
+ return QVariant();
+ }
+}
+
+QModelIndex UserIDProxyModel::mapFromSource(const QModelIndex &sourceIndex) const
+{
+ if (!sourceIndex.isValid()) {
+ return {};
+ }
+ int row = 0;
+ for (int i = 0; i < sourceIndex.row(); i++) {
+ row += userIDsOfSourceRow(i);
+ }
+ return index(row, sourceIndex.column(), {});
+}
+
+QModelIndex UserIDProxyModel::mapToSource(const QModelIndex &proxyIndex) const
+{
+ if (!proxyIndex.isValid()) {
+ return {};
+ }
+ return sourceModel()->index(sourceRowForProxyIndex(proxyIndex), proxyIndex.column(), {});
+}
+
+int UserIDProxyModel::rowCount(const QModelIndex &parent) const
+{
+ if (parent.isValid()) {
+ return 0;
+ }
+ int sum = 0;
+ for (int i = 0; i < sourceModel()->rowCount(); i++) {
+ sum += userIDsOfSourceRow(i);
+ }
+ return sum;
+}
+
+QModelIndex UserIDProxyModel::index(int row, int column, const QModelIndex &parent) const
+{
+ if (parent.isValid()) {
+ return {};
+ }
+ return createIndex(row, column, nullptr);
+}
+
+QModelIndex UserIDProxyModel::parent(const QModelIndex &) const
+{
+ return {};
+}
+
+int UserIDProxyModel::columnCount(const QModelIndex &index) const
+{
+ return sourceModel()->columnCount(mapToSource(index));
+}
+
+QVariant UserIDProxyModel::data(const QModelIndex &index, int role) const
+{
+ const auto row = sourceRowForProxyIndex(index);
+ const auto offset = sourceOffsetForProxyIndex(index);
+ const auto model = dynamic_cast<AbstractKeyListModel *>(sourceModel());
+ const auto key = model->key(model->index(row, 0));
+ if (key.isNull()) {
+ return AbstractKeyListSortFilterProxyModel::data(index, role);
+ }
+ const auto userId = key.userID(offset);
+ if ((role == Qt::DisplayRole || role == Qt::EditRole || role == Qt::AccessibleTextRole)) {
+ if (index.column() == KeyList::Columns::PrettyName) {
+ auto name = QString::fromUtf8(userId.name());
+ if (name.isEmpty()) {
+ return AbstractKeyListSortFilterProxyModel::data(index, role);
+ }
+ return name;
+ }
+ if (index.column() == KeyList::Columns::PrettyEMail) {
+ return QString::fromUtf8(userId.email());
+ }
+ if (index.column() == KeyList::Columns::Validity) {
+ return Formatting::complianceStringShort(userId);
+ }
+ if (index.column() == KeyList::Columns::Summary) {
+ return Formatting::summaryLine(userId);
+ }
+ if (index.column() == KeyList::Columns::Origin) {
+ return Formatting::origin(userId.origin());
+ }
+ if (index.column() == KeyList::Columns::LastUpdate) {
+ if (role == Qt::AccessibleTextRole) {
+ return Formatting::accessibleDate(userId.lastUpdate());
+ } else {
+ return Formatting::dateString(userId.lastUpdate());
+ }
+ }
+ }
+ if (role == Qt::BackgroundRole) {
+ if (!SystemInfo::isHighContrastModeActive()) {
+ return returnIfValid(KeyFilterManager::instance()->bgColor(userId));
+ }
+ } else if (role == Qt::ForegroundRole) {
+ if (!SystemInfo::isHighContrastModeActive()) {
+ return returnIfValid(KeyFilterManager::instance()->fgColor(userId));
+ }
+ }
+ return AbstractKeyListSortFilterProxyModel::data(index, role);
+}
+
+int UserIDProxyModel::sourceRowForProxyIndex(const QModelIndex &index) const
+{
+ int row = index.row();
+ int i;
+ for (i = 0; row >= userIDsOfSourceRow(i); i++) {
+ row -= userIDsOfSourceRow(i);
+ }
+ return i;
+}
+
+int UserIDProxyModel::sourceOffsetForProxyIndex(const QModelIndex &index) const
+{
+ int row = index.row();
+ int i;
+ for (i = 0; row >= userIDsOfSourceRow(i); i++) {
+ row -= userIDsOfSourceRow(i);
+ }
+ auto model = dynamic_cast<AbstractKeyListModel *>(sourceModel());
+ auto key = model->key(model->index(sourceRowForProxyIndex(index), 0));
+ int tmp = row;
+ for (int j = 0; j <= tmp; j++) {
+ // account for filtered out S/MIME user IDs
+ if (key.protocol() == GpgME::Protocol::CMS && !key.userID(j).email()) {
+ row++;
+ }
+ }
+ return row;
+}
+
+int UserIDProxyModel::userIDsOfSourceRow(int sourceRow) const
+{
+ auto model = dynamic_cast<AbstractKeyListModel *>(sourceModel());
+ auto key = model->key(model->index(sourceRow, 0));
+
+ if (key.protocol() == GpgME::OpenPGP) {
+ return key.numUserIDs();
+ }
+ // Try to filter out some useless SMIME user ids
+ int count = 0;
+ const auto &uids = key.userIDs();
+ for (auto it = uids.begin(); it != uids.end(); ++it) {
+ const auto &uid = *it;
+ if (uid.email()) {
+ count++;
+ }
+ }
+ return count;
+}
+
+UserIDProxyModel *UserIDProxyModel::clone() const
+{
+ auto model = new UserIDProxyModel(QObject::parent());
+ model->setSourceModel(sourceModel());
+ return model;
+}
diff --git a/src/models/useridproxymodel.h b/src/models/useridproxymodel.h
new file mode 100644
index 00000000..213c68af
--- /dev/null
+++ b/src/models/useridproxymodel.h
@@ -0,0 +1,34 @@
+/*
+ SPDX-FileCopyrightText: 2024 g10 Code GmbH
+ SPDX-FileContributor: Tobias Fella <tobias.fella@gnupg.com>
+ SPDX-License-Identifier: GPL-2.0-or-later
+*/
+
+#pragma once
+
+#include "kleo_export.h"
+#include <Libkleo/KeyListSortFilterProxyModel>
+
+namespace Kleo
+{
+
+class KLEO_EXPORT UserIDProxyModel : public Kleo::AbstractKeyListSortFilterProxyModel
+{
+ Q_OBJECT
+
+public:
+ explicit UserIDProxyModel(QObject *parent = nullptr);
+
+ QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override;
+ QModelIndex mapToSource(const QModelIndex &proxyIndex) const override;
+ int rowCount(const QModelIndex &parent = {}) const override;
+ QModelIndex index(int row, int column, const QModelIndex &parent) const override;
+ virtual QModelIndex parent(const QModelIndex &) const override;
+ int columnCount(const QModelIndex &) const override;
+ QVariant data(const QModelIndex &index, int role) const override;
+ int sourceRowForProxyIndex(const QModelIndex &index) const;
+ int sourceOffsetForProxyIndex(const QModelIndex &index) const;
+ int userIDsOfSourceRow(int sourceRow) const;
+ UserIDProxyModel *clone() const override;
+};
+}
diff --git a/src/utils/compliance.cpp b/src/utils/compliance.cpp
index 3c9f3c90..722d3126 100644
--- a/src/utils/compliance.cpp
+++ b/src/utils/compliance.cpp
@@ -1,150 +1,161 @@
/* -*- mode: c++; c-basic-offset:4 -*-
utils/compliance.cpp
This file is part of libkleopatra
SPDX-FileCopyrightText: 2022 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-libkleo.h>
#include "compliance.h"
#include "algorithm.h"
#include "cryptoconfig.h"
#include "gnupg.h"
#include "keyhelpers.h"
#include "stringutils.h"
#include "systeminfo.h"
#include <libkleo/debug.h>
#include <libkleo/keyfiltermanager.h>
#include <libkleo_debug.h>
#include <KColorScheme>
#include <KLocalizedString>
#include <QPushButton>
#include <gpgme++/key.h>
bool Kleo::DeVSCompliance::isActive()
{
return getCryptoConfigStringValue("gpg", "compliance") == QLatin1String{"de-vs"};
}
bool Kleo::DeVSCompliance::isCompliant()
{
if (!isActive()) {
return false;
}
// The pseudo option compliance_de_vs was fully added in 2.2.34;
// For versions between 2.2.28 and 2.2.33 there was a broken config
// value with a wrong type. So for them we add an extra check. This
// can be removed in future versions because for GnuPG we could assume
// non-compliance for older versions as versions of Kleopatra for
// which this matters are bundled with new enough versions of GnuPG anyway.
if (engineIsVersion(2, 2, 28) && !engineIsVersion(2, 2, 34)) {
return true;
}
return getCryptoConfigIntValue("gpg", "compliance_de_vs", 0) != 0;
}
bool Kleo::DeVSCompliance::algorithmIsCompliant(std::string_view algo)
{
return !isActive() || Kleo::contains(compliantAlgorithms(), algo);
}
bool Kleo::DeVSCompliance::allSubkeysAreCompliant(const GpgME::Key &key)
{
if (!isActive()) {
return true;
}
// there is at least one usable subkey
const auto usableSubkeys = Kleo::count_if(key.subkeys(), [](const auto &sub) {
return !sub.isExpired() && !sub.isRevoked();
});
if (usableSubkeys == 0) {
qCDebug(LIBKLEO_LOG) << __func__ << "No usable subkeys found for key" << key;
return false;
}
// and all usable subkeys are compliant
return Kleo::all_of(key.subkeys(), [](const auto &sub) {
return sub.isDeVs() || sub.isExpired() || sub.isRevoked();
});
}
+bool Kleo::DeVSCompliance::userIDIsCompliant(const GpgME::UserID &id)
+{
+ if (!isActive()) {
+ return true;
+ }
+ return (id.parent().keyListMode() & GpgME::Validate) //
+ && !id.isRevoked() //
+ && id.validity() >= GpgME::UserID::Full //
+ && allSubkeysAreCompliant(id.parent());
+}
+
bool Kleo::DeVSCompliance::keyIsCompliant(const GpgME::Key &key)
{
if (!isActive()) {
return true;
}
return (key.keyListMode() & GpgME::Validate) //
&& allUserIDsHaveFullValidity(key) //
&& allSubkeysAreCompliant(key);
}
const std::vector<std::string> &Kleo::DeVSCompliance::compliantAlgorithms()
{
static const std::vector<std::string> compliantAlgos = {
"brainpoolP256r1",
"brainpoolP384r1",
"brainpoolP512r1",
"rsa3072",
"rsa4096",
};
return isActive() ? compliantAlgos : Kleo::availableAlgorithms();
}
const std::vector<std::string> &Kleo::DeVSCompliance::preferredCompliantAlgorithms()
{
static std::vector<std::string> result;
if (result.empty()) {
const auto &preferredAlgos = Kleo::preferredAlgorithms();
result.reserve(preferredAlgos.size());
Kleo::copy_if(preferredAlgos, std::back_inserter(result), Kleo::DeVSCompliance::algorithmIsCompliant);
}
return result;
}
void Kleo::DeVSCompliance::decorate(QPushButton *button)
{
decorate(button, isCompliant());
}
void Kleo::DeVSCompliance::decorate(QPushButton *button, bool compliant)
{
if (!button) {
return;
}
if (compliant) {
button->setIcon(QIcon::fromTheme(QStringLiteral("security-high")));
if (!SystemInfo::isHighContrastModeActive()) {
const auto bgColor = KColorScheme(QPalette::Active, KColorScheme::View).background(KColorScheme::PositiveBackground).color().name();
button->setStyleSheet(QStringLiteral("QPushButton { background-color: %1; };").arg(bgColor));
}
} else {
button->setIcon(QIcon::fromTheme(QStringLiteral("security-medium")));
if (!SystemInfo::isHighContrastModeActive()) {
const auto bgColor = KColorScheme(QPalette::Active, KColorScheme::View).background(KColorScheme::NegativeBackground).color().name();
button->setStyleSheet(QStringLiteral("QPushButton { background-color: %1; };").arg(bgColor));
}
}
}
QString Kleo::DeVSCompliance::name()
{
return name(isCompliant());
}
QString Kleo::DeVSCompliance::name(bool compliant)
{
const auto filterId = compliant ? QStringLiteral("de-vs-filter") : QStringLiteral("not-de-vs-filter");
if (auto filter = KeyFilterManager::instance()->keyFilterByID(filterId)) {
return filter->name();
}
return compliant ? i18n("VS-NfD compliant") : i18n("Not VS-NfD compliant");
}
diff --git a/src/utils/compliance.h b/src/utils/compliance.h
index 7344f5ba..9f1fd707 100644
--- a/src/utils/compliance.h
+++ b/src/utils/compliance.h
@@ -1,114 +1,123 @@
/* -*- mode: c++; c-basic-offset:4 -*-
utils/compliance.h
This file is part of libkleopatra
SPDX-FileCopyrightText: 2022 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include "kleo_export.h"
#include <string_view>
#include <vector>
class QPushButton;
class QString;
namespace GpgME
{
class Key;
+class UserID;
}
namespace Kleo::DeVSCompliance
{
/**
* Returns true, if compliance mode "de-vs" is configured for GnuPG.
* Note: It does not check whether the used GnuPG is actually compliant.
*/
KLEO_EXPORT bool isActive();
/**
* Returns true, if compliance mode "de-vs" is configured for GnuPG and if
* GnuPG passes a basic compliance check, i.e. at least libgcrypt and the used
* RNG are compliant.
*/
KLEO_EXPORT bool isCompliant();
/**
* Returns true, if the given algorithm is compliant with compliance mode
* "de-vs". Always returns true, if compliance mode "de-vs" is not active.
*/
KLEO_EXPORT bool algorithmIsCompliant(std::string_view algo);
/**
* Returns true, if all usable subkeys of the key \p key are compliant with
* compliance mode "de-vs". Usable subkeys are those that are neither revoked
* nor expired. If the key doesn't have any usable subkeys, then false is
* returned.
* Always returns true, if compliance mode "de-vs" is not active.
*/
KLEO_EXPORT bool allSubkeysAreCompliant(const GpgME::Key &key);
+/**
+ * Returns true, if the key \p key is compliant with compliance mode "de-vs".
+ * This function behaves like DeVSCompliance::keyIsCompliant, but only considers
+ * user id \p id; all other user ids are ignored.
+ * \see keyIsCompliant
+ */
+bool userIDIsCompliant(const GpgME::UserID &id);
+
/**
* Returns true, if the key \p key is compliant with compliance mode "de-vs".
* A key is considered compliant if all usable subkeys are compliant and if
* all not revoked user IDs have at least full validity. The second condition
* requires that the key has been validated.
* Always returns true, if compliance mode "de-vs" is not active.
*
* \see allSubkeysAreCompliant
*/
KLEO_EXPORT bool keyIsCompliant(const GpgME::Key &key);
/**
* Returns a static list of the available compliant algorithms.
*/
KLEO_EXPORT const std::vector<std::string> &compliantAlgorithms();
/**
* Returns a static list of the preferred compliant algorithms with decreasing
* preference.
* Can be used to determine the default algorithm for generating new keys.
*/
KLEO_EXPORT const std::vector<std::string> &preferredCompliantAlgorithms();
/**
* \overload
*
* Sets the appropriate icon and, unless high-contrast mode is active, the
* appropriate background color of \p button depending on the state of
* compliance.
*/
KLEO_EXPORT void decorate(QPushButton *button);
/**
* Sets the appropriate icon and, unless high-contrast mode is active, the
* appropriate background color of \p button depending on the value of
* \p compliant.
*/
KLEO_EXPORT void decorate(QPushButton *button, bool compliant);
/**
* \overload
*
* Returns a localized name for the compliance or non-compliance depending on
* the state of compliance.
*/
KLEO_EXPORT QString name();
/**
* Returns a localized name for the compliance or non-compliance depending on
* the value of \p compliant.
*
* \note The localized name is taken from the de-vs-filter filter resp. the
* not-de-vs-filter. This allows the customization of the name for different
* users because VS-NfD compliance is called differently in different
* environments, e.g. NATO RESTRICTED or EU RESTRICTED.
*/
KLEO_EXPORT QString name(bool compliant);
}
diff --git a/src/utils/formatting.cpp b/src/utils/formatting.cpp
index c77605cf..2c0142fb 100644
--- a/src/utils/formatting.cpp
+++ b/src/utils/formatting.cpp
@@ -1,1421 +1,1475 @@
/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; -*-
utils/formatting.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2021, 2022 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-libkleo.h>
#include "formatting.h"
#include "algorithm.h"
#include "compat.h"
#include "compliance.h"
#include "cryptoconfig.h"
#include "gnupg.h"
#include "keyhelpers.h"
#include <libkleo/dn.h>
#include <libkleo/keycache.h>
#include <libkleo/keygroup.h>
#include <libkleo_debug.h>
#include <KEmailAddress>
#include <KLocalizedString>
#include <QGpgME/CryptoConfig>
#include <QGpgME/Protocol>
#include <QDateTime>
#include <QIcon>
#include <QLocale>
#include <QRegularExpression>
#include <QString>
#include <QTextDocument> // for Qt::escape
#include <gpgme++/importresult.h>
#include <gpgme++/key.h>
#include <gpg-error.h>
using namespace GpgME;
using namespace Kleo;
namespace
{
QIcon iconForValidityAndCompliance(UserID::Validity validity, bool isCompliant)
{
switch (validity) {
case UserID::Ultimate:
case UserID::Full:
case UserID::Marginal:
return isCompliant ? Formatting::successIcon() : Formatting::infoIcon();
case UserID::Never:
return Formatting::errorIcon();
case UserID::Undefined:
case UserID::Unknown:
default:
return Formatting::infoIcon();
}
}
QIcon iconForValidity(const UserID &userId)
{
const bool keyIsCompliant = !DeVSCompliance::isActive() || //
(DeVSCompliance::isCompliant() && DeVSCompliance::keyIsCompliant(userId.parent()));
return iconForValidityAndCompliance(userId.validity(), keyIsCompliant);
}
}
QIcon Formatting::IconProvider::icon(const GpgME::Key &key) const
{
if (usage.canEncrypt() && !Kleo::canBeUsedForEncryption(key)) {
return Formatting::errorIcon();
}
if (usage.canSign() && !Kleo::canBeUsedForSigning(key)) {
return Formatting::errorIcon();
}
if (key.isBad()) {
return Formatting::errorIcon();
}
const auto primaryUserId = key.userID(0);
if (Kleo::isRevokedOrExpired(primaryUserId)) {
return Formatting::errorIcon();
}
return iconForValidity(primaryUserId);
}
QIcon Formatting::IconProvider::icon(const KeyGroup &group) const
{
if (usage.canEncrypt() && !Kleo::all_of(group.keys(), Kleo::canBeUsedForEncryption)) {
return Formatting::errorIcon();
}
if (usage.canSign() && !Kleo::all_of(group.keys(), Kleo::canBeUsedForSigning)) {
return Formatting::errorIcon();
}
return validityIcon(group);
}
QIcon Formatting::successIcon()
{
return QIcon::fromTheme(QStringLiteral("emblem-success"));
}
QIcon Formatting::infoIcon()
{
return QIcon::fromTheme(QStringLiteral("emblem-information"));
}
QIcon Formatting::questionIcon()
{
return QIcon::fromTheme(QStringLiteral("emblem-question"));
}
QIcon Formatting::unavailableIcon()
{
return QIcon::fromTheme(QStringLiteral("emblem-unavailable"));
}
QIcon Formatting::warningIcon()
{
return QIcon::fromTheme(QStringLiteral("emblem-warning"));
}
QIcon Formatting::errorIcon()
{
return QIcon::fromTheme(QStringLiteral("emblem-error"));
}
//
// Name
//
QString Formatting::prettyName(int proto, const char *id, const char *name_, const char *comment_)
{
if (proto == GpgME::OpenPGP) {
const QString name = QString::fromUtf8(name_);
if (name.isEmpty()) {
return QString();
}
const QString comment = QString::fromUtf8(comment_);
if (comment.isEmpty()) {
return name;
}
return QStringLiteral("%1 (%2)").arg(name, comment);
}
if (proto == GpgME::CMS) {
const DN subject(id);
const QString cn = subject[QStringLiteral("CN")].trimmed();
if (cn.isEmpty()) {
return subject.prettyDN();
}
return cn;
}
return QString();
}
QString Formatting::prettyNameAndEMail(int proto, const char *id, const char *name_, const char *email_, const char *comment_)
{
return prettyNameAndEMail(proto, QString::fromUtf8(id), QString::fromUtf8(name_), prettyEMail(email_, id), QString::fromUtf8(comment_));
}
QString Formatting::prettyNameAndEMail(int proto, const QString &id, const QString &name, const QString &email, const QString &comment)
{
if (proto == GpgME::OpenPGP) {
if (name.isEmpty()) {
if (email.isEmpty()) {
return QString();
} else if (comment.isEmpty()) {
return QStringLiteral("<%1>").arg(email);
} else {
return QStringLiteral("(%2) <%1>").arg(email, comment);
}
}
if (email.isEmpty()) {
if (comment.isEmpty()) {
return name;
} else {
return QStringLiteral("%1 (%2)").arg(name, comment);
}
}
if (comment.isEmpty()) {
return QStringLiteral("%1 <%2>").arg(name, email);
} else {
return QStringLiteral("%1 (%3) <%2>").arg(name, email, comment);
}
}
if (proto == GpgME::CMS) {
const DN subject(id);
const QString cn = subject[QStringLiteral("CN")].trimmed();
if (cn.isEmpty()) {
return subject.prettyDN();
}
return cn;
}
return QString();
}
QString Formatting::prettyUserID(const UserID &uid)
{
if (uid.parent().protocol() == GpgME::OpenPGP) {
return prettyNameAndEMail(uid);
}
const QByteArray id = QByteArray(uid.id()).trimmed();
if (id.startsWith('<')) {
return prettyEMail(uid.email(), uid.id());
}
if (id.startsWith('(')) {
// ### parse uri/dns:
return QString::fromUtf8(uid.id());
} else {
return DN(uid.id()).prettyDN();
}
}
QString Formatting::prettyKeyID(const char *id)
{
if (!id) {
return QString();
}
return QLatin1String("0x") + QString::fromLatin1(id).toUpper();
}
QString Formatting::prettyNameAndEMail(const UserID &uid)
{
return prettyNameAndEMail(uid.parent().protocol(), uid.id(), uid.name(), uid.email(), uid.comment());
}
QString Formatting::prettyNameAndEMail(const Key &key)
{
return prettyNameAndEMail(key.userID(0));
}
QString Formatting::prettyName(const Key &key)
{
return prettyName(key.userID(0));
}
QString Formatting::prettyName(const UserID &uid)
{
return prettyName(uid.parent().protocol(), uid.id(), uid.name(), uid.comment());
}
QString Formatting::prettyName(const UserID::Signature &sig)
{
return prettyName(GpgME::OpenPGP, sig.signerUserID(), sig.signerName(), sig.signerComment());
}
//
// EMail
//
QString Formatting::prettyEMail(const Key &key)
{
for (unsigned int i = 0, end = key.numUserIDs(); i < end; ++i) {
const QString email = prettyEMail(key.userID(i));
if (!email.isEmpty()) {
return email;
}
}
return QString();
}
QString Formatting::prettyEMail(const UserID &uid)
{
return prettyEMail(uid.email(), uid.id());
}
QString Formatting::prettyEMail(const UserID::Signature &sig)
{
return prettyEMail(sig.signerEmail(), sig.signerUserID());
}
QString Formatting::prettyEMail(const char *email_, const char *id)
{
QString email;
QString name;
QString comment;
if (email_ && KEmailAddress::splitAddress(QString::fromUtf8(email_), name, email, comment) == KEmailAddress::AddressOk) {
return email;
} else {
return DN(id)[QStringLiteral("EMAIL")].trimmed();
}
}
//
// Tooltip
//
namespace
{
static QString protect_whitespace(QString s)
{
static const QLatin1Char SP(' ');
static const QLatin1Char NBSP('\xA0');
return s.replace(SP, NBSP);
}
template<typename T_arg>
QString format_row(const QString &field, const T_arg &arg)
{
return QStringLiteral("<tr><th>%1:</th><td>%2</td></tr>").arg(protect_whitespace(field), arg);
}
QString format_row(const QString &field, const QString &arg)
{
return QStringLiteral("<tr><th>%1:</th><td>%2</td></tr>").arg(protect_whitespace(field), arg.toHtmlEscaped());
}
QString format_row(const QString &field, const char *arg)
{
return format_row(field, QString::fromUtf8(arg));
}
QString format_keytype(const Key &key)
{
const Subkey subkey = key.subkey(0);
if (key.hasSecret()) {
return i18n("%1-bit %2 (secret key available)", subkey.length(), QLatin1String(subkey.publicKeyAlgorithmAsString()));
} else {
return i18n("%1-bit %2", subkey.length(), QLatin1String(subkey.publicKeyAlgorithmAsString()));
}
}
QString format_subkeytype(const Subkey &subkey)
{
const auto algo = subkey.publicKeyAlgorithm();
if (algo == Subkey::AlgoECC || algo == Subkey::AlgoECDSA || algo == Subkey::AlgoECDH || algo == Subkey::AlgoEDDSA) {
return QString::fromStdString(subkey.algoName());
}
return i18n("%1-bit %2", subkey.length(), QLatin1String(subkey.publicKeyAlgorithmAsString()));
}
QString format_keyusage(const Key &key)
{
QStringList capabilities;
if (Kleo::keyHasSign(key)) {
if (key.isQualified()) {
capabilities.push_back(i18n("Signing (Qualified)"));
} else {
capabilities.push_back(i18n("Signing"));
}
}
if (Kleo::keyHasEncrypt(key)) {
capabilities.push_back(i18n("Encryption"));
}
if (Kleo::keyHasCertify(key)) {
capabilities.push_back(i18n("Certifying User-IDs"));
}
if (Kleo::keyHasAuthenticate(key)) {
capabilities.push_back(i18n("SSH Authentication"));
}
return capabilities.join(QLatin1String(", "));
}
QString format_subkeyusage(const Subkey &subkey)
{
QStringList capabilities;
if (subkey.canSign()) {
if (subkey.isQualified()) {
capabilities.push_back(i18n("Signing (Qualified)"));
} else {
capabilities.push_back(i18n("Signing"));
}
}
if (subkey.canEncrypt()) {
capabilities.push_back(i18n("Encryption"));
}
if (subkey.canCertify()) {
capabilities.push_back(i18n("Certifying User-IDs"));
}
if (subkey.canAuthenticate()) {
capabilities.push_back(i18n("SSH Authentication"));
}
return capabilities.join(QLatin1String(", "));
}
static QString time_t2string(time_t t)
{
const QDateTime dt = QDateTime::fromSecsSinceEpoch(quint32(t));
return QLocale().toString(dt, QLocale::ShortFormat);
}
static QString make_red(const QString &txt)
{
return QLatin1String("<font color=\"red\">") + txt.toHtmlEscaped() + QLatin1String("</font>");
}
}
QString Formatting::toolTip(const Key &key, int flags)
{
if (flags == 0 || (key.protocol() != GpgME::CMS && key.protocol() != GpgME::OpenPGP)) {
return QString();
}
const Subkey subkey = key.subkey(0);
QString result;
if (flags & Validity) {
if (key.protocol() == GpgME::OpenPGP || (key.keyListMode() & Validate)) {
if (key.isRevoked()) {
result = make_red(i18n("Revoked"));
} else if (key.isExpired()) {
result = make_red(i18n("Expired"));
} else if (key.isDisabled()) {
result = i18n("Disabled");
} else if (key.keyListMode() & GpgME::Validate) {
unsigned int fullyTrusted = 0;
for (const auto &uid : key.userIDs()) {
if (uid.validity() >= UserID::Validity::Full) {
fullyTrusted++;
}
}
if (fullyTrusted == key.numUserIDs()) {
result = i18n("All User-IDs are certified.");
const auto compliance = complianceStringForKey(key);
if (!compliance.isEmpty()) {
result += QStringLiteral("<br>") + compliance;
}
} else {
result = i18np("One User-ID is not certified.", "%1 User-IDs are not certified.", key.numUserIDs() - fullyTrusted);
}
} else {
result = i18n("The validity cannot be checked at the moment.");
}
} else {
result = i18n("The validity cannot be checked at the moment.");
}
}
if (flags == Validity) {
return result;
}
result += QLatin1String("<table border=\"0\">");
if (key.protocol() == GpgME::CMS) {
if (flags & SerialNumber) {
result += format_row(i18n("Serial number"), key.issuerSerial());
}
if (flags & Issuer) {
result += format_row(i18n("Issuer"), key.issuerName());
}
}
if (flags & UserIDs) {
const std::vector<UserID> uids = key.userIDs();
if (!uids.empty()) {
result += format_row(key.protocol() == GpgME::CMS ? i18n("Subject") : i18n("User-ID"), prettyUserID(uids.front()));
}
if (uids.size() > 1) {
for (auto it = uids.begin() + 1, end = uids.end(); it != end; ++it) {
if (!it->isRevoked() && !it->isInvalid()) {
result += format_row(i18n("a.k.a."), prettyUserID(*it));
}
}
}
}
if (flags & ExpiryDates) {
result += format_row(i18n("Valid from"), time_t2string(subkey.creationTime()));
if (!subkey.neverExpires()) {
result += format_row(i18n("Valid until"), time_t2string(subkey.expirationTime()));
}
}
if (flags & CertificateType) {
result += format_row(i18n("Type"), format_keytype(key));
}
if (flags & CertificateUsage) {
result += format_row(i18n("Usage"), format_keyusage(key));
}
if (flags & KeyID) {
result += format_row(i18n("Key-ID"), QString::fromLatin1(key.shortKeyID()));
}
if (flags & Fingerprint) {
result += format_row(i18n("Fingerprint"), key.primaryFingerprint());
}
if (flags & OwnerTrust) {
if (key.protocol() == GpgME::OpenPGP) {
result += format_row(i18n("Certification trust"), ownerTrustShort(key));
} else if (key.isRoot()) {
result += format_row(i18n("Trusted issuer?"), key.userID(0).validity() == UserID::Ultimate ? i18n("Yes") : i18n("No"));
}
}
if (flags & StorageLocation) {
if (const char *card = subkey.cardSerialNumber()) {
result += format_row(i18n("Stored"), i18nc("stored...", "on SmartCard with serial no. %1", QString::fromUtf8(card)));
} else {
result += format_row(i18n("Stored"), i18nc("stored...", "on this computer"));
}
}
if (flags & Subkeys) {
for (const auto &sub : key.subkeys()) {
result += QLatin1String("<hr/>");
result += format_row(i18n("Subkey"), sub.fingerprint());
if (sub.isRevoked()) {
result += format_row(i18n("Status"), i18n("Revoked"));
} else if (sub.isExpired()) {
result += format_row(i18n("Status"), i18n("Expired"));
}
if (flags & ExpiryDates) {
result += format_row(i18n("Valid from"), time_t2string(sub.creationTime()));
if (!sub.neverExpires()) {
result += format_row(i18n("Valid until"), time_t2string(sub.expirationTime()));
}
}
if (flags & CertificateType) {
result += format_row(i18n("Type"), format_subkeytype(sub));
}
if (flags & CertificateUsage) {
result += format_row(i18n("Usage"), format_subkeyusage(sub));
}
if (flags & StorageLocation) {
if (const char *card = sub.cardSerialNumber()) {
result += format_row(i18n("Stored"), i18nc("stored...", "on SmartCard with serial no. %1", QString::fromUtf8(card)));
} else {
result += format_row(i18n("Stored"), i18nc("stored...", "on this computer"));
}
}
}
}
result += QLatin1String("</table>");
return result;
}
namespace
{
template<typename Container>
QString getValidityStatement(const Container &keys)
{
const bool allKeysAreOpenPGP = std::all_of(keys.cbegin(), keys.cend(), [](const Key &key) {
return key.protocol() == GpgME::OpenPGP;
});
const bool allKeysAreValidated = std::all_of(keys.cbegin(), keys.cend(), [](const Key &key) {
return key.keyListMode() & Validate;
});
if (allKeysAreOpenPGP || allKeysAreValidated) {
const bool someKeysAreBad = std::any_of(keys.cbegin(), keys.cend(), std::mem_fn(&Key::isBad));
if (someKeysAreBad) {
return i18n("Some keys are revoked, expired, disabled, or invalid.");
} else {
const bool allKeysAreFullyValid = std::all_of(keys.cbegin(), keys.cend(), &Kleo::allUserIDsHaveFullValidity);
if (allKeysAreFullyValid) {
return i18n("All keys are certified.");
} else {
return i18n("Some keys are not certified.");
}
}
}
return i18n("The validity of the keys cannot be checked at the moment.");
}
}
QString Formatting::toolTip(const KeyGroup &group, int flags)
{
static const unsigned int maxNumKeysForTooltip = 20;
if (group.isNull()) {
return QString();
}
const KeyGroup::Keys &keys = group.keys();
if (keys.size() == 0) {
return i18nc("@info:tooltip", "This group does not contain any keys.");
}
const QString validity = (flags & Validity) ? getValidityStatement(keys) : QString();
if (flags == Validity) {
return validity;
}
// list either up to maxNumKeysForTooltip keys or (maxNumKeysForTooltip-1) keys followed by "and n more keys"
const unsigned int numKeysForTooltip = keys.size() > maxNumKeysForTooltip ? maxNumKeysForTooltip - 1 : keys.size();
QStringList result;
result.reserve(3 + 2 + numKeysForTooltip + 2);
if (!validity.isEmpty()) {
result.push_back(QStringLiteral("<p>"));
result.push_back(validity.toHtmlEscaped());
result.push_back(QStringLiteral("</p>"));
}
result.push_back(QStringLiteral("<p>"));
result.push_back(i18n("Keys:"));
{
auto it = keys.cbegin();
for (unsigned int i = 0; i < numKeysForTooltip; ++i, ++it) {
result.push_back(QLatin1String("<br>") + Formatting::summaryLine(*it).toHtmlEscaped());
}
}
if (keys.size() > numKeysForTooltip) {
result.push_back(QLatin1String("<br>") + i18ncp("this follows a list of keys", "and 1 more key", "and %1 more keys", keys.size() - numKeysForTooltip));
}
result.push_back(QStringLiteral("</p>"));
return result.join(QLatin1Char('\n'));
}
//
// Creation and Expiration
//
namespace
{
static QDate time_t2date(time_t t)
{
if (!t) {
return {};
}
const QDateTime dt = QDateTime::fromSecsSinceEpoch(quint32(t));
return dt.date();
}
static QString accessible_date_format()
{
return i18nc(
"date format suitable for screen readers; "
"d: day as a number without a leading zero, "
"MMMM: localized month name, "
"yyyy: year as a four digit number",
"MMMM d, yyyy");
}
template<typename T>
QString expiration_date_string(const T &tee, const QString &noExpiration)
{
return tee.neverExpires() ? noExpiration : Formatting::dateString(time_t2date(tee.expirationTime()));
}
template<typename T>
QDate creation_date(const T &tee)
{
return time_t2date(tee.creationTime());
}
template<typename T>
QDate expiration_date(const T &tee)
{
return time_t2date(tee.expirationTime());
}
}
QString Formatting::dateString(time_t t)
{
return dateString(time_t2date(t));
}
QString Formatting::dateString(const QDate &date)
{
return QLocale().toString(date, QLocale::ShortFormat);
}
QString Formatting::accessibleDate(time_t t)
{
return accessibleDate(time_t2date(t));
}
QString Formatting::accessibleDate(const QDate &date)
{
return QLocale().toString(date, accessible_date_format());
}
QString Formatting::expirationDateString(const Key &key, const QString &noExpiration)
{
// if key is remote but has a non-zero expiration date (e.g. a key looked up via WKD),
// then we assume that the date is valid; if the date is zero for a remote key, then
// we don't know if it's unknown or unlimited
return isRemoteKey(key) && (key.subkey(0).expirationTime() == 0) //
? i18nc("@info the expiration date of the key is unknown", "unknown")
: expiration_date_string(key.subkey(0), noExpiration);
}
QString Formatting::expirationDateString(const Subkey &subkey, const QString &noExpiration)
{
return expiration_date_string(subkey, noExpiration);
}
QString Formatting::expirationDateString(const UserID::Signature &sig, const QString &noExpiration)
{
return expiration_date_string(sig, noExpiration);
}
QDate Formatting::expirationDate(const Key &key)
{
return expiration_date(key.subkey(0));
}
QDate Formatting::expirationDate(const Subkey &subkey)
{
return expiration_date(subkey);
}
QDate Formatting::expirationDate(const UserID::Signature &sig)
{
return expiration_date(sig);
}
QString Formatting::accessibleExpirationDate(const Key &key, const QString &noExpiration)
{
// if key is remote but has a non-zero expiration date (e.g. a key looked up via WKD),
// then we assume that the date is valid; if the date is zero for a remote key, then
// we don't know if it's unknown or unlimited
return isRemoteKey(key) && (key.subkey(0).expirationTime() == 0) //
? i18nc("@info the expiration date of the key is unknown", "unknown")
: accessibleExpirationDate(key.subkey(0), noExpiration);
}
QString Formatting::accessibleExpirationDate(const Subkey &subkey, const QString &noExpiration)
{
if (subkey.neverExpires()) {
return noExpiration.isEmpty() ? i18n("unlimited") : noExpiration;
} else {
return accessibleDate(expirationDate(subkey));
}
}
QString Formatting::accessibleExpirationDate(const UserID::Signature &sig, const QString &noExpiration)
{
if (sig.neverExpires()) {
return noExpiration.isEmpty() ? i18n("unlimited") : noExpiration;
} else {
return accessibleDate(expirationDate(sig));
}
}
QString Formatting::creationDateString(const Key &key)
{
return dateString(creation_date(key.subkey(0)));
}
QString Formatting::creationDateString(const Subkey &subkey)
{
return dateString(creation_date(subkey));
}
QString Formatting::creationDateString(const UserID::Signature &sig)
{
return dateString(creation_date(sig));
}
QDate Formatting::creationDate(const Key &key)
{
return creation_date(key.subkey(0));
}
QDate Formatting::creationDate(const Subkey &subkey)
{
return creation_date(subkey);
}
QDate Formatting::creationDate(const UserID::Signature &sig)
{
return creation_date(sig);
}
QString Formatting::accessibleCreationDate(const Key &key)
{
return accessibleDate(creationDate(key));
}
QString Formatting::accessibleCreationDate(const Subkey &subkey)
{
return accessibleDate(creationDate(subkey));
}
//
// Types
//
QString Formatting::displayName(GpgME::Protocol p)
{
if (p == GpgME::CMS) {
return i18nc("X.509/CMS encryption standard", "S/MIME");
}
if (p == GpgME::OpenPGP) {
return i18n("OpenPGP");
}
return i18nc("Unknown encryption protocol", "Unknown");
}
QString Formatting::type(const Key &key)
{
return displayName(key.protocol());
}
QString Formatting::type(const Subkey &subkey)
{
return QString::fromUtf8(subkey.publicKeyAlgorithmAsString());
}
QString Formatting::type(const KeyGroup &group)
{
Q_UNUSED(group)
return i18nc("a group of keys/certificates", "Group");
}
//
// Status / Validity
//
QString Formatting::ownerTrustShort(const Key &key)
{
return ownerTrustShort(key.ownerTrust());
}
QString Formatting::ownerTrustShort(Key::OwnerTrust trust)
{
switch (trust) {
case Key::Unknown:
return i18nc("unknown trust level", "unknown");
case Key::Never:
return i18n("untrusted");
case Key::Marginal:
return i18nc("marginal trust", "marginal");
case Key::Full:
return i18nc("full trust", "full");
case Key::Ultimate:
return i18nc("ultimate trust", "ultimate");
case Key::Undefined:
return i18nc("undefined trust", "undefined");
default:
Q_ASSERT(!"unexpected owner trust value");
break;
}
return QString();
}
QString Formatting::validityShort(const Subkey &subkey)
{
if (subkey.isRevoked()) {
return i18n("revoked");
}
if (subkey.isExpired()) {
return i18n("expired");
}
if (subkey.isDisabled()) {
return i18n("disabled");
}
if (subkey.isInvalid()) {
return i18n("invalid");
}
return i18nc("as in good/valid signature", "good");
}
QString Formatting::validityShort(const UserID &uid)
{
if (uid.isRevoked()) {
return i18n("revoked");
}
if (uid.isInvalid()) {
return i18n("invalid");
}
switch (uid.validity()) {
case UserID::Unknown:
return i18nc("unknown trust level", "unknown");
case UserID::Undefined:
return i18nc("undefined trust", "undefined");
case UserID::Never:
return i18n("untrusted");
case UserID::Marginal:
return i18nc("marginal trust", "marginal");
case UserID::Full:
return i18nc("full trust", "full");
case UserID::Ultimate:
return i18nc("ultimate trust", "ultimate");
}
return QString();
}
QString Formatting::validityShort(const UserID::Signature &sig)
{
switch (sig.status()) {
case UserID::Signature::NoError:
if (!sig.isInvalid()) {
/* See RFC 4880 Section 5.2.1 */
switch (sig.certClass()) {
case 0x10: /* Generic */
case 0x11: /* Persona */
case 0x12: /* Casual */
case 0x13: /* Positive */
return i18n("valid");
case 0x30:
return i18n("revoked");
default:
return i18n("class %1", sig.certClass());
}
}
Q_FALLTHROUGH();
// fall through:
case UserID::Signature::GeneralError:
return i18n("invalid");
case UserID::Signature::SigExpired:
return i18n("expired");
case UserID::Signature::KeyExpired:
return i18n("certificate expired");
case UserID::Signature::BadSignature:
return i18nc("fake/invalid signature", "bad");
case UserID::Signature::NoPublicKey: {
/* GnuPG returns the same error for no public key as for expired
* or revoked certificates. */
const auto key = KeyCache::instance()->findByKeyIDOrFingerprint(sig.signerKeyID());
if (key.isNull()) {
return i18n("no public key");
} else if (key.isExpired()) {
return i18n("key expired");
} else if (key.isRevoked()) {
return i18n("key revoked");
} else if (key.isDisabled()) {
return i18n("key disabled");
}
/* can't happen */
return QStringLiteral("unknown");
}
}
return QString();
}
QIcon Formatting::validityIcon(const UserID::Signature &sig)
{
switch (sig.status()) {
case UserID::Signature::NoError:
if (!sig.isInvalid()) {
/* See RFC 4880 Section 5.2.1 */
switch (sig.certClass()) {
case 0x10: /* Generic */
case 0x11: /* Persona */
case 0x12: /* Casual */
case 0x13: /* Positive */
return Formatting::successIcon();
case 0x30:
return Formatting::errorIcon();
default:
return QIcon();
}
}
Q_FALLTHROUGH();
// fall through:
case UserID::Signature::BadSignature:
case UserID::Signature::GeneralError:
return Formatting::errorIcon();
case UserID::Signature::SigExpired:
case UserID::Signature::KeyExpired:
return Formatting::infoIcon();
case UserID::Signature::NoPublicKey:
return Formatting::questionIcon();
}
return QIcon();
}
QString Formatting::formatKeyLink(const Key &key)
{
if (key.isNull()) {
return QString();
}
return QStringLiteral("<a href=\"key:%1\">%2</a>").arg(QLatin1String(key.primaryFingerprint()), Formatting::prettyName(key));
}
QString Formatting::formatForComboBox(const GpgME::Key &key)
{
const QString name = prettyName(key);
QString mail = prettyEMail(key);
if (!mail.isEmpty()) {
mail = QLatin1Char('<') + mail + QLatin1Char('>');
}
return i18nc("name, email, key id", "%1 %2 (%3)", name, mail, QLatin1String(key.shortKeyID())).simplified();
}
+QString Formatting::nameAndEmailForSummaryLine(const UserID &id)
+{
+ Q_ASSERT(!id.isNull());
+
+ const QString email = Formatting::prettyEMail(id);
+ const QString name = Formatting::prettyName(id);
+
+ if (name.isEmpty()) {
+ return email;
+ } else if (email.isEmpty()) {
+ return name;
+ } else {
+ return QStringLiteral("%1 <%2>").arg(name, email);
+ }
+}
+
QString Formatting::nameAndEmailForSummaryLine(const Key &key)
{
Q_ASSERT(!key.isNull());
const QString email = Formatting::prettyEMail(key);
const QString name = Formatting::prettyName(key);
if (name.isEmpty()) {
return email;
} else if (email.isEmpty()) {
return name;
} else {
return QStringLiteral("%1 <%2>").arg(name, email);
}
}
const char *Formatting::summaryToString(const Signature::Summary summary)
{
if (summary & Signature::Red) {
return "RED";
}
if (summary & Signature::Green) {
return "GREEN";
}
return "YELLOW";
}
QString Formatting::signatureToString(const Signature &sig, const Key &key)
{
if (sig.isNull()) {
return QString();
}
const bool red = (sig.summary() & Signature::Red);
const bool valid = (sig.summary() & Signature::Valid);
if (red) {
if (key.isNull()) {
if (const char *fpr = sig.fingerprint()) {
return i18n("Bad signature by unknown certificate %1: %2", QString::fromLatin1(fpr), Formatting::errorAsString(sig.status()));
} else {
return i18n("Bad signature by an unknown certificate: %1", Formatting::errorAsString(sig.status()));
}
} else {
return i18n("Bad signature by %1: %2", nameAndEmailForSummaryLine(key), Formatting::errorAsString(sig.status()));
}
} else if (valid) {
if (key.isNull()) {
if (const char *fpr = sig.fingerprint()) {
return i18n("Good signature by unknown certificate %1.", QString::fromLatin1(fpr));
} else {
return i18n("Good signature by an unknown certificate.");
}
} else {
return i18n("Good signature by %1.", nameAndEmailForSummaryLine(key));
}
} else if (key.isNull()) {
if (const char *fpr = sig.fingerprint()) {
return i18n("Invalid signature by unknown certificate %1: %2", QString::fromLatin1(fpr), Formatting::errorAsString(sig.status()));
} else {
return i18n("Invalid signature by an unknown certificate: %1", Formatting::errorAsString(sig.status()));
}
} else {
return i18n("Invalid signature by %1: %2", nameAndEmailForSummaryLine(key), Formatting::errorAsString(sig.status()));
}
}
//
// ImportResult
//
QString Formatting::importMetaData(const Import &import, const QStringList &ids)
{
const QString result = importMetaData(import);
if (result.isEmpty()) {
return QString();
} else {
return result + QLatin1Char('\n') + i18n("This certificate was imported from the following sources:") + QLatin1Char('\n') + ids.join(QLatin1Char('\n'));
}
}
QString Formatting::importMetaData(const Import &import)
{
if (import.isNull()) {
return QString();
}
if (import.error().isCanceled()) {
return i18n("The import of this certificate was canceled.");
}
if (import.error()) {
return i18n("An error occurred importing this certificate: %1", Formatting::errorAsString(import.error()));
}
const unsigned int status = import.status();
if (status & Import::NewKey) {
return (status & Import::ContainedSecretKey) ? i18n("This certificate was new to your keystore. The secret key is available.")
: i18n("This certificate is new to your keystore.");
}
QStringList results;
if (status & Import::NewUserIDs) {
results.push_back(i18n("New user-ids were added to this certificate by the import."));
}
if (status & Import::NewSignatures) {
results.push_back(i18n("New signatures were added to this certificate by the import."));
}
if (status & Import::NewSubkeys) {
results.push_back(i18n("New subkeys were added to this certificate by the import."));
}
return results.empty() ? i18n("The import contained no new data for this certificate. It is unchanged.") : results.join(QLatin1Char('\n'));
}
//
// Overview in CertificateDetailsDialog
//
QString Formatting::formatOverview(const Key &key)
{
return toolTip(key, AllOptions);
}
QString Formatting::usageString(const Subkey &sub)
{
QStringList usageStrings;
if (sub.canCertify()) {
usageStrings << i18n("Certify");
}
if (sub.canSign()) {
usageStrings << i18n("Sign");
}
if (sub.canEncrypt()) {
usageStrings << i18n("Encrypt");
}
if (sub.canAuthenticate()) {
usageStrings << i18n("Authenticate");
}
if (sub.canRenc()) {
usageStrings << i18nc("Means 'Additional Decryption Subkey'; Don't try translating that, though.", "ADSK");
}
return usageStrings.join(QLatin1String(", "));
}
+QString Formatting::summaryLine(const UserID &id)
+{
+ return i18nc("name <email> (validity, protocol, creation date)",
+ "%1 (%2, %3, created: %4)",
+ nameAndEmailForSummaryLine(id),
+ Formatting::complianceStringShort(id),
+ displayName(id.parent().protocol()),
+ Formatting::creationDateString(id.parent()));
+}
+
QString Formatting::summaryLine(const Key &key)
{
return nameAndEmailForSummaryLine(key) + QLatin1Char(' ')
+ i18nc("(validity, protocol, creation date)",
"(%1, %2, created: %3)",
Formatting::complianceStringShort(key),
displayName(key.protocol()),
Formatting::creationDateString(key));
}
QString Formatting::summaryLine(const KeyGroup &group)
{
switch (group.source()) {
case KeyGroup::ApplicationConfig:
case KeyGroup::GnuPGConfig:
return i18ncp("name of group of keys (n key(s), validity)",
"%2 (1 key, %3)",
"%2 (%1 keys, %3)",
group.keys().size(),
group.name(),
Formatting::complianceStringShort(group));
case KeyGroup::Tags:
return i18ncp("name of group of keys (n key(s), validity, tag)",
"%2 (1 key, %3, tag)",
"%2 (%1 keys, %3, tag)",
group.keys().size(),
group.name(),
Formatting::complianceStringShort(group));
default:
return i18ncp("name of group of keys (n key(s), validity, group ...)",
"%2 (1 key, %3, unknown origin)",
"%2 (%1 keys, %3, unknown origin)",
group.keys().size(),
group.name(),
Formatting::complianceStringShort(group));
}
}
// Icon for certificate selection indication
QIcon Formatting::iconForUid(const UserID &uid)
{
if (Kleo::isRevokedOrExpired(uid)) {
return Formatting::errorIcon();
}
return iconForValidity(uid);
}
QString Formatting::validity(const UserID &uid)
{
switch (uid.validity()) {
case UserID::Ultimate:
return i18n("The certificate is marked as your own.");
case UserID::Full:
return i18n("The certificate belongs to this recipient.");
case UserID::Marginal:
return i18n("The trust model indicates marginally that the certificate belongs to this recipient.");
case UserID::Never:
return i18n("This certificate should not be used.");
case UserID::Undefined:
case UserID::Unknown:
default:
return i18n("There is no indication that this certificate belongs to this recipient.");
}
}
QString Formatting::validity(const KeyGroup &group)
{
if (group.isNull()) {
return QString();
}
const KeyGroup::Keys &keys = group.keys();
if (keys.size() == 0) {
return i18n("This group does not contain any keys.");
}
return getValidityStatement(keys);
}
namespace
{
template<typename Container>
UserID::Validity minimalValidity(const Container &keys)
{
const int minValidity = std::accumulate(keys.cbegin(), keys.cend(), UserID::Ultimate + 1, [](int validity, const Key &key) {
return std::min<int>(validity, minimalValidityOfNotRevokedUserIDs(key));
});
return minValidity <= UserID::Ultimate ? static_cast<UserID::Validity>(minValidity) : UserID::Unknown;
}
template<typename Container>
bool allKeysAreCompliant(const Container &keys)
{
if (!DeVSCompliance::isActive()) {
return true;
}
if (!DeVSCompliance::isCompliant()) {
return false;
}
return Kleo::all_of(keys, DeVSCompliance::keyIsCompliant);
}
}
QIcon Formatting::validityIcon(const KeyGroup &group)
{
if (Kleo::any_of(group.keys(), std::mem_fn(&Key::isBad))) {
return Formatting::errorIcon();
}
return iconForValidityAndCompliance(minimalValidity(group.keys()), allKeysAreCompliant(group.keys()));
}
bool Formatting::uidsHaveFullValidity(const Key &key)
{
return allUserIDsHaveFullValidity(key);
}
QString Formatting::complianceMode()
{
const auto complianceValue = getCryptoConfigStringValue("gpg", "compliance");
return complianceValue == QLatin1String("gnupg") ? QString() : complianceValue;
}
bool Formatting::isKeyDeVs(const GpgME::Key &key)
{
return DeVSCompliance::allSubkeysAreCompliant(key);
}
QString Formatting::complianceStringForKey(const GpgME::Key &key)
{
// There will likely be more in the future for other institutions
// for now we only have DE-VS
if (DeVSCompliance::isCompliant()) {
return isRemoteKey(key) //
? i18nc("@info the compliance of the key with certain requirements is unknown", "unknown")
: DeVSCompliance::name(DeVSCompliance::keyIsCompliant(key));
}
return QString();
}
+QString Formatting::complianceStringShort(const GpgME::UserID &id)
+{
+ if (DeVSCompliance::isCompliant() && DeVSCompliance::userIDIsCompliant(id)) {
+ return QStringLiteral("★ ") + DeVSCompliance::name(true);
+ }
+ const bool keyValidityChecked = (id.parent().keyListMode() & GpgME::Validate);
+ if (keyValidityChecked && id.validity() >= UserID::Full) {
+ return i18nc("As in 'this user ID is valid.'", "certified");
+ }
+ if (id.parent().isExpired() || isExpired(id)) {
+ return i18n("expired");
+ }
+ if (id.parent().isRevoked() || id.isRevoked()) {
+ return i18n("revoked");
+ }
+ if (id.parent().isDisabled()) {
+ return i18n("disabled");
+ }
+ if (id.parent().isInvalid() || id.isInvalid()) {
+ return i18n("invalid");
+ }
+ if (keyValidityChecked) {
+ return i18nc("As in 'this user ID is not certified'", "not certified");
+ }
+
+ return i18nc("The validity of this user ID has not been/could not be checked", "not checked");
+}
+
QString Formatting::complianceStringShort(const GpgME::Key &key)
{
if (DeVSCompliance::isCompliant() && DeVSCompliance::keyIsCompliant(key)) {
return QStringLiteral("★ ") + DeVSCompliance::name(true);
}
const bool keyValidityChecked = (key.keyListMode() & GpgME::Validate);
if (keyValidityChecked && Kleo::allUserIDsHaveFullValidity(key)) {
return i18nc("As in all user IDs are valid.", "certified");
}
if (key.isExpired()) {
return i18n("expired");
}
if (key.isRevoked()) {
return i18n("revoked");
}
if (key.isDisabled()) {
return i18n("disabled");
}
if (key.isInvalid()) {
return i18n("invalid");
}
if (keyValidityChecked) {
return i18nc("As in not all user IDs are valid.", "not certified");
}
return i18nc("The validity of the user IDs has not been/could not be checked", "not checked");
}
QString Formatting::complianceStringShort(const KeyGroup &group)
{
const KeyGroup::Keys &keys = group.keys();
const bool allKeysFullyValid = std::all_of(keys.cbegin(), keys.cend(), &Kleo::allUserIDsHaveFullValidity);
if (allKeysFullyValid) {
return i18nc("As in all keys are valid.", "all certified");
}
return i18nc("As in not all keys are valid.", "not all certified");
}
QString Formatting::prettyID(const char *id)
{
if (!id) {
return QString();
}
QString ret = QString::fromLatin1(id).toUpper().replace(QRegularExpression(QStringLiteral("(....)")), QStringLiteral("\\1 ")).trimmed();
// For the standard 10 group fingerprint let us use a double space in the
// middle to increase readability
if (ret.size() == 49) {
ret.insert(24, QLatin1Char(' '));
}
return ret;
}
QString Formatting::accessibleHexID(const char *id)
{
static const QRegularExpression groupOfFourRegExp{QStringLiteral("(?:(.)(.)(.)(.))")};
QString ret;
ret = QString::fromLatin1(id);
if (!ret.isEmpty() && (ret.size() % 4 == 0)) {
ret = ret.replace(groupOfFourRegExp, QStringLiteral("\\1 \\2 \\3 \\4, ")).chopped(2);
}
return ret;
}
QString Formatting::origin(int o)
{
switch (o) {
case Key::OriginKS:
return i18n("Keyserver");
case Key::OriginDane:
return QStringLiteral("DANE");
case Key::OriginWKD:
return QStringLiteral("WKD");
case Key::OriginURL:
return QStringLiteral("URL");
case Key::OriginFile:
return i18n("File import");
case Key::OriginSelf:
return i18n("Generated");
case Key::OriginOther:
case Key::OriginUnknown:
default:
return i18n("Unknown");
}
}
QString Formatting::deVsString(bool compliant)
{
return DeVSCompliance::name(compliant);
}
namespace
{
QString formatTrustScope(const char *trustScope)
{
static const QRegularExpression escapedNonAlphaNum{QStringLiteral(R"(\\([^0-9A-Za-z]))")};
const auto scopeRegExp = QString::fromUtf8(trustScope);
if (scopeRegExp.startsWith(u"<[^>]+[@.]") && scopeRegExp.endsWith(u">$")) {
// looks like a trust scope regular expression created by gpg
auto domain = scopeRegExp.mid(10, scopeRegExp.size() - 10 - 2);
domain.replace(escapedNonAlphaNum, QStringLiteral(R"(\1)"));
return domain;
}
return scopeRegExp;
}
}
QString Formatting::trustSignatureDomain(const GpgME::UserID::Signature &sig)
{
return formatTrustScope(sig.trustScope());
}
QString Formatting::trustSignature(const GpgME::UserID::Signature &sig)
{
switch (sig.trustValue()) {
case TrustSignatureTrust::Partial:
return i18nc("Certifies this key as partially trusted introducer for 'domain name'.",
"Certifies this key as partially trusted introducer for '%1'.",
trustSignatureDomain(sig));
case TrustSignatureTrust::Complete:
return i18nc("Certifies this key as fully trusted introducer for 'domain name'.",
"Certifies this key as fully trusted introducer for '%1'.",
trustSignatureDomain(sig));
default:
return {};
}
}
QString Formatting::errorAsString(const GpgME::Error &error)
{
#ifdef Q_OS_WIN
// On Windows, we set GpgME resp. libgpg-error to return (translated) error messages as UTF-8
const char *s = error.asString();
qCDebug(LIBKLEO_LOG) << __func__ << "gettext_use_utf8(-1) returns" << gettext_use_utf8(-1);
qCDebug(LIBKLEO_LOG) << __func__ << "error:" << s;
qCDebug(LIBKLEO_LOG) << __func__ << "error (percent-encoded):" << QByteArray{s}.toPercentEncoding();
return QString::fromUtf8(s);
#else
return QString::fromLocal8Bit(error.asString());
#endif
}
QString Formatting::prettyAlgorithmName(const std::string &algorithm)
{
static const std::map<std::string, QString> displayNames = {
{"brainpoolP256r1", i18nc("@info", "ECC (Brainpool P-256)")},
{"brainpoolP384r1", i18nc("@info", "ECC (Brainpool P-384)")},
{"brainpoolP512r1", i18nc("@info", "ECC (Brainpool P-512)")},
{"curve25519", i18nc("@info", "ECC (Curve25519)")},
{"curve448", i18nc("@info", "ECC (Curve448)")},
{"nistp256", i18nc("@info", "ECC (NIST P-256)")},
{"nistp384", i18nc("@info", "ECC (NIST P-384)")},
{"nistp521", i18nc("@info", "ECC (NIST P-521)")},
{"rsa2048", i18nc("@info", "RSA 2048")},
{"rsa3072", i18nc("@info", "RSA 3072")},
{"rsa4096", i18nc("@info", "RSA 4096")},
};
const auto it = displayNames.find(algorithm);
return (it != displayNames.end()) ? it->second : i18nc("@info", "Unknown algorithm");
}
diff --git a/src/utils/formatting.h b/src/utils/formatting.h
index f86d20a3..e1299c28 100644
--- a/src/utils/formatting.h
+++ b/src/utils/formatting.h
@@ -1,229 +1,232 @@
/* -*- mode: c++; c-basic-offset:4 -*-
utils/formatting.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2021, 2022 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include "keyusage.h"
#include "kleo_export.h"
#include <QStringList>
#include <gpgme++/key.h>
class QString;
class QDate;
class QIcon;
namespace GpgME
{
class Error;
class Import;
}
namespace Kleo
{
class KeyGroup;
namespace Formatting
{
class KLEO_EXPORT IconProvider
{
public:
inline explicit IconProvider(KeyUsage::Flags requiredUsages)
: usage{requiredUsages}
{
}
QIcon icon(const GpgME::Key &key) const;
QIcon icon(const KeyGroup &group) const;
private:
KeyUsage usage;
};
KLEO_EXPORT QIcon successIcon();
KLEO_EXPORT QIcon infoIcon();
KLEO_EXPORT QIcon questionIcon();
KLEO_EXPORT QIcon unavailableIcon();
KLEO_EXPORT QIcon warningIcon();
KLEO_EXPORT QIcon errorIcon();
KLEO_EXPORT QString prettyNameAndEMail(int proto, const char *id, const char *name, const char *email, const char *comment = nullptr);
KLEO_EXPORT QString prettyNameAndEMail(int proto, const QString &id, const QString &name, const QString &email, const QString &comment = {});
KLEO_EXPORT QString prettyNameAndEMail(const GpgME::Key &key);
KLEO_EXPORT QString prettyNameAndEMail(const GpgME::UserID &key);
KLEO_EXPORT QString prettyUserID(const GpgME::UserID &uid);
KLEO_EXPORT QString prettyKeyID(const char *id);
KLEO_EXPORT QString prettyName(int proto, const char *id, const char *name, const char *comment = nullptr);
KLEO_EXPORT QString prettyName(const GpgME::Key &key);
KLEO_EXPORT QString prettyName(const GpgME::UserID &uid);
KLEO_EXPORT QString prettyName(const GpgME::UserID::Signature &sig);
KLEO_EXPORT QString prettyEMail(const char *email, const char *id);
KLEO_EXPORT QString prettyEMail(const GpgME::Key &key);
KLEO_EXPORT QString prettyEMail(const GpgME::UserID &uid);
KLEO_EXPORT QString prettyEMail(const GpgME::UserID::Signature &sig);
/* Formats a fingerprint or keyid into groups of four */
KLEO_EXPORT QString prettyID(const char *id);
KLEO_EXPORT QString accessibleHexID(const char *id);
// clang-format off
enum ToolTipOption {
KeyID = 0x001,
Validity = 0x002,
StorageLocation = 0x004,
SerialNumber = 0x008,
Issuer = 0x010,
Subject = 0x020,
ExpiryDates = 0x040,
CertificateType = 0x080,
CertificateUsage = 0x100,
Fingerprint = 0x200,
UserIDs = 0x400,
OwnerTrust = 0x800,
Subkeys = 0x1000,
AllOptions = 0xffff
};
// clang-format on
KLEO_EXPORT QString toolTip(const GpgME::Key &key, int opts);
KLEO_EXPORT QString toolTip(const Kleo::KeyGroup &group, int opts);
/// Returns expiration date of @p key as string, or @p noExpiration if the key doesn't expire.
KLEO_EXPORT QString expirationDateString(const GpgME::Key &key, const QString &noExpiration = {});
/// Returns expiration date of @p subkey as string, or @p noExpiration if the subkey doesn't expire.
KLEO_EXPORT QString expirationDateString(const GpgME::Subkey &subkey, const QString &noExpiration = {});
/// Returns expiration date of @p sig as string, or @p noExpiration if the signature doesn't expire.
KLEO_EXPORT QString expirationDateString(const GpgME::UserID::Signature &sig, const QString &noExpiration = {});
KLEO_EXPORT QDate expirationDate(const GpgME::Key &key);
KLEO_EXPORT QDate expirationDate(const GpgME::Subkey &subkey);
KLEO_EXPORT QDate expirationDate(const GpgME::UserID::Signature &sig);
/**
* Returns expiration date of @p key as string suitable for screen readers.
* If the key doesn't expire, then it returns @p noExpiration if @p noExpiration is not empty. Otherwise,
* returns the localization of "unlimited".
*/
KLEO_EXPORT QString accessibleExpirationDate(const GpgME::Key &key, const QString &noExpiration = {});
/**
* Returns expiration date of @p subkey as string suitable for screen readers.
* If the subkey doesn't expire, then it returns @p noExpiration if @p noExpiration is not empty. Otherwise,
* returns the localization of "unlimited".
*/
KLEO_EXPORT QString accessibleExpirationDate(const GpgME::Subkey &subkey, const QString &noExpiration = {});
/**
* Returns expiration date of @p sig as string suitable for screen readers.
* If the signature doesn't expire, then it returns @p noExpiration if @p noExpiration is not empty. Otherwise,
* returns the localization of "unlimited".
*/
KLEO_EXPORT QString accessibleExpirationDate(const GpgME::UserID::Signature &sig, const QString &noExpiration = {});
KLEO_EXPORT QString creationDateString(const GpgME::Key &key);
KLEO_EXPORT QString creationDateString(const GpgME::Subkey &subkey);
KLEO_EXPORT QString creationDateString(const GpgME::UserID::Signature &sig);
KLEO_EXPORT QDate creationDate(const GpgME::Key &key);
KLEO_EXPORT QDate creationDate(const GpgME::Subkey &subkey);
KLEO_EXPORT QDate creationDate(const GpgME::UserID::Signature &sig);
KLEO_EXPORT QString accessibleCreationDate(const GpgME::Key &key);
KLEO_EXPORT QString accessibleCreationDate(const GpgME::Subkey &subkey);
/* Convert a GPGME style time or a QDate to a localized string */
KLEO_EXPORT QString dateString(time_t t);
KLEO_EXPORT QString dateString(const QDate &date);
KLEO_EXPORT QString accessibleDate(time_t t);
KLEO_EXPORT QString accessibleDate(const QDate &date);
KLEO_EXPORT QString displayName(GpgME::Protocol prot);
KLEO_EXPORT QString type(const GpgME::Key &key);
KLEO_EXPORT QString type(const GpgME::Subkey &subkey);
KLEO_EXPORT QString type(const Kleo::KeyGroup &group);
KLEO_EXPORT QString ownerTrustShort(const GpgME::Key &key);
KLEO_EXPORT QString ownerTrustShort(GpgME::Key::OwnerTrust trust);
KLEO_EXPORT QString validityShort(const GpgME::Subkey &subkey);
KLEO_EXPORT QString validityShort(const GpgME::UserID &uid);
KLEO_EXPORT QString validityShort(const GpgME::UserID::Signature &sig);
KLEO_EXPORT QIcon validityIcon(const GpgME::UserID::Signature &sig);
/* A sentence about the validity of the UserID */
KLEO_EXPORT QString validity(const GpgME::UserID &uid);
KLEO_EXPORT QString validity(const Kleo::KeyGroup &group);
KLEO_EXPORT QIcon validityIcon(const Kleo::KeyGroup &group);
KLEO_EXPORT QString formatForComboBox(const GpgME::Key &key);
KLEO_EXPORT QString formatKeyLink(const GpgME::Key &key);
KLEO_EXPORT QString signatureToString(const GpgME::Signature &sig, const GpgME::Key &key);
KLEO_EXPORT const char *summaryToString(const GpgME::Signature::Summary summary);
KLEO_EXPORT QString importMetaData(const GpgME::Import &import);
KLEO_EXPORT QString importMetaData(const GpgME::Import &import, const QStringList &sources);
KLEO_EXPORT QString formatOverview(const GpgME::Key &key);
KLEO_EXPORT QString usageString(const GpgME::Subkey &subkey);
+KLEO_EXPORT QString summaryLine(const GpgME::UserID &id);
KLEO_EXPORT QString summaryLine(const GpgME::Key &key);
KLEO_EXPORT QString summaryLine(const KeyGroup &group);
KLEO_EXPORT QString nameAndEmailForSummaryLine(const GpgME::Key &key);
+KLEO_EXPORT QString nameAndEmailForSummaryLine(const GpgME::UserID &id);
KLEO_EXPORT QIcon iconForUid(const GpgME::UserID &uid);
/* Is the key valid i.e. are all uids fully trusted? */
KLEO_EXPORT bool uidsHaveFullValidity(const GpgME::Key &key);
/* The compliance mode of the gnupg system. Empty if compliance
* mode is not set.
* Use Kleo::gnupgComplianceMode() instead.
*/
KLEO_DEPRECATED_EXPORT QString complianceMode();
/* Is the given key in compliance with CO_DE_VS? */
KLEO_EXPORT bool isKeyDeVs(const GpgME::Key &key);
/**
* Use Kleo::DeVSCompliance::name(bool) instead.
*/
KLEO_DEPRECATED_EXPORT QString deVsString(bool compliant = true);
/* A sentence if the key confirms to the current compliance mode */
KLEO_EXPORT QString complianceStringForKey(const GpgME::Key &key);
/* A single word for use in keylists to describe the validity of the
* given key, including any conformance statements relevant to the
* current conformance mode. */
KLEO_EXPORT QString complianceStringShort(const GpgME::Key &key);
+KLEO_EXPORT QString complianceStringShort(const GpgME::UserID &id);
KLEO_EXPORT QString complianceStringShort(const Kleo::KeyGroup &group);
/* The origin of the key mapped to a localized string */
KLEO_EXPORT QString origin(int o);
/* Human-readable trust signature scope (for trust signature regexp created by GnuPG) */
KLEO_EXPORT QString trustSignatureDomain(const GpgME::UserID::Signature &sig);
/* Summary of trust signature properties */
KLEO_EXPORT QString trustSignature(const GpgME::UserID::Signature &sig);
/**
* Returns the value of Error::asString() for the error \p error as Unicode string.
*/
KLEO_EXPORT QString errorAsString(const GpgME::Error &error);
/**
* Returns a name suitable for being displayed for the GPG algorithm name @p algorithm.
*/
KLEO_EXPORT QString prettyAlgorithmName(const std::string &algorithm);
}
}
diff --git a/src/utils/keyhelpers.cpp b/src/utils/keyhelpers.cpp
index 00937f05..ccbf196e 100644
--- a/src/utils/keyhelpers.cpp
+++ b/src/utils/keyhelpers.cpp
@@ -1,295 +1,309 @@
/*
utils/keyhelpers.cpp
This file is part of libkleopatra, the KDE keymanagement library
SPDX-FileCopyrightText: 2022 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-libkleo.h>
#include "keyhelpers.h"
#include <libkleo/algorithm.h>
#include <libkleo/compat.h>
#include <libkleo/keycache.h>
#include <libkleo_debug.h>
#include <QDate>
// needed for GPGME_VERSION_NUMBER
#include <gpgme.h>
#include <algorithm>
#include <iterator>
using namespace Kleo;
using namespace GpgME;
namespace
{
bool havePublicKeyForSignature(const GpgME::UserID::Signature &signature)
{
// GnuPG returns status "NoPublicKey" for missing signing keys, but also
// for expired or revoked signing keys.
return (signature.status() != GpgME::UserID::Signature::NoPublicKey) //
|| !KeyCache::instance()->findByKeyIDOrFingerprint(signature.signerKeyID()).isNull();
}
auto _getMissingSignerKeyIds(const std::vector<GpgME::UserID::Signature> &signatures)
{
return std::accumulate(std::begin(signatures), std::end(signatures), std::set<QString>{}, [](auto &keyIds, const auto &signature) {
if (!havePublicKeyForSignature(signature)) {
keyIds.insert(QLatin1String{signature.signerKeyID()});
}
return keyIds;
});
}
}
std::set<QString> Kleo::getMissingSignerKeyIds(const std::vector<GpgME::UserID> &userIds)
{
return std::accumulate(std::begin(userIds), std::end(userIds), std::set<QString>{}, [](auto &keyIds, const auto &userID) {
if (!userID.isBad()) {
const auto newKeyIds = _getMissingSignerKeyIds(userID.signatures());
std::copy(std::begin(newKeyIds), std::end(newKeyIds), std::inserter(keyIds, std::end(keyIds)));
}
return keyIds;
});
}
std::set<QString> Kleo::getMissingSignerKeyIds(const std::vector<GpgME::Key> &keys)
{
return std::accumulate(std::begin(keys), std::end(keys), std::set<QString>{}, [](auto &keyIds, const auto &key) {
if (!key.isBad()) {
const auto newKeyIds = getMissingSignerKeyIds(key.userIDs());
std::copy(std::begin(newKeyIds), std::end(newKeyIds), std::inserter(keyIds, std::end(keyIds)));
}
return keyIds;
});
}
bool Kleo::isRemoteKey(const GpgME::Key &key)
{
// a remote key looked up via WKD has key list mode Local; therefore we also look for the key in the local key ring
return (key.keyListMode() == GpgME::Extern) || KeyCache::instance()->findByFingerprint(key.primaryFingerprint()).isNull();
}
GpgME::UserID::Validity Kleo::minimalValidityOfNotRevokedUserIDs(const Key &key)
{
const std::vector<UserID> userIDs = key.userIDs();
const int minValidity = std::accumulate(userIDs.begin(), userIDs.end(), UserID::Ultimate + 1, [](int validity, const UserID &userID) {
return userID.isRevoked() ? validity : std::min(validity, static_cast<int>(userID.validity()));
});
return minValidity <= UserID::Ultimate ? static_cast<UserID::Validity>(minValidity) : UserID::Unknown;
}
GpgME::UserID::Validity Kleo::maximalValidityOfUserIDs(const Key &key)
{
const auto userIDs = key.userIDs();
const int maxValidity = std::accumulate(userIDs.begin(), userIDs.end(), 0, [](int validity, const UserID &userID) {
return std::max(validity, static_cast<int>(userID.validity()));
});
return static_cast<UserID::Validity>(maxValidity);
}
bool Kleo::allUserIDsHaveFullValidity(const GpgME::Key &key)
{
return minimalValidityOfNotRevokedUserIDs(key) >= UserID::Full;
}
namespace
{
bool isLastValidUserID(const GpgME::UserID &userId)
{
if (Kleo::isRevokedOrExpired(userId)) {
return false;
}
const auto userIds = userId.parent().userIDs();
const int numberOfValidUserIds = std::count_if(std::begin(userIds), std::end(userIds), [](const auto &u) {
return !Kleo::isRevokedOrExpired(u);
});
return numberOfValidUserIds == 1;
}
bool hasValidUserID(const GpgME::Key &key)
{
return Kleo::any_of(key.userIDs(), [](const auto &u) {
return !Kleo::isRevokedOrExpired(u);
});
}
}
bool Kleo::isSelfSignature(const GpgME::UserID::Signature &signature)
{
return !qstrcmp(signature.parent().parent().keyID(), signature.signerKeyID());
}
bool Kleo::isRevokedOrExpired(const GpgME::UserID &userId)
{
if (userId.isRevoked() || userId.parent().isExpired()) {
return true;
}
const auto sigs = userId.signatures();
std::vector<GpgME::UserID::Signature> selfSigs;
std::copy_if(std::begin(sigs), std::end(sigs), std::back_inserter(selfSigs), &Kleo::isSelfSignature);
std::sort(std::begin(selfSigs), std::end(selfSigs));
// check the most recent signature
const auto sig = !selfSigs.empty() ? selfSigs.back() : GpgME::UserID::Signature{};
return !sig.isNull() && (sig.isRevokation() || sig.isExpired());
}
+bool Kleo::isExpired(const UserID &userID)
+{
+ if (userID.parent().isExpired()) {
+ return true;
+ }
+ const auto sigs = userID.signatures();
+ std::vector<GpgME::UserID::Signature> selfSigs;
+ std::copy_if(std::begin(sigs), std::end(sigs), std::back_inserter(selfSigs), &Kleo::isSelfSignature);
+ std::sort(std::begin(selfSigs), std::end(selfSigs));
+ // check the most recent signature
+ const auto sig = !selfSigs.empty() ? selfSigs.back() : GpgME::UserID::Signature{};
+ return !sig.isNull() && sig.isExpired();
+}
+
bool Kleo::canCreateCertifications(const GpgME::Key &key)
{
return Kleo::keyHasCertify(key) && canBeUsedForSecretKeyOperations(key);
}
bool Kleo::canBeCertified(const GpgME::Key &key)
{
return key.protocol() == GpgME::OpenPGP //
&& !key.isBad() //
&& hasValidUserID(key);
}
namespace
{
static inline bool subkeyHasSecret(const GpgME::Subkey &subkey)
{
#if GPGME_VERSION_NUMBER >= 0x011102 // 1.17.2
// we need to check the primary subkey because Key::hasSecret() is also true if just the secret key stub of an offline key is available
return subkey.isSecret();
#else
// older versions of GpgME did not always set the secret flag for card keys
return subkey.isSecret() || subkey.isCardKey();
#endif
}
}
bool Kleo::canBeUsedForEncryption(const GpgME::Key &key)
{
return !key.isBad() && Kleo::any_of(key.subkeys(), [](const auto &subkey) {
return subkey.canEncrypt() && !subkey.isBad();
});
}
bool Kleo::canBeUsedForSigning(const GpgME::Key &key)
{
return !key.isBad() && Kleo::any_of(key.subkeys(), [](const auto &subkey) {
return subkey.canSign() && !subkey.isBad() && subkeyHasSecret(subkey);
});
}
bool Kleo::canBeUsedForSecretKeyOperations(const GpgME::Key &key)
{
return subkeyHasSecret(key.subkey(0));
}
bool Kleo::canRevokeUserID(const GpgME::UserID &userId)
{
return (!userId.isNull() //
&& userId.parent().protocol() == GpgME::OpenPGP //
&& !isLastValidUserID(userId));
}
bool Kleo::isSecretKeyStoredInKeyRing(const GpgME::Key &key)
{
return key.subkey(0).isSecret() && !key.subkey(0).isCardKey();
}
bool Kleo::userHasCertificationKey()
{
const auto secretKeys = KeyCache::instance()->secretKeys();
return Kleo::any_of(secretKeys, [](const auto &k) {
return (k.protocol() == GpgME::OpenPGP) && canCreateCertifications(k);
});
}
Kleo::CertificationRevocationFeasibility Kleo::userCanRevokeCertification(const GpgME::UserID::Signature &certification)
{
const auto certificationKey = KeyCache::instance()->findByKeyIDOrFingerprint(certification.signerKeyID());
const bool isSelfSignature = qstrcmp(certification.parent().parent().keyID(), certification.signerKeyID()) == 0;
if (!certificationKey.hasSecret()) {
return CertificationNotMadeWithOwnKey;
} else if (isSelfSignature) {
return CertificationIsSelfSignature;
} else if (certification.isRevokation()) {
return CertificationIsRevocation;
} else if (certification.isExpired()) {
return CertificationIsExpired;
} else if (certification.isInvalid()) {
return CertificationIsInvalid;
} else if (!canCreateCertifications(certificationKey)) {
return CertificationKeyNotAvailable;
}
return CertificationCanBeRevoked;
}
bool Kleo::userCanRevokeCertifications(const GpgME::UserID &userId)
{
if (userId.numSignatures() == 0) {
qCWarning(LIBKLEO_LOG) << __func__ << "- Error: Signatures of user ID" << QString::fromUtf8(userId.id()) << "not available";
}
return Kleo::any_of(userId.signatures(), [](const auto &certification) {
return userCanRevokeCertification(certification) == CertificationCanBeRevoked;
});
}
bool Kleo::userIDBelongsToKey(const GpgME::UserID &userID, const GpgME::Key &key)
{
return !qstricmp(userID.parent().primaryFingerprint(), key.primaryFingerprint());
}
static time_t creationDate(const GpgME::UserID &uid)
{
// returns the date of the first self-signature
for (unsigned int i = 0, numSignatures = uid.numSignatures(); i < numSignatures; ++i) {
const auto sig = uid.signature(i);
if (Kleo::isSelfSignature(sig)) {
return sig.creationTime();
}
}
return 0;
}
bool Kleo::userIDsAreEqual(const GpgME::UserID &lhs, const GpgME::UserID &rhs)
{
return (qstrcmp(lhs.parent().primaryFingerprint(), rhs.parent().primaryFingerprint()) == 0 //
&& qstrcmp(lhs.id(), rhs.id()) == 0 //
&& creationDate(lhs) == creationDate(rhs));
}
static inline bool isOpenPGPCertification(const GpgME::UserID::Signature &sig)
{
// certification class is 0x10, ..., 0x13
return (sig.certClass() & ~0x03) == 0x10;
}
static bool isOpenPGPCertificationByUser(const GpgME::UserID::Signature &sig)
{
if (!isOpenPGPCertification(sig)) {
return false;
}
const auto certificationKey = KeyCache::instance()->findByKeyIDOrFingerprint(sig.signerKeyID());
return certificationKey.ownerTrust() == Key::Ultimate;
}
bool Kleo::userIDIsCertifiedByUser(const GpgME::UserID &userId)
{
if (userId.parent().protocol() != GpgME::OpenPGP) {
qCWarning(LIBKLEO_LOG) << __func__ << "not called with OpenPGP key";
return false;
}
if (userId.numSignatures() == 0) {
qCWarning(LIBKLEO_LOG) << __func__ << "- Error: Signatures of user ID" << QString::fromUtf8(userId.id()) << "not available";
}
for (unsigned int i = 0, numSignatures = userId.numSignatures(); i < numSignatures; ++i) {
const auto sig = userId.signature(i);
if ((sig.status() == UserID::Signature::NoError) && !sig.isBad() && sig.isExportable() && isOpenPGPCertificationByUser(sig)) {
return true;
}
}
return false;
}
diff --git a/src/utils/keyhelpers.h b/src/utils/keyhelpers.h
index 8a4197ba..72cf18c0 100644
--- a/src/utils/keyhelpers.h
+++ b/src/utils/keyhelpers.h
@@ -1,210 +1,213 @@
/*
utils/keyhelpers.h
This file is part of libkleopatra, the KDE keymanagement library
SPDX-FileCopyrightText: 2021-2022 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include "kleo_export.h"
#include <QStringList>
#include <gpgme++/key.h>
#include <algorithm>
#include <set>
#include <vector>
class QDate;
namespace Kleo
{
template<typename KeyContainer>
QStringList getFingerprints(const KeyContainer &keys)
{
QStringList fingerprints;
fingerprints.reserve(keys.size());
std::transform(std::begin(keys), std::end(keys), std::back_inserter(fingerprints), [](const auto &key) {
return QString::fromLatin1(key.primaryFingerprint());
});
return fingerprints;
}
KLEO_EXPORT std::set<QString> getMissingSignerKeyIds(const std::vector<GpgME::UserID> &userIds);
KLEO_EXPORT std::set<QString> getMissingSignerKeyIds(const std::vector<GpgME::Key> &keys);
/**
* Returns true, if the key \p key is the result of a lookup which is not present
* in the local key ring.
*/
KLEO_EXPORT bool isRemoteKey(const GpgME::Key &key);
KLEO_EXPORT GpgME::UserID::Validity minimalValidityOfNotRevokedUserIDs(const GpgME::Key &key);
KLEO_EXPORT GpgME::UserID::Validity maximalValidityOfUserIDs(const GpgME::Key &key);
/* Is the key valid i.e. are all not revoked uids fully trusted? */
KLEO_EXPORT bool allUserIDsHaveFullValidity(const GpgME::Key &key);
template<typename RangeOfKeys>
bool allKeysHaveProtocol(const RangeOfKeys &keys, GpgME::Protocol protocol)
{
return std::all_of(std::begin(keys), std::end(keys), [protocol](const auto &key) {
return key.protocol() == protocol;
});
}
template<typename RangeOfKeys>
bool anyKeyHasProtocol(const RangeOfKeys &keys, GpgME::Protocol protocol)
{
return std::any_of(std::begin(keys), std::end(keys), [protocol](const auto &key) {
return key.protocol() == protocol;
});
}
/** Returns true if \p signature is a self-signature. */
KLEO_EXPORT bool isSelfSignature(const GpgME::UserID::Signature &signature);
/**
* Returns true if the most recent self-signature of \p userId is a revocation
* signature or if it has expired.
*/
KLEO_EXPORT bool isRevokedOrExpired(const GpgME::UserID &userId);
+/** Returns true if the most recent self-signature of \p userId has expired. */
+KLEO_EXPORT bool isExpired(const GpgME::UserID &userId);
+
/**
* Returns true if \p key can be used to certify user IDs, i.e. if the key
* has the required capability and if the secret key of the (primary)
* certification subkey is available in the keyring or on a smart card.
*/
KLEO_EXPORT bool canCreateCertifications(const GpgME::Key &key);
/**
* Returns true if the key \p key can be certified, i.e. it is an OpenPGP key
* which is neither revoked nor expired and which has at least one user ID
* that is neither revoked nor expired.
*/
KLEO_EXPORT bool canBeCertified(const GpgME::Key &key);
/**
* Returns true if the certificate \p key can be used for encryption, i.e. if
* it has at least one encryption subkey that is neither expired nor revoked
* nor otherwise invalid.
*/
KLEO_EXPORT bool canBeUsedForEncryption(const GpgME::Key &key);
/**
* Returns true if the certificate \p key can be used for signing data, i.e. if
* it has at least one signing subkey that is neither expired nor revoked
* nor otherwise invalid and for which the secret key is available.
*/
KLEO_EXPORT bool canBeUsedForSigning(const GpgME::Key &key);
/**
* Returns true if \p key can be used for operations requiring the secret key,
* i.e. if the secret key of the primary key pair is available in the keyring
* or on a smart card.
*
* \note Key::hasSecret() also returns true if a secret key stub, e.g. of an
* offline key, is available in the keyring.
*/
KLEO_EXPORT bool canBeUsedForSecretKeyOperations(const GpgME::Key &key);
/**
* Returns true if \p userId can be revoked, i.e. if it isn't the last valid
* user ID of an OpenPGP key.
*/
KLEO_EXPORT bool canRevokeUserID(const GpgME::UserID &userId);
/**
* Returns true if the secret key of the primary key pair of \p key is stored
* in the keyring.
*/
KLEO_EXPORT bool isSecretKeyStoredInKeyRing(const GpgME::Key &key);
/**
* Returns true if any keys suitable for certifying user IDs are available in
* the keyring or on a smart card.
*
* \sa canCreateCertifications
*/
KLEO_EXPORT bool userHasCertificationKey();
enum CertificationRevocationFeasibility {
CertificationCanBeRevoked = 0,
CertificationNotMadeWithOwnKey,
CertificationIsSelfSignature,
CertificationIsRevocation,
CertificationIsExpired,
CertificationIsInvalid,
CertificationKeyNotAvailable,
};
/**
* Checks if the user can revoke the given \p certification.
*/
KLEO_EXPORT CertificationRevocationFeasibility userCanRevokeCertification(const GpgME::UserID::Signature &certification);
/**
* Returns true if the user can revoke any of the certifications of the \p userId.
*
* \sa userCanRevokeCertification
*/
KLEO_EXPORT bool userCanRevokeCertifications(const GpgME::UserID &userId);
/**
* Returns true, if the user ID \p userID belongs to the key \p key.
*/
KLEO_EXPORT bool userIDBelongsToKey(const GpgME::UserID &userID, const GpgME::Key &key);
/**
* Returns a unary predicate to check if a user ID belongs to the key \p key.
*/
inline auto userIDBelongsToKey(const GpgME::Key &key)
{
return [key](const GpgME::UserID &userID) {
return userIDBelongsToKey(userID, key);
};
}
/**
* Returns true, if the two user IDs \p lhs and \p rhs are equal.
*
* Equality means that both user IDs belong to the same key, contain identical
* text, and have the same creation date (i.e. the creation date of the first
* self-signature is the same).
*/
KLEO_EXPORT bool userIDsAreEqual(const GpgME::UserID &lhs, const GpgME::UserID &rhs);
/**
* Returns true, if the user ID \p userId has a valid, exportable certification
* that was made with one of the available ultimately trusted OpenPGP keys.
*/
KLEO_EXPORT bool userIDIsCertifiedByUser(const GpgME::UserID &userId);
struct KLEO_EXPORT KeysByProtocol {
std::vector<GpgME::Key> openpgp;
std::vector<GpgME::Key> cms;
};
/**
* Partitions the keys \p keys into OpenPGP keys and CMS certificates.
*/
template<typename KeyContainer>
KeysByProtocol partitionKeysByProtocol(const KeyContainer &keys)
{
KeysByProtocol result;
std::partition_copy(std::begin(keys), std::end(keys), std::back_inserter(result.openpgp), std::back_inserter(result.cms), [](const auto &key) {
return key.protocol() == GpgME::OpenPGP;
});
return result;
}
}

File Metadata

Mime Type
text/x-diff
Expires
Mon, Dec 8, 7:26 AM (5 h, 57 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
b8/9f/e877e613fc5ce392282b7bde0f3c

Event Timeline