123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938 |
- 'use strict';
- var curve = require('../curve');
- var elliptic = require('../../elliptic');
- var BN = require('bn.js');
- var inherits = require('inherits');
- var Base = curve.base;
- var assert = elliptic.utils.assert;
- function ShortCurve(conf) {
- Base.call(this, 'short', conf);
- this.a = new BN(conf.a, 16).toRed(this.red);
- this.b = new BN(conf.b, 16).toRed(this.red);
- this.tinv = this.two.redInvm();
- this.zeroA = this.a.fromRed().cmpn(0) === 0;
- this.threeA = this.a.fromRed().sub(this.p).cmpn(-3) === 0;
- // If the curve is endomorphic, precalculate beta and lambda
- this.endo = this._getEndomorphism(conf);
- this._endoWnafT1 = new Array(4);
- this._endoWnafT2 = new Array(4);
- }
- inherits(ShortCurve, Base);
- module.exports = ShortCurve;
- ShortCurve.prototype._getEndomorphism = function _getEndomorphism(conf) {
- // No efficient endomorphism
- if (!this.zeroA || !this.g || !this.n || this.p.modn(3) !== 1)
- return;
- // Compute beta and lambda, that lambda * P = (beta * Px; Py)
- var beta;
- var lambda;
- if (conf.beta) {
- beta = new BN(conf.beta, 16).toRed(this.red);
- } else {
- var betas = this._getEndoRoots(this.p);
- // Choose the smallest beta
- beta = betas[0].cmp(betas[1]) < 0 ? betas[0] : betas[1];
- beta = beta.toRed(this.red);
- }
- if (conf.lambda) {
- lambda = new BN(conf.lambda, 16);
- } else {
- // Choose the lambda that is matching selected beta
- var lambdas = this._getEndoRoots(this.n);
- if (this.g.mul(lambdas[0]).x.cmp(this.g.x.redMul(beta)) === 0) {
- lambda = lambdas[0];
- } else {
- lambda = lambdas[1];
- assert(this.g.mul(lambda).x.cmp(this.g.x.redMul(beta)) === 0);
- }
- }
- // Get basis vectors, used for balanced length-two representation
- var basis;
- if (conf.basis) {
- basis = conf.basis.map(function(vec) {
- return {
- a: new BN(vec.a, 16),
- b: new BN(vec.b, 16)
- };
- });
- } else {
- basis = this._getEndoBasis(lambda);
- }
- return {
- beta: beta,
- lambda: lambda,
- basis: basis
- };
- };
- ShortCurve.prototype._getEndoRoots = function _getEndoRoots(num) {
- // Find roots of for x^2 + x + 1 in F
- // Root = (-1 +- Sqrt(-3)) / 2
- //
- var red = num === this.p ? this.red : BN.mont(num);
- var tinv = new BN(2).toRed(red).redInvm();
- var ntinv = tinv.redNeg();
- var s = new BN(3).toRed(red).redNeg().redSqrt().redMul(tinv);
- var l1 = ntinv.redAdd(s).fromRed();
- var l2 = ntinv.redSub(s).fromRed();
- return [ l1, l2 ];
- };
- ShortCurve.prototype._getEndoBasis = function _getEndoBasis(lambda) {
- // aprxSqrt >= sqrt(this.n)
- var aprxSqrt = this.n.ushrn(Math.floor(this.n.bitLength() / 2));
- // 3.74
- // Run EGCD, until r(L + 1) < aprxSqrt
- var u = lambda;
- var v = this.n.clone();
- var x1 = new BN(1);
- var y1 = new BN(0);
- var x2 = new BN(0);
- var y2 = new BN(1);
- // NOTE: all vectors are roots of: a + b * lambda = 0 (mod n)
- var a0;
- var b0;
- // First vector
- var a1;
- var b1;
- // Second vector
- var a2;
- var b2;
- var prevR;
- var i = 0;
- var r;
- var x;
- while (u.cmpn(0) !== 0) {
- var q = v.div(u);
- r = v.sub(q.mul(u));
- x = x2.sub(q.mul(x1));
- var y = y2.sub(q.mul(y1));
- if (!a1 && r.cmp(aprxSqrt) < 0) {
- a0 = prevR.neg();
- b0 = x1;
- a1 = r.neg();
- b1 = x;
- } else if (a1 && ++i === 2) {
- break;
- }
- prevR = r;
- v = u;
- u = r;
- x2 = x1;
- x1 = x;
- y2 = y1;
- y1 = y;
- }
- a2 = r.neg();
- b2 = x;
- var len1 = a1.sqr().add(b1.sqr());
- var len2 = a2.sqr().add(b2.sqr());
- if (len2.cmp(len1) >= 0) {
- a2 = a0;
- b2 = b0;
- }
- // Normalize signs
- if (a1.negative) {
- a1 = a1.neg();
- b1 = b1.neg();
- }
- if (a2.negative) {
- a2 = a2.neg();
- b2 = b2.neg();
- }
- return [
- { a: a1, b: b1 },
- { a: a2, b: b2 }
- ];
- };
- ShortCurve.prototype._endoSplit = function _endoSplit(k) {
- var basis = this.endo.basis;
- var v1 = basis[0];
- var v2 = basis[1];
- var c1 = v2.b.mul(k).divRound(this.n);
- var c2 = v1.b.neg().mul(k).divRound(this.n);
- var p1 = c1.mul(v1.a);
- var p2 = c2.mul(v2.a);
- var q1 = c1.mul(v1.b);
- var q2 = c2.mul(v2.b);
- // Calculate answer
- var k1 = k.sub(p1).sub(p2);
- var k2 = q1.add(q2).neg();
- return { k1: k1, k2: k2 };
- };
- ShortCurve.prototype.pointFromX = function pointFromX(x, odd) {
- x = new BN(x, 16);
- if (!x.red)
- x = x.toRed(this.red);
- var y2 = x.redSqr().redMul(x).redIAdd(x.redMul(this.a)).redIAdd(this.b);
- var y = y2.redSqrt();
- if (y.redSqr().redSub(y2).cmp(this.zero) !== 0)
- throw new Error('invalid point');
- // XXX Is there any way to tell if the number is odd without converting it
- // to non-red form?
- var isOdd = y.fromRed().isOdd();
- if (odd && !isOdd || !odd && isOdd)
- y = y.redNeg();
- return this.point(x, y);
- };
- ShortCurve.prototype.validate = function validate(point) {
- if (point.inf)
- return true;
- var x = point.x;
- var y = point.y;
- var ax = this.a.redMul(x);
- var rhs = x.redSqr().redMul(x).redIAdd(ax).redIAdd(this.b);
- return y.redSqr().redISub(rhs).cmpn(0) === 0;
- };
- ShortCurve.prototype._endoWnafMulAdd =
- function _endoWnafMulAdd(points, coeffs, jacobianResult) {
- var npoints = this._endoWnafT1;
- var ncoeffs = this._endoWnafT2;
- for (var i = 0; i < points.length; i++) {
- var split = this._endoSplit(coeffs[i]);
- var p = points[i];
- var beta = p._getBeta();
- if (split.k1.negative) {
- split.k1.ineg();
- p = p.neg(true);
- }
- if (split.k2.negative) {
- split.k2.ineg();
- beta = beta.neg(true);
- }
- npoints[i * 2] = p;
- npoints[i * 2 + 1] = beta;
- ncoeffs[i * 2] = split.k1;
- ncoeffs[i * 2 + 1] = split.k2;
- }
- var res = this._wnafMulAdd(1, npoints, ncoeffs, i * 2, jacobianResult);
- // Clean-up references to points and coefficients
- for (var j = 0; j < i * 2; j++) {
- npoints[j] = null;
- ncoeffs[j] = null;
- }
- return res;
- };
- function Point(curve, x, y, isRed) {
- Base.BasePoint.call(this, curve, 'affine');
- if (x === null && y === null) {
- this.x = null;
- this.y = null;
- this.inf = true;
- } else {
- this.x = new BN(x, 16);
- this.y = new BN(y, 16);
- // Force redgomery representation when loading from JSON
- if (isRed) {
- this.x.forceRed(this.curve.red);
- this.y.forceRed(this.curve.red);
- }
- if (!this.x.red)
- this.x = this.x.toRed(this.curve.red);
- if (!this.y.red)
- this.y = this.y.toRed(this.curve.red);
- this.inf = false;
- }
- }
- inherits(Point, Base.BasePoint);
- ShortCurve.prototype.point = function point(x, y, isRed) {
- return new Point(this, x, y, isRed);
- };
- ShortCurve.prototype.pointFromJSON = function pointFromJSON(obj, red) {
- return Point.fromJSON(this, obj, red);
- };
- Point.prototype._getBeta = function _getBeta() {
- if (!this.curve.endo)
- return;
- var pre = this.precomputed;
- if (pre && pre.beta)
- return pre.beta;
- var beta = this.curve.point(this.x.redMul(this.curve.endo.beta), this.y);
- if (pre) {
- var curve = this.curve;
- var endoMul = function(p) {
- return curve.point(p.x.redMul(curve.endo.beta), p.y);
- };
- pre.beta = beta;
- beta.precomputed = {
- beta: null,
- naf: pre.naf && {
- wnd: pre.naf.wnd,
- points: pre.naf.points.map(endoMul)
- },
- doubles: pre.doubles && {
- step: pre.doubles.step,
- points: pre.doubles.points.map(endoMul)
- }
- };
- }
- return beta;
- };
- Point.prototype.toJSON = function toJSON() {
- if (!this.precomputed)
- return [ this.x, this.y ];
- return [ this.x, this.y, this.precomputed && {
- doubles: this.precomputed.doubles && {
- step: this.precomputed.doubles.step,
- points: this.precomputed.doubles.points.slice(1)
- },
- naf: this.precomputed.naf && {
- wnd: this.precomputed.naf.wnd,
- points: this.precomputed.naf.points.slice(1)
- }
- } ];
- };
- Point.fromJSON = function fromJSON(curve, obj, red) {
- if (typeof obj === 'string')
- obj = JSON.parse(obj);
- var res = curve.point(obj[0], obj[1], red);
- if (!obj[2])
- return res;
- function obj2point(obj) {
- return curve.point(obj[0], obj[1], red);
- }
- var pre = obj[2];
- res.precomputed = {
- beta: null,
- doubles: pre.doubles && {
- step: pre.doubles.step,
- points: [ res ].concat(pre.doubles.points.map(obj2point))
- },
- naf: pre.naf && {
- wnd: pre.naf.wnd,
- points: [ res ].concat(pre.naf.points.map(obj2point))
- }
- };
- return res;
- };
- Point.prototype.inspect = function inspect() {
- if (this.isInfinity())
- return '<EC Point Infinity>';
- return '<EC Point x: ' + this.x.fromRed().toString(16, 2) +
- ' y: ' + this.y.fromRed().toString(16, 2) + '>';
- };
- Point.prototype.isInfinity = function isInfinity() {
- return this.inf;
- };
- Point.prototype.add = function add(p) {
- // O + P = P
- if (this.inf)
- return p;
- // P + O = P
- if (p.inf)
- return this;
- // P + P = 2P
- if (this.eq(p))
- return this.dbl();
- // P + (-P) = O
- if (this.neg().eq(p))
- return this.curve.point(null, null);
- // P + Q = O
- if (this.x.cmp(p.x) === 0)
- return this.curve.point(null, null);
- var c = this.y.redSub(p.y);
- if (c.cmpn(0) !== 0)
- c = c.redMul(this.x.redSub(p.x).redInvm());
- var nx = c.redSqr().redISub(this.x).redISub(p.x);
- var ny = c.redMul(this.x.redSub(nx)).redISub(this.y);
- return this.curve.point(nx, ny);
- };
- Point.prototype.dbl = function dbl() {
- if (this.inf)
- return this;
- // 2P = O
- var ys1 = this.y.redAdd(this.y);
- if (ys1.cmpn(0) === 0)
- return this.curve.point(null, null);
- var a = this.curve.a;
- var x2 = this.x.redSqr();
- var dyinv = ys1.redInvm();
- var c = x2.redAdd(x2).redIAdd(x2).redIAdd(a).redMul(dyinv);
- var nx = c.redSqr().redISub(this.x.redAdd(this.x));
- var ny = c.redMul(this.x.redSub(nx)).redISub(this.y);
- return this.curve.point(nx, ny);
- };
- Point.prototype.getX = function getX() {
- return this.x.fromRed();
- };
- Point.prototype.getY = function getY() {
- return this.y.fromRed();
- };
- Point.prototype.mul = function mul(k) {
- k = new BN(k, 16);
- if (this._hasDoubles(k))
- return this.curve._fixedNafMul(this, k);
- else if (this.curve.endo)
- return this.curve._endoWnafMulAdd([ this ], [ k ]);
- else
- return this.curve._wnafMul(this, k);
- };
- Point.prototype.mulAdd = function mulAdd(k1, p2, k2) {
- var points = [ this, p2 ];
- var coeffs = [ k1, k2 ];
- if (this.curve.endo)
- return this.curve._endoWnafMulAdd(points, coeffs);
- else
- return this.curve._wnafMulAdd(1, points, coeffs, 2);
- };
- Point.prototype.jmulAdd = function jmulAdd(k1, p2, k2) {
- var points = [ this, p2 ];
- var coeffs = [ k1, k2 ];
- if (this.curve.endo)
- return this.curve._endoWnafMulAdd(points, coeffs, true);
- else
- return this.curve._wnafMulAdd(1, points, coeffs, 2, true);
- };
- Point.prototype.eq = function eq(p) {
- return this === p ||
- this.inf === p.inf &&
- (this.inf || this.x.cmp(p.x) === 0 && this.y.cmp(p.y) === 0);
- };
- Point.prototype.neg = function neg(_precompute) {
- if (this.inf)
- return this;
- var res = this.curve.point(this.x, this.y.redNeg());
- if (_precompute && this.precomputed) {
- var pre = this.precomputed;
- var negate = function(p) {
- return p.neg();
- };
- res.precomputed = {
- naf: pre.naf && {
- wnd: pre.naf.wnd,
- points: pre.naf.points.map(negate)
- },
- doubles: pre.doubles && {
- step: pre.doubles.step,
- points: pre.doubles.points.map(negate)
- }
- };
- }
- return res;
- };
- Point.prototype.toJ = function toJ() {
- if (this.inf)
- return this.curve.jpoint(null, null, null);
- var res = this.curve.jpoint(this.x, this.y, this.curve.one);
- return res;
- };
- function JPoint(curve, x, y, z) {
- Base.BasePoint.call(this, curve, 'jacobian');
- if (x === null && y === null && z === null) {
- this.x = this.curve.one;
- this.y = this.curve.one;
- this.z = new BN(0);
- } else {
- this.x = new BN(x, 16);
- this.y = new BN(y, 16);
- this.z = new BN(z, 16);
- }
- if (!this.x.red)
- this.x = this.x.toRed(this.curve.red);
- if (!this.y.red)
- this.y = this.y.toRed(this.curve.red);
- if (!this.z.red)
- this.z = this.z.toRed(this.curve.red);
- this.zOne = this.z === this.curve.one;
- }
- inherits(JPoint, Base.BasePoint);
- ShortCurve.prototype.jpoint = function jpoint(x, y, z) {
- return new JPoint(this, x, y, z);
- };
- JPoint.prototype.toP = function toP() {
- if (this.isInfinity())
- return this.curve.point(null, null);
- var zinv = this.z.redInvm();
- var zinv2 = zinv.redSqr();
- var ax = this.x.redMul(zinv2);
- var ay = this.y.redMul(zinv2).redMul(zinv);
- return this.curve.point(ax, ay);
- };
- JPoint.prototype.neg = function neg() {
- return this.curve.jpoint(this.x, this.y.redNeg(), this.z);
- };
- JPoint.prototype.add = function add(p) {
- // O + P = P
- if (this.isInfinity())
- return p;
- // P + O = P
- if (p.isInfinity())
- return this;
- // 12M + 4S + 7A
- var pz2 = p.z.redSqr();
- var z2 = this.z.redSqr();
- var u1 = this.x.redMul(pz2);
- var u2 = p.x.redMul(z2);
- var s1 = this.y.redMul(pz2.redMul(p.z));
- var s2 = p.y.redMul(z2.redMul(this.z));
- var h = u1.redSub(u2);
- var r = s1.redSub(s2);
- if (h.cmpn(0) === 0) {
- if (r.cmpn(0) !== 0)
- return this.curve.jpoint(null, null, null);
- else
- return this.dbl();
- }
- var h2 = h.redSqr();
- var h3 = h2.redMul(h);
- var v = u1.redMul(h2);
- var nx = r.redSqr().redIAdd(h3).redISub(v).redISub(v);
- var ny = r.redMul(v.redISub(nx)).redISub(s1.redMul(h3));
- var nz = this.z.redMul(p.z).redMul(h);
- return this.curve.jpoint(nx, ny, nz);
- };
- JPoint.prototype.mixedAdd = function mixedAdd(p) {
- // O + P = P
- if (this.isInfinity())
- return p.toJ();
- // P + O = P
- if (p.isInfinity())
- return this;
- // 8M + 3S + 7A
- var z2 = this.z.redSqr();
- var u1 = this.x;
- var u2 = p.x.redMul(z2);
- var s1 = this.y;
- var s2 = p.y.redMul(z2).redMul(this.z);
- var h = u1.redSub(u2);
- var r = s1.redSub(s2);
- if (h.cmpn(0) === 0) {
- if (r.cmpn(0) !== 0)
- return this.curve.jpoint(null, null, null);
- else
- return this.dbl();
- }
- var h2 = h.redSqr();
- var h3 = h2.redMul(h);
- var v = u1.redMul(h2);
- var nx = r.redSqr().redIAdd(h3).redISub(v).redISub(v);
- var ny = r.redMul(v.redISub(nx)).redISub(s1.redMul(h3));
- var nz = this.z.redMul(h);
- return this.curve.jpoint(nx, ny, nz);
- };
- JPoint.prototype.dblp = function dblp(pow) {
- if (pow === 0)
- return this;
- if (this.isInfinity())
- return this;
- if (!pow)
- return this.dbl();
- if (this.curve.zeroA || this.curve.threeA) {
- var r = this;
- for (var i = 0; i < pow; i++)
- r = r.dbl();
- return r;
- }
- // 1M + 2S + 1A + N * (4S + 5M + 8A)
- // N = 1 => 6M + 6S + 9A
- var a = this.curve.a;
- var tinv = this.curve.tinv;
- var jx = this.x;
- var jy = this.y;
- var jz = this.z;
- var jz4 = jz.redSqr().redSqr();
- // Reuse results
- var jyd = jy.redAdd(jy);
- for (var i = 0; i < pow; i++) {
- var jx2 = jx.redSqr();
- var jyd2 = jyd.redSqr();
- var jyd4 = jyd2.redSqr();
- var c = jx2.redAdd(jx2).redIAdd(jx2).redIAdd(a.redMul(jz4));
- var t1 = jx.redMul(jyd2);
- var nx = c.redSqr().redISub(t1.redAdd(t1));
- var t2 = t1.redISub(nx);
- var dny = c.redMul(t2);
- dny = dny.redIAdd(dny).redISub(jyd4);
- var nz = jyd.redMul(jz);
- if (i + 1 < pow)
- jz4 = jz4.redMul(jyd4);
- jx = nx;
- jz = nz;
- jyd = dny;
- }
- return this.curve.jpoint(jx, jyd.redMul(tinv), jz);
- };
- JPoint.prototype.dbl = function dbl() {
- if (this.isInfinity())
- return this;
- if (this.curve.zeroA)
- return this._zeroDbl();
- else if (this.curve.threeA)
- return this._threeDbl();
- else
- return this._dbl();
- };
- JPoint.prototype._zeroDbl = function _zeroDbl() {
- var nx;
- var ny;
- var nz;
- // Z = 1
- if (this.zOne) {
- // hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html
- // #doubling-mdbl-2007-bl
- // 1M + 5S + 14A
- // XX = X1^2
- var xx = this.x.redSqr();
- // YY = Y1^2
- var yy = this.y.redSqr();
- // YYYY = YY^2
- var yyyy = yy.redSqr();
- // S = 2 * ((X1 + YY)^2 - XX - YYYY)
- var s = this.x.redAdd(yy).redSqr().redISub(xx).redISub(yyyy);
- s = s.redIAdd(s);
- // M = 3 * XX + a; a = 0
- var m = xx.redAdd(xx).redIAdd(xx);
- // T = M ^ 2 - 2*S
- var t = m.redSqr().redISub(s).redISub(s);
- // 8 * YYYY
- var yyyy8 = yyyy.redIAdd(yyyy);
- yyyy8 = yyyy8.redIAdd(yyyy8);
- yyyy8 = yyyy8.redIAdd(yyyy8);
- // X3 = T
- nx = t;
- // Y3 = M * (S - T) - 8 * YYYY
- ny = m.redMul(s.redISub(t)).redISub(yyyy8);
- // Z3 = 2*Y1
- nz = this.y.redAdd(this.y);
- } else {
- // hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html
- // #doubling-dbl-2009-l
- // 2M + 5S + 13A
- // A = X1^2
- var a = this.x.redSqr();
- // B = Y1^2
- var b = this.y.redSqr();
- // C = B^2
- var c = b.redSqr();
- // D = 2 * ((X1 + B)^2 - A - C)
- var d = this.x.redAdd(b).redSqr().redISub(a).redISub(c);
- d = d.redIAdd(d);
- // E = 3 * A
- var e = a.redAdd(a).redIAdd(a);
- // F = E^2
- var f = e.redSqr();
- // 8 * C
- var c8 = c.redIAdd(c);
- c8 = c8.redIAdd(c8);
- c8 = c8.redIAdd(c8);
- // X3 = F - 2 * D
- nx = f.redISub(d).redISub(d);
- // Y3 = E * (D - X3) - 8 * C
- ny = e.redMul(d.redISub(nx)).redISub(c8);
- // Z3 = 2 * Y1 * Z1
- nz = this.y.redMul(this.z);
- nz = nz.redIAdd(nz);
- }
- return this.curve.jpoint(nx, ny, nz);
- };
- JPoint.prototype._threeDbl = function _threeDbl() {
- var nx;
- var ny;
- var nz;
- // Z = 1
- if (this.zOne) {
- // hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html
- // #doubling-mdbl-2007-bl
- // 1M + 5S + 15A
- // XX = X1^2
- var xx = this.x.redSqr();
- // YY = Y1^2
- var yy = this.y.redSqr();
- // YYYY = YY^2
- var yyyy = yy.redSqr();
- // S = 2 * ((X1 + YY)^2 - XX - YYYY)
- var s = this.x.redAdd(yy).redSqr().redISub(xx).redISub(yyyy);
- s = s.redIAdd(s);
- // M = 3 * XX + a
- var m = xx.redAdd(xx).redIAdd(xx).redIAdd(this.curve.a);
- // T = M^2 - 2 * S
- var t = m.redSqr().redISub(s).redISub(s);
- // X3 = T
- nx = t;
- // Y3 = M * (S - T) - 8 * YYYY
- var yyyy8 = yyyy.redIAdd(yyyy);
- yyyy8 = yyyy8.redIAdd(yyyy8);
- yyyy8 = yyyy8.redIAdd(yyyy8);
- ny = m.redMul(s.redISub(t)).redISub(yyyy8);
- // Z3 = 2 * Y1
- nz = this.y.redAdd(this.y);
- } else {
- // hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-2001-b
- // 3M + 5S
- // delta = Z1^2
- var delta = this.z.redSqr();
- // gamma = Y1^2
- var gamma = this.y.redSqr();
- // beta = X1 * gamma
- var beta = this.x.redMul(gamma);
- // alpha = 3 * (X1 - delta) * (X1 + delta)
- var alpha = this.x.redSub(delta).redMul(this.x.redAdd(delta));
- alpha = alpha.redAdd(alpha).redIAdd(alpha);
- // X3 = alpha^2 - 8 * beta
- var beta4 = beta.redIAdd(beta);
- beta4 = beta4.redIAdd(beta4);
- var beta8 = beta4.redAdd(beta4);
- nx = alpha.redSqr().redISub(beta8);
- // Z3 = (Y1 + Z1)^2 - gamma - delta
- nz = this.y.redAdd(this.z).redSqr().redISub(gamma).redISub(delta);
- // Y3 = alpha * (4 * beta - X3) - 8 * gamma^2
- var ggamma8 = gamma.redSqr();
- ggamma8 = ggamma8.redIAdd(ggamma8);
- ggamma8 = ggamma8.redIAdd(ggamma8);
- ggamma8 = ggamma8.redIAdd(ggamma8);
- ny = alpha.redMul(beta4.redISub(nx)).redISub(ggamma8);
- }
- return this.curve.jpoint(nx, ny, nz);
- };
- JPoint.prototype._dbl = function _dbl() {
- var a = this.curve.a;
- // 4M + 6S + 10A
- var jx = this.x;
- var jy = this.y;
- var jz = this.z;
- var jz4 = jz.redSqr().redSqr();
- var jx2 = jx.redSqr();
- var jy2 = jy.redSqr();
- var c = jx2.redAdd(jx2).redIAdd(jx2).redIAdd(a.redMul(jz4));
- var jxd4 = jx.redAdd(jx);
- jxd4 = jxd4.redIAdd(jxd4);
- var t1 = jxd4.redMul(jy2);
- var nx = c.redSqr().redISub(t1.redAdd(t1));
- var t2 = t1.redISub(nx);
- var jyd8 = jy2.redSqr();
- jyd8 = jyd8.redIAdd(jyd8);
- jyd8 = jyd8.redIAdd(jyd8);
- jyd8 = jyd8.redIAdd(jyd8);
- var ny = c.redMul(t2).redISub(jyd8);
- var nz = jy.redAdd(jy).redMul(jz);
- return this.curve.jpoint(nx, ny, nz);
- };
- JPoint.prototype.trpl = function trpl() {
- if (!this.curve.zeroA)
- return this.dbl().add(this);
- // hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#tripling-tpl-2007-bl
- // 5M + 10S + ...
- // XX = X1^2
- var xx = this.x.redSqr();
- // YY = Y1^2
- var yy = this.y.redSqr();
- // ZZ = Z1^2
- var zz = this.z.redSqr();
- // YYYY = YY^2
- var yyyy = yy.redSqr();
- // M = 3 * XX + a * ZZ2; a = 0
- var m = xx.redAdd(xx).redIAdd(xx);
- // MM = M^2
- var mm = m.redSqr();
- // E = 6 * ((X1 + YY)^2 - XX - YYYY) - MM
- var e = this.x.redAdd(yy).redSqr().redISub(xx).redISub(yyyy);
- e = e.redIAdd(e);
- e = e.redAdd(e).redIAdd(e);
- e = e.redISub(mm);
- // EE = E^2
- var ee = e.redSqr();
- // T = 16*YYYY
- var t = yyyy.redIAdd(yyyy);
- t = t.redIAdd(t);
- t = t.redIAdd(t);
- t = t.redIAdd(t);
- // U = (M + E)^2 - MM - EE - T
- var u = m.redIAdd(e).redSqr().redISub(mm).redISub(ee).redISub(t);
- // X3 = 4 * (X1 * EE - 4 * YY * U)
- var yyu4 = yy.redMul(u);
- yyu4 = yyu4.redIAdd(yyu4);
- yyu4 = yyu4.redIAdd(yyu4);
- var nx = this.x.redMul(ee).redISub(yyu4);
- nx = nx.redIAdd(nx);
- nx = nx.redIAdd(nx);
- // Y3 = 8 * Y1 * (U * (T - U) - E * EE)
- var ny = this.y.redMul(u.redMul(t.redISub(u)).redISub(e.redMul(ee)));
- ny = ny.redIAdd(ny);
- ny = ny.redIAdd(ny);
- ny = ny.redIAdd(ny);
- // Z3 = (Z1 + E)^2 - ZZ - EE
- var nz = this.z.redAdd(e).redSqr().redISub(zz).redISub(ee);
- return this.curve.jpoint(nx, ny, nz);
- };
- JPoint.prototype.mul = function mul(k, kbase) {
- k = new BN(k, kbase);
- return this.curve._wnafMul(this, k);
- };
- JPoint.prototype.eq = function eq(p) {
- if (p.type === 'affine')
- return this.eq(p.toJ());
- if (this === p)
- return true;
- // x1 * z2^2 == x2 * z1^2
- var z2 = this.z.redSqr();
- var pz2 = p.z.redSqr();
- if (this.x.redMul(pz2).redISub(p.x.redMul(z2)).cmpn(0) !== 0)
- return false;
- // y1 * z2^3 == y2 * z1^3
- var z3 = z2.redMul(this.z);
- var pz3 = pz2.redMul(p.z);
- return this.y.redMul(pz3).redISub(p.y.redMul(z3)).cmpn(0) === 0;
- };
- JPoint.prototype.eqXToP = function eqXToP(x) {
- var zs = this.z.redSqr();
- var rx = x.toRed(this.curve.red).redMul(zs);
- if (this.x.cmp(rx) === 0)
- return true;
- var xc = x.clone();
- var t = this.curve.redN.redMul(zs);
- for (;;) {
- xc.iadd(this.curve.n);
- if (xc.cmp(this.curve.p) >= 0)
- return false;
- rx.redIAdd(t);
- if (this.x.cmp(rx) === 0)
- return true;
- }
- return false;
- };
- JPoint.prototype.inspect = function inspect() {
- if (this.isInfinity())
- return '<EC JPoint Infinity>';
- return '<EC JPoint x: ' + this.x.toString(16, 2) +
- ' y: ' + this.y.toString(16, 2) +
- ' z: ' + this.z.toString(16, 2) + '>';
- };
- JPoint.prototype.isInfinity = function isInfinity() {
- // XXX This code assumes that zero is always zero in red
- return this.z.cmpn(0) === 0;
- };
|