From 30efac07926a6d37c2ebf21cb78232acfab79a54 Mon Sep 17 00:00:00 2001 From: Felix Hammerl Date: Fri, 4 Jul 2014 17:58:25 +0200 Subject: [PATCH] [WO-259] introduce proper signature checking --- package.json | 2 +- src/js/crypto/pgp.js | 129 +++++++++++++-- src/js/dao/email-dao.js | 178 +++++++++++++------- test/integration/email-dao-test.js | 56 +++++-- test/unit/email-dao-test.js | 250 +++++++++++++++++++++++++++++ test/unit/pgp-test.js | 67 +++++++- 6 files changed, 585 insertions(+), 97 deletions(-) diff --git a/package.json b/package.json index 39bbe74..ef82595 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "dependencies": { "crypto-lib": "https://github.com/whiteout-io/crypto-lib/tarball/v0.2.0", "imap-client": "https://github.com/whiteout-io/imap-client/tarball/v0.3.4", - "mailreader": "https://github.com/whiteout-io/mailreader/tarball/v0.3.3", + "mailreader": "https://github.com/whiteout-io/mailreader/tarball/dev/WO-259", "pgpmailer": "https://github.com/whiteout-io/pgpmailer/tarball/v0.3.5", "pgpbuilder": "https://github.com/whiteout-io/pgpbuilder/tarball/v0.3.4", "requirejs": "2.1.14", diff --git a/src/js/crypto/pgp.js b/src/js/crypto/pgp.js index a3c8570..55223bf 100644 --- a/src/js/crypto/pgp.js +++ b/src/js/crypto/pgp.js @@ -296,13 +296,13 @@ define(function(require) { }; /** - * [decrypt description] + * Decrypts a ciphertext * @param {String} ciphertext The encrypted PGP message block * @param {String} publicKeyArmored The public key used to sign the message * @param {Function} callback(error, plaintext, signaturesValid) signaturesValid is undefined in case there are no signature, null in case there are signatures but the wrong public key or no key was used to verify, true if the signature was successfully verified, or false if the signataure verification failed. */ PGP.prototype.decrypt = function(ciphertext, publicKeyArmored, callback) { - var publicKeys, message, signaturesValid; + var publicKeys, message; // check keys if (!this._privateKey) { @@ -334,24 +334,119 @@ define(function(require) { return; } - // check if signatures are valid - if (decrypted.signatures.length > 0) { - signaturesValid = true; // signature is correct - for (var i = 0; i < decrypted.signatures.length; i++) { - if (decrypted.signatures[i].valid === false) { - signaturesValid = false; // signature is wrong ... message was tampered with - break; - } else if (decrypted.signatures[i].valid === null) { - signaturesValid = null; // signature not found for the specified public key - break; - } - } - } - // return decrypted plaintext - callback(null, decrypted.text, signaturesValid); + callback(null, decrypted.text, checkSignatureValidity(decrypted.signatures)); } }; + /** + * Verifies a clearsigned message + * @param {String} clearSignedText The clearsigned text, usually from a signed pgp/inline message + * @param {String} publicKeyArmored The public key used to signed the message + * @param {Function} callback(error, signaturesValid) signaturesValid is undefined in case there are no signature, null in case there are signatures but the wrong public key or no key was used to verify, true if the signature was successfully verified, or false if the signataure verification failed. + */ + PGP.prototype.verifyClearSignedMessage = function(clearSignedText, publicKeyArmored, callback) { + var publicKeys, + message; + + // check keys + if (!this._privateKey) { + callback(new Error('Error verifying signed PGP message. Keys must be set!')); + return; + } + + // read keys and ciphertext message + try { + if (publicKeyArmored) { + // parse public keys if available ... + publicKeys = openpgp.key.readArmored(publicKeyArmored).keys; + } else { + // use own public key to know if signatures are available + publicKeys = [this._publicKey]; + } + message = openpgp.cleartext.readArmored(clearSignedText); + } catch (err) { + callback(new Error('Error verifying signed PGP message!')); + return; + } + + openpgp.verifyClearSignedMessage(publicKeys, message, function(err, result) { + if (err) { + callback(new Error('Error verifying PGP message!')); + return; + } + + callback(null, checkSignatureValidity(result.signatures)); + }); + }; + + /** + * Verifies a message with a detached signature + * @param {String} message The signed text, usually from a signed pgp/mime message + * @param {String} pgpSignature The detached signature, usually from a signed pgp/mime message + * @param {String} publicKeyArmored The public key used to signed the message + * @param {Function} callback(error, signaturesValid) signaturesValid is undefined in case there are no signature, null in case there are signatures but the wrong public key or no key was used to verify, true if the signature was successfully verified, or false if the signataure verification failed. + */ + PGP.prototype.verifySignedMessage = function(message, pgpSignature, publicKeyArmored, callback) { + var publicKeys; + + // check keys + if (!this._privateKey) { + callback(new Error('Error verifying signed PGP message. Keys must be set!')); + return; + } + + // read keys and ciphertext message + try { + if (publicKeyArmored) { + // parse public keys if available ... + publicKeys = openpgp.key.readArmored(publicKeyArmored).keys; + } else { + // use own public key to know if signatures are available + publicKeys = [this._publicKey]; + } + } catch (err) { + callback(new Error('Error verifying signed PGP message!')); + return; + } + + var signatures; + try { + var msg = openpgp.message.readSignedContent(message, pgpSignature); + signatures = msg.verify(publicKeys); + } catch (err) { + callback(new Error('Error verifying signed PGP message!')); + return; + } + + callback(null, checkSignatureValidity(signatures)); + }; + + /** + * Checks signature validity + * @param {Object} decrypted OpenPGP.js Signature array + * @return {undefined|null|true|false} + * If signatures array is empty (the message was not signed), returns undefined + * If you're using the wrong public key, returns null. + * If signatures are invalid, returns false. + * If everything is in order, returns true + */ + function checkSignatureValidity(signatures) { + if (!(signatures || []).length) { + // signatures array is empty (the message was not signed) + return; + } + + for (var i = 0; i < signatures.length; i++) { + if (signatures[i].valid !== true) { // null | false + // you're using the wrong public key or signatures are invalid + return signatures[i].valid; + } + } + + // everything is in order + return true; + } + return PGP; }); \ No newline at end of file diff --git a/src/js/dao/email-dao.js b/src/js/dao/email-dao.js index 4e63b89..4df6872 100644 --- a/src/js/dao/email-dao.js +++ b/src/js/dao/email-dao.js @@ -698,79 +698,82 @@ define(function(require) { if (message.encrypted) { // show the encrypted message message.body = filterBodyParts(message.bodyParts, 'encrypted')[0].content; - done(); - return; + return done(); } - // for unencrypted messages, this is the array where the body parts are located var root = message.bodyParts; if (message.signed) { - var signedPart = filterBodyParts(message.bodyParts, 'signed')[0]; - message.signedMessage = signedPart.signedMessage; - message.signature = signedPart.signature; - // TODO check integrity - // in case of a signed message, you only want to show the signed content and ignore the rest - root = signedPart.content; + // PGP/MIME signed + var signedRoot = filterBodyParts(message.bodyParts, 'signed')[0]; // in case of a signed message, you only want to show the signed content and ignore the rest + message.signedMessage = signedRoot.signedMessage; + message.signature = signedRoot.signature; + root = signedRoot.content; } - // if the message is plain text and contains pgp/inline, we are only interested in the encrypted - // content, the rest (corporate mail footer, attachments, etc.) is discarded. var body = _.pluck(filterBodyParts(root, 'text'), 'content').join('\n'); /* - * here's how the pgp/inline regex works: - * - any content before the PGP block will be discarded - * - "-----BEGIN PGP MESSAGE-----" must be at the beginning (and end) of a line - * - "-----END PGP MESSAGE-----" must be at the beginning (and end) of a line - * - the regex must not match a pgp block in a plain text reply or forward of a pgp/inline message. - * (the encryption will break for replies/forward, because "> " corrupts the PGP block with non-radix-64 characters) + * if the message is plain text and contains pgp/inline, we are only interested in the encrypted + * content, the rest (corporate mail footer, attachments, etc.) is discarded. + * "-----BEGIN/END (...)-----" must be at the start/end of a line, + * the regex must not match a pgp block in a plain text reply or forward of a pgp/inline message, + * the encryption will break for replies/forward, because "> " corrupts the PGP block with non-radix-64 characters, */ - var pgpInlineRegex = /^-{5}BEGIN PGP MESSAGE-{5}$[^>]*^-{5}END PGP MESSAGE-{5}$/im; - var pgpInlineMatch = pgpInlineRegex.exec(body); + var pgpInlineMatch = /^-{5}BEGIN PGP MESSAGE-{5}[\s\S]*-{5}END PGP MESSAGE-{5}$/im.exec(body); if (pgpInlineMatch) { - // show the plain text content - message.body = pgpInlineMatch[0]; + message.body = pgpInlineMatch[0]; // show the plain text content + message.encrypted = true; // signal the ui that we're handling encrypted content - // - replace the bodyParts info with an artificial bodyPart of type "encrypted" - // - _isPgpInline is only used internally to avoid trying to parse non-MIME text with the mailreader - // - set the encrypted flag so we can signal the ui that we're handling encrypted content - message.encrypted = true; + // replace the bodyParts info with an artificial bodyPart of type "encrypted" message.bodyParts = [{ type: 'encrypted', content: pgpInlineMatch[0], - _isPgpInline: true + _isPgpInline: true // used internally to avoid trying to parse non-MIME text with the mailreader }]; - done(); - return; + return done(); } - /* - * here's how the clear signing regex works: - * - any content before the PGP block will be discarded - * - "-----BEGIN PGP SIGNED MESSAGE-----" must be at the beginning (and end) of a line - * - "-----END PGP SIGNATURE-----" must be at the beginning (and end) of a line - * - the regex must not match a pgp block in a plain text reply or forward of a pgp/signed message. - * (the encryption will break for replies/forward, because "> " corrupts the PGP block with non-radix-64 characters) + * any content before/after the PGP block will be discarded, + * "-----BEGIN/END (...)-----" must be at the start/end of a line, + * after \n\n the signed payload begins, + * the text is followed by a final \n and then the pgp signature begins + * untrusted attachments and html is ignored */ - var clearSignedRegex = /^-{5}BEGIN PGP SIGNED MESSAGE-{5}[\s\S]*\n{2}([\S\s]*)(-{5}BEGIN PGP SIGNATURE-{5}[\S\s]*-{5}END PGP SIGNATURE-{5})$/im; - var clearSignedMatch = clearSignedRegex.exec(body); + var clearSignedMatch = /^-{5}BEGIN PGP SIGNED MESSAGE-{5}[\s\S]*\n\n([\s\S]*)\n-{5}BEGIN PGP SIGNATURE-{5}[\S\s]*-{5}END PGP SIGNATURE-{5}$/im.exec(body); if (clearSignedMatch) { - message.clearSignedMessage = clearSignedMatch[0]; + // PGP/INLINE signed message.signed = true; - // TODO check integrity - - message.body = clearSignedMatch[1].trim(); - } else { - message.body = body; + message.clearSignedMessage = clearSignedMatch[0]; + body = clearSignedMatch[1]; } - message.attachments = filterBodyParts(root, 'attachment'); - message.html = _.pluck(filterBodyParts(root, 'html'), 'content').join('\n'); - inlineExternalImages(message); + if (!message.signed) { + // message is not signed, so we're done here + return setBody(); + } - done(); + // check the signatures for signed messages + self._checkSignatures(message, function(err, signaturesValid) { + if (err) { + return done(err); + } + + message.signaturesValid = signaturesValid; + setBody(); + }); + + function setBody() { + message.body = body; + if (!message.clearSignedMessage) { + message.attachments = filterBodyParts(root, 'attachment'); + message.html = _.pluck(filterBodyParts(root, 'html'), 'content').join('\n'); + inlineExternalImages(message); + } + + done(); + } } @@ -780,6 +783,27 @@ define(function(require) { } }; + EmailDAO.prototype._checkSignatures = function(message, callback) { + var self = this; + + self._keychain.getReceiverPublicKey(message.from[0].address, function(err, senderPublicKey) { + if (err) { + return callback(err); + } + + // get the receiver's public key to check the message signature + var senderKey = senderPublicKey ? senderPublicKey.publicKey : undefined; + + if (message.clearSignedMessage) { + self._pgp.verifyClearSignedMessage(message.clearSignedMessage, senderKey, callback); + } else if (message.signedMessage && message.signature) { + self._pgp.verifySignedMessage(message.signedMessage, message.signature, senderKey, callback); + } else { + callback(null, undefined); + } + }); + }; + /** * Retrieves an attachment matching a body part for a given uid and a folder * @@ -859,33 +883,65 @@ define(function(require) { // parse the decrypted raw content in the mailparser self._mailreader.parse({ bodyParts: [encryptedNode] - }, function(err, parsedBodyParts) { + }, function(err, root) { if (err) { showError(err.errMsg || err.message); return; } - // we have successfully interpreted the descrypted message, - // so let's update the views on the message parts - message.body = _.pluck(filterBodyParts(parsedBodyParts, 'text'), 'content').join('\n'); - message.html = _.pluck(filterBodyParts(parsedBodyParts, 'html'), 'content').join('\n'); - message.attachments = _.reject(filterBodyParts(parsedBodyParts, 'attachment'), function(attmt) { - // remove the pgp-signature from the attachments - return attmt.mimeType === "application/pgp-signature"; - }); - inlineExternalImages(message); + if (!message.signed) { + // message had no signature in the ciphertext, so there's a little extra effort to be done here + // is there a signed MIME node? + var signedRoot = filterBodyParts(root, 'signed')[0]; + if (!signedRoot) { + // no signed MIME node, obviously an unsigned PGP/MIME message + return setBody(); + } - message.decrypted = true; + // if there is something signed in here, we're only interested in the signed content + message.signedMessage = signedRoot.signedMessage; + message.signature = signedRoot.signature; + root = signedRoot.content; - // we're done here! - done(); + // check the signatures for encrypted messages + self._checkSignatures(message, function(err, signaturesValid) { + if (err) { + return done(err); + } + + message.signed = typeof signaturesValid !== 'undefined'; + message.signaturesValid = signaturesValid; + setBody(); + }); + return; + } + + // message had a signature in the ciphertext, so we're done here + setBody(); + + function setBody() { + // we have successfully interpreted the descrypted message, + // so let's update the views on the message parts + message.body = _.pluck(filterBodyParts(root, 'text'), 'content').join('\n'); + message.html = _.pluck(filterBodyParts(root, 'html'), 'content').join('\n'); + message.attachments = _.reject(filterBodyParts(root, 'attachment'), function(attmt) { + // remove the pgp-signature from the attachments + return attmt.mimeType === "application/pgp-signature"; + }); + inlineExternalImages(message); + + message.decrypted = true; + + // we're done here! + done(); + } }); }); }); function showError(msg) { message.body = msg; - message.decrypted = true; // display error msh in body + message.decrypted = true; // display error msg in body done(); } diff --git a/test/integration/email-dao-test.js b/test/integration/email-dao-test.js index 0a1a0d4..e05ba7d 100644 --- a/test/integration/email-dao-test.js +++ b/test/integration/email-dao-test.js @@ -69,11 +69,11 @@ define(function(require) { raw: "Content-Type: multipart/encrypted; boundary=\"Apple-Mail=_FE60F127-D74F-4377-93CC-56430132A178\"; protocol=\"application/pgp-encrypted\";\r\nSubject: test15\r\nMime-Version: 1.0 (Mac OS X Mail 7.3 \\(1878.2\\))\r\nX-Pgp-Agent: GPGMail (null)\r\nFrom: safewithme \r\nDate: Mon, 23 Jun 2014 21:25:10 +0200\r\nContent-Transfer-Encoding: 7bit\r\nMessage-Id: \r\nContent-Description: OpenPGP encrypted message\r\nTo: safewithme.testuser@gmail.com\r\nX-Mailer: Apple Mail (2.1878.2)\r\n\r\nThis is an OpenPGP/MIME encrypted message (RFC 2440 and 3156)\r\n--Apple-Mail=_FE60F127-D74F-4377-93CC-56430132A178\r\nContent-Transfer-Encoding: 7bit\r\nContent-Type: application/pgp-encrypted\r\nContent-Description: PGP/MIME Versions Identification\r\n\r\nVersion: 1\r\n\r\n--Apple-Mail=_FE60F127-D74F-4377-93CC-56430132A178\r\nContent-Transfer-Encoding: 7bit\r\nContent-Disposition: inline;\r\n\tfilename=encrypted.asc\r\nContent-Type: application/octet-stream;\r\n\tname=encrypted.asc\r\nContent-Description: OpenPGP encrypted message\r\n\r\n-----BEGIN PGP MESSAGE-----\r\nComment: GPGTools - https://gpgtools.org\r\n\r\nhQEMA9f7k/zfv8I8AQf/U11mVSMI5wFM4gh20M1eF1xx5Gs+JJK7hIqKoW7z127k\r\nqB+QEnPvWqa5vV2+gi22bDeXgVNELN3wb2CAznMM82TnpiSbmLLMSrGtx8BAJsfW\r\n+yxaDMvGMn6JHsGPQ6rij7ar9yGCgw6gGOrCuxzCLSgeajbmEn76lyIOtDxY0KSK\r\nisW0K1iD9SeJJiMnMg/EP0Sf9sUzIZjQNJpoz9S23keAHqij/eNexrdmobLMQamF\r\n9BxforwfewMEBv+/+atTj91nS260RBB2g+S6tv1RNJbZ3zTbqr06lviPTQ5zoWT0\r\n2uUEipYhNp4WTaqHg2KfopfzUIt1M0TJGwSidPWkkcnpb+dDeC1JYd3gy0IejhiJ\r\nOUo67gwaiFiwbKhJUTbBI0ZLV3StxBh6M6MEuabDOiBKDuRSPd3Tk8ZylVYjHA/Z\r\noMFW4cKGHp8t2bVs16DyUuIFFJV4UNtXFoJBGYjq8x2WXFLPXaH//eSgF96AxE1+\r\nG3NwPHu1J0uf5s7LAX669FT/6fNpd7oKsOStmYWVII2smA0RasQjApWXu/ouYFIe\r\nwF1GKRcVzSNjc9lUqVoyhKYDwZ+UBZNgbecJc+szvYWbj1X3cUQkVxAVe9Kvbuuu\r\nBbKBghZkt0o2c/asIPTVcMLFRCYXauLlpNMQqxtdPWzyx/mKPe2r4qo+7Yoq6/zh\r\n1QVsqHfNd3TslOWanH2iOrylPPHCZ5eph+RHkPxE/lYJOqZgZnpcW5wusAyqaPfX\r\niSh8aoHDXa9VT/rMB5wz7EJppv75YLUaHHqnD7oJEMqSlxhDYy62TDWjVAPv2ITF\r\n3z9pfjAXDitGKqpwM2re+vCR0Lg3KMBhE3zL4Z8QPRK4I7Oekb6WiK90TlBc9xdr\r\nhC3dDu+lWPkhU7f1wEiiminVxPQLMNfnBSErwMqC9LSHXuBcnqYWhMgl9auN/oqf\r\nbAyFYTWY+rk+B8sAJU5aTlwC5GavRzCAZFPHCRmOLVrLCD0MPS1x/cBQ/pL/yiID\r\nMFBLuxnb4GC9ZQZA7x63rlHAXtjEj5VDZcEJiJWsHapTksscjC0r2IRADSw/xWUp\r\nil+7Z2ajBNAQShECeIOkA3NLjaykDAMhOcZbg2Lw3V+EcF/kG6DJbvpombySOuys\r\n/EwDC2fVrxiEt7dPWmhlCu7wwyyMX0cjYikAQAGw1Xa5UQhdss3ivAuBSvhmFAhh\r\nyMvU8Lxtd01NT/hHMNcYUo/zs0dUZxibfI8zvRemGwLxy0pIHoi+77Lv6ejtkQlK\r\nlIAWew6Slyk2sFoDq/U/f+AEIodsNrHw2uljkzfw3tFUrm204s8L0woN3nSXuzLZ\r\nmGn56Ep7tk+K88eTCz5lW5gc3AyGlr1YK6/iC3wwre8P72kblwDKOKBrHogMo/Ed\r\n9cpldxBtLBbMohJ29N0V9fQ=\r\n=cI14\r\n-----END PGP MESSAGE-----\r\n\r\n--Apple-Mail=_FE60F127-D74F-4377-93CC-56430132A178--", uid: 804 }, { - description: "Apple Mail (no attachment): Encrypted and signed", + description: "Apple Mail (no attachment - PGP/MIME): Encrypted and signed", raw: "Content-Type: multipart/encrypted; boundary=\"Apple-Mail=_2E255D9C-89D7-4F15-81B9-0821710B1B04\"; protocol=\"application/pgp-encrypted\";\r\nSubject: test12\r\nMime-Version: 1.0 (Mac OS X Mail 7.3 \\(1878.2\\))\r\nX-Pgp-Agent: GPGMail (null)\r\nFrom: safewithme \r\nDate: Mon, 23 Jun 2014 21:22:54 +0200\r\nContent-Transfer-Encoding: 7bit\r\nMessage-Id: <1995DC5D-366B-427A-8420-C0E6F93FCAE6@gmail.com>\r\nContent-Description: OpenPGP encrypted message\r\nTo: safewithme.testuser@gmail.com\r\nX-Mailer: Apple Mail (2.1878.2)\r\n\r\nThis is an OpenPGP/MIME encrypted message (RFC 2440 and 3156)\r\n--Apple-Mail=_2E255D9C-89D7-4F15-81B9-0821710B1B04\r\nContent-Transfer-Encoding: 7bit\r\nContent-Type: application/pgp-encrypted\r\nContent-Description: PGP/MIME Versions Identification\r\n\r\nVersion: 1\r\n\r\n--Apple-Mail=_2E255D9C-89D7-4F15-81B9-0821710B1B04\r\nContent-Transfer-Encoding: 7bit\r\nContent-Disposition: inline;\r\n\tfilename=encrypted.asc\r\nContent-Type: application/octet-stream;\r\n\tname=encrypted.asc\r\nContent-Description: OpenPGP encrypted message\r\n\r\n-----BEGIN PGP MESSAGE-----\r\nComment: GPGTools - https://gpgtools.org\r\n\r\nhQEMA9f7k/zfv8I8AQf/SN6kzGCE5Ht0OHBofUocR3CaADSI1ricHiWLzk74FT+6\r\nB7wZcqNR2wI35nwYR2qGjJdLHQP321b6viA0SH5w2drDnfuOAvdGDv/9dK0X4c2z\r\n4WZLu7AndKQ9HlpwYTaXguplfBx77QjwaS43x8otIQgI/D9dQ+kIlDgzj4hm4TBn\r\nh171NaXKs3cw93v1h9lM66kzkta30A3sORdtAPNQ7bEYKYlQhCa4KHXXclRdjccQ\r\nfnAx5oBGycbpRvgn88VkmUl7+THNoCtFDvh1gG/XTGaxPH0XWYv5D9ojH9NyPD8s\r\nE2rwU93cMsuesIQcxBCax3DjoWrPp1qAsd4o0JP28snpZZVwIxigO5CE05nkUyYS\r\nHBettFNr9JL2eZox4+FRmY0NV8R0CqSqo+cYy6yu5UlZtJbN4+4Uk6xfXE9ApyWG\r\nt+BFIx9QpiuXFr4z/iFZ/QJ2i8f+teQyFDGA33Kr0y+PD1AZcyUy8m9eWhZHebl2\r\ntEqWqNINHoPr27My8+tG7HDBWmyBPuTyGEwdg93N6psb124NSZOe8pfYLPSyWFnb\r\nQ4bIw8hGfoPkzqjE4tDZ7YtFLZJvlSxyQViTxeFJ3A6wOq+3bebIvmRqV6mTdbi4\r\nNiqFNA3aSjUid1z8L7MbtpPVSdwmwXUrpRiM5Rr17CqcaPnmRUlxSSMucX5PLqQv\r\nKu1PEPzvyqE+Hqnaxi2gMaYj0+TRUAKXLJrjlWDRpIKp3K8Ic5dFdA8KUHqRBz7N\r\nUh/LOBPPWZNriT9vNrPdvjuiGiRcL3WGU4bu301U4g6gpUEHKNEcbXfyuCz6Iwh6\r\nhKfKiBLTHT//jr5TQKzs0cwyPCsOmG08bbgrnj59KoF6apuIXrw9RRvYVumChRx3\r\nvZIPlOF7g/2ncF1kHq+ChVu0zO0syti9efIV7vbpgZ385AdnRUHH3Eqk0lnYB3JK\r\nFuktWoFZB7VppOp8up9mznX4E5RRGJBAIdj61soZ4bHNSZeBbDECiwxW37xRTtd9\r\nUi/FVZzbGC1+gxbITJYSeWvAB9hmDHiO5fbCdohb3Wn8Z8dWb4FE/tx/TVpwmLat\r\n0uaHlteA6QLVPbQT1EaKyZhsNW9uqJ2LTEtyfe0JfNAXduF6phEyA2ZRBS8b82Jb\r\najK/8pFjqkm25q2aTPkFeVzWMYL/1w9EEPbFuqVXmD153NElebL4odAPE3ZCHpZs\r\nxgbcLw6zAYnQgNal8papOHrghs37iej++gBrzDbAf6Mj09wqTv7xESxpqH9hhSEs\r\nqcoEg2l5U9pSZ3oHq9z783EONSfDXQAl0RE=\r\n=Wsnw\r\n-----END PGP MESSAGE-----\r\n\r\n--Apple-Mail=_2E255D9C-89D7-4F15-81B9-0821710B1B04--", uid: 805 }, { - description: "Apple Mail (no attachment): Encrypted", + description: "Apple Mail (no attachment - PGP/MIME): Encrypted", raw: "Content-Type: multipart/encrypted; boundary=\"Apple-Mail=_930898CD-0C01-4F0E-8769-2B6F056DC2CD\"; protocol=\"application/pgp-encrypted\";\r\nSubject: test13\r\nMime-Version: 1.0 (Mac OS X Mail 7.3 \\(1878.2\\))\r\nX-Pgp-Agent: GPGMail (null)\r\nFrom: safewithme \r\nDate: Mon, 23 Jun 2014 21:23:32 +0200\r\nContent-Transfer-Encoding: 7bit\r\nMessage-Id: <0AF825EB-5923-4F11-B189-A3D8032A6D6C@gmail.com>\r\nContent-Description: OpenPGP encrypted message\r\nTo: safewithme.testuser@gmail.com\r\nX-Mailer: Apple Mail (2.1878.2)\r\n\r\nThis is an OpenPGP/MIME encrypted message (RFC 2440 and 3156)\r\n--Apple-Mail=_930898CD-0C01-4F0E-8769-2B6F056DC2CD\r\nContent-Transfer-Encoding: 7bit\r\nContent-Type: application/pgp-encrypted\r\nContent-Description: PGP/MIME Versions Identification\r\n\r\nVersion: 1\r\n\r\n--Apple-Mail=_930898CD-0C01-4F0E-8769-2B6F056DC2CD\r\nContent-Transfer-Encoding: 7bit\r\nContent-Disposition: inline;\r\n\tfilename=encrypted.asc\r\nContent-Type: application/octet-stream;\r\n\tname=encrypted.asc\r\nContent-Description: OpenPGP encrypted message\r\n\r\n-----BEGIN PGP MESSAGE-----\r\nComment: GPGTools - https://gpgtools.org\r\n\r\nhQEMA9f7k/zfv8I8AQf/WaCFHXTlBnLbqis/zjpRP7gcOvS8uUT3D77RO8Tbuu/6\r\nrh9AtSf78QLF3ogkDB5jlGkfQOxrPbMMyE9CzC8UPRZy5xdbGsUbv7z3biFfVX8P\r\nBmZSyAXTTduf4ewrp6cy7Mbm/wxSGnSMWW6ut30276izXJsw+SywMhg7dWojJyYs\r\nLWNhs5qQWHDoJdB6j/3T++gtpdE2Tv+hrzXrhskBf/rf3XfZmvi7UmFk0lVGpVXP\r\nyuX0iyyfaj8cV2ubycR79NKUlBp76HSZFBsDEY1Zbb/GJaHG/5lHbixf9klFbdoL\r\nGPF51IbQypL1dlYPffvGz/u3M5ctBvoUK4jgLYWOsMlnbIzD5WpmjmL5e3+cwcJj\r\noCbbtyYBJSuzY/4tmx5DRVAnoN0hWo3nLTfVNweMtKd1jms4FookhVZchxtuJkjy\r\nxPjygCncmf3PNmGARKFxZ05PvHlSPhGQ1YcqDRRpXRU+Cj78OHtbaA==\r\n=Ckmq\r\n-----END PGP MESSAGE-----\r\n\r\n--Apple-Mail=_930898CD-0C01-4F0E-8769-2B6F056DC2CD--", uid: 806 }, { @@ -81,7 +81,7 @@ define(function(require) { raw: "From: safewithme \r\nContent-Type: multipart/signed; boundary=\"Apple-Mail=_D557BC30-CF1E-41FD-8932-E73ED2C124EA\"; protocol=\"application/pgp-signature\"; micalg=pgp-sha512\r\nSubject: test17\r\nMessage-Id: <6B942730-FFE4-476C-980C-FF1ABA0740BD@gmail.com>\r\nDate: Mon, 23 Jun 2014 21:26:11 +0200\r\nTo: safewithme.testuser@gmail.com\r\nMime-Version: 1.0 (Mac OS X Mail 7.3 \\(1878.2\\))\r\nX-Mailer: Apple Mail (2.1878.2)\r\n\r\n\r\n--Apple-Mail=_D557BC30-CF1E-41FD-8932-E73ED2C124EA\r\nContent-Type: multipart/mixed;\r\n\tboundary=\"Apple-Mail=_6461D724-7906-49CB-BA38-D66A4E146EC3\"\r\n\r\n\r\n--Apple-Mail=_6461D724-7906-49CB-BA38-D66A4E146EC3\r\nContent-Transfer-Encoding: 7bit\r\nContent-Type: text/plain;\r\n\tcharset=us-ascii\r\n\r\ntest17\r\n\r\n--Apple-Mail=_6461D724-7906-49CB-BA38-D66A4E146EC3\r\nContent-Disposition: attachment;\r\n\tfilename=test.bin\r\nContent-Type: application/macbinary;\r\n\tx-unix-mode=0644;\r\n\tname=\"test.bin\"\r\nContent-Transfer-Encoding: 7bit\r\n\r\ntestattachment\r\n--Apple-Mail=_6461D724-7906-49CB-BA38-D66A4E146EC3\r\nContent-Transfer-Encoding: 7bit\r\nContent-Type: text/plain;\r\n\tcharset=us-ascii\r\n\r\n\r\n\r\n--Apple-Mail=_6461D724-7906-49CB-BA38-D66A4E146EC3--\r\n\r\n--Apple-Mail=_D557BC30-CF1E-41FD-8932-E73ED2C124EA\r\nContent-Transfer-Encoding: 7bit\r\nContent-Disposition: attachment;\r\n\tfilename=signature.asc\r\nContent-Type: application/pgp-signature;\r\n\tname=signature.asc\r\nContent-Description: Message signed with OpenPGP using GPGMail\r\n\r\n-----BEGIN PGP SIGNATURE-----\r\nComment: GPGTools - https://gpgtools.org\r\n\r\niQEcBAEBCgAGBQJTqH9TAAoJENf7k/zfv8I8ygYH/j6ICLJaxBLNhUvBxuXlZHse\r\nH1Rfg/rtF1UCJdqHRrefIYDTVUu1jiTjH1DKXJdujD+mNGhDUqBkF8vn+Hmu86H4\r\n/E9trGeygCkYZNdug1HINI4+fezGa3D28uDkPeN9LlDZBKBVXuEx+EAGBgJLaPbH\r\n7vdlqDqbwlXCU2JO6uGr4sqcTS0UMZaC0VLhBQyXelGQurjoD8XvamBnt5oRxtEc\r\nvftg7s9FKdErNC3mPoUkhFeQKXUiHACH/TzFUdXTh0K7y2ZXQFVmEg/+jjmoFX4D\r\nKPIvjrlM6FqDwo067tIT+S4WJ5MdcDcZRbyS6QkBuMVbugWeUokf/f8zgHQPnHA=\r\n=pJiW\r\n-----END PGP SIGNATURE-----\r\n\r\n--Apple-Mail=_D557BC30-CF1E-41FD-8932-E73ED2C124EA--", uid: 807 }, { - description: "Apple Mail (no attachment): Signed", + description: "Apple Mail (no attachment - PGP/MIME): Signed", raw: "From: safewithme \r\nContent-Type: multipart/signed; boundary=\"Apple-Mail=_D32A0631-70E5-46E7-8204-7A7D5EF145B1\"; protocol=\"application/pgp-signature\"; micalg=pgp-sha512\r\nSubject: test14\r\nMessage-Id: \r\nDate: Mon, 23 Jun 2014 21:24:23 +0200\r\nTo: safewithme.testuser@gmail.com\r\nMime-Version: 1.0 (Mac OS X Mail 7.3 \\(1878.2\\))\r\nX-Mailer: Apple Mail (2.1878.2)\r\n\r\n\r\n--Apple-Mail=_D32A0631-70E5-46E7-8204-7A7D5EF145B1\r\nContent-Transfer-Encoding: 7bit\r\nContent-Type: text/plain;\r\n\tcharset=us-ascii\r\n\r\ntest14\r\n\r\n--Apple-Mail=_D32A0631-70E5-46E7-8204-7A7D5EF145B1\r\nContent-Transfer-Encoding: 7bit\r\nContent-Disposition: attachment;\r\n\tfilename=signature.asc\r\nContent-Type: application/pgp-signature;\r\n\tname=signature.asc\r\nContent-Description: Message signed with OpenPGP using GPGMail\r\n\r\n-----BEGIN PGP SIGNATURE-----\r\nComment: GPGTools - https://gpgtools.org\r\n\r\niQEcBAEBCgAGBQJTqH7nAAoJENf7k/zfv8I8crcH/1h2LEOiAddU7tXokMxfA+FT\r\nSPezAU3eUSzlDLIjq+6pFlFXmH+IVQcxx7dbiHekLtDiIweII58KOAHYodadO4Gg\r\ni/wist5rGpysHX1djQ6D/pqvxr8jEwxQ0tyvEkcDzMXcGolUZLQTDRHaCpgJAFrM\r\n525YHJ1UxAzlojq+/92EzI8JdqH+KT56BGCiBHFj6QlWF1OXV4L+mNk1zRMyESjI\r\n0LPYFYrUtBopy/0DvrqAkFFOOS6j6XjPa2Finofv49LqOc4ntpOSs0DwrDPb5Nn3\r\nMlDvsT80Bf+RfQdc8PzTAyN5Puv+XjDET407jVUsfKwEv/aUHRZnNXWAR2G+9xE=\r\n=CGVw\r\n-----END PGP SIGNATURE-----\r\n\r\n--Apple-Mail=_D32A0631-70E5-46E7-8204-7A7D5EF145B1--", uid: 808 }, { @@ -93,11 +93,11 @@ define(function(require) { raw: "Message-ID: <53A87DC2.6010102@gmail.com>\r\nDate: Mon, 23 Jun 2014 21:19:30 +0200\r\nFrom: Andris Testbox2 \r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:24.0) Gecko/20100101 Thunderbird/24.2.0\r\nMIME-Version: 1.0\r\nTo: safewithme \r\nSubject: test9\r\nX-Enigmail-Version: 1.6\r\nContent-Type: multipart/encrypted;\r\n protocol=\"application/pgp-encrypted\";\r\n boundary=\"N184DqJgpX0F6MPTPjS0P6IxMgfbap3qD\"\r\n\r\nThis is an OpenPGP/MIME encrypted message (RFC 4880 and 3156)\r\n--N184DqJgpX0F6MPTPjS0P6IxMgfbap3qD\r\nContent-Type: application/pgp-encrypted\r\nContent-Description: PGP/MIME version identification\r\n\r\nVersion: 1\r\n\r\n--N184DqJgpX0F6MPTPjS0P6IxMgfbap3qD\r\nContent-Type: application/octet-stream; name=\"encrypted.asc\"\r\nContent-Description: OpenPGP encrypted message\r\nContent-Disposition: inline; filename=\"encrypted.asc\"\r\n\r\n-----BEGIN PGP MESSAGE-----\r\nVersion: GnuPG v1.4.13 (Darwin)\r\nComment: GPGTools - https://gpgtools.org\r\nComment: Using GnuPG with Thunderbird - http://www.enigmail.net/\r\n\r\nhQEMA9f7k/zfv8I8AQf5AX+nmpkoDAn7MHFwQZ9ENIOtYRRTY1aakavO0oVuiOWm\r\nerJO+/4ORrtSapkZjp9cnE0Kwuo44fkmbObt+JquHVg4Bcxee3IpViTOx1dV+IPr\r\n/R5zcPp3myk9hqwkpwlCPOUVXmD8YQeLQQfiM90E0+ldF9q1Q4UKW/usmaJQBwWd\r\nWrR9KURfBrh1eqIO927YXIInnhlCl0ZiYwghCeJ7nrfHF7a3ftHuSMJhkywNKGWH\r\nhL7FghCwpmVuHyneB8lJVGz4txwnW51kK05Il46Uab1y9jSutUN+5IVWmRx6m+zt\r\n7aj3Cyd8rAzc9LdP0XEPOe1+cht9GTXXMdj+Kk5758npNB32pMfQ8YSOcIU1luyk\r\nKWE6yG5+7ZWFXrbdXVjXRKXMN31c4Hw0Ccv1kO/viAvthAU3hFZM0cBp+PtiOrxS\r\nynBBi7r2o3xb8NTGGYRq/P0H9Odemj2x6OGbIXS40ApTAGKeNNhHpF5HwaAWuMul\r\n2Pnjdt8x34PiKd/L/TOSAtmQZqkQ3xmYOMpP5XKiCYTBeMMM46Gz4rbTnrJawW5X\r\n8nxvQjJmDzcAByS9bJSNh0a6vF+JbTNbTH7kIjqPUm57zyie2+uBCjg5dIeqZt4l\r\nF85Ai+chMwtUNZ50tEfPhk1opf+HsJ8OfrNEOiA8xCQNL3ZUPnaHkhLAd8bh05zI\r\nyzJOBLwSrFpCMZWkPm1PK6J99M6JH5MJyZxvdQfH/YyhCdqiyCUQc1lObdPBLN/A\r\n/Lb1xUnqppA7yvr6RpWQ+EAUVohknGRhqdL/PxOcCv9GY2DW0dHxUdYbyBzoFj6h\r\nhmzaCIUmhjDGLi4qCxLdn46IKKFtEncMBGgrIQTgDGHXyUaUlEVtWs3I6aRkgYbz\r\no2t3UytJxyMUevCpSBADlHO0Rd1q0MyEsmGKKYoyJSt1NX4C6pmEl3kVJoyvkQWb\r\nbgFBG0KYkx5+UtBGlubYrP2MS2xRQ+6hHCJtIFOfQHcWlUg4jy8mZNVjV+S+zvbV\r\nGjIhjdmvFAvp/sgcwTGmYbh4LpUP/pI+jmIh3Gg8l1PDlh95uSzKJ770m2U8W7ws\r\nNbgG3RdxD0ZocJkeYslvHKid3kf2LIKeH1ADJj/t6rfD/4k31iQeGcNASnDNsel3\r\njbp8HJ9LSm0h0xeWCiOLqa/c6ysXLravA7nBC1XHKE3u4tcIjcZEYt6Z\r\n=qwkL\r\n-----END PGP MESSAGE-----\r\n\r\n--N184DqJgpX0F6MPTPjS0P6IxMgfbap3qD--", uid: 810 }, { - description: "Thunderbird (no attachment): Encrypted and signed", + description: "Thunderbird (no attachment - PGP/INLINE): Encrypted and signed", raw: "Message-ID: <53A8796A.6000502@gmail.com>\r\nDate: Mon, 23 Jun 2014 21:00:58 +0200\r\nFrom: Andris Testbox2 \r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:24.0) Gecko/20100101 Thunderbird/24.2.0\r\nMIME-Version: 1.0\r\nTo: safewithme.testuser@gmail.com\r\nSubject: test4\r\nX-Enigmail-Version: 1.6\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Transfer-Encoding: quoted-printable\r\n\r\n\r\n-----BEGIN PGP MESSAGE-----\r\nCharset: ISO-8859-1\r\nVersion: GnuPG v1.4.13 (Darwin)\r\nComment: GPGTools - https://gpgtools.org\r\nComment: Using GnuPG with Thunderbird - http://www.enigmail.net/\r\n\r\nhQEMA9f7k/zfv8I8AQf8CCwYxQuhh9kAd4Vz8nUP8YLScfexq8juAawc5bsq3sf5\r\nMl1E6zzM8d0M8ultmL+Y2RRYX82/kEvc1c3bWNJNSagwqxlD54dToXTraGkE+hbF\r\nlMnsAq/jpcsXH0G9oFPwMi5NMWKbZQIUdsi3Iszx8x1WEWcV9XE4C0xg0LfN66vr\r\n1ykTTcg+wv4XmxvljvgA+VT6HvS1jqE/NrfseDtQJNIs42sfylgJF0neOfkrjrn/\r\nDljslmd1WgbDjbAk+hzT+8zmRfCLK2GhRtsRskdGGSzDiYhAc1qLU6GtVuhig088\r\nF3Gk1Sqgnffi1/X16j2sN5URjteaXnvHIJwGypuoLsnAjmQyh0vVs8hVb4jZw+aR\r\n8atbrYPLCN8VnIRK+4E9v45aGef3U8Dul3FXx06s6UZVGTaPHOFIkFJhfA4Vswh5\r\n6n7A5kAhGx9VgChOyjaGpBdpFuhsD1fpixhVlTCAmIanJwYi5Cvz2nfN8QOIamsc\r\ndM0bE0utru2YCmmzVgVjDr4xtM7tAPfresDnEXt/eqRiIFntT/tD8yke4/vTMS3s\r\nJVMhFrlm14BohvRnaF0sUeFiMSbDL1ox8pmtRUdIFY3mhq+R9XUpFb7ktOd6husG\r\nthMDtT+1Tb7/W2rHFx7nJIQtjKbgM79/Pson+O2LzX6fY7qeQKnUX9dBb15t5e94\r\n78yazU5T1JmMHA+Szzu6OMy3eHkkOqxsst62nMXgrgGk7NhUNl+3oP5k/aT6iqA2\r\n3deWy5YfwtC0hRHWnT6zweJJohOVwrQQJ9oxTSi3iJ0=3D\r\n=3D9EeZ\r\n-----END PGP MESSAGE-----\r\n\r\n", uid: 811 }, { - description: "Thunderbird (no attachment): Encrypted", + description: "Thunderbird (no attachment - PGP/INLINE): Encrypted", raw: "Message-ID: <53A87AD7.9090109@gmail.com>\r\nDate: Mon, 23 Jun 2014 21:07:03 +0200\r\nFrom: Andris Testbox2 \r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:24.0) Gecko/20100101 Thunderbird/24.2.0\r\nMIME-Version: 1.0\r\nTo: safewithme.testuser@gmail.com\r\nSubject: test5\r\nX-Enigmail-Version: 1.6\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Transfer-Encoding: quoted-printable\r\n\r\n\r\n-----BEGIN PGP MESSAGE-----\r\nCharset: ISO-8859-1\r\nVersion: GnuPG v1.4.13 (Darwin)\r\nComment: GPGTools - https://gpgtools.org\r\nComment: Using GnuPG with Thunderbird - http://www.enigmail.net/\r\n\r\nhQEMA9f7k/zfv8I8AQf+KmR4WIpBMlhm7HFxWEEmRAezEaKWX1X9oDCMBMC/WTPa\r\nzegDeysvFsh7SvLDZngm+hPDxCWIh+h/6EZaWGuQBJKcyglTncZEA3T5vz+IRJoP\r\ngFUVZ9YLaT58DAzLOpg8noNAEp0+E+cfDsVvhBI8Hzx7VRt1/msO+RWWEZOnD1xw\r\nD5iJ0AfONzAcfHc0jJosz8/iUkWBexBwtG+dm4mdyE+X6g30zHY6afa5/E7LvfXd\r\nZUFr+pgHa1eQYKtqtyeZPrli0zSHtFMOdr8eDkp89/MZgQbbYHEaLTjWUzDsogDT\r\n3FzTbm4t4fPolEHgZFnDwCrqPTRZAN999zscD12CiMkdTc0iVy4mH50QgeF/m/w7\r\n7ewbgh38TN8YbXvaA6A=3D\r\n=3Di2Il\r\n-----END PGP MESSAGE-----\r\n\r\n", uid: 812 }, { @@ -109,9 +109,13 @@ define(function(require) { raw: "Message-ID: <53A87E4B.50702@gmail.com>\r\nDate: Mon, 23 Jun 2014 21:21:47 +0200\r\nFrom: Andris Testbox2 \r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:24.0) Gecko/20100101 Thunderbird/24.2.0\r\nMIME-Version: 1.0\r\nTo: safewithme \r\nSubject: test11\r\nX-Enigmail-Version: 1.6\r\nContent-Type: multipart/signed; micalg=pgp-sha512;\r\n protocol=\"application/pgp-signature\";\r\n boundary=\"LldNQubkCiWQwPKXrfghi6DLbotCLEBuX\"\r\n\r\nThis is an OpenPGP/MIME signed message (RFC 4880 and 3156)\r\n--LldNQubkCiWQwPKXrfghi6DLbotCLEBuX\r\nContent-Type: multipart/mixed;\r\n boundary=\"------------070307080002050009010403\"\r\n\r\nThis is a multi-part message in MIME format.\r\n--------------070307080002050009010403\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Transfer-Encoding: quoted-printable\r\n\r\ntest11\r\n\r\n--------------070307080002050009010403\r\nContent-Type: application/macbinary;\r\n name=\"test.bin\"\r\nContent-Transfer-Encoding: base64\r\nContent-Disposition: attachment;\r\n filename=\"test.bin\"\r\n\r\ndGVzdGF0dGFjaG1lbnQ=\r\n--------------070307080002050009010403--\r\n\r\n--LldNQubkCiWQwPKXrfghi6DLbotCLEBuX\r\nContent-Type: application/pgp-signature; name=\"signature.asc\"\r\nContent-Description: OpenPGP digital signature\r\nContent-Disposition: attachment; filename=\"signature.asc\"\r\n\r\n-----BEGIN PGP SIGNATURE-----\r\nVersion: GnuPG v1.4.13 (Darwin)\r\nComment: GPGTools - https://gpgtools.org\r\nComment: Using GnuPG with Thunderbird - http://www.enigmail.net/\r\n\r\niQEcBAEBCgAGBQJTqH5OAAoJENf7k/zfv8I8oFoH/R6EFTw2CYUQoOKSAQstWIHp\r\nfVVseLOkFbByUV5eLuGVBNI3DM4GQ6C7dGntKAn34a1iTGcAIZH+fIhaZ2WtNdtA\r\nR+Ijn8xDjbF/BWvcTBOaRvgw9b8viPxhkVYa3PioHYz6krt/LmFqFdp/phWZcqR4\r\njzWMX55h4FOw3YBNGiz2NuIg+iGrFRWPYgd8NVUmJKReZHs8C/6HGz7F4/A24k6Y\r\n7xms9D6Er+MhspSl+1dlRdHjtXiRqC5Ld1hi2KBKc6YzgOLpVw5l9sffbnH+aRG4\r\ndH+2J5U3elqBDK1i3GyG8ixLSB0FGW9+lhYNosZne2xy8SbQKdgsnTBnWSGevP0=\r\n=xiih\r\n-----END PGP SIGNATURE-----\r\n\r\n--LldNQubkCiWQwPKXrfghi6DLbotCLEBuX--", uid: 814 }, { - description: "Thunderbird (no attachment): Signed", + description: "Thunderbird (no attachment - PGP/INLINE): Signed", raw: "Message-ID: <53A87B12.9010706@gmail.com>\r\nDate: Mon, 23 Jun 2014 21:08:02 +0200\r\nFrom: Andris Testbox2 \r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:24.0) Gecko/20100101 Thunderbird/24.2.0\r\nMIME-Version: 1.0\r\nTo: safewithme.testuser@gmail.com\r\nSubject: test6\r\nX-Enigmail-Version: 1.6\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Transfer-Encoding: 7bit\r\n\r\n\r\n-----BEGIN PGP SIGNED MESSAGE-----\r\nHash: SHA512\r\n\r\ntest6\r\n-----BEGIN PGP SIGNATURE-----\r\nVersion: GnuPG v1.4.13 (Darwin)\r\nComment: GPGTools - https://gpgtools.org\r\nComment: Using GnuPG with Thunderbird - http://www.enigmail.net/\r\n\r\niQEcBAEBCgAGBQJTqHsSAAoJENf7k/zfv8I8wz4H/RWo1qJvvJtMl7GyqGGbaByX\r\n/D7/yWJzMdE0Y7J/tHIexQ/sZnmcDlHG0mtJKgI7EOh2EyV+r+78vF71Mlc+bg8g\r\n3B4TKyp0QU1Pb6SETG//FtKrU7SnkjKujHvRMpzcOcm0ZLBDpmftyWLvp9Dg3KOF\r\n5sMBGpJRn1pqX2DxXZtc1rYOmSAaxFI5jewPws0DCDkLDGp9gLyusNeDHkmAT4AG\r\nDqsDPQvW0R4Sy7aQFT7GjrdnCiLyikynkocUpR95fDnjHJ6Xbyj2Yj9/ofewPQ//\r\nMq39sIYbcqlDBAhsOlii3ekdzLS4xEOkvtFoD4pufyLj3pYY60FG4bPygcccYkI=\r\n=IkRV\r\n-----END PGP SIGNATURE-----\r\n", uid: 815 + }, { + description: "Mailvelope (no attachment - PGP/INLINE): encrypted and signed", + raw: "MIME-Version: 1.0\r\nReceived: by 10.195.18.8 with HTTP; Fri, 4 Jul 2014 06:58:43 -0700 (PDT)\r\nDate: Fri, 4 Jul 2014 15:58:43 +0200\r\nDelivered-To: safewithme.testuser@gmail.com\r\nMessage-ID: \r\nSubject: \r\nFrom: safewithme testuser \r\nTo: safewithme testuser \r\nContent-Type: text/plain; charset=UTF-8\r\n\r\n-----BEGIN PGP MESSAGE-----\r\nVersion: Mailvelope v0.9.0\r\nComment: Email security by Mailvelope - https://www.mailvelope.com\r\n\r\nwcBMA9f7k/zfv8I8AQf9F/Gm4HqJ/RlU2w+qIbJ4Va2PFR04OITlZIuUAWms\r\nPhPo4cGFbgxQnBzD7goswvNLXfEo4Q6/wxqT/wuwLGQdQJDoEduQxO5p77c1\r\n+dw/sa+pcr4jdwjebjV45NODVGDxgSF+YIwwKN3XXF6VqcisLLYBONYTHIU8\r\nKdTYR+R8SXSpMGISLUyyeY3Jaw5Et8cEoo0a1z8Fx04Ycv2Gw9Io0NVEqxYR\r\n86HUCLsOSARZC1aJ6hf9wheB528o0wuM6ESJ1LWnMWudyrkMjAiW6AiH89G8\r\npykTuYvc/GH3q7eEKNtY5khuZwi2Z7VJFrTeaEt4cb6HxlUlECudYw79uAHu\r\nt9JGATfaNeUpZV2xLEPBhsW5VrY4nDpbWVLp9stAKNFEiH6Ai/rgNwJ2A9Xr\r\nnWAji8YlIOOdO1iNVaYEQWEW1s5Hw5rYB83LKg==\r\n=ungD\r\n-----END PGP MESSAGE-----\r\n", + uid: 816 }]; imapFolders = { @@ -471,7 +475,7 @@ define(function(require) { }; }); - it.skip('should parse Apple Mail (attachment - PGP/MIME): Encrypted and signed', function(done) { + it('should parse Apple Mail (attachment - PGP/MIME): Encrypted and signed', function(done) { emailDao.onIncomingMessage = function(messages) { emailDao.getBody({ folder: currentFolder, @@ -494,7 +498,7 @@ define(function(require) { }; }); - it.skip('should parse Apple Mail (no attachment): Encrypted and signed', function(done) { + it('should parse Apple Mail (no attachment): Encrypted and signed', function(done) { emailDao.onIncomingMessage = function(messages) { emailDao.getBody({ folder: currentFolder, @@ -554,7 +558,7 @@ define(function(require) { expect(err).to.not.exist; expect(message.encrypted).to.be.false; expect(message.signed).to.be.true; - //TODO (check plaintext signatures): expect(message.signaturesValid).to.be.true; + expect(message.signaturesValid).to.be.true; expect(message.attachments.length).to.equal(1); expect(message.body).to.equal('test17\n'); done(); @@ -577,7 +581,7 @@ define(function(require) { expect(err).to.not.exist; expect(message.encrypted).to.be.false; expect(message.signed).to.be.true; - //TODO (check plaintext signatures): expect(message.signaturesValid).to.be.true; + expect(message.signaturesValid).to.be.true; expect(message.attachments.length).to.equal(0); expect(message.body).to.equal('test14'); done(); @@ -715,7 +719,7 @@ define(function(require) { expect(err).to.not.exist; expect(message.encrypted).to.be.false; expect(message.signed).to.be.true; - //TODO (check plaintext signatures): expect(message.signaturesValid).to.be.true; + expect(message.signaturesValid).to.be.true; expect(message.attachments.length).to.equal(1); expect(message.body).to.equal('test11'); done(); @@ -724,7 +728,7 @@ define(function(require) { }; }); - it('should parse Thunderbird (no attachment): Signed w/ PGP/inline', function(done) { + it('should parse Thunderbird (no attachment): Signed w/ PGP/INLINE', function(done) { emailDao.onIncomingMessage = function(messages) { emailDao.getBody({ folder: currentFolder, @@ -738,7 +742,7 @@ define(function(require) { expect(err).to.not.exist; expect(message.encrypted).to.be.false; expect(message.signed).to.be.true; - //TODO (check plaintext signatures): expect(message.signaturesValid).to.be.true; + expect(message.signaturesValid).to.be.true; expect(message.attachments.length).to.equal(0); expect(message.body).to.equal('test6'); done(); @@ -746,13 +750,33 @@ define(function(require) { }); }; }); + + it('should parse Mailvelope: encrypted (unsigned) w/PGP/INLINE', function(done) { + emailDao.onIncomingMessage = function(messages) { + emailDao.getBody({ + folder: currentFolder, + message: messages[22] + }, function(err, message) { + expect(err).to.not.exist; + emailDao.decryptBody({ + message: message, + folder: currentFolder + }, function(err) { + expect(err).to.not.exist; + expect(message.encrypted).to.be.true; + expect(message.signed).to.be.false; + expect(message.signaturesValid).to.be.undefined; + expect(message.attachments.length).to.equal(0); + expect(message.body).to.equal('this is a test'); + done(); + }); + }); + }; + }); }); }); describe('SMTP Tests', function() { - // phantomjs is just sooo slow - - it('should send a plaintext message', function(done) { sinon.stub(smtpServer, 'onmail', function(mail) { expect(mail.from).to.equal(testAccount.user); diff --git a/test/unit/email-dao-test.js b/test/unit/email-dao-test.js index e8f9111..ad51041 100644 --- a/test/unit/email-dao-test.js +++ b/test/unit/email-dao-test.js @@ -909,6 +909,62 @@ define(function(require) { expect(message.loadingBody).to.be.true; }); + it('should read a signed pgp/mime from the device', function(done) { + var message, signed, pt, signedMimeTree, signature; + + pt = 'bender is great!'; + signed = 'omg signed text'; + signedMimeTree = 'trallalalalala'; + signature = 'ugauga'; + message = { + uid: uid, + signed: true, + from: [{ + address: 'asdasdasd' + }] + }; + + localListStub.withArgs({ + folder: inboxFolder, + uid: uid + }).yieldsAsync(null, [{ + bodyParts: [{ + type: 'text', + content: pt + }, { + type: 'signed', + content: [{ + type: 'text', + content: signed + }], + signedMessage: signedMimeTree, + signature: signature + }] + }]); + keychainStub.getReceiverPublicKey.withArgs(message.from[0].address).yieldsAsync(null, mockKeyPair.publicKey); + pgpStub.verifySignedMessage.withArgs(signedMimeTree, signature, mockKeyPair.publicKey.publicKey).yieldsAsync(null, true); + + dao.getBody({ + message: message, + folder: inboxFolder + }, function(err, msg) { + expect(err).to.not.exist; + + expect(msg).to.equal(message); + expect(msg.body).to.equal(signed); + expect(message.signed).to.be.true; + expect(message.signaturesValid).to.be.true; + expect(message.loadingBody).to.be.false; + + expect(localListStub.calledOnce).to.be.true; + expect(pgpStub.verifySignedMessage.calledOnce).to.be.true; + expect(keychainStub.getReceiverPublicKey.calledOnce).to.be.true; + + done(); + }); + expect(message.loadingBody).to.be.true; + }); + it('should read a pgp/inline from the device', function(done) { var message, ct, pt; @@ -951,6 +1007,47 @@ define(function(require) { expect(message.loadingBody).to.be.true; }); + it('should read a signed pgp/inline from the device', function(done) { + var message, pt; + + pt = '-----BEGIN PGP SIGNED MESSAGE-----\n\ntest6\n-----BEGIN PGP SIGNATURE----------END PGP SIGNATURE-----'; + message = { + uid: uid, + from: [{ + address: 'asdasdasd' + }] + }; + + localListStub.yieldsAsync(null, [{ + bodyParts: [{ + type: 'text', + content: pt + }] + }]); + keychainStub.getReceiverPublicKey.withArgs(message.from[0].address).yieldsAsync(null, mockKeyPair.publicKey); + pgpStub.verifyClearSignedMessage.withArgs(pt, mockKeyPair.publicKey.publicKey).yieldsAsync(null, true); + + dao.getBody({ + message: message, + folder: inboxFolder + }, function(err, msg) { + expect(err).to.not.exist; + + expect(msg).to.equal(message); + expect(msg.body).to.equal('test6'); + expect(message.signed).to.be.true; + expect(message.signaturesValid).to.be.true; + expect(message.loadingBody).to.be.false; + + expect(localListStub.calledOnce).to.be.true; + expect(pgpStub.verifyClearSignedMessage.calledOnce).to.be.true; + expect(keychainStub.getReceiverPublicKey.calledOnce).to.be.true; + + done(); + }); + expect(message.loadingBody).to.be.true; + }); + it('should stream from imap and set plain text body', function(done) { var message, body, uid; @@ -1260,6 +1357,8 @@ define(function(require) { expect(error).to.not.exist; expect(msg).to.equal(message); expect(message.decrypted).to.be.true; + expect(message.signed).to.be.true; + expect(message.signaturesValid).to.be.true; expect(message.body).to.equal(parsed); expect(message.decryptingBody).to.be.false; expect(keychainStub.getReceiverPublicKey.calledOnce).to.be.true; @@ -1272,6 +1371,71 @@ define(function(require) { expect(message.decryptingBody).to.be.true; }); + it('decrypt a pgp/mime message with inner signature', function(done) { + var message, ct, pt, parsed, signed, signedMimeTree, signature; + + pt = 'bender is great'; + ct = '-----BEGIN PGP MESSAGE-----asdasdasd-----END PGP MESSAGE-----'; + signedMimeTree = 'trallalalalala'; + signature = 'ugauga'; + signed = 'omg signed text'; + parsed = 'bender! bender! bender!'; + message = { + from: [{ + address: 'asdasdasd' + }], + body: ct, + encrypted: true, + bodyParts: [{ + type: 'encrypted', + content: ct + }] + }; + + keychainStub.getReceiverPublicKey.withArgs(message.from[0].address).yieldsAsync(null, mockKeyPair.publicKey); + pgpStub.decrypt.withArgs(ct, mockKeyPair.publicKey.publicKey).yieldsAsync(null, pt, undefined); + pgpStub.verifySignedMessage.withArgs(signedMimeTree, signature, mockKeyPair.publicKey.publicKey).yieldsAsync(null, true); + + parseStub.withArgs({ + bodyParts: [{ + type: 'encrypted', + content: ct, + raw: pt + }] + }).yieldsAsync(null, [{ + type: 'encrypted', + content: [{ + type: 'signed', + content: [{ + type: 'text', + content: signed + }], + signedMessage: signedMimeTree, + signature: signature + }] + }]); + + dao.decryptBody({ + message: message + }, function(error, msg) { + expect(error).to.not.exist; + expect(msg).to.equal(message); + expect(message.decrypted).to.be.true; + expect(message.body).to.equal(signed); + expect(message.signed).to.be.true; + expect(message.signaturesValid).to.be.true; + expect(message.decryptingBody).to.be.false; + expect(keychainStub.getReceiverPublicKey.calledTwice).to.be.true; + expect(pgpStub.decrypt.calledOnce).to.be.true; + expect(pgpStub.verifySignedMessage.calledOnce).to.be.true; + expect(parseStub.calledOnce).to.be.true; + + done(); + }); + + expect(message.decryptingBody).to.be.true; + }); + it('decrypt a pgp/inline message', function(done) { var message, ct, pt; @@ -1302,6 +1466,8 @@ define(function(require) { expect(message.decrypted).to.be.true; expect(message.body).to.equal(pt); expect(message.decryptingBody).to.be.false; + expect(message.signed).to.be.true; + expect(message.signaturesValid).to.be.true; expect(keychainStub.getReceiverPublicKey.calledOnce).to.be.true; expect(pgpStub.decrypt.calledOnce).to.be.true; expect(parseStub.called).to.be.false; @@ -1608,6 +1774,90 @@ define(function(require) { describe('internal API', function() { + describe('#_checkSignatures', function() { + it('should check signatures in clearsigned message', function(done) { + var message = { + from: [{ + address: 'asdasdasd' + }], + clearSignedMessage: 'trallalalalala' + }; + + keychainStub.getReceiverPublicKey.withArgs(message.from[0].address).yieldsAsync(null, mockKeyPair.publicKey); + pgpStub.verifyClearSignedMessage.withArgs(message.clearSignedMessage, mockKeyPair.publicKey.publicKey).yieldsAsync(null, true); + + dao._checkSignatures(message, function(error, signaturesValid) { + expect(error).to.not.exist; + expect(signaturesValid).to.be.true; + expect(keychainStub.getReceiverPublicKey.calledOnce).to.be.true; + expect(pgpStub.verifyClearSignedMessage.calledOnce).to.be.true; + done(); + }); + }); + + it('should check signatures in pgp/mime signed message', function(done) { + var message = { + from: [{ + address: 'asdasdasd' + }], + signedMessage: 'trallalalalala', + signature: 'ugauga' + }; + + keychainStub.getReceiverPublicKey.withArgs(message.from[0].address).yieldsAsync(null, mockKeyPair.publicKey); + pgpStub.verifySignedMessage.withArgs(message.signedMessage, message.signature, mockKeyPair.publicKey.publicKey).yieldsAsync(null, true); + + dao._checkSignatures(message, function(error, signaturesValid) { + expect(error).to.not.exist; + expect(signaturesValid).to.be.true; + expect(keychainStub.getReceiverPublicKey.calledOnce).to.be.true; + expect(pgpStub.verifySignedMessage.calledOnce).to.be.true; + done(); + }); + }); + + it('should error while checking signatures', function(done) { + var message = { + from: [{ + address: 'asdasdasd' + }], + signedMessage: 'trallalalalala', + signature: 'ugauga' + }; + + keychainStub.getReceiverPublicKey.withArgs(message.from[0].address).yieldsAsync(null, mockKeyPair.publicKey); + pgpStub.verifySignedMessage.yieldsAsync(new Error()); + + dao._checkSignatures(message, function(error, signaturesValid) { + expect(error).to.exist; + expect(signaturesValid).to.not.exist; + expect(keychainStub.getReceiverPublicKey.calledOnce).to.be.true; + expect(pgpStub.verifySignedMessage.calledOnce).to.be.true; + done(); + }); + }); + + it('should error while fetching public key', function(done) { + var message = { + from: [{ + address: 'asdasdasd' + }], + signedMessage: 'trallalalalala', + signature: 'ugauga' + }; + + keychainStub.getReceiverPublicKey.yieldsAsync(new Error()); + + dao._checkSignatures(message, function(error, signaturesValid) { + expect(error).to.exist; + expect(signaturesValid).to.not.exist; + expect(keychainStub.getReceiverPublicKey.calledOnce).to.be.true; + expect(pgpStub.verifySignedMessage.called).to.be.false; + done(); + }); + }); + }); + describe('#_initFoldersFromDisk', function() { beforeEach(function() { sinon.stub(dao, 'refreshFolder'); diff --git a/test/unit/pgp-test.js b/test/unit/pgp-test.js index e5c8349..0fe19ec 100644 --- a/test/unit/pgp-test.js +++ b/test/unit/pgp-test.js @@ -203,6 +203,7 @@ define(function(require) { '> \n' + '> Thursday, Nov 21, 2013 7:32 PM asdf@example.com wrote:\n' + '> > secret 3'; + var wrongPubkey = '-----BEGIN PGP PUBLIC KEY BLOCK-----\r\nVersion: OpenPGP.js v.1.20131116\r\nComment: Whiteout Mail - http://whiteout.io\r\n\r\nxsBNBFKODs4BB/9iOF4THsjQMY+WEpT7ShgKxj4bHzRRaQkqczS4nZvP0U3g\r\nqeqCnbpagyeKXA+bhWFQW4GmXtgAoeD5PXs6AZYrw3tWNxLKu2Oe6Tp9K/XI\r\nxTMQ2wl4qZKDXHvuPsJ7cmgaWqpPyXtxA4zHHS3WrkI/6VzHAcI/y6x4szSB\r\nKgSuhI3hjh3s7TybUC1U6AfoQGx/S7e3WwlCOrK8GTClirN/2mCPRC5wuIft\r\nnkoMfA6jK8d2OPrJ63shy5cgwHOjQg/xuk46dNS7tkvGmbaa+X0PgqSKB+Hf\r\nYPPNS/ylg911DH9qa8BqYU2QpNh9jUKXSF+HbaOM+plWkCSAL7czV+R3ABEB\r\nAAHNLVdoaXRlb3V0IFVzZXIgPHNhZmV3aXRobWUudGVzdHVzZXJAZ21haWwu\r\nY29tPsLAXAQQAQgAEAUCUo4O2gkQ1/uT/N+/wjwAAN2cB/9gFRmAfvEQ2qz+\r\nWubmT2EsSSnjPMxzG4uyykFoa+TaZCWo2Xa2tQghmU103kEkQb1OEjRjpgwJ\r\nYX9Kghnl8DByM686L5AXnRyHP78qRJCLXSXl0AGicboUDp5sovaa4rswQceH\r\nvcdWgZ/mgHTRoiQeJddy9k+H6MPFiyFaVcFwegVsmpc+dCcC8yT+qh8ZIbyG\r\nRJU60PmKKN7LUusP+8DbSv39zCGJCBlVVKyA4MzdF5uM+sqTdXbKzOrT5DGd\r\nCZaox4s+w16Sq1rHzZKFWfQPfKLDB9pyA0ufCVRA3AF6BUi7G3ZqhZiHNhMP\r\nNvE45V/hS1PbZcfPVoUjE2qc1Ix1\r\n=7Wpe\r\n-----END PGP PUBLIC KEY BLOCK-----'; beforeEach(function(done) { pgp.importKeys({ @@ -297,7 +298,6 @@ define(function(require) { describe('Decrypt and verify', function() { var ciphertext; - var wrongPubkey = '-----BEGIN PGP PUBLIC KEY BLOCK-----\r\nVersion: OpenPGP.js v.1.20131116\r\nComment: Whiteout Mail - http://whiteout.io\r\n\r\nxsBNBFKODs4BB/9iOF4THsjQMY+WEpT7ShgKxj4bHzRRaQkqczS4nZvP0U3g\r\nqeqCnbpagyeKXA+bhWFQW4GmXtgAoeD5PXs6AZYrw3tWNxLKu2Oe6Tp9K/XI\r\nxTMQ2wl4qZKDXHvuPsJ7cmgaWqpPyXtxA4zHHS3WrkI/6VzHAcI/y6x4szSB\r\nKgSuhI3hjh3s7TybUC1U6AfoQGx/S7e3WwlCOrK8GTClirN/2mCPRC5wuIft\r\nnkoMfA6jK8d2OPrJ63shy5cgwHOjQg/xuk46dNS7tkvGmbaa+X0PgqSKB+Hf\r\nYPPNS/ylg911DH9qa8BqYU2QpNh9jUKXSF+HbaOM+plWkCSAL7czV+R3ABEB\r\nAAHNLVdoaXRlb3V0IFVzZXIgPHNhZmV3aXRobWUudGVzdHVzZXJAZ21haWwu\r\nY29tPsLAXAQQAQgAEAUCUo4O2gkQ1/uT/N+/wjwAAN2cB/9gFRmAfvEQ2qz+\r\nWubmT2EsSSnjPMxzG4uyykFoa+TaZCWo2Xa2tQghmU103kEkQb1OEjRjpgwJ\r\nYX9Kghnl8DByM686L5AXnRyHP78qRJCLXSXl0AGicboUDp5sovaa4rswQceH\r\nvcdWgZ/mgHTRoiQeJddy9k+H6MPFiyFaVcFwegVsmpc+dCcC8yT+qh8ZIbyG\r\nRJU60PmKKN7LUusP+8DbSv39zCGJCBlVVKyA4MzdF5uM+sqTdXbKzOrT5DGd\r\nCZaox4s+w16Sq1rHzZKFWfQPfKLDB9pyA0ufCVRA3AF6BUi7G3ZqhZiHNhMP\r\nNvE45V/hS1PbZcfPVoUjE2qc1Ix1\r\n=7Wpe\r\n-----END PGP PUBLIC KEY BLOCK-----'; beforeEach(function(done) { pgp.encrypt(message, [pubkey], function(err, ct) { @@ -355,7 +355,70 @@ define(function(require) { }); }); - }); + describe('Verify clearsigned message', function() { + var clearsigned; + beforeEach(function() { + clearsigned = openpgp.signClearMessage(pgp._privateKey, 'this is a clearsigned message'); + }); + + it('should work', function(done) { + pgp.verifyClearSignedMessage(clearsigned, pubkey, function(err, signaturesValid) { + expect(err).to.not.exist; + expect(signaturesValid).to.be.true; + done(); + }); + }); + + it('should fail', function(done) { + pgp.verifyClearSignedMessage(clearsigned.replace('clearsigned', 'invalid'), pubkey, function(err, signaturesValid) { + expect(err).to.not.exist; + expect(signaturesValid).to.be.false; + done(); + }); + }); + it.skip('should be null for wrong public key', function(done) { + pgp.verifyClearSignedMessage(clearsigned, wrongPubkey, function(err, signaturesValid) { + expect(err).to.not.exist; + expect(signaturesValid).to.be.null; + done(); + }); + }); + }); + + describe('Verify detached signature', function() { + var signedMessage, signature; + + beforeEach(function() { + signedMessage = 'this is a signed message'; + var clearsigned = openpgp.signClearMessage(pgp._privateKey, signedMessage); + var signatureHeader = '-----BEGIN PGP SIGNATURE-----'; + signature = signatureHeader + clearsigned.split(signatureHeader).pop(); + }); + + it('should work', function(done) { + pgp.verifySignedMessage(signedMessage, signature, pubkey, function(err, signaturesValid) { + expect(err).to.not.exist; + expect(signaturesValid).to.be.true; + done(); + }); + }); + + it('should fail', function(done) { + pgp.verifySignedMessage(signedMessage.replace('signed', 'invalid'), signature, pubkey, function(err, signaturesValid) { + expect(err).to.not.exist; + expect(signaturesValid).to.be.false; + done(); + }); + }); + it('should be null for wrong public key', function(done) { + pgp.verifySignedMessage(signedMessage, signature, wrongPubkey, function(err, signaturesValid) { + expect(err).to.not.exist; + expect(signaturesValid).to.be.null; + done(); + }); + }); + }); + }); }); }); \ No newline at end of file