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 
36 using namespace Kleo;
37 using namespace GpgME;
38 
39 namespace
40 {
41 
42 static 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 
50 static 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 
58 static 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 
71 static 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 
84 class KeyResolverCore::Private
85 {
86 public:
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 
136 bool 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 
148 bool 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 
173 void 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 
187 void 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 
213 void 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 
227 namespace
228 {
229 std::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 
251 void 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 
288 std::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 
316 void 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 
345 void 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 
363 void 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 
377 std::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 
407 void 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 
449 std::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
465 void 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 
489 auto 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 
524 namespace
525 {
526 bool hasUnresolvedSender(const QMap<Protocol, std::vector<Key>> &signingKeys, Protocol protocol)
527 {
528  return signingKeys.value(protocol).empty();
529 }
530 
531 bool 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 
538 bool 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 
545 auto 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 
556 template<typename T>
557 auto 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 
566 KeyResolverCore::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 
719 KeyResolverCore::KeyResolverCore(bool encrypt, bool sign, Protocol fmt)
720  : d(new Private(this, encrypt, sign, fmt))
721 {
722 }
723 
724 KeyResolverCore::~KeyResolverCore() = default;
725 
726 void KeyResolverCore::setSender(const QString &address)
727 {
728  d->setSender(address);
729 }
730 
731 QString KeyResolverCore::normalizedSender() const
732 {
733  return d->mSender;
734 }
735 
736 void KeyResolverCore::setRecipients(const QStringList &addresses)
737 {
738  d->addRecipients(addresses);
739 }
740 
741 void KeyResolverCore::setSigningKeys(const QStringList &fingerprints)
742 {
743  d->setSigningKeys(fingerprints);
744 }
745 
746 void KeyResolverCore::setOverrideKeys(const QMap<Protocol, QMap<QString, QStringList>> &overrides)
747 {
748  d->setOverrideKeys(overrides);
749 }
750 
751 void KeyResolverCore::setAllowMixedProtocols(bool allowMixed)
752 {
753  d->mAllowMixed = allowMixed;
754 }
755 
756 void KeyResolverCore::setPreferredProtocol(Protocol proto)
757 {
758  d->mPreferredProtocol = proto;
759 }
760 
761 void KeyResolverCore::setMinimumValidity(int validity)
762 {
763  d->mMinimumValidity = validity;
764 }
765 
766 KeyResolverCore::Result KeyResolverCore::resolve()
767 {
768  return d->resolve();
769 }
QString fromUtf8(const char *str, int size)
bool empty() const const
const T value(const Key &key, const T &defaultValue) const const
QMap::iterator insert(const Key &key, const T &value)
bool empty() const const
UnknownProtocol
QString fromStdString(const std::string &str)
KCODECS_EXPORT QString normalizedAddress(const QString &displayName, const QString &addrSpec, const QString &comment=QString())
PostalAddress address(const QVariant &location)
QString toLower() const const
const QList< QKeySequence > & end()
T value(int i) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Thu Feb 15 2024 03:56:14 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.