mirror of
https://github.com/moparisthebest/mail
synced 2024-11-25 18:32:20 -05:00
refactored crypto
This commit is contained in:
parent
5d409933e5
commit
ccebe011cb
@ -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);
|
||||
});
|
||||
};
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
};
|
||||
return self;
|
||||
});
|
@ -1,4 +1,4 @@
|
||||
(function() {
|
||||
define(['backbone'], function(Backbone) {
|
||||
'use strict';
|
||||
|
||||
app.model.Email = Backbone.Model.extend({
|
||||
@ -24,4 +24,4 @@
|
||||
|
||||
});
|
||||
|
||||
}());
|
||||
});
|
@ -1,4 +1,4 @@
|
||||
define(['cryptoLib/util'], function(util) {
|
||||
define(['cryptoLib/util', 'js/model/email-model'], function(util) {
|
||||
'use strict';
|
||||
|
||||
var self = {};
|
||||
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user