diff --git a/src/js/crypto/crypto.js b/src/js/crypto/crypto.js index ec46992..48f6b9f 100644 --- a/src/js/crypto/crypto.js +++ b/src/js/crypto/crypto.js @@ -2,7 +2,7 @@ * High level crypto api that invokes native crypto (if available) and * gracefully degrades to JS crypto (if unavailable) */ -define(['cryptoLib/util', 'cryptoLib/aes-cbc', 'cryptoLib/rsa', 'cryptoLib/crypto-batch'], function(util, aes, rsa, cryptoBatch) { +define(['cryptoLib/util', 'cryptoLib/aes-cbc', 'cryptoLib/rsa', 'cryptoLib/crypto-batch', 'js/crypto/pbkdf2'], function(util, aes, rsa, cryptoBatch, pbkdf2) { 'use strict'; var self = {}; @@ -26,7 +26,7 @@ define(['cryptoLib/util', 'cryptoLib/aes-cbc', 'cryptoLib/rsa', 'cryptoLib/crypt self.rsaKeySize = args.rsaKeySize; // derive PBKDF2 from password in web worker thread - self.deriveKey(args.password, self.keySize, function(err, pbkdf2) { + self.deriveKey(args.password, self.keySize, function(err, derivedKey) { if (err) { callback(err); return; @@ -35,15 +35,15 @@ define(['cryptoLib/util', 'cryptoLib/aes-cbc', 'cryptoLib/rsa', 'cryptoLib/crypt // check if key exists if (!args.storedKeypair) { // generate keys, encrypt and persist if none exists - generateKeypair(pbkdf2); + generateKeypair(derivedKey); } else { // decrypt key - decryptKeypair(args.storedKeypair, pbkdf2); + decryptKeypair(args.storedKeypair, derivedKey); } }); - function generateKeypair(pbkdf2) { + function generateKeypair(derivedKey) { // generate RSA keypair in web worker rsa.generateKeypair(self.rsaKeySize, function(err, generatedKeypair) { if (err) { @@ -53,7 +53,7 @@ define(['cryptoLib/util', 'cryptoLib/aes-cbc', 'cryptoLib/rsa', 'cryptoLib/crypt // encrypt keypair var iv = util.random(self.ivSize); - var encryptedPrivateKey = aes.encrypt(generatedKeypair.privkeyPem, pbkdf2, iv); + var encryptedPrivateKey = aes.encrypt(generatedKeypair.privkeyPem, derivedKey, iv); // new encrypted keypair object var newKeypair = { @@ -75,7 +75,7 @@ define(['cryptoLib/util', 'cryptoLib/aes-cbc', 'cryptoLib/rsa', 'cryptoLib/crypt }); } - function decryptKeypair(storedKeypair, pbkdf2) { + function decryptKeypair(storedKeypair, derivedKey) { var decryptedPrivateKey; // validate input @@ -86,10 +86,10 @@ define(['cryptoLib/util', 'cryptoLib/aes-cbc', 'cryptoLib/rsa', 'cryptoLib/crypt return; } - // try to decrypt with pbkdf2 + // try to decrypt with derivedKey try { var prK = storedKeypair.privateKey; - decryptedPrivateKey = aes.decrypt(prK.encryptedKey, pbkdf2, prK.iv); + decryptedPrivateKey = aes.decrypt(prK.encryptedKey, derivedKey, prK.iv); } catch (ex) { callback({ errMsg: 'Wrong password!' @@ -111,7 +111,6 @@ define(['cryptoLib/util', 'cryptoLib/aes-cbc', 'cryptoLib/rsa', 'cryptoLib/crypt password: password, keySize: keySize }, callback, function() { - var pbkdf2 = new app.crypto.PBKDF2(); return pbkdf2.getKey(password, keySize); }); }; diff --git a/src/js/crypto/pbkdf2.js b/src/js/crypto/pbkdf2.js index f21635b..227bbb2 100644 --- a/src/js/crypto/pbkdf2.js +++ b/src/js/crypto/pbkdf2.js @@ -1,16 +1,18 @@ /** * A Wrapper for Forge's PBKDF2 function */ -app.crypto.PBKDF2 = function() { +define(['forge'], function(forge) { 'use strict'; + var self = {}; + /** * PBKDF2-HMAC-SHA1 key derivation with a constant salt and 1000 iterations * @param password [String] The password in UTF8 * @param keySize [Number] The key size in bits * @return [String] The base64 encoded key */ - this.getKey = function(password, keySize) { + self.getKey = function(password, keySize) { var salt = forge.util.decode64("vbhmLjC+Ub6MSbhS6/CkOwxB25wvwRkSLP2DzDtYb+4="); var key = forge.pkcs5.pbkdf2(password, salt, 1000, keySize / 8); var keyBase64 = forge.util.encode64(key); @@ -18,4 +20,5 @@ app.crypto.PBKDF2 = function() { return keyBase64; }; -}; \ No newline at end of file + return self; +}); \ No newline at end of file diff --git a/src/js/model/email-model.js b/src/js/model/email-model.js index 0ecab0a..f4d6b7e 100644 --- a/src/js/model/email-model.js +++ b/src/js/model/email-model.js @@ -1,4 +1,4 @@ -(function() { +define(['backbone'], function(Backbone) { 'use strict'; app.model.Email = Backbone.Model.extend({ @@ -24,4 +24,4 @@ }); -}()); \ No newline at end of file +}); \ No newline at end of file diff --git a/test/test-data.js b/test/test-data.js index bbdabdc..21f4627 100644 --- a/test/test-data.js +++ b/test/test-data.js @@ -1,4 +1,4 @@ -define(['cryptoLib/util'], function(util) { +define(['cryptoLib/util', 'js/model/email-model'], function(util) { 'use strict'; var self = {}; diff --git a/test/unit/crypto-test.js b/test/unit/crypto-test.js index 11b17b8..8160bf1 100644 --- a/test/unit/crypto-test.js +++ b/test/unit/crypto-test.js @@ -1,108 +1,109 @@ -'use strict'; +define(['js/crypto/crypto', 'cryptoLib/util', 'test/test-data'], function(crypto, util, testData) { + 'use strict'; -module("Crypto Api"); + module("Crypto Api"); -var crypto_test = { - user: 'crypto_test@example.com', - password: 'Password', - keySize: 128, - ivSize: 128, - rsaKeySize: 1024 -}; + var cryptoTest = { + user: 'crypto_test@example.com', + password: 'Password', + keySize: 128, + ivSize: 128, + rsaKeySize: 1024 + }; -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'); + asyncTest("Init without keypair", 4, function() { + // init dependencies + ok(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, 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(); - }); -}); - -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 && !generatedKeypair, 'Init crypto with keypair input'); - - start(); - }); -}); - -asyncTest("PBKDF2 (Async/Worker)", 2, function() { - crypto_test.crypto.deriveKey(crypto_test.password, crypto_test.keySize, function(err, key) { - ok(!err); - equal(crypto_test.util.base642Str(key).length * 8, crypto_test.keySize, 'Keysize ' + crypto_test.keySize); - - start(); - }); -}); - -asyncTest("AES en/decrypt (Async/Worker)", 4, function() { - var secret = 'Big secret'; - - var key = crypto_test.util.random(crypto_test.keySize); - var iv = crypto_test.util.random(crypto_test.ivSize); - - crypto_test.crypto.aesEncrypt(secret, key, iv, function(err, ciphertext) { - ok(!err); - ok(ciphertext, 'Encrypt item'); - - crypto_test.crypto.aesDecrypt(ciphertext, key, iv, function(err, decrypted) { - ok(!err); - equal(decrypted, secret, 'Decrypt item'); + // test without passing keys + crypto.init({ + emailAddress: cryptoTest.user, + password: cryptoTest.password, + keySize: cryptoTest.keySize, + rsaKeySize: cryptoTest.rsaKeySize + }, 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); + cryptoTest.generatedKeypair = generatedKeypair; start(); }); }); -}); -asyncTest("AES/RSA encrypt batch for User (Async/Worker)", 2, function() { - // generate test data - var collection, td = new TestData(); + asyncTest("Init with keypair", 1, function() { + // test with passing keypair + crypto.init({ + emailAddress: cryptoTest.user, + password: cryptoTest.password, + keySize: cryptoTest.keySize, + rsaKeySize: cryptoTest.rsaKeySize, + storedKeypair: cryptoTest.generatedKeypair + }, function(err, generatedKeypair) { + ok(!err && !generatedKeypair, 'Init crypto with keypair input'); - collection = td.getEmailCollection(10); - crypto_test.list = collection.toJSON(); - - var receiverPubkeys = [crypto_test.generatedKeypair.publicKey]; - - 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; - - start(); + start(); + }); }); -}); -asyncTest("AES/RSA decrypt batch for User (Async/Worker)", 3, function() { + asyncTest("PBKDF2 (Async/Worker)", 2, function() { + crypto.deriveKey(cryptoTest.password, cryptoTest.keySize, function(err, key) { + ok(!err); + equal(util.base642Str(key).length * 8, cryptoTest.keySize, 'Keysize ' + cryptoTest.keySize); - var senderPubkeys = [crypto_test.generatedKeypair.publicKey]; - - 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'); - - start(); + start(); + }); }); + + asyncTest("AES en/decrypt (Async/Worker)", 4, function() { + var secret = 'Big secret'; + + var key = util.random(cryptoTest.keySize); + var iv = util.random(cryptoTest.ivSize); + + crypto.aesEncrypt(secret, key, iv, function(err, ciphertext) { + ok(!err); + ok(ciphertext, 'Encrypt item'); + + crypto.aesDecrypt(ciphertext, key, iv, function(err, decrypted) { + ok(!err); + equal(decrypted, secret, 'Decrypt item'); + + start(); + }); + }); + }); + + asyncTest("AES/RSA encrypt batch for User (Async/Worker)", 2, function() { + // generate test data + var collection; + + collection = testData.getEmailCollection(10); + cryptoTest.list = collection.toJSON(); + + var receiverPubkeys = [cryptoTest.generatedKeypair.publicKey]; + + crypto.encryptListForUser(cryptoTest.list, receiverPubkeys, function(err, encryptedList) { + ok(!err && encryptedList, 'Encrypt list for user'); + equal(encryptedList.length, cryptoTest.list.length, 'Length of list'); + cryptoTest.encryptedList = encryptedList; + + start(); + }); + }); + + asyncTest("AES/RSA decrypt batch for User (Async/Worker)", 3, function() { + + var senderPubkeys = [cryptoTest.generatedKeypair.publicKey]; + + crypto.decryptListForUser(cryptoTest.encryptedList, senderPubkeys, function(err, decryptedList) { + ok(!err && decryptedList, 'Decrypt list'); + equal(decryptedList.length, cryptoTest.list.length, 'Length of list'); + deepEqual(decryptedList, cryptoTest.list, 'Decrypted list is correct'); + + start(); + }); + }); + }); \ No newline at end of file diff --git a/test/unit/main.js b/test/unit/main.js index 59a9ad6..afd9f46 100644 --- a/test/unit/main.js +++ b/test/unit/main.js @@ -25,7 +25,8 @@ function startTests() { 'test/unit/aes-test', 'test/unit/rsa-test', 'test/unit/lawnchair-dao-test', - 'test/unit/keychain-dao-test' + 'test/unit/keychain-dao-test', + 'test/unit/crypto-test' ], function() { //Tests loaded, run tests QUnit.start();