2020-07-11 11:31:52 +03:00
|
|
|
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) {
|
2020-05-11 21:23:04 +03:00
|
|
|
// Write the header
|
|
|
|
///////////
|
2020-05-26 19:45:49 +03:00
|
|
|
|
2020-05-27 22:02:25 +03:00
|
|
|
if (! ceremonyPower) ceremonyPower = power;
|
2020-05-11 21:23:04 +03:00
|
|
|
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);
|
|
|
|
|
2020-05-26 19:45:49 +03:00
|
|
|
const buff = new Uint8Array(curve.F1.n8);
|
2020-05-11 21:23:04 +03:00
|
|
|
Scalar.toRprLE(buff, 0, curve.q, curve.F1.n8);
|
|
|
|
await fd.write(buff);
|
|
|
|
await fd.writeULE32(power); // power
|
2020-05-26 19:45:49 +03:00
|
|
|
await fd.writeULE32(ceremonyPower); // power
|
2020-05-11 21:23:04 +03:00
|
|
|
|
|
|
|
const headerSize = fd.pos - pHeaderSize - 8;
|
|
|
|
|
|
|
|
const oldPos = fd.pos;
|
|
|
|
|
2020-07-28 14:02:24 +03:00
|
|
|
await fd.writeULE64(headerSize, pHeaderSize);
|
2020-05-11 21:23:04 +03:00
|
|
|
|
|
|
|
fd.pos = oldPos;
|
|
|
|
}
|
|
|
|
|
2020-07-11 11:31:52 +03:00
|
|
|
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");
|
2020-05-11 21:23:04 +03:00
|
|
|
|
|
|
|
fd.pos = sections[1][0].p;
|
|
|
|
const n8 = await fd.readULE32();
|
|
|
|
const buff = await fd.read(n8);
|
|
|
|
const q = Scalar.fromRprLE(buff);
|
2020-06-30 16:45:21 +03:00
|
|
|
|
|
|
|
const curve = await getCurveFromQ(q);
|
|
|
|
|
2020-07-11 11:31:52 +03:00
|
|
|
if (curve.F1.n64*8 != n8) throw new Error(fd.fileName +": Invalid size");
|
2020-05-11 21:23:04 +03:00
|
|
|
|
|
|
|
const power = await fd.readULE32();
|
2020-05-26 19:45:49 +03:00
|
|
|
const ceremonyPower = await fd.readULE32();
|
2020-05-11 21:23:04 +03:00
|
|
|
|
2020-07-11 11:31:52 +03:00
|
|
|
if (fd.pos-sections[1][0].p != sections[1][0].size) throw new Error("Invalid PTau header size");
|
2020-05-11 21:23:04 +03:00
|
|
|
|
2020-05-26 19:45:49 +03:00
|
|
|
return {curve, power, ceremonyPower};
|
2020-05-11 21:23:04 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-07-11 11:31:52 +03:00
|
|
|
export async function readPtauPubKey(fd, curve, montgomery) {
|
2020-05-11 21:23:04 +03:00
|
|
|
|
|
|
|
const buff = await fd.read(curve.F1.n8*2*6 + curve.F2.n8*2*3);
|
|
|
|
|
|
|
|
return fromPtauPubKeyRpr(buff, 0, curve, montgomery);
|
|
|
|
}
|
|
|
|
|
2020-07-11 11:31:52 +03:00
|
|
|
export function fromPtauPubKeyRpr(buff, pos, curve, montgomery) {
|
2020-05-11 21:23:04 +03:00
|
|
|
|
|
|
|
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 {
|
2020-06-30 16:45:21 +03:00
|
|
|
p = curve.G1.fromRprUncompressed( buff, pos );
|
2020-05-11 21:23:04 +03:00
|
|
|
}
|
|
|
|
pos += curve.G1.F.n8*2;
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
function readG2() {
|
|
|
|
let p;
|
|
|
|
if (montgomery) {
|
|
|
|
p = curve.G2.fromRprLEM( buff, pos );
|
|
|
|
} else {
|
2020-06-30 16:45:21 +03:00
|
|
|
p = curve.G2.fromRprUncompressed( buff, pos );
|
2020-05-11 21:23:04 +03:00
|
|
|
}
|
|
|
|
pos += curve.G2.F.n8*2;
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-11 11:31:52 +03:00
|
|
|
export function toPtauPubKeyRpr(buff, pos, curve, key, montgomery) {
|
2020-05-11 21:23:04 +03:00
|
|
|
|
|
|
|
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 {
|
2020-06-30 16:45:21 +03:00
|
|
|
curve.G1.toRprUncompressed(buff, pos, p);
|
2020-05-11 21:23:04 +03:00
|
|
|
}
|
|
|
|
pos += curve.F1.n8*2;
|
|
|
|
}
|
|
|
|
|
|
|
|
async function writeG2(p) {
|
|
|
|
if (montgomery) {
|
|
|
|
curve.G2.toRprLEM(buff, pos, p);
|
|
|
|
} else {
|
2020-06-30 16:45:21 +03:00
|
|
|
curve.G2.toRprUncompressed(buff, pos, p);
|
2020-05-11 21:23:04 +03:00
|
|
|
}
|
|
|
|
pos += curve.F2.n8*2;
|
|
|
|
}
|
|
|
|
|
|
|
|
return buff;
|
2020-05-10 11:10:42 +03:00
|
|
|
}
|
|
|
|
|
2020-07-11 11:31:52 +03:00
|
|
|
export async function writePtauPubKey(fd, curve, key, montgomery) {
|
2020-05-26 19:45:49 +03:00
|
|
|
const buff = new Uint8Array(curve.F1.n8*2*6 + curve.F2.n8*2*3);
|
2020-05-11 21:23:04 +03:00
|
|
|
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);
|
2020-05-26 19:45:49 +03:00
|
|
|
c.partialHash = await fd.read(216);
|
2020-07-14 12:55:12 +03:00
|
|
|
c.nextChallenge = await fd.read(64);
|
2020-05-15 22:30:37 +03:00
|
|
|
c.type = await fd.readULE32();
|
|
|
|
|
2020-05-26 19:45:49 +03:00
|
|
|
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();
|
|
|
|
|
2020-05-15 22:30:37 +03:00
|
|
|
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");
|
|
|
|
}
|
2020-05-11 21:23:04 +03:00
|
|
|
|
|
|
|
return c;
|
|
|
|
|
|
|
|
async function readG1() {
|
2020-06-30 16:45:21 +03:00
|
|
|
const pBuff = await fd.read(curve.G1.F.n8*2);
|
2020-05-11 21:23:04 +03:00
|
|
|
return curve.G1.fromRprLEM( pBuff );
|
|
|
|
}
|
|
|
|
|
|
|
|
async function readG2() {
|
2020-06-30 16:45:21 +03:00
|
|
|
const pBuff = await fd.read(curve.G2.F.n8*2);
|
2020-05-11 21:23:04 +03:00
|
|
|
return curve.G2.fromRprLEM( pBuff );
|
|
|
|
}
|
2020-05-15 22:30:37 +03:00
|
|
|
|
|
|
|
async function readDV(n) {
|
|
|
|
const b = await fd.read(n);
|
|
|
|
return new Uint8Array(b);
|
|
|
|
}
|
2020-05-11 21:23:04 +03:00
|
|
|
}
|
|
|
|
|
2020-07-11 11:31:52 +03:00
|
|
|
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");
|
2020-05-11 21:23:04 +03:00
|
|
|
|
|
|
|
fd.pos = sections[7][0].p;
|
|
|
|
const nContributions = await fd.readULE32();
|
|
|
|
const contributions = [];
|
|
|
|
for (let i=0; i<nContributions; i++) {
|
|
|
|
const c = await readContribution(fd, curve);
|
|
|
|
c.id = i+1;
|
|
|
|
contributions.push(c);
|
|
|
|
}
|
|
|
|
|
2020-07-11 11:31:52 +03:00
|
|
|
if (fd.pos-sections[7][0].p != sections[7][0].size) throw new Error("Invalid contribution section size");
|
2020-05-11 21:23:04 +03:00
|
|
|
|
|
|
|
return contributions;
|
|
|
|
}
|
|
|
|
|
|
|
|
async function writeContribution(fd, curve, contribution) {
|
|
|
|
|
2020-05-26 19:45:49 +03:00
|
|
|
const buffG1 = new Uint8Array(curve.F1.n8*2);
|
|
|
|
const buffG2 = new Uint8Array(curve.F2.n8*2);
|
2020-05-11 21:23:04 +03:00
|
|
|
await writeG1(contribution.tauG1);
|
|
|
|
await writeG2(contribution.tauG2);
|
|
|
|
await writeG1(contribution.alphaG1);
|
|
|
|
await writeG1(contribution.betaG1);
|
|
|
|
await writeG2(contribution.betaG2);
|
|
|
|
await writePtauPubKey(fd, curve, contribution.key, true);
|
|
|
|
await fd.write(contribution.partialHash);
|
2020-07-14 12:55:12 +03:00
|
|
|
await fd.write(contribution.nextChallenge);
|
2020-05-15 22:30:37 +03:00
|
|
|
await fd.writeULE32(contribution.type || 0);
|
|
|
|
|
|
|
|
const params = [];
|
|
|
|
if (contribution.name) {
|
|
|
|
params.push(1); // Param Name
|
|
|
|
const nameData = new TextEncoder("utf-8").encode(contribution.name.substring(0,64));
|
|
|
|
params.push(nameData.byteLength);
|
|
|
|
for (let i=0; i<nameData.byteLength; i++) params.push(nameData[i]);
|
|
|
|
}
|
|
|
|
if (contribution.type == 1) {
|
|
|
|
params.push(2); // Param numIterationsExp
|
|
|
|
params.push(contribution.numIterationsExp);
|
|
|
|
|
|
|
|
params.push(3); // Beacon Hash
|
|
|
|
params.push(contribution.beaconHash.byteLength);
|
|
|
|
for (let i=0; i<contribution.beaconHash.byteLength; i++) params.push(contribution.beaconHash[i]);
|
|
|
|
}
|
|
|
|
if (params.length>0) {
|
|
|
|
const paramsBuff = new Uint8Array(params);
|
|
|
|
await fd.writeULE32(paramsBuff.byteLength);
|
|
|
|
await fd.write(paramsBuff);
|
|
|
|
} else {
|
|
|
|
await fd.writeULE32(0);
|
|
|
|
}
|
2020-05-11 21:23:04 +03:00
|
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-07-11 11:31:52 +03:00
|
|
|
export async function writeContributions(fd, curve, contributions) {
|
2020-05-11 21:23:04 +03:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2020-07-28 14:02:24 +03:00
|
|
|
await fd.writeULE64(contributionsSize, pContributionsSize);
|
2020-05-11 21:23:04 +03:00
|
|
|
fd.pos = oldPos;
|
2020-05-10 11:10:42 +03:00
|
|
|
}
|
|
|
|
|
2020-07-14 12:55:12 +03:00
|
|
|
export function calculateFirstChallengeHash(curve, power, logger) {
|
|
|
|
if (logger) logger.debug("Calculating First Challenge Hash");
|
2020-05-27 22:02:25 +03:00
|
|
|
|
2020-05-11 21:23:04 +03:00
|
|
|
const hasher = new Blake2b(64);
|
|
|
|
|
2020-05-26 19:45:49 +03:00
|
|
|
const vG1 = new Uint8Array(curve.G1.F.n8*2);
|
|
|
|
const vG2 = new Uint8Array(curve.G2.F.n8*2);
|
2020-06-30 16:45:21 +03:00
|
|
|
curve.G1.toRprUncompressed(vG1, 0, curve.G1.g);
|
|
|
|
curve.G2.toRprUncompressed(vG2, 0, curve.G2.g);
|
2020-05-11 21:23:04 +03:00
|
|
|
|
2020-05-27 22:02:25 +03:00
|
|
|
hasher.update(Blake2b(64).digest());
|
2020-05-11 21:23:04 +03:00
|
|
|
|
|
|
|
let n;
|
2020-05-27 22:02:25 +03:00
|
|
|
|
2020-09-07 13:43:50 +03:00
|
|
|
n=(2 ** power)*2 -1;
|
2020-07-11 11:31:52 +03:00
|
|
|
if (logger) logger.debug("Calculate Initial Hash: tauG1");
|
2020-05-27 22:02:25 +03:00
|
|
|
hashBlock(vG1, n);
|
2020-09-07 13:43:50 +03:00
|
|
|
n= 2 ** power;
|
2020-07-11 11:31:52 +03:00
|
|
|
if (logger) logger.debug("Calculate Initial Hash: tauG2");
|
2020-05-27 22:02:25 +03:00
|
|
|
hashBlock(vG2, n);
|
2020-07-11 11:31:52 +03:00
|
|
|
if (logger) logger.debug("Calculate Initial Hash: alphaTauG1");
|
2020-05-27 22:02:25 +03:00
|
|
|
hashBlock(vG1, n);
|
2020-07-11 11:31:52 +03:00
|
|
|
if (logger) logger.debug("Calculate Initial Hash: betaTauG1");
|
2020-05-27 22:02:25 +03:00
|
|
|
hashBlock(vG1, n);
|
2020-05-11 21:23:04 +03:00
|
|
|
hasher.update(vG2);
|
|
|
|
|
|
|
|
return hasher.digest();
|
2020-05-27 22:02:25 +03:00
|
|
|
|
|
|
|
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<blockSize; i++) {
|
|
|
|
bigBuff.set(buff, i*buff.byteLength);
|
|
|
|
}
|
|
|
|
for (let i=0; i<nBlocks; i++) {
|
|
|
|
hasher.update(bigBuff);
|
2020-07-11 11:31:52 +03:00
|
|
|
if (logger) logger.debug("Initial hash: " +i*blockSize);
|
2020-05-27 22:02:25 +03:00
|
|
|
}
|
|
|
|
for (let i=0; i<rem; i++) {
|
|
|
|
hasher.update(buff);
|
|
|
|
}
|
|
|
|
}
|
2020-05-11 21:23:04 +03:00
|
|
|
}
|
|
|
|
|
2020-05-15 22:30:37 +03:00
|
|
|
|
2020-07-14 12:55:12 +03:00
|
|
|
export function keyFromBeacon(curve, challengeHash, beaconHash, numIterationsExp) {
|
2020-05-15 22:30:37 +03:00
|
|
|
|
2020-06-18 20:14:06 +03:00
|
|
|
const rng = misc.rngFromBeaconParams(beaconHash, numIterationsExp);
|
2020-05-15 22:30:37 +03:00
|
|
|
|
2020-07-14 12:55:12 +03:00
|
|
|
const key = keyPair.createPTauKey(curve, challengeHash, rng);
|
2020-05-15 22:30:37 +03:00
|
|
|
|
|
|
|
return key;
|
|
|
|
}
|
|
|
|
|