WIP on crypto wrapper providers for AES, TripleDES, Rabbit, 3 tests failing

This commit is contained in:
David Humphrey (:humph) david.humphrey@senecacollege.ca 2013-11-29 15:39:42 -05:00
parent 807f93798c
commit 58f57cca19
5 changed files with 350 additions and 0 deletions

View File

@ -0,0 +1,125 @@
define(function(require) {
var FILE_SYSTEM_NAME = require('src/constants').FILE_SYSTEM_NAME;
// AES encryption, see http://code.google.com/p/crypto-js/#AES
require("crypto-js/rollups/aes");
// DES, Triple DES, see http://code.google.com/p/crypto-js/#DES,_Triple_DES
require("crypto-js/rollups/tripledes");
// Rabbit, see http://code.google.com/p/crypto-js/#Rabbi
require("crypto-js/rollups/rabbit");
function CryptoWrappedContext(context, encrypt, decrypt) {
this.context = context;
this.encrypt = encrypt;
this.decrypt = decrypt;
}
CryptoWrappedContext.prototype.clear = function(callback) {
this.context.clear(callback);
};
CryptoWrappedContext.prototype.get = function(key, callback) {
var that = this;
var encryptedKey = this.encrypt(key);
this.context.get(encryptedKey, function(err, value) {
if(err) {
callback(err);
return;
}
if(value) {
value = that.decrypt(value);
}
callback(null, value);
});
};
CryptoWrappedContext.prototype.put = function(key, value, callback) {
var encryptedKey = this.encrypt(key);
var encryptedValue = this.encrypt(value);
this.context.put(encryptedKey, encryptedValue, callback);
};
CryptoWrappedContext.prototype.delete = function(key, callback) {
var encryptedKey = this.encrypt(key);
this.context.delete(encryptedKey, callback);
};
// Custom formatting for encryption objects <-> strings
var formatter = {
stringify: function(cipherParams) {
// create json object with ciphertext
var jsonObj = {
ct: cipherParams.ciphertext.toString(CryptoJS.enc.Base64)
};
// cache iv and salt
if (cipherParams.iv) {
jsonObj.iv = cipherParams.iv.toString();
}
if (cipherParams.salt) {
jsonObj.s = cipherParams.salt.toString();
}
// stringify json object
return JSON.stringify(jsonObj);
},
parse: function(jsonString) {
// parse json string
var jsonObj;
try {
jsonObj = JSON.parse(jsonString);
} catch(e) {
throw e;
}
// extract ciphertext from json object, and create cipher params object
var cipherParams = CryptoJS.lib.CipherParams.create({
ciphertext: CryptoJS.enc.Base64.parse(jsonObj.ct)
});
// extract iv and salt
if (jsonObj.iv) {
cipherParams.iv = CryptoJS.enc.Hex.parse(jsonObj.iv);
}
if (jsonObj.s) {
cipherParams.salt = CryptoJS.enc.Hex.parse(jsonObj.s);
}
return cipherParams;
}
};
function buildCryptoWrapper(encryptionType) {
// It is up to the app using this wrapper how the passphrase is acquired, probably by
// prompting the user to enter it when the file system is being opened.
function CryptoWrappedProvider(passphrase, provider) {
this.provider = provider;
this.encrypt = function(plain) {
console.log('encrypt', plain, CryptoJS[encryptionType].encrypt(plain, passphrase).toString());
return CryptoJS[encryptionType].encrypt(plain, passphrase, {format: formatter}).toString();
};
this.decrypt = function(encrypted) {
console.log('decrypt', encrypted, CryptoJS[encryptionType].decrypt(encrypted, passphrase).toString());
return CryptoJS[encryptionType].decrypt(encrypted, passphrase, {format: formatter}).toString();
};
}
CryptoWrappedProvider.isSupported = function() {
return true;
};
CryptoWrappedProvider.prototype.open = function(callback) {
this.provider.open(callback);
};
CryptoWrappedProvider.prototype.getReadOnlyContext = function() {
return new CryptoWrappedContext(this.provider.getReadOnlyContext(), this.encrypt, this.decrypt);
};
CryptoWrappedProvider.prototype.getReadWriteContext = function() {
return new CryptoWrappedContext(this.provider.getReadWriteContext(), this.encrypt, this.decrypt);
};
return CryptoWrappedProvider;
}
return {
AESWrapper: buildCryptoWrapper('AES'),
TripleDESWrapper: buildCryptoWrapper('TripleDES'),
RabbitWrapper: buildCryptoWrapper('Rabbit')
};
});

View File

@ -3,12 +3,32 @@ define(function(require) {
var IndexedDB = require('src/providers/indexeddb');
var WebSQL = require('src/providers/websql');
var Memory = require('src/providers/memory');
var CryptoWrappers = require('src/providers/crypto-wrappers');
return {
IndexedDB: IndexedDB,
WebSQL: WebSQL,
Memory: Memory,
/**
* Wrappers for composing various types of providers
*/
// Encryption Wrappers
AESWrapper: CryptoWrappers.AESWrapper,
TripleDESWrapper: CryptoWrappers.TripleDESWrapper,
RabbitWrapper: CryptoWrappers.RabbitWrapper,
// Convenience encryption wrapper
EncryptionWrapper: CryptoWrappers.AESWrapper,
/**
* Convenience Provider references
*/
// The default provider to use when none is specified
Default: IndexedDB,
// The Fallback provider does automatic fallback checks
Fallback: (function() {
if(IndexedDB.isSupported()) {

View File

@ -0,0 +1,192 @@
define(["IDBFS"], function(IDBFS) {
// We reuse the same set of tests for all crypto providers.
// buildTestsFor() creates a set of tests bound to a crypto
// provider, and uses a Memory() provider internally.
function buildTestsFor(wrapperName) {
var passphrase = '' + Date.now();
function createProvider() {
var memoryProvider = new IDBFS.FileSystem.providers.Memory();
return new IDBFS.FileSystem.providers[wrapperName](passphrase, memoryProvider);
}
describe("IDBFS.FileSystem.providers" + wrapperName, function() {
it("is supported -- if it isn't, none of these tests can run.", function() {
expect(IDBFS.FileSystem.providers[wrapperName].isSupported()).toEqual(true);
});
it("has open, getReadOnlyContext, and getReadWriteContext instance methods", function() {
var indexedDBProvider = createProvider();
expect(typeof indexedDBProvider.open).toEqual('function');
expect(typeof indexedDBProvider.getReadOnlyContext).toEqual('function');
expect(typeof indexedDBProvider.getReadWriteContext).toEqual('function');
});
describe("open an Memory provider", function() {
it("should open a new Memory database", function() {
var complete = false;
var _error, _result;
var provider = createProvider();
provider.open(function(err, firstAccess) {
_error = err;
_result = firstAccess;
complete = true;
});
waitsFor(function() {
return complete;
}, 'test to complete', DEFAULT_TIMEOUT);
runs(function() {
expect(_error).toEqual(null);
expect(_result).toEqual(true);
});
});
});
describe("Read/Write operations on an Memory provider", function() {
it("should allow put() and get()", function() {
var complete = false;
var _error, _result;
var provider = createProvider();
provider.open(function(err, firstAccess) {
_error = err;
var context = provider.getReadWriteContext();
context.put("key", "value", function(err, result) {
_error = _error || err;
context.get("key", function(err, result) {
_error = _error || err;
_result = result;
complete = true;
});
});
});
waitsFor(function() {
return complete;
}, 'test to complete', DEFAULT_TIMEOUT);
runs(function() {
expect(_error).toEqual(null);
expect(_result).toEqual("value");
});
});
it("should allow delete()", function() {
var complete = false;
var _error, _result;
var provider = createProvider();
provider.open(function(err, firstAccess) {
_error = err;
var context = provider.getReadWriteContext();
context.put("key", "value", function(err, result) {
_error = _error || err;
context.delete("key", function(err, result) {
_error = _error || err;
context.get("key", function(err, result) {
_error = _error || err;
_result = result;
complete = true;
});
});
});
});
waitsFor(function() {
return complete;
}, 'test to complete', DEFAULT_TIMEOUT);
runs(function() {
expect(_error).toEqual(null);
expect(_result).toEqual(null);
});
});
it("should allow clear()", function() {
var complete = false;
var _error, _result1, _result2;
var provider = createProvider();
provider.open(function(err, firstAccess) {
_error = err;
var context = provider.getReadWriteContext();
context.put("key1", "value1", function(err, result) {
_error = _error || err;
context.put("key2", "value2", function(err, result) {
_error = _error || err;
context.clear(function(err) {
_error = _error || err;
context.get("key1", function(err, result) {
_error = _error || err;
_result1 = result;
context.get("key2", function(err, result) {
_error = _error || err;
_result2 = result;
complete = true;
});
});
});
});
});
});
waitsFor(function() {
return complete;
}, 'test to complete', DEFAULT_TIMEOUT);
runs(function() {
expect(_error).toEqual(null);
expect(_result1).toEqual(null);
expect(_result2).toEqual(null);
});
});
it("should fail when trying to write on ReadOnlyContext", function() {
var complete = false;
var _error, _result;
var provider = createProvider();
provider.open(function(err, firstAccess) {
_error = err;
var context = provider.getReadOnlyContext();
context.put("key1", "value1", function(err, result) {
_error = _error || err;
_result = result;
complete = true;
});
});
waitsFor(function() {
return complete;
}, 'test to complete', DEFAULT_TIMEOUT);
runs(function() {
expect(_error).toBeDefined();
expect(_result).toEqual(null);
});
});
});
});
}
buildTestsFor('AESWrapper');
buildTestsFor('TripleDESWrapper');
buildTestsFor('RabbitWrapper');
});

View File

@ -16,6 +16,18 @@ define(["IDBFS"], function(IDBFS) {
expect(typeof IDBFS.FileSystem.providers.Memory).toEqual('function');
});
it("has an AESWrapper constructor", function() {
expect(typeof IDBFS.FileSystem.providers.AESWrapper).toEqual('function');
});
it("has a TripleDESWrapper constructor", function() {
expect(typeof IDBFS.FileSystem.providers.TripleDESWrapper).toEqual('function');
});
it("has a RabbitWrapper constructor", function() {
expect(typeof IDBFS.FileSystem.providers.RabbitWrapper).toEqual('function');
});
it("has a Default constructor", function() {
expect(typeof IDBFS.FileSystem.providers.Default).toEqual('function');
});

View File

@ -33,6 +33,7 @@ define([
"spec/providers/providers.memory.spec",
"spec/providers/providers.indexeddb.spec",
"spec/providers/providers.websql.spec",
"spec/providers/providers.crypto.spec",
// Ported node.js tests (filenames match names in https://github.com/joyent/node/tree/master/test)
"spec/node-js/simple/test-fs-mkdir",