mirror of
https://github.com/moparisthebest/mail
synced 2024-11-22 17:02:17 -05:00
Merge pull request #61 from whiteout-io/dev/WO-383
[WO-383] decrypt pgp/inline
This commit is contained in:
commit
e6de5366c9
@ -382,8 +382,38 @@ define(function(require) {
|
|||||||
root = signedPart.content;
|
root = signedPart.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(self._emailSync.filterBodyParts(root, 'text'), 'content').join('\n');
|
||||||
|
|
||||||
|
/*
|
||||||
|
* here's how the 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)
|
||||||
|
*/
|
||||||
|
var match = body.match(/^-{5}BEGIN PGP MESSAGE-{5}$[^>]*^-{5}END PGP MESSAGE-{5}$/im);
|
||||||
|
if (match) {
|
||||||
|
// show the plain text content
|
||||||
|
message.body = match[0];
|
||||||
|
|
||||||
|
// - 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;
|
||||||
|
message.bodyParts = [{
|
||||||
|
type: 'encrypted',
|
||||||
|
content: match[0],
|
||||||
|
_isPgpInline: true
|
||||||
|
}];
|
||||||
|
done();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
message.attachments = self._emailSync.filterBodyParts(root, 'attachment');
|
message.attachments = self._emailSync.filterBodyParts(root, 'attachment');
|
||||||
message.body = _.pluck(self._emailSync.filterBodyParts(root, 'text'), 'content').join('\n');
|
message.body = body;
|
||||||
message.html = _.pluck(self._emailSync.filterBodyParts(root, 'html'), 'content').join('\n');
|
message.html = _.pluck(self._emailSync.filterBodyParts(root, 'html'), 'content').join('\n');
|
||||||
|
|
||||||
done();
|
done();
|
||||||
@ -439,14 +469,23 @@ define(function(require) {
|
|||||||
var encryptedNode = self._emailSync.filterBodyParts(message.bodyParts, 'encrypted')[0];
|
var encryptedNode = self._emailSync.filterBodyParts(message.bodyParts, 'encrypted')[0];
|
||||||
self._crypto.decrypt(encryptedNode.content, senderPublicKey.publicKey, function(err, decrypted) {
|
self._crypto.decrypt(encryptedNode.content, senderPublicKey.publicKey, function(err, decrypted) {
|
||||||
if (err || !decrypted) {
|
if (err || !decrypted) {
|
||||||
showError(err.errMsg || err.message);
|
showError(err.errMsg || err.message || 'An error occurred during the decryption.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the encrypted node contains pgp/inline, we must not parse it
|
||||||
|
// with the mailreader as it is not well-formed MIME
|
||||||
|
if (encryptedNode._isPgpInline) {
|
||||||
|
message.body = decrypted;
|
||||||
|
message.decrypted = true;
|
||||||
|
done();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// the mailparser works on the .raw property
|
// the mailparser works on the .raw property
|
||||||
encryptedNode.raw = decrypted;
|
encryptedNode.raw = decrypted;
|
||||||
|
|
||||||
// parse the decrpyted raw content in the mailparser
|
// parse the decrypted raw content in the mailparser
|
||||||
self._mailreader.parse({
|
self._mailreader.parse({
|
||||||
bodyParts: [encryptedNode]
|
bodyParts: [encryptedNode]
|
||||||
}, function(err, parsedBodyParts) {
|
}, function(err, parsedBodyParts) {
|
||||||
@ -467,7 +506,6 @@ define(function(require) {
|
|||||||
|
|
||||||
message.decrypted = true;
|
message.decrypted = true;
|
||||||
|
|
||||||
|
|
||||||
// we're done here!
|
// we're done here!
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -21,6 +21,7 @@ define(function(require) {
|
|||||||
var emailAddress, passphrase, asymKeySize, mockkeyId, dummyEncryptedMail,
|
var emailAddress, passphrase, asymKeySize, mockkeyId, dummyEncryptedMail,
|
||||||
dummyDecryptedMail, mockKeyPair, account, verificationMail, verificationUuid,
|
dummyDecryptedMail, mockKeyPair, account, verificationMail, verificationUuid,
|
||||||
corruptedVerificationMail, corruptedVerificationUuid,
|
corruptedVerificationMail, corruptedVerificationUuid,
|
||||||
|
localListStub, localStoreStub, imapGetStub,
|
||||||
nonWhitelistedMail;
|
nonWhitelistedMail;
|
||||||
|
|
||||||
beforeEach(function(done) {
|
beforeEach(function(done) {
|
||||||
@ -122,6 +123,10 @@ define(function(require) {
|
|||||||
dao = new EmailDAO(keychainStub, pgpStub, devicestorageStub, pgpBuilderStub, mailreader, emailSync);
|
dao = new EmailDAO(keychainStub, pgpStub, devicestorageStub, pgpBuilderStub, mailreader, emailSync);
|
||||||
dao._account = account;
|
dao._account = account;
|
||||||
|
|
||||||
|
localListStub = sinon.stub(emailSync, '_localListMessages');
|
||||||
|
localStoreStub = sinon.stub(emailSync, '_localStoreMessages');
|
||||||
|
imapGetStub = sinon.stub(emailSync, '_getBodyParts');
|
||||||
|
|
||||||
expect(dao._keychain).to.equal(keychainStub);
|
expect(dao._keychain).to.equal(keychainStub);
|
||||||
expect(dao._crypto).to.equal(pgpStub);
|
expect(dao._crypto).to.equal(pgpStub);
|
||||||
expect(dao._devicestorage).to.equal(devicestorageStub);
|
expect(dao._devicestorage).to.equal(devicestorageStub);
|
||||||
@ -606,8 +611,7 @@ define(function(require) {
|
|||||||
|
|
||||||
describe('getBody', function() {
|
describe('getBody', function() {
|
||||||
var folder = 'asdasdasdasdasd',
|
var folder = 'asdasdasdasdasd',
|
||||||
uid = 1234,
|
uid = 1234;
|
||||||
localListStub, localStoreStub, imapGetStub;
|
|
||||||
|
|
||||||
it('should not do anything if the message already has content', function() {
|
it('should not do anything if the message already has content', function() {
|
||||||
var message = {
|
var message = {
|
||||||
@ -629,7 +633,7 @@ define(function(require) {
|
|||||||
uid: uid
|
uid: uid
|
||||||
};
|
};
|
||||||
|
|
||||||
localListStub = sinon.stub(emailSync, '_localListMessages').withArgs({
|
localListStub.withArgs({
|
||||||
folder: folder,
|
folder: folder,
|
||||||
uid: uid
|
uid: uid
|
||||||
}).yieldsAsync(null, [{
|
}).yieldsAsync(null, [{
|
||||||
@ -656,7 +660,7 @@ define(function(require) {
|
|||||||
expect(message.loadingBody).to.be.true;
|
expect(message.loadingBody).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should read an encrypted body from the device', function(done) {
|
it('should read a pgp/mime from the device', function(done) {
|
||||||
var message, ct, pt;
|
var message, ct, pt;
|
||||||
|
|
||||||
pt = 'bender is great!';
|
pt = 'bender is great!';
|
||||||
@ -666,7 +670,7 @@ define(function(require) {
|
|||||||
encrypted: true
|
encrypted: true
|
||||||
};
|
};
|
||||||
|
|
||||||
localListStub = sinon.stub(emailSync, '_localListMessages').withArgs({
|
localListStub.withArgs({
|
||||||
folder: folder,
|
folder: folder,
|
||||||
uid: uid
|
uid: uid
|
||||||
}).yieldsAsync(null, [{
|
}).yieldsAsync(null, [{
|
||||||
@ -697,6 +701,48 @@ define(function(require) {
|
|||||||
expect(message.loadingBody).to.be.true;
|
expect(message.loadingBody).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should read a pgp/inline from the device', function(done) {
|
||||||
|
var message, ct, pt;
|
||||||
|
|
||||||
|
ct = '-----BEGIN PGP MESSAGE-----\nasdasdasd\n-----END PGP MESSAGE-----';
|
||||||
|
pt = 'bla bla yadda yadda';
|
||||||
|
message = {
|
||||||
|
uid: uid
|
||||||
|
};
|
||||||
|
|
||||||
|
localListStub.yieldsAsync(null, [{
|
||||||
|
bodyParts: [{
|
||||||
|
type: 'text',
|
||||||
|
content: pt
|
||||||
|
}, {
|
||||||
|
type: 'text',
|
||||||
|
content: ct
|
||||||
|
}, {
|
||||||
|
type: 'text',
|
||||||
|
content: pt
|
||||||
|
}]
|
||||||
|
}]);
|
||||||
|
|
||||||
|
dao.getBody({
|
||||||
|
message: message,
|
||||||
|
folder: folder
|
||||||
|
}, function(err, msg) {
|
||||||
|
expect(err).to.not.exist;
|
||||||
|
|
||||||
|
expect(msg).to.equal(message);
|
||||||
|
expect(msg.body).to.equal(ct);
|
||||||
|
expect(msg.bodyParts[0].type).to.equal('encrypted');
|
||||||
|
expect(msg.bodyParts[0].content).to.equal(ct);
|
||||||
|
expect(msg.encrypted).to.be.true;
|
||||||
|
expect(message.loadingBody).to.be.false;
|
||||||
|
|
||||||
|
expect(localListStub.calledOnce).to.be.true;
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
expect(message.loadingBody).to.be.true;
|
||||||
|
});
|
||||||
|
|
||||||
it('should stream from imap and set plain text body', function(done) {
|
it('should stream from imap and set plain text body', function(done) {
|
||||||
var message, body;
|
var message, body;
|
||||||
|
|
||||||
@ -710,17 +756,17 @@ define(function(require) {
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
|
|
||||||
localListStub = sinon.stub(emailSync, '_localListMessages').withArgs({
|
localListStub.withArgs({
|
||||||
folder: folder,
|
folder: folder,
|
||||||
uid: uid
|
uid: uid
|
||||||
}).yieldsAsync(null, [message]);
|
}).yieldsAsync(null, [message]);
|
||||||
|
|
||||||
localStoreStub = sinon.stub(emailSync, '_localStoreMessages').withArgs({
|
localStoreStub.withArgs({
|
||||||
folder: folder,
|
folder: folder,
|
||||||
emails: [message]
|
emails: [message]
|
||||||
}).yieldsAsync();
|
}).yieldsAsync();
|
||||||
|
|
||||||
imapGetStub = sinon.stub(emailSync, '_getBodyParts').withArgs({
|
imapGetStub.withArgs({
|
||||||
folder: folder,
|
folder: folder,
|
||||||
uid: message.uid,
|
uid: message.uid,
|
||||||
bodyParts: message.bodyParts
|
bodyParts: message.bodyParts
|
||||||
@ -763,17 +809,17 @@ define(function(require) {
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
|
|
||||||
localListStub = sinon.stub(emailSync, '_localListMessages').withArgs({
|
localListStub.withArgs({
|
||||||
folder: folder,
|
folder: folder,
|
||||||
uid: uid
|
uid: uid
|
||||||
}).yieldsAsync(null, [message]);
|
}).yieldsAsync(null, [message]);
|
||||||
|
|
||||||
localStoreStub = sinon.stub(emailSync, '_localStoreMessages').withArgs({
|
localStoreStub.withArgs({
|
||||||
folder: folder,
|
folder: folder,
|
||||||
emails: [message]
|
emails: [message]
|
||||||
}).yieldsAsync();
|
}).yieldsAsync();
|
||||||
|
|
||||||
imapGetStub = sinon.stub(emailSync, '_getBodyParts').withArgs({
|
imapGetStub.withArgs({
|
||||||
folder: folder,
|
folder: folder,
|
||||||
uid: message.uid,
|
uid: message.uid,
|
||||||
bodyParts: message.bodyParts
|
bodyParts: message.bodyParts
|
||||||
@ -814,9 +860,9 @@ define(function(require) {
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
|
|
||||||
localListStub = sinon.stub(emailSync, '_localListMessages').yieldsAsync(null, [message]);
|
localListStub.yieldsAsync(null, [message]);
|
||||||
localStoreStub = sinon.stub(emailSync, '_localStoreMessages').yieldsAsync({});
|
localStoreStub.yieldsAsync({});
|
||||||
imapGetStub = sinon.stub(emailSync, '_getBodyParts').yieldsAsync(null, [{
|
imapGetStub.yieldsAsync(null, [{
|
||||||
type: 'text',
|
type: 'text',
|
||||||
content: 'bender is great! bender is great!'
|
content: 'bender is great! bender is great!'
|
||||||
}]);
|
}]);
|
||||||
@ -845,9 +891,8 @@ define(function(require) {
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
|
|
||||||
localListStub = sinon.stub(emailSync, '_localListMessages').yields(null, [message]);
|
localListStub.yields(null, [message]);
|
||||||
imapGetStub = sinon.stub(emailSync, '_getBodyParts').yieldsAsync({});
|
imapGetStub.yieldsAsync({});
|
||||||
localStoreStub = sinon.stub(emailSync, '_localStoreMessages');
|
|
||||||
|
|
||||||
dao.getBody({
|
dao.getBody({
|
||||||
message: message,
|
message: message,
|
||||||
@ -924,6 +969,16 @@ define(function(require) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('decryptBody', function() {
|
describe('decryptBody', function() {
|
||||||
|
var parseStub;
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
parseStub = sinon.stub(mailreader, 'parse');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function() {
|
||||||
|
mailreader.parse.restore();
|
||||||
|
});
|
||||||
|
|
||||||
it('should do nothing when the message is not encrypted', function() {
|
it('should do nothing when the message is not encrypted', function() {
|
||||||
var message = {
|
var message = {
|
||||||
encrypted: false,
|
encrypted: false,
|
||||||
@ -974,7 +1029,7 @@ define(function(require) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('decrypt a pgp/mime message', function(done) {
|
it('decrypt a pgp/mime message', function(done) {
|
||||||
var message, ct, pt, parsed, parseStub;
|
var message, ct, pt, parsed;
|
||||||
|
|
||||||
pt = 'bender is great';
|
pt = 'bender is great';
|
||||||
ct = '-----BEGIN PGP MESSAGE-----asdasdasd-----END PGP MESSAGE-----';
|
ct = '-----BEGIN PGP MESSAGE-----asdasdasd-----END PGP MESSAGE-----';
|
||||||
@ -991,10 +1046,9 @@ define(function(require) {
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
keychainStub.getReceiverPublicKey.withArgs(message.from[0].address).yieldsAsync(null, mockKeyPair.publicKey);
|
keychainStub.getReceiverPublicKey.withArgs(message.from[0].address).yieldsAsync(null, mockKeyPair.publicKey);
|
||||||
pgpStub.decrypt.withArgs(ct, mockKeyPair.publicKey.publicKey).yieldsAsync(null, pt);
|
pgpStub.decrypt.withArgs(ct, mockKeyPair.publicKey.publicKey).yieldsAsync(null, pt);
|
||||||
parseStub = sinon.stub(mailreader, 'parse').withArgs({
|
parseStub.withArgs({
|
||||||
bodyParts: [{
|
bodyParts: [{
|
||||||
type: 'encrypted',
|
type: 'encrypted',
|
||||||
content: ct,
|
content: ct,
|
||||||
@ -1012,17 +1066,54 @@ define(function(require) {
|
|||||||
message: message
|
message: message
|
||||||
}, function(error, msg) {
|
}, function(error, msg) {
|
||||||
expect(error).to.not.exist;
|
expect(error).to.not.exist;
|
||||||
|
|
||||||
expect(msg).to.equal(message);
|
expect(msg).to.equal(message);
|
||||||
expect(message.decrypted).to.be.true;
|
expect(message.decrypted).to.be.true;
|
||||||
expect(message.body).to.equal(parsed);
|
expect(message.body).to.equal(parsed);
|
||||||
expect(message.decryptingBody).to.be.false;
|
expect(message.decryptingBody).to.be.false;
|
||||||
|
|
||||||
expect(keychainStub.getReceiverPublicKey.calledOnce).to.be.true;
|
expect(keychainStub.getReceiverPublicKey.calledOnce).to.be.true;
|
||||||
expect(pgpStub.decrypt.calledOnce).to.be.true;
|
expect(pgpStub.decrypt.calledOnce).to.be.true;
|
||||||
expect(parseStub.calledOnce).to.be.true;
|
expect(parseStub.calledOnce).to.be.true;
|
||||||
|
|
||||||
mailreader.parse.restore();
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(message.decryptingBody).to.be.true;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('decrypt a pgp/inline message', function(done) {
|
||||||
|
var message, ct, pt;
|
||||||
|
|
||||||
|
pt = 'bender is great';
|
||||||
|
ct = '-----BEGIN PGP MESSAGE-----asdasdasd-----END PGP MESSAGE-----';
|
||||||
|
message = {
|
||||||
|
from: [{
|
||||||
|
address: 'asdasdasd'
|
||||||
|
}],
|
||||||
|
body: ct,
|
||||||
|
encrypted: true,
|
||||||
|
bodyParts: [{
|
||||||
|
type: 'encrypted',
|
||||||
|
content: ct,
|
||||||
|
_isPgpInline: true
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
|
||||||
|
keychainStub.getReceiverPublicKey.withArgs(message.from[0].address).yieldsAsync(null, mockKeyPair.publicKey);
|
||||||
|
pgpStub.decrypt.withArgs(ct, mockKeyPair.publicKey.publicKey).yieldsAsync(null, pt);
|
||||||
|
|
||||||
|
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(pt);
|
||||||
|
expect(message.decryptingBody).to.be.false;
|
||||||
|
expect(keychainStub.getReceiverPublicKey.calledOnce).to.be.true;
|
||||||
|
expect(pgpStub.decrypt.calledOnce).to.be.true;
|
||||||
|
expect(parseStub.called).to.be.false;
|
||||||
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1042,7 +1133,6 @@ define(function(require) {
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
|
|
||||||
var parseStub = sinon.spy(mailreader, 'parse');
|
|
||||||
keychainStub.getReceiverPublicKey.yields(null, mockKeyPair.publicKey);
|
keychainStub.getReceiverPublicKey.yields(null, mockKeyPair.publicKey);
|
||||||
pgpStub.decrypt.yields({
|
pgpStub.decrypt.yields({
|
||||||
errMsg: 'asd'
|
errMsg: 'asd'
|
||||||
@ -1055,12 +1145,10 @@ define(function(require) {
|
|||||||
expect(msg.body).to.equal('asd');
|
expect(msg.body).to.equal('asd');
|
||||||
expect(msg).to.exist;
|
expect(msg).to.exist;
|
||||||
expect(message.decryptingBody).to.be.false;
|
expect(message.decryptingBody).to.be.false;
|
||||||
|
|
||||||
expect(keychainStub.getReceiverPublicKey.calledOnce).to.be.true;
|
expect(keychainStub.getReceiverPublicKey.calledOnce).to.be.true;
|
||||||
expect(pgpStub.decrypt.calledOnce).to.be.true;
|
expect(pgpStub.decrypt.calledOnce).to.be.true;
|
||||||
expect(parseStub.called).to.be.false;
|
expect(parseStub.called).to.be.false;
|
||||||
|
|
||||||
mailreader.parse.restore();
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -1078,23 +1166,18 @@ define(function(require) {
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
|
|
||||||
var parseStub = sinon.spy(mailreader, 'parse');
|
|
||||||
keychainStub.getReceiverPublicKey.yields({});
|
keychainStub.getReceiverPublicKey.yields({});
|
||||||
|
|
||||||
dao.decryptBody({
|
dao.decryptBody({
|
||||||
message: message
|
message: message
|
||||||
}, function(error, msg) {
|
}, function(error, msg) {
|
||||||
expect(error).to.exist;
|
expect(error).to.exist;
|
||||||
|
|
||||||
expect(msg).to.not.exist;
|
expect(msg).to.not.exist;
|
||||||
|
|
||||||
expect(message.decryptingBody).to.be.false;
|
expect(message.decryptingBody).to.be.false;
|
||||||
|
|
||||||
expect(keychainStub.getReceiverPublicKey.calledOnce).to.be.true;
|
expect(keychainStub.getReceiverPublicKey.calledOnce).to.be.true;
|
||||||
expect(pgpStub.decrypt.called).to.be.false;
|
expect(pgpStub.decrypt.called).to.be.false;
|
||||||
expect(parseStub.called).to.be.false;
|
expect(parseStub.called).to.be.false;
|
||||||
|
|
||||||
mailreader.parse.restore();
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user