summaryrefslogtreecommitdiffstats
path: root/framework/src/domain/mime/mailcrypto.cpp
diff options
context:
space:
mode:
authorChristian Mollekopf <chrigi_1@fastmail.fm>2018-05-05 10:39:32 +0200
committerChristian Mollekopf <chrigi_1@fastmail.fm>2018-05-06 17:21:01 +0200
commit01594e68275a09c67b5ee258e2af86598118a6a0 (patch)
tree4f859815f6455906bb656f9cc27ba5d6e4111599 /framework/src/domain/mime/mailcrypto.cpp
parent481cb9f600caf3f45596bf78b5ba2bd07007969c (diff)
downloadkube-01594e68275a09c67b5ee258e2af86598118a6a0.tar.gz
kube-01594e68275a09c67b5ee258e2af86598118a6a0.zip
Port to gpgme only.
QGpgme and Gpgmepp are not readily available, the cmake files buggy, the buildsystem horrendous and generally just difficult to build on windows. Given that all they are is a wrapper around gpgme, we're better of without all the indirections. What we loose is: * QGpgme moved the work to separate threads (but we then blocked anyways), something that we can just do in our own code should we want to. * QGpgme has a function to prettify dn's that was used to show the signer. Also something we could bring back should we need to (don't know where it is useful atm.) Ported messagepart to gpgme Almost there Moved the crypto bits to a separate file All gpg code is in one place. All tests passing Use error codes Cleanup
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}