add silent public key verification

This commit is contained in:
Felix Hammerl 2013-12-03 13:26:29 +01:00
parent 0470ae00ea
commit 99a6cda40d
3 changed files with 346 additions and 4 deletions

View File

@ -38,7 +38,8 @@ define(function(require) {
},
checkOutboxInterval: 5000,
iconPath: '/img/icon.png',
verificationUrl: '/verify/'
verificationUrl: '/verify/',
verificationUuidLength: 36
};
/**

View File

@ -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);

View File

@ -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() {