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

KDE's Doxygen guidelines are available online.