diff options
Diffstat (limited to 'framework')
18 files changed, 692 insertions, 479 deletions
diff --git a/framework/src/CMakeLists.txt b/framework/src/CMakeLists.txt index e975e61e..a07bdb75 100644 --- a/framework/src/CMakeLists.txt +++ b/framework/src/CMakeLists.txt | |||
@@ -4,8 +4,7 @@ find_package(KF5Mime 4.87.0 CONFIG REQUIRED) | |||
4 | find_package(KF5CalendarCore CONFIG REQUIRED) | 4 | find_package(KF5CalendarCore CONFIG REQUIRED) |
5 | find_package(Sink 0.6.0 CONFIG REQUIRED) | 5 | find_package(Sink 0.6.0 CONFIG REQUIRED) |
6 | find_package(KAsync CONFIG REQUIRED) | 6 | find_package(KAsync CONFIG REQUIRED) |
7 | find_package(QGpgme CONFIG REQUIRED) | 7 | find_package(Gpgme REQUIRED) |
8 | find_package(Gpgmepp CONFIG REQUIRED) | ||
9 | find_package(KF5Codecs CONFIG REQUIRED) | 8 | find_package(KF5Codecs CONFIG REQUIRED) |
10 | find_package(KF5Contacts CONFIG REQUIRED) | 9 | find_package(KF5Contacts CONFIG REQUIRED) |
11 | 10 | ||
@@ -73,8 +72,7 @@ target_link_libraries(kubeframework | |||
73 | KF5::Codecs | 72 | KF5::Codecs |
74 | KF5::Contacts | 73 | KF5::Contacts |
75 | KAsync | 74 | KAsync |
76 | QGpgme | 75 | gpgme |
77 | Gpgmepp | ||
78 | ) | 76 | ) |
79 | install(TARGETS kubeframework DESTINATION ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) | 77 | install(TARGETS kubeframework DESTINATION ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) |
80 | 78 | ||
diff --git a/framework/src/domain/composercontroller.cpp b/framework/src/domain/composercontroller.cpp index fc436003..88c0c839 100644 --- a/framework/src/domain/composercontroller.cpp +++ b/framework/src/domain/composercontroller.cpp | |||
@@ -36,7 +36,7 @@ | |||
36 | #include "mime/mailcrypto.h" | 36 | #include "mime/mailcrypto.h" |
37 | #include "async.h" | 37 | #include "async.h" |
38 | 38 | ||
39 | std::vector<MailCrypto::Key> &operator+=(std::vector<MailCrypto::Key> &list, const std::vector<MailCrypto::Key> &add) | 39 | std::vector<Crypto::Key> &operator+=(std::vector<Crypto::Key> &list, const std::vector<Crypto::Key> &add) |
40 | { | 40 | { |
41 | list.insert(std::end(list), std::begin(add), std::end(add)); | 41 | list.insert(std::end(list), std::begin(add), std::end(add)); |
42 | return list; | 42 | return list; |
@@ -133,11 +133,11 @@ public: | |||
133 | mb.fromUnicodeString(addressee); | 133 | mb.fromUnicodeString(addressee); |
134 | 134 | ||
135 | SinkLog() << "Searching key for: " << mb.address(); | 135 | SinkLog() << "Searching key for: " << mb.address(); |
136 | asyncRun<std::vector<MailCrypto::Key>>(this, | 136 | asyncRun<std::vector<Crypto::Key>>(this, |
137 | [mb] { | 137 | [mb] { |
138 | return MailCrypto::findKeys(QStringList{} << mb.address(), false, false); | 138 | return Crypto::findKeys(QStringList{} << mb.address(), false, false); |
139 | }, | 139 | }, |
140 | [this, addressee, id](const std::vector<MailCrypto::Key> &keys) { | 140 | [this, addressee, id](const std::vector<Crypto::Key> &keys) { |
141 | if (!keys.empty()) { | 141 | if (!keys.empty()) { |
142 | if (keys.size() > 1) { | 142 | if (keys.size() > 1) { |
143 | SinkWarning() << "Found more than one key, encrypting to all of them."; | 143 | SinkWarning() << "Found more than one key, encrypting to all of them."; |
@@ -227,10 +227,10 @@ void ComposerController::findPersonalKey() | |||
227 | { | 227 | { |
228 | auto identity = getIdentity(); | 228 | auto identity = getIdentity(); |
229 | SinkLog() << "Looking for personal key for: " << identity.address(); | 229 | SinkLog() << "Looking for personal key for: " << identity.address(); |
230 | asyncRun<std::vector<MailCrypto::Key>>(this, [=] { | 230 | asyncRun<std::vector<Crypto::Key>>(this, [=] { |
231 | return MailCrypto::findKeys(QStringList{} << identity.address(), true); | 231 | return Crypto::findKeys(QStringList{} << identity.address(), true); |
232 | }, | 232 | }, |
233 | [this](const std::vector<MailCrypto::Key> &keys) { | 233 | [this](const std::vector<Crypto::Key> &keys) { |
234 | if (keys.empty()) { | 234 | if (keys.empty()) { |
235 | SinkWarning() << "Failed to find a personal key."; | 235 | SinkWarning() << "Failed to find a personal key."; |
236 | } else if (keys.size() > 1) { | 236 | } else if (keys.size() > 1) { |
@@ -419,23 +419,23 @@ void ComposerController::recordForAutocompletion(const QByteArray &addrSpec, con | |||
419 | } | 419 | } |
420 | } | 420 | } |
421 | 421 | ||
422 | std::vector<MailCrypto::Key> ComposerController::getRecipientKeys() | 422 | std::vector<Crypto::Key> ComposerController::getRecipientKeys() |
423 | { | 423 | { |
424 | std::vector<MailCrypto::Key> keys; | 424 | std::vector<Crypto::Key> keys; |
425 | { | 425 | { |
426 | const auto list = toController()->getList<std::vector<MailCrypto::Key>>("key"); | 426 | const auto list = toController()->getList<std::vector<Crypto::Key>>("key"); |
427 | for (const auto &l: list) { | 427 | for (const auto &l: list) { |
428 | keys.insert(std::end(keys), std::begin(l), std::end(l)); | 428 | keys.insert(std::end(keys), std::begin(l), std::end(l)); |
429 | } | 429 | } |
430 | } | 430 | } |
431 | { | 431 | { |
432 | const auto list = ccController()->getList<std::vector<MailCrypto::Key>>("key"); | 432 | const auto list = ccController()->getList<std::vector<Crypto::Key>>("key"); |
433 | for (const auto &l: list) { | 433 | for (const auto &l: list) { |
434 | keys.insert(std::end(keys), std::begin(l), std::end(l)); | 434 | keys.insert(std::end(keys), std::begin(l), std::end(l)); |
435 | } | 435 | } |
436 | } | 436 | } |
437 | { | 437 | { |
438 | const auto list = bccController()->getList<std::vector<MailCrypto::Key>>("key"); | 438 | const auto list = bccController()->getList<std::vector<Crypto::Key>>("key"); |
439 | for (const auto &l: list) { | 439 | for (const auto &l: list) { |
440 | keys.insert(std::end(keys), std::begin(l), std::end(l)); | 440 | keys.insert(std::end(keys), std::begin(l), std::end(l)); |
441 | } | 441 | } |
@@ -463,17 +463,17 @@ KMime::Message::Ptr ComposerController::assembleMessage() | |||
463 | }; | 463 | }; |
464 | }); | 464 | }); |
465 | 465 | ||
466 | MailCrypto::Key attachedKey; | 466 | Crypto::Key attachedKey; |
467 | std::vector<MailCrypto::Key> signingKeys; | 467 | std::vector<Crypto::Key> signingKeys; |
468 | if (getSign()) { | 468 | if (getSign()) { |
469 | signingKeys = getPersonalKeys().value<std::vector<MailCrypto::Key>>(); | 469 | signingKeys = getPersonalKeys().value<std::vector<Crypto::Key>>(); |
470 | Q_ASSERT(!signingKeys.empty()); | 470 | Q_ASSERT(!signingKeys.empty()); |
471 | attachedKey = signingKeys[0]; | 471 | attachedKey = signingKeys[0]; |
472 | } | 472 | } |
473 | std::vector<MailCrypto::Key> encryptionKeys; | 473 | std::vector<Crypto::Key> encryptionKeys; |
474 | if (getEncrypt()) { | 474 | if (getEncrypt()) { |
475 | //Encrypt to self so we can read the sent message | 475 | //Encrypt to self so we can read the sent message |
476 | auto personalKeys = getPersonalKeys().value<std::vector<MailCrypto::Key>>(); | 476 | auto personalKeys = getPersonalKeys().value<std::vector<Crypto::Key>>(); |
477 | 477 | ||
478 | attachedKey = personalKeys[0]; | 478 | attachedKey = personalKeys[0]; |
479 | 479 | ||
diff --git a/framework/src/domain/composercontroller.h b/framework/src/domain/composercontroller.h index 8a831ed5..0d12fcce 100644 --- a/framework/src/domain/composercontroller.h +++ b/framework/src/domain/composercontroller.h | |||
@@ -65,7 +65,7 @@ class KUBE_EXPORT ComposerController : public Kube::Controller | |||
65 | KUBE_CONTROLLER_PROPERTY(KMime::Message::Ptr, ExistingMessage, existingMessage) | 65 | KUBE_CONTROLLER_PROPERTY(KMime::Message::Ptr, ExistingMessage, existingMessage) |
66 | KUBE_CONTROLLER_PROPERTY(Sink::ApplicationDomain::Mail, ExistingMail, existingMail) | 66 | KUBE_CONTROLLER_PROPERTY(Sink::ApplicationDomain::Mail, ExistingMail, existingMail) |
67 | 67 | ||
68 | KUBE_CONTROLLER_PROPERTY(/*std::vector<MailCrypto::Key>*/QVariant, PersonalKeys, personalKeys) | 68 | KUBE_CONTROLLER_PROPERTY(/*std::vector<Crypto::Key>*/QVariant, PersonalKeys, personalKeys) |
69 | KUBE_CONTROLLER_PROPERTY(bool, FoundPersonalKeys, foundPersonalKeys) | 69 | KUBE_CONTROLLER_PROPERTY(bool, FoundPersonalKeys, foundPersonalKeys) |
70 | 70 | ||
71 | KUBE_CONTROLLER_LISTCONTROLLER(to) | 71 | KUBE_CONTROLLER_LISTCONTROLLER(to) |
@@ -109,7 +109,7 @@ private: | |||
109 | void setMessage(const QSharedPointer<KMime::Message> &msg); | 109 | void setMessage(const QSharedPointer<KMime::Message> &msg); |
110 | void addAttachmentPart(KMime::Content *partToAttach); | 110 | void addAttachmentPart(KMime::Content *partToAttach); |
111 | KMime::Message::Ptr assembleMessage(); | 111 | KMime::Message::Ptr assembleMessage(); |
112 | std::vector<MailCrypto::Key> getRecipientKeys(); | 112 | std::vector<Crypto::Key> getRecipientKeys(); |
113 | 113 | ||
114 | QScopedPointer<Completer> mRecipientCompleter; | 114 | QScopedPointer<Completer> mRecipientCompleter; |
115 | QScopedPointer<Selector> mIdentitySelector; | 115 | QScopedPointer<Selector> mIdentitySelector; |
diff --git a/framework/src/domain/mime/CMakeLists.txt b/framework/src/domain/mime/CMakeLists.txt index 1e55e461..9b4da136 100644 --- a/framework/src/domain/mime/CMakeLists.txt +++ b/framework/src/domain/mime/CMakeLists.txt | |||
@@ -1,9 +1,9 @@ | |||
1 | add_library(mailcrypto STATIC | 1 | add_library(mailcrypto STATIC |
2 | mailcrypto.cpp | 2 | mailcrypto.cpp |
3 | crypto.cpp | ||
3 | ) | 4 | ) |
4 | target_link_libraries(mailcrypto | 5 | target_link_libraries(mailcrypto |
5 | Qt5::Core | 6 | Qt5::Core |
6 | KF5::Mime | 7 | KF5::Mime |
7 | QGpgme | 8 | gpgme |
8 | Gpgmepp | ||
9 | ) | 9 | ) |
diff --git a/framework/src/domain/mime/attachmentmodel.cpp b/framework/src/domain/mime/attachmentmodel.cpp index 45013f85..95a65d81 100644 --- a/framework/src/domain/mime/attachmentmodel.cpp +++ b/framework/src/domain/mime/attachmentmodel.cpp | |||
@@ -219,7 +219,7 @@ bool AttachmentModel::importPublicKey(const QModelIndex &index) | |||
219 | const auto part = static_cast<MimeTreeParser::MessagePart *>(index.internalPointer()); | 219 | const auto part = static_cast<MimeTreeParser::MessagePart *>(index.internalPointer()); |
220 | Q_ASSERT(part); | 220 | Q_ASSERT(part); |
221 | auto pkey = part->node()->decodedContent(); | 221 | auto pkey = part->node()->decodedContent(); |
222 | auto result = MailCrypto::importKey(pkey); | 222 | auto result = Crypto::importKey(pkey); |
223 | 223 | ||
224 | bool success = true; | 224 | bool success = true; |
225 | QString message; | 225 | QString message; |
diff --git a/framework/src/domain/mime/crypto.cpp b/framework/src/domain/mime/crypto.cpp new file mode 100644 index 00000000..9722dba8 --- /dev/null +++ b/framework/src/domain/mime/crypto.cpp | |||
@@ -0,0 +1,442 @@ | |||
1 | /* | ||
2 | Copyright (c) 2009 Constantin Berzan <exit3219@gmail.com> | ||
3 | Copyright (C) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com | ||
4 | Copyright (c) 2010 Leo Franchi <lfranchi@kde.org> | ||
5 | Copyright (c) 2017 Christian Mollekopf <mollekopf@kolabsys.com> | ||
6 | |||
7 | This library is free software; you can redistribute it and/or modify it | ||
8 | under the terms of the GNU Library General Public License as published by | ||
9 | the Free Software Foundation; either version 2 of the License, or (at your | ||
10 | option) any later version. | ||
11 | |||
12 | This library is distributed in the hope that it will be useful, but WITHOUT | ||
13 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
14 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public | ||
15 | License for more details. | ||
16 | |||
17 | You should have received a copy of the GNU Library General Public License | ||
18 | along with this library; see the file COPYING.LIB. If not, write to the | ||
19 | Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
20 | 02110-1301, USA. | ||
21 | */ | ||
22 | #include "crypto.h" | ||
23 | |||
24 | #include "framework/src/errors.h" | ||
25 | |||
26 | #include <gpgme.h> | ||
27 | |||
28 | #include <QDebug> | ||
29 | #include <QDateTime> | ||
30 | |||
31 | #include <future> | ||
32 | #include <utility> | ||
33 | |||
34 | using namespace Crypto; | ||
35 | |||
36 | QDebug operator<< (QDebug d, const Key &key) | ||
37 | { | ||
38 | d << key.fingerprint; | ||
39 | return d; | ||
40 | } | ||
41 | |||
42 | QDebug operator<< (QDebug d, const Error &error) | ||
43 | { | ||
44 | d << error.errorCode(); | ||
45 | return d; | ||
46 | } | ||
47 | |||
48 | struct Data { | ||
49 | Data(const QByteArray &buffer) | ||
50 | { | ||
51 | const bool copy = false; | ||
52 | const gpgme_error_t e = gpgme_data_new_from_mem(&data, buffer.constData(), buffer.size(), int(copy)); | ||
53 | if (e) { | ||
54 | qWarning() << "Failed to copy data?" << e; | ||
55 | } | ||
56 | } | ||
57 | |||
58 | ~Data() | ||
59 | { | ||
60 | gpgme_data_release(data); | ||
61 | } | ||
62 | gpgme_data_t data; | ||
63 | }; | ||
64 | |||
65 | static gpgme_error_t checkEngine(CryptoProtocol protocol) | ||
66 | { | ||
67 | gpgme_check_version(0); | ||
68 | const gpgme_protocol_t p = protocol == CMS ? GPGME_PROTOCOL_CMS : GPGME_PROTOCOL_OpenPGP; | ||
69 | return gpgme_engine_check_version(p); | ||
70 | } | ||
71 | |||
72 | static std::pair<gpgme_error_t, gpgme_ctx_t> createForProtocol(CryptoProtocol proto) | ||
73 | { | ||
74 | if (auto e = checkEngine(proto)) { | ||
75 | qWarning() << "GPG Engine check failed." << e; | ||
76 | return std::make_pair(e, nullptr); | ||
77 | } | ||
78 | gpgme_ctx_t ctx = 0; | ||
79 | if (auto e = gpgme_new(&ctx)) { | ||
80 | return std::make_pair(e, nullptr); | ||
81 | } | ||
82 | |||
83 | switch (proto) { | ||
84 | case OpenPGP: | ||
85 | if (auto e = gpgme_set_protocol(ctx, GPGME_PROTOCOL_OpenPGP)) { | ||
86 | gpgme_release(ctx); | ||
87 | return std::make_pair(e, nullptr); | ||
88 | } | ||
89 | break; | ||
90 | case CMS: | ||
91 | if (auto e = gpgme_set_protocol(ctx, GPGME_PROTOCOL_CMS)) { | ||
92 | gpgme_release(ctx); | ||
93 | return std::make_pair(e, nullptr); | ||
94 | } | ||
95 | break; | ||
96 | default: | ||
97 | Q_ASSERT(false); | ||
98 | return std::make_pair(1, nullptr); | ||
99 | } | ||
100 | return std::make_pair(GPG_ERR_NO_ERROR, ctx); | ||
101 | } | ||
102 | |||
103 | |||
104 | struct Context { | ||
105 | Context(CryptoProtocol protocol = OpenPGP) | ||
106 | { | ||
107 | gpgme_error_t code; | ||
108 | std::tie(code, context) = createForProtocol(protocol); | ||
109 | error = Error{code}; | ||
110 | } | ||
111 | |||
112 | ~Context() | ||
113 | { | ||
114 | gpgme_release(context); | ||
115 | } | ||
116 | |||
117 | operator bool() const | ||
118 | { | ||
119 | return !error; | ||
120 | } | ||
121 | Error error; | ||
122 | gpgme_ctx_t context; | ||
123 | }; | ||
124 | |||
125 | |||
126 | static QByteArray toBA(gpgme_data_t out) | ||
127 | { | ||
128 | size_t length = 0; | ||
129 | auto data = gpgme_data_release_and_get_mem (out, &length); | ||
130 | auto outdata = QByteArray{data, static_cast<int>(length)}; | ||
131 | gpgme_free(data); | ||
132 | return outdata; | ||
133 | } | ||
134 | |||
135 | static std::vector<Recipient> copyRecipients(gpgme_decrypt_result_t result) | ||
136 | { | ||
137 | std::vector<Recipient> recipients; | ||
138 | for (gpgme_recipient_t r = result->recipients ; r ; r = r->next) { | ||
139 | recipients.push_back({QByteArray{r->keyid}, {r->status}}); | ||
140 | } | ||
141 | return recipients; | ||
142 | } | ||
143 | |||
144 | static std::vector<Signature> copySignatures(gpgme_verify_result_t result) | ||
145 | { | ||
146 | std::vector<Signature> signatures; | ||
147 | for (gpgme_signature_t is = result->signatures ; is ; is = is->next) { | ||
148 | Signature sig; | ||
149 | sig.fingerprint = QByteArray{is->fpr}; | ||
150 | sig.creationTime.setTime_t(is->timestamp); | ||
151 | sig.summary = is->summary; | ||
152 | sig.status = {is->status}; | ||
153 | sig.validity = is->validity; | ||
154 | sig.validity_reason = is->validity_reason; | ||
155 | signatures.push_back(sig); | ||
156 | } | ||
157 | return signatures; | ||
158 | } | ||
159 | |||
160 | |||
161 | VerificationResult Crypto::verifyDetachedSignature(CryptoProtocol protocol, const QByteArray &signature, const QByteArray &text) | ||
162 | { | ||
163 | Context context{protocol}; | ||
164 | if (!context) { | ||
165 | qWarning() << "Failed to create context " << context.error; | ||
166 | return {{}, context.error}; | ||
167 | } | ||
168 | auto ctx = context.context; | ||
169 | |||
170 | auto err = gpgme_op_verify(ctx, Data{signature}.data, Data{text}.data, 0); | ||
171 | gpgme_verify_result_t res = gpgme_op_verify_result(ctx); | ||
172 | return {copySignatures(res), {err}}; | ||
173 | } | ||
174 | |||
175 | VerificationResult Crypto::verifyOpaqueSignature(CryptoProtocol protocol, const QByteArray &signature, QByteArray &outdata) | ||
176 | { | ||
177 | Context context{protocol}; | ||
178 | if (!context) { | ||
179 | qWarning() << "Failed to create context " << context.error; | ||
180 | return VerificationResult{{}, context.error}; | ||
181 | } | ||
182 | auto ctx = context.context; | ||
183 | |||
184 | gpgme_data_t out; | ||
185 | const gpgme_error_t e = gpgme_data_new(&out); | ||
186 | Q_ASSERT(!e); | ||
187 | auto err = gpgme_op_verify(ctx, Data{signature}.data, 0, out); | ||
188 | |||
189 | VerificationResult result{{}, {err}}; | ||
190 | if (gpgme_verify_result_t res = gpgme_op_verify_result(ctx)) { | ||
191 | result.signatures = copySignatures(res); | ||
192 | } | ||
193 | |||
194 | outdata = toBA(out); | ||
195 | return result; | ||
196 | } | ||
197 | |||
198 | std::pair<DecryptionResult,VerificationResult> Crypto::decryptAndVerify(CryptoProtocol protocol, const QByteArray &ciphertext, QByteArray &outdata) | ||
199 | { | ||
200 | Context context{protocol}; | ||
201 | if (!context) { | ||
202 | qWarning() << "Failed to create context " << context.error; | ||
203 | return std::make_pair(DecryptionResult{{}, context.error}, VerificationResult{{}, context.error}); | ||
204 | } | ||
205 | auto ctx = context.context; | ||
206 | |||
207 | gpgme_data_t out; | ||
208 | if (gpgme_error_t e = gpgme_data_new(&out)) { | ||
209 | qWarning() << "Failed to allocated data" << e; | ||
210 | } | ||
211 | auto err = gpgme_op_decrypt_verify(ctx, Data{ciphertext}.data, out); | ||
212 | if (err) { | ||
213 | qWarning() << "Failed to decrypt and verify" << Error{err}; | ||
214 | } | ||
215 | |||
216 | VerificationResult verificationResult{{}, {err}}; | ||
217 | if (gpgme_verify_result_t res = gpgme_op_verify_result(ctx)) { | ||
218 | verificationResult.signatures = copySignatures(res); | ||
219 | } | ||
220 | |||
221 | DecryptionResult decryptionResult{{}, {err}}; | ||
222 | if (gpgme_decrypt_result_t res = gpgme_op_decrypt_result(ctx)) { | ||
223 | decryptionResult.recipients = copyRecipients(res); | ||
224 | } | ||
225 | |||
226 | outdata = toBA(out); | ||
227 | return std::make_pair(decryptionResult, verificationResult); | ||
228 | } | ||
229 | |||
230 | ImportResult Crypto::importKeys(CryptoProtocol protocol, const QByteArray &certData) | ||
231 | { | ||
232 | Context context{protocol}; | ||
233 | if (!context) { | ||
234 | qWarning() << "Failed to create context " << context.error; | ||
235 | return {0, 0, 0}; | ||
236 | } | ||
237 | if (auto err = gpgme_op_import(context.context, Data{certData}.data)) { | ||
238 | qWarning() << "Import failed"; | ||
239 | return {0, 0, 0}; | ||
240 | } | ||
241 | if (auto result = gpgme_op_import_result(context.context)) { | ||
242 | return {result->considered, result->imported, result->unchanged}; | ||
243 | } else { | ||
244 | return {0, 0, 0}; | ||
245 | } | ||
246 | } | ||
247 | |||
248 | static KeyListResult listKeys(CryptoProtocol protocol, const std::vector<const char*> &patterns, bool secretOnly, int keyListMode) | ||
249 | { | ||
250 | Context context{protocol}; | ||
251 | if (!context) { | ||
252 | qWarning() << "Failed to create context " << context.error; | ||
253 | return {{}, context.error}; | ||
254 | } | ||
255 | auto ctx = context.context; | ||
256 | |||
257 | gpgme_set_keylist_mode(ctx, keyListMode); | ||
258 | |||
259 | KeyListResult result; | ||
260 | result.error = {GPG_ERR_NO_ERROR}; | ||
261 | if (patterns.size() > 1) { | ||
262 | qWarning() << "Listing multiple patterns"; | ||
263 | if (auto err = gpgme_op_keylist_ext_start(ctx, const_cast<const char **>(patterns.data()), int(secretOnly), 0)) { | ||
264 | qWarning() << "Error while listing keys"; | ||
265 | result.error = {err}; | ||
266 | } | ||
267 | } else if (patterns.size() == 1) { | ||
268 | qWarning() << "Listing one patterns " << patterns.data()[0]; | ||
269 | if (auto err = gpgme_op_keylist_start(ctx, patterns.data()[0], int(secretOnly))) { | ||
270 | qWarning() << "Error while listing keys"; | ||
271 | result.error = {err}; | ||
272 | } | ||
273 | } else { | ||
274 | qWarning() << "Listing all"; | ||
275 | if (auto err = gpgme_op_keylist_start(ctx, 0, int(secretOnly))) { | ||
276 | qWarning() << "Error while listing keys"; | ||
277 | result.error = {err}; | ||
278 | } | ||
279 | } | ||
280 | |||
281 | |||
282 | while (true) { | ||
283 | gpgme_key_t key; | ||
284 | if (auto e = gpgme_op_keylist_next(ctx, &key)) { | ||
285 | break; | ||
286 | } | ||
287 | Key k; | ||
288 | if (key->subkeys) { | ||
289 | k.keyId = QByteArray{key->subkeys->keyid}; | ||
290 | k.shortKeyId = k.keyId.right(8); | ||
291 | k.fingerprint = QByteArray{key->subkeys->fpr}; | ||
292 | } | ||
293 | for (gpgme_user_id_t uid = key->uids ; uid ; uid = uid->next) { | ||
294 | k.userIds.push_back(UserId{QByteArray{uid->name}, QByteArray{uid->email}, QByteArray{uid->uid}}); | ||
295 | } | ||
296 | k.isExpired = key->expired; | ||
297 | result.keys.push_back(k); | ||
298 | } | ||
299 | gpgme_op_keylist_end(ctx); | ||
300 | return result; | ||
301 | } | ||
302 | |||
303 | /** | ||
304 | * Get the given `key` in the armor format. | ||
305 | */ | ||
306 | Expected<Error, QByteArray> Crypto::exportPublicKey(const Key &key) | ||
307 | { | ||
308 | Context context; | ||
309 | if (!context) { | ||
310 | return makeUnexpected(Error{context.error}); | ||
311 | } | ||
312 | |||
313 | gpgme_data_t out; | ||
314 | const gpgme_error_t e = gpgme_data_new(&out); | ||
315 | Q_ASSERT(!e); | ||
316 | |||
317 | qDebug() << "Exporting public key:" << key.keyId; | ||
318 | if (auto err = gpgme_op_export(context.context, key.keyId, 0, out)) { | ||
319 | return makeUnexpected(Error{err}); | ||
320 | } | ||
321 | |||
322 | return toBA(out); | ||
323 | } | ||
324 | |||
325 | Expected<Error, QByteArray> Crypto::signAndEncrypt(const QByteArray &content, const std::vector<Key> &encryptionKeys, const std::vector<Key> &signingKeys) | ||
326 | { | ||
327 | Context context; | ||
328 | if (!context) { | ||
329 | return makeUnexpected(Error{context.error}); | ||
330 | } | ||
331 | |||
332 | for (const auto &signingKey : signingKeys) { | ||
333 | //TODO do we have to free those again? | ||
334 | gpgme_key_t key; | ||
335 | if (auto e = gpgme_get_key(context.context, signingKey.fingerprint, &key, /*secret*/ false)) { | ||
336 | qWarning() << "Failed to retrive signing key " << signingKey.fingerprint << e; | ||
337 | } else { | ||
338 | gpgme_signers_add(context.context, key); | ||
339 | } | ||
340 | } | ||
341 | |||
342 | gpgme_key_t * const keys = new gpgme_key_t[encryptionKeys.size() + 1]; | ||
343 | gpgme_key_t * keys_it = keys; | ||
344 | for (const auto &k : encryptionKeys) { | ||
345 | gpgme_key_t key; | ||
346 | if (auto e = gpgme_get_key(context.context, k.fingerprint, &key, /*secret*/ false)) { | ||
347 | qWarning() << "Failed to retrive key " << k.fingerprint << e; | ||
348 | } else { | ||
349 | *keys_it++ = key; | ||
350 | } | ||
351 | } | ||
352 | *keys_it++ = 0; | ||
353 | |||
354 | gpgme_data_t out; | ||
355 | const gpgme_error_t e = gpgme_data_new(&out); | ||
356 | Q_ASSERT(!e); | ||
357 | |||
358 | gpgme_error_t err = !signingKeys.empty() ? | ||
359 | gpgme_op_encrypt_sign(context.context, keys, GPGME_ENCRYPT_ALWAYS_TRUST, Data{content}.data, out) : | ||
360 | gpgme_op_encrypt(context.context, keys, GPGME_ENCRYPT_ALWAYS_TRUST, Data{content}.data, out); | ||
361 | delete[] keys; | ||
362 | if (err) { | ||
363 | qWarning() << "Encryption failed:" << Error{err}; | ||
364 | return makeUnexpected(Error{err}); | ||
365 | } | ||
366 | |||
367 | return toBA(out); | ||
368 | ; | ||
369 | } | ||
370 | |||
371 | Expected<Error, std::pair<QByteArray, QString>> | ||
372 | Crypto::sign(const QByteArray &content, const std::vector<Key> &signingKeys) | ||
373 | { | ||
374 | Context context; | ||
375 | if (!context) { | ||
376 | return makeUnexpected(Error{context.error}); | ||
377 | } | ||
378 | |||
379 | for (const auto &signingKey : signingKeys) { | ||
380 | //TODO do we have to free those again? | ||
381 | gpgme_key_t key; | ||
382 | if (auto e = gpgme_get_key(context.context, signingKey.fingerprint, &key, /*secret*/ false)) { | ||
383 | qWarning() << "Failed to retrive signing key " << signingKey.fingerprint << e; | ||
384 | } else { | ||
385 | gpgme_signers_add(context.context, key); | ||
386 | } | ||
387 | } | ||
388 | |||
389 | gpgme_data_t out; | ||
390 | const gpgme_error_t e = gpgme_data_new(&out); | ||
391 | Q_ASSERT(!e); | ||
392 | |||
393 | if (auto err = gpgme_op_sign(context.context, Data{content}.data, out, GPGME_SIG_MODE_DETACH)) { | ||
394 | qWarning() << "Signing failed:" << Error{err}; | ||
395 | return makeUnexpected(Error{err}); | ||
396 | } | ||
397 | |||
398 | |||
399 | const QByteArray algo = [&] { | ||
400 | if (gpgme_sign_result_t res = gpgme_op_sign_result(context.context)) { | ||
401 | for (gpgme_new_signature_t is = res->signatures ; is ; is = is->next) { | ||
402 | return QByteArray{gpgme_hash_algo_name(is->hash_algo)}; | ||
403 | } | ||
404 | } | ||
405 | return QByteArray{}; | ||
406 | }(); | ||
407 | // RFC 3156 Section 5: | ||
408 | // Hash-symbols are constructed [...] by converting the text name to lower | ||
409 | // case and prefixing it with the four characters "pgp-". | ||
410 | const auto micAlg = (QString("pgp-") + algo).toLower(); | ||
411 | |||
412 | return std::pair<QByteArray, QString>{toBA(out), micAlg}; | ||
413 | } | ||
414 | |||
415 | ImportResult Crypto::importKey(const QByteArray &pkey) | ||
416 | { | ||
417 | return importKeys(OpenPGP, pkey); | ||
418 | } | ||
419 | |||
420 | std::vector<Key> Crypto::findKeys(const QStringList &patterns, bool findPrivate, bool remote) | ||
421 | { | ||
422 | QByteArrayList list; | ||
423 | std::transform(patterns.constBegin(), patterns.constEnd(), std::back_inserter(list), [] (const QString &s) { return s.toUtf8(); }); | ||
424 | std::vector<char const *> pattern; | ||
425 | std::transform(list.constBegin(), list.constEnd(), std::back_inserter(pattern), [] (const QByteArray &s) { return s.constData(); }); | ||
426 | pattern.push_back(0); | ||
427 | |||
428 | const KeyListResult res = listKeys(OpenPGP, pattern, findPrivate, remote ? GPGME_KEYLIST_MODE_EXTERN : GPGME_KEYLIST_MODE_LOCAL); | ||
429 | if (res.error) { | ||
430 | qWarning() << "Failed to lookup keys: " << res.error; | ||
431 | return {}; | ||
432 | } | ||
433 | qDebug() << "got keys:" << res.keys.size(); | ||
434 | for (const auto &key : res.keys) { | ||
435 | qDebug() << "isexpired:" << key.isExpired; | ||
436 | for (const auto &userId : key.userIds) { | ||
437 | qDebug() << "userID:" << userId.email; | ||
438 | } | ||
439 | } | ||
440 | return res.keys; | ||
441 | } | ||
442 | |||
diff --git a/framework/src/domain/mime/crypto.h b/framework/src/domain/mime/crypto.h new file mode 100644 index 00000000..fa79785a --- /dev/null +++ b/framework/src/domain/mime/crypto.h | |||
@@ -0,0 +1,123 @@ | |||
1 | /* | ||
2 | Copyright (c) 2016 Christian Mollekopf <mollekopf@kolabsys.com> | ||
3 | |||
4 | This library is free software; you can redistribute it and/or modify it | ||
5 | under the terms of the GNU Library General Public License as published by | ||
6 | the Free Software Foundation; either version 2 of the License, or (at your | ||
7 | option) any later version. | ||
8 | |||
9 | This library is distributed in the hope that it will be useful, but WITHOUT | ||
10 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public | ||
12 | License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU Library General Public License | ||
15 | along with this library; see the file COPYING.LIB. If not, write to the | ||
16 | Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
17 | 02110-1301, USA. | ||
18 | */ | ||
19 | |||
20 | #pragma once | ||
21 | |||
22 | #include "framework/src/errors.h" | ||
23 | |||
24 | #include <QByteArray> | ||
25 | #include <QVariant> | ||
26 | |||
27 | #include <functional> | ||
28 | #include <memory> | ||
29 | #include <gpgme.h> | ||
30 | #include <QDateTime> | ||
31 | |||
32 | namespace Crypto { | ||
33 | |||
34 | enum CryptoProtocol { | ||
35 | UnknownProtocol, | ||
36 | OpenPGP, | ||
37 | CMS | ||
38 | }; | ||
39 | |||
40 | |||
41 | struct UserId { | ||
42 | QByteArray name; | ||
43 | QByteArray email; | ||
44 | QByteArray id; | ||
45 | }; | ||
46 | |||
47 | struct Key { | ||
48 | QByteArray keyId; | ||
49 | QByteArray shortKeyId; | ||
50 | QByteArray fingerprint; | ||
51 | bool isExpired = false; | ||
52 | std::vector<UserId> userIds; | ||
53 | }; | ||
54 | |||
55 | struct Error { | ||
56 | gpgme_error_t error; | ||
57 | gpgme_err_code_t errorCode() const { | ||
58 | return gpgme_err_code(error); | ||
59 | } | ||
60 | operator bool() const | ||
61 | { | ||
62 | return error != GPG_ERR_NO_ERROR; | ||
63 | } | ||
64 | }; | ||
65 | |||
66 | struct Signature { | ||
67 | QByteArray fingerprint; | ||
68 | gpgme_sigsum_t summary; | ||
69 | Error status; | ||
70 | gpgme_validity_t validity; | ||
71 | gpgme_error_t validity_reason; | ||
72 | QDateTime creationTime; | ||
73 | }; | ||
74 | |||
75 | struct VerificationResult { | ||
76 | std::vector<Signature> signatures; | ||
77 | Error error; | ||
78 | }; | ||
79 | |||
80 | struct Recipient { | ||
81 | QByteArray keyId; | ||
82 | Error status; | ||
83 | }; | ||
84 | |||
85 | struct DecryptionResult { | ||
86 | std::vector<Recipient> recipients; | ||
87 | Error error; | ||
88 | }; | ||
89 | |||
90 | struct KeyListResult { | ||
91 | std::vector<Key> keys; | ||
92 | Error error; | ||
93 | }; | ||
94 | |||
95 | |||
96 | std::vector<Key> findKeys(const QStringList &filter, bool findPrivate = false, bool remote = false); | ||
97 | |||
98 | Expected<Error, QByteArray> exportPublicKey(const Key &key); | ||
99 | struct ImportResult { | ||
100 | int considered; | ||
101 | int imported; | ||
102 | int unchanged; | ||
103 | }; | ||
104 | ImportResult importKeys(CryptoProtocol protocol, const QByteArray &certData); | ||
105 | ImportResult importKey(const QByteArray &key); | ||
106 | |||
107 | /** | ||
108 | * Sign the given content and returns the signing data and the algorithm used | ||
109 | * for integrity check in the "pgp-<algorithm>" format. | ||
110 | */ | ||
111 | Expected<Error, std::pair<QByteArray, QString>> | ||
112 | sign(const QByteArray &content, const std::vector<Key> &signingKeys); | ||
113 | Expected<Error, QByteArray> signAndEncrypt(const QByteArray &content, const std::vector<Key> &encryptionKeys, const std::vector<Key> &signingKeys); | ||
114 | |||
115 | std::pair<DecryptionResult,VerificationResult> decryptAndVerify(CryptoProtocol protocol, const QByteArray &ciphertext, QByteArray &outdata); | ||
116 | VerificationResult verifyDetachedSignature(CryptoProtocol protocol, const QByteArray &signature, const QByteArray &outdata); | ||
117 | VerificationResult verifyOpaqueSignature(CryptoProtocol protocol, const QByteArray &signature, QByteArray &outdata); | ||
118 | }; | ||
119 | |||
120 | Q_DECLARE_METATYPE(Crypto::Key); | ||
121 | |||
122 | QDebug operator<< (QDebug d, const Crypto::Key &); | ||
123 | QDebug operator<< (QDebug d, const Crypto::Error &); | ||
diff --git a/framework/src/domain/mime/mailcrypto.cpp b/framework/src/domain/mime/mailcrypto.cpp index a7f3772a..e429a212 100644 --- a/framework/src/domain/mime/mailcrypto.cpp +++ b/framework/src/domain/mime/mailcrypto.cpp | |||
@@ -22,22 +22,7 @@ | |||
22 | #include "mailcrypto.h" | 22 | #include "mailcrypto.h" |
23 | 23 | ||
24 | #include "framework/src/errors.h" | 24 | #include "framework/src/errors.h" |
25 | 25 | #include "crypto.h" | |
26 | #include <QGpgME/DataProvider> | ||
27 | #include <QGpgME/EncryptJob> | ||
28 | #include <QGpgME/ExportJob> | ||
29 | #include <QGpgME/ImportFromKeyserverJob> | ||
30 | #include <QGpgME/ImportJob> | ||
31 | #include <QGpgME/Protocol> | ||
32 | #include <QGpgME/SignEncryptJob> | ||
33 | #include <QGpgME/SignJob> | ||
34 | |||
35 | #include <gpgme++/data.h> | ||
36 | #include <gpgme++/encryptionresult.h> | ||
37 | #include <gpgme++/global.h> | ||
38 | #include <gpgme++/importresult.h> | ||
39 | #include <gpgme++/keylistresult.h> | ||
40 | #include <gpgme++/signingresult.h> | ||
41 | 26 | ||
42 | #include <QDebug> | 27 | #include <QDebug> |
43 | 28 | ||
@@ -45,22 +30,8 @@ | |||
45 | #include <utility> | 30 | #include <utility> |
46 | 31 | ||
47 | using namespace MailCrypto; | 32 | using namespace MailCrypto; |
33 | using namespace Crypto; | ||
48 | 34 | ||
49 | QDebug operator<< (QDebug d, const MailCrypto::Key &key) | ||
50 | { | ||
51 | d << key.key.primaryFingerprint(); | ||
52 | return d; | ||
53 | } | ||
54 | |||
55 | static std::vector<GpgME::Key> toGpgME(const std::vector<Key> keys) | ||
56 | { | ||
57 | std::vector<GpgME::Key> list; | ||
58 | std::transform(keys.begin(), keys.end(), std::back_inserter(list), [] (const Key &key) { return key.key; }); | ||
59 | return list; | ||
60 | } | ||
61 | |||
62 | // replace simple LFs by CRLFs for all MIME supporting CryptPlugs | ||
63 | // according to RfC 2633, 3.1.1 Canonicalization | ||
64 | static QByteArray canonicalizeContent(KMime::Content *content) | 35 | static QByteArray canonicalizeContent(KMime::Content *content) |
65 | { | 36 | { |
66 | // if (d->format & Kleo::InlineOpenPGPFormat) { | 37 | // if (d->format & Kleo::InlineOpenPGPFormat) { |
@@ -125,29 +96,6 @@ static QByteArray canonicalizeContent(KMime::Content *content) | |||
125 | } | 96 | } |
126 | 97 | ||
127 | /** | 98 | /** |
128 | * Get the given `key` in the armor format. | ||
129 | */ | ||
130 | Expected<Error, QByteArray> exportPublicKey(const Key &key) | ||
131 | { | ||
132 | // Not using the Qt API because it apparently blocks (the `result` signal is never | ||
133 | // triggered) | ||
134 | std::unique_ptr<GpgME::Context> ctx(GpgME::Context::createForProtocol(GpgME::OpenPGP)); | ||
135 | ctx->setArmor(true); | ||
136 | |||
137 | QGpgME::QByteArrayDataProvider dp; | ||
138 | GpgME::Data data(&dp); | ||
139 | |||
140 | qDebug() << "Exporting public key:" << key.key.shortKeyID(); | ||
141 | auto error = ctx->exportPublicKeys(key.key.keyID(), data); | ||
142 | |||
143 | if (error.code()) { | ||
144 | return makeUnexpected(Error{error}); | ||
145 | } | ||
146 | |||
147 | return dp.data(); | ||
148 | } | ||
149 | |||
150 | /** | ||
151 | * Create an Email with `msg` as a body and `key` as an attachment. | 99 | * Create an Email with `msg` as a body and `key` as an attachment. |
152 | * | 100 | * |
153 | * Will create the given structure: | 101 | * Will create the given structure: |
@@ -178,7 +126,7 @@ appendPublicKey(std::unique_ptr<KMime::Content> msg, const Key &key) | |||
178 | { | 126 | { |
179 | keyAttachment->contentType()->setMimeType("application/pgp-keys"); | 127 | keyAttachment->contentType()->setMimeType("application/pgp-keys"); |
180 | keyAttachment->contentDisposition()->setDisposition(KMime::Headers::CDattachment); | 128 | keyAttachment->contentDisposition()->setDisposition(KMime::Headers::CDattachment); |
181 | keyAttachment->contentDisposition()->setFilename(QString("0x") + key.key.shortKeyID() + ".asc"); | 129 | keyAttachment->contentDisposition()->setFilename(QString("0x") + key.shortKeyId + ".asc"); |
182 | keyAttachment->setBody(publicKeyData); | 130 | keyAttachment->setBody(publicKeyData); |
183 | } | 131 | } |
184 | 132 | ||
@@ -192,44 +140,6 @@ appendPublicKey(std::unique_ptr<KMime::Content> msg, const Key &key) | |||
192 | return result; | 140 | return result; |
193 | } | 141 | } |
194 | 142 | ||
195 | Expected<Error, QByteArray> encrypt(const QByteArray &content, const std::vector<Key> &encryptionKeys) | ||
196 | { | ||
197 | QByteArray resultData; | ||
198 | |||
199 | const QGpgME::Protocol *const proto = QGpgME::openpgp(); | ||
200 | std::unique_ptr<QGpgME::EncryptJob> job(proto->encryptJob(/* armor = */ true)); | ||
201 | const auto result = job->exec(toGpgME(encryptionKeys), content, /* alwaysTrust = */ true, resultData); | ||
202 | |||
203 | if (result.error().code()) { | ||
204 | qWarning() << "Encryption failed:" << result.error().asString(); | ||
205 | return makeUnexpected(Error{result.error()}); | ||
206 | } | ||
207 | |||
208 | return resultData; | ||
209 | } | ||
210 | |||
211 | Expected<Error, QByteArray> signAndEncrypt(const QByteArray &content, | ||
212 | const std::vector<Key> &signingKeys, const std::vector<Key> &encryptionKeys) | ||
213 | { | ||
214 | QByteArray resultData; | ||
215 | |||
216 | const QGpgME::Protocol *const proto = QGpgME::openpgp(); | ||
217 | std::unique_ptr<QGpgME::SignEncryptJob> job(proto->signEncryptJob(/* armor = */ true)); | ||
218 | const auto result = job->exec(toGpgME(signingKeys), toGpgME(encryptionKeys), content, /* alwaysTrust = */ true, resultData); | ||
219 | |||
220 | if (result.first.error().code()) { | ||
221 | qWarning() << "Signing failed:" << result.first.error().asString(); | ||
222 | return makeUnexpected(Error{result.first.error()}); | ||
223 | } | ||
224 | |||
225 | if (result.second.error().code()) { | ||
226 | qWarning() << "Encryption failed:" << result.second.error().asString(); | ||
227 | return makeUnexpected(Error{result.second.error()}); | ||
228 | } | ||
229 | |||
230 | return resultData; | ||
231 | } | ||
232 | |||
233 | /** | 143 | /** |
234 | * Create a message part like this (according to RFC 3156 Section 4): | 144 | * Create a message part like this (according to RFC 3156 Section 4): |
235 | * | 145 | * |
@@ -295,11 +205,7 @@ Expected<Error, std::unique_ptr<KMime::Content>> | |||
295 | createEncryptedEmail(KMime::Content *content, const std::vector<Key> &encryptionKeys, | 205 | createEncryptedEmail(KMime::Content *content, const std::vector<Key> &encryptionKeys, |
296 | const Key &attachedKey, const std::vector<Key> &signingKeys = {}) | 206 | const Key &attachedKey, const std::vector<Key> &signingKeys = {}) |
297 | { | 207 | { |
298 | auto contentToEncrypt = canonicalizeContent(content); | 208 | auto encryptionResult = signAndEncrypt(canonicalizeContent(content), encryptionKeys, signingKeys); |
299 | |||
300 | auto encryptionResult = signingKeys.empty() ? | ||
301 | encrypt(contentToEncrypt, encryptionKeys) : | ||
302 | signAndEncrypt(contentToEncrypt, signingKeys, encryptionKeys); | ||
303 | 209 | ||
304 | if (!encryptionResult) { | 210 | if (!encryptionResult) { |
305 | return makeUnexpected(Error{encryptionResult.error()}); | 211 | return makeUnexpected(Error{encryptionResult.error()}); |
@@ -317,33 +223,6 @@ createEncryptedEmail(KMime::Content *content, const std::vector<Key> &encryption | |||
317 | } | 223 | } |
318 | 224 | ||
319 | /** | 225 | /** |
320 | * Sign the given content and returns the signing data and the algorithm used | ||
321 | * for integrity check in the "pgp-<algorithm>" format. | ||
322 | */ | ||
323 | Expected<Error, std::pair<QByteArray, QString>> | ||
324 | sign(const QByteArray &content, const std::vector<Key> &signingKeys) | ||
325 | { | ||
326 | QByteArray resultData; | ||
327 | |||
328 | const QGpgME::Protocol *const proto = QGpgME::openpgp(); | ||
329 | std::unique_ptr<QGpgME::SignJob> job(proto->signJob(/* armor = */ true)); | ||
330 | const auto result = job->exec(toGpgME(signingKeys), content, GpgME::Detached, resultData); | ||
331 | |||
332 | if (result.error().code()) { | ||
333 | qWarning() << "Signing failed:" << result.error().asString(); | ||
334 | return makeUnexpected(Error{result.error()}); | ||
335 | } | ||
336 | |||
337 | auto algo = result.createdSignature(0).hashAlgorithmAsString(); | ||
338 | // RFC 3156 Section 5: | ||
339 | // Hash-symbols are constructed [...] by converting the text name to lower | ||
340 | // case and prefixing it with the four characters "pgp-". | ||
341 | auto micAlg = (QString("pgp-") + algo).toLower(); | ||
342 | |||
343 | return std::pair<QByteArray, QString>{resultData, micAlg}; | ||
344 | } | ||
345 | |||
346 | /** | ||
347 | * Create a message part like this (according to RFC 3156 Section 5): | 226 | * Create a message part like this (according to RFC 3156 Section 5): |
348 | * | 227 | * |
349 | * + `multipart/signed` | 228 | * + `multipart/signed` |
@@ -438,66 +317,3 @@ MailCrypto::processCrypto(std::unique_ptr<KMime::Content> content, const std::ve | |||
438 | } | 317 | } |
439 | } | 318 | } |
440 | 319 | ||
441 | void MailCrypto::importKeys(const std::vector<Key> &keys) | ||
442 | { | ||
443 | const QGpgME::Protocol *const backend = QGpgME::openpgp(); | ||
444 | Q_ASSERT(backend); | ||
445 | auto *job = backend->importFromKeyserverJob(); | ||
446 | job->exec(toGpgME(keys)); | ||
447 | } | ||
448 | |||
449 | MailCrypto::ImportResult MailCrypto::importKey(const QByteArray &pkey) | ||
450 | { | ||
451 | const auto *proto = QGpgME::openpgp(); | ||
452 | std::unique_ptr<QGpgME::ImportJob> job(proto->importJob()); | ||
453 | auto result = job->exec(pkey); | ||
454 | return {result.numConsidered(), result.numImported(), result.numUnchanged()}; | ||
455 | } | ||
456 | |||
457 | static GpgME::KeyListResult listKeys(const QStringList &patterns, bool secretOnly, int keyListMode, std::vector<Key> &keys) | ||
458 | { | ||
459 | QByteArrayList list; | ||
460 | std::transform(patterns.constBegin(), patterns.constEnd(), std::back_inserter(list), [] (const QString &s) { return s.toUtf8(); }); | ||
461 | std::vector<char const *> pattern; | ||
462 | std::transform(list.constBegin(), list.constEnd(), std::back_inserter(pattern), [] (const QByteArray &s) { return s.constData(); }); | ||
463 | pattern.push_back(0); | ||
464 | |||
465 | GpgME::initializeLibrary(); | ||
466 | auto ctx = QSharedPointer<GpgME::Context>{GpgME::Context::createForProtocol(GpgME::OpenPGP)}; | ||
467 | ctx->setKeyListMode(keyListMode); | ||
468 | if (const GpgME::Error err = ctx->startKeyListing(pattern.data(), secretOnly)) { | ||
469 | return GpgME::KeyListResult(0, err); | ||
470 | } | ||
471 | |||
472 | GpgME::Error err; | ||
473 | do { | ||
474 | keys.push_back( Key{ctx->nextKey(err)}); | ||
475 | } while ( !err ); | ||
476 | |||
477 | keys.pop_back(); | ||
478 | |||
479 | const GpgME::KeyListResult result = ctx->endKeyListing(); | ||
480 | ctx->cancelPendingOperation(); | ||
481 | return result; | ||
482 | } | ||
483 | |||
484 | std::vector<Key> MailCrypto::findKeys(const QStringList &filter, bool findPrivate, bool remote) | ||
485 | { | ||
486 | std::vector<Key> keys; | ||
487 | GpgME::KeyListResult res = listKeys(filter, findPrivate, remote ? GpgME::Extern : GpgME::Local, keys); | ||
488 | if (res.error()) { | ||
489 | qWarning() << "Failed to lookup keys: " << res.error().asString(); | ||
490 | return {}; | ||
491 | } | ||
492 | qWarning() << "got keys:" << keys.size(); | ||
493 | |||
494 | for (auto i = keys.begin(); i != keys.end(); ++i) { | ||
495 | qWarning() << "key isnull:" << i->key.isNull() << "isexpired:" << i->key.isExpired(); | ||
496 | qWarning() << "key numuserIds:" << i->key.numUserIDs(); | ||
497 | for (uint k = 0; k < i->key.numUserIDs(); ++k) { | ||
498 | qWarning() << "userIDs:" << i->key.userID(k).email(); | ||
499 | } | ||
500 | } | ||
501 | |||
502 | return keys; | ||
503 | } | ||
diff --git a/framework/src/domain/mime/mailcrypto.h b/framework/src/domain/mime/mailcrypto.h index c9247859..6f19063d 100644 --- a/framework/src/domain/mime/mailcrypto.h +++ b/framework/src/domain/mime/mailcrypto.h | |||
@@ -22,42 +22,19 @@ | |||
22 | #include "framework/src/errors.h" | 22 | #include "framework/src/errors.h" |
23 | 23 | ||
24 | #include <KMime/Message> | 24 | #include <KMime/Message> |
25 | #include <gpgme++/key.h> | ||
26 | 25 | ||
27 | #include <QByteArray> | 26 | #include <QByteArray> |
28 | #include <QVariant> | 27 | #include <QVariant> |
29 | 28 | ||
30 | #include <functional> | 29 | #include <functional> |
31 | #include <memory> | 30 | #include <memory> |
31 | #include "crypto.h" | ||
32 | 32 | ||
33 | namespace MailCrypto { | 33 | namespace MailCrypto { |
34 | 34 | ||
35 | struct Key { | 35 | Expected<Crypto::Error, std::unique_ptr<KMime::Content>> |
36 | GpgME::Key key; | 36 | processCrypto(std::unique_ptr<KMime::Content> content, const std::vector<Crypto::Key> &signingKeys, |
37 | }; | 37 | const std::vector<Crypto::Key> &encryptionKeys, const Crypto::Key &attachedKey); |
38 | |||
39 | struct Error { | ||
40 | GpgME::Error key; | ||
41 | }; | ||
42 | |||
43 | Expected<Error, std::unique_ptr<KMime::Content>> | ||
44 | processCrypto(std::unique_ptr<KMime::Content> content, const std::vector<Key> &signingKeys, | ||
45 | const std::vector<Key> &encryptionKeys, const Key &attachedKey); | ||
46 | |||
47 | std::vector<Key> findKeys(const QStringList &filter, bool findPrivate = false, bool remote = false); | ||
48 | |||
49 | void importKeys(const std::vector<Key> &keys); | ||
50 | |||
51 | struct ImportResult { | ||
52 | int considered; | ||
53 | int imported; | ||
54 | int unchanged; | ||
55 | }; | ||
56 | |||
57 | ImportResult importKey(const QByteArray &key); | ||
58 | 38 | ||
59 | }; // namespace MailCrypto | 39 | }; // namespace MailCrypto |
60 | 40 | ||
61 | Q_DECLARE_METATYPE(MailCrypto::Key); | ||
62 | |||
63 | QDebug operator<< (QDebug d, const MailCrypto::Key &); | ||
diff --git a/framework/src/domain/mime/mailtemplates.cpp b/framework/src/domain/mime/mailtemplates.cpp index ac2b2b72..c7ae481e 100644 --- a/framework/src/domain/mime/mailtemplates.cpp +++ b/framework/src/domain/mime/mailtemplates.cpp | |||
@@ -1027,8 +1027,8 @@ static KMime::Types::Mailbox::List stringListToMailboxes(const QStringList &list | |||
1027 | KMime::Message::Ptr MailTemplates::createMessage(KMime::Message::Ptr existingMessage, | 1027 | KMime::Message::Ptr MailTemplates::createMessage(KMime::Message::Ptr existingMessage, |
1028 | const QStringList &to, const QStringList &cc, const QStringList &bcc, | 1028 | const QStringList &to, const QStringList &cc, const QStringList &bcc, |
1029 | const KMime::Types::Mailbox &from, const QString &subject, const QString &body, bool htmlBody, | 1029 | const KMime::Types::Mailbox &from, const QString &subject, const QString &body, bool htmlBody, |
1030 | const QList<Attachment> &attachments, const std::vector<MailCrypto::Key> &signingKeys, | 1030 | const QList<Attachment> &attachments, const std::vector<Crypto::Key> &signingKeys, |
1031 | const std::vector<MailCrypto::Key> &encryptionKeys, const MailCrypto::Key &attachedKey) | 1031 | const std::vector<Crypto::Key> &encryptionKeys, const Crypto::Key &attachedKey) |
1032 | { | 1032 | { |
1033 | auto mail = existingMessage; | 1033 | auto mail = existingMessage; |
1034 | if (!mail) { | 1034 | if (!mail) { |
diff --git a/framework/src/domain/mime/mailtemplates.h b/framework/src/domain/mime/mailtemplates.h index a894d120..0188120b 100644 --- a/framework/src/domain/mime/mailtemplates.h +++ b/framework/src/domain/mime/mailtemplates.h | |||
@@ -38,5 +38,5 @@ namespace MailTemplates | |||
38 | void forward(const KMime::Message::Ptr &origMsg, const std::function<void(const KMime::Message::Ptr &result)> &callback); | 38 | void forward(const KMime::Message::Ptr &origMsg, const std::function<void(const KMime::Message::Ptr &result)> &callback); |
39 | QString plaintextContent(const KMime::Message::Ptr &origMsg); | 39 | QString plaintextContent(const KMime::Message::Ptr &origMsg); |
40 | QString body(const KMime::Message::Ptr &msg, bool &isHtml); | 40 | QString body(const KMime::Message::Ptr &msg, bool &isHtml); |
41 | KMime::Message::Ptr createMessage(KMime::Message::Ptr existingMessage, const QStringList &to, const QStringList &cc, const QStringList &bcc, const KMime::Types::Mailbox &from, const QString &subject, const QString &body, bool htmlBody, const QList<Attachment> &attachments, const std::vector<MailCrypto::Key> &signingKeys = {}, const std::vector<MailCrypto::Key> &encryptionKeys = {}, const MailCrypto::Key &attachedKey = {}); | 41 | KMime::Message::Ptr createMessage(KMime::Message::Ptr existingMessage, const QStringList &to, const QStringList &cc, const QStringList &bcc, const KMime::Types::Mailbox &from, const QString &subject, const QString &body, bool htmlBody, const QList<Attachment> &attachments, const std::vector<Crypto::Key> &signingKeys = {}, const std::vector<Crypto::Key> &encryptionKeys = {}, const Crypto::Key &attachedKey = {}); |
42 | }; | 42 | }; |
diff --git a/framework/src/domain/mime/mimetreeparser/CMakeLists.txt b/framework/src/domain/mime/mimetreeparser/CMakeLists.txt index b35bd958..c3f317ee 100644 --- a/framework/src/domain/mime/mimetreeparser/CMakeLists.txt +++ b/framework/src/domain/mime/mimetreeparser/CMakeLists.txt | |||
@@ -2,9 +2,8 @@ set(CMAKE_CXX_VISIBILITY_PRESET default) | |||
2 | 2 | ||
3 | find_package(Qt5 COMPONENTS REQUIRED Core Gui) | 3 | find_package(Qt5 COMPONENTS REQUIRED Core Gui) |
4 | find_package(KF5Mime 4.87.0 CONFIG REQUIRED) | 4 | find_package(KF5Mime 4.87.0 CONFIG REQUIRED) |
5 | find_package(QGpgme CONFIG REQUIRED) | ||
6 | find_package(Gpgmepp CONFIG REQUIRED) | ||
7 | find_package(KF5Codecs CONFIG REQUIRED) | 5 | find_package(KF5Codecs CONFIG REQUIRED) |
6 | find_package(Gpgme REQUIRED) | ||
8 | 7 | ||
9 | # target_include_directories does not handle empty include paths | 8 | # target_include_directories does not handle empty include paths |
10 | include_directories(${GPGME_INCLUDES}) | 9 | include_directories(${GPGME_INCLUDES}) |
@@ -49,8 +48,8 @@ target_link_libraries(kube_otp | |||
49 | 48 | ||
50 | target_link_libraries(kube_otp | 49 | target_link_libraries(kube_otp |
51 | PRIVATE | 50 | PRIVATE |
52 | QGpgme | 51 | gpgme |
53 | Gpgmepp | 52 | mailcrypto |
54 | KF5::Codecs | 53 | KF5::Codecs |
55 | Qt5::Gui | 54 | Qt5::Gui |
56 | ) | 55 | ) |
diff --git a/framework/src/domain/mime/mimetreeparser/autotests/CMakeLists.txt b/framework/src/domain/mime/mimetreeparser/autotests/CMakeLists.txt index f0b1f5f5..e67884e0 100644 --- a/framework/src/domain/mime/mimetreeparser/autotests/CMakeLists.txt +++ b/framework/src/domain/mime/mimetreeparser/autotests/CMakeLists.txt | |||
@@ -15,7 +15,7 @@ macro(add_mimetreeparser_unittest _source) | |||
15 | ecm_add_test(${_source} util.cpp setupenv.cpp | 15 | ecm_add_test(${_source} util.cpp setupenv.cpp |
16 | TEST_NAME ${_name} | 16 | TEST_NAME ${_name} |
17 | NAME_PREFIX "mimetreeparser-" | 17 | NAME_PREFIX "mimetreeparser-" |
18 | LINK_LIBRARIES kube_otp Qt5::Test KF5::Mime Gpgmepp | 18 | LINK_LIBRARIES kube_otp Qt5::Test KF5::Mime gpgme |
19 | ) | 19 | ) |
20 | endmacro () | 20 | endmacro () |
21 | 21 | ||
@@ -24,7 +24,7 @@ macro(add_mimetreeparser_class_unittest _source _additionalSource) | |||
24 | ecm_add_test(${_source} ${_additionalSource} | 24 | ecm_add_test(${_source} ${_additionalSource} |
25 | TEST_NAME ${_name} | 25 | TEST_NAME ${_name} |
26 | NAME_PREFIX "mimetreeparser-" | 26 | NAME_PREFIX "mimetreeparser-" |
27 | LINK_LIBRARIES kube_otp Qt5::Test KF5::Mime Gpgmepp | 27 | LINK_LIBRARIES kube_otp Qt5::Test KF5::Mime gpgme |
28 | ) | 28 | ) |
29 | endmacro () | 29 | endmacro () |
30 | 30 | ||
@@ -37,7 +37,7 @@ macro(add_mimetreeparser_crypto_unittest _source) | |||
37 | kube_otp | 37 | kube_otp |
38 | Qt5::Test | 38 | Qt5::Test |
39 | KF5::Mime | 39 | KF5::Mime |
40 | Gpgmepp | 40 | gpgme |
41 | ) | 41 | ) |
42 | add_gpg_crypto_test(${_name} mimetreeparser-${_name}) | 42 | add_gpg_crypto_test(${_name} mimetreeparser-${_name}) |
43 | endmacro () | 43 | endmacro () |
diff --git a/framework/src/domain/mime/mimetreeparser/messagepart.cpp b/framework/src/domain/mime/mimetreeparser/messagepart.cpp index 2fd4f496..4bed80da 100644 --- a/framework/src/domain/mime/mimetreeparser/messagepart.cpp +++ b/framework/src/domain/mime/mimetreeparser/messagepart.cpp | |||
@@ -27,102 +27,12 @@ | |||
27 | 27 | ||
28 | #include <KMime/Content> | 28 | #include <KMime/Content> |
29 | 29 | ||
30 | #include <QGpgME/DN> | 30 | #include <crypto.h> |
31 | #include <QGpgME/DataProvider> | ||
32 | |||
33 | #include <gpgme++/context.h> | ||
34 | #include <gpgme++/data.h> | ||
35 | #include <gpgme++/verificationresult.h> | ||
36 | #include <gpgme++/key.h> | ||
37 | #include <gpgme++/keylistresult.h> | ||
38 | #include <gpgme++/decryptionresult.h> | ||
39 | #include <gpgme++/importresult.h> | ||
40 | #include <gpgme.h> | ||
41 | |||
42 | 31 | ||
43 | #include <QTextCodec> | 32 | #include <QTextCodec> |
44 | #include <sstream> | ||
45 | 33 | ||
46 | using namespace MimeTreeParser; | 34 | using namespace MimeTreeParser; |
47 | 35 | using namespace Crypto; | |
48 | static GpgME::Data fromBA(const QByteArray &ba) | ||
49 | { | ||
50 | return {ba.data(), static_cast<size_t>(ba.size()), false}; | ||
51 | } | ||
52 | |||
53 | |||
54 | static GpgME::Protocol toGpgMe(CryptoProtocol p) | ||
55 | { | ||
56 | switch (p) { | ||
57 | case UnknownProtocol: | ||
58 | return GpgME::UnknownProtocol; | ||
59 | case CMS: | ||
60 | return GpgME::CMS; | ||
61 | case OpenPGP: | ||
62 | return GpgME::OpenPGP; | ||
63 | } | ||
64 | return GpgME::UnknownProtocol; | ||
65 | } | ||
66 | |||
67 | static QSharedPointer<GpgME::Context> gpgContext(CryptoProtocol protocol) | ||
68 | { | ||
69 | GpgME::initializeLibrary(); | ||
70 | auto error = GpgME::checkEngine(toGpgMe(protocol)); | ||
71 | if (error) { | ||
72 | qWarning() << "Engine check failed: " << error.asString(); | ||
73 | } | ||
74 | auto ctx = QSharedPointer<GpgME::Context>(GpgME::Context::createForProtocol(toGpgMe(protocol))); | ||
75 | Q_ASSERT(ctx); | ||
76 | return ctx; | ||
77 | } | ||
78 | |||
79 | static GpgME::VerificationResult verifyDetachedSignature(CryptoProtocol protocol, const QByteArray &signature, const QByteArray &text) | ||
80 | { | ||
81 | return gpgContext(protocol)->verifyDetachedSignature(fromBA(signature), fromBA(text)); | ||
82 | } | ||
83 | |||
84 | static GpgME::VerificationResult verifyOpaqueSignature(CryptoProtocol protocol, const QByteArray &signature, QByteArray &outdata) | ||
85 | { | ||
86 | QGpgME::QByteArrayDataProvider out; | ||
87 | GpgME::Data wrapper(&out); | ||
88 | const auto result = gpgContext(protocol)->verifyOpaqueSignature(fromBA(signature), wrapper); | ||
89 | outdata = out.data(); | ||
90 | return result; | ||
91 | } | ||
92 | |||
93 | |||
94 | static std::pair<GpgME::DecryptionResult,GpgME::VerificationResult> decryptAndVerify(CryptoProtocol protocol, const QByteArray &ciphertext, QByteArray &outdata) | ||
95 | { | ||
96 | QGpgME::QByteArrayDataProvider out; | ||
97 | GpgME::Data wrapper(&out); | ||
98 | const std::pair<GpgME::DecryptionResult,GpgME::VerificationResult> res = gpgContext(protocol)->decryptAndVerify(fromBA(ciphertext), wrapper); | ||
99 | outdata = out.data(); | ||
100 | return res; | ||
101 | } | ||
102 | |||
103 | static void importKeys(CryptoProtocol protocol, const QByteArray &certData) | ||
104 | { | ||
105 | gpgContext(protocol)->importKeys(fromBA(certData)); | ||
106 | } | ||
107 | |||
108 | static GpgME::KeyListResult listKeys(CryptoProtocol protocol, const char *pattern, bool secretOnly, std::vector<GpgME::Key> &keys) { | ||
109 | auto ctx = gpgContext(protocol); | ||
110 | if (const GpgME::Error err = ctx->startKeyListing(pattern, secretOnly)) { | ||
111 | return GpgME::KeyListResult( 0, err ); | ||
112 | } | ||
113 | |||
114 | GpgME::Error err; | ||
115 | do { | ||
116 | keys.push_back( ctx->nextKey(err)); | ||
117 | } while ( !err ); | ||
118 | |||
119 | keys.pop_back(); | ||
120 | |||
121 | const GpgME::KeyListResult result = ctx->endKeyListing(); | ||
122 | ctx->cancelPendingOperation(); | ||
123 | return result; | ||
124 | } | ||
125 | |||
126 | 36 | ||
127 | //------MessagePart----------------------- | 37 | //------MessagePart----------------------- |
128 | MessagePart::MessagePart(ObjectTreeParser *otp, const QString &text, KMime::Content *node) | 38 | MessagePart::MessagePart(ObjectTreeParser *otp, const QString &text, KMime::Content *node) |
@@ -771,10 +681,7 @@ SignedMessagePart::SignedMessagePart(ObjectTreeParser *otp, | |||
771 | { | 681 | { |
772 | mMetaData.isSigned = true; | 682 | mMetaData.isSigned = true; |
773 | mMetaData.isGoodSignature = false; | 683 | mMetaData.isGoodSignature = false; |
774 | //FIXME | ||
775 | // mMetaData.keyTrust = GpgME::Signature::Unknown; | ||
776 | mMetaData.status = tr("Wrong Crypto Plug-In."); | 684 | mMetaData.status = tr("Wrong Crypto Plug-In."); |
777 | mMetaData.status_code = GPGME_SIG_STAT_NONE; | ||
778 | } | 685 | } |
779 | 686 | ||
780 | SignedMessagePart::~SignedMessagePart() | 687 | SignedMessagePart::~SignedMessagePart() |
@@ -792,99 +699,65 @@ bool SignedMessagePart::isSigned() const | |||
792 | return mMetaData.isSigned; | 699 | return mMetaData.isSigned; |
793 | } | 700 | } |
794 | 701 | ||
795 | static int signatureToStatus(const GpgME::Signature &sig) | ||
796 | { | ||
797 | switch (sig.status().code()) { | ||
798 | case GPG_ERR_NO_ERROR: | ||
799 | return GPGME_SIG_STAT_GOOD; | ||
800 | case GPG_ERR_BAD_SIGNATURE: | ||
801 | return GPGME_SIG_STAT_BAD; | ||
802 | case GPG_ERR_NO_PUBKEY: | ||
803 | return GPGME_SIG_STAT_NOKEY; | ||
804 | case GPG_ERR_NO_DATA: | ||
805 | return GPGME_SIG_STAT_NOSIG; | ||
806 | case GPG_ERR_SIG_EXPIRED: | ||
807 | return GPGME_SIG_STAT_GOOD_EXP; | ||
808 | case GPG_ERR_KEY_EXPIRED: | ||
809 | return GPGME_SIG_STAT_GOOD_EXPKEY; | ||
810 | default: | ||
811 | return GPGME_SIG_STAT_ERROR; | ||
812 | } | ||
813 | } | ||
814 | 702 | ||
815 | QString prettifyDN(const char *uid) | 703 | static QString prettifyDN(const char *uid) |
816 | { | 704 | { |
817 | return QGpgME::DN(uid).prettyDN(); | 705 | // We used to use QGpgME::DN::prettyDN here. But I'm not sure what we actually need it for. |
706 | return QString::fromUtf8(uid); | ||
818 | } | 707 | } |
819 | 708 | ||
820 | void SignedMessagePart::sigStatusToMetaData(const GpgME::Signature &signature) | 709 | void SignedMessagePart::sigStatusToMetaData(const Signature &signature) |
821 | { | 710 | { |
822 | GpgME::Key key; | 711 | mMetaData.isGoodSignature = signature.status & GPG_ERR_NO_ERROR; |
823 | mMetaData.status_code = signatureToStatus(signature); | ||
824 | mMetaData.isGoodSignature = mMetaData.status_code & GPGME_SIG_STAT_GOOD; | ||
825 | // save extended signature status flags | 712 | // save extended signature status flags |
826 | auto summary = signature.summary(); | 713 | auto summary = signature.summary; |
827 | mMetaData.keyMissing = summary & GpgME::Signature::KeyMissing; | 714 | mMetaData.keyMissing = summary & GPGME_SIGSUM_KEY_MISSING; |
828 | mMetaData.keyExpired = summary & GpgME::Signature::KeyExpired; | 715 | mMetaData.keyExpired = summary & GPGME_SIGSUM_KEY_EXPIRED; |
829 | mMetaData.keyRevoked = summary & GpgME::Signature::KeyRevoked; | 716 | mMetaData.keyRevoked = summary & GPGME_SIGSUM_KEY_REVOKED; |
830 | mMetaData.sigExpired = summary & GpgME::Signature::SigExpired; | 717 | mMetaData.sigExpired = summary & GPGME_SIGSUM_SIG_EXPIRED; |
831 | mMetaData.crlMissing = summary & GpgME::Signature::CrlMissing; | 718 | mMetaData.crlMissing = summary & GPGME_SIGSUM_CRL_MISSING; |
832 | mMetaData.crlTooOld = summary & GpgME::Signature::CrlTooOld; | 719 | mMetaData.crlTooOld = summary & GPGME_SIGSUM_CRL_TOO_OLD; |
833 | 720 | ||
834 | if (mMetaData.isGoodSignature && !key.keyID()) { | 721 | Key key; |
722 | if (mMetaData.isGoodSignature) { | ||
835 | // Search for the key by its fingerprint so that we can check for trust etc. | 723 | // Search for the key by its fingerprint so that we can check for trust etc. |
836 | std::vector<GpgME::Key> found_keys; | 724 | const auto keys = findKeys({signature.fingerprint}); |
837 | auto res = listKeys(mProtocol, signature.fingerprint(), false, found_keys); | 725 | if (keys.size() > 1) { |
838 | if (res.error()) { | ||
839 | qCDebug(MIMETREEPARSER_LOG) << "Error while searching key for Fingerprint: " << signature.fingerprint(); | ||
840 | } | ||
841 | if (found_keys.size() > 1) { | ||
842 | // Should not happen | 726 | // Should not happen |
843 | qCDebug(MIMETREEPARSER_LOG) << "Oops: Found more then one Key for Fingerprint: " << signature.fingerprint(); | 727 | qCDebug(MIMETREEPARSER_LOG) << "Oops: Found more then one Key for Fingerprint: " << signature.fingerprint; |
844 | } | 728 | } |
845 | if (found_keys.empty()) { | 729 | if (keys.empty()) { |
846 | // Should not happen at this point | 730 | // Should not happen at this point |
847 | qCWarning(MIMETREEPARSER_LOG) << "Oops: Found no Key for Fingerprint: " << signature.fingerprint(); | 731 | qCWarning(MIMETREEPARSER_LOG) << "Oops: Found no Key for Fingerprint: " << signature.fingerprint; |
848 | } else { | 732 | } else { |
849 | key = found_keys[0]; | 733 | key = keys[0]; |
850 | } | 734 | } |
851 | } | 735 | } |
852 | 736 | ||
853 | if (key.keyID()) { | 737 | mMetaData.keyId = key.keyId; |
854 | mMetaData.keyId = key.keyID(); | ||
855 | } | ||
856 | if (mMetaData.keyId.isEmpty()) { | 738 | if (mMetaData.keyId.isEmpty()) { |
857 | mMetaData.keyId = signature.fingerprint(); | 739 | mMetaData.keyId = signature.fingerprint; |
858 | } | 740 | } |
859 | auto keyTrust = signature.validity(); | 741 | mMetaData.keyIsTrusted = signature.validity == GPGME_VALIDITY_FULL || signature.validity == GPGME_VALIDITY_ULTIMATE; |
860 | mMetaData.keyIsTrusted = keyTrust & GpgME::Signature::Full || keyTrust & GpgME::Signature::Ultimate; | 742 | if (!key.userIds.empty()) { |
861 | if (key.numUserIDs() > 0 && key.userID(0).id()) { | 743 | mMetaData.signer = prettifyDN(key.userIds[0].id); |
862 | mMetaData.signer = prettifyDN(key.userID(0).id()); | 744 | } |
863 | } | 745 | for (const auto &userId : key.userIds) { |
864 | for (uint iMail = 0; iMail < key.numUserIDs(); ++iMail) { | 746 | QString email = QString::fromUtf8(userId.email); |
865 | // The following if /should/ always result in TRUE but we | 747 | // ### work around gpgme 0.3.QString text() const Q_DECL_OVERRIDE;x / cryptplug bug where the |
866 | // won't trust implicitely the plugin that gave us these data. | 748 | // ### email addresses are specified as angle-addr, not addr-spec: |
867 | if (key.userID(iMail).email()) { | 749 | if (email.startsWith(QLatin1Char('<')) && email.endsWith(QLatin1Char('>'))) { |
868 | QString email = QString::fromUtf8(key.userID(iMail).email()); | 750 | email = email.mid(1, email.length() - 2); |
869 | // ### work around gpgme 0.3.QString text() const Q_DECL_OVERRIDE;x / cryptplug bug where the | 751 | } |
870 | // ### email addresses are specified as angle-addr, not addr-spec: | 752 | if (!email.isEmpty()) { |
871 | if (email.startsWith(QLatin1Char('<')) && email.endsWith(QLatin1Char('>'))) { | 753 | mMetaData.signerMailAddresses.append(email); |
872 | email = email.mid(1, email.length() - 2); | ||
873 | } | ||
874 | if (!email.isEmpty()) { | ||
875 | mMetaData.signerMailAddresses.append(email); | ||
876 | } | ||
877 | } | 754 | } |
878 | } | 755 | } |
879 | 756 | ||
880 | if (signature.creationTime()) { | 757 | mMetaData.creationTime = signature.creationTime; |
881 | mMetaData.creationTime.setTime_t(signature.creationTime()); | ||
882 | } else { | ||
883 | mMetaData.creationTime = QDateTime(); | ||
884 | } | ||
885 | if (mMetaData.signer.isEmpty()) { | 758 | if (mMetaData.signer.isEmpty()) { |
886 | if (key.numUserIDs() > 0 && key.userID(0).name()) { | 759 | if (!key.userIds.empty()) { |
887 | mMetaData.signer = prettifyDN(key.userID(0).name()); | 760 | mMetaData.signer = prettifyDN(key.userIds[0].name); |
888 | } | 761 | } |
889 | if (!mMetaData.signerMailAddresses.empty()) { | 762 | if (!mMetaData.signerMailAddresses.empty()) { |
890 | if (mMetaData.signer.isEmpty()) { | 763 | if (mMetaData.signer.isEmpty()) { |
@@ -924,18 +797,13 @@ void SignedMessagePart::startVerificationDetached(const QByteArray &text, KMime: | |||
924 | } | 797 | } |
925 | 798 | ||
926 | mMetaData.isSigned = false; | 799 | mMetaData.isSigned = false; |
927 | //FIXME | ||
928 | // mMetaData.keyTrust = GpgME::Signature::Unknown; | ||
929 | mMetaData.status = tr("Wrong Crypto Plug-In."); | 800 | mMetaData.status = tr("Wrong Crypto Plug-In."); |
930 | mMetaData.status_code = GPGME_SIG_STAT_NONE; | ||
931 | 801 | ||
932 | if (!signature.isEmpty()) { | 802 | if (!signature.isEmpty()) { |
933 | auto result = verifyDetachedSignature(mProtocol, signature, text); | 803 | setVerificationResult(verifyDetachedSignature(mProtocol, signature, text), false, text); |
934 | setVerificationResult(result, false, text); | ||
935 | } else { | 804 | } else { |
936 | QByteArray outdata; | 805 | QByteArray outdata; |
937 | auto result = verifyOpaqueSignature(mProtocol, text, outdata); | 806 | setVerificationResult(verifyOpaqueSignature(mProtocol, text, outdata), false, outdata); |
938 | setVerificationResult(result, false, outdata); | ||
939 | } | 807 | } |
940 | 808 | ||
941 | if (!mMetaData.isSigned) { | 809 | if (!mMetaData.isSigned) { |
@@ -943,11 +811,11 @@ void SignedMessagePart::startVerificationDetached(const QByteArray &text, KMime: | |||
943 | } | 811 | } |
944 | } | 812 | } |
945 | 813 | ||
946 | void SignedMessagePart::setVerificationResult(const GpgME::VerificationResult &result, bool parseText, const QByteArray &plainText) | 814 | void SignedMessagePart::setVerificationResult(const VerificationResult &result, bool parseText, const QByteArray &plainText) |
947 | { | 815 | { |
948 | auto signatures = result.signatures(); | 816 | auto signatures = result.signatures; |
949 | // FIXME | 817 | // FIXME |
950 | // mMetaData.auditLogError = result.error(); | 818 | // mMetaData.auditLogError = result.error; |
951 | if (!signatures.empty()) { | 819 | if (!signatures.empty()) { |
952 | mMetaData.isSigned = true; | 820 | mMetaData.isSigned = true; |
953 | sigStatusToMetaData(signatures.front()); | 821 | sigStatusToMetaData(signatures.front()); |
@@ -994,10 +862,7 @@ EncryptedMessagePart::EncryptedMessagePart(ObjectTreeParser *otp, | |||
994 | mMetaData.isGoodSignature = false; | 862 | mMetaData.isGoodSignature = false; |
995 | mMetaData.isEncrypted = false; | 863 | mMetaData.isEncrypted = false; |
996 | mMetaData.isDecryptable = false; | 864 | mMetaData.isDecryptable = false; |
997 | //FIXME | ||
998 | // mMetaData.keyTrust = GpgME::Signature::Unknown; | ||
999 | mMetaData.status = tr("Wrong Crypto Plug-In."); | 865 | mMetaData.status = tr("Wrong Crypto Plug-In."); |
1000 | mMetaData.status_code = GPGME_SIG_STAT_NONE; | ||
1001 | } | 866 | } |
1002 | 867 | ||
1003 | EncryptedMessagePart::~EncryptedMessagePart() | 868 | EncryptedMessagePart::~EncryptedMessagePart() |
@@ -1055,59 +920,61 @@ bool EncryptedMessagePart::okDecryptMIME(KMime::Content &data) | |||
1055 | 920 | ||
1056 | const QByteArray ciphertext = data.decodedContent(); | 921 | const QByteArray ciphertext = data.decodedContent(); |
1057 | QByteArray plainText; | 922 | QByteArray plainText; |
1058 | const auto res = decryptAndVerify(mProtocol, ciphertext, plainText); | 923 | DecryptionResult decryptResult; |
1059 | const GpgME::DecryptionResult &decryptResult = res.first; | 924 | VerificationResult verifyResult; |
1060 | const GpgME::VerificationResult &verifyResult = res.second; | 925 | std::tie(decryptResult, verifyResult) = decryptAndVerify(mProtocol, ciphertext, plainText); |
1061 | mMetaData.isSigned = verifyResult.signatures().size() > 0; | 926 | mMetaData.isSigned = verifyResult.signatures.size() > 0; |
1062 | 927 | ||
1063 | if (verifyResult.signatures().size() > 0) { | 928 | if (verifyResult.signatures.size() > 0) { |
1064 | //We simply attach a signed message part to indicate that this content is also signed | 929 | //We simply attach a signed message part to indicate that this content is also signed |
1065 | auto subPart = SignedMessagePart::Ptr(new SignedMessagePart(mOtp, QString::fromUtf8(plainText), mProtocol, mFromAddress, nullptr, nullptr)); | 930 | auto subPart = SignedMessagePart::Ptr(new SignedMessagePart(mOtp, QString::fromUtf8(plainText), mProtocol, mFromAddress, nullptr, nullptr)); |
1066 | subPart->setVerificationResult(verifyResult, true, plainText); | 931 | subPart->setVerificationResult(verifyResult, true, plainText); |
1067 | appendSubPart(subPart); | 932 | appendSubPart(subPart); |
1068 | } | 933 | } |
1069 | 934 | ||
1070 | if (decryptResult.error() && mMetaData.isSigned) { | 935 | if (decryptResult.error && mMetaData.isSigned) { |
1071 | //Only a signed part | 936 | //Only a signed part |
1072 | mMetaData.isEncrypted = false; | 937 | mMetaData.isEncrypted = false; |
1073 | mDecryptedData = plainText; | 938 | mDecryptedData = plainText; |
1074 | return true; | 939 | return true; |
1075 | } | 940 | } |
1076 | 941 | ||
1077 | if (mMetaData.isEncrypted && decryptResult.numRecipients() > 0) { | 942 | if (mMetaData.isEncrypted && decryptResult.recipients.size()) { |
1078 | mMetaData.keyId = decryptResult.recipient(0).keyID(); | 943 | mMetaData.keyId = decryptResult.recipients.at(0).keyId; |
1079 | } | 944 | } |
1080 | 945 | ||
1081 | if (!decryptResult.error()) { | 946 | if (!decryptResult.error) { |
1082 | mDecryptedData = plainText; | 947 | mDecryptedData = plainText; |
1083 | setText(QString::fromUtf8(mDecryptedData.constData())); | 948 | setText(QString::fromUtf8(mDecryptedData.constData())); |
1084 | } else { | 949 | } else { |
1085 | mMetaData.isEncrypted = decryptResult.error().code() != GPG_ERR_NO_DATA; | 950 | const auto errorCode = decryptResult.error.errorCode(); |
1086 | mMetaData.errorText = QString::fromLocal8Bit(decryptResult.error().asString()); | 951 | mMetaData.isEncrypted = errorCode != GPG_ERR_NO_DATA; |
1087 | 952 | qWarning() << "Failed to decrypt : " << decryptResult.error; | |
1088 | std::stringstream ss; | 953 | |
1089 | ss << decryptResult << '\n' << verifyResult; | 954 | const bool noSecretKeyAvilable = [&] { |
1090 | qWarning() << "Decryption failed: " << ss.str().c_str(); | 955 | foreach (const auto &recipient, decryptResult.recipients) { |
1091 | 956 | if (!(recipient.status.errorCode() == GPG_ERR_NO_SECKEY)) { | |
1092 | bool passphraseError = decryptResult.error().isCanceled() || decryptResult.error().code() == GPG_ERR_NO_SECKEY; | 957 | return false; |
1093 | 958 | } | |
1094 | auto noSecKey = true; | 959 | } |
1095 | foreach (const GpgME::DecryptionResult::Recipient &recipient, decryptResult.recipients()) { | 960 | return true; |
1096 | noSecKey &= (recipient.status().code() == GPG_ERR_NO_SECKEY); | 961 | }(); |
1097 | } | 962 | bool passphraseError = errorCode == GPG_ERR_CANCELED || errorCode == GPG_ERR_NO_SECKEY; |
1098 | if (!passphraseError && !noSecKey) { // GpgME do not detect passphrase error correctly | 963 | //We only get a decryption failed error when we enter the wrong passphrase.... |
964 | if (!passphraseError && !noSecretKeyAvilable) { | ||
1099 | passphraseError = true; | 965 | passphraseError = true; |
1100 | } | 966 | } |
1101 | 967 | ||
1102 | if(noSecKey) { | 968 | if(noSecretKeyAvilable) { |
1103 | mError = NoKeyError; | 969 | mError = NoKeyError; |
1104 | mMetaData.errorText = tr("Could not decrypt the data. ") + tr("No key found for recepients."); | 970 | mMetaData.errorText = tr("Could not decrypt the data. ") + tr("No key found for recepients."); |
1105 | } else if (passphraseError) { | 971 | } else if (passphraseError) { |
1106 | mError = PassphraseError; | 972 | mError = PassphraseError; |
973 | // mMetaData.errorText = QString::fromLocal8Bit(decryptResult.error().asString()); | ||
1107 | } else { | 974 | } else { |
1108 | mError = UnknownError; | 975 | mError = UnknownError; |
1109 | mMetaData.errorText = tr("Could not decrypt the data. ") | 976 | mMetaData.errorText = tr("Could not decrypt the data. "); |
1110 | + tr("Error: %1").arg(mMetaData.errorText); | 977 | // + tr("Error: %1").arg(QString::fromLocal8Bit(decryptResult.error().asString())); |
1111 | } | 978 | } |
1112 | return false; | 979 | return false; |
1113 | } | 980 | } |
diff --git a/framework/src/domain/mime/mimetreeparser/messagepart.h b/framework/src/domain/mime/mimetreeparser/messagepart.h index 3c07ca88..c576699e 100644 --- a/framework/src/domain/mime/mimetreeparser/messagepart.h +++ b/framework/src/domain/mime/mimetreeparser/messagepart.h | |||
@@ -23,6 +23,7 @@ | |||
23 | #include "util.h" | 23 | #include "util.h" |
24 | #include "enums.h" | 24 | #include "enums.h" |
25 | #include "partmetadata.h" | 25 | #include "partmetadata.h" |
26 | #include <crypto.h> | ||
26 | 27 | ||
27 | #include <KMime/Message> | 28 | #include <KMime/Message> |
28 | 29 | ||
@@ -32,13 +33,6 @@ | |||
32 | class QTextCodec; | 33 | class QTextCodec; |
33 | class PartPrivate; | 34 | class PartPrivate; |
34 | 35 | ||
35 | namespace GpgME | ||
36 | { | ||
37 | class ImportResult; | ||
38 | class VerificationResult; | ||
39 | class Signature; | ||
40 | } | ||
41 | |||
42 | namespace KMime | 36 | namespace KMime |
43 | { | 37 | { |
44 | class Content; | 38 | class Content; |
@@ -55,11 +49,10 @@ class MultiPartAlternativeBodyPartFormatter; | |||
55 | class SignedMessagePart; | 49 | class SignedMessagePart; |
56 | class EncryptedMessagePart; | 50 | class EncryptedMessagePart; |
57 | 51 | ||
58 | enum CryptoProtocol { | 52 | using Crypto::CryptoProtocol; |
59 | UnknownProtocol, | 53 | using Crypto::CryptoProtocol::CMS; |
60 | OpenPGP, | 54 | using Crypto::CryptoProtocol::OpenPGP; |
61 | CMS | 55 | using Crypto::CryptoProtocol::UnknownProtocol; |
62 | }; | ||
63 | 56 | ||
64 | class MessagePart : public QObject | 57 | class MessagePart : public QObject |
65 | { | 58 | { |
@@ -366,8 +359,8 @@ public: | |||
366 | QString htmlContent() const Q_DECL_OVERRIDE; | 359 | QString htmlContent() const Q_DECL_OVERRIDE; |
367 | 360 | ||
368 | private: | 361 | private: |
369 | void sigStatusToMetaData(const GpgME::Signature &signature); | 362 | void sigStatusToMetaData(const Crypto::Signature &signature); |
370 | void setVerificationResult(const GpgME::VerificationResult &result, bool parseText, const QByteArray &plainText); | 363 | void setVerificationResult(const Crypto::VerificationResult &result, bool parseText, const QByteArray &plainText); |
371 | 364 | ||
372 | protected: | 365 | protected: |
373 | CryptoProtocol mProtocol; | 366 | CryptoProtocol mProtocol; |
diff --git a/framework/src/domain/mime/mimetreeparser/partmetadata.h b/framework/src/domain/mime/mimetreeparser/partmetadata.h index 44a9cf7e..583eb454 100644 --- a/framework/src/domain/mime/mimetreeparser/partmetadata.h +++ b/framework/src/domain/mime/mimetreeparser/partmetadata.h | |||
@@ -37,7 +37,6 @@ public: | |||
37 | QByteArray keyId; | 37 | QByteArray keyId; |
38 | bool keyIsTrusted = false; | 38 | bool keyIsTrusted = false; |
39 | QString status; // to be used for unknown plug-ins | 39 | QString status; // to be used for unknown plug-ins |
40 | int status_code; // to be used for i18n of OpenPGP and S/MIME CryptPlugs | ||
41 | QString errorText; | 40 | QString errorText; |
42 | QDateTime creationTime; | 41 | QDateTime creationTime; |
43 | QString decryptionError; | 42 | QString decryptionError; |
diff --git a/framework/src/domain/mime/mimetreeparser/tests/CMakeLists.txt b/framework/src/domain/mime/mimetreeparser/tests/CMakeLists.txt index 9937ab06..fcb1988a 100644 --- a/framework/src/domain/mime/mimetreeparser/tests/CMakeLists.txt +++ b/framework/src/domain/mime/mimetreeparser/tests/CMakeLists.txt | |||
@@ -15,12 +15,11 @@ target_link_libraries(mimetreeparsertest | |||
15 | Qt5::Core | 15 | Qt5::Core |
16 | Qt5::Test | 16 | Qt5::Test |
17 | KF5::Mime | 17 | KF5::Mime |
18 | Gpgmepp | 18 | gpgme |
19 | QGpgme | ||
20 | ) | 19 | ) |
21 | 20 | ||
22 | ecm_add_test(gpgerrortest.cpp | 21 | ecm_add_test(gpgerrortest.cpp |
23 | TEST_NAME "gpgerrortest" | 22 | TEST_NAME "gpgerrortest" |
24 | NAME_PREFIX "mimetreeparser-" | 23 | NAME_PREFIX "mimetreeparser-" |
25 | LINK_LIBRARIES Qt5::Core Qt5::Test kube_otp Gpgmepp QGpgme | 24 | LINK_LIBRARIES Qt5::Core Qt5::Test kube_otp gpgme |
26 | ) | 25 | ) |
diff --git a/framework/src/domain/mime/tests/mailtemplatetest.cpp b/framework/src/domain/mime/tests/mailtemplatetest.cpp index 75debd75..20ea8c5e 100644 --- a/framework/src/domain/mime/tests/mailtemplatetest.cpp +++ b/framework/src/domain/mime/tests/mailtemplatetest.cpp | |||
@@ -356,7 +356,7 @@ private slots: | |||
356 | QString body = "body"; | 356 | QString body = "body"; |
357 | QList<Attachment> attachments; | 357 | QList<Attachment> attachments; |
358 | 358 | ||
359 | auto keys = MailCrypto::findKeys({}, true, false); | 359 | auto keys = Crypto::findKeys({}, true, false); |
360 | auto result = MailTemplates::createMessage({}, to, cc, bcc, from, subject, body, false, attachments, keys, {}, keys[0]); | 360 | auto result = MailTemplates::createMessage({}, to, cc, bcc, from, subject, body, false, attachments, keys, {}, keys[0]); |
361 | 361 | ||
362 | QVERIFY(result); | 362 | QVERIFY(result); |
@@ -398,7 +398,7 @@ private slots: | |||
398 | QString body = "body"; | 398 | QString body = "body"; |
399 | QList<Attachment> attachments = {{"name", "filename", "mimetype", true, "inlineAttachment"}, {"name", "filename", "mimetype", false, "nonInlineAttachment"}}; | 399 | QList<Attachment> attachments = {{"name", "filename", "mimetype", true, "inlineAttachment"}, {"name", "filename", "mimetype", false, "nonInlineAttachment"}}; |
400 | 400 | ||
401 | auto result = MailTemplates::createMessage({}, to, cc, bcc, from, subject, body, false, attachments, MailCrypto::findKeys({}, true, false)); | 401 | auto result = MailTemplates::createMessage({}, to, cc, bcc, from, subject, body, false, attachments, Crypto::findKeys({}, true, false)); |
402 | 402 | ||
403 | QVERIFY(result); | 403 | QVERIFY(result); |
404 | QCOMPARE(result->subject()->asUnicodeString(), subject); | 404 | QCOMPARE(result->subject()->asUnicodeString(), subject); |