This commit is contained in:
Ryan X. Charles 2014-08-09 17:43:24 -07:00
parent 1a01674532
commit c4064cc6e1
9 changed files with 255 additions and 48 deletions

View File

@ -13,6 +13,7 @@ privsec.base58 = require('./lib/base58');
privsec.base58check = require('./lib/base58check');
privsec.bn = require('./lib/bn');
privsec.constants = require('./lib/constants');
privsec.ecdsa = require('./lib/ecdsa');
privsec.hash = require('./lib/hash');
privsec.key = require('./lib/key');
privsec.point = require('./lib/point');

92
lib/ecdsa.js Normal file
View File

@ -0,0 +1,92 @@
var bn = require('./bn');
var point = require('./point');
var Signature = require('./signature');
var Privkey = require('./privkey');
var Pubkey = require('./pubkey');
var Random = require('./random');
var ECDSA = function(hash, key, sig, k) {
this.hash = hash;
this.key = key;
this.sig = sig;
this.k = k;
};
ECDSA.prototype.sigError = function() {
if (!Buffer.isBuffer(this.hash) || this.hash.length !== 32)
return 'Invalid hash';
try {
this.key.pubkey.validate();
} catch (e) {
return 'Invalid pubkey: ' + e;
};
var r = this.sig.r;
var s = this.sig.s;
if (!(r.gt(0) && r.lt(point.getN()))
|| !(s.gt(0) && s.lt(point.getN())))
return 'r and s not in range';
var e = bn.fromBuffer(this.hash);
var n = point.getN();
var sinv = s.invm(n);
var u1 = sinv.mul(e).mod(n);
var u2 = sinv.mul(r).mod(n);
var p = point.getG().mulAdd(u1, this.key.pubkey.p, u2);
if (p.isInfinity())
return 'p is infinity';
if (!(p.getX().mod(n).cmp(r) === 0))
return 'Invalid signature';
else
return false;
};
ECDSA.prototype.randomK = function() {
var N = point.getN();
var k;
do {
k = bn.fromBuffer(Random.getRandomBuffer(32));
} while (!(k.lt(N) && k.gt(0)));
this.k = k;
return this;
};
ECDSA.prototype.sign = function() {
var hash = this.hash;
var privkey = this.key.privkey;
var k = this.k;
var d = privkey.n;
if (!hash || !privkey || !k || !d)
throw new Error('ecdsa: invalid parameters');
var N = point.getN();
var G = point.getG();
var e = bn(hash);
do {
var Q = G.mul(k);
var r = Q.x.mod(N);
var s = k.invm(N).mul(e.add(d.mul(r))).mod(N);
} while (r.cmp(0) <= 0 || s.cmp(0) <= 0);
this.sig = new Signature(r, s);
return this.sig;
};
ECDSA.prototype.signRandomK = function() {
var k = this.randomK();
return this.sign();
};
ECDSA.prototype.verify = function() {
if (!this.sigError())
return true;
else
return false;
};
module.exports = ECDSA;

View File

@ -4,42 +4,42 @@ var Random = require('./random');
var bn = require('./bn');
var point = require('./point');
function Key(priv, pub) {
this.priv = priv;
this.pub = pub;
function Key(privkey, pubkey) {
this.privkey = privkey;
this.pubkey = pubkey;
};
Key.prototype.fromRandom = function() {
do {
var privbuf = Random.getRandomBuffer(32);
this.priv = new Privkey(bn(privbuf));
var condition = this.priv.n.lt(point.getN());
this.privkey = new Privkey(bn(privbuf));
var condition = this.privkey.n.lt(point.getN());
} while (!condition);
this.priv2pub();
this.privkey2pubkey();
};
Key.prototype.fromString = function(str) {
var obj = JSON.parse(str);
if (obj.priv) {
this.priv = new Privkey();
this.priv.fromString(obj.priv);
this.privkey = new Privkey();
this.privkey.fromString(obj.priv);
}
if (obj.pub) {
this.pub = new Pubkey();
this.pub.fromString(obj.pub);
this.pubkey = new Pubkey();
this.pubkey.fromString(obj.pub);
}
};
Key.prototype.priv2pub = function() {
this.pub = new Pubkey(point.getG().mul(this.priv.n));
Key.prototype.privkey2pubkey = function() {
this.pubkey = new Pubkey(point.getG().mul(this.privkey.n));
};
Key.prototype.toString = function() {
var obj = {};
if (this.priv)
obj.priv = this.priv.toString();
if (this.pub)
obj.pub = this.pub.toString();
if (this.privkey)
obj.priv = this.privkey.toString();
if (this.pubkey)
obj.pub = this.pubkey.toString();
return JSON.stringify(obj);
};

View File

@ -29,4 +29,17 @@ Point.prototype.getY = function() {
return bn(this._getY().toArray());
};
//https://www.iacr.org/archive/pkc2003/25670211/25670211.pdf
Point.prototype.validate = function() {
var p2 = Point.fromX(this.getY().isOdd(), this.getX());
if (!(p2.y.cmp(this.y) === 0))
throw new Error('point: Invalid y value of public key');
if (!(this.getX().gt(-1) && this.getX().lt(Point.getN()))
||!(this.getY().gt(-1) && this.getY().lt(Point.getN())))
throw new Error('point: Point does not lie on the curve');
if (!(this.mul(Point.getN()).isInfinity()))
throw new Error('point: Point times N must be infinity');
return this;
};
module.exports = Point;

View File

@ -2,6 +2,8 @@ var point = require('./point');
var bn = require('./bn');
var Pubkey = function(p) {
if (p && !p.getX() && !p.getY())
throw new Error('pubkey: Invalid point');
this.p = p;
};
@ -64,4 +66,14 @@ Pubkey.prototype.toString = function() {
return this.toDER(true).toString('hex');
};
//https://www.iacr.org/archive/pkc2003/25670211/25670211.pdf
Pubkey.prototype.validate = function() {
if (this.p.isInfinity())
throw new Error('point: Point cannot be equal to Infinity');
if (this.p.eq(point(bn(0), bn(0))))
throw new Error('point: Point cannot be equal to 0, 0');
this.p.validate();
return this;
};
module.exports = Pubkey;

41
test/test.ecdsa.js Normal file
View File

@ -0,0 +1,41 @@
var ECDSA = require('../lib/ecdsa');
var Hash = require('../lib/hash');
var Key = require('../lib/key');
var Privkey = require('../lib/privkey');
var Pubkey = require('../lib/pubkey');
var bn = require('../lib/bn');
var point = require('../lib/point');
var should = require('chai').should();
describe("ecdsa", function() {
it('should create a blank ecdsa', function() {
var ecdsa = new ECDSA();
});
var ecdsa = new ECDSA();
ecdsa.hash = Hash.sha256(new Buffer('test data'));
ecdsa.key = new Key();
ecdsa.key.privkey = new Privkey(bn.fromBuffer(new Buffer('fee0a1f7afebf9d2a5a80c0c98a31c709681cce195cbcd06342b517970c0be1e', 'hex')));
ecdsa.key.pubkey = new Pubkey(point(bn.fromBuffer(new Buffer('ac242d242d23be966085a2b2b893d989f824e06c9ad0395a8a52f055ba39abb2', 'hex')),
bn.fromBuffer(new Buffer('4836ab292c105a711ed10fcfd30999c31ff7c02456147747e03e739ad527c380', 'hex'))));
describe('#signRandomK', function() {
it('should produce a signature', function() {
ecdsa.signRandomK();
should.exist(ecdsa.sig);
});
});
describe('#verify', function() {
it('should verify a signature that was just signed', function() {
ecdsa.signRandomK();
ecdsa.verify().should.equal(true);
});
});
});

View File

@ -1,8 +1,8 @@
var should = require('chai').should();
var bn = require('../lib/bn');
var point = require('../lib/point');
var privkey = require('../lib/privkey');
var pubkey = require('../lib/pubkey');
var Privkey = require('../lib/privkey');
var Pubkey = require('../lib/pubkey');
var Key = require('../lib/key');
describe('key', function() {
@ -13,12 +13,12 @@ describe('key', function() {
});
it('should make a key with a priv and pub', function() {
var priv = new privkey();
var pub = new pubkey();
var priv = new Privkey();
var pub = new Pubkey();
var key = new Key(priv, pub);
should.exist(key);
should.exist(key.priv);
should.exist(key.pub);
should.exist(key.privkey);
should.exist(key.pubkey);
});
describe("#fromRandom", function() {
@ -26,11 +26,11 @@ describe('key', function() {
it('should make a new priv and pub', function() {
var key = new Key();
key.fromRandom();
should.exist(key.priv);
should.exist(key.pub);
key.priv.n.gt(bn(0)).should.equal(true);
key.pub.p.getX().gt(bn(0)).should.equal(true);
key.pub.p.getY().gt(bn(0)).should.equal(true);
should.exist(key.privkey);
should.exist(key.pubkey);
key.privkey.n.gt(bn(0)).should.equal(true);
key.pubkey.p.getX().gt(bn(0)).should.equal(true);
key.pubkey.p.getY().gt(bn(0)).should.equal(true);
});
});
@ -40,49 +40,49 @@ describe('key', function() {
it('should recover a key creating with toString', function() {
var key = new Key();
key.fromRandom();
var priv = key.priv;
var pub = key.pub;
var priv = key.privkey;
var pub = key.pubkey;
var str = key.toString();
key.fromString(str);
should.exist(key.priv);
should.exist(key.pub);
key.priv.toString().should.equal(priv.toString());
key.pub.toString().should.equal(pub.toString());
should.exist(key.privkey);
should.exist(key.pubkey);
key.privkey.toString().should.equal(priv.toString());
key.pubkey.toString().should.equal(pub.toString());
});
it('should work with only privkey set', function() {
it('should work with only Privkey set', function() {
var key = new Key();
key.fromRandom();
key.pub = undefined;
var priv = key.priv;
key.pubkey = undefined;
var priv = key.privkey;
var str = key.toString();
key.fromString(str);
should.exist(key.priv);
key.priv.toString().should.equal(priv.toString());
should.exist(key.privkey);
key.privkey.toString().should.equal(priv.toString());
});
it('should work with only pubkey set', function() {
it('should work with only Pubkey set', function() {
var key = new Key();
key.fromRandom();
key.priv = undefined;
var pub = key.pub;
key.privkey = undefined;
var pub = key.pubkey;
var str = key.toString();
key.fromString(str);
should.exist(key.pub);
key.pub.toString().should.equal(pub.toString());
should.exist(key.pubkey);
key.pubkey.toString().should.equal(pub.toString());
});
});
describe("#priv2pub", function() {
describe("#privkey2pubkey", function() {
it('should convert this known privkey to known pubkey', function() {
it('should convert this known Privkey to known Pubkey', function() {
var privhex = '906977a061af29276e40bf377042ffbde414e496ae2260bbf1fa9d085637bfff';
var pubhex = '02a1633cafcc01ebfb6d78e39f687a1f0995c62fc95f51ead10a02ee0be551b5dc';
var key = new Key();
key.priv = new privkey(bn(new Buffer(privhex, 'hex')));
key.priv2pub();
key.pub.toString().should.equal(pubhex);
key.privkey = new Privkey(bn(new Buffer(privhex, 'hex')));
key.privkey2pubkey();
key.pubkey.toString().should.equal(pubhex);
});
});

View File

@ -89,4 +89,24 @@ describe('point', function() {
});
describe('#validate', function() {
it('should validate this valid point', function() {
var x = bn.fromBuffer(new Buffer('ac242d242d23be966085a2b2b893d989f824e06c9ad0395a8a52f055ba39abb2', 'hex'));
var y = bn.fromBuffer(new Buffer('4836ab292c105a711ed10fcfd30999c31ff7c02456147747e03e739ad527c380', 'hex'));
var p = point(x, y);
should.exist(p.validate());
});
it('should invalidate this invalid point', function() {
var x = bn.fromBuffer(new Buffer('ac242d242d23be966085a2b2b893d989f824e06c9ad0395a8a52f055ba39abb2', 'hex'));
var y = bn.fromBuffer(new Buffer('0000000000000000000000000000000000000000000000000000000000000000', 'hex'));
var p = point(x, y);
(function() {
p.validate();
}).should.throw('point: Invalid y value of public key');
});
});
});

View File

@ -93,4 +93,32 @@ describe('pubkey', function() {
});
describe('#validate', function() {
it('should not throw an error if pubkey is valid', function() {
var hex = '031ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a';
var pk = new pubkey();
pk.fromString(hex);
should.exist(pk.validate());
});
it('should not throw an error if pubkey is invalid', function() {
var hex = '041ff0fe0f7b15ffaa85ff9f4744d539139c252a49710fb053bb9f2b933173ff9a0000000000000000000000000000000000000000000000000000000000000000';
var pk = new pubkey();
pk.fromString(hex);
(function() {
pk.validate();
}).should.throw('point: Invalid y value of public key');
});
it('should not throw an error if pubkey is infinity', function() {
var pk = new pubkey();
pk.p = point.getG().mul(point.getN());
(function() {
pk.validate();
}).should.throw('point: Point cannot be equal to Infinity');
});
});
});