From 89173c3e63559cb6c7b9d5ddf6663d2b3c17859b Mon Sep 17 00:00:00 2001 From: Jordi Baylina Date: Sun, 12 Aug 2018 20:37:43 +0200 Subject: [PATCH] refactor curve and add tests --- package-lock.json | 46 ++++++ package.json | 1 + src/constants.js | 39 ++++++ src/f12field.js | 55 ++++++++ src/f1field.js | 78 +++++++++++ src/{znfield.js => f2field.js} | 8 +- src/f6field.js | 0 src/g1curve.js | 147 ------------------- src/g2curve.js | 28 ---- src/gcurve.js | 167 ++++++++++++++++++++++ src/pairing.js | 249 ++++++++++++++++++++++++++++++++- test/algebra.js | 35 +++++ 12 files changed, 670 insertions(+), 183 deletions(-) create mode 100644 src/constants.js create mode 100644 src/f12field.js create mode 100644 src/f1field.js rename src/{znfield.js => f2field.js} (91%) create mode 100644 src/f6field.js delete mode 100644 src/g1curve.js delete mode 100644 src/g2curve.js create mode 100644 src/gcurve.js create mode 100644 test/algebra.js diff --git a/package-lock.json b/package-lock.json index 5fd8aff..e604506 100644 --- a/package-lock.json +++ b/package-lock.json @@ -74,6 +74,11 @@ "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=" }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==" + }, "babel-code-frame": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", @@ -138,6 +143,19 @@ "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=" }, + "chai": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.1.2.tgz", + "integrity": "sha1-D2RYS6ZC8PKs4oBiefTwbKI61zw=", + "requires": { + "assertion-error": "^1.0.1", + "check-error": "^1.0.1", + "deep-eql": "^3.0.0", + "get-func-name": "^2.0.0", + "pathval": "^1.0.0", + "type-detect": "^4.0.0" + } + }, "chalk": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", @@ -171,6 +189,11 @@ "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=" }, + "check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=" + }, "circular-json": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", @@ -227,6 +250,14 @@ "ms": "2.0.0" } }, + "deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "requires": { + "type-detect": "^4.0.0" + } + }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -477,6 +508,11 @@ "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=" }, + "get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=" + }, "glob": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", @@ -798,6 +834,11 @@ "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" }, + "pathval": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", + "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=" + }, "pify": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", @@ -1033,6 +1074,11 @@ "prelude-ls": "~1.1.2" } }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==" + }, "uri-js": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", diff --git a/package.json b/package.json index 45be40b..38f5d91 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "license": "GPL-3.0", "dependencies": { "big-integer": "^1.6.34", + "chai": "^4.1.2", "eslint": "^5.3.0" }, "devDependencies": { diff --git a/src/constants.js b/src/constants.js new file mode 100644 index 0000000..c99b5cc --- /dev/null +++ b/src/constants.js @@ -0,0 +1,39 @@ +const bigInt = require("big-integer"); + +const F1Field = require("./f1field"); +const F2Field = require("./f1field"); + +const C = { + + // Module of the field + q : bigInt("21888242871839275222246405745257275088696311157297823662689037894645226208583"), + + // Order of the group + r : bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617"), + + g1 : [ bigInt(1), bigInt(2) ], + g2 : + [ + [ + bigInt("10857046999023057135944570762232829481370756359578518086990519993285655852781"), + bigInt("11559732032986387107991004021392285783925812861821192530917403151452391805634") + ], + [ + bigInt("8495653923123431417604973247489272438418190587263600148770280649306958101930"), + bigInt("4082367875863433681332203403145435568316851327593401208105741076214120093531") + ] + ] + +}; + +const F1 = new F1Field(C.q); +const F2 = new F2Field(C.q); + +C.two_inv= F1.inverse(bigInt(2)); + +C.coef_b = bigInt(3); +C.twist = [bigInt(9) , bigInt(1)]; +// C.twist_coeff_b = F2.mulEscalar( F2.inverse(C.twist), C.coef_b ); + + +module.exports = C; diff --git a/src/f12field.js b/src/f12field.js new file mode 100644 index 0000000..2f2a2eb --- /dev/null +++ b/src/f12field.js @@ -0,0 +1,55 @@ + + + +class F12Field { + constructor(p) { + this.p = n; + } + + add(a, b) { + const maxGrade = Math.max(a.length, b.length); + const res = new Array(maxGrade); + for (let i=0; i= 0; --i) + { + const bit = this.loop_count_bits[i]; + + c = this._doubleStep(R); + res.coeffs.push(c); + + if (bit) + { + c = this._addStep(Qcopy, R); + res.coeffs.push(c); + } + } + + const Q1 = this.G2.mul_by_q(Qcopy); // TODO mul_by_q + assert(this.F2.equal(Q1[2], this.F2.one)); + const Q2 = this.G2.mul_by_q(Q1); + assert(this.F2.equal(Q2[2], this.F2.one)); + + if (this.loopCountNef) + { + R.Y = this.F2.neg(R.Y); + } + Q2.Y = this.F2.neg(Q2.Y); + + c = this._addStep(Q1, R); + res.coeffs.push(c); + + c = this._addStep(Q2, R); + res.coeffs.push(c); + + return res; + } + + _millerLoop(pre1, pre2) { + let f = this.F12.one; + + let idx = 0; + + let c; + + for (let i = this.loop_count_bits.length-2; i >= 0; --i) + { + const bit = this.loop_count_bits[i]; + + /* code below gets executed for all bits (EXCEPT the MSB itself) of + alt_bn128_param_p (skipping leading zeros) in MSB to LSB + order */ + + c = pre2.coeffs[idx++]; + f = this.F12.square(f); + f = this.F12.mul_by_024( + f, + c.ell_0, + this.F2.mul(pre1.PY, c.ell_VW), + this.F2.mul(pre1.PX, c.ell_VV)); + + if (bit) + { + c = pre2.coeffs[idx++]; + f = this.F12.mul_by_024( + f, + c.ell_0, + this.F2.mul(pre1.PY, c.ell_VW), + this.F2.mul(pre1.PX, c.ell_VV)); + } + + } + + if (this.loopCountNef) + { + f = this.F12.inverse(f); + } + + c = pre2.coeffs[idx++]; + f = this.F12.mul_by_024( + f, + c.ell_0, + this.F2.mul(pre1.PY, c.ell_VW), + this.F2.mul(pre1.PX, c.ell_VV)); + + c = pre2.coeffs[idx++]; + f = this.F12.mul_by_024( + f, + c.ell_0, + this.F2.mul(pre1.PY, c.ell_VW), + this.F2.mul(pre1.PX, c.ell_VV)); + + return f; + } + + _doubleStep(current) { + const X = current.X; + const Y = current.Y; + const Z = current.Z; + + const A = this.F2.mulEscalar(this.F1.mul(X,Y), constants.two_inv); // A = X1 * Y1 / 2 + const B = this.F2.square(Y); // B = Y1^2 + const C = this.F2.square(Z); // C = Z1^2 + const D = this.F2.add(C, this.F1.add(C,C)); // D = 3 * C + const E = this.F2.mul(constants.twist_coeff_b, D); // E = twist_b * D + const F = this.F2.add(E, this.F2.add(E,E)); // F = 3 * E + const G = + this.F2.mulEscalar( + this.F2.sum( B , F ), + constants.two_inv); // G = (B+F)/2 + const H = + this.F2.sub( + this.F2.square( this.F2.add(Y,Z) ), + this.F2.add( B , C)); // H = (Y1+Z1)^2-(B+C) + const I = this.F2.sub(E, B); // I = E-B + const J = this.F2.square(X); // J = X1^2 + const E_squared = this.F2.square(E); // E_squared = E^2 + + current.X = this.F2.mul( A, this.F2.sub(B,F) ); // X3 = A * (B-F) + current.Y = + this.F2.sub( + this.F2.sub( this.F2.square(G) , E_squared ), + this.F2.add( E_squared , E_squared )); // Y3 = G^2 - 3*E^2 + current.Z = this.F2.mul( B, H ); // Z3 = B * H + const c = { + ell_0 : this.F2.mul( I, constants.twist), // ell_0 = xi * I + ell_VW: this.F2.neg( H ), // ell_VW = - H (later: * yP) + ell_VV: this.F2.add( J , this.F2.add(J,J) ) // ell_VV = 3*J (later: * xP) + }; + + return c; + } + + _addStep(base, current) { + + const X1 = current.X; + const Y1 = current.Y; + const Z1 = current.Z; + const x2 = base.X; + const y2 = base.Y; + + const D = this.F2.sub( X1, this.F2.mul(x2,Z1) ); // D = X1 - X2*Z1 + const E = this.F2.sub( Y1, this.F2.mul(y2,Z1) ); // E = Y1 - Y2*Z1 + const F = this.F2.square(D); // F = D^2 + const G = this.F2.square(E); // G = E^2 + const H = this.F2.mul(D,F); // H = D*F + const I = this.F2.mul(X1,F); // I = X1 * F + const J = + this.F2.sub( + this.F2.add( H, this.F2.mul(Z1,G) ), + this.F2.add( I, I )); // J = H + Z1*G - (I+I) + + current.X = this.F2.mul( D , J ); // X3 = D*J + current.Y = + this.F2.sub( + this.F2.mul( E , this.F2.sub(I,J) ), + this.F2.mul( H , Y1)); // Y3 = E*(I-J)-(H*Y1) + current.Z = this.F2.mul(Z1,H); + const c = { + ell_0 : + this.F2.mul( + constants.twist, + this.F2.sub( + this.F2.mul(E , x2), + this.F2.mul(D , y2))), // ell_0 = xi * (E * X2 - D * Y2) + ell_VV : this.F2.neg(E), // ell_VV = - E (later: * xP) + ell_VW : D // ell_VW = D (later: * yP ) + }; + + return c; + } +} diff --git a/test/algebra.js b/test/algebra.js new file mode 100644 index 0000000..b1a7007 --- /dev/null +++ b/test/algebra.js @@ -0,0 +1,35 @@ +const F1Field = require("../src/f1field.js"); +const GCurve = require("../src/gcurve.js"); +const constants = require("../src/constants.js"); +const chai = require('chai'); + +const assert = chai.assert; + +describe("Curve G1 Test", () => { + + it ("r*one == 0", () => { + const F1 = new F1Field(constants.q); + const G1 = new GCurve(F1, constants.g1); + + const res = G1.mulEscalar(G1.g, constants.r); + + assert(G1.equals(res, G1.zero), "G1 does not have range r"); + }); + + it("Should add match in various", () => { + const F1 = new F1Field(constants.q); + const G1 = new GCurve(F1, constants.g1); + + const r1 = F1.e(33); + const r2 = F1.e(44); + + const gr1 = G1.mulEscalar(G1.g, r1); + const gr2 = G1.mulEscalar(G1.g, r2); + + const grsum1 = G1.add(gr1, gr2); + + const grsum2 = G1.mulEscalar(G1.g, r1.add(r2)); + + assert(G1.equals(grsum1, grsum2)); + }); +});