diff --git a/src/js/crypto/crypto-batch-worker.js b/src/js/crypto/crypto-batch-worker.js index 914c154..b30460b 100644 --- a/src/js/crypto/crypto-batch-worker.js +++ b/src/js/crypto/crypto-batch-worker.js @@ -2,6 +2,7 @@ 'use strict'; // import web worker dependencies + importScripts('../../lib/underscore-1.4.4.min.js'); importScripts('../../lib/forge/forge.rsa.bundle.js'); importScripts('../app-config.js'); importScripts('./crypto-batch.js'); @@ -20,18 +21,15 @@ aes = new cryptoLib.AesCBC(forge), rsa = new cryptoLib.RSA(forge), util = new cryptoLib.Util(), - batch = new cryptoLib.CryptoBatch(aes, rsa, util); + batch = new cryptoLib.CryptoBatch(aes, rsa, util, _); - // pass RSA keys to module - rsa.init(i.pubkeyPem, i.privkeyPem); - - if (i.type === 'encrypt' && i.list) { + if (i.type === 'encrypt' && i.receiverPubkeys && i.senderPrivkey && i.list) { // start encryption - output = batch.encryptListForUser(i.list); + output = batch.encryptListForUser(i.list, i.receiverPubkeys, i.senderPrivkey); - } else if (i.type === 'decrypt' && i.list) { + } else if (i.type === 'decrypt' && i.senderPubkeys && i.receiverPrivkey && i.list) { // start decryption - output = batch.decryptListForUser(i.list); + output = batch.decryptListForUser(i.list, i.senderPubkeys, i.receiverPrivkey); } else { throw 'Not all arguments for web worker crypto are defined!'; diff --git a/src/js/crypto/crypto-batch.js b/src/js/crypto/crypto-batch.js index 6a38e95..a8622f3 100644 --- a/src/js/crypto/crypto-batch.js +++ b/src/js/crypto/crypto-batch.js @@ -4,11 +4,11 @@ /** * Crypto batch library for processing large sets of data */ - var CryptoBatch = function(aes, rsa, util) { + var CryptoBatch = function(aes, rsa, util, _) { /** * Encrypt a list of items using AES - * @list list [Array] The list of items to encrypt + * @param list [Array] The list of items to encrypt */ this.encryptList = function(list) { list.forEach(function(i) { @@ -22,7 +22,7 @@ /** * Decrypt a list of items using AES - * @list list [Array] The list of items to decrypt + * @param list [Array] The list of items to decrypt */ this.decryptList = function(list) { list.forEach(function(i) { @@ -36,19 +36,33 @@ /** * Encrypt and sign a list of items using AES and RSA - * @list list [Array] The list of items to encrypt + * @param list [Array] The list of items to encrypt + * @param receiverPubkeys [Array] A list of public keys used to encrypt + * @param senderPrivkey [Array] The sender's private key used to sign */ - this.encryptListForUser = function(list) { - // encrypt list + this.encryptListForUser = function(list, receiverPubkeys, senderPrivkey) { + // encrypt list with aes var encryptedList = this.encryptList(list); + // set sender private key + rsa.init(null, senderPrivkey.privateKey); + // encrypt keys for user encryptedList.forEach(function(i) { + // fetch correct public key + var pk = _.findWhere(receiverPubkeys, { + _id: i.receiverPk + }); + // set rsa public key used to encrypt + rsa.init(pk.publicKey); + // process new values i.encryptedKey = rsa.encrypt(i.key); i.signature = rsa.sign([i.iv, util.str2Base64(i.id), i.encryptedKey, i.ciphertext]); + i.senderPk = senderPrivkey._id; // delete old ones delete i.key; + delete i.receiverPk; }); return encryptedList; @@ -56,22 +70,35 @@ /** * Decrypt and verify a list of items using AES and RSA - * @list list [Array] The list of items to decrypt + * @param list [Array] The list of items to decrypt + * @param senderPubkeys [Array] A list of public keys used to verify + * @param receiverPrivkey [Array] The receiver's private key used to decrypt */ - this.decryptListForUser = function(encryptedList) { + this.decryptListForUser = function(encryptedList, senderPubkeys, receiverPrivkey) { var j, self = this; + // set receiver private key + rsa.init(null, receiverPrivkey.privateKey); + // decrypt keys for user encryptedList.forEach(function(i) { + // fetch correct public key + var pk = _.findWhere(senderPubkeys, { + _id: i.senderPk + }); + // set rsa public key used to verify + rsa.init(pk.publicKey); + // verify signature if (!rsa.verify([i.iv, util.str2Base64(i.id), i.encryptedKey, i.ciphertext], i.signature)) { throw new Error('Verifying RSA signature failed!'); } - // precoess new values + // process new values i.key = rsa.decrypt(i.encryptedKey); // delete old values delete i.signature; delete i.encryptedKey; + delete i.senderPk; }); // decrypt list diff --git a/src/js/crypto/crypto.js b/src/js/crypto/crypto.js index f59e4de..d0b738f 100644 --- a/src/js/crypto/crypto.js +++ b/src/js/crypto/crypto.js @@ -96,6 +96,8 @@ app.crypto.Crypto = function(window, util) { } }; + // TODO: not required since key is synced before crypto init in keychain dao getUserKeyPair + /** * Return a Public Key object containing the Public Key PEM */ @@ -109,6 +111,8 @@ app.crypto.Crypto = function(window, util) { }; }; + // TODO: not required since key is synced before crypto init in keychain dao getUserKeyPair + /** * Return a Private Key object containing the encrypted private key */ @@ -123,6 +127,8 @@ app.crypto.Crypto = function(window, util) { 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'; @@ -261,25 +267,37 @@ app.crypto.Crypto = function(window, util) { // En/Decrypt something speficially using the user's secret key // - this.encryptListForUser = function(list, recipientPubkey, callback) { + this.encryptListForUser = function(list, receiverPubkeys, callback) { var envelope, envelopes = [], self = this; + if (!receiverPubkeys || receiverPubkeys.length !== 1) { + callback({ + errMsg: 'Encryption is currently implemented for only one receiver!' + }); + return; + } + + var keypair = rsa.exportKeys(); + var senderPrivkey = { + _id: keypair._id, + privateKey: keypair.privkeyPem + }; + // package objects into batchable envelope format list.forEach(function(i) { envelope = { id: i.id, plaintext: i, key: util.random(self.keySize), - iv: util.random(self.ivSize) + iv: util.random(self.ivSize), + receiverPk: receiverPubkeys[0]._id }; envelopes.push(envelope); }); if (window.Worker) { - var keypair = rsa.exportKeys(); - var worker = new Worker(app.config.workerPath + '/crypto/crypto-batch-worker.js'); worker.onmessage = function(e) { callback(null, e.data); @@ -287,21 +305,32 @@ app.crypto.Crypto = function(window, util) { worker.postMessage({ type: 'encrypt', list: envelopes, - pubkeyPem: keypair.pubkeyPem, - privkeyPem: keypair.privkeyPem + senderPrivkey: senderPrivkey, + receiverPubkeys: receiverPubkeys }); } else { - var batch = new cryptoLib.CryptoBatch(aes, rsa, util); - var encryptedList = batch.encryptListForUser(envelopes); + var batch = new cryptoLib.CryptoBatch(aes, rsa, util, _); + var encryptedList = batch.encryptListForUser(envelopes, receiverPubkeys, senderPrivkey); callback(null, encryptedList); } }; - this.decryptListForUser = function(list, recipientPubkey, callback) { - if (window.Worker) { + this.decryptListForUser = function(list, senderPubkeys, callback) { + if (!senderPubkeys || senderPubkeys < 1) { + callback({ + errMsg: 'Sender public keys must be set!' + }); + return; + } - var keypair = rsa.exportKeys(); + var keypair = rsa.exportKeys(); + var receiverPrivkey = { + _id: keypair._id, + privateKey: keypair.privkeyPem + }; + + if (window.Worker) { var worker = new Worker(app.config.workerPath + '/crypto/crypto-batch-worker.js'); worker.onmessage = function(e) { @@ -310,13 +339,13 @@ app.crypto.Crypto = function(window, util) { worker.postMessage({ type: 'decrypt', list: list, - pubkeyPem: keypair.pubkeyPem, - privkeyPem: keypair.privkeyPem + receiverPrivkey: receiverPrivkey, + senderPubkeys: senderPubkeys }); } else { - var batch = new cryptoLib.CryptoBatch(aes, rsa, util); - var decryptedList = batch.decryptListForUser(list); + var batch = new cryptoLib.CryptoBatch(aes, rsa, util, _); + var decryptedList = batch.decryptListForUser(list, senderPubkeys, receiverPrivkey); callback(null, decryptedList); } }; diff --git a/test/unit/crypto-test.js b/test/unit/crypto-test.js index dc8cfcb..fa70a3a 100644 --- a/test/unit/crypto-test.js +++ b/test/unit/crypto-test.js @@ -98,7 +98,9 @@ asyncTest("AES/RSA encrypt batch for User (Async/Worker)", 2, function() { collection = td.getEmailCollection(10); crypto_test.list = collection.toJSON(); - crypto_test.crypto.encryptListForUser(crypto_test.list, null, function(err, encryptedList) { + var receiverPubkeys = [crypto_test.crypto.getPublicKey()]; + + crypto_test.crypto.encryptListForUser(crypto_test.list, receiverPubkeys, function(err, encryptedList) { ok(!err && encryptedList, 'Encrypt list for user'); equal(encryptedList.length, crypto_test.list.length, 'Length of list'); crypto_test.encryptedList = encryptedList; @@ -108,7 +110,10 @@ asyncTest("AES/RSA encrypt batch for User (Async/Worker)", 2, function() { }); asyncTest("AES/RSA decrypt batch for User (Async/Worker)", 3, function() { - crypto_test.crypto.decryptListForUser(crypto_test.encryptedList, null, function(err, decryptedList) { + + var senderPubkeys = [crypto_test.crypto.getPublicKey()]; + + crypto_test.crypto.decryptListForUser(crypto_test.encryptedList, senderPubkeys, function(err, decryptedList) { ok(!err && decryptedList, 'Decrypt list'); equal(decryptedList.length, crypto_test.list.length, 'Length of list'); deepEqual(decryptedList, crypto_test.list, 'Decrypted list is correct');