From 4c7a37c274f8f911bb71cfd20f29640f836750bb Mon Sep 17 00:00:00 2001 From: Jordi Baylina Date: Fri, 15 May 2020 21:30:37 +0200 Subject: [PATCH] contribution generated in wasm --- cli.js | 52 +++- src/keypair.js | 19 +- src/mpc_applykey.js | 125 ++++++++++ src/powersoftau_beacon.js | 167 +++++++++++++ src/powersoftau_challangecontribute.js | 287 ++++++++++++++++++++++ src/powersoftau_contribute.js | 327 +++++++------------------ src/powersoftau_import.js | 3 +- src/powersoftau_utils.js | 162 ++++++------ src/powersoftau_verify.js | 66 ++++- src/powersoftaw.js | 5 +- 10 files changed, 880 insertions(+), 333 deletions(-) create mode 100644 src/mpc_applykey.js create mode 100644 src/powersoftau_beacon.js create mode 100644 src/powersoftau_challangecontribute.js diff --git a/cli.js b/cli.js index 1d85ad2..e8753f9 100755 --- a/cli.js +++ b/cli.js @@ -116,17 +116,17 @@ const commands = [ action: powersOfTawExportChallange }, { - cmd: "powersoftaw contribute [response]", + cmd: "powersoftaw challange contribute [response]", description: "Contribute to a challange", - alias: ["ptc"], + alias: ["ptcc"], options: "-verbose|v -entropy|e", - action: powersOfTawContribute + action: powersOfTawChallangeContribute }, { cmd: "powersoftaw import <", description: "import a response to a ptaw file", alias: ["pti"], - options: "-verbose|v -nopoints -nocheck", + options: "-verbose|v -nopoints -nocheck -description|d -name|n", action: powersOfTawImport }, { @@ -136,6 +136,20 @@ const commands = [ options: "-verbose|v", action: powersOfTawVerify }, + { + cmd: "powersoftaw beacon ", + description: "adds a beacon", + alias: ["ptb"], + options: "-verbose|v -name|n", + action: powersOfTawBeacon + }, + { + cmd: "powersoftaw contribute ", + description: "verifies a powers of tau file", + alias: ["ptc"], + options: "-verbose|v -name|n -entropy|e", + action: powersOfTawContribute + }, ]; @@ -513,7 +527,7 @@ async function powersOfTawExportChallange(params, options) { } -async function powersOfTawContribute(params, options) { +async function powersOfTawChallangeContribute(params, options) { let challangeName; let responseName; @@ -525,7 +539,7 @@ async function powersOfTawContribute(params, options) { responseName = params[1]; } - return await powersOfTaw.contribute(bn128, challangeName, responseName, options.entropy, options.verbose); + return await powersOfTaw.challangeContribute(bn128, challangeName, responseName, options.entropy, options.verbose); } @@ -543,7 +557,7 @@ async function powersOfTawImport(params, options) { if (options.nopoints) importPoints = false; if (options.nocheck) doCheck = false; - const res = await powersOfTaw.impoertResponse(oldPtauName, response, newPtauName, importPoints, options.verbose); + const res = await powersOfTaw.impoertResponse(oldPtauName, response, newPtauName, options.name, importPoints, options.verbose); if (res) return res; if (!doCheck) return; @@ -564,6 +578,30 @@ async function powersOfTawVerify(params, options) { } } +async function powersOfTawBeacon(params, options) { + let oldPtauName; + let newPtauName; + let beaconHashStr; + let numIterationsExp; + + oldPtauName = params[0]; + newPtauName = params[1]; + beaconHashStr = params[2]; + numIterationsExp = params[3]; + + return await powersOfTaw.beacon(oldPtauName, newPtauName, options.name ,numIterationsExp, beaconHashStr, options.verbose); +} + +async function powersOfTawContribute(params, options) { + let oldPtauName; + let newPtauName; + + oldPtauName = params[0]; + newPtauName = params[1]; + + return await powersOfTaw.contribute(oldPtauName, newPtauName, options.name , options.entropy, options.verbose); +} + function generateVerifier_original(verificationKey) { let template = fs.readFileSync(path.join( __dirname, "templates", "verifier_original.sol"), "utf-8"); diff --git a/src/keypair.js b/src/keypair.js index 807a6be..755c6c1 100644 --- a/src/keypair.js +++ b/src/keypair.js @@ -30,9 +30,7 @@ function getG2sp(persinalization, challange, g1s, g1sx) { } -function createKeyPair(curve, personalization, challangeHash, rng ) { - const k = {}; - k.prvKey= curve.Fr.fromRng(rng); +function calculatePubKey(k, curve, personalization, challangeHash, rng ) { k.g1_s = curve.G1.affine(curve.G1.fromRng(rng)); k.g1_sx = curve.G1.affine(curve.G1.mulScalar(k.g1_s, k.prvKey)); k.g2_sp = curve.G2.affine(getG2sp(personalization, challangeHash, k.g1_s, k.g1_sx)); @@ -41,10 +39,17 @@ function createKeyPair(curve, personalization, challangeHash, rng ) { } function createPTauKey(curve, challangeHash, rng) { - const key = {}; - key.tau = createKeyPair(curve, 0, challangeHash, rng); - key.alpha = createKeyPair(curve, 1, challangeHash, rng); - key.beta = createKeyPair(curve, 2, challangeHash, rng); + const key = { + tau: {}, + alpha: {}, + beta: {} + }; + key.tau.prvKey = curve.Fr.fromRng(rng); + key.alpha.prvKey = curve.Fr.fromRng(rng); + key.beta.prvKey = curve.Fr.fromRng(rng); + calculatePubKey(key.tau, curve, 0, challangeHash, rng); + calculatePubKey(key.alpha, curve, 1, challangeHash, rng); + calculatePubKey(key.beta, curve, 2, challangeHash, rng); return key; } diff --git a/src/mpc_applykey.js b/src/mpc_applykey.js new file mode 100644 index 0000000..0feb5bb --- /dev/null +++ b/src/mpc_applykey.js @@ -0,0 +1,125 @@ + +const buildTaskManager = require("./taskmanager"); + +/* + This function creates a new section in the fdTo file with id idSection. + It multiplies the pooints in fdFrom by first, first*inc, first*inc^2, .... + nPoint Times. + It also updates the newChallangeHasher with the new points +*/ + +async function applyKey(params) { + const { + fdFrom, + sections, + curve, + fdTo, + sectionId, + NPoints, + G:Gs, + first, + inc, + newChallangeHasher, + responseHasher, + returnPoints, + sectionName, + verbose + } = params; + const G = curve[Gs]; + const MAX_CHUNK_SIZE = 1024; + + let res = []; + const sG = G.F.n8*2; + const buffU = new ArrayBuffer(sG); + const buffUv = new Uint8Array(buffU); + const scG = G.F.n8; + const buffC = new ArrayBuffer(scG); + const buffCv = new Uint8Array(buffC); + + const taskManager = await buildTaskManager(contributeThread, { + ffjavascript: "ffjavascript" + },{ + curve: curve.name + }); + + fdFrom.pos = sections[sectionId][0].p; + await fdTo.writeULE32(sectionId); // tauG1 + const pSection = fdTo.pos; + await fdTo.writeULE64(0); // Temporally set to 0 length + let t = first; + let writePointer = fdTo.pos; + let beginWritePointer = fdTo.pos; + for (let i=0; i< NPoints; i+=MAX_CHUNK_SIZE) { + if ((verbose)&&i) console.log(`${sectionName}: ` + i); + const n = Math.min(NPoints - i, MAX_CHUNK_SIZE); + const buff = await fdFrom.read(n*sG); + await taskManager.addTask({ + cmd: "MUL", + G: Gs, + first: t, + inc: inc.toString(), + buff: buff.slice(), + n: n, + writePos: writePointer + }, async function(r) { + return await fdTo.write(r.buff, r.writePos); + }); + t = curve.Fr.mul(t, curve.Fr.pow(inc, n)); + writePointer += n*sG; + } + + await taskManager.finish(); + + const sSize = fdTo.pos - pSection -8; + const lastPos = fdTo.pos; + await fdTo.writeULE64(sSize, pSection); + fdTo.pos = lastPos; + + fdTo.pos = beginWritePointer; + for (let i=0; i=0) res[idx] = P; + } + + return res; +} + + +function contributeThread(ctx, task) { + if (task.cmd == "INIT") { + ctx.assert = ctx.modules.assert; + if (task.curve == "bn128") { + ctx.curve = ctx.modules.ffjavascript.bn128; + } else { + ctx.assert(false, "curve not defined"); + } + return {}; + } else if (task.cmd == "MUL") { + const G = ctx.curve[task.G]; + const sG = G.F.n64*8*2; + const buffDest = new ArrayBuffer(sG*task.n); + let t = ctx.curve.Fr.e(task.first); + let inc = ctx.curve.Fr.e(task.inc); + for (let i=0; i=256) { + console.log("Maximum lenght of beacon hash is 255 bytes"); + return false; + } + + numIterationsExp = parseInt(numIterationsExp); + if ((numIterationsExp<10)||(numIterationsExp>63)) { + console.log("Invalid numIterationsExp. (Must be between 10 and 63)"); + return false; + } + + await Blake2b.ready(); + + const {fd: fdOld, sections} = await utils.readBinFile(oldPtauFilename, "ptau", 1); + const {curve, power} = await utils.readPTauHeader(fdOld, sections); + const contributions = await utils.readContributions(fdOld, curve, sections); + const currentContribution = { + name: name, + type: 1, // Beacon + numIterationsExp: numIterationsExp, + beaconHash: beaconHash + }; + + let lastChallangeHash; + + if (contributions.length>0) { + lastChallangeHash = contributions[contributions.length-1].nextChallange; + } else { + lastChallangeHash = utils.calculateFirstChallangeHash(curve, power); + } + + currentContribution.key = utils.keyFromBeacon(curve, lastChallangeHash, beaconHash, numIterationsExp); + + const fdNew = await utils.createBinFile(newPTauFilename, "ptau", 1, 7); + await utils.writePTauHeader(fdNew, curve, power); + + const newChallangeHasher = new Blake2b(64); + newChallangeHasher.update(lastChallangeHash); + + const responseHasher = new Blake2b(64); + responseHasher.update(lastChallangeHash); + + currentContribution.tauG1 = (await applyKey({ + fdFrom: fdOld, + sections, + curve, + fdTo: fdNew, + sectionId: 2, + NPoints: (1 << power) * 2 -1, + G: "G1", + first: curve.Fr.one, + inc: currentContribution.key.tau.prvKey, + newChallangeHasher, + responseHasher, + returnPoints: [1], + sectionName: "tauG1", + verbose + }))[0]; + + currentContribution.tauG2 = (await applyKey({ + fdFrom: fdOld, + sections, + curve, + fdTo: fdNew, + sectionId: 3, + NPoints: 1 << power, + G: "G2", + first: curve.Fr.one, + inc: currentContribution.key.tau.prvKey, + newChallangeHasher, + responseHasher, + returnPoints: [1], + sectionName: "tauG2", + verbose + }))[0]; + + currentContribution.alphaG1 = (await applyKey({ + fdFrom: fdOld, + sections, + curve, + fdTo: fdNew, + sectionId: 4, + NPoints: 1 << power, + G: "G1", + first: currentContribution.key.alpha.prvKey, + inc: currentContribution.key.tau.prvKey, + newChallangeHasher, + responseHasher, + returnPoints: [0], + sectionName: "alphaTauG1", + verbose + }))[0]; + + currentContribution.betaG1 = (await applyKey({ + fdFrom: fdOld, + sections, + curve, + fdTo: fdNew, + sectionId: 5, + NPoints: 1 << power, + G: "G1", + first: currentContribution.key.beta.prvKey, + inc: currentContribution.key.tau.prvKey, + newChallangeHasher, + responseHasher, + returnPoints: [0], + sectionName: "betaTauG1", + verbose + }))[0]; + + currentContribution.betaG2 = (await applyKey({ + fdFrom: fdOld, + sections, + curve, + fdTo: fdNew, + sectionId: 6, + NPoints: 1, + G: "G2", + first: currentContribution.key.beta.prvKey, + inc: currentContribution.key.tau.prvKey, + newChallangeHasher, + responseHasher, + returnPoints: [0], + sectionName: "betaG2", + verbose + }))[0]; + + currentContribution.nextChallange = newChallangeHasher.digest(); + currentContribution.partialHash = responseHasher.getPartialHash(); + + const buffKey = new ArrayBuffer(curve.F1.n8*2*6+curve.F2.n8*2*3); + + utils.toPtauPubKeyRpr(buffKey, 0, curve, currentContribution.key, false); + + responseHasher.update(new Uint8Array(buffKey)); + const hashResponse = responseHasher.digest(); + + console.log("Contribution Response Hash imported: "); + console.log(utils.formatHash(hashResponse)); + + contributions.push(currentContribution); + + await utils.writeContributions(fdNew, curve, contributions); + + await fdOld.close(); + await fdNew.close(); +} + +module.exports = beacon; diff --git a/src/powersoftau_challangecontribute.js b/src/powersoftau_challangecontribute.js new file mode 100644 index 0000000..9c855b7 --- /dev/null +++ b/src/powersoftau_challangecontribute.js @@ -0,0 +1,287 @@ +// Format of the output +// Hash of the last contribution 64 Bytes +// 2^N*2-1 TauG1 Points (compressed) +// 2^N TauG2 Points (compressed) +// 2^N AlphaTauG1 Points (compressed) +// 2^N BetaTauG1 Points (compressed) +// Public Key +// BetaG2 (compressed) +// G1*s (compressed) +// G1*s*tau (compressed) +// G1*t (compressed) +// G1*t*alpha (compressed) +// G1*u (compressed) +// G1*u*beta (compressed) +// G2*sp*tau (compressed) +// G2*tp*alpha (compressed) +// G2*up*beta (compressed) + +const fastFile = require("fastfile"); +const assert = require("assert"); +const blake2b = require("blake2b-wasm"); +const readline = require("readline"); +const crypto = require("crypto"); +const ChaCha = require("ffjavascript").ChaCha; +const fs = require("fs"); +const utils = require("./powersoftau_utils"); + + +const buildTaskManager = require("./taskmanager"); +const keyPair = require("./keypair"); + + +const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout +}); + +function askEntropy() { + return new Promise((resolve) => { + rl.question("Enter a random text. (Entropy): ", (input) => resolve(input) ); + }); +} + + +async function challangeContribute(curve, challangeFilename, responesFileName, entropy, verbose) { + await blake2b.ready(); + + const MAX_CHUNK_SIZE = 1024; + + let stats = await fs.promises.stat(challangeFilename); + + const sG1 = curve.F1.n64*8*2; + const scG1 = curve.F1.n64*8; // Compresed size + const sG2 = curve.F2.n64*8*2; + const scG2 = curve.F2.n64*8; // Compresed size + const domainSize = (stats.size + sG1 - 64 - sG2) / (4*sG1 + sG2); + let e = domainSize; + let power = 0; + while (e>1) { + e = e /2; + power += 1; + } + + assert(1< { + console.log(k, ".g1_s_x: " + key[k].g1_s[0].toString(16)); + console.log(k, ".g1_s_y: " + key[k].g1_s[1].toString(16)); + console.log(k, ".g1_sx_x: " + key[k].g1_sx[0].toString(16)); + console.log(k, ".g1_sx_y: " + key[k].g1_sx[1].toString(16)); + console.log(k, ".g2_sp_x_c0: " + key[k].g2_sp[0][0].toString(16)); + console.log(k, ".g2_sp_x_c1: " + key[k].g2_sp[0][1].toString(16)); + console.log(k, ".g2_sp_y_c0: " + key[k].g2_sp[1][0].toString(16)); + console.log(k, ".g2_sp_y_c1: " + key[k].g2_sp[1][1].toString(16)); + console.log(k, ".g2_spx_x_c0: " + key[k].g2_spx[0][0].toString(16)); + console.log(k, ".g2_spx_x_c1: " + key[k].g2_spx[0][1].toString(16)); + console.log(k, ".g2_spx_y_c0: " + key[k].g2_spx[1][0].toString(16)); + console.log(k, ".g2_spx_y_c1: " + key[k].g2_spx[1][1].toString(16)); + console.log(""); + }); + } + + + await fdTo.write(challangeHash); + writePointer += 64; + + const taskManager = await buildTaskManager(contributeThread, { + ffjavascript: "ffjavascript" + },{ + curve: curve.name + }); + + // TauG1 + let t = curve.Fr.e(1); + for (let i=0; i1) { - e = e /2; - power += 1; + if (contributions.length>0) { + lastChallangeHash = contributions[contributions.length-1].nextChallange; + } else { + lastChallangeHash = utils.calculateFirstChallangeHash(curve, power); } - assert(1< { - console.log(k, ".g1_s_x: " + key[k].g1_s[0].toString(16)); - console.log(k, ".g1_s_y: " + key[k].g1_s[1].toString(16)); - console.log(k, ".g1_sx_x: " + key[k].g1_sx[0].toString(16)); - console.log(k, ".g1_sx_y: " + key[k].g1_sx[1].toString(16)); - console.log(k, ".g2_sp_x_c0: " + key[k].g2_sp[0][0].toString(16)); - console.log(k, ".g2_sp_x_c1: " + key[k].g2_sp[0][1].toString(16)); - console.log(k, ".g2_sp_y_c0: " + key[k].g2_sp[1][0].toString(16)); - console.log(k, ".g2_sp_y_c1: " + key[k].g2_sp[1][1].toString(16)); - console.log(k, ".g2_spx_x_c0: " + key[k].g2_spx[0][0].toString(16)); - console.log(k, ".g2_spx_x_c1: " + key[k].g2_spx[0][1].toString(16)); - console.log(k, ".g2_spx_y_c0: " + key[k].g2_spx[1][0].toString(16)); - console.log(k, ".g2_spx_y_c1: " + key[k].g2_spx[1][1].toString(16)); - console.log(""); - }); - } +// const rng = new ChaCha(seed); + const rng = new ChaCha(); + curContribution.key = keyPair.createPTauKey(curve, lastChallangeHash, rng); - await fdTo.write(challangeHash); - writePointer += 64; + const newChallangeHasher = new Blake2b(64); + newChallangeHasher.update(lastChallangeHash); - const taskManager = await buildTaskManager(contributeThread, { - ffjavascript: "ffjavascript" - },{ - curve: curve.name - }); + const responseHasher = new Blake2b(64); + responseHasher.update(lastChallangeHash); - // TauG1 - let t = curve.Fr.e(1); - for (let i=0; i0) { + const paramsBuff = new Uint8Array(params); + await fd.writeULE32(paramsBuff.byteLength); + await fd.write(paramsBuff); + } else { + await fd.writeULE32(0); + } async function writeG1(p) { @@ -352,7 +341,7 @@ function formatHash(b) { S += "\t\t"; for (let j=0; j<4; j++) { if (j>0) S += " "; - S += a.getUint32(i*4+j).toString(16).padStart(8, "0"); + S += a.getUint32(i*16+j*4).toString(16).padStart(8, "0"); } } return S; @@ -394,6 +383,38 @@ function calculateFirstChallangeHash(curve, power) { return hasher.digest(); } + +function keyFromBeacon(curve, challangeHash, beaconHash, numIterationsExp) { + let nIterationsInner; + let nIterationsOuter; + if (numIterationsExp<32) { + nIterationsInner = (1 << numIterationsExp) >>> 0; + nIterationsOuter = 1; + } else { + nIterationsInner = 0x100000000; + nIterationsOuter = (1 << (numIterationsExp-32)) >>> 0; + } + + let curHash = beaconHash; + for (let i=0; i>4; let idx = pageId.indexOf(page); if (idx < 0) idx = loadPage(page); - return pages[page][n & 0xF] % (NSet-1); + return pages[idx][n & 0xF] % (NSet-1); }; } diff --git a/src/powersoftaw.js b/src/powersoftaw.js index eb54a54..9cf0c56 100644 --- a/src/powersoftaw.js +++ b/src/powersoftaw.js @@ -1,6 +1,9 @@ module.exports.newAccumulator = require("./powersoftau_new"); module.exports.exportChallange = require("./powersoftau_export"); -module.exports.contribute = require("./powersoftau_contribute"); +module.exports.challangeContribute = require("./powersoftau_challangecontribute"); module.exports.impoertResponse = require("./powersoftau_import"); module.exports.verify = require("./powersoftau_verify"); +module.exports.challangeContribute = require("./powersoftau_challangecontribute"); +module.exports.beacon = require("./powersoftau_beacon"); +module.exports.contribute = require("./powersoftau_contribute");