diff --git a/src/utils/keys.cpp b/src/utils/keys.cpp index a99d1a026..559d1b2f0 100644 --- a/src/utils/keys.cpp +++ b/src/utils/keys.cpp @@ -1,143 +1,162 @@ /* -*- mode: c++; c-basic-offset:4 -*- utils/keys.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2022 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #include "keys.h" #include #include #include // needed for GPGME_VERSION_NUMBER #include #include #include 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 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 key.canCertify() && 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) { #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(); #else // older versions of GpgME did not always set the secret flag for card keys return key.subkey(0).isSecret() || key.subkey(0).isCardKey(); #endif } 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(KLEOPATRA_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/keys.h b/src/utils/keys.h index 088dc7bb1..3d74664e2 100644 --- a/src/utils/keys.h +++ b/src/utils/keys.h @@ -1,113 +1,122 @@ /* -*- mode: c++; c-basic-offset:4 -*- utils/keys.h This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2022 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once #include namespace Kleo { struct CertificatePair { GpgME::Key openpgp; GpgME::Key cms; }; /** Returns true if \p signature is a self-signature. */ 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. */ 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. */ 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. */ bool canBeCertified(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. */ 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. */ 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. */ 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 */ bool userHasCertificationKey(); enum CertificationRevocationFeasibility { CertificationCanBeRevoked = 0, CertificationNotMadeWithOwnKey, CertificationIsSelfSignature, CertificationIsRevocation, CertificationIsExpired, CertificationIsInvalid, CertificationKeyNotAvailable, }; /** * Checks if the user can revoke the given \p certification. */ CertificationRevocationFeasibility userCanRevokeCertification(const GpgME::UserID::Signature &certification); /** * Returns true if the user can revoke any of the certifications of the \p userId. * * \sa userCanRevokeCertification */ bool userCanRevokeCertifications(const GpgME::UserID &userId); /** * Returns true, if the user ID \p userID belongs to the key \p key. */ 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). + */ +bool userIDsAreEqual(const GpgME::UserID &lhs, const GpgME::UserID &rhs); + }