File indexing completed on 2024-05-26 05:24:35
0001 /* 0002 utils/keyhelpers.cpp 0003 0004 This file is part of libkleopatra, the KDE keymanagement library 0005 SPDX-FileCopyrightText: 2022 g10 Code GmbH 0006 SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de> 0007 0008 SPDX-License-Identifier: GPL-2.0-or-later 0009 */ 0010 0011 #include <config-libkleo.h> 0012 0013 #include "keyhelpers.h" 0014 0015 #include <libkleo/algorithm.h> 0016 #include <libkleo/compat.h> 0017 #include <libkleo/keycache.h> 0018 0019 #include <libkleo_debug.h> 0020 0021 #include <QDate> 0022 0023 // needed for GPGME_VERSION_NUMBER 0024 #include <gpgme.h> 0025 0026 #include <iterator> 0027 0028 using namespace Kleo; 0029 using namespace GpgME; 0030 0031 namespace 0032 { 0033 bool havePublicKeyForSignature(const GpgME::UserID::Signature &signature) 0034 { 0035 // GnuPG returns status "NoPublicKey" for missing signing keys, but also 0036 // for expired or revoked signing keys. 0037 return (signature.status() != GpgME::UserID::Signature::NoPublicKey) // 0038 || !KeyCache::instance()->findByKeyIDOrFingerprint(signature.signerKeyID()).isNull(); 0039 } 0040 0041 auto _getMissingSignerKeyIds(const std::vector<GpgME::UserID::Signature> &signatures) 0042 { 0043 return std::accumulate(std::begin(signatures), std::end(signatures), std::set<QString>{}, [](auto &keyIds, const auto &signature) { 0044 if (!havePublicKeyForSignature(signature)) { 0045 keyIds.insert(QLatin1StringView{signature.signerKeyID()}); 0046 } 0047 return keyIds; 0048 }); 0049 } 0050 } 0051 0052 std::set<QString> Kleo::getMissingSignerKeyIds(const std::vector<GpgME::UserID> &userIds) 0053 { 0054 return std::accumulate(std::begin(userIds), std::end(userIds), std::set<QString>{}, [](auto &keyIds, const auto &userID) { 0055 if (!userID.isBad()) { 0056 const auto newKeyIds = _getMissingSignerKeyIds(userID.signatures()); 0057 std::copy(std::begin(newKeyIds), std::end(newKeyIds), std::inserter(keyIds, std::end(keyIds))); 0058 } 0059 return keyIds; 0060 }); 0061 } 0062 0063 std::set<QString> Kleo::getMissingSignerKeyIds(const std::vector<GpgME::Key> &keys) 0064 { 0065 return std::accumulate(std::begin(keys), std::end(keys), std::set<QString>{}, [](auto &keyIds, const auto &key) { 0066 if (!key.isBad()) { 0067 const auto newKeyIds = getMissingSignerKeyIds(key.userIDs()); 0068 std::copy(std::begin(newKeyIds), std::end(newKeyIds), std::inserter(keyIds, std::end(keyIds))); 0069 } 0070 return keyIds; 0071 }); 0072 } 0073 0074 bool Kleo::isRemoteKey(const GpgME::Key &key) 0075 { 0076 // a remote key looked up via WKD has key list mode Local; therefore we also look for the key in the local key ring 0077 return (key.keyListMode() == GpgME::Extern) || KeyCache::instance()->findByFingerprint(key.primaryFingerprint()).isNull(); 0078 } 0079 0080 GpgME::UserID::Validity Kleo::minimalValidityOfNotRevokedUserIDs(const Key &key) 0081 { 0082 const std::vector<UserID> userIDs = key.userIDs(); 0083 const int minValidity = std::accumulate(userIDs.begin(), userIDs.end(), UserID::Ultimate + 1, [](int validity, const UserID &userID) { 0084 return userID.isRevoked() ? validity : std::min(validity, static_cast<int>(userID.validity())); 0085 }); 0086 return minValidity <= UserID::Ultimate ? static_cast<UserID::Validity>(minValidity) : UserID::Unknown; 0087 } 0088 0089 GpgME::UserID::Validity Kleo::maximalValidityOfUserIDs(const Key &key) 0090 { 0091 const auto userIDs = key.userIDs(); 0092 const int maxValidity = std::accumulate(userIDs.begin(), userIDs.end(), 0, [](int validity, const UserID &userID) { 0093 return std::max(validity, static_cast<int>(userID.validity())); 0094 }); 0095 return static_cast<UserID::Validity>(maxValidity); 0096 } 0097 0098 bool Kleo::allUserIDsHaveFullValidity(const GpgME::Key &key) 0099 { 0100 return minimalValidityOfNotRevokedUserIDs(key) >= UserID::Full; 0101 } 0102 0103 namespace 0104 { 0105 bool isLastValidUserID(const GpgME::UserID &userId) 0106 { 0107 if (Kleo::isRevokedOrExpired(userId)) { 0108 return false; 0109 } 0110 const auto userIds = userId.parent().userIDs(); 0111 const int numberOfValidUserIds = std::count_if(std::begin(userIds), std::end(userIds), [](const auto &u) { 0112 return !Kleo::isRevokedOrExpired(u); 0113 }); 0114 return numberOfValidUserIds == 1; 0115 } 0116 0117 bool hasValidUserID(const GpgME::Key &key) 0118 { 0119 return Kleo::any_of(key.userIDs(), [](const auto &u) { 0120 return !Kleo::isRevokedOrExpired(u); 0121 }); 0122 } 0123 } 0124 0125 bool Kleo::isSelfSignature(const GpgME::UserID::Signature &signature) 0126 { 0127 return !qstrcmp(signature.parent().parent().keyID(), signature.signerKeyID()); 0128 } 0129 0130 bool Kleo::isRevokedOrExpired(const GpgME::UserID &userId) 0131 { 0132 if (userId.isRevoked() || userId.parent().isExpired()) { 0133 return true; 0134 } 0135 const auto sigs = userId.signatures(); 0136 std::vector<GpgME::UserID::Signature> selfSigs; 0137 std::copy_if(std::begin(sigs), std::end(sigs), std::back_inserter(selfSigs), &Kleo::isSelfSignature); 0138 std::sort(std::begin(selfSigs), std::end(selfSigs)); 0139 // check the most recent signature 0140 const auto sig = !selfSigs.empty() ? selfSigs.back() : GpgME::UserID::Signature{}; 0141 return !sig.isNull() && (sig.isRevokation() || sig.isExpired()); 0142 } 0143 0144 bool Kleo::isExpired(const UserID &userID) 0145 { 0146 if (userID.parent().isExpired()) { 0147 return true; 0148 } 0149 const auto sigs = userID.signatures(); 0150 std::vector<GpgME::UserID::Signature> selfSigs; 0151 std::copy_if(std::begin(sigs), std::end(sigs), std::back_inserter(selfSigs), &Kleo::isSelfSignature); 0152 std::sort(std::begin(selfSigs), std::end(selfSigs)); 0153 // check the most recent signature 0154 const auto sig = !selfSigs.empty() ? selfSigs.back() : GpgME::UserID::Signature{}; 0155 return !sig.isNull() && sig.isExpired(); 0156 } 0157 0158 bool Kleo::canCreateCertifications(const GpgME::Key &key) 0159 { 0160 return Kleo::keyHasCertify(key) && canBeUsedForSecretKeyOperations(key); 0161 } 0162 0163 bool Kleo::canBeCertified(const GpgME::Key &key) 0164 { 0165 return key.protocol() == GpgME::OpenPGP // 0166 && !key.isBad() // 0167 && hasValidUserID(key); 0168 } 0169 0170 namespace 0171 { 0172 static inline bool subkeyHasSecret(const GpgME::Subkey &subkey) 0173 { 0174 #if GPGME_VERSION_NUMBER >= 0x011102 // 1.17.2 0175 // 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 0176 return subkey.isSecret(); 0177 #else 0178 // older versions of GpgME did not always set the secret flag for card keys 0179 return subkey.isSecret() || subkey.isCardKey(); 0180 #endif 0181 } 0182 } 0183 0184 bool Kleo::canBeUsedForEncryption(const GpgME::Key &key) 0185 { 0186 return !key.isBad() && Kleo::any_of(key.subkeys(), [](const auto &subkey) { 0187 return subkey.canEncrypt() && !subkey.isBad(); 0188 }); 0189 } 0190 0191 bool Kleo::canBeUsedForSigning(const GpgME::Key &key) 0192 { 0193 return !key.isBad() && Kleo::any_of(key.subkeys(), [](const auto &subkey) { 0194 return subkey.canSign() && !subkey.isBad() && subkeyHasSecret(subkey); 0195 }); 0196 } 0197 0198 bool Kleo::canBeUsedForSecretKeyOperations(const GpgME::Key &key) 0199 { 0200 return subkeyHasSecret(key.subkey(0)); 0201 } 0202 0203 bool Kleo::canRevokeUserID(const GpgME::UserID &userId) 0204 { 0205 return (!userId.isNull() // 0206 && userId.parent().protocol() == GpgME::OpenPGP // 0207 && !isLastValidUserID(userId)); 0208 } 0209 0210 bool Kleo::isSecretKeyStoredInKeyRing(const GpgME::Key &key) 0211 { 0212 return key.subkey(0).isSecret() && !key.subkey(0).isCardKey(); 0213 } 0214 0215 bool Kleo::userHasCertificationKey() 0216 { 0217 const auto secretKeys = KeyCache::instance()->secretKeys(); 0218 return Kleo::any_of(secretKeys, [](const auto &k) { 0219 return (k.protocol() == GpgME::OpenPGP) && canCreateCertifications(k); 0220 }); 0221 } 0222 0223 Kleo::CertificationRevocationFeasibility Kleo::userCanRevokeCertification(const GpgME::UserID::Signature &certification) 0224 { 0225 const auto certificationKey = KeyCache::instance()->findByKeyIDOrFingerprint(certification.signerKeyID()); 0226 const bool isSelfSignature = qstrcmp(certification.parent().parent().keyID(), certification.signerKeyID()) == 0; 0227 if (!certificationKey.hasSecret()) { 0228 return CertificationNotMadeWithOwnKey; 0229 } else if (isSelfSignature) { 0230 return CertificationIsSelfSignature; 0231 } else if (certification.isRevokation()) { 0232 return CertificationIsRevocation; 0233 } else if (certification.isExpired()) { 0234 return CertificationIsExpired; 0235 } else if (certification.isInvalid()) { 0236 return CertificationIsInvalid; 0237 } else if (!canCreateCertifications(certificationKey)) { 0238 return CertificationKeyNotAvailable; 0239 } 0240 return CertificationCanBeRevoked; 0241 } 0242 0243 bool Kleo::userCanRevokeCertifications(const GpgME::UserID &userId) 0244 { 0245 if (userId.numSignatures() == 0) { 0246 qCWarning(LIBKLEO_LOG) << __func__ << "- Error: Signatures of user ID" << QString::fromUtf8(userId.id()) << "not available"; 0247 } 0248 return Kleo::any_of(userId.signatures(), [](const auto &certification) { 0249 return userCanRevokeCertification(certification) == CertificationCanBeRevoked; 0250 }); 0251 } 0252 0253 bool Kleo::userIDBelongsToKey(const GpgME::UserID &userID, const GpgME::Key &key) 0254 { 0255 return !qstricmp(userID.parent().primaryFingerprint(), key.primaryFingerprint()); 0256 } 0257 0258 static time_t creationDate(const GpgME::UserID &uid) 0259 { 0260 // returns the date of the first self-signature 0261 for (unsigned int i = 0, numSignatures = uid.numSignatures(); i < numSignatures; ++i) { 0262 const auto sig = uid.signature(i); 0263 if (Kleo::isSelfSignature(sig)) { 0264 return sig.creationTime(); 0265 } 0266 } 0267 return 0; 0268 } 0269 0270 bool Kleo::userIDsAreEqual(const GpgME::UserID &lhs, const GpgME::UserID &rhs) 0271 { 0272 return (qstrcmp(lhs.parent().primaryFingerprint(), rhs.parent().primaryFingerprint()) == 0 // 0273 && qstrcmp(lhs.id(), rhs.id()) == 0 // 0274 && creationDate(lhs) == creationDate(rhs)); 0275 } 0276 0277 static inline bool isOpenPGPCertification(const GpgME::UserID::Signature &sig) 0278 { 0279 // certification class is 0x10, ..., 0x13 0280 return (sig.certClass() & ~0x03) == 0x10; 0281 } 0282 0283 static bool isOpenPGPCertificationByUser(const GpgME::UserID::Signature &sig) 0284 { 0285 if (!isOpenPGPCertification(sig)) { 0286 return false; 0287 } 0288 const auto certificationKey = KeyCache::instance()->findByKeyIDOrFingerprint(sig.signerKeyID()); 0289 return certificationKey.ownerTrust() == Key::Ultimate; 0290 } 0291 0292 bool Kleo::userIDIsCertifiedByUser(const GpgME::UserID &userId) 0293 { 0294 if (userId.parent().protocol() != GpgME::OpenPGP) { 0295 qCWarning(LIBKLEO_LOG) << __func__ << "not called with OpenPGP key"; 0296 return false; 0297 } 0298 if (userId.numSignatures() == 0) { 0299 qCWarning(LIBKLEO_LOG) << __func__ << "- Error: Signatures of user ID" << QString::fromUtf8(userId.id()) << "not available"; 0300 } 0301 for (unsigned int i = 0, numSignatures = userId.numSignatures(); i < numSignatures; ++i) { 0302 const auto sig = userId.signature(i); 0303 if ((sig.status() == UserID::Signature::NoError) && !sig.isBad() && sig.isExportable() && isOpenPGPCertificationByUser(sig)) { 0304 return true; 0305 } 0306 } 0307 return false; 0308 }