diff --git a/src/ecdsa.js b/src/ecdsa.js index f46e56d..ebb2216 100644 --- a/src/ecdsa.js +++ b/src/ecdsa.js @@ -69,6 +69,11 @@ var ecdsa = { var s = k.modInverse(n).multiply(e.add(d.multiply(r))).mod(n) + if (s.compareTo(n.divide(BigInteger.valueOf(2))) > 0) { + // Make 's' value 'low', as per https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki#low-s-values-in-signatures + s = n.subtract(s); + } + return ecdsa.serializeSig(r, s) }, diff --git a/test/ecdsa.js b/test/ecdsa.js index ebf937a..d000ab2 100644 --- a/test/ecdsa.js +++ b/test/ecdsa.js @@ -1,6 +1,9 @@ var assert = require('assert') var crypto = require('../').crypto var ecdsa = require('..').ecdsa +var sec = require('../src/jsbn/sec.js') +var BigInteger = require('../src/jsbn/jsbn.js') +var ecparams = sec("secp256k1") var rng = require('secure-random') var BigInteger = require('..').BigInteger @@ -55,5 +58,18 @@ describe('ecdsa', function() { assert.ok(ecdsa.verify(hash2, sig_c, s2), 'Verify constant signature') }) + + it('should sign with low S value', function() { + var priv = new ECKey('ca48ec9783cf3ad0dfeff1fc254395a2e403cbbc666477b61b45e31d3b8ab458') + var message = 'Vires in numeris' + var signature = priv.sign(message) + var parsed = ecdsa.parseSig(signature) + + // Check that the 's' value is 'low', to prevent possible transaction malleability as per + // https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki#low-s-values-in-signatures + assert.ok(parsed.s.compareTo(ecparams.getN().divide(BigInteger.valueOf(2))) <= 0) + + assert.ok(priv.verify(message, signature)) + }) }) })