keyresolvercore.cpp 27.5 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*  -*- c++ -*-
    kleo/keyresolvercore.cpp

    This file is part of libkleopatra, the KDE keymanagement library
    SPDX-FileCopyrightText: 2004 Klarälvdalens Datakonsult AB
    SPDX-FileCopyrightText: 2018 Intevation GmbH
    SPDX-FileCopyrightText: 2021 g10 Code GmbH
    SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>

    Based on kpgp.cpp
    SPDX-FileCopyrightText: 2001, 2002 the KPGP authors
    See file libkdenetwork/AUTHORS.kpgp for details

    SPDX-License-Identifier: GPL-2.0-or-later
*/

#include "keyresolvercore.h"

Ingo Klöcker's avatar
Ingo Klöcker committed
19
#include "kleo/enum.h"
20
#include "kleo/keygroup.h"
21
22
23
24
25
26
27
28
29
30
31
32
#include "models/keycache.h"
#include "utils/formatting.h"

#include <gpgme++/key.h>

#include "libkleo_debug.h"

using namespace Kleo;
using namespace GpgME;

namespace {

33
34
35
36
37
38
39
40
41
42
QDebug operator<<(QDebug debug, const GpgME::Key &key)
{
    if (key.isNull()) {
        debug << "Null";
    } else {
        debug << Formatting::summaryLine(key);
    }
    return debug.maybeSpace();
}

43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
static inline bool ValidEncryptionKey(const Key &key)
{
    if (key.isNull() || key.isRevoked() || key.isExpired() ||
        key.isDisabled() || !key.canEncrypt()) {
        return false;
    }
    return true;
}

static inline bool ValidSigningKey(const Key &key)
{
    if (key.isNull() || key.isRevoked() || key.isExpired() ||
        key.isDisabled() || !key.canSign() || !key.hasSecret()) {
        return false;
    }
    return true;
}

61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
static int keyValidity(const Key &key, const QString &address)
{
    // returns the validity of the UID matching the address or, if no UID matches, the maximal validity of all UIDs
    int overallValidity = UserID::Validity::Unknown;
    for (const auto &uid: key.userIDs()) {
        if (QString::fromStdString(uid.addrSpec()).toLower() == address.toLower()) {
            return uid.validity();
        }
        overallValidity = std::max(overallValidity, static_cast<int>(uid.validity()));
    }
    return overallValidity;
}

static int minimumValidity(const std::vector<Key> &keys, const QString &address)
{
    const int minValidity = std::accumulate(keys.cbegin(), keys.cend(), UserID::Ultimate + 1,
                                            [address] (int validity, const Key &key) {
                                                return std::min<int>(validity, keyValidity(key, address));
                                            });
    return minValidity <= UserID::Ultimate ? static_cast<UserID::Validity>(minValidity) : UserID::Unknown;
}

83
84
85
86
87
bool allKeysHaveProtocol(const std::vector<Key> &keys, Protocol protocol)
{
    return std::all_of(keys.cbegin(), keys.cend(), [protocol] (const Key &key) { return key.protocol() == protocol; });
}

88
89
90
91
92
bool anyKeyHasProtocol(const std::vector<Key> &keys, Protocol protocol)
{
    return std::any_of(std::begin(keys), std::end(keys), [protocol] (const Key &key) { return key.protocol() == protocol; });
}

93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
} // namespace

class KeyResolverCore::Private
{
public:
    Private(KeyResolverCore* qq, bool enc, bool sig, Protocol fmt)
        : q(qq)
        , mFormat(fmt)
        , mEncrypt(enc)
        , mSign(sig)
        , mCache(KeyCache::instance())
        , mPreferredProtocol(UnknownProtocol)
        , mMinimumValidity(UserID::Marginal)
        , mCompliance(Formatting::complianceMode())
    {
    }

    ~Private() = default;

    bool isAcceptableSigningKey(const Key &key);
    bool isAcceptableEncryptionKey(const Key &key, const QString &address = QString());
114
    void setSender(const QString &address);
115
    void addRecipients(const QStringList &addresses);
116
    void setOverrideKeys(const QMap<Protocol, QMap<QString, QStringList>> &overrides);
117
    void resolveOverrides();
118
119
    std::vector<Key> resolveRecipientWithGroup(const QString &address, Protocol protocol);
    void resolveEncryptionGroups();
120
121
    std::vector<Key> resolveSenderWithGroup(const QString &address, Protocol protocol);
    void resolveSigningGroups();
122
123
    void resolveSign(Protocol proto);
    void setSigningKeys(const QStringList &fingerprints);
124
    std::vector<Key> resolveRecipient(const QString &address, Protocol protocol);
125
    void resolveEnc(Protocol proto);
126
    void mergeEncryptionKeys();
127
    Result resolve();
128
129
130
131
132

    KeyResolverCore *const q;
    QString mSender;
    QStringList mRecipients;
    QMap<Protocol, std::vector<Key>> mSigKeys;
133
    QMap<QString, QMap<Protocol, std::vector<Key>>> mEncKeys;
134
    QMap<QString, QMap<Protocol, QStringList>> mOverrides;
135
136
137
138
139
140
141
142

    Protocol mFormat;
    QStringList mFatalErrors;
    bool mEncrypt;
    bool mSign;
    // The cache is needed as a member variable to avoid rebuilding
    // it between calls if we are the only user.
    std::shared_ptr<const KeyCache> mCache;
143
    bool mAllowMixed = true;
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
    Protocol mPreferredProtocol;
    int mMinimumValidity;
    QString mCompliance;
};

bool KeyResolverCore::Private::isAcceptableSigningKey(const Key &key)
{
    if (!ValidSigningKey(key)) {
        return false;
    }
    if (mCompliance == QLatin1String("de-vs")) {
        if (!Formatting::isKeyDeVs(key)) {
            qCDebug(LIBKLEO_LOG) << "Rejected sig key" << key.primaryFingerprint()
                                    << "because it is not de-vs compliant.";
            return false;
        }
    }
    return true;
}

bool KeyResolverCore::Private::isAcceptableEncryptionKey(const Key &key, const QString &address)
{
    if (!ValidEncryptionKey(key)) {
        return false;
    }

    if (mCompliance == QLatin1String("de-vs")) {
        if (!Formatting::isKeyDeVs(key)) {
            qCDebug(LIBKLEO_LOG) << "Rejected enc key" << key.primaryFingerprint()
                                    << "because it is not de-vs compliant.";
            return false;
        }
    }

    if (address.isEmpty()) {
        return true;
    }
    for (const auto &uid: key.userIDs()) {
        if (uid.addrSpec() == address.toStdString()) {
            if (uid.validity() >= mMinimumValidity) {
                return true;
            }
        }
    }
    return false;
}

191
192
193
194
195
196
197
198
199
200
void KeyResolverCore::Private::setSender(const QString &address)
{
    const auto normalized = UserID::addrSpecFromString (address.toUtf8().constData());
    if (normalized.empty()) {
        // should not happen bug in the caller, non localized
        // error for bug reporting.
        mFatalErrors << QStringLiteral("The sender address '%1' could not be extracted").arg(address);
        return;
    }
    const auto normStr = QString::fromUtf8(normalized.c_str());
201
    mSender = normStr;
202
203
204
    addRecipients({address});
}

205
206
207
208
209
210
211
212
void KeyResolverCore::Private::addRecipients(const QStringList &addresses)
{
    if (!mEncrypt) {
        return;
    }

    // Internally we work with normalized addresses. Normalization
    // matches the gnupg one.
213
    for (const auto &addr: addresses) {
214
215
216
217
218
219
220
221
222
223
224
        // PGP Uids are defined to be UTF-8 (RFC 4880 §5.11)
        const auto normalized = UserID::addrSpecFromString (addr.toUtf8().constData());
        if (normalized.empty()) {
            // should not happen bug in the caller, non localized
            // error for bug reporting.
            mFatalErrors << QStringLiteral("The mail address for '%1' could not be extracted").arg(addr);
            continue;
        }
        const QString normStr = QString::fromUtf8(normalized.c_str());

        mRecipients << normStr;
225
226

        // Initially add empty lists of keys for both protocols
227
        mEncKeys[normStr] = {{CMS, {}}, {OpenPGP, {}}};
228
229
230
    }
}

231
232
void KeyResolverCore::Private::setOverrideKeys(const QMap<Protocol, QMap<QString, QStringList>> &overrides)
{
233
234
235
236
237
238
239
240
    for (auto protocolIt = overrides.cbegin(); protocolIt != overrides.cend(); ++protocolIt) {
        const Protocol &protocol = protocolIt.key();
        const auto &addressFingerprintMap = protocolIt.value();
        for (auto addressIt = addressFingerprintMap.cbegin(); addressIt != addressFingerprintMap.cend(); ++addressIt) {
            const QString &address = addressIt.key();
            const QStringList &fingerprints = addressIt.value();
            const QString normalizedAddress = QString::fromUtf8(UserID::addrSpecFromString(address.toUtf8().constData()).c_str());
            mOverrides[normalizedAddress][protocol] = fingerprints;
241
242
243
244
        }
    }
}

245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
namespace
{
std::vector<Key> resolveOverride(const QString &address, Protocol protocol, const QStringList &fingerprints)
{
    std::vector<Key> keys;
    for (const auto &fprOrId: fingerprints) {
        const Key key = KeyCache::instance()->findByKeyIDOrFingerprint(fprOrId.toUtf8().constData());
        if (key.isNull()) {
            // FIXME: Report to caller
            qCDebug (LIBKLEO_LOG) << "Failed to find override key for:" << address << "fpr:" << fprOrId;
            continue;
        }
        if (protocol != UnknownProtocol && key.protocol() != protocol) {
            qCDebug(LIBKLEO_LOG) << "Ignoring key" << Formatting::summaryLine(key) << "given as" << Formatting::displayName(protocol) << "override for"
                                 << address;
            continue;
        }
        qCDebug(LIBKLEO_LOG) << "Using key" << Formatting::summaryLine(key) << "as" << Formatting::displayName(protocol) << "override for" << address;
        keys.push_back(key);
    }
    return keys;
}
}

269
270
271
272
273
274
void KeyResolverCore::Private::resolveOverrides()
{
    if (!mEncrypt) {
        // No encryption we are done.
        return;
    }
275
276
277
278
279
280
281
    for (auto addressIt = mOverrides.cbegin(); addressIt != mOverrides.cend(); ++addressIt) {
        const QString &address = addressIt.key();
        const auto &protocolFingerprintsMap = addressIt.value();

        if (!mRecipients.contains(address)) {
            qCDebug(LIBKLEO_LOG) << "Overrides provided for an address that is "
                "neither sender nor recipient. Address:" << address;
282
283
            continue;
        }
284

285
286
287
288
289
290
291
292
293
294
295
296
        const QStringList commonOverride = protocolFingerprintsMap.value(UnknownProtocol);
        if (!commonOverride.empty()) {
            mEncKeys[address][UnknownProtocol] = resolveOverride(address, UnknownProtocol, commonOverride);
            if (protocolFingerprintsMap.contains(OpenPGP)) {
                qCDebug(LIBKLEO_LOG) << "Ignoring OpenPGP-specific override for" << address << "in favor of common override";
            }
            if (protocolFingerprintsMap.contains(CMS)) {
                qCDebug(LIBKLEO_LOG) << "Ignoring S/MIME-specific override for" << address << "in favor of common override";
            }
        } else {
            if (mFormat != CMS) {
                mEncKeys[address][OpenPGP] = resolveOverride(address, OpenPGP, protocolFingerprintsMap.value(OpenPGP));
297
            }
298
299
            if (mFormat != OpenPGP) {
                mEncKeys[address][CMS] = resolveOverride(address, CMS, protocolFingerprintsMap.value(CMS));
300
301
302
303
304
            }
        }
    }
}

305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
std::vector<Key> KeyResolverCore::Private::resolveSenderWithGroup(const QString &address, Protocol protocol)
{
    // prefer single-protocol groups over mixed-protocol groups
    auto group = mCache->findGroup(address, protocol, KeyUsage::Sign);
    if (group.isNull()) {
        group = mCache->findGroup(address, UnknownProtocol, KeyUsage::Sign);
    }
    if (group.isNull()) {
        return {};
    }

    // take the first key matching the protocol
    const auto &keys = group.keys();
    const auto it = std::find_if(std::begin(keys), std::end(keys), [protocol] (const auto &key) { return key.protocol() == protocol; });
    if (it == std::end(keys)) {
        qCDebug(LIBKLEO_LOG) << "group" << group.name() << "has no" << Formatting::displayName(protocol) << "signing key";
        return {};
    }
    const auto key = *it;
    if (!isAcceptableSigningKey(key)) {
        qCDebug(LIBKLEO_LOG) << "group" << group.name() << "has unacceptable signing key" << key;
        return {};
    }
    return {key};
}

void KeyResolverCore::Private::resolveSigningGroups()
{
    auto &protocolKeysMap = mSigKeys;
    if (!protocolKeysMap[UnknownProtocol].empty()) {
        // already resolved by common override
        return;
    }
    if (mFormat == OpenPGP) {
        if (!protocolKeysMap[OpenPGP].empty()) {
            // already resolved by override
            return;
        }
        protocolKeysMap[OpenPGP] = resolveSenderWithGroup(mSender, OpenPGP);
    } else if (mFormat == CMS) {
        if (!protocolKeysMap[CMS].empty()) {
            // already resolved by override
            return;
        }
        protocolKeysMap[CMS] = resolveSenderWithGroup(mSender, CMS);
    } else {
        protocolKeysMap[OpenPGP] = resolveSenderWithGroup(mSender, OpenPGP);
        protocolKeysMap[CMS] = resolveSenderWithGroup(mSender, CMS);
    }
}

356
357
void KeyResolverCore::Private::resolveSign(Protocol proto)
{
358
    if (!mSigKeys[proto].empty()) {
359
360
361
        // Explicitly set
        return;
    }
362
363
364
365
    const auto key = mCache->findBestByMailBox(mSender.toUtf8().constData(), proto, KeyUsage::Sign);
    if (key.isNull()) {
        qCDebug(LIBKLEO_LOG) << "Failed to find" << Formatting::displayName(proto) << "signing key for" << mSender;
        return;
366
    }
367
368
369
    if (!isAcceptableSigningKey(key)) {
        qCDebug(LIBKLEO_LOG) << "Unacceptable signing key" << key.primaryFingerprint() << "for" << mSender;
        return;
370
    }
371
    mSigKeys.insert(proto, {key});
372
373
374
375
376
377
378
379
380
381
382
}

void KeyResolverCore::Private::setSigningKeys(const QStringList &fingerprints)
{
    if (mSign) {
        for (const auto &fpr: fingerprints) {
            const auto key = mCache->findByKeyIDOrFingerprint(fpr.toUtf8().constData());
            if (key.isNull()) {
                qCDebug(LIBKLEO_LOG) << "Failed to find signing key with fingerprint" << fpr;
                continue;
            }
383
            mSigKeys[key.protocol()].push_back(key);
384
385
386
387
        }
    }
}

388
std::vector<Key> KeyResolverCore::Private::resolveRecipientWithGroup(const QString &address, Protocol protocol)
389
{
390
    const auto group = mCache->findGroup(address, protocol, KeyUsage::Encrypt);
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
    if (group.isNull()) {
        return {};
    }

    // If we have one unacceptable group key we reject the
    // whole group to avoid the situation where one key is
    // skipped or the operation fails.
    //
    // We are in Autoresolve land here. In the GUI we
    // will also show unacceptable group keys so that the
    // user can see which key is not acceptable.
    const auto &keys = group.keys();
    const bool allKeysAreAcceptable =
        std::all_of(std::begin(keys), std::end(keys), [this] (const auto &key) { return isAcceptableEncryptionKey(key); });
    if (!allKeysAreAcceptable) {
        qCDebug(LIBKLEO_LOG) << "group" << group.name() << "has at least one unacceptable key";
        return {};
    }
    for (const auto &k: keys) {
        qCDebug(LIBKLEO_LOG) << "Resolved encrypt to" << address << "with key" << k.primaryFingerprint();
    }
    std::vector<Key> result;
    std::copy(std::begin(keys), std::end(keys), std::back_inserter(result));
    return result;
}

void KeyResolverCore::Private::resolveEncryptionGroups()
{
    for (auto it = mEncKeys.begin(); it != mEncKeys.end(); ++it) {
        const QString &address = it.key();
        auto &protocolKeysMap = it.value();
        if (!protocolKeysMap[UnknownProtocol].empty()) {
            // already resolved by common override
            continue;
425
        }
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
        if (mFormat == OpenPGP) {
            if (!protocolKeysMap[OpenPGP].empty()) {
                // already resolved by override
                continue;
            }
            protocolKeysMap[OpenPGP] = resolveRecipientWithGroup(address, OpenPGP);
        } else if (mFormat == CMS) {
            if (!protocolKeysMap[CMS].empty()) {
                // already resolved by override
                continue;
            }
            protocolKeysMap[CMS] = resolveRecipientWithGroup(address, CMS);
        } else {
            // prefer single-protocol groups over mixed-protocol groups
            const auto openPGPGroupKeys = resolveRecipientWithGroup(address, OpenPGP);
            const auto smimeGroupKeys = resolveRecipientWithGroup(address, CMS);
            if (!openPGPGroupKeys.empty() && !smimeGroupKeys.empty()) {
                protocolKeysMap[OpenPGP] = openPGPGroupKeys;
                protocolKeysMap[CMS] = smimeGroupKeys;
            } else if (openPGPGroupKeys.empty() && smimeGroupKeys.empty()) {
                // no single-protocol groups found;
                // if mixed protocols are allowed, then look for any group with encryption keys
                if (mAllowMixed) {
                    protocolKeysMap[UnknownProtocol] = resolveRecipientWithGroup(address, UnknownProtocol);
                }
            } else {
                // there is a single-protocol group only for one protocol; use this group for all protocols
                protocolKeysMap[UnknownProtocol] = !openPGPGroupKeys.empty() ? openPGPGroupKeys : smimeGroupKeys;
            }
455
        }
456
    }
457
}
458

459
460
std::vector<Key> KeyResolverCore::Private::resolveRecipient(const QString &address, Protocol protocol)
{
461
462
463
464
    const auto key = mCache->findBestByMailBox(address.toUtf8().constData(), protocol, KeyUsage::Encrypt);
    if (key.isNull()) {
        qCDebug(LIBKLEO_LOG) << "Failed to find any" << Formatting::displayName(protocol) << "key for:" << address;
        return {};
465
    }
466
467
468
469
470
471
472
    if (!isAcceptableEncryptionKey(key, address)) {
        qCDebug(LIBKLEO_LOG) << "key for:" << address << key.primaryFingerprint()
                             << "has not enough validity";
        return {};
    }
    qCDebug(LIBKLEO_LOG) << "Resolved encrypt to" << address << "with key" << key.primaryFingerprint();
    return {key};
473
474
}

475
476
477
// Try to find matching keys in the provided protocol for the unresolved addresses
void KeyResolverCore::Private::resolveEnc(Protocol proto)
{
478
479
480
481
    for (auto it = mEncKeys.begin(); it != mEncKeys.end(); ++it) {
        const QString &address = it.key();
        auto &protocolKeysMap = it.value();
        if (!protocolKeysMap[proto].empty()) {
482
            // already resolved for current protocol (by override or group)
483
484
            continue;
        }
485
486
487
488
489
        const std::vector<Key> &commonOverrideOrGroup = protocolKeysMap[UnknownProtocol];
        if (!commonOverrideOrGroup.empty()) {
            // there is a common override or group; use it for current protocol if possible
            if (allKeysHaveProtocol(commonOverrideOrGroup, proto)) {
                protocolKeysMap[proto] = commonOverrideOrGroup;
490
491
                continue;
            } else {
492
                qCDebug(LIBKLEO_LOG) << "Common override/group for" << address << "is unusable for" << Formatting::displayName(proto);
493
494
495
                continue;
            }
        }
496
        protocolKeysMap[proto] = resolveRecipient(address, proto);
497
498
499
    }
}

500
auto getBestEncryptionKeys(const QMap<QString, QMap<Protocol, std::vector<Key>>> &encryptionKeys, Protocol preferredProtocol)
501
{
502
503
504
    QMap<QString, std::vector<Key>> result;

    for (auto it = encryptionKeys.begin(); it != encryptionKeys.end(); ++it) {
505
506
        const QString &address = it.key();
        auto &protocolKeysMap = it.value();
507
508
509
        const std::vector<Key> &overrideKeys = protocolKeysMap[UnknownProtocol];
        if (!overrideKeys.empty()) {
            result.insert(address, overrideKeys);
510
511
            continue;
        }
512
513
        const std::vector<Key> &keysOpenPGP = protocolKeysMap[OpenPGP];
        const std::vector<Key> &keysCMS = protocolKeysMap[CMS];
514
        if (keysOpenPGP.empty() && keysCMS.empty()) {
515
            result.insert(address, {});
516
        } else if (!keysOpenPGP.empty() && keysCMS.empty()) {
517
            result.insert(address, keysOpenPGP);
518
        } else if (keysOpenPGP.empty() && !keysCMS.empty()) {
519
            result.insert(address, keysCMS);
520
521
522
523
        } else {
            // check whether OpenPGP keys or S/MIME keys have higher validity
            const int validityPGP = minimumValidity(keysOpenPGP, address);
            const int validityCMS = minimumValidity(keysCMS, address);
524
525
            if ((validityCMS > validityPGP) || (validityCMS == validityPGP && preferredProtocol == CMS)) {
                result.insert(address, keysCMS);
526
            } else {
527
                result.insert(address, keysOpenPGP);
528
529
530
            }
        }
    }
531
532

    return result;
533
534
}

535
536
537
538
539
540
541
542
543
namespace
{
bool hasUnresolvedRecipients(const QMap<QString, QMap<Protocol, std::vector<Key>>> &encryptionKeys, Protocol protocol)
{
    return std::any_of(std::cbegin(encryptionKeys), std::cend(encryptionKeys),
                       [protocol] (const auto &protocolKeysMap) {
                           return protocolKeysMap.value(protocol).empty();
                       });
}
544
545
546
547
548
549
550
551

bool anyCommonOverrideHasKeyOfType(const QMap<QString, QMap<Protocol, std::vector<Key>>> &encryptionKeys, Protocol protocol)
{
    return std::any_of(std::cbegin(encryptionKeys), std::cend(encryptionKeys),
                       [protocol] (const auto &protocolKeysMap) {
                           return anyKeyHasProtocol(protocolKeysMap.value(UnknownProtocol), protocol);
                       });
}
552
553
554
555
556
557
558
559
560
561

auto keysForProtocol(const QMap<QString, QMap<Protocol, std::vector<Key>>> &encryptionKeys, Protocol protocol)
{
    QMap<QString, std::vector<Key>> keys;
    for (auto it = std::begin(encryptionKeys), end = std::end(encryptionKeys); it != end; ++it) {
        const QString &address = it.key();
        const auto &protocolKeysMap = it.value();
        keys.insert(address, protocolKeysMap.value(protocol));
    }
    return keys;
562
563
}

564
565
566
567
568
569
570
571
572
573
574
template<typename T>
auto concatenate(std::vector<T> v1, const std::vector<T> &v2)
{
    v1.reserve(v1.size() + v2.size());
    v1.insert(std::end(v1), std::begin(v2), std::end(v2));
    return v1;
}

}

KeyResolverCore::Result KeyResolverCore::Private::resolve()
575
576
{
    qCDebug(LIBKLEO_LOG) << "Starting ";
577
    if (!mSign && !mEncrypt) {
578
        // nothing to do
579
        return {AllResolved, {}, {}};
580
581
582
    }

    // First resolve through overrides
583
    resolveOverrides();
584

585
586
587
588
589
590
591
592
    // check protocols needed for overrides
    const bool commonOverridesNeedOpenPGP = anyCommonOverrideHasKeyOfType(mEncKeys, OpenPGP);
    const bool commonOverridesNeedCMS = anyCommonOverrideHasKeyOfType(mEncKeys, CMS);
    if ((mFormat == OpenPGP && commonOverridesNeedCMS)
            || (mFormat == CMS && commonOverridesNeedOpenPGP)
            || (!mAllowMixed && commonOverridesNeedOpenPGP && commonOverridesNeedCMS)) {
        // invalid protocol requirements -> clear intermediate result and abort resolution
        mEncKeys.clear();
593
        return {Error, {}, {}};
594
595
    }

596
    // Next look for matching groups of keys
597
598
599
    if (mSign) {
        resolveSigningGroups();
    }
600
601
602
    if (mEncrypt) {
        resolveEncryptionGroups();
    }
603

604
    // Then look for signing / encryption keys
605
    if (mFormat == OpenPGP || mFormat == UnknownProtocol) {
606
607
        resolveSign(OpenPGP);
        resolveEnc(OpenPGP);
608
    }
609
610
611
612
613
614
615
616
617
    const bool pgpOnly = (!mEncrypt || !hasUnresolvedRecipients(mEncKeys, OpenPGP)) && (!mSign || mSigKeys.contains(OpenPGP));

    if (mFormat == OpenPGP) {
        return {
            SolutionFlags((pgpOnly ? AllResolved : SomeUnresolved) | OpenPGPOnly),
            {OpenPGP, mSigKeys.value(OpenPGP), keysForProtocol(mEncKeys, OpenPGP)},
            {}
        };
    }
618

619
    if (mFormat == CMS || mFormat == UnknownProtocol) {
620
621
        resolveSign(CMS);
        resolveEnc(CMS);
622
    }
623
    const bool cmsOnly = (!mEncrypt || !hasUnresolvedRecipients(mEncKeys, CMS)) && (!mSign || mSigKeys.contains(CMS));
624

625
626
627
628
629
630
    if (mFormat == CMS) {
        return {
            SolutionFlags((cmsOnly ? AllResolved : SomeUnresolved) | CMSOnly),
            {CMS, mSigKeys.value(CMS), keysForProtocol(mEncKeys, CMS)},
            {}
        };
631
632
    }

633
634
635
636
637
638
639
640
    // check if single-protocol solution has been found
    if (cmsOnly && (!pgpOnly || mPreferredProtocol == CMS)) {
        if (!mAllowMixed) {
            return {
                SolutionFlags(AllResolved | CMSOnly),
                {CMS, mSigKeys.value(CMS), keysForProtocol(mEncKeys, CMS)},
                {OpenPGP, mSigKeys.value(OpenPGP), keysForProtocol(mEncKeys, OpenPGP)}
            };
641
        } else {
642
643
            return {
                SolutionFlags(AllResolved | CMSOnly),
644
                {CMS, mSigKeys.value(CMS), keysForProtocol(mEncKeys, CMS)},
645
646
                {}
            };
647
        }
648
649
650
651
652
653
654
655
656
657
658
    }
    if (pgpOnly) {
        if (!mAllowMixed) {
            return {
                SolutionFlags(AllResolved | OpenPGPOnly),
                {OpenPGP, mSigKeys.value(OpenPGP), keysForProtocol(mEncKeys, OpenPGP)},
                {CMS, mSigKeys.value(CMS), keysForProtocol(mEncKeys, CMS)}
            };
        } else {
            return {
                SolutionFlags(AllResolved | OpenPGPOnly),
659
                {OpenPGP, mSigKeys.value(OpenPGP), keysForProtocol(mEncKeys, OpenPGP)},
660
661
                {}
            };
662
663
664
        }
    }

665
666
667
668
669
670
671
672
673
674
675
676
677
678
    if (!mAllowMixed) {
        // return incomplete single-protocol solution
        if (mPreferredProtocol == CMS) {
            return {
                SolutionFlags(SomeUnresolved | CMSOnly),
                {CMS, mSigKeys.value(CMS), keysForProtocol(mEncKeys, CMS)},
                {OpenPGP, mSigKeys.value(OpenPGP), keysForProtocol(mEncKeys, OpenPGP)}
            };
        } else {
            return {
                SolutionFlags(SomeUnresolved | OpenPGPOnly),
                {OpenPGP, mSigKeys.value(OpenPGP), keysForProtocol(mEncKeys, OpenPGP)},
                {CMS, mSigKeys.value(CMS), keysForProtocol(mEncKeys, CMS)}
            };
679
        }
680
    }
681

682
683
684
685
686
687
688
689
690
    const auto bestEncryptionKeys = getBestEncryptionKeys(mEncKeys, mPreferredProtocol);
    const bool allAddressesAreResolved = std::all_of(std::begin(bestEncryptionKeys), std::end(bestEncryptionKeys),
                                                     [] (const auto &keys) { return !keys.empty(); });
    if (allAddressesAreResolved) {
        return {
            SolutionFlags(AllResolved | MixedProtocols),
            {UnknownProtocol, concatenate(mSigKeys.value(OpenPGP), mSigKeys.value(CMS)), bestEncryptionKeys},
            {}
        };
691
692
    }

693
694
695
696
697
    const bool allKeysAreOpenPGP = std::all_of(std::begin(bestEncryptionKeys), std::end(bestEncryptionKeys),
                                               [] (const auto &keys) { return allKeysHaveProtocol(keys, OpenPGP); });
    if (allKeysAreOpenPGP) {
        return {
            SolutionFlags(SomeUnresolved | OpenPGPOnly),
698
            {OpenPGP, mSigKeys.value(OpenPGP), bestEncryptionKeys},
699
700
701
702
703
704
705
706
707
            {}
        };
    }

    const bool allKeysAreCMS = std::all_of(std::begin(bestEncryptionKeys), std::end(bestEncryptionKeys),
                                           [] (const auto &keys) { return allKeysHaveProtocol(keys, CMS); });
    if (allKeysAreCMS) {
        return {
            SolutionFlags(SomeUnresolved | CMSOnly),
708
            {CMS, mSigKeys.value(CMS), bestEncryptionKeys},
709
710
711
712
713
714
715
716
717
            {}
        };
    }

    return {
        SolutionFlags(SomeUnresolved | MixedProtocols),
        {UnknownProtocol, concatenate(mSigKeys.value(OpenPGP), mSigKeys.value(CMS)), bestEncryptionKeys},
        {}
    };
718
719
}

720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
KeyResolverCore::KeyResolverCore(bool encrypt, bool sign, Protocol fmt)
    : d(new Private(this, encrypt, sign, fmt))
{
}

KeyResolverCore::~KeyResolverCore() = default;

void KeyResolverCore::setSender(const QString &address)
{
    d->setSender(address);
}

QString KeyResolverCore::normalizedSender() const
{
    return d->mSender;
}

void KeyResolverCore::setRecipients(const QStringList &addresses)
{
    d->addRecipients(addresses);
}

void KeyResolverCore::setSigningKeys(const QStringList &fingerprints)
{
    d->setSigningKeys(fingerprints);
}

void KeyResolverCore::setOverrideKeys(const QMap<Protocol, QMap<QString, QStringList>> &overrides)
{
    d->setOverrideKeys(overrides);
}

752
753
754
755
756
void KeyResolverCore::setAllowMixedProtocols(bool allowMixed)
{
    d->mAllowMixed = allowMixed;
}

757
758
759
760
761
762
763
764
765
766
void KeyResolverCore::setPreferredProtocol(Protocol proto)
{
    d->mPreferredProtocol = proto;
}

void KeyResolverCore::setMinimumValidity(int validity)
{
    d->mMinimumValidity = validity;
}

767
KeyResolverCore::Result KeyResolverCore::resolve()
768
769
770
{
    return d->resolve();
}