Merge pull request #183 from dcousens/ecconst

EC constants and bitShifts
This commit is contained in:
Wei Lu 2014-05-22 21:36:59 +10:00
commit e4aac0f2de
4 changed files with 58 additions and 43 deletions

View file

@ -5,6 +5,10 @@
var assert = require('assert') var assert = require('assert')
var BigInteger = require('bigi') var BigInteger = require('bigi')
// constants
var TWO = BigInteger.valueOf(2)
var THREE = BigInteger.valueOf(3)
function ECFieldElementFp(q,x) { function ECFieldElementFp(q,x) {
this.x = x; this.x = x;
// TODO if(x.compareTo(q) >= 0) error // TODO if(x.compareTo(q) >= 0) error
@ -94,15 +98,15 @@ function pointFpEquals(other) {
var u, v; var u, v;
// u = Y2 * Z1 - Y1 * Z2 // u = Y2 * Z1 - Y1 * Z2
u = other.y.toBigInteger().multiply(this.z).subtract(this.y.toBigInteger().multiply(other.z)).mod(this.curve.q); u = other.y.toBigInteger().multiply(this.z).subtract(this.y.toBigInteger().multiply(other.z)).mod(this.curve.q);
if(!u.equals(BigInteger.ZERO)) return false; if (u.signum() !== 0) return false;
// v = X2 * Z1 - X1 * Z2 // v = X2 * Z1 - X1 * Z2
v = other.x.toBigInteger().multiply(this.z).subtract(this.x.toBigInteger().multiply(other.z)).mod(this.curve.q); v = other.x.toBigInteger().multiply(this.z).subtract(this.x.toBigInteger().multiply(other.z)).mod(this.curve.q);
return v.equals(BigInteger.ZERO); return v.signum() === 0;
} }
function pointFpIsInfinity() { function pointFpIsInfinity() {
if((this.x == null) && (this.y == null)) return true; if ((this.x == null) && (this.y == null)) return true;
return this.z.equals(BigInteger.ZERO) && !this.y.toBigInteger().equals(BigInteger.ZERO); return this.z.signum() === 0 && this.y.toBigInteger().signum() !== 0;
} }
function pointFpNegate() { function pointFpNegate() {
@ -118,14 +122,13 @@ function pointFpAdd(b) {
// v = X2 * Z1 - X1 * Z2 // v = X2 * Z1 - X1 * Z2
var v = b.x.toBigInteger().multiply(this.z).subtract(this.x.toBigInteger().multiply(b.z)).mod(this.curve.q); var v = b.x.toBigInteger().multiply(this.z).subtract(this.x.toBigInteger().multiply(b.z)).mod(this.curve.q);
if(BigInteger.ZERO.equals(v)) { if(v.signum() === 0) {
if(BigInteger.ZERO.equals(u)) { if(u.signum() === 0) {
return this.twice(); // this == b, so double return this.twice(); // this == b, so double
} }
return this.curve.getInfinity(); // this = -b, so infinity return this.curve.getInfinity(); // this = -b, so infinity
} }
var THREE = new BigInteger("3");
var x1 = this.x.toBigInteger(); var x1 = this.x.toBigInteger();
var y1 = this.y.toBigInteger(); var y1 = this.y.toBigInteger();
var x2 = b.x.toBigInteger(); var x2 = b.x.toBigInteger();
@ -148,10 +151,8 @@ function pointFpAdd(b) {
function pointFpTwice() { function pointFpTwice() {
if(this.isInfinity()) return this; if(this.isInfinity()) return this;
if(this.y.toBigInteger().signum() == 0) return this.curve.getInfinity(); if(this.y.toBigInteger().signum() === 0) return this.curve.getInfinity();
// TODO: optimized handling of constants
var THREE = new BigInteger("3");
var x1 = this.x.toBigInteger(); var x1 = this.x.toBigInteger();
var y1 = this.y.toBigInteger(); var y1 = this.y.toBigInteger();
@ -161,16 +162,16 @@ function pointFpTwice() {
// w = 3 * x1^2 + a * z1^2 // w = 3 * x1^2 + a * z1^2
var w = x1.square().multiply(THREE); var w = x1.square().multiply(THREE);
if(!BigInteger.ZERO.equals(a)) { if(a.signum() !== 0) {
w = w.add(this.z.square().multiply(a)); w = w.add(this.z.square().multiply(a));
} }
w = w.mod(this.curve.q); w = w.mod(this.curve.q);
// x3 = 2 * y1 * z1 * (w^2 - 8 * x1 * y1^2 * z1) // x3 = 2 * y1 * z1 * (w^2 - 8 * x1 * y1^2 * z1)
var x3 = w.square().subtract(x1.shiftLeft(3).multiply(y1sqz1)).shiftLeft(1).multiply(y1z1).mod(this.curve.q); var x3 = w.square().subtract(x1.shiftLeft(3).multiply(y1sqz1)).shiftLeft(1).multiply(y1z1).mod(this.curve.q);
// y3 = 4 * y1^2 * z1 * (3 * w * x1 - 2 * y1^2 * z1) - w^3 // y3 = 4 * y1^2 * z1 * (3 * w * x1 - 2 * y1^2 * z1) - w^3
var y3 = w.multiply(THREE).multiply(x1).subtract(y1sqz1.shiftLeft(1)).shiftLeft(2).multiply(y1sqz1).subtract(w.square().multiply(w)).mod(this.curve.q); var y3 = w.multiply(THREE).multiply(x1).subtract(y1sqz1.shiftLeft(1)).shiftLeft(2).multiply(y1sqz1).subtract(w.pow(3)).mod(this.curve.q);
// z3 = 8 * (y1 * z1)^3 // z3 = 8 * (y1 * z1)^3
var z3 = y1z1.square().multiply(y1z1).shiftLeft(3).mod(this.curve.q); var z3 = y1z1.pow(3).shiftLeft(3).mod(this.curve.q);
return new ECPointFp(this.curve, this.curve.fromBigInteger(x3), this.curve.fromBigInteger(y3), z3); return new ECPointFp(this.curve, this.curve.fromBigInteger(x3), this.curve.fromBigInteger(y3), z3);
} }
@ -179,10 +180,10 @@ function pointFpTwice() {
// TODO: modularize the multiplication algorithm // TODO: modularize the multiplication algorithm
function pointFpMultiply(k) { function pointFpMultiply(k) {
if(this.isInfinity()) return this; if(this.isInfinity()) return this;
if(k.signum() == 0) return this.curve.getInfinity(); if(k.signum() === 0) return this.curve.getInfinity()
var e = k; var e = k;
var h = e.multiply(new BigInteger("3")); var h = e.multiply(THREE)
var neg = this.negate(); var neg = this.negate();
var R = this; var R = this;
@ -327,8 +328,6 @@ ECPointFp.prototype.getEncoded = function(compressed) {
return buffer return buffer
} }
var SEVEN = BigInteger.valueOf(7)
ECPointFp.decodeFrom = function (curve, buffer) { ECPointFp.decodeFrom = function (curve, buffer) {
var type = buffer.readUInt8(0) var type = buffer.readUInt8(0)
var compressed = type !== 0x04 var compressed = type !== 0x04
@ -340,14 +339,18 @@ ECPointFp.decodeFrom = function (curve, buffer) {
assert(type === 0x02 || type === 0x03, 'Invalid sequence tag') assert(type === 0x02 || type === 0x03, 'Invalid sequence tag')
var isYEven = (type === 0x02) var isYEven = (type === 0x02)
var a = curve.getA().toBigInteger()
var b = curve.getB().toBigInteger()
var p = curve.getQ() var p = curve.getQ()
// We precalculate (p + 1) / 4 where p is the field order // We precalculate (p + 1) / 4 where p is the field order
var P_OVER_FOUR = p.add(BigInteger.ONE).shiftRight(2) if (!curve.P_OVER_FOUR) {
curve.P_OVER_FOUR = p.add(BigInteger.ONE).shiftRight(2)
}
// Convert x to point // Convert x to point
var alpha = x.square().multiply(x).add(SEVEN).mod(p) var alpha = x.pow(3).add(a.multiply(x)).add(b).mod(p)
var beta = alpha.modPow(P_OVER_FOUR, p) var beta = alpha.modPow(curve.P_OVER_FOUR, p)
// If beta is even, but y isn't, or vice versa, then convert it, // If beta is even, but y isn't, or vice versa, then convert it,
// otherwise we're done and y == beta. // otherwise we're done and y == beta.
@ -392,17 +395,17 @@ ECPointFp.prototype.add2D = function (b) {
ECPointFp.prototype.twice2D = function () { ECPointFp.prototype.twice2D = function () {
if (this.isInfinity()) return this; if (this.isInfinity()) return this;
if (this.y.toBigInteger().signum() == 0) { if (this.y.toBigInteger().signum() === 0) {
// if y1 == 0, then (x1, y1) == (x1, -y1) // if y1 == 0, then (x1, y1) == (x1, -y1)
// and hence this = -this and thus 2(x1, y1) == infinity // and hence this = -this and thus 2(x1, y1) == infinity
return this.curve.getInfinity(); return this.curve.getInfinity();
} }
var TWO = this.curve.fromBigInteger(BigInteger.valueOf(2)); var FpTWO = this.curve.fromBigInteger(TWO);
var THREE = this.curve.fromBigInteger(BigInteger.valueOf(3)); var FpTHREE = this.curve.fromBigInteger(THREE)
var gamma = this.x.square().multiply(THREE).add(this.curve.a).divide(this.y.multiply(TWO)); var gamma = this.x.square().multiply(FpTHREE).add(this.curve.a).divide(this.y.multiply(FpTWO));
var x3 = gamma.square().subtract(this.x.multiply(TWO)); var x3 = gamma.square().subtract(this.x.multiply(FpTWO));
var y3 = gamma.multiply(this.x.subtract(x3)).subtract(this.y); var y3 = gamma.multiply(this.x.subtract(x3)).subtract(this.y);
return new ECPointFp(this.curve, x3, y3); return new ECPointFp(this.curve, x3, y3);
@ -410,10 +413,10 @@ ECPointFp.prototype.twice2D = function () {
ECPointFp.prototype.multiply2D = function (k) { ECPointFp.prototype.multiply2D = function (k) {
if(this.isInfinity()) return this; if(this.isInfinity()) return this;
if(k.signum() == 0) return this.curve.getInfinity(); if (k.signum() === 0) return this.curve.getInfinity()
var e = k; var e = k;
var h = e.multiply(new BigInteger("3")); var h = e.multiply(THREE)
var neg = this.negate(); var neg = this.negate();
var R = this; var R = this;
@ -438,10 +441,9 @@ ECPointFp.prototype.isOnCurve = function () {
var y = this.getY().toBigInteger(); var y = this.getY().toBigInteger();
var a = this.curve.getA().toBigInteger(); var a = this.curve.getA().toBigInteger();
var b = this.curve.getB().toBigInteger(); var b = this.curve.getB().toBigInteger();
var n = this.curve.getQ(); var p = this.curve.getQ()
var lhs = y.multiply(y).mod(n); var lhs = y.square().mod(p)
var rhs = x.multiply(x).multiply(x) var rhs = x.pow(3).add(a.multiply(x)).add(b).mod(p)
.add(a.multiply(x)).add(b).mod(n);
return lhs.equals(rhs); return lhs.equals(rhs);
}; };

View file

@ -6,8 +6,6 @@ var ecparams = sec("secp256k1")
var BigInteger = require('bigi') var BigInteger = require('bigi')
var ECPointFp = require('./ec').ECPointFp var ECPointFp = require('./ec').ECPointFp
var P_OVER_FOUR = null
function implShamirsTrick(P, k, Q, l) { function implShamirsTrick(P, k, Q, l) {
var m = Math.max(k.bitLength(), l.bitLength()) var m = Math.max(k.bitLength(), l.bitLength())
var Z = P.add2D(Q) var Z = P.add2D(Q)
@ -75,9 +73,9 @@ var ecdsa = {
var s = k.modInverse(n).multiply(e.add(D.multiply(r))).mod(n) var s = k.modInverse(n).multiply(e.add(D.multiply(r))).mod(n)
assert.notEqual(s.signum(), 0, 'Invalid S value') assert.notEqual(s.signum(), 0, 'Invalid S value')
var N_OVER_TWO = n.divide(BigInteger.valueOf(2)) var N_OVER_TWO = n.shiftRight(1)
// Make 's' value 'low' as per bip62 // enforce low S values, see bip62: 'low s values in signatures'
if (s.compareTo(N_OVER_TWO) > 0) { if (s.compareTo(N_OVER_TWO) > 0) {
s = n.subtract(s) s = n.subtract(s)
} }
@ -124,7 +122,6 @@ var ecdsa = {
} }
var c = s.modInverse(n) var c = s.modInverse(n)
var u1 = e.multiply(c).mod(n) var u1 = e.multiply(c).mod(n)
var u2 = r.multiply(c).mod(n) var u2 = r.multiply(c).mod(n)
@ -257,23 +254,23 @@ var ecdsa = {
var a = curve.getA().toBigInteger() var a = curve.getA().toBigInteger()
var b = curve.getB().toBigInteger() var b = curve.getB().toBigInteger()
// We precalculate (p + 1) / 4 where p is if the field order // We precalculate (p + 1) / 4 where p is the field order
if (!P_OVER_FOUR) { if (!curve.P_OVER_FOUR) {
P_OVER_FOUR = p.add(BigInteger.ONE).divide(BigInteger.valueOf(4)) curve.P_OVER_FOUR = p.add(BigInteger.ONE).shiftRight(2)
} }
// 1.1 Compute x // 1.1 Compute x
var x = isSecondKey ? r.add(n) : r var x = isSecondKey ? r.add(n) : r
// 1.3 Convert x to point // 1.3 Convert x to point
var alpha = x.multiply(x).multiply(x).add(a.multiply(x)).add(b).mod(p) var alpha = x.pow(3).add(a.multiply(x)).add(b).mod(p)
var beta = alpha.modPow(P_OVER_FOUR, p) var beta = alpha.modPow(curve.P_OVER_FOUR, p)
// If beta is even, but y isn't, or vice versa, then convert it, // If beta is even, but y isn't, or vice versa, then convert it,
// otherwise we're done and y == beta. // otherwise we're done and y == beta.
var y = (beta.isEven() ^ isYEven) ? p.subtract(beta) : beta var y = (beta.isEven() ^ isYEven) ? p.subtract(beta) : beta
// 1.4 Check that nR is at infinity // 1.4 Check that nR isn't at infinity
var R = new ECPointFp(curve, curve.fromBigInteger(x), curve.fromBigInteger(y)) var R = new ECPointFp(curve, curve.fromBigInteger(x), curve.fromBigInteger(y))
R.validate() R.validate()

View file

@ -64,6 +64,21 @@ describe('ec', function() {
}) })
}) })
}) })
it('supports other curves', function() {
var f = fixtures.valid.ECPointFp[1]
var ecparams2 = sec('secp256r1')
var curve = ecparams2.getCurve()
var D = BigInteger.ONE
var Q = ecparams2.getG().multiply(D)
var buffer = Q.getEncoded(true)
var decoded = ECPointFp.decodeFrom(curve, buffer)
assert(Q.equals(decoded.Q))
assert(decoded.compressed, true)
})
}) })
}) })
}) })

View file

@ -59,7 +59,8 @@ describe('ecdsa', function() {
var psig = ecdsa.parseSig(signature) var psig = ecdsa.parseSig(signature)
// See BIP62 for more information // See BIP62 for more information
assert(psig.s.compareTo(ecparams.getN().divide(BigInteger.valueOf(2))) <= 0) var N_OVER_TWO = ecparams.getN().shiftRight(1)
assert(psig.s.compareTo(N_OVER_TWO) <= 0)
}) })
}) })