diff --git a/src/utils/keyhelpers.cpp b/src/utils/keyhelpers.cpp index ccbf196e..b2e24ff6 100644 --- a/src/utils/keyhelpers.cpp +++ b/src/utils/keyhelpers.cpp @@ -1,309 +1,293 @@ /* 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); + return subkey.canSign() && !subkey.isBad() && subkey.isSecret(); }); } bool Kleo::canBeUsedForSecretKeyOperations(const GpgME::Key &key) { - return subkeyHasSecret(key.subkey(0)); + // 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 key.subkey(0).isSecret(); } 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; }