Skip to content

Commit

Permalink
[WIP] Add Point codec methods:
Browse files Browse the repository at this point in the history
Add Point#eq
Add Point#encode
Add Point#encodeCompressed
Add Curve#decodePoint
Add overrides for Montgomery curves/points
  • Loading branch information
sublimator committed Jul 16, 2015
1 parent 397d5f7 commit 890e34b
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 52 deletions.
50 changes: 46 additions & 4 deletions lib/elliptic/curve/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

var bn = require('bn.js');
var elliptic = require('../../elliptic');

var getNAF = elliptic.utils.getNAF;
var getJSF = elliptic.utils.getJSF;
var assert = elliptic.utils.assert;
var utils = elliptic.utils;
var getNAF = utils.getNAF;
var getJSF = utils.getJSF;
var assert = utils.assert;
var intToBE = utils.intToBE;

function BaseCurve(type, conf) {
this.type = type;
Expand Down Expand Up @@ -239,10 +240,51 @@ function BasePoint(curve, type) {
}
BaseCurve.BasePoint = BasePoint;

BasePoint.prototype.eq = function eq(/*other*/) {
throw new Error('Not implemented');
};

BasePoint.prototype.validate = function validate() {
return this.curve.validate(this);
};

BaseCurve.prototype.decodePoint = function decodePoint(bytes, enc) {
bytes = utils.toArray(bytes, enc);

var len = this.p.byteLength();
if (bytes[0] === 0x04 && bytes.length - 1 === 2 * len) {
return this.point(bytes.slice(1, 1 + len),
bytes.slice(1 + len, 1 + 2 * len));
} else if ((bytes[0] === 0x02 || bytes[0] === 0x03) &&
bytes.length - 1 === len) {
return this.pointFromX(bytes.slice(1, 1 + len), bytes[0] === 0x03);
}
throw new Error('Unknown point format');
};

BasePoint.prototype.encodeCompressed = function encodeCompressed(enc) {
return this.encode(true, enc);
};

BasePoint.prototype._encode = function _encode(compact) {
var len = this.curve.p.byteLength();
var x = intToBE(this.getX(), len);

return compact ?
[ this.getY().isEven() ? 0x02 : 0x03 ].concat(x) :
[ 0x04 ].concat(x, intToBE(this.getY(), len)) ;
};

BasePoint.prototype.encode = function encode(compact, enc) {
// compact is optional argument
if (typeof compact === 'string') {
enc = compact;
compact = null;
}

return utils.encode(this._encode(compact), enc);
};

BasePoint.prototype.precompute = function precompute(power) {
if (this.precomputed)
return this;
Expand Down
15 changes: 15 additions & 0 deletions lib/elliptic/curve/mont.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ var bn = require('bn.js');
var inherits = require('inherits');
var Base = curve.base;

var elliptic = require('../../elliptic');
var utils = elliptic.utils;

function MontCurve(conf) {
Base.call(this, 'mont', conf);

Expand Down Expand Up @@ -42,6 +45,10 @@ function Point(curve, x, z) {
}
inherits(Point, Base.BasePoint);

MontCurve.prototype.decodePoint = function decodePoint(bytes, enc) {
return this.point(utils.toArray(bytes, enc), 1);
};

MontCurve.prototype.point = function point(x, z) {
return new Point(this, x, z);
};
Expand All @@ -54,6 +61,10 @@ Point.prototype.precompute = function precompute() {
// No-op
};

Point.prototype._encode = function _encode(/*`compact` ignored*/) {
return utils.intToBE(this.getX(), this.curve.p.byteLength());
};

Point.fromJSON = function fromJSON(curve, obj) {
return new Point(curve, obj[0], obj[1] || curve.one);
};
Expand Down Expand Up @@ -147,6 +158,10 @@ Point.prototype.mulAdd = function mulAdd() {
throw new Error('Not supported on Montgomery curve');
};

Point.prototype.eq = function eq(other) {
return this.getX().cmp(other.getX()) === 0;
};

Point.prototype.normalize = function normalize() {
this.x = this.x.redMul(this.z.redInvm());
this.z = this.curve.one;
Expand Down
53 changes: 5 additions & 48 deletions lib/elliptic/ec/key.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@

var bn = require('bn.js');

var elliptic = require('../../elliptic');
var utils = elliptic.utils;

function KeyPair(ec, options) {
this.ec = ec;
this.priv = null;
Expand Down Expand Up @@ -52,39 +49,19 @@ KeyPair.prototype.validate = function validate() {
};

KeyPair.prototype.getPublic = function getPublic(compact, enc) {
if (!this.pub)
this.pub = this.ec.g.mul(this.priv);

// compact is optional argument
if (typeof compact === 'string') {
enc = compact;
compact = null;
}

if (!this.pub)
this.pub = this.ec.g.mul(this.priv);

if (!enc)
return this.pub;

var len = this.ec.curve.p.byteLength();
var x = this.pub.getX().toArray();

for (var i = x.length; i < len; i++)
x.unshift(0);

var res;
if (this.ec.curve.type !== 'mont') {
if (compact) {
res = [ this.pub.getY().isEven() ? 0x02 : 0x03 ].concat(x);
} else {
var y = this.pub.getY().toArray();
for (var i = y.length; i < len; i++)
y.unshift(0);
var res = [ 0x04 ].concat(x, y);
}
} else {
res = x;
}

return utils.encode(res, enc);
return this.pub.encode(compact, enc);
};

KeyPair.prototype.getPrivate = function getPrivate(enc) {
Expand All @@ -107,27 +84,7 @@ KeyPair.prototype._importPublic = function _importPublic(key, enc) {
this.pub = this.ec.curve.point(key.x, key.y);
return;
}

key = utils.toArray(key, enc);
if (this.ec.curve.type !== 'mont')
return this._importPublicShort(key);
else
return this._importPublicMont(key);
};

KeyPair.prototype._importPublicShort = function _importPublicShort(key) {
var len = this.ec.curve.p.byteLength();
if (key[0] === 0x04 && key.length - 1 === 2 * len) {
this.pub = this.ec.curve.point(
key.slice(1, 1 + len),
key.slice(1 + len, 1 + 2 * len));
} else if ((key[0] === 0x02 || key[0] === 0x03) && key.length - 1 === len) {
this.pub = this.ec.curve.pointFromX(key.slice(1, 1 + len), key[0] === 0x03);
}
};

KeyPair.prototype._importPublicMont = function _importPublicMont(key) {
this.pub = this.ec.curve.point(key, 1);
this.pub = this.ec.curve.decodePoint(key, enc);
};

// ECDH
Expand Down
8 changes: 8 additions & 0 deletions lib/elliptic/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,3 +177,11 @@ function intToLE(num, padTo) {
return bytes;
}
utils.intToLE = intToLE;

function intToBE(num, padTo) {
var bytes = num.toArray();
while (bytes.length < padTo)
bytes.unshift(0);
return bytes;
}
utils.intToBE = intToBE;
84 changes: 84 additions & 0 deletions test/curve-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,3 +162,87 @@ describe('Curve', function() {
assert(p.eq(neg2));
});
});

describe('Point codec', function () {
function makeShortTest(definition) {
var curve = elliptic.curves.secp256k1.curve;

return function() {
var co = definition.coordinates;
var p = curve.point(co.x, co.y);

// Encodes as expected
assert.equal(p.encode('hex'), definition.encoded);
assert.equal(p.encodeCompressed('hex'), definition.compactEncoded);

// Decodes as expected
assert(curve.decodePoint(definition.encoded, 'hex').eq(p));
assert(curve.decodePoint(definition.compactEncoded, 'hex').eq(p));
}
}

function makeMontTest(definition) {
var curve = elliptic.curves.curve25519.curve;

return function() {
var co = definition.coordinates;
var p = curve.point(co.x, co.z);
var encoded = p.encode('hex');
var decoded = curve.decodePoint(encoded, 'hex');
assert(decoded.eq(p));
assert.equal(encoded, definition.encoded);
}
}

var shortPointEvenY = {
coordinates: {
x: "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
y: "483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8",
},
compactEncoded:
"02" +
"79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
encoded:
"04" +
"79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" +
"483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8"
};

var shortPointOddY = {
coordinates: {
x: "fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556",
y: "ae12777aacfbb620f3be96017f45c560de80f0f6518fe4a03c870c36b075f297",
},
compactEncoded:
"03" +
"fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556",
encoded:
"04" +
"fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556" +
"ae12777aacfbb620f3be96017f45c560de80f0f6518fe4a03c870c36b075f297"
};

it('should throw when trying to decode random bytes', function() {
assert.throws(function() {
shortCurve.decodePoint(
'05' +
'79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798')
});
});

it('should be able to encode/decode a short curve point with even Y',
makeShortTest(shortPointEvenY));

it('should be able to encode/decode a short curve point with odd Y',
makeShortTest(shortPointOddY));

it('should be able to encode/decode a mont curve point', makeMontTest({
coordinates: {
// curve25519.curve.g.mul(new bn('6')).getX().toString(16, 2)
x: "26954ccdc99ebf34f8f1dde5e6bb080685fec73640494c28f9fe0bfa8c794531",
z: "1"
},
encoded:
"26954ccdc99ebf34f8f1dde5e6bb080685fec73640494c28f9fe0bfa8c794531"
}));
})

0 comments on commit 890e34b

Please sign in to comment.