Commit 549b3ed8 authored by amrani's avatar amrani

adding messaging API to cryptostorage

parent 0b8bd163
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
/* /*
* Copyright 2015, Nexedi SA * Copyright 2015, Nexedi SA
* Released under the LGPL license. * Released under the LGPL license.
* http://www.gnu.org/licenses/lgpl.html * hcrypto, Uint8Array, ArrayBuffer*/
*/
/*jslint nomen: true*/ /*jslint nomen: true*/
/*global jIO, RSVP, DOMException, Blob, crypto, Uint8Array, ArrayBuffer*/ /*jslint maxlen: 160 */
/*global jIO, RSVP, DOMException, Blob, crypto, Uint8Array, ArrayBuffer, CryptoKey*/
(function (jIO, RSVP, DOMException, Blob, crypto, Uint8Array, ArrayBuffer) { (function (jIO, RSVP, DOMException, Blob, crypto, Uint8Array, ArrayBuffer,
CryptoKey) {
"use strict"; "use strict";
/* /*
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 strong user key : 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 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 (json_key) { .push(function () {
var jio = jIO.createJIO({ var jio = jIO.createJIO({
type: "crypt", type: "crypt"
key: "Hwm0jPAmQC",
sub_storage: {storage_definition} sub_storage: {storage_definition}
}); });
}); });
...@@ -38,9 +34,6 @@ ...@@ -38,9 +34,6 @@
* @constructor * @constructor
*/ */
var MIME_TYPE = "application/x-jio-aes-gcm-encryption";
//used unibabel.js for converting function
function hexToBuffer(hex) { function hexToBuffer(hex) {
// Xxxx use Uint8Array or ArrayBuffer or DataView // Xxxx use Uint8Array or ArrayBuffer or DataView
...@@ -63,254 +56,182 @@ ...@@ -63,254 +56,182 @@
return arr; 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) {
if (spec.key !== undefined && spec.key !== null
&& typeof spec.key === 'string') {
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 () {
var passphraseKey = utf8ToBuffer(that._key);
return window.crypto.subtle.importKey(
"raw",
passphraseKey,
{
name: "PBKDF2"
},
false,
["deriveBits", "deriveKey"
]
);
})
.push(function (key) {
return window.crypto.subtle.deriveKey({ return window.crypto.subtle.deriveKey({
"name": "PBKDF2", "name": "PBKDF2",
"salt": hexToBuffer("6f0904840608c09ae18c131542fbd1bd"), "salt": hexToBuffer("6f0904840608c09ae18c131542fbd1bd"),
"iterations": 1000, "iterations": 1000,
//we can add iteration number but slow CPU will freez //we can add iteration number but slow CPU will freez
"hash": "SHA-256" "hash": "SHA-256"
}, key, { }, that.importedkey, {
"name": "AES-GCM", "name": "AES-GCM",
"length": 256 "length": 256
}, false, ["encrypt", "decrypt"]); }, false, ["encrypt", "decrypt"]);
}) })
.push(function (res) { .push(function (res) {
that._key = res; that.key = res;
that._userkey = false;
return; return;
}); });
} }
function addevent(that) {
try {
window.addEventListener('message', function (event) {
if (event.origin !== window.origin) {return; }
if (!(event.data instanceof CryptoKey)) {return; }
if (event.data.algorithm.name !== "PBKDF2" &&
event.data.usages[0] !== "deriveKey") {return; }
that.importedkey = event.data;
convertKey(that);
}, false);
} catch (error) {
throw new jIO.util.jIOError(error + "failed to build event lisener please reload the page", 803);
}
}
var MIME_TYPE = "application/x-jio-aes-gcm-encryption";
function CryptStorage(spec) {
addevent(this);
this.importedkey = "";
this.key = "";
this._sub_storage = jIO.createJIO(spec.sub_storage);
}
CryptStorage.prototype.get = function () { CryptStorage.prototype.get = function () {
return this._sub_storage.get.apply(this._sub_storage, return this._sub_storage.get.apply(this._sub_storage,
arguments); arguments);
}; };
CryptStorage.prototype.post = function () { CryptStorage.prototype.post = function () {
return this._sub_storage.post.apply(this._sub_storage, return this._sub_storage.post.apply(this._sub_storage,
arguments); arguments);
}; };
CryptStorage.prototype.put = function () { CryptStorage.prototype.put = function () {
return this._sub_storage.put.apply(this._sub_storage, return this._sub_storage.put.apply(this._sub_storage,
arguments); arguments);
}; };
CryptStorage.prototype.remove = function () { CryptStorage.prototype.remove = function () {
return this._sub_storage.remove.apply(this._sub_storage, return this._sub_storage.remove.apply(this._sub_storage,
arguments); arguments);
}; };
CryptStorage.prototype.hasCapacity = function () { CryptStorage.prototype.hasCapacity = function () {
return this._sub_storage.hasCapacity.apply(this._sub_storage, return this._sub_storage.hasCapacity.apply(this._sub_storage,
arguments); arguments);
}; };
CryptStorage.prototype.buildQuery = function () { CryptStorage.prototype.buildQuery = function () {
return this._sub_storage.buildQuery.apply(this._sub_storage, return this._sub_storage.buildQuery.apply(this._sub_storage,
arguments); arguments);
}; };
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 () { return jIO.util.readBlobAsDataURL(blob);
if (that._userkey === true) { })
return convertKey(that); .push(function (dataURL) {
//string->arraybuffer
var strLen = dataURL.target.result.length,
buf = new ArrayBuffer(strLen),
bufView = new Uint8Array(buf),
i;
dataURL = dataURL.target.result;
for (i = 0; i < strLen; i += 1) {
bufView[i] = dataURL.charCodeAt(i);
}
return crypto.subtle.encrypt({
name: "AES-GCM",
iv: initializaton_vector
},
that.key, buf);
})
.push(function (coded) {
var blob = new Blob([initializaton_vector, coded], {
type: MIME_TYPE
});
return that._sub_storage.putAttachment(id, name, blob);
})
.push(undefined, function (error) {
if (error instanceof DOMException) {
if (error.name === "OperationError") {
throw new jIO.util.jIOError(error.name + " : failed to decrypt due to mismatching password", 801);
} }
return; if (error.name === "InvalidAccessError") {
}) throw new jIO.util.jIOError(error.name + " : invalid encryption algorithm, or invalid key for specified encryption algorithm", 801);
.push(function () {
return jIO.util.readBlobAsDataURL(blob);
})
.push(function (dataURL) {
//string->arraybuffer
var strLen = dataURL.target.result.length,
buf = new ArrayBuffer(strLen),
bufView = new Uint8Array(buf),
i;
dataURL = dataURL.target.result;
for (i = 0; i < strLen; i += 1) {
bufView[i] = dataURL.charCodeAt(i);
} }
return crypto.subtle.encrypt({ } else if (error instanceof TypeError) {
name: "AES-GCM", throw new jIO.util.jIOError(error + " : password is not type CRYPTOKEY ", 801);
iv: initializaton_vector }
}, });
that._key, buf);
})
.push(function (coded) {
var blob = new Blob([initializaton_vector, coded], {
type: MIME_TYPE
});
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 () { return new RSVP.Queue()
if (that._userkey === true) { .push(function () {
return convertKey(that); return jIO.util.readBlobAsArrayBuffer(blob);
} })
return; .push(function (coded) {
}) var initializaton_vector;
.push(function () { coded = coded.target.result;
return jIO.util.readBlobAsArrayBuffer(blob); initializaton_vector = new Uint8Array(coded.slice(0, 12));
}) return new RSVP.Queue()
.push(function (coded) { .push(function () {
var initializaton_vector; return crypto.subtle.decrypt({
coded = coded.target.result; name: "AES-GCM",
initializaton_vector = new Uint8Array(coded.slice(0, 12)); iv: initializaton_vector
return new RSVP.Queue() },
.push(function () { that.key, coded.slice(12));
return crypto.subtle.decrypt({ })
name: "AES-GCM", .push(function (arr) {
iv: initializaton_vector arr = String.fromCharCode.apply(null, new Uint8Array(arr));
}, return jIO.util.dataURItoBlob(arr);
that._key, coded.slice(12)); })
}) .push(undefined, function (error) {
.push(function (arr) { if (error instanceof DOMException) {
//arraybuffer->string if (error.name === "OperationError") {
arr = String.fromCharCode.apply(null, new Uint8Array(arr)); throw new jIO.util.jIOError(error.name + " : failed to decrypt due to mismatching password", 801);
return jIO.util.dataURItoBlob(arr); }
}) if (error.name === "InvalidAccessError") {
.push(undefined, function (error) { throw new jIO.util.jIOError(error.name + " : invalid encryption algorithm, or invalid key for specified encryption algorithm", 801);
if (error instanceof DOMException) { }
return blob; return blob;
} }
throw error; if (error instanceof TypeError) {
}); throw new jIO.util.jIOError(error + " : password is not type CRYPTOKEY ", 801);
}); }
}); });
} });
//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 () {
return this._sub_storage.removeAttachment.apply(this._sub_storage, return this._sub_storage.removeAttachment.apply(this._sub_storage,
arguments); arguments);
}; };
CryptStorage.prototype.allAttachments = function () { CryptStorage.prototype.allAttachments = function () {
return this._sub_storage.allAttachments.apply(this._sub_storage, return this._sub_storage.allAttachments.apply(this._sub_storage,
arguments); arguments);
}; };
jIO.addStorage('crypt', CryptStorage); jIO.addStorage('crypt', CryptStorage);
}(jIO, RSVP, DOMException, Blob, crypto, Uint8Array, ArrayBuffer)); }(jIO, RSVP, DOMException, Blob, crypto, Uint8Array, ArrayBuffer, CryptoKey));
\ No newline at end of file \ No newline at end of file
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