Connect
+
+
+ Sum Numbers under Asynchronous Shares MPC
+ Input Number (between 0 and 100) Sum
+
+
+
diff --git a/demos/asyncshare/client.js b/demos/asyncshare/client.js
new file mode 100644
index 000000000..abbca673c
--- /dev/null
+++ b/demos/asyncshare/client.js
@@ -0,0 +1,66 @@
+/**
+ * Do not modify this file unless you have to.
+ * This file has UI handlers.
+ */
+
+// eslint-disable-next-line no-unused-vars
+function connect() {
+ $('#connectButton').prop('disabled', true);
+ var computation_id = $('#computation_id').val();
+ var party_count = parseInt($('#count').val());
+ //var receiver_ratios =
+
+ if (isNaN(party_count)) {
+ $('#output').append("Party count must be a valid number!
");
+ $('#connectButton').prop('disabled', false);
+ } else {
+ var options = { party_count: party_count};
+ options.onError = function (error) {
+ $('#output').append(""+error+'
');
+ };
+ options.onConnect = function () {
+ $('#button').attr('disabled', false); $('#output').append('All parties Connected!
');
+ };
+
+ var hostname = window.location.hostname.trim();
+ var port = window.location.port;
+ if (port == null || port === '') {
+ port = '80';
+ }
+ if (!(hostname.startsWith('http://') || hostname.startsWith('https://'))) {
+ hostname = 'http://' + hostname;
+ }
+ if (hostname.endsWith('/')) {
+ hostname = hostname.substring(0, hostname.length-1);
+ }
+ if (hostname.indexOf(':') > -1 && hostname.lastIndexOf(':') > hostname.indexOf(':')) {
+ hostname = hostname.substring(0, hostname.lastIndexOf(':'));
+ }
+
+ hostname = hostname + ':' + port;
+ // eslint-disable-next-line no-undef
+ mpc.connect(hostname, computation_id, options);
+ }
+}
+
+// eslint-disable-next-line no-unused-vars
+function submit() {
+ var input = parseInt($('#number').val());
+
+ if (isNaN(input)) {
+ $('#output').append("Input a valid number!
");
+ } else if (100 < input || input < 0 || input !== Math.floor(input)) {
+ $('#output').append("Input a WHOLE number between 0 and 100!
");
+ } else {
+ $('#button').attr('disabled', true);
+ $('#output').append('Starting...
');
+ // eslint-disable-next-line no-undef
+ var promise = mpc.compute(input);
+ promise.then(handleResult);
+ }
+}
+
+function handleResult(result) {
+ $('#output').append('Result is: ' + result + '
');
+ $('#button').attr('disabled', false);
+}
diff --git a/demos/asyncshare/mpc.js b/demos/asyncshare/mpc.js
new file mode 100644
index 000000000..24714ca0b
--- /dev/null
+++ b/demos/asyncshare/mpc.js
@@ -0,0 +1,43 @@
+(function (exports, node) {
+ var saved_instance;
+
+ /**
+ * Connect to the server and initialize the jiff instance
+ */
+ exports.connect = function (hostname, computation_id, options) {
+ var opt = Object.assign({}, options);
+ opt.warn = false;
+ if (node) {
+ // eslint-disable-next-line no-undef
+ jiff = require('../../lib/jiff-client');
+ jiff_asynchronousshare = require('../../lib/ext/jiff-client-asynchronousshare');
+ }
+
+ opt.autoConnect = false;
+ // eslint-disable-next-line no-undef
+ saved_instance = jiff.make_jiff(hostname, computation_id, opt);
+ saved_instance.apply_extension(jiff_asynchronousshare, opt);
+ saved_instance.connect();
+ return saved_instance;
+ };
+
+ /**
+ * The MPC computation
+ */
+ exports.compute = async function (input, jiff_instance) {
+ if (jiff_instance == null) {
+ jiff_instance = saved_instance;
+ }
+
+ // The MPC implementation should go *HERE*
+ var shares = jiff_instance.share(input, 3, null, null, null, null, {1: 1, 2: 2});
+
+ var sum = shares[1];
+ for (var i = 2; i <= jiff_instance.party_count; i++) {
+ sum = sum.sadd(shares[i]);
+ }
+
+ // Return a promise to the final output(s)
+ return jiff_instance.open(sum, [1,2]);
+ };
+}((typeof exports === 'undefined' ? this.mpc = {} : exports), typeof exports !== 'undefined'));
diff --git a/demos/asyncshare/party.js b/demos/asyncshare/party.js
new file mode 100644
index 000000000..a1d8f7189
--- /dev/null
+++ b/demos/asyncshare/party.js
@@ -0,0 +1,43 @@
+/**
+ * Do not change this unless you have to.
+ * This code parses input command line arguments,
+ * and calls the appropriate initialization and MPC protocol from ./mpc.js
+ */
+
+console.log('Command line arguments: [ [ []]]]');
+
+var mpc = require('./mpc');
+
+// Read Command line arguments
+var input = parseInt(process.argv[2], 10);
+
+var party_count = process.argv[3];
+if (party_count == null) {
+ party_count = 2;
+} else {
+ party_count = parseInt(party_count);
+}
+
+var computation_id = process.argv[4];
+if (computation_id == null) {
+ computation_id = 'test';
+}
+
+var party_id = process.argv[5];
+if (party_id != null) {
+ party_id = parseInt(party_id, 10);
+}
+
+// JIFF options
+var options = {party_count: party_count, party_id: party_id};
+options.onConnect = function (jiff_instance) {
+ var promise = mpc.compute(input);
+
+ promise.then(function (v) {
+ console.log(v);
+ jiff_instance.disconnect(true);
+ });
+};
+
+// Connect
+mpc.connect('http://localhost:8080', computation_id, options);
diff --git a/demos/asyncshare/server.js b/demos/asyncshare/server.js
new file mode 100644
index 000000000..5a05428b8
--- /dev/null
+++ b/demos/asyncshare/server.js
@@ -0,0 +1,21 @@
+var express = require('express');
+var app = express();
+var http = require('http').Server(app);
+var base_instance = require('../../lib/jiff-server').make_jiff(http, { logs:true });
+var jiffAsyncShareServer = require('../../lib/ext/jiff-server-asyncshare');
+base_instance.apply_extension(jiffAsyncShareServer);
+
+//Serve static files
+//Configure App
+app.use('/demos', express.static('demos'));
+app.use('/lib', express.static('lib'));
+app.use('/lib/ext', express.static('lib/ext'));
+
+// Serve static files.
+http.listen(8080, function () {
+ console.log('listening on *:8080');
+});
+
+console.log('Direct your browser to *:8080/demos/asyncshare/client.html.');
+console.log('To run a node.js based party: node demos/asyncshare/party ');
+console.log();
diff --git a/demos/asyncshare/test.js b/demos/asyncshare/test.js
new file mode 100644
index 000000000..5fef4b897
--- /dev/null
+++ b/demos/asyncshare/test.js
@@ -0,0 +1,131 @@
+// Chai
+var assert = require('chai').assert;
+
+var mpc = require('./mpc.js');
+
+// Generic Testing Parameters
+var showProgress = false;
+var party_count = 3;
+var parallelismDegree = 100; // Max number of test cases running in parallel
+var n = 1000;
+var Zp = null;
+
+// Parameters specific to this demo
+var maxValue = 1000;
+
+
+/**
+ * CHANGE THIS: Generate inputs for your tests
+ * Should return an object with this format:
+ * {
+ * 'party_id': [ 'test1_input', 'test2_input', ...]
+ * }
+ */
+function generateInputs(party_count) {
+ var inputs = {};
+ var i;
+
+ for (i = 0; i < party_count; i++) {
+ inputs[i+1] = [];
+ }
+
+ for (i = 0; i < party_count; i++) {
+ for (var j = 0; j < n; j++) {
+ inputs[i+1].push(Math.floor((Math.random() * maxValue)));
+ }
+ }
+ return inputs;
+}
+
+/**
+ * CHANGE THIS: Compute the expected results not in MPC
+ * @param {object} inputs - same format as generateInputs output.
+ * Should return a single array with the expected result for every test in order
+ * [ 'test1_output', 'test2_output', ... ]
+ */
+function computeResults(inputs) {
+ var results = [];
+
+ for (var j = 0; j < n; j++) {
+ var sum = 0;
+ for (var i = 1; i <= party_count; i++) {
+ sum += inputs[i][j];
+ }
+ results.push(sum);
+ }
+ return results;
+}
+
+/**
+ * Do not change unless you have to.
+ */
+// eslint-disable-next-line no-undef
+describe('Test', function () {
+ this.timeout(0); // Remove timeout
+
+ // eslint-disable-next-line no-undef
+ it('Exhaustive', function (done) {
+ var count = 0;
+
+ var inputs = generateInputs(party_count);
+ var realResults = computeResults(inputs);
+
+ var onConnect = function (jiff_instance) {
+ var partyInputs = inputs[jiff_instance.id];
+
+ var testResults = [];
+ (function one_test_case(j) {
+ if (jiff_instance.id === 1 && showProgress) {
+ console.log('\tStart ', j > partyInputs.length ? partyInputs.length : j, '/', partyInputs.length);
+ }
+
+ if (j < partyInputs.length) {
+ var promises = [];
+ for (var t = 0; t < parallelismDegree && (j + t) < partyInputs.length; t++) {
+ promises.push(mpc.compute(partyInputs[j + t], jiff_instance));
+ }
+
+ Promise.all(promises).then(function (parallelResults) {
+ for (var t = 0; t < parallelResults.length; t++) {
+ testResults.push(parallelResults[t]);
+ }
+
+ one_test_case(j + parallelismDegree);
+ });
+
+ return;
+ }
+
+ // If we reached here, it means we are done
+ count++;
+ for (var i = 0; i < testResults.length; i++) {
+ // construct debugging message
+ var ithInputs = inputs[1][i] + '';
+ for (var p = 2; p <= party_count; p++) {
+ ithInputs += ',' + inputs[p][i];
+ }
+ var msg = 'Party: ' + jiff_instance.id + '. inputs: [' + ithInputs + ']';
+
+ // assert results are accurate
+ try {
+ assert.deepEqual(testResults[i].toString(), realResults[i].toString(), msg);
+ } catch (assertionError) {
+ done(assertionError);
+ done = function () {
+ };
+ }
+ }
+
+ jiff_instance.disconnect(true);
+ if (count === party_count) {
+ done();
+ }
+ })(0);
+ };
+
+ var options = { party_count: party_count, onError: console.log, onConnect: onConnect, Zp: Zp };
+ for (var i = 0; i < party_count; i++) {
+ mpc.connect('http://localhost:8080', 'mocha-test', options);
+ }
+ });
+});
\ No newline at end of file
diff --git a/lib/ext/jiff-client-asynchronousshare.js b/lib/ext/jiff-client-asynchronousshare.js
new file mode 100644
index 000000000..68d46a39d
--- /dev/null
+++ b/lib/ext/jiff-client-asynchronousshare.js
@@ -0,0 +1,1066 @@
+/**
+ * @namespace jiff_fixedpoint
+ * @version 1.0
+ */
+(function (exports, node) {
+ /**
+ * The name of this extension: 'multipleshares'
+ * @type {string}
+ * @memberOf jiff_asynchronousshare
+ */
+ exports.name = 'asyncshare';
+
+ $ = require('jquery-deferred');
+
+ // export functions
+ function jiff_compute_shares(jiff, secret, parties_list, threshold, Zp, parties_ratios) {
+ var shares = {}; // Keeps the shares
+ var i;
+ // Each player's random polynomial f must have
+ // degree threshold - 1, so that threshold many points are needed
+ // to interpolate/reconstruct.
+ var t = threshold - 1;
+ var polynomial = Array(t + 1); // stores the coefficients
+
+ // Each players's random polynomial f must be constructed
+ // such that f(0) = secret
+ polynomial[0] = secret;
+
+ // Compute the random polynomial f's coefficients
+ for (i = 1; i <= t; i++) {
+ polynomial[i] = jiff.helpers.random(Zp);
+ }
+ if (parties_ratios == null) {
+ // Compute each players share such that share[i] = f(i)
+ for (i = 0; i < parties_list.length; i++) {
+ var p_id = parties_list[i];
+ shares[p_id] = polynomial[0];
+ var power = jiff.helpers.get_party_number(p_id, 0, parties_list.length);
+
+ for (var j = 1; j < polynomial.length; j++) {
+ var tmp = jiff.helpers.mod((polynomial[j] * power), Zp);
+ shares[p_id] = jiff.helpers.mod((shares[p_id] + tmp), Zp);
+ power = jiff.helpers.mod(power * jiff.helpers.get_party_number(p_id, 0, parties_list.length), Zp);
+ }
+ }
+ return shares;
+ }
+
+ // Compute each players share such that share[i] = f(i)
+ for (i = 0; i < parties_list.length; i++) {
+ var p_id = parties_list[i];
+ var p_ratio = p_id in parties_ratios ? parties_ratios[p_id] : 1;
+ shares[p_id] = [];
+ for (var share_num = 0; share_num < p_ratio; share_num++) {
+ shares[p_id][share_num] = polynomial[0];
+ var power = jiff.helpers.get_party_number(p_id, share_num, parties_list.length);
+
+ for (var j = 1; j < polynomial.length; j++) {
+ var tmp = jiff.helpers.mod((polynomial[j] * power), Zp);
+ shares[p_id][share_num] = jiff.helpers.mod((shares[p_id][share_num] + tmp), Zp);
+ power = jiff.helpers.mod(power * jiff.helpers.get_party_number(p_id, share_num, parties_list.length), Zp);
+ }
+ }
+ }
+ return shares;
+ }
+
+ function jiff_lagrange(jiff, shares) {
+ var lagrange_coeff = []; // will contain shares.length many elements.
+ // Compute the Lagrange coefficients at 0.
+ for (var i = 0; i < shares.length; i++) {
+ for (var share_num = 0; share_num < shares[i].value.length; share_num++) {
+ var pi = jiff.helpers.get_party_number(shares[i].sender_id, share_num, jiff.party_count);
+ lagrange_coeff[pi] = 1;
+
+ for (var j = 0; j < shares.length; j++) {
+ for (var n = 0; n < shares[j].value.length; n++) {
+ var pj = jiff.helpers.get_party_number(shares[j].sender_id, n, jiff.party_count);
+ if (pj !== pi) {
+ var inv = jiff.helpers.extended_gcd(pi - pj, shares[i].Zp)[0];
+ lagrange_coeff[pi] = jiff.helpers.mod(lagrange_coeff[pi] * (0 - pj), shares[i].Zp) * inv;
+ lagrange_coeff[pi] = jiff.helpers.mod(lagrange_coeff[pi], shares[i].Zp);
+ }
+ }
+ }
+ }
+ }
+
+ // Reconstruct the secret via Lagrange interpolation
+ var recons_secret = 0;
+ for (var p = 0; p < shares.length; p++) {
+ for (share_num = 0; share_num < shares[p].value.length; share_num++) {
+ var party = jiff.helpers.get_party_number(shares[p].sender_id, share_num, jiff.party_count);
+ var tmp = jiff.helpers.mod((shares[p].value[share_num] * lagrange_coeff[party]), shares[p].Zp);
+ recons_secret = jiff.helpers.mod((recons_secret + tmp), shares[p].Zp);
+ }
+ }
+ return recons_secret;
+ }
+
+ function createMultipleSharesSecretShare(jiff, share, share_helpers) {
+ // Keep a copy of the previous implementation of changed primitives
+ share.legacy = {};
+ var internals = ['cadd', 'sadd', 'csub', 'ssub',
+ 'cmult', 'smult', 'smult_bgw',
+ 'cdivfac', 'cdiv', 'sdiv', 'smod',
+ 'cxor_bit', 'sxor_bit', 'cor_bit', 'sor_bit', 'not',
+ 'slt', 'slteq', 'sgt', 'sgteq', 'seq', 'sneq',
+ 'clt', 'clteq', 'cgt', 'cgteq', 'ceq', 'cneq',
+ 'lt_halfprime', 'if_else' ];
+ for (var i = 0; i < internals.length; i++) {
+ var key = internals[i];
+ share.legacy[key] = share[key];
+ }
+
+ function max(x, y) {
+ return x > y ? x : y;
+ }
+
+ share.refresh = function (op_id) {
+ return share.isadd(share.jiff.server_generate_and_share({number: 0}, share.holders, share.threshold, share.Zp, op_id, share.ratios)[0]);
+ }
+
+ /** Secret Share Clone
+ * @method clone
+ * @memberof SecretShare
+ * @instance
+ * @param {boolean} ready - whether the value of the share is ready or deferred.
+ * @param {promise} promise - a promise to the value of the share.
+ * @param {number} value - the value of the share (null if not ready).
+ * @return {SecretShare} - clone of share with changed value/promise
+ */
+ share.clone = function (ready, promise, value, threshold, id) {
+ return share.jiff.secret_share(share.jiff, ready, promise, value, share.holders, threshold, share.Zp, id, share.ratios);
+ };
+
+ share.cadd = function (o) {
+ if (!(share.isConstant(o))) {
+ throw new Error('parameter should be a number (+)');
+ }
+
+ var ready_add = function () {
+ var result = [];
+ for (var i = 0; i < share.value.length; i++) {
+ result.push(jiff.helpers.mod(share_helpers['+'](share.value[i], o), share.Zp));
+ }
+ return result;
+ };
+
+ if (share.ready) {
+ return share.clone(true, null, ready_add());
+ }
+
+ var promise = share.promise.then(function () {
+ return ready_add();
+ }, share.error);
+ return share.clone(false, promise, null);
+ };
+
+ share.sadd = function (o) {
+ share.jiff.helpers.are_shares_compatible(share, o);
+
+ // add the two share when ready
+ var ready_add = function () {
+ var result = [];
+ for (var i = 0; i < share.value.length; i++) {
+ result.push(share.jiff.helpers.mod(share_helpers['+'](share.value[i], o.value[i]), share.Zp));
+ }
+ return result;
+ };
+
+ if (share.ready && o.ready) {
+ return share.clone(true, null, ready_add(), max(share.threshold, o.threshold));
+ }
+
+ var promise = share.pick_promise(o).then( function () {
+ return ready_add();
+ }, share.error);
+ return share.clone(false, promise, null, max(share.threshold, o.threshold));
+ };
+
+ share.csub = function (o) {
+ if (!(share.isConstant(o))) {
+ throw new Error('parameter should be a number (-)');
+ }
+
+ var ready_sub = function () {
+ var result = [];
+ for (var i = 0; i < share.value.length; i++) {
+ result.push(jiff.helpers.mod(share_helpers['-'](share.value[i], o), share.Zp));
+ }
+ return result;
+ };
+
+ if (share.ready) {
+ return share.clone(true, null, ready_sub());
+ }
+
+ var promise = share.promise.then(function () {
+ return ready_sub();
+ }, share.error);
+ return share.clone(false, promise, null);
+ };
+
+ share.ssub = function (o) {
+ share.jiff.helpers.are_shares_compatible(share, o);
+
+ var ready_sub = function () {
+ var result = [];
+ for (var i = 0; i < share.value.length; i++) {
+ result.push(share.jiff.helpers.mod(share_helpers['-'](share.value[i], o.value[i]), share.Zp));
+ }
+ return result;
+ };
+
+ if (share.ready && o.ready) {
+ return share.clone(true, null, ready_sub(), max(share.threshold, o.threshold));
+ }
+
+ // promise to execute ready_add when both are ready
+ var promise = share.pick_promise(o).then(function () {
+ return ready_sub();
+ }, share.error);
+ return share.clone(false, promise, null, max(share.threshold, o.threshold));
+ };
+
+ share.cmult = function (o) {
+ if (!(share.isConstant(o))) {
+ throw new Error('parameter should be a number (*)');
+ }
+
+ var ready_mult = function () {
+ var result = [];
+ for (var i = 0; i < share.value.length; i++) {
+ result.push(jiff.helpers.mod(share_helpers['*'](share.value[i], o), share.Zp));
+ }
+ return result;
+ };
+
+ if (share.ready) {
+ return share.clone(true, null, ready_mult());
+ }
+
+ var promise = share.promise.then(function () {
+ return ready_mult();
+ }, share.error);
+ return share.clone(false, promise, null);
+ };
+
+ share.smult = function (o, op_id) {
+ share.jiff.helpers.are_shares_compatible(share, o);
+
+ if (op_id == null) {
+ op_id = share.jiff.counters.gen_op_id('*', share.holders);
+ }
+
+ var final_deferred = $.Deferred();
+ var final_promise = final_deferred.promise();
+ var result = share.clone(false, final_promise, null, max(share.threshold, o.threshold), 'share'+op_id);
+
+ // Get shares of triplets.
+ var triplet = jiff.triplet(share.holders, max(share.threshold, o.threshold), share.Zp, op_id + ':triplet', share.ratios);
+
+ var a = triplet[0];
+ var b = triplet[1];
+ var c = triplet[2];
+
+ // d = s - a. e = o - b.
+ var d = share.isadd(a.icmult(-1));
+ var e = o.isadd(b.icmult(-1));
+
+ // Open d and e.
+ // The only communication cost.
+ var e_promise = share.jiff.internal_open(e, e.holders, op_id + ':open1');
+ var d_promise = share.jiff.internal_open(d, d.holders, op_id + ':open2');
+
+ Promise.all([e_promise, d_promise]).then(function (arr) {
+ var e_open = arr[0];
+ var d_open = arr[1];
+
+ // result_share = d_open * e_open + d_open * b_share + e_open * a_share + c.
+ var t1 = share.jiff.helpers.mod(share_helpers['*'](d_open, e_open), share.Zp);
+ var t2 = b.icmult(d_open);
+ var t3 = a.icmult(e_open);
+
+ // All this happens locally.
+ var final_result = t2.icadd(t1);
+ final_result = final_result.isadd(t3);
+ final_result = final_result.isadd(c);
+
+ final_result.wThen(final_deferred.resolve);
+ });
+
+ return result;
+ };
+
+ share.smult_bgw = function(o, op_id) {
+ share.jiff.helpers.are_shares_compatible(share, o);
+ if ((share.threshold - 1) + (o.threshold - 1) > share.holders.length - 1) {
+ throw new Error('threshold too high for BGW (*)');
+ }
+
+ var final_deferred = $.Deferred();
+ var final_promise = final_deferred.promise();
+
+ var ready_mult_bgw = function () {
+ var result = [];
+ var promises = [];
+
+ for (var i = 0; i < share.value.length; i++) {
+ var share_tmp = share.clone(true, null, share.value[i]);
+ var o_tmp = o.clone(true, null, o.value[i]);
+ var multshare = share_tmp.legacy.smult_bgw(o_tmp, op_id);
+ promises.push(multshare.promise);
+
+ multshare.promise.then((function (i, multshare) {
+ result[i] = multshare.value;
+ }).bind(null, i, multshare));
+ }
+
+ Promise.all(promises).then(function () {
+ final_deferred.resolve(result);
+ }, share.error);
+ };
+
+ if (share.ready && o.ready) {
+ ready_mult_bgw();
+ return share.clone(false, final_promise, null, max(share.threshold, o.threshold));
+ }
+
+ // promise to execute ready_add when both are ready
+ share.pick_promise(o).then( function () {
+ ready_mult_bgw();
+ }, share.error);
+ return share.clone(false, final_promise, null, max(share.threshold, o.threshold));
+ };
+
+ share.cdiv = function (o, op_id) {
+ if (!(share.isConstant(o))) {
+ throw new Error('parameter should be a number (/)');
+ }
+
+ if (share_helpers['<='](o, 0)) {
+ throw new Error('divisor must be > 0 (cst/): ' + o);
+ }
+
+ if (share_helpers['<='](share.Zp, o)) {
+ throw new Error('divisor must be < share.Zp (' + share.Zp + ') in (cst/): ' + o);
+ }
+
+ if (op_id == null) {
+ op_id = share.jiff.counters.gen_op_id('c/', share.holders);
+ }
+
+ // Allocate share for result to which the answer will be resolved once available
+ var final_deferred = $.Deferred();
+ var final_promise = final_deferred.promise();
+ var result = share.clone(false, final_promise, null, max(share.threshold, o.threshold), 'share'+op_id);
+
+ var ZpOVERc = share_helpers['floor/'](share.Zp, o);
+
+ // add uniform noise to self so we can open
+ var nOVERc = share.jiff.server_generate_and_share({max: ZpOVERc}, share.holders, share.threshold, share.Zp, op_id + ':nOVERc', share.ratios)[0];
+ var nMODc = share.jiff.server_generate_and_share({max: o}, share.holders, share.threshold, share.Zp, op_id + ':nMODc', share.ratios)[0];
+ var noise = nOVERc.icmult(o).isadd(nMODc);
+
+ var noisyX = share.isadd(noise);
+
+ share.jiff.internal_open(noisyX, noisyX.holders, op_id + ':open').then(function (noisyX) {
+ var wrapped = share.icgt(noisyX, op_id + ':wrap_cgt'); // 1 => x + noise wrapped around Zp, 0 otherwise
+
+ // if we did not wrap
+ var noWrapDiv = share_helpers['floor/'](noisyX, o);
+ var unCorrectedQuotient = nOVERc.icmult(-1).icadd(noWrapDiv).icsub(1);
+ var verify = share.issub(unCorrectedQuotient.icmult(o));
+ var isNotCorrect = verify.icgteq(o, op_id + ':cor1');
+ var noWrapAnswer = unCorrectedQuotient.isadd(isNotCorrect); // if incorrect => isNotCorrect = 1 => quotient = unCorrectedQuotient - 1
+
+ // if we wrapped
+ var wrapDiv = share_helpers['floor/'](share_helpers['+'](noisyX, share.Zp), o);
+ var unCorrectedQuotient2 = nOVERc.icmult(-1).icadd(wrapDiv).icsub(1);
+ var verify2 = share.issub(unCorrectedQuotient2.icmult(o));
+ var isNotCorrect2 = verify2.icgteq(o, op_id + ':cor2');
+ var wrapAnswer = unCorrectedQuotient2.isadd(isNotCorrect2); // if incorrect => isNotCorrect = 1 => quotient = unCorrectedQuotient - 1
+
+ var answer = noWrapAnswer.isadd(wrapped.ismult(wrapAnswer.issub(noWrapAnswer), op_id + ':smult'));
+ answer.wThen(final_deferred.resolve);
+ });
+
+ // special case, if result is zero, sometimes we will get to -1 due to how correction happens above (.csub(1) and then compare)
+ var zeroIt = share.iclt(o, op_id + ':zero_check').inot();
+ return result.ismult(zeroIt, op_id + ':zero_it');
+ };
+
+ share.cdivfac = function (o) {
+ if (!(share.isConstant(o))) {
+ throw new Error('Parameter should be a number (cdivfac)');
+ }
+ var inv = share.jiff.helpers.extended_gcd(o, share.Zp)[0];
+
+ var ready_cdivfac = function () {
+ var result = [];
+ for (var i = 0; i < share.value.length; i++) {
+ result.push(share.jiff.helpers.mod(share_helpers['*'](share.value[i], inv), share.Zp))
+ }
+ return result;
+ }
+
+ if (share.ready) {
+ // If share is ready.
+ return share.clone(true, null, ready_cdivfac());
+ }
+
+ var promise = share.promise.then(function () {
+ return ready_cdivfac();
+ }, share.error);
+ return share.clone(false, promise, undefined);
+ };
+
+ share.sdiv = function (o, l, op_id) {
+ // check that shares are compatible to compute
+ share.jiff.helpers.are_shares_compatible(share, o);
+ var final_deferred = $.Deferred();
+ var final_promise = final_deferred.promise();
+
+ var ready_div = function () {
+ var result = [];
+ var promises = [];
+
+ for (var i = 0; i < share.value.length; i++) {
+ var share_tmp = share.clone(true, null, share.value[i]);
+ var o_tmp = o.clone(true, null, o.value[i]);
+ var divshare = share_tmp.legacy.sdiv(o_tmp, l, op_id + ':' + i);
+ promises.push(divshare.promise);
+
+ divshare.promise.then((function (i, divshare) {
+ console.log('divshare', i, divshare.value);
+ result[i] = divshare.value;
+ }).bind(null, i, divshare));
+ }
+
+ Promise.all(promises).then(function () {
+ final_deferred.resolve(result);
+ });
+ };
+
+ if (share.ready && o.ready) {
+ ready_div();
+ var clone = share.clone(false, final_promise, null);
+ clone.threshold = max(share.threshold, o.threshold);
+ return clone;
+ }
+
+ // promise to execute ready_add when both are ready
+ share.pick_promise(o).then( function () {
+ ready_div();
+ }, share.error);
+ var clone = share.clone(false, final_promise, null);
+ clone.threshold = max(share.threshold, o.threshold);
+ return clone;
+ };
+
+ share.smod = function (o, l, op_id) {
+ // check that shares are compatible to compute
+ share.jiff.helpers.are_shares_compatible(share, o);
+ var result, modshare;
+
+ var ready_mod = function () {
+ result = [];
+ var share_val = share.value;
+ var o_val = o.value;
+ for (var i = 0; i < share.value.length; i++) {
+ share.value = share_val[i];
+ o.value = o_val[i];
+ modshare = share.legacy.smod(o, l, op_id);
+ if (modshare.ready) {
+ result.push(modshare.value);
+ } else {
+ modshare.promise.then(function () {
+ result.push(modshare.value);
+ });
+ }
+ }
+ share.value = share_val;
+ o.value = o_val;
+ return result;
+ };
+
+ if (share.ready && o.ready) {
+ if (typeof (share.value) === 'number') {
+ return share.legacy.smod(o, l, op_id);
+ } else {
+ return share.jiff.secret_share(share.jiff, true, null, ready_mod(), share.holders, max(share.threshold, o.threshold), share.Zp);
+ }
+ }
+
+ // promise to execute ready_add when both are ready
+ var promise = share.pick_promise(o).then( function () {
+ if (typeof (share.value) === 'number') {
+ modshare = share.legacy.smod(o, l, op_id);
+ if (modshare.ready) {
+ return modshare.value;
+ }
+ result = modshare.promise.then(function () {
+ return modshare.value;
+ });
+ return result;
+ } else {
+ return ready_mod();
+ }
+ }, share.error);
+ return share.jiff.secret_share(share.jiff, false, promise, undefined, share.holders, max(share.threshold, o.threshold), share.Zp);
+ };
+
+ share.lt_halfprime = function (op_id) {
+ if (op_id == null) {
+ op_id = share.jiff.counters.gen_op_id('lt_hp', share.holders);
+ }
+
+ var final_deferred = $.Deferred();
+ var final_promise = final_deferred.promise();
+ var result = share.clone(false, final_promise, null, share.threshold, 'share'+op_id);
+
+ // if 2*self is even, then self is less than half prime, otherwise self is greater or equal to half prime
+ var share2 = share.icmult(2);
+
+ // To check if share is even, we will use pre-shared bits as some form of a bit mask
+ var bitLength = share_helpers['floor'](share.jiff.helpers.bLog(share2.Zp, 2)); // TODO: this leaks one bit, fix it for mod 2^n
+ var bits = share.jiff.server_generate_and_share({
+ bit: true,
+ count: bitLength,
+ }, share2.holders, share2.threshold, share2.Zp, op_id + ':number:bits', share.ratios);
+ bits[bitLength] = share.jiff.server_generate_and_share({number: 0}, share2.holders, share2.threshold, share2.Zp, op_id + ':number:' + bitLength, share.ratios)[0]; // remove this line when fixing TODO
+
+ // bit composition: r = (rl ... r1 r0)_10
+ var r = share.jiff.protocols.bits.bit_composition(bits);
+
+ // open share + noise, and utilize opened value with shared bit representation of noise to check the least significant digit of share.
+ share2.jiff.internal_open(r.isadd(share2), share2.holders, op_id + ':open').then(function (result) {
+ var wrapped = share.jiff.protocols.bits.cgt(bits, result, op_id + ':bits.cgt');
+ var isOdd = share.jiff.helpers.mod(result, 2);
+ isOdd = bits[0].icxor_bit(isOdd);
+ isOdd = isOdd.isxor_bit(wrapped, op_id + ':sxor_bit');
+
+ var answer = isOdd.inot();
+ answer.wThen(final_deferred.resolve);
+ });
+
+ return result;
+ };
+
+ share.isadd = share.sadd;
+ share.issub = share.ssub;
+ share.ismult = share.smult;
+ share.icadd = share.cadd;
+ share.icsub = share.csub;
+ share.icmult = share.cmult;
+ share.ilt_halfprime = share.lt_halfprime;
+
+ return share;
+ }
+
+
+ function make_jiff(base_instance, options) {
+ var jiff = base_instance;
+
+ // Parse options
+ if (options == null) {
+ options = {};
+ }
+
+ /**
+ * Check that two shares are compatible
+ * @method shares_compatible
+ * @memberof jiff-instance.helpers
+ * @instance
+ * @param {SecretShare} s1 - first share
+ * @param {SecretShare} s2 - second share
+ * @return {boolean} true if shares are compatible, false otherwise.
+ */
+ var old_shares_compatible = jiff.helpers.are_shares_compatible;
+ jiff.helpers.are_shares_compatible = function (s1, s2){
+ old_shares_compatible(s1, s2);
+ if (!jiff.helpers.ratios_equals(s1.ratios, s2.ratios)) {
+ throw new Error('share must have the same ratios');
+ }
+ };
+
+ /**
+ * Check that two shares' ratios are equal.
+ * @method ratios_equals
+ * @memberof jiff-instance.helpers
+ * @instance
+ * @param {Array} ratios1 - the first share's ratios.
+ * @param {Array} ratios2 - the second share's ratios.
+ * @return {boolean} true if ratios1 is equal to ratios2, false otherwise.
+ */
+ jiff.helpers.ratios_equals = function (ratios1, ratios2) {
+ for (var p_id in ratios1) {
+ if (ratios1[p_id] !== ratios2[p_id]) {
+ return false;
+ }
+ }
+ return true;
+ };
+
+ jiff.helpers.get_party_number = function (party_id, share_num, party_count) {
+ if (share_num == null){
+ share_num = 0;
+ }
+ if (typeof(party_id) === 'number') {
+ return party_id + (share_num * (jiff.party_count+1));
+ }
+ if (party_id.startsWith('s')) {
+ return parseInt(party_id.substring(1), 10) * (party_count+1); // n+1 reserved for server
+ }
+ return parseInt(party_id, 10) + (share_num * (party_count+1));
+ };
+
+ var old_secret_share = jiff.secret_share;
+ jiff.secret_share = function (jiff, ready, promise, value, holders, threshold, Zp, id) {
+ var share = old_secret_share(jiff, ready, promise, value, holders, threshold, Zp, id);
+ share.ratios = arguments[8].length > 0 ? arguments[8][0] : arguments[8];
+ return share;
+ };
+
+ function receive_share(jiff, json_msg) {
+ // Decrypt share
+ for (var i = 0; i < json_msg['share'].length; i++) {
+ json_msg['share'][i] = jiff.hooks.decryptSign(jiff, json_msg['share'][i], jiff.secret_key, jiff.keymap[json_msg['party_id']]);
+ }
+ json_msg = jiff.execute_array_hooks('afterOperation', [jiff, 'share', json_msg], 2);
+ var sender_id = json_msg['party_id'];
+ var op_id = json_msg['op_id'];
+ var share = json_msg['share'];
+
+ // Call hook
+ share = jiff.execute_array_hooks('receiveShare', [jiff, sender_id, share], 2);
+
+ // check if a deferred is set up (maybe the share was received early)
+ if (jiff.deferreds[op_id] == null) {
+ jiff.deferreds[op_id] = {};
+ }
+ if (jiff.deferreds[op_id][sender_id] == null) {
+ // Share is received before deferred was setup, store it.
+ jiff.deferreds[op_id][sender_id] = $.Deferred();
+ }
+
+ // Deferred is already setup, resolve it.
+ jiff.deferreds[op_id][sender_id].resolve(share);
+ }
+
+ function receive_open(jiff, json_msg) {
+ // Decrypt share
+ if (json_msg['party_id'] !== jiff.id) {
+ for (var i = 0; i < json_msg['share'].length; i++) {
+ json_msg['share'][i] = jiff.hooks.decryptSign(jiff, json_msg['share'][i], jiff.secret_key, jiff.keymap[json_msg['party_id']]);
+ }
+ json_msg = jiff.execute_array_hooks('afterOperation', [jiff, 'open', json_msg], 2);
+ }
+
+ var sender_id = json_msg['party_id'];
+ var op_id = json_msg['op_id'];
+ var share = json_msg['share'];
+ var Zp = json_msg['Zp'];
+
+ // call hook
+ share = jiff.execute_array_hooks('receiveOpen', [jiff, sender_id, share, Zp], 2);
+
+ // Resolve the deferred.
+ if (jiff.deferreds[op_id] == null) {
+ jiff.deferreds[op_id] = {};
+ }
+ if (jiff.deferreds[op_id][sender_id] == null) {
+ jiff.deferreds[op_id][sender_id] = $.Deferred();
+ }
+
+ jiff.deferreds[op_id][sender_id].resolve({value: share, sender_id: sender_id, Zp: Zp});
+ }
+
+ function jiff_broadcast(jiff, share, parties, op_ids) {
+ for (var index = 0; index < parties.length; index++) {
+ var i = parties[index]; // Party id
+ if (i === jiff.id) {
+ receive_open(jiff, {party_id: i, share: share.value, op_id: op_ids[i], Zp: share.Zp});
+ continue;
+ }
+
+ // encrypt, sign and send
+ var msg = {party_id: i, share: share.value.slice(), op_id: op_ids[i], Zp: share.Zp};
+ msg = jiff.execute_array_hooks('beforeOperation', [jiff, 'open', msg], 2);
+ for (var j = 0; j < msg['share'].length; j++) {
+ msg['share'][j] = jiff.hooks.encryptSign(jiff, msg['share'][j].toString(), jiff.keymap[msg['party_id']], jiff.secret_key);
+ }
+ jiff.socket.safe_emit('open', JSON.stringify(msg));
+ }
+ }
+
+ jiff_open = function(jiff, share, parties, op_ids) {
+ var i;
+
+ if (!(share.jiff === jiff)) {
+ throw 'share does not belong to given instance';
+ }
+
+ // Default values
+ if (parties == null || parties === []) {
+ parties = [];
+ for (i = 1; i <= jiff.party_count; i++) {
+ parties.push(i);
+ }
+ } else {
+ parties.sort();
+ }
+
+ // If not a receiver nor holder, do nothing
+ if (share.holders.indexOf(jiff.id) === -1 && parties.indexOf(jiff.id) === -1) {
+ return null;
+ }
+
+ // Compute operation ids (one for each party that will receive a result
+ if (op_ids == null) {
+ op_ids = {};
+ }
+
+ if (typeof (op_ids) === 'string' || typeof (op_ids) === 'number') {
+ var tmp = {};
+ for (i = 0; i < parties.length; i++) {
+ tmp[parties[i]] = op_ids;
+ }
+ op_ids = tmp;
+ } else {
+ var holders_label = share.holders.join(',');
+ for (i = 0; i < parties.length; i++) {
+ if (op_ids[parties[i]] == null) {
+ op_ids[parties[i]] = jiff.counters.gen_open_id(parties[i], holders_label);
+ }
+ }
+ }
+
+ // Party is a holder
+ if (share.holders.indexOf(jiff.id) > -1) {
+ // Call hook
+ share = jiff.execute_array_hooks('beforeOpen', [jiff, share, parties], 1);
+
+ // refresh/reshare, so that the original share remains secret, instead
+ // a new share is sent/open without changing the actual value.
+ share = share.refresh('refresh:' + op_ids[parties[0]]);
+
+ // The given share has been computed, share it to all parties
+ if (share.ready) {
+ jiff_broadcast(jiff, share, parties, op_ids);
+ } else {
+ jiff.counters.pending_opens++;
+ // Share is not ready, setup sharing as a callback to its promise
+ share.promise.then(function () {
+ jiff.counters.pending_opens--;
+ jiff_broadcast(jiff, share, parties, op_ids);
+ }, share.error);
+ }
+ }
+
+ // Party is a receiver
+ if (parties.indexOf(jiff.id) > -1) {
+ var shares = []; // this will store received shares
+ var numberofshares = 0;
+ var final_deferred = $.Deferred(); // will be resolved when the final value is reconstructed
+ var final_promise = final_deferred.promise();
+ for (i = 0; i < share.holders.length; i++) {
+ var p_id = share.holders[i];
+
+ // Setup a deferred for receiving a share from party p_id
+ if (jiff.deferreds[op_ids[jiff.id]] == null) {
+ jiff.deferreds[op_ids[jiff.id]] = {};
+ }
+ if (jiff.deferreds[op_ids[jiff.id]][p_id] == null) {
+ jiff.deferreds[op_ids[jiff.id]][p_id] = $.Deferred();
+ }
+
+ // Clean up deferred when fulfilled
+ var promise = jiff.deferreds[op_ids[jiff.id]][p_id].promise();
+
+ // destroy deferred when done
+ (function (promise, p_id) { // p_id is modified in a for loop, must do this to avoid scoping issues.
+ promise.then(function (received_share) {
+ jiff.deferreds[op_ids[jiff.id]][p_id] = null;
+ shares.push(received_share);
+ numberofshares += received_share['value'].length;
+
+ // Too few shares, nothing to do.
+ if (numberofshares < share.threshold) {
+ return;
+ }
+
+ // Enough shares to reconstruct.
+ // If did not already reconstruct, do it.
+ if (final_deferred != null) {
+ var recons_secret = jiff_lagrange(jiff, shares);
+ recons_secret = jiff.execute_array_hooks('afterReconstructShare', [jiff, recons_secret], 1);
+ final_deferred.resolve(recons_secret);
+ final_deferred = null;
+ }
+
+ // If all shares were received, clean up.
+ if (shares.length === share.holders.length) {
+ shares = null;
+ jiff.deferreds[op_ids[jiff.id]] = null;
+ }
+ });
+ })(promise, p_id);
+ }
+ return final_promise;
+ }
+ };
+
+
+ jiff.open = function (share, parties, op_ids) {
+ return jiff.internal_open(share, parties, op_ids);
+ };
+
+ jiff.internal_open = function (share, parties, op_ids) {
+ return jiff_open(jiff, share, parties, op_ids);
+ };
+
+ jiff_share = function (jiff, secret, threshold, receivers_list, senders_list, Zp, share_id, receivers_ratios) {
+ var i, j, p_id, p_ratio;
+
+ // defaults
+ if (Zp == null) {
+ Zp = jiff.Zp;
+ }
+ if (receivers_list == null) {
+ receivers_list = [];
+ for (i = 1; i <= jiff.party_count; i++) {
+ receivers_list.push(i);
+ }
+ }
+ if (senders_list == null) {
+ senders_list = [];
+ for (i = 1; i <= jiff.party_count; i++) {
+ senders_list.push(i);
+ }
+ }
+ if (threshold == null) {
+ threshold = receivers_list.length;
+ }
+ if (threshold < 0) {
+ threshold = 2;
+ }
+ if (threshold > receivers_list.length) {
+ threshold = receivers_list.length;
+ }
+ if (receivers_ratios == null) {
+ receivers_ratios = {1: 2, 2: 1}
+ }
+
+ // if party is uninvolved in the share, do nothing
+ if (receivers_list.indexOf(jiff.id) === -1 && senders_list.indexOf(jiff.id) === -1) {
+ return {};
+ }
+
+ // compute operation id
+ receivers_list.sort(); // sort to get the same order
+ senders_list.sort();
+ if (share_id == null) {
+ share_id = jiff.counters.gen_share_id(receivers_list, senders_list);
+ }
+
+ // stage sending of shares
+ if (senders_list.indexOf(jiff.id) > -1) {
+ // Call hook
+ secret = jiff.execute_array_hooks('beforeShare', [jiff, secret, threshold, receivers_list, senders_list, Zp], 1);
+
+ // compute shares
+ var shares = jiff_compute_shares(jiff, secret, receivers_list, threshold, Zp, receivers_ratios);
+
+ // Call hook
+ shares = jiff.execute_array_hooks('afterComputeShare', [jiff, shares, threshold, receivers_list, senders_list, Zp], 1);
+
+ // send shares
+ for (i = 0; i < receivers_list.length; i++) {
+ p_id = receivers_list[i];
+ if (p_id === jiff.id) {
+ continue;
+ }
+
+ // send encrypted and signed shares_id[p_id] to party p_id
+ var msg = {party_id: p_id, share: shares[p_id], op_id: share_id, p_ratio: p_ratio};
+ msg = jiff.execute_array_hooks('beforeOperation', [jiff, 'share', msg], 2);
+ p_ratio = (p_id in receivers_ratios) ? receivers_ratios[p_id] : 1;
+ for (j = 0; j < p_ratio; j++) {
+ msg['share'][j] = jiff.hooks.encryptSign(jiff, msg['share'][j].toString(10), jiff.keymap[msg['party_id']], jiff.secret_key);
+ }
+ jiff.socket.safe_emit('share', JSON.stringify(msg));
+ }
+ }
+
+ // stage receiving of shares
+ var result = {};
+ if (receivers_list.indexOf(jiff.id) > -1) {
+ // setup a map of deferred for every received share
+ if (jiff.deferreds[share_id] == null) {
+ jiff.deferreds[share_id] = {};
+ }
+
+ for (i = 0; i < senders_list.length; i++) {
+ p_id = senders_list[i];
+ p_ratio = (p_id in receivers_ratios) ? receivers_ratios[p_ratio] : 1;
+ if (p_id === jiff.id) {
+ var my_share = jiff.execute_array_hooks('receiveShare', [jiff, p_id, shares[p_id]], 2);
+ result[p_id] = jiff.secret_share(jiff, true, null, my_share, receivers_list, threshold, Zp, null, receivers_ratios);
+ continue; // Keep party's own share
+ }
+
+ // check if a deferred is set up (maybe the message was previously received)
+ if (jiff.deferreds[share_id][p_id] == null) {
+ // not ready, setup a deferred
+ jiff.deferreds[share_id][p_id] = $.Deferred();
+ }
+
+ var promise = jiff.deferreds[share_id][p_id].promise();
+
+ // destroy deferred when done
+ (function (promise, p_id) { // p_id is modified in a for loop, must do this to avoid scoping issues.
+ promise.then(function () {
+ jiff.deferreds[share_id][p_id] = null;
+ });
+ })(promise, p_id);
+
+ // receive share_i[id] from party p_id
+ result[p_id] = jiff.secret_share(jiff, false, promise, undefined, receivers_list, threshold, Zp, share_id + ':' + p_id, receivers_ratios);
+ }
+ }
+ return result;
+ };
+
+ jiff.share = function (secret, threshold, receivers_list, senders_list, Zp, share_id, receivers_ratios) {
+ // type check to confirm the secret to be shared is a number
+ // for fixed-point extension it should allow non-ints
+ if (secret != null && (typeof(secret) !== 'number' || Math.floor(secret) !== secret || secret < 0)) {
+ throw new Error('secret must be a non-negative whole number');
+ }
+ if (secret != null && (secret >= (Zp == null ? jiff.Zp : Zp))) {
+ throw new Error('secret must fit inside Zp');
+ }
+ return jiff.internal_share(secret, threshold, receivers_list, senders_list, Zp, share_id, receivers_ratios);
+ };
+
+ /**
+ * Same as jiff-instance.share, but used by internal JIFF primitives/protocols.
+ */
+ jiff.internal_share = function (secret, threshold, receivers_list, senders_list, Zp, share_id, receivers_ratios) {
+ return jiff_share(jiff, secret, threshold, receivers_list, senders_list, Zp, share_id, receivers_ratios);
+ };
+
+
+ function jiff_triplet(jiff, receivers_list, threshold, Zp, triplet_id, receivers_ratios) {
+ if (Zp == null) {
+ Zp = jiff.Zp;
+ }
+ if (receivers_list == null) {
+ receivers_list = [];
+ for (var i = 1; i <= jiff.party_count; i++) {
+ receivers_list.push(i);
+ }
+ }
+ if (threshold == null) {
+ threshold = receivers_list.length;
+ }
+
+ // Get the id of the triplet needed.
+ if (triplet_id == null) {
+ triplet_id = jiff.counters.gen_triplet_id(receivers_list);
+ }
+
+ // Send a request to the server.
+ var msg = { triplet_id: triplet_id, receivers: receivers_list, threshold: threshold, Zp: Zp, ratios: receivers_ratios};
+ msg = jiff.execute_array_hooks('beforeOperation', [jiff, 'triplet', msg], 2);
+ msg = JSON.stringify(msg);
+
+ // Setup deferred to handle receiving the triplets later.
+ var a_deferred = $.Deferred();
+ var b_deferred = $.Deferred();
+ var c_deferred = $.Deferred();
+ jiff.deferreds[triplet_id] = {a: a_deferred, b: b_deferred, c: c_deferred};
+
+ // send a request to the server.
+ if (jiff.id === 's1') {
+ jiff.socket.safe_emit('triplet', msg);
+ } else {
+ var cipher = jiff.hooks.encryptSign(jiff, msg, jiff.keymap['s1'], jiff.secret_key);
+ jiff.socket.safe_emit('triplet', JSON.stringify(cipher));
+ }
+
+ var a_share = jiff.secret_share(jiff, false, a_deferred.promise(), undefined, receivers_list, threshold, Zp, triplet_id + ':a', receivers_ratios);
+ var b_share = jiff.secret_share(jiff, false, b_deferred.promise(), undefined, receivers_list, threshold, Zp, triplet_id + ':b', receivers_ratios);
+ var c_share = jiff.secret_share(jiff, false, c_deferred.promise(), undefined, receivers_list, threshold, Zp, triplet_id + ':c', receivers_ratios);
+
+ return [a_share, b_share, c_share];
+ }
+
+ jiff.triplet = function (receivers_list, threshold, Zp, triplet_id, receivers_ratios) {
+ return jiff_triplet(jiff, receivers_list, threshold, Zp, triplet_id, receivers_ratios);
+ };
+
+ // Change set up of receiving matching shares to call correct receive_share function
+ jiff.socket.off('share');
+ jiff.socket.on('share', function (msg, callback) {
+ callback(true); // send ack to server
+
+ // parse message
+ var json_msg = JSON.parse(msg);
+ var sender_id = json_msg['party_id'];
+
+ if (jiff.keymap[sender_id] != null) {
+ receive_share(jiff, json_msg);
+ } else {
+ if (jiff.messagesWaitingKeys[sender_id] == null) {
+ jiff.messagesWaitingKeys[sender_id] = [];
+ }
+ jiff.messagesWaitingKeys[sender_id].push({label: 'share', msg: json_msg});
+ }
+ });
+
+ // Change set up of opening shares to call correct receive_open function
+ jiff.socket.off('open');
+ jiff.socket.on('open', function (msg, callback) {
+ callback(true); // send ack to server
+
+ // parse message
+ var json_msg = JSON.parse(msg);
+ var sender_id = json_msg['party_id'];
+
+ if (jiff.keymap[sender_id] != null) {
+ receive_open(jiff, json_msg);
+ } else {
+ if (jiff.messagesWaitingKeys[sender_id] == null) {
+ jiff.messagesWaitingKeys[sender_id] = [];
+ }
+ jiff.messagesWaitingKeys[sender_id].push({ label: 'open', msg: json_msg });
+ }
+ });
+
+ /* HOOKS */
+ jiff.hooks.createSecretShare.push(createMultipleSharesSecretShare);
+ // parse content of share/open messages to be integers
+ jiff.hooks.afterOperation[0] = function (jiff, label, msg) {
+ if (label === 'share' || label === 'open') {
+ if (msg['share'].length >= 1) {
+ for (var i = 0; i < msg['share'].length; i++) {
+ msg['share'][i] = parseInt(msg['share'][i], 10);
+ }
+ } else {
+ msg['share'] = parseInt(msg['share'], 10);
+ }
+ }
+ return msg;
+ };
+
+ return jiff;
+ }
+
+ // Expose API
+ exports.make_jiff = make_jiff;
+ exports.sharing_schemes = {shamir_share: jiff_compute_shares, shamir_reconstruct: jiff_lagrange};
+}((typeof exports === 'undefined' ? this.jiff_asynchronousshare = {} : exports), typeof exports !== 'undefined'));
diff --git a/lib/ext/jiff-server-asyncshare.js b/lib/ext/jiff-server-asyncshare.js
new file mode 100644
index 000000000..c23efbe7a
--- /dev/null
+++ b/lib/ext/jiff-server-asyncshare.js
@@ -0,0 +1,114 @@
+global.crypto = require('crypto');
+var client_asyncshare = require('./jiff-client-asynchronousshare.js');
+
+exports.name = 'asyncshare';
+exports.make_jiff = function (base_instance, options) {
+ var jiff = base_instance;
+
+ initialize_hooks(jiff, options);
+
+ jiff.helpers.get_party_number = function (party_id, share_num, party_count) {
+ if (share_num == null) {
+ share_num = 0;
+ }
+ if (typeof(party_id) === 'number') {
+ return party_id + (share_num * (party_count+1));
+ }
+ if (party_id.startsWith('s')) {
+ return parseInt(party_id.substring(1), 10) * (party_count+1); // n+1 reserved for server
+ }
+ return parseInt(party_id, 10) + (share_num * (party_count+1));
+ };
+
+
+ // Helpers for creating triplets/numbers and sharing them.
+ jiff.request_triplet_share = function (msg, computation_id, from_id) {
+ // parse message
+ var triplet_id = msg.triplet_id;
+ var receivers = msg.receivers;
+ var threshold = msg.threshold;
+ var Zp = msg.Zp;
+ var ratios = msg.ratios;
+
+ jiff.hooks.log(jiff, 'triplet ' + triplet_id + ' from ' + computation_id + '-' + from_id + ':: ' + JSON.stringify(msg));
+
+ if (jiff.triplets_map[computation_id] == null) {
+ jiff.triplets_map[computation_id] = {};
+ }
+
+ var all_triplets = jiff.triplets_map[computation_id];
+
+ if (all_triplets[triplet_id] == null) { // Generate Triplet.
+ var triplet = jiff.hooks.generateTriplet(jiff, computation_id, Zp);
+ var a = triplet.a;
+ var b = triplet.b;
+ var c = triplet.c;
+
+ var a_shares = jiff.hooks.computeShares(jiff, a, receivers, threshold, Zp, ratios);
+ var b_shares = jiff.hooks.computeShares(jiff, b, receivers, threshold, Zp, ratios);
+ var c_shares = jiff.hooks.computeShares(jiff, c, receivers, threshold, Zp, ratios);
+
+ var triplet_shares = {};
+ for (var i = 0; i < receivers.length; i++) {
+ var pid = receivers[i];
+ a = a_shares[pid];
+ b = b_shares[pid];
+ c = c_shares[pid];
+
+ triplet_shares[pid] = {a: a, b: b, c: c};
+ }
+
+ all_triplets[triplet_id] = triplet_shares;
+ }
+
+ return {triplet: all_triplets[triplet_id][from_id], triplet_id: triplet_id};
+ };
+
+ jiff.request_number_share = function (msg, computation_id, from_id) {
+ // parse message/request
+ var base_number_id = msg.number_id;
+ var receivers = msg.receivers;
+ var threshold = msg.threshold;
+ var Zp = msg.Zp;
+ var count = msg.count;
+ var ratios = msg.__args[0];
+
+ if (count == null) {
+ count = 1;
+ }
+
+ jiff.hooks.log(jiff, 'number ' + base_number_id + ' from ' + computation_id + '-' + from_id + ':: ' + JSON.stringify(msg));
+
+ if (jiff.numbers_map[computation_id] == null) {
+ jiff.numbers_map[computation_id] = {};
+ }
+
+ var result = [];
+ var all_numbers = jiff.numbers_map[computation_id];
+ for (var i = 0; i < count; i++) {
+ var number_id = base_number_id + ':' + i;
+ if (all_numbers[number_id] == null) { // Generate shares for number.
+ var number = jiff.hooks.generateNumber(jiff, computation_id, msg);
+ all_numbers[number_id] = jiff.hooks.computeShares(jiff, number, receivers, threshold, Zp, ratios);
+ }
+ result.push({number_id: number_id, number: all_numbers[number_id][from_id]});
+ }
+
+ return result;
+ };
+
+ return jiff;
+};
+
+function initialize_hooks(jiff, options) {
+ if (options.hooks == null) {
+ options.hooks = {};
+ }
+
+ // sharing hooks
+ if (options.hooks.computeShares == null) {
+ jiff.hooks.computeShares = client_asyncshare.sharing_schemes.shamir_share;
+ } else {
+ jiff.hooks.computeShares = options.hooks.computeShares;
+ }
+}
diff --git a/lib/jiff-client.js b/lib/jiff-client.js
index be245b4e9..a456ace5e 100644
--- a/lib/jiff-client.js
+++ b/lib/jiff-client.js
@@ -821,6 +821,8 @@
* @return {SecretShare[]} - this party's share of the generated number.
*/
function jiff_server_share_number(jiff, options, receivers_list, threshold, Zp, number_id) {
+ var __args = arguments[6];
+
if (Zp == null) {
Zp = jiff.Zp;
}
@@ -846,7 +848,7 @@
options.count = 1;
}
- var msg = {number_id: number_id, receivers: receivers_list, threshold: threshold, Zp: Zp};
+ var msg = {number_id: number_id, receivers: receivers_list, threshold: threshold, Zp: Zp, __args: __args};
msg = Object.assign(msg, options);
msg = jiff.execute_array_hooks('beforeOperation', [jiff, 'number', msg], 2);
@@ -857,7 +859,7 @@
for (i = 0; i < options.count; i++) {
var deferred = new Deferred();
jiff.deferreds[number_id + ':' + i] = deferred;
- shares[i] = jiff.secret_share(jiff, false, deferred.promise, undefined, receivers_list, threshold, Zp, number_id + ':' + i);
+ shares[i] = jiff.secret_share(jiff, false, deferred.promise, undefined, receivers_list, threshold, Zp, number_id + ':' + i, __args[0]);
}
// Send a request to the server.
@@ -1622,6 +1624,19 @@
return self.jiff.open(self, parties, op_ids);
};
+ /** Secret Share Clone
+ * @method clone
+ * @memberof SecretShare
+ * @instance
+ * @param {boolean} ready - whether the value of the share is ready or deferred.
+ * @param {promise} promise - a promise to the value of the share.
+ * @param {number} value - the value of the share (null if not ready).
+ * @return {SecretShare} - clone of share with changed value/promise
+ */
+ self.clone = function (ready, promise, value, threshold, id) {
+ return self.jiff.secret_share(self.jiff, ready, promise, value, self.holders, threshold, self.Zp, id);
+ };
+
/**
* Generic Addition.
* Uses either the constant or secret version of this operator depending on type of paramter.
@@ -1876,13 +1891,13 @@
if (self.ready) {
// if share is ready
- return self.jiff.secret_share(self.jiff, true, null, self.jiff.helpers.mod(share_helpers['+'](self.value, cst), self.Zp), self.holders, self.threshold, self.Zp);
+ return self.clone(true, null, self.jiff.helpers.mod(share_helpers['+'](self.value, cst), self.Zp), self.threshold);
}
var promise = self.promise.then(function () {
return self.jiff.helpers.mod(share_helpers['+'](self.value, cst), self.Zp);
}, self.error);
- return self.jiff.secret_share(self.jiff, false, promise, undefined, self.holders, self.threshold, self.Zp);
+ return self.clone(false, promise, undefined, self.threshold);
};
/**
@@ -1900,13 +1915,13 @@
if (self.ready) {
// if share is ready
- return self.jiff.secret_share(self.jiff, true, null, self.jiff.helpers.mod(share_helpers['-'](self.value, cst), self.Zp), self.holders, self.threshold, self.Zp);
+ return self.clone(true, null, self.jiff.helpers.mod(share_helpers['-'](self.value, cst), self.Zp), self.threshold);
}
var promise = self.promise.then(function () {
return self.jiff.helpers.mod(share_helpers['-'](self.value, cst), self.Zp);
}, self.error);
- return self.jiff.secret_share(self.jiff, false, promise, undefined, self.holders, self.threshold, self.Zp);
+ return self.clone(false, promise, undefined, self.threshold);
};
/**
@@ -1924,13 +1939,13 @@
if (self.ready) {
// if share is ready
- return self.jiff.secret_share(self.jiff, true, null, self.jiff.helpers.mod(share_helpers['*'](self.value, cst), self.Zp), self.holders, self.threshold, self.Zp);
+ return self.clone(true, null, self.jiff.helpers.mod(share_helpers['*'](self.value, cst), self.Zp), self.threshold);
}
var promise = self.promise.then(function () {
return self.jiff.helpers.mod(share_helpers['*'](self.value, cst), self.Zp);
}, self.error);
- return self.jiff.secret_share(self.jiff, false, promise, undefined, self.holders, self.threshold, self.Zp);
+ return self.clone(false, promise, undefined, self.threshold);
};
/**
@@ -1950,13 +1965,13 @@
if (self.ready) {
// If share is ready.
- return self.jiff.secret_share(self.jiff, true, null, self.jiff.helpers.mod(share_helpers['*'](self.value, inv), self.Zp), self.holders, self.threshold, self.Zp);
+ return self.clone(true, null, self.jiff.helpers.mod(share_helpers['+'](self.value, inv), self.Zp), self.threshold);
}
var promise = self.promise.then(function () {
return self.jiff.helpers.mod(share_helpers['*'](self.value, inv), self.Zp);
}, self.error);
- return self.jiff.secret_share(self.jiff, false, promise, undefined, self.holders, self.threshold, self.Zp);
+ return self.clone(false, promise, undefined, self.threshold);
};
/**
@@ -1985,12 +2000,12 @@
if (self.ready && o.ready) {
// both shares are ready
- return self.jiff.secret_share(self.jiff, true, null, ready_add(), self.holders, max(self.threshold, o.threshold), self.Zp);
+ return self.clone(true, null, ready_add(), max(self.threshold, o.threshold));
}
// promise to execute ready_add when both are ready
var promise = self.pick_promise(o).then(ready_add, self.error);
- return self.jiff.secret_share(self.jiff, false, promise, undefined, self.holders, max(self.threshold, o.threshold), self.Zp);
+ return self.clone(false, promise, undefined, max(self.threshold, o.threshold));
};
/**
@@ -2019,12 +2034,12 @@
if (self.ready && o.ready) {
// both shares are ready
- return self.jiff.secret_share(self.jiff, true, null, ready_sub(), self.holders, max(self.threshold, o.threshold), self.Zp);
+ return self.clone(true, null, ready_sub(), max(self.threshold, o.threshold));
}
// promise to execute ready_add when both are ready
var promise = self.pick_promise(o).then(ready_sub, self.error);
- return self.jiff.secret_share(self.jiff, false, promise, undefined, self.holders, max(self.threshold, o.threshold), self.Zp);
+ return self.clone(false, promise, undefined, max(self.threshold, o.threshold));
};
/**
@@ -2057,7 +2072,7 @@
var final_deferred = new Deferred();
var final_promise = final_deferred.promise;
- var result = self.jiff.secret_share(self.jiff, false, final_promise, undefined, self.holders, max(self.threshold, o.threshold), self.Zp, 'share:' + op_id);
+ var result = self.clone(false, final_promise, undefined, max(self.threshold, o.threshold), 'share:' + op_id);
// Get shares of triplets.
var triplet = jiff.triplet(self.holders, max(self.threshold, o.threshold), self.Zp, op_id + ':triplet');
@@ -2128,7 +2143,7 @@
var final_deferred = new Deferred();
var final_promise = final_deferred.promise;
- var result = self.jiff.secret_share(self.jiff, false, final_promise, undefined, self.holders, max(self.threshold, o.threshold), self.Zp, 'share:' + op_id);
+ var result = self.clone(false, final_promise, undefined, max(self.threshold, o.threshold), 'share:' + op_id);
Promise.all([self.promise, o.promise]).then(
function () {
@@ -2160,7 +2175,7 @@
var final_deferred = new Deferred();
var final_promise = final_deferred.promise;
- var result = self.jiff.secret_share(self.jiff, false, final_promise, undefined, self.holders, threshold, self.Zp, 'share:' + op_id);
+ var result = self.clone(false, final_promise, undefined, threshold, 'share:' + op_id);
self.wThen(function () {
var intermediate_shares = self.jiff.internal_share(self.value, threshold, self.holders, self.holders, self.Zp, op_id);
@@ -2394,7 +2409,7 @@
var final_deferred = new Deferred();
var final_promise = final_deferred.promise;
- var result = self.jiff.secret_share(self.jiff, false, final_promise, undefined, self.holders, max(self.threshold, o.threshold), self.Zp, 'share:' + op_id);
+ var result = self.clone(false, final_promise, undefined, max(self.threshold, o.threshold), 'share:' + op_id);
var w = self.ilt_halfprime(op_id + ':halfprime:1');
Promise.all([w.promise]).then(function () {
@@ -2458,7 +2473,7 @@
var final_deferred = new Deferred();
var final_promise = final_deferred.promise;
- var result = self.jiff.secret_share(self.jiff, false, final_promise, undefined, self.holders, self.threshold, self.Zp, 'share:' + op_id);
+ var result = self.clone(false, final_promise, undefined, self.threshold, 'share:' + op_id);
var w = share_helpers['<'](cst, share_helpers['/'](self.Zp, 2)) ? 1 : 0;
var x = self.ilt_halfprime(op_id + ':halfprime:1');
@@ -2519,7 +2534,7 @@
var final_deferred = new Deferred();
var final_promise = final_deferred.promise;
- var result = self.jiff.secret_share(self.jiff, false, final_promise, undefined, self.holders, self.threshold, self.Zp, 'share:' + op_id);
+ var result = self.clone(false, final_promise, undefined, self.threshold, 'share:' + op_id);
var w = self.ilt_halfprime(op_id + ':halfprime:1');
Promise.all([w.promise]).then(function () {
@@ -2716,7 +2731,7 @@
// Allocate share for result to which the answer will be resolved once available
var final_deferred = new Deferred();
var final_promise = final_deferred.promise;
- var result = self.jiff.secret_share(self.jiff, false, final_promise, undefined, self.holders, self.threshold, self.Zp, 'share:' + op_id);
+ var result = self.clone(false, final_promise, undefined, self.threshold, 'share:' + op_id);
var ZpOVERc = share_helpers['floor/'](self.Zp, cst);
@@ -2813,7 +2828,7 @@
var final_deferred = new Deferred();
var final_promise = final_deferred.promise;
- var result = self.jiff.secret_share(self.jiff, false, final_promise, undefined, self.holders, self.threshold, self.Zp, 'share:' + op_id);
+ var result = self.clone(false, final_promise, undefined, self.threshold, 'share:' + op_id);
// if 2*self is even, then self is less than half prime, otherwise self is greater or equal to half prime
var share = self.icmult(2);
@@ -3904,6 +3919,27 @@
return s1.Zp === s2.Zp;
};
+ /**
+ * Check that two shares are compatible
+ * @method are_shares_compatible
+ * @memberof jiff-instance.helpers
+ * @instance
+ * @param {SecretShare} s1 - first share
+ * @param {SecretShare} s2 - second share
+ * @return {boolean} true if shares are compatible, false otherwise.
+ */
+ jiff.helpers.are_shares_compatible = function (s1, s2){
+ if (!(s2.jiff === s1.jiff)) {
+ throw new Error('shares do not belong to the same instance (+)');
+ }
+ if (!jiff.helpers.Zp_equals(s1, s2)) {
+ throw new Error('shares must belong to the same field (Zp)');
+ }
+ if (!jiff.helpers.array_equals(s1.holders, s2.holders)) {
+ throw new Error('shares must be held by the same parties');
+ }
+ };
+
/**
* Generate a random integer between 0 and max-1 [inclusive].
* Modify this to change the source of randomness and how it is generated.
@@ -4368,7 +4404,11 @@
* @returns {SecretShare[]} an array of secret shares of shares of zeros / random bits / random numbers / random non-zero numbers according to options.
*/
jiff.server_generate_and_share = function (options, receivers_list, threshold, Zp, number_id) {
- return jiff_server_share_number(jiff, options, receivers_list, threshold, Zp, number_id)
+ var __args = [];
+ for (var i = 5; i < arguments.length; i++) {
+ __args.push(arguments[i]);
+ }
+ return jiff_server_share_number(jiff, options, receivers_list, threshold, Zp, number_id, __args);
};
/**
@@ -4846,7 +4886,7 @@
// initialize result
var deferred = new Deferred();
- var result = jiff.secret_share(jiff, false, deferred.promise, undefined, bits[0].holders, bits[0].threshold, bits[0].Zp, 'share:' + op_id);
+ var result = bits[0].clone(false, deferred.promise, undefined, bits[0].threshold, 'share'+op_id);
// Subtract bits2 from bits1, only keeping track of borrow
var borrow = bits[0].inot().icmult(constant_bits[0]);
@@ -5367,7 +5407,7 @@
final_deferred.promise.then(resolve_many_secrets.bind(null, deferreds));
// get useless share of zero (just for padding)
- var zero = jiff.secret_share(jiff, true, null, 0, bits[0].holders, 1, bits[0].Zp, 'share:' + op_id + ':zero_share');
+ var zero = bits[0].clone(true, null, 0, 1, 'share' + op_id + 'zero_share');
var initial = [ zero ];
// special case
@@ -5511,7 +5551,7 @@
// initialize result
var deferred = new Deferred();
- var result = jiff.secret_share(jiff, false, deferred.promise, undefined, bits1[0].holders, Math.max(bits1[0].threshold, bits2[0].threshold), bits1[0].Zp, 'share:' + op_id);
+ var result = bits1[0].clone(false, deferred.promise, undefined, Math.max(bits1[0].threshold, bits2[0].threshold), 'share' + op_id);
// big or of bitwise XORs
var initial = bits1[0].isxor_bit(bits2[0], op_id + ':sxor_bit:initial');
@@ -5575,7 +5615,7 @@
// initialize result
var deferred = new Deferred();
- var result = jiff.secret_share(jiff, false, deferred.promise, undefined, bits1[0].holders, Math.max(bits1[0].threshold, bits2[0].threshold), bits1[0].Zp, 'share:' + op_id);
+ var result = bits1[0].clone(false, deferred.promise, undefined, Math.max(bits1[0].threshold, bits2[0].threshold), 'share' + op_id);
// Subtract bits2 from bits1, only keeping track of borrow
var borrow = bits1[0].inot().ismult(bits2[0], op_id + ':smult:initial');
@@ -5762,7 +5802,7 @@
if (constant.toString() === '1') {
return {
quotient: bits,
- remainder: [jiff.secret_share(jiff, true, null, 0, bits[0].holders, 1, bits[0].Zp, 'share:' + op_id + ':zero_share')]
+ remainder: [bits[0].clone(true, null, 0, 1, 'share' + op_id + ':zero_share')]
}
}
@@ -5874,7 +5914,7 @@
// add bit i to the head of remainder (least significant bit)
// turn into a secret without communication, just for typing
- var cbit_share = jiff.secret_share(jiff, true, null, constant_bits[i], bits[0].holders, 1, bits[0].Zp, 'share:' + op_id + ':' + iterationCounter);
+ var cbit_share = bits[0].clone(true, null, constant_bits[i], 1, 'share:' + op_id + ':' + iterationCounter);
_remainder.unshift(cbit_share);
// Get the next bit of the quotient
diff --git a/tests/suite/config/asyncShare/arithmetic.json b/tests/suite/config/asyncShare/arithmetic.json
new file mode 100644
index 000000000..ffbd5b3cf
--- /dev/null
+++ b/tests/suite/config/asyncShare/arithmetic.json
@@ -0,0 +1,50 @@
+{
+ "tests": [ "+", "-", "*", "*bgw", "|", "^", "!", "/", "%"],
+ "suiteConf": {
+ "port": 3001,
+ "extensions": [ "asyncShare" ],
+ "generation": {
+ "file": "config/asyncShare/generation.js",
+ "function": "generateArithmeticInputs"
+ },
+ "computation": {
+ "file": "config/asyncShare/computations.js",
+ "function": "compute"
+ }
+ },
+
+ "testConf": {
+ "default": {
+ "count": 1,
+ "parallel": 25,
+ "options": {
+ "Zp": 15485867,
+ "party_count": 4
+ }
+ },
+ "*bgw": {
+ "count": 1,
+ "parallel": 25,
+ "options": {
+ "Zp": 15485867,
+ "party_count": 3
+ }
+ },
+ "/": {
+ "count": 1,
+ "parallel": 3,
+ "options": {
+ "Zp": 127,
+ "party_count": 3
+ }
+ },
+ "%": {
+ "count": 1,
+ "parallel": 3,
+ "options": {
+ "Zp": 127,
+ "party_count": 3
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/suite/config/asyncShare/comparison.json b/tests/suite/config/asyncShare/comparison.json
new file mode 100644
index 000000000..0b178bade
--- /dev/null
+++ b/tests/suite/config/asyncShare/comparison.json
@@ -0,0 +1,42 @@
+{
+ "tests": [ "<","<=", ">", ">=", "==", "!=" ],
+ "suiteConf": {
+ "port": 3001,
+ "extensions": ["asyncShare"],
+ "generation": {
+ "file": "config/asyncShare/generation.js",
+ "function": "generateComparisonInputs"
+ },
+ "computation": {
+ "file": "config/asyncShare/computations.js",
+ "function": "compute"
+ }
+ },
+
+ "testConf": {
+ "default": {
+ "count": 15,
+ "parallel": 5,
+ "options": {
+ "Zp": 2039,
+ "party_count": 3
+ }
+ },
+ "==": {
+ "count": 15,
+ "parallel": 5,
+ "options": {
+ "Zp": 2039,
+ "party_count": 3
+ }
+ },
+ "!=": {
+ "count": 15,
+ "parallel": 5,
+ "options": {
+ "Zp": 2039,
+ "party_count": 3
+ }
+ }
+ }
+}
diff --git a/tests/suite/config/asyncShare/computations.js b/tests/suite/config/asyncShare/computations.js
new file mode 100644
index 000000000..ea15a480d
--- /dev/null
+++ b/tests/suite/config/asyncShare/computations.js
@@ -0,0 +1,82 @@
+// Flags success/failure
+var errors = [];
+var successes = [];
+
+// Override with different interpreters
+var baseComputations = require('../../computations.js');
+
+var testConfig;
+
+
+// sharing
+baseComputations.shareHook = async function (jiff_instance, test, testInputs, input, threshold, receivers, senders, ratios) {
+ var shares = jiff_instance.share(input, threshold, receivers, senders, jiff_instance.Zp, null, ratios);
+ return shares;
+}
+
+baseComputations.shareParameters = function (jiff_instance, test, testInputs) {
+ var ratios = testInputs[1];
+ testInputs = testInputs[0];
+ var input = testInputs[jiff_instance.id];
+
+ // Figure out who is sharing
+ var senders = [];
+ for (var p in testInputs) {
+ if (testInputs.hasOwnProperty(p) && p != 'constant') {
+ senders.push(/^\d+$/.test(p.toString()) ? parseInt(p) : p);
+ }
+ }
+ senders.sort();
+
+ // Figure out threshold
+ var threshold = test === '*bgw' ? Math.floor(jiff_instance.party_count / 2) : jiff_instance.party_count;
+ return {input: input, threshold: threshold, senders: senders, receivers: null, constant: testInputs['constant'], ratios: ratios};
+};
+
+baseComputations.singleTest = async function (jiff_instance, test, testInputs) {
+ try {
+ // Share for MPC
+ var shareParameters = baseComputations.shareParameters(jiff_instance, test, testInputs);
+ var shares = await baseComputations.shareHook(jiff_instance, test, testInputs, shareParameters.input, shareParameters.threshold, shareParameters.receivers, shareParameters.senders, {1: 2, 2: 2, 3: 1, 4: 2});
+ if (shares == null) {
+ return null;
+ }
+
+ shares['constant'] = shareParameters.constant;
+
+ // Compute in the Open
+ var actualResult = await baseComputations.singleCompute(jiff_instance, shareParameters, test, testInputs[0], baseComputations.openInterpreter);
+
+ // Compute under MPC
+ var mpcResult = await baseComputations.singleCompute(jiff_instance, shareParameters, test, shares, baseComputations.mpcInterpreter);
+ if (mpcResult == null) {
+ return null;
+ }
+
+ // Open
+ mpcResult = await baseComputations.openHook(jiff_instance, test, mpcResult);
+
+ // Verify result
+ // Assert both results are equal
+ if (!baseComputations.verifyResultHook(test, mpcResult, actualResult)) {
+ errors.push(baseComputations.errorMessage(jiff_instance, test, testInputs[0], shareParameters, mpcResult, actualResult));
+ return false;
+ }
+
+ successes.push(baseComputations.successMessage(jiff_instance, test, testInputs[0], shareParameters, mpcResult, actualResult));
+ } catch (err) {
+ console.log(err);
+ errors.push(err);
+ return false;
+ }
+
+ return true;
+};
+
+// Default Computation Scheme
+exports.compute = function (_jiff_instance, _test, _inputs, _testParallel, _done, _testConfig) {
+ testConfig = _testConfig;
+ return baseComputations.compute.apply(baseComputations, arguments);
+};
+
+
diff --git a/tests/suite/config/asyncShare/constant arithmetic.json b/tests/suite/config/asyncShare/constant arithmetic.json
new file mode 100644
index 000000000..f35d2a24a
--- /dev/null
+++ b/tests/suite/config/asyncShare/constant arithmetic.json
@@ -0,0 +1,44 @@
+{
+ "tests": [ "+", "-", "*", "|", "^", "cdivfac", "/Zp127", "/Zp2039" ],
+ "suiteConf": {
+ "port": 3001,
+ "extensions": ["asyncShare"],
+ "generation": {
+ "file": "config/asyncShare/generation.js",
+ "function": "generateConstantArithmeticInputs"
+ },
+ "computation": {
+ "file": "config/asyncShare/computations.js",
+ "function": "compute"
+ }
+ },
+
+ "testConf": {
+ "default": {
+ "count": 100,
+ "parallel": 50,
+ "options": {
+ "Zp": 15485867,
+ "party_count": 3
+ }
+ },
+ "/Zp127": {
+ "alias": "/",
+ "count": 8,
+ "parallel": 3,
+ "options": {
+ "Zp": 127,
+ "party_count": 3
+ }
+ },
+ "/Zp2039": {
+ "alias": "/",
+ "count": 5,
+ "parallel": 3,
+ "options": {
+ "Zp": 2039,
+ "party_count": 3
+ }
+ }
+ }
+}
diff --git a/tests/suite/config/asyncShare/constant comparison.json b/tests/suite/config/asyncShare/constant comparison.json
new file mode 100644
index 000000000..c98caf4ad
--- /dev/null
+++ b/tests/suite/config/asyncShare/constant comparison.json
@@ -0,0 +1,42 @@
+{
+ "tests": [ "<", "<=", ">", ">=", "==", "!=" ],
+ "suiteConf": {
+ "port": 3001,
+ "extensions": ["asyncShare"],
+ "generation": {
+ "file": "config/asyncShare/generation.js",
+ "function": "generateConstantComparisonInputs"
+ },
+ "computation": {
+ "file": "config/asyncShare/computations.js",
+ "function": "compute"
+ }
+ },
+
+ "testConf": {
+ "default": {
+ "count": 15,
+ "parallel": 5,
+ "options": {
+ "Zp": 2039,
+ "party_count": 3
+ }
+ },
+ "==": {
+ "count": 15,
+ "parallel": 5,
+ "options": {
+ "Zp": 2039,
+ "party_count": 3
+ }
+ },
+ "!=": {
+ "count": 15,
+ "parallel": 5,
+ "options": {
+ "Zp": 2039,
+ "party_count": 3
+ }
+ }
+ }
+}
diff --git a/tests/suite/config/asyncShare/generation.js b/tests/suite/config/asyncShare/generation.js
new file mode 100644
index 000000000..eb96ab880
--- /dev/null
+++ b/tests/suite/config/asyncShare/generation.js
@@ -0,0 +1,115 @@
+// Reuse base generation but with different underlying generation methods
+var baseGeneration = require('../base/generation.js');
+
+// Sharing/Opening test cases with no operations
+baseGeneration.generateShareRatios = function(test, count, options){
+ var t, p, oneRatio;
+ var inputs = [];
+ var threshold = Math.floor(Math.random() * options.party_count) + 1;
+ for (t = 0; t < count; t++) {
+ oneRatio = {};
+ for (p = 1; p <= options.party_count; p++) {
+ oneRatio[p] = baseGeneration.generateNonZeroUniform(test, options, threshold);
+ }
+ inputs.push(oneRatio);
+ }
+ return inputs;
+};
+
+var oldArithmeticInputs = baseGeneration.generateArithmeticInputs;
+baseGeneration.generateArithmeticInputs = function (test, count, options) {
+ var inputs = [];
+ var arithInputs = oldArithmeticInputs(test, count, options);
+ var ratioInputs = baseGeneration.generateShareRatios(test,count,options);
+ for (var t = 0; t < count; t++) {
+ inputs.push([arithInputs[t], ratioInputs[t]]);
+ }
+ return inputs;
+};
+
+var oldConstantArithmeticInputs = baseGeneration.generateConstantArithmeticInputs;
+baseGeneration.generateConstantArithmeticInputs = function (test, count, options) {
+ var inputs = [];
+ var arithInputs = oldConstantArithmeticInputs(test, count, options);
+ var ratioInputs = baseGeneration.generateShareRatios(test,count,options);
+ for (var t = 0; t < count; t++) {
+ inputs.push([arithInputs[t], ratioInputs[t]]);
+ }
+ return inputs;
+}
+
+var oldComparisonInputs = baseGeneration.generateComparisonInputs;
+baseGeneration.generateComparisonInputs = function (test, count, options) {
+ var inputs = [];
+ var arithInputs = oldComparisonInputs(test, count, options);
+ var ratioInputs = baseGeneration.generateShareRatios(test,count,options);
+ for (var t = 0; t < count; t++) {
+ inputs.push([arithInputs[t], ratioInputs[t]]);
+ }
+ return inputs;
+}
+
+var oldConstantComparisonInputs = baseGeneration.generateConstantComparisonInputs;
+baseGeneration.generateConstantComparisonInputs = function (test, count, options) {
+ var inputs = [];
+ var arithInputs = oldConstantComparisonInputs(test, count, options);
+ var ratioInputs = baseGeneration.generateShareRatios(test,count,options);
+ for (var t = 0; t < count; t++) {
+ inputs.push([arithInputs[t], ratioInputs[t]]);
+ }
+ return inputs;
+}
+
+
+baseGeneration.generateShareInputs = function (test, count, options) {
+ var all_parties = [];
+ for (var k = 1; k <= options.party_count; k++) {
+ all_parties.push(k);
+ }
+
+ var inputs = [];
+ // Generate test cases one at a time
+ // A test case consists of
+ // 1) input numbers
+ // 2) sharing threshold
+ // 3) array of senders
+ // 4) array of receivers
+ var max = options.max || options.Zp;
+ for (var t = 0; t < count; t++) {
+ var oneTest = { numbers: {} };
+ // Generate numbers
+ for (var p = 1; p <= options.party_count; p++) {
+ oneTest['numbers'][p] = baseGeneration.generateUniform(test, options, max);
+ }
+ // 1 <= Threshold <= party_count
+ oneTest['threshold'] = Math.floor(Math.random() * options.party_count) + 1;
+
+ // Generate senders/receivers
+ var sn = Math.ceil(Math.random() * options.party_count); // At least one sender, at most all.
+ var rn = oneTest['threshold'] + Math.floor(Math.random() * (options.party_count - oneTest['threshold'] + 1)); // At least as many receivers as threshold.
+
+ // Generate actual receivers and senders arrays
+ var senders = all_parties.slice();
+ var receivers = all_parties.slice();
+
+ // remove random parties until proper counts are reached.
+ while (senders.length > sn) {
+ senders.splice(Math.floor(Math.random() * senders.length), 1);
+ }
+ while (receivers.length > rn) {
+ receivers.splice(Math.floor(Math.random() * receivers.length), 1);
+ }
+ oneTest['senders'] = senders;
+ oneTest['receivers'] = receivers;
+
+ // Generate receiver ratios between 1 <= threshold
+ for (p = 1; p < receivers; p++) {
+ oneTest['receiver_ratios'][p] = baseGeneration.generateUniform(test, options, oneTest['threshold']);
+ }
+
+ inputs.push(oneTest);
+ }
+ return inputs;
+};
+
+module.exports = baseGeneration;
\ No newline at end of file
diff --git a/tests/suite/config/asyncShare/share.json b/tests/suite/config/asyncShare/share.json
new file mode 100644
index 000000000..6f6e95720
--- /dev/null
+++ b/tests/suite/config/asyncShare/share.json
@@ -0,0 +1,58 @@
+{
+ "tests": [ "share2", "share3", "share4", "share5", "share10" ],
+ "suiteConf": {
+ "port": 3001,
+ "extensions": ["asyncShare"],
+ "generation": {
+ "file": "config/asyncShare/generation.js",
+ "function": "generateShareInputs"
+ },
+ "computation": {
+ "file": "./computations-share.js",
+ "function": "compute"
+ }
+ },
+
+ "testConf": {
+ "share2": {
+ "count": 100,
+ "parallel": 25,
+ "options": {
+ "Zp": 2039,
+ "party_count": 2
+ }
+ },
+ "share3": {
+ "count": 100,
+ "parallel": 25,
+ "options": {
+ "Zp": 2039,
+ "party_count": 3
+ }
+ },
+ "share4": {
+ "count": 80,
+ "parallel": 20,
+ "options": {
+ "Zp": 2039,
+ "party_count": 4
+ }
+ },
+ "share5": {
+ "count": 50,
+ "parallel": 20,
+ "options": {
+ "Zp": 2039,
+ "party_count": 5
+ }
+ },
+ "share10": {
+ "count": 40,
+ "parallel": 15,
+ "options": {
+ "Zp": 2039,
+ "party_count": 10
+ }
+ }
+ }
+}
diff --git a/tests/suite/config/bits/arithmetic.json b/tests/suite/config/bits/arithmetic.json
index 5cbacd4b8..c215174b4 100644
--- a/tests/suite/config/bits/arithmetic.json
+++ b/tests/suite/config/bits/arithmetic.json
@@ -56,4 +56,4 @@
}
}
}
-}
\ No newline at end of file
+}
diff --git a/tests/suite/config/bits/computations.js b/tests/suite/config/bits/computations.js
index f4f49ad9a..028ef1d6f 100644
--- a/tests/suite/config/bits/computations.js
+++ b/tests/suite/config/bits/computations.js
@@ -211,4 +211,4 @@ baseComputations.verifyResultHook = function (test, mpcResult, expectedResult) {
exports.compute = function (_jiff_instance, _test, _inputs, _testParallel, _done, _testConfig) {
testConfig = _testConfig;
return baseComputations.compute.apply(baseComputations, arguments);
-};
\ No newline at end of file
+};
diff --git a/tests/suite/config/bits/decomposition.json b/tests/suite/config/bits/decomposition.json
index c33dfd110..66e8e931e 100644
--- a/tests/suite/config/bits/decomposition.json
+++ b/tests/suite/config/bits/decomposition.json
@@ -99,4 +99,4 @@
}
}
}
-}
\ No newline at end of file
+}
diff --git a/tests/suite/init.js b/tests/suite/init.js
index de9fb6aff..59cb8b70c 100644
--- a/tests/suite/init.js
+++ b/tests/suite/init.js
@@ -6,7 +6,8 @@ var extensions = {
bigNumber: require('../../lib/ext/jiff-client-bignumber'),
fixedpoint: require('../../lib/ext/jiff-client-fixedpoint'),
negativeNumber: require('../../lib/ext/jiff-client-negativenumber'),
- restAPI: require('../../lib/ext/jiff-client-restful')
+ restAPI: require('../../lib/ext/jiff-client-restful'),
+ asyncShare: require('../../lib/ext/jiff-client-asynchronousshare')
};
// Create the jiff instances for tests
diff --git a/tests/suite/server.js b/tests/suite/server.js
index 8c3ca9b8e..c9bae939b 100644
--- a/tests/suite/server.js
+++ b/tests/suite/server.js
@@ -1,4 +1,9 @@
-var extensions = process.env['JIFF_TEST_NAME'];
+var name = process.env['JIFF_TEST_NAME'];
+var suite = process.env['JIFF_TEST_SUITE'];
+// JIFF test configuration
+var config = require('./config/' + name + '/' + suite + '.json');
+// Get extensions
+var extensions = config['suiteConf']['extensions'];
var express = require('express');
var app = express();
@@ -7,6 +12,7 @@ var http = require('http').Server(app);
var jiffServer = require('../../lib/jiff-server');
var jiffRestAPIServer = require('../../lib/ext/jiff-server-restful');
var jiffBigNumberServer = require('../../lib/ext/jiff-server-bignumber');
+var jiffAsyncShareServer = require('../../lib/ext/jiff-server-asyncshare');
var options = {
logs: true,
@@ -23,6 +29,9 @@ if (extensions != null && extensions.indexOf('restAPI') > -1) {
app.use(bodyParser.json());
jiff_instance.apply_extension(jiffRestAPIServer, options);
}
+if (extensions != null && extensions.indexOf('asyncShare') > -1) {
+ jiff_instance.apply_extension(jiffAsyncShareServer, options);
+}
// Serve static files.
app.use('/demos', express.static('demos'));
diff --git a/tests/suite/suite.sh b/tests/suite/suite.sh
index 15e844cb8..35f0178a1 100755
--- a/tests/suite/suite.sh
+++ b/tests/suite/suite.sh
@@ -10,14 +10,14 @@ echo "====================" >> "${logs}"
echo "NEW TEST $(date)" >> "${logs}"
echo "====================" >> "${logs}"
-node tests/suite/server.js >> "${logs}" &
-
EXIT_CODE=0
i=0
for f in tests/suite/config/${JIFF_TEST_NAME}/*.json; do
FULLNAME=$(basename "$f")
export JIFF_TEST_SUITE="${FULLNAME%.json}"
+ node tests/suite/server.js >> "${logs}" &
+
if [ "$2" == "parallel" ]
then
./node_modules/.bin/mocha --reporter spec tests/suite/index.js &
@@ -31,6 +31,8 @@ for f in tests/suite/config/${JIFF_TEST_NAME}/*.json; do
EXIT_CODE=$CODE
fi
fi
+
+ kill $(ps aux | grep "node tests/suite/server\.js" | awk '{ print $2}')
done
if [ "$2" == "parallel" ]