Merge pull request #226 from dcousens/rfc6979fix
Stricter ecdsa RFC 6979 adherence
This commit is contained in:
commit
0198477c6d
4 changed files with 64 additions and 46 deletions
36
package.json
36
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",
|
||||
"coveralls": "~2.10.0",
|
||||
"mocha-lcov-reporter": "0.0.1",
|
||||
"helloblock-js": "^0.2.1",
|
||||
"browserify": "~4.1.5",
|
||||
"jshint": "2.5.1"
|
||||
"coveralls": "~2.10.0",
|
||||
"helloblock-js": "^0.2.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"
|
||||
}
|
||||
}
|
||||
|
|
54
src/ecdsa.js
54
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)
|
||||
v = crypto.HmacSHA256(v, k)
|
||||
|
||||
// Step G
|
||||
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')
|
||||
// Step H1/H2a, ignored as tlen === qlen (256 bit)
|
||||
// Step H2b
|
||||
v = crypto.HmacSHA256(v, k)
|
||||
|
||||
return kB
|
||||
var T = BigInteger.fromBuffer(v)
|
||||
|
||||
// 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')
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
})
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue