mirror of
https://github.com/moparisthebest/mail
synced 2024-12-22 15:28:49 -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,
|
||||
iconPath: '/img/icon.png',
|
||||
verificationUrl: '/verify/'
|
||||
verificationUrl: '/verify/',
|
||||
verificationUuidLength: 36
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -3,8 +3,8 @@ define(function(require) {
|
||||
|
||||
var util = require('cryptoLib/util'),
|
||||
_ = require('underscore'),
|
||||
str = require('js/app-config').string;
|
||||
// config = require('js/app-config').config;
|
||||
str = require('js/app-config').string,
|
||||
config = require('js/app-config').config;
|
||||
|
||||
var EmailDAO = function(keychain, imapClient, smtpClient, crypto, devicestorage) {
|
||||
var self = this;
|
||||
@ -375,6 +375,19 @@ define(function(require) {
|
||||
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
|
||||
self._localStoreMessages({
|
||||
folder: folder.path,
|
||||
@ -425,6 +438,54 @@ define(function(require) {
|
||||
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) {
|
||||
if (containsArmoredCiphertext(message)) {
|
||||
decrypt(message, localCallback);
|
||||
|
@ -14,7 +14,7 @@ define(function(require) {
|
||||
var dao, keychainStub, imapClientStub, smtpClientStub, pgpStub, devicestorageStub;
|
||||
|
||||
var emailAddress, passphrase, asymKeySize, mockkeyId, dummyEncryptedMail,
|
||||
dummyDecryptedMail, mockKeyPair, account, publicKey;
|
||||
dummyDecryptedMail, mockKeyPair, account, publicKey, verificationMail, verificationUuid;
|
||||
|
||||
beforeEach(function() {
|
||||
emailAddress = 'asdf@asdf.com';
|
||||
@ -32,6 +32,19 @@ define(function(require) {
|
||||
subject: '[whiteout] qweasd',
|
||||
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 = {
|
||||
uid: 1234,
|
||||
from: [{
|
||||
@ -1057,6 +1070,7 @@ define(function(require) {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should error while storing messages from the remote locally', function(done) {
|
||||
var invocations, folder, localListStub, imapListStub, imapGetStub, localStoreStub;
|
||||
|
||||
@ -1096,6 +1110,7 @@ define(function(require) {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should error while fetching messages from the remote', function(done) {
|
||||
var invocations, folder, localListStub, imapListStub, imapGetStub, localStoreStub;
|
||||
|
||||
@ -1135,6 +1150,271 @@ define(function(require) {
|
||||
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() {
|
||||
|
Loading…
Reference in New Issue
Block a user