mirror of
https://github.com/CryptKeeperZK/ffjavascript.git
synced 2026-01-10 00:27:56 -05:00
bls12-381 working
This commit is contained in:
3
index.js
3
index.js
@@ -9,7 +9,8 @@ exports.ZqField = exports.F1Field;
|
||||
|
||||
exports.EC = require("./src/ec");
|
||||
|
||||
exports.bn128 = require("./src/bn128.js");
|
||||
exports.buildBn128 = require("./src/bn128.js");
|
||||
exports.buildBls12381 = require("./src/bls12381.js");
|
||||
|
||||
exports.utils = require("./src/utils");
|
||||
exports.ChaCha = require("./src/chacha");
|
||||
|
||||
25
src/bls12381.js
Normal file
25
src/bls12381.js
Normal file
@@ -0,0 +1,25 @@
|
||||
const bls12381_wasm = require("wasmsnark").bls12381_wasm;
|
||||
const buildEngine = require("./engine");
|
||||
const Scalar = require("./scalar");
|
||||
|
||||
let curve;
|
||||
|
||||
module.exports = async function buildBls12381() {
|
||||
|
||||
if (curve) return curve;
|
||||
const params = {
|
||||
name: "bls12381",
|
||||
wasm: bls12381_wasm,
|
||||
q: Scalar.e("1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab", 16),
|
||||
r: Scalar.e("73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001", 16),
|
||||
n8q: 48,
|
||||
n8r: 32,
|
||||
cofactorG1: Scalar.e("0x396c8c005555e1568c00aaab0000aaab", 16),
|
||||
cofactorG2: Scalar.e("0x5d543a95414e7f1091d50792876a202cd91de4547085abaa68a205b2e5a7ddfa628f1cb4d9e82ef21537e293a6691ae1616ec6e786f0c70cf1c38e31c7238e5", 16),
|
||||
singleThread: false
|
||||
};
|
||||
|
||||
curve = await buildEngine(params);
|
||||
return curve;
|
||||
};
|
||||
|
||||
744
src/bn128.js
744
src/bn128.js
@@ -1,732 +1,24 @@
|
||||
/*
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const Scalar = require("./scalar");
|
||||
const F1Field = require("./f1field");
|
||||
const F2Field = require("./f2field");
|
||||
const F3Field = require("./f3field");
|
||||
const PolField = require("./polfield");
|
||||
const EC = require("./ec.js");
|
||||
const buildEngine = require("./engine");
|
||||
const bn128_wasm = require("wasmsnark").bn128_wasm;
|
||||
const buildEngine = require("./engine");
|
||||
const Scalar = require("./scalar");
|
||||
|
||||
let curve;
|
||||
|
||||
let engine = null;
|
||||
module.exports = async function buildBn128() {
|
||||
|
||||
if (curve) return curve;
|
||||
const params = {
|
||||
name: "bn128",
|
||||
wasm: bn128_wasm,
|
||||
q: Scalar.e("21888242871839275222246405745257275088696311157297823662689037894645226208583"),
|
||||
r: Scalar.e("21888242871839275222246405745257275088548364400416034343698204186575808495617"),
|
||||
n8q: 32,
|
||||
n8r: 32,
|
||||
cofactorG2: Scalar.e("30644e72e131a029b85045b68181585e06ceecda572a2489345f2299c0f9fa8d", 16),
|
||||
singleThread: false
|
||||
};
|
||||
|
||||
class BN128 {
|
||||
curve = await buildEngine(params);
|
||||
return curve;
|
||||
};
|
||||
|
||||
constructor() {
|
||||
|
||||
this.name = "bn128";
|
||||
this.q = Scalar.fromString("21888242871839275222246405745257275088696311157297823662689037894645226208583");
|
||||
this.r = Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617");
|
||||
|
||||
|
||||
this.F1 = new F1Field(this.q);
|
||||
this.nonResidueF2 = this.F1.e("21888242871839275222246405745257275088696311157297823662689037894645226208582");
|
||||
|
||||
this.F2 = new F2Field(this.F1, this.nonResidueF2);
|
||||
this.g1 = [ this.F1.e(1), this.F1.e(2), this.F1.e(1)];
|
||||
this.g2 = [
|
||||
[
|
||||
this.F1.e("10857046999023057135944570762232829481370756359578518086990519993285655852781"),
|
||||
this.F1.e("11559732032986387107991004021392285783925812861821192530917403151452391805634")
|
||||
],
|
||||
[
|
||||
this.F1.e("8495653923123431417604973247489272438418190587263600148770280649306958101930"),
|
||||
this.F1.e("4082367875863433681332203403145435568316851327593401208105741076214120093531")
|
||||
],
|
||||
[
|
||||
this.F1.e("1"),
|
||||
this.F1.e("0")
|
||||
]
|
||||
];
|
||||
this.G1 = new EC(this.F1, this.g1);
|
||||
this.G2 = new EC(this.F2, this.g2);
|
||||
|
||||
this.G1.b = this.F1.e(3);
|
||||
this.G2.b = this.F2.div([this.F1.e(3),this.F1.e(0)], [this.F1.e(9), this.F1.e(1)]);
|
||||
this.G2.cofactor = Scalar.e("0x30644e72e131a029b85045b68181585e06ceecda572a2489345f2299c0f9fa8d");
|
||||
|
||||
this.nonResidueF6 = [ this.F1.e("9"), this.F1.e("1") ];
|
||||
|
||||
this.F6 = new F3Field(this.F2, this.nonResidueF6);
|
||||
this.F12 = new F2Field(this.F6, this.nonResidueF6);
|
||||
this.Fr = new F1Field(this.r);
|
||||
this.PFr = new PolField(this.Fr);
|
||||
this.Gt = new F2Field(this.F6, this.nonResidueF6);
|
||||
this.Gt.neg = this.F12.conjugate.bind(this.Gt);
|
||||
|
||||
|
||||
const self = this;
|
||||
this.F12._mulByNonResidue = function(a) {
|
||||
return [self.F2.mul(this.nonResidue, a[2]), a[0], a[1]];
|
||||
};
|
||||
|
||||
|
||||
this.PrePSize= 192;
|
||||
this.PreQSize= 19776;
|
||||
|
||||
this._preparePairing();
|
||||
|
||||
this.G1.batchApplyKey = this.batchApplyKeyG1.bind(this);
|
||||
this.G2.batchApplyKey = this.batchApplyKeyG2.bind(this);
|
||||
this.Fr.batchApplyKey = this.batchApplyKeyFr.bind(this);
|
||||
this.G1.batchLEMtoU = this.batchLEMtoUG1.bind(this);
|
||||
this.G2.batchLEMtoU = this.batchLEMtoUG2.bind(this);
|
||||
this.G1.batchLEMtoC = this.batchLEMtoCG1.bind(this);
|
||||
this.G2.batchLEMtoC = this.batchLEMtoCG2.bind(this);
|
||||
this.G1.batchUtoLEM = this.batchUtoLEMG1.bind(this);
|
||||
this.G2.batchUtoLEM = this.batchUtoLEMG2.bind(this);
|
||||
this.G1.batchCtoLEM = this.batchCtoLEMG1.bind(this);
|
||||
this.G2.batchCtoLEM = this.batchCtoLEMG2.bind(this);
|
||||
this.G1.batchToJacobian = this.batchToJacobianG1.bind(this);
|
||||
this.G2.batchToJacobian = this.batchToJacobianG2.bind(this);
|
||||
this.G1.batchToAffine = this.batchToAffineG1.bind(this);
|
||||
this.G2.batchToAffine = this.batchToAffineG2.bind(this);
|
||||
this.G1.ifft = this.ifftG1.bind(this);
|
||||
this.G1.fft = this.fftG1.bind(this);
|
||||
this.G2.ifft = this.ifftG2.bind(this);
|
||||
this.G2.fft = this.fftG2.bind(this);
|
||||
this.G1.fftMix = this.fftMixG1.bind(this);
|
||||
this.G1.fftJoin = this.fftJoinG1.bind(this);
|
||||
this.G1.fftFinal = this.fftFinalG1.bind(this);
|
||||
this.G2.fftMix = this.fftMixG2.bind(this);
|
||||
this.G2.fftJoin = this.fftJoinG2.bind(this);
|
||||
this.G2.fftFinal = this.fftFinalG2.bind(this);
|
||||
|
||||
this.Fr.batchToMontgomery = this.batchToMontgomeryFr.bind(this);
|
||||
this.Fr.batchFromMontgomery = this.batchFromMontgomeryFr.bind(this);
|
||||
this.Fr.fft = this.fftFr.bind(this);
|
||||
this.Fr.ifft = this.ifftFr.bind(this);
|
||||
|
||||
this.G1.multiExpAffine = this.multiExpAffineG1.bind(this);
|
||||
this.G2.multiExpAffine = this.multiExpAffineG2.bind(this);
|
||||
this.G1.multiExp = this.multiExpG1.bind(this);
|
||||
this.G2.multiExp = this.multiExpG2.bind(this);
|
||||
}
|
||||
|
||||
async loadEngine() {
|
||||
const self = this;
|
||||
if (!engine) {
|
||||
engine = await buildEngine(this, bn128_wasm /* , true */ ); // Set single Trherad tot true to debug
|
||||
}
|
||||
self.engine = engine;
|
||||
}
|
||||
|
||||
async batchApplyKeyG1(buff, first, inc, inType, outType) {
|
||||
await this.loadEngine();
|
||||
const res = await engine.batchApplyKey("G1", buff, first, inc, inType, outType);
|
||||
return res;
|
||||
}
|
||||
|
||||
async batchApplyKeyG2(buff, first, inc, inType, outType) {
|
||||
await this.loadEngine();
|
||||
const res = await engine.batchApplyKey("G2", buff, first, inc, inType, outType);
|
||||
return res;
|
||||
}
|
||||
|
||||
async batchApplyKeyFr(buff, first, inc) {
|
||||
await this.loadEngine();
|
||||
const res = await engine.batchApplyKey("Fr", buff, first, inc);
|
||||
return res;
|
||||
}
|
||||
|
||||
async batchLEMtoUG1(buff) {
|
||||
await this.loadEngine();
|
||||
// const res = await engine.batchConvert("G1", "LEM", "U", buff );
|
||||
const res = await engine.batchConvert("g1m_batchLEMtoU", buff, this.F1.n8*2, this.F1.n8*2);
|
||||
return res;
|
||||
}
|
||||
|
||||
async batchLEMtoUG2(buff) {
|
||||
await this.loadEngine();
|
||||
// const res = await engine.batchConvert("G2", "LEM", "U",buff);
|
||||
const res = await engine.batchConvert("g2m_batchLEMtoU", buff, this.F2.n8*2, this.F2.n8*2);
|
||||
return res;
|
||||
}
|
||||
|
||||
async batchLEMtoCG1(buff) {
|
||||
await this.loadEngine();
|
||||
// const res = await engine.batchConvert("G1", "LEM", "C", buff);
|
||||
const res = await engine.batchConvert("g1m_batchLEMtoC", buff, this.F1.n8*2, this.F1.n8);
|
||||
return res;
|
||||
}
|
||||
|
||||
async batchLEMtoCG2(buff) {
|
||||
await this.loadEngine();
|
||||
// const res = await engine.batchConvert("G2", "LEM", "C", buff);
|
||||
const res = await engine.batchConvert("g2m_batchLEMtoC", buff, this.F2.n8*2, this.F2.n8);
|
||||
return res;
|
||||
}
|
||||
/////
|
||||
|
||||
async batchUtoLEMG1(buff) {
|
||||
await this.loadEngine();
|
||||
// const res = await engine.batchConvert("G1", "LEM", "U", buff );
|
||||
const res = await engine.batchConvert("g1m_batchUtoLEM", buff, this.F1.n8*2, this.F1.n8*2);
|
||||
return res;
|
||||
}
|
||||
|
||||
async batchUtoLEMG2(buff) {
|
||||
await this.loadEngine();
|
||||
// const res = await engine.batchConvert("G2", "LEM", "U",buff);
|
||||
const res = await engine.batchConvert("g2m_batchUtoLEM", buff, this.F2.n8*2, this.F2.n8*2);
|
||||
return res;
|
||||
}
|
||||
|
||||
async batchCtoLEMG1(buff) {
|
||||
await this.loadEngine();
|
||||
// const res = await engine.batchConvert("G1", "LEM", "C", buff);
|
||||
const res = await engine.batchConvert("g1m_batchCtoLEM", buff, this.F1.n8, this.F1.n8*2);
|
||||
return res;
|
||||
}
|
||||
|
||||
async batchCtoLEMG2(buff) {
|
||||
await this.loadEngine();
|
||||
// const res = await engine.batchConvert("G2", "LEM", "C", buff);
|
||||
const res = await engine.batchConvert("g2m_batchCtoLEM", buff, this.F2.n8, this.F2.n8*2);
|
||||
return res;
|
||||
}
|
||||
|
||||
async batchToMontgomeryFr(buff) {
|
||||
await this.loadEngine();
|
||||
const res = await engine.batchConvert("frm_batchToMontgomery", buff, this.Fr.n8, this.Fr.n8);
|
||||
return res;
|
||||
}
|
||||
|
||||
async batchFromMontgomeryFr(buff) {
|
||||
await this.loadEngine();
|
||||
const res = await engine.batchConvert("frm_batchFromMontgomery", buff, this.Fr.n8, this.Fr.n8);
|
||||
return res;
|
||||
}
|
||||
|
||||
async batchToAffineG1(buff) {
|
||||
await this.loadEngine();
|
||||
const res = await engine.batchConvert("g1m_batchToAffine", buff, this.G1.F.n8*3, this.G1.F.n8*2);
|
||||
return res;
|
||||
}
|
||||
|
||||
async batchToAffineG2(buff) {
|
||||
await this.loadEngine();
|
||||
const res = await engine.batchConvert("g2m_batchToAffine", buff, this.G2.F.n8*3, this.G2.F.n8*2);
|
||||
return res;
|
||||
}
|
||||
|
||||
async batchToJacobianG1(buff) {
|
||||
await this.loadEngine();
|
||||
const res = await engine.batchConvert("g1m_batchToJacobian", buff, this.G1.F.n8*2, this.G1.F.n8*3);
|
||||
return res;
|
||||
}
|
||||
|
||||
async batchToJacobianG2(buff) {
|
||||
await this.loadEngine();
|
||||
const res = await engine.batchConvert("g2m_batchToJacobian", buff, this.G2.F.n8*2, this.G2.F.n8*3);
|
||||
return res;
|
||||
}
|
||||
|
||||
async fftG1(buff, inType, outType, log) {
|
||||
await this.loadEngine();
|
||||
const res = await engine.fft("G1", buff, false, inType, outType, log);
|
||||
return res;
|
||||
}
|
||||
|
||||
async ifftG1(buff, inType, outType, log) {
|
||||
await this.loadEngine();
|
||||
const res = await engine.fft("G1", buff, true, inType, outType, log);
|
||||
return res;
|
||||
}
|
||||
|
||||
async fftG2(buff, inType, outType, log) {
|
||||
await this.loadEngine();
|
||||
const res = await engine.fft("G2", buff, false, inType, outType, log);
|
||||
return res;
|
||||
}
|
||||
|
||||
async ifftG2(buff, inType, outType, log) {
|
||||
await this.loadEngine();
|
||||
const res = await engine.fft("G2", buff, true, inType, outType, log);
|
||||
return res;
|
||||
}
|
||||
|
||||
async fftFr(buff, log) {
|
||||
await this.loadEngine();
|
||||
const res = await engine.fft("Fr", buff, false, undefined, undefined, log);
|
||||
return res;
|
||||
}
|
||||
|
||||
async ifftFr(buff, log) {
|
||||
await this.loadEngine();
|
||||
const res = await engine.fft("Fr", buff, true, undefined, undefined, log);
|
||||
return res;
|
||||
}
|
||||
|
||||
async fftMixG1(buff, log) {
|
||||
await this.loadEngine();
|
||||
const res = await engine.fftMix("G1", buff, log);
|
||||
return res;
|
||||
}
|
||||
|
||||
async fftJoinG1(buff1, buff2, first, inc, log) {
|
||||
await this.loadEngine();
|
||||
const res = await engine.fftJoin("G1", buff1, buff2, first, inc, log);
|
||||
return res;
|
||||
}
|
||||
|
||||
async fftFinalG1(buff1, factor, log) {
|
||||
await this.loadEngine();
|
||||
const res = await engine.fftFinal("G1", buff1, factor, log);
|
||||
return res;
|
||||
}
|
||||
|
||||
async fftMixG2(buff, log) {
|
||||
await this.loadEngine();
|
||||
const res = await engine.fftMix("G2", buff, log);
|
||||
return res;
|
||||
}
|
||||
|
||||
async fftJoinG2(buff1, buff2, first, inc, log) {
|
||||
await this.loadEngine();
|
||||
const res = await engine.fftJoin("G2", buff1, buff2, first, inc, log);
|
||||
return res;
|
||||
}
|
||||
|
||||
async fftFinalG2(buff1, factor, log) {
|
||||
await this.loadEngine();
|
||||
const res = await engine.fftFinal("G2", buff1, factor, log);
|
||||
return res;
|
||||
}
|
||||
|
||||
async multiExpAffineG1(buffBases, buffScalars) {
|
||||
await this.loadEngine();
|
||||
const res = await engine.multiExp("G1", buffBases, buffScalars, "affine");
|
||||
return res;
|
||||
}
|
||||
|
||||
async multiExpAffineG2(buffBases, buffScalars) {
|
||||
await this.loadEngine();
|
||||
const res = await engine.multiExp("G2", buffBases, buffScalars, "affine");
|
||||
return res;
|
||||
}
|
||||
|
||||
async multiExpG1(buffBases, buffScalars) {
|
||||
await this.loadEngine();
|
||||
const res = await engine.multiExp("G1", buffBases, buffScalars, "jacobian");
|
||||
return res;
|
||||
}
|
||||
|
||||
async multiExpG2(buffBases, buffScalars) {
|
||||
await this.loadEngine();
|
||||
const res = await engine.multiExp("G2", buffBases, buffScalars, "jacobian");
|
||||
return res;
|
||||
}
|
||||
|
||||
async pairingEq() {
|
||||
const self = this;
|
||||
await self.loadEngine();
|
||||
const res = await engine.pairingEq(...arguments);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
_preparePairing() {
|
||||
this.loopCount = Scalar.fromString("29793968203157093288");// CONSTANT
|
||||
|
||||
// Set loopCountNeg
|
||||
if (Scalar.isNegative(this.loopCount)) {
|
||||
this.loopCount = this.loopCount.neg();
|
||||
this.loopCountNeg = true;
|
||||
} else {
|
||||
this.loopCountNeg = false;
|
||||
}
|
||||
|
||||
// Set loop_count_bits
|
||||
let lc = this.loopCount;
|
||||
this.loop_count_bits = []; // Constant
|
||||
while (!Scalar.isZero(lc)) {
|
||||
this.loop_count_bits.push( Scalar.isOdd(lc) );
|
||||
lc = Scalar.shiftRight(lc, 1);
|
||||
}
|
||||
|
||||
this.two_inv = this.F1.inv(this.F1.e(2));
|
||||
|
||||
this.coef_b = this.F1.e(3);
|
||||
this.twist = [this.F1.e(9) , this.F1.e(1)];
|
||||
this.twist_coeff_b = this.F2.mulScalar( this.F2.inv(this.twist), this.coef_b );
|
||||
|
||||
this.frobenius_coeffs_c1_1 = this.F1.e("21888242871839275222246405745257275088696311157297823662689037894645226208582");
|
||||
this.twist_mul_by_q_X =
|
||||
[
|
||||
this.F1.e("21575463638280843010398324269430826099269044274347216827212613867836435027261"),
|
||||
this.F1.e("10307601595873709700152284273816112264069230130616436755625194854815875713954")
|
||||
];
|
||||
this.twist_mul_by_q_Y =
|
||||
[
|
||||
this.F1.e("2821565182194536844548159561693502659359617185244120367078079554186484126554"),
|
||||
this.F1.e("3505843767911556378687030309984248845540243509899259641013678093033130930403")
|
||||
];
|
||||
|
||||
this.final_exponent = Scalar.fromString("552484233613224096312617126783173147097382103762957654188882734314196910839907541213974502761540629817009608548654680343627701153829446747810907373256841551006201639677726139946029199968412598804882391702273019083653272047566316584365559776493027495458238373902875937659943504873220554161550525926302303331747463515644711876653177129578303191095900909191624817826566688241804408081892785725967931714097716709526092261278071952560171111444072049229123565057483750161460024353346284167282452756217662335528813519139808291170539072125381230815729071544861602750936964829313608137325426383735122175229541155376346436093930287402089517426973178917569713384748081827255472576937471496195752727188261435633271238710131736096299798168852925540549342330775279877006784354801422249722573783561685179618816480037695005515426162362431072245638324744480");
|
||||
|
||||
}
|
||||
|
||||
async pairing(p1, p2) {
|
||||
const self = this;
|
||||
await self.loadEngine();
|
||||
const res = await engine.pairing(p1, p2);
|
||||
return res;
|
||||
}
|
||||
|
||||
pairing_old(p1, p2) {
|
||||
|
||||
const pre1 = this.precomputeG1(p1);
|
||||
const pre2 = this.precomputeG2(p2);
|
||||
|
||||
const r1 = this.millerLoop(pre1, pre2);
|
||||
|
||||
const res = this.finalExponentiation(r1);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
precomputeG1(p) {
|
||||
const Pcopy = this.G1.affine(p);
|
||||
|
||||
const res = {};
|
||||
res.PX = Pcopy[0];
|
||||
res.PY = Pcopy[1];
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
precomputeG2(p) {
|
||||
|
||||
const Qcopy = this.G2.affine(p);
|
||||
|
||||
const res = {
|
||||
QX: Qcopy[0],
|
||||
QY: Qcopy[1],
|
||||
coeffs: []
|
||||
};
|
||||
|
||||
const R = {
|
||||
X: Qcopy[0],
|
||||
Y: Qcopy[1],
|
||||
Z: this.F2.one
|
||||
};
|
||||
|
||||
let c;
|
||||
|
||||
for (let i = this.loop_count_bits.length-2; i >= 0; --i)
|
||||
{
|
||||
const bit = this.loop_count_bits[i];
|
||||
|
||||
c = this._doubleStep(R);
|
||||
res.coeffs.push(c);
|
||||
|
||||
if (bit)
|
||||
{
|
||||
c = this._addStep(Qcopy, R);
|
||||
res.coeffs.push(c);
|
||||
}
|
||||
}
|
||||
|
||||
const Q1 = this.G2.affine(this._g2MulByQ(Qcopy));
|
||||
if (!this.F2.eq(Q1[2], this.F2.one))
|
||||
{
|
||||
throw new Error("Expected values are not equal");
|
||||
}
|
||||
const Q2 = this.G2.affine(this._g2MulByQ(Q1));
|
||||
if (!this.F2.eq(Q2[2], this.F2.one))
|
||||
{
|
||||
throw new Error("Expected values are not equal");
|
||||
}
|
||||
|
||||
if (this.loopCountNeg)
|
||||
{
|
||||
R.Y = this.F2.neg(R.Y);
|
||||
}
|
||||
Q2[1] = this.F2.neg(Q2[1]);
|
||||
|
||||
c = this._addStep(Q1, R);
|
||||
res.coeffs.push(c);
|
||||
|
||||
c = this._addStep(Q2, R);
|
||||
res.coeffs.push(c);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
millerLoop(pre1, pre2) {
|
||||
let f = this.F12.one;
|
||||
|
||||
let idx = 0;
|
||||
|
||||
let c;
|
||||
|
||||
for (let i = this.loop_count_bits.length-2; i >= 0; --i)
|
||||
{
|
||||
const bit = this.loop_count_bits[i];
|
||||
|
||||
/* code below gets executed for all bits (EXCEPT the MSB itself) of
|
||||
alt_bn128_param_p (skipping leading zeros) in MSB to LSB
|
||||
order */
|
||||
|
||||
c = pre2.coeffs[idx++];
|
||||
f = this.F12.square(f);
|
||||
f = this._mul_by_024(
|
||||
f,
|
||||
c.ell_0,
|
||||
this.F2.mulScalar(c.ell_VW , pre1.PY),
|
||||
this.F2.mulScalar(c.ell_VV , pre1.PX));
|
||||
|
||||
if (bit)
|
||||
{
|
||||
c = pre2.coeffs[idx++];
|
||||
f = this._mul_by_024(
|
||||
f,
|
||||
c.ell_0,
|
||||
this.F2.mulScalar(c.ell_VW, pre1.PY),
|
||||
this.F2.mulScalar(c.ell_VV, pre1.PX));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (this.loopCountNeg)
|
||||
{
|
||||
f = this.F12.inverse(f);
|
||||
}
|
||||
|
||||
c = pre2.coeffs[idx++];
|
||||
f = this._mul_by_024(
|
||||
f,
|
||||
c.ell_0,
|
||||
this.F2.mulScalar(c.ell_VW, pre1.PY),
|
||||
this.F2.mulScalar(c.ell_VV, pre1.PX));
|
||||
|
||||
c = pre2.coeffs[idx++];
|
||||
f = this._mul_by_024(
|
||||
f,
|
||||
c.ell_0,
|
||||
this.F2.mulScalar(c.ell_VW, pre1.PY),
|
||||
this.F2.mulScalar(c.ell_VV, pre1.PX));
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
finalExponentiation(elt) {
|
||||
// TODO: There is an optimization in FF
|
||||
|
||||
const res = this.F12.pow(elt,this.final_exponent);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
_doubleStep(current) {
|
||||
const X = current.X;
|
||||
const Y = current.Y;
|
||||
const Z = current.Z;
|
||||
|
||||
const A = this.F2.mulScalar(this.F2.mul(X,Y), this.two_inv); // A = X1 * Y1 / 2
|
||||
const B = this.F2.square(Y); // B = Y1^2
|
||||
const C = this.F2.square(Z); // C = Z1^2
|
||||
const D = this.F2.add(C, this.F2.add(C,C)); // D = 3 * C
|
||||
const E = this.F2.mul(this.twist_coeff_b, D); // E = twist_b * D
|
||||
const F = this.F2.add(E, this.F2.add(E,E)); // F = 3 * E
|
||||
const G =
|
||||
this.F2.mulScalar(
|
||||
this.F2.add( B , F ),
|
||||
this.two_inv); // G = (B+F)/2
|
||||
const H =
|
||||
this.F2.sub(
|
||||
this.F2.square( this.F2.add(Y,Z) ),
|
||||
this.F2.add( B , C)); // H = (Y1+Z1)^2-(B+C)
|
||||
const I = this.F2.sub(E, B); // I = E-B
|
||||
const J = this.F2.square(X); // J = X1^2
|
||||
const E_squared = this.F2.square(E); // E_squared = E^2
|
||||
|
||||
current.X = this.F2.mul( A, this.F2.sub(B,F) ); // X3 = A * (B-F)
|
||||
current.Y =
|
||||
this.F2.sub(
|
||||
this.F2.sub( this.F2.square(G) , E_squared ),
|
||||
this.F2.add( E_squared , E_squared )); // Y3 = G^2 - 3*E^2
|
||||
current.Z = this.F2.mul( B, H ); // Z3 = B * H
|
||||
|
||||
const c = {
|
||||
ell_0 : this.F2.mul( I, this.twist), // ell_0 = xi * I
|
||||
ell_VW: this.F2.neg( H ), // ell_VW = - H (later: * yP)
|
||||
ell_VV: this.F2.add( J , this.F2.add(J,J) ) // ell_VV = 3*J (later: * xP)
|
||||
};
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
_addStep(base, current) {
|
||||
|
||||
const X1 = current.X;
|
||||
const Y1 = current.Y;
|
||||
const Z1 = current.Z;
|
||||
const x2 = base[0];
|
||||
const y2 = base[1];
|
||||
|
||||
const D = this.F2.sub( X1, this.F2.mul(x2,Z1) ); // D = X1 - X2*Z1
|
||||
|
||||
// console.log("Y: "+ A[0].affine(this.q).toString(16));
|
||||
|
||||
const E = this.F2.sub( Y1, this.F2.mul(y2,Z1) ); // E = Y1 - Y2*Z1
|
||||
const F = this.F2.square(D); // F = D^2
|
||||
const G = this.F2.square(E); // G = E^2
|
||||
const H = this.F2.mul(D,F); // H = D*F
|
||||
const I = this.F2.mul(X1,F); // I = X1 * F
|
||||
const J =
|
||||
this.F2.sub(
|
||||
this.F2.add( H, this.F2.mul(Z1,G) ),
|
||||
this.F2.add( I, I )); // J = H + Z1*G - (I+I)
|
||||
|
||||
current.X = this.F2.mul( D , J ); // X3 = D*J
|
||||
current.Y =
|
||||
this.F2.sub(
|
||||
this.F2.mul( E , this.F2.sub(I,J) ),
|
||||
this.F2.mul( H , Y1)); // Y3 = E*(I-J)-(H*Y1)
|
||||
current.Z = this.F2.mul(Z1,H);
|
||||
const c = {
|
||||
ell_0 :
|
||||
this.F2.mul(
|
||||
this.twist,
|
||||
this.F2.sub(
|
||||
this.F2.mul(E , x2),
|
||||
this.F2.mul(D , y2))), // ell_0 = xi * (E * X2 - D * Y2)
|
||||
ell_VV : this.F2.neg(E), // ell_VV = - E (later: * xP)
|
||||
ell_VW : D // ell_VW = D (later: * yP )
|
||||
};
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
_mul_by_024(a, ell_0, ell_VW, ell_VV) {
|
||||
|
||||
// Old implementation
|
||||
/*
|
||||
const b = [
|
||||
[ell_0, this.F2.zero, ell_VV],
|
||||
[this.F2.zero, ell_VW, this.F2.zero]
|
||||
];
|
||||
|
||||
return this.F12.mul(a,b);
|
||||
*/
|
||||
|
||||
// This is a new implementation,
|
||||
// But it does not look worthy
|
||||
// at least in javascript.
|
||||
|
||||
let z0 = a[0][0];
|
||||
let z1 = a[0][1];
|
||||
let z2 = a[0][2];
|
||||
let z3 = a[1][0];
|
||||
let z4 = a[1][1];
|
||||
let z5 = a[1][2];
|
||||
|
||||
const x0 = ell_0;
|
||||
const x2 = ell_VV;
|
||||
const x4 = ell_VW;
|
||||
|
||||
const D0 = this.F2.mul(z0, x0);
|
||||
const D2 = this.F2.mul(z2, x2);
|
||||
const D4 = this.F2.mul(z4, x4);
|
||||
const t2 = this.F2.add(z0, z4);
|
||||
let t1 = this.F2.add(z0, z2);
|
||||
const s0 = this.F2.add(this.F2.add(z1,z3),z5);
|
||||
|
||||
// For z.a_.a_ = z0.
|
||||
let S1 = this.F2.mul(z1, x2);
|
||||
let T3 = this.F2.add(S1, D4);
|
||||
let T4 = this.F2.add( this.F2.mul(this.nonResidueF6, T3),D0);
|
||||
z0 = T4;
|
||||
|
||||
// For z.a_.b_ = z1
|
||||
T3 = this.F2.mul(z5, x4);
|
||||
S1 = this.F2.add(S1, T3);
|
||||
T3 = this.F2.add(T3, D2);
|
||||
T4 = this.F2.mul(this.nonResidueF6, T3);
|
||||
T3 = this.F2.mul(z1, x0);
|
||||
S1 = this.F2.add(S1, T3);
|
||||
T4 = this.F2.add(T4, T3);
|
||||
z1 = T4;
|
||||
|
||||
// For z.a_.c_ = z2
|
||||
let t0 = this.F2.add(x0, x2);
|
||||
T3 = this.F2.sub(
|
||||
this.F2.mul(t1, t0),
|
||||
this.F2.add(D0, D2));
|
||||
T4 = this.F2.mul(z3, x4);
|
||||
S1 = this.F2.add(S1, T4);
|
||||
|
||||
// For z.b_.a_ = z3 (z3 needs z2)
|
||||
t0 = this.F2.add(z2, z4);
|
||||
z2 = this.F2.add(T3, T4);
|
||||
t1 = this.F2.add(x2, x4);
|
||||
T3 = this.F2.sub(
|
||||
this.F2.mul(t0,t1),
|
||||
this.F2.add(D2, D4));
|
||||
|
||||
T4 = this.F2.mul(this.nonResidueF6, T3);
|
||||
T3 = this.F2.mul(z3, x0);
|
||||
S1 = this.F2.add(S1, T3);
|
||||
T4 = this.F2.add(T4, T3);
|
||||
z3 = T4;
|
||||
|
||||
// For z.b_.b_ = z4
|
||||
T3 = this.F2.mul(z5, x2);
|
||||
S1 = this.F2.add(S1, T3);
|
||||
T4 = this.F2.mul(this.nonResidueF6, T3);
|
||||
t0 = this.F2.add(x0, x4);
|
||||
T3 = this.F2.sub(
|
||||
this.F2.mul(t2,t0),
|
||||
this.F2.add(D0, D4));
|
||||
T4 = this.F2.add(T4, T3);
|
||||
z4 = T4;
|
||||
|
||||
// For z.b_.c_ = z5.
|
||||
t0 = this.F2.add(this.F2.add(x0, x2), x4);
|
||||
T3 = this.F2.sub(this.F2.mul(s0, t0), S1);
|
||||
z5 = T3;
|
||||
|
||||
return [
|
||||
[z0, z1, z2],
|
||||
[z3, z4, z5]
|
||||
];
|
||||
|
||||
|
||||
}
|
||||
|
||||
_g2MulByQ(p) {
|
||||
const fmx = [p[0][0], this.F1.mul(p[0][1], this.frobenius_coeffs_c1_1 )];
|
||||
const fmy = [p[1][0], this.F1.mul(p[1][1], this.frobenius_coeffs_c1_1 )];
|
||||
const fmz = [p[2][0], this.F1.mul(p[2][1], this.frobenius_coeffs_c1_1 )];
|
||||
return [
|
||||
this.F2.mul(this.twist_mul_by_q_X , fmx),
|
||||
this.F2.mul(this.twist_mul_by_q_Y , fmy),
|
||||
fmz
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new BN128();
|
||||
|
||||
1266
src/engine.js
1266
src/engine.js
File diff suppressed because it is too large
Load Diff
113
src/engine_applykey.js
Normal file
113
src/engine_applykey.js
Normal file
@@ -0,0 +1,113 @@
|
||||
|
||||
module.exports = function buildBatchApplyKey(curve, groupName) {
|
||||
const G = curve[groupName];
|
||||
const Fr = curve.Fr;
|
||||
const tm = curve.tm;
|
||||
|
||||
curve[groupName].batchApplyKey = async function(buff, first, inc, inType, outType) {
|
||||
inType = inType || "affine";
|
||||
outType = outType || "affine";
|
||||
let fnName, fnAffine;
|
||||
let sGin, sGmid, sGout;
|
||||
if (groupName == "G1") {
|
||||
if (inType == "jacobian") {
|
||||
sGin = G.F.n8*3;
|
||||
fnName = "g1m_batchApplyKey";
|
||||
} else {
|
||||
sGin = G.F.n8*2;
|
||||
fnName = "g1m_batchApplyKeyMixed";
|
||||
}
|
||||
sGmid = G.F.n8*3;
|
||||
if (outType == "jacobian") {
|
||||
sGout = G.F.n8*3;
|
||||
} else {
|
||||
fnAffine = "g1m_batchToAffine";
|
||||
sGout = G.F.n8*2;
|
||||
}
|
||||
} else if (groupName == "G2") {
|
||||
if (inType == "jacobian") {
|
||||
sGin = G.F.n8*3;
|
||||
fnName = "g2m_batchApplyKey";
|
||||
} else {
|
||||
sGin = G.F.n8*2;
|
||||
fnName = "g2m_batchApplyKeyMixed";
|
||||
}
|
||||
sGmid = G.F.n8*3;
|
||||
if (outType == "jacobian") {
|
||||
sGout = G.F.n8*3;
|
||||
} else {
|
||||
fnAffine = "g2m_batchToAffine";
|
||||
sGout = G.F.n8*2;
|
||||
}
|
||||
} else if (groupName == "Fr") {
|
||||
fnName = "frm_batchApplyKey";
|
||||
sGin = G.n8;
|
||||
sGmid = G.n8;
|
||||
sGout = G.n8;
|
||||
} else {
|
||||
throw new Error("Invalid group: " + groupName);
|
||||
}
|
||||
const nPoints = Math.floor(buff.byteLength / sGin);
|
||||
const pointsPerChunk = Math.floor(nPoints/tm.concurrency);
|
||||
const opPromises = [];
|
||||
inc = Fr.e(inc);
|
||||
let t = Fr.e(first);
|
||||
for (let i=0; i<tm.concurrency; i++) {
|
||||
let n;
|
||||
if (i< tm.concurrency-1) {
|
||||
n = pointsPerChunk;
|
||||
} else {
|
||||
n = nPoints - i*pointsPerChunk;
|
||||
}
|
||||
if (n==0) continue;
|
||||
|
||||
const task = [];
|
||||
|
||||
task.push({
|
||||
cmd: "ALLOCSET",
|
||||
var: 0,
|
||||
buff: buff.slice(i*pointsPerChunk*sGin, i*pointsPerChunk*sGin + n*sGin)
|
||||
});
|
||||
task.push({cmd: "ALLOCSET", var: 1, buff: t});
|
||||
task.push({cmd: "ALLOCSET", var: 2, buff: inc});
|
||||
task.push({cmd: "ALLOC", var: 3, len: n*Math.max(sGmid, sGout)});
|
||||
task.push({
|
||||
cmd: "CALL",
|
||||
fnName: fnName,
|
||||
params: [
|
||||
{var: 0},
|
||||
{val: n},
|
||||
{var: 1},
|
||||
{var: 2},
|
||||
{var:3}
|
||||
]
|
||||
});
|
||||
if (fnAffine) {
|
||||
task.push({
|
||||
cmd: "CALL",
|
||||
fnName: fnAffine,
|
||||
params: [
|
||||
{var: 3},
|
||||
{val: n},
|
||||
{var: 3},
|
||||
]
|
||||
});
|
||||
}
|
||||
task.push({cmd: "GET", out: 0, var: 3, len: n*sGout});
|
||||
|
||||
opPromises.push(tm.queueAction(task));
|
||||
t = Fr.mul(t, Fr.exp(inc, n));
|
||||
}
|
||||
|
||||
const result = await Promise.all(opPromises);
|
||||
|
||||
const outBuff = new Uint8Array(nPoints*sGout);
|
||||
let p=0;
|
||||
for (let i=0; i<result.length; i++) {
|
||||
outBuff.set(result[i][0], p);
|
||||
p += result[i][0].byteLength;
|
||||
}
|
||||
|
||||
return outBuff;
|
||||
};
|
||||
};
|
||||
45
src/engine_batchconvert.js
Normal file
45
src/engine_batchconvert.js
Normal file
@@ -0,0 +1,45 @@
|
||||
const assert = require("assert");
|
||||
|
||||
module.exports = function buildBatchConvert(tm, fnName, sIn, sOut) {
|
||||
return async function batchConvert(buffIn) {
|
||||
const nPoints = Math.floor(buffIn.byteLength / sIn);
|
||||
assert( nPoints * sIn === buffIn.byteLength);
|
||||
const pointsPerChunk = Math.floor(nPoints/tm.concurrency);
|
||||
const opPromises = [];
|
||||
for (let i=0; i<tm.concurrency; i++) {
|
||||
let n;
|
||||
if (i< tm.concurrency-1) {
|
||||
n = pointsPerChunk;
|
||||
} else {
|
||||
n = nPoints - i*pointsPerChunk;
|
||||
}
|
||||
if (n==0) continue;
|
||||
|
||||
const buffChunk = buffIn.slice(i*pointsPerChunk*sIn, i*pointsPerChunk*sIn + n*sIn);
|
||||
const task = [
|
||||
{cmd: "ALLOCSET", var: 0, buff:buffChunk},
|
||||
{cmd: "ALLOC", var: 1, len:sOut * n},
|
||||
{cmd: "CALL", fnName: fnName, params: [
|
||||
{var: 0},
|
||||
{val: n},
|
||||
{var: 1}
|
||||
]},
|
||||
{cmd: "GET", out: 0, var: 1, len:sOut * n},
|
||||
];
|
||||
opPromises.push(
|
||||
tm.queueAction(task)
|
||||
);
|
||||
}
|
||||
|
||||
const result = await Promise.all(opPromises);
|
||||
|
||||
const fullBuffOut = new Uint8Array(nPoints*sOut);
|
||||
let p =0;
|
||||
for (let i=0; i<result.length; i++) {
|
||||
fullBuffOut.set(result[i][0], p);
|
||||
p+=result[i][0].byteLength;
|
||||
}
|
||||
|
||||
return fullBuffOut;
|
||||
};
|
||||
};
|
||||
455
src/engine_fft.js
Normal file
455
src/engine_fft.js
Normal file
@@ -0,0 +1,455 @@
|
||||
const assert = require("assert");
|
||||
const {log2, buffReverseBits} = require("./utils");
|
||||
|
||||
module.exports = function buildFFT(curve, groupName) {
|
||||
const G = curve[groupName];
|
||||
const Fr = curve.Fr;
|
||||
const tm = G.tm;
|
||||
async function _fft(buff, inverse, inType, outType, log) {
|
||||
inType = inType || "affine";
|
||||
outType = outType || "affine";
|
||||
const MAX_BITS_THREAD = 12;
|
||||
|
||||
let sIn, sMid, sOut, fnIn2Mid, fnMid2Out, fnName, fnFFTMix, fnFFTJoin, fnFFTFinal;
|
||||
if (groupName == "G1") {
|
||||
if (inType == "affine") {
|
||||
sIn = G.F.n8*2;
|
||||
fnIn2Mid = "g1m_batchToJacobian";
|
||||
} else {
|
||||
sIn = G.F.n8*3;
|
||||
}
|
||||
sMid = G.F.n8*3;
|
||||
if (inverse) {
|
||||
fnName = "g1m_ifft";
|
||||
fnFFTFinal = "g1m_fftFinal";
|
||||
} else {
|
||||
fnName = "g1m_fft";
|
||||
}
|
||||
fnFFTJoin = "g1m_fftJoin";
|
||||
fnFFTMix = "g1m_fftMix";
|
||||
|
||||
if (outType == "affine") {
|
||||
sOut = G.F.n8*2;
|
||||
fnMid2Out = "g1m_batchToAffine";
|
||||
} else {
|
||||
sOut = G.F.n8*3;
|
||||
}
|
||||
|
||||
} else if (groupName == "G2") {
|
||||
if (inType == "affine") {
|
||||
sIn = G.F.n8*2;
|
||||
fnIn2Mid = "g2m_batchToJacobian";
|
||||
} else {
|
||||
sIn = G.F.n8*3;
|
||||
}
|
||||
sMid = G.F.n8*3;
|
||||
if (inverse) {
|
||||
fnName = "g2m_ifft";
|
||||
fnFFTFinal = "g2m_fftFinal";
|
||||
} else {
|
||||
fnName = "g2m_fft";
|
||||
}
|
||||
fnFFTJoin = "g2m_fftJoin";
|
||||
fnFFTMix = "g2m_fftMix";
|
||||
if (outType == "affine") {
|
||||
sOut = G.F.n8*2;
|
||||
fnMid2Out = "g2m_batchToAffine";
|
||||
} else {
|
||||
sOut = G.F.n8*3;
|
||||
}
|
||||
} else if (groupName == "Fr") {
|
||||
sIn = G.n8;
|
||||
sMid = G.n8;
|
||||
sOut = G.n8;
|
||||
if (inverse) {
|
||||
fnName = "frm_ifft";
|
||||
fnFFTFinal = "frm_fftFinal";
|
||||
} else {
|
||||
fnName = "frm_fft";
|
||||
}
|
||||
fnFFTMix = "frm_fftMix";
|
||||
fnFFTJoin = "frm_fftJoin";
|
||||
}
|
||||
|
||||
|
||||
|
||||
const nPoints = buff.byteLength / sIn;
|
||||
const bits = log2(nPoints);
|
||||
|
||||
assert( (1 << bits) == nPoints, "fft must be multiple of 2" );
|
||||
|
||||
let inv;
|
||||
if (inverse) {
|
||||
inv = Fr.inv(Fr.e(nPoints));
|
||||
}
|
||||
|
||||
|
||||
|
||||
let buffOut;
|
||||
if (nPoints <= (1 << MAX_BITS_THREAD)) {
|
||||
const task = [];
|
||||
task.push({cmd: "ALLOC", var: 0, len: sMid*nPoints});
|
||||
task.push({cmd: "SET", var: 0, buff: buff});
|
||||
if (fnIn2Mid) {
|
||||
task.push({cmd: "CALL", fnName:fnIn2Mid, params: [{var:0}, {val: nPoints}, {var: 0}]});
|
||||
}
|
||||
task.push({cmd: "CALL", fnName:fnName, params: [{var:0}, {val: nPoints}]});
|
||||
if (fnMid2Out) {
|
||||
task.push({cmd: "CALL", fnName:fnMid2Out, params: [{var:0}, {val: nPoints}, {var: 0}]});
|
||||
}
|
||||
task.push({cmd: "GET", out: 0, var: 0, len: sOut*nPoints});
|
||||
|
||||
const res = await tm.queueAction(task);
|
||||
buffOut = res[0];
|
||||
} else {
|
||||
|
||||
buffReverseBits(buff, sIn);
|
||||
|
||||
let chunks;
|
||||
const pointsInChunk = 1 << MAX_BITS_THREAD;
|
||||
const chunkSize = pointsInChunk * sIn;
|
||||
const nChunks = nPoints / pointsInChunk;
|
||||
|
||||
const promises = [];
|
||||
for (let i = 0; i< nChunks; i++) {
|
||||
const task = [];
|
||||
task.push({cmd: "ALLOC", var: 0, len: sMid*pointsInChunk});
|
||||
const buffChunk = buff.slice( (pointsInChunk * i)*sIn, (pointsInChunk * (i+1))*sIn);
|
||||
task.push({cmd: "SET", var: 0, buff: buffChunk});
|
||||
if (fnIn2Mid) {
|
||||
task.push({cmd: "CALL", fnName:fnIn2Mid, params: [{var:0}, {val: pointsInChunk}, {var: 0}]});
|
||||
}
|
||||
for (let j=1; j<=MAX_BITS_THREAD;j++) {
|
||||
task.push({cmd: "CALL", fnName:fnFFTMix, params: [{var:0}, {val: pointsInChunk}, {val: j}]});
|
||||
}
|
||||
task.push({cmd: "GET", out:0, var: 0, len: sMid*pointsInChunk});
|
||||
promises.push(tm.queueAction(task).then( (r) => {
|
||||
if (log) log(`fft: ${i}/${nChunks}`);
|
||||
return r;
|
||||
}));
|
||||
}
|
||||
|
||||
chunks = await Promise.all(promises);
|
||||
for (let i = 0; i< nChunks; i++) chunks[i] = chunks[i][0];
|
||||
|
||||
for (let i = MAX_BITS_THREAD+1; i<=bits; i++) {
|
||||
if (log) log(`${i}/${bits}`);
|
||||
const nGroups = 1 << (bits - i);
|
||||
const nChunksPerGroup = nChunks / nGroups;
|
||||
const opPromises = [];
|
||||
for (let j=0; j<nGroups; j++) {
|
||||
for (let k=0; k <nChunksPerGroup/2; k++) {
|
||||
const first = Fr.exp( Fr.w[i], k*pointsInChunk);
|
||||
const inc = Fr.w[i];
|
||||
const o1 = j*nChunksPerGroup + k;
|
||||
const o2 = j*nChunksPerGroup + k + nChunksPerGroup/2;
|
||||
|
||||
const task = [];
|
||||
task.push({cmd: "ALLOCSET", var: 0, buff: chunks[o1]});
|
||||
task.push({cmd: "ALLOCSET", var: 1, buff: chunks[o2]});
|
||||
task.push({cmd: "ALLOCSET", var: 2, buff: first});
|
||||
task.push({cmd: "ALLOCSET", var: 3, buff: inc});
|
||||
task.push({cmd: "CALL", fnName: fnFFTJoin, params:[
|
||||
{var: 0},
|
||||
{var: 1},
|
||||
{val: pointsInChunk},
|
||||
{var: 2},
|
||||
{var: 3}
|
||||
]});
|
||||
if (i==bits) {
|
||||
if (fnFFTFinal) {
|
||||
task.push({cmd: "ALLOCSET", var: 4, buff: inv});
|
||||
task.push({cmd: "CALL", fnName: fnFFTFinal, params:[
|
||||
{var: 0},
|
||||
{val: pointsInChunk},
|
||||
{var: 4},
|
||||
]});
|
||||
task.push({cmd: "CALL", fnName: fnFFTFinal, params:[
|
||||
{var: 1},
|
||||
{val: pointsInChunk},
|
||||
{var: 4},
|
||||
]});
|
||||
}
|
||||
if (fnMid2Out) {
|
||||
task.push({cmd: "CALL", fnName:fnMid2Out, params: [{var:0}, {val: pointsInChunk}, {var: 0}]});
|
||||
task.push({cmd: "CALL", fnName:fnMid2Out, params: [{var:1}, {val: pointsInChunk}, {var: 1}]});
|
||||
}
|
||||
task.push({cmd: "GET", out: 0, var: 0, len: pointsInChunk*sOut});
|
||||
task.push({cmd: "GET", out: 1, var: 1, len: pointsInChunk*sOut});
|
||||
} else {
|
||||
task.push({cmd: "GET", out: 0, var: 0, len: pointsInChunk*sMid});
|
||||
task.push({cmd: "GET", out: 1, var: 1, len: pointsInChunk*sMid});
|
||||
}
|
||||
opPromises.push(tm.queueAction(task));
|
||||
}
|
||||
}
|
||||
|
||||
const res = await Promise.all(opPromises);
|
||||
for (let j=0; j<nGroups; j++) {
|
||||
for (let k=0; k <nChunksPerGroup/2; k++) {
|
||||
const o1 = j*nChunksPerGroup + k;
|
||||
const o2 = j*nChunksPerGroup + k + nChunksPerGroup/2;
|
||||
const resChunk = res.shift();
|
||||
chunks[o1] = resChunk[0];
|
||||
chunks[o2] = resChunk[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buffOut = new Uint8Array(nPoints * sOut);
|
||||
if (inverse) {
|
||||
buffOut.set(chunks[0].slice((pointsInChunk-1)*sOut));
|
||||
let p= sOut;
|
||||
for (let i=nChunks-1; i>0; i--) {
|
||||
buffOut.set(chunks[i], p);
|
||||
p += chunkSize;
|
||||
delete chunks[i]; // Liberate mem
|
||||
}
|
||||
buffOut.set(chunks[0].slice(0, (pointsInChunk-1)*sOut), p);
|
||||
delete chunks[nChunks-1];
|
||||
} else {
|
||||
for (let i=0; i<nChunks; i++) {
|
||||
buffOut.set(chunks[i], pointsInChunk*sOut*i);
|
||||
delete chunks[i];
|
||||
}
|
||||
return buffOut;
|
||||
}
|
||||
}
|
||||
|
||||
return buffOut;
|
||||
}
|
||||
|
||||
G.fft = async function(buff, inType, outType, log) {
|
||||
return await _fft(buff, false, inType, outType, log);
|
||||
};
|
||||
|
||||
G.ifft = async function(buff, inType, outType, log) {
|
||||
return await _fft(buff, true, inType, outType, log);
|
||||
};
|
||||
|
||||
G.fftMix = async function fftMix(buff) {
|
||||
const sG = G.F.n8*3;
|
||||
let fnName, fnFFTJoin;
|
||||
if (groupName == "G1") {
|
||||
fnName = "g1m_fftMix";
|
||||
fnFFTJoin = "g1m_fftJoin";
|
||||
} else if (groupName == "G2") {
|
||||
fnName = "g2m_fftMix";
|
||||
fnFFTJoin = "g2m_fftJoin";
|
||||
} else if (groupName == "Fr") {
|
||||
fnName = "frm_fftMix";
|
||||
fnFFTJoin = "frm_fftJoin";
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
const nPoints = Math.floor(buff.byteLength / sG);
|
||||
const power = log2(nPoints);
|
||||
|
||||
let nChunks = 1 << log2(tm.concurrency);
|
||||
|
||||
if (nPoints <= nChunks*2) nChunks = 1;
|
||||
|
||||
const pointsPerChunk = nPoints / nChunks;
|
||||
|
||||
const powerChunk = log2(pointsPerChunk);
|
||||
|
||||
const opPromises = [];
|
||||
for (let i=0; i<nChunks; i++) {
|
||||
const task = [];
|
||||
const b = buff.slice((i* pointsPerChunk)*sG, ((i+1)* pointsPerChunk)*sG);
|
||||
task.push({cmd: "ALLOCSET", var: 0, buff: b});
|
||||
for (let j=1; j<=powerChunk; j++) {
|
||||
task.push({cmd: "CALL", fnName: fnName, params: [
|
||||
{var: 0},
|
||||
{val: pointsPerChunk},
|
||||
{val: j}
|
||||
]});
|
||||
}
|
||||
task.push({cmd: "GET", out: 0, var: 0, len: pointsPerChunk*sG});
|
||||
opPromises.push(
|
||||
tm.queueAction(task)
|
||||
);
|
||||
}
|
||||
|
||||
const result = await Promise.all(opPromises);
|
||||
|
||||
const chunks = [];
|
||||
for (let i=0; i<result.length; i++) chunks[i] = result[i][0];
|
||||
|
||||
|
||||
for (let i = powerChunk+1; i<=power; i++) {
|
||||
const nGroups = 1 << (power - i);
|
||||
const nChunksPerGroup = nChunks / nGroups;
|
||||
const opPromises = [];
|
||||
for (let j=0; j<nGroups; j++) {
|
||||
for (let k=0; k <nChunksPerGroup/2; k++) {
|
||||
const first = Fr.exp( Fr.w[i], k*pointsPerChunk);
|
||||
const inc = Fr.w[i];
|
||||
const o1 = j*nChunksPerGroup + k;
|
||||
const o2 = j*nChunksPerGroup + k + nChunksPerGroup/2;
|
||||
|
||||
const task = [];
|
||||
task.push({cmd: "ALLOCSET", var: 0, buff: chunks[o1]});
|
||||
task.push({cmd: "ALLOCSET", var: 1, buff: chunks[o2]});
|
||||
task.push({cmd: "ALLOCSET", var: 2, buff: first});
|
||||
task.push({cmd: "ALLOCSET", var: 3, buff: inc});
|
||||
task.push({cmd: "CALL", fnName: fnFFTJoin, params:[
|
||||
{var: 0},
|
||||
{var: 1},
|
||||
{val: pointsPerChunk},
|
||||
{var: 2},
|
||||
{var: 3}
|
||||
]});
|
||||
task.push({cmd: "GET", out: 0, var: 0, len: pointsPerChunk*sG});
|
||||
task.push({cmd: "GET", out: 1, var: 1, len: pointsPerChunk*sG});
|
||||
opPromises.push(tm.queueAction(task));
|
||||
}
|
||||
}
|
||||
|
||||
const res = await Promise.all(opPromises);
|
||||
for (let j=0; j<nGroups; j++) {
|
||||
for (let k=0; k <nChunksPerGroup/2; k++) {
|
||||
const o1 = j*nChunksPerGroup + k;
|
||||
const o2 = j*nChunksPerGroup + k + nChunksPerGroup/2;
|
||||
const resChunk = res.shift();
|
||||
chunks[o1] = resChunk[0];
|
||||
chunks[o2] = resChunk[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const fullBuffOut = new Uint8Array(nPoints*sG);
|
||||
let p =0;
|
||||
for (let i=0; i<nChunks; i++) {
|
||||
fullBuffOut.set(chunks[i], p);
|
||||
p+=chunks[i].byteLength;
|
||||
}
|
||||
|
||||
return fullBuffOut;
|
||||
};
|
||||
|
||||
G.fftJoin = async function fftJoin(buff1, buff2, first, inc) {
|
||||
const sG = G.F.n8*3;
|
||||
let fnName;
|
||||
if (groupName == "G1") {
|
||||
fnName = "g1m_fftJoin";
|
||||
} else if (groupName == "G2") {
|
||||
fnName = "g2m_fftJoin";
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
assert (buff1.byteLength == buff2.byteLength);
|
||||
const nPoints = Math.floor(buff1.byteLength / sG);
|
||||
assert (nPoints == 1 << log2(nPoints));
|
||||
|
||||
let nChunks = 1 << log2(tm.concurrency);
|
||||
if (nPoints <= nChunks*2) nChunks = 1;
|
||||
|
||||
const pointsPerChunk = nPoints / nChunks;
|
||||
|
||||
|
||||
const opPromises = [];
|
||||
for (let i=0; i<nChunks; i++) {
|
||||
const task = [];
|
||||
|
||||
const firstChunk = Fr.mul(first, Fr.exp(inc, i*pointsPerChunk));
|
||||
const b1 = buff1.slice((i* pointsPerChunk)*sG, ((i+1)* pointsPerChunk)*sG);
|
||||
const b2 = buff2.slice((i* pointsPerChunk)*sG, ((i+1)* pointsPerChunk)*sG);
|
||||
task.push({cmd: "ALLOCSET", var: 0, buff: b1});
|
||||
task.push({cmd: "ALLOCSET", var: 1, buff: b2});
|
||||
task.push({cmd: "ALLOCSET", var: 2, buff: firstChunk});
|
||||
task.push({cmd: "ALLOCSET", var: 3, buff: inc});
|
||||
task.push({cmd: "CALL", fnName: fnName, params: [
|
||||
{var: 0},
|
||||
{var: 1},
|
||||
{val: pointsPerChunk},
|
||||
{var: 2},
|
||||
{var: 3}
|
||||
]});
|
||||
task.push({cmd: "GET", out: 0, var: 0, len: pointsPerChunk*sG});
|
||||
task.push({cmd: "GET", out: 1, var: 1, len: pointsPerChunk*sG});
|
||||
opPromises.push(
|
||||
tm.queueAction(task)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
const result = await Promise.all(opPromises);
|
||||
|
||||
const fullBuffOut1 = new Uint8Array(nPoints*sG);
|
||||
const fullBuffOut2 = new Uint8Array(nPoints*sG);
|
||||
let p =0;
|
||||
for (let i=0; i<result.length; i++) {
|
||||
fullBuffOut1.set(result[i][0], p);
|
||||
fullBuffOut2.set(result[i][1], p);
|
||||
p+=result[i][0].byteLength;
|
||||
}
|
||||
|
||||
return [fullBuffOut1, fullBuffOut2];
|
||||
};
|
||||
|
||||
G.fftFinal = async function fftFinal(buff, factor) {
|
||||
const sG = G.F.n8*3;
|
||||
const sGout = G.F.n8*2;
|
||||
let fnName, fnToAffine;
|
||||
if (groupName == "G1") {
|
||||
fnName = "g1m_fftFinal";
|
||||
fnToAffine = "g1m_batchToAffine";
|
||||
} else if (groupName == "G2") {
|
||||
fnName = "g2m_fftFinal";
|
||||
fnToAffine = "g2m_batchToAffine";
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
const nPoints = Math.floor(buff.byteLength / sG);
|
||||
assert (nPoints == 1 << log2(nPoints));
|
||||
|
||||
const pointsPerChunk = Math.floor(nPoints / tm.concurrency);
|
||||
|
||||
const opPromises = [];
|
||||
for (let i=0; i<tm.concurrency; i++) {
|
||||
let n;
|
||||
if (i< tm.concurrency-1) {
|
||||
n = pointsPerChunk;
|
||||
} else {
|
||||
n = nPoints - i*pointsPerChunk;
|
||||
}
|
||||
if (n==0) continue;
|
||||
const task = [];
|
||||
const b = buff.slice((i* pointsPerChunk)*sG, (i*pointsPerChunk+n)*sG);
|
||||
task.push({cmd: "ALLOCSET", var: 0, buff: b});
|
||||
task.push({cmd: "ALLOCSET", var: 1, buff: factor});
|
||||
task.push({cmd: "CALL", fnName: fnName, params: [
|
||||
{var: 0},
|
||||
{val: n},
|
||||
{var: 1},
|
||||
]});
|
||||
task.push({cmd: "CALL", fnName: fnToAffine, params: [
|
||||
{var: 0},
|
||||
{val: n},
|
||||
{var: 0},
|
||||
]});
|
||||
task.push({cmd: "GET", out: 0, var: 0, len: n*sGout});
|
||||
opPromises.push(
|
||||
tm.queueAction(task)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
const result = await Promise.all(opPromises);
|
||||
|
||||
const fullBuffOut = new Uint8Array(nPoints*sGout);
|
||||
let p =0;
|
||||
for (let i=result.length-1; i>=0; i--) {
|
||||
fullBuffOut.set(result[i][0], p);
|
||||
p+=result[i][0].byteLength;
|
||||
}
|
||||
|
||||
return fullBuffOut;
|
||||
};
|
||||
};
|
||||
85
src/engine_multiexp.js
Normal file
85
src/engine_multiexp.js
Normal file
@@ -0,0 +1,85 @@
|
||||
const assert = require("assert");
|
||||
const {log2} = require("./utils");
|
||||
|
||||
const pTSizes = [
|
||||
1 , 1, 1, 1, 2, 3, 4, 5,
|
||||
6 , 7, 7, 8, 9, 10, 11, 12,
|
||||
13, 13, 14, 15, 16, 16, 17, 17,
|
||||
17, 17, 17, 17, 17, 17, 17, 17
|
||||
];
|
||||
|
||||
module.exports = function buildMultiexp(curve, groupName) {
|
||||
const G = curve[groupName];
|
||||
async function _multiExp(buffBases, buffScalars, inType) {
|
||||
inType = inType || "affine";
|
||||
|
||||
let sGIn;
|
||||
let fnName;
|
||||
if (groupName == "G1") {
|
||||
if (inType == "affine") {
|
||||
fnName = "g1m_multiexpAffine_chunk";
|
||||
sGIn = G.F.n8*2;
|
||||
} else {
|
||||
fnName = "g1m_multiexp_chunk";
|
||||
sGIn = G.F.n8*3;
|
||||
}
|
||||
} else if (groupName == "G2") {
|
||||
if (inType == "affine") {
|
||||
fnName = "g2m_multiexpAffine_chunk";
|
||||
sGIn = G.F.n8*2;
|
||||
} else {
|
||||
fnName = "g2m_multiexp_chunk";
|
||||
sGIn = G.F.n8*3;
|
||||
}
|
||||
} else {
|
||||
assert(false, "Invalid group");
|
||||
}
|
||||
const nPoints = Math.floor(buffBases.byteLength / sGIn);
|
||||
const sScalar = Math.floor(buffScalars.byteLength / nPoints);
|
||||
assert( sScalar * nPoints == buffScalars.byteLength, "Scalar size does not match");
|
||||
|
||||
const bitChunkSize = pTSizes[log2(nPoints)];
|
||||
const nChunks = Math.floor((sScalar*8 - 1) / bitChunkSize) +1;
|
||||
|
||||
const opPromises = [];
|
||||
for (let i=0; i<nChunks; i++) {
|
||||
const task = [
|
||||
{cmd: "ALLOCSET", var: 0, buff: buffBases},
|
||||
{cmd: "ALLOCSET", var: 1, buff: buffScalars},
|
||||
{cmd: "ALLOC", var: 2, len: G.F.n8*3},
|
||||
{cmd: "CALL", fnName: fnName, params: [
|
||||
{var: 0},
|
||||
{var: 1},
|
||||
{val: sScalar},
|
||||
{val: nPoints},
|
||||
{val: i*bitChunkSize},
|
||||
{val: Math.min(sScalar*8 - i*bitChunkSize, bitChunkSize)},
|
||||
{var: 2}
|
||||
]},
|
||||
{cmd: "GET", out: 0, var: 2, len: G.F.n8*3}
|
||||
];
|
||||
opPromises.push(
|
||||
G.tm.queueAction(task)
|
||||
);
|
||||
}
|
||||
|
||||
const result = await Promise.all(opPromises);
|
||||
|
||||
let res = G.zero;
|
||||
for (let i=result.length-1; i>=0; i--) {
|
||||
if (!G.isZero(res)) {
|
||||
for (let j=0; j<bitChunkSize; j++) res = G.double(res);
|
||||
}
|
||||
res = G.add(res, result[i][0]);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
G.multiExp = async function multiExpAffine(buffBases, buffScalars) {
|
||||
return await _multiExp(buffBases, buffScalars, "jacobian");
|
||||
};
|
||||
G.multiExpAffine = async function multiExpAffine(buffBases, buffScalars) {
|
||||
return await _multiExp(buffBases, buffScalars, "affine");
|
||||
};
|
||||
};
|
||||
88
src/engine_pairing.js
Normal file
88
src/engine_pairing.js
Normal file
@@ -0,0 +1,88 @@
|
||||
|
||||
module.exports = function buildPairing(curve) {
|
||||
const tm = curve.tm;
|
||||
curve.pairing = function pairing(a, b) {
|
||||
|
||||
tm.startSyncOp();
|
||||
const pA = tm.allocBuff(curve.G1.toJacobian(a));
|
||||
const pB = tm.allocBuff(curve.G2.toJacobian(b));
|
||||
const pRes = tm.alloc(curve.Gt.n8);
|
||||
tm.instance.exports[curve.name + "_pairing"](pA, pB, pRes);
|
||||
|
||||
const res = tm.getBuff(pRes, curve.Gt.n8);
|
||||
|
||||
tm.endSyncOp();
|
||||
return res;
|
||||
};
|
||||
|
||||
curve.pairingEq = async function pairingEq() {
|
||||
let buffCt;
|
||||
let nEqs;
|
||||
if ((arguments.length % 2) == 1) {
|
||||
buffCt = arguments[arguments.length-1];
|
||||
nEqs = (arguments.length -1) /2;
|
||||
} else {
|
||||
buffCt = curve.Gt.one;
|
||||
nEqs = arguments.length /2;
|
||||
}
|
||||
|
||||
const opPromises = [];
|
||||
for (let i=0; i<nEqs; i++) {
|
||||
|
||||
const task = [];
|
||||
|
||||
const g1Buff = curve.G1.toJacobian(arguments[i*2]);
|
||||
task.push({cmd: "ALLOCSET", var: 0, buff: g1Buff});
|
||||
task.push({cmd: "ALLOC", var: 1, len: curve.prePSize});
|
||||
|
||||
const g2Buff = curve.G2.toJacobian(arguments[i*2 +1]);
|
||||
task.push({cmd: "ALLOCSET", var: 2, buff: g2Buff});
|
||||
task.push({cmd: "ALLOC", var: 3, len: curve.preQSize});
|
||||
|
||||
task.push({cmd: "ALLOC", var: 4, len: curve.Gt.n8});
|
||||
|
||||
task.push({cmd: "CALL", fnName: curve.name + "_prepareG1", params: [
|
||||
{var: 0},
|
||||
{var: 1}
|
||||
]});
|
||||
|
||||
task.push({cmd: "CALL", fnName: curve.name + "_prepareG2", params: [
|
||||
{var: 2},
|
||||
{var: 3}
|
||||
]});
|
||||
|
||||
task.push({cmd: "CALL", fnName: curve.name + "_millerLoop", params: [
|
||||
{var: 1},
|
||||
{var: 3},
|
||||
{var: 4}
|
||||
]});
|
||||
|
||||
task.push({cmd: "GET", out: 0, var: 4, len: curve.Gt.n8});
|
||||
|
||||
opPromises.push(
|
||||
tm.queueAction(task)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
const result = await Promise.all(opPromises);
|
||||
|
||||
tm.startSyncOp();
|
||||
const pRes = tm.alloc(curve.Gt.n8);
|
||||
tm.instance.exports.ftm_one(pRes);
|
||||
|
||||
for (let i=0; i<result.length; i++) {
|
||||
const pMR = tm.allocBuff(result[i][0]);
|
||||
tm.instance.exports.ftm_mul(pRes, pMR, pRes);
|
||||
}
|
||||
tm.instance.exports[curve.name + "_finalExponentiation"](pRes, pRes);
|
||||
|
||||
const pCt = tm.allocBuff(buffCt);
|
||||
|
||||
const r = !!tm.instance.exports.ftm_eq(pRes, pCt);
|
||||
|
||||
tm.endSyncOp();
|
||||
|
||||
return r;
|
||||
};
|
||||
};
|
||||
0
src/negine_applykey.js
Normal file
0
src/negine_applykey.js
Normal file
41
src/random.js
Normal file
41
src/random.js
Normal file
@@ -0,0 +1,41 @@
|
||||
/* global window */
|
||||
const ChaCha = require("./chacha");
|
||||
|
||||
module.exports.getRandomBytes = getRandomBytes;
|
||||
module.exports.getRandomSeed = getRandomSeed;
|
||||
module.exports.getThreadRng = getThreadRng;
|
||||
|
||||
function getRandomBytes(n) {
|
||||
let array = new Uint8Array(n);
|
||||
if (typeof window !== "undefined") { // Browser
|
||||
if (typeof window.crypto !== "undefined") { // Supported
|
||||
window.crypto.getRandomValues(array);
|
||||
} else { // fallback
|
||||
for (let i=0; i<n; i++) {
|
||||
array[i] = (Math.random()*4294967296)>>>0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else { // NodeJS
|
||||
module.require("crypto").randomFillSync(array);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
function getRandomSeed() {
|
||||
const arr = getRandomBytes(32);
|
||||
const arrV = new Uint32Array(arr.buffer);
|
||||
const seed = [];
|
||||
for (let i=0; i<8; i++) {
|
||||
seed.push(arrV[i]);
|
||||
}
|
||||
return seed;
|
||||
}
|
||||
|
||||
let threadRng = null;
|
||||
|
||||
function getThreadRng() {
|
||||
if (threadRng) return threadRng;
|
||||
threadRng = new ChaCha(getRandomSeed());
|
||||
return threadRng;
|
||||
}
|
||||
@@ -18,6 +18,7 @@ Scalar.__proto__.toRprLE = function rprBE(buff, o, e, n8) {
|
||||
const l = (((s.length-7)*4 - 1) >> 5)+1; // Number of 32bit words;
|
||||
for (let i=0; i<l; i++) v[i] = parseInt(s.substring(s.length-8*i-8, s.length-8*i), 16);
|
||||
for (let i=l; i<v.length; i++) v[i] = 0;
|
||||
for (let i=v.length*4; i<n8; i++) buff[i] = Scalar.toNumber(Scalar.band(Scalar.shiftRight(e, i*8), 0xFF));
|
||||
};
|
||||
|
||||
// Returns a buffer with Big Endian Representation
|
||||
@@ -49,6 +50,21 @@ Scalar.__proto__.fromRprBE = function rprLEM(buff, o, n8) {
|
||||
return Scalar.fromString(a.join(""), 16);
|
||||
};
|
||||
|
||||
Scalar.__proto__.toString = function toString(a, radix) {
|
||||
return a.toString(radix);
|
||||
};
|
||||
|
||||
Scalar.__proto__.toLEBuff = function toLEBuff(a) {
|
||||
const buff = new Uint8Array(Math.floor((Scalar.bitLength(a) - 1) / 8) +1);
|
||||
Scalar.toRprLE(buff, 0, a, buff.byteLength);
|
||||
return buff;
|
||||
};
|
||||
|
||||
|
||||
|
||||
Scalar.zero = Scalar.e(0);
|
||||
Scalar.one = Scalar.e(1);
|
||||
|
||||
module.exports = Scalar;
|
||||
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ module.exports.fromArray = function fromArray(a, radix) {
|
||||
|
||||
module.exports.bitLength = function (a) {
|
||||
const aS =a.toString(16);
|
||||
return (aS.length-1)*4 +hexLen[aS[0]];
|
||||
return (aS.length-1)*4 +hexLen[parseInt(aS[0], 16)];
|
||||
};
|
||||
|
||||
module.exports.isNegative = function (a) {
|
||||
|
||||
242
src/threadman.js
Normal file
242
src/threadman.js
Normal file
@@ -0,0 +1,242 @@
|
||||
/* global navigator, Blob, Worker, WebAssembly */
|
||||
/*
|
||||
Copyright 2019 0KIMS association.
|
||||
|
||||
This file is part of wasmsnark (Web Assembly zkSnark Prover).
|
||||
|
||||
wasmsnark 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.
|
||||
|
||||
wasmsnark 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 wasmsnark. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const MEM_SIZE = 4096; // Memory size in 64K Pakes (256Mb)
|
||||
|
||||
|
||||
const assert = require("assert");
|
||||
const thread = require("./threadman_thread");
|
||||
const FFT = require("./fft");
|
||||
const {log2, buffReverseBits} = require("./utils");
|
||||
|
||||
const inBrowser = (typeof window !== "undefined");
|
||||
let NodeWorker;
|
||||
let NodeCrypto;
|
||||
if (!inBrowser) {
|
||||
NodeWorker = require("worker_threads").Worker;
|
||||
NodeCrypto = require("crypto");
|
||||
}
|
||||
|
||||
class Deferred {
|
||||
constructor() {
|
||||
this.promise = new Promise((resolve, reject)=> {
|
||||
this.reject = reject;
|
||||
this.resolve = resolve;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
async function buildThreadManager(wasm, singleThread) {
|
||||
const tm = new ThreadManager();
|
||||
|
||||
tm.memory = new WebAssembly.Memory({initial:MEM_SIZE});
|
||||
tm.u8 = new Uint8Array(tm.memory.buffer);
|
||||
tm.u32 = new Uint32Array(tm.memory.buffer);
|
||||
|
||||
const wasmModule = await WebAssembly.compile(wasm.code);
|
||||
|
||||
tm.instance = await WebAssembly.instantiate(wasmModule, {
|
||||
env: {
|
||||
"memory": tm.memory
|
||||
}
|
||||
});
|
||||
|
||||
tm.singleThread = singleThread;
|
||||
tm.initalPFree = tm.u32[0]; // Save the Pointer to free space.
|
||||
tm.pq = wasm.pq;
|
||||
tm.pr = wasm.pr;
|
||||
tm.pG1gen = wasm.pG1gen;
|
||||
tm.pG1zero = wasm.pG1zero;
|
||||
tm.pG2gen = wasm.pG2gen;
|
||||
tm.pG2zero = wasm.pG2zero;
|
||||
tm.pOneT = wasm.pOneT;
|
||||
|
||||
// tm.pTmp0 = tm.alloc(curve.G2.F.n8*3);
|
||||
// tm.pTmp1 = tm.alloc(curve.G2.F.n8*3);
|
||||
|
||||
|
||||
if (singleThread) {
|
||||
tm.code = wasm.code;
|
||||
tm.taskManager = thread();
|
||||
await tm.taskManager([{
|
||||
cmd: "INIT",
|
||||
init: MEM_SIZE,
|
||||
code: wasm.code
|
||||
}]);
|
||||
tm.concurrency = 1;
|
||||
} else {
|
||||
tm.workers = [];
|
||||
tm.pendingDeferreds = [];
|
||||
tm.working = [];
|
||||
|
||||
let concurrency;
|
||||
|
||||
if ((typeof(navigator) === "object") && navigator.hardwareConcurrency) {
|
||||
concurrency = navigator.hardwareConcurrency;
|
||||
} else {
|
||||
const os = require("os");
|
||||
concurrency = os.cpus().length;
|
||||
}
|
||||
tm.concurrency = concurrency;
|
||||
|
||||
for (let i = 0; i<concurrency; i++) {
|
||||
|
||||
if (inBrowser) {
|
||||
const blob = new Blob(["(", thread.toString(), ")(self);"], { type: "text/javascript" });
|
||||
const url = URL.createObjectURL(blob);
|
||||
|
||||
tm.workers[i] = new Worker(url);
|
||||
|
||||
tm.workers[i].onmessage = getOnMsg(i);
|
||||
|
||||
} else {
|
||||
tm.workers[i] = new NodeWorker("(" + thread.toString()+ ")(require('worker_threads').parentPort);", {eval: true});
|
||||
|
||||
tm.workers[i].on("message", getOnMsg(i));
|
||||
}
|
||||
|
||||
tm.working[i]=false;
|
||||
}
|
||||
|
||||
const initPromises = [];
|
||||
for (let i=0; i<tm.workers.length;i++) {
|
||||
const copyCode = wasm.code.buffer.slice(0);
|
||||
initPromises.push(tm.postAction(i, [{
|
||||
cmd: "INIT",
|
||||
init: MEM_SIZE,
|
||||
code: copyCode
|
||||
}], [copyCode]));
|
||||
}
|
||||
|
||||
await Promise.all(initPromises);
|
||||
|
||||
}
|
||||
return tm;
|
||||
|
||||
function getOnMsg(i) {
|
||||
return function(e) {
|
||||
let data;
|
||||
if ((e)&&(e.data)) {
|
||||
data = e.data;
|
||||
} else {
|
||||
data = e;
|
||||
}
|
||||
|
||||
tm.working[i]=false;
|
||||
tm.pendingDeferreds[i].resolve(data);
|
||||
tm.processWorks();
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class ThreadManager {
|
||||
constructor() {
|
||||
this.actionQueue = [];
|
||||
this.oldPFree = 0;
|
||||
}
|
||||
|
||||
startSyncOp() {
|
||||
assert(this.oldPFree == 0);
|
||||
this.oldPFree = this.u32[0];
|
||||
}
|
||||
|
||||
endSyncOp() {
|
||||
assert(this.oldPFree != 0);
|
||||
this.u32[0] = this.oldPFree;
|
||||
this.oldPFree = 0;
|
||||
}
|
||||
|
||||
postAction(workerId, e, transfers, _deferred) {
|
||||
assert(this.working[workerId] == false);
|
||||
this.working[workerId] = true;
|
||||
|
||||
this.pendingDeferreds[workerId] = _deferred ? _deferred : new Deferred();
|
||||
this.workers[workerId].postMessage(e, transfers);
|
||||
|
||||
return this.pendingDeferreds[workerId].promise;
|
||||
}
|
||||
|
||||
processWorks() {
|
||||
for (let i=0; (i<this.workers.length)&&(this.actionQueue.length > 0); i++) {
|
||||
if (this.working[i] == false) {
|
||||
const work = this.actionQueue.shift();
|
||||
this.postAction(i, work.data, work.transfers, work.deferred);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
queueAction(actionData, transfers) {
|
||||
const d = new Deferred();
|
||||
|
||||
if (this.singleThread) {
|
||||
const res = this.taskManager(actionData);
|
||||
d.resolve(res);
|
||||
} else {
|
||||
this.actionQueue.push({
|
||||
data: actionData,
|
||||
transfers: transfers,
|
||||
deferred: d
|
||||
});
|
||||
this.processWorks();
|
||||
}
|
||||
return d.promise;
|
||||
}
|
||||
|
||||
resetMemory() {
|
||||
this.u32[0] = this.initalPFree;
|
||||
}
|
||||
|
||||
allocBuff(buff) {
|
||||
const pointer = this.alloc(buff.byteLength);
|
||||
this.setBuff(pointer, buff);
|
||||
return pointer;
|
||||
}
|
||||
|
||||
getBuff(pointer, length) {
|
||||
return this.u8.slice(pointer, pointer+ length);
|
||||
}
|
||||
|
||||
setBuff(pointer, buffer) {
|
||||
this.u8.set(new Uint8Array(buffer), pointer);
|
||||
}
|
||||
|
||||
alloc(length) {
|
||||
while (this.u32[0] & 3) this.u32[0]++; // Return always aligned pointers
|
||||
const res = this.u32[0];
|
||||
this.u32[0] += length;
|
||||
return res;
|
||||
}
|
||||
|
||||
terminate() {
|
||||
for (let i=0; i<this.workers.length; i++) {
|
||||
this.workers[i].postMessage([{cmd: "TERMINATE"}]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
module.exports = buildThreadManager;
|
||||
@@ -1,5 +1,6 @@
|
||||
/* global BigInt */
|
||||
const assert = require("assert");
|
||||
const Scalar =require("./scalar");
|
||||
|
||||
module.exports.stringifyBigInts = function stringifyBigInts(o) {
|
||||
if ((typeof(o) == "bigint") || o.eq !== undefined) {
|
||||
@@ -102,6 +103,10 @@ module.exports.leBuff2int = function leBuff2int(buff) {
|
||||
|
||||
module.exports.leInt2Buff = function leInt2Buff(n, len) {
|
||||
let r = n;
|
||||
if (typeof len === "undefined") {
|
||||
len = Math.floor((Scalar.bitLength(n) - 1) / 8) +1;
|
||||
if (len==0) len = 1;
|
||||
}
|
||||
const buff = Buffer.alloc(len);
|
||||
let o = 0;
|
||||
while (o < len) {
|
||||
|
||||
359
src/wasm_curve.js
Normal file
359
src/wasm_curve.js
Normal file
@@ -0,0 +1,359 @@
|
||||
|
||||
|
||||
const assert = require("assert");
|
||||
const Scalar = require("./scalar");
|
||||
const utils = require("./utils");
|
||||
const buildBatchConvert = require("./engine_batchconvert");
|
||||
|
||||
class WasmCurve {
|
||||
|
||||
constructor(tm, prefix, F, pGen, pGb, cofactor) {
|
||||
this.tm = tm;
|
||||
this.prefix = prefix;
|
||||
this.F = F;
|
||||
|
||||
this.pOp1 = tm.alloc(F.n8*3);
|
||||
this.pOp2 = tm.alloc(F.n8*3);
|
||||
this.pOp3 = tm.alloc(F.n8*3);
|
||||
this.tm.instance.exports[prefix + "_zero"](this.pOp1);
|
||||
this.zero = this.tm.getBuff(this.pOp1, F.n8*3);
|
||||
this.tm.instance.exports[prefix + "_zeroAffine"](this.pOp1);
|
||||
this.zeroAffine = this.tm.getBuff(this.pOp1, F.n8*2);
|
||||
this.one = this.tm.getBuff(pGen, F.n8*3);
|
||||
this.g = this.one;
|
||||
this.oneAffine = this.tm.getBuff(pGen, F.n8*2);
|
||||
this.gAffine = this.oneAffine;
|
||||
this.b = this.tm.getBuff(pGb, F.n8);
|
||||
|
||||
if (cofactor) {
|
||||
this.cofactor = Scalar.toLEBuff(cofactor);
|
||||
}
|
||||
|
||||
this.negone = this.neg(this.one);
|
||||
this.two = this.add(this.one, this.one);
|
||||
|
||||
this.batchLEMtoC = buildBatchConvert(tm, prefix + "_batchLEMtoC", F.n8*2, F.n8);
|
||||
this.batchLEMtoU = buildBatchConvert(tm, prefix + "_batchLEMtoU", F.n8*2, F.n8*2);
|
||||
this.batchCtoLEM = buildBatchConvert(tm, prefix + "_batchCtoLEM", F.n8, F.n8*2);
|
||||
this.batchUtoLEM = buildBatchConvert(tm, prefix + "_batchUtoLEM", F.n8*2, F.n8*2);
|
||||
this.batchToJacobian = buildBatchConvert(tm, prefix + "_batchToJacobian", F.n8*2, F.n8*3);
|
||||
this.batchToAffine = buildBatchConvert(tm, prefix + "_batchToAffine", F.n8*3, F.n8*2);
|
||||
}
|
||||
|
||||
op2(opName, a, b) {
|
||||
this.tm.setBuff(this.pOp1, a);
|
||||
this.tm.setBuff(this.pOp2, b);
|
||||
this.tm.instance.exports[this.prefix + opName](this.pOp1, this.pOp2, this.pOp3);
|
||||
return this.tm.getBuff(this.pOp3, this.F.n8*3);
|
||||
}
|
||||
|
||||
op2bool(opName, a, b) {
|
||||
this.tm.setBuff(this.pOp1, a);
|
||||
this.tm.setBuff(this.pOp2, b);
|
||||
return !!this.tm.instance.exports[this.prefix + opName](this.pOp1, this.pOp2, this.pOp3);
|
||||
}
|
||||
|
||||
op1(opName, a) {
|
||||
this.tm.setBuff(this.pOp1, a);
|
||||
this.tm.instance.exports[this.prefix + opName](this.pOp1, this.pOp3);
|
||||
return this.tm.getBuff(this.pOp3, this.F.n8*3);
|
||||
}
|
||||
|
||||
op1Affine(opName, a) {
|
||||
this.tm.setBuff(this.pOp1, a);
|
||||
this.tm.instance.exports[this.prefix + opName](this.pOp1, this.pOp3);
|
||||
return this.tm.getBuff(this.pOp3, this.F.n8*2);
|
||||
}
|
||||
|
||||
op1Bool(opName, a) {
|
||||
this.tm.setBuff(this.pOp1, a);
|
||||
return !!this.tm.instance.exports[this.prefix + opName](this.pOp1, this.pOp3);
|
||||
}
|
||||
|
||||
add(a,b) {
|
||||
if (a.byteLength == this.F.n8*3) {
|
||||
if (b.byteLength == this.F.n8*3) {
|
||||
return this.op2("_add", a, b);
|
||||
} else if (b.byteLength == this.F.n8*2) {
|
||||
return this.op2("_addMixed", a, b);
|
||||
} else {
|
||||
assert(false, "invalid point size");
|
||||
}
|
||||
} else if (a.byteLength == this.F.n8*2) {
|
||||
if (b.byteLength == this.F.n8*3) {
|
||||
return this.op2("_addMixed", b, a);
|
||||
} else if (b.byteLength == this.F.n8*2) {
|
||||
return this.op2("_addAffine", a, b);
|
||||
} else {
|
||||
assert(false, "invalid point size");
|
||||
}
|
||||
} else {
|
||||
assert(false, "invalid point size");
|
||||
}
|
||||
}
|
||||
|
||||
sub(a,b) {
|
||||
if (a.byteLength == this.F.n8*3) {
|
||||
if (b.byteLength == this.F.n8*3) {
|
||||
return this.op2("_sub", a, b);
|
||||
} else if (b.byteLength == this.F.n8*2) {
|
||||
return this.op2("_subMixed", a, b);
|
||||
} else {
|
||||
assert(false, "invalid point size");
|
||||
}
|
||||
} else if (a.byteLength == this.F.n8*2) {
|
||||
if (b.byteLength == this.F.n8*3) {
|
||||
return this.op2("_subMixed", b, a);
|
||||
} else if (b.byteLength == this.F.n8*2) {
|
||||
return this.op2("_subAffine", a, b);
|
||||
} else {
|
||||
assert(false, "invalid point size");
|
||||
}
|
||||
} else {
|
||||
assert(false, "invalid point size");
|
||||
}
|
||||
}
|
||||
|
||||
neg(a) {
|
||||
if (a.byteLength == this.F.n8*3) {
|
||||
return this.op1("_neg", a);
|
||||
} else if (a.byteLength == this.F.n8*2) {
|
||||
return this.op1Affine("_negAffine", a);
|
||||
} else {
|
||||
assert(false, "invalid point size");
|
||||
}
|
||||
}
|
||||
|
||||
double(a) {
|
||||
if (a.byteLength == this.F.n8*3) {
|
||||
return this.op1("_double", a);
|
||||
} else if (a.byteLength == this.F.n8*2) {
|
||||
return this.op1("_doubleAffine", a);
|
||||
} else {
|
||||
assert(false, "invalid point size");
|
||||
}
|
||||
}
|
||||
|
||||
isZero(a) {
|
||||
if (a.byteLength == this.F.n8*3) {
|
||||
return this.op1Bool("_isZero", a);
|
||||
} else if (a.byteLength == this.F.n8*2) {
|
||||
return this.op1Bool("_isZeroAffine", a);
|
||||
} else {
|
||||
assert(false, "invalid point size");
|
||||
}
|
||||
}
|
||||
|
||||
timesScalar(a, s) {
|
||||
if (!(s instanceof Uint8Array)) {
|
||||
s = Scalar.toLEBuff(Scalar.e(s));
|
||||
}
|
||||
let fnName;
|
||||
if (a.byteLength == this.F.n8*3) {
|
||||
fnName = this.prefix + "_timesScalar";
|
||||
} else if (a.byteLength == this.F.n8*2) {
|
||||
fnName = this.prefix + "_timesScalarAffine";
|
||||
} else {
|
||||
assert(false, "invalid point size");
|
||||
}
|
||||
this.tm.setBuff(this.pOp1, a);
|
||||
this.tm.setBuff(this.pOp2, s);
|
||||
this.tm.instance.exports[fnName](this.pOp1, this.pOp2, s.byteLength, this.pOp3);
|
||||
return this.tm.getBuff(this.pOp3, this.F.n8*3);
|
||||
}
|
||||
|
||||
timesFr(a, s) {
|
||||
let fnName;
|
||||
if (a.byteLength == this.F.n8*3) {
|
||||
fnName = this.prefix + "_timesFr";
|
||||
} else if (a.byteLength == this.F.n8*2) {
|
||||
fnName = this.prefix + "_timesFrAffine";
|
||||
} else {
|
||||
assert(false, "invalid point size");
|
||||
}
|
||||
this.tm.setBuff(this.pOp1, a);
|
||||
this.tm.setBuff(this.pOp2, s);
|
||||
this.tm.instance.exports[fnName](this.pOp1, this.pOp2, this.pOp3);
|
||||
return this.tm.getBuff(this.pOp3, this.F.n8*3);
|
||||
}
|
||||
|
||||
eq(a,b) {
|
||||
if (a.byteLength == this.F.n8*3) {
|
||||
if (b.byteLength == this.F.n8*3) {
|
||||
return this.op2bool("_eq", a, b);
|
||||
} else if (b.byteLength == this.F.n8*2) {
|
||||
return this.op2bool("_eqMixed", a, b);
|
||||
} else {
|
||||
assert(false, "invalid point size");
|
||||
}
|
||||
} else if (a.byteLength == this.F.n8*2) {
|
||||
if (b.byteLength == this.F.n8*3) {
|
||||
return this.op2bool("_eqMixed", b, a);
|
||||
} else if (b.byteLength == this.F.n8*2) {
|
||||
return this.op2bool("_eqAffine", a, b);
|
||||
} else {
|
||||
assert(false, "invalid point size");
|
||||
}
|
||||
} else {
|
||||
assert(false, "invalid point size");
|
||||
}
|
||||
}
|
||||
|
||||
toAffine(a) {
|
||||
if (a.byteLength == this.F.n8*3) {
|
||||
return this.op1Affine("_toAffine", a);
|
||||
} else if (a.byteLength == this.F.n8*2) {
|
||||
return a;
|
||||
} else {
|
||||
assert(false, "invalid point size");
|
||||
}
|
||||
}
|
||||
|
||||
toJacobian(a) {
|
||||
if (a.byteLength == this.F.n8*3) {
|
||||
return a;
|
||||
} else if (a.byteLength == this.F.n8*2) {
|
||||
return this.op1("_toJacobian", a);
|
||||
} else {
|
||||
assert(false, "invalid point size");
|
||||
}
|
||||
}
|
||||
|
||||
toRprUncompressed(arr, offset, a) {
|
||||
this.tm.setBuff(this.pOp1, a);
|
||||
if (a.byteLength == this.F.n8*3) {
|
||||
this.tm.instance.exports[this.prefix + "_toAffine"](this.pOp1, this.pOp1);
|
||||
} else if (a.byteLength != this.F.n8*2) {
|
||||
assert(false, "invalid point size");
|
||||
}
|
||||
this.tm.instance.exports[this.prefix + "_LEMtoU"](this.pOp1, this.pOp1);
|
||||
const res = this.tm.getBuff(this.pOp1, this.F.n8*2);
|
||||
arr.set(res, offset);
|
||||
}
|
||||
|
||||
fromRprUncompressed(arr, offset) {
|
||||
const buff = arr.slice(offset, offset + this.F.n8*2);
|
||||
this.tm.setBuff(this.pOp1, buff);
|
||||
this.tm.instance.exports[this.prefix + "_UtoLEM"](this.pOp1, this.pOp1);
|
||||
return this.tm.getBuff(this.pOp1, this.F.n8*2);
|
||||
}
|
||||
|
||||
toUncompressed(a) {
|
||||
const buff = new Uint8Array(this.F.n8*2);
|
||||
this.toRprUncompressed(buff, 0, a);
|
||||
return buff;
|
||||
}
|
||||
|
||||
toRprLEM(arr, offset, a) {
|
||||
if (a.byteLength == this.F.n8*2) {
|
||||
arr.set(a, offset);
|
||||
return;
|
||||
} else if (a.byteLength == this.F.n8*3) {
|
||||
this.tm.setBuff(this.pOp1, a);
|
||||
this.tm.instance.exports[this.prefix + "_toAffine"](this.pOp1, this.pOp1);
|
||||
const res = this.tm.getBuff(this.pOp1, this.F.n8*2);
|
||||
arr.set(res, offset);
|
||||
} else {
|
||||
assert(false, "invalid point size");
|
||||
}
|
||||
}
|
||||
|
||||
fromRprLEM(arr, offset) {
|
||||
offset = offset || 0;
|
||||
return arr.slice(offset, offset+this.F.n8*2);
|
||||
}
|
||||
|
||||
toString(a, radix) {
|
||||
if (a.byteLength == this.F.n8*3) {
|
||||
const x = this.F.toString(a.slice(0, this.F.n8), radix);
|
||||
const y = this.F.toString(a.slice(this.F.n8, this.F.n8*2), radix);
|
||||
const z = this.F.toString(a.slice(this.F.n8*2), radix);
|
||||
return `[ ${x}, ${y}, ${z} ]`;
|
||||
} else if (a.byteLength == this.F.n8*2) {
|
||||
const x = this.F.toString(a.slice(0, this.F.n8), radix);
|
||||
const y = this.F.toString(a.slice(this.F.n8), radix);
|
||||
return `[ ${x}, ${y} ]`;
|
||||
} else {
|
||||
assert(false, "invalid point size");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fromRng(rng) {
|
||||
const F = this.F;
|
||||
let P = [];
|
||||
let greatest;
|
||||
let x3b;
|
||||
do {
|
||||
P[0] = F.fromRng(rng);
|
||||
greatest = rng.nextBool();
|
||||
x3b = F.add(F.mul(F.square(P[0]), P[0]), this.b);
|
||||
} while (!F.isSquare(x3b));
|
||||
|
||||
P[1] = F.sqrt(x3b);
|
||||
|
||||
const s = F.isNegative(P[1]);
|
||||
if (greatest ^ s) P[1] = F.neg(P[1]);
|
||||
|
||||
let Pbuff = new Uint8Array(this.F.n8*2);
|
||||
Pbuff.set(P[0]);
|
||||
Pbuff.set(P[1], this.F.n8);
|
||||
|
||||
if (this.cofactor) {
|
||||
Pbuff = this.timesScalar(Pbuff, this.cofactor);
|
||||
}
|
||||
|
||||
return Pbuff;
|
||||
}
|
||||
|
||||
|
||||
|
||||
toObject(a) {
|
||||
if (this.isZero(a)) {
|
||||
return [
|
||||
this.F.toObject(this.F.zero),
|
||||
this.F.toObject(this.F.one),
|
||||
this.F.toObject(this.F.zero),
|
||||
];
|
||||
}
|
||||
const x = this.F.toObject(a.slice(0, this.F.n8));
|
||||
const y = this.F.toObject(a.slice(this.F.n8, this.F.n8*2));
|
||||
let z;
|
||||
if (a.byteLength == this.F.n8*3) {
|
||||
z = this.F.toObject(a.slice(this.F.n8*2, this.F.n8*3));
|
||||
} else {
|
||||
z = this.F.toObject(this.F.one);
|
||||
}
|
||||
return [x, y, z];
|
||||
}
|
||||
|
||||
fromObject(a) {
|
||||
const x = this.F.fromObject(a[0]);
|
||||
const y = this.F.fromObject(a[1]);
|
||||
let z;
|
||||
if (a.length==3) {
|
||||
z = this.F.fromObject(a[2]);
|
||||
} else {
|
||||
z = this.F.one;
|
||||
}
|
||||
if (this.F.isZero(z, this.F.one)) {
|
||||
return this.zeroAffine;
|
||||
} else if (this.F.eq(z, this.F.one)) {
|
||||
const buff = new Uint8Array(this.F.n8*2);
|
||||
buff.set(x);
|
||||
buff.set(y, this.F.n8);
|
||||
return buff;
|
||||
} else {
|
||||
const buff = new Uint8Array(this.F.n8*3);
|
||||
buff.set(x);
|
||||
buff.set(y, this.F.n8);
|
||||
buff.set(z, this.F.n8*2);
|
||||
return buff;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
module.exports = WasmCurve;
|
||||
|
||||
231
src/wasm_field1.js
Normal file
231
src/wasm_field1.js
Normal file
@@ -0,0 +1,231 @@
|
||||
|
||||
|
||||
const assert = require("assert");
|
||||
const Scalar = require("./scalar");
|
||||
const utils = require("./utils");
|
||||
const {getThreadRng} = require("./random");
|
||||
const buildBatchConvert = require("./engine_batchconvert");
|
||||
|
||||
|
||||
class WasmField1 {
|
||||
|
||||
constructor(tm, prefix, n8, p) {
|
||||
this.tm = tm;
|
||||
this.prefix = prefix;
|
||||
|
||||
this.p = p;
|
||||
this.n8 = n8;
|
||||
this.type = "F1";
|
||||
this.m = 1;
|
||||
|
||||
this.half = Scalar.shiftRight(p, Scalar.one);
|
||||
this.bitLength = Scalar.bitLength(p);
|
||||
this.mask = Scalar.sub(Scalar.shiftLeft(Scalar.one, this.bitLength), Scalar.one);
|
||||
|
||||
this.pOp1 = tm.alloc(n8);
|
||||
this.pOp2 = tm.alloc(n8);
|
||||
this.pOp3 = tm.alloc(n8);
|
||||
this.tm.instance.exports[prefix + "_zero"](this.pOp1);
|
||||
this.zero = this.getBuff(this.pOp1);
|
||||
this.tm.instance.exports[prefix + "_one"](this.pOp1);
|
||||
this.one = this.getBuff(this.pOp1);
|
||||
|
||||
this.negone = this.neg(this.one);
|
||||
this.two = this.add(this.one, this.one);
|
||||
|
||||
this.n64 = Math.floor(n8/8);
|
||||
this.n32 = Math.floor(n8/4);
|
||||
|
||||
assert(this.n64*8 == this.n8, "n8 must be a multiple of 8");
|
||||
|
||||
this.half = Scalar.shiftRight(this.p, Scalar.one);
|
||||
this.nqr = this.two;
|
||||
let r = this.exp(this.nqr, this.half);
|
||||
while (!this.eq(r, this.negone)) {
|
||||
this.nqr = this.add(this.nqr, this.one);
|
||||
r = this.exp(this.nqr, this.half);
|
||||
}
|
||||
|
||||
|
||||
this.s = 0;
|
||||
let t = Scalar.sub(this.p, Scalar.one);
|
||||
|
||||
while ( !Scalar.isOdd(t) ) {
|
||||
this.s = this.s + 1;
|
||||
t = Scalar.shiftRight(t, Scalar.one);
|
||||
}
|
||||
|
||||
this.w = [];
|
||||
this.w[this.s] = this.exp(this.nqr, t);
|
||||
|
||||
for (let i= this.s-1; i>=0; i--) {
|
||||
this.w[i] = this.square(this.w[i+1]);
|
||||
}
|
||||
|
||||
assert(this.eq(this.w[0], this.one));
|
||||
|
||||
|
||||
this.batchToMontgomery = buildBatchConvert(tm, prefix + "_batchToMontgomery", this.n8, this.n8);
|
||||
this.batchFromMontgomery = buildBatchConvert(tm, prefix + "_batchFromMontgomery", this.n8, this.n8);
|
||||
}
|
||||
|
||||
getBuff(p) {
|
||||
return this.tm.u8.slice(p, p+this.n8);
|
||||
}
|
||||
|
||||
setBuff(p, buff) {
|
||||
return this.tm.u8.set(buff, p);
|
||||
}
|
||||
|
||||
op2(opName, a, b) {
|
||||
this.setBuff(this.pOp1, a);
|
||||
this.setBuff(this.pOp2, b);
|
||||
this.tm.instance.exports[this.prefix + opName](this.pOp1, this.pOp2, this.pOp3);
|
||||
return this.getBuff(this.pOp3);
|
||||
}
|
||||
|
||||
op2Bool(opName, a, b) {
|
||||
this.setBuff(this.pOp1, a);
|
||||
this.setBuff(this.pOp2, b);
|
||||
return !!this.tm.instance.exports[this.prefix + opName](this.pOp1, this.pOp2);
|
||||
}
|
||||
|
||||
op1(opName, a) {
|
||||
this.setBuff(this.pOp1, a);
|
||||
this.tm.instance.exports[this.prefix + opName](this.pOp1, this.pOp3);
|
||||
return this.getBuff(this.pOp3);
|
||||
}
|
||||
|
||||
op1Bool(opName, a) {
|
||||
this.setBuff(this.pOp1, a);
|
||||
return !!this.tm.instance.exports[this.prefix + opName](this.pOp1, this.pOp3);
|
||||
}
|
||||
|
||||
add(a,b) {
|
||||
return this.op2("_add", a, b);
|
||||
}
|
||||
|
||||
|
||||
eq(a,b) {
|
||||
return this.op2Bool("_eq", a, b);
|
||||
}
|
||||
|
||||
isZero(a) {
|
||||
return this.op1Bool("_isZero", a);
|
||||
}
|
||||
|
||||
sub(a,b) {
|
||||
return this.op2("_sub", a, b);
|
||||
}
|
||||
|
||||
neg(a) {
|
||||
return this.op1("_neg", a);
|
||||
}
|
||||
|
||||
inv(a) {
|
||||
return this.op1("_inverse", a);
|
||||
}
|
||||
|
||||
toMontgomery(a) {
|
||||
return this.op1("_toMontgomery", a);
|
||||
}
|
||||
|
||||
fromMontgomery(a) {
|
||||
return this.op1("_fromMontgomery", a);
|
||||
}
|
||||
|
||||
mul(a,b) {
|
||||
return this.op2("_mul", a, b);
|
||||
}
|
||||
|
||||
square(a) {
|
||||
return this.op1("_square", a);
|
||||
}
|
||||
|
||||
isSquare(a) {
|
||||
return this.op1Bool("_isSquare", a);
|
||||
}
|
||||
|
||||
sqrt(a) {
|
||||
return this.op1("_sqrt", a);
|
||||
}
|
||||
|
||||
exp(a, b) {
|
||||
if (!(b instanceof Uint8Array)) {
|
||||
b = Scalar.toLEBuff(Scalar.e(b));
|
||||
}
|
||||
this.tm.setBuff(this.pOp1, a);
|
||||
this.tm.setBuff(this.pOp2, b);
|
||||
this.tm.instance.exports[this.prefix + "_exp"](this.pOp1, this.pOp2, b.byteLength, this.pOp3);
|
||||
return this.tm.getBuff(this.pOp3, this.n8);
|
||||
}
|
||||
|
||||
isNegative(a) {
|
||||
return this.op1Bool("_isNegative", a);
|
||||
}
|
||||
|
||||
e(a, b) {
|
||||
if (a instanceof Uint8Array) return a;
|
||||
let ra = Scalar.e(a, b);
|
||||
if (Scalar.isNegative(ra)) {
|
||||
ra = Scalar.neg(ra);
|
||||
if (Scalar.gt(ra, this.p)) {
|
||||
ra = Scalar.mod(ra, this.p);
|
||||
}
|
||||
ra = Scalar.sub(this.p, ra);
|
||||
} else {
|
||||
if (Scalar.gt(ra, this.p)) {
|
||||
ra = Scalar.mod(ra, this.p);
|
||||
}
|
||||
}
|
||||
const buff = utils.leInt2Buff(ra, this.n8);
|
||||
return this.toMontgomery(buff);
|
||||
}
|
||||
|
||||
toString(a, radix) {
|
||||
const an = this.fromMontgomery(a);
|
||||
const s = Scalar.fromRprLE(an, 0);
|
||||
return Scalar.toString(s, radix);
|
||||
}
|
||||
|
||||
fromRng(rng) {
|
||||
let v;
|
||||
do {
|
||||
v = Scalar.zero;
|
||||
for (let i=0; i<this.n64; i++) {
|
||||
v = Scalar.add(v, Scalar.shiftLeft(rng.nextU64(), 64*i));
|
||||
}
|
||||
v = Scalar.band(v, this.mask);
|
||||
} while (Scalar.geq(v, this.p));
|
||||
return this.e(v);
|
||||
}
|
||||
|
||||
random() {
|
||||
return this.fromRng(getThreadRng());
|
||||
}
|
||||
|
||||
toObject(a) {
|
||||
const an = this.fromMontgomery(a);
|
||||
return Scalar.fromRprLE(an, 0);
|
||||
}
|
||||
|
||||
fromObject(a) {
|
||||
const buff = new Uint8Array(this.n8);
|
||||
Scalar.toRprLE(buff, 0, a, this.n8);
|
||||
return this.toMontgomery(buff);
|
||||
}
|
||||
|
||||
toRprLE(buff, offset, a) {
|
||||
buff.set(this.fromMontgomery(a), offset);
|
||||
}
|
||||
|
||||
fromRprLE(buff, offset) {
|
||||
const res = buff.slice(offset, offset + this.n8);
|
||||
return this.toMontgomery(res);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
module.exports = WasmField1;
|
||||
|
||||
171
src/wasm_field2.js
Normal file
171
src/wasm_field2.js
Normal file
@@ -0,0 +1,171 @@
|
||||
|
||||
|
||||
const assert = require("assert");
|
||||
const {getThreadRng} = require("./random");
|
||||
const Scalar = require("./scalar");
|
||||
|
||||
|
||||
class WasmField2 {
|
||||
|
||||
constructor(tm, prefix, F) {
|
||||
this.tm = tm;
|
||||
this.prefix = prefix;
|
||||
|
||||
this.F = F;
|
||||
this.type = "F2";
|
||||
this.m = F.m * 2;
|
||||
this.n8 = this.F.n8*2;
|
||||
this.n32 = this.F.n32*2;
|
||||
this.n64 = this.F.n64*2;
|
||||
|
||||
this.pOp1 = tm.alloc(F.n8*2);
|
||||
this.pOp2 = tm.alloc(F.n8*2);
|
||||
this.pOp3 = tm.alloc(F.n8*2);
|
||||
this.tm.instance.exports[prefix + "_zero"](this.pOp1);
|
||||
this.zero = tm.getBuff(this.pOp1, this.n8);
|
||||
this.tm.instance.exports[prefix + "_one"](this.pOp1);
|
||||
this.one = tm.getBuff(this.pOp1, this.n8);
|
||||
|
||||
this.negone = this.neg(this.one);
|
||||
this.two = this.add(this.one, this.one);
|
||||
|
||||
}
|
||||
|
||||
op2(opName, a, b) {
|
||||
this.tm.setBuff(this.pOp1, a);
|
||||
this.tm.setBuff(this.pOp2, b);
|
||||
this.tm.instance.exports[this.prefix + opName](this.pOp1, this.pOp2, this.pOp3);
|
||||
return this.tm.getBuff(this.pOp3, this.n8);
|
||||
}
|
||||
|
||||
op2Bool(opName, a, b) {
|
||||
this.tm.setBuff(this.pOp1, a);
|
||||
this.tm.setBuff(this.pOp2, b);
|
||||
return !!this.tm.instance.exports[this.prefix + opName](this.pOp1, this.pOp2);
|
||||
}
|
||||
|
||||
op1(opName, a) {
|
||||
this.tm.setBuff(this.pOp1, a);
|
||||
this.tm.instance.exports[this.prefix + opName](this.pOp1, this.pOp3);
|
||||
return this.tm.getBuff(this.pOp3, this.n8);
|
||||
}
|
||||
|
||||
op1Bool(opName, a) {
|
||||
this.tm.setBuff(this.pOp1, a);
|
||||
return !!this.tm.instance.exports[this.prefix + opName](this.pOp1, this.pOp3);
|
||||
}
|
||||
|
||||
add(a,b) {
|
||||
return this.op2("_add", a, b);
|
||||
}
|
||||
|
||||
eq(a,b) {
|
||||
return this.op2Bool("_eq", a, b);
|
||||
}
|
||||
|
||||
isZero(a) {
|
||||
return this.op1Bool("_isZero", a);
|
||||
}
|
||||
|
||||
sub(a,b) {
|
||||
return this.op2("_sub", a, b);
|
||||
}
|
||||
|
||||
neg(a) {
|
||||
return this.op1("_neg", a);
|
||||
}
|
||||
|
||||
inv(a) {
|
||||
return this.op1("_inverse", a);
|
||||
}
|
||||
|
||||
isNegative(a) {
|
||||
return this.op1Bool("_isNegative", a);
|
||||
}
|
||||
|
||||
toMontgomery(a) {
|
||||
return this.op1("_toMontgomery", a);
|
||||
}
|
||||
|
||||
fromMontgomery(a) {
|
||||
return this.op1("_fromMontgomery", a);
|
||||
}
|
||||
|
||||
mul(a,b) {
|
||||
return this.op2("_mul", a, b);
|
||||
}
|
||||
|
||||
square(a) {
|
||||
return this.op1("_square", a);
|
||||
}
|
||||
|
||||
isSquare(a) {
|
||||
return this.op1Bool("_isSquare", a);
|
||||
}
|
||||
|
||||
sqrt(a) {
|
||||
return this.op1("_sqrt", a);
|
||||
}
|
||||
|
||||
exp(a, b) {
|
||||
if (!(b instanceof Uint8Array)) {
|
||||
b = Scalar.toLEBuff(Scalar.e(b));
|
||||
}
|
||||
this.tm.setBuff(this.pOp1, a);
|
||||
this.tm.setBuff(this.pOp2, b);
|
||||
this.tm.instance.exports[this.prefix + "_exp"](this.pOp1, this.pOp2, b.byteLength, this.pOp3);
|
||||
return this.tm.getBuff(this.pOp3, this.n8);
|
||||
}
|
||||
|
||||
e(a, b) {
|
||||
if (a instanceof Uint8Array) return a;
|
||||
if ((Array.isArray(a)) && (a.length == 2)) {
|
||||
const c1 = this.F.e(a[0], b);
|
||||
const c2 = this.F.e(a[1], b);
|
||||
const res = new Uint8Array(this.F.n8*2);
|
||||
res.set(c1);
|
||||
res.set(c2, this.F.n8*2);
|
||||
return res;
|
||||
} else {
|
||||
assert(false, "invalid F2");
|
||||
}
|
||||
}
|
||||
|
||||
toString(a, radix) {
|
||||
const s1 = this.F.toString(a.slice(0, this.F.n8), radix);
|
||||
const s2 = this.F.toString(a.slice(this.F.n8), radix);
|
||||
return `[${s1}, ${s2}]`;
|
||||
}
|
||||
|
||||
fromRng(rng) {
|
||||
const c1 = this.F.fromRng(rng);
|
||||
const c2 = this.F.fromRng(rng);
|
||||
const res = new Uint8Array(this.F.n8*2);
|
||||
res.set(c1);
|
||||
res.set(c2, this.F.n8);
|
||||
return res;
|
||||
}
|
||||
|
||||
random() {
|
||||
return this.fromRng(getThreadRng());
|
||||
}
|
||||
|
||||
toObject(a) {
|
||||
const c1 = this.F.toObject(a.slice(0, this.F.n8));
|
||||
const c2 = this.F.toObject(a.slice(this.F.n8, this.F.n8*2));
|
||||
return [c1, c2];
|
||||
}
|
||||
|
||||
fromObject(a) {
|
||||
const buff = new Uint8Array(this.F.n8*2);
|
||||
const b1 = this.F.fromObject(a[0]);
|
||||
const b2 = this.F.fromObject(a[1]);
|
||||
buff.set(b1);
|
||||
buff.set(b2, this.F.n8);
|
||||
return buff;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = WasmField2;
|
||||
|
||||
180
src/wasm_field3.js
Normal file
180
src/wasm_field3.js
Normal file
@@ -0,0 +1,180 @@
|
||||
|
||||
|
||||
const assert = require("assert");
|
||||
const {getThreadRng} = require("./random");
|
||||
const Scalar = require("./scalar");
|
||||
|
||||
|
||||
class WasmField3 {
|
||||
|
||||
constructor(tm, prefix, F) {
|
||||
this.tm = tm;
|
||||
this.prefix = prefix;
|
||||
|
||||
this.F = F;
|
||||
this.type = "F3";
|
||||
this.m = F.m * 3;
|
||||
this.n8 = this.F.n8*3;
|
||||
this.n32 = this.F.n32*3;
|
||||
this.n64 = this.F.n64*3;
|
||||
|
||||
this.pOp1 = tm.alloc(F.n8*3);
|
||||
this.pOp2 = tm.alloc(F.n8*3);
|
||||
this.pOp3 = tm.alloc(F.n8*3);
|
||||
this.tm.instance.exports[prefix + "_zero"](this.pOp1);
|
||||
this.zero = tm.getBuff(this.pOp1, this.n8);
|
||||
this.tm.instance.exports[prefix + "_one"](this.pOp1);
|
||||
this.one = tm.getBuff(this.pOp1, this.n8);
|
||||
|
||||
this.negone = this.neg(this.one);
|
||||
this.two = this.add(this.one, this.one);
|
||||
|
||||
}
|
||||
|
||||
op2(opName, a, b) {
|
||||
this.tm.setBuff(this.pOp1, a);
|
||||
this.tm.setBuff(this.pOp2, b);
|
||||
this.tm.instance.exports[this.prefix + opName](this.pOp1, this.pOp2, this.pOp3);
|
||||
return this.tm.getBuff(this.pOp3, this.n8);
|
||||
}
|
||||
|
||||
op2Bool(opName, a, b) {
|
||||
this.tm.setBuff(this.pOp1, a);
|
||||
this.tm.setBuff(this.pOp2, b);
|
||||
return !!this.tm.instance.exports[this.prefix + opName](this.pOp1, this.pOp2);
|
||||
}
|
||||
|
||||
op1(opName, a) {
|
||||
this.tm.setBuff(this.pOp1, a);
|
||||
this.tm.instance.exports[this.prefix + opName](this.pOp1, this.pOp3);
|
||||
return this.tm.getBuff(this.pOp3, this.n8);
|
||||
}
|
||||
|
||||
op1Bool(opName, a) {
|
||||
this.tm.setBuff(this.pOp1, a);
|
||||
return !!this.tm.instance.exports[this.prefix + opName](this.pOp1, this.pOp3);
|
||||
}
|
||||
|
||||
|
||||
eq(a,b) {
|
||||
return this.op2Bool("_eq", a, b);
|
||||
}
|
||||
|
||||
isZero(a) {
|
||||
return this.op1Bool("_isZero", a);
|
||||
}
|
||||
|
||||
add(a,b) {
|
||||
return this.op2("_add", a, b);
|
||||
}
|
||||
|
||||
sub(a,b) {
|
||||
return this.op2("_sub", a, b);
|
||||
}
|
||||
|
||||
neg(a) {
|
||||
return this.op1("_neg", a);
|
||||
}
|
||||
|
||||
inv(a) {
|
||||
return this.op1("_inverse", a);
|
||||
}
|
||||
|
||||
isNegative(a) {
|
||||
return this.op1Bool("_isNegative", a);
|
||||
}
|
||||
|
||||
toMontgomery(a) {
|
||||
return this.op1("_toMontgomery", a);
|
||||
}
|
||||
|
||||
fromMontgomery(a) {
|
||||
return this.op1("_fromMontgomery", a);
|
||||
}
|
||||
|
||||
mul(a,b) {
|
||||
return this.op2("_mul", a, b);
|
||||
}
|
||||
|
||||
square(a) {
|
||||
return this.op1("_square", a);
|
||||
}
|
||||
|
||||
isSquare(a) {
|
||||
return this.op1Bool("_isSquare", a);
|
||||
}
|
||||
|
||||
sqrt(a) {
|
||||
return this.op1("_sqrt", a);
|
||||
}
|
||||
|
||||
exp(a, b) {
|
||||
if (!(b instanceof Uint8Array)) {
|
||||
b = Scalar.toLEBuff(Scalar.e(b));
|
||||
}
|
||||
this.tm.setBuff(this.pOp1, a);
|
||||
this.tm.setBuff(this.pOp2, b);
|
||||
this.tm.instance.exports[this.prefix + "_exp"](this.pOp1, this.pOp2, b.byteLength, this.pOp3);
|
||||
return this.getBuff(this.pOp3, this.n8);
|
||||
}
|
||||
|
||||
e(a, b) {
|
||||
if (a instanceof Uint8Array) return a;
|
||||
if ((Array.isArray(a)) && (a.length == 3)) {
|
||||
const c1 = this.F.e(a[0], b);
|
||||
const c2 = this.F.e(a[1], b);
|
||||
const c3 = this.F.e(a[2], b);
|
||||
const res = new Uint8Array(this.F.n8*3);
|
||||
res.set(c1);
|
||||
res.set(c2, this.F.n8);
|
||||
res.set(c3, this.F.n8*2);
|
||||
return res;
|
||||
} else {
|
||||
assert(false, "invalid F3");
|
||||
}
|
||||
}
|
||||
|
||||
toString(a, radix) {
|
||||
const s1 = this.F.toString(a.slice(0, this.F.n8), radix);
|
||||
const s2 = this.F.toString(a.slice(this.F.n8, this.F.n8*2), radix);
|
||||
const s3 = this.F.toString(a.slice(this.F.n8*2), radix);
|
||||
return `[${s1}, ${s2}, ${s3}]`;
|
||||
}
|
||||
|
||||
fromRng(rng) {
|
||||
const c1 = this.F.fromRng(rng);
|
||||
const c2 = this.F.fromRng(rng);
|
||||
const c3 = this.F.fromRng(rng);
|
||||
const res = new Uint8Array(this.F.n8*3);
|
||||
res.set(c1);
|
||||
res.set(c2, this.F.n8);
|
||||
res.set(c3, this.F.n8*2);
|
||||
return res;
|
||||
}
|
||||
|
||||
random() {
|
||||
return this.fromRng(getThreadRng());
|
||||
}
|
||||
|
||||
toObject(a) {
|
||||
const c1 = this.F.toObject(a.slice(0, this.F.n8));
|
||||
const c2 = this.F.toObject(a.slice(this.F.n8, this.F.n8*2));
|
||||
const c3 = this.F.toObject(a.slice(this.F.n8*2, this.F.n8*3));
|
||||
return [c1, c2, c3];
|
||||
}
|
||||
|
||||
fromObject(a) {
|
||||
const buff = new Uint8Array(this.F.n8*2);
|
||||
const b1 = this.F.fromObject(a[0]);
|
||||
const b2 = this.F.fromObject(a[1]);
|
||||
const b3 = this.F.fromObject(a[2]);
|
||||
buff.set(b1);
|
||||
buff.set(b2, this.F.n8);
|
||||
buff.set(b3, this.F.n8*2);
|
||||
return buff;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = WasmField3;
|
||||
|
||||
Reference in New Issue
Block a user