From e301a14657711646620841d27ccb926043b112f4 Mon Sep 17 00:00:00 2001 From: Ruben de Vries Date: Fri, 25 Apr 2014 14:27:15 +0200 Subject: [PATCH 1/4] added Script.getMultiSigInfo as easy helper to return information about a multisig script --- lib/Script.js | 24 ++++++++++++++++++++++++ test/test.Script.js | 29 +++++++++++++++++++++++++---- 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/lib/Script.js b/lib/Script.js index 1a4f858..7f86fb9 100644 --- a/lib/Script.js +++ b/lib/Script.js @@ -187,6 +187,30 @@ Script.prototype.finishedMultiSig = function() { return missing === 0; }; +Script.prototype.getMultiSigInfo = function() { + if (!this.isMultiSig()) { + throw new Error("Script.getMultiSigInfo(): Not a multiSig script."); + } + + var nsigs = this.chunks[0] - 80; //see OP_2-OP_16; + var npks = this.chunks[this.chunks.length - 2] - 80; //see OP_2-OP_16; + + var pks = []; + for (var i = 1; i < this.chunks.length - 2; i++) { + pks.push(this.chunks[i]); + } + + if (pks.length != npks) { + throw new Error("Script.getMultiSigInfo(): Amount of PKs does not match what the script specifies."); + } + + return { + nsigs : nsigs, + npks : npks, + pks : pks + } +}; + Script.prototype.prependOp0 = function() { var chunks = [0]; for (i in this.chunks) { diff --git a/test/test.Script.js b/test/test.Script.js index 869476a..0c10089 100644 --- a/test/test.Script.js +++ b/test/test.Script.js @@ -34,6 +34,7 @@ describe('Script', function() { var script = Script.createPubKeyHashOut(addr.payload()); script.isP2SH().should.be.false; }); + describe('#finishedMultiSig', function() { it('should report that this scriptSig has finished being signed', function() { var scriptHex = '00493046022100954d2aa7af9a2de34b04e4151842933df81acc379580cd0c057883cfb0994a8b022100de1530692eda9cdb567c94e05fc856cfbc26fcf3482148bde85f143032f4902501483045022100d164d174118497d93e0062b573be78d4b9417aee09889cd242a966af73367917022054f095be0bce9edee556a2216239fcad45a7a64d8fb318dc5375c9159724689a014c695221033e4cc6b6ee8d8ce3335fed6d4917b2bbbac0f5743b2ced101ba036f95c51e59421023147410ce15e0a31c2bb970cdf01517266dc2c9182f5938636ed363cfd4cc3ae210342a3c8a4b20c7a122a011a07063df04e4c5ad520a1302a2a66e174fd9b0d4ea453ae'; @@ -53,6 +54,26 @@ describe('Script', function() { }); }); + describe('#getMultiSigInfo', function() { + it('should report the expected pubkeys', function() { + // using same test case as used in #createMultisig + // 3 of 5 multisig, unsorted + // test case generated with: bitcoind createmultisig 3 '["02c525d65d18be8fb36ab50a21bee02ac9fdc2c176fa18791ac664ea4b95572ae0", "02b937d54b550a3afdc2819772822d25869495f9e588b56a0205617d80514f0758", "0266dd7664e65958f3cc67bf92ad6243bc495df5ab56691719263977104b635bea","02ee91377073b04d1d9d19597b81a7be3db6554bd7d16151cb5599a6107a589e70", "02c8f63ad4822ef360b5c300f08488fa0fa24af2b2bebb6d6b602ca938ee5af793"]' + var scriptHex = '532102c525d65d18be8fb36ab50a21bee02ac9fdc2c176fa18791ac664ea4b95572ae02102b937d54b550a3afdc2819772822d25869495f9e588b56a0205617d80514f0758210266dd7664e65958f3cc67bf92ad6243bc495df5ab56691719263977104b635bea2102ee91377073b04d1d9d19597b81a7be3db6554bd7d16151cb5599a6107a589e702102c8f63ad4822ef360b5c300f08488fa0fa24af2b2bebb6d6b602ca938ee5af79355ae'; + var scriptBuf = new Buffer(scriptHex, 'hex'); + var script = new Script(scriptBuf); + var info = script.getMultiSigInfo(); + + info.nsigs.should.equal(3); + info.npks.should.equal(5); + + info.pks.length.should.equal(info.npks); + info.pks.map(function(pk) { + testPubKeysHex.indexOf(pk.toString('hex')).should.not.equal(-1); + }); + }); + }); + describe('prependOp0', function() { it('should prepend the script with OP_0', function() { var scriptHex = '483045022002da7fae9b98615115b7e9a4f1d9efd581463b670f91ec6404a14cb6fc9c4531022100ff449d72ba4e72deb4317267e2d38cec9fd2f58a9afa39c9f5e35f5678694ff50100004c695221033e4cc6b6ee8d8ce3335fed6d4917b2bbbac0f5743b2ced101ba036f95c51e59421023147410ce15e0a31c2bb970cdf01517266dc2c9182f5938636ed363cfd4cc3ae210342a3c8a4b20c7a122a011a07063df04e4c5ad520a1302a2a66e174fd9b0d4ea453ae'; @@ -116,14 +137,14 @@ describe('Script', function() { var pubs = testPubKeysHex.map( function(hex) { return new Buffer(hex,'hex'); }); - var s1 = Script.createMultisig(3,pubs, {noSorting: true}); - + // 3 of 5 multisig, unsorted // test case generated with: bitcoind createmultisig 3 '["02c525d65d18be8fb36ab50a21bee02ac9fdc2c176fa18791ac664ea4b95572ae0", "02b937d54b550a3afdc2819772822d25869495f9e588b56a0205617d80514f0758", "0266dd7664e65958f3cc67bf92ad6243bc495df5ab56691719263977104b635bea","02ee91377073b04d1d9d19597b81a7be3db6554bd7d16151cb5599a6107a589e70", "02c8f63ad4822ef360b5c300f08488fa0fa24af2b2bebb6d6b602ca938ee5af793"]' - + var s1 = Script.createMultisig(3,pubs, {noSorting: true}); s1.getBuffer().toString('hex').should.equal('532102c525d65d18be8fb36ab50a21bee02ac9fdc2c176fa18791ac664ea4b95572ae02102b937d54b550a3afdc2819772822d25869495f9e588b56a0205617d80514f0758210266dd7664e65958f3cc67bf92ad6243bc495df5ab56691719263977104b635bea2102ee91377073b04d1d9d19597b81a7be3db6554bd7d16151cb5599a6107a589e702102c8f63ad4822ef360b5c300f08488fa0fa24af2b2bebb6d6b602ca938ee5af79355ae'); - var s2 = Script.createMultisig(3,pubs); + // 3 of 5 multisig, sorted // test case generated with: bitcoind createmultisig 3 '["0266dd7664e65958f3cc67bf92ad6243bc495df5ab56691719263977104b635bea", "02b937d54b550a3afdc2819772822d25869495f9e588b56a0205617d80514f0758", "02c525d65d18be8fb36ab50a21bee02ac9fdc2c176fa18791ac664ea4b95572ae0", "02c8f63ad4822ef360b5c300f08488fa0fa24af2b2bebb6d6b602ca938ee5af793", "02ee91377073b04d1d9d19597b81a7be3db6554bd7d16151cb5599a6107a589e70"]' + var s2 = Script.createMultisig(3,pubs); s2.getBuffer().toString('hex').should.equal('53210266dd7664e65958f3cc67bf92ad6243bc495df5ab56691719263977104b635bea2102b937d54b550a3afdc2819772822d25869495f9e588b56a0205617d80514f07582102c525d65d18be8fb36ab50a21bee02ac9fdc2c176fa18791ac664ea4b95572ae02102c8f63ad4822ef360b5c300f08488fa0fa24af2b2bebb6d6b602ca938ee5af7932102ee91377073b04d1d9d19597b81a7be3db6554bd7d16151cb5599a6107a589e7055ae'); }); From 4860b8f3c7eb3f848f53e5abfed4208b4fecc423 Mon Sep 17 00:00:00 2001 From: Ruben de Vries Date: Fri, 25 Apr 2014 15:02:19 +0200 Subject: [PATCH 2/4] updated naming for getMultiSigInfo --- lib/Script.js | 12 ++++++------ test/test.Script.js | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/Script.js b/lib/Script.js index 7f86fb9..f8684b4 100644 --- a/lib/Script.js +++ b/lib/Script.js @@ -193,21 +193,21 @@ Script.prototype.getMultiSigInfo = function() { } var nsigs = this.chunks[0] - 80; //see OP_2-OP_16; - var npks = this.chunks[this.chunks.length - 2] - 80; //see OP_2-OP_16; + var npubkeys = this.chunks[this.chunks.length - 2] - 80; //see OP_2-OP_16; - var pks = []; + var pubkeys = []; for (var i = 1; i < this.chunks.length - 2; i++) { - pks.push(this.chunks[i]); + pubkeys.push(this.chunks[i]); } - if (pks.length != npks) { + if (pubkeys.length != npubkeys) { throw new Error("Script.getMultiSigInfo(): Amount of PKs does not match what the script specifies."); } return { nsigs : nsigs, - npks : npks, - pks : pks + npubkeys : npubkeys, + pubkeys : pubkeys } }; diff --git a/test/test.Script.js b/test/test.Script.js index 0c10089..3117d03 100644 --- a/test/test.Script.js +++ b/test/test.Script.js @@ -65,11 +65,11 @@ describe('Script', function() { var info = script.getMultiSigInfo(); info.nsigs.should.equal(3); - info.npks.should.equal(5); + info.npubkeys.should.equal(5); - info.pks.length.should.equal(info.npks); - info.pks.map(function(pk) { - testPubKeysHex.indexOf(pk.toString('hex')).should.not.equal(-1); + info.pubkeys.length.should.equal(info.npubkeys); + info.pubkeys.map(function(pubkey) { + testPubKeysHex.indexOf(pubkey.toString('hex')).should.not.equal(-1); }); }); }); From c7b33eca9ab40ddb2afa629e0c6b8c6f0cba2518 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Fri, 25 Apr 2014 15:00:51 -0300 Subject: [PATCH 3/4] update key signing to use new SecureRandom ...and throw an error if you try to use the old, non-secure random number generator. --- browser/vendor/eckey.js | 2 +- browser/vendor/rng.js | 4 ++- lib/browser/Key.js | 64 ++++++++++++++++++++++++++++++++++++++--- lib/browser/Point.js | 1 - 4 files changed, 64 insertions(+), 7 deletions(-) diff --git a/browser/vendor/eckey.js b/browser/vendor/eckey.js index 74e3e45..62b6cd0 100644 --- a/browser/vendor/eckey.js +++ b/browser/vendor/eckey.js @@ -7,7 +7,7 @@ Bitcoin.ECKey = (function () { if (!input) { // Generate new key var n = ecparams.getN(); - this.priv = ECDSA.getBigRandom(n); + //this.priv = ECDSA.getBigRandom(n); } else if (input instanceof BigInteger) { // Input is a private key value this.priv = input; diff --git a/browser/vendor/rng.js b/browser/vendor/rng.js index 03afc3a..77918e5 100644 --- a/browser/vendor/rng.js +++ b/browser/vendor/rng.js @@ -65,4 +65,6 @@ function rng_get_bytes(ba) { function SecureRandom() {} -SecureRandom.prototype.nextBytes = rng_get_bytes; +SecureRandom.prototype.nextBytes = function() { + throw new Error('Should not use old RNG'); +}; diff --git a/lib/browser/Key.js b/lib/browser/Key.js index 259e9a7..e5df264 100644 --- a/lib/browser/Key.js +++ b/lib/browser/Key.js @@ -90,6 +90,62 @@ Key.prototype.regenerateSync = function() { }; Key.prototype.signSync = function(hash) { + var getSECCurveByName = require('../../browser/vendor-bundle.js').getSECCurveByName; + var BigInteger = require('../../browser/vendor-bundle.js').BigInteger; + var rng = new SecureRandom(); + var ecparams = getSECCurveByName('secp256k1'); + + var rng = {}; + rng.nextBytes = function(array) { + var buf = SecureRandom.getRandomBuffer(array.length); + var a = bufferToArray(SecureRandom.getRandomBuffer(array.length)); + for (var i in array) { + array[i] = a[i]; + } + }; + + var getBigRandom = function (limit) { + return new BigInteger(limit.bitLength(), rng) + .mod(limit.subtract(BigInteger.ONE)) + .add(BigInteger.ONE); + }; + + var sign = function (hash, priv) { + var d = priv; + var n = ecparams.getN(); + var e = BigInteger.fromByteArrayUnsigned(hash); + + do { + var k = getBigRandom(n); + var G = ecparams.getG(); + var Q = G.multiply(k); + var r = Q.getX().toBigInteger().mod(n); + } while (r.compareTo(BigInteger.ZERO) <= 0); + + var s = k.modInverse(n).multiply(e.add(d.multiply(r))).mod(n); + + return serializeSig(r, s); + }; + + var serializeSig = function (r, s) { + var rBa = r.toByteArraySigned(); + var sBa = s.toByteArraySigned(); + + var sequence = []; + sequence.push(0x02); // INTEGER + sequence.push(rBa.length); + sequence = sequence.concat(rBa); + + sequence.push(0x02); // INTEGER + sequence.push(sBa.length); + sequence = sequence.concat(sBa); + + sequence.unshift(sequence.length); + sequence.unshift(0x30); // SEQUENCE + + return sequence; + }; + if (!this.private) { throw new Error('Key does not have a private key set'); } @@ -97,10 +153,10 @@ Key.prototype.signSync = function(hash) { if (!Buffer.isBuffer(hash) || hash.length !== 32) { throw new Error('Arg should be a 32 bytes hash buffer'); } - var eck = new ECKey(this.private.toString('hex')); - eck.setCompressed(this._compressed); - var signature = eck.sign(bufferToArray(hash)); - // return it as a buffer to keep c++ compatibility + var privhex = this.private.toString('hex'); + var privnum = new BigInteger(privhex, 16); + var signature = sign(bufferToArray(hash), privnum); + return new Buffer(signature); }; diff --git a/lib/browser/Point.js b/lib/browser/Point.js index fba0b3d..8c6c085 100644 --- a/lib/browser/Point.js +++ b/lib/browser/Point.js @@ -4,7 +4,6 @@ var imports = require('soop').imports(); var Key = imports.Key || require('./Key'); var bignum = imports.bignum || require('bignum'); var assert = require('assert'); -var ECKey = require('../../browser/vendor-bundle.js').ECKey; var ECPointFp = require('../../browser/vendor-bundle.js').ECPointFp; var ECFieldElementFp = require('../../browser/vendor-bundle.js').ECFieldElementFp; var getSECCurveByName = require('../../browser/vendor-bundle.js').getSECCurveByName; From 9af3501f044b3b6610b58a49a39fc6ea089fd28e Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Fri, 25 Apr 2014 15:07:38 -0300 Subject: [PATCH 4/4] up version to 0.1.15 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bd78c74..1a6f151 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "bitcore", "description": "Bitcoin Library", - "version": "0.1.13", + "version": "0.1.15", "author": { "name": "Stephen Pair", "email": "stephen@bitpay.com"