File indexing completed on 2024-06-16 04:55:58

0001 /* -*- mode: c++; c-basic-offset:4 -*-
0002     crypto/sender.cpp
0003 
0004     This file is part of Kleopatra, the KDE keymanager
0005     SPDX-FileCopyrightText: 2009 Klarälvdalens Datakonsult AB
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 #include <config-kleopatra.h>
0011 
0012 #include "sender.h"
0013 
0014 #include <Libkleo/KeyCache>
0015 #include <Libkleo/Predicates>
0016 #include <Libkleo/Stl_Util>
0017 
0018 #include <utils/cached.h>
0019 #include <utils/kleo_assert.h>
0020 
0021 #include <KMime/Types>
0022 
0023 #include <gpgme++/key.h>
0024 
0025 #include <algorithm>
0026 
0027 using namespace Kleo;
0028 using namespace Kleo::Crypto;
0029 using namespace KMime::Types;
0030 using namespace GpgME;
0031 
0032 namespace KMime
0033 {
0034 namespace Types
0035 {
0036 static bool operator==(const AddrSpec &lhs, const AddrSpec &rhs)
0037 {
0038     return lhs.localPart == rhs.localPart && lhs.domain == rhs.domain;
0039 }
0040 
0041 static bool operator==(const Mailbox &lhs, const Mailbox &rhs)
0042 {
0043     return lhs.name() == rhs.name() && lhs.addrSpec() == rhs.addrSpec();
0044 }
0045 
0046 static bool determine_ambiguous(const Mailbox &mb, const std::vector<Key> &keys)
0047 {
0048     Q_UNUSED(mb)
0049     // ### really do check when we don't only show matching keys
0050     return keys.size() != 1;
0051 }
0052 } // namespace Types
0053 } // namespace KMime
0054 
0055 class Sender::Private
0056 {
0057     friend class ::Kleo::Crypto::Sender;
0058 
0059 public:
0060     explicit Private(const Mailbox &mb)
0061         : mailbox(mb)
0062     {
0063         // ### also fill up to a certain number of keys with those
0064         // ### that don't match, for the case where there's a low
0065         // ### total number of keys
0066         const QString email = mb.addrSpec().asString();
0067         const std::vector<Key> signers = KeyCache::instance()->findSigningKeysByMailbox(email);
0068         const std::vector<Key> encrypt = KeyCache::instance()->findEncryptionKeysByMailbox(email);
0069         kdtools::separate_if(signers.cbegin(), signers.cend(), std::back_inserter(pgpSigners), std::back_inserter(cmsSigners), [](const Key &key) {
0070             return key.protocol() == OpenPGP;
0071         });
0072         kdtools::separate_if(encrypt.cbegin(),
0073                              encrypt.cend(),
0074                              std::back_inserter(pgpEncryptToSelfKeys),
0075                              std::back_inserter(cmsEncryptToSelfKeys),
0076                              [](const Key &key) {
0077                                  return key.protocol() == OpenPGP;
0078                              });
0079     }
0080 
0081 private:
0082     const Mailbox mailbox;
0083     std::vector<Key> pgpSigners, cmsSigners, pgpEncryptToSelfKeys, cmsEncryptToSelfKeys;
0084     cached<bool> signingAmbiguous[2], encryptionAmbiguous[2];
0085     Key signingKey[2], cmsEncryptionKey;
0086     UserID pgpEncryptionUid;
0087 };
0088 
0089 Sender::Sender(const Mailbox &mb)
0090     : d(new Private(mb))
0091 {
0092 }
0093 
0094 void Sender::detach()
0095 {
0096     if (d && !d.unique()) {
0097         d.reset(new Private(*d));
0098     }
0099 }
0100 
0101 bool Sender::deepEquals(const Sender &other) const
0102 {
0103     static const _detail::ByFingerprint<std::equal_to> compare = {};
0104     return mailbox() == other.mailbox() //
0105         && compare(d->signingKey[CMS], other.d->signingKey[CMS]) //
0106         && compare(d->signingKey[OpenPGP], other.d->signingKey[OpenPGP]) //
0107         && compare(d->cmsEncryptionKey, other.d->cmsEncryptionKey) //
0108         && compare(d->pgpEncryptionUid.parent(), other.d->pgpEncryptionUid.parent()) && strcmp(d->pgpEncryptionUid.id(), other.d->pgpEncryptionUid.id()) == 0
0109         && std::equal(d->pgpSigners.cbegin(), d->pgpSigners.cend(), other.d->pgpSigners.cbegin(), compare)
0110         && std::equal(d->cmsSigners.cbegin(), d->cmsSigners.cend(), other.d->cmsSigners.cbegin(), compare)
0111         && std::equal(d->pgpEncryptToSelfKeys.cbegin(), d->pgpEncryptToSelfKeys.cend(), other.d->pgpEncryptToSelfKeys.cbegin(), compare)
0112         && std::equal(d->cmsEncryptToSelfKeys.cbegin(), d->cmsEncryptToSelfKeys.cend(), other.d->cmsEncryptToSelfKeys.cbegin(), compare);
0113 }
0114 
0115 bool Sender::isSigningAmbiguous(GpgME::Protocol proto) const
0116 {
0117     if (d->signingAmbiguous[proto].dirty()) {
0118         d->signingAmbiguous[proto] = determine_ambiguous(d->mailbox, signingCertificateCandidates(proto));
0119     }
0120     return d->signingAmbiguous[proto];
0121 }
0122 
0123 bool Sender::isEncryptionAmbiguous(GpgME::Protocol proto) const
0124 {
0125     if (d->encryptionAmbiguous[proto].dirty()) {
0126         d->encryptionAmbiguous[proto] = determine_ambiguous(d->mailbox, encryptToSelfCertificateCandidates(proto));
0127     }
0128     return d->encryptionAmbiguous[proto];
0129 }
0130 
0131 const Mailbox &Sender::mailbox() const
0132 {
0133     return d->mailbox;
0134 }
0135 
0136 const std::vector<Key> &Sender::signingCertificateCandidates(GpgME::Protocol proto) const
0137 {
0138     if (proto == OpenPGP) {
0139         return d->pgpSigners;
0140     }
0141     if (proto == CMS) {
0142         return d->cmsSigners;
0143     }
0144     kleo_assert_fail(proto == OpenPGP || proto == CMS);
0145 #if 0
0146     return
0147         proto == OpenPGP ? d->pgpSigners :
0148         proto == CMS     ? d->cmsSigners :
0149         // even though gcc warns about this line, it's completely ok, promise:
0150         kleo_assert_fail(proto == OpenPGP || proto == CMS);
0151 #endif
0152 }
0153 
0154 const std::vector<Key> &Sender::encryptToSelfCertificateCandidates(GpgME::Protocol proto) const
0155 {
0156     if (proto == OpenPGP) {
0157         return d->pgpEncryptToSelfKeys;
0158     }
0159     if (proto == CMS) {
0160         return d->cmsEncryptToSelfKeys;
0161     }
0162     kleo_assert_fail(proto == OpenPGP || proto == CMS);
0163 #if 0
0164     return
0165         proto == OpenPGP ? d->pgpEncryptToSelfKeys :
0166         proto == CMS     ? d->cmsEncryptToSelfKeys :
0167         // even though gcc warns about this line, it's completely ok, promise:
0168         kleo_assert_fail(proto == OpenPGP || proto == CMS);
0169 #endif
0170 }
0171 
0172 void Sender::setResolvedSigningKey(const Key &key)
0173 {
0174     if (key.isNull()) {
0175         return;
0176     }
0177     const Protocol proto = key.protocol();
0178     kleo_assert(proto == OpenPGP || proto == CMS);
0179     detach();
0180     d->signingKey[proto] = key;
0181     d->signingAmbiguous[proto] = false;
0182 }
0183 
0184 Key Sender::resolvedSigningKey(GpgME::Protocol proto) const
0185 {
0186     kleo_assert(proto == OpenPGP || proto == CMS);
0187     return d->signingKey[proto];
0188 }
0189 
0190 void Sender::setResolvedEncryptionKey(const Key &key)
0191 {
0192     if (key.isNull()) {
0193         return;
0194     }
0195     const Protocol proto = key.protocol();
0196     kleo_assert(proto == OpenPGP || proto == CMS);
0197     detach();
0198     if (proto == OpenPGP) {
0199         d->pgpEncryptionUid = key.userID(0);
0200     } else {
0201         d->cmsEncryptionKey = key;
0202     }
0203     d->encryptionAmbiguous[proto] = false;
0204 }
0205 
0206 Key Sender::resolvedEncryptionKey(GpgME::Protocol proto) const
0207 {
0208     kleo_assert(proto == OpenPGP || proto == CMS);
0209     if (proto == OpenPGP) {
0210         return d->pgpEncryptionUid.parent();
0211     } else {
0212         return d->cmsEncryptionKey;
0213     }
0214 }
0215 
0216 void Sender::setResolvedOpenPGPEncryptionUserID(const UserID &uid)
0217 {
0218     if (uid.isNull()) {
0219         return;
0220     }
0221     detach();
0222     d->pgpEncryptionUid = uid;
0223 }
0224 
0225 UserID Sender::resolvedOpenPGPEncryptionUserID() const
0226 {
0227     return d->pgpEncryptionUid;
0228 }