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
635Kleo::Result Kleo::KeyResolver::setEncryptToSelfKeys(const QStringList &fingerprints)
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
724void Kleo::KeyResolver::setPrimaryRecipients(const QStringList &addresses)
725{
726 d->mPrimaryEncryptionKeys = getEncryptionItems(addresses);
727}
728
729void Kleo::KeyResolver::setSecondaryRecipients(const QStringList &addresses)
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());
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(i18nc("@action:button", "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(i18nc("@action:button", "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(i18nc("@action:button", "&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.");
1346 msg,
1347 i18nc("@title:window", "Missing Key Warning"),
1348 KGuiItem(i18nc("@action:button", "Send &Unencrypted")))
1350 return Kleo::Canceled;
1351 }
1352 finalySendUnencrypted = true;
1353 } else if (emptyListCount > 0) {
1354 const QString msg = (emptyListCount == 1) ? i18n(
1355 "You did not select an encryption key for one of "
1356 "the recipients: this person will not be able to "
1357 "decrypt the message if you encrypt it.")
1358 : i18n(
1359 "You did not select encryption keys for some of "
1360 "the recipients: these persons will not be able to "
1361 "decrypt the message if you encrypt it.");
1363 if (KMessageBox::warningContinueCancel(nullptr, msg, i18nc("@title:window", "Missing Key Warning"), KGuiItem(i18nc("@action:button", "&Encrypt")))
1365 return Kleo::Canceled;
1366 }
1367 }
1368
1369 std::transform(d->mPrimaryEncryptionKeys.begin(),
1370 d->mPrimaryEncryptionKeys.end(),
1371 items.begin(),
1372 d->mPrimaryEncryptionKeys.begin(),
1373 CopyKeysAndEncryptionPreferences);
1374 std::transform(d->mSecondaryEncryptionKeys.begin(),
1375 d->mSecondaryEncryptionKeys.end(),
1376 items.begin() + d->mPrimaryEncryptionKeys.size(),
1377 d->mSecondaryEncryptionKeys.begin(),
1378 CopyKeysAndEncryptionPreferences);
1379
1380 d->mOpenPGPEncryptToSelfKeys.clear();
1381 d->mSMIMEEncryptToSelfKeys.clear();
1382
1383 std::remove_copy_if(senderKeys.begin(),
1384 senderKeys.end(),
1385 std::back_inserter(d->mOpenPGPEncryptToSelfKeys),
1386 NotValidTrustedOpenPGPEncryptionKey); // -= trusted (see above, too)?
1387 std::remove_copy_if(senderKeys.begin(),
1388 senderKeys.end(),
1389 std::back_inserter(d->mSMIMEEncryptToSelfKeys),
1390 NotValidTrustedSMIMEEncryptionKey); // -= trusted (see above, too)?
1391
1392 return Kleo::Ok;
1393}
1394
1395std::vector<Kleo::KeyResolver::SplitInfo> Kleo::KeyResolver::encryptionItems(Kleo::CryptoMessageFormat f) const
1396{
1397 dump();
1398 auto it = d->mFormatInfoMap.find(f);
1399 return it != d->mFormatInfoMap.end() ? it->second.splitInfos : std::vector<SplitInfo>();
1400}
1401
1402void Kleo::KeyResolver::setAutocryptEnabled(bool autocryptEnabled)
1403{
1404 d->mAutocryptEnabled = autocryptEnabled;
1405}
1406
1407std::map<QByteArray, QString> Kleo::KeyResolver::useAutocrypt() const
1408{
1409 return d->mAutocryptMap;
1410}
1411
1412void Kleo::KeyResolver::setAkonadiLookupEnabled(bool akonadiLoopkupEnabled)
1413{
1414 d->mAkonadiLookupEnabled = akonadiLoopkupEnabled;
1415}
1416
1417std::vector<GpgME::Key> Kleo::KeyResolver::signingKeys(CryptoMessageFormat f) const
1418{
1419 dump();
1420 auto it = d->mFormatInfoMap.find(f);
1421 return it != d->mFormatInfoMap.end() ? it->second.signKeys : std::vector<GpgME::Key>();
1422}
1423
1424//
1425//
1426// KeyResolverPrivate helper methods below:
1427//
1428//
1429
1430std::vector<GpgME::Key> Kleo::KeyResolver::selectKeys(const QString &person, const QString &msg, const std::vector<GpgME::Key> &selectedKeys) const
1431{
1432 const bool opgp = containsOpenPGP(mCryptoMessageFormats);
1433 const bool x509 = containsSMIME(mCryptoMessageFormats);
1434
1435 QPointer<Kleo::KeySelectionDialog> dlg = new Kleo::KeySelectionDialog(
1436 i18n("Encryption Key Selection"),
1437 msg,
1439 selectedKeys,
1440 Kleo::KeySelectionDialog::ValidEncryptionKeys & ~(opgp ? 0 : Kleo::KeySelectionDialog::OpenPGPKeys) & ~(x509 ? 0 : Kleo::KeySelectionDialog::SMIMEKeys),
1441 true,
1442 true); // multi-selection and "remember choice" box
1443
1444 if (dlg->exec() != QDialog::Accepted) {
1445 delete dlg;
1446 return {};
1447 }
1448
1449 std::vector<GpgME::Key> keys = dlg->selectedKeys();
1450 keys.erase(std::remove_if(keys.begin(), keys.end(), NotValidEncryptionKey), keys.end());
1451 if (!keys.empty() && dlg->rememberSelection()) {
1452 setKeysForAddress(person, dlg->pgpKeyFingerprints(), dlg->smimeFingerprints());
1453 }
1454
1455 delete dlg;
1456 return keys;
1457}
1458
1459std::vector<GpgME::Key> Kleo::KeyResolver::getEncryptionKeys(const QString &person, bool quiet) const
1460{
1461 const QString address = canonicalAddress(person).toLower();
1462
1463 // First look for this person's address in the address->key dictionary
1464 const QStringList fingerprints = keysForAddress(address);
1465
1466 if (!fingerprints.empty()) {
1467 qCDebug(MESSAGECOMPOSER_LOG) << "Using encryption keys 0x" << fingerprints.join(QLatin1StringView(", 0x")) << "for" << person;
1468 std::vector<GpgME::Key> keys = lookup(fingerprints);
1469 if (!keys.empty()) {
1470 // Check if all of the keys are trusted and valid encryption keys
1471 if (std::any_of(keys.begin(), keys.end(),
1472 NotValidTrustedEncryptionKey)) { // -= trusted?
1473 // not ok, let the user select: this is not conditional on !quiet,
1474 // since it's a bug in the configuration and the user should be
1475 // notified about it as early as possible:
1476 keys = selectKeys(person,
1477 i18nc("if in your language something like "
1478 "'certificate(s)' is not possible please "
1479 "use the plural in the translation",
1480 "There is a problem with the "
1481 "encryption certificate(s) for \"%1\".\n\n"
1482 "Please re-select the certificate(s) which should "
1483 "be used for this recipient.",
1484 person),
1485 keys);
1486 }
1487 bool canceled = false;
1488 keys = trustedOrConfirmed(keys, address, canceled);
1489 if (canceled) {
1490 return {};
1491 }
1492
1493 if (!keys.empty()) {
1494 return keys;
1495 }
1496 // keys.empty() is considered cancel by callers, so go on
1497 }
1498 }
1499
1500 // Now search all public keys for matching keys
1501 std::vector<GpgME::Key> matchingKeys = lookup(QStringList(address));
1502 matchingKeys.erase(std::remove_if(matchingKeys.begin(), matchingKeys.end(), NotValidEncryptionKey), matchingKeys.end());
1503
1504 if (matchingKeys.empty() && d->mAutocryptEnabled) {
1505 qCDebug(MESSAGECOMPOSER_LOG) << "Search in Autocrypt storage a key for " << address;
1506 const auto storage = MessageCore::AutocryptStorage::self();
1507 const auto recipient = storage->getRecipient(address.toUtf8());
1508 if (recipient) {
1509 const auto key = recipient->gpgKey();
1510 if (!key.isNull() && ValidEncryptionKey(key)) {
1511 qCDebug(MESSAGECOMPOSER_LOG) << "Found an valid autocrypt key.";
1512 matchingKeys.push_back(key);
1513 } else {
1514 const auto gossipKey = recipient->gossipKey();
1515 if (!gossipKey.isNull() && ValidEncryptionKey(gossipKey)) {
1516 qCDebug(MESSAGECOMPOSER_LOG) << "Found an valid autocrypt gossip key.";
1517 matchingKeys.push_back(gossipKey);
1518 }
1519 }
1520 }
1521 // Accept any autocrypt key, as the validility is not used in Autocrypt.
1522 if (matchingKeys.size() == 1) {
1523 if (recipient->prefer_encrypt()) {
1524 d->mContactPreferencesMap[address].encryptionPreference = AlwaysEncryptIfPossible;
1525 }
1526 d->mAutocryptMap[matchingKeys[0].primaryFingerprint()] = address;
1527 return matchingKeys;
1528 }
1529 }
1530
1531 // if called with quite == true (from EncryptionPreferenceCounter), we only want to
1532 // check if there are keys for this recipients, not (yet) their validity, so
1533 // don't show the untrusted encryption key warning in that case
1534 bool canceled = false;
1535 if (!quiet) {
1536 matchingKeys = trustedOrConfirmed(matchingKeys, address, canceled);
1537 }
1538 if (canceled) {
1539 return {};
1540 }
1541 if (quiet || matchingKeys.size() == 1) {
1542 return matchingKeys;
1543 }
1544
1545 // no match until now, or more than one key matches; let the user
1546 // choose the key(s)
1547 // FIXME: let user get the key from keyserver
1548 return trustedOrConfirmed(selectKeys(person,
1549 matchingKeys.empty() ? i18nc("if in your language something like "
1550 "'certificate(s)' is not possible please "
1551 "use the plural in the translation",
1552 "<qt>No valid and trusted encryption certificate was "
1553 "found for \"%1\".<br/><br/>"
1554 "Select the certificate(s) which should "
1555 "be used for this recipient. If there is no suitable certificate in the list "
1556 "you can also search for external certificates by clicking the button: "
1557 "search for external certificates.</qt>",
1558 person.toHtmlEscaped())
1559 : i18nc("if in your language something like "
1560 "'certificate(s)' is not possible please "
1561 "use the plural in the translation",
1562 "More than one certificate matches \"%1\".\n\n"
1563 "Select the certificate(s) which should "
1564 "be used for this recipient.",
1565 person.toHtmlEscaped()),
1566 matchingKeys),
1567 address,
1568 canceled);
1569 // we can ignore 'canceled' here, since trustedOrConfirmed() returns
1570 // an empty vector when canceled == true, and we'd just do the same
1571}
1572
1573std::vector<GpgME::Key> Kleo::KeyResolver::lookup(const QStringList &patterns, bool secret) const
1574{
1575 if (patterns.empty()) {
1576 return {};
1577 }
1578 qCDebug(MESSAGECOMPOSER_LOG) << "( \"" << patterns.join(QLatin1StringView("\", \"")) << "\"," << secret << ")";
1579 std::vector<GpgME::Key> result;
1580 if (mCryptoMessageFormats & (InlineOpenPGPFormat | OpenPGPMIMEFormat)) {
1581 if (const QGpgME::Protocol *p = QGpgME::openpgp()) {
1582 std::unique_ptr<QGpgME::KeyListJob> job(p->keyListJob(false, false, true)); // use validating keylisting
1583 if (job.get()) {
1584 std::vector<GpgME::Key> keys;
1585 job->exec(patterns, secret, keys);
1586 result.insert(result.end(), keys.begin(), keys.end());
1587 }
1588 }
1589 }
1590 if (mCryptoMessageFormats & (SMIMEFormat | SMIMEOpaqueFormat)) {
1591 if (const QGpgME::Protocol *p = QGpgME::smime()) {
1592 std::unique_ptr<QGpgME::KeyListJob> job(p->keyListJob(false, false, true)); // use validating keylisting
1593 if (job.get()) {
1594 std::vector<GpgME::Key> keys;
1595 job->exec(patterns, secret, keys);
1596 result.insert(result.end(), keys.begin(), keys.end());
1597 }
1598 }
1599 }
1600 qCDebug(MESSAGECOMPOSER_LOG) << " returned" << result.size() << "keys";
1601 return result;
1602}
1603
1604void Kleo::KeyResolver::addKeys(const std::vector<Item> &items, CryptoMessageFormat f)
1605{
1606 dump();
1607 for (auto it = items.begin(); it != items.end(); ++it) {
1608 SplitInfo si(QStringList(it->address));
1609 std::remove_copy_if(it->keys.begin(), it->keys.end(), std::back_inserter(si.keys), IsNotForFormat(f));
1610 dump();
1611 if (si.keys.empty()) {
1612 qCWarning(MESSAGECOMPOSER_LOG) << "Kleo::KeyResolver::addKeys(): Fix EncryptionFormatPreferenceCounter."
1613 << "It detected a common format, but the list of such keys for recipient \"" << it->address << "\" is empty!";
1614 }
1615 d->mFormatInfoMap[f].splitInfos.push_back(si);
1616 }
1617 dump();
1618}
1619
1620void Kleo::KeyResolver::addKeys(const std::vector<Item> &items)
1621{
1622 dump();
1623 for (auto it = items.begin(); it != items.end(); ++it) {
1624 SplitInfo si(QStringList(it->address));
1625 CryptoMessageFormat f = AutoFormat;
1626 for (unsigned int i = 0; i < numConcreteCryptoMessageFormats; ++i) {
1627 const CryptoMessageFormat fmt = concreteCryptoMessageFormats[i];
1628 if ((fmt & it->format) && std::any_of(it->keys.begin(), it->keys.end(), IsForFormat(fmt))) {
1629 f = fmt;
1630 break;
1631 }
1632 }
1633 if (f == AutoFormat) {
1634 qCWarning(MESSAGECOMPOSER_LOG) << "Something went wrong. Didn't find a format for \"" << it->address << "\"";
1635 } else {
1636 std::remove_copy_if(it->keys.begin(), it->keys.end(), std::back_inserter(si.keys), IsNotForFormat(f));
1637 }
1638 d->mFormatInfoMap[f].splitInfos.push_back(si);
1639 }
1640 dump();
1641}
1642
1643MessageComposer::ContactPreference Kleo::KeyResolver::lookupContactPreferences(const QString &address) const
1644{
1645 const auto it = d->mContactPreferencesMap.find(address);
1646 if (it != d->mContactPreferencesMap.end()) {
1647 return it->second;
1648 }
1649
1650 MessageComposer::ContactPreference pref;
1651
1652 if (!d->mAkonadiLookupEnabled) {
1653 return pref;
1654 }
1655
1656 auto job = new Akonadi::ContactSearchJob();
1657 job->setLimit(1);
1658 job->setQuery(Akonadi::ContactSearchJob::Email, address);
1659 job->exec();
1660
1661 const KContacts::Addressee::List res = job->contacts();
1662 if (!res.isEmpty()) {
1663 KContacts::Addressee addr = res.at(0);
1664 pref.fillFromAddressee(addr);
1665 }
1666
1667 const_cast<KeyResolver *>(this)->setContactPreferences(address, pref);
1668
1669 return pref;
1670}
1671
1672void Kleo::KeyResolver::setContactPreferences(const QString &address, const MessageComposer::ContactPreference &pref)
1673{
1674 d->mContactPreferencesMap.insert(std::make_pair(address, pref));
1675}
1676
1677void Kleo::KeyResolver::saveContactPreference(const QString &email, const MessageComposer::ContactPreference &pref) const
1678{
1679 d->mContactPreferencesMap.insert(std::make_pair(email, pref));
1680 auto saveContactPreferencesJob = new MessageComposer::SaveContactPreferenceJob(email, pref);
1681 saveContactPreferencesJob->start();
1682}
1683
1684QStringList Kleo::KeyResolver::keysForAddress(const QString &address) const
1685{
1686 if (address.isEmpty()) {
1687 return {};
1688 }
1689 const QString addr = canonicalAddress(address).toLower();
1690 const auto pref = lookupContactPreferences(addr);
1691 return pref.pgpKeyFingerprints + pref.smimeCertFingerprints;
1692}
1693
1694void Kleo::KeyResolver::setKeysForAddress(const QString &address, const QStringList &pgpKeyFingerprints, const QStringList &smimeCertFingerprints) const
1695{
1696 if (address.isEmpty()) {
1697 return;
1698 }
1699 const QString addr = canonicalAddress(address).toLower();
1700 auto pref = lookupContactPreferences(addr);
1701 pref.pgpKeyFingerprints = pgpKeyFingerprints;
1702 pref.smimeCertFingerprints = smimeCertFingerprints;
1703 saveContactPreference(addr, pref);
1704}
1705
1706bool Kleo::KeyResolver::encryptToSelf() const
1707{
1708 return mEncryptToSelf;
1709}
1710
1711bool Kleo::KeyResolver::showApprovalDialog() const
1712{
1713 return mShowApprovalDialog;
1714}
AddresseeList List
KeyResolver(bool encrypt, bool sign, GpgME::Protocol protocol=GpgME::UnknownProtocol, bool allowMixed=true)
void setSigningKeys(const QStringList &fingerprints)
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...)
QAction * end(const QObject *recvr, const char *slot, QObject *parent)
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()
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 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 Mon Nov 4 2024 16:33:25 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.