mirror of
https://github.com/hanzoai/elliptic.git
synced 2026-09-23 23:51:03 +00:00
curve: implement Greg Maxwell's trick
TL;DR: Prior to this patch `.mulAdd()` converted point from Jacobian to Cartesian representation to check its' `.getX()` value against `signature.r`. However, it is possible to avoid the expensive inversion in `.toP()` conversion function by multiplying `signature.r` with `point.z.redSqr()`. Some adjustments (by adding `curve.n` several times) may be needed, but overall it saves about 5% of execution time during `.verify()`. Big kudos to @gmaxwell for implementing it, and to @briansmith for sharing it with me.
This commit is contained in:
@@ -28,6 +28,16 @@ function BaseCurve(type, conf) {
|
||||
this._wnafT2 = new Array(4);
|
||||
this._wnafT3 = new Array(4);
|
||||
this._wnafT4 = new Array(4);
|
||||
|
||||
// Generalized Greg Maxwell's trick
|
||||
var adjustCount = this.n && this.p.div(this.n);
|
||||
if (!adjustCount || adjustCount.cmpn(100) > 0) {
|
||||
this._maxwellAdjust = 0;
|
||||
this.redN = null;
|
||||
} else {
|
||||
this._maxwellAdjust = adjustCount.toNumber();
|
||||
this.redN = this.n.toRed(this.red);
|
||||
}
|
||||
}
|
||||
module.exports = BaseCurve;
|
||||
|
||||
@@ -116,7 +126,8 @@ BaseCurve.prototype._wnafMul = function _wnafMul(p, k) {
|
||||
BaseCurve.prototype._wnafMulAdd = function _wnafMulAdd(defW,
|
||||
points,
|
||||
coeffs,
|
||||
len) {
|
||||
len,
|
||||
jacobianResult) {
|
||||
var wndWidth = this._wnafT1;
|
||||
var wnd = this._wnafT2;
|
||||
var naf = this._wnafT3;
|
||||
@@ -229,7 +240,11 @@ BaseCurve.prototype._wnafMulAdd = function _wnafMulAdd(defW,
|
||||
// Zeroify references
|
||||
for (var i = 0; i < len; i++)
|
||||
wnd[i] = null;
|
||||
return acc.toP();
|
||||
|
||||
if (jacobianResult)
|
||||
return acc;
|
||||
else
|
||||
return acc.toP();
|
||||
};
|
||||
|
||||
function BasePoint(curve, type) {
|
||||
|
||||
@@ -364,7 +364,11 @@ Point.prototype.mul = function mul(k) {
|
||||
};
|
||||
|
||||
Point.prototype.mulAdd = function mulAdd(k1, p, k2) {
|
||||
return this.curve._wnafMulAdd(1, [ this, p ], [ k1, k2 ], 2);
|
||||
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() {
|
||||
@@ -405,6 +409,20 @@ Point.prototype.eq = function eq(other) {
|
||||
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 t = this.curve.redN.redMul(this.z);
|
||||
for (var i = 1; i <= this.curve._maxwellAdjust; i++) {
|
||||
rx.redIAdd(t);
|
||||
if (this.x.cmp(rx) === 0)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// Compatibility with BaseCurve
|
||||
Point.prototype.toP = Point.prototype.normalize;
|
||||
Point.prototype.mixedAdd = Point.prototype.add;
|
||||
|
||||
@@ -158,6 +158,10 @@ Point.prototype.mulAdd = function mulAdd() {
|
||||
throw new Error('Not supported on Montgomery curve');
|
||||
};
|
||||
|
||||
Point.prototype.jumlAdd = function jumlAdd() {
|
||||
throw new Error('Not supported on Montgomery curve');
|
||||
};
|
||||
|
||||
Point.prototype.eq = function eq(other) {
|
||||
return this.getX().cmp(other.getX()) === 0;
|
||||
};
|
||||
|
||||
@@ -217,7 +217,7 @@ ShortCurve.prototype.validate = function validate(point) {
|
||||
};
|
||||
|
||||
ShortCurve.prototype._endoWnafMulAdd =
|
||||
function _endoWnafMulAdd(points, coeffs) {
|
||||
function _endoWnafMulAdd(points, coeffs, jacobianResult) {
|
||||
var npoints = this._endoWnafT1;
|
||||
var ncoeffs = this._endoWnafT2;
|
||||
for (var i = 0; i < points.length; i++) {
|
||||
@@ -239,7 +239,7 @@ ShortCurve.prototype._endoWnafMulAdd =
|
||||
ncoeffs[i * 2] = split.k1;
|
||||
ncoeffs[i * 2 + 1] = split.k2;
|
||||
}
|
||||
var res = this._wnafMulAdd(1, npoints, ncoeffs, i * 2);
|
||||
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++) {
|
||||
@@ -440,6 +440,15 @@ Point.prototype.mulAdd = function mulAdd(k1, p2, k2) {
|
||||
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 &&
|
||||
@@ -895,6 +904,21 @@ JPoint.prototype.eq = function eq(p) {
|
||||
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 t = this.curve.redN.redMul(zs);
|
||||
for (var i = 1; i <= this.curve._maxwellAdjust; i++) {
|
||||
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>';
|
||||
|
||||
@@ -166,11 +166,25 @@ EC.prototype.verify = function verify(msg, signature, key, enc) {
|
||||
var u1 = sinv.mul(msg).umod(this.n);
|
||||
var u2 = sinv.mul(r).umod(this.n);
|
||||
|
||||
var p = this.g.mulAdd(u1, key.getPublic(), u2);
|
||||
if (this.curve._maxwellAdjust === 0) {
|
||||
var p = this.g.mulAdd(u1, key.getPublic(), u2);
|
||||
if (p.isInfinity())
|
||||
return false;
|
||||
|
||||
return p.getX().umod(this.n).cmp(r) === 0;
|
||||
}
|
||||
|
||||
// NOTE: Greg Maxwell's trick, inspired by:
|
||||
// https://git.io/vad3K
|
||||
|
||||
var p = this.g.jmulAdd(u1, key.getPublic(), u2);
|
||||
if (p.isInfinity())
|
||||
return false;
|
||||
|
||||
return p.getX().umod(this.n).cmp(r) === 0;
|
||||
// Compare `p.x` of Jacobian point with `r`,
|
||||
// this will do `p.x == r * p.z^2` instead of multiplying `p.x` by the
|
||||
// inverse of `p.z^2`
|
||||
return p.eqXToP(r);
|
||||
};
|
||||
|
||||
EC.prototype.recoverPubKey = function(msg, signature, j, enc) {
|
||||
|
||||
+1
-1
@@ -46,7 +46,7 @@
|
||||
"mocha": "^2.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"bn.js": "^4.0.0",
|
||||
"bn.js": "^4.4.0",
|
||||
"brorand": "^1.0.1",
|
||||
"hash.js": "^1.0.0",
|
||||
"inherits": "^2.0.1"
|
||||
|
||||
Reference in New Issue
Block a user