Libkleo

keyhelpers.cpp
1/*
2 utils/keyhelpers.cpp
3
4 This file is part of libkleopatra, the KDE keymanagement library
5 SPDX-FileCopyrightText: 2022 g10 Code GmbH
6 SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
7
8 SPDX-License-Identifier: GPL-2.0-or-later
9*/
10
11#include <config-libkleo.h>
12
13#include "keyhelpers.h"
14
15#include <libkleo/algorithm.h>
16#include <libkleo/compat.h>
17#include <libkleo/keycache.h>
18
19#include <libkleo_debug.h>
20
21#include <QDate>
22
23#include <iterator>
24
25using namespace Kleo;
26using namespace GpgME;
27
28namespace
29{
30bool havePublicKeyForSignature(const GpgME::UserID::Signature &signature)
31{
32 // GnuPG returns status "NoPublicKey" for missing signing keys, but also
33 // for expired or revoked signing keys.
34 return (signature.status() != GpgME::UserID::Signature::NoPublicKey) //
35 || !KeyCache::instance()->findByKeyIDOrFingerprint(signature.signerKeyID()).isNull();
36}
37
38auto _getMissingSignerKeyIds(const std::vector<GpgME::UserID::Signature> &signatures)
39{
40 return std::accumulate(std::begin(signatures), std::end(signatures), std::set<QString>{}, [](auto &keyIds, const auto &signature) {
41 if (!havePublicKeyForSignature(signature)) {
42 keyIds.insert(QLatin1StringView{signature.signerKeyID()});
43 }
44 return keyIds;
45 });
46}
47}
48
49std::set<QString> Kleo::getMissingSignerKeyIds(const std::vector<GpgME::UserID> &userIds)
50{
51 return std::accumulate(std::begin(userIds), std::end(userIds), std::set<QString>{}, [](auto &keyIds, const auto &userID) {
52 if (!userID.isBad()) {
53 const auto newKeyIds = _getMissingSignerKeyIds(userID.signatures());
54 std::copy(std::begin(newKeyIds), std::end(newKeyIds), std::inserter(keyIds, std::end(keyIds)));
55 }
56 return keyIds;
57 });
58}
59
60std::set<QString> Kleo::getMissingSignerKeyIds(const std::vector<GpgME::Key> &keys)
61{
62 return std::accumulate(std::begin(keys), std::end(keys), std::set<QString>{}, [](auto &keyIds, const auto &key) {
63 if (!key.isBad()) {
64 const auto newKeyIds = getMissingSignerKeyIds(key.userIDs());
65 std::copy(std::begin(newKeyIds), std::end(newKeyIds), std::inserter(keyIds, std::end(keyIds)));
66 }
67 return keyIds;
68 });
69}
70
71bool Kleo::isRemoteKey(const GpgME::Key &key)
72{
73 // a remote key looked up via WKD has key list mode Local; therefore we also look for the key in the local key ring
74 return (key.keyListMode() == GpgME::Extern) || KeyCache::instance()->findByFingerprint(key.primaryFingerprint()).isNull();
75}
76
77GpgME::UserID::Validity Kleo::minimalValidityOfNotRevokedUserIDs(const Key &key)
78{
79 const std::vector<UserID> userIDs = key.userIDs();
80 const int minValidity = std::accumulate(userIDs.begin(), userIDs.end(), UserID::Ultimate + 1, [](int validity, const UserID &userID) {
81 return userID.isRevoked() ? validity : std::min(validity, static_cast<int>(userID.validity()));
82 });
83 return minValidity <= UserID::Ultimate ? static_cast<UserID::Validity>(minValidity) : UserID::Unknown;
84}
85
86GpgME::UserID::Validity Kleo::maximalValidityOfUserIDs(const Key &key)
87{
88 const auto userIDs = key.userIDs();
89 const int maxValidity = std::accumulate(userIDs.begin(), userIDs.end(), 0, [](int validity, const UserID &userID) {
90 return std::max(validity, static_cast<int>(userID.validity()));
91 });
92 return static_cast<UserID::Validity>(maxValidity);
93}
94
95bool Kleo::allUserIDsHaveFullValidity(const GpgME::Key &key)
96{
97 return minimalValidityOfNotRevokedUserIDs(key) >= UserID::Full;
98}
99
100namespace
101{
102bool isLastValidUserID(const GpgME::UserID &userId)
103{
104 if (Kleo::isRevokedOrExpired(userId)) {
105 return false;
106 }
107 const auto userIds = userId.parent().userIDs();
108 const int numberOfValidUserIds = std::count_if(std::begin(userIds), std::end(userIds), [](const auto &u) {
109 return !Kleo::isRevokedOrExpired(u);
110 });
111 return numberOfValidUserIds == 1;
112}
113
114bool hasValidUserID(const GpgME::Key &key)
115{
116 return Kleo::any_of(key.userIDs(), [](const auto &u) {
117 return !Kleo::isRevokedOrExpired(u);
118 });
119}
120}
121
122bool Kleo::isSelfSignature(const GpgME::UserID::Signature &signature)
123{
124 return !qstrcmp(signature.parent().parent().keyID(), signature.signerKeyID());
125}
126
127bool Kleo::isRevokedOrExpired(const GpgME::UserID &userId)
128{
129 if (userId.isRevoked() || userId.parent().isExpired()) {
130 return true;
131 }
132 const auto sigs = userId.signatures();
133 std::vector<GpgME::UserID::Signature> selfSigs;
134 std::copy_if(std::begin(sigs), std::end(sigs), std::back_inserter(selfSigs), &Kleo::isSelfSignature);
135 std::sort(std::begin(selfSigs), std::end(selfSigs));
136 // check the most recent signature
137 const auto sig = !selfSigs.empty() ? selfSigs.back() : GpgME::UserID::Signature{};
138 return !sig.isNull() && (sig.isRevokation() || sig.isExpired());
139}
140
141bool Kleo::isExpired(const UserID &userID)
142{
143 if (userID.parent().isExpired()) {
144 return true;
145 }
146 const auto sigs = userID.signatures();
147 std::vector<GpgME::UserID::Signature> selfSigs;
148 std::copy_if(std::begin(sigs), std::end(sigs), std::back_inserter(selfSigs), &Kleo::isSelfSignature);
149 std::sort(std::begin(selfSigs), std::end(selfSigs));
150 // check the most recent signature
151 const auto sig = !selfSigs.empty() ? selfSigs.back() : GpgME::UserID::Signature{};
152 return !sig.isNull() && sig.isExpired();
153}
154
155bool Kleo::canCreateCertifications(const GpgME::Key &key)
156{
157 return Kleo::keyHasCertify(key) && canBeUsedForSecretKeyOperations(key);
158}
159
160bool Kleo::canBeCertified(const GpgME::Key &key)
161{
162 return key.protocol() == GpgME::OpenPGP //
163 && !key.isBad() //
164 && hasValidUserID(key);
165}
166
167bool Kleo::canBeUsedForEncryption(const GpgME::Key &key)
168{
169 return !key.isBad() && Kleo::any_of(key.subkeys(), [](const auto &subkey) {
170 return subkey.canEncrypt() && !subkey.isBad();
171 });
172}
173
174bool Kleo::canBeUsedForSigning(const GpgME::Key &key)
175{
176 return !key.isBad() && Kleo::any_of(key.subkeys(), [](const auto &subkey) {
177 return subkey.canSign() && !subkey.isBad() && subkey.isSecret();
178 });
179}
180
181bool Kleo::canBeUsedForSecretKeyOperations(const GpgME::Key &key)
182{
183 // 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
184 return key.subkey(0).isSecret();
185}
186
187bool Kleo::canRevokeUserID(const GpgME::UserID &userId)
188{
189 return (!userId.isNull() //
190 && userId.parent().protocol() == GpgME::OpenPGP //
191 && !isLastValidUserID(userId));
192}
193
194bool Kleo::isSecretKeyStoredInKeyRing(const GpgME::Key &key)
195{
196 return key.subkey(0).isSecret() && !key.subkey(0).isCardKey();
197}
198
199bool Kleo::userHasCertificationKey()
200{
201 const auto secretKeys = KeyCache::instance()->secretKeys();
202 return Kleo::any_of(secretKeys, [](const auto &k) {
203 return (k.protocol() == GpgME::OpenPGP) && canCreateCertifications(k);
204 });
205}
206
207Kleo::CertificationRevocationFeasibility Kleo::userCanRevokeCertification(const GpgME::UserID::Signature &certification)
208{
209 const auto certificationKey = KeyCache::instance()->findByKeyIDOrFingerprint(certification.signerKeyID());
210 const bool isSelfSignature = qstrcmp(certification.parent().parent().keyID(), certification.signerKeyID()) == 0;
211 if (!certificationKey.hasSecret()) {
212 return CertificationNotMadeWithOwnKey;
213 } else if (isSelfSignature) {
214 return CertificationIsSelfSignature;
215 } else if (certification.isRevokation()) {
216 return CertificationIsRevocation;
217 } else if (certification.isExpired()) {
218 return CertificationIsExpired;
219 } else if (certification.isInvalid()) {
220 return CertificationIsInvalid;
221 } else if (!canCreateCertifications(certificationKey)) {
222 return CertificationKeyNotAvailable;
223 }
224 return CertificationCanBeRevoked;
225}
226
227bool Kleo::userCanRevokeCertifications(const GpgME::UserID &userId)
228{
229 if (userId.numSignatures() == 0) {
230 qCWarning(LIBKLEO_LOG) << __func__ << "- Error: Signatures of user ID" << QString::fromUtf8(userId.id()) << "not available";
231 }
232 return Kleo::any_of(userId.signatures(), [](const auto &certification) {
233 return userCanRevokeCertification(certification) == CertificationCanBeRevoked;
234 });
235}
236
237bool Kleo::userIDBelongsToKey(const GpgME::UserID &userID, const GpgME::Key &key)
238{
239 return !qstricmp(userID.parent().primaryFingerprint(), key.primaryFingerprint());
240}
241
242static time_t creationDate(const GpgME::UserID &uid)
243{
244 // returns the date of the first self-signature
245 for (unsigned int i = 0, numSignatures = uid.numSignatures(); i < numSignatures; ++i) {
246 const auto sig = uid.signature(i);
247 if (Kleo::isSelfSignature(sig)) {
248 return sig.creationTime();
249 }
250 }
251 return 0;
252}
253
254bool Kleo::userIDsAreEqual(const GpgME::UserID &lhs, const GpgME::UserID &rhs)
255{
256 return (qstrcmp(lhs.parent().primaryFingerprint(), rhs.parent().primaryFingerprint()) == 0 //
257 && qstrcmp(lhs.id(), rhs.id()) == 0 //
258 && creationDate(lhs) == creationDate(rhs));
259}
260
261static inline bool isOpenPGPCertification(const GpgME::UserID::Signature &sig)
262{
263 // certification class is 0x10, ..., 0x13
264 return (sig.certClass() & ~0x03) == 0x10;
265}
266
267static bool isOpenPGPCertificationByUser(const GpgME::UserID::Signature &sig)
268{
269 if (!isOpenPGPCertification(sig)) {
270 return false;
271 }
272 const auto certificationKey = KeyCache::instance()->findByKeyIDOrFingerprint(sig.signerKeyID());
273 return certificationKey.ownerTrust() == Key::Ultimate;
274}
275
276bool Kleo::userIDIsCertifiedByUser(const GpgME::UserID &userId)
277{
278 if (userId.parent().protocol() != GpgME::OpenPGP) {
279 qCWarning(LIBKLEO_LOG) << __func__ << "not called with OpenPGP key";
280 return false;
281 }
282 if (userId.numSignatures() == 0) {
283 qCWarning(LIBKLEO_LOG) << __func__ << "- Error: Signatures of user ID" << QString::fromUtf8(userId.id()) << "not available";
284 }
285 for (unsigned int i = 0, numSignatures = userId.numSignatures(); i < numSignatures; ++i) {
286 const auto sig = userId.signature(i);
287 if ((sig.status() == UserID::Signature::NoError) && !sig.isBad() && sig.isExportable() && isOpenPGPCertificationByUser(sig)) {
288 return true;
289 }
290 }
291 return false;
292}
QString fromUtf8(QByteArrayView str)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 4 2024 16:29:01 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.