mirror of
https://github.com/moparisthebest/mail
synced 2024-11-22 17:02:17 -05:00
add silent public key verification
This commit is contained in:
parent
0470ae00ea
commit
99a6cda40d
@ -38,7 +38,8 @@ define(function(require) {
|
|||||||
},
|
},
|
||||||
checkOutboxInterval: 5000,
|
checkOutboxInterval: 5000,
|
||||||
iconPath: '/img/icon.png',
|
iconPath: '/img/icon.png',
|
||||||
verificationUrl: '/verify/'
|
verificationUrl: '/verify/',
|
||||||
|
verificationUuidLength: 36
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -3,8 +3,8 @@ define(function(require) {
|
|||||||
|
|
||||||
var util = require('cryptoLib/util'),
|
var util = require('cryptoLib/util'),
|
||||||
_ = require('underscore'),
|
_ = require('underscore'),
|
||||||
str = require('js/app-config').string;
|
str = require('js/app-config').string,
|
||||||
// config = require('js/app-config').config;
|
config = require('js/app-config').config;
|
||||||
|
|
||||||
var EmailDAO = function(keychain, imapClient, smtpClient, crypto, devicestorage) {
|
var EmailDAO = function(keychain, imapClient, smtpClient, crypto, devicestorage) {
|
||||||
var self = this;
|
var self = this;
|
||||||
@ -375,6 +375,19 @@ define(function(require) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isVerificationMail(message)) {
|
||||||
|
verify(message, function(err) {
|
||||||
|
if (err) {
|
||||||
|
self._account.busy = false;
|
||||||
|
callback(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
after();
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// add the encrypted message to the local storage
|
// add the encrypted message to the local storage
|
||||||
self._localStoreMessages({
|
self._localStoreMessages({
|
||||||
folder: folder.path,
|
folder: folder.path,
|
||||||
@ -425,6 +438,54 @@ define(function(require) {
|
|||||||
return delta;
|
return delta;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isVerificationMail(email) {
|
||||||
|
return email.subject === str.subjectPrefix + str.verificationSubject;
|
||||||
|
}
|
||||||
|
|
||||||
|
function verify(email, localCallback) {
|
||||||
|
var uuid, index, verifyUrlPrefix = config.cloudUrl + config.verificationUrl;
|
||||||
|
|
||||||
|
if (!email.unread) {
|
||||||
|
// don't bother if the email was already marked as read
|
||||||
|
localCallback();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
index = email.body.indexOf(verifyUrlPrefix);
|
||||||
|
if (index === -1) {
|
||||||
|
// there's no url in the email, so forget about that.
|
||||||
|
localCallback();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uuid = email.body.substr(index + verifyUrlPrefix.length, config.verificationUuidLength);
|
||||||
|
self._keychain.verifyPublicKey(uuid, function(err) {
|
||||||
|
if (err) {
|
||||||
|
localCallback({
|
||||||
|
errMsg: 'Verifying your public key failed: ' + err.errMsg
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// public key has been verified, mark the message as read, delete it, and ignore it in the future
|
||||||
|
self.markRead({
|
||||||
|
folder: options.folder,
|
||||||
|
uid: email.uid
|
||||||
|
}, function(err) {
|
||||||
|
if (err) {
|
||||||
|
localCallback(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self._imapDeleteMessage({
|
||||||
|
folder: options.folder,
|
||||||
|
uid: email.uid
|
||||||
|
}, localCallback);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function handleMessage(message, localCallback) {
|
function handleMessage(message, localCallback) {
|
||||||
if (containsArmoredCiphertext(message)) {
|
if (containsArmoredCiphertext(message)) {
|
||||||
decrypt(message, localCallback);
|
decrypt(message, localCallback);
|
||||||
|
@ -14,7 +14,7 @@ define(function(require) {
|
|||||||
var dao, keychainStub, imapClientStub, smtpClientStub, pgpStub, devicestorageStub;
|
var dao, keychainStub, imapClientStub, smtpClientStub, pgpStub, devicestorageStub;
|
||||||
|
|
||||||
var emailAddress, passphrase, asymKeySize, mockkeyId, dummyEncryptedMail,
|
var emailAddress, passphrase, asymKeySize, mockkeyId, dummyEncryptedMail,
|
||||||
dummyDecryptedMail, mockKeyPair, account, publicKey;
|
dummyDecryptedMail, mockKeyPair, account, publicKey, verificationMail, verificationUuid;
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
emailAddress = 'asdf@asdf.com';
|
emailAddress = 'asdf@asdf.com';
|
||||||
@ -32,6 +32,19 @@ define(function(require) {
|
|||||||
subject: '[whiteout] qweasd',
|
subject: '[whiteout] qweasd',
|
||||||
body: '-----BEGIN PGP MESSAGE-----\nasd\n-----END PGP MESSAGE-----'
|
body: '-----BEGIN PGP MESSAGE-----\nasd\n-----END PGP MESSAGE-----'
|
||||||
};
|
};
|
||||||
|
verificationUuid = 'OMFG_FUCKING_BASTARD_UUID_FROM_HELL!';
|
||||||
|
verificationMail = {
|
||||||
|
from: [{
|
||||||
|
name: 'Whiteout Test',
|
||||||
|
address: 'whiteout.test@t-online.de'
|
||||||
|
}], // sender address
|
||||||
|
to: [{
|
||||||
|
address: 'safewithme.testuser@gmail.com'
|
||||||
|
}], // list of receivers
|
||||||
|
subject: "[whiteout] New public key uploaded", // Subject line
|
||||||
|
body: 'yadda yadda bla blabla foo bar https://keys.whiteout.io/verify/' + verificationUuid, // plaintext body
|
||||||
|
unread: true
|
||||||
|
};
|
||||||
dummyDecryptedMail = {
|
dummyDecryptedMail = {
|
||||||
uid: 1234,
|
uid: 1234,
|
||||||
from: [{
|
from: [{
|
||||||
@ -1057,6 +1070,7 @@ define(function(require) {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should error while storing messages from the remote locally', function(done) {
|
it('should error while storing messages from the remote locally', function(done) {
|
||||||
var invocations, folder, localListStub, imapListStub, imapGetStub, localStoreStub;
|
var invocations, folder, localListStub, imapListStub, imapGetStub, localStoreStub;
|
||||||
|
|
||||||
@ -1096,6 +1110,7 @@ define(function(require) {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should error while fetching messages from the remote', function(done) {
|
it('should error while fetching messages from the remote', function(done) {
|
||||||
var invocations, folder, localListStub, imapListStub, imapGetStub, localStoreStub;
|
var invocations, folder, localListStub, imapListStub, imapGetStub, localStoreStub;
|
||||||
|
|
||||||
@ -1135,6 +1150,271 @@ define(function(require) {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should verify an authentication mail', function(done) {
|
||||||
|
var invocations, folder, localListStub, imapListStub,
|
||||||
|
imapGetStub, markReadStub, imapDeleteStub;
|
||||||
|
|
||||||
|
invocations = 0;
|
||||||
|
folder = 'FOLDAAAA';
|
||||||
|
dao._account.folders = [{
|
||||||
|
type: 'Folder',
|
||||||
|
path: folder,
|
||||||
|
messages: []
|
||||||
|
}];
|
||||||
|
|
||||||
|
localListStub = sinon.stub(dao, '_localListMessages').yields(null, []);
|
||||||
|
imapListStub = sinon.stub(dao, '_imapListMessages').yields(null, [verificationMail]);
|
||||||
|
imapGetStub = sinon.stub(dao, '_imapGetMessage').yields(null, verificationMail);
|
||||||
|
keychainStub.verifyPublicKey.withArgs(verificationUuid).yields();
|
||||||
|
markReadStub = sinon.stub(dao, 'markRead').withArgs({
|
||||||
|
folder: folder,
|
||||||
|
uid: verificationMail.uid
|
||||||
|
}).yields();
|
||||||
|
imapDeleteStub = sinon.stub(dao, '_imapDeleteMessage').withArgs({
|
||||||
|
folder: folder,
|
||||||
|
uid: verificationMail.uid
|
||||||
|
}).yields();
|
||||||
|
|
||||||
|
dao.sync({
|
||||||
|
folder: folder
|
||||||
|
}, function(err) {
|
||||||
|
expect(err).to.not.exist;
|
||||||
|
|
||||||
|
if (invocations === 0) {
|
||||||
|
expect(dao._account.busy).to.be.true;
|
||||||
|
invocations++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(dao._account.busy).to.be.false;
|
||||||
|
expect(dao._account.folders[0].messages).to.be.empty;
|
||||||
|
expect(localListStub.calledOnce).to.be.true;
|
||||||
|
expect(imapListStub.calledOnce).to.be.true;
|
||||||
|
expect(imapGetStub.calledOnce).to.be.true;
|
||||||
|
expect(keychainStub.verifyPublicKey.calledOnce).to.be.true;
|
||||||
|
expect(markReadStub.calledOnce).to.be.true;
|
||||||
|
expect(imapDeleteStub.calledOnce).to.be.true;
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fail during deletion of an authentication mail', function(done) {
|
||||||
|
var invocations, folder, localListStub, imapListStub,
|
||||||
|
imapGetStub, markReadStub, imapDeleteStub;
|
||||||
|
|
||||||
|
invocations = 0;
|
||||||
|
folder = 'FOLDAAAA';
|
||||||
|
dao._account.folders = [{
|
||||||
|
type: 'Folder',
|
||||||
|
path: folder,
|
||||||
|
messages: []
|
||||||
|
}];
|
||||||
|
|
||||||
|
localListStub = sinon.stub(dao, '_localListMessages').yields(null, []);
|
||||||
|
imapListStub = sinon.stub(dao, '_imapListMessages').yields(null, [verificationMail]);
|
||||||
|
imapGetStub = sinon.stub(dao, '_imapGetMessage').yields(null, verificationMail);
|
||||||
|
keychainStub.verifyPublicKey.yields();
|
||||||
|
markReadStub = sinon.stub(dao, 'markRead').yields();
|
||||||
|
imapDeleteStub = sinon.stub(dao, '_imapDeleteMessage').yields({});
|
||||||
|
|
||||||
|
dao.sync({
|
||||||
|
folder: folder
|
||||||
|
}, function(err) {
|
||||||
|
|
||||||
|
if (invocations === 0) {
|
||||||
|
expect(err).to.not.exist;
|
||||||
|
expect(dao._account.busy).to.be.true;
|
||||||
|
invocations++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(err).to.exist;
|
||||||
|
expect(dao._account.busy).to.be.false;
|
||||||
|
expect(dao._account.folders[0].messages).to.be.empty;
|
||||||
|
expect(localListStub.calledOnce).to.be.true;
|
||||||
|
expect(imapListStub.calledOnce).to.be.true;
|
||||||
|
expect(imapGetStub.calledOnce).to.be.true;
|
||||||
|
expect(keychainStub.verifyPublicKey.calledOnce).to.be.true;
|
||||||
|
expect(markReadStub.calledOnce).to.be.true;
|
||||||
|
expect(imapDeleteStub.calledOnce).to.be.true;
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should fail during marking an authentication mail read', function(done) {
|
||||||
|
var invocations, folder, localListStub, imapListStub,
|
||||||
|
imapGetStub, markReadStub, imapDeleteStub;
|
||||||
|
|
||||||
|
invocations = 0;
|
||||||
|
folder = 'FOLDAAAA';
|
||||||
|
dao._account.folders = [{
|
||||||
|
type: 'Folder',
|
||||||
|
path: folder,
|
||||||
|
messages: []
|
||||||
|
}];
|
||||||
|
|
||||||
|
localListStub = sinon.stub(dao, '_localListMessages').yields(null, []);
|
||||||
|
imapListStub = sinon.stub(dao, '_imapListMessages').yields(null, [verificationMail]);
|
||||||
|
imapGetStub = sinon.stub(dao, '_imapGetMessage').yields(null, verificationMail);
|
||||||
|
keychainStub.verifyPublicKey.yields();
|
||||||
|
markReadStub = sinon.stub(dao, 'markRead').yields({});
|
||||||
|
imapDeleteStub = sinon.stub(dao, '_imapDeleteMessage');
|
||||||
|
|
||||||
|
dao.sync({
|
||||||
|
folder: folder
|
||||||
|
}, function(err) {
|
||||||
|
|
||||||
|
if (invocations === 0) {
|
||||||
|
expect(err).to.not.exist;
|
||||||
|
expect(dao._account.busy).to.be.true;
|
||||||
|
invocations++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(err).to.exist;
|
||||||
|
expect(dao._account.busy).to.be.false;
|
||||||
|
expect(dao._account.folders[0].messages).to.be.empty;
|
||||||
|
expect(localListStub.calledOnce).to.be.true;
|
||||||
|
expect(imapListStub.calledOnce).to.be.true;
|
||||||
|
expect(imapGetStub.calledOnce).to.be.true;
|
||||||
|
expect(keychainStub.verifyPublicKey.calledOnce).to.be.true;
|
||||||
|
expect(markReadStub.calledOnce).to.be.true;
|
||||||
|
expect(imapDeleteStub.called).to.be.false;
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should fail during verifying authentication', function(done) {
|
||||||
|
var invocations, folder, localListStub, imapListStub,
|
||||||
|
imapGetStub, markReadStub, imapDeleteStub;
|
||||||
|
|
||||||
|
invocations = 0;
|
||||||
|
folder = 'FOLDAAAA';
|
||||||
|
dao._account.folders = [{
|
||||||
|
type: 'Folder',
|
||||||
|
path: folder,
|
||||||
|
messages: []
|
||||||
|
}];
|
||||||
|
|
||||||
|
localListStub = sinon.stub(dao, '_localListMessages').yields(null, []);
|
||||||
|
imapListStub = sinon.stub(dao, '_imapListMessages').yields(null, [verificationMail]);
|
||||||
|
imapGetStub = sinon.stub(dao, '_imapGetMessage').yields(null, verificationMail);
|
||||||
|
keychainStub.verifyPublicKey.yields({});
|
||||||
|
markReadStub = sinon.stub(dao, 'markRead');
|
||||||
|
imapDeleteStub = sinon.stub(dao, '_imapDeleteMessage');
|
||||||
|
|
||||||
|
dao.sync({
|
||||||
|
folder: folder
|
||||||
|
}, function(err) {
|
||||||
|
|
||||||
|
if (invocations === 0) {
|
||||||
|
expect(err).to.not.exist;
|
||||||
|
expect(dao._account.busy).to.be.true;
|
||||||
|
invocations++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(err).to.exist;
|
||||||
|
expect(dao._account.busy).to.be.false;
|
||||||
|
expect(dao._account.folders[0].messages).to.be.empty;
|
||||||
|
expect(localListStub.calledOnce).to.be.true;
|
||||||
|
expect(imapListStub.calledOnce).to.be.true;
|
||||||
|
expect(imapGetStub.calledOnce).to.be.true;
|
||||||
|
expect(keychainStub.verifyPublicKey.calledOnce).to.be.true;
|
||||||
|
expect(markReadStub.called).to.be.false;
|
||||||
|
expect(imapDeleteStub.called).to.be.false;
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should not bother about read authentication mails', function(done) {
|
||||||
|
var invocations, folder, localListStub, imapListStub,
|
||||||
|
imapGetStub, markReadStub, imapDeleteStub;
|
||||||
|
|
||||||
|
invocations = 0;
|
||||||
|
folder = 'FOLDAAAA';
|
||||||
|
dao._account.folders = [{
|
||||||
|
type: 'Folder',
|
||||||
|
path: folder,
|
||||||
|
messages: []
|
||||||
|
}];
|
||||||
|
|
||||||
|
verificationMail.unread = false;
|
||||||
|
|
||||||
|
localListStub = sinon.stub(dao, '_localListMessages').yields(null, []);
|
||||||
|
imapListStub = sinon.stub(dao, '_imapListMessages').yields(null, [verificationMail]);
|
||||||
|
imapGetStub = sinon.stub(dao, '_imapGetMessage').yields(null, verificationMail);
|
||||||
|
markReadStub = sinon.stub(dao, 'markRead');
|
||||||
|
imapDeleteStub = sinon.stub(dao, '_imapDeleteMessage');
|
||||||
|
|
||||||
|
dao.sync({
|
||||||
|
folder: folder
|
||||||
|
}, function(err) {
|
||||||
|
expect(err).to.not.exist;
|
||||||
|
|
||||||
|
if (invocations === 0) {
|
||||||
|
expect(dao._account.busy).to.be.true;
|
||||||
|
invocations++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(dao._account.busy).to.be.false;
|
||||||
|
expect(dao._account.folders[0].messages).to.be.empty;
|
||||||
|
expect(localListStub.calledOnce).to.be.true;
|
||||||
|
expect(imapListStub.calledOnce).to.be.true;
|
||||||
|
expect(imapGetStub.calledOnce).to.be.true;
|
||||||
|
expect(keychainStub.verifyPublicKey.called).to.be.false;
|
||||||
|
expect(markReadStub.called).to.be.false;
|
||||||
|
expect(imapDeleteStub.called).to.be.false;
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should not bother about read authentication mails', function(done) {
|
||||||
|
var invocations, folder, localListStub, imapListStub,
|
||||||
|
imapGetStub, markReadStub, imapDeleteStub;
|
||||||
|
|
||||||
|
invocations = 0;
|
||||||
|
folder = 'FOLDAAAA';
|
||||||
|
dao._account.folders = [{
|
||||||
|
type: 'Folder',
|
||||||
|
path: folder,
|
||||||
|
messages: []
|
||||||
|
}];
|
||||||
|
|
||||||
|
verificationMail.body = 'url? there is no url.';
|
||||||
|
|
||||||
|
localListStub = sinon.stub(dao, '_localListMessages').yields(null, []);
|
||||||
|
imapListStub = sinon.stub(dao, '_imapListMessages').yields(null, [verificationMail]);
|
||||||
|
imapGetStub = sinon.stub(dao, '_imapGetMessage').yields(null, verificationMail);
|
||||||
|
markReadStub = sinon.stub(dao, 'markRead');
|
||||||
|
imapDeleteStub = sinon.stub(dao, '_imapDeleteMessage');
|
||||||
|
|
||||||
|
dao.sync({
|
||||||
|
folder: folder
|
||||||
|
}, function(err) {
|
||||||
|
expect(err).to.not.exist;
|
||||||
|
|
||||||
|
if (invocations === 0) {
|
||||||
|
expect(dao._account.busy).to.be.true;
|
||||||
|
invocations++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(dao._account.busy).to.be.false;
|
||||||
|
expect(dao._account.folders[0].messages).to.be.empty;
|
||||||
|
expect(localListStub.calledOnce).to.be.true;
|
||||||
|
expect(imapListStub.calledOnce).to.be.true;
|
||||||
|
expect(imapGetStub.calledOnce).to.be.true;
|
||||||
|
expect(keychainStub.verifyPublicKey.called).to.be.false;
|
||||||
|
expect(markReadStub.called).to.be.false;
|
||||||
|
expect(imapDeleteStub.called).to.be.false;
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('markAsRead', function() {
|
describe('markAsRead', function() {
|
||||||
|
Loading…
Reference in New Issue
Block a user