mirror of
https://github.com/moparisthebest/mail
synced 2025-02-07 02:20:14 -05:00
refactored keychaindao
This commit is contained in:
parent
e7185a8baa
commit
5d409933e5
@ -2,264 +2,270 @@
|
||||
* A high-level Data-Access Api for handling Keypair synchronization
|
||||
* between the cloud service and the device's local storage
|
||||
*/
|
||||
app.dao.KeychainDAO = function(jsonDao, cloudstorage) {
|
||||
define(['underscore', 'js/dao/lawnchair-dao'], function(_, jsonDao) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Get an array of public keys by looking in local storage and
|
||||
* fetching missing keys from the cloud service.
|
||||
* @param ids [Array] the key ids as [{_id, userId}]
|
||||
* @return [PublicKeyCollection] The requiested public keys
|
||||
*/
|
||||
this.getPublicKeys = function(ids, callback) {
|
||||
var already, pubkeys = [];
|
||||
var KeychainDAO = function(cloudstorage) {
|
||||
var self = this;
|
||||
|
||||
var after = _.after(ids.length, function() {
|
||||
callback(null, pubkeys);
|
||||
});
|
||||
/**
|
||||
* Get an array of public keys by looking in local storage and
|
||||
* fetching missing keys from the cloud service.
|
||||
* @param ids [Array] the key ids as [{_id, userId}]
|
||||
* @return [PublicKeyCollection] The requiested public keys
|
||||
*/
|
||||
self.getPublicKeys = function(ids, callback) {
|
||||
var already, pubkeys = [];
|
||||
|
||||
_.each(ids, function(i) {
|
||||
// lookup locally and in storage
|
||||
lookupPublicKey(i._id, function(err, pubkey) {
|
||||
if (err || !pubkey) {
|
||||
callback({
|
||||
errMsg: 'Error looking up public key!',
|
||||
err: err
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// check if public key with that id has already been fetched
|
||||
already = null;
|
||||
already = _.findWhere(pubkeys, {
|
||||
_id: i._id
|
||||
});
|
||||
if (!already) {
|
||||
pubkeys.push(pubkey);
|
||||
}
|
||||
|
||||
after(); // asynchronously iterate through objects
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the local user's key either from local storage
|
||||
* or fetches it from the cloud. The private key is encrypted.
|
||||
* If no key pair exists, null is returned.
|
||||
* return [Object] The user's key pair {publicKey, privateKey}
|
||||
*/
|
||||
this.getUserKeyPair = function(userId, callback) {
|
||||
// search for user's public key locally
|
||||
jsonDao.list('publickey', 0, null, function(allPubkeys) {
|
||||
var pubkey = _.findWhere(allPubkeys, {
|
||||
userId: userId
|
||||
var after = _.after(ids.length, function() {
|
||||
callback(null, pubkeys);
|
||||
});
|
||||
|
||||
if (!pubkey || !pubkey._id) {
|
||||
// no public key by that user id in storage
|
||||
// find from cloud by email address
|
||||
cloudstorage.getPublicKeyByUserId(userId, function(err, cloudPubkey) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cloudPubkey && cloudPubkey._id) {
|
||||
// there is a public key for that user already in the cloud...
|
||||
// sync keypair to local storage
|
||||
syncKeypair(cloudPubkey._id);
|
||||
} else {
|
||||
// continue without keypair... generate in crypto.js
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
// that user's public key is already in local storage...
|
||||
// sync keypair to the cloud
|
||||
syncKeypair(pubkey._id);
|
||||
}
|
||||
});
|
||||
|
||||
function syncKeypair(keypairId) {
|
||||
// persist key pair in local storage
|
||||
lookupPublicKey(keypairId, function(err, savedPubkey) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// persist private key in local storage
|
||||
lookupPrivateKey(keypairId, function(err, savedPrivkey) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// validate fetched key
|
||||
if (savedPubkey && savedPubkey.publicKey && savedPrivkey && savedPrivkey.encryptedKey) {
|
||||
callback(null, {
|
||||
publicKey: savedPubkey,
|
||||
privateKey: savedPrivkey
|
||||
_.each(ids, function(i) {
|
||||
// lookup locally and in storage
|
||||
lookupPublicKey(i._id, function(err, pubkey) {
|
||||
if (err || !pubkey) {
|
||||
callback({
|
||||
errMsg: 'Error looking up public key!',
|
||||
err: err
|
||||
});
|
||||
return;
|
||||
|
||||
} else {
|
||||
// continue without keypair... generate in crypto.js
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
// check if public key with that id has already been fetched
|
||||
already = null;
|
||||
already = _.findWhere(pubkeys, {
|
||||
_id: i._id
|
||||
});
|
||||
if (!already) {
|
||||
pubkeys.push(pubkey);
|
||||
}
|
||||
|
||||
after(); // asynchronously iterate through objects
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks to see if the user's key pair is stored both
|
||||
* locally and in the cloud and persist arccordingly
|
||||
* @param [Object] The user's key pair {publicKey, privateKey}
|
||||
*/
|
||||
this.putUserKeyPair = function(keypair, callback) {
|
||||
// validate input
|
||||
if (!keypair || !keypair.publicKey || !keypair.privateKey || !keypair.publicKey.userId || keypair.publicKey.userId !== keypair.privateKey.userId) {
|
||||
callback({
|
||||
errMsg: 'Incorrect input!'
|
||||
/**
|
||||
* Gets the local user's key either from local storage
|
||||
* or fetches it from the cloud. The private key is encrypted.
|
||||
* If no key pair exists, null is returned.
|
||||
* return [Object] The user's key pair {publicKey, privateKey}
|
||||
*/
|
||||
self.getUserKeyPair = function(userId, callback) {
|
||||
// search for user's public key locally
|
||||
jsonDao.list('publickey', 0, null, function(allPubkeys) {
|
||||
var pubkey = _.findWhere(allPubkeys, {
|
||||
userId: userId
|
||||
});
|
||||
|
||||
if (!pubkey || !pubkey._id) {
|
||||
// no public key by that user id in storage
|
||||
// find from cloud by email address
|
||||
cloudstorage.getPublicKeyByUserId(userId, function(err, cloudPubkey) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cloudPubkey && cloudPubkey._id) {
|
||||
// there is a public key for that user already in the cloud...
|
||||
// sync keypair to local storage
|
||||
syncKeypair(cloudPubkey._id);
|
||||
} else {
|
||||
// continue without keypair... generate in crypto.js
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
// that user's public key is already in local storage...
|
||||
// sync keypair to the cloud
|
||||
syncKeypair(pubkey._id);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// store public key locally
|
||||
saveLocalPublicKey(keypair.publicKey, function(err) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
function syncKeypair(keypairId) {
|
||||
// persist key pair in local storage
|
||||
lookupPublicKey(keypairId, function(err, savedPubkey) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// persist private key in local storage
|
||||
lookupPrivateKey(keypairId, function(err, savedPrivkey) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// validate fetched key
|
||||
if (savedPubkey && savedPubkey.publicKey && savedPrivkey && savedPrivkey.encryptedKey) {
|
||||
callback(null, {
|
||||
publicKey: savedPubkey,
|
||||
privateKey: savedPrivkey
|
||||
});
|
||||
return;
|
||||
|
||||
} else {
|
||||
// continue without keypair... generate in crypto.js
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks to see if the user's key pair is stored both
|
||||
* locally and in the cloud and persist arccordingly
|
||||
* @param [Object] The user's key pair {publicKey, privateKey}
|
||||
*/
|
||||
self.putUserKeyPair = function(keypair, callback) {
|
||||
// validate input
|
||||
if (!keypair || !keypair.publicKey || !keypair.privateKey || !keypair.publicKey.userId || keypair.publicKey.userId !== keypair.privateKey.userId) {
|
||||
callback({
|
||||
errMsg: 'Incorrect input!'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// persist public key in cloud storage
|
||||
cloudstorage.putPublicKey(keypair.publicKey, function(err) {
|
||||
// validate result
|
||||
// store public key locally
|
||||
saveLocalPublicKey(keypair.publicKey, function(err) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// store private key locally
|
||||
saveLocalPrivateKey(keypair.privateKey, function(err) {
|
||||
// persist public key in cloud storage
|
||||
cloudstorage.putPublicKey(keypair.publicKey, function(err) {
|
||||
// validate result
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// persist private key in cloud storage
|
||||
cloudstorage.putPrivateKey(keypair.privateKey, function(err) {
|
||||
// validate result
|
||||
// store private key locally
|
||||
saveLocalPrivateKey(keypair.privateKey, function(err) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null);
|
||||
// persist private key in cloud storage
|
||||
cloudstorage.putPrivateKey(keypair.privateKey, function(err) {
|
||||
// validate result
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
//
|
||||
// Helper functions
|
||||
//
|
||||
|
||||
function lookupPublicKey(id, callback) {
|
||||
// lookup in local storage
|
||||
jsonDao.read('publickey_' + id, function(pubkey) {
|
||||
if (!pubkey) {
|
||||
// fetch from cloud storage
|
||||
cloudstorage.getPublicKey(id, function(err, cloudPubkey) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// cache public key in cache
|
||||
saveLocalPublicKey(cloudPubkey, function(err) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, cloudPubkey);
|
||||
});
|
||||
});
|
||||
|
||||
} else {
|
||||
callback(null, pubkey);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function lookupPrivateKey(id, callback) {
|
||||
// lookup in local storage
|
||||
jsonDao.read('privatekey_' + id, function(privkey) {
|
||||
if (!privkey) {
|
||||
// fetch from cloud storage
|
||||
cloudstorage.getPrivateKey(id, function(err, cloudPrivkey) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// cache private key in cache
|
||||
saveLocalPrivateKey(cloudPrivkey, function(err) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, cloudPrivkey);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
} else {
|
||||
callback(null, privkey);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function saveLocalPublicKey(pubkey, callback) {
|
||||
// persist public key (email, _id)
|
||||
var pkLookupKey = 'publickey_' + pubkey._id;
|
||||
|
||||
jsonDao.persist(pkLookupKey, pubkey, function(res1) {
|
||||
// validate result
|
||||
if (res1.key !== pkLookupKey) {
|
||||
callback({
|
||||
errMsg: 'Persisting public key in local storage went wrong!'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
function saveLocalPrivateKey(privkey, callback) {
|
||||
// persist private key (email, _id)
|
||||
var prkLookupKey = 'privatekey_' + privkey._id;
|
||||
|
||||
jsonDao.persist(prkLookupKey, privkey, function(res1) {
|
||||
// validate result
|
||||
if (res1.key !== prkLookupKey) {
|
||||
callback({
|
||||
errMsg: 'Persisting private key in local storage went wrong!'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
//
|
||||
// Helper functions
|
||||
//
|
||||
|
||||
function lookupPublicKey(id, callback) {
|
||||
// lookup in local storage
|
||||
jsonDao.read('publickey_' + id, function(pubkey) {
|
||||
if (!pubkey) {
|
||||
// fetch from cloud storage
|
||||
cloudstorage.getPublicKey(id, function(err, cloudPubkey) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// cache public key in cache
|
||||
saveLocalPublicKey(cloudPubkey, function(err) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, cloudPubkey);
|
||||
});
|
||||
});
|
||||
|
||||
} else {
|
||||
callback(null, pubkey);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function lookupPrivateKey(id, callback) {
|
||||
// lookup in local storage
|
||||
jsonDao.read('privatekey_' + id, function(privkey) {
|
||||
if (!privkey) {
|
||||
// fetch from cloud storage
|
||||
cloudstorage.getPrivateKey(id, function(err, cloudPrivkey) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// cache private key in cache
|
||||
saveLocalPrivateKey(cloudPrivkey, function(err) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
callback(null, cloudPrivkey);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
} else {
|
||||
callback(null, privkey);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function saveLocalPublicKey(pubkey, callback) {
|
||||
// persist public key (email, _id)
|
||||
var pkLookupKey = 'publickey_' + pubkey._id;
|
||||
|
||||
jsonDao.persist(pkLookupKey, pubkey, function(res1) {
|
||||
// validate result
|
||||
if (res1.key !== pkLookupKey) {
|
||||
callback({
|
||||
errMsg: 'Persisting public key in local storage went wrong!'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
function saveLocalPrivateKey(privkey, callback) {
|
||||
// persist private key (email, _id)
|
||||
var prkLookupKey = 'privatekey_' + privkey._id;
|
||||
|
||||
jsonDao.persist(prkLookupKey, privkey, function(res1) {
|
||||
// validate result
|
||||
if (res1.key !== prkLookupKey) {
|
||||
callback({
|
||||
errMsg: 'Persisting private key in local storage went wrong!'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
return KeychainDAO;
|
||||
});
|
@ -1,81 +1,84 @@
|
||||
module("Keychain DAO");
|
||||
define(['js/dao/keychain-dao', 'js/dao/lawnchair-dao'], function(KeychainDAO, jsonDao) {
|
||||
'use strict';
|
||||
|
||||
var keychaindao_test = {
|
||||
user: 'keychaindao_test@example.com',
|
||||
password: 'Password',
|
||||
keySize: 128,
|
||||
ivSize: 128,
|
||||
rsaKeySize: 512
|
||||
};
|
||||
module("Keychain DAO");
|
||||
|
||||
asyncTest("Init", 2, function() {
|
||||
// init dependencies
|
||||
var util = new cryptoLib.Util(window, uuid);
|
||||
var jsonDao = new app.dao.LawnchairDAO(Lawnchair);
|
||||
jsonDao.init(keychaindao_test.user);
|
||||
var crypto = new app.crypto.Crypto(window, util);
|
||||
// cloud storage stub
|
||||
var cloudstorageStub = {
|
||||
putPublicKey: function(pk, callback) {
|
||||
callback();
|
||||
},
|
||||
putPrivateKey: function(prk, callback) {
|
||||
callback();
|
||||
}
|
||||
var keychaindaoTest = {
|
||||
user: 'keychaindao_test@example.com',
|
||||
password: 'Password',
|
||||
keySize: 128,
|
||||
ivSize: 128,
|
||||
rsaKeySize: 512
|
||||
};
|
||||
|
||||
keychaindao_test.keychainDao = new app.dao.KeychainDAO(jsonDao, cloudstorageStub);
|
||||
ok(keychaindao_test.keychainDao);
|
||||
asyncTest("Init", 2, function() {
|
||||
|
||||
// clear db before test
|
||||
jsonDao.clear(function() {
|
||||
ok(true, 'cleared db');
|
||||
// stubbing
|
||||
var cloudstorageStub = {
|
||||
putPublicKey: function(pk, callback) {
|
||||
callback();
|
||||
},
|
||||
putPrivateKey: function(prk, callback) {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
start();
|
||||
// module instancing
|
||||
keychaindaoTest.keychainDao = new KeychainDAO(cloudstorageStub);
|
||||
ok(keychaindaoTest.keychainDao);
|
||||
|
||||
// init and clear db before test
|
||||
jsonDao.init(keychaindaoTest.user);
|
||||
jsonDao.clear(function() {
|
||||
ok(true, 'cleared db');
|
||||
|
||||
start();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
asyncTest("Put User Keypair", 1, function() {
|
||||
asyncTest("Put User Keypair", 1, function() {
|
||||
|
||||
keychaindao_test.keypair = {
|
||||
publicKey: {
|
||||
_id: '1',
|
||||
userId: keychaindao_test.user,
|
||||
publicKey: 'asdf'
|
||||
},
|
||||
privateKey: {
|
||||
_id: '1',
|
||||
userId: keychaindao_test.user,
|
||||
encryptedKey: 'qwer',
|
||||
iv: 'yxvc'
|
||||
}
|
||||
};
|
||||
keychaindaoTest.keypair = {
|
||||
publicKey: {
|
||||
_id: '123',
|
||||
userId: keychaindaoTest.user,
|
||||
publicKey: 'asdf'
|
||||
},
|
||||
privateKey: {
|
||||
_id: '123',
|
||||
userId: keychaindaoTest.user,
|
||||
encryptedKey: 'qwer',
|
||||
iv: 'yxvc'
|
||||
}
|
||||
};
|
||||
|
||||
keychaindao_test.keychainDao.putUserKeyPair(keychaindao_test.keypair, function(err) {
|
||||
ok(!err);
|
||||
keychaindaoTest.keychainDao.putUserKeyPair(keychaindaoTest.keypair, function(err) {
|
||||
ok(!err);
|
||||
|
||||
start();
|
||||
start();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
asyncTest("Get User Keypair", 2, function() {
|
||||
keychaindao_test.keychainDao.getUserKeyPair(keychaindao_test.user, function(err, keypair) {
|
||||
ok(!err);
|
||||
ok(keypair && keypair.publicKey && keypair.privateKey);
|
||||
asyncTest("Get User Keypair", 2, function() {
|
||||
keychaindaoTest.keychainDao.getUserKeyPair(keychaindaoTest.user, function(err, keypair) {
|
||||
ok(!err);
|
||||
ok(keypair && keypair.publicKey && keypair.privateKey);
|
||||
|
||||
start();
|
||||
start();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
asyncTest("Get Public Keys", 2, function() {
|
||||
var pubkeyIds = [{
|
||||
_id: keychaindao_test.keypair.publicKey._id
|
||||
}
|
||||
];
|
||||
keychaindao_test.keychainDao.getPublicKeys(pubkeyIds, function(err, pubkeys) {
|
||||
ok(!err);
|
||||
deepEqual(pubkeys[0], keychaindao_test.keypair.publicKey, "Fetch public key");
|
||||
asyncTest("Get Public Keys", 2, function() {
|
||||
var pubkeyIds = [{
|
||||
_id: keychaindaoTest.keypair.publicKey._id
|
||||
}
|
||||
];
|
||||
keychaindaoTest.keychainDao.getPublicKeys(pubkeyIds, function(err, pubkeys) {
|
||||
ok(!err);
|
||||
deepEqual(pubkeys[0], keychaindaoTest.keypair.publicKey, "Fetch public key");
|
||||
|
||||
start();
|
||||
start();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
@ -24,9 +24,10 @@ function startTests() {
|
||||
'test/unit/forge-test',
|
||||
'test/unit/aes-test',
|
||||
'test/unit/rsa-test',
|
||||
'test/unit/lawnchair-dao-test'
|
||||
'test/unit/lawnchair-dao-test',
|
||||
'test/unit/keychain-dao-test'
|
||||
], function() {
|
||||
|
||||
QUnit.start(); //Tests loaded, run tests
|
||||
//Tests loaded, run tests
|
||||
QUnit.start();
|
||||
});
|
||||
}
|
Loading…
Reference in New Issue
Block a user