Commit 0b8bd163 authored by amrani's avatar amrani

adding friedly user password to cryptostorage

parent d963b0a5
...@@ -75,6 +75,7 @@ module.exports = function (grunt) { ...@@ -75,6 +75,7 @@ module.exports = function (grunt) {
indent: 2, indent: 2,
maxerr: 3, maxerr: 3,
predef: [ predef: [
'DataView',
'define', 'define',
'exports', 'exports',
'require', 'require',
......
...@@ -12,20 +12,17 @@ ...@@ -12,20 +12,17 @@
/* /*
The cryptography system used by this storage is AES-GCM. The cryptography system used by this storage is AES-GCM.
Here is an example of how to generate a key to the json format: Here is an example of how to generate a strong user key :
go to the website : https://randomkeygen.com/ pike a key and memorize it
let suppose the key choseen is "Hwm0jPAmQC" then :
return new RSVP.Queue() return new RSVP.Queue()
.push(function () {
return crypto.subtle.generateKey({name: "AES-GCM", length: 256},
true, ["encrypt", "decrypt"]);
})
.push(function (key) {
return crypto.subtle.exportKey("jwk", key);
})
.push(function (json_key) { .push(function (json_key) {
var jio = jIO.createJIO({ var jio = jIO.createJIO({
type: "crypt", type: "crypt",
key: json_key, key: "Hwm0jPAmQC",
sub_storage: {storage_definition} sub_storage: {storage_definition}
}); });
}); });
...@@ -43,22 +40,142 @@ ...@@ -43,22 +40,142 @@
var MIME_TYPE = "application/x-jio-aes-gcm-encryption"; var MIME_TYPE = "application/x-jio-aes-gcm-encryption";
//used unibabel.js for converting function
function hexToBuffer(hex) {
// Xxxx use Uint8Array or ArrayBuffer or DataView
var i,
byteLen = hex.length / 2,
arr,
j = 0;
if (byteLen !== parseInt(byteLen, 10)) {
throw new Error("Invalid hex length '" + hex.length + "'");
}
arr = new Uint8Array(byteLen);
for (i = 0; i < byteLen; i += 1) {
arr[i] = parseInt(hex[j] + hex[j + 1], 16);
j += 2;
}
return arr;
}
function utf8ToBinaryString(str) {
var escstr = encodeURIComponent(str),
// replaces any uri escape sequence, such as %0A,
// with binary escape, such as 0x0A
binstr = escstr.replace(/%([0-9A-F]{2})/g, function (p1) {
return String.fromCharCode(parseInt(p1, 16));
});
return binstr;
}
function binaryStringToBuffer(binstr) {
var buf;
if ("undefined" !== Uint8Array) {
buf = new Uint8Array(binstr.length);
} else {
buf = [];
}
Array.prototype.forEach.call(binstr, function (ch, i) {
buf[i] = ch.charCodeAt(0);
});
return buf;
}
function utf8ToBuffer(str) {
var binstr = utf8ToBinaryString(str),
buf = binaryStringToBuffer(binstr);
return buf;
}
function hex(buffer) {
var hexCodes = [],
view = new DataView(buffer),
i,
value,
stringValue,
padding,
paddedValue;
for (i = 0; i < view.byteLength; i += 4) {
// Using getUint32 reduces the number
//of iterations needed (we process 4 bytes each time)
value = view.getUint32(i);
// toString(16) will give the hex representation
//of the number without padding
stringValue = value.toString(16);
// We use concatenation and slice for padding
padding = '00000000';
paddedValue = (padding + stringValue).slice(-padding.length);
hexCodes.push(paddedValue);
}
}
function sha256(str) {
// We transform the string into an arraybuffer.
return crypto.subtle.digest("SHA-256", utf8ToBuffer(str))
.then(function (hash) {
return hex(hash);
});
}
//function for JIO.js substorage
//API for using String Key form user
function CryptStorage(spec) { function CryptStorage(spec) {
this._key = spec.key; if (spec.key !== undefined && spec.key !== null
this._jsonKey = true; && typeof spec.key === 'string') {
this._sub_storage = jIO.createJIO(spec.sub_storage); this._key = spec.key;
this._userkey = true;
//hash the value of spec.key so if some one try to get it back
sha256(spec.key).then(function (digest) {spec.key = digest; });
this._sub_storage = jIO.createJIO(spec.sub_storage);
} else {
spec.key = null;
this._userkey = false;
this._key = null;
this._sub_storage = jIO.createJIO(spec.sub_storage);
}
} }
function convertKey(that) { function convertKey(that) {
return new RSVP.Queue() return new RSVP.Queue()
.push(function () { .push(function () {
return crypto.subtle.importKey("jwk", that._key, var passphraseKey = utf8ToBuffer(that._key);
"AES-GCM", false, return window.crypto.subtle.importKey(
["encrypt", "decrypt"]); "raw",
passphraseKey,
{
name: "PBKDF2"
},
false,
["deriveBits", "deriveKey"
]
);
})
.push(function (key) {
return window.crypto.subtle.deriveKey({
"name": "PBKDF2",
"salt": hexToBuffer("6f0904840608c09ae18c131542fbd1bd"),
"iterations": 1000,
//we can add iteration number but slow CPU will freez
"hash": "SHA-256"
}, key, {
"name": "AES-GCM",
"length": 256
}, false, ["encrypt", "decrypt"]);
}) })
.push(function (res) { .push(function (res) {
that._key = res; that._key = res;
that._jsonKey = false; that._userkey = false;
return; return;
}); });
} }
...@@ -97,84 +214,91 @@ ...@@ -97,84 +214,91 @@
CryptStorage.prototype.putAttachment = function (id, name, blob) { CryptStorage.prototype.putAttachment = function (id, name, blob) {
var initializaton_vector = crypto.getRandomValues(new Uint8Array(12)), var initializaton_vector = crypto.getRandomValues(new Uint8Array(12)),
that = this; that = this;
if (that._key !== null) {
return new RSVP.Queue() return new RSVP.Queue()
.push(function () { .push(function () {
if (that._jsonKey === true) { if (that._userkey === true) {
return convertKey(that); return convertKey(that);
} }
return; return;
}) })
.push(function () { .push(function () {
return jIO.util.readBlobAsDataURL(blob); return jIO.util.readBlobAsDataURL(blob);
}) })
.push(function (dataURL) { .push(function (dataURL) {
//string->arraybuffer //string->arraybuffer
var strLen = dataURL.target.result.length, var strLen = dataURL.target.result.length,
buf = new ArrayBuffer(strLen), buf = new ArrayBuffer(strLen),
bufView = new Uint8Array(buf), bufView = new Uint8Array(buf),
i; i;
dataURL = dataURL.target.result;
dataURL = dataURL.target.result; for (i = 0; i < strLen; i += 1) {
for (i = 0; i < strLen; i += 1) { bufView[i] = dataURL.charCodeAt(i);
bufView[i] = dataURL.charCodeAt(i); }
} return crypto.subtle.encrypt({
return crypto.subtle.encrypt({ name: "AES-GCM",
name : "AES-GCM", iv: initializaton_vector
iv : initializaton_vector },
}, that._key, buf);
that._key, buf); })
}) .push(function (coded) {
.push(function (coded) { var blob = new Blob([initializaton_vector, coded], {
var blob = new Blob([initializaton_vector, coded], {type: MIME_TYPE}); type: MIME_TYPE
return that._sub_storage.putAttachment(id, name, blob); });
}); return that._sub_storage.putAttachment(id, name, blob);
});
} //if the password is not a string or null or undefined
//we do not crypt data
return that._sub_storage.putAttachment(id, name, blob);
}; };
CryptStorage.prototype.getAttachment = function (id, name) { CryptStorage.prototype.getAttachment = function (id, name) {
var that = this; var that = this;
if (that._key !== null) {
return that._sub_storage.getAttachment(id, name) return that._sub_storage.getAttachment(id, name)
.push(function (blob) { .push(function (blob) {
if (blob.type !== MIME_TYPE) { if (blob.type !== MIME_TYPE) {
return blob; return blob;
} }
return new RSVP.Queue() return new RSVP.Queue()
.push(function () { .push(function () {
if (that._jsonKey === true) { if (that._userkey === true) {
return convertKey(that); return convertKey(that);
} }
return; return;
}) })
.push(function () { .push(function () {
return jIO.util.readBlobAsArrayBuffer(blob); return jIO.util.readBlobAsArrayBuffer(blob);
}) })
.push(function (coded) { .push(function (coded) {
var initializaton_vector; var initializaton_vector;
coded = coded.target.result;
coded = coded.target.result; initializaton_vector = new Uint8Array(coded.slice(0, 12));
initializaton_vector = new Uint8Array(coded.slice(0, 12)); return new RSVP.Queue()
return new RSVP.Queue() .push(function () {
.push(function () { return crypto.subtle.decrypt({
return crypto.subtle.decrypt({ name: "AES-GCM",
name : "AES-GCM", iv: initializaton_vector
iv : initializaton_vector },
}, that._key, coded.slice(12));
that._key, coded.slice(12)); })
}) .push(function (arr) {
.push(function (arr) { //arraybuffer->string
//arraybuffer->string arr = String.fromCharCode.apply(null, new Uint8Array(arr));
arr = String.fromCharCode.apply(null, new Uint8Array(arr)); return jIO.util.dataURItoBlob(arr);
return jIO.util.dataURItoBlob(arr); })
}) .push(undefined, function (error) {
.push(undefined, function (error) { if (error instanceof DOMException) {
if (error instanceof DOMException) { return blob;
return blob; }
} throw error;
throw error; });
}); });
}); });
}); }
//if the password is not a string or null or undefined
//we do not crypt data
return that._sub_storage.getAttachment(id, name);
}; };
CryptStorage.prototype.removeAttachment = function () { CryptStorage.prototype.removeAttachment = function () {
...@@ -189,4 +313,4 @@ ...@@ -189,4 +313,4 @@
jIO.addStorage('crypt', CryptStorage); jIO.addStorage('crypt', CryptStorage);
}(jIO, RSVP, DOMException, Blob, crypto, Uint8Array, ArrayBuffer)); }(jIO, RSVP, DOMException, Blob, crypto, Uint8Array, ArrayBuffer));
\ No newline at end of file
...@@ -255,12 +255,15 @@ ...@@ -255,12 +255,15 @@
}) })
.push(function (evt) { .push(function (evt) {
if (evt.target.response instanceof Blob) { if (evt.target.response instanceof Blob) {
return evt.target.response; //create a new blob with type AES-GCM to decrypt back
var cryptblob = new Blob([evt.target.response],
{type: "application/x-jio-aes-gcm-encryption"});
return cryptblob;
} }
return new Blob( return new Blob(
[evt.target.responseText], [evt.target.responseText],
{"type": evt.target.getResponseHeader('Content-Type') || {"type": "application/x-jio-aes-gcm-encryption"}
"application/octet-stream"} // evt.target.getResponseHeader('Content-Type') ||
); );
}, function (error) { }, function (error) {
if (error.target !== undefined && error.target.status === 409) { if (error.target !== undefined && error.target.status === 409) {
...@@ -325,4 +328,4 @@ ...@@ -325,4 +328,4 @@
jIO.addStorage('dropbox', DropboxStorage); jIO.addStorage('dropbox', DropboxStorage);
}(jIO, RSVP, Blob, JSON)); }(jIO, RSVP, Blob, JSON));
\ No newline at end of file
...@@ -11,9 +11,15 @@ ...@@ -11,9 +11,15 @@
equal = QUnit.equal, equal = QUnit.equal,
throws = QUnit.throws, throws = QUnit.throws,
module = QUnit.module, module = QUnit.module,
key = {"alg": "A256GCM", "ext": true, key = "password",
"k": "seeaLzpu8dHG07bO2ANH2GywbTqs_zrs4Vq8zmtYeE4", key_generated_by_password = {
"key_ops": ["encrypt", "decrypt"], "kty": "oct"}; "alg": "A256GCM",
"ext": true,
"k": "wBHHU4Es8IqMCnH03Jxhc1ZTQN7hzo6GkCNnbA_0kjI",
"key_ops": ["encrypt", "decrypt"],
"kty": "oct"
};
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// Custom test substorage definition // Custom test substorage definition
...@@ -362,8 +368,7 @@ ...@@ -362,8 +368,7 @@
test("return substorage getattachment if not data url", function () { test("return substorage getattachment if not data url", function () {
var id = "/", var id = "/",
attachment = "stringattachment", attachment = "stringattachment",
blob = new Blob(['foo'], blob = new Blob(['foo'], {type: 'application/x-jio-aes-gcm-encryption'});
{type: 'application/x-jio-aes-gcm-encryption'});
Storage200.prototype.getAttachment = function (arg1, arg2) { Storage200.prototype.getAttachment = function (arg1, arg2) {
equal(arg1, id, "getAttachment 200 called"); equal(arg1, id, "getAttachment 200 called");
...@@ -390,10 +395,12 @@ ...@@ -390,10 +395,12 @@
var id = "/", var id = "/",
attachment = "stringattachment", attachment = "stringattachment",
value = "azertyuio\npàç_è-('é&", value = "azertyuio\npàç_è-('é&",
tocheck = "data:application/x-jio-aes-gcm-encryption;base64" + tocheck = "data:application/x-jio-aes-gcm-encryption" +
",+p/Ho+KgGHZC2zDLMbQQS2tXcsy0g+Ho41VZnlPEkXdmG9zm36c8iLCkv" + ";base64,2lHQ9xpJJ9qd81DxZEyd1LICtaV3XD+I2d5cp137L4NQC" +
"lanyWCN510NK4hj1EgWQ6WrLS5pCmA/yeAWh+HyfPkYKDRHVBl6+Hxd53I" + "vdkasBaFkPUE5XiY88g5z0oN9dcDASfChmvgqrkDExKS+zVglvVVs" +
"TmiWQ6Vix2jaIQg==", "CyECYorZ5fwgMCWAL5vUNCCaqhFVFyng==",
blob = jIO.util.dataURItoBlob(tocheck); blob = jIO.util.dataURItoBlob(tocheck);
...@@ -427,6 +434,7 @@ ...@@ -427,6 +434,7 @@
}); });
}); });
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// CryptStorage.putAttachment // CryptStorage.putAttachment
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
...@@ -445,7 +453,7 @@ ...@@ -445,7 +453,7 @@
return new RSVP.Queue() return new RSVP.Queue()
.push(function () { .push(function () {
return crypto.subtle.importKey("jwk", key, return crypto.subtle.importKey("jwk", key_generated_by_password,
"AES-GCM", false, ["decrypt"]); "AES-GCM", false, ["decrypt"]);
}) })
.push(function (res) { .push(function (res) {
...@@ -460,11 +468,10 @@ ...@@ -460,11 +468,10 @@
coded = coded.target.result; coded = coded.target.result;
iv = new Uint8Array(coded.slice(0, 12)); iv = new Uint8Array(coded.slice(0, 12));
return crypto.subtle.decrypt({name : "AES-GCM", iv : iv}, return crypto.subtle.decrypt({name: "AES-GCM", iv: iv},
decryptKey, coded.slice(12)); decryptKey, coded.slice(12));
}) })
.push(function (arr) { .push(function (arr) {
arr = String.fromCharCode.apply(null, new Uint8Array(arr)); arr = String.fromCharCode.apply(null, new Uint8Array(arr));
equal( equal(
arr, arr,
...@@ -492,6 +499,7 @@ ...@@ -492,6 +499,7 @@
return decodeAES(arg3); return decodeAES(arg3);
}; };
stop(); stop();
expect(7); expect(7);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment