File indexing completed on 2024-06-02 05:29:15

0001 /*
0002   SPDX-FileCopyrightText: 2009 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.net
0003   SPDX-FileCopyrightText: 2009 Leo Franchi <lfranchi@kde.org>
0004 
0005   SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "signjobtest.h"
0009 
0010 #include "cryptofunctions.h"
0011 #include "qtest_messagecomposer.h"
0012 #include <QTest>
0013 
0014 #include <KMime/Content>
0015 
0016 #include <Libkleo/Enum>
0017 
0018 #include <MessageComposer/Composer>
0019 #include <MessageComposer/SignJob>
0020 #include <MessageComposer/TransparentJob>
0021 
0022 #include "setupenv.h"
0023 #include <MessageCore/NodeHelper>
0024 
0025 #include <cstdlib>
0026 
0027 QTEST_MAIN(SignJobTest)
0028 
0029 using namespace MessageComposer;
0030 
0031 void SignJobTest::initTestCase()
0032 {
0033     Test::setupEnv();
0034 }
0035 
0036 void SignJobTest::testContentDirect()
0037 {
0038     const std::vector<GpgME::Key> &keys = Test::getKeys();
0039 
0040     Composer composer;
0041     auto sJob = new SignJob(&composer);
0042 
0043     QVERIFY(sJob);
0044 
0045     const QByteArray data(QString::fromLocal8Bit("one flew over the cuckoo's nest").toUtf8());
0046     auto content = new KMime::Content;
0047     content->setBody(data);
0048 
0049     sJob->setContent(content);
0050     sJob->setCryptoMessageFormat(Kleo::OpenPGPMIMEFormat);
0051     sJob->setSigningKeys(keys);
0052 
0053     checkSignJob(sJob);
0054 }
0055 
0056 void SignJobTest::testContentChained()
0057 {
0058     const std::vector<GpgME::Key> &keys = Test::getKeys();
0059 
0060     const QByteArray data(QString::fromLocal8Bit("one flew over the cuckoo's nest").toUtf8());
0061     auto content = new KMime::Content;
0062     content->setBody(data);
0063 
0064     auto tJob = new TransparentJob;
0065     tJob->setContent(content);
0066 
0067     Composer composer;
0068     auto sJob = new SignJob(&composer);
0069 
0070     sJob->setCryptoMessageFormat(Kleo::OpenPGPMIMEFormat);
0071     sJob->setSigningKeys(keys);
0072 
0073     sJob->appendSubjob(tJob);
0074 
0075     checkSignJob(sJob);
0076 }
0077 
0078 void SignJobTest::testHeaders()
0079 {
0080     const std::vector<GpgME::Key> &keys = Test::getKeys();
0081 
0082     Composer composer;
0083     auto sJob = new SignJob(&composer);
0084 
0085     QVERIFY(sJob);
0086 
0087     const QByteArray data(QString::fromLocal8Bit("one flew over the cuckoo's nest").toUtf8());
0088     auto content = new KMime::Content;
0089     content->setBody(data);
0090 
0091     sJob->setContent(content);
0092     sJob->setCryptoMessageFormat(Kleo::OpenPGPMIMEFormat);
0093     sJob->setSigningKeys(keys);
0094 
0095     VERIFYEXEC(sJob);
0096 
0097     KMime::Content *result = sJob->content();
0098     result->assemble();
0099 
0100     QVERIFY(result->contentType(false));
0101     QCOMPARE(result->contentType()->mimeType(), "multipart/signed");
0102     QCOMPARE(result->contentType()->charset(), "ISO-8859-1");
0103     QVERIFY(result->contentType()
0104                 ->parameter(QString::fromLocal8Bit("micalg"))
0105                 .startsWith(QLatin1StringView("pgp-sha"))); // sha1 or sha256, depending on GnuPG version
0106     QCOMPARE(result->contentType()->parameter(QString::fromLocal8Bit("protocol")), QString::fromLocal8Bit("application/pgp-signature"));
0107     QCOMPARE(result->contentTransferEncoding()->encoding(), KMime::Headers::CE7Bit);
0108 
0109     delete result;
0110 }
0111 
0112 void SignJobTest::testRecommentationRFC3156()
0113 {
0114     const std::vector<GpgME::Key> &keys = Test::getKeys();
0115 
0116     const QString data = QStringLiteral("=2D Magic foo\nFrom test\n\n-- quaak\nOhno");
0117     KMime::Headers::contentEncoding cte = KMime::Headers::CEquPr;
0118 
0119     Composer composer;
0120     auto sJob = new SignJob(&composer);
0121 
0122     QVERIFY(sJob);
0123 
0124     auto content = new KMime::Content;
0125     content->setBody(data.toUtf8());
0126 
0127     sJob->setContent(content);
0128     sJob->setCryptoMessageFormat(Kleo::OpenPGPMIMEFormat);
0129     sJob->setSigningKeys(keys);
0130 
0131     VERIFYEXEC(sJob);
0132 
0133     KMime::Content *result = sJob->content();
0134     result->assemble();
0135 
0136     const QByteArray body = MessageCore::NodeHelper::firstChild(result)->body();
0137     QCOMPARE(QString::fromUtf8(body), QStringLiteral("=3D2D Magic foo\n=46rom test\n\n=2D- quaak\nOhno"));
0138 
0139     ComposerTestUtil::verify(true, false, result, data.toUtf8(), Kleo::OpenPGPMIMEFormat, cte);
0140     delete result;
0141 }
0142 
0143 void SignJobTest::testMixedContent()
0144 {
0145     const std::vector<GpgME::Key> &keys = Test::getKeys();
0146 
0147     const QString data = QStringLiteral("=2D Magic foo\nFrom test\n\n-- quaak\nOhno");
0148 
0149     Composer composer;
0150     auto sJob = new SignJob(&composer);
0151 
0152     QVERIFY(sJob);
0153 
0154     auto content = new KMime::Content;
0155     content->contentType()->setMimeType(QByteArrayLiteral("multipart/mixed"));
0156     content->contentType()->setBoundary(KMime::multiPartBoundary());
0157     auto subcontent = new KMime::Content;
0158     subcontent->contentType()->setMimeType(QByteArrayLiteral("text/plain"));
0159     subcontent->setBody(data.toUtf8());
0160     auto attachment = new KMime::Content;
0161     attachment->contentType()->setMimeType(QByteArrayLiteral("text/plain"));
0162     const QByteArray attachmentData("an attachment");
0163     attachment->setBody(attachmentData);
0164 
0165     content->appendContent(subcontent);
0166     content->appendContent(attachment);
0167     content->assemble();
0168 
0169     sJob->setContent(content);
0170     sJob->setCryptoMessageFormat(Kleo::OpenPGPMIMEFormat);
0171     sJob->setSigningKeys(keys);
0172 
0173     VERIFYEXEC(sJob);
0174 
0175     KMime::Content *result = sJob->content();
0176     result->assemble();
0177 
0178     KMime::Content *firstChild = MessageCore::NodeHelper::firstChild(result);
0179     QCOMPARE(result->contents().count(), 2);
0180     QCOMPARE(firstChild->contents().count(), 2);
0181     QCOMPARE(firstChild->body(), QByteArray());
0182     QCOMPARE(firstChild->contentType()->mimeType(), QByteArrayLiteral("multipart/mixed"));
0183     QCOMPARE(firstChild->contents()[0]->body(), data.toUtf8());
0184     QCOMPARE(firstChild->contents()[1]->body(), attachmentData);
0185 
0186     ComposerTestUtil::verify(true, false, result, data.toUtf8(), Kleo::OpenPGPMIMEFormat, KMime::Headers::CE7Bit);
0187     delete result;
0188 }
0189 
0190 void SignJobTest::checkSignJob(SignJob *sJob)
0191 {
0192     VERIFYEXEC(sJob);
0193 
0194     KMime::Content *result = sJob->content();
0195     Q_ASSERT(result);
0196     result->assemble();
0197 
0198     ComposerTestUtil::verifySignature(result,
0199                                       QString::fromLocal8Bit("one flew over the cuckoo's nest").toUtf8(),
0200                                       Kleo::OpenPGPMIMEFormat,
0201                                       KMime::Headers::CE7Bit);
0202 
0203     delete result;
0204 }
0205 
0206 void SignJobTest::testProtectedHeaders_data()
0207 {
0208     QTest::addColumn<bool>("protectedHeaders");
0209     QTest::addColumn<QString>("referenceFile");
0210 
0211     QTest::newRow("simple-non-obvoscate") << true << QStringLiteral("protected_headers-non-obvoscate.mbox");
0212     QTest::newRow("non-protected_headers") << false << QStringLiteral("non-protected_headers.mbox");
0213 }
0214 
0215 void SignJobTest::testProtectedHeaders()
0216 {
0217     QFETCH(bool, protectedHeaders);
0218     QFETCH(QString, referenceFile);
0219 
0220     const std::vector<GpgME::Key> &keys = Test::getKeys();
0221 
0222     Composer composer;
0223     auto sJob = new SignJob(&composer);
0224 
0225     QVERIFY(sJob);
0226 
0227     const QByteArray data(QString::fromLocal8Bit("one flew over the cuckoo's nest").toUtf8());
0228     const QString subject(QStringLiteral("asdfghjklö"));
0229 
0230     auto content = new KMime::Content;
0231     content->contentType(true)->setMimeType("text/plain");
0232     content->setBody(data);
0233 
0234     KMime::Message skeletonMessage;
0235     skeletonMessage.contentType(true)->setMimeType("foo/bla");
0236     skeletonMessage.to(true)->from7BitString("to@test.de, to2@test.de");
0237     skeletonMessage.cc(true)->from7BitString("cc@test.de, cc2@test.de");
0238     skeletonMessage.bcc(true)->from7BitString("bcc@test.de, bcc2@test.de");
0239     skeletonMessage.subject(true)->fromUnicodeString(subject, "utf-8");
0240 
0241     sJob->setContent(content);
0242     sJob->setCryptoMessageFormat(Kleo::OpenPGPMIMEFormat);
0243     sJob->setSigningKeys(keys);
0244     sJob->setSkeletonMessage(&skeletonMessage);
0245     sJob->setProtectedHeaders(protectedHeaders);
0246 
0247     VERIFYEXEC(sJob);
0248 
0249     QCOMPARE(skeletonMessage.subject()->asUnicodeString(), subject);
0250 
0251     KMime::Content *result = sJob->content();
0252     result->assemble();
0253 
0254     Test::compareFile(result->contents().at(0), QStringLiteral(MAIL_DATA_DIR "/") + referenceFile);
0255 }
0256 
0257 void SignJobTest::testProtectedHeadersOverwrite()
0258 {
0259     const std::vector<GpgME::Key> &keys = Test::getKeys();
0260     const auto referenceFile = QStringLiteral("protected_headers-non-obvoscate.mbox");
0261 
0262     Composer composer;
0263     auto sJob = new SignJob(&composer);
0264 
0265     QVERIFY(sJob);
0266 
0267     const QByteArray data(QString::fromLocal8Bit("one flew over the cuckoo's nest").toUtf8());
0268     const QString subject(QStringLiteral("asdfghjklö"));
0269 
0270     auto content = new KMime::Content;
0271     content->contentType(true)->setMimeType("text/plain");
0272     content->setBody(data);
0273 
0274     KMime::Message skeletonMessage;
0275     skeletonMessage.contentType(true)->setMimeType("foo/bla");
0276     skeletonMessage.to(true)->from7BitString("to@test.de, to2@test.de");
0277     skeletonMessage.cc(true)->from7BitString("cc@test.de, cc2@test.de");
0278     skeletonMessage.bcc(true)->from7BitString("bcc@test.de, bcc2@test.de");
0279     skeletonMessage.subject(true)->fromUnicodeString(subject, "utf-8");
0280 
0281     sJob->setContent(content);
0282     sJob->setCryptoMessageFormat(Kleo::OpenPGPMIMEFormat);
0283     sJob->setSigningKeys(keys);
0284     sJob->setSkeletonMessage(&skeletonMessage);
0285     sJob->setProtectedHeaders(true);
0286 
0287     VERIFYEXEC(sJob);
0288 
0289     QCOMPARE(skeletonMessage.subject()->asUnicodeString(), subject);
0290     skeletonMessage.to()->from7BitString("overwrite@example.org");
0291     skeletonMessage.cc()->from7BitString("cc_overwrite@example.org");
0292     skeletonMessage.bcc()->from7BitString("bcc_overwrite@example.org");
0293     skeletonMessage.subject()->fromUnicodeString(subject + QStringLiteral("_owerwrite"), "utf-8");
0294 
0295     KMime::Content *result = sJob->content();
0296     result->assemble();
0297 
0298     Test::compareFile(result->contents().at(0), QStringLiteral(MAIL_DATA_DIR "/") + referenceFile);
0299 }
0300 
0301 void SignJobTest::testProtectedHeadersSkipLong()
0302 {
0303     const std::vector<GpgME::Key> &keys = Test::getKeys();
0304     const auto referenceFile = QStringLiteral("protected_headers-non-obvoscate.mbox");
0305 
0306     Composer composer;
0307     auto sJob = new SignJob(&composer);
0308 
0309     QVERIFY(sJob);
0310 
0311     const QByteArray data(QString::fromLocal8Bit("one flew over the cuckoo's nest").toUtf8());
0312     const QString subject(QStringLiteral("asdfghjklö"));
0313 
0314     auto content = new KMime::Content;
0315     content->contentType(true)->setMimeType("text/plain");
0316     content->setBody(data);
0317 
0318     KMime::Message skeletonMessage;
0319     auto face = new KMime::Headers::Generic("Face");
0320     face->from7BitString(
0321         "iVBORw0KGgoAAAANSUhEUgAAADAAAAAwBAMAAAClLOS0AAAAElBMVEX9/ftIS5IsNImXjKgc\n"
0322         " I3Dh29sdnA7JAAAB60lEQVR4nI2US5ajMAxFXeF4HtlhXlZ7AdCCBWCcORTx/rfSkgyEyqg14uii\n"
0323         " pw8SpnzaRmrm/8yWD0dZqn80G5WlUOVEnsCPNKp6D2w98JsWHcJuiOiCPnXGNPDIU/U7BuKPGe7G\n"
0324         " fAW/rNPhl+A4bqkC6IZDiwOwXb0CnKBtpjMJhgZoB96bXl5Gtftfr+AHJ4LlhRm8VOvmJlCvwM3U\n"
0325         " d41f9maX1dOADG4cQe1lDD3RAzVH+DXMhhUTfjOIkWexvAHRkCSiyZmuYBWlGpGfV0CihDyrW8yh\n"
0326         " bOYDaFU5d+8AU8YXqtQNEz5P92KskWFq54h/ONWs4OtpbDoiGCzmNatc05rmBE6AtQpKsdsOfoLD\n"
0327         " UBgci6JAqgqI8bI2F+AwjleAew6OwFYSWA8CVgFdrQqfCnIW8MKAdexscwWTbOML8Q0myV4y8218\n"
0328         " 7FIKomwvf5bO0ph+gZFbZCD7q2DZAc7nyQxv4FSLTcHjkLo5KS/Ox41lPHIExyEZRb4lP5zgR7c8\n"
0329         " BV822d6UTiAjiQnGVfyyIZfpYmZg+gpiTvHsg4EzHOIVsMC9lsvP0He2B+qHmHOO6fsAAehpqSc+\n"
0330         " yBNIF1wUgOXzCJDVBIB0Icfd2vKAN2jAubRfOvt3INsO+3XvdgGBgAH/OepfoIJ/RyuNKjhmbCAA\n"
0331         " AAAASUVORK5CYII=");
0332     skeletonMessage.contentType(true)->setMimeType("foo/bla");
0333     skeletonMessage.to(true)->from7BitString("to@test.de, to2@test.de");
0334     skeletonMessage.cc(true)->from7BitString("cc@test.de, cc2@test.de");
0335     skeletonMessage.bcc(true)->from7BitString("bcc@test.de, bcc2@test.de");
0336     skeletonMessage.appendHeader(face);
0337     skeletonMessage.subject(true)->fromUnicodeString(subject, "utf-8");
0338 
0339     sJob->setContent(content);
0340     sJob->setCryptoMessageFormat(Kleo::OpenPGPMIMEFormat);
0341     sJob->setSigningKeys(keys);
0342     sJob->setSkeletonMessage(&skeletonMessage);
0343     sJob->setProtectedHeaders(true);
0344 
0345     VERIFYEXEC(sJob);
0346 
0347     KMime::Content *result = sJob->content();
0348     result->assemble();
0349 
0350     Test::compareFile(result->contents().at(0), QStringLiteral(MAIL_DATA_DIR "/") + referenceFile);
0351 }
0352 
0353 #include "moc_signjobtest.cpp"