Libkleo

keyresolvercore.cpp
1/* -*- c++ -*-
2 kleo/keyresolvercore.cpp
3
4 This file is part of libkleopatra, the KDE keymanagement library
5 SPDX-FileCopyrightText: 2004 Klarälvdalens Datakonsult AB
6 SPDX-FileCopyrightText: 2018 Intevation GmbH
7 SPDX-FileCopyrightText: 2021 g10 Code GmbH
8 SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
9
10 Based on kpgp.cpp
11 SPDX-FileCopyrightText: 2001, 2002 the KPGP authors
12 See file libkdenetwork/AUTHORS.kpgp for details
13
14 SPDX-License-Identifier: GPL-2.0-or-later
15*/
16
17#include <config-libkleo.h>
18
19#include "keyresolvercore.h"
20
21#include "enum.h"
22#include "keygroup.h"
23
24#include <libkleo/compat.h>
25#include <libkleo/compliance.h>
26#include <libkleo/formatting.h>
27#include <libkleo/gnupg.h>
28#include <libkleo/keycache.h>
29#include <libkleo/keyhelpers.h>
30
31#include "kleo/debug.h"
32#include <libkleo_debug.h>
33
34#include <gpgme++/key.h>
35
36using namespace Kleo;
37using namespace GpgME;
38
39namespace
40{
41
42static inline bool ValidEncryptionKey(const Key &key)
43{
44 if (key.isNull() || key.isRevoked() || key.isExpired() || key.isDisabled() || !Kleo::keyHasEncrypt(key)) {
45 return false;
46 }
47 return true;
48}
49
50static inline bool ValidSigningKey(const Key &key)
51{
52 if (key.isNull() || key.isRevoked() || key.isExpired() || key.isDisabled() || !Kleo::keyHasSign(key) || !key.hasSecret()) {
53 return false;
54 }
55 return true;
56}
57
58static int keyValidity(const Key &key, const QString &address)
59{
60 // returns the validity of the UID matching the address or, if no UID matches, the maximal validity of all UIDs
61 int overallValidity = UserID::Validity::Unknown;
62 for (const auto &uid : key.userIDs()) {
63 if (QString::fromStdString(uid.addrSpec()).toLower() == address.toLower()) {
64 return uid.validity();
65 }
66 overallValidity = std::max(overallValidity, static_cast<int>(uid.validity()));
67 }
68 return overallValidity;
69}
70
71static int minimumValidity(const std::vector<Key> &keys, const QString &address)
72{
73 const int minValidity = std::accumulate(keys.cbegin(), //
74 keys.cend(),
75 UserID::Ultimate + 1,
76 [address](int validity, const Key &key) {
77 return std::min<int>(validity, keyValidity(key, address));
78 });
79 return minValidity <= UserID::Ultimate ? static_cast<UserID::Validity>(minValidity) : UserID::Unknown;
80}
81
82} // namespace
83
84class KeyResolverCore::Private
85{
86public:
87 Private(KeyResolverCore *qq, bool enc, bool sig, Protocol fmt)
88 : q(qq)
89 , mFormat(fmt)
90 , mEncrypt(enc)
91 , mSign(sig)
92 , mCache(KeyCache::instance())
93 , mPreferredProtocol(UnknownProtocol)
94 , mMinimumValidity(UserID::Marginal)
95 {
96 }
97
98 ~Private() = default;
99
100 bool isAcceptableSigningKey(const Key &key);
101 bool isAcceptableEncryptionKey(const Key &key, const QString &address = QString());
102 void setSender(const QString &address);
103 void addRecipients(const QStringList &addresses);
104 void setOverrideKeys(const QMap<Protocol, QMap<QString, QStringList>> &overrides);
105 void resolveOverrides();
106 std::vector<Key> resolveRecipientWithGroup(const QString &address, Protocol protocol);
107 void resolveEncryptionGroups();
108 std::vector<Key> resolveSenderWithGroup(const QString &address, Protocol protocol);
109 void resolveSigningGroups();
110 void resolveSign(Protocol proto);
111 void setSigningKeys(const QStringList &fingerprints);
112 std::vector<Key> resolveRecipient(const QString &address, Protocol protocol);
113 void resolveEnc(Protocol proto);
114 void mergeEncryptionKeys();
115 Result resolve();
116
117 KeyResolverCore *const q;
118 QString mSender;
119 QStringList mRecipients;
123
124 Protocol mFormat;
125 QStringList mFatalErrors;
126 bool mEncrypt;
127 bool mSign;
128 // The cache is needed as a member variable to avoid rebuilding
129 // it between calls if we are the only user.
130 std::shared_ptr<const KeyCache> mCache;
131 bool mAllowMixed = true;
132 Protocol mPreferredProtocol;
133 int mMinimumValidity;
134};
135
136bool KeyResolverCore::Private::isAcceptableSigningKey(const Key &key)
137{
138 if (!ValidSigningKey(key)) {
139 return false;
140 }
141 if (DeVSCompliance::isCompliant() && !DeVSCompliance::keyIsCompliant(key)) {
142 qCDebug(LIBKLEO_LOG) << "Rejected sig key" << key.primaryFingerprint() << "because it is not de-vs compliant.";
143 return false;
144 }
145 return true;
146}
147
148bool KeyResolverCore::Private::isAcceptableEncryptionKey(const Key &key, const QString &address)
149{
150 if (!ValidEncryptionKey(key)) {
151 return false;
152 }
153
154 if (DeVSCompliance::isCompliant() && !DeVSCompliance::keyIsCompliant(key)) {
155 qCDebug(LIBKLEO_LOG) << "Rejected enc key" << key.primaryFingerprint() << "because it is not de-vs compliant.";
156 return false;
157 }
158
159 if (address.isEmpty()) {
160 // group key must satisfy minimum validity for all user IDs
161 return Kleo::minimalValidityOfNotRevokedUserIDs(key) >= mMinimumValidity;
162 }
163 for (const auto &uid : key.userIDs()) {
164 if (uid.addrSpec() == address.toStdString()) {
165 if (uid.validity() >= mMinimumValidity) {
166 return true;
167 }
168 }
169 }
170 return false;
171}
172
173void KeyResolverCore::Private::setSender(const QString &address)
174{
175 const auto normalized = UserID::addrSpecFromString(address.toUtf8().constData());
176 if (normalized.empty()) {
177 // should not happen bug in the caller, non localized
178 // error for bug reporting.
179 mFatalErrors << QStringLiteral("The sender address '%1' could not be extracted").arg(address);
180 return;
181 }
182 const auto normStr = QString::fromUtf8(normalized.c_str());
183 mSender = normStr;
184 addRecipients({address});
185}
186
187void KeyResolverCore::Private::addRecipients(const QStringList &addresses)
188{
189 if (!mEncrypt) {
190 return;
191 }
192
193 // Internally we work with normalized addresses. Normalization
194 // matches the gnupg one.
195 for (const auto &addr : addresses) {
196 // PGP Uids are defined to be UTF-8 (RFC 4880 §5.11)
197 const auto normalized = UserID::addrSpecFromString(addr.toUtf8().constData());
198 if (normalized.empty()) {
199 // should not happen bug in the caller, non localized
200 // error for bug reporting.
201 mFatalErrors << QStringLiteral("The mail address for '%1' could not be extracted").arg(addr);
202 continue;
203 }
204 const QString normStr = QString::fromUtf8(normalized.c_str());
205
206 mRecipients << normStr;
207
208 // Initially add empty lists of keys for both protocols
209 mEncKeys[normStr] = {{CMS, {}}, {OpenPGP, {}}};
210 }
211}
212
213void KeyResolverCore::Private::setOverrideKeys(const QMap<Protocol, QMap<QString, QStringList>> &overrides)
214{
215 for (auto protocolIt = overrides.cbegin(); protocolIt != overrides.cend(); ++protocolIt) {
216 const Protocol &protocol = protocolIt.key();
217 const auto &addressFingerprintMap = protocolIt.value();
218 for (auto addressIt = addressFingerprintMap.cbegin(); addressIt != addressFingerprintMap.cend(); ++addressIt) {
219 const QString &address = addressIt.key();
220 const QStringList &fingerprints = addressIt.value();
221 const QString normalizedAddress = QString::fromUtf8(UserID::addrSpecFromString(address.toUtf8().constData()).c_str());
222 mOverrides[normalizedAddress][protocol] = fingerprints;
223 }
224 }
225}
226
227namespace
228{
229std::vector<Key> resolveOverride(const QString &address, Protocol protocol, const QStringList &fingerprints)
230{
231 std::vector<Key> keys;
232 for (const auto &fprOrId : fingerprints) {
233 const Key key = KeyCache::instance()->findByKeyIDOrFingerprint(fprOrId.toUtf8().constData());
234 if (key.isNull()) {
235 // FIXME: Report to caller
236 qCDebug(LIBKLEO_LOG) << "Failed to find override key for:" << address << "fpr:" << fprOrId;
237 continue;
238 }
239 if (protocol != UnknownProtocol && key.protocol() != protocol) {
240 qCDebug(LIBKLEO_LOG) << "Ignoring key" << Formatting::summaryLine(key) << "given as" << Formatting::displayName(protocol) << "override for"
241 << address;
242 continue;
243 }
244 qCDebug(LIBKLEO_LOG) << "Using key" << Formatting::summaryLine(key) << "as" << Formatting::displayName(protocol) << "override for" << address;
245 keys.push_back(key);
246 }
247 return keys;
248}
249}
250
251void KeyResolverCore::Private::resolveOverrides()
252{
253 if (!mEncrypt) {
254 // No encryption we are done.
255 return;
256 }
257 for (auto addressIt = mOverrides.cbegin(); addressIt != mOverrides.cend(); ++addressIt) {
258 const QString &address = addressIt.key();
259 const auto &protocolFingerprintsMap = addressIt.value();
260
261 if (!mRecipients.contains(address)) {
262 qCDebug(LIBKLEO_LOG) << "Overrides provided for an address that is "
263 "neither sender nor recipient. Address:"
264 << address;
265 continue;
266 }
267
268 const QStringList commonOverride = protocolFingerprintsMap.value(UnknownProtocol);
269 if (!commonOverride.empty()) {
270 mEncKeys[address][UnknownProtocol] = resolveOverride(address, UnknownProtocol, commonOverride);
271 if (protocolFingerprintsMap.contains(OpenPGP)) {
272 qCDebug(LIBKLEO_LOG) << "Ignoring OpenPGP-specific override for" << address << "in favor of common override";
273 }
274 if (protocolFingerprintsMap.contains(CMS)) {
275 qCDebug(LIBKLEO_LOG) << "Ignoring S/MIME-specific override for" << address << "in favor of common override";
276 }
277 } else {
278 if (mFormat != CMS) {
279 mEncKeys[address][OpenPGP] = resolveOverride(address, OpenPGP, protocolFingerprintsMap.value(OpenPGP));
280 }
281 if (mFormat != OpenPGP) {
282 mEncKeys[address][CMS] = resolveOverride(address, CMS, protocolFingerprintsMap.value(CMS));
283 }
284 }
285 }
286}
287
288std::vector<Key> KeyResolverCore::Private::resolveSenderWithGroup(const QString &address, Protocol protocol)
289{
290 // prefer single-protocol groups over mixed-protocol groups
291 auto group = mCache->findGroup(address, protocol, KeyCache::KeyUsage::Sign);
292 if (group.isNull()) {
293 group = mCache->findGroup(address, UnknownProtocol, KeyCache::KeyUsage::Sign);
294 }
295 if (group.isNull()) {
296 return {};
297 }
298
299 // take the first key matching the protocol
300 const auto &keys = group.keys();
301 const auto it = std::find_if(std::begin(keys), std::end(keys), [protocol](const auto &key) {
302 return key.protocol() == protocol;
303 });
304 if (it == std::end(keys)) {
305 qCDebug(LIBKLEO_LOG) << "group" << group.name() << "has no" << Formatting::displayName(protocol) << "signing key";
306 return {};
307 }
308 const auto key = *it;
309 if (!isAcceptableSigningKey(key)) {
310 qCDebug(LIBKLEO_LOG) << "group" << group.name() << "has unacceptable signing key" << key;
311 return {};
312 }
313 return {key};
314}
315
316void KeyResolverCore::Private::resolveSigningGroups()
317{
318 auto &protocolKeysMap = mSigKeys;
319 if (!protocolKeysMap[UnknownProtocol].empty()) {
320 // already resolved by common override
321 return;
322 }
323 if (mFormat == OpenPGP) {
324 if (!protocolKeysMap[OpenPGP].empty()) {
325 // already resolved by override
326 return;
327 }
328 protocolKeysMap[OpenPGP] = resolveSenderWithGroup(mSender, OpenPGP);
329 } else if (mFormat == CMS) {
330 if (!protocolKeysMap[CMS].empty()) {
331 // already resolved by override
332 return;
333 }
334 protocolKeysMap[CMS] = resolveSenderWithGroup(mSender, CMS);
335 } else {
336 if (protocolKeysMap[OpenPGP].empty()) {
337 protocolKeysMap[OpenPGP] = resolveSenderWithGroup(mSender, OpenPGP);
338 }
339 if (protocolKeysMap[CMS].empty()) {
340 protocolKeysMap[CMS] = resolveSenderWithGroup(mSender, CMS);
341 }
342 }
343}
344
345void KeyResolverCore::Private::resolveSign(Protocol proto)
346{
347 if (!mSigKeys[proto].empty()) {
348 // Explicitly set
349 return;
350 }
351 const auto key = mCache->findBestByMailBox(mSender.toUtf8().constData(), proto, KeyCache::KeyUsage::Sign);
352 if (key.isNull()) {
353 qCDebug(LIBKLEO_LOG) << "Failed to find" << Formatting::displayName(proto) << "signing key for" << mSender;
354 return;
355 }
356 if (!isAcceptableSigningKey(key)) {
357 qCDebug(LIBKLEO_LOG) << "Unacceptable signing key" << key.primaryFingerprint() << "for" << mSender;
358 return;
359 }
360 mSigKeys.insert(proto, {key});
361}
362
363void KeyResolverCore::Private::setSigningKeys(const QStringList &fingerprints)
364{
365 if (mSign) {
366 for (const auto &fpr : fingerprints) {
367 const auto key = mCache->findByKeyIDOrFingerprint(fpr.toUtf8().constData());
368 if (key.isNull()) {
369 qCDebug(LIBKLEO_LOG) << "Failed to find signing key with fingerprint" << fpr;
370 continue;
371 }
372 mSigKeys[key.protocol()].push_back(key);
373 }
374 }
375}
376
377std::vector<Key> KeyResolverCore::Private::resolveRecipientWithGroup(const QString &address, Protocol protocol)
378{
379 const auto group = mCache->findGroup(address, protocol, KeyCache::KeyUsage::Encrypt);
380 if (group.isNull()) {
381 return {};
382 }
383
384 // If we have one unacceptable group key we reject the
385 // whole group to avoid the situation where one key is
386 // skipped or the operation fails.
387 //
388 // We are in Autoresolve land here. In the GUI we
389 // will also show unacceptable group keys so that the
390 // user can see which key is not acceptable.
391 const auto &keys = group.keys();
392 const bool allKeysAreAcceptable = std::all_of(std::begin(keys), std::end(keys), [this](const auto &key) {
393 return isAcceptableEncryptionKey(key);
394 });
395 if (!allKeysAreAcceptable) {
396 qCDebug(LIBKLEO_LOG) << "group" << group.name() << "has at least one unacceptable key";
397 return {};
398 }
399 for (const auto &k : keys) {
400 qCDebug(LIBKLEO_LOG) << "Resolved encrypt to" << address << "with key" << k.primaryFingerprint();
401 }
402 std::vector<Key> result;
403 std::copy(std::begin(keys), std::end(keys), std::back_inserter(result));
404 return result;
405}
406
407void KeyResolverCore::Private::resolveEncryptionGroups()
408{
409 for (auto it = mEncKeys.begin(); it != mEncKeys.end(); ++it) {
410 const QString &address = it.key();
411 auto &protocolKeysMap = it.value();
412 if (!protocolKeysMap[UnknownProtocol].empty()) {
413 // already resolved by common override
414 continue;
415 }
416 if (mFormat == OpenPGP) {
417 if (!protocolKeysMap[OpenPGP].empty()) {
418 // already resolved by override
419 continue;
420 }
421 protocolKeysMap[OpenPGP] = resolveRecipientWithGroup(address, OpenPGP);
422 } else if (mFormat == CMS) {
423 if (!protocolKeysMap[CMS].empty()) {
424 // already resolved by override
425 continue;
426 }
427 protocolKeysMap[CMS] = resolveRecipientWithGroup(address, CMS);
428 } else {
429 // prefer single-protocol groups over mixed-protocol groups
430 const auto openPGPGroupKeys = resolveRecipientWithGroup(address, OpenPGP);
431 const auto smimeGroupKeys = resolveRecipientWithGroup(address, CMS);
432 if (!openPGPGroupKeys.empty() && !smimeGroupKeys.empty()) {
433 protocolKeysMap[OpenPGP] = openPGPGroupKeys;
434 protocolKeysMap[CMS] = smimeGroupKeys;
435 } else if (openPGPGroupKeys.empty() && smimeGroupKeys.empty()) {
436 // no single-protocol groups found;
437 // if mixed protocols are allowed, then look for any group with encryption keys
438 if (mAllowMixed) {
439 protocolKeysMap[UnknownProtocol] = resolveRecipientWithGroup(address, UnknownProtocol);
440 }
441 } else {
442 // there is a single-protocol group only for one protocol; use this group for all protocols
443 protocolKeysMap[UnknownProtocol] = !openPGPGroupKeys.empty() ? openPGPGroupKeys : smimeGroupKeys;
444 }
445 }
446 }
447}
448
449std::vector<Key> KeyResolverCore::Private::resolveRecipient(const QString &address, Protocol protocol)
450{
451 const auto key = mCache->findBestByMailBox(address.toUtf8().constData(), protocol, KeyCache::KeyUsage::Encrypt);
452 if (key.isNull()) {
453 qCDebug(LIBKLEO_LOG) << "Failed to find any" << Formatting::displayName(protocol) << "key for:" << address;
454 return {};
455 }
456 if (!isAcceptableEncryptionKey(key, address)) {
457 qCDebug(LIBKLEO_LOG) << "key for:" << address << key.primaryFingerprint() << "has not enough validity";
458 return {};
459 }
460 qCDebug(LIBKLEO_LOG) << "Resolved encrypt to" << address << "with key" << key.primaryFingerprint();
461 return {key};
462}
463
464// Try to find matching keys in the provided protocol for the unresolved addresses
465void KeyResolverCore::Private::resolveEnc(Protocol proto)
466{
467 for (auto it = mEncKeys.begin(); it != mEncKeys.end(); ++it) {
468 const QString &address = it.key();
469 auto &protocolKeysMap = it.value();
470 if (!protocolKeysMap[proto].empty()) {
471 // already resolved for current protocol (by override or group)
472 continue;
473 }
474 const std::vector<Key> &commonOverrideOrGroup = protocolKeysMap[UnknownProtocol];
475 if (!commonOverrideOrGroup.empty()) {
476 // there is a common override or group; use it for current protocol if possible
477 if (allKeysHaveProtocol(commonOverrideOrGroup, proto)) {
478 protocolKeysMap[proto] = commonOverrideOrGroup;
479 continue;
480 } else {
481 qCDebug(LIBKLEO_LOG) << "Common override/group for" << address << "is unusable for" << Formatting::displayName(proto);
482 continue;
483 }
484 }
485 protocolKeysMap[proto] = resolveRecipient(address, proto);
486 }
487}
488
489auto getBestEncryptionKeys(const QMap<QString, QMap<Protocol, std::vector<Key>>> &encryptionKeys, Protocol preferredProtocol)
490{
492
493 for (auto it = encryptionKeys.begin(); it != encryptionKeys.end(); ++it) {
494 const QString &address = it.key();
495 auto &protocolKeysMap = it.value();
496 const std::vector<Key> &overrideKeys = protocolKeysMap[UnknownProtocol];
497 if (!overrideKeys.empty()) {
498 result.insert(address, overrideKeys);
499 continue;
500 }
501 const std::vector<Key> &keysOpenPGP = protocolKeysMap[OpenPGP];
502 const std::vector<Key> &keysCMS = protocolKeysMap[CMS];
503 if (keysOpenPGP.empty() && keysCMS.empty()) {
504 result.insert(address, {});
505 } else if (!keysOpenPGP.empty() && keysCMS.empty()) {
506 result.insert(address, keysOpenPGP);
507 } else if (keysOpenPGP.empty() && !keysCMS.empty()) {
508 result.insert(address, keysCMS);
509 } else {
510 // check whether OpenPGP keys or S/MIME keys have higher validity
511 const int validityPGP = minimumValidity(keysOpenPGP, address);
512 const int validityCMS = minimumValidity(keysCMS, address);
513 if ((validityCMS > validityPGP) || (validityCMS == validityPGP && preferredProtocol == CMS)) {
514 result.insert(address, keysCMS);
515 } else {
516 result.insert(address, keysOpenPGP);
517 }
518 }
519 }
520
521 return result;
522}
523
524namespace
525{
526bool hasUnresolvedSender(const QMap<Protocol, std::vector<Key>> &signingKeys, Protocol protocol)
527{
528 return signingKeys.value(protocol).empty();
529}
530
531bool hasUnresolvedRecipients(const QMap<QString, QMap<Protocol, std::vector<Key>>> &encryptionKeys, Protocol protocol)
532{
533 return std::any_of(std::cbegin(encryptionKeys), std::cend(encryptionKeys), [protocol](const auto &protocolKeysMap) {
534 return protocolKeysMap.value(protocol).empty();
535 });
536}
537
538bool anyCommonOverrideHasKeyOfType(const QMap<QString, QMap<Protocol, std::vector<Key>>> &encryptionKeys, Protocol protocol)
539{
540 return std::any_of(std::cbegin(encryptionKeys), std::cend(encryptionKeys), [protocol](const auto &protocolKeysMap) {
541 return anyKeyHasProtocol(protocolKeysMap.value(UnknownProtocol), protocol);
542 });
543}
544
545auto keysForProtocol(const QMap<QString, QMap<Protocol, std::vector<Key>>> &encryptionKeys, Protocol protocol)
546{
548 for (auto it = std::begin(encryptionKeys), end = std::end(encryptionKeys); it != end; ++it) {
549 const QString &address = it.key();
550 const auto &protocolKeysMap = it.value();
551 keys.insert(address, protocolKeysMap.value(protocol));
552 }
553 return keys;
554}
555
556template<typename T>
557auto concatenate(std::vector<T> v1, const std::vector<T> &v2)
558{
559 v1.reserve(v1.size() + v2.size());
560 v1.insert(std::end(v1), std::begin(v2), std::end(v2));
561 return v1;
562}
563
564}
565
566KeyResolverCore::Result KeyResolverCore::Private::resolve()
567{
568 qCDebug(LIBKLEO_LOG) << "Starting ";
569 if (!mSign && !mEncrypt) {
570 // nothing to do
571 return {AllResolved, {}, {}};
572 }
573
574 // First resolve through overrides
575 resolveOverrides();
576
577 // check protocols needed for overrides
578 const bool commonOverridesNeedOpenPGP = anyCommonOverrideHasKeyOfType(mEncKeys, OpenPGP);
579 const bool commonOverridesNeedCMS = anyCommonOverrideHasKeyOfType(mEncKeys, CMS);
580 if ((mFormat == OpenPGP && commonOverridesNeedCMS) //
581 || (mFormat == CMS && commonOverridesNeedOpenPGP) //
582 || (!mAllowMixed && commonOverridesNeedOpenPGP && commonOverridesNeedCMS)) {
583 // invalid protocol requirements -> clear intermediate result and abort resolution
584 mEncKeys.clear();
585 return {Error, {}, {}};
586 }
587
588 // Next look for matching groups of keys
589 if (mSign) {
590 resolveSigningGroups();
591 }
592 if (mEncrypt) {
593 resolveEncryptionGroups();
594 }
595
596 // Then look for signing / encryption keys
597 if (mFormat == OpenPGP || mFormat == UnknownProtocol) {
598 resolveSign(OpenPGP);
599 resolveEnc(OpenPGP);
600 }
601 const bool pgpOnly = ((!mEncrypt || !hasUnresolvedRecipients(mEncKeys, OpenPGP)) //
602 && (!mSign || !hasUnresolvedSender(mSigKeys, OpenPGP)));
603
604 if (mFormat == OpenPGP) {
605 return {
606 SolutionFlags((pgpOnly ? AllResolved : SomeUnresolved) | OpenPGPOnly),
607 {OpenPGP, mSigKeys.value(OpenPGP), keysForProtocol(mEncKeys, OpenPGP)},
608 {},
609 };
610 }
611
612 if (mFormat == CMS || mFormat == UnknownProtocol) {
613 resolveSign(CMS);
614 resolveEnc(CMS);
615 }
616 const bool cmsOnly = ((!mEncrypt || !hasUnresolvedRecipients(mEncKeys, CMS)) //
617 && (!mSign || !hasUnresolvedSender(mSigKeys, CMS)));
618
619 if (mFormat == CMS) {
620 return {
621 SolutionFlags((cmsOnly ? AllResolved : SomeUnresolved) | CMSOnly),
622 {CMS, mSigKeys.value(CMS), keysForProtocol(mEncKeys, CMS)},
623 {},
624 };
625 }
626
627 // check if single-protocol solution has been found
628 if (cmsOnly && (!pgpOnly || mPreferredProtocol == CMS)) {
629 if (!mAllowMixed) {
630 return {
631 SolutionFlags(AllResolved | CMSOnly),
632 {CMS, mSigKeys.value(CMS), keysForProtocol(mEncKeys, CMS)},
633 {OpenPGP, mSigKeys.value(OpenPGP), keysForProtocol(mEncKeys, OpenPGP)},
634 };
635 } else {
636 return {
637 SolutionFlags(AllResolved | CMSOnly),
638 {CMS, mSigKeys.value(CMS), keysForProtocol(mEncKeys, CMS)},
639 {},
640 };
641 }
642 }
643 if (pgpOnly) {
644 if (!mAllowMixed) {
645 return {
646 SolutionFlags(AllResolved | OpenPGPOnly),
647 {OpenPGP, mSigKeys.value(OpenPGP), keysForProtocol(mEncKeys, OpenPGP)},
648 {CMS, mSigKeys.value(CMS), keysForProtocol(mEncKeys, CMS)},
649 };
650 } else {
651 return {
652 SolutionFlags(AllResolved | OpenPGPOnly),
653 {OpenPGP, mSigKeys.value(OpenPGP), keysForProtocol(mEncKeys, OpenPGP)},
654 {},
655 };
656 }
657 }
658
659 if (!mAllowMixed) {
660 // return incomplete single-protocol solution
661 if (mPreferredProtocol == CMS) {
662 return {
663 SolutionFlags(SomeUnresolved | CMSOnly),
664 {CMS, mSigKeys.value(CMS), keysForProtocol(mEncKeys, CMS)},
665 {OpenPGP, mSigKeys.value(OpenPGP), keysForProtocol(mEncKeys, OpenPGP)},
666 };
667 } else {
668 return {
669 SolutionFlags(SomeUnresolved | OpenPGPOnly),
670 {OpenPGP, mSigKeys.value(OpenPGP), keysForProtocol(mEncKeys, OpenPGP)},
671 {CMS, mSigKeys.value(CMS), keysForProtocol(mEncKeys, CMS)},
672 };
673 }
674 }
675
676 const auto bestEncryptionKeys = getBestEncryptionKeys(mEncKeys, mPreferredProtocol);
677 // we are in mixed mode, i.e. we need an OpenPGP signing key and an S/MIME signing key
678 const bool senderIsResolved = (!mSign || (!hasUnresolvedSender(mSigKeys, OpenPGP) && !hasUnresolvedSender(mSigKeys, CMS)));
679 const bool allRecipientsAreResolved = std::all_of(std::begin(bestEncryptionKeys), std::end(bestEncryptionKeys), [](const auto &keys) {
680 return !keys.empty();
681 });
682 if (senderIsResolved && allRecipientsAreResolved) {
683 return {
684 SolutionFlags(AllResolved | MixedProtocols),
685 {UnknownProtocol, concatenate(mSigKeys.value(OpenPGP), mSigKeys.value(CMS)), bestEncryptionKeys},
686 {},
687 };
688 }
689
690 const bool allKeysAreOpenPGP = std::all_of(std::begin(bestEncryptionKeys), std::end(bestEncryptionKeys), [](const auto &keys) {
691 return allKeysHaveProtocol(keys, OpenPGP);
692 });
693 if (allKeysAreOpenPGP) {
694 return {
695 SolutionFlags(SomeUnresolved | OpenPGPOnly),
696 {OpenPGP, mSigKeys.value(OpenPGP), bestEncryptionKeys},
697 {},
698 };
699 }
700
701 const bool allKeysAreCMS = std::all_of(std::begin(bestEncryptionKeys), std::end(bestEncryptionKeys), [](const auto &keys) {
702 return allKeysHaveProtocol(keys, CMS);
703 });
704 if (allKeysAreCMS) {
705 return {
706 SolutionFlags(SomeUnresolved | CMSOnly),
707 {CMS, mSigKeys.value(CMS), bestEncryptionKeys},
708 {},
709 };
710 }
711
712 return {
713 SolutionFlags(SomeUnresolved | MixedProtocols),
714 {UnknownProtocol, concatenate(mSigKeys.value(OpenPGP), mSigKeys.value(CMS)), bestEncryptionKeys},
715 {},
716 };
717}
718
719KeyResolverCore::KeyResolverCore(bool encrypt, bool sign, Protocol fmt)
720 : d(new Private(this, encrypt, sign, fmt))
721{
722}
723
724KeyResolverCore::~KeyResolverCore() = default;
725
726void KeyResolverCore::setSender(const QString &address)
727{
728 d->setSender(address);
729}
730
731QString KeyResolverCore::normalizedSender() const
732{
733 return d->mSender;
734}
735
736void KeyResolverCore::setRecipients(const QStringList &addresses)
737{
738 d->addRecipients(addresses);
739}
740
741void KeyResolverCore::setSigningKeys(const QStringList &fingerprints)
742{
743 d->setSigningKeys(fingerprints);
744}
745
746void KeyResolverCore::setOverrideKeys(const QMap<Protocol, QMap<QString, QStringList>> &overrides)
747{
748 d->setOverrideKeys(overrides);
749}
750
751void KeyResolverCore::setAllowMixedProtocols(bool allowMixed)
752{
753 d->mAllowMixed = allowMixed;
754}
755
756void KeyResolverCore::setPreferredProtocol(Protocol proto)
757{
758 d->mPreferredProtocol = proto;
759}
760
761void KeyResolverCore::setMinimumValidity(int validity)
762{
763 d->mMinimumValidity = validity;
764}
765
766KeyResolverCore::Result KeyResolverCore::resolve()
767{
768 return d->resolve();
769}
KCODECS_EXPORT QString normalizedAddress(const QString &displayName, const QString &addrSpec, const QString &comment=QString())
QAction * end(const QObject *recvr, const char *slot, QObject *parent)
PostalAddress address(const QVariant &location)
bool empty() const const
T value(qsizetype i) const const
bool empty() const const
iterator insert(const Key &key, const T &value)
UnknownProtocol
QString fromStdString(const std::string &str)
QString fromUtf8(QByteArrayView str)
QString toLower() const const
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.