File indexing completed on 2024-05-12 05:28:16
0001 // SPDX-FileCopyrightText: 2009 Constantin Berzan <exit3219@gmail.com> 0002 // SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com 0003 // SPDX-FileCopyrightText: 2010 Leo Franchi <lfranchi@kde.org> 0004 // SPDX-FileCopyrightText: 2017 Christian Mollekopf <mollekopf@kolabsys.com> 0005 // SPDX-License-Identifier: LGPL-2.0-or-later 0006 // 0007 #include "mailcrypto.h" 0008 0009 #include <QDebug> 0010 0011 #include <future> 0012 #include <utility> 0013 0014 using namespace MailCrypto; 0015 using namespace Crypto; 0016 0017 #ifdef _WIN32 0018 Expected<Error, std::unique_ptr<KMime::Content>> 0019 MailCrypto::processCrypto(std::unique_ptr<KMime::Content> content, const std::vector<Key> &signingKeys, const std::vector<Key> &encryptionKeys) 0020 { 0021 Q_UNUSED(content) 0022 Q_UNUSED(signingKeys) 0023 Q_UNUSED(encryptionKeys) 0024 return makeUnexpected(Error{}); 0025 } 0026 #else 0027 static QByteArray canonicalizeContent(KMime::Content *content) 0028 { 0029 // if (d->format & Kleo::InlineOpenPGPFormat) { 0030 // return d->content->body(); 0031 // } else if (!(d->format & Kleo::SMIMEOpaqueFormat)) { 0032 0033 // replace "From " and "--" at the beginning of lines 0034 // with encoded versions according to RfC 3156, 3 0035 // Note: If any line begins with the string "From ", it is strongly 0036 // suggested that either the Quoted-Printable or Base64 MIME encoding 0037 // be applied. 0038 const auto encoding = content->contentTransferEncoding()->encoding(); 0039 if ((encoding == KMime::Headers::CEquPr || encoding == KMime::Headers::CE7Bit) && !content->contentType(false)) { 0040 QByteArray body = content->encodedBody(); 0041 bool changed = false; 0042 QList<QByteArray> search; 0043 QList<QByteArray> replacements; 0044 0045 search << "From " 0046 << "from " 0047 << "-"; 0048 replacements << "From=20" 0049 << "from=20" 0050 << "=2D"; 0051 0052 if (content->contentTransferEncoding()->encoding() == KMime::Headers::CE7Bit) { 0053 for (int i = 0; i < search.size(); ++i) { 0054 const auto pos = body.indexOf(search[i]); 0055 if (pos == 0 || (pos > 0 && body.at(pos - 1) == '\n')) { 0056 changed = true; 0057 break; 0058 } 0059 } 0060 if (changed) { 0061 content->contentTransferEncoding()->setEncoding(KMime::Headers::CEquPr); 0062 content->assemble(); 0063 body = content->encodedBody(); 0064 } 0065 } 0066 0067 for (int i = 0; i < search.size(); ++i) { 0068 const auto pos = body.indexOf(search[i]); 0069 if (pos == 0 || (pos > 0 && body.at(pos - 1) == '\n')) { 0070 changed = true; 0071 body.replace(pos, search[i].size(), replacements[i]); 0072 } 0073 } 0074 0075 if (changed) { 0076 qDebug() << "Content changed"; 0077 content->setBody(body); 0078 content->contentTransferEncoding()->setDecoded(false); 0079 } 0080 } 0081 0082 return KMime::LFtoCRLF(content->encodedContent()); 0083 // } else { // SMimeOpaque doesn't need LFtoCRLF, else it gets munged 0084 // return content->encodedContent(); 0085 // } 0086 } 0087 0088 /** 0089 * Create a message part like this (according to RFC 3156 Section 4): 0090 * 0091 * - multipart/encrypted 0092 * - application/pgp-encrypted (version information) 0093 * - application/octet-stream (given encrypted data) 0094 * 0095 * The encrypted data can be generated by the `encrypt` or `signAndEncrypt` functions. 0096 */ 0097 std::unique_ptr<KMime::Content> createEncryptedPart(QByteArray encryptedData) 0098 { 0099 auto result = std::unique_ptr<KMime::Content>(new KMime::Content); 0100 0101 result->contentType()->setMimeType("multipart/encrypted"); 0102 result->contentType()->setBoundary(KMime::multiPartBoundary()); 0103 result->contentType()->setParameter(QStringLiteral("protocol"), QStringLiteral("application/pgp-encrypted")); 0104 0105 KMime::Content *controlInformation = new KMime::Content; 0106 { 0107 controlInformation->contentType()->setMimeType("application/pgp-encrypted"); 0108 controlInformation->contentDescription()->from7BitString("PGP/MIME version identification"); 0109 controlInformation->setBody("Version: 1"); 0110 0111 result->addContent(controlInformation); 0112 } 0113 0114 KMime::Content *encryptedPartPart = new KMime::Content; 0115 { 0116 const QString filename = QStringLiteral("msg.asc"); 0117 0118 encryptedPartPart->contentType()->setMimeType("application/octet-stream"); 0119 encryptedPartPart->contentType()->setName(filename, "utf-8"); 0120 0121 encryptedPartPart->contentDescription()->from7BitString("OpenPGP encrypted message"); 0122 0123 encryptedPartPart->contentDisposition()->setDisposition(KMime::Headers::CDinline); 0124 encryptedPartPart->contentDisposition()->setFilename(filename); 0125 0126 encryptedPartPart->setBody(encryptedData); 0127 0128 result->addContent(encryptedPartPart); 0129 } 0130 0131 return result; 0132 } 0133 0134 /** 0135 * Create a message part like this (according to RFC 3156 Section 5): 0136 * 0137 * + `multipart/signed` 0138 * - whatever the given original `message` is (should be canonicalized) 0139 * - `application/octet-stream` (the given `signature`) 0140 * 0141 * The signature can be generated by the `sign` function. 0142 */ 0143 std::unique_ptr<KMime::Content> createSignedPart(std::unique_ptr<KMime::Content> message, const QByteArray &signature, const QString &micAlg) 0144 { 0145 auto result = std::unique_ptr<KMime::Content>(new KMime::Content); 0146 0147 result->contentType()->setMimeType("multipart/signed"); 0148 result->contentType()->setBoundary(KMime::multiPartBoundary()); 0149 result->contentType()->setParameter(QStringLiteral("micalg"), micAlg); 0150 result->contentType()->setParameter(QStringLiteral("protocol"), QStringLiteral("application/pgp-signature")); 0151 0152 result->addContent(message.release()); 0153 0154 KMime::Content *signedPartPart = new KMime::Content; 0155 signedPartPart->contentType()->setMimeType("application/pgp-signature"); 0156 signedPartPart->contentType()->setName(QStringLiteral("signature.asc"), "utf-8"); 0157 signedPartPart->contentDisposition(true)->setDisposition(KMime::Headers::CDattachment); 0158 signedPartPart->contentDisposition(true)->setFilename(QStringLiteral("signature.asc")); 0159 signedPartPart->contentDescription()->from7BitString("OpenPGP digital signature"); 0160 signedPartPart->setBody(signature); 0161 result->addContent(signedPartPart); 0162 0163 return result; 0164 } 0165 0166 Expected<Error, std::unique_ptr<KMime::Content>> 0167 MailCrypto::processCrypto(std::unique_ptr<KMime::Content> content, const std::vector<Key> &signingKeys, const std::vector<Key> &encryptionKeys) 0168 { 0169 if (!encryptionKeys.empty()) { 0170 auto encryptionResult = signAndEncrypt(canonicalizeContent(content.get()), encryptionKeys, signingKeys); 0171 if (!encryptionResult) { 0172 return makeUnexpected(Error{encryptionResult.error()}); 0173 } 0174 return createEncryptedPart(encryptionResult.value()); 0175 } else if (!signingKeys.empty()) { 0176 auto signingResult = sign(canonicalizeContent(content.get()), signingKeys); 0177 if (!signingResult) { 0178 return makeUnexpected(Error{signingResult.error()}); 0179 } 0180 QByteArray signingData; 0181 QString micAlg; 0182 std::tie(signingData, micAlg) = signingResult.value(); 0183 return createSignedPart(std::move(content), signingData, micAlg); 0184 } else { 0185 qWarning() << "Processing cryptography, but neither signing nor encrypting"; 0186 Q_ASSERT(false); 0187 return content; 0188 } 0189 } 0190 #endif