Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F27326006
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
30 KB
Subscribers
None
View Options
diff --git a/src/core/crypto.cpp b/src/core/crypto.cpp
index 6dbe503..d5a4dd2 100644
--- a/src/core/crypto.cpp
+++ b/src/core/crypto.cpp
@@ -1,598 +1,593 @@
// SPDX-FileCopyrightText: 2009 Constantin Berzan <exit3219@gmail.com>
// SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
// SPDX-FileCopyrightText: 2010 Leo Franchi <lfranchi@kde.org>
// SPDX-FileCopyrightText: 2017 Christian Mollekopf <mollekopf@kolabsys.com>
// SPDX-License-Identifier: LGPL-2.0-or-later
#include "crypto.h"
-#ifndef _WIN32
#include <gpgme.h>
-#endif
#include <QDebug>
#include <QFile>
#include <future>
#include <utility>
using namespace Crypto;
QDebug operator<<(QDebug d, const Key &key)
{
d << key.fingerprint;
return d;
}
QDebug operator<<(QDebug d, const Error &error)
{
d << error.error;
return d;
}
-#ifndef _WIN32
namespace Crypto
{
struct Data {
Data(const QByteArray &buffer)
{
const bool copy = false;
const gpgme_error_t e = gpgme_data_new_from_mem(&data, buffer.constData(), buffer.size(), int(copy));
if (e) {
qWarning() << "Failed to copy data?" << e;
}
}
~Data()
{
gpgme_data_release(data);
}
gpgme_data_t data;
};
}
static gpgme_error_t checkEngine(CryptoProtocol protocol)
{
gpgme_check_version(nullptr);
const gpgme_protocol_t p = protocol == CMS ? GPGME_PROTOCOL_CMS : GPGME_PROTOCOL_OpenPGP;
return gpgme_engine_check_version(p);
}
static std::pair<gpgme_error_t, gpgme_ctx_t> createForProtocol(CryptoProtocol proto)
{
if (auto e = checkEngine(proto)) {
qWarning() << "GPG Engine check failed." << e;
return std::make_pair(e, nullptr);
}
gpgme_ctx_t ctx = nullptr;
if (auto e = gpgme_new(&ctx)) {
return std::make_pair(e, nullptr);
}
switch (proto) {
case OpenPGP:
if (auto e = gpgme_set_protocol(ctx, GPGME_PROTOCOL_OpenPGP)) {
gpgme_release(ctx);
return std::make_pair(e, nullptr);
}
break;
case CMS:
if (auto e = gpgme_set_protocol(ctx, GPGME_PROTOCOL_CMS)) {
gpgme_release(ctx);
return std::make_pair(e, nullptr);
}
break;
default:
Q_ASSERT(false);
return std::make_pair(1, nullptr);
}
// We want the output to always be ASCII armored
gpgme_set_armor(ctx, 1);
// Trust new keys
if (auto e = gpgme_set_ctx_flag(ctx, "trust-model", "tofu+pgp")) {
gpgme_release(ctx);
return std::make_pair(e, nullptr);
}
// That's a great way to bring signature verification to a crawl
if (auto e = gpgme_set_ctx_flag(ctx, "auto-key-retrieve", "0")) {
gpgme_release(ctx);
return std::make_pair(e, nullptr);
}
return std::make_pair(GPG_ERR_NO_ERROR, ctx);
}
gpgme_error_t gpgme_passphrase(void *hook, const char *uid_hint, const char *passphrase_info, int prev_was_bad, int fd)
{
Q_UNUSED(hook);
Q_UNUSED(prev_was_bad);
// uid_hint will be something like "CAA5183608F0FB50 Test1 Kolab <test1@kolab.org>" (CAA518... is the key)
// pahhphrase_info will be something like "CAA5183608F0FB50 2E3B7787B1B75920 1 0"
qInfo() << "Requested passphrase for " << (uid_hint ? QByteArray{uid_hint} : QByteArray{})
<< (passphrase_info ? QByteArray{passphrase_info} : QByteArray{});
QFile file;
file.open(fd, QIODevice::WriteOnly);
// FIXME hardcoded as a test
QByteArray passphrase = QByteArray{"test1"} + QByteArray{"\n"};
file.write(passphrase);
file.close();
return 0;
}
namespace Crypto
{
struct Context {
Context(CryptoProtocol protocol = OpenPGP)
{
gpgme_error_t code;
std::tie(code, context) = createForProtocol(protocol);
error = Error{code};
}
~Context()
{
gpgme_release(context);
}
operator bool() const
{
return !error;
}
Error error;
gpgme_ctx_t context;
};
}
static QByteArray toBA(gpgme_data_t out)
{
size_t length = 0;
auto data = gpgme_data_release_and_get_mem(out, &length);
auto outdata = QByteArray{data, static_cast<int>(length)};
gpgme_free(data);
return outdata;
}
static std::vector<Recipient> copyRecipients(gpgme_decrypt_result_t result)
{
std::vector<Recipient> recipients;
for (gpgme_recipient_t r = result->recipients; r; r = r->next) {
recipients.push_back({QByteArray{r->keyid}, r->status != GPG_ERR_NO_SECKEY});
}
return recipients;
}
static std::vector<Signature> copySignatures(gpgme_verify_result_t result)
{
std::vector<Signature> signatures;
for (gpgme_signature_t is = result->signatures; is; is = is->next) {
Signature sig;
sig.fingerprint = QByteArray{is->fpr};
sig.creationTime.setSecsSinceEpoch(is->timestamp);
if (is->summary & GPGME_SIGSUM_VALID) {
sig.result = Signature::Ok;
} else {
sig.result = Signature::Invalid;
if (is->summary & GPGME_SIGSUM_KEY_EXPIRED) {
sig.result = Signature::Expired;
}
if (is->summary & GPGME_SIGSUM_KEY_MISSING) {
sig.result = Signature::KeyNotFound;
}
}
sig.status = {is->status};
sig.isTrusted = is->validity == GPGME_VALIDITY_FULL || is->validity == GPGME_VALIDITY_ULTIMATE;
signatures.push_back(sig);
}
return signatures;
}
VerificationResult Crypto::verifyDetachedSignature(CryptoProtocol protocol, const QByteArray &signature, const QByteArray &text)
{
Context context{protocol};
if (!context) {
qWarning() << "Failed to create context " << context.error;
return {{}, context.error};
}
auto ctx = context.context;
auto err = gpgme_op_verify(ctx, Data{signature}.data, Data{text}.data, nullptr);
gpgme_verify_result_t res = gpgme_op_verify_result(ctx);
return {copySignatures(res), {err}};
}
static DecryptionResult::Result toResult(gpgme_error_t err)
{
if (err == GPG_ERR_NO_DATA) {
return DecryptionResult::NotEncrypted;
} else if (err == GPG_ERR_NO_SECKEY) {
return DecryptionResult::NoSecretKeyError;
} else if (err == GPG_ERR_CANCELED || err == GPG_ERR_INV_PASSPHRASE) {
return DecryptionResult::PassphraseError;
} else if (err == GPG_ERR_NO_ERROR) {
return DecryptionResult::NoSecretKeyError;
}
qWarning() << "unknown error" << err << gpgme_strerror(err);
return DecryptionResult::NoSecretKeyError;
}
VerificationResult Crypto::verifyOpaqueSignature(CryptoProtocol protocol, const QByteArray &signature, QByteArray &outdata)
{
Context context{protocol};
if (!context) {
qWarning() << "Failed to create context " << context.error;
return VerificationResult{{}, context.error};
}
auto ctx = context.context;
gpgme_data_t out;
const gpgme_error_t e = gpgme_data_new(&out);
Q_ASSERT(!e);
auto err = gpgme_op_verify(ctx, Data{signature}.data, nullptr, out);
VerificationResult result{{}, {err}};
if (gpgme_verify_result_t res = gpgme_op_verify_result(ctx)) {
result.signatures = copySignatures(res);
}
outdata = toBA(out);
return result;
}
std::pair<DecryptionResult, VerificationResult> Crypto::decryptAndVerify(CryptoProtocol protocol, const QByteArray &ciphertext, QByteArray &outdata)
{
Context context{protocol};
if (!context) {
qWarning() << "Failed to create context " << gpgme_strerror(context.error);
qWarning() << "returning early";
return std::make_pair(DecryptionResult{{}, {context.error}, DecryptionResult::NoSecretKeyError}, VerificationResult{{}, context.error});
}
auto ctx = context.context;
gpgme_data_t out;
if (gpgme_error_t e = gpgme_data_new(&out)) {
qWarning() << "Failed to allocated data" << e;
}
auto err = gpgme_op_decrypt_verify(ctx, Data{ciphertext}.data, out);
if (err) {
qWarning() << "Failed to decrypt and verify" << Error{err};
// We make sure we don't return any plain-text if the decryption failed to prevent EFAIL
if (err == GPG_ERR_DECRYPT_FAILED) {
return std::make_pair(DecryptionResult{{}, {err}, DecryptionResult::DecryptionError}, VerificationResult{{}, {err}});
}
}
VerificationResult verificationResult{{}, {err}};
if (gpgme_verify_result_t res = gpgme_op_verify_result(ctx)) {
verificationResult.signatures = copySignatures(res);
}
DecryptionResult decryptionResult{{}, {err}};
if (gpgme_decrypt_result_t res = gpgme_op_decrypt_result(ctx)) {
decryptionResult.recipients = copyRecipients(res);
}
decryptionResult.result = toResult(err);
outdata = toBA(out);
return std::make_pair(decryptionResult, verificationResult);
}
static DecryptionResult decryptGPGME(CryptoProtocol protocol, const QByteArray &ciphertext, QByteArray &outdata)
{
Context context{protocol};
if (!context) {
qWarning() << "Failed to create context " << context.error;
return DecryptionResult{{}, context.error};
}
auto ctx = context.context;
gpgme_data_t out;
if (gpgme_error_t e = gpgme_data_new(&out)) {
qWarning() << "Failed to allocated data" << e;
}
auto err = gpgme_op_decrypt(ctx, Data{ciphertext}.data, out);
if (err) {
qWarning() << "Failed to decrypt" << gpgme_strerror(err);
// We make sure we don't return any plain-text if the decryption failed to prevent EFAIL
if (err == GPG_ERR_DECRYPT_FAILED) {
return DecryptionResult{{}, {err}};
}
}
DecryptionResult decryptionResult{{}, {err}};
if (gpgme_decrypt_result_t res = gpgme_op_decrypt_result(ctx)) {
decryptionResult.recipients = copyRecipients(res);
}
decryptionResult.result = toResult(err);
outdata = toBA(out);
return decryptionResult;
}
DecryptionResult Crypto::decrypt(CryptoProtocol protocol, const QByteArray &ciphertext, QByteArray &outdata)
{
return decryptGPGME(protocol, ciphertext, outdata);
}
ImportResult Crypto::importKey(CryptoProtocol protocol, const QByteArray &certData)
{
Context context{protocol};
if (!context) {
qWarning() << "Failed to create context " << context.error;
return {0, 0, 0};
}
if (gpgme_op_import(context.context, Data{certData}.data)) {
qWarning() << "Import failed";
return {0, 0, 0};
}
if (auto result = gpgme_op_import_result(context.context)) {
return {result->considered, result->imported, result->unchanged};
} else {
return {0, 0, 0};
}
}
static bool validateKey(const gpgme_key_t key)
{
if (key->revoked) {
qWarning() << "Key is revoked " << key->fpr;
return false;
}
if (key->expired) {
qWarning() << "Key is expired " << key->fpr;
return false;
}
if (key->disabled) {
qWarning() << "Key is disabled " << key->fpr;
return false;
}
if (key->invalid) {
qWarning() << "Key is invalid " << key->fpr;
return false;
}
return true;
}
static KeyListResult listKeys(CryptoProtocol protocol, const std::vector<const char *> &patterns, bool secretOnly, int keyListMode, bool importKeys)
{
Context context{protocol};
if (!context) {
qWarning() << "Failed to create context " << context.error;
return {{}, context.error};
}
auto ctx = context.context;
gpgme_set_keylist_mode(ctx, keyListMode);
KeyListResult result;
result.error = {GPG_ERR_NO_ERROR};
auto zeroTerminatedPatterns = patterns;
zeroTerminatedPatterns.push_back(nullptr);
if (patterns.size() > 1) {
if (auto err = gpgme_op_keylist_ext_start(ctx, const_cast<const char **>(zeroTerminatedPatterns.data()), int(secretOnly), 0)) {
result.error = {err};
qWarning() << "Error while listing keys:" << result.error;
}
} else if (patterns.size() == 1) {
if (auto err = gpgme_op_keylist_start(ctx, zeroTerminatedPatterns[0], int(secretOnly))) {
result.error = {err};
qWarning() << "Error while listing keys:" << result.error;
}
} else {
if (auto err = gpgme_op_keylist_start(ctx, nullptr, int(secretOnly))) {
result.error = {err};
qWarning() << "Error while listing keys:" << result.error;
}
}
std::vector<gpgme_key_t> listedKeys;
while (true) {
gpgme_key_t key;
if (auto err = gpgme_op_keylist_next(ctx, &key)) {
if (gpgme_err_code(err) != GPG_ERR_EOF) {
qWarning() << "Error after listing keys" << result.error << gpgme_strerror(err);
}
break;
}
listedKeys.push_back(key);
Key k;
if (key->subkeys) {
k.keyId = QByteArray{key->subkeys->keyid};
k.shortKeyId = k.keyId.right(8);
k.fingerprint = QByteArray{key->subkeys->fpr};
}
for (gpgme_user_id_t uid = key->uids; uid; uid = uid->next) {
k.userIds.push_back(UserId{QByteArray{uid->name}, QByteArray{uid->email}, QByteArray{uid->uid}});
}
k.isUsable = validateKey(key);
result.keys.push_back(k);
}
gpgme_op_keylist_end(ctx);
if (importKeys && !listedKeys.empty()) {
listedKeys.push_back(nullptr);
if (auto err = gpgme_op_import_keys(ctx, const_cast<gpgme_key_t *>(listedKeys.data()))) {
qWarning() << "Error while importing keys" << gpgme_strerror(err);
}
}
return result;
}
/**
* Get the given `key` in the armor format.
*/
Expected<Error, QByteArray> Crypto::exportPublicKey(const Key &key)
{
Context context;
if (!context) {
return makeUnexpected(Error{context.error});
}
gpgme_data_t out;
const gpgme_error_t e = gpgme_data_new(&out);
Q_ASSERT(!e);
qDebug() << "Exporting public key:" << key.keyId;
if (auto err = gpgme_op_export(context.context, key.keyId.data(), 0, out)) {
return makeUnexpected(Error{err});
}
return toBA(out);
}
Expected<Error, QByteArray> Crypto::signAndEncrypt(const QByteArray &content, const std::vector<Key> &encryptionKeys, const std::vector<Key> &signingKeys)
{
Context context;
if (!context) {
return makeUnexpected(Error{context.error});
}
for (const auto &signingKey : signingKeys) {
qDebug() << "Signing with " << signingKey;
// TODO do we have to free those again?
gpgme_key_t key;
if (auto e = gpgme_get_key(context.context, signingKey.fingerprint.data(), &key, /*secret*/ false)) {
qWarning() << "Failed to retrieve signing key " << signingKey.fingerprint << Error{e};
return makeUnexpected(Error{e});
} else {
gpgme_signers_add(context.context, key);
}
}
gpgme_key_t *const keys = new gpgme_key_t[encryptionKeys.size() + 1];
gpgme_key_t *keys_it = keys;
for (const auto &k : encryptionKeys) {
qDebug() << "Encrypting to " << k;
gpgme_key_t key;
if (auto e = gpgme_get_key(context.context, k.fingerprint.data(), &key, /*secret*/ false)) {
delete[] keys;
qWarning() << "Failed to retrieve key " << k.fingerprint << Error{e};
return makeUnexpected(Error{e});
} else {
if (!key->can_encrypt || !validateKey(key)) {
qWarning() << "Key cannot be used for encryption " << k.fingerprint;
delete[] keys;
return makeUnexpected(Error{e});
}
*keys_it++ = key;
}
}
*keys_it++ = nullptr;
gpgme_data_t out;
if (auto e = gpgme_data_new(&out)) {
qWarning() << "Failed to allocate output buffer";
delete[] keys;
return makeUnexpected(Error{e});
}
gpgme_error_t err = !signingKeys.empty() ? gpgme_op_encrypt_sign(context.context, keys, GPGME_ENCRYPT_ALWAYS_TRUST, Data{content}.data, out)
: gpgme_op_encrypt(context.context, keys, GPGME_ENCRYPT_ALWAYS_TRUST, Data{content}.data, out);
delete[] keys;
if (err) {
qWarning() << "Encryption failed:" << gpgme_err_code(err);
switch (gpgme_err_code(err)) {
case GPG_ERR_UNUSABLE_PUBKEY:
for (const auto &k : encryptionKeys) {
qWarning() << "Encryption key:" << k;
}
break;
case GPG_ERR_UNUSABLE_SECKEY:
for (const auto &k : signingKeys) {
qWarning() << "Signing key:" << k;
}
break;
default:
break;
}
return makeUnexpected(Error{err});
}
return toBA(out);
}
Expected<Error, std::pair<QByteArray, QString>> Crypto::sign(const QByteArray &content, const std::vector<Key> &signingKeys)
{
Context context;
if (!context) {
return makeUnexpected(Error{context.error});
}
for (const auto &signingKey : signingKeys) {
// TODO do we have to free those again?
gpgme_key_t key;
if (auto e = gpgme_get_key(context.context, signingKey.fingerprint.data(), &key, /*secret*/ false)) {
qWarning() << "Failed to retrieve signing key " << signingKey.fingerprint << Error{e};
return makeUnexpected(Error{e});
} else {
gpgme_signers_add(context.context, key);
}
}
gpgme_data_t out;
const gpgme_error_t e = gpgme_data_new(&out);
Q_ASSERT(!e);
if (auto err = gpgme_op_sign(context.context, Data{content}.data, out, GPGME_SIG_MODE_DETACH)) {
qWarning() << "Signing failed:" << Error{err};
return makeUnexpected(Error{err});
}
const QByteArray algo = [&] {
if (gpgme_sign_result_t res = gpgme_op_sign_result(context.context)) {
if (gpgme_new_signature_t is = res->signatures) {
return QByteArray{gpgme_hash_algo_name(is->hash_algo)};
}
}
return QByteArray{};
}();
// RFC 3156 Section 5:
// Hash-symbols are constructed [...] by converting the text name to lower
// case and prefixing it with the four characters "pgp-".
const auto micAlg = (QStringLiteral("pgp-") + QString::fromUtf8(algo)).toLower();
return std::pair<QByteArray, QString>{toBA(out), micAlg};
}
std::vector<Key> Crypto::findKeys(const QStringList &patterns, bool findPrivate, bool remote)
{
QByteArrayList list;
std::transform(patterns.constBegin(), patterns.constEnd(), std::back_inserter(list), [](const QString &s) {
return s.toUtf8();
});
std::vector<char const *> pattern;
std::transform(list.constBegin(), list.constEnd(), std::back_inserter(pattern), [](const QByteArray &s) {
return s.constData();
});
const KeyListResult res = listKeys(OpenPGP, pattern, findPrivate, remote ? GPGME_KEYLIST_MODE_EXTERN : GPGME_KEYLIST_MODE_LOCAL, remote);
if (res.error) {
qWarning() << "Failed to lookup keys: " << res.error;
return {};
}
qDebug() << "Found " << res.keys.size() << " keys for the patterns: " << patterns;
std::vector<Key> usableKeys;
for (const auto &key : res.keys) {
if (!key.isUsable) {
qWarning() << "Key is not usable: " << key.fingerprint;
continue;
}
qDebug() << "Key:" << key.fingerprint;
for (const auto &userId : key.userIds) {
qDebug() << " userID:" << userId.email;
}
usableKeys.push_back(key);
}
return usableKeys;
}
-
-#endif
diff --git a/src/core/crypto.h b/src/core/crypto.h
index 2e043a6..5f78705 100644
--- a/src/core/crypto.h
+++ b/src/core/crypto.h
@@ -1,106 +1,104 @@
// SPDX-FileCopyrightText: 2016 Christian Mollekopf <mollekopf@kolabsys.com>
// SPDX-License-Identifier: LGPL-2.0-or-later
#pragma once
#include "errors.h"
#include "mimetreeparser_core_export.h"
#include <QByteArray>
#include <QVariant>
#include <QDateTime>
#include <functional>
#include <memory>
namespace Crypto
{
enum CryptoProtocol { UnknownProtocol, OpenPGP, CMS };
struct UserId {
QByteArray name;
QByteArray email;
QByteArray id;
};
struct Key {
QByteArray keyId;
QByteArray shortKeyId;
QByteArray fingerprint;
bool isUsable = false;
std::vector<UserId> userIds;
};
struct Error {
unsigned int error;
operator bool() const
{
return error != 0;
}
};
struct Signature {
QByteArray fingerprint;
Error status;
QDateTime creationTime;
enum Result { Ok, NotVerified, Expired, KeyNotFound, Invalid };
Result result{NotVerified};
bool isTrusted{false};
};
struct VerificationResult {
std::vector<Signature> signatures;
Error error;
};
struct Recipient {
QByteArray keyId;
bool secretKeyAvailable{false};
};
struct DecryptionResult {
std::vector<Recipient> recipients;
Error error;
enum Result { NoError, NotEncrypted, PassphraseError, NoSecretKeyError, DecryptionError };
Result result{NoError};
};
struct KeyListResult {
std::vector<Key> keys;
Error error;
};
struct ImportResult {
int considered;
int imported;
int unchanged;
};
-#ifndef _WIN32
MIMETREEPARSER_CORE_EXPORT std::vector<Key> findKeys(const QStringList &filter, bool findPrivate = false, bool remote = false);
MIMETREEPARSER_CORE_EXPORT Expected<Error, QByteArray> exportPublicKey(const Key &key);
MIMETREEPARSER_CORE_EXPORT ImportResult importKey(CryptoProtocol protocol, const QByteArray &certData);
MIMETREEPARSER_CORE_EXPORT ImportResult importKey(CryptoProtocol protocol, const Key &key);
/**
* Sign the given content and returns the signing data and the algorithm used
* for integrity check in the "pgp-<algorithm>" format.
*/
MIMETREEPARSER_CORE_EXPORT Expected<Error, std::pair<QByteArray, QString>> sign(const QByteArray &content, const std::vector<Key> &signingKeys);
MIMETREEPARSER_CORE_EXPORT Expected<Error, QByteArray>
signAndEncrypt(const QByteArray &content, const std::vector<Key> &encryptionKeys, const std::vector<Key> &signingKeys);
MIMETREEPARSER_CORE_EXPORT std::pair<DecryptionResult, VerificationResult>
decryptAndVerify(CryptoProtocol protocol, const QByteArray &ciphertext, QByteArray &outdata);
MIMETREEPARSER_CORE_EXPORT DecryptionResult decrypt(CryptoProtocol protocol, const QByteArray &ciphertext, QByteArray &outdata);
MIMETREEPARSER_CORE_EXPORT VerificationResult verifyDetachedSignature(CryptoProtocol protocol, const QByteArray &signature, const QByteArray &outdata);
MIMETREEPARSER_CORE_EXPORT VerificationResult verifyOpaqueSignature(CryptoProtocol protocol, const QByteArray &signature, QByteArray &outdata);
};
-#endif
Q_DECLARE_METATYPE(Crypto::Key);
MIMETREEPARSER_CORE_EXPORT QDebug operator<<(QDebug d, const Crypto::Key &);
MIMETREEPARSER_CORE_EXPORT QDebug operator<<(QDebug d, const Crypto::Error &);
diff --git a/src/core/mailcrypto.cpp b/src/core/mailcrypto.cpp
index 7df2fd4..6a3e32b 100644
--- a/src/core/mailcrypto.cpp
+++ b/src/core/mailcrypto.cpp
@@ -1,190 +1,179 @@
// SPDX-FileCopyrightText: 2009 Constantin Berzan <exit3219@gmail.com>
// SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
// SPDX-FileCopyrightText: 2010 Leo Franchi <lfranchi@kde.org>
// SPDX-FileCopyrightText: 2017 Christian Mollekopf <mollekopf@kolabsys.com>
// SPDX-License-Identifier: LGPL-2.0-or-later
//
#include "mailcrypto.h"
#include <QDebug>
#include <future>
#include <utility>
using namespace MailCrypto;
using namespace Crypto;
-#ifdef _WIN32
-Expected<Error, std::unique_ptr<KMime::Content>>
-MailCrypto::processCrypto(std::unique_ptr<KMime::Content> content, const std::vector<Key> &signingKeys, const std::vector<Key> &encryptionKeys)
-{
- Q_UNUSED(content)
- Q_UNUSED(signingKeys)
- Q_UNUSED(encryptionKeys)
- return makeUnexpected(Error{});
-}
-#else
static QByteArray canonicalizeContent(KMime::Content *content)
{
// if (d->format & Kleo::InlineOpenPGPFormat) {
// return d->content->body();
// } else if (!(d->format & Kleo::SMIMEOpaqueFormat)) {
// replace "From " and "--" at the beginning of lines
// with encoded versions according to RfC 3156, 3
// Note: If any line begins with the string "From ", it is strongly
// suggested that either the Quoted-Printable or Base64 MIME encoding
// be applied.
const auto encoding = content->contentTransferEncoding()->encoding();
if ((encoding == KMime::Headers::CEquPr || encoding == KMime::Headers::CE7Bit) && !content->contentType(false)) {
QByteArray body = content->encodedBody();
bool changed = false;
QList<QByteArray> search;
QList<QByteArray> replacements;
search << "From "
<< "from "
<< "-";
replacements << "From=20"
<< "from=20"
<< "=2D";
if (content->contentTransferEncoding()->encoding() == KMime::Headers::CE7Bit) {
for (int i = 0; i < search.size(); ++i) {
const auto pos = body.indexOf(search[i]);
if (pos == 0 || (pos > 0 && body.at(pos - 1) == '\n')) {
changed = true;
break;
}
}
if (changed) {
content->contentTransferEncoding()->setEncoding(KMime::Headers::CEquPr);
content->assemble();
body = content->encodedBody();
}
}
for (int i = 0; i < search.size(); ++i) {
const auto pos = body.indexOf(search[i]);
if (pos == 0 || (pos > 0 && body.at(pos - 1) == '\n')) {
changed = true;
body.replace(pos, search[i].size(), replacements[i]);
}
}
if (changed) {
qDebug() << "Content changed";
content->setBody(body);
content->contentTransferEncoding()->setDecoded(false);
}
}
return KMime::LFtoCRLF(content->encodedContent());
// } else { // SMimeOpaque doesn't need LFtoCRLF, else it gets munged
// return content->encodedContent();
// }
}
/**
* Create a message part like this (according to RFC 3156 Section 4):
*
* - multipart/encrypted
* - application/pgp-encrypted (version information)
* - application/octet-stream (given encrypted data)
*
* The encrypted data can be generated by the `encrypt` or `signAndEncrypt` functions.
*/
std::unique_ptr<KMime::Content> createEncryptedPart(QByteArray encryptedData)
{
auto result = std::unique_ptr<KMime::Content>(new KMime::Content);
result->contentType()->setMimeType("multipart/encrypted");
result->contentType()->setBoundary(KMime::multiPartBoundary());
result->contentType()->setParameter(QStringLiteral("protocol"), QStringLiteral("application/pgp-encrypted"));
KMime::Content *controlInformation = new KMime::Content;
{
controlInformation->contentType()->setMimeType("application/pgp-encrypted");
controlInformation->contentDescription()->from7BitString("PGP/MIME version identification");
controlInformation->setBody("Version: 1");
result->addContent(controlInformation);
}
KMime::Content *encryptedPartPart = new KMime::Content;
{
const QString filename = QStringLiteral("msg.asc");
encryptedPartPart->contentType()->setMimeType("application/octet-stream");
encryptedPartPart->contentType()->setName(filename, "utf-8");
encryptedPartPart->contentDescription()->from7BitString("OpenPGP encrypted message");
encryptedPartPart->contentDisposition()->setDisposition(KMime::Headers::CDinline);
encryptedPartPart->contentDisposition()->setFilename(filename);
encryptedPartPart->setBody(encryptedData);
result->addContent(encryptedPartPart);
}
return result;
}
/**
* Create a message part like this (according to RFC 3156 Section 5):
*
* + `multipart/signed`
* - whatever the given original `message` is (should be canonicalized)
* - `application/octet-stream` (the given `signature`)
*
* The signature can be generated by the `sign` function.
*/
std::unique_ptr<KMime::Content> createSignedPart(std::unique_ptr<KMime::Content> message, const QByteArray &signature, const QString &micAlg)
{
auto result = std::unique_ptr<KMime::Content>(new KMime::Content);
result->contentType()->setMimeType("multipart/signed");
result->contentType()->setBoundary(KMime::multiPartBoundary());
result->contentType()->setParameter(QStringLiteral("micalg"), micAlg);
result->contentType()->setParameter(QStringLiteral("protocol"), QStringLiteral("application/pgp-signature"));
result->addContent(message.release());
KMime::Content *signedPartPart = new KMime::Content;
signedPartPart->contentType()->setMimeType("application/pgp-signature");
signedPartPart->contentType()->setName(QStringLiteral("signature.asc"), "utf-8");
signedPartPart->contentDisposition(true)->setDisposition(KMime::Headers::CDattachment);
signedPartPart->contentDisposition(true)->setFilename(QStringLiteral("signature.asc"));
signedPartPart->contentDescription()->from7BitString("OpenPGP digital signature");
signedPartPart->setBody(signature);
result->addContent(signedPartPart);
return result;
}
Expected<Error, std::unique_ptr<KMime::Content>>
MailCrypto::processCrypto(std::unique_ptr<KMime::Content> content, const std::vector<Key> &signingKeys, const std::vector<Key> &encryptionKeys)
{
if (!encryptionKeys.empty()) {
auto encryptionResult = signAndEncrypt(canonicalizeContent(content.get()), encryptionKeys, signingKeys);
if (!encryptionResult) {
return makeUnexpected(Error{encryptionResult.error()});
}
return createEncryptedPart(encryptionResult.value());
} else if (!signingKeys.empty()) {
auto signingResult = sign(canonicalizeContent(content.get()), signingKeys);
if (!signingResult) {
return makeUnexpected(Error{signingResult.error()});
}
QByteArray signingData;
QString micAlg;
std::tie(signingData, micAlg) = signingResult.value();
return createSignedPart(std::move(content), signingData, micAlg);
} else {
qWarning() << "Processing cryptography, but neither signing nor encrypting";
Q_ASSERT(false);
return content;
}
}
-#endif
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Fri, Aug 29, 7:59 AM (1 d, 17 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
29/a2/5d385a52d264d2ee47656f604fc3
Attached To
rMTP MIME Tree Parser
Event Timeline
Log In to Comment