recover public key from signature
This commit is contained in:
parent
65c3545cb6
commit
ca7fdd77c1
96
lib/ecdsa.js
96
lib/ecdsa.js
|
@ -1,5 +1,5 @@
|
|||
var bn = require('./bn');
|
||||
var point = require('./point');
|
||||
var BN = require('./bn');
|
||||
var Point = require('./point');
|
||||
var Signature = require('./signature');
|
||||
var Key = require('./key');
|
||||
var Privkey = require('./privkey');
|
||||
|
@ -15,29 +15,93 @@ var ECDSA = function ECDSA(hash, key, sig, k) {
|
|||
this.k = k;
|
||||
};
|
||||
|
||||
ECDSA.prototype.calci = function() {
|
||||
for (var i = 0; i < 4; i++) {
|
||||
this.sig.i = i;
|
||||
try {
|
||||
var Qprime = this.sig2pubkey();
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Qprime.point.eq(this.key.pubkey.point)) {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
this.sig.i = undefined;
|
||||
throw new Error('Unable to find valid recovery factor');
|
||||
};
|
||||
|
||||
ECDSA.prototype.fromString = function(str) {
|
||||
var obj = JSON.parse(str);
|
||||
if (obj.hash)
|
||||
this.hash = new Buffer(obj.hash, 'hex');
|
||||
if (obj.key)
|
||||
this.key = (new Key()).fromString(obj.key);
|
||||
this.key = Key().fromString(obj.key);
|
||||
if (obj.sig)
|
||||
this.sig = (new Signature()).fromString(obj.sig);
|
||||
this.sig = Signature().fromString(obj.sig);
|
||||
if (obj.k)
|
||||
this.k = bn(obj.k, 10);
|
||||
this.k = BN(obj.k, 10);
|
||||
return this;
|
||||
};
|
||||
|
||||
ECDSA.prototype.randomK = function() {
|
||||
var N = point.getN();
|
||||
var N = Point.getN();
|
||||
var k;
|
||||
do {
|
||||
k = bn.fromBuffer(Random.getRandomBuffer(32));
|
||||
k = BN().fromBuffer(Random.getRandomBuffer(32));
|
||||
} while (!(k.lt(N) && k.gt(0)));
|
||||
this.k = k;
|
||||
return this;
|
||||
};
|
||||
|
||||
ECDSA.prototype.sig2pubkey = function() {
|
||||
var i = this.sig.i;
|
||||
if (!(i === 0 || i === 1 || i === 2 || i === 3))
|
||||
throw new Error('signature: i must be equal to 0, 1, 2, or 3');
|
||||
|
||||
var e = BN().fromBuffer(this.hash);
|
||||
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);
|
||||
|
||||
var pubkey = new Pubkey(Q);
|
||||
pubkey.validate();
|
||||
|
||||
return pubkey;
|
||||
};
|
||||
|
||||
ECDSA.prototype.sigError = function() {
|
||||
if (!Buffer.isBuffer(this.hash) || this.hash.length !== 32)
|
||||
return 'Invalid hash';
|
||||
|
@ -46,21 +110,21 @@ ECDSA.prototype.sigError = function() {
|
|||
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())))
|
||||
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 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.point, u2);
|
||||
var p = Point.getG().mulAdd(u1, this.key.pubkey.point, u2);
|
||||
if (p.isInfinity())
|
||||
return 'p is infinity';
|
||||
|
||||
|
@ -79,9 +143,9 @@ ECDSA.prototype.sign = function() {
|
|||
if (!hash || !privkey || !k || !d)
|
||||
throw new Error('ecdsa: invalid parameters');
|
||||
|
||||
var N = point.getN();
|
||||
var G = point.getG();
|
||||
var e = bn(hash);
|
||||
var N = Point.getN();
|
||||
var G = Point.getG();
|
||||
var e = BN().fromBuffer(hash);
|
||||
|
||||
do {
|
||||
var Q = G.mul(k);
|
||||
|
|
|
@ -4,7 +4,7 @@ var Key = require('../lib/key');
|
|||
var Privkey = require('../lib/privkey');
|
||||
var Pubkey = require('../lib/pubkey');
|
||||
var Signature = require('../lib/signature');
|
||||
var bn = require('../lib/bn');
|
||||
var BN = require('../lib/bn');
|
||||
var point = require('../lib/point');
|
||||
var should = require('chai').should();
|
||||
|
||||
|
@ -17,9 +17,36 @@ describe("ECDSA", function() {
|
|||
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'))));
|
||||
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('#calci', function() {
|
||||
|
||||
it('should calculate i', function() {
|
||||
ecdsa.randomK();
|
||||
ecdsa.sign();
|
||||
ecdsa.calci();
|
||||
should.exist(ecdsa.sig.i);
|
||||
});
|
||||
|
||||
it('should calulate this known i', function() {
|
||||
var hash = Hash.sha256(new Buffer('some data'));
|
||||
var r = BN('71706645040721865894779025947914615666559616020894583599959600180037551395766', 10);
|
||||
var s = BN('109412465507152403114191008482955798903072313614214706891149785278625167723646', 10);
|
||||
var ecdsa = new ECDSA();
|
||||
ecdsa.key = new Key();
|
||||
ecdsa.key.privkey = Privkey();
|
||||
ecdsa.key.privkey.bn = BN().fromBuffer(Hash.sha256(new Buffer('test')));
|
||||
ecdsa.key.privkey2pubkey();
|
||||
ecdsa.hash = hash;
|
||||
ecdsa.sig = new Signature(r, s);
|
||||
|
||||
ecdsa.calci();
|
||||
ecdsa.sig.i.should.equal(1);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('#fromString', function() {
|
||||
|
||||
|
@ -46,12 +73,24 @@ describe("ECDSA", function() {
|
|||
it('should generate a random k that is (almost always) greater than this relatively small number', function() {
|
||||
ecdsa.randomK();
|
||||
var k1 = ecdsa.k;
|
||||
var k2 = bn(Math.pow(2, 32)).mul(bn(Math.pow(2, 32))).mul(bn(Math.pow(2, 32)));
|
||||
var k2 = BN(Math.pow(2, 32)).mul(BN(Math.pow(2, 32))).mul(BN(Math.pow(2, 32)));
|
||||
k2.gt(k1).should.equal(false);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('#sig2pubkey', function() {
|
||||
|
||||
it('should calculate the correct public key', function() {
|
||||
ecdsa.k = BN('114860389168127852803919605627759231199925249596762615988727970217268189974335', 10);
|
||||
ecdsa.sign();
|
||||
ecdsa.sig.i = 1;
|
||||
var pubkey = ecdsa.sig2pubkey();
|
||||
pubkey.point.eq(ecdsa.key.pubkey.point).should.equal(true);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('#sigError', function() {
|
||||
|
||||
it('should return an error if the hash is invalid', function() {
|
||||
|
@ -73,15 +112,15 @@ describe("ECDSA", function() {
|
|||
ecdsa.key = new Key();
|
||||
ecdsa.key.pubkey = pk;
|
||||
ecdsa.sig = new Signature();
|
||||
ecdsa.sig.r = bn(0);
|
||||
ecdsa.sig.s = bn(0);
|
||||
ecdsa.sig.r = BN(0);
|
||||
ecdsa.sig.s = BN(0);
|
||||
ecdsa.sigError().should.equal("r and s not in range");
|
||||
});
|
||||
|
||||
it('should return an error if the signature is incorrect', function() {
|
||||
ecdsa.sig = new Signature();
|
||||
ecdsa.sig.fromString('3046022100e9915e6236695f093a4128ac2a956c40ed971531de2f4f41ba05fac7e2bd019c02210094e6a4a769cc7f2a8ab3db696c7cd8d56bcdbfff860a8c81de4bc6a798b90827');
|
||||
ecdsa.sig.r = ecdsa.sig.r.add(bn(1));
|
||||
ecdsa.sig.r = ecdsa.sig.r.add(BN(1));
|
||||
ecdsa.sigError().should.equal("Invalid signature");
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in New Issue