From c7b33eca9ab40ddb2afa629e0c6b8c6f0cba2518 Mon Sep 17 00:00:00 2001 From: "Ryan X. Charles" Date: Fri, 25 Apr 2014 15:00:51 -0300 Subject: [PATCH] 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 74e3e4562..62b6cd067 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 03afc3a9f..77918e59a 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 259e9a7b9..e5df2644a 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 fba0b3d45..8c6c085f8 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;