Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F34381726
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
15 KB
Subscribers
None
View Options
diff --git a/src/utils/keyhelpers.cpp b/src/utils/keyhelpers.cpp
index e0690923..5d454432 100644
--- a/src/utils/keyhelpers.cpp
+++ b/src/utils/keyhelpers.cpp
@@ -1,240 +1,262 @@
/*
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::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);
}
-bool Kleo::canBeUsedForSecretKeyOperations(const GpgME::Key &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 key.subkey(0).isSecret();
+ return subkey.isSecret();
#else
// older versions of GpgME did not always set the secret flag for card keys
- return key.subkey(0).isSecret() || key.subkey(0).isCardKey();
+ 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));
}
diff --git a/src/utils/keyhelpers.h b/src/utils/keyhelpers.h
index 6b1b2361..80873e1c 100644
--- a/src/utils/keyhelpers.h
+++ b/src/utils/keyhelpers.h
@@ -1,172 +1,186 @@
/*
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 \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);
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Jan 3, 11:45 PM (14 h, 40 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
d7/b6/be3a2a30959c25253769c6bca332
Attached To
rLIBKLEO Libkleo
Event Timeline
Log In to Comment