Add some additional crypto capabilities; Encrypt model store

This commit is contained in:
Martin Kleinschrodt 2013-11-12 11:24:44 +01:00
parent abfc4a01a9
commit 35d85dc940
2 changed files with 76 additions and 35 deletions

View File

@ -16,12 +16,15 @@ define(["sjcl"], function(sjcl) {
* @type {[type]}
*/
var container = Object.create({}, {
cipher: {value: ciphers.AES, writable: true, enumerable: true},
mode: {value: modes.CCM, writable: true, enumerable: true},
iv: {value: "", writable: true, enumerable: true},
ct: {value: "", writable: true},
adata: {value: [], writable: true},
ts: {value: 64, writable: true}
cipher: {value: ciphers.AES, writable: true, enumerable: true},
mode: {value: modes.CCM, writable: true, enumerable: true},
iv: {value: "", writable: true, enumerable: true},
salt: {value: "", writable: true, enumerable: true},
keySize: {value: 256, writable: true, enumerable: true},
iter: {value: 1000, writable: true, enumerable: true},
ct: {value: "", writable: true},
adata: {value: [], writable: true},
ts: {value: 64, writable: true}
});
/**
@ -30,63 +33,86 @@ define(["sjcl"], function(sjcl) {
* @param {string} passphrase
* A string to be used as base for the key derivation
* @param {array} salt
* Salt to be used for key derivation. Will be generated if not provided
* Base64 encoded salt to be used for key derivation. Will be generated if not provided
* @param {number} size
* Desired key size. Defaults to 256
* @param {number} iter
* Numer of iterations to use for the key derivation algorithm. Defaults to 1000
* @return {object}
* Key object containing the actual _key_ along with the used _salt_
* Key object containing the actual _key_ (base64 encoded) along with the used _salt_ and _iter_ations used
*/
function genKey(passphrase, salt, size, iter) {
salt = salt || sjcl.random.randomWords(4,0);
var s = salt ? sjcl.codec.base64.toBits(salt) : sjcl.random.randomWords(4,0);
size = size || 256;
var p = sjcl.misc.cachedPbkdf2(passphrase, {iter: iter || 1000, salt: salt});
p.key = p.key.slice(0, size/32);
iter = iter || 1000;
var p = sjcl.misc.cachedPbkdf2(passphrase, {iter: iter, salt: s});
p.key = sjcl.codec.base64.fromBits(p.key.slice(0, size/32));
p.salt = sjcl.codec.base64.fromBits(s);
p.size = size;
return p;
}
/**
* Decrypts a value inside a _crypto.container_ using the provided _key_
* @param {string} key
* The encryption key
* The encryption key (base64 encoded)
* @param {object} value
* A _crypto.container_ containing the value to be decrypted
*/
function decrypt(key, cont) {
var aes = new sjcl.cipher.aes(key);
var aes = new sjcl.cipher.aes(sjcl.codec.base64.toBits(key));
var iv = sjcl.codec.base64.toBits(cont.iv);
var ct = sjcl.codec.base64.toBits(cont.ct);
var pt = sjcl.mode[cont.mode.toLowerCase()].decrypt(aes, ct, iv, unescape(cont.adata), cont.ts);
var pt = sjcl.mode[cont.mode].decrypt(aes, ct, iv, cont.adata, cont.ts);
return sjcl.codec.utf8String.fromBits(pt);
}
/**
* Encrypts the _value_ using the provided _key_ and wraps it into a _crypto.container_ object.
* @param {string} key
* Key to be used for encryption
* Key to be used for encryption (base64 encoded)
* @param {string} value
* Value to be encrypted
* @param {array} adata
* @param {string} adata
* Authenticated data to be used for checking the integrity of the encrypted data and whether
* a description was successful
* @param {number} tagSize
*/
function encrypt(key, value, adata, tagSize) {
var cont = Object.create(container);
var iv = sjcl.random.randomWords(4,0);
var aes = new sjcl.cipher.aes(key);
var aes = new sjcl.cipher.aes(sjcl.codec.base64.toBits(key));
var pt = sjcl.codec.utf8String.toBits(value);
cont.adata = adata || sjcl.codec.base64.fromBits(sjcl.random.randomWords(4,0));
cont.ts = tagSize || 0;
var ct = sjcl.mode[cont.mode].encrypt(aes, pt, iv, cont.adata, cont.ts);
cont.iv = sjcl.codec.base64.fromBits(iv);
cont.ct = sjcl.codec.base64.fromBits(ct);
return cont;
}
function pwdDecrypt(pwd, cont) {
var p = genKey(pwd, cont.salt, cont.keySize, cont.iter);
return decrypt(p.key, cont);
}
function pwdEncrypt(pwd, value) {
var p = genKey(pwd);
var cont = encrypt(p.key, value);
cont.salt = p.salt;
cont.iter = p.iter;
cont.keySize = p.size;
return cont;
}
return {
ciphers: ciphers,
modes: modes,
container: container,
genKey: genKey,
decrypt: decrypt,
encrypt: encrypt
encrypt: encrypt,
pwdDecrypt: pwdDecrypt,
pwdEncrypt: pwdEncrypt
};
});

View File

@ -1,4 +1,34 @@
define(["safe/crypto", "safe/util"], function(crypto, util) {
var store = Object.create({}, {
password: {
value: "", writable: true
},
fetch: {
value: function(coll, password) {
this.password = password || this.password;
var obj = {};
var json = localStorage.getItem("coll_" + coll.name);
if (json) {
try {
var c = JSON.parse(json);
obj.records = crypto.pwdDecrypt(this.password, c);
} catch (e) {
console.error("*** failed decrypting! ***", e);
}
}
return coll.parse(obj);
}
},
save: {
value: function(coll) {
var pt = JSON.stringify(coll.raw());
var c = crypto.pwdEncrypt(this.password, pt);
localStorage.setItem("coll_" + coll.name, JSON.stringify(c));
}
}
});
var record = Object.create({}, {
name: {
enumerable: true,
@ -18,7 +48,7 @@ define(["safe/crypto", "safe/util"], function(crypto, util) {
},
store: {
writable: true,
value: null
value: store
},
records: {
writable: true,
@ -27,7 +57,7 @@ define(["safe/crypto", "safe/util"], function(crypto, util) {
},
parse: {
value: function(obj) {
this.records = obj.records;
this.records = obj.records || [];
}
},
raw: {
@ -68,21 +98,6 @@ define(["safe/crypto", "safe/util"], function(crypto, util) {
}
});
var store = Object.create({}, {
fetch: {
value: function(coll) {
var json = localStorage.getItem("coll_" + coll.name) || "{}";
return coll.parse(JSON.parse(json));
}
},
save: {
value: function(coll) {
var json = JSON.stringify(coll.raw());
localStorage.setItem("coll_" + coll.name, json);
}
}
});
return {
record: record,
collection: collection,