diff --git a/index.js b/index.js
index 0cf4e31..efc81fd 100644
--- a/index.js
+++ b/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");
diff --git a/package-lock.json b/package-lock.json
index 91586ae..53c0444 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -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",
diff --git a/package.json b/package.json
index 017e6c5..ae7ca92 100644
--- a/package.json
+++ b/package.json
@@ -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"
}
diff --git a/src/bn128.js b/src/bn128.js
index ebf58f9..0a23325 100644
--- a/src/bn128.js
+++ b/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
diff --git a/src/chacha.js b/src/chacha.js
new file mode 100644
index 0000000..602fad9
--- /dev/null
+++ b/src/chacha.js
@@ -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;
+ }
+};
diff --git a/src/ec.js b/src/ec.js
index 30746fd..764682e 100644
--- a/src/ec.js
+++ b/src/ec.js
@@ -17,7 +17,27 @@
snarkjs. If not, see .
*/
+
+
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;
diff --git a/src/engine.js b/src/engine.js
new file mode 100644
index 0000000..9e1e476
--- /dev/null
+++ b/src/engine.js
@@ -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 .
+*/
+
+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 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=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 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;
+};
diff --git a/src/f1field.js b/src/f1field.js
index 35cf4b0..c2da4ca 100644
--- a/src/f1field.js
+++ b/src/f1field.js
@@ -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;
diff --git a/src/f1field_bigint.js b/src/f1field_bigint.js
index f91406d..7a9c7a9 100644
--- a/src/f1field_bigint.js
+++ b/src/f1field_bigint.js
@@ -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> 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.p);
+ v = (v * this.Ri) % this.p; // Convert from montgomery
+ return v;
+ }
+
};
diff --git a/src/f2field.js b/src/f2field.js
index 6da5622..a87c2f0 100644
--- a/src/f2field.js
+++ b/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;
diff --git a/src/f3field.js b/src/f3field.js
index e2914ce..79d5a0d 100644
--- a/src/f3field.js
+++ b/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;
diff --git a/src/fsqrt.js b/src/fsqrt.js
new file mode 100644
index 0000000..7c5d82c
--- /dev/null
+++ b/src/fsqrt.js
@@ -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> 5)+1; // Number of 32bit words;
+ for (let i=0; i> 5)+1; // Number of 32bit words;
+ for (let i=0; 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 {
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);
});
diff --git a/test/sqrt.js b/test/sqrt.js
new file mode 100644
index 0000000..fd11436
--- /dev/null
+++ b/test/sqrt.js
@@ -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 .
+*/
+
+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));
+ }
+ }
+ });
+
+});
+