ecdsa: fixes edge case presented in #336
This commit is contained in:
parent
4c61380fa5
commit
e9778ae358
3 changed files with 29 additions and 13 deletions
|
@ -52,7 +52,7 @@
|
||||||
"bs58check": "1.0.3",
|
"bs58check": "1.0.3",
|
||||||
"crypto-browserify": "^3.2.6",
|
"crypto-browserify": "^3.2.6",
|
||||||
"ecurve": "1.0.0",
|
"ecurve": "1.0.0",
|
||||||
"typeforce": "0.0.2"
|
"typeforce": "0.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"async": "^0.9.0",
|
"async": "^0.9.0",
|
||||||
|
|
34
src/ecdsa.js
34
src/ecdsa.js
|
@ -9,9 +9,10 @@ var ZERO = new Buffer([0])
|
||||||
var ONE = new Buffer([1])
|
var ONE = new Buffer([1])
|
||||||
|
|
||||||
// https://tools.ietf.org/html/rfc6979#section-3.2
|
// https://tools.ietf.org/html/rfc6979#section-3.2
|
||||||
function deterministicGenerateK(curve, hash, d) {
|
function deterministicGenerateK(curve, hash, d, checkSig) {
|
||||||
typeForce('Buffer', hash)
|
typeForce('Buffer', hash)
|
||||||
typeForce('BigInteger', d)
|
typeForce('BigInteger', d)
|
||||||
|
typeForce('Function', checkSig)
|
||||||
|
|
||||||
// sanity check
|
// sanity check
|
||||||
assert.equal(hash.length, 32, 'Hash must be 256 bit')
|
assert.equal(hash.length, 32, 'Hash must be 256 bit')
|
||||||
|
@ -55,8 +56,8 @@ function deterministicGenerateK(curve, hash, d) {
|
||||||
|
|
||||||
var T = BigInteger.fromBuffer(v)
|
var T = BigInteger.fromBuffer(v)
|
||||||
|
|
||||||
// Step H3, repeat until T is within the interval [1, n - 1]
|
// Step H3, repeat until T is within the interval [1, n - 1] and is suitable for ECDSA
|
||||||
while ((T.signum() <= 0) || (T.compareTo(curve.n) >= 0)) {
|
while ((T.signum() <= 0) || (T.compareTo(curve.n) >= 0) || !checkSig(T)) {
|
||||||
k = crypto.createHmac('sha256', k)
|
k = crypto.createHmac('sha256', k)
|
||||||
.update(v)
|
.update(v)
|
||||||
.update(ZERO)
|
.update(ZERO)
|
||||||
|
@ -64,6 +65,9 @@ function deterministicGenerateK(curve, hash, d) {
|
||||||
|
|
||||||
v = crypto.createHmac('sha256', k).update(v).digest()
|
v = crypto.createHmac('sha256', k).update(v).digest()
|
||||||
|
|
||||||
|
// Step H1/H2a, again, ignored as tlen === qlen (256 bit)
|
||||||
|
// Step H2b again
|
||||||
|
v = crypto.createHmac('sha256', k).update(v).digest()
|
||||||
T = BigInteger.fromBuffer(v)
|
T = BigInteger.fromBuffer(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,18 +75,28 @@ function deterministicGenerateK(curve, hash, d) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function sign(curve, hash, d) {
|
function sign(curve, hash, d) {
|
||||||
var k = deterministicGenerateK(curve, hash, d)
|
var r, s
|
||||||
|
|
||||||
|
var e = BigInteger.fromBuffer(hash)
|
||||||
var n = curve.n
|
var n = curve.n
|
||||||
var G = curve.G
|
var G = curve.G
|
||||||
var Q = G.multiply(k)
|
|
||||||
var e = BigInteger.fromBuffer(hash)
|
|
||||||
|
|
||||||
var r = Q.affineX.mod(n)
|
deterministicGenerateK(curve, hash, d, function(k) {
|
||||||
assert.notEqual(r.signum(), 0, 'Invalid R value')
|
var Q = G.multiply(k)
|
||||||
|
|
||||||
var s = k.modInverse(n).multiply(e.add(d.multiply(r))).mod(n)
|
if (curve.isInfinity(Q))
|
||||||
assert.notEqual(s.signum(), 0, 'Invalid S value')
|
return false
|
||||||
|
|
||||||
|
r = Q.affineX.mod(n)
|
||||||
|
if (r.signum() === 0)
|
||||||
|
return false
|
||||||
|
|
||||||
|
s = k.modInverse(n).multiply(e.add(d.multiply(r))).mod(n)
|
||||||
|
if (s.signum() === 0)
|
||||||
|
return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
var N_OVER_TWO = n.shiftRight(1)
|
var N_OVER_TWO = n.shiftRight(1)
|
||||||
|
|
||||||
|
|
|
@ -15,12 +15,14 @@ var fixtures = require('./fixtures/ecdsa.json')
|
||||||
|
|
||||||
describe('ecdsa', function() {
|
describe('ecdsa', function() {
|
||||||
describe('deterministicGenerateK', function() {
|
describe('deterministicGenerateK', function() {
|
||||||
|
function checkSig() { return true }
|
||||||
|
|
||||||
fixtures.valid.forEach(function(f) {
|
fixtures.valid.forEach(function(f) {
|
||||||
it('for \"' + f.message + '\"', function() {
|
it('for \"' + f.message + '\"', function() {
|
||||||
var d = BigInteger.fromHex(f.d)
|
var d = BigInteger.fromHex(f.d)
|
||||||
var h1 = crypto.sha256(f.message)
|
var h1 = crypto.sha256(f.message)
|
||||||
|
|
||||||
var k = ecdsa.deterministicGenerateK(curve, h1, d)
|
var k = ecdsa.deterministicGenerateK(curve, h1, d, checkSig)
|
||||||
assert.equal(k.toHex(), f.k)
|
assert.equal(k.toHex(), f.k)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -35,7 +37,7 @@ describe('ecdsa', function() {
|
||||||
var d = new BigInteger('1')
|
var d = new BigInteger('1')
|
||||||
var h1 = new Buffer(32)
|
var h1 = new Buffer(32)
|
||||||
|
|
||||||
var k = ecdsa.deterministicGenerateK(curve, h1, d)
|
var k = ecdsa.deterministicGenerateK(curve, h1, d, checkSig)
|
||||||
|
|
||||||
assert.equal(k.toString(), '42')
|
assert.equal(k.toString(), '42')
|
||||||
}))
|
}))
|
||||||
|
|
Loading…
Reference in a new issue