17#include <config-libkleo.h>
19#include "keyresolvercore.h"
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>
31#include "kleo/debug.h"
32#include <libkleo_debug.h>
34#include <gpgme++/key.h>
42static inline bool ValidEncryptionKey(
const Key &key)
44 if (key.isNull() || key.isRevoked() || key.isExpired() || key.isDisabled() || !Kleo::keyHasEncrypt(key)) {
50static inline bool ValidSigningKey(
const Key &key)
52 if (key.isNull() || key.isRevoked() || key.isExpired() || key.isDisabled() || !Kleo::keyHasSign(key) || !key.hasSecret()) {
58static int keyValidity(
const Key &key,
const QString &address)
61 int overallValidity = UserID::Validity::Unknown;
62 for (
const auto &uid : key.userIDs()) {
64 return uid.validity();
66 overallValidity = std::max(overallValidity,
static_cast<int>(uid.validity()));
68 return overallValidity;
71static int minimumValidity(
const std::vector<Key> &keys,
const QString &address)
73 const int minValidity = std::accumulate(keys.cbegin(),
76 [address](
int validity,
const Key &key) {
77 return std::min<int>(validity, keyValidity(key, address));
79 return minValidity <= UserID::Ultimate ? static_cast<UserID::Validity>(minValidity) : UserID::
Unknown;
84class KeyResolverCore::Private
87 Private(KeyResolverCore *qq,
bool enc,
bool sig, Protocol fmt)
92 , mCache(KeyCache::instance())
94 , mMinimumValidity(UserID::Marginal)
100 bool isAcceptableSigningKey(
const Key &key);
101 bool isAcceptableEncryptionKey(
const Key &key,
const QString &address =
QString());
102 void setSender(
const QString &address);
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();
117 KeyResolverCore *
const q;
130 std::shared_ptr<const KeyCache> mCache;
131 bool mAllowMixed =
true;
132 Protocol mPreferredProtocol;
133 int mMinimumValidity;
136bool KeyResolverCore::Private::isAcceptableSigningKey(
const Key &key)
138 if (!ValidSigningKey(key)) {
141 if (DeVSCompliance::isCompliant() && !DeVSCompliance::keyIsCompliant(key)) {
142 qCDebug(LIBKLEO_LOG) <<
"Rejected sig key" << key.primaryFingerprint() <<
"because it is not de-vs compliant.";
148bool KeyResolverCore::Private::isAcceptableEncryptionKey(
const Key &key,
const QString &address)
150 if (!ValidEncryptionKey(key)) {
154 if (DeVSCompliance::isCompliant() && !DeVSCompliance::keyIsCompliant(key)) {
155 qCDebug(LIBKLEO_LOG) <<
"Rejected enc key" << key.primaryFingerprint() <<
"because it is not de-vs compliant.";
161 return Kleo::minimalValidityOfNotRevokedUserIDs(key) >= mMinimumValidity;
163 for (
const auto &uid : key.userIDs()) {
164 if (uid.addrSpec() ==
address.toStdString()) {
165 if (uid.validity() >= mMinimumValidity) {
173void KeyResolverCore::Private::setSender(
const QString &address)
175 const auto normalized = UserID::addrSpecFromString(
address.toUtf8().constData());
176 if (normalized.empty()) {
179 mFatalErrors << QStringLiteral(
"The sender address '%1' could not be extracted").arg(address);
187void KeyResolverCore::Private::addRecipients(
const QStringList &addresses)
195 for (
const auto &addr : addresses) {
197 const auto normalized = UserID::addrSpecFromString(addr.toUtf8().constData());
198 if (normalized.empty()) {
201 mFatalErrors << QStringLiteral(
"The mail address for '%1' could not be extracted").arg(addr);
206 mRecipients << normStr;
209 mEncKeys[normStr] = {{CMS, {}}, {OpenPGP, {}}};
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) {
229std::vector<Key> resolveOverride(
const QString &address, Protocol protocol,
const QStringList &fingerprints)
231 std::vector<Key> keys;
232 for (
const auto &fprOrId : fingerprints) {
233 const Key key = KeyCache::instance()->findByKeyIDOrFingerprint(fprOrId.toUtf8().constData());
236 qCDebug(LIBKLEO_LOG) <<
"Failed to find override key for:" <<
address <<
"fpr:" << fprOrId;
239 if (protocol != UnknownProtocol && key.protocol() != protocol) {
240 qCDebug(LIBKLEO_LOG) <<
"Ignoring key" << Formatting::summaryLine(key) <<
"given as" << Formatting::displayName(protocol) <<
"override for"
244 qCDebug(LIBKLEO_LOG) <<
"Using key" << Formatting::summaryLine(key) <<
"as" << Formatting::displayName(protocol) <<
"override for" <<
address;
251void KeyResolverCore::Private::resolveOverrides()
257 for (
auto addressIt = mOverrides.cbegin(); addressIt != mOverrides.cend(); ++addressIt) {
259 const auto &protocolFingerprintsMap = addressIt.value();
261 if (!mRecipients.contains(address)) {
262 qCDebug(LIBKLEO_LOG) <<
"Overrides provided for an address that is "
263 "neither sender nor recipient. Address:"
268 const QStringList commonOverride = protocolFingerprintsMap.
value(UnknownProtocol);
269 if (!commonOverride.
empty()) {
271 if (protocolFingerprintsMap.contains(OpenPGP)) {
272 qCDebug(LIBKLEO_LOG) <<
"Ignoring OpenPGP-specific override for" <<
address <<
"in favor of common override";
274 if (protocolFingerprintsMap.contains(CMS)) {
275 qCDebug(LIBKLEO_LOG) <<
"Ignoring S/MIME-specific override for" <<
address <<
"in favor of common override";
278 if (mFormat != CMS) {
279 mEncKeys[
address][OpenPGP] = resolveOverride(address, OpenPGP, protocolFingerprintsMap.value(OpenPGP));
281 if (mFormat != OpenPGP) {
282 mEncKeys[
address][CMS] = resolveOverride(address, CMS, protocolFingerprintsMap.value(CMS));
288std::vector<Key> KeyResolverCore::Private::resolveSenderWithGroup(
const QString &address, Protocol protocol)
291 auto group = mCache->findGroup(address, protocol, KeyCache::KeyUsage::Sign);
292 if (group.isNull()) {
293 group = mCache->findGroup(address, UnknownProtocol, KeyCache::KeyUsage::Sign);
295 if (group.isNull()) {
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;
304 if (it == std::end(keys)) {
305 qCDebug(LIBKLEO_LOG) <<
"group" << group.name() <<
"has no" << Formatting::displayName(protocol) <<
"signing key";
308 const auto key = *it;
309 if (!isAcceptableSigningKey(key)) {
310 qCDebug(LIBKLEO_LOG) <<
"group" << group.name() <<
"has unacceptable signing key" << key;
316void KeyResolverCore::Private::resolveSigningGroups()
318 auto &protocolKeysMap = mSigKeys;
319 if (!protocolKeysMap[UnknownProtocol].empty()) {
323 if (mFormat == OpenPGP) {
324 if (!protocolKeysMap[OpenPGP].empty()) {
328 protocolKeysMap[OpenPGP] = resolveSenderWithGroup(mSender, OpenPGP);
329 }
else if (mFormat == CMS) {
330 if (!protocolKeysMap[CMS].empty()) {
334 protocolKeysMap[CMS] = resolveSenderWithGroup(mSender, CMS);
336 if (protocolKeysMap[OpenPGP].empty()) {
337 protocolKeysMap[OpenPGP] = resolveSenderWithGroup(mSender, OpenPGP);
339 if (protocolKeysMap[CMS].empty()) {
340 protocolKeysMap[CMS] = resolveSenderWithGroup(mSender, CMS);
345void KeyResolverCore::Private::resolveSign(Protocol proto)
347 if (!mSigKeys[proto].empty()) {
351 const auto key = mCache->findBestByMailBox(mSender.toUtf8().constData(), proto, KeyCache::KeyUsage::Sign);
353 qCDebug(LIBKLEO_LOG) <<
"Failed to find" << Formatting::displayName(proto) <<
"signing key for" << mSender;
356 if (!isAcceptableSigningKey(key)) {
357 qCDebug(LIBKLEO_LOG) <<
"Unacceptable signing key" << key.primaryFingerprint() <<
"for" << mSender;
360 mSigKeys.insert(proto, {key});
363void KeyResolverCore::Private::setSigningKeys(
const QStringList &fingerprints)
366 for (
const auto &fpr : fingerprints) {
367 const auto key = mCache->findByKeyIDOrFingerprint(fpr.toUtf8().constData());
369 qCDebug(LIBKLEO_LOG) <<
"Failed to find signing key with fingerprint" << fpr;
372 mSigKeys[key.protocol()].push_back(key);
377std::vector<Key> KeyResolverCore::Private::resolveRecipientWithGroup(
const QString &address, Protocol protocol)
379 const auto group = mCache->findGroup(address, protocol, KeyCache::KeyUsage::Encrypt);
380 if (group.isNull()) {
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);
395 if (!allKeysAreAcceptable) {
396 qCDebug(LIBKLEO_LOG) <<
"group" << group.name() <<
"has at least one unacceptable key";
399 for (
const auto &k : keys) {
400 qCDebug(LIBKLEO_LOG) <<
"Resolved encrypt to" <<
address <<
"with key" << k.primaryFingerprint();
402 std::vector<Key> result;
403 std::copy(std::begin(keys), std::end(keys), std::back_inserter(result));
407void KeyResolverCore::Private::resolveEncryptionGroups()
409 for (
auto it = mEncKeys.begin(); it != mEncKeys.end(); ++it) {
411 auto &protocolKeysMap = it.value();
412 if (!protocolKeysMap[UnknownProtocol].empty()) {
416 if (mFormat == OpenPGP) {
417 if (!protocolKeysMap[OpenPGP].empty()) {
421 protocolKeysMap[OpenPGP] = resolveRecipientWithGroup(address, OpenPGP);
422 }
else if (mFormat == CMS) {
423 if (!protocolKeysMap[CMS].empty()) {
427 protocolKeysMap[CMS] = resolveRecipientWithGroup(address, CMS);
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()) {
439 protocolKeysMap[
UnknownProtocol] = resolveRecipientWithGroup(address, UnknownProtocol);
443 protocolKeysMap[
UnknownProtocol] = !openPGPGroupKeys.empty() ? openPGPGroupKeys : smimeGroupKeys;
449std::vector<Key> KeyResolverCore::Private::resolveRecipient(
const QString &address, Protocol protocol)
451 const auto key = mCache->findBestByMailBox(
address.toUtf8().constData(), protocol, KeyCache::KeyUsage::Encrypt);
453 qCDebug(LIBKLEO_LOG) <<
"Failed to find any" << Formatting::displayName(protocol) <<
"key for:" <<
address;
456 if (!isAcceptableEncryptionKey(key, address)) {
457 qCDebug(LIBKLEO_LOG) <<
"key for:" <<
address << key.primaryFingerprint() <<
"has not enough validity";
460 qCDebug(LIBKLEO_LOG) <<
"Resolved encrypt to" <<
address <<
"with key" << key.primaryFingerprint();
465void KeyResolverCore::Private::resolveEnc(Protocol proto)
467 for (
auto it = mEncKeys.begin(); it != mEncKeys.end(); ++it) {
469 auto &protocolKeysMap = it.value();
470 if (!protocolKeysMap[proto].empty()) {
474 const std::vector<Key> &commonOverrideOrGroup = protocolKeysMap[
UnknownProtocol];
475 if (!commonOverrideOrGroup.empty()) {
477 if (allKeysHaveProtocol(commonOverrideOrGroup, proto)) {
478 protocolKeysMap[proto] = commonOverrideOrGroup;
481 qCDebug(LIBKLEO_LOG) <<
"Common override/group for" <<
address <<
"is unusable for" << Formatting::displayName(proto);
485 protocolKeysMap[proto] = resolveRecipient(address, proto);
489auto getBestEncryptionKeys(
const QMap<
QString,
QMap<Protocol, std::vector<Key>>> &encryptionKeys, Protocol preferredProtocol)
493 for (
auto it = encryptionKeys.begin(); it != encryptionKeys.end(); ++it) {
495 auto &protocolKeysMap = it.value();
496 const std::vector<Key> &overrideKeys = protocolKeysMap[
UnknownProtocol];
497 if (!overrideKeys.empty()) {
498 result.
insert(address, overrideKeys);
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);
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);
516 result.
insert(address, keysOpenPGP);
526bool hasUnresolvedSender(
const QMap<Protocol, std::vector<Key>> &signingKeys, Protocol protocol)
528 return signingKeys.value(protocol).empty();
531bool hasUnresolvedRecipients(
const QMap<
QString,
QMap<Protocol, std::vector<Key>>> &encryptionKeys, Protocol protocol)
533 return std::any_of(std::cbegin(encryptionKeys), std::cend(encryptionKeys), [protocol](
const auto &protocolKeysMap) {
534 return protocolKeysMap.value(protocol).empty();
538bool anyCommonOverrideHasKeyOfType(
const QMap<
QString,
QMap<Protocol, std::vector<Key>>> &encryptionKeys, Protocol protocol)
540 return std::any_of(std::cbegin(encryptionKeys), std::cend(encryptionKeys), [protocol](
const auto &protocolKeysMap) {
541 return anyKeyHasProtocol(protocolKeysMap.value(UnknownProtocol), protocol);
545auto keysForProtocol(
const QMap<
QString,
QMap<Protocol, std::vector<Key>>> &encryptionKeys, Protocol protocol)
548 for (
auto it = std::begin(encryptionKeys), end = std::end(encryptionKeys); it !=
end; ++it) {
550 const auto &protocolKeysMap = it.value();
551 keys.
insert(address, protocolKeysMap.value(protocol));
557auto concatenate(std::vector<T> v1,
const std::vector<T> &v2)
559 v1.reserve(v1.size() + v2.size());
560 v1.
insert(std::end(v1), std::begin(v2), std::end(v2));
566KeyResolverCore::Result KeyResolverCore::Private::resolve()
568 qCDebug(LIBKLEO_LOG) <<
"Starting ";
569 if (!mSign && !mEncrypt) {
571 return {AllResolved, {}, {}};
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)) {
585 return {Error, {}, {}};
590 resolveSigningGroups();
593 resolveEncryptionGroups();
597 if (mFormat == OpenPGP || mFormat == UnknownProtocol) {
598 resolveSign(OpenPGP);
601 const bool pgpOnly = ((!mEncrypt || !hasUnresolvedRecipients(mEncKeys, OpenPGP))
602 && (!mSign || !hasUnresolvedSender(mSigKeys, OpenPGP)));
604 if (mFormat == OpenPGP) {
606 SolutionFlags((pgpOnly ? AllResolved : SomeUnresolved) | OpenPGPOnly),
607 {OpenPGP, mSigKeys.value(OpenPGP), keysForProtocol(mEncKeys, OpenPGP)},
612 if (mFormat == CMS || mFormat == UnknownProtocol) {
616 const bool cmsOnly = ((!mEncrypt || !hasUnresolvedRecipients(mEncKeys, CMS))
617 && (!mSign || !hasUnresolvedSender(mSigKeys, CMS)));
619 if (mFormat == CMS) {
621 SolutionFlags((cmsOnly ? AllResolved : SomeUnresolved) | CMSOnly),
622 {CMS, mSigKeys.value(CMS), keysForProtocol(mEncKeys, CMS)},
628 if (cmsOnly && (!pgpOnly || mPreferredProtocol == CMS)) {
631 SolutionFlags(AllResolved | CMSOnly),
632 {CMS, mSigKeys.value(CMS), keysForProtocol(mEncKeys, CMS)},
633 {OpenPGP, mSigKeys.value(OpenPGP), keysForProtocol(mEncKeys, OpenPGP)},
637 SolutionFlags(AllResolved | CMSOnly),
638 {CMS, mSigKeys.value(CMS), keysForProtocol(mEncKeys, CMS)},
646 SolutionFlags(AllResolved | OpenPGPOnly),
647 {OpenPGP, mSigKeys.value(OpenPGP), keysForProtocol(mEncKeys, OpenPGP)},
648 {CMS, mSigKeys.value(CMS), keysForProtocol(mEncKeys, CMS)},
652 SolutionFlags(AllResolved | OpenPGPOnly),
653 {OpenPGP, mSigKeys.value(OpenPGP), keysForProtocol(mEncKeys, OpenPGP)},
661 if (mPreferredProtocol == CMS) {
663 SolutionFlags(SomeUnresolved | CMSOnly),
664 {CMS, mSigKeys.value(CMS), keysForProtocol(mEncKeys, CMS)},
665 {OpenPGP, mSigKeys.value(OpenPGP), keysForProtocol(mEncKeys, OpenPGP)},
669 SolutionFlags(SomeUnresolved | OpenPGPOnly),
670 {OpenPGP, mSigKeys.value(OpenPGP), keysForProtocol(mEncKeys, OpenPGP)},
671 {CMS, mSigKeys.value(CMS), keysForProtocol(mEncKeys, CMS)},
676 const auto bestEncryptionKeys = getBestEncryptionKeys(mEncKeys, mPreferredProtocol);
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();
682 if (senderIsResolved && allRecipientsAreResolved) {
684 SolutionFlags(AllResolved | MixedProtocols),
685 {
UnknownProtocol, concatenate(mSigKeys.value(OpenPGP), mSigKeys.value(CMS)), bestEncryptionKeys},
690 const bool allKeysAreOpenPGP = std::all_of(std::begin(bestEncryptionKeys), std::end(bestEncryptionKeys), [](
const auto &keys) {
691 return allKeysHaveProtocol(keys, OpenPGP);
693 if (allKeysAreOpenPGP) {
695 SolutionFlags(SomeUnresolved | OpenPGPOnly),
696 {OpenPGP, mSigKeys.value(OpenPGP), bestEncryptionKeys},
701 const bool allKeysAreCMS = std::all_of(std::begin(bestEncryptionKeys), std::end(bestEncryptionKeys), [](
const auto &keys) {
702 return allKeysHaveProtocol(keys, CMS);
706 SolutionFlags(SomeUnresolved | CMSOnly),
707 {CMS, mSigKeys.value(CMS), bestEncryptionKeys},
713 SolutionFlags(SomeUnresolved | MixedProtocols),
714 {
UnknownProtocol, concatenate(mSigKeys.value(OpenPGP), mSigKeys.value(CMS)), bestEncryptionKeys},
719KeyResolverCore::KeyResolverCore(
bool encrypt,
bool sign, Protocol fmt)
720 : d(new Private(this, encrypt, sign, fmt))
724KeyResolverCore::~KeyResolverCore() =
default;
726void KeyResolverCore::setSender(
const QString &address)
728 d->setSender(address);
731QString KeyResolverCore::normalizedSender()
const
736void KeyResolverCore::setRecipients(
const QStringList &addresses)
738 d->addRecipients(addresses);
741void KeyResolverCore::setSigningKeys(
const QStringList &fingerprints)
743 d->setSigningKeys(fingerprints);
748 d->setOverrideKeys(overrides);
751void KeyResolverCore::setAllowMixedProtocols(
bool allowMixed)
753 d->mAllowMixed = allowMixed;
756void KeyResolverCore::setPreferredProtocol(Protocol proto)
758 d->mPreferredProtocol = proto;
761void KeyResolverCore::setMinimumValidity(
int validity)
763 d->mMinimumValidity = validity;
766KeyResolverCore::Result KeyResolverCore::resolve()
PostalAddress address(const QVariant &location)
const QList< QKeySequence > & end()
T value(qsizetype i) const const
iterator insert(const Key &key, const T &value)
QString fromStdString(const std::string &str)
QString fromUtf8(QByteArrayView str)
QString toLower() const const