/* Copyright 2018 0KIMS association. This file is part of snarkJS. snarkJS is a free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. snarkJS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with snarkJS. If not, see . */ import { Scalar } from "ffjavascript"; import Blake2b from "blake2b-wasm"; import * as keyPair from "./keypair.js"; import * as misc from "./misc.js"; import { getCurveFromQ } from "./curves.js"; export async function writePTauHeader(fd, curve, power, ceremonyPower) { // Write the header /////////// if (! ceremonyPower) ceremonyPower = power; await fd.writeULE32(1); // Header type const pHeaderSize = fd.pos; await fd.writeULE64(0); // Temporally set to 0 length await fd.writeULE32(curve.F1.n64*8); const buff = new Uint8Array(curve.F1.n8); Scalar.toRprLE(buff, 0, curve.q, curve.F1.n8); await fd.write(buff); await fd.writeULE32(power); // power await fd.writeULE32(ceremonyPower); // power const headerSize = fd.pos - pHeaderSize - 8; const oldPos = fd.pos; await fd.writeULE64(headerSize, pHeaderSize); fd.pos = oldPos; } export async function readPTauHeader(fd, sections) { if (!sections[1]) throw new Error(fd.fileName + ": File has no header"); if (sections[1].length>1) throw new Error(fd.fileName +": File has more than one header"); fd.pos = sections[1][0].p; const n8 = await fd.readULE32(); const buff = await fd.read(n8); const q = Scalar.fromRprLE(buff); const curve = await getCurveFromQ(q); if (curve.F1.n64*8 != n8) throw new Error(fd.fileName +": Invalid size"); const power = await fd.readULE32(); const ceremonyPower = await fd.readULE32(); if (fd.pos-sections[1][0].p != sections[1][0].size) throw new Error("Invalid PTau header size"); return {curve, power, ceremonyPower}; } export async function readPtauPubKey(fd, curve, montgomery) { const buff = await fd.read(curve.F1.n8*2*6 + curve.F2.n8*2*3); return fromPtauPubKeyRpr(buff, 0, curve, montgomery); } export function fromPtauPubKeyRpr(buff, pos, curve, montgomery) { const key = { tau: {}, alpha: {}, beta: {} }; key.tau.g1_s = readG1(); key.tau.g1_sx = readG1(); key.alpha.g1_s = readG1(); key.alpha.g1_sx = readG1(); key.beta.g1_s = readG1(); key.beta.g1_sx = readG1(); key.tau.g2_spx = readG2(); key.alpha.g2_spx = readG2(); key.beta.g2_spx = readG2(); return key; function readG1() { let p; if (montgomery) { p = curve.G1.fromRprLEM( buff, pos ); } else { p = curve.G1.fromRprUncompressed( buff, pos ); } pos += curve.G1.F.n8*2; return p; } function readG2() { let p; if (montgomery) { p = curve.G2.fromRprLEM( buff, pos ); } else { p = curve.G2.fromRprUncompressed( buff, pos ); } pos += curve.G2.F.n8*2; return p; } } export function toPtauPubKeyRpr(buff, pos, curve, key, montgomery) { writeG1(key.tau.g1_s); writeG1(key.tau.g1_sx); writeG1(key.alpha.g1_s); writeG1(key.alpha.g1_sx); writeG1(key.beta.g1_s); writeG1(key.beta.g1_sx); writeG2(key.tau.g2_spx); writeG2(key.alpha.g2_spx); writeG2(key.beta.g2_spx); async function writeG1(p) { if (montgomery) { curve.G1.toRprLEM(buff, pos, p); } else { curve.G1.toRprUncompressed(buff, pos, p); } pos += curve.F1.n8*2; } async function writeG2(p) { if (montgomery) { curve.G2.toRprLEM(buff, pos, p); } else { curve.G2.toRprUncompressed(buff, pos, p); } pos += curve.F2.n8*2; } return buff; } export async function writePtauPubKey(fd, curve, key, montgomery) { const buff = new Uint8Array(curve.F1.n8*2*6 + curve.F2.n8*2*3); toPtauPubKeyRpr(buff, 0, curve, key, montgomery); await fd.write(buff); } async function readContribution(fd, curve) { const c = {}; c.tauG1 = await readG1(); c.tauG2 = await readG2(); c.alphaG1 = await readG1(); c.betaG1 = await readG1(); c.betaG2 = await readG2(); c.key = await readPtauPubKey(fd, curve, true); c.partialHash = await fd.read(216); c.nextChallenge = await fd.read(64); c.type = await fd.readULE32(); const buffV = new Uint8Array(curve.G1.F.n8*2*6+curve.G2.F.n8*2*3); toPtauPubKeyRpr(buffV, 0, curve, c.key, false); const responseHasher = Blake2b(64); responseHasher.setPartialHash(c.partialHash); responseHasher.update(buffV); c.responseHash = responseHasher.digest(); const paramLength = await fd.readULE32(); const curPos = fd.pos; let lastType =0; while (fd.pos-curPos < paramLength) { const buffType = await readDV(1); if (buffType[0]<= lastType) throw new Error("Parameters in the contribution must be sorted"); lastType = buffType[0]; if (buffType[0]==1) { // Name const buffLen = await readDV(1); const buffStr = await readDV(buffLen[0]); c.name = new TextDecoder().decode(buffStr); } else if (buffType[0]==2) { const buffExp = await readDV(1); c.numIterationsExp = buffExp[0]; } else if (buffType[0]==3) { const buffLen = await readDV(1); c.beaconHash = await readDV(buffLen[0]); } else { throw new Error("Parameter not recognized"); } } if (fd.pos != curPos + paramLength) { throw new Error("Parametes do not match"); } return c; async function readG1() { const pBuff = await fd.read(curve.G1.F.n8*2); return curve.G1.fromRprLEM( pBuff ); } async function readG2() { const pBuff = await fd.read(curve.G2.F.n8*2); return curve.G2.fromRprLEM( pBuff ); } async function readDV(n) { const b = await fd.read(n); return new Uint8Array(b); } } export async function readContributions(fd, curve, sections) { if (!sections[7]) throw new Error(fd.fileName + ": File has no contributions"); if (sections[7][0].length>1) throw new Error(fd.fileName +": File has more than one contributions section"); fd.pos = sections[7][0].p; const nContributions = await fd.readULE32(); const contributions = []; 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) { curve.G1.toRprLEM(buffG1, 0, p); await fd.write(buffG1); } async function writeG2(p) { curve.G2.toRprLEM(buffG2, 0, p); await fd.write(buffG2); } } export async function writeContributions(fd, curve, contributions) { await fd.writeULE32(7); // Header type const pContributionsSize = fd.pos; await fd.writeULE64(0); // Temporally set to 0 length await fd.writeULE32(contributions.length); for (let i=0; i< contributions.length; i++) { await writeContribution(fd, curve, contributions[i]); } const contributionsSize = fd.pos - pContributionsSize - 8; const oldPos = fd.pos; await fd.writeULE64(contributionsSize, pContributionsSize); fd.pos = oldPos; } export function calculateFirstChallengeHash(curve, power, logger) { if (logger) logger.debug("Calculating First Challenge Hash"); const hasher = new Blake2b(64); const vG1 = new Uint8Array(curve.G1.F.n8*2); const vG2 = new Uint8Array(curve.G2.F.n8*2); curve.G1.toRprUncompressed(vG1, 0, curve.G1.g); curve.G2.toRprUncompressed(vG2, 0, curve.G2.g); hasher.update(Blake2b(64).digest()); let n; n=(2 ** power)*2 -1; if (logger) logger.debug("Calculate Initial Hash: tauG1"); hashBlock(vG1, n); n= 2 ** power; if (logger) logger.debug("Calculate Initial Hash: tauG2"); hashBlock(vG2, n); if (logger) logger.debug("Calculate Initial Hash: alphaTauG1"); hashBlock(vG1, n); if (logger) logger.debug("Calculate Initial Hash: betaTauG1"); hashBlock(vG1, n); hasher.update(vG2); return hasher.digest(); function hashBlock(buff, n) { const blockSize = 500000; const nBlocks = Math.floor(n / blockSize); const rem = n % blockSize; const bigBuff = new Uint8Array(blockSize * buff.byteLength); for (let i=0; i