mirror of
https://github.com/zkitter/ffjavascript.git
synced 2026-01-09 15:07:56 -05:00
Integration with wasmsnark, sqrtF2, formats
This commit is contained in:
1
index.js
1
index.js
@@ -12,5 +12,6 @@ exports.EC = require("./src/ec");
|
||||
exports.bn128 = require("./src/bn128.js");
|
||||
|
||||
exports.utils = require("./src/utils");
|
||||
exports.ChaCha = require("./src/chacha");
|
||||
|
||||
|
||||
|
||||
39
package-lock.json
generated
39
package-lock.json
generated
@@ -124,6 +124,30 @@
|
||||
"resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.48.tgz",
|
||||
"integrity": "sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w=="
|
||||
},
|
||||
"blake2b": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/blake2b/-/blake2b-2.1.3.tgz",
|
||||
"integrity": "sha512-pkDss4xFVbMb4270aCyGD3qLv92314Et+FsKzilCLxDz5DuZ2/1g3w4nmBbu6nKApPspnjG7JcwTjGZnduB1yg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"blake2b-wasm": "^1.1.0",
|
||||
"nanoassert": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"blake2b-wasm": {
|
||||
"version": "1.1.7",
|
||||
"resolved": "https://registry.npmjs.org/blake2b-wasm/-/blake2b-wasm-1.1.7.tgz",
|
||||
"integrity": "sha512-oFIHvXhlz/DUgF0kq5B1CqxIDjIJwh9iDeUUGQUcvgiGz7Wdw03McEO7CfLBy7QKGdsydcMCgO9jFNBAFCtFcA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"nanoassert": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"blakejs": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.1.0.tgz",
|
||||
"integrity": "sha1-ad+S75U6qIylGjLfarHFShVfx6U="
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
@@ -772,6 +796,12 @@
|
||||
"integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
|
||||
"dev": true
|
||||
},
|
||||
"nanoassert": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/nanoassert/-/nanoassert-1.1.0.tgz",
|
||||
"integrity": "sha1-TzFS4JVA/eKMdvRLGbvNHVpCR40=",
|
||||
"dev": true
|
||||
},
|
||||
"natural-compare": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
|
||||
@@ -1128,6 +1158,15 @@
|
||||
"integrity": "sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==",
|
||||
"dev": true
|
||||
},
|
||||
"wasmsnark": {
|
||||
"version": "0.0.10",
|
||||
"resolved": "https://registry.npmjs.org/wasmsnark/-/wasmsnark-0.0.10.tgz",
|
||||
"integrity": "sha512-ARrJWhxvnBJXMERwBcnEnO5ByLwYhJZr1xwac9dl61SN7+1eOmG7Od3SJL1GzU6zaf86b9wza20y1d5ThCecLw==",
|
||||
"requires": {
|
||||
"big-integer": "^1.6.42",
|
||||
"blakejs": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"which": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
|
||||
|
||||
@@ -25,9 +25,11 @@
|
||||
},
|
||||
"homepage": "https://github.com/iden3/ffjs#readme",
|
||||
"dependencies": {
|
||||
"big-integer": "^1.6.48"
|
||||
"big-integer": "^1.6.48",
|
||||
"wasmsnark": "0.0.10"
|
||||
},
|
||||
"devDependencies": {
|
||||
"blake2b": "^2.1.3",
|
||||
"chai": "^4.2.0",
|
||||
"eslint": "^6.8.0"
|
||||
}
|
||||
|
||||
67
src/bn128.js
67
src/bn128.js
@@ -22,11 +22,18 @@ const F1Field = require("./f1field");
|
||||
const F2Field = require("./f2field");
|
||||
const F3Field = require("./f3field");
|
||||
const EC = require("./ec.js");
|
||||
const buildEngine = require("./engine");
|
||||
const bn128_wasm = require("wasmsnark").bn128_wasm;
|
||||
|
||||
|
||||
let engine = null;
|
||||
|
||||
|
||||
class BN128 {
|
||||
|
||||
constructor() {
|
||||
|
||||
this.name = "bn128";
|
||||
this.q = Scalar.fromString("21888242871839275222246405745257275088696311157297823662689037894645226208583");
|
||||
this.r = Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617");
|
||||
|
||||
@@ -53,6 +60,10 @@ class BN128 {
|
||||
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);
|
||||
@@ -68,6 +79,54 @@ class BN128 {
|
||||
|
||||
this._preparePairing();
|
||||
|
||||
this.G1.batchApplyKey = this.batchApplyKeyG1.bind(this);
|
||||
this.G2.batchApplyKey = this.batchApplyKeyG2.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);
|
||||
}
|
||||
|
||||
async loadEngine() {
|
||||
if (!engine) {
|
||||
engine = await buildEngine(this, bn128_wasm, true);
|
||||
}
|
||||
}
|
||||
|
||||
async batchApplyKeyG1(buff, first, inc) {
|
||||
await this.loadEngine();
|
||||
const res = await engine.batchApplyKey("G1", buff, first, inc);
|
||||
return res;
|
||||
}
|
||||
|
||||
async batchApplyKeyG2(buff, first, inc) {
|
||||
await this.loadEngine();
|
||||
const res = await engine.batchApplyKey("G2", buff, first, inc);
|
||||
return res;
|
||||
}
|
||||
|
||||
async batchLEMtoUG1(buff) {
|
||||
await this.loadEngine();
|
||||
const res = await engine.batchConvert("G1", "LEM", "U", buff );
|
||||
return res;
|
||||
}
|
||||
|
||||
async batchLEMtoUG2(buff) {
|
||||
await this.loadEngine();
|
||||
const res = await engine.batchConvert("G2", "LEM", "U",buff);
|
||||
return res;
|
||||
}
|
||||
|
||||
async batchLEMtoCG1(buff) {
|
||||
await this.loadEngine();
|
||||
const res = await engine.batchConvert("G1", "LEM", "C", buff);
|
||||
return res;
|
||||
}
|
||||
|
||||
async batchLEMtoCG2(buff) {
|
||||
await this.loadEngine();
|
||||
const res = await engine.batchConvert("G2", "LEM", "C", buff);
|
||||
return res;
|
||||
}
|
||||
|
||||
_preparePairing() {
|
||||
@@ -253,7 +312,7 @@ class BN128 {
|
||||
finalExponentiation(elt) {
|
||||
// TODO: There is an optimization in FF
|
||||
|
||||
const res = this.F12.exp(elt,this.final_exponent);
|
||||
const res = this.F12.pow(elt,this.final_exponent);
|
||||
|
||||
return res;
|
||||
}
|
||||
@@ -307,7 +366,7 @@ class BN128 {
|
||||
|
||||
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));
|
||||
// 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
|
||||
@@ -342,14 +401,14 @@ class BN128 {
|
||||
_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
|
||||
|
||||
96
src/chacha.js
Normal file
96
src/chacha.js
Normal file
@@ -0,0 +1,96 @@
|
||||
|
||||
|
||||
const Scalar = require("./scalar");
|
||||
|
||||
|
||||
function quarterRound(st, a, b, c, d) {
|
||||
|
||||
st[a] = (st[a] + st[b]) >>> 0;
|
||||
st[d] = (st[d] ^ st[a]) >>> 0;
|
||||
st[d] = ((st[d] << 16) | ((st[d]>>>16) & 0xFFFF)) >>> 0;
|
||||
|
||||
st[c] = (st[c] + st[d]) >>> 0;
|
||||
st[b] = (st[b] ^ st[c]) >>> 0;
|
||||
st[b] = ((st[b] << 12) | ((st[b]>>>20) & 0xFFF)) >>> 0;
|
||||
|
||||
st[a] = (st[a] + st[b]) >>> 0;
|
||||
st[d] = (st[d] ^ st[a]) >>> 0;
|
||||
st[d] = ((st[d] << 8) | ((st[d]>>>24) & 0xFF)) >>> 0;
|
||||
|
||||
st[c] = (st[c] + st[d]) >>> 0;
|
||||
st[b] = (st[b] ^ st[c]) >>> 0;
|
||||
st[b] = ((st[b] << 7) | ((st[b]>>>25) & 0x7F)) >>> 0;
|
||||
}
|
||||
|
||||
function doubleRound(st) {
|
||||
quarterRound(st, 0, 4, 8,12);
|
||||
quarterRound(st, 1, 5, 9,13);
|
||||
quarterRound(st, 2, 6,10,14);
|
||||
quarterRound(st, 3, 7,11,15);
|
||||
|
||||
quarterRound(st, 0, 5,10,15);
|
||||
quarterRound(st, 1, 6,11,12);
|
||||
quarterRound(st, 2, 7, 8,13);
|
||||
quarterRound(st, 3, 4, 9,14);
|
||||
}
|
||||
|
||||
module.exports = class ChaCha {
|
||||
|
||||
constructor(seed) {
|
||||
seed = seed || [0,0,0,0,0,0,0,0];
|
||||
this.state = [
|
||||
0x61707865,
|
||||
0x3320646E,
|
||||
0x79622D32,
|
||||
0x6B206574,
|
||||
seed[0],
|
||||
seed[1],
|
||||
seed[2],
|
||||
seed[3],
|
||||
seed[4],
|
||||
seed[5],
|
||||
seed[6],
|
||||
seed[7],
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
];
|
||||
this.idx = 16;
|
||||
this.buff = new Array(16);
|
||||
}
|
||||
|
||||
nextU32() {
|
||||
if (this.idx == 16) this.update();
|
||||
return this.buff[this.idx++];
|
||||
}
|
||||
|
||||
nextU64() {
|
||||
return Scalar.add(Scalar.mul(this.nextU32(), 0x100000000), this.nextU32());
|
||||
}
|
||||
|
||||
nextBool() {
|
||||
return (this.nextU32() & 1) == 1;
|
||||
}
|
||||
|
||||
update() {
|
||||
// Copy the state
|
||||
for (let i=0; i<16; i++) this.buff[i] = this.state[i];
|
||||
|
||||
// Apply the rounds
|
||||
for (let i=0; i<10; i++) doubleRound(this.buff);
|
||||
|
||||
// Add to the initial
|
||||
for (let i=0; i<16; i++) this.buff[i] = (this.buff[i] + this.state[i]) >>> 0;
|
||||
|
||||
this.idx = 0;
|
||||
|
||||
this.state[12] = (this.state[12] + 1) >>> 0;
|
||||
if (this.state[12] != 0) return;
|
||||
this.state[13] = (this.state[13] + 1) >>> 0;
|
||||
if (this.state[13] != 0) return;
|
||||
this.state[14] = (this.state[14] + 1) >>> 0;
|
||||
if (this.state[14] != 0) return;
|
||||
this.state[15] = (this.state[15] + 1) >>> 0;
|
||||
}
|
||||
};
|
||||
178
src/ec.js
178
src/ec.js
@@ -17,7 +17,27 @@
|
||||
snarkjs. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
const fUtils = require("./futils.js");
|
||||
const Scalar = require("./scalar");
|
||||
const assert = require("assert");
|
||||
|
||||
|
||||
function isGreatest(F, a) {
|
||||
if (Array.isArray(a)) {
|
||||
for (let i=a.length-1; i>=0; i--) {
|
||||
if (!F.F.isZero(a[i])) {
|
||||
return isGreatest(F.F, a[i]);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
} else {
|
||||
const na = F.neg(a);
|
||||
return Scalar.gt(a, na);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class EC {
|
||||
|
||||
@@ -138,8 +158,10 @@ class EC {
|
||||
|
||||
affine(p) {
|
||||
const F = this.F;
|
||||
if (this.eq(p, this.zero)) {
|
||||
if (this.isZero(p)) {
|
||||
return this.zero;
|
||||
} else if (F.eq(p[2], F.one)) {
|
||||
return p;
|
||||
} else {
|
||||
const Z_inv = F.inv(p[2]);
|
||||
const Z2_inv = F.square(Z_inv);
|
||||
@@ -209,11 +231,165 @@ class EC {
|
||||
return (F.eq(U1,U2) && F.eq(S1,S2));
|
||||
}
|
||||
|
||||
isZero(p) {
|
||||
return this.F.isZero(p[2]);
|
||||
}
|
||||
|
||||
toString(p) {
|
||||
const cp = this.affine(p);
|
||||
return `[ ${this.F.toString(cp[0])} , ${this.F.toString(cp[1])} ]`;
|
||||
}
|
||||
|
||||
fromRng(rng) {
|
||||
const F = this.F;
|
||||
let P = [];
|
||||
let greatest;
|
||||
do {
|
||||
P[0] = F.fromRng(rng);
|
||||
greatest = rng.nextBool();
|
||||
const x3b = F.add(F.mul(F.square(P[0]), P[0]), this.b);
|
||||
P[1] = F.sqrt(x3b);
|
||||
} while ((P[1] == null)||(F.isZero[P]));
|
||||
|
||||
const s = isGreatest(F, P[1]);
|
||||
if (greatest ^ s) P[1] = F.neg(P[1]);
|
||||
P[2] = F.one;
|
||||
|
||||
if (this.cofactor) {
|
||||
P = this.mulScalar(P, this.cofactor);
|
||||
}
|
||||
|
||||
P = this.affine(P);
|
||||
|
||||
return P;
|
||||
|
||||
}
|
||||
|
||||
toRprLE(buff, o, p) {
|
||||
p = this.affine(p);
|
||||
if (this.isZero(p)) {
|
||||
const BuffV = new Uint8Array(buff, o, this.F.n8*2);
|
||||
BuffV.fill(0);
|
||||
return;
|
||||
}
|
||||
this.F.toRprLE(buff, o, p[0]);
|
||||
this.F.toRprLE(buff, o+this.F.n8, p[1]);
|
||||
}
|
||||
|
||||
toRprBE(buff, o, p) {
|
||||
p = this.affine(p);
|
||||
if (this.isZero(p)) {
|
||||
const BuffV = new Uint8Array(buff, o, this.F.n8*2);
|
||||
BuffV.fill(0);
|
||||
return;
|
||||
}
|
||||
this.F.toRprBE(buff, o, p[0]);
|
||||
this.F.toRprBE(buff, o+this.F.n8, p[1]);
|
||||
}
|
||||
|
||||
toRprLEM(buff, o, p) {
|
||||
p = this.affine(p);
|
||||
if (this.isZero(p)) {
|
||||
const BuffV = new Uint8Array(buff, o, this.F.n8*2);
|
||||
BuffV.fill(0);
|
||||
return;
|
||||
}
|
||||
this.F.toRprLEM(buff, o, p[0]);
|
||||
this.F.toRprLEM(buff, o+this.F.n8, p[1]);
|
||||
}
|
||||
|
||||
|
||||
toRprBEM(buff, o, p) {
|
||||
p = this.affine(p);
|
||||
if (this.isZero(p)) {
|
||||
const BuffV = new Uint8Array(buff, o, this.F.n8*2);
|
||||
BuffV.fill(0);
|
||||
return;
|
||||
}
|
||||
this.F.toRprBEM(buff, o, p[0]);
|
||||
this.F.toRprBEM(buff, o+this.F.n8, p[1]);
|
||||
}
|
||||
|
||||
fromRprLE(buff, o) {
|
||||
o = o || 0;
|
||||
const x = this.F.fromRprLE(buff, o);
|
||||
const y = this.F.fromRprLE(buff, o+this.F.n8);
|
||||
if (this.F.isZero(x) && this.F.isZero(y)) {
|
||||
return this.zero;
|
||||
}
|
||||
return [x, y, this.F.one];
|
||||
}
|
||||
|
||||
fromRprBE(buff, o) {
|
||||
o = o || 0;
|
||||
const x = this.F.fromRprBE(buff, o);
|
||||
const y = this.F.fromRprBE(buff, o+this.F.n8);
|
||||
if (this.F.isZero(x) && this.F.isZero(y)) {
|
||||
return this.zero;
|
||||
}
|
||||
return [x, y, this.F.one];
|
||||
}
|
||||
|
||||
fromRprLEM(buff, o) {
|
||||
o = o || 0;
|
||||
const x = this.F.fromRprLEM(buff, o);
|
||||
const y = this.F.fromRprLEM(buff, o+this.F.n8);
|
||||
if (this.F.isZero(x) && this.F.isZero(y)) {
|
||||
return this.zero;
|
||||
}
|
||||
return [x, y, this.F.one];
|
||||
}
|
||||
|
||||
fromRprBEM(buff, o) {
|
||||
o = o || 0;
|
||||
const x = this.F.fromRprBEM(buff, o);
|
||||
const y = this.F.fromRprBEM(buff, o+this.F.n8);
|
||||
if (this.F.isZero(x) && this.F.isZero(y)) {
|
||||
return this.zero;
|
||||
}
|
||||
return [x, y, this.F.one];
|
||||
}
|
||||
|
||||
fromRprCompressed(buff, o) {
|
||||
const F = this.F;
|
||||
const v = new Uint8Array(buff, o, F.n8);
|
||||
if (v[0] & 0x40) return this.zero;
|
||||
const P = new Array(3);
|
||||
|
||||
const greatest = ((v[0] & 0x80) != 0);
|
||||
v[0] = v[0] & 0x7F;
|
||||
P[0] = F.fromRprBE(buff, o);
|
||||
if (greatest) v[0] = v[0] | 0x80; // set back again the old value
|
||||
|
||||
const x3b = F.add(F.mul(F.square(P[0]), P[0]), this.b);
|
||||
P[1] = F.sqrt(x3b);
|
||||
|
||||
if (P[1] === null) {
|
||||
assert(false, "Invalid Point!");
|
||||
}
|
||||
|
||||
const s = isGreatest(F, P[1]);
|
||||
if (greatest ^ s) P[1] = F.neg(P[1]);
|
||||
P[2] = F.one;
|
||||
|
||||
return P;
|
||||
}
|
||||
|
||||
toRprCompressed(buff, o, p) {
|
||||
p = this.affine(p);
|
||||
const v = new Uint8Array(buff, o, this.F.n8);
|
||||
if (this.isZero(p)) {
|
||||
v.fill(0);
|
||||
v[0] = 0x40;
|
||||
return;
|
||||
}
|
||||
this.F.toRprBE(buff, o, p[0]);
|
||||
|
||||
if (isGreatest(this.F, p[1])) {
|
||||
v[0] = v[0] | 0x80;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = EC;
|
||||
|
||||
401
src/engine.js
Normal file
401
src/engine.js
Normal file
@@ -0,0 +1,401 @@
|
||||
/* 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("./engine_thread");
|
||||
|
||||
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 buildEngine(curve, wasm, singleThread) {
|
||||
const engine = new Engine();
|
||||
|
||||
engine.curve = curve;
|
||||
|
||||
engine.memory = new WebAssembly.Memory({initial:MEM_SIZE});
|
||||
engine.u8 = new Uint8Array(engine.memory.buffer);
|
||||
engine.u32 = new Uint32Array(engine.memory.buffer);
|
||||
|
||||
const wasmModule = await WebAssembly.compile(wasm.code);
|
||||
|
||||
engine.instance = await WebAssembly.instantiate(wasmModule, {
|
||||
env: {
|
||||
"memory": engine.memory
|
||||
}
|
||||
});
|
||||
|
||||
engine.singleThread = singleThread;
|
||||
engine.initalPFree = engine.u32[0]; // Save the Pointer to free space.
|
||||
engine.pq = wasm.pq;
|
||||
engine.pr = wasm.pr;
|
||||
engine.pG1gen = wasm.pG1gen;
|
||||
engine.pG1zero = wasm.pG1zero;
|
||||
engine.pG2gen = wasm.pG2gen;
|
||||
engine.pG2zero = wasm.pG2zero;
|
||||
engine.pOneT = wasm.pOneT;
|
||||
|
||||
engine.pTmp0 = engine.alloc(curve.G2.F.n8*3);
|
||||
engine.pTmp1 = engine.alloc(curve.G2.F.n8*3);
|
||||
|
||||
|
||||
if (singleThread) {
|
||||
engine.code = wasm.code;
|
||||
engine.taskManager = thread();
|
||||
await engine.taskManager({
|
||||
command: "INIT",
|
||||
init: MEM_SIZE,
|
||||
code: wasm.code
|
||||
});
|
||||
engine.concurrency = 1;
|
||||
} else {
|
||||
engine.workers = [];
|
||||
engine.pendingDeferreds = [];
|
||||
engine.working = [];
|
||||
|
||||
let concurrency;
|
||||
|
||||
if ((typeof(navigator) === "object") && navigator.hardwareConcurrency) {
|
||||
concurrency = navigator.hardwareConcurrency;
|
||||
} else {
|
||||
concurrency = 8;
|
||||
}
|
||||
|
||||
for (let i = 0; i<concurrency; i++) {
|
||||
|
||||
if (inBrowser) {
|
||||
const blob = new Blob(["(", thread.toString(), ")(self);"], { type: "text/javascript" });
|
||||
const url = URL.createObjectURL(blob);
|
||||
|
||||
engine.workers[i] = new Worker(url);
|
||||
|
||||
engine.workers[i].onmessage = getOnMsg(i);
|
||||
|
||||
} else {
|
||||
engine.workers[i] = new NodeWorker("(" + thread.toString()+ ")(require('worker_threads').parentPort);", {eval: true});
|
||||
|
||||
engine.workers[i].on("message", getOnMsg(i));
|
||||
}
|
||||
|
||||
engine.working[i]=false;
|
||||
}
|
||||
|
||||
const initPromises = [];
|
||||
for (let i=0; i<engine.workers.length;i++) {
|
||||
const copyCode = wasm.code.buffer.slice(0);
|
||||
initPromises.push(engine.postAction(i, {
|
||||
command: "INIT",
|
||||
init: MEM_SIZE,
|
||||
code: copyCode
|
||||
|
||||
}, [copyCode]));
|
||||
}
|
||||
|
||||
await Promise.all(initPromises);
|
||||
|
||||
}
|
||||
return engine;
|
||||
|
||||
function getOnMsg(i) {
|
||||
return function(e) {
|
||||
let data;
|
||||
if ((e)&&(e.data)) {
|
||||
data = e.data;
|
||||
} else {
|
||||
data = e;
|
||||
}
|
||||
|
||||
engine.working[i]=false;
|
||||
engine.pendingDeferreds[i].resolve(data);
|
||||
engine.processWorks();
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Engine {
|
||||
constructor() {
|
||||
this.actionQueue = [];
|
||||
}
|
||||
|
||||
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) {
|
||||
if (this.singleThread) {
|
||||
const res = this.taskManager(actionData);
|
||||
return res[0];
|
||||
} else {
|
||||
const d = new Deferred();
|
||||
this.actionQueue.push({
|
||||
data: actionData,
|
||||
transfers: transfers,
|
||||
deferred: d
|
||||
});
|
||||
this.processWorks();
|
||||
return d.promise;
|
||||
}
|
||||
}
|
||||
|
||||
resetMemory() {
|
||||
this.u32[0] = this.initalPFree;
|
||||
}
|
||||
|
||||
allocG1(P) {
|
||||
const pointer = this.alloc(this.curve.G1.F.n8*3);
|
||||
if (P) this.setG1(pointer, P);
|
||||
return pointer;
|
||||
}
|
||||
|
||||
allocG2(P) {
|
||||
const pointer = this.alloc(this.curve.G2.F.n8*3);
|
||||
if (P) this.setG2(pointer, P);
|
||||
return pointer;
|
||||
}
|
||||
|
||||
allocF1(e) {
|
||||
const pointer = this.alloc(this.curve.F1.n8);
|
||||
if (e) this.setF1(pointer, e);
|
||||
return pointer;
|
||||
}
|
||||
|
||||
allocF2(e) {
|
||||
const pointer = this.alloc(this.curve.F2.n8);
|
||||
if (e) this.setF2(pointer, e);
|
||||
return pointer;
|
||||
}
|
||||
|
||||
allocFr(e) {
|
||||
const pointer = this.alloc(this.curve.Fr.n8);
|
||||
if (e) this.setFr(pointer, e);
|
||||
return pointer;
|
||||
}
|
||||
|
||||
setG1(pointer, point) {
|
||||
this.curve.G1.toRprLE(this.memory, pointer, point);
|
||||
this.instance.g1m_toMontgomery(pointer, pointer);
|
||||
}
|
||||
|
||||
setG2(pointer, point) {
|
||||
this.curve.G2.toRprLE(this.memory, pointer, point);
|
||||
this.instance.g2m_toMontgomery(pointer, pointer);
|
||||
}
|
||||
|
||||
setF1(pointer, element) {
|
||||
this.curve.F2.toRprLE(this.memory, pointer, element);
|
||||
this.instance.f1m_toMontgomery(pointer, pointer);
|
||||
}
|
||||
|
||||
setF2(pointer, element) {
|
||||
this.curve.F1.toRprLE(this.memory, pointer, element);
|
||||
this.instance.f2m_toMontgomery(pointer, pointer);
|
||||
}
|
||||
|
||||
setFr(pointer, element) {
|
||||
this.curve.Fr.toRprLE(this.memory, pointer, element);
|
||||
this.instance.frm_toMontgomery(pointer, pointer);
|
||||
}
|
||||
|
||||
getG1(pointer) {
|
||||
this.instance.g1m_fromMontgomery(pointer, this.pTmp1);
|
||||
return this.curve.G1.fromRprLE(this.memory, this.pTmp1);
|
||||
}
|
||||
|
||||
getG2(pointer) {
|
||||
this.instance.g2m_fromMontgomery(pointer, this.pTmp1);
|
||||
return this.curve.G2.fromRprLE(this.memory, this.pTmp1);
|
||||
}
|
||||
|
||||
getF1(pointer) {
|
||||
this.instance.f1m_fromMontgomery(pointer, this.pTmp1);
|
||||
return this.curve.F1.fromRprLE(this.memory, this.pTmp1);
|
||||
}
|
||||
|
||||
getF2(pointer) {
|
||||
this.instance.f2m_fromMontgomery(pointer, this.pTmp1);
|
||||
return this.curve.F2.fromRprLE(this.memory, this.pTmp1);
|
||||
}
|
||||
|
||||
getFt(pointer) {
|
||||
this.instance.frm_fromMontgomery(pointer, this.pTmp1);
|
||||
return this.curve.Frm.fromRprLE(this.memory, this.pTmp1);
|
||||
}
|
||||
|
||||
allocBuff(buff) {
|
||||
const pointer = this.alloc(buff.byteLength);
|
||||
this.setBuffer(pointer, buff);
|
||||
return pointer;
|
||||
}
|
||||
|
||||
getBuffer(pointer, length) {
|
||||
return new Uint8Array(this.u8.buffer, this.u8.byteOffset + pointer, length);
|
||||
}
|
||||
|
||||
setBuffer(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({command: "TERMINATE"});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async batchApplyKey(Gs, buff, first, inc) {
|
||||
const self = this;
|
||||
const G = self.curve[Gs];
|
||||
const Fr = self.curve.Fr;
|
||||
const sG = G.F.n8*2;
|
||||
const nPoints = Math.floor(buff.byteLength / sG);
|
||||
const pointsPerChunk = Math.floor(nPoints/self.concurrency);
|
||||
const opPromises = [];
|
||||
const bInc = new Uint8Array(Fr.n8);
|
||||
Fr.toRprLEM(bInc.buffer, 0, inc);
|
||||
let t = Fr.e(first);
|
||||
for (let i=0; i<self.concurrency; i++) {
|
||||
const n = Math.min(nPoints - i, pointsPerChunk);
|
||||
if (n==0) continue;
|
||||
const bFirst = new Uint8Array(Fr.n8);
|
||||
Fr.toRprLEM(bFirst.buffer, 0, t);
|
||||
|
||||
opPromises.push(
|
||||
self.queueAction({
|
||||
command: "BATCH_APPLY_KEY",
|
||||
Gs: Gs,
|
||||
buff: buff.slice(i*pointsPerChunk*sG, i*pointsPerChunk*sG + n*sG),
|
||||
first: bFirst,
|
||||
inc: bInc,
|
||||
n: n
|
||||
})
|
||||
);
|
||||
t = Fr.mul(t, Fr.pow(inc, n));
|
||||
}
|
||||
|
||||
const result = await Promise.all(opPromises);
|
||||
|
||||
const outBuff = new Uint8Array(buff.byteLength);
|
||||
for (let i=0; i<self.concurrency; i++) {
|
||||
const n = Math.min(nPoints - i, pointsPerChunk);
|
||||
if (n==0) continue;
|
||||
outBuff.set(result[i], i*pointsPerChunk*sG);
|
||||
}
|
||||
|
||||
return outBuff;
|
||||
}
|
||||
|
||||
|
||||
async batchConvert(Gs, fr, to, fullBuffIn) {
|
||||
const self = this;
|
||||
const G = self.curve[Gs];
|
||||
let sGin;
|
||||
if (["LEM", "U"].indexOf(fr)>=0) {
|
||||
sGin = G.F.n8*2;
|
||||
} else if (["C"].indexOf(fr)>=0) {
|
||||
sGin = G.F.n8;
|
||||
} else {
|
||||
throw new Error("Invaid convertion format: "+fr);
|
||||
}
|
||||
let sGout;
|
||||
if (["LEM", "U"].indexOf(fr)>=0) {
|
||||
sGout = G.F.n8*2;
|
||||
} else if (["C"].indexOf(fr)>=0) {
|
||||
sGout = G.F.n8;
|
||||
} else {
|
||||
throw new Error("Invaid convertion format: "+fr);
|
||||
}
|
||||
const nPoints = Math.floor(fullBuffIn.byteLength / sGin);
|
||||
const pointsPerChunk = Math.floor(nPoints/self.concurrency);
|
||||
const opPromises = [];
|
||||
for (let i=0; i<self.concurrency; i++) {
|
||||
const n = Math.min(nPoints - i, pointsPerChunk);
|
||||
if (n==0) continue;
|
||||
|
||||
opPromises.push(
|
||||
self.queueAction({
|
||||
command: "BATCH_CONVERT",
|
||||
Gs: Gs,
|
||||
fr: fr,
|
||||
sGin: sGin,
|
||||
sGout: sGout,
|
||||
to: to,
|
||||
buff: fullBuffIn.slice(i*pointsPerChunk*sGin, i*pointsPerChunk*sGin + n*sGin),
|
||||
n: n
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
const result = await Promise.all(opPromises);
|
||||
|
||||
const fullBuffOut = new Uint8Array(nPoints*sGout);
|
||||
for (let i=0; i<self.concurrency; i++) {
|
||||
const n = Math.min(nPoints - i, pointsPerChunk);
|
||||
if (n==0) continue;
|
||||
fullBuffOut.set(result[i], i*pointsPerChunk*sGout);
|
||||
}
|
||||
|
||||
return fullBuffOut;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
module.exports = buildEngine;
|
||||
142
src/engine_thread.js
Normal file
142
src/engine_thread.js
Normal file
@@ -0,0 +1,142 @@
|
||||
/* global WebAssembly */
|
||||
|
||||
module.exports = function thread(self) {
|
||||
let instance;
|
||||
let memory;
|
||||
let u32;
|
||||
let u8;
|
||||
|
||||
if (self) {
|
||||
self.onmessage = function(e) {
|
||||
let data;
|
||||
if (e.data) {
|
||||
data = e.data;
|
||||
} else {
|
||||
data = e;
|
||||
}
|
||||
|
||||
if (data.command == "INIT") {
|
||||
init(data).then(function() {
|
||||
self.postMessage(data.result);
|
||||
});
|
||||
} else if (data.command == "TERMINATE") {
|
||||
process.exit();
|
||||
} else {
|
||||
const res = taskManager(data);
|
||||
self.postMessage(res[0], [res[1]]);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async function init(data) {
|
||||
const code = new Uint8Array(data.code);
|
||||
const wasmModule = await WebAssembly.compile(code);
|
||||
memory = new WebAssembly.Memory({initial:data.init});
|
||||
u32 = new Uint32Array(memory.buffer);
|
||||
u8 = new Uint8Array(memory.buffer);
|
||||
|
||||
instance = await WebAssembly.instantiate(wasmModule, {
|
||||
env: {
|
||||
"memory": memory
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
function alloc(length) {
|
||||
while (u32[0] & 3) u32[0]++; // Return always aligned pointers
|
||||
const res = u32[0];
|
||||
u32[0] += length;
|
||||
while (u32[0] > memory.buffer.byteLength) {
|
||||
memory.grow(100);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
function allocBuffer(buffer) {
|
||||
const p = alloc(buffer.byteLength);
|
||||
setBuffer(p, buffer);
|
||||
return p;
|
||||
}
|
||||
|
||||
function getBuffer(pointer, length) {
|
||||
return new Uint8Array(u8.buffer, u8.byteOffset + pointer, length);
|
||||
}
|
||||
|
||||
function setBuffer(pointer, buffer) {
|
||||
u8.set(new Uint8Array(buffer), pointer);
|
||||
}
|
||||
|
||||
function batchApplyKey(task) {
|
||||
const outBuffLen = task.buff.byteLength;
|
||||
const oldAlloc = u32[0];
|
||||
const pBufIn = allocBuffer(task.buff);
|
||||
const pFirst = allocBuffer(task.first);
|
||||
const pInc = allocBuffer(task.inc);
|
||||
const pBuffOut = alloc(outBuffLen);
|
||||
if (task.Gs == "G1") {
|
||||
instance.exports.g1m_batchApplyKey(pBufIn, task.n, pFirst, pInc, pBuffOut);
|
||||
} else {
|
||||
instance.exports.g2m_batchApplyKey(pBufIn, task.n, pFirst, pInc, pBuffOut);
|
||||
}
|
||||
|
||||
const outBuff = getBuffer(pBuffOut, outBuffLen).slice();
|
||||
u32[0] = oldAlloc;
|
||||
return [ outBuff, outBuff];
|
||||
}
|
||||
|
||||
function batchConvert(task) {
|
||||
const oldAlloc = u32[0];
|
||||
|
||||
const outBuffLen = task.n*task.sGin;
|
||||
const pBufIn = allocBuffer(task.buff);
|
||||
const pBuffOut = alloc(outBuffLen);
|
||||
if (task.Gs == "G1") {
|
||||
if (task.fr=="LEM") {
|
||||
if (task.to=="U") {
|
||||
instance.exports.g1m_batchLEMtoU(pBufIn, task.n, pBuffOut);
|
||||
} else if (task.to=="C") {
|
||||
instance.exports.g1m_batchLEMtoC(pBufIn, task.n, pBuffOut);
|
||||
} else {
|
||||
throw new Error("Invalid to: "+task.to);
|
||||
}
|
||||
} else {
|
||||
throw new Error("Invalid fr: "+task.fr);
|
||||
}
|
||||
} else if (task.Gs == "G2") {
|
||||
if (task.fr=="LEM") {
|
||||
if (task.to=="U") {
|
||||
instance.exports.g2m_batchLEMtoU(pBufIn, task.n, pBuffOut);
|
||||
} else if (task.to=="C") {
|
||||
instance.exports.g2m_batchLEMtoC(pBufIn, task.n, pBuffOut);
|
||||
} else {
|
||||
throw new Error("Invalid to: "+task.to);
|
||||
}
|
||||
} else {
|
||||
throw new Error("Invalid fr: "+task.fr);
|
||||
}
|
||||
} else {
|
||||
throw new Error("Invalid group: "+task.gs);
|
||||
}
|
||||
|
||||
const outBuff = getBuffer(pBuffOut, outBuffLen).slice();
|
||||
u32[0] = oldAlloc;
|
||||
return [ outBuff, outBuff];
|
||||
}
|
||||
|
||||
function taskManager(task) {
|
||||
if (task.command == "INIT") {
|
||||
return init(task);
|
||||
} else if (task.command == "BATCH_APPLY_KEY") {
|
||||
return batchApplyKey(task);
|
||||
} else if (task.command == "BATCH_CONVERT") {
|
||||
return batchConvert(task);
|
||||
} else {
|
||||
console.log("Invalid task", task);
|
||||
throw new Error("Invalid task");
|
||||
}
|
||||
}
|
||||
|
||||
return taskManager;
|
||||
};
|
||||
@@ -1,6 +1,53 @@
|
||||
const Scalar = require("./scalar");
|
||||
const assert = require("assert");
|
||||
|
||||
const supportsNativeBigInt = typeof BigInt === "function";
|
||||
|
||||
let F1Field;
|
||||
if (supportsNativeBigInt) {
|
||||
module.exports = require("./f1field_native");
|
||||
F1Field = require("./f1field_native");
|
||||
} else {
|
||||
module.exports = require("./f1field_bigint");
|
||||
F1Field = require("./f1field_bigint");
|
||||
}
|
||||
|
||||
|
||||
// Returns a buffer with Little Endian Representation
|
||||
F1Field.prototype.toRprLE = function toRprLE(buff, o, e) {
|
||||
Scalar.toRprLE(buff, o, e, this.n64*8);
|
||||
};
|
||||
|
||||
// Returns a buffer with Big Endian Representation
|
||||
F1Field.prototype.toRprBE = function toRprBE(buff, o, e) {
|
||||
Scalar.toRprBE(buff, o, e, this.n64*8);
|
||||
};
|
||||
|
||||
// Returns a buffer with Big Endian Montgomery Representation
|
||||
F1Field.prototype.toRprBEM = function toRprBEM(buff, o, e) {
|
||||
return this.toRprBE(buff, o, this.mul(this.R, e));
|
||||
};
|
||||
|
||||
F1Field.prototype.toRprLEM = function toRprLEM(buff, o, e) {
|
||||
return this.toRprLE(buff, o, this.mul(this.R, e));
|
||||
};
|
||||
|
||||
|
||||
// Pases a buffer with Little Endian Representation
|
||||
F1Field.prototype.fromRprLE = function fromRprLE(buff, o) {
|
||||
return Scalar.fromRprLE(buff, o, this.n8);
|
||||
};
|
||||
|
||||
// Pases a buffer with Big Endian Representation
|
||||
F1Field.prototype.fromRprBE = function fromRprBE(buff, o) {
|
||||
return Scalar.fromRprBE(buff, o, this.n8);
|
||||
};
|
||||
|
||||
F1Field.prototype.fromRprLEM = function fromRprLEM(buff, o) {
|
||||
return this.mul(this.fromRprLE(buff, o), this.Ri);
|
||||
};
|
||||
|
||||
F1Field.prototype.fromRprBEM = function fromRprBEM(buff, o) {
|
||||
return this.mul(this.fromRprBE(buff, o), this.Ri);
|
||||
};
|
||||
|
||||
|
||||
module.exports = F1Field;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
const bigInt = require("big-integer");
|
||||
const assert = require("assert");
|
||||
const buildSqrt = require("./fsqrt");
|
||||
|
||||
function getRandomByte() {
|
||||
if (typeof window !== "undefined") { // Browser
|
||||
@@ -19,28 +20,33 @@ function getRandomByte() {
|
||||
|
||||
module.exports = class ZqField {
|
||||
constructor(p) {
|
||||
this.type="F1";
|
||||
this.one = bigInt.one;
|
||||
this.zero = bigInt.zero;
|
||||
this.p = bigInt(p);
|
||||
this.minusone = this.p.minus(bigInt.one);
|
||||
this.m = 1;
|
||||
this.negone = this.p.minus(bigInt.one);
|
||||
this.two = bigInt(2);
|
||||
this.half = this.p.shiftRight(1);
|
||||
this.bitLength = this.p.bitLength();
|
||||
this.mask = bigInt.one.shiftLeft(this.bitLength).minus(bigInt.one);
|
||||
|
||||
this.n64 = Math.floor((this.bitLength - 1) / 64)+1;
|
||||
this.n32 = this.n64*2;
|
||||
this.n8 = this.n64*8;
|
||||
this.R = bigInt.one.shiftLeft(this.n64*64);
|
||||
|
||||
const e = this.minusone.shiftRight(this.one);
|
||||
this.Ri = this.inv(this.R);
|
||||
/*
|
||||
const e = this.negone.shiftRight(this.one);
|
||||
this.nqr = this.two;
|
||||
let r = this.pow(this.nqr, e);
|
||||
while (!r.equals(this.minusone)) {
|
||||
while (!r.equals(this.negone)) {
|
||||
this.nqr = this.nqr.add(this.one);
|
||||
r = this.pow(this.nqr, e);
|
||||
}
|
||||
|
||||
this.s = this.zero;
|
||||
this.t = this.minusone;
|
||||
this.t = this.negone;
|
||||
|
||||
while (!this.t.isOdd()) {
|
||||
this.s = this.s.add(this.one);
|
||||
@@ -48,6 +54,8 @@ module.exports = class ZqField {
|
||||
}
|
||||
|
||||
this.nqr_to_t = this.pow(this.nqr, this.t);
|
||||
*/
|
||||
buildSqrt(this);
|
||||
}
|
||||
|
||||
e(a,b) {
|
||||
@@ -200,12 +208,12 @@ module.exports = class ZqField {
|
||||
return a.isZero() ? bigInt.one : bigInt.zero;
|
||||
}
|
||||
|
||||
sqrt(n) {
|
||||
sqrt_old(n) {
|
||||
|
||||
if (n.equals(this.zero)) return this.zero;
|
||||
|
||||
// Test that have solution
|
||||
const res = this.pow(n, this.minusone.shiftRight(this.one));
|
||||
const res = this.pow(n, this.negone.shiftRight(this.one));
|
||||
if (!res.equals(this.one)) return null;
|
||||
|
||||
let m = parseInt(this.s);
|
||||
@@ -273,6 +281,19 @@ module.exports = class ZqField {
|
||||
return a.isZero();
|
||||
}
|
||||
|
||||
fromRng(rng) {
|
||||
let v;
|
||||
do {
|
||||
v = bigInt(0);
|
||||
for (let i=0; i<this.n64; i++) {
|
||||
v = v.add(v, rng.nextU64().shiftLeft(64*i));
|
||||
}
|
||||
v = v.and(this.mask);
|
||||
} while (v.geq(this.p));
|
||||
v = v.times(this.Ri).mod(this.q);
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
const assert = require("assert");
|
||||
const Scalar = require("./scalar");
|
||||
const futils = require("./futils");
|
||||
const buildSqrt = require("./fsqrt");
|
||||
|
||||
function getRandomByte() {
|
||||
if (typeof window !== "undefined") { // Browser
|
||||
@@ -21,28 +22,34 @@ function getRandomByte() {
|
||||
|
||||
module.exports = class ZqField {
|
||||
constructor(p) {
|
||||
this.type="F1";
|
||||
this.one = 1n;
|
||||
this.zero = 0n;
|
||||
this.p = BigInt(p);
|
||||
this.minusone = this.p-1n;
|
||||
this.m = 1;
|
||||
this.negone = this.p-1n;
|
||||
this.two = 2n;
|
||||
this.half = this.p >> 1n;
|
||||
this.bitLength = Scalar.bitLength(this.p);
|
||||
this.mask = (1n << BigInt(this.bitLength)) - 1n;
|
||||
|
||||
this.n64 = Math.floor((this.bitLength - 1) / 64)+1;
|
||||
this.n32 = this.n64*2;
|
||||
this.n8 = this.n64*8;
|
||||
this.R = this.e(1n << BigInt(this.n64*64));
|
||||
|
||||
const e = this.minusone >> 1n;
|
||||
this.Ri = this.inv(this.R);
|
||||
/*
|
||||
const e = this.negone >> 1n;
|
||||
this.nqr = this.two;
|
||||
let r = this.pow(this.nqr, e);
|
||||
while (!this.eq(r, this.minusone)) {
|
||||
while (!this.eq(r, this.negone)) {
|
||||
this.nqr = this.nqr + 1n;
|
||||
r = this.pow(this.nqr, e);
|
||||
}
|
||||
|
||||
|
||||
this.s = 0;
|
||||
this.t = this.minusone;
|
||||
this.t = this.negone;
|
||||
|
||||
while ((this.t & 1n) == 0n) {
|
||||
this.s = this.s + 1;
|
||||
@@ -50,6 +57,8 @@ module.exports = class ZqField {
|
||||
}
|
||||
|
||||
this.nqr_to_t = this.pow(this.nqr, this.t);
|
||||
*/
|
||||
buildSqrt(this);
|
||||
}
|
||||
|
||||
e(a,b) {
|
||||
@@ -219,12 +228,12 @@ module.exports = class ZqField {
|
||||
return (a) ? 0n : 1n;
|
||||
}
|
||||
|
||||
sqrt(n) {
|
||||
sqrt_old(n) {
|
||||
|
||||
if (n == 0n) return this.zero;
|
||||
|
||||
// Test that have solution
|
||||
const res = this.pow(n, this.minusone >> this.one);
|
||||
const res = this.pow(n, this.negone >> this.one);
|
||||
if ( res != 1n ) return null;
|
||||
|
||||
let m = this.s;
|
||||
@@ -292,5 +301,18 @@ module.exports = class ZqField {
|
||||
return a == 0n;
|
||||
}
|
||||
|
||||
fromRng(rng) {
|
||||
let v;
|
||||
do {
|
||||
v=0n;
|
||||
for (let i=0; i<this.n64; i++) {
|
||||
v += rng.nextU64() << BigInt(64 *i);
|
||||
}
|
||||
v &= this.mask;
|
||||
} while (v >= this.p);
|
||||
v = (v * this.Ri) % this.p; // Convert from montgomery
|
||||
return v;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
103
src/f2field.js
103
src/f2field.js
@@ -18,13 +18,23 @@
|
||||
*/
|
||||
|
||||
const fUtils = require("./futils.js");
|
||||
const buildSqrt = require("./fsqrt");
|
||||
|
||||
class F2Field {
|
||||
constructor(F, nonResidue) {
|
||||
this.type="F2";
|
||||
this.F = F;
|
||||
this.zero = [this.F.zero, this.F.zero];
|
||||
this.one = [this.F.one, this.F.zero];
|
||||
this.negone = this.neg(this.one);
|
||||
this.nonResidue = nonResidue;
|
||||
this.m = F.m*2;
|
||||
this.p = F.p;
|
||||
this.n64 = F.n64*2;
|
||||
this.n32 = this.n64*2;
|
||||
this.n8 = this.n64*8;
|
||||
|
||||
buildSqrt(this);
|
||||
}
|
||||
|
||||
_mulByNonResidue(a) {
|
||||
@@ -57,6 +67,13 @@ class F2Field {
|
||||
return this.sub(this.zero, a);
|
||||
}
|
||||
|
||||
conjugate(a) {
|
||||
return [
|
||||
a[0],
|
||||
this.F.neg(a[1])
|
||||
];
|
||||
}
|
||||
|
||||
mul(a, b) {
|
||||
const aA = this.F.mul(a[0] , b[0]);
|
||||
const bB = this.F.mul(a[1] , b[1]);
|
||||
@@ -120,13 +137,97 @@ class F2Field {
|
||||
return fUtils.mulScalar(this, base, e);
|
||||
}
|
||||
|
||||
exp(base, e) {
|
||||
pow(base, e) {
|
||||
return fUtils.exp(this, base, e);
|
||||
}
|
||||
|
||||
toString(a) {
|
||||
return `[ ${this.F.toString(a[0])} , ${this.F.toString(a[1])} ]`;
|
||||
}
|
||||
|
||||
fromRng(rng) {
|
||||
const c0 = this.F.fromRng(rng);
|
||||
const c1 = this.F.fromRng(rng);
|
||||
return [c0, c1];
|
||||
}
|
||||
|
||||
gt(a, b) {
|
||||
if (this.F.gt(a[0], b[0])) return true;
|
||||
if (this.F.gt(b[0], a[0])) return false;
|
||||
if (this.F.gt(a[1], b[1])) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
geq(a, b) {
|
||||
return this.gt(a, b) || this.eq(a, b);
|
||||
}
|
||||
|
||||
lt(a, b) {
|
||||
return !this.geq(a,b);
|
||||
}
|
||||
|
||||
leq(a, b) {
|
||||
return !this.gt(a,b);
|
||||
}
|
||||
|
||||
neq(a, b) {
|
||||
return !this.eq(a,b);
|
||||
}
|
||||
|
||||
random() {
|
||||
return [this.F.random(), this.F.random()];
|
||||
}
|
||||
|
||||
|
||||
toRprLE(buff, o, e) {
|
||||
this.F.toRprLE(buff, o, e[0]);
|
||||
this.F.toRprLE(buff, o+this.F.n8, e[1]);
|
||||
}
|
||||
|
||||
toRprBE(buff, o, e) {
|
||||
this.F.toRprBE(buff, o, e[1]);
|
||||
this.F.toRprBE(buff, o+this.F.n8, e[0]);
|
||||
}
|
||||
|
||||
toRprLEM(buff, o, e) {
|
||||
this.F.toRprLEM(buff, o, e[0]);
|
||||
this.F.toRprLEM(buff, o+this.F.n8, e[1]);
|
||||
}
|
||||
|
||||
|
||||
toRprBEM(buff, o, e) {
|
||||
this.F.toRprBEM(buff, o, e[1]);
|
||||
this.F.toRprBEM(buff, o+this.F.n8, e[0]);
|
||||
}
|
||||
|
||||
fromRprLE(buff, o) {
|
||||
o = o || 0;
|
||||
const c0 = this.F.fromRprLE(buff, o);
|
||||
const c1 = this.F.fromRprLE(buff, o+this.F.n8);
|
||||
return [c0, c1];
|
||||
}
|
||||
|
||||
fromRprBE(buff, o) {
|
||||
o = o || 0;
|
||||
const c1 = this.F.fromRprBE(buff, o);
|
||||
const c0 = this.F.fromRprBE(buff, o+this.F.n8);
|
||||
return [c0, c1];
|
||||
}
|
||||
|
||||
fromRprLEM(buff, o) {
|
||||
o = o || 0;
|
||||
const c0 = this.F.fromRprLEM(buff, o);
|
||||
const c1 = this.F.fromRprLEM(buff, o+this.F.n8);
|
||||
return [c0, c1];
|
||||
}
|
||||
|
||||
fromRprBEM(buff, o) {
|
||||
o = o || 0;
|
||||
const c1 = this.F.fromRprBEM(buff, o);
|
||||
const c0 = this.F.fromRprBEM(buff, o+this.F.n8);
|
||||
return [c0, c1];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = F2Field;
|
||||
|
||||
103
src/f3field.js
103
src/f3field.js
@@ -21,10 +21,17 @@ const fUtils = require("./futils.js");
|
||||
|
||||
class F3Field {
|
||||
constructor(F, nonResidue) {
|
||||
this.type="F3";
|
||||
this.F = F;
|
||||
this.zero = [this.F.zero, this.F.zero, this.F.zero];
|
||||
this.one = [this.F.one, this.F.zero, this.F.zero];
|
||||
this.negone = this.neg(this.one);
|
||||
this.nonResidue = nonResidue;
|
||||
this.m = F.m*3;
|
||||
this.p = F.p;
|
||||
this.n64 = F.n64*2;
|
||||
this.n32 = this.n64*2;
|
||||
this.n8 = this.n64*8;
|
||||
}
|
||||
|
||||
_mulByNonResidue(a) {
|
||||
@@ -171,6 +178,102 @@ class F3Field {
|
||||
toString(a) {
|
||||
return `[ ${this.F.toString(a[0])} , ${this.F.toString(a[1])}, ${this.F.toString(a[2])} ]`;
|
||||
}
|
||||
|
||||
fromRng(rng) {
|
||||
const c0 = this.F.fromRng(rng);
|
||||
const c1 = this.F.fromRng(rng);
|
||||
const c2 = this.F.fromRng(rng);
|
||||
return [c0, c1, c2];
|
||||
}
|
||||
|
||||
gt(a, b) {
|
||||
if (this.F.gt(a[0], b[0])) return true;
|
||||
if (this.F.gt(b[0], a[0])) return false;
|
||||
if (this.F.gt(a[1], b[1])) return true;
|
||||
if (this.F.gt(b[1], a[1])) return false;
|
||||
if (this.F.gt(a[2], b[2])) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
geq(a, b) {
|
||||
return this.gt(a, b) || this.eq(a, b);
|
||||
}
|
||||
|
||||
lt(a, b) {
|
||||
return !this.geq(a,b);
|
||||
}
|
||||
|
||||
leq(a, b) {
|
||||
return !this.gt(a,b);
|
||||
}
|
||||
|
||||
neq(a, b) {
|
||||
return !this.eq(a,b);
|
||||
}
|
||||
|
||||
random() {
|
||||
return [this.F.random(), this.F.random(), this.F.random()];
|
||||
}
|
||||
|
||||
|
||||
toRprLE(buff, o, e) {
|
||||
this.F.toRprLE(buff, o, e[0]);
|
||||
this.F.toRprLE(buff, o+this.F.n8, e[1]);
|
||||
this.F.toRprLE(buff, o+this.F.n8*2, e[2]);
|
||||
}
|
||||
|
||||
toRprBE(buff, o, e) {
|
||||
this.F.toRprBE(buff, o, e[2]);
|
||||
this.F.toRprBE(buff, o+this.F.n8, e[1]);
|
||||
this.F.toRprBE(buff, o+this.F.n8*2, e[0]);
|
||||
}
|
||||
|
||||
toRprLEM(buff, o, e) {
|
||||
this.F.toRprLEM(buff, o, e[0]);
|
||||
this.F.toRprLEM(buff, o+this.F.n8, e[1]);
|
||||
this.F.toRprLEM(buff, o+this.F.n8*2, e[2]);
|
||||
}
|
||||
|
||||
|
||||
toRprBEM(buff, o, e) {
|
||||
this.F.toRprBEM(buff, o, e[2]);
|
||||
this.F.toRprBEM(buff, o+this.F.n8, e[1]);
|
||||
this.F.toRprBEM(buff, o+this.F.n8*2, e[0]);
|
||||
}
|
||||
|
||||
fromRprLE(buff, o) {
|
||||
o = o || 0;
|
||||
const c0 = this.F.fromRprLE(buff, o);
|
||||
const c1 = this.F.fromRprLE(buff, o+this.n8);
|
||||
const c2 = this.F.fromRprLE(buff, o+this.n8*2);
|
||||
return [c0, c1, c2];
|
||||
}
|
||||
|
||||
fromRprBE(buff, o) {
|
||||
o = o || 0;
|
||||
const c2 = this.F.fromRprBE(buff, o);
|
||||
const c1 = this.F.fromRprBE(buff, o+this.n8);
|
||||
const c0 = this.F.fromRprBE(buff, o+this.n8*2);
|
||||
return [c0, c1, c2];
|
||||
}
|
||||
|
||||
fromRprLEM(buff, o) {
|
||||
o = o || 0;
|
||||
const c0 = this.F.fromRprLEM(buff, o);
|
||||
const c1 = this.F.fromRprLEM(buff, o+this.n8);
|
||||
const c2 = this.F.fromRprLEM(buff, o+this.n8*2);
|
||||
return [c0, c1, c2];
|
||||
}
|
||||
|
||||
fromRprBEM(buff, o) {
|
||||
o = o || 0;
|
||||
const c2 = this.F.fromRprBEM(buff, o);
|
||||
const c1 = this.F.fromRprBEM(buff, o+this.n8);
|
||||
const c0 = this.F.fromRprBEM(buff, o+this.n8*2);
|
||||
return [c0, c1, c2];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = F3Field;
|
||||
|
||||
256
src/fsqrt.js
Normal file
256
src/fsqrt.js
Normal file
@@ -0,0 +1,256 @@
|
||||
const Scalar = require("./scalar");
|
||||
const assert = require("assert");
|
||||
// Check here: https://eprint.iacr.org/2012/685.pdf
|
||||
|
||||
module.exports = function buildSqrt (F) {
|
||||
if ((F.m % 2) == 1) {
|
||||
if (Scalar.eq(Scalar.mod(F.p, 4), 1 )) {
|
||||
if (Scalar.eq(Scalar.mod(F.p, 8), 1 )) {
|
||||
if (Scalar.eq(Scalar.mod(F.p, 16), 1 )) {
|
||||
// alg7_muller(F);
|
||||
alg5_tonelliShanks(F);
|
||||
} else if (Scalar.eq(Scalar.mod(F.p, 16), 1 )) {
|
||||
alg4_kong(F);
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
} else if (Scalar.eq(Scalar.mod(F.p, 8), 5 )) {
|
||||
alg3_atkin(F);
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
} else if (Scalar.eq(Scalar.mod(F.p, 4), 3 )) {
|
||||
alg2_shanks(F);
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
} else {
|
||||
const pm2mod4 = Scalar.mod(Scalar.pow(F.p, F.m/2), 4);
|
||||
if (pm2mod4 == 1) {
|
||||
alg10_adj(F);
|
||||
} else if (pm2mod4 == 3) {
|
||||
alg9_adj(F);
|
||||
} else {
|
||||
alg8_complex(F);
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function alg7_muller(F) {
|
||||
F.sqrt_q = Scalar.pow(F.p, F.m);
|
||||
F.sqrt_2 = F.add(F.one, F.one);
|
||||
F.sqrt_4 = F.add(F.sqrt_2, F.sqrt_2);
|
||||
F.sqrt_e = Scalar.div( Scalar.sub(F.sqrt_q, 1) , 2);
|
||||
|
||||
F.sqrt_bits = Scalar.bits(Scalar.div( Scalar.sub(F.sqrt_q, 1) , 4));
|
||||
|
||||
F.sqrt_v = function(alfa) {
|
||||
const d = [];
|
||||
d[0] = alfa;
|
||||
d[1] = this.sub(this.square(alfa), this.sqrt_2);
|
||||
for (let j=F.sqrt_bits.length-2; j>0; j--) {
|
||||
const d0 =
|
||||
this.sub(
|
||||
this.mul(d[0], d[1]),
|
||||
alfa
|
||||
);
|
||||
const d1 =
|
||||
this.sub(
|
||||
this.square( d[ 1 - F.sqrt_bits[j] ]),
|
||||
this.sqrt_2
|
||||
);
|
||||
d[ 1 - F.sqrt_bits[j] ] = d0;
|
||||
d[ F.sqrt_bits[j] ] = d1;
|
||||
/*
|
||||
d[ 1 - F.sqrt_bits[j] ] =
|
||||
this.sub(
|
||||
this.mul(d[0], d[1]),
|
||||
alfa
|
||||
);
|
||||
d[ F.sqrt_bits[j] ] =
|
||||
this.sub(
|
||||
this.square( d[ 1 - F.sqrt_bits[j] ]),
|
||||
this.sqrt_2
|
||||
);
|
||||
*/
|
||||
}
|
||||
if (F.sqrt_bits[0] == 1) {
|
||||
return this.sub(
|
||||
this.mul(d[0], d[1]),
|
||||
alfa
|
||||
);
|
||||
} else {
|
||||
return this.sub(
|
||||
this.square(d[0]),
|
||||
this.sqrt_2
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
F.sqrt = function(a) {
|
||||
if (this.isZero(a)) return this.zero;
|
||||
if (this.eq(a, this.sqrt_4)) return this.sqrt_2;
|
||||
|
||||
|
||||
let t = this.one;
|
||||
let a1 = this.pow( this.sub(a, F.sqrt_4), F.sqrt_e);
|
||||
|
||||
while (this.eq(a1, this.one)) {
|
||||
t = this.random();
|
||||
while (this.isZero(t) || this.eq(t, this.one)) {
|
||||
t = this.random();
|
||||
}
|
||||
|
||||
const b = this.sub(this.mul(a, this.square(t)), this.sqrt_4);
|
||||
if (this.isZero(b)) {
|
||||
return this.mul(this.sqrt_2, this.inv(t));
|
||||
}
|
||||
|
||||
a1 = this.pow(b, F.sqrt_e);
|
||||
}
|
||||
|
||||
const alfa = this.sub(this.mul(a, this.square(t)), this.sqrt_2);
|
||||
|
||||
const x = this.div( this.sqrt_v(alfa), t);
|
||||
|
||||
const a0 = this.sub(this.square(x), a);
|
||||
|
||||
if (!this.isZero(a0)) return null;
|
||||
|
||||
return x;
|
||||
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
function alg5_tonelliShanks(F) {
|
||||
F.sqrt_q = Scalar.pow(F.p, F.m);
|
||||
|
||||
F.sqrt_s = 0;
|
||||
F.sqrt_t = Scalar.sub(F.sqrt_q, 1);
|
||||
|
||||
while (!Scalar.isOdd(F.sqrt_t)) {
|
||||
F.sqrt_s = F.sqrt_s + 1;
|
||||
F.sqrt_t = Scalar.div(F.sqrt_t, 2);
|
||||
}
|
||||
|
||||
let c0 = F.one;
|
||||
|
||||
while (F.eq(c0, F.one)) {
|
||||
const c = F.random();
|
||||
F.sqrt_z = F.pow(c, F.sqrt_t);
|
||||
c0 = F.pow(F.sqrt_z, 1 << (F.sqrt_s-1) );
|
||||
}
|
||||
|
||||
F.sqrt_tm1d2 = Scalar.div(Scalar.sub(F.sqrt_t, 1),2);
|
||||
|
||||
F.sqrt = function(a) {
|
||||
const F=this;
|
||||
if (F.isZero(a)) return F.zero;
|
||||
let w = F.pow(a, F.sqrt_tm1d2);
|
||||
const a0 = F.pow( F.mul(F.square(w), a), 1 << (F.sqrt_s-1) );
|
||||
if (F.eq(a0, F.negone)) return null;
|
||||
|
||||
let v = F.sqrt_s;
|
||||
let x = F.mul(a, w);
|
||||
let b = F.mul(x, w);
|
||||
let z = F.sqrt_z;
|
||||
while (!F.eq(b, F.one)) {
|
||||
let b2k = F.square(b);
|
||||
let k=1;
|
||||
while (!F.eq(b2k, F.one)) {
|
||||
b2k = F.square(b2k);
|
||||
k++;
|
||||
}
|
||||
|
||||
w = z;
|
||||
for (let i=0; i<v-k-1; i++) {
|
||||
w = F.square(w);
|
||||
}
|
||||
z = F.square(w);
|
||||
b = F.mul(b, z);
|
||||
x = F.mul(x, w);
|
||||
v = k;
|
||||
}
|
||||
return F.geq(x, F.zero) ? x : F.neg(x);
|
||||
};
|
||||
}
|
||||
|
||||
function alg4_kong(F) {
|
||||
F.sqrt = function() {
|
||||
assert(false, "Not implemented");
|
||||
};
|
||||
}
|
||||
|
||||
function alg3_atkin(F) {
|
||||
F.sqrt = function() {
|
||||
assert(false, "Not implemented");
|
||||
};
|
||||
}
|
||||
|
||||
function alg2_shanks(F) {
|
||||
|
||||
F.sqrt_q = Scalar.pow(F.p, F.m);
|
||||
F.sqrt_e1 = Scalar.div( Scalar.sub(F.sqrt_q, 3) , 4);
|
||||
|
||||
F.sqrt = function(a) {
|
||||
if (this.isZero(a)) return this.zero;
|
||||
|
||||
// Test that have solution
|
||||
const a1 = this.pow(a, this.sqrt_e1);
|
||||
|
||||
const a0 = this.mul(this.square(a1), a);
|
||||
|
||||
if ( this.eq(a0, this.negone) ) return null;
|
||||
|
||||
const x = this.mul(a1, a);
|
||||
|
||||
return F.geq(x, F.zero) ? x : F.neg(x);
|
||||
};
|
||||
}
|
||||
|
||||
function alg10_adj(F) {
|
||||
F.sqrt = function() {
|
||||
assert(false, "Not implemented");
|
||||
};
|
||||
}
|
||||
|
||||
function alg9_adj(F) {
|
||||
F.sqrt_q = Scalar.pow(F.p, F.m/2);
|
||||
F.sqrt_e34 = Scalar.div( Scalar.sub(F.sqrt_q, 3) , 4);
|
||||
F.sqrt_e12 = Scalar.div( Scalar.sub(F.sqrt_q, 1) , 2);
|
||||
|
||||
F.frobenius = function(n, x) {
|
||||
if ((n%2) == 1) {
|
||||
return F.conjugate(x);
|
||||
} else {
|
||||
return x;
|
||||
}
|
||||
};
|
||||
|
||||
F.sqrt = function(a) {
|
||||
const F = this;
|
||||
const a1 = F.pow(a, F.sqrt_e34);
|
||||
const alfa = F.mul(F.square(a1), a);
|
||||
const a0 = F.mul(F.frobenius(1, alfa), alfa);
|
||||
if (F.eq(a0, F.negone)) return null;
|
||||
const x0 = F.mul(a1, a);
|
||||
let x;
|
||||
if (F.eq(alfa, F.negone)) {
|
||||
x = F.mul(x0, [F.F.zero, F.F.one]);
|
||||
} else {
|
||||
const b = F.pow(F.add(F.one, alfa), F.sqrt_e12);
|
||||
x = F.mul(b, x0);
|
||||
}
|
||||
return F.geq(x, F.zero) ? x : F.neg(x);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
function alg8_complex(F) {
|
||||
F.sqrt = function() {
|
||||
assert(false, "Not implemented");
|
||||
};
|
||||
}
|
||||
@@ -28,8 +28,8 @@ class PolField {
|
||||
constructor (F) {
|
||||
this.F = F;
|
||||
|
||||
let rem = F.t;
|
||||
let s = F.s;
|
||||
let rem = F.sqrt_t;
|
||||
let s = F.sqrt_s;
|
||||
|
||||
const five = this.F.add(this.F.add(this.F.two, this.F.two), this.F.one);
|
||||
|
||||
|
||||
@@ -1,9 +1,55 @@
|
||||
|
||||
const assert = require("assert");
|
||||
|
||||
const supportsNativeBigInt = typeof BigInt === "function";
|
||||
|
||||
let Scalar;
|
||||
if (supportsNativeBigInt) {
|
||||
module.exports = require("./scalar_native.js");
|
||||
Scalar = require("./scalar_native.js");
|
||||
} else {
|
||||
module.exports = require("./scalar_bigint.js");
|
||||
Scalar = require("./scalar_bigint.js");
|
||||
}
|
||||
|
||||
|
||||
// Returns a buffer with Little Endian Representation
|
||||
Scalar.__proto__.toRprLE = function rprBE(buff, o, e, n8) {
|
||||
const s = "0000000" + e.toString(16);
|
||||
const v = new Uint32Array(buff, o, n8/4);
|
||||
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;
|
||||
};
|
||||
|
||||
// Returns a buffer with Big Endian Representation
|
||||
Scalar.__proto__.toRprBE = function rprLEM(buff, o, e, n8) {
|
||||
const s = "0000000" + e.toString(16);
|
||||
const v = new DataView(buff, o, n8);
|
||||
const l = (((s.length-7)*4 - 1) >> 5)+1; // Number of 32bit words;
|
||||
for (let i=0; i<l; i++) v.setUint32(n8-i*4 -4, parseInt(s.substring(s.length-8*i-8, s.length-8*i), 16), false);
|
||||
for (let i=0; i<n8/4-l; i++) v[i] = 0;
|
||||
};
|
||||
|
||||
// Pases a buffer with Little Endian Representation
|
||||
Scalar.__proto__.fromRprLE = function rprLEM(buff, o, n8) {
|
||||
n8 = n8 || buff.byteLength;
|
||||
const v = new Uint32Array(buff, o, n8/4);
|
||||
const a = new Array(n8/4);
|
||||
v.forEach( (ch,i) => a[a.length-i-1] = ch.toString(16).padStart(8,"0") );
|
||||
return Scalar.fromString(a.join(""), 16);
|
||||
};
|
||||
|
||||
// Pases a buffer with Big Endian Representation
|
||||
Scalar.__proto__.fromRprBE = function rprLEM(buff, o, n8) {
|
||||
n8 = n8 || buff.byteLength;
|
||||
const v = new DataView(buff, o, n8);
|
||||
const a = new Array(n8/4);
|
||||
for (let i=0; i<n8/4; i++) {
|
||||
a[i] = v.getUint32(i*4, false).toString(16).padStart(8, "0");
|
||||
}
|
||||
return Scalar.fromString(a.join(""), 16);
|
||||
};
|
||||
|
||||
module.exports = Scalar;
|
||||
|
||||
|
||||
|
||||
|
||||
137
test.js
Normal file
137
test.js
Normal file
@@ -0,0 +1,137 @@
|
||||
|
||||
const bn128 = require("./index").bn128;
|
||||
const utils = require("./index").utils;
|
||||
|
||||
const Fq = bn128.F1;
|
||||
|
||||
const blake2b = require("blake2b");
|
||||
|
||||
const ChaCha = require("./src/chacha");
|
||||
|
||||
const pubKey = {
|
||||
tau_g1_s: [
|
||||
Fq.e("0x1403cf4fed293e66a8cd522be9f938524111f6f08762371bff53ee387a39cf13"),
|
||||
Fq.e("0x2accbda355c222301a1bd802db7454d86a4ec2ee89ae895ca21f147d6b705740"),
|
||||
Fq.e("1")
|
||||
],
|
||||
tau_g1_sx: [
|
||||
Fq.e("0x12996cf89d854246f1ab002e446436b77a64349117ec1fb2aa57a304890e81ef"),
|
||||
Fq.e("0x0c17fd067df52c480a1db3c6890821f975932d89d0d53c6c60777cc56f1dd712"),
|
||||
Fq.e("1")
|
||||
],
|
||||
alpha_g1_s:[
|
||||
Fq.e("0x12a64bbe8af7fcb19052e25e188c1fcdac454928142f8e89f58e03249e18b223"),
|
||||
Fq.e("0x22be31a388d0ec551530e1b1581b671b4340e88990de805a7bfed8bdb9c1accd"),
|
||||
Fq.e("1")
|
||||
],
|
||||
alpha_g1_sx: [
|
||||
Fq.e("0x262ff8dd594374c6ed5e892ba31315f6e47c500784a12ea8d2c573730888a392"),
|
||||
Fq.e("0x0b3a94f2b61178f2974e039cfd671e7405ec43eb2c09dc8f43a34f450917a62f"),
|
||||
Fq.e("1")
|
||||
],
|
||||
beta_g1_s: [
|
||||
Fq.e("0x0d9b3088b69daf6746c6bba4f9b359234abbfd3306bce14b198e7a5556c777e6"),
|
||||
Fq.e("0x066d1acac914883df6a9dc57dc2037a481ba4b8646efe13e2584b9258bd52d0c"),
|
||||
Fq.e("1")
|
||||
],
|
||||
beta_g1_sx: [
|
||||
Fq.e("0x248232878c359dbe632c387dc0d955520e8d3363f1cd9621ec9fd4a05460c754"),
|
||||
Fq.e("0x12074f06ef232a472cb36c328e760c4acfb4bedad4ca3ee09971578a0fe185ab"),
|
||||
Fq.e("1")
|
||||
],
|
||||
tau_g2_spx: [
|
||||
[
|
||||
Fq.e("0x0fe02fcc3aee51c1f3a37f3f152ebe5476ae659468f2ee81cdeb19d0dad366c5"),
|
||||
Fq.e("0x01aeb4db892bcb273aada80f5eab10e2e50ae59a5c274b0d7303f5c5a52ee88b"),
|
||||
],[
|
||||
Fq.e("0x2d00022d840d493fb93c68a63b29e2692c0cd3caf354fe60eae1ebacefc2c948"),
|
||||
Fq.e("0x204065ff10344153a08cfe4ae543c47fba883ef8a54530fa6a52c87e5c28ef2b"),
|
||||
],[
|
||||
Fq.e("1"),
|
||||
Fq.e("0")
|
||||
]
|
||||
],
|
||||
alpha_g2_spx: [
|
||||
[
|
||||
Fq.e("0x2e649d01a58a7795762df8f0634c273ebce6950a9a2ba3d4459458620d3164a0"),
|
||||
Fq.e("0x1b58044d3e205a918124fea3983583199b4f99fd0abb39ede2c684b0810bdc1e"),
|
||||
],[
|
||||
Fq.e("0x021d41558cea5fa32c9f3de5834cb2ee45ce4cdf471353395d019dfe0c9c2509"),
|
||||
Fq.e("0x1c04148bac3f17b219c2655cd63ad2596ea63293103487be488a1d5a9054ddbf"),
|
||||
],[
|
||||
Fq.e("1"),
|
||||
Fq.e("0")
|
||||
]
|
||||
],
|
||||
beta_g2_spx: [
|
||||
[
|
||||
Fq.e("0x029251aed5163109667300035ce200b7195fc6e261581ba38776d87d7f0b1a7d"),
|
||||
Fq.e("0x09d6847f1b945ccdc00418a807f4b0af67ec5c0030c4f203581eff9d4af4347f"),
|
||||
],[
|
||||
Fq.e("0x04b62ecdc94bf94fcefdf93f06ca4f63026a47a0d4138941b8ee45b9f7177e5c"),
|
||||
Fq.e("0x1f0a6bff3945f207f407ff1c813b66a28b495f55a3788c3e200c74817e86f7ce"),
|
||||
],[
|
||||
Fq.e("1"),
|
||||
Fq.e("0")
|
||||
]
|
||||
]
|
||||
};
|
||||
|
||||
|
||||
const challange = Buffer.from(
|
||||
"bc0bde7980381fa642b2097591dd83f1"+
|
||||
"ed15b003e15c35520af32c95eb519149"+
|
||||
"2a6f3175215635cfc10e6098e2c612d0"+
|
||||
"ca84f1a9f90b5333560c8af59b9209f4", "hex");
|
||||
|
||||
function getG2sp(persinalization, challange, g1s, g1sx) {
|
||||
|
||||
const h = blake2b(64);
|
||||
h.update(Buffer.from([persinalization]));
|
||||
h.update(challange);
|
||||
h.update( utils.beInt2Buff(g1s[0],32));
|
||||
h.update( utils.beInt2Buff(g1s[1],32));
|
||||
h.update( utils.beInt2Buff(g1sx[0],32));
|
||||
h.update( utils.beInt2Buff(g1sx[1],32));
|
||||
const hash = Buffer.from(h.digest());
|
||||
|
||||
const seed = [];
|
||||
for (let i=0; i<8; i++) {
|
||||
seed[i] = hash.readUInt32BE(i*4);
|
||||
}
|
||||
|
||||
const rng = new ChaCha(seed);
|
||||
|
||||
const g2_sp = bn128.G2.fromRng(rng);
|
||||
|
||||
return g2_sp;
|
||||
|
||||
}
|
||||
|
||||
const tau_g2_sp = getG2sp(0, challange, pubKey.tau_g1_s, pubKey.tau_g1_sx);
|
||||
if (bn128.F12.eq(
|
||||
bn128.pairing(pubKey.tau_g1_sx,tau_g2_sp),
|
||||
bn128.pairing(pubKey.tau_g1_s, pubKey.tau_g2_spx))) {
|
||||
console.log("Key tau matches!!");
|
||||
} else {
|
||||
console.log("ERROR Key tau does not match!!");
|
||||
}
|
||||
|
||||
const alpha_g2_sp = getG2sp(1, challange, pubKey.alpha_g1_s, pubKey.alpha_g1_sx);
|
||||
if (bn128.F12.eq(
|
||||
bn128.pairing(pubKey.alpha_g1_sx, alpha_g2_sp),
|
||||
bn128.pairing(pubKey.alpha_g1_s , pubKey.alpha_g2_spx))) {
|
||||
console.log("Key alfa matches!!");
|
||||
} else {
|
||||
console.log("ERROR Alfa tau does not match!!");
|
||||
}
|
||||
|
||||
const beta_g2_sp = getG2sp(2, challange, pubKey.beta_g1_s, pubKey.beta_g1_sx);
|
||||
if (bn128.F12.eq(
|
||||
bn128.pairing(pubKey.beta_g1_sx, beta_g2_sp),
|
||||
bn128.pairing(pubKey.beta_g1_s , pubKey.beta_g2_spx))) {
|
||||
console.log("Key beta matches!!");
|
||||
} else {
|
||||
console.log("ERROR Beta tau does not match!!");
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ describe("F1 testing", () => {
|
||||
let b = F.sqrt(a);
|
||||
assert(F.eq(F.e(0), F.sqrt(F.e("0"))));
|
||||
assert(F.eq(b, F.e("2")));
|
||||
assert(F.sqrt(F.nqr) === null);
|
||||
// assert(F.sqrt(F.nqr) === null);
|
||||
});
|
||||
|
||||
it("Should compute sqrt of 100 random numbers", () => {
|
||||
@@ -237,4 +237,29 @@ describe("Pairing", () => {
|
||||
assert(bn128.F12.eq(res, bn128.F12.one)); */
|
||||
}
|
||||
}).timeout(10000);
|
||||
it("Should test rpr of F2", () => {
|
||||
const P1 = [
|
||||
[
|
||||
bn128.F1.e("1b2327ce7815d3358fe89fd8e5695305ed23682db29569f549ab8f48cae1f1c4",16),
|
||||
bn128.F1.e("1ed41ca6b3edc06237af648f845c270ff83bcde333f17863c1b71a43b271b46d",16)
|
||||
],
|
||||
[
|
||||
bn128.F1.e("122057912ab892abcf2e729f0f342baea3fe1b484840eb97c7d78cd7530f4ab5",16),
|
||||
bn128.F1.e("2cb317fd40d56eeb17b0c1ff9443661a42ec00cea060012873b3f643f1a5bff8",16)
|
||||
],
|
||||
[
|
||||
bn128.F1.one,
|
||||
bn128.F1.zero
|
||||
]
|
||||
];
|
||||
const buff = new ArrayBuffer(64);
|
||||
bn128.G2.toRprCompressed(buff, 0, P1);
|
||||
|
||||
const P2 = bn128.G2.fromRprCompressed(buff, 0);
|
||||
|
||||
console.log(P1[1][0].toString(16));
|
||||
console.log(P2[1][0].toString(16));
|
||||
|
||||
assert(bn128.G2.eq(P1,P2));
|
||||
}).timeout(10000);
|
||||
});
|
||||
|
||||
87
test/sqrt.js
Normal file
87
test/sqrt.js
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
Copyright 2018 0kims association.
|
||||
|
||||
This file is part of zksnark JavaScript library.
|
||||
|
||||
zksnark JavaScript library 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.
|
||||
|
||||
zksnark JavaScript library 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
|
||||
zksnark JavaScript library. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const chai = require("chai");
|
||||
|
||||
const Scalar = require("../src/scalar.js");
|
||||
const bn128 = require("../src/bn128.js");
|
||||
const F1Field = require("../src/f1field.js");
|
||||
|
||||
const assert = chai.assert;
|
||||
|
||||
|
||||
describe("Sqrt testing", () => {
|
||||
/*
|
||||
it("Should compute sqrts", () => {
|
||||
const F = new F1Field(bn128.r);
|
||||
const a = F.e(2);
|
||||
const b = F.sqrt_v(a);
|
||||
console.log(F.toString(b));
|
||||
});
|
||||
*/
|
||||
it("Should compute basic sqrts", () => {
|
||||
const F = new F1Field(bn128.r);
|
||||
assert(F.eq(F.e(0), F.sqrt(F.e("0"))));
|
||||
const a = F.e("9");
|
||||
let b = F.sqrt(a);
|
||||
assert(F.eq(b, F.e("3")));
|
||||
assert(F.sqrt(F.sqrt_z) === null);
|
||||
});
|
||||
it("Should compute sqrt p%4 = 1", () => {
|
||||
const F = new F1Field(bn128.r);
|
||||
const e = Scalar.div(Scalar.pow(F.p, F.m), 2);
|
||||
for (let i=0; i<100; i++) {
|
||||
const x2 = F.random();
|
||||
const x = F.sqrt(x2);
|
||||
if (x==null) {
|
||||
assert(F.eq( F.pow(x2, e), F.negone));
|
||||
} else {
|
||||
assert(F.eq(F.square(x), x2));
|
||||
}
|
||||
}
|
||||
});
|
||||
it("Should compute sqrt p%4 = 3", () => {
|
||||
const F = new F1Field(bn128.q);
|
||||
const e = Scalar.div(Scalar.pow(F.p, F.m), 2);
|
||||
for (let i=0; i<100; i++) {
|
||||
const x2 = F.random();
|
||||
const x = F.sqrt(x2);
|
||||
if (x==null) {
|
||||
assert(F.eq( F.pow(x2, e), F.negone));
|
||||
} else {
|
||||
assert(F.eq(F.square(x), x2));
|
||||
}
|
||||
}
|
||||
});
|
||||
it("Should compute sqrt m=2 p%4 = 3", () => {
|
||||
const F = bn128.F2;
|
||||
const e = Scalar.div(Scalar.pow(F.p, F.m), 2);
|
||||
for (let i=0; i<100; i++) {
|
||||
const x2 = F.random();
|
||||
const x = F.sqrt(x2);
|
||||
if (x==null) {
|
||||
assert(F.eq( F.pow(x2, e), F.negone));
|
||||
} else {
|
||||
assert(F.eq(F.square(x), x2));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user