1
0
mirror of https://github.com/moparisthebest/mail synced 2024-12-22 15:28:49 -05:00

Merge pull request #77 from whiteout-io/dev/WO-292

Added integration tests for Apple Mail and Thunderbird
This commit is contained in:
Felix Hammerl 2014-06-27 17:54:28 +02:00
commit 0cd4430103
6 changed files with 406 additions and 41 deletions

View File

@ -277,10 +277,11 @@ define(function(require) {
};
/**
* Decrypt and verify a pgp message for a single sender
* Decrypt and verify a pgp message for a single sender.
* You need to check if signatures are both present and valid in the callback!
*/
PGP.prototype.decrypt = function(ciphertext, publicKeyArmored, callback) {
var publicKeys, message, signaturesValid;
var publicKeys, message, signaturesValid, signaturesPresent;
// check keys
if (!this._privateKey || !publicKeyArmored) {
@ -308,18 +309,15 @@ define(function(require) {
// check if signatures are valid
signaturesValid = true;
signaturesPresent = !!decrypted.signatures.length;
decrypted.signatures.forEach(function(sig) {
if (!sig.valid) {
signaturesValid = false;
}
});
if (!signaturesValid) {
callback(new Error('Verifying PGP signature failed!'));
return;
}
// return decrypted plaintext
callback(null, decrypted.text);
callback(null, decrypted.text, signaturesPresent, signaturesValid);
}
};

View File

@ -707,7 +707,7 @@ define(function(require) {
if (message.signed) {
var signedPart = filterBodyParts(message.bodyParts, 'signed')[0];
message.message = signedPart.message;
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
@ -719,17 +719,18 @@ define(function(require) {
var body = _.pluck(filterBodyParts(root, 'text'), 'content').join('\n');
/*
* here's how the regex works:
* 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)
*/
var match = body.match(/^-{5}BEGIN PGP MESSAGE-{5}$[^>]*^-{5}END PGP MESSAGE-{5}$/im);
if (match) {
var pgpInlineRegex = /^-{5}BEGIN PGP MESSAGE-{5}$[^>]*^-{5}END PGP MESSAGE-{5}$/im;
var pgpInlineMatch = pgpInlineRegex.exec(body);
if (pgpInlineMatch) {
// show the plain text content
message.body = match[0];
message.body = pgpInlineMatch[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
@ -737,15 +738,35 @@ define(function(require) {
message.encrypted = true;
message.bodyParts = [{
type: 'encrypted',
content: match[0],
content: pgpInlineMatch[0],
_isPgpInline: true
}];
done();
return;
}
message.attachments = filterBodyParts(root, 'attachment');
/*
* 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)
*/
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);
if (clearSignedMatch) {
message.clearSignedMessage = clearSignedMatch[0];
message.signed = true;
// TODO check integrity
message.body = clearSignedMatch[1].trim();
} else {
message.body = body;
}
message.attachments = filterBodyParts(root, 'attachment');
message.html = _.pluck(filterBodyParts(root, 'html'), 'content').join('\n');
inlineExternalImages(message);
@ -818,10 +839,13 @@ define(function(require) {
// get the receiver's public key to check the message signature
var encryptedNode = filterBodyParts(message.bodyParts, 'encrypted')[0];
self._pgp.decrypt(encryptedNode.content, senderPublicKey.publicKey, function(err, decrypted) {
self._pgp.decrypt(encryptedNode.content, senderPublicKey.publicKey, function(err, decrypted, signaturesPresent, signaturesValid) {
if (err || !decrypted) {
showError(err.errMsg || err.message || 'An error occurred during the decryption.');
return;
return showError(err.message || 'An error occurred during the decryption.');
}
if (signaturesPresent && !signaturesValid) {
return callback(new Error('Could not verifying the authenticity of this message because PGP signature check failed! This message may have been tampered with!'));
}
// if the encrypted node contains pgp/inline, we must not parse it
@ -847,7 +871,6 @@ define(function(require) {
// 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) {
@ -856,6 +879,9 @@ define(function(require) {
});
inlineExternalImages(message);
// if the decryption worked and signatures are present, everything's fine.
// no error is thrown if signatures are not present
message.signed = signaturesPresent;
message.decrypted = true;
// we're done here!

View File

@ -377,10 +377,9 @@ define(function(require) {
// decrypt the session key
var ct = regSessionKey.encryptedRegSessionKey;
self._pgp.decrypt(ct, serverPubkey.publicKey, function(err, decrypedSessionKey) {
if (err) {
callback(err);
return;
self._pgp.decrypt(ct, serverPubkey.publicKey, function(err, decrypedSessionKey, signaturesPresent, signaturesValid) {
if (err || !(/*signaturesPresent &&*/ signaturesValid)) {
return callback(err || new Error('Verifying PGP signature failed!'));
}
uploadDeviceSecret(decrypedSessionKey);
@ -465,18 +464,16 @@ define(function(require) {
// decrypt the session key
var ct1 = authSessionKey.encryptedAuthSessionKey;
self._pgp.decrypt(ct1, serverPubkey.publicKey, function(err, decryptedSessionKey) {
if (err) {
callback(err);
return;
self._pgp.decrypt(ct1, serverPubkey.publicKey, function(err, decryptedSessionKey, signaturesPresent, signaturesValid) {
if (err || !(/*signaturesPresent &&*/ signaturesValid)) {
return callback(err || new Error('Verifying PGP signature failed!'));
}
// decrypt the challenge
var ct2 = authSessionKey.encryptedChallenge;
self._pgp.decrypt(ct2, serverPubkey.publicKey, function(err, decryptedChallenge) {
if (err) {
callback(err);
return;
self._pgp.decrypt(ct2, serverPubkey.publicKey, function(err, decryptedChallenge, signaturesPresent, signaturesValid) {
if (err || !(/*signaturesPresent &&*/ signaturesValid)) {
return callback(err || new Error('Verifying PGP signature failed!'));
}
encryptChallenge(decryptedSessionKey, decryptedChallenge);

File diff suppressed because one or more lines are too long

View File

@ -1326,15 +1326,13 @@ define(function(require) {
};
keychainStub.getReceiverPublicKey.yieldsAsync(null, mockKeyPair.publicKey);
pgpStub.decrypt.yieldsAsync({
errMsg: 'asd'
});
pgpStub.decrypt.yieldsAsync(new Error('fail.'));
dao.decryptBody({
message: message
}, function(error, msg) {
expect(error).to.not.exist;
expect(msg.body).to.equal('asd');
expect(msg.body).to.equal('fail.');
expect(msg).to.exist;
expect(message.decryptingBody).to.be.false;
expect(keychainStub.getReceiverPublicKey.calledOnce).to.be.true;

View File

@ -799,7 +799,7 @@ define(function(require) {
lookupPublicKeyStub.yields(null, {
publicKey: 'pubkey'
});
pgpStub.decrypt.withArgs('asdf', 'pubkey').yields(null, 'decrypted');
pgpStub.decrypt.withArgs('asdf', 'pubkey').yields(null, 'decrypted', true, true);
getDeviceSecretStub.yields(42);
keychainDao.registerDevice({
@ -823,7 +823,7 @@ define(function(require) {
lookupPublicKeyStub.yields(null, {
publicKey: 'pubkey'
});
pgpStub.decrypt.withArgs('asdf', 'pubkey').yields(null, 'decrypted');
pgpStub.decrypt.withArgs('asdf', 'pubkey').yields(null, 'decrypted', true, true);
getDeviceSecretStub.yields(null, 'secret');
cryptoStub.encrypt.withArgs('secret', 'decrypted').yields(42);
@ -848,7 +848,7 @@ define(function(require) {
lookupPublicKeyStub.yields(null, {
publicKey: 'pubkey'
});
pgpStub.decrypt.withArgs('asdf', 'pubkey').yields(null, 'decrypted');
pgpStub.decrypt.withArgs('asdf', 'pubkey').yields(null, 'decrypted', true, true);
getDeviceSecretStub.yields(null, 'secret');
cryptoStub.encrypt.withArgs('secret', 'decrypted').yields(null, 'encryptedDeviceSecret');
privkeyDaoStub.uploadDeviceSecret.yields();
@ -987,7 +987,7 @@ define(function(require) {
publickKey: 'publicKey'
});
pgpStub.decrypt.yields(null, 'decryptedStuff');
pgpStub.decrypt.yields(null, 'decryptedStuff', true, true);
getDeviceSecretStub.yields(null, 'deviceSecret');
cryptoStub.encrypt.yields(null, 'encryptedStuff');
privkeyDaoStub.verifyAuthentication.yields(42);
@ -1008,7 +1008,7 @@ define(function(require) {
lookupPublicKeyStub.yields();
pgpStub.decrypt.yields(null, 'decryptedStuff');
pgpStub.decrypt.yields(null, 'decryptedStuff', true, true);
getDeviceSecretStub.yields(null, 'deviceSecret');
cryptoStub.encrypt.yields(null, 'encryptedStuff');
privkeyDaoStub.verifyAuthentication.yields();
@ -1031,7 +1031,7 @@ define(function(require) {
publicKey: 'publicKey'
});
pgpStub.decrypt.yields(null, 'decryptedStuff');
pgpStub.decrypt.yields(null, 'decryptedStuff', true, true);
getDeviceSecretStub.yields(null, 'deviceSecret');
cryptoStub.encrypt.yields(null, 'encryptedStuff');
privkeyDaoStub.verifyAuthentication.yields();