/* Code by Bingsheng Zhang, Thomas Smith, Vincent de Almeida Dependencies can be found in 'package.json' and installed using 'npm install' */ var port = 8080; var Buffer = require('buffer').Buffer; var CTX = require('milagro-crypto-js'); var express = require('express'); var bodyParser = require("body-parser"); var app = express(); // Express server configuration app.use(express.static('test')); app.use(bodyParser.urlencoded({ extended: false })); app.use(bodyParser.json()); //default test app.get('/', function(request, response){ var data = { message: 'hello world', value: 5 }; //response.send('Hey there'+request.ip); response.json(data); console.log('request from'+request.ip); }); //parameter generation function app.get('/param', function(request, response){ var param = gpGen(); console.log('Generated Group Param'); response.json(param); }); //combine public keys and return the full combined one - JSON Version app.get('/combpk', function(request, response){ console.log('\nEndpoint /combpk called'); var partials = request.query['PK']; var parsed = []; console.log('Combining...'); for (var i = partials.length - 1; i >= 0; i--) { console.log('PK' +i+ ': '+partials[i]); parsed.push(JSON.parse(partials[i])); } var PK = combine_pks(parsed); response.json(PK); }); //byte array version app.post('/cmpkstring', function(request, response){ console.log('\nEndpoint /cmpkstring called'); var ctx = new CTX("BN254CX"); var partials = request.body.PKs; var parsed = []; if(partials.length > 1)//if we're submitting more than one key { console.log('Combining ' + partials.length + " public keys into one..."); for (var i = partials.length - 1; i >= 0; i--) { console.log('PK' + i + ': ' + partials[i]); var bytes = Buffer.from(partials[i].split(','), 'hex'); var pk = new ctx.ECP.fromBytes(bytes); parsed.push(pk); } } else if(partials.length === 1) { console.log("Combining just one public key..."); var bytes = Buffer.from(partials[0].split(','), 'hex'); var pk = new ctx.ECP.fromBytes(bytes); parsed.push(pk); } response.json(combine_pks(parsed)); }); //addition function on homomorphically encrypted variables //this may need some work, different method of serialisation maybe? app.post('/add_ciphers', function(request, response){ console.log("\nEndpoint /add_ciphers called"); const C1s = request.body.ciphers.c1s; const C2s = request.body.ciphers.c2s; const CIPHER_COUNT = C1s.length; // Will store a list of parsed ciphers from the C1s and C2s arrays passed in var parsedCiphers = []; var ctx = new CTX("BN254CX"); if(CIPHER_COUNT > 1) { console.log("Combining " + CIPHER_COUNT + " ciphers"); for (var i = 0; i < CIPHER_COUNT; i++) { var c1Bytes = Buffer.from(C1s[i].split(','), 'hex'); var newC1 = new ctx.ECP.fromBytes(c1Bytes); var c2Bytes = Buffer.from(C2s[i].split(','), 'hex'); var newC2 = new ctx.ECP.fromBytes(c2Bytes); var cipher = { C1 : newC1, C2 : newC2 }; parsedCiphers.push(cipher); } } else if(CIPHER_COUNT === 1) { console.log("Combining only one cipher"); var c1Bytes = Buffer.from(C1s[0].split(','), 'hex'); var newC1 = new ctx.ECP.fromBytes(c1Bytes); var c2Bytes = Buffer.from(C2s[0].split(','), 'hex'); var newC2 = new ctx.ECP.fromBytes(c2Bytes); var cipher = { C1 : newC1, C2 : newC2 }; parsedCiphers.push(cipher); } // Combine the ciphers here var combinedCipher = add(parsedCiphers); // Get the byte string of the C1 and C2 part for transmission var C1Bytes = []; combinedCipher.C1.toBytes(C1Bytes); var C2Bytes = []; combinedCipher.C2.toBytes(C2Bytes); var responseData = { C1: C1Bytes.toString(), C2: C2Bytes.toString() }; response.json(responseData); }); app.post('/get_tally', function(request, response){ console.log("\nEndpoint /get_tally called"); // Extract the data from the request const TEMP_PARAMS = JSON.parse(JSON.parse(request.body.param).crypto); const BALLOT_CIPHER = request.body.ballot_cipher; const PART_DECS = request.body.part_decs; const VOTERS_COUNT = request.body.voters_count; // Re-build parameters var ctx = new CTX("BN254CX"); var n = new ctx.BIG(); var g1 = new ctx.ECP(); var g2 = new ctx.ECP2(); n.copy(TEMP_PARAMS.n); g1.copy(TEMP_PARAMS.g1); g2.copy(TEMP_PARAMS.g2); var params = { n : n, g1 : g1, g2 : g2 }; // Initialise the ballot cipher var c1Bytes = Buffer.from(BALLOT_CIPHER.C1.split(','), 'hex'); var newC1 = new ctx.ECP.fromBytes(c1Bytes); var c2Bytes = Buffer.from(BALLOT_CIPHER.C2.split(','), 'hex'); var newC2 = new ctx.ECP.fromBytes(c2Bytes); var cipher = { C1 : newC1, C2 : newC2 }; // Initialise all of the partial decryptions var partials = []; for(var i = 0; i < PART_DECS.length; i++) { var bytes = Buffer.from(PART_DECS[i].split(','), 'hex'); var dec = { D : new ctx.ECP.fromBytes(bytes) }; partials.push(dec); } // Send the decrypted cipher value (vote tally for an option) response.send("" + getCipherVal(params, partials, cipher, VOTERS_COUNT).M); }); var server = app.listen(port, function(){ var host = server.address().address; var appPort = server.address().port; console.log('Server listening on ' + host + ':'+ port); }); /* Cryptography functions written by Bingsheng Zhang Uses the milagro-crypto-js library at: https://github.com/milagro-crypto/milagro-crypto-js */ //Group parameter generator: returns rng object and generators g1,g2 for G1,G2 as well as order gpGen = function() { //init, and base generators var ctx = new CTX("BN254CX"); var n=new ctx.BIG(0); n.rcopy(ctx.ROM_CURVE.CURVE_Order); //get generator P for G1 P = new ctx.ECP(0); gx = new ctx.BIG(0); gx.rcopy(ctx.ROM_CURVE.CURVE_Gx); if (ctx.ECP.CURVETYPE != ctx.ECP.MONTGOMERY) { gy = new ctx.BIG(0); gy.rcopy(ctx.ROM_CURVE.CURVE_Gy); P.setxy(gx, gy); } else P.setx(gx); //get generator Q for G2 var A=new ctx.BIG(0); var B=new ctx.BIG(0); A.rcopy(ctx.ROM_CURVE.CURVE_Pxa); B.rcopy(ctx.ROM_CURVE.CURVE_Pxb); var Qx=new ctx.FP2(0); Qx.bset(A,B); A.rcopy(ctx.ROM_CURVE.CURVE_Pya); B.rcopy(ctx.ROM_CURVE.CURVE_Pyb); var Qy=new ctx.FP2(0); Qy.bset(A,B); var Q=new ctx.ECP2(); Q.setxy(Qy,Qy); return{ n:n, g1:P, g2:Q } }; //creates ElGamal public and secret key keyGen = function(params) { var ctx = new CTX("BN254CX"); //set rng var RAW = []; var d = new Date();//time for seed, not secure var rng = new ctx.RAND(); rng.clean(); RAW[0] = d.getSeconds(); RAW[1] = d.getMinutes(); RAW[2] = d.getMilliseconds(); rng.seed(3, RAW); //ElGamal var sk = new ctx.BIG(0); sk = ctx.BIG.randomnum(params.n,rng); var pk = new ctx.ECP(0); pk = ctx.PAIR.G1mul(params.g1,sk); return{ PK:pk, SK:sk } }; //combine multiple public key together //the input is an array of PKs combine_pks = function(PKs) { var ctx = new CTX("BN254CX"); var pk=new ctx.ECP(); //copy the first pk pk.copy(PKs[0]); //multiple the rest PKs for(i=1;i