mirror of
https://github.com/moparisthebest/mail
synced 2025-02-16 15:10:10 -05:00
refactored crypto.js tp use keypair input
This commit is contained in:
parent
6a33f17f42
commit
09d4c1c56f
@ -7,9 +7,6 @@ app.crypto.Crypto = function(window, util) {
|
||||
|
||||
var aes = new cryptoLib.AesCBC(forge); // use AES-CBC mode by default
|
||||
var rsa = new cryptoLib.RSA(forge, util); // use RSA for asym. crypto
|
||||
var keyStore = new app.dao.LocalStorageDAO(window);
|
||||
|
||||
var storageId; // storage id for the encrypted keypair in local storage
|
||||
|
||||
/**
|
||||
* Initializes the crypto modules by fetching the user's
|
||||
@ -26,35 +23,28 @@ app.crypto.Crypto = function(window, util) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.emailAddress = args.emailAddress;
|
||||
this.keySize = args.keySize;
|
||||
this.ivSize = args.keySize;
|
||||
this.rsaKeySize = args.rsaKeySize;
|
||||
|
||||
storageId = self.emailAddress + '_encryptedKeypair';
|
||||
self.emailAddress = args.emailAddress;
|
||||
self.keySize = args.keySize;
|
||||
self.ivSize = args.keySize;
|
||||
self.rsaKeySize = args.rsaKeySize;
|
||||
|
||||
// derive PBKDF2 from password in web worker thread
|
||||
this.deriveKey(args.password, self.keySize, function(pbkdf2) {
|
||||
|
||||
// TODO: rm keystore logix and check args.storedKeypair
|
||||
|
||||
// fetch user's encrypted secret key from keychain/storage
|
||||
var storedKeypair = keyStore.read(storageId);
|
||||
self.deriveKey(args.password, self.keySize, function(pbkdf2) {
|
||||
|
||||
// check if key exists
|
||||
if (!storedKeypair) {
|
||||
if (!args.storedKeypair) {
|
||||
// generate keys, encrypt and persist if none exists
|
||||
generateKeypair(pbkdf2);
|
||||
} else {
|
||||
// decrypt key
|
||||
decryptKeypair(storedKeypair, pbkdf2);
|
||||
decryptKeypair(args.storedKeypair, pbkdf2);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
function generateKeypair(pbkdf2) {
|
||||
// generate RSA keypair in web worker
|
||||
rsa.generateKeypair(self.rsaKeySize, function(err, keypair) {
|
||||
rsa.generateKeypair(self.rsaKeySize, function(err, generatedKeypair) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
@ -62,28 +52,43 @@ app.crypto.Crypto = function(window, util) {
|
||||
|
||||
// encrypt keypair
|
||||
var iv = util.random(self.ivSize);
|
||||
var encryptedKeys = aes.encrypt(JSON.stringify(keypair), pbkdf2, iv);
|
||||
var encryptedPrivateKey = aes.encrypt(generatedKeypair.privkeyPem, pbkdf2, iv);
|
||||
|
||||
// store encrypted keypair
|
||||
var newStoredKeypair = {
|
||||
_id: keypair._id,
|
||||
userId: self.emailAddress,
|
||||
encryptedKey: encryptedKeys,
|
||||
iv: iv
|
||||
// new encrypted keypair object
|
||||
var newKeypair = {
|
||||
publicKey: {
|
||||
_id: generatedKeypair._id,
|
||||
userId: self.emailAddress,
|
||||
publicKey: generatedKeypair.pubkeyPem
|
||||
},
|
||||
privateKey: {
|
||||
_id: generatedKeypair._id,
|
||||
userId: self.emailAddress,
|
||||
encryptedKey: encryptedPrivateKey,
|
||||
iv: iv
|
||||
}
|
||||
};
|
||||
keyStore.persist(storageId, newStoredKeypair);
|
||||
|
||||
// TODO: return generated keypair for storage in keychain dao
|
||||
callback();
|
||||
// return generated keypair for storage in keychain dao
|
||||
callback(null, newKeypair);
|
||||
});
|
||||
}
|
||||
|
||||
function decryptKeypair(storedKeypair, pbkdf2) {
|
||||
var keypairJson, keypair;
|
||||
var decryptedPrivateKey;
|
||||
|
||||
// validate input
|
||||
if (!storedKeypair || !storedKeypair.privateKey || !storedKeypair.privateKey.encryptedKey || !storedKeypair.privateKey.iv) {
|
||||
callback({
|
||||
errMsg: 'Incomplete arguments for private key decryption!'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// try to decrypt with pbkdf2
|
||||
try {
|
||||
keypairJson = aes.decrypt(storedKeypair.encryptedKey, pbkdf2, storedKeypair.iv);
|
||||
keypair = JSON.parse(keypairJson);
|
||||
var prK = storedKeypair.privateKey;
|
||||
decryptedPrivateKey = aes.decrypt(prK.encryptedKey, pbkdf2, prK.iv);
|
||||
} catch (ex) {
|
||||
callback({
|
||||
errMsg: 'Wrong password!'
|
||||
@ -91,56 +96,12 @@ app.crypto.Crypto = function(window, util) {
|
||||
return;
|
||||
}
|
||||
// set rsa keys
|
||||
rsa.init(keypair.pubkeyPem, keypair.privkeyPem, keypair._id);
|
||||
rsa.init(storedKeypair.publicKey.publicKey, decryptedPrivateKey, storedKeypair.publicKey._id);
|
||||
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: not required since key is synced before crypto init in keychain dao getUserKeyPair
|
||||
|
||||
/**
|
||||
* Return a Public Key object containing the Public Key PEM
|
||||
*/
|
||||
this.getPublicKey = function() {
|
||||
var keypair = rsa.exportKeys();
|
||||
|
||||
return {
|
||||
_id: keypair._id,
|
||||
userId: this.emailAddress,
|
||||
publicKey: keypair.pubkeyPem
|
||||
};
|
||||
};
|
||||
|
||||
// TODO: not required since key is synced before crypto init in keychain dao getUserKeyPair
|
||||
|
||||
/**
|
||||
* Return a Private Key object containing the encrypted private key
|
||||
*/
|
||||
this.getEncryptedPrivateKey = function(emailAddress) {
|
||||
if (!emailAddress && !storageId) {
|
||||
throw new Error('Emailaddress needs to be set or crypto needs to be initiated!');
|
||||
}
|
||||
|
||||
var strgId = (storageId) ? storageId : emailAddress + '_encryptedKeypair';
|
||||
var storedKeypair = keyStore.read(strgId);
|
||||
|
||||
return storedKeypair;
|
||||
};
|
||||
|
||||
// TODO: not required since key is synced before crypto init in keychain dao getUserKeyPair
|
||||
|
||||
this.putEncryptedPrivateKey = function(privkey) {
|
||||
var strgId = (storageId) ? storageId : privkey.userId + '_encryptedKeypair';
|
||||
|
||||
// validate private key object
|
||||
if (!strgId || !privkey || !privkey._id || !privkey.userId || !privkey.encryptedKey || !privkey.iv) {
|
||||
throw new Error('Invalid encrypted private key object... will not store!');
|
||||
}
|
||||
|
||||
return keyStore.persist(strgId, privkey);
|
||||
};
|
||||
|
||||
/**
|
||||
* Do PBKDF2 key derivation in a WebWorker thread
|
||||
*/
|
||||
|
@ -8,41 +8,42 @@ var crypto_test = {
|
||||
rsaKeySize: 1024
|
||||
};
|
||||
|
||||
asyncTest("Init", 2, function() {
|
||||
asyncTest("Init without keypair", 4, function() {
|
||||
// init dependencies
|
||||
crypto_test.util = new cryptoLib.Util(window, uuid);
|
||||
crypto_test.crypto = new app.crypto.Crypto(window, crypto_test.util);
|
||||
ok(crypto_test.crypto, 'Crypto');
|
||||
|
||||
// test without passing keys
|
||||
crypto_test.crypto.init({
|
||||
emailAddress: crypto_test.user,
|
||||
password: crypto_test.password,
|
||||
keySize: crypto_test.keySize,
|
||||
rsaKeySize: crypto_test.rsaKeySize
|
||||
}, function(err) {
|
||||
ok(!err, 'Init crypto');
|
||||
}, function(err, generatedKeypair) {
|
||||
ok(!err && generatedKeypair, 'Init crypto without keypair input');
|
||||
var pk = generatedKeypair.publicKey;
|
||||
ok(pk._id && pk.userId, 'Key ID: ' + pk._id);
|
||||
ok(pk.publicKey.indexOf('-----BEGIN PUBLIC KEY-----') === 0, pk.publicKey);
|
||||
crypto_test.generatedKeypair = generatedKeypair;
|
||||
|
||||
start();
|
||||
});
|
||||
});
|
||||
|
||||
test("Get Public Key PEM", 2, function() {
|
||||
var pk = crypto_test.crypto.getPublicKey();
|
||||
ok(pk._id && pk.userId, 'Key ID: ' + pk._id);
|
||||
ok(pk.publicKey.indexOf('-----BEGIN PUBLIC KEY-----') === 0, pk.publicKey);
|
||||
});
|
||||
asyncTest("Init with keypair", 1, function() {
|
||||
// test with passing keypair
|
||||
crypto_test.crypto.init({
|
||||
emailAddress: crypto_test.user,
|
||||
password: crypto_test.password,
|
||||
keySize: crypto_test.keySize,
|
||||
rsaKeySize: crypto_test.rsaKeySize,
|
||||
storedKeypair: crypto_test.generatedKeypair
|
||||
}, function(err, generatedKeypair) {
|
||||
ok(!err, 'Init crypto with keypair input');
|
||||
|
||||
test("Get Encrypted Private Key", 2, function() {
|
||||
var prk = crypto_test.crypto.getEncryptedPrivateKey();
|
||||
ok(prk._id && prk.userId, 'Key ID: ' + prk._id);
|
||||
ok(prk.encryptedKey, prk.encryptedKey);
|
||||
|
||||
crypto_test.prk = prk;
|
||||
});
|
||||
|
||||
test("Put Encrypted Private Key", 1, function() {
|
||||
crypto_test.crypto.putEncryptedPrivateKey(crypto_test.prk);
|
||||
ok(true);
|
||||
start();
|
||||
});
|
||||
});
|
||||
|
||||
asyncTest("PBKDF2 (Async/Worker)", 1, function() {
|
||||
@ -98,7 +99,7 @@ asyncTest("AES/RSA encrypt batch for User (Async/Worker)", 2, function() {
|
||||
collection = td.getEmailCollection(10);
|
||||
crypto_test.list = collection.toJSON();
|
||||
|
||||
var receiverPubkeys = [crypto_test.crypto.getPublicKey()];
|
||||
var receiverPubkeys = [crypto_test.generatedKeypair.publicKey];
|
||||
|
||||
crypto_test.crypto.encryptListForUser(crypto_test.list, receiverPubkeys, function(err, encryptedList) {
|
||||
ok(!err && encryptedList, 'Encrypt list for user');
|
||||
@ -111,7 +112,7 @@ asyncTest("AES/RSA encrypt batch for User (Async/Worker)", 2, function() {
|
||||
|
||||
asyncTest("AES/RSA decrypt batch for User (Async/Worker)", 3, function() {
|
||||
|
||||
var senderPubkeys = [crypto_test.crypto.getPublicKey()];
|
||||
var senderPubkeys = [crypto_test.generatedKeypair.publicKey];
|
||||
|
||||
crypto_test.crypto.decryptListForUser(crypto_test.encryptedList, senderPubkeys, function(err, decryptedList) {
|
||||
ok(!err && decryptedList, 'Decrypt list');
|
||||
|
@ -25,8 +25,9 @@ asyncTest("Init", 3, function() {
|
||||
password: devicestorage_test.password,
|
||||
keySize: devicestorage_test.keySize,
|
||||
rsaKeySize: devicestorage_test.rsaKeySize
|
||||
}, function(err) {
|
||||
ok(!err, 'Init crypto');
|
||||
}, function(err, generatedKeypair) {
|
||||
ok(!err && generatedKeypair, 'Init crypto');
|
||||
devicestorage_test.generatedKeypair = generatedKeypair;
|
||||
|
||||
// clear db before tests
|
||||
devicestorage_test.jsonDao.clear(function(err) {
|
||||
@ -39,7 +40,7 @@ asyncTest("Init", 3, function() {
|
||||
});
|
||||
|
||||
asyncTest("Encrypt list for user", 2, function() {
|
||||
var receiverPubkeys = [devicestorage_test.crypto.getPublicKey()];
|
||||
var receiverPubkeys = [devicestorage_test.generatedKeypair.publicKey];
|
||||
|
||||
devicestorage_test.crypto.encryptListForUser(devicestorage_test.list, receiverPubkeys, function(err, encryptedList) {
|
||||
ok(!err);
|
||||
@ -60,7 +61,7 @@ asyncTest("Store encrypted list", 1, function() {
|
||||
|
||||
asyncTest("List items", 3, function() {
|
||||
|
||||
var senderPubkeys = [devicestorage_test.crypto.getPublicKey()];
|
||||
var senderPubkeys = [devicestorage_test.generatedKeypair.publicKey];
|
||||
|
||||
var offset = 2,
|
||||
num = 6;
|
||||
|
@ -57,11 +57,11 @@
|
||||
<script src="forge-test.js"></script>
|
||||
<script src="aes-test.js"></script>
|
||||
<script src="rsa-test.js"></script>
|
||||
<script src="crypto-test.js"></script>
|
||||
<script src="localstorage-dao-test.js"></script>
|
||||
<script src="lawnchair-dao-test.js"></script>
|
||||
<script src="devicestorage-test.js"></script>
|
||||
<script src="keychain-dao-test.js"></script>
|
||||
<script src="crypto-test.js"></script>
|
||||
<script src="devicestorage-test.js"></script>
|
||||
<script src="email-dao-test.js"></script>
|
||||
|
||||
</body>
|
||||
|
Loading…
Reference in New Issue
Block a user