Messagelib

keyresolver.cpp
1/* -*- c++ -*-
2 keyresolver.cpp
3
4 This file is part of libkleopatra, the KDE keymanagement library
5 SPDX-FileCopyrightText: 2004 Klarälvdalens Datakonsult AB
6
7 Based on kpgp.cpp
8 Copyright (C) 2001,2002 the KPGP authors
9 See file libkdenetwork/AUTHORS.kpgp for details
10
11 SPDX-License-Identifier: GPL-2.0-or-later
12*/
13
14#include "composer/keyresolver.h"
15
16#include "contactpreference/savecontactpreferencejob.h"
17
18#include "utils/kleo_util.h"
19#include <KCursorSaver>
20
21#include <KEmailAddress>
22
23#include <Libkleo/Algorithm>
24#include <Libkleo/Compliance>
25#include <Libkleo/ExpiryChecker>
26#include <Libkleo/KeySelectionDialog>
27
28#include <QGpgME/KeyListJob>
29#include <QGpgME/Protocol>
30
31#include <gpgme++/key.h>
32#include <gpgme++/keylistresult.h>
33
34#include "messagecomposer_debug.h"
35#include <Akonadi/ContactSearchJob>
36#include <KLocalizedString>
37#include <KMessageBox>
38
39#include <MessageCore/AutocryptRecipient>
40#include <MessageCore/AutocryptStorage>
41
42#include <QPointer>
43
44#include <algorithm>
45#include <cassert>
46#include <ctime>
47#include <functional>
48#include <iostream>
49#include <iterator>
50#include <map>
51#include <memory>
52#include <set>
53
54//
55// some predicates to be used in STL algorithms:
56//
57
58static inline bool EmptyKeyList(const Kleo::KeyApprovalDialog::Item &item)
59{
60 return item.keys.empty();
61}
62
63static inline QString ItemDotAddress(const Kleo::KeyResolver::Item &item)
64{
65 return item.address;
66}
67
68static inline bool ApprovalNeeded(const Kleo::KeyResolver::Item &item)
69{
70 bool approvalNeeded = item.pref == Kleo::NeverEncrypt || item.keys.empty();
71 if (!approvalNeeded && Kleo::DeVSCompliance::isCompliant()) {
72 approvalNeeded = !Kleo::all_of(item.keys, &Kleo::DeVSCompliance::keyIsCompliant);
73 }
74 return approvalNeeded;
75}
76
77static inline Kleo::KeyResolver::Item CopyKeysAndEncryptionPreferences(const Kleo::KeyResolver::Item &oldItem, const Kleo::KeyApprovalDialog::Item &newItem)
78{
79 return Kleo::KeyResolver::Item(oldItem.address, newItem.keys, newItem.pref, oldItem.signPref, oldItem.format);
80}
81
82static bool ValidOpenPGPEncryptionKey(const GpgME::Key &key)
83{
84 if (key.protocol() != GpgME::OpenPGP) {
85 return false;
86 }
87 if (key.isRevoked()) {
88 qCWarning(MESSAGECOMPOSER_LOG) << "is revoked";
89 }
90 if (key.isExpired()) {
91 qCWarning(MESSAGECOMPOSER_LOG) << "is expired";
92 }
93 if (key.isDisabled()) {
94 qCWarning(MESSAGECOMPOSER_LOG) << "is disabled";
95 }
96 if (!key.canEncrypt()) {
97 qCWarning(MESSAGECOMPOSER_LOG) << "can't encrypt";
98 }
99 if (key.isRevoked() || key.isExpired() || key.isDisabled() || !key.canEncrypt()) {
100 return false;
101 }
102 return true;
103}
104
105static bool ValidTrustedOpenPGPEncryptionKey(const GpgME::Key &key)
106{
107 if (!ValidOpenPGPEncryptionKey(key)) {
108 return false;
109 }
110 const std::vector<GpgME::UserID> uids = key.userIDs();
111 auto end(uids.end());
112 for (auto it = uids.begin(); it != end; ++it) {
113 if (!it->isRevoked() && it->validity() >= GpgME::UserID::Marginal) {
114 return true;
115 } else if (it->isRevoked()) {
116 qCWarning(MESSAGECOMPOSER_LOG) << "a userid is revoked";
117 } else {
118 qCWarning(MESSAGECOMPOSER_LOG) << "bad validity" << int(it->validity());
119 }
120 }
121 return false;
122}
123
124static bool ValidSMIMEEncryptionKey(const GpgME::Key &key)
125{
126 if (key.protocol() != GpgME::CMS) {
127 return false;
128 }
129 if (key.isRevoked() || key.isExpired() || key.isDisabled() || !key.canEncrypt()) {
130 return false;
131 }
132 return true;
133}
134
135static bool ValidTrustedSMIMEEncryptionKey(const GpgME::Key &key)
136{
137 if (!ValidSMIMEEncryptionKey(key)) {
138 return false;
139 }
140 return true;
141}
142
143static inline bool ValidTrustedEncryptionKey(const GpgME::Key &key)
144{
145 switch (key.protocol()) {
146 case GpgME::OpenPGP:
147 return ValidTrustedOpenPGPEncryptionKey(key);
148 case GpgME::CMS:
149 return ValidTrustedSMIMEEncryptionKey(key);
150 default:
151 return false;
152 }
153}
154
155static inline bool ValidEncryptionKey(const GpgME::Key &key)
156{
157 switch (key.protocol()) {
158 case GpgME::OpenPGP:
159 return ValidOpenPGPEncryptionKey(key);
160 case GpgME::CMS:
161 return ValidSMIMEEncryptionKey(key);
162 default:
163 return false;
164 }
165}
166
167static inline bool ValidSigningKey(const GpgME::Key &key)
168{
169 if (key.isRevoked() || key.isExpired() || key.isDisabled() || !key.canSign()) {
170 return false;
171 }
172 return key.hasSecret();
173}
174
175static inline bool ValidOpenPGPSigningKey(const GpgME::Key &key)
176{
177 return key.protocol() == GpgME::OpenPGP && ValidSigningKey(key);
178}
179
180static inline bool ValidSMIMESigningKey(const GpgME::Key &key)
181{
182 return key.protocol() == GpgME::CMS && ValidSigningKey(key);
183}
184
185static inline bool NotValidTrustedOpenPGPEncryptionKey(const GpgME::Key &key)
186{
187 return !ValidTrustedOpenPGPEncryptionKey(key);
188}
189
190static inline bool NotValidTrustedSMIMEEncryptionKey(const GpgME::Key &key)
191{
192 return !ValidTrustedSMIMEEncryptionKey(key);
193}
194
195static inline bool NotValidTrustedEncryptionKey(const GpgME::Key &key)
196{
197 return !ValidTrustedEncryptionKey(key);
198}
199
200static inline bool NotValidEncryptionKey(const GpgME::Key &key)
201{
202 return !ValidEncryptionKey(key);
203}
204
205static inline bool NotValidOpenPGPSigningKey(const GpgME::Key &key)
206{
207 return !ValidOpenPGPSigningKey(key);
208}
209
210static inline bool NotValidSMIMESigningKey(const GpgME::Key &key)
211{
212 return !ValidSMIMESigningKey(key);
213}
214
215namespace
216{
217struct ByTrustScore {
218 static int score(const GpgME::UserID &uid)
219 {
220 return uid.isRevoked() || uid.isInvalid() ? -1 : uid.validity();
221 }
222
223 bool operator()(const GpgME::UserID &lhs, const GpgME::UserID &rhs) const
224 {
225 return score(lhs) < score(rhs);
226 }
227};
228}
229
230static std::vector<GpgME::UserID> matchingUIDs(const std::vector<GpgME::UserID> &uids, const QString &address)
231{
232 if (address.isEmpty()) {
233 return {};
234 }
235
236 std::vector<GpgME::UserID> result;
237 result.reserve(uids.size());
238
239 for (auto it = uids.begin(), end = uids.end(); it != end; ++it) {
240 // PENDING(marc) check DN for an EMAIL, too, in case of X.509 certs... :/
241 if (const char *email = it->email()) {
242 if (*email && QString::fromUtf8(email).simplified().toLower() == address) {
243 result.push_back(*it);
244 }
245 }
246 }
247 return result;
248}
249
250static GpgME::UserID findBestMatchUID(const GpgME::Key &key, const QString &address)
251{
252 const std::vector<GpgME::UserID> all = key.userIDs();
253 if (all.empty()) {
254 return {};
255 }
256 const std::vector<GpgME::UserID> matching = matchingUIDs(all, address.toLower());
257 const std::vector<GpgME::UserID> &v = matching.empty() ? all : matching;
258 return *std::max_element(v.begin(), v.end(), ByTrustScore());
259}
260
261static QStringList keysAsStrings(const std::vector<GpgME::Key> &keys)
262{
263 QStringList strings;
264 strings.reserve(keys.size());
265 for (auto it = keys.begin(); it != keys.end(); ++it) {
266 assert(!(*it).userID(0).isNull());
267 const auto userID = (*it).userID(0);
268 QString keyLabel = QString::fromUtf8(userID.email());
269 if (keyLabel.isEmpty()) {
270 keyLabel = QString::fromUtf8(userID.name());
271 }
272 if (keyLabel.isEmpty()) {
273 keyLabel = QString::fromUtf8(userID.id());
274 }
275 strings.append(keyLabel);
276 }
277 return strings;
278}
279
280static std::vector<GpgME::Key> trustedOrConfirmed(const std::vector<GpgME::Key> &keys, const QString &address, bool &canceled)
281{
282 // PENDING(marc) work on UserIDs here?
283 std::vector<GpgME::Key> fishies;
284 std::vector<GpgME::Key> ickies;
285 std::vector<GpgME::Key> rewookies;
286 auto it = keys.begin();
287 const auto end = keys.end();
288 for (; it != end; ++it) {
289 const GpgME::Key &key = *it;
290 assert(ValidEncryptionKey(key));
291 const GpgME::UserID uid = findBestMatchUID(key, address);
292 if (uid.isRevoked()) {
293 rewookies.push_back(key);
294 }
295 if (!uid.isRevoked() && uid.validity() == GpgME::UserID::Marginal) {
296 fishies.push_back(key);
297 }
298 if (!uid.isRevoked() && uid.validity() < GpgME::UserID::Never) {
299 ickies.push_back(key);
300 }
301 }
302
303 if (fishies.empty() && ickies.empty() && rewookies.empty()) {
304 return keys;
305 }
306
307 // if some keys are not fully trusted, let the user confirm their use
308 QString msg = address.isEmpty() ? i18n(
309 "One or more of your configured OpenPGP encryption "
310 "keys or S/MIME certificates is not fully trusted "
311 "for encryption.")
312 : i18n(
313 "One or more of the OpenPGP encryption keys or S/MIME "
314 "certificates for recipient \"%1\" is not fully trusted "
315 "for encryption.",
316 address);
317
318 if (!fishies.empty()) {
319 // certificates can't have marginal trust
320 msg += i18n("\nThe following keys are only marginally trusted: \n");
321 msg += keysAsStrings(fishies).join(QLatin1Char(','));
322 }
323 if (!ickies.empty()) {
324 msg += i18n("\nThe following keys or certificates have unknown trust level: \n");
325 msg += keysAsStrings(ickies).join(QLatin1Char(','));
326 }
327 if (!rewookies.empty()) {
328 msg += i18n("\nThe following keys or certificates are <b>revoked</b>: \n");
329 msg += keysAsStrings(rewookies).join(QLatin1Char(','));
330 }
331
333 msg,
334 i18nc("@title:window", "Not Fully Trusted Encryption Keys"),
337 QStringLiteral("not fully trusted encryption key warning"))
339 return keys;
340 } else {
341 canceled = true;
342 }
343 return {};
344}
345
346namespace
347{
348struct IsNotForFormat : public std::function<bool(GpgME::Key)> {
349 IsNotForFormat(Kleo::CryptoMessageFormat f)
350 : format(f)
351 {
352 }
353
354 bool operator()(const GpgME::Key &key) const
355 {
356 return (isOpenPGP(format) && key.protocol() != GpgME::OpenPGP) || (isSMIME(format) && key.protocol() != GpgME::CMS);
357 }
358
359 const Kleo::CryptoMessageFormat format;
360};
361
362struct IsForFormat : std::function<bool(GpgME::Key)> {
363 explicit IsForFormat(Kleo::CryptoMessageFormat f)
364 : protocol(isOpenPGP(f) ? GpgME::OpenPGP
365 : isSMIME(f) ? GpgME::CMS
366 : GpgME::UnknownProtocol)
367 {
368 }
369
370 bool operator()(const GpgME::Key &key) const
371 {
372 return key.protocol() == protocol;
373 }
374
375 const GpgME::Protocol protocol;
376};
377}
378
379class Kleo::KeyResolver::SigningPreferenceCounter : public std::function<void(Kleo::KeyResolver::Item)>
380{
381public:
382 SigningPreferenceCounter() = default;
383
384 void operator()(const Kleo::KeyResolver::Item &item);
385#define make_int_accessor(x) \
386 unsigned int num##x() const \
387 { \
388 return m##x; \
389 }
390 make_int_accessor(UnknownSigningPreference) make_int_accessor(NeverSign) make_int_accessor(AlwaysSign) make_int_accessor(AlwaysSignIfPossible)
391 make_int_accessor(AlwaysAskForSigning) make_int_accessor(AskSigningWheneverPossible) make_int_accessor(Total)
392#undef make_int_accessor
393 private : unsigned int mTotal = 0;
394 unsigned int mUnknownSigningPreference = 0;
395 unsigned int mNeverSign = 0;
396 unsigned int mAlwaysSign = 0;
397 unsigned int mAlwaysSignIfPossible = 0;
398 unsigned int mAlwaysAskForSigning = 0;
399 unsigned int mAskSigningWheneverPossible = 0;
400};
401
402void Kleo::KeyResolver::SigningPreferenceCounter::operator()(const Kleo::KeyResolver::Item &item)
403{
404 switch (item.signPref) {
405#define CASE(x) \
406 case x: \
407 ++m##x; \
408 break
409 CASE(UnknownSigningPreference);
410 CASE(NeverSign);
411 CASE(AlwaysSign);
412 CASE(AlwaysSignIfPossible);
413 CASE(AlwaysAskForSigning);
414 CASE(AskSigningWheneverPossible);
415#undef CASE
416 }
417 ++mTotal;
418}
419
420class Kleo::KeyResolver::EncryptionPreferenceCounter : public std::function<void(Item)>
421{
422 const Kleo::KeyResolver *_this;
423
424public:
425 EncryptionPreferenceCounter(const Kleo::KeyResolver *kr, EncryptionPreference defaultPreference)
426 : _this(kr)
427 , mDefaultPreference(defaultPreference)
428 {
429 }
430
431 void operator()(Item &item);
432
433 template<typename Container>
434 void process(Container &c)
435 {
436 *this = std::for_each(c.begin(), c.end(), *this);
437 }
438
439#define make_int_accessor(x) \
440 unsigned int num##x() const \
441 { \
442 return m##x; \
443 }
444 make_int_accessor(NoKey) make_int_accessor(NeverEncrypt) make_int_accessor(UnknownPreference) make_int_accessor(AlwaysEncrypt)
445 make_int_accessor(AlwaysEncryptIfPossible) make_int_accessor(AlwaysAskForEncryption) make_int_accessor(AskWheneverPossible) make_int_accessor(Total)
446#undef make_int_accessor
447 private : EncryptionPreference mDefaultPreference;
448 unsigned int mTotal = 0;
449 unsigned int mNoKey = 0;
450 unsigned int mNeverEncrypt = 0;
451 unsigned int mUnknownPreference = 0;
452 unsigned int mAlwaysEncrypt = 0;
453 unsigned int mAlwaysEncryptIfPossible = 0;
454 unsigned int mAlwaysAskForEncryption = 0;
455 unsigned int mAskWheneverPossible = 0;
456};
457
458void Kleo::KeyResolver::EncryptionPreferenceCounter::operator()(Item &item)
459{
460 if (_this) {
461 if (item.needKeys) {
462 item.keys = _this->getEncryptionKeys(item.address, true);
463 }
464 if (item.keys.empty()) {
465 ++mNoKey;
466 return;
467 }
468 }
469 switch (!item.pref ? mDefaultPreference : item.pref) {
470#define CASE(x) \
471 case Kleo::x: \
472 ++m##x; \
473 break
474 CASE(NeverEncrypt);
475 CASE(UnknownPreference);
476 CASE(AlwaysEncrypt);
477 CASE(AlwaysEncryptIfPossible);
478 CASE(AlwaysAskForEncryption);
479 CASE(AskWheneverPossible);
480#undef CASE
481 }
482 ++mTotal;
483}
484
485namespace
486{
487class FormatPreferenceCounterBase : public std::function<void(Kleo::KeyResolver::Item)>
488{
489public:
490 FormatPreferenceCounterBase() = default;
491
492#define make_int_accessor(x) \
493 unsigned int num##x() const \
494 { \
495 return m##x; \
496 }
497 make_int_accessor(Total) make_int_accessor(InlineOpenPGP) make_int_accessor(OpenPGPMIME) make_int_accessor(SMIME) make_int_accessor(SMIMEOpaque)
498#undef make_int_accessor
499
500 [[nodiscard]] unsigned int numOf(Kleo::CryptoMessageFormat f) const
501 {
502 switch (f) {
503#define CASE(x) \
504 case Kleo::x##Format: \
505 return m##x
506 CASE(InlineOpenPGP);
507 CASE(OpenPGPMIME);
508 CASE(SMIME);
509 CASE(SMIMEOpaque);
510#undef CASE
511 default:
512 return 0;
513 }
514 }
515
516protected:
517 unsigned int mTotal = 0;
518 unsigned int mInlineOpenPGP = 0;
519 unsigned int mOpenPGPMIME = 0;
520 unsigned int mSMIME = 0;
521 unsigned int mSMIMEOpaque = 0;
522};
523
524class EncryptionFormatPreferenceCounter : public FormatPreferenceCounterBase
525{
526public:
527 EncryptionFormatPreferenceCounter()
528 : FormatPreferenceCounterBase()
529 {
530 }
531
532 void operator()(const Kleo::KeyResolver::Item &item);
533};
534
535class SigningFormatPreferenceCounter : public FormatPreferenceCounterBase
536{
537public:
538 SigningFormatPreferenceCounter()
539 : FormatPreferenceCounterBase()
540 {
541 }
542
543 void operator()(const Kleo::KeyResolver::Item &item);
544};
545
546#define CASE(x) \
547 if (item.format & Kleo::x##Format) { \
548 ++m##x; \
549 }
550void EncryptionFormatPreferenceCounter::operator()(const Kleo::KeyResolver::Item &item)
551{
552 if (item.format & (Kleo::InlineOpenPGPFormat | Kleo::OpenPGPMIMEFormat)
553 && std::any_of(item.keys.begin(),
554 item.keys.end(),
555 ValidTrustedOpenPGPEncryptionKey)) { // -= trusted?
556 CASE(OpenPGPMIME);
557 CASE(InlineOpenPGP);
558 }
559 if (item.format & (Kleo::SMIMEFormat | Kleo::SMIMEOpaqueFormat)
560 && std::any_of(item.keys.begin(),
561 item.keys.end(),
562 ValidTrustedSMIMEEncryptionKey)) { // -= trusted?
563 CASE(SMIME);
564 CASE(SMIMEOpaque);
565 }
566 ++mTotal;
567}
568
569void SigningFormatPreferenceCounter::operator()(const Kleo::KeyResolver::Item &item)
570{
571 CASE(InlineOpenPGP);
572 CASE(OpenPGPMIME);
573 CASE(SMIME);
574 CASE(SMIMEOpaque);
575 ++mTotal;
576}
577
578#undef CASE
579} // anon namespace
580
581static QString canonicalAddress(const QString &_address)
582{
584 if (!address.contains(QLatin1Char('@'))) {
585 // local address
586 // return address + '@' + KNetwork::KResolver::localHostName();
587 return address + QLatin1StringView("@localdomain");
588 } else {
589 return address;
590 }
591}
592
593struct FormatInfo {
594 std::vector<Kleo::KeyResolver::SplitInfo> splitInfos;
595 std::vector<GpgME::Key> signKeys;
596};
597
598struct Q_DECL_HIDDEN Kleo::KeyResolver::KeyResolverPrivate {
599 bool mAkonadiLookupEnabled = true;
600 bool mAutocryptEnabled = false;
601 std::set<QByteArray> alreadyWarnedFingerprints;
602
603 std::vector<GpgME::Key> mOpenPGPSigningKeys; // signing
604 std::vector<GpgME::Key> mSMIMESigningKeys; // signing
605
606 std::vector<GpgME::Key> mOpenPGPEncryptToSelfKeys; // encryption to self
607 std::vector<GpgME::Key> mSMIMEEncryptToSelfKeys; // encryption to self
608
609 std::vector<Item> mPrimaryEncryptionKeys; // encryption to To/CC
610 std::vector<Item> mSecondaryEncryptionKeys; // encryption to BCC
611
612 std::map<CryptoMessageFormat, FormatInfo> mFormatInfoMap;
613
614 // key=email address, value=crypto preferences for this contact (from kabc)
615 using ContactPreferencesMap = std::map<QString, MessageComposer::ContactPreference>;
616 ContactPreferencesMap mContactPreferencesMap;
617 std::map<QByteArray, QString> mAutocryptMap;
618 std::shared_ptr<Kleo::ExpiryChecker> expiryChecker;
619};
620
621Kleo::KeyResolver::KeyResolver(bool encToSelf, bool showApproval, bool oppEncryption, unsigned int f, const std::shared_ptr<Kleo::ExpiryChecker> &expiryChecker)
622 : d(new KeyResolverPrivate)
623 , mEncryptToSelf(encToSelf)
624 , mShowApprovalDialog(showApproval)
625 , mOpportunisticEncyption(oppEncryption)
626 , mCryptoMessageFormats(f)
627{
628 d->expiryChecker = expiryChecker;
629}
630
631Kleo::KeyResolver::~KeyResolver() = default;
632
634{
635 if (!encryptToSelf()) {
636 return Kleo::Ok;
637 }
638
639 std::vector<GpgME::Key> keys = lookup(fingerprints);
640 std::remove_copy_if(keys.begin(), keys.end(), std::back_inserter(d->mOpenPGPEncryptToSelfKeys),
641 NotValidTrustedOpenPGPEncryptionKey); // -= trusted?
642 std::remove_copy_if(keys.begin(), keys.end(), std::back_inserter(d->mSMIMEEncryptToSelfKeys),
643 NotValidTrustedSMIMEEncryptionKey); // -= trusted?
644
645 if (d->mOpenPGPEncryptToSelfKeys.size() + d->mSMIMEEncryptToSelfKeys.size() < keys.size()) {
646 // too few keys remain...
647 const QString msg = i18n(
648 "One or more of your configured OpenPGP encryption "
649 "keys or S/MIME certificates is not usable for "
650 "encryption. Please reconfigure your encryption keys "
651 "and certificates for this identity in the identity "
652 "configuration dialog.\n"
653 "If you choose to continue, and the keys are needed "
654 "later on, you will be prompted to specify the keys "
655 "to use.");
657 msg,
658 i18nc("@title:window", "Unusable Encryption Keys"),
661 QStringLiteral("unusable own encryption key warning"))
663 ? Kleo::Ok
664 : Kleo::Canceled;
665 }
666
667 // check for near-expiry:
668 std::vector<GpgME::Key>::const_iterator end(d->mOpenPGPEncryptToSelfKeys.end());
669
670 for (auto it = d->mOpenPGPEncryptToSelfKeys.begin(); it != end; ++it) {
671 d->expiryChecker->checkKey(*it, Kleo::ExpiryChecker::OwnEncryptionKey);
672 }
673 std::vector<GpgME::Key>::const_iterator end2(d->mSMIMEEncryptToSelfKeys.end());
674 for (auto it = d->mSMIMEEncryptToSelfKeys.begin(); it != end2; ++it) {
675 d->expiryChecker->checkKey(*it, Kleo::ExpiryChecker::OwnEncryptionKey);
676 }
677
678 return Kleo::Ok;
679}
680
681Kleo::Result Kleo::KeyResolver::setSigningKeys(const QStringList &fingerprints)
682{
683 std::vector<GpgME::Key> keys = lookup(fingerprints, true); // secret keys
684 std::remove_copy_if(keys.begin(), keys.end(), std::back_inserter(d->mOpenPGPSigningKeys), NotValidOpenPGPSigningKey);
685 std::remove_copy_if(keys.begin(), keys.end(), std::back_inserter(d->mSMIMESigningKeys), NotValidSMIMESigningKey);
686
687 if (d->mOpenPGPSigningKeys.size() + d->mSMIMESigningKeys.size() < keys.size()) {
688 // too few keys remain...
689 const QString msg = i18n(
690 "One or more of your configured OpenPGP signing keys "
691 "or S/MIME signing certificates is not usable for "
692 "signing. Please reconfigure your signing keys "
693 "and certificates for this identity in the identity "
694 "configuration dialog.\n"
695 "If you choose to continue, and the keys are needed "
696 "later on, you will be prompted to specify the keys "
697 "to use.");
699 msg,
700 i18nc("@title:window", "Unusable Signing Keys"),
703 QStringLiteral("unusable signing key warning"))
705 ? Kleo::Ok
706 : Kleo::Canceled;
707 }
708
709 // check for near expiry:
710
711 for (auto it = d->mOpenPGPSigningKeys.begin(), total = d->mOpenPGPSigningKeys.end(); it != total; ++it) {
712 d->expiryChecker->checkKey(*it, Kleo::ExpiryChecker::OwnSigningKey);
713 }
714
715 for (auto it = d->mSMIMESigningKeys.begin(), total = d->mSMIMESigningKeys.end(); it != total; ++it) {
716 d->expiryChecker->checkKey(*it, Kleo::ExpiryChecker::OwnSigningKey);
717 }
718
719 return Kleo::Ok;
720}
721
723{
724 d->mPrimaryEncryptionKeys = getEncryptionItems(addresses);
725}
726
728{
729 d->mSecondaryEncryptionKeys = getEncryptionItems(addresses);
730}
731
732std::vector<Kleo::KeyResolver::Item> Kleo::KeyResolver::getEncryptionItems(const QStringList &addresses)
733{
734 std::vector<Item> items;
735 items.reserve(addresses.size());
736 QStringList::const_iterator end(addresses.constEnd());
737 for (QStringList::const_iterator it = addresses.constBegin(); it != end; ++it) {
738 QString addr = canonicalAddress(*it).toLower();
739 const auto pref = lookupContactPreferences(addr);
740
741 items.emplace_back(*it, /*getEncryptionKeys( *it, true ),*/
742 pref.encryptionPreference,
743 pref.signingPreference,
744 pref.cryptoMessageFormat);
745 }
746 return items;
747}
748
749static Kleo::Action action(bool doit, bool ask, bool donot, bool requested)
750{
751 if (requested && !donot) {
752 return Kleo::DoIt;
753 }
754 if (doit && !ask && !donot) {
755 return Kleo::DoIt;
756 }
757 if (!doit && ask && !donot) {
758 return Kleo::Ask;
759 }
760 if (!doit && !ask && donot) {
761 return requested ? Kleo::Conflict : Kleo::DontDoIt;
762 }
763 if (!doit && !ask && !donot) {
764 return Kleo::DontDoIt;
765 }
766 return Kleo::Conflict;
767}
768
769Kleo::Action Kleo::KeyResolver::checkSigningPreferences(bool signingRequested) const
770{
771 if (signingRequested && d->mOpenPGPSigningKeys.empty() && d->mSMIMESigningKeys.empty()) {
772 return Impossible;
773 }
774
775 SigningPreferenceCounter count;
776 count = std::for_each(d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(), count);
777 count = std::for_each(d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(), count);
778
779 unsigned int sign = count.numAlwaysSign();
780 unsigned int ask = count.numAlwaysAskForSigning();
781 const unsigned int dontSign = count.numNeverSign();
782 if (signingPossible()) {
783 sign += count.numAlwaysSignIfPossible();
784 ask += count.numAskSigningWheneverPossible();
785 }
786
787 return action(sign, ask, dontSign, signingRequested);
788}
789
790bool Kleo::KeyResolver::signingPossible() const
791{
792 return !d->mOpenPGPSigningKeys.empty() || !d->mSMIMESigningKeys.empty();
793}
794
795Kleo::Action Kleo::KeyResolver::checkEncryptionPreferences(bool encryptionRequested) const
796{
797 if (d->mPrimaryEncryptionKeys.empty() && d->mSecondaryEncryptionKeys.empty()) {
798 return DontDoIt;
799 }
800
801 if (encryptionRequested && encryptToSelf() && d->mOpenPGPEncryptToSelfKeys.empty() && d->mSMIMEEncryptToSelfKeys.empty()) {
802 return Impossible;
803 }
804
805 if (!encryptionRequested && !mOpportunisticEncyption) {
806 // try to minimize crypto ops (including key lookups) by only
807 // looking up keys when at least one of the encryption
808 // preferences needs it:
809 EncryptionPreferenceCounter count(nullptr, UnknownPreference);
810 count.process(d->mPrimaryEncryptionKeys);
811 count.process(d->mSecondaryEncryptionKeys);
812 if (!count.numAlwaysEncrypt()
813 && !count.numAlwaysAskForEncryption() // this guy might not need a lookup, when declined, but it's too complex to implement that here
814 && !count.numAlwaysEncryptIfPossible() && !count.numAskWheneverPossible()) {
815 return DontDoIt;
816 }
817 }
818
819 EncryptionPreferenceCounter count(this, mOpportunisticEncyption ? AskWheneverPossible : UnknownPreference);
820 count = std::for_each(d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(), count);
821 count = std::for_each(d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(), count);
822
823 unsigned int encrypt = count.numAlwaysEncrypt();
824 unsigned int ask = count.numAlwaysAskForEncryption();
825 const unsigned int dontEncrypt = count.numNeverEncrypt() + count.numNoKey();
826 if (encryptionPossible()) {
827 encrypt += count.numAlwaysEncryptIfPossible();
828 ask += count.numAskWheneverPossible();
829 }
830
831 const Action act = action(encrypt, ask, dontEncrypt, encryptionRequested);
832 if (act != Ask
833 || std::for_each(
834 d->mPrimaryEncryptionKeys.begin(),
835 d->mPrimaryEncryptionKeys.end(),
836 std::for_each(d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(), EncryptionPreferenceCounter(this, UnknownPreference)))
837 .numAlwaysAskForEncryption()) {
838 return act;
839 } else {
840 return AskOpportunistic;
841 }
842}
843
844bool Kleo::KeyResolver::encryptionPossible() const
845{
846 return std::none_of(d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(), EmptyKeyList)
847 && std::none_of(d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(), EmptyKeyList);
848}
849
850Kleo::Result Kleo::KeyResolver::resolveAllKeys(bool &signingRequested, bool &encryptionRequested)
851{
852 if (!encryptionRequested && !signingRequested) {
853 // make a dummy entry with all recipients, but no signing or
854 // encryption keys to avoid special-casing on the caller side:
855 dump();
856 d->mFormatInfoMap[OpenPGPMIMEFormat].splitInfos.emplace_back(allRecipients());
857 dump();
858 return Kleo::Ok;
859 }
860 Kleo::Result result = Kleo::Ok;
861 if (encryptionRequested) {
862 bool finalySendUnencrypted = false;
863 result = resolveEncryptionKeys(signingRequested, finalySendUnencrypted);
864 if (finalySendUnencrypted) {
865 encryptionRequested = false;
866 }
867 }
868 if (result != Kleo::Ok) {
869 return result;
870 }
871 if (encryptionRequested) {
872 result = resolveSigningKeysForEncryption();
873 } else {
874 result = resolveSigningKeysForSigningOnly();
875 if (result == Kleo::Failure) {
876 signingRequested = false;
877 return Kleo::Ok;
878 }
879 }
880 return result;
881}
882
883Kleo::Result Kleo::KeyResolver::resolveEncryptionKeys(bool signingRequested, bool &finalySendUnencrypted)
884{
885 //
886 // 1. Get keys for all recipients:
887 //
888 qCDebug(MESSAGECOMPOSER_LOG) << "resolving enc keys" << d->mPrimaryEncryptionKeys.size();
889 for (auto it = d->mPrimaryEncryptionKeys.begin(); it != d->mPrimaryEncryptionKeys.end(); ++it) {
890 qCDebug(MESSAGECOMPOSER_LOG) << "checking primary:" << it->address;
891 if (!it->needKeys) {
892 continue;
893 }
894 it->keys = getEncryptionKeys(it->address, false);
895 qCDebug(MESSAGECOMPOSER_LOG) << "got # keys:" << it->keys.size();
896 if (it->keys.empty()) {
897 return Kleo::Canceled;
898 }
899 QString addr = canonicalAddress(it->address).toLower();
900 const auto pref = lookupContactPreferences(addr);
901 it->pref = pref.encryptionPreference;
902 it->signPref = pref.signingPreference;
903 it->format = pref.cryptoMessageFormat;
904 qCDebug(MESSAGECOMPOSER_LOG) << "set key data:" << int(it->pref) << int(it->signPref) << int(it->format);
905 }
906
907 for (auto it = d->mSecondaryEncryptionKeys.begin(), total = d->mSecondaryEncryptionKeys.end(); it != total; ++it) {
908 if (!it->needKeys) {
909 continue;
910 }
911 it->keys = getEncryptionKeys(it->address, false);
912 if (it->keys.empty()) {
913 return Kleo::Canceled;
914 }
915 QString addr = canonicalAddress(it->address).toLower();
916 const auto pref = lookupContactPreferences(addr);
917 it->pref = pref.encryptionPreference;
918 it->signPref = pref.signingPreference;
919 it->format = pref.cryptoMessageFormat;
920 }
921
922 // 1a: Present them to the user
923
924 const Kleo::Result res = showKeyApprovalDialog(finalySendUnencrypted);
925 if (res != Kleo::Ok) {
926 return res;
927 }
928
929 //
930 // 2. Check what the primary recipients need
931 //
932
933 // 2a. Try to find a common format for all primary recipients,
934 // else use as many formats as needed
935
936 const EncryptionFormatPreferenceCounter primaryCount =
937 std::for_each(d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(), EncryptionFormatPreferenceCounter());
938
939 CryptoMessageFormat commonFormat = AutoFormat;
940 for (unsigned int i = 0; i < numConcreteCryptoMessageFormats; ++i) {
941 if (!(concreteCryptoMessageFormats[i] & mCryptoMessageFormats)) {
942 continue;
943 }
944 if (signingRequested && signingKeysFor(concreteCryptoMessageFormats[i]).empty()) {
945 continue;
946 }
947 if (encryptToSelf() && encryptToSelfKeysFor(concreteCryptoMessageFormats[i]).empty()) {
948 continue;
949 }
950 if (primaryCount.numOf(concreteCryptoMessageFormats[i]) == primaryCount.numTotal()) {
951 commonFormat = concreteCryptoMessageFormats[i];
952 break;
953 }
954 }
955 qCDebug(MESSAGECOMPOSER_LOG) << "got commonFormat for primary recipients:" << int(commonFormat);
956 if (commonFormat != AutoFormat) {
957 addKeys(d->mPrimaryEncryptionKeys, commonFormat);
958 } else {
959 addKeys(d->mPrimaryEncryptionKeys);
960 }
961
962 collapseAllSplitInfos(); // these can be encrypted together
963
964 // 2b. Just try to find _something_ for each secondary recipient,
965 // with a preference to a common format (if that exists)
966
967 const EncryptionFormatPreferenceCounter secondaryCount =
968 std::for_each(d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(), EncryptionFormatPreferenceCounter());
969
970 if (commonFormat != AutoFormat && secondaryCount.numOf(commonFormat) == secondaryCount.numTotal()) {
971 addKeys(d->mSecondaryEncryptionKeys, commonFormat);
972 } else {
973 addKeys(d->mSecondaryEncryptionKeys);
974 }
975
976 // 3. Check for expiry:
977
978 for (unsigned int i = 0; i < numConcreteCryptoMessageFormats; ++i) {
979 const std::vector<SplitInfo> si_list = encryptionItems(concreteCryptoMessageFormats[i]);
980 for (auto sit = si_list.begin(), total = si_list.end(); sit != total; ++sit) {
981 for (auto kit = sit->keys.begin(); kit != sit->keys.end(); ++kit) {
982 d->expiryChecker->checkKey(*kit, Kleo::ExpiryChecker::EncryptionKey);
983 }
984 }
985 }
986
987 // 4. Check that we have the right keys for encryptToSelf()
988
989 if (!encryptToSelf()) {
990 return Kleo::Ok;
991 }
992
993 // 4a. Check for OpenPGP keys
994
995 qCDebug(MESSAGECOMPOSER_LOG) << "sizes of encryption items:" << encryptionItems(InlineOpenPGPFormat).size() << encryptionItems(OpenPGPMIMEFormat).size()
996 << encryptionItems(SMIMEFormat).size() << encryptionItems(SMIMEOpaqueFormat).size();
997 if (!encryptionItems(InlineOpenPGPFormat).empty() || !encryptionItems(OpenPGPMIMEFormat).empty()) {
998 // need them
999 if (d->mOpenPGPEncryptToSelfKeys.empty()) {
1000 const QString msg = i18n(
1001 "Examination of recipient's encryption preferences "
1002 "yielded that the message should be encrypted using "
1003 "OpenPGP, at least for some recipients;\n"
1004 "however, you have not configured valid trusted "
1005 "OpenPGP encryption keys for this identity.\n"
1006 "You may continue without encrypting to yourself, "
1007 "but be aware that you will not be able to read your "
1008 "own messages if you do so.");
1010 msg,
1011 i18nc("@title:window", "Unusable Encryption Keys"),
1014 QStringLiteral("encrypt-to-self will fail warning"))
1016 return Kleo::Canceled;
1017 }
1018 // FIXME: Allow selection
1019 }
1020 addToAllSplitInfos(d->mOpenPGPEncryptToSelfKeys, InlineOpenPGPFormat | OpenPGPMIMEFormat);
1021 }
1022
1023 // 4b. Check for S/MIME certs:
1024
1025 if (!encryptionItems(SMIMEFormat).empty() || !encryptionItems(SMIMEOpaqueFormat).empty()) {
1026 // need them
1027 if (d->mSMIMEEncryptToSelfKeys.empty()) {
1028 // don't have one
1029 const QString msg = i18n(
1030 "Examination of recipient's encryption preferences "
1031 "yielded that the message should be encrypted using "
1032 "S/MIME, at least for some recipients;\n"
1033 "however, you have not configured valid "
1034 "S/MIME encryption certificates for this identity.\n"
1035 "You may continue without encrypting to yourself, "
1036 "but be aware that you will not be able to read your "
1037 "own messages if you do so.");
1039 msg,
1040 i18nc("@title:window", "Unusable Encryption Keys"),
1043 QStringLiteral("encrypt-to-self will fail warning"))
1045 return Kleo::Canceled;
1046 }
1047 // FIXME: Allow selection
1048 }
1049 addToAllSplitInfos(d->mSMIMEEncryptToSelfKeys, SMIMEFormat | SMIMEOpaqueFormat);
1050 }
1051
1052 // FIXME: Present another message if _both_ OpenPGP and S/MIME keys
1053 // are missing.
1054
1055 return Kleo::Ok;
1056}
1057
1058Kleo::Result Kleo::KeyResolver::resolveSigningKeysForEncryption()
1059{
1060 if ((!encryptionItems(InlineOpenPGPFormat).empty() || !encryptionItems(OpenPGPMIMEFormat).empty()) && d->mOpenPGPSigningKeys.empty()) {
1061 const QString msg = i18n(
1062 "Examination of recipient's signing preferences "
1063 "yielded that the message should be signed using "
1064 "OpenPGP, at least for some recipients;\n"
1065 "however, you have not configured valid "
1066 "OpenPGP signing certificates for this identity.");
1068 msg,
1069 i18nc("@title:window", "Unusable Signing Keys"),
1070 KGuiItem(i18n("Do Not OpenPGP-Sign")),
1072 QStringLiteral("signing will fail warning"))
1074 return Kleo::Canceled;
1075 }
1076 // FIXME: Allow selection
1077 }
1078 if ((!encryptionItems(SMIMEFormat).empty() || !encryptionItems(SMIMEOpaqueFormat).empty()) && d->mSMIMESigningKeys.empty()) {
1079 const QString msg = i18n(
1080 "Examination of recipient's signing preferences "
1081 "yielded that the message should be signed using "
1082 "S/MIME, at least for some recipients;\n"
1083 "however, you have not configured valid "
1084 "S/MIME signing certificates for this identity.");
1086 msg,
1087 i18nc("@title:window", "Unusable Signing Keys"),
1088 KGuiItem(i18n("Do Not S/MIME-Sign")),
1090 QStringLiteral("signing will fail warning"))
1092 return Kleo::Canceled;
1093 }
1094 // FIXME: Allow selection
1095 }
1096
1097 // FIXME: Present another message if _both_ OpenPGP and S/MIME keys
1098 // are missing.
1099
1100 for (auto it = d->mFormatInfoMap.begin(); it != d->mFormatInfoMap.end(); ++it) {
1101 if (!it->second.splitInfos.empty()) {
1102 dump();
1103 it->second.signKeys = signingKeysFor(it->first);
1104 dump();
1105 }
1106 }
1107
1108 return Kleo::Ok;
1109}
1110
1111Kleo::Result Kleo::KeyResolver::resolveSigningKeysForSigningOnly()
1112{
1113 //
1114 // we don't need to distinguish between primary and secondary
1115 // recipients here:
1116 //
1117 SigningFormatPreferenceCounter count;
1118 count = std::for_each(d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(), count);
1119 count = std::for_each(d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(), count);
1120
1121 // try to find a common format that works for all (and that we have signing keys for):
1122
1123 CryptoMessageFormat commonFormat = AutoFormat;
1124
1125 for (unsigned int i = 0; i < numConcreteCryptoMessageFormats; ++i) {
1126 const auto res = concreteCryptoMessageFormats[i];
1127 if (!(mCryptoMessageFormats & res)) {
1128 continue; // skip
1129 }
1130 if (signingKeysFor(res).empty()) {
1131 continue; // skip
1132 }
1133 if (count.numOf(res) == count.numTotal()) {
1134 commonFormat = res;
1135 break;
1136 }
1137 }
1138
1139 if (commonFormat != AutoFormat) { // found
1140 dump();
1141 FormatInfo &fi = d->mFormatInfoMap[commonFormat];
1142 fi.signKeys = signingKeysFor(commonFormat);
1143 fi.splitInfos.resize(1);
1144 fi.splitInfos.front() = SplitInfo(allRecipients());
1145 dump();
1146 return Kleo::Ok;
1147 }
1148
1149 const QString msg = i18n(
1150 "Examination of recipient's signing preferences "
1151 "showed no common type of signature matching your "
1152 "available signing keys.\n"
1153 "Send message without signing?");
1154 if (KMessageBox::warningContinueCancel(nullptr, msg, i18nc("@title:window", "No signing possible"), KStandardGuiItem::cont()) == KMessageBox::Continue) {
1155 d->mFormatInfoMap[OpenPGPMIMEFormat].splitInfos.emplace_back(allRecipients());
1156 return Kleo::Failure; // means "Ok, but without signing"
1157 }
1158 return Kleo::Canceled;
1159}
1160
1161std::vector<GpgME::Key> Kleo::KeyResolver::signingKeysFor(CryptoMessageFormat f) const
1162{
1163 if (isOpenPGP(f)) {
1164 return d->mOpenPGPSigningKeys;
1165 }
1166 if (isSMIME(f)) {
1167 return d->mSMIMESigningKeys;
1168 }
1169 return {};
1170}
1171
1172std::vector<GpgME::Key> Kleo::KeyResolver::encryptToSelfKeysFor(CryptoMessageFormat f) const
1173{
1174 if (isOpenPGP(f)) {
1175 return d->mOpenPGPEncryptToSelfKeys;
1176 }
1177 if (isSMIME(f)) {
1178 return d->mSMIMEEncryptToSelfKeys;
1179 }
1180 return {};
1181}
1182
1183QStringList Kleo::KeyResolver::allRecipients() const
1184{
1185 QStringList result;
1186 std::transform(d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(), std::back_inserter(result), ItemDotAddress);
1187 std::transform(d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(), std::back_inserter(result), ItemDotAddress);
1188 return result;
1189}
1190
1191void Kleo::KeyResolver::collapseAllSplitInfos()
1192{
1193 dump();
1194 for (unsigned int i = 0; i < numConcreteCryptoMessageFormats; ++i) {
1195 auto pos = d->mFormatInfoMap.find(concreteCryptoMessageFormats[i]);
1196 if (pos == d->mFormatInfoMap.end()) {
1197 continue;
1198 }
1199 std::vector<SplitInfo> &v = pos->second.splitInfos;
1200 if (v.size() < 2) {
1201 continue;
1202 }
1203 SplitInfo &si = v.front();
1204 for (auto it = v.begin() + 1; it != v.end(); ++it) {
1205 si.keys.insert(si.keys.end(), it->keys.begin(), it->keys.end());
1206 std::copy(it->recipients.begin(), it->recipients.end(), std::back_inserter(si.recipients));
1207 }
1208 v.resize(1);
1209 }
1210 dump();
1211}
1212
1213void Kleo::KeyResolver::addToAllSplitInfos(const std::vector<GpgME::Key> &keys, unsigned int f)
1214{
1215 dump();
1216 if (!f || keys.empty()) {
1217 return;
1218 }
1219 for (unsigned int i = 0; i < numConcreteCryptoMessageFormats; ++i) {
1220 if (!(f & concreteCryptoMessageFormats[i])) {
1221 continue;
1222 }
1223 auto pos = d->mFormatInfoMap.find(concreteCryptoMessageFormats[i]);
1224 if (pos == d->mFormatInfoMap.end()) {
1225 continue;
1226 }
1227 std::vector<SplitInfo> &v = pos->second.splitInfos;
1228 for (auto it = v.begin(); it != v.end(); ++it) {
1229 it->keys.insert(it->keys.end(), keys.begin(), keys.end());
1230 }
1231 }
1232 dump();
1233}
1234
1235void Kleo::KeyResolver::dump() const
1236{
1237#ifndef NDEBUG
1238 if (d->mFormatInfoMap.empty()) {
1239 qCDebug(MESSAGECOMPOSER_LOG) << "Keyresolver: Format info empty";
1240 }
1241 for (auto it = d->mFormatInfoMap.begin(); it != d->mFormatInfoMap.end(); ++it) {
1242 qCDebug(MESSAGECOMPOSER_LOG) << "Format info for " << Kleo::cryptoMessageFormatToString(it->first) << ": Signing keys: ";
1243 for (auto sit = it->second.signKeys.begin(); sit != it->second.signKeys.end(); ++sit) {
1244 qCDebug(MESSAGECOMPOSER_LOG) << " " << sit->shortKeyID() << " ";
1245 }
1246 unsigned int i = 0;
1247 for (auto sit = it->second.splitInfos.begin(), sitEnd = it->second.splitInfos.end(); sit != sitEnd; ++sit, ++i) {
1248 qCDebug(MESSAGECOMPOSER_LOG) << " SplitInfo #" << i << " encryption keys: ";
1249 for (auto kit = sit->keys.begin(), sitEnd = sit->keys.end(); kit != sitEnd; ++kit) {
1250 qCDebug(MESSAGECOMPOSER_LOG) << " " << kit->shortKeyID();
1251 }
1252 qCDebug(MESSAGECOMPOSER_LOG) << " SplitInfo #" << i << " recipients: " << qPrintable(sit->recipients.join(QLatin1StringView(", ")));
1253 }
1254 }
1255#endif
1256}
1257
1258Kleo::Result Kleo::KeyResolver::showKeyApprovalDialog(bool &finalySendUnencrypted)
1259{
1260 const bool showKeysForApproval = showApprovalDialog() || std::any_of(d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(), ApprovalNeeded)
1261 || std::any_of(d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(), ApprovalNeeded);
1262
1263 if (!showKeysForApproval) {
1264 return Kleo::Ok;
1265 }
1266
1267 std::vector<Kleo::KeyApprovalDialog::Item> items;
1268 items.reserve(d->mPrimaryEncryptionKeys.size() + d->mSecondaryEncryptionKeys.size());
1269 std::copy(d->mPrimaryEncryptionKeys.begin(), d->mPrimaryEncryptionKeys.end(), std::back_inserter(items));
1270 std::copy(d->mSecondaryEncryptionKeys.begin(), d->mSecondaryEncryptionKeys.end(), std::back_inserter(items));
1271
1272 std::vector<GpgME::Key> senderKeys;
1273 senderKeys.reserve(d->mOpenPGPEncryptToSelfKeys.size() + d->mSMIMEEncryptToSelfKeys.size());
1274 std::copy(d->mOpenPGPEncryptToSelfKeys.begin(), d->mOpenPGPEncryptToSelfKeys.end(), std::back_inserter(senderKeys));
1275 std::copy(d->mSMIMEEncryptToSelfKeys.begin(), d->mSMIMEEncryptToSelfKeys.end(), std::back_inserter(senderKeys));
1276
1278
1279 QPointer<Kleo::KeyApprovalDialog> dlg = new Kleo::KeyApprovalDialog(items, senderKeys);
1280
1281 if (dlg->exec() == QDialog::Rejected) {
1282 delete dlg;
1283 return Kleo::Canceled;
1284 }
1285
1286 items = dlg->items();
1287 senderKeys = dlg->senderKeys();
1288 const bool prefsChanged = dlg->preferencesChanged();
1289 delete dlg;
1290
1291 if (prefsChanged) {
1292 for (uint i = 0, total = items.size(); i < total; ++i) {
1293 auto pref = lookupContactPreferences(items[i].address);
1294 pref.encryptionPreference = items[i].pref;
1295 pref.pgpKeyFingerprints.clear();
1296 pref.smimeCertFingerprints.clear();
1297 const std::vector<GpgME::Key> &keys = items[i].keys;
1298 for (auto it = keys.begin(), end = keys.end(); it != end; ++it) {
1299 if (it->protocol() == GpgME::OpenPGP) {
1300 if (const char *fpr = it->primaryFingerprint()) {
1301 pref.pgpKeyFingerprints.push_back(QLatin1StringView(fpr));
1302 }
1303 } else if (it->protocol() == GpgME::CMS) {
1304 if (const char *fpr = it->primaryFingerprint()) {
1305 pref.smimeCertFingerprints.push_back(QLatin1StringView(fpr));
1306 }
1307 }
1308 }
1309 saveContactPreference(items[i].address, pref);
1310 }
1311 }
1312
1313 // show a warning if the user didn't select an encryption key for
1314 // herself:
1315 if (encryptToSelf() && senderKeys.empty()) {
1316 const QString msg = i18n(
1317 "You did not select an encryption key for yourself "
1318 "(encrypt to self). You will not be able to decrypt "
1319 "your own message if you encrypt it.");
1320 if (KMessageBox::warningContinueCancel(nullptr, msg, i18nc("@title:window", "Missing Key Warning"), KGuiItem(i18n("&Encrypt")))
1322 return Kleo::Canceled;
1323 } else {
1324 mEncryptToSelf = false;
1325 }
1326 }
1327
1328 // count empty key ID lists
1329 const unsigned int emptyListCount = std::count_if(items.begin(), items.end(), EmptyKeyList);
1330
1331 // show a warning if the user didn't select an encryption key for
1332 // some of the recipients
1333 if (items.size() == emptyListCount) {
1334 const QString msg = (d->mPrimaryEncryptionKeys.size() + d->mSecondaryEncryptionKeys.size() == 1)
1335 ? i18n(
1336 "You did not select an encryption key for the "
1337 "recipient of this message; therefore, the message "
1338 "will not be encrypted.")
1339 : i18n(
1340 "You did not select an encryption key for any of the "
1341 "recipients of this message; therefore, the message "
1342 "will not be encrypted.");
1343 if (KMessageBox::warningContinueCancel(nullptr, msg, i18nc("@title:window", "Missing Key Warning"), KGuiItem(i18n("Send &Unencrypted")))
1345 return Kleo::Canceled;
1346 }
1347 finalySendUnencrypted = true;
1348 } else if (emptyListCount > 0) {
1349 const QString msg = (emptyListCount == 1) ? i18n(
1350 "You did not select an encryption key for one of "
1351 "the recipients: this person will not be able to "
1352 "decrypt the message if you encrypt it.")
1353 : i18n(
1354 "You did not select encryption keys for some of "
1355 "the recipients: these persons will not be able to "
1356 "decrypt the message if you encrypt it.");
1358 if (KMessageBox::warningContinueCancel(nullptr, msg, i18nc("@title:window", "Missing Key Warning"), KGuiItem(i18n("&Encrypt")))
1360 return Kleo::Canceled;
1361 }
1362 }
1363
1364 std::transform(d->mPrimaryEncryptionKeys.begin(),
1365 d->mPrimaryEncryptionKeys.end(),
1366 items.begin(),
1367 d->mPrimaryEncryptionKeys.begin(),
1368 CopyKeysAndEncryptionPreferences);
1369 std::transform(d->mSecondaryEncryptionKeys.begin(),
1370 d->mSecondaryEncryptionKeys.end(),
1371 items.begin() + d->mPrimaryEncryptionKeys.size(),
1372 d->mSecondaryEncryptionKeys.begin(),
1373 CopyKeysAndEncryptionPreferences);
1374
1375 d->mOpenPGPEncryptToSelfKeys.clear();
1376 d->mSMIMEEncryptToSelfKeys.clear();
1377
1378 std::remove_copy_if(senderKeys.begin(),
1379 senderKeys.end(),
1380 std::back_inserter(d->mOpenPGPEncryptToSelfKeys),
1381 NotValidTrustedOpenPGPEncryptionKey); // -= trusted (see above, too)?
1382 std::remove_copy_if(senderKeys.begin(),
1383 senderKeys.end(),
1384 std::back_inserter(d->mSMIMEEncryptToSelfKeys),
1385 NotValidTrustedSMIMEEncryptionKey); // -= trusted (see above, too)?
1386
1387 return Kleo::Ok;
1388}
1389
1390std::vector<Kleo::KeyResolver::SplitInfo> Kleo::KeyResolver::encryptionItems(Kleo::CryptoMessageFormat f) const
1391{
1392 dump();
1393 auto it = d->mFormatInfoMap.find(f);
1394 return it != d->mFormatInfoMap.end() ? it->second.splitInfos : std::vector<SplitInfo>();
1395}
1396
1398{
1399 d->mAutocryptEnabled = autocryptEnabled;
1400}
1401
1402std::map<QByteArray, QString> Kleo::KeyResolver::useAutocrypt() const
1403{
1404 return d->mAutocryptMap;
1405}
1406
1407void Kleo::KeyResolver::setAkonadiLookupEnabled(bool akonadiLoopkupEnabled)
1408{
1409 d->mAkonadiLookupEnabled = akonadiLoopkupEnabled;
1410}
1411
1412std::vector<GpgME::Key> Kleo::KeyResolver::signingKeys(CryptoMessageFormat f) const
1413{
1414 dump();
1415 auto it = d->mFormatInfoMap.find(f);
1416 return it != d->mFormatInfoMap.end() ? it->second.signKeys : std::vector<GpgME::Key>();
1417}
1418
1419//
1420//
1421// KeyResolverPrivate helper methods below:
1422//
1423//
1424
1425std::vector<GpgME::Key> Kleo::KeyResolver::selectKeys(const QString &person, const QString &msg, const std::vector<GpgME::Key> &selectedKeys) const
1426{
1427 const bool opgp = containsOpenPGP(mCryptoMessageFormats);
1428 const bool x509 = containsSMIME(mCryptoMessageFormats);
1429
1430 QPointer<Kleo::KeySelectionDialog> dlg = new Kleo::KeySelectionDialog(
1431 i18n("Encryption Key Selection"),
1432 msg,
1434 selectedKeys,
1435 Kleo::KeySelectionDialog::ValidEncryptionKeys & ~(opgp ? 0 : Kleo::KeySelectionDialog::OpenPGPKeys) & ~(x509 ? 0 : Kleo::KeySelectionDialog::SMIMEKeys),
1436 true,
1437 true); // multi-selection and "remember choice" box
1438
1439 if (dlg->exec() != QDialog::Accepted) {
1440 delete dlg;
1441 return {};
1442 }
1443
1444 std::vector<GpgME::Key> keys = dlg->selectedKeys();
1445 keys.erase(std::remove_if(keys.begin(), keys.end(), NotValidEncryptionKey), keys.end());
1446 if (!keys.empty() && dlg->rememberSelection()) {
1447 setKeysForAddress(person, dlg->pgpKeyFingerprints(), dlg->smimeFingerprints());
1448 }
1449
1450 delete dlg;
1451 return keys;
1452}
1453
1454std::vector<GpgME::Key> Kleo::KeyResolver::getEncryptionKeys(const QString &person, bool quiet) const
1455{
1456 const QString address = canonicalAddress(person).toLower();
1457
1458 // First look for this person's address in the address->key dictionary
1459 const QStringList fingerprints = keysForAddress(address);
1460
1461 if (!fingerprints.empty()) {
1462 qCDebug(MESSAGECOMPOSER_LOG) << "Using encryption keys 0x" << fingerprints.join(QLatin1StringView(", 0x")) << "for" << person;
1463 std::vector<GpgME::Key> keys = lookup(fingerprints);
1464 if (!keys.empty()) {
1465 // Check if all of the keys are trusted and valid encryption keys
1466 if (std::any_of(keys.begin(), keys.end(),
1467 NotValidTrustedEncryptionKey)) { // -= trusted?
1468 // not ok, let the user select: this is not conditional on !quiet,
1469 // since it's a bug in the configuration and the user should be
1470 // notified about it as early as possible:
1471 keys = selectKeys(person,
1472 i18nc("if in your language something like "
1473 "'certificate(s)' is not possible please "
1474 "use the plural in the translation",
1475 "There is a problem with the "
1476 "encryption certificate(s) for \"%1\".\n\n"
1477 "Please re-select the certificate(s) which should "
1478 "be used for this recipient.",
1479 person),
1480 keys);
1481 }
1482 bool canceled = false;
1483 keys = trustedOrConfirmed(keys, address, canceled);
1484 if (canceled) {
1485 return {};
1486 }
1487
1488 if (!keys.empty()) {
1489 return keys;
1490 }
1491 // keys.empty() is considered cancel by callers, so go on
1492 }
1493 }
1494
1495 // Now search all public keys for matching keys
1496 std::vector<GpgME::Key> matchingKeys = lookup(QStringList(address));
1497 matchingKeys.erase(std::remove_if(matchingKeys.begin(), matchingKeys.end(), NotValidEncryptionKey), matchingKeys.end());
1498
1499 if (matchingKeys.empty() && d->mAutocryptEnabled) {
1500 qCDebug(MESSAGECOMPOSER_LOG) << "Search in Autocrypt storage a key for " << address;
1501 const auto storage = MessageCore::AutocryptStorage::self();
1502 const auto recipient = storage->getRecipient(address.toUtf8());
1503 if (recipient) {
1504 const auto key = recipient->gpgKey();
1505 if (!key.isNull() && ValidEncryptionKey(key)) {
1506 qCDebug(MESSAGECOMPOSER_LOG) << "Found an valid autocrypt key.";
1507 matchingKeys.push_back(key);
1508 } else {
1509 const auto gossipKey = recipient->gossipKey();
1510 if (!gossipKey.isNull() && ValidEncryptionKey(gossipKey)) {
1511 qCDebug(MESSAGECOMPOSER_LOG) << "Found an valid autocrypt gossip key.";
1512 matchingKeys.push_back(gossipKey);
1513 }
1514 }
1515 }
1516 // Accept any autocrypt key, as the validility is not used in Autocrypt.
1517 if (matchingKeys.size() == 1) {
1518 if (recipient->prefer_encrypt()) {
1519 d->mContactPreferencesMap[address].encryptionPreference = AlwaysEncryptIfPossible;
1520 }
1521 d->mAutocryptMap[matchingKeys[0].primaryFingerprint()] = address;
1522 return matchingKeys;
1523 }
1524 }
1525
1526 // if called with quite == true (from EncryptionPreferenceCounter), we only want to
1527 // check if there are keys for this recipients, not (yet) their validity, so
1528 // don't show the untrusted encryption key warning in that case
1529 bool canceled = false;
1530 if (!quiet) {
1531 matchingKeys = trustedOrConfirmed(matchingKeys, address, canceled);
1532 }
1533 if (canceled) {
1534 return {};
1535 }
1536 if (quiet || matchingKeys.size() == 1) {
1537 return matchingKeys;
1538 }
1539
1540 // no match until now, or more than one key matches; let the user
1541 // choose the key(s)
1542 // FIXME: let user get the key from keyserver
1543 return trustedOrConfirmed(selectKeys(person,
1544 matchingKeys.empty() ? i18nc("if in your language something like "
1545 "'certificate(s)' is not possible please "
1546 "use the plural in the translation",
1547 "<qt>No valid and trusted encryption certificate was "
1548 "found for \"%1\".<br/><br/>"
1549 "Select the certificate(s) which should "
1550 "be used for this recipient. If there is no suitable certificate in the list "
1551 "you can also search for external certificates by clicking the button: "
1552 "search for external certificates.</qt>",
1553 person.toHtmlEscaped())
1554 : i18nc("if in your language something like "
1555 "'certificate(s)' is not possible please "
1556 "use the plural in the translation",
1557 "More than one certificate matches \"%1\".\n\n"
1558 "Select the certificate(s) which should "
1559 "be used for this recipient.",
1560 person.toHtmlEscaped()),
1561 matchingKeys),
1562 address,
1563 canceled);
1564 // we can ignore 'canceled' here, since trustedOrConfirmed() returns
1565 // an empty vector when canceled == true, and we'd just do the same
1566}
1567
1568std::vector<GpgME::Key> Kleo::KeyResolver::lookup(const QStringList &patterns, bool secret) const
1569{
1570 if (patterns.empty()) {
1571 return {};
1572 }
1573 qCDebug(MESSAGECOMPOSER_LOG) << "( \"" << patterns.join(QLatin1StringView("\", \"")) << "\"," << secret << ")";
1574 std::vector<GpgME::Key> result;
1575 if (mCryptoMessageFormats & (InlineOpenPGPFormat | OpenPGPMIMEFormat)) {
1576 if (const QGpgME::Protocol *p = QGpgME::openpgp()) {
1577 std::unique_ptr<QGpgME::KeyListJob> job(p->keyListJob(false, false, true)); // use validating keylisting
1578 if (job.get()) {
1579 std::vector<GpgME::Key> keys;
1580 job->exec(patterns, secret, keys);
1581 result.insert(result.end(), keys.begin(), keys.end());
1582 }
1583 }
1584 }
1585 if (mCryptoMessageFormats & (SMIMEFormat | SMIMEOpaqueFormat)) {
1586 if (const QGpgME::Protocol *p = QGpgME::smime()) {
1587 std::unique_ptr<QGpgME::KeyListJob> job(p->keyListJob(false, false, true)); // use validating keylisting
1588 if (job.get()) {
1589 std::vector<GpgME::Key> keys;
1590 job->exec(patterns, secret, keys);
1591 result.insert(result.end(), keys.begin(), keys.end());
1592 }
1593 }
1594 }
1595 qCDebug(MESSAGECOMPOSER_LOG) << " returned" << result.size() << "keys";
1596 return result;
1597}
1598
1599void Kleo::KeyResolver::addKeys(const std::vector<Item> &items, CryptoMessageFormat f)
1600{
1601 dump();
1602 for (auto it = items.begin(); it != items.end(); ++it) {
1603 SplitInfo si(QStringList(it->address));
1604 std::remove_copy_if(it->keys.begin(), it->keys.end(), std::back_inserter(si.keys), IsNotForFormat(f));
1605 dump();
1606 if (si.keys.empty()) {
1607 qCWarning(MESSAGECOMPOSER_LOG) << "Kleo::KeyResolver::addKeys(): Fix EncryptionFormatPreferenceCounter."
1608 << "It detected a common format, but the list of such keys for recipient \"" << it->address << "\" is empty!";
1609 }
1610 d->mFormatInfoMap[f].splitInfos.push_back(si);
1611 }
1612 dump();
1613}
1614
1615void Kleo::KeyResolver::addKeys(const std::vector<Item> &items)
1616{
1617 dump();
1618 for (auto it = items.begin(); it != items.end(); ++it) {
1619 SplitInfo si(QStringList(it->address));
1620 CryptoMessageFormat f = AutoFormat;
1621 for (unsigned int i = 0; i < numConcreteCryptoMessageFormats; ++i) {
1622 const CryptoMessageFormat fmt = concreteCryptoMessageFormats[i];
1623 if ((fmt & it->format) && std::any_of(it->keys.begin(), it->keys.end(), IsForFormat(fmt))) {
1624 f = fmt;
1625 break;
1626 }
1627 }
1628 if (f == AutoFormat) {
1629 qCWarning(MESSAGECOMPOSER_LOG) << "Something went wrong. Didn't find a format for \"" << it->address << "\"";
1630 } else {
1631 std::remove_copy_if(it->keys.begin(), it->keys.end(), std::back_inserter(si.keys), IsNotForFormat(f));
1632 }
1633 d->mFormatInfoMap[f].splitInfos.push_back(si);
1634 }
1635 dump();
1636}
1637
1638MessageComposer::ContactPreference Kleo::KeyResolver::lookupContactPreferences(const QString &address) const
1639{
1640 const auto it = d->mContactPreferencesMap.find(address);
1641 if (it != d->mContactPreferencesMap.end()) {
1642 return it->second;
1643 }
1644
1645 MessageComposer::ContactPreference pref;
1646
1647 if (!d->mAkonadiLookupEnabled) {
1648 return pref;
1649 }
1650
1651 auto job = new Akonadi::ContactSearchJob();
1652 job->setLimit(1);
1653 job->setQuery(Akonadi::ContactSearchJob::Email, address);
1654 job->exec();
1655
1656 const KContacts::Addressee::List res = job->contacts();
1657 if (!res.isEmpty()) {
1658 KContacts::Addressee addr = res.at(0);
1659 pref.fillFromAddressee(addr);
1660 }
1661
1662 const_cast<KeyResolver *>(this)->setContactPreferences(address, pref);
1663
1664 return pref;
1665}
1666
1667void Kleo::KeyResolver::setContactPreferences(const QString &address, const MessageComposer::ContactPreference &pref)
1668{
1669 d->mContactPreferencesMap.insert(std::make_pair(address, pref));
1670}
1671
1672void Kleo::KeyResolver::saveContactPreference(const QString &email, const MessageComposer::ContactPreference &pref) const
1673{
1674 d->mContactPreferencesMap.insert(std::make_pair(email, pref));
1675 auto saveContactPreferencesJob = new MessageComposer::SaveContactPreferenceJob(email, pref);
1676 saveContactPreferencesJob->start();
1677}
1678
1679QStringList Kleo::KeyResolver::keysForAddress(const QString &address) const
1680{
1681 if (address.isEmpty()) {
1682 return {};
1683 }
1684 const QString addr = canonicalAddress(address).toLower();
1685 const auto pref = lookupContactPreferences(addr);
1686 return pref.pgpKeyFingerprints + pref.smimeCertFingerprints;
1687}
1688
1689void Kleo::KeyResolver::setKeysForAddress(const QString &address, const QStringList &pgpKeyFingerprints, const QStringList &smimeCertFingerprints) const
1690{
1691 if (address.isEmpty()) {
1692 return;
1693 }
1694 const QString addr = canonicalAddress(address).toLower();
1695 auto pref = lookupContactPreferences(addr);
1696 pref.pgpKeyFingerprints = pgpKeyFingerprints;
1697 pref.smimeCertFingerprints = smimeCertFingerprints;
1698 saveContactPreference(addr, pref);
1699}
1700
1701bool Kleo::KeyResolver::encryptToSelf() const
1702{
1703 return mEncryptToSelf;
1704}
1705
1706bool Kleo::KeyResolver::showApprovalDialog() const
1707{
1708 return mShowApprovalDialog;
1709}
AddresseeList List
Kleo::Result setEncryptToSelfKeys(const QStringList &fingerprints)
Set the fingerprints of keys to be used for encrypting to self.
std::vector< SplitInfo > encryptionItems(CryptoMessageFormat f) const
void setPrimaryRecipients(const QStringList &addresses)
Set the list of primary (To/CC) recipient addresses.
void setAkonadiLookupEnabled(bool akonadiLookupEnabled)
Disable ContactSearchJob in KeyResolver.
void setContactPreferences(const QString &address, const MessageComposer::ContactPreference &preference)
Sets crypto preferences for given email address.
std::vector< GpgME::Key > signingKeys(CryptoMessageFormat f) const
Kleo::Result setSigningKeys(const QStringList &fingerprints)
Set the fingerprints of keys to be used for signing.
Action checkSigningPreferences(bool signingRequested) const
Determine whether to sign or not, depending on the per-recipient signing preferences,...
Action checkEncryptionPreferences(bool encryptionRequested) const
Determine whether to encrypt or not, depending on the per-recipient encryption preferences,...
void setSecondaryRecipients(const QStringList &addresses)
Set the list of secondary (BCC) recipient addresses.
void setAutocryptEnabled(bool autocryptEnabled)
If Autocrypt keys are used to find valid PGP Keys.
Kleo::Result resolveAllKeys(bool &signingRequested, bool &encryptionRequested)
Queries the user for missing keys and displays a key approval dialog if needed.
KCODECS_EXPORT QByteArray extractEmailAddress(const QByteArray &address)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
PostalAddress address(const QVariant &location)
ButtonCode warningContinueCancel(QWidget *parent, const QString &text, const QString &title=QString(), const KGuiItem &buttonContinue=KStandardGuiItem::cont(), const KGuiItem &buttonCancel=KStandardGuiItem::cancel(), const QString &dontAskAgainName=QString(), Options options=Notify)
KGuiItem cont()
KGuiItem cancel()
const QList< QKeySequence > & end()
void append(const T &value)
QList::const_iterator constBegin() const const
QList::const_iterator constEnd() const const
bool empty() const const
void reserve(int alloc)
int size() const const
UnknownProtocol
const QChar at(int position) const const
QString fromUtf8(const char *str, int size)
bool isEmpty() const const
QString simplified() const const
QString toHtmlEscaped() const const
QString toLower() const const
QString join(const QString &separator) const const
WaitCursor
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sun Feb 25 2024 18:37:30 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.