diff options
Diffstat (limited to 'framework/src/domain/mime/mailcrypto.cpp')
-rw-r--r-- | framework/src/domain/mime/mailcrypto.cpp | 192 |
1 files changed, 4 insertions, 188 deletions
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 | } | ||