From c9c53598e826012fc532bb877af00f1e0803326b Mon Sep 17 00:00:00 2001 From: Tankred Hase Date: Tue, 14 May 2013 20:28:12 +0200 Subject: [PATCH] started refacroting crypto --- src/js/crypto/crypto.js | 96 ++++++++++++++++++++++------------------ src/js/crypto/rsa.js | 34 +++++++------- test/unit/crypto-test.js | 6 ++- test/unit/rsa-test.js | 2 +- 4 files changed, 78 insertions(+), 60 deletions(-) diff --git a/src/js/crypto/crypto.js b/src/js/crypto/crypto.js index 0eb77f0..150fea0 100644 --- a/src/js/crypto/crypto.js +++ b/src/js/crypto/crypto.js @@ -5,56 +5,80 @@ app.crypto.Crypto = function(window, util) { 'use strict'; - var symmetricUserKey = null, // the user's secret key used to encrypt item-keys - keyId = null, // the key ID linking the user's key set - aes = new app.crypto.AesCBC(forge); // use AES-CBC mode by default + var keypair = null, // the user's keys used to encrypt item-keys + aes = new app.crypto.AesCBC(forge), // use AES-CBC mode by default + rsa = new app.crypto.RSA(forge, util); // use RSA for asym. crypto /** * Initializes the crypto modules by fetching the user's * encrypted secret key from storage and storing it in memory. */ - this.init = function(emailAddress, password, keySize, ivSize, callback) { - this.emailAddress = emailAddress; - this.keySize = keySize; - this.ivSize = ivSize; + this.init = function(args, callback) { + var self = this; + + this.emailAddress = args.emailAddress; + this.keySize = args.keySize; + this.ivSize = args.keySize; // derive PBKDF2 from password in web worker thread - this.deriveKey(password, keySize, function(pbkdf2) { + this.deriveKey(args.password, args.keySize, function(pbkdf2) { // fetch user's encrypted secret key from keychain/storage var keyStore = new app.dao.LocalStorageDAO(window); - var storageId = emailAddress + '_encryptedSymmetricKey'; - var storedKey = keyStore.read(storageId); + var storageId = args.emailAddress + '_encryptedKeypair'; + var storedKeypair = keyStore.read(storageId); // check if key exists - if (!storedKey) { - // generate key, encrypt and persist if none exists - symmetricUserKey = util.random(keySize); - var iv = util.random(ivSize); - var key = aes.encrypt(symmetricUserKey, pbkdf2, iv); - storedKey = { - _id: util.UUID(), - userId: emailAddress, - encryptedKey: key, - keyIV: iv - }; - keyStore.persist(storageId, storedKey); + if (!storedKeypair) { + // generate keys, encrypt and persist if none exists + generateKeypair(keyStore, storageId, pbkdf2); } else { // decrypt key - try { - symmetricUserKey = aes.decrypt(storedKey.encryptedKey, pbkdf2, storedKey.keyIV); - } catch (ex) { - callback({ - errMsg: 'Wrong password!' - }); + decryptKeypair(storedKeypair, pbkdf2); + } + + }); + + function generateKeypair(keyStore, storageId, pbkdf2) { + // generate RSA keypair in web worker + rsa.generateKeypair(rsa_test.keySize, function(err) { + if (err) { + callback(err); return; } + keypair = rsa.exportKeys(); + + // encrypt keypair + var iv = util.random(self.ivSize); + var encryptedKeys = aes.encrypt(JSON.stringify(keypair), pbkdf2, iv); + + // store encrypted keypair + var newStoredKeypair = { + _id: keypair._id, + userId: args.emailAddress, + encryptedKeys: encryptedKeys, + keyIV: iv + }; + keyStore.persist(storageId, newStoredKeypair); + + callback(); + }); + } + + function decryptKeypair(storedKeypair, pbkdf2) { + try { + var keypairJson = aes.decrypt(storedKeypair.encryptedKeys, pbkdf2, storedKeypair.keyIV); + keypair = JSON.parse(keypairJson); + } catch (ex) { + callback({ + errMsg: 'Wrong password!' + }); + return; } - keyId = storedKey._id; callback(); - }); + } }; /** @@ -86,18 +110,6 @@ app.crypto.Crypto = function(window, util) { } }; - /** - * Derive an asymmetric keypait from the user's secret - */ - this.deriveKeyPair = function(naclCrypto, callback) { - naclCrypto.generateKeypair(symmetricUserKey, function(keys) { - if (keyId) { - keys.id = keyId; - } - callback(keys); - }); - }; - // // En/Decrypts single item // diff --git a/src/js/crypto/rsa.js b/src/js/crypto/rsa.js index 9fbb156..5bd100a 100644 --- a/src/js/crypto/rsa.js +++ b/src/js/crypto/rsa.js @@ -1,24 +1,24 @@ /** * A Wrapper for Forge's RSA encryption */ -app.crypto.RSA = function(forge) { +app.crypto.RSA = function(forge, util) { 'use strict'; var utl = forge.util; - var publicKey = null, - privateKey = null; + var keypair = null; /** * Initializes the RSA module by passing the user's keypair * The private key is option and required only for decryption * and signing */ - this.init = function(pubkeyPem, privkeyPem) { - publicKey = forge.pki.publicKeyFromPem(pubkeyPem); + this.init = function(pubkeyPem, privkeyPem, keyId) { + keypair.publicKey = forge.pki.publicKeyFromPem(pubkeyPem); if (privkeyPem) { - privateKey = forge.pki.privateKeyFromPem(privkeyPem); + keypair.privateKey = forge.pki.privateKeyFromPem(privkeyPem); } + keypair._id = keyId; }; /** @@ -28,8 +28,8 @@ app.crypto.RSA = function(forge) { forge.rsa.generateKeyPair({ bits: keySize, workerScript: app.config.workerPath + '/../../lib/forge/prime.worker.js' - }, function(err, keypair) { - if (err || !keypair.publicKey || !keypair.privateKey) { + }, function(err, newKeypair) { + if (err || !newKeypair || !newKeypair.publicKey || !newKeypair.privateKey) { callback({ errMsg: 'RSA keygeneration failed!', err: err @@ -37,8 +37,9 @@ app.crypto.RSA = function(forge) { return; } - publicKey = keypair.publicKey; - privateKey = keypair.privateKey; + keypair = newKeypair; + // generate unique keypair ID + keypair._id = util.UUID(); callback(); }); @@ -49,8 +50,9 @@ app.crypto.RSA = function(forge) { */ this.exportKeys = function() { return { - pubkeyPem: forge.pki.publicKeyToPem(publicKey), - privkeyPem: forge.pki.privateKeyToPem(privateKey) + _id: keypair._id, + pubkeyPem: forge.pki.publicKeyToPem(keypair.publicKey), + privkeyPem: forge.pki.privateKeyToPem(keypair.privateKey) }; }; @@ -61,7 +63,7 @@ app.crypto.RSA = function(forge) { */ this.encrypt = function(plaintext) { // encode plaintext to utf8 and encrypt - var ct = publicKey.encrypt(utl.encodeUtf8(plaintext)); + var ct = keypair.publicKey.encrypt(utl.encodeUtf8(plaintext)); // encode ciphtext to base64 return utl.encode64(ct); }; @@ -75,7 +77,7 @@ app.crypto.RSA = function(forge) { // decode base64 ciphertext to utf8 var ctUtf8 = utl.decode64(ciphertext); // decrypt and decode to utf16 - return utl.decodeUtf8(privateKey.decrypt(ctUtf8)); + return utl.decodeUtf8(keypair.privateKey.decrypt(ctUtf8)); }; /** @@ -91,7 +93,7 @@ app.crypto.RSA = function(forge) { }); // encode signature to base64 - return utl.encode64(privateKey.sign(sha)); + return utl.encode64(keypair.privateKey.sign(sha)); }; /** @@ -110,7 +112,7 @@ app.crypto.RSA = function(forge) { sha.update(utl.decode64(i)); }); - return publicKey.verify(sha.digest().getBytes(), sigUtf8); + return keypair.publicKey.verify(sha.digest().getBytes(), sigUtf8); }; }; \ No newline at end of file diff --git a/test/unit/crypto-test.js b/test/unit/crypto-test.js index e970ad4..2a78f56 100644 --- a/test/unit/crypto-test.js +++ b/test/unit/crypto-test.js @@ -13,7 +13,11 @@ asyncTest("Init", 2, function() { crypto_test.crypto = new app.crypto.Crypto(window, crypto_test.util); ok(crypto_test.crypto, 'Crypto'); - crypto_test.crypto.init(crypto_test.user, crypto_test.password, crypto_test.keySize, crypto_test.ivSize, function() { + crypto_test.crypto.init({ + emailAddress: crypto_test.user, + password: crypto_test.password, + keySize: crypto_test.keySize + }, function() { ok(true, 'Init crypto'); start(); diff --git a/test/unit/rsa-test.js b/test/unit/rsa-test.js index 9b31b1f..dc927fe 100644 --- a/test/unit/rsa-test.js +++ b/test/unit/rsa-test.js @@ -2,7 +2,7 @@ module("RSA Crypto"); var rsa_test = { keySize: 1024, - rsa: new app.crypto.RSA(forge), + rsa: new app.crypto.RSA(forge, new app.crypto.Util(window, uuid)), test_message: '06a9214036b8a15b512e03d534120006' };