bitcore/lib/crypto/ecdsa.js

217 lines
5.2 KiB
JavaScript
Raw Normal View History

2014-08-19 17:15:54 -07:00
var BN = require('./bn');
var Point = require('./point');
2014-08-09 17:43:24 -07:00
var Signature = require('./signature');
var Keypair = require('./keypair');
2014-08-09 17:43:24 -07:00
var Pubkey = require('./pubkey');
var Random = require('./random');
2014-08-28 16:07:28 -07:00
var ECDSA = function ECDSA(obj) {
if (!(this instanceof ECDSA))
2014-08-28 16:07:28 -07:00
return new ECDSA(obj);
if (obj)
this.set(obj);
};
ECDSA.prototype.set = function(obj) {
2014-09-16 10:10:06 -07:00
this.hashbuf = obj.hashbuf || this.hashbuf;
this.keypair = obj.keypair || this.keypair;
this.sig = obj.sig || this.sig;
this.k = obj.k || this.k;
this.verified = obj.verified || this.verified;
2014-08-28 16:07:28 -07:00
return this;
2014-08-09 17:43:24 -07:00
};
2014-08-19 17:15:54 -07:00
ECDSA.prototype.calci = function() {
for (var i = 0; i < 4; i++) {
this.sig.i = i;
try {
var Qprime = this.sig2pubkey();
} catch (e) {
continue;
}
if (Qprime.point.eq(this.keypair.pubkey.point)) {
this.sig.compressed = this.keypair.pubkey.compressed;
2014-08-19 17:15:54 -07:00
return this;
}
}
this.sig.i = undefined;
throw new Error('Unable to find valid recovery factor');
};
2014-08-09 19:42:25 -07:00
ECDSA.prototype.fromString = function(str) {
var obj = JSON.parse(str);
2014-08-20 10:46:01 -07:00
if (obj.hashbuf)
this.hashbuf = new Buffer(obj.hashbuf, 'hex');
if (obj.keypair)
this.keypair = Keypair().fromString(obj.keypair);
2014-08-09 19:42:25 -07:00
if (obj.sig)
2014-08-19 17:15:54 -07:00
this.sig = Signature().fromString(obj.sig);
2014-08-09 19:42:25 -07:00
if (obj.k)
2014-08-19 17:15:54 -07:00
this.k = BN(obj.k, 10);
2014-08-09 19:42:25 -07:00
return this;
};
ECDSA.prototype.randomK = function() {
2014-08-19 17:15:54 -07:00
var N = Point.getN();
2014-08-09 19:42:25 -07:00
var k;
do {
2014-08-19 17:15:54 -07:00
k = BN().fromBuffer(Random.getRandomBuffer(32));
2014-08-09 19:42:25 -07:00
} while (!(k.lt(N) && k.gt(0)));
this.k = k;
return this;
};
2014-08-24 14:26:17 -07:00
// Information about public key recovery:
// https://bitcointalk.org/index.php?topic=6430.0
// http://stackoverflow.com/questions/19665491/how-do-i-get-an-ecdsa-public-key-from-just-a-bitcoin-signature-sec1-4-1-6-k
2014-08-19 17:15:54 -07:00
ECDSA.prototype.sig2pubkey = function() {
var i = this.sig.i;
if (!(i === 0 || i === 1 || i === 2 || i === 3))
2014-08-24 12:50:21 -07:00
throw new Error('i must be equal to 0, 1, 2, or 3');
2014-08-19 17:15:54 -07:00
2014-08-20 10:46:01 -07:00
var e = BN().fromBuffer(this.hashbuf);
2014-08-19 17:15:54 -07:00
var r = this.sig.r;
var s = this.sig.s;
// A set LSB signifies that the y-coordinate is odd
var isYOdd = i & 1;
// The more significant bit specifies whether we should use the
// first or second candidate key.
var isSecondKey = i >> 1;
var n = Point.getN();
var G = Point.getG();
// 1.1 Let x = r + jn
var x = isSecondKey ? r.add(n) : r;
var R = Point.fromX(isYOdd, x);
// 1.4 Check that nR is at infinity
var nR = R.mul(n);
if (!nR.isInfinity())
throw new Error('nR is not a valid curve point');
// Compute -e from e
var eNeg = e.neg().mod(n);
// 1.6.1 Compute Q = r^-1 (sR - eG)
// Q = r^-1 (sR + -eG)
var rInv = r.invm(n);
//var Q = R.multiplyTwo(s, G, eNeg).mul(rInv);
var Q = R.mul(s).add(G.mul(eNeg)).mul(rInv);
2014-08-28 17:41:38 -07:00
var pubkey = new Pubkey({point: Q});
2014-08-22 19:43:32 -07:00
pubkey.compressed = this.sig.compressed;
2014-08-19 17:15:54 -07:00
pubkey.validate();
return pubkey;
};
2014-08-09 17:43:24 -07:00
ECDSA.prototype.sigError = function() {
2014-08-20 10:46:01 -07:00
if (!Buffer.isBuffer(this.hashbuf) || this.hashbuf.length !== 32)
2014-09-16 11:44:51 -07:00
return 'hashbuf must be a 32 byte buffer';
2014-08-09 17:43:24 -07:00
try {
this.keypair.pubkey.validate();
2014-08-09 17:43:24 -07:00
} catch (e) {
return 'Invalid pubkey: ' + e;
2014-08-19 17:15:54 -07:00
}
2014-08-09 17:43:24 -07:00
var r = this.sig.r;
var s = this.sig.s;
2014-08-19 17:15:54 -07:00
if (!(r.gt(0) && r.lt(Point.getN()))
|| !(s.gt(0) && s.lt(Point.getN())))
2014-08-09 17:43:24 -07:00
return 'r and s not in range';
2014-08-20 10:46:01 -07:00
var e = BN().fromBuffer(this.hashbuf);
2014-08-19 17:15:54 -07:00
var n = Point.getN();
2014-08-09 17:43:24 -07:00
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.keypair.pubkey.point, u2);
2014-08-09 17:43:24 -07:00
if (p.isInfinity())
return 'p is infinity';
if (!(p.getX().mod(n).cmp(r) === 0))
return 'Invalid signature';
else
return false;
};
ECDSA.prototype.sign = function() {
2014-08-20 10:46:01 -07:00
var hashbuf = this.hashbuf;
var privkey = this.keypair.privkey;
2014-08-09 17:43:24 -07:00
var k = this.k;
2014-08-13 12:23:06 -07:00
var d = privkey.bn;
2014-08-09 17:43:24 -07:00
2014-08-21 15:50:38 -07:00
if (!k)
throw new Error('You must specify k - perhaps you should run signRandomK instead');
if (!hashbuf || !privkey || !d)
throw new Error('invalid parameters');
2014-08-09 17:43:24 -07:00
2014-09-16 11:33:49 -07:00
if (!Buffer.isBuffer(hashbuf) || hashbuf.length !== 32)
throw new Error('hashbuf must be a 32 byte buffer');
2014-08-19 17:15:54 -07:00
var N = Point.getN();
var G = Point.getG();
2014-08-20 10:46:01 -07:00
var e = BN().fromBuffer(hashbuf);
2014-08-09 17:43:24 -07:00
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: r, s: s, compressed: this.keypair.pubkey.compressed});
2014-08-09 17:43:24 -07:00
return this.sig;
};
ECDSA.prototype.signRandomK = function() {
var k = this.randomK();
return this.sign();
};
2014-08-09 19:42:25 -07:00
ECDSA.prototype.toString = function() {
var obj = {};
2014-08-20 10:46:01 -07:00
if (this.hashbuf)
obj.hashbuf = this.hashbuf.toString('hex');
if (this.keypair)
obj.keypair = this.keypair.toString();
2014-08-09 19:42:25 -07:00
if (this.sig)
obj.sig = this.sig.toString();
if (this.k)
obj.k = this.k.toString();
return JSON.stringify(obj);
};
2014-08-09 17:43:24 -07:00
ECDSA.prototype.verify = function() {
if (!this.sigError())
return true;
else
return false;
};
2014-09-16 11:34:28 -07:00
ECDSA.sign = function(hashbuf, keypair) {
return ECDSA().set({
hashbuf: hashbuf,
keypair: keypair
}).signRandomK();
};
ECDSA.verify = function(hashbuf, sig, pubkey) {
return ECDSA().set({
hashbuf: hashbuf,
sig: sig,
keypair: Keypair().set({pubkey: pubkey})
}).verify();
};
2014-08-09 17:43:24 -07:00
module.exports = ECDSA;