summaryrefslogtreecommitdiffstats
path: root/framework/src/domain/mime/mailcrypto.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/domain/mime/mailcrypto.cpp')
-rw-r--r--framework/src/domain/mime/mailcrypto.cpp192
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
47using namespace MailCrypto; 32using namespace MailCrypto;
33using namespace Crypto;
48 34
49QDebug operator<< (QDebug d, const MailCrypto::Key &key)
50{
51 d << key.key.primaryFingerprint();
52 return d;
53}
54
55static 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
64static QByteArray canonicalizeContent(KMime::Content *content) 35static 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 */
130Expected<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
195Expected<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
211Expected<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>>
295createEncryptedEmail(KMime::Content *content, const std::vector<Key> &encryptionKeys, 205createEncryptedEmail(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 */
323Expected<Error, std::pair<QByteArray, QString>>
324sign(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
441void 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
449MailCrypto::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
457static 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
484std::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}