diff --git a/package.json b/package.json index 12900bcf0..5e3886701 100644 --- a/package.json +++ b/package.json @@ -36,42 +36,42 @@ "url": "https://github.com/bitcoinjs/bitcoinjs-lib.git" }, "devDependencies": { - "mocha": "1.18.2", - "istanbul": "0.1.30", - "uglify-js": "2.4.13", - "sinon": "1.9.0", + "browserify": "~4.1.5", "coveralls": "~2.10.0", - "mocha-lcov-reporter": "0.0.1", "helloblock-js": "^0.2.1", - "browserify": "~4.1.5", - "jshint": "2.5.1" + "istanbul": "0.1.30", + "jshint": "2.5.1", + "mocha": "1.18.2", + "mocha-lcov-reporter": "0.0.1", + "sinon": "1.9.0", + "uglify-js": "2.4.13" }, "testling": { "browsers": [ + "android-browser/4.2..latest", "chrome/20..latest", "firefox/21..latest", - "safari/latest", - "opera/15..latest", - "iphone/6..latest", "ipad/6..latest", - "android-browser/4.2..latest" + "iphone/6..latest", + "opera/15..latest", + "safari/latest" ], "harness": "mocha-bdd", "files": "test/*.js" }, "scripts": { - "unit": "./node_modules/.bin/istanbul test ./node_modules/.bin/_mocha -- --reporter list `find test -maxdepth 1 -not -type d`", - "test": "npm run-script unit", - "integration": "./node_modules/.bin/_mocha --reporter list test/integration/*.js", - "jshint": "./node_modules/.bin/jshint --config jshint.json src/*.js ; true", + "compile": "./node_modules/.bin/browserify ./src/index.js -s Bitcoin | ./node_modules/.bin/uglifyjs > bitcoinjs-min.js", "coverage": "./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha -- --reporter list test/*.js", "coveralls": "npm run-script coverage && node ./node_modules/.bin/coveralls < coverage/lcov.info", - "compile": "./node_modules/.bin/browserify ./src/index.js -s Bitcoin | ./node_modules/.bin/uglifyjs > bitcoinjs-min.js" + "integration": "./node_modules/.bin/_mocha --reporter list test/integration/*.js", + "jshint": "./node_modules/.bin/jshint --config jshint.json src/*.js ; true", + "test": "npm run-script unit", + "unit": "./node_modules/.bin/istanbul test ./node_modules/.bin/_mocha -- --reporter list `find test -maxdepth 1 -not -type d`" }, "dependencies": { "bigi": "1.1.0", "crypto-js": "3.1.2-3", - "ecurve": "0.9.0", + "ecurve": "0.10.0", "secure-random": "0.2.1" } } diff --git a/src/ecdsa.js b/src/ecdsa.js index 60fe624f8..aeba1de4e 100644 --- a/src/ecdsa.js +++ b/src/ecdsa.js @@ -5,6 +5,7 @@ var BigInteger = require('bigi') var ECSignature = require('./ecsignature') var Point = require('ecurve').Point +// https://tools.ietf.org/html/rfc6979#section-3.2 function deterministicGenerateK(curve, hash, d) { assert(Buffer.isBuffer(hash), 'Hash must be a Buffer, not ' + hash) assert.equal(hash.length, 32, 'Hash must be 256 bit') @@ -13,22 +14,40 @@ function deterministicGenerateK(curve, hash, d) { var x = d.toBuffer(32) var k = new Buffer(32) var v = new Buffer(32) - k.fill(0) + + // Step B v.fill(1) + // Step C + k.fill(0) + + // Step D k = crypto.HmacSHA256(Buffer.concat([v, new Buffer([0]), x, hash]), k) + + // Step E v = crypto.HmacSHA256(v, k) + // Step F k = crypto.HmacSHA256(Buffer.concat([v, new Buffer([1]), x, hash]), k) + + // Step G v = crypto.HmacSHA256(v, k) + + // Step H1/H2a, ignored as tlen === qlen (256 bit) + // Step H2b v = crypto.HmacSHA256(v, k) - var n = curve.n - var kB = BigInteger.fromBuffer(v).mod(n) - assert(kB.compareTo(BigInteger.ONE) > 0, 'Invalid k value') - assert(kB.compareTo(n) < 0, 'Invalid k value') + var T = BigInteger.fromBuffer(v) - return kB + // Step H3, repeat until T is within the interval [1, n - 1] + while ((T.signum() <= 0) || (T.compareTo(curve.n) >= 0)) { + k = crypto.HmacSHA256(Buffer.concat([v, new Buffer([0])]), k) + v = crypto.HmacSHA256(v, k) + + T = BigInteger.fromBuffer(v) + } + + return T } function sign(curve, hash, d) { @@ -97,8 +116,7 @@ function recoverPubKey(curve, e, signature, i) { var s = signature.s // A set LSB signifies that the y-coordinate is odd - // By reduction, the y-coordinate is even if it is clear - var isYEven = !(i & 1) + var isYOdd = i & 1 // The more significant bit specifies whether we should use the // first or second candidate key. @@ -106,28 +124,12 @@ function recoverPubKey(curve, e, signature, i) { var n = curve.n var G = curve.G - var p = curve.p - var a = curve.a - var b = curve.b - - // We precalculate (p + 1) / 4 where p is the field order - if (!curve.P_OVER_FOUR) { - curve.P_OVER_FOUR = p.add(BigInteger.ONE).shiftRight(2) - } // 1.1 Let x = r + jn var x = isSecondKey ? r.add(n) : r - - // 1.2, 1.3 Convert x to a point R using routine specified in Section 2.3.4 - var alpha = x.pow(3).add(a.multiply(x)).add(b).mod(p) - var beta = alpha.modPow(curve.P_OVER_FOUR, p) - - // If beta is even, but y isn't, or vice versa, then convert it, - // otherwise we're done and y == beta. - var y = (beta.isEven() ^ isYEven) ? p.subtract(beta) : beta + var R = curve.pointFromX(isYOdd, x) // 1.4 Check that nR is at infinity - var R = Point.fromAffine(curve, x, y) var nR = R.multiply(n) assert(curve.isInfinity(nR), 'nR is not a valid curve point') diff --git a/test/ecdsa.js b/test/ecdsa.js index f025e5edd..84c0b44a3 100644 --- a/test/ecdsa.js +++ b/test/ecdsa.js @@ -3,6 +3,7 @@ var crypto = require('../src/crypto') var ecdsa = require('../src/ecdsa') var message = require('../src/message') var networks = require('../src/networks') +var sinon = require('sinon') var BigInteger = require('bigi') var ECSignature = require('../src/ecsignature') @@ -15,7 +16,7 @@ var fixtures = require('./fixtures/ecdsa.json') describe('ecdsa', function() { describe('deterministicGenerateK', function() { fixtures.valid.forEach(function(f) { - it('determines k for \"' + f.message + '\"', function() { + it('for \"' + f.message + '\"', function() { var d = BigInteger.fromHex(f.d) var h1 = crypto.sha256(f.message) @@ -23,6 +24,21 @@ describe('ecdsa', function() { assert.equal(k.toHex(), f.k) }) }) + + it('loops until an appropriate k value is found', sinon.test(function() { + this.mock(BigInteger).expects('fromBuffer') + .exactly(3) + .onCall(0).returns(new BigInteger('0')) + .onCall(1).returns(curve.n) + .onCall(2).returns(new BigInteger('42')) + + var d = new BigInteger('1') + var h1 = new Buffer(32) + + var k = ecdsa.deterministicGenerateK(curve, h1, d) + + assert.equal(k.toString(), '42') + })) }) describe('recoverPubKey', function() { diff --git a/test/eckey.js b/test/eckey.js index ea884ad43..bfd061661 100644 --- a/test/eckey.js +++ b/test/eckey.js @@ -26,7 +26,7 @@ describe('ECKey', function() { var d = new BigInteger(f.d) var privKey = new ECKey(d) - assert.equal(privKey.pub.Q.toString(), f.Q.toString()) + assert.equal(privKey.pub.Q.toString(), f.Q) }) })