'use strict';
|
|
var utils = require('../utils');
|
var BN = require('bn.js');
|
var inherits = require('inherits');
|
var Base = require('./base');
|
|
var assert = utils.assert;
|
|
function EdwardsCurve(conf) {
|
// NOTE: Important as we are creating point in Base.call()
|
this.twisted = (conf.a | 0) !== 1;
|
this.mOneA = this.twisted && (conf.a | 0) === -1;
|
this.extended = this.mOneA;
|
|
Base.call(this, 'edwards', conf);
|
|
this.a = new BN(conf.a, 16).umod(this.red.m);
|
this.a = this.a.toRed(this.red);
|
this.c = new BN(conf.c, 16).toRed(this.red);
|
this.c2 = this.c.redSqr();
|
this.d = new BN(conf.d, 16).toRed(this.red);
|
this.dd = this.d.redAdd(this.d);
|
|
assert(!this.twisted || this.c.fromRed().cmpn(1) === 0);
|
this.oneC = (conf.c | 0) === 1;
|
}
|
inherits(EdwardsCurve, Base);
|
module.exports = EdwardsCurve;
|
|
EdwardsCurve.prototype._mulA = function _mulA(num) {
|
if (this.mOneA)
|
return num.redNeg();
|
else
|
return this.a.redMul(num);
|
};
|
|
EdwardsCurve.prototype._mulC = function _mulC(num) {
|
if (this.oneC)
|
return num;
|
else
|
return this.c.redMul(num);
|
};
|
|
// Just for compatibility with Short curve
|
EdwardsCurve.prototype.jpoint = function jpoint(x, y, z, t) {
|
return this.point(x, y, z, t);
|
};
|
|
EdwardsCurve.prototype.pointFromX = function pointFromX(x, odd) {
|
x = new BN(x, 16);
|
if (!x.red)
|
x = x.toRed(this.red);
|
|
var x2 = x.redSqr();
|
var rhs = this.c2.redSub(this.a.redMul(x2));
|
var lhs = this.one.redSub(this.c2.redMul(this.d).redMul(x2));
|
|
var y2 = rhs.redMul(lhs.redInvm());
|
var y = y2.redSqrt();
|
if (y.redSqr().redSub(y2).cmp(this.zero) !== 0)
|
throw new Error('invalid point');
|
|
var isOdd = y.fromRed().isOdd();
|
if (odd && !isOdd || !odd && isOdd)
|
y = y.redNeg();
|
|
return this.point(x, y);
|
};
|
|
EdwardsCurve.prototype.pointFromY = function pointFromY(y, odd) {
|
y = new BN(y, 16);
|
if (!y.red)
|
y = y.toRed(this.red);
|
|
// x^2 = (y^2 - c^2) / (c^2 d y^2 - a)
|
var y2 = y.redSqr();
|
var lhs = y2.redSub(this.c2);
|
var rhs = y2.redMul(this.d).redMul(this.c2).redSub(this.a);
|
var x2 = lhs.redMul(rhs.redInvm());
|
|
if (x2.cmp(this.zero) === 0) {
|
if (odd)
|
throw new Error('invalid point');
|
else
|
return this.point(this.zero, y);
|
}
|
|
var x = x2.redSqrt();
|
if (x.redSqr().redSub(x2).cmp(this.zero) !== 0)
|
throw new Error('invalid point');
|
|
if (x.fromRed().isOdd() !== odd)
|
x = x.redNeg();
|
|
return this.point(x, y);
|
};
|
|
EdwardsCurve.prototype.validate = function validate(point) {
|
if (point.isInfinity())
|
return true;
|
|
// Curve: A * X^2 + Y^2 = C^2 * (1 + D * X^2 * Y^2)
|
point.normalize();
|
|
var x2 = point.x.redSqr();
|
var y2 = point.y.redSqr();
|
var lhs = x2.redMul(this.a).redAdd(y2);
|
var rhs = this.c2.redMul(this.one.redAdd(this.d.redMul(x2).redMul(y2)));
|
|
return lhs.cmp(rhs) === 0;
|
};
|
|
function Point(curve, x, y, z, t) {
|
Base.BasePoint.call(this, curve, 'projective');
|
if (x === null && y === null && z === null) {
|
this.x = this.curve.zero;
|
this.y = this.curve.one;
|
this.z = this.curve.one;
|
this.t = this.curve.zero;
|
this.zOne = true;
|
} else {
|
this.x = new BN(x, 16);
|
this.y = new BN(y, 16);
|
this.z = z ? new BN(z, 16) : this.curve.one;
|
this.t = t && new BN(t, 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);
|
if (this.t && !this.t.red)
|
this.t = this.t.toRed(this.curve.red);
|
this.zOne = this.z === this.curve.one;
|
|
// Use extended coordinates
|
if (this.curve.extended && !this.t) {
|
this.t = this.x.redMul(this.y);
|
if (!this.zOne)
|
this.t = this.t.redMul(this.z.redInvm());
|
}
|
}
|
}
|
inherits(Point, Base.BasePoint);
|
|
EdwardsCurve.prototype.pointFromJSON = function pointFromJSON(obj) {
|
return Point.fromJSON(this, obj);
|
};
|
|
EdwardsCurve.prototype.point = function point(x, y, z, t) {
|
return new Point(this, x, y, z, t);
|
};
|
|
Point.fromJSON = function fromJSON(curve, obj) {
|
return new Point(curve, obj[0], obj[1], obj[2]);
|
};
|
|
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) +
|
' z: ' + this.z.fromRed().toString(16, 2) + '>';
|
};
|
|
Point.prototype.isInfinity = function isInfinity() {
|
// XXX This code assumes that zero is always zero in red
|
return this.x.cmpn(0) === 0 &&
|
(this.y.cmp(this.z) === 0 ||
|
(this.zOne && this.y.cmp(this.curve.c) === 0));
|
};
|
|
Point.prototype._extDbl = function _extDbl() {
|
// hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html
|
// #doubling-dbl-2008-hwcd
|
// 4M + 4S
|
|
// A = X1^2
|
var a = this.x.redSqr();
|
// B = Y1^2
|
var b = this.y.redSqr();
|
// C = 2 * Z1^2
|
var c = this.z.redSqr();
|
c = c.redIAdd(c);
|
// D = a * A
|
var d = this.curve._mulA(a);
|
// E = (X1 + Y1)^2 - A - B
|
var e = this.x.redAdd(this.y).redSqr().redISub(a).redISub(b);
|
// G = D + B
|
var g = d.redAdd(b);
|
// F = G - C
|
var f = g.redSub(c);
|
// H = D - B
|
var h = d.redSub(b);
|
// X3 = E * F
|
var nx = e.redMul(f);
|
// Y3 = G * H
|
var ny = g.redMul(h);
|
// T3 = E * H
|
var nt = e.redMul(h);
|
// Z3 = F * G
|
var nz = f.redMul(g);
|
return this.curve.point(nx, ny, nz, nt);
|
};
|
|
Point.prototype._projDbl = function _projDbl() {
|
// hyperelliptic.org/EFD/g1p/auto-twisted-projective.html
|
// #doubling-dbl-2008-bbjlp
|
// #doubling-dbl-2007-bl
|
// and others
|
// Generally 3M + 4S or 2M + 4S
|
|
// B = (X1 + Y1)^2
|
var b = this.x.redAdd(this.y).redSqr();
|
// C = X1^2
|
var c = this.x.redSqr();
|
// D = Y1^2
|
var d = this.y.redSqr();
|
|
var nx;
|
var ny;
|
var nz;
|
var e;
|
var h;
|
var j;
|
if (this.curve.twisted) {
|
// E = a * C
|
e = this.curve._mulA(c);
|
// F = E + D
|
var f = e.redAdd(d);
|
if (this.zOne) {
|
// X3 = (B - C - D) * (F - 2)
|
nx = b.redSub(c).redSub(d).redMul(f.redSub(this.curve.two));
|
// Y3 = F * (E - D)
|
ny = f.redMul(e.redSub(d));
|
// Z3 = F^2 - 2 * F
|
nz = f.redSqr().redSub(f).redSub(f);
|
} else {
|
// H = Z1^2
|
h = this.z.redSqr();
|
// J = F - 2 * H
|
j = f.redSub(h).redISub(h);
|
// X3 = (B-C-D)*J
|
nx = b.redSub(c).redISub(d).redMul(j);
|
// Y3 = F * (E - D)
|
ny = f.redMul(e.redSub(d));
|
// Z3 = F * J
|
nz = f.redMul(j);
|
}
|
} else {
|
// E = C + D
|
e = c.redAdd(d);
|
// H = (c * Z1)^2
|
h = this.curve._mulC(this.z).redSqr();
|
// J = E - 2 * H
|
j = e.redSub(h).redSub(h);
|
// X3 = c * (B - E) * J
|
nx = this.curve._mulC(b.redISub(e)).redMul(j);
|
// Y3 = c * E * (C - D)
|
ny = this.curve._mulC(e).redMul(c.redISub(d));
|
// Z3 = E * J
|
nz = e.redMul(j);
|
}
|
return this.curve.point(nx, ny, nz);
|
};
|
|
Point.prototype.dbl = function dbl() {
|
if (this.isInfinity())
|
return this;
|
|
// Double in extended coordinates
|
if (this.curve.extended)
|
return this._extDbl();
|
else
|
return this._projDbl();
|
};
|
|
Point.prototype._extAdd = function _extAdd(p) {
|
// hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html
|
// #addition-add-2008-hwcd-3
|
// 8M
|
|
// A = (Y1 - X1) * (Y2 - X2)
|
var a = this.y.redSub(this.x).redMul(p.y.redSub(p.x));
|
// B = (Y1 + X1) * (Y2 + X2)
|
var b = this.y.redAdd(this.x).redMul(p.y.redAdd(p.x));
|
// C = T1 * k * T2
|
var c = this.t.redMul(this.curve.dd).redMul(p.t);
|
// D = Z1 * 2 * Z2
|
var d = this.z.redMul(p.z.redAdd(p.z));
|
// E = B - A
|
var e = b.redSub(a);
|
// F = D - C
|
var f = d.redSub(c);
|
// G = D + C
|
var g = d.redAdd(c);
|
// H = B + A
|
var h = b.redAdd(a);
|
// X3 = E * F
|
var nx = e.redMul(f);
|
// Y3 = G * H
|
var ny = g.redMul(h);
|
// T3 = E * H
|
var nt = e.redMul(h);
|
// Z3 = F * G
|
var nz = f.redMul(g);
|
return this.curve.point(nx, ny, nz, nt);
|
};
|
|
Point.prototype._projAdd = function _projAdd(p) {
|
// hyperelliptic.org/EFD/g1p/auto-twisted-projective.html
|
// #addition-add-2008-bbjlp
|
// #addition-add-2007-bl
|
// 10M + 1S
|
|
// A = Z1 * Z2
|
var a = this.z.redMul(p.z);
|
// B = A^2
|
var b = a.redSqr();
|
// C = X1 * X2
|
var c = this.x.redMul(p.x);
|
// D = Y1 * Y2
|
var d = this.y.redMul(p.y);
|
// E = d * C * D
|
var e = this.curve.d.redMul(c).redMul(d);
|
// F = B - E
|
var f = b.redSub(e);
|
// G = B + E
|
var g = b.redAdd(e);
|
// X3 = A * F * ((X1 + Y1) * (X2 + Y2) - C - D)
|
var tmp = this.x.redAdd(this.y).redMul(p.x.redAdd(p.y)).redISub(c).redISub(d);
|
var nx = a.redMul(f).redMul(tmp);
|
var ny;
|
var nz;
|
if (this.curve.twisted) {
|
// Y3 = A * G * (D - a * C)
|
ny = a.redMul(g).redMul(d.redSub(this.curve._mulA(c)));
|
// Z3 = F * G
|
nz = f.redMul(g);
|
} else {
|
// Y3 = A * G * (D - C)
|
ny = a.redMul(g).redMul(d.redSub(c));
|
// Z3 = c * F * G
|
nz = this.curve._mulC(f).redMul(g);
|
}
|
return this.curve.point(nx, ny, nz);
|
};
|
|
Point.prototype.add = function add(p) {
|
if (this.isInfinity())
|
return p;
|
if (p.isInfinity())
|
return this;
|
|
if (this.curve.extended)
|
return this._extAdd(p);
|
else
|
return this._projAdd(p);
|
};
|
|
Point.prototype.mul = function mul(k) {
|
if (this._hasDoubles(k))
|
return this.curve._fixedNafMul(this, k);
|
else
|
return this.curve._wnafMul(this, k);
|
};
|
|
Point.prototype.mulAdd = function mulAdd(k1, p, k2) {
|
return this.curve._wnafMulAdd(1, [ this, p ], [ k1, k2 ], 2, false);
|
};
|
|
Point.prototype.jmulAdd = function jmulAdd(k1, p, k2) {
|
return this.curve._wnafMulAdd(1, [ this, p ], [ k1, k2 ], 2, true);
|
};
|
|
Point.prototype.normalize = function normalize() {
|
if (this.zOne)
|
return this;
|
|
// Normalize coordinates
|
var zi = this.z.redInvm();
|
this.x = this.x.redMul(zi);
|
this.y = this.y.redMul(zi);
|
if (this.t)
|
this.t = this.t.redMul(zi);
|
this.z = this.curve.one;
|
this.zOne = true;
|
return this;
|
};
|
|
Point.prototype.neg = function neg() {
|
return this.curve.point(this.x.redNeg(),
|
this.y,
|
this.z,
|
this.t && this.t.redNeg());
|
};
|
|
Point.prototype.getX = function getX() {
|
this.normalize();
|
return this.x.fromRed();
|
};
|
|
Point.prototype.getY = function getY() {
|
this.normalize();
|
return this.y.fromRed();
|
};
|
|
Point.prototype.eq = function eq(other) {
|
return this === other ||
|
this.getX().cmp(other.getX()) === 0 &&
|
this.getY().cmp(other.getY()) === 0;
|
};
|
|
Point.prototype.eqXToP = function eqXToP(x) {
|
var rx = x.toRed(this.curve.red).redMul(this.z);
|
if (this.x.cmp(rx) === 0)
|
return true;
|
|
var xc = x.clone();
|
var t = this.curve.redN.redMul(this.z);
|
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;
|
}
|
};
|
|
// Compatibility with BaseCurve
|
Point.prototype.toP = Point.prototype.normalize;
|
Point.prototype.mixedAdd = Point.prototype.add;
|