diff --git a/shrine-webclient/src/main/html/js-i2b2/cells/plugins/MedCo/workersController.js b/shrine-webclient/src/main/html/js-i2b2/cells/plugins/MedCo/workersController.js index 46d1a37d5..bc5f3936e 100644 --- a/shrine-webclient/src/main/html/js-i2b2/cells/plugins/MedCo/workersController.js +++ b/shrine-webclient/src/main/html/js-i2b2/cells/plugins/MedCo/workersController.js @@ -1,258 +1,265 @@ // a structure to handle the workers (background threads) i2b2.MedCo.ctrlr.background = { // from outside you should just call the following // - init() // - toBeEncrypted() x N (call this functions whenever you have something to encrypt) // - encryptionDone() (check if encryption has finished) // - toBeDecrypted() x N (call this functions whenever you have something to decrypt) // - decryptionDone() (check if decryption has finished) // here is stored the list of workers of with the workload can be spread workers: [], // ------------------ Encryption ------------------ // K and S are the parameters that will be used to encrypt K: null, S: null, // aggregate public key of the cothority (used to decrypt) AggregateKey: null, //"GKrufk6bsuMwegxcPqr7B1aFWKit1szJlugZ01HkSPA=", // This is a map from plaintexts to the corresponding ciphertext to avoid encrypting again. // It is updated every time a worker encrypts new integer. encryptionCache: {}, // A list of plaintexts that have to be encrypted (notice that some of these my have the corresponding ciphertext // in the encryptionCache). When a plaintext is encrypted it is saved in encryptionCache and removed from toEncrypt toEncrypt: [], // store the number of remaining plaintext to encrypt ("toEncrypt.length" may be different from "remainingToEncrypt" // since when a plaintext is sent to a worker it is removed from toEncrypt but "remainingToEncrypt" is not // decreased yet) remainingToEncrypt: 0, // ------------------ Decryption ------------------ // secret key of the user. Used to decrypt the patient counts received. SecKey: null, // This is a map from ciphertexts to the corresponding plaintexts to avoid decrypting again. // It is updated every time a worker decrypts new cipher. decryptionCache: {}, // A list of ciphertexts that have to be decrypted (notice that some of these my have the corresponding plaintext // in the decryptionCache). When a ciphertext is decrypted it is saved in decryptionCache and removed from toDecrypt. toDecrypt: [], // store the number of remaining plaintext to decrypt ("toDecrypt.length" may be different from "remainingToDecrypt" // since when a plaintext is sent to a worker it is removed from toDecrypt but "remainingToDecrypt" is not // decreased yet) remainingToDecrypt: 0, // initialize workers and the ephemeral key for the encryption init: function(num_workers, secKey){ //aggregateKey this.SecKey = secKey; this.AggregateKey = null; + this.K = null; + this.S = null; + this.initWorkers(num_workers); - this.initEncrypt(); + var group_toml = "http://" + location.host + "/shrine-client/js-i2b2/cells/plugins/MedCo/group.toml"; fetch(group_toml).then(function(response) { response.text().then(function(text) { this.AggregateKey = AggKeys(text); + this.initEncrypt() }); - }); + }) }, // init initializes the workers (to create them one time and then just send them the strings to // encrypt/decrypt). Initialise a worker may take some seconds because each one of them ahas to import // the crypto javascript initWorkers: function(num_workers){ var that = i2b2.MedCo.ctrlr.background; // first kill the existing workers, if any for (var i=0; i < this.workers.length; i++) { this.workers[i].terminate(); } this.workers = []; // todo be careful the importPath to points to the correct location of the file // var importPath = document.location["href"]; // // you have to tell the workers where the crypto library is so that they can import it // importPath = importPath.substr(0, importPath.lastIndexOf('/')) + "/scripts/cryptolib.js"; var importPath = "http://" + location.host + "/shrine-client/js-i2b2/cells/plugins/MedCo/cryptolib.js"; // todo check is correct for (var i=0; i < num_workers; i++){ // start a new worker var w = new Worker(URL.createObjectURL(new Blob(["("+worker_code.toString()+")()"], {type: 'text/javascript'}))); // register to each worker an encrypt function that will look for something to encrypt and send it // to the worker w.encrypt = function (){ // look for something to encrypt while (that.toEncrypt.length > 0){ if (that.toEncrypt[0] in that.encryptionCache){ // it has been encrypted in the past that.toEncrypt.shift(); that.remainingToEncrypt -= 1; document.getElementById("MedCo_encrypting").innerHTML = that.remainingToEncrypt; } else{ + if (that.K==null || that.S==null){ + alert("initEncrypt failed in computing K and S.") + } w.postMessage({"enc": {"plain": that.toEncrypt.shift(), "K": that.K, "S": that.S}}); break } } }; w.decrypt = function(){ // look for something to decrypt while (that.toDecrypt.length){ if (that.toDecrypt[0] in that.decryptionCache){ // it has been decrypted in the past that.toDecrypt.shift(); that.remainingToDecrypt -= 1; } else{ w.postMessage({"dec": {"cipher": that.toDecrypt.shift(), "secKey": that.SecKey}}); break } } }; // register event for the result w.onmessage = function(e) { var task = Object.keys(e.data)[0]; switch (task) { case "enc": that.encryptionCache[e.data[task]["plain"]] = e.data[task]["cipher"]; that.remainingToEncrypt -= 1; document.getElementById("MedCo_encrypting").innerHTML = that.remainingToEncrypt; // start another encryption w.encrypt(); break; case "dec": that.decryptionCache[e.data[task]["cipher"]] = e.data[task]["plain"]; that.remainingToDecrypt -= 1; // start another decryption w.decrypt(); break; default: alert("Message received from the worker not recognized:" + JSON.stringify(e.data)) } }; w.postMessage({"init": {"import": importPath}}); this.workers.push(w) } }, initEncrypt: function(){ // var context = i2b2.MedCo.ctrlr.background; // init the ephemeral key of the light encryption if (this.AggregateKey == null){ - alert("aggregate not loaded") + alert("Aggregate not computed.") } [this.K, this.S] = LightEncryptStr_init(this.AggregateKey); this.encryptionCache = {}; // old ciphertexts are not valid anymore }, // receives a list of plaintexts to be encrypted and append it to the toEncrypt list toBeEncrypted: function(toEncrypt){ // var context = i2b2.MedCo.ctrlr.background; // other plaintext to be encrypted are appended to the list this.toEncrypt = this.toEncrypt.concat(toEncrypt); this.remainingToEncrypt += toEncrypt.length; // sends to each worker something to encrypt for(var i=0; i < this.workers.length; i++){ this.workers[i].encrypt(); // function registered to the worker (see init) } document.getElementById("MedCo_encrypting").innerHTML = this.remainingToEncrypt; }, // receives a list of ciphertexts to be decrypted and append it to the toDecrypt list toBeDecrypted: function(toDecrypt){ // var context = i2b2.MedCo.ctrlr.background; // other plaintext to be encrypted are appended to the list this.toDecrypt = this.toDecrypt.concat(toDecrypt); this.remainingToDecrypt += toDecrypt.length; // sends to each worker something to encrypt for(var i=0; i < this.workers.length; i++){ this.workers[i].decrypt(); // function registered to the worker (see init) } }, // checks if there are other plaintexts to be encrypted encryptionDone: function() { return i2b2.MedCo.ctrlr.background.remainingToEncrypt == 0; }, // checks if there are other chipertexts to be decrypted decryptionDone: function() { return i2b2.MedCo.ctrlr.background.remainingToDecrypt == 0; } }; // all worker code goes here (this function is just used to create workers in the i2b2.MedCo.ctrlr.background.init) function worker_code() { // a worker responds to 3 different messages, init, encrypt, decrypt onmessage = function(e) { // e.data is in one of the following forms -> response: // - {"init": {"import": "http://.../cryptolib.js"}} -> no response // - {"enc": {"plain": "123", "K": "...", "S": "..."}} -> {"enc": {"plain": "", "cipher": "<ciphertext>"}} // - todo {"dec": { "cipher": "...", secKey: "..." }} -> {"dec": {"plain": "<plaintext>", "cipher": "<ciphertext>"}} var task = Object.keys(e.data)[0]; switch (task){ case "init": self.importScripts(e.data[task]["import"]); // e.g. self.importScripts("http://localhost:63342/i-b-webclient/tools/gopherjsCrypto/tests/scripts/useme.js"); // console.log("worker says: imported cryptolib"); break; case "enc": var plain = e.data[task]["plain"]; var K = e.data[task]["K"]; var S = e.data[task]["S"]; var cipher = LightEncryptStr(plain, K, S); // console.log("worker says: plain: " + plain + "; cipher: ", cipher); // {"enc": {"plain": "<plaintext>", "cipher": "<ciphertext>"}} var response = {}; response[task] = {}; response[task]["plain"] = plain; response[task]["cipher"] = cipher; postMessage(response); break; case "dec": var cipher = e.data[task]["cipher"]; var secKey = e.data[task]["secKey"]; var plain = parseInt(DecryptStr(cipher, secKey)); // console.log("worker says: cipher: " + cipher + "; plain: ", plain); // {"dec": {"plain": "<plaintext>", "cipher": "<ciphertext>"}} var response = {}; response[task] = {}; response[task]["plain"] = plain; response[task]["cipher"] = cipher; postMessage(response); break; default: alert("worker says: message not recognized: " + JSON.stringify(e.data)) } }; } \ No newline at end of file