From fcf64569b27c86729257ce299e1dc2e06d6cfae5 Mon Sep 17 00:00:00 2001 From: Tankred Hase Date: Wed, 15 May 2013 12:26:32 +0200 Subject: [PATCH] refactored crypto for user to use RSA encrypt/sign --- src/js/crypto/crypto.js | 46 ++++++++++++++---------------- src/js/crypto/rsa.js | 6 ++-- src/js/crypto/util.js | 12 +++----- test/unit/crypto-test.js | 61 +++++++++++++--------------------------- 4 files changed, 49 insertions(+), 76 deletions(-) diff --git a/src/js/crypto/crypto.js b/src/js/crypto/crypto.js index b13c2fd..adb3398 100644 --- a/src/js/crypto/crypto.js +++ b/src/js/crypto/crypto.js @@ -66,15 +66,19 @@ app.crypto.Crypto = function(window, util) { } function decryptKeypair(storedKeypair, pbkdf2) { + var keypairJson, keypair; + // try to decrypt with pbkdf2 try { - var keypairJson = aes.decrypt(storedKeypair.encryptedKeys, pbkdf2, storedKeypair.keyIV); - var keypair = JSON.parse(keypairJson); + keypairJson = aes.decrypt(storedKeypair.encryptedKeys, pbkdf2, storedKeypair.keyIV); + keypair = JSON.parse(keypairJson); } catch (ex) { callback({ errMsg: 'Wrong password!' }); return; } + // set rsa keys + rsa.init(keypair.pubkeyPem, keypair.privkeyPem, keypair._id); callback(); } @@ -205,22 +209,7 @@ app.crypto.Crypto = function(window, util) { // En/Decrypt something speficially using the user's secret key // - this.aesEncryptForUser = function(plaintext, iv, callback) { - var ciphertext = aes.encrypt(plaintext, symmetricUserKey, iv); - callback(ciphertext); - }; - this.aesDecryptForUser = function(ciphertext, iv, callback) { - var decrypted = aes.decrypt(ciphertext, symmetricUserKey, iv); - callback(decrypted); - }; - this.aesEncryptForUserSync = function(plaintext, iv) { - return aes.encrypt(plaintext, symmetricUserKey, iv); - }; - this.aesDecryptForUserSync = function(ciphertext, iv) { - return aes.decrypt(ciphertext, symmetricUserKey, iv); - }; - - this.aesEncryptListForUser = function(list, callback) { + this.encryptListForUser = function(list, recipientPubkey, callback) { var envelope, envelopes = [], self = this; @@ -242,26 +231,33 @@ app.crypto.Crypto = function(window, util) { encryptedList.forEach(function(i) { // process new values i.itemIV = i.iv; - i.keyIV = util.random(self.ivSize); - i.encryptedKey = self.aesEncryptForUserSync(i.key, i.keyIV); + i.encryptedKey = rsa.encrypt(i.key); + i.keyIV = rsa.sign([i.itemIV, i.encryptedKey, i.ciphertext]); // delete old ones delete i.iv; delete i.key; }); - callback(encryptedList); + callback(null, encryptedList); }); }; - this.aesDecryptListForUser = function(encryptedList, callback) { + this.decryptListForUser = function(encryptedList, recipientPubkey, callback) { var list = [], self = this; // decrypt keys for user encryptedList.forEach(function(i) { - // decrypt item key - i.key = self.aesDecryptForUserSync(i.encryptedKey, i.keyIV); + // verify signature + if (!rsa.verify([i.itemIV, i.encryptedKey, i.ciphertext], i.keyIV)) { + callback({ + errMsg: 'Verifying RSA signature failed!' + }); + return; + } + // precoess new values i.iv = i.itemIV; + i.key = rsa.decrypt(i.encryptedKey); // delete old values delete i.keyIV; delete i.itemIV; @@ -275,7 +271,7 @@ app.crypto.Crypto = function(window, util) { list.push(i.plaintext); }); - callback(list); + callback(null, list); }); }; diff --git a/src/js/crypto/rsa.js b/src/js/crypto/rsa.js index 5bd100a..26b0128 100644 --- a/src/js/crypto/rsa.js +++ b/src/js/crypto/rsa.js @@ -14,11 +14,13 @@ app.crypto.RSA = function(forge, util) { * and signing */ this.init = function(pubkeyPem, privkeyPem, keyId) { - keypair.publicKey = forge.pki.publicKeyFromPem(pubkeyPem); + keypair = { + _id: keyId, + publicKey: forge.pki.publicKeyFromPem(pubkeyPem) + }; if (privkeyPem) { keypair.privateKey = forge.pki.privateKeyFromPem(privkeyPem); } - keypair._id = keyId; }; /** diff --git a/src/js/crypto/util.js b/src/js/crypto/util.js index 6cffdc5..975e460 100644 --- a/src/js/crypto/util.js +++ b/src/js/crypto/util.js @@ -44,15 +44,13 @@ var Util = function(window, uuid, crypt) { * @list list [Array] The list of items to encrypt */ this.encryptList = function(aes, list) { - var json, ct, outList = []; + var outList = []; list.forEach(function(i) { // stringify to JSON before encryption - json = JSON.stringify(i.plaintext); - ct = aes.encrypt(json, i.key, i.iv); outList.push({ id: i.id, - ciphertext: ct, + ciphertext: aes.encrypt(JSON.stringify(i.plaintext), i.key, i.iv), key: i.key, iv: i.iv }); @@ -67,15 +65,13 @@ var Util = function(window, uuid, crypt) { * @list list [Array] The list of items to decrypt */ this.decryptList = function(aes, list) { - var json, pt, outList = []; + var outList = []; list.forEach(function(i) { // decrypt JSON and parse to object literal - json = aes.decrypt(i.ciphertext, i.key, i.iv); - pt = JSON.parse(json); outList.push({ id: i.id, - plaintext: pt, + plaintext: JSON.parse(aes.decrypt(i.ciphertext, i.key, i.iv)), key: i.key, iv: i.iv }); diff --git a/test/unit/crypto-test.js b/test/unit/crypto-test.js index 2a78f56..7df9fe3 100644 --- a/test/unit/crypto-test.js +++ b/test/unit/crypto-test.js @@ -32,33 +32,7 @@ asyncTest("PBKDF2 (Async/Worker)", 1, function() { }); }); -asyncTest("En/Decrypt for User", 4, function() { - var secret = "Secret stuff"; - - var itemKey = crypto_test.util.random(crypto_test.keySize); - var itemIV = crypto_test.util.random(crypto_test.ivSize); - var keyIV = crypto_test.util.random(crypto_test.ivSize); - - crypto_test.crypto.aesEncrypt(secret, itemKey, itemIV, function(ciphertext) { - ok(ciphertext, 'Encrypt item'); - - crypto_test.crypto.aesEncryptForUser(itemKey, keyIV, function(encryptedKey) { - ok(encryptedKey, 'Encrypt item key'); - - crypto_test.crypto.aesDecryptForUser(encryptedKey, keyIV, function(decryptedKey) { - equal(decryptedKey, itemKey, 'Decrypt item key'); - - crypto_test.crypto.aesDecrypt(ciphertext, decryptedKey, itemIV, function(decrypted) { - equal(decrypted, secret, 'Decrypt item'); - - start(); - }); - }); - }); - }); -}); - -asyncTest("AES (Async/Worker)", 2, function() { +asyncTest("AES en/decrypt (Async/Worker)", 2, function() { var secret = 'Big secret'; var key = crypto_test.util.random(crypto_test.keySize); @@ -75,7 +49,7 @@ asyncTest("AES (Async/Worker)", 2, function() { }); }); -asyncTest("AES batch (Async/Worker)", 5, function() { +asyncTest("AES en/decrypt batch (Async/Worker)", 5, function() { // generate test data var collection, list, td = new TestData(); @@ -96,23 +70,28 @@ asyncTest("AES batch (Async/Worker)", 5, function() { }); }); -asyncTest("AES batch for User (Async/Worker)", 5, function() { +asyncTest("AES/RSA encrypt batch for User (Async/Worker)", 2, function() { // generate test data - var collection, list, td = new TestData(); + var collection, td = new TestData(); - collection = td.getEmailCollection(100); - list = collection.toJSON(); + collection = td.getEmailCollection(10); + crypto_test.list = collection.toJSON(); - crypto_test.crypto.aesEncryptListForUser(list, function(encryptedList) { - ok(encryptedList, 'Encrypt list for user'); - equal(encryptedList.length, list.length, 'Length of list'); + crypto_test.crypto.encryptListForUser(crypto_test.list, null, function(err, encryptedList) { + ok(!err && encryptedList, 'Encrypt list for user'); + equal(encryptedList.length, crypto_test.list.length, 'Length of list'); + crypto_test.encryptedList = encryptedList; - crypto_test.crypto.aesDecryptListForUser(encryptedList, function(decryptedList) { - ok(decryptedList, 'Decrypt list'); - equal(decryptedList.length, list.length, 'Length of list'); - deepEqual(decryptedList, list, 'Decrypted list is correct'); + start(); + }); +}); - start(); - }); +asyncTest("AES/RSA decrypt batch for User (Async/Worker)", 3, function() { + crypto_test.crypto.decryptListForUser(crypto_test.encryptedList, null, 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'); + + start(); }); }); \ No newline at end of file