Commit 69c4ee92 authored by Sandro Knauß's avatar Sandro Knauß
Browse files

Merge remote-tracking branch 'hefee/autocrypt'

parents 08387aa3 c9af5c82
Pipeline #49852 canceled with stage
in 0 seconds
# clang-format
883b9686e77c4caa0e82bc969af1aacc2635fbb9
f3b280b616aceaa28870ba530206d6af65f10f49
......@@ -86,10 +86,39 @@ add_messagecomposer_test( sendlaterdialogtest.cpp )
add_messagecomposer_test( sendlaterinfotest.cpp )
# Crypto
add_messagecomposer_cryptotest( autocryptheadersjobtest.cpp )
add_messagecomposer_cryptotest( signjobtest.cpp )
add_messagecomposer_cryptotest( encryptjobtest.cpp )
add_messagecomposer_cryptotest( signencrypttest.cpp )
add_messagecomposer_cryptotest( signandencrypttest.cpp )
add_executable( keyresolvertest
cryptofunctions.cpp
setupenv.cpp
keyresolvertest.cpp
)
target_link_libraries( keyresolvertest
KF5::AkonadiCore
KF5::Completion
KF5::Contacts
KF5::GuiAddons
KF5::I18n
KF5::IconThemes
KF5::Ldap
KF5::Libkdepim
KF5::Libkleo
KF5::MailTransport
KF5::MessageComposer
KF5::MessageCore
KF5::MessageViewer
KF5::Mime
KF5::PimTextEdit
QGpgme
Qt5::Test
)
add_gpg_crypto_test(keyresolvertest messagecomposer-keyresolvertest)
set_tests_properties(messagecomposer-keyresolvertest PROPERTIES RUN_SERIAL TRUE) # can't be parallelized due to gpg-agent
if (KDEPIM_RUN_AKONADI_TEST)
set(KDEPIMLIBS_RUN_ISOLATED_TESTS TRUE)
set(KDEPIMLIBS_RUN_SQLITE_ISOLATED_TESTS TRUE)
......
/*
SPDX-FileCopyrightText: 2020 Sandro Knauß <knauss@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "autocryptheadersjobtest.h"
#include "cryptofunctions.h"
#include "qtest_messagecomposer.h"
#include "setupenv.h"
#include <KMime/Content>
#include <Libkleo/Enum>
#include <MessageComposer/AutocryptHeadersJob>
#include <MessageComposer/Composer>
#include <MessageComposer/Util>
#include <QGpgME/KeyListJob>
#include <QGpgME/Protocol>
#include <gpgme++/decryptionresult.h>
#include <gpgme++/verificationresult.h>
#include <KCharsets>
#include <stdlib.h>
#include <MessageComposer/TransparentJob>
#include <QDebug>
#include <QTest>
#include <decryptionresult.h>
#include <keylistresult.h>
QTEST_MAIN(AutocryptHeadersJobTest)
using namespace MessageComposer;
void AutocryptHeadersJobTest::initTestCase()
{
Test::setupEnv();
}
void AutocryptHeadersJobTest::testAutocryptHeader()
{
Composer composer;
KMime::Message skeletonMessage;
skeletonMessage.from(true)->from7BitString("Alice <alice@autocrypt.example>");
skeletonMessage.to(true)->from7BitString("Bob <bob@autocrypt.example>");
skeletonMessage.subject(true)->from7BitString("an Autocrypt header example using Ed25519+Cv25519 key");
skeletonMessage.date(true)->from7BitString("Tue, 22 Jan 2019 12:56:25 +0100");
skeletonMessage.messageID(true)->from7BitString("<abe640bb-018d-4f9d-b4d8-1636d6164e22@autocrypt.example>");
KMime::Content content;
content.contentType(true)->from7BitString("text/plain");
content.setBody("This is an example e-mail with Autocrypt header and Ed25519+Cv25519 key (key\nfingerprint: ) as defined in Level 1 revision 1.1.\n");
auto job = QGpgME::openpgp()->keyListJob(false);
std::vector<GpgME::Key> ownKeys;
auto res = job->exec(QStringList(QString::fromLatin1(skeletonMessage.from()[0].addresses()[0])), false, ownKeys);
auto aJob = new AutocryptHeadersJob(&composer);
QVERIFY(aJob);
aJob->setContent(&content);
aJob->setSkeletonMessage(&skeletonMessage);
aJob->setSenderKey(ownKeys[0]);
aJob->setPreferEncrypted(true);
VERIFYEXEC(aJob);
skeletonMessage.assemble();
content.assemble();
auto referenceFile = QStringLiteral("autcryptheader.mbox");
QFile f(referenceFile);
QVERIFY(f.open(QIODevice::WriteOnly | QIODevice::Truncate));
f.write(skeletonMessage.head());
f.write(content.encodedContent());
f.close();
Test::compareFile(referenceFile, QStringLiteral(MAIL_DATA_DIR "/") + referenceFile);
}
void AutocryptHeadersJobTest::testContentChained()
{
Composer composer;
KMime::Message skeletonMessage;
skeletonMessage.from(true)->from7BitString("Alice <alice@autocrypt.example>");
skeletonMessage.to(true)->from7BitString("Bob <bob@autocrypt.example>");
skeletonMessage.subject(true)->from7BitString("an Autocrypt header example using Ed25519+Cv25519 key");
skeletonMessage.date(true)->from7BitString("Tue, 22 Jan 2019 12:56:25 +0100");
skeletonMessage.messageID(true)->from7BitString("<abe640bb-018d-4f9d-b4d8-1636d6164e22@autocrypt.example>");
KMime::Content content;
content.contentType(true)->from7BitString("text/plain");
content.setBody("This is an example e-mail with Autocrypt header and Ed25519+Cv25519 key (key\nfingerprint: ) as defined in Level 1 revision 1.1.\n");
auto job = QGpgME::openpgp()->keyListJob(false);
std::vector<GpgME::Key> ownKeys;
auto res = job->exec(QStringList(QString::fromLatin1(skeletonMessage.from()[0].addresses()[0])), false, ownKeys);
auto aJob = new AutocryptHeadersJob(&composer);
QVERIFY(aJob);
auto tJob = new TransparentJob;
tJob->setContent(&content);
aJob->setSkeletonMessage(&skeletonMessage);
aJob->setSenderKey(ownKeys[0]);
aJob->setPreferEncrypted(true);
QVERIFY(aJob->appendSubjob(tJob));
VERIFYEXEC(aJob);
skeletonMessage.assemble();
content.assemble();
auto referenceFile = QStringLiteral("autcryptheader.mbox");
QFile f(referenceFile);
QVERIFY(f.open(QIODevice::WriteOnly | QIODevice::Truncate));
f.write(skeletonMessage.head());
f.write(content.encodedContent());
f.close();
Test::compareFile(referenceFile, QStringLiteral(MAIL_DATA_DIR "/") + referenceFile);
}
void AutocryptHeadersJobTest::testAutocryptGossipHeader()
{
Composer composer;
KMime::Message skeletonMessage;
skeletonMessage.from(true)->from7BitString("Alice <alice@autocrypt.example>");
KMime::Content content;
content.setBody(
"Hi Bob and Carol,\n"
"\n"
"I wanted to introduce the two of you to each other.\n"
"\n"
"I hope you are both doing well! You can now both \"reply all\" here,\n"
"and the thread will remain encrypted.\n");
auto job = QGpgME::openpgp()->keyListJob(false);
std::vector<GpgME::Key> ownKeys;
job->exec(QStringList(QString::fromLatin1(skeletonMessage.from()[0].addresses()[0])), false, ownKeys);
std::vector<GpgME::Key> keys;
job->exec(QStringList({QStringLiteral("bob@autocrypt.example"), QStringLiteral("carol@autocrypt.example")}), false, keys);
auto aJob = new AutocryptHeadersJob(&composer);
QVERIFY(aJob);
aJob->setContent(&content);
aJob->setSkeletonMessage(&skeletonMessage);
aJob->setSenderKey(ownKeys[0]);
aJob->setPreferEncrypted(true);
aJob->setGossipKeys(keys);
VERIFYEXEC(aJob);
content.contentType(true)->from7BitString("text/plain");
content.assemble();
auto referenceFile = QStringLiteral("autocryptgossipheader.mbox");
Test::compareFile(&content, QStringLiteral(MAIL_DATA_DIR "/") + referenceFile);
}
void AutocryptHeadersJobTest::testSetGnupgHome()
{
Composer composer;
KMime::Message skeletonMessage;
skeletonMessage.from(true)->from7BitString("Alice <alice@autocrypt.example>");
KMime::Content content;
content.setBody(
"Hi Bob and Carol,\n"
"\n"
"I wanted to introduce the two of you to each other.\n"
"\n"
"I hope you are both doing well! You can now both \"reply all\" here,\n"
"and the thread will remain encrypted.\n");
auto exportJob = QGpgME::openpgp()->keyListJob(false);
std::vector<GpgME::Key> ownKeys;
exportJob->exec(QStringList(QString::fromLatin1(skeletonMessage.from()[0].addresses()[0])), false, ownKeys);
std::vector<GpgME::Key> keys;
exportJob->exec(QStringList({QStringLiteral("bob@autocrypt.example"), QStringLiteral("carol@autocrypt.example")}), false, keys);
QTemporaryDir dir;
{ // test with an empty gnupg Home
auto aJob = new AutocryptHeadersJob(&composer);
QVERIFY(aJob);
aJob->setContent(&content);
aJob->setSkeletonMessage(&skeletonMessage);
aJob->setSenderKey(ownKeys[0]);
aJob->setPreferEncrypted(true);
aJob->setGossipKeys(keys);
aJob->setGnupgHome(dir.path());
QCOMPARE(aJob->exec(), false);
}
// Populate Keyring with needed keys.
Test::populateKeyring(dir.path(), ownKeys[0]);
for (const auto key : keys) {
Test::populateKeyring(dir.path(), key);
}
auto aJob = new AutocryptHeadersJob(&composer);
QVERIFY(aJob);
aJob->setContent(&content);
aJob->setSkeletonMessage(&skeletonMessage);
aJob->setSenderKey(ownKeys[0]);
aJob->setPreferEncrypted(true);
aJob->setGossipKeys(keys);
aJob->setGnupgHome(dir.path());
VERIFYEXEC(aJob);
content.contentType(true)->from7BitString("text/plain");
content.assemble();
auto referenceFile = QStringLiteral("autocryptgossipheader.mbox");
Test::compareFile(&content, QStringLiteral(MAIL_DATA_DIR "/") + referenceFile);
}
void AutocryptHeadersJobTest::testStripSenderKey()
{
Composer composer;
KMime::Message skeletonMessage;
skeletonMessage.from(true)->from7BitString("Alice <alice@autocrypt.example>");
KMime::Content content;
content.setBody(
"Hi Bob and Carol,\n"
"\n"
"I wanted to introduce the two of you to each other.\n"
"\n"
"I hope you are both doing well! You can now both \"reply all\" here,\n"
"and the thread will remain encrypted.\n");
auto job = QGpgME::openpgp()->keyListJob(false);
std::vector<GpgME::Key> ownKeys;
job->exec(QStringList(QString::fromLatin1(skeletonMessage.from()[0].addresses()[0])), false, ownKeys);
std::vector<GpgME::Key> keys;
job->exec(QStringList({QStringLiteral("bob@autocrypt.example"), QStringLiteral("carol@autocrypt.example")}), false, keys);
keys.push_back(ownKeys[0]);
auto aJob = new AutocryptHeadersJob(&composer);
QVERIFY(aJob);
aJob->setContent(&content);
aJob->setSkeletonMessage(&skeletonMessage);
aJob->setSenderKey(ownKeys[0]);
aJob->setPreferEncrypted(true);
aJob->setGossipKeys(keys);
VERIFYEXEC(aJob);
content.contentType(true)->from7BitString("text/plain");
content.assemble();
auto referenceFile = QStringLiteral("autocryptgossipheader.mbox");
Test::compareFile(&content, QStringLiteral(MAIL_DATA_DIR "/") + referenceFile);
}
/*
SPDX-FileCopyrightText: 2020 Sandro Knauß <knauss@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef AUTOCRYPTHEADERSJOBTEST_H
#define AUTOCRYPTHEADERSJOBTEST_H
#include <QObject>
namespace MessageComposer
{
class AutocryptHeadersJob;
}
class AutocryptHeadersJobTest : public QObject
{
Q_OBJECT
public Q_SLOTS:
void initTestCase();
private Q_SLOTS:
void testAutocryptHeader();
void testContentChained();
void testAutocryptGossipHeader();
void testSetGnupgHome();
void testStripSenderKey();
};
#endif
......@@ -35,7 +35,10 @@ using namespace MessageCore;
#include <MimeTreeParser/ObjectTreeParser>
#include <MimeTreeParser/SimpleObjectTreeSource>
#include <QGpgME/KeyListJob>
#include <QGpgME/Protocol>
#include <gpgme++/key.h>
#include <gpgme++/keylistresult.h>
#include <QDebug>
#include <QTest>
......@@ -593,6 +596,238 @@ void CryptoComposerTest::testCTEbase64()
testOpenPGPInline();
}
void CryptoComposerTest::testSetGnupgHome_data()
{
QTest::addColumn<QString>("data");
QTest::addColumn<bool>("sign");
QString data(QStringLiteral("All happy families are alike; each unhappy family is unhappy in its own way."));
QTest::newRow("sign") << data << true;
QTest::newRow("notsign") << data << false;
}
void CryptoComposerTest::testSetGnupgHome()
{
QFETCH(QString, data);
QFETCH(bool, sign);
bool encrypt(true);
KMime::Message::Ptr message;
QTemporaryDir dir;
{
Composer composer;
fillComposerData(&composer, data);
fillComposerCryptoData(&composer);
composer.setGnupgHome(dir.path());
composer.setSignAndEncrypt(sign, encrypt);
composer.setMessageCryptoFormat(Kleo::OpenPGPMIMEFormat);
QCOMPARE(composer.exec(), false);
}
const std::vector<GpgME::Key> &keys = Test::getKeys();
for (const auto key : keys) {
Test::populateKeyring(dir.path(), key);
}
{
Composer composer;
fillComposerData(&composer, data);
fillComposerCryptoData(&composer);
composer.setGnupgHome(dir.path());
composer.setSignAndEncrypt(sign, encrypt);
composer.setMessageCryptoFormat(Kleo::OpenPGPMIMEFormat);
VERIFYEXEC((&composer));
QCOMPARE(composer.resultMessages().size(), 1);
message = composer.resultMessages().first();
}
ComposerTestUtil::verify(sign, encrypt, message.data(), data.toUtf8(), Kleo::OpenPGPMIMEFormat, Headers::CE7Bit);
QCOMPARE(message->from()->asUnicodeString(), QString::fromLocal8Bit("me@me.me"));
QCOMPARE(message->to()->asUnicodeString(), QString::fromLocal8Bit("you@you.you"));
}
void CryptoComposerTest::testAutocryptHeaders_data()
{
QTest::addColumn<QString>("data");
QTest::addColumn<bool>("encrypt");
QTest::addColumn<bool>("sign");
QString data(QStringLiteral("All happy families are alike; each unhappy family is unhappy in its own way."));
QTest::newRow("encrypt+sign") << data << true << true;
QTest::newRow("encrypt") << data << true << false;
QTest::newRow("sign") << data << false << true;
QTest::newRow("noencrypt+nosign") << data << false << false;
}
void CryptoComposerTest::testAutocryptHeaders()
{
QFETCH(QString, data);
QFETCH(bool, encrypt);
QFETCH(bool, sign);
std::vector<GpgME::Key> keys = Test::getKeys();
KMime::Message::Ptr message;
QTemporaryDir dir;
{
Composer composer;
fillComposerData(&composer, data);
fillComposerCryptoData(&composer);
composer.setAutocryptEnabled(true);
composer.setSenderEncryptionKey(keys[0]);
composer.setGnupgHome(dir.path());
composer.setSignAndEncrypt(sign, encrypt);
composer.setMessageCryptoFormat(Kleo::OpenPGPMIMEFormat);
QCOMPARE(composer.exec(), false);
}
for (const auto key : keys) {
Test::populateKeyring(dir.path(), key);
}
{
Composer composer;
fillComposerData(&composer, data);
fillComposerCryptoData(&composer);
composer.setAutocryptEnabled(true);
composer.setSenderEncryptionKey(keys[0]);
composer.setGnupgHome(dir.path());
composer.setSignAndEncrypt(sign, encrypt);
composer.setMessageCryptoFormat(Kleo::OpenPGPMIMEFormat);
VERIFYEXEC((&composer));
QCOMPARE(composer.resultMessages().size(), 1);
message = composer.resultMessages().first();
}
if (sign || encrypt) {
ComposerTestUtil::verify(sign, encrypt, message.data(), data.toUtf8(), Kleo::OpenPGPMIMEFormat, Headers::CE7Bit);
}
QVERIFY(message->headerByType("autocrypt"));
QVERIFY(message->headerByType("Autocrypt")
->asUnicodeString()
.startsWith(QStringLiteral("addr=me@me.me; keydata= mQENBEr9ij4BCADaFvyhzV7IrCAr/sCvfoPerAd4dYIGTeCeBmInu3p4oEG0rXTB2zL2t9zd7jV")));
QVERIFY(!message->headerByType("autocrypt-gossip"));
QCOMPARE(message->from()->asUnicodeString(), QString::fromLocal8Bit("me@me.me"));
QCOMPARE(message->to()->asUnicodeString(), QString::fromLocal8Bit("you@you.you"));
}
void CryptoComposerTest::testAutocryptGossip_data()
{
QTest::addColumn<QString>("data");
QTest::addColumn<bool>("encrypt");
QTest::addColumn<bool>("sign");
QTest::addColumn<QStringList>("recipients");
QString data(QStringLiteral("All happy families are alike; each unhappy family is unhappy in its own way."));
QStringList recipients({QStringLiteral("you@you.you")});
QTest::newRow("encrypt") << data << true << false << recipients;
QTest::newRow("encrypt+sign") << data << true << true << recipients;
QTest::newRow("sign") << data << false << true << recipients;
recipients.append(QStringLiteral("leo@kdab.com"));
QTest::newRow("encrypt-multiple") << data << true << false << recipients;
QTest::newRow("encrypt+sign-multiple") << data << true << true << recipients;
QTest::newRow("sign-multiple") << data << false << true << recipients;
}
void CryptoComposerTest::testAutocryptGossip()
{
QFETCH(QString, data);
QFETCH(bool, encrypt);
QFETCH(bool, sign);
QFETCH(QStringList, recipients);
std::vector<GpgME::Key> keys = Test::getKeys();
KMime::Message::Ptr message;
{
Composer composer;
fillComposerData(&composer, data);
composer.infoPart()->setTo(recipients);
fillComposerCryptoData(&composer);
if (recipients.size() > 1) {
auto job = QGpgME::openpgp()->keyListJob(false);
std::vector<GpgME::Key> eKeys;
GpgME::KeyListResult res = job->exec(recipients, false, eKeys);
QVERIFY(!res.error());
QCOMPARE(eKeys.size(), 1);
eKeys.push_back(keys[0]);
eKeys.push_back(keys[1]);
QVector<QPair<QStringList, std::vector<GpgME::Key>>> encKeys;
encKeys.append({recipients, eKeys});
composer.setEncryptionKeys(encKeys);
}
composer.setAutocryptEnabled(true);
composer.setSenderEncryptionKey(keys[0]);
composer.setSignAndEncrypt(sign, encrypt);
composer.setMessageCryptoFormat(Kleo::OpenPGPMIMEFormat);
VERIFYEXEC((&composer));
QCOMPARE(composer.resultMessages().size(), 1);
message = composer.resultMessages().first();
}
if (sign || encrypt) {
ComposerTestUtil::verify(sign, encrypt, message.data(), data.toUtf8(), Kleo::OpenPGPMIMEFormat, Headers::CE7Bit);
}
MimeTreeParser::SimpleObjectTreeSource testSource;
MimeTreeParser::NodeHelper nh;
MimeTreeParser::ObjectTreeParser otp(&testSource, &nh);
testSource.setDecryptMessage(true);
otp.parseObjectTree(message.data());
QVERIFY(!message->headerByType("autocrypt-gossip"));
if (encrypt && recipients.size() > 1) {
QCOMPARE(nh.headers("autocrypt-gossip", message.data()).size(), 2);
auto headers = QStringList();
for (const auto header : nh.headers("autocrypt-gossip", message.data())) {
headers.append(header->asUnicodeString());
}
headers.sort();
QVERIFY(
headers[0].startsWith(QStringLiteral("addr=leo@kdab.com; keydata= mQINBEr4pSwBEADG/B1VaoxT7mnQfwekkY+f8wkqFVLvTwN0W59Ze/pxmuRf/iS0tZjsEiPK0za")));
QVERIFY(
headers[1].startsWith(QStringLiteral("addr=you@you.com; keydata= mI0ESw2YuAEEALakcld4goNkwIL5gMETM3R+zI+AoJcuQWUpvS7AqwyR9/UAkVd3D+r32CgWhFi")));
} else {