diff --git a/lib/ecdsa.js b/lib/ecdsa.js index 6e2a630..5c2fadb 100644 --- a/lib/ecdsa.js +++ b/lib/ecdsa.js @@ -97,6 +97,7 @@ ECDSA.prototype.sig2pubkey = function() { var Q = R.mul(s).add(G.mul(eNeg)).mul(rInv); var pubkey = new Pubkey(Q); + pubkey.compressed = this.sig.compressed; pubkey.validate(); return pubkey; @@ -156,7 +157,7 @@ ECDSA.prototype.sign = function() { 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); + this.sig = new Signature(r, s, undefined, this.key.pubkey.compressed); return this.sig; }; diff --git a/lib/message.js b/lib/message.js index a3b8018..126a241 100644 --- a/lib/message.js +++ b/lib/message.js @@ -65,7 +65,7 @@ Message.prototype.verify = function() { return this; } - var address = Address().fromPubkey(ecdsa.key.pubkey); + var address = Address().fromPubkey(ecdsa.key.pubkey, undefined, this.sig.compressed); //TODO: what if livenet/testnet mismatch? if (address.hashbuf.toString('hex') === this.address.hashbuf.toString('hex')) this.verified = true; diff --git a/lib/signature.js b/lib/signature.js index 965bcaa..775d9b4 100644 --- a/lib/signature.js +++ b/lib/signature.js @@ -2,17 +2,25 @@ var BN = require('./bn'); var Point = require('./point'); var Pubkey = require('./pubkey'); -var Signature = function Signature(r, s, i) { +var Signature = function Signature(r, s, i, compressed) { if (!(this instanceof Signature)) - return new Signature(r, s, i); + return new Signature(r, s, i, compressed); this.r = r; this.s = s; this.i = i; //public key recovery parameter in range [0, 3] + this.compressed = compressed; }; Signature.prototype.fromCompact = function(buf) { + var compressed = true; + if (i < 0) { + var compressed = false; + i = i + 4; + } + var i = buf.slice(0, 1)[0] - 27 - 4; //TODO: handle uncompressed pubkeys + var b2 = buf.slice(1, 33); var b3 = buf.slice(33, 65); @@ -23,6 +31,7 @@ Signature.prototype.fromCompact = function(buf) { if (b3.length !== 32) throw new Error('s must be 32 bytes'); + this.compressed = compressed; this.i = i; this.r = BN().fromBuffer(b2); this.s = BN().fromBuffer(b3); @@ -102,12 +111,17 @@ Signature.parseDER = function(buf) { return obj; }; -Signature.prototype.toCompact = function(i) { +Signature.prototype.toCompact = function(i, compressed) { i = typeof i === 'number' ? i : this.i; + compressed = typeof compressed === 'boolean' ? compressed : this.compressed; + if (!(i === 0 || i === 1 || i === 2 || i === 3)) throw new Error('i must be equal to 0, 1, 2, or 3'); - var b1 = new Buffer([i + 27 + 4]); //TODO: handle uncompressed pubkeys + var val = i + 27 + 4; + if (compressed === false) + val = val - 4; + var b1 = new Buffer([val]); var b2 = this.r.toBuffer({size: 32}); var b3 = this.s.toBuffer({size: 32}); return Buffer.concat([b1, b2, b3]); diff --git a/test/test.message.js b/test/test.message.js index 00e7bff..91b1123 100644 --- a/test/test.message.js +++ b/test/test.message.js @@ -56,6 +56,24 @@ describe('Message', function() { sigbuf.length.should.equal(1 + 32 + 32); }); + it('should sign with a compressed pubkey', function() { + var key = Key().fromRandom(); + key.pubkey.compressed = true; + var sigstr = Message.sign(messagebuf, key); + var sigbuf = new Buffer(sigstr, 'base64'); + sigbuf[0].should.be.above(27 + 4 - 1); + sigbuf[0].should.be.below(27 + 4 + 4 - 1); + }); + + it('should sign with an uncompressed pubkey', function() { + var key = Key().fromRandom(); + key.pubkey.compressed = false; + var sigstr = Message.sign(messagebuf, key); + var sigbuf = new Buffer(sigstr, 'base64'); + sigbuf[0].should.be.above(27 - 1); + sigbuf[0].should.be.below(27 + 4 - 1); + }); + }); describe('@verify', function() {