[WO-383] decrypt pgp/inline

This commit is contained in:
Felix Hammerl 2014-05-13 13:13:36 +02:00
parent ea7a0ceb2a
commit 8767ccda3b
2 changed files with 156 additions and 35 deletions

View File

@ -371,8 +371,38 @@ define(function(require) {
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.body = _.pluck(self._emailSync.filterBodyParts(root, 'text'), 'content').join('\n');
message.body = body;
message.html = _.pluck(self._emailSync.filterBodyParts(root, 'html'), 'content').join('\n');
done();
@ -412,14 +442,23 @@ define(function(require) {
var encryptedNode = self._emailSync.filterBodyParts(message.bodyParts, 'encrypted')[0];
self._crypto.decrypt(encryptedNode.content, senderPublicKey.publicKey, function(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;
}
// the mailparser works on the .raw property
encryptedNode.raw = decrypted;
// parse the decrpyted raw content in the mailparser
// parse the decrypted raw content in the mailparser
self._mailreader.parse({
bodyParts: [encryptedNode]
}, function(err, parsedBodyParts) {
@ -440,7 +479,6 @@ define(function(require) {
message.decrypted = true;
// we're done here!
done();
});

View File

@ -21,6 +21,7 @@ define(function(require) {
var emailAddress, passphrase, asymKeySize, mockkeyId, dummyEncryptedMail,
dummyDecryptedMail, mockKeyPair, account, verificationMail, verificationUuid,
corruptedVerificationMail, corruptedVerificationUuid,
localListStub, localStoreStub, imapGetStub,
nonWhitelistedMail;
beforeEach(function(done) {
@ -122,6 +123,10 @@ define(function(require) {
dao = new EmailDAO(keychainStub, pgpStub, devicestorageStub, pgpBuilderStub, mailreader, emailSync);
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._crypto).to.equal(pgpStub);
expect(dao._devicestorage).to.equal(devicestorageStub);
@ -606,8 +611,7 @@ define(function(require) {
describe('getBody', function() {
var folder = 'asdasdasdasdasd',
uid = 1234,
localListStub, localStoreStub, imapGetStub;
uid = 1234;
it('should not do anything if the message already has content', function() {
var message = {
@ -629,7 +633,7 @@ define(function(require) {
uid: uid
};
localListStub = sinon.stub(emailSync, '_localListMessages').withArgs({
localListStub.withArgs({
folder: folder,
uid: uid
}).yieldsAsync(null, [{
@ -656,7 +660,7 @@ define(function(require) {
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;
pt = 'bender is great!';
@ -666,7 +670,7 @@ define(function(require) {
encrypted: true
};
localListStub = sinon.stub(emailSync, '_localListMessages').withArgs({
localListStub.withArgs({
folder: folder,
uid: uid
}).yieldsAsync(null, [{
@ -697,6 +701,48 @@ define(function(require) {
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) {
var message, body;
@ -710,17 +756,17 @@ define(function(require) {
}]
};
localListStub = sinon.stub(emailSync, '_localListMessages').withArgs({
localListStub.withArgs({
folder: folder,
uid: uid
}).yieldsAsync(null, [message]);
localStoreStub = sinon.stub(emailSync, '_localStoreMessages').withArgs({
localStoreStub.withArgs({
folder: folder,
emails: [message]
}).yieldsAsync();
imapGetStub = sinon.stub(emailSync, '_getBodyParts').withArgs({
imapGetStub.withArgs({
folder: folder,
uid: message.uid,
bodyParts: message.bodyParts
@ -763,17 +809,17 @@ define(function(require) {
}]
};
localListStub = sinon.stub(emailSync, '_localListMessages').withArgs({
localListStub.withArgs({
folder: folder,
uid: uid
}).yieldsAsync(null, [message]);
localStoreStub = sinon.stub(emailSync, '_localStoreMessages').withArgs({
localStoreStub.withArgs({
folder: folder,
emails: [message]
}).yieldsAsync();
imapGetStub = sinon.stub(emailSync, '_getBodyParts').withArgs({
imapGetStub.withArgs({
folder: folder,
uid: message.uid,
bodyParts: message.bodyParts
@ -814,9 +860,9 @@ define(function(require) {
}]
};
localListStub = sinon.stub(emailSync, '_localListMessages').yieldsAsync(null, [message]);
localStoreStub = sinon.stub(emailSync, '_localStoreMessages').yieldsAsync({});
imapGetStub = sinon.stub(emailSync, '_getBodyParts').yieldsAsync(null, [{
localListStub.yieldsAsync(null, [message]);
localStoreStub.yieldsAsync({});
imapGetStub.yieldsAsync(null, [{
type: 'text',
content: 'bender is great! bender is great!'
}]);
@ -845,9 +891,8 @@ define(function(require) {
}]
};
localListStub = sinon.stub(emailSync, '_localListMessages').yields(null, [message]);
imapGetStub = sinon.stub(emailSync, '_getBodyParts').yieldsAsync({});
localStoreStub = sinon.stub(emailSync, '_localStoreMessages');
localListStub.yields(null, [message]);
imapGetStub.yieldsAsync({});
dao.getBody({
message: message,
@ -867,6 +912,16 @@ define(function(require) {
});
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() {
var message = {
encrypted: false,
@ -917,7 +972,7 @@ define(function(require) {
});
it('decrypt a pgp/mime message', function(done) {
var message, ct, pt, parsed, parseStub;
var message, ct, pt, parsed;
pt = 'bender is great';
ct = '-----BEGIN PGP MESSAGE-----asdasdasd-----END PGP MESSAGE-----';
@ -934,10 +989,9 @@ define(function(require) {
}]
};
keychainStub.getReceiverPublicKey.withArgs(message.from[0].address).yieldsAsync(null, mockKeyPair.publicKey);
pgpStub.decrypt.withArgs(ct, mockKeyPair.publicKey.publicKey).yieldsAsync(null, pt);
parseStub = sinon.stub(mailreader, 'parse').withArgs({
parseStub.withArgs({
bodyParts: [{
type: 'encrypted',
content: ct,
@ -955,17 +1009,54 @@ define(function(require) {
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(parsed);
expect(message.decryptingBody).to.be.false;
expect(keychainStub.getReceiverPublicKey.calledOnce).to.be.true;
expect(pgpStub.decrypt.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();
});
@ -985,7 +1076,6 @@ define(function(require) {
}]
};
var parseStub = sinon.spy(mailreader, 'parse');
keychainStub.getReceiverPublicKey.yields(null, mockKeyPair.publicKey);
pgpStub.decrypt.yields({
errMsg: 'asd'
@ -998,12 +1088,10 @@ define(function(require) {
expect(msg.body).to.equal('asd');
expect(msg).to.exist;
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;
mailreader.parse.restore();
done();
});
});
@ -1021,23 +1109,18 @@ define(function(require) {
}]
};
var parseStub = sinon.spy(mailreader, 'parse');
keychainStub.getReceiverPublicKey.yields({});
dao.decryptBody({
message: message
}, function(error, msg) {
expect(error).to.exist;
expect(msg).to.not.exist;
expect(message.decryptingBody).to.be.false;
expect(keychainStub.getReceiverPublicKey.calledOnce).to.be.true;
expect(pgpStub.decrypt.called).to.be.false;
expect(parseStub.called).to.be.false;
mailreader.parse.restore();
done();
});
});