diff options
author | Minijackson <minijackson@riseup.net> | 2018-03-07 17:30:50 +0100 |
---|---|---|
committer | Minijackson <minijackson@riseup.net> | 2018-03-08 16:29:10 +0100 |
commit | 7ab399ce3b4b33439fe2607cf38e3f27a7d6008e (patch) | |
tree | 6f2f35c27ab0661beb84f3dce551664308ece19c /framework/src/domain/mime/mailcrypto.cpp | |
parent | 22e745e2cb5949c6a529af076d1471ace79dff13 (diff) | |
download | kube-7ab399ce3b4b33439fe2607cf38e3f27a7d6008e.tar.gz kube-7ab399ce3b4b33439fe2607cf38e3f27a7d6008e.zip |
Use Expected in crypto + pass attached key + add bool part of Expected + remove old code
Diffstat (limited to 'framework/src/domain/mime/mailcrypto.cpp')
-rw-r--r-- | framework/src/domain/mime/mailcrypto.cpp | 568 |
1 files changed, 143 insertions, 425 deletions
diff --git a/framework/src/domain/mime/mailcrypto.cpp b/framework/src/domain/mime/mailcrypto.cpp index 4a26829f..f25c75fe 100644 --- a/framework/src/domain/mime/mailcrypto.cpp +++ b/framework/src/domain/mime/mailcrypto.cpp | |||
@@ -21,6 +21,8 @@ | |||
21 | */ | 21 | */ |
22 | #include "mailcrypto.h" | 22 | #include "mailcrypto.h" |
23 | 23 | ||
24 | #include "framework/src/errors.h" | ||
25 | |||
24 | #include <QGpgME/DataProvider> | 26 | #include <QGpgME/DataProvider> |
25 | #include <QGpgME/EncryptJob> | 27 | #include <QGpgME/EncryptJob> |
26 | #include <QGpgME/ExportJob> | 28 | #include <QGpgME/ExportJob> |
@@ -41,119 +43,6 @@ | |||
41 | #include <future> | 43 | #include <future> |
42 | #include <utility> | 44 | #include <utility> |
43 | 45 | ||
44 | /* | ||
45 | * FIXME: | ||
46 | * | ||
47 | * This code is WIP. | ||
48 | * It currently only implements OpenPGPMIMEFormat for signing. | ||
49 | * All the commented code are intentional leftovers that we can clean-up | ||
50 | * once all necessary signing mechanisms have been implemented. | ||
51 | * | ||
52 | * Creating an ecrypted mail: | ||
53 | * * get keys (email -> fingreprint -> key) | ||
54 | * * Use Kleo::OpenPGPMIMEFormat, | ||
55 | * | ||
56 | */ | ||
57 | |||
58 | // bool chooseCTE() | ||
59 | // { | ||
60 | // Q_Q(SinglepartJob); | ||
61 | |||
62 | // auto allowed = KMime::encodingsForData(data); | ||
63 | |||
64 | // if (!q->globalPart()->is8BitAllowed()) { | ||
65 | // allowed.removeAll(KMime::Headers::CE8Bit); | ||
66 | // } | ||
67 | |||
68 | // #if 0 //TODO signing | ||
69 | // // In the following cases only QP and Base64 are allowed: | ||
70 | // // - the buffer will be OpenPGP/MIME signed and it contains trailing | ||
71 | // // whitespace (cf. RFC 3156) | ||
72 | // // - a line starts with "From " | ||
73 | // if ((willBeSigned && cf.hasTrailingWhitespace()) || | ||
74 | // cf.hasLeadingFrom()) { | ||
75 | // ret.removeAll(DwMime::kCte8bit); | ||
76 | // ret.removeAll(DwMime::kCte7bit); | ||
77 | // } | ||
78 | // #endif | ||
79 | |||
80 | // if (contentTransferEncoding) { | ||
81 | // // Specific CTE set. Check that our data fits in it. | ||
82 | // if (!allowed.contains(contentTransferEncoding->encoding())) { | ||
83 | // q->setError(JobBase::BugError); | ||
84 | // q->setErrorText(i18n("%1 Content-Transfer-Encoding cannot correctly encode this message.", | ||
85 | // KMime::nameForEncoding(contentTransferEncoding->encoding()))); | ||
86 | // return false; | ||
87 | // // TODO improve error message in case 8bit is requested but not allowed. | ||
88 | // } | ||
89 | // } else { | ||
90 | // // No specific CTE set. Choose the best one. | ||
91 | // Q_ASSERT(!allowed.isEmpty()); | ||
92 | // contentTransferEncoding = new KMime::Headers::ContentTransferEncoding; | ||
93 | // contentTransferEncoding->setEncoding(allowed.first()); | ||
94 | // } | ||
95 | // qCDebug(MESSAGECOMPOSER_LOG) << "Settled on encoding" << KMime::nameForEncoding(contentTransferEncoding->encoding()); | ||
96 | // return true; | ||
97 | // } | ||
98 | |||
99 | KMime::Content *createPart(const QByteArray &encodedBody, const QByteArray &mimeType, const QByteArray &charset) | ||
100 | { | ||
101 | auto resultContent = new KMime::Content; | ||
102 | |||
103 | auto contentType = new KMime::Headers::ContentType; | ||
104 | contentType->setMimeType(mimeType); | ||
105 | contentType->setCharset(charset); | ||
106 | // if (!chooseCTE()) { | ||
107 | // Q_ASSERT(error()); | ||
108 | // emitResult(); | ||
109 | // return; | ||
110 | // } | ||
111 | |||
112 | // Set headers. | ||
113 | // if (contentDescription) { | ||
114 | // resultContent->setHeader(contentDescription); | ||
115 | // } | ||
116 | // if (contentDisposition) { | ||
117 | // resultContent->setHeader(contentDisposition); | ||
118 | // } | ||
119 | // if (contentID) { | ||
120 | // resultContent->setHeader(contentID); | ||
121 | // } | ||
122 | // Q_ASSERT(contentTransferEncoding); // chooseCTE() created it if it didn't exist. | ||
123 | auto contentTransferEncoding = new KMime::Headers::ContentTransferEncoding; | ||
124 | auto allowed = KMime::encodingsForData(encodedBody); | ||
125 | Q_ASSERT(!allowed.isEmpty()); | ||
126 | contentTransferEncoding->setEncoding(allowed.first()); | ||
127 | resultContent->setHeader(contentTransferEncoding); | ||
128 | |||
129 | if (contentType) { | ||
130 | resultContent->setHeader(contentType); | ||
131 | } | ||
132 | |||
133 | // Set data. | ||
134 | resultContent->setBody(encodedBody); | ||
135 | return resultContent; | ||
136 | } | ||
137 | |||
138 | KMime::Content *setBodyAndCTE(QByteArray &encodedBody, KMime::Headers::ContentType *contentType, KMime::Content *ret) | ||
139 | { | ||
140 | // MessageComposer::Composer composer; | ||
141 | // MessageComposer::SinglepartJob cteJob(&composer); | ||
142 | auto part = createPart(encodedBody, contentType->mimeType(), contentType->charset()); | ||
143 | part->assemble(); | ||
144 | |||
145 | // cteJob.contentType()->setMimeType(contentType->mimeType()); | ||
146 | // cteJob.contentType()->setCharset(contentType->charset()); | ||
147 | // cteJob.setData(encodedBody); | ||
148 | // cteJob.exec(); | ||
149 | // cteJob.content()->assemble(); | ||
150 | |||
151 | ret->contentTransferEncoding()->setEncoding(part->contentTransferEncoding()->encoding()); | ||
152 | ret->setBody(part->encodedBody()); | ||
153 | |||
154 | return ret; | ||
155 | } | ||
156 | |||
157 | // replace simple LFs by CRLFs for all MIME supporting CryptPlugs | 46 | // replace simple LFs by CRLFs for all MIME supporting CryptPlugs |
158 | // according to RfC 2633, 3.1.1 Canonicalization | 47 | // according to RfC 2633, 3.1.1 Canonicalization |
159 | static QByteArray canonicalizeContent(KMime::Content *content) | 48 | static QByteArray canonicalizeContent(KMime::Content *content) |
@@ -220,44 +109,60 @@ static QByteArray canonicalizeContent(KMime::Content *content) | |||
220 | } | 109 | } |
221 | 110 | ||
222 | /** | 111 | /** |
112 | * Get the given `key` in the armor format. | ||
113 | */ | ||
114 | Expected<GpgME::Error, QByteArray> exportPublicKey(const GpgME::Key &key) | ||
115 | { | ||
116 | // Not using the Qt API because it apparently blocks (the `result` signal is never | ||
117 | // triggered) | ||
118 | std::unique_ptr<GpgME::Context> ctx(GpgME::Context::createForProtocol(GpgME::OpenPGP)); | ||
119 | ctx->setArmor(true); | ||
120 | |||
121 | QGpgME::QByteArrayDataProvider dp; | ||
122 | GpgME::Data data(&dp); | ||
123 | |||
124 | qDebug() << "Exporting public key:" << key.shortKeyID(); | ||
125 | auto error = ctx->exportPublicKeys(key.keyID(), data); | ||
126 | |||
127 | if (error.code()) { | ||
128 | return makeUnexpected(error); | ||
129 | } | ||
130 | |||
131 | return dp.data(); | ||
132 | } | ||
133 | |||
134 | /** | ||
223 | * Create an Email with `msg` as a body and `key` as an attachment. | 135 | * Create an Email with `msg` as a body and `key` as an attachment. |
224 | * | 136 | * |
137 | * Will create the given structure: | ||
138 | * | ||
139 | * + `multipart/mixed` | ||
140 | * - the given `msg` | ||
141 | * - `application/pgp-keys` (the given `key` as attachment) | ||
142 | * | ||
225 | * Used by the `createSignedEmail` and `createEncryptedEmail` functions. | 143 | * Used by the `createSignedEmail` and `createEncryptedEmail` functions. |
226 | */ | 144 | */ |
227 | KMime::Content *setMessageAndPublicKey(KMime::Content *msg, const GpgME::Key &key) | 145 | Expected<GpgME::Error, KMime::Content *> appendPublicKey(KMime::Content *msg, const GpgME::Key &key) |
228 | { | 146 | { |
147 | const auto publicKeyExportResult = exportPublicKey(key); | ||
148 | |||
149 | if(!publicKeyExportResult) { | ||
150 | // "Could not export public key" | ||
151 | return makeUnexpected(publicKeyExportResult.error()); | ||
152 | } | ||
153 | |||
154 | const auto publicKeyData = publicKeyExportResult.value(); | ||
155 | |||
229 | auto result = new KMime::Content; | 156 | auto result = new KMime::Content; |
230 | result->contentType()->setMimeType("multipart/mixed"); | 157 | result->contentType()->setMimeType("multipart/mixed"); |
231 | result->contentType()->setBoundary(KMime::multiPartBoundary()); | 158 | result->contentType()->setBoundary(KMime::multiPartBoundary()); |
232 | 159 | ||
233 | KMime::Content *keyAttachment = new KMime::Content; | 160 | KMime::Content *keyAttachment = new KMime::Content; |
234 | { | 161 | { |
235 | // Not using the Qt API because it apparently blocks (the `result` signal is never | ||
236 | // triggered) | ||
237 | std::unique_ptr<GpgME::Context> ctx(GpgME::Context::createForProtocol(GpgME::OpenPGP)); | ||
238 | ctx->setArmor(true); | ||
239 | |||
240 | qDebug() << "Setting up data container"; | ||
241 | QGpgME::QByteArrayDataProvider dp; | ||
242 | GpgME::Data data(&dp); | ||
243 | |||
244 | qDebug() << "Exporting public key"; | ||
245 | auto error = ctx->exportPublicKeys(key.keyID(), data); | ||
246 | |||
247 | if (error.code()) { | ||
248 | qWarning() << "Could not export public key:" << error.asString(); | ||
249 | // TODO: XXX: handle errors better | ||
250 | // throw std::runtime_error("Export public key failed"); | ||
251 | delete result; | ||
252 | return msg; | ||
253 | } | ||
254 | |||
255 | keyAttachment->contentType()->setMimeType("application/pgp-keys"); | 162 | keyAttachment->contentType()->setMimeType("application/pgp-keys"); |
256 | keyAttachment->contentDisposition()->setDisposition(KMime::Headers::CDattachment); | 163 | keyAttachment->contentDisposition()->setDisposition(KMime::Headers::CDattachment); |
257 | keyAttachment->contentDisposition()->setFilename(QString("0x") + key.shortKeyID() + ".asc"); | 164 | keyAttachment->contentDisposition()->setFilename(QString("0x") + key.shortKeyID() + ".asc"); |
258 | qDebug() << "Getting data"; | 165 | keyAttachment->setBody(publicKeyData); |
259 | qWarning() << "Data:" << dp.data().constData(); | ||
260 | keyAttachment->setBody(dp.data()); | ||
261 | } | 166 | } |
262 | 167 | ||
263 | msg->assemble(); | 168 | msg->assemble(); |
@@ -272,7 +177,7 @@ KMime::Content *setMessageAndPublicKey(KMime::Content *msg, const GpgME::Key &ke | |||
272 | return result; | 177 | return result; |
273 | } | 178 | } |
274 | 179 | ||
275 | QByteArray encrypt(const QByteArray &content, const std::vector<GpgME::Key> &encryptionKeys) | 180 | Expected<GpgME::Error, QByteArray> encrypt(const QByteArray &content, const std::vector<GpgME::Key> &encryptionKeys) |
276 | { | 181 | { |
277 | QByteArray resultData; | 182 | QByteArray resultData; |
278 | 183 | ||
@@ -282,15 +187,13 @@ QByteArray encrypt(const QByteArray &content, const std::vector<GpgME::Key> &enc | |||
282 | 187 | ||
283 | if (result.error().code()) { | 188 | if (result.error().code()) { |
284 | qWarning() << "Encryption failed:" << result.error().asString(); | 189 | qWarning() << "Encryption failed:" << result.error().asString(); |
285 | // TODO: XXX: handle errors better | 190 | return makeUnexpected(result.error()); |
286 | // throw std::runtime_error("Signing failed"); | ||
287 | return ""; | ||
288 | } | 191 | } |
289 | 192 | ||
290 | return resultData; | 193 | return resultData; |
291 | } | 194 | } |
292 | 195 | ||
293 | QByteArray signAndEncrypt(const QByteArray &content, const std::vector<GpgME::Key> &signingKeys, | 196 | Expected<GpgME::Error, QByteArray> signAndEncrypt(const QByteArray &content, const std::vector<GpgME::Key> &signingKeys, |
294 | const std::vector<GpgME::Key> &encryptionKeys) | 197 | const std::vector<GpgME::Key> &encryptionKeys) |
295 | { | 198 | { |
296 | QByteArray resultData; | 199 | QByteArray resultData; |
@@ -301,26 +204,29 @@ QByteArray signAndEncrypt(const QByteArray &content, const std::vector<GpgME::Ke | |||
301 | 204 | ||
302 | if (result.first.error().code()) { | 205 | if (result.first.error().code()) { |
303 | qWarning() << "Signing failed:" << result.first.error().asString(); | 206 | qWarning() << "Signing failed:" << result.first.error().asString(); |
304 | // TODO: XXX: handle errors better | 207 | return makeUnexpected(result.first.error()); |
305 | // throw std::runtime_error("Signing failed"); | ||
306 | return ""; | ||
307 | } | 208 | } |
308 | 209 | ||
309 | if (result.second.error().code()) { | 210 | if (result.second.error().code()) { |
310 | qWarning() << "Encryption failed:" << result.second.error().asString(); | 211 | qWarning() << "Encryption failed:" << result.second.error().asString(); |
311 | // TODO: XXX: handle errors better | 212 | return makeUnexpected(result.second.error()); |
312 | // throw std::runtime_error("Encryption failed"); | ||
313 | return ""; | ||
314 | } | 213 | } |
315 | 214 | ||
316 | return resultData; | 215 | return resultData; |
317 | } | 216 | } |
318 | 217 | ||
319 | // Create a message like this (according to RFC 3156 Section 4): | 218 | /** |
320 | // | 219 | * Create a message like this (according to RFC 3156 Section 4): |
321 | // - multipart/encrypted | 220 | * |
322 | // - application/pgp-encrypted (version information) | 221 | * - multipart/encrypted |
323 | // - application/octet-stream (given encrypted data) | 222 | * - application/pgp-encrypted (version information) |
223 | * - application/octet-stream (given encrypted data) | ||
224 | * | ||
225 | * Should not be used directly since the public key should be attached, hence | ||
226 | * the `createEncryptedEmail` function. | ||
227 | * | ||
228 | * The encrypted data can be generated by the `encrypt` or `signAndEncrypt` functions. | ||
229 | */ | ||
324 | KMime::Content *createEncryptedPart(QByteArray encryptedData) | 230 | KMime::Content *createEncryptedPart(QByteArray encryptedData) |
325 | { | 231 | { |
326 | KMime::Content *result = new KMime::Content; | 232 | KMime::Content *result = new KMime::Content; |
@@ -358,20 +264,47 @@ KMime::Content *createEncryptedPart(QByteArray encryptedData) | |||
358 | return result; | 264 | return result; |
359 | } | 265 | } |
360 | 266 | ||
361 | KMime::Content *createEncryptedEmail(KMime::Content *content, const std::vector<GpgME::Key> &encryptionKeys, | 267 | /** |
362 | bool sign, const std::vector<GpgME::Key> &signingKeys = {}) | 268 | * Create an encrypted (optionally signed) email with a public key attached to it. |
269 | * | ||
270 | * Will create a message like this: | ||
271 | * | ||
272 | * + `multipart/mixed` | ||
273 | * - `multipart/encrypted` | ||
274 | * + `application/pgp-encrypted | ||
275 | * + `application/octet-stream` (a generated encrypted version of the original message) | ||
276 | * - `application/pgp-keys` (the public key as attachment, which is the first of the | ||
277 | * `signingKeys`) | ||
278 | */ | ||
279 | Expected<GpgME::Error, KMime::Content *> | ||
280 | createEncryptedEmail(KMime::Content *content, const std::vector<GpgME::Key> &encryptionKeys, | ||
281 | const GpgME::Key &attachedKey, const std::vector<GpgME::Key> &signingKeys = {}) | ||
363 | { | 282 | { |
364 | auto contentToEncrypt = canonicalizeContent(content); | 283 | auto contentToEncrypt = canonicalizeContent(content); |
365 | 284 | ||
366 | auto encryptedData = sign ? encrypt(contentToEncrypt, encryptionKeys) : | 285 | auto encryptionResult = signingKeys.empty() ? |
286 | encrypt(contentToEncrypt, encryptionKeys) : | ||
367 | signAndEncrypt(contentToEncrypt, signingKeys, encryptionKeys); | 287 | signAndEncrypt(contentToEncrypt, signingKeys, encryptionKeys); |
288 | |||
289 | if (!encryptionResult) { | ||
290 | return makeUnexpected(encryptionResult.error()); | ||
291 | } | ||
292 | |||
293 | auto encryptedData = encryptionResult.value(); | ||
294 | |||
368 | KMime::Content *encryptedPart = createEncryptedPart(encryptedData); | 295 | KMime::Content *encryptedPart = createEncryptedPart(encryptedData); |
369 | 296 | ||
370 | // TODO: check signingKeys not empty | 297 | auto publicKeyAppendResult = appendPublicKey(encryptedPart, attachedKey); |
371 | return setMessageAndPublicKey(encryptedPart, signingKeys[0]); | 298 | |
299 | // TODO: this is ugly | ||
300 | if (!publicKeyAppendResult) { | ||
301 | delete encryptedPart; | ||
302 | } | ||
303 | |||
304 | return publicKeyAppendResult; | ||
372 | } | 305 | } |
373 | 306 | ||
374 | QByteArray sign(const QByteArray &content, const std::vector<GpgME::Key> &signingKeys) | 307 | Expected<GpgME::Error, QByteArray> sign(const QByteArray &content, const std::vector<GpgME::Key> &signingKeys) |
375 | { | 308 | { |
376 | QByteArray resultData; | 309 | QByteArray resultData; |
377 | 310 | ||
@@ -381,20 +314,25 @@ QByteArray sign(const QByteArray &content, const std::vector<GpgME::Key> &signin | |||
381 | 314 | ||
382 | if (result.error().code()) { | 315 | if (result.error().code()) { |
383 | qWarning() << "Signing failed:" << result.error().asString(); | 316 | qWarning() << "Signing failed:" << result.error().asString(); |
384 | // TODO: XXX: handle errors better | 317 | return makeUnexpected(result.error()); |
385 | // throw std::runtime_error("Signing failed"); | ||
386 | return ""; | ||
387 | } | 318 | } |
388 | 319 | ||
389 | return resultData; | 320 | return resultData; |
390 | } | 321 | } |
391 | 322 | ||
392 | // Create a message like this (according to RFC 3156 Section 5): | 323 | /** |
393 | // | 324 | * Create a message like this (according to RFC 3156 Section 5): |
394 | // - multipart/signed | 325 | * |
395 | // - whatever the given original message is (should be canonicalized) | 326 | * + `multipart/signed` |
396 | // - application/octet-stream (given encrypted data) | 327 | * - whatever the given original `message` is (should be canonicalized) |
397 | KMime::Content *createSignedPart(KMime::Content *message, const QByteArray &signedData, const QString &micAlg) | 328 | * - `application/octet-stream` (the given `signature`) |
329 | * | ||
330 | * Should not be used directly since the public key should be attached, hence | ||
331 | * the `createSignedEmail` function. | ||
332 | * | ||
333 | * The signature can be generated by the `sign` function. | ||
334 | */ | ||
335 | KMime::Content *createSignedPart(KMime::Content *message, const QByteArray &signature, const QString &micAlg) | ||
398 | { | 336 | { |
399 | KMime::Content *result = new KMime::Content; | 337 | KMime::Content *result = new KMime::Content; |
400 | 338 | ||
@@ -413,7 +351,7 @@ KMime::Content *createSignedPart(KMime::Content *message, const QByteArray &sign | |||
413 | signedPartPart->contentDescription()->from7BitString( | 351 | signedPartPart->contentDescription()->from7BitString( |
414 | "This is a digitally signed message part"); | 352 | "This is a digitally signed message part"); |
415 | 353 | ||
416 | signedPartPart->setBody(signedData); | 354 | signedPartPart->setBody(signature); |
417 | 355 | ||
418 | result->addContent(signedPartPart); | 356 | result->addContent(signedPartPart); |
419 | } | 357 | } |
@@ -421,283 +359,64 @@ KMime::Content *createSignedPart(KMime::Content *message, const QByteArray &sign | |||
421 | return result; | 359 | return result; |
422 | } | 360 | } |
423 | 361 | ||
424 | KMime::Content *createSignedEmail(KMime::Content *content, const std::vector<GpgME::Key> &signingKeys) | 362 | /** |
363 | * Create a signed email with a public key attached to it. | ||
364 | * | ||
365 | * Will create a message like this: | ||
366 | * | ||
367 | * + `multipart/mixed` | ||
368 | * - `multipart/signed` | ||
369 | * + whatever the given original `content` is (should not be canonalized) | ||
370 | * + `application/octet-stream` (a generated signature of the original message) | ||
371 | * - `application/pgp-keys` (the public key as attachment, which is the first of the | ||
372 | * `signingKeys`) | ||
373 | */ | ||
374 | Expected<GpgME::Error, KMime::Content *> createSignedEmail(KMime::Content *content, | ||
375 | const std::vector<GpgME::Key> &signingKeys, const GpgME::Key &attachedKey) | ||
425 | { | 376 | { |
426 | auto contentToSign = canonicalizeContent(content); | 377 | Q_ASSERT(!signingKeys.empty()); |
427 | 378 | ||
428 | auto signedData = sign(contentToSign, signingKeys); | 379 | auto contentToSign = canonicalizeContent(content); |
429 | KMime::Content *signedPart = createSignedPart(content, signedData, "TODO: pgp-something"); | ||
430 | |||
431 | // TODO: check signingKeys not empty | ||
432 | return setMessageAndPublicKey(signedPart, signingKeys[0]); | ||
433 | } | ||
434 | |||
435 | void makeToplevelContentType(KMime::Content *content, bool encrypt, const QByteArray &hashAlgo) | ||
436 | { | ||
437 | //Kleo::CryptoMessageFormat format, | ||
438 | // switch (format) { | ||
439 | // default: | ||
440 | // case Kleo::InlineOpenPGPFormat: | ||
441 | // case Kleo::OpenPGPMIMEFormat: | ||
442 | if (encrypt) { | ||
443 | content->contentType()->setMimeType(QByteArrayLiteral("multipart/encrypted")); | ||
444 | content->contentType()->setParameter(QStringLiteral("protocol"), QStringLiteral("application/pgp-encrypted")); | ||
445 | } else { | ||
446 | content->contentType()->setMimeType(QByteArrayLiteral("multipart/signed")); | ||
447 | content->contentType()->setParameter(QStringLiteral("protocol"), QStringLiteral("application/pgp-signature")); | ||
448 | content->contentType()->setParameter(QStringLiteral("micalg"), QString::fromLatin1(QByteArray(QByteArrayLiteral("pgp-") + hashAlgo)).toLower()); | ||
449 | } | ||
450 | return; | ||
451 | // case Kleo::SMIMEFormat: | ||
452 | // if (sign) { | ||
453 | // qCDebug(MESSAGECOMPOSER_LOG) << "setting headers for SMIME"; | ||
454 | // content->contentType()->setMimeType(QByteArrayLiteral("multipart/signed")); | ||
455 | // content->contentType()->setParameter(QStringLiteral("protocol"), QString::fromAscii("application/pkcs7-signature")); | ||
456 | // content->contentType()->setParameter(QStringLiteral("micalg"), QString::fromAscii(hashAlgo).toLower()); | ||
457 | // return; | ||
458 | // } | ||
459 | // // fall through (for encryption, there's no difference between | ||
460 | // // SMIME and SMIMEOpaque, since there is no mp/encrypted for | ||
461 | // // S/MIME) | ||
462 | // case Kleo::SMIMEOpaqueFormat: | ||
463 | |||
464 | // qCDebug(MESSAGECOMPOSER_LOG) << "setting headers for SMIME/opaque"; | ||
465 | // content->contentType()->setMimeType(QByteArrayLiteral("application/pkcs7-mime")); | ||
466 | |||
467 | // if (sign) { | ||
468 | // content->contentType()->setParameter(QStringLiteral("smime-type"), QString::fromAscii("signed-data")); | ||
469 | // } else { | ||
470 | // content->contentType()->setParameter(QStringLiteral("smime-type"), QString::fromAscii("enveloped-data")); | ||
471 | // } | ||
472 | // content->contentType()->setParameter(QStringLiteral("name"), QString::fromAscii("smime.p7m")); | ||
473 | // } | ||
474 | } | ||
475 | 380 | ||
476 | void setNestedContentType(KMime::Content *content, bool encrypt) | 381 | auto signingResult = sign(contentToSign, signingKeys); |
477 | { | ||
478 | // , Kleo::CryptoMessageFormat format | ||
479 | // switch (format) { | ||
480 | // case Kleo::OpenPGPMIMEFormat: | ||
481 | if (encrypt) { | ||
482 | content->contentType()->setMimeType(QByteArrayLiteral("application/octet-stream")); | ||
483 | } else { | ||
484 | content->contentType()->setMimeType(QByteArrayLiteral("application/pgp-signature")); | ||
485 | content->contentType()->setParameter(QStringLiteral("name"), QString::fromLatin1("signature.asc")); | ||
486 | content->contentDescription()->from7BitString("This is a digitally signed message part."); | ||
487 | } | ||
488 | return; | ||
489 | // case Kleo::SMIMEFormat: | ||
490 | // if (sign) { | ||
491 | // content->contentType()->setMimeType(QByteArrayLiteral("application/pkcs7-signature")); | ||
492 | // content->contentType()->setParameter(QStringLiteral("name"), QString::fromAscii("smime.p7s")); | ||
493 | // return; | ||
494 | // } | ||
495 | // // fall through: | ||
496 | // default: | ||
497 | // case Kleo::InlineOpenPGPFormat: | ||
498 | // case Kleo::SMIMEOpaqueFormat: | ||
499 | // ; | ||
500 | // } | ||
501 | } | ||
502 | 382 | ||
503 | void setNestedContentDisposition(KMime::Content *content, bool encrypt) | 383 | if (!signingResult) { |
504 | { | 384 | return makeUnexpected(signingResult.error()); |
505 | // Kleo::CryptoMessageFormat format, | ||
506 | // if (!sign && format & Kleo::OpenPGPMIMEFormat) { | ||
507 | if (encrypt) { | ||
508 | content->contentDisposition()->setDisposition(KMime::Headers::CDinline); | ||
509 | content->contentDisposition()->setFilename(QStringLiteral("msg.asc")); | ||
510 | // } else if (sign && format & Kleo::SMIMEFormat) { | ||
511 | // content->contentDisposition()->setDisposition(KMime::Headers::CDattachment); | ||
512 | // content->contentDisposition()->setFilename(QStringLiteral("smime.p7s")); | ||
513 | } | 385 | } |
514 | } | ||
515 | 386 | ||
516 | // bool MessageComposer::Util::makeMultiMime(Kleo::CryptoMessageFormat format, bool sign) | 387 | auto signedData = signingResult.value(); |
517 | // { | ||
518 | // switch (format) { | ||
519 | // default: | ||
520 | // case Kleo::InlineOpenPGPFormat: | ||
521 | // case Kleo::SMIMEOpaqueFormat: return false; | ||
522 | // case Kleo::OpenPGPMIMEFormat: return true; | ||
523 | // case Kleo::SMIMEFormat: return sign; // only on sign - there's no mp/encrypted for S/MIME | ||
524 | // } | ||
525 | // } | ||
526 | |||
527 | KMime::Content *composeHeadersAndBody(KMime::Content *orig, QByteArray encodedBody, bool encrypt, const QByteArray &hashAlgo) | ||
528 | { | ||
529 | // Kleo::CryptoMessageFormat format, | ||
530 | KMime::Content *result = new KMime::Content; | ||
531 | 388 | ||
532 | // called should have tested that the signing/encryption failed | 389 | KMime::Content *signedPart = createSignedPart(content, signedData, "TODO: pgp-something"); |
533 | Q_ASSERT(!encodedBody.isEmpty()); | ||
534 | |||
535 | // if (!(format & Kleo::InlineOpenPGPFormat)) { // make a MIME message | ||
536 | // qDebug() << "making MIME message, format:" << format; | ||
537 | makeToplevelContentType(result, encrypt, hashAlgo); | ||
538 | |||
539 | // if (makeMultiMime(sign)) { // sign/enc PGPMime, sign SMIME | ||
540 | if (true) { // sign/enc PGPMime, sign SMIME | ||
541 | |||
542 | const QByteArray boundary = KMime::multiPartBoundary(); | ||
543 | result->contentType()->setBoundary(boundary); | ||
544 | |||
545 | result->assemble(); | ||
546 | //qCDebug(MESSAGECOMPOSER_LOG) << "processed header:" << result->head(); | ||
547 | |||
548 | // Build the encapsulated MIME parts. | ||
549 | // Build a MIME part holding the code information | ||
550 | // taking the body contents returned in ciphertext. | ||
551 | KMime::Content *code = new KMime::Content; | ||
552 | setNestedContentType(code, encrypt); | ||
553 | setNestedContentDisposition(code, encrypt); | ||
554 | |||
555 | if (encrypt) { // enc PGPMime (and / or sign) | ||
556 | |||
557 | //addPublicKeyAsAttachment(content, signingKeys[0]); | ||
558 | |||
559 | setBodyAndCTE(encodedBody, orig->contentType(), code); | ||
560 | |||
561 | // Build a MIME part holding the version information | ||
562 | // taking the body contents returned in | ||
563 | // structuring.data.bodyTextVersion. | ||
564 | KMime::Content *vers = new KMime::Content; | ||
565 | vers->contentType()->setMimeType("application/pgp-encrypted"); | ||
566 | vers->contentDisposition()->setDisposition(KMime::Headers::CDattachment); | ||
567 | vers->contentTransferEncoding()->setEncoding(KMime::Headers::CE7Bit); | ||
568 | vers->setBody("Version: 1"); | ||
569 | |||
570 | result->addContent(vers); | ||
571 | result->addContent(code); | ||
572 | } else { // sign PGPMime, sign SMIME | ||
573 | // if (format & Kleo::AnySMIME) { // sign SMIME | ||
574 | // code->contentTransferEncoding()->setEncoding(KMime::Headers::CEbase64); | ||
575 | // code->contentTransferEncoding()->needToEncode(); | ||
576 | // code->setBody(encodedBody); | ||
577 | // } else { // sign PGPMmime | ||
578 | setBodyAndCTE(encodedBody, orig->contentType(), code); | ||
579 | // } | ||
580 | result->addContent(orig); | ||
581 | result->addContent(code); | ||
582 | } | ||
583 | } else { //enc SMIME, sign/enc SMIMEOpaque | ||
584 | result->contentTransferEncoding()->setEncoding(KMime::Headers::CEbase64); | ||
585 | result->contentDisposition()->setDisposition(KMime::Headers::CDattachment); | ||
586 | result->contentDisposition()->setFilename(QStringLiteral("smime.p7m")); | ||
587 | 390 | ||
588 | result->assemble(); | 391 | auto publicKeyAppendResult = appendPublicKey(signedPart, attachedKey); |
589 | //qCDebug(MESSAGECOMPOSER_LOG) << "processed header:" << result->head(); | ||
590 | 392 | ||
591 | result->setBody(encodedBody); | 393 | // TODO: this is ugly (maybe use a std::unique_ptr for signedPart) |
592 | } | 394 | if (!publicKeyAppendResult) { |
593 | // } else { // sign/enc PGPInline | 395 | delete signedPart; |
594 | // result->setHead(orig->head()); | 396 | } |
595 | // result->parse(); | ||
596 | 397 | ||
597 | // // fixing ContentTransferEncoding | 398 | return publicKeyAppendResult; |
598 | // setBodyAndCTE(encodedBody, orig->contentType(), result); | ||
599 | // } | ||
600 | result->assemble(); | ||
601 | return result; | ||
602 | } | 399 | } |
603 | 400 | ||
604 | // bool binaryHint(Kleo::CryptoMessageFormat f) | 401 | KMime::Content *MailCrypto::processCrypto(KMime::Content *content, |
605 | // { | 402 | const std::vector<GpgME::Key> &signingKeys, const std::vector<GpgME::Key> &encryptionKeys, |
606 | // switch (f) { | 403 | const GpgME::Key &attachedKey, MailCrypto::Protocol protocol) |
607 | // case Kleo::SMIMEFormat: | ||
608 | // case Kleo::SMIMEOpaqueFormat: | ||
609 | // return true; | ||
610 | // default: | ||
611 | // case Kleo::OpenPGPMIMEFormat: | ||
612 | // case Kleo::InlineOpenPGPFormat: | ||
613 | // return false; | ||
614 | // } | ||
615 | // } | ||
616 | // | ||
617 | // GpgME::SignatureMode signingMode(Kleo::CryptoMessageFormat f) | ||
618 | // { | ||
619 | // switch (f) { | ||
620 | // case Kleo::SMIMEOpaqueFormat: | ||
621 | // return GpgME::NormalSignatureMode; | ||
622 | // case Kleo::InlineOpenPGPFormat: | ||
623 | // return GpgME::Clearsigned; | ||
624 | // default: | ||
625 | // case Kleo::SMIMEFormat: | ||
626 | // case Kleo::OpenPGPMIMEFormat: | ||
627 | // return GpgME::Detached; | ||
628 | // } | ||
629 | // } | ||
630 | |||
631 | KMime::Content *MailCrypto::processCrypto(KMime::Content *content, const std::vector<GpgME::Key> &signingKeys, const std::vector<GpgME::Key> &encryptionKeys, MailCrypto::Protocol protocol) | ||
632 | { | 404 | { |
633 | 405 | ||
634 | const bool sign = !signingKeys.empty(); | 406 | qDebug() << "Attaching key:" << attachedKey.shortKeyID() << "from processCrypto"; |
635 | 407 | ||
636 | if(!encryptionKeys.empty()) { | 408 | if(!encryptionKeys.empty()) { |
637 | return createEncryptedEmail(content, encryptionKeys, sign, signingKeys); | 409 | // TODO |
638 | } else if(sign) { | 410 | return createEncryptedEmail(content, encryptionKeys, attachedKey, signingKeys).value(); |
639 | return createSignedEmail(content, signingKeys); | 411 | } else if(!signingKeys.empty()) { |
412 | // TODO | ||
413 | return createSignedEmail(content, signingKeys, signingKeys[0]).value(); | ||
640 | } else { | 414 | } else { |
641 | qWarning() << "Processing cryptography, but neither signing nor encrypting"; | 415 | qWarning() << "Processing cryptography, but neither signing nor encrypting"; |
642 | return content; | 416 | return content; |
643 | } | 417 | } |
644 | |||
645 | const QGpgME::Protocol *const proto = protocol == MailCrypto::SMIME ? QGpgME::smime() : QGpgME::openpgp(); | ||
646 | Q_ASSERT(proto); | ||
647 | |||
648 | auto signingMode = GpgME::Detached; | ||
649 | bool armor = true; | ||
650 | bool textMode = false; | ||
651 | //const bool sign = !signingKeys.empty(); | ||
652 | const bool encrypt = !encryptionKeys.empty(); | ||
653 | |||
654 | QByteArray resultContent; | ||
655 | QByteArray hashAlgo; | ||
656 | //Trust provided keys and don't check them for validity | ||
657 | bool alwaysTrust = true; | ||
658 | if (sign && encrypt) { | ||
659 | std::unique_ptr<QGpgME::SignEncryptJob> job(proto->signEncryptJob(armor, textMode)); | ||
660 | const auto res = job->exec(signingKeys, encryptionKeys, canonicalizeContent(content), alwaysTrust, resultContent); | ||
661 | if (res.first.error().code()) { | ||
662 | qWarning() << "Signing failed:" << res.first.error().asString(); | ||
663 | return nullptr; | ||
664 | } else { | ||
665 | hashAlgo = res.first.createdSignature(0).hashAlgorithmAsString(); | ||
666 | } | ||
667 | if (res.second.error().code()) { | ||
668 | qWarning() << "Encryption failed:" << res.second.error().asString(); | ||
669 | return nullptr; | ||
670 | } | ||
671 | } else if (sign) { | ||
672 | std::unique_ptr<QGpgME::SignJob> job(proto->signJob(armor, textMode)); | ||
673 | auto result = job->exec(signingKeys, canonicalizeContent(content), signingMode, resultContent); | ||
674 | if (result.error().code()) { | ||
675 | qWarning() << "Signing failed:" << result.error().asString(); | ||
676 | return nullptr; | ||
677 | } | ||
678 | hashAlgo = result.createdSignature(0).hashAlgorithmAsString(); | ||
679 | } else if (encrypt) { | ||
680 | std::unique_ptr<QGpgME::EncryptJob> job(proto->encryptJob(armor, textMode)); | ||
681 | const auto result = job->exec(encryptionKeys, canonicalizeContent(content), alwaysTrust, resultContent); | ||
682 | if (result.error().code()) { | ||
683 | qWarning() << "Encryption failed:" << result.error().asString(); | ||
684 | return nullptr; | ||
685 | } | ||
686 | hashAlgo = "pgp-sha1"; | ||
687 | } else { | ||
688 | qWarning() << "Not signing or encrypting"; | ||
689 | return nullptr; | ||
690 | } | ||
691 | |||
692 | return composeHeadersAndBody(content, resultContent, encrypt, hashAlgo); | ||
693 | } | ||
694 | |||
695 | KMime::Content *MailCrypto::sign(KMime::Content *content, const std::vector<GpgME::Key> &signers) | ||
696 | { | ||
697 | return processCrypto(content, signers, {}, OPENPGP); | ||
698 | } | 418 | } |
699 | 419 | ||
700 | |||
701 | void MailCrypto::importKeys(const std::vector<GpgME::Key> &keys) | 420 | void MailCrypto::importKeys(const std::vector<GpgME::Key> &keys) |
702 | { | 421 | { |
703 | const QGpgME::Protocol *const backend = QGpgME::openpgp(); | 422 | const QGpgME::Protocol *const backend = QGpgME::openpgp(); |
@@ -753,4 +472,3 @@ std::vector<GpgME::Key> MailCrypto::findKeys(const QStringList &filter, bool fin | |||
753 | 472 | ||
754 | return keys; | 473 | return keys; |
755 | } | 474 | } |
756 | |||