Page MenuHome GnuPG

No OneTemporary

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

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

Event Timeline