diff --git a/bitcore.js b/bitcore.js index aa0de4bc0..13f729325 100644 --- a/bitcore.js +++ b/bitcore.js @@ -20,6 +20,7 @@ requireWhenAccessed('const', './const'); requireWhenAccessed('Deserialize', './lib/Deserialize'); requireWhenAccessed('log', './util/log'); requireWhenAccessed('networks', './networks'); +requireWhenAccessed('SecureRandom', './lib/SecureRandom'); requireWhenAccessed('util', './util/util'); requireWhenAccessed('EncodedData', './util/EncodedData'); requireWhenAccessed('VersionedData', './util/VersionedData'); diff --git a/browser/build.js b/browser/build.js index 05f7a6266..38f97e466 100644 --- a/browser/build.js +++ b/browser/build.js @@ -42,6 +42,7 @@ var modules = [ 'lib/SINKey', 'lib/Script', 'lib/ScriptInterpreter', + 'lib/SecureRandom', 'lib/Sign', 'lib/Transaction', 'lib/TransactionBuilder', diff --git a/lib/BIP32.js b/lib/BIP32.js index 94a7602aa..e1f8bfde3 100644 --- a/lib/BIP32.js +++ b/lib/BIP32.js @@ -3,6 +3,7 @@ var base58 = imports.base58 || require('base58-native').base58; var coinUtil = imports.coinUtil || require('../util'); var Key = imports.Key || require('./Key'); var Point = imports.Point || require('./Point'); +var SecureRandom = imports.SecureRandom || require('./SecureRandom'); var bignum = imports.bignum || require('bignum'); var crypto = require('crypto'); var networks = require('../networks'); @@ -27,7 +28,7 @@ var BIP32 = function(bytes) { this.depth = 0x00; this.parentFingerprint = new Buffer([0, 0, 0, 0]); this.childIndex = new Buffer([0, 0, 0, 0]); - this.chainCode = Key.generateSync().private; + this.chainCode = SecureRandom.getRandomBuffer(32); this.eckey = Key.generateSync(); this.hasPrivateKey = true; this.pubKeyHash = coinUtil.sha256ripe160(this.eckey.public); diff --git a/lib/Connection.js b/lib/Connection.js index f03e95330..4b40d1621 100644 --- a/lib/Connection.js +++ b/lib/Connection.js @@ -17,7 +17,8 @@ var util = imports.util || require('../util'); var Parser = imports.Parser || require('../util/BinaryParser'); var buffertools = imports.buffertools || require('buffertools'); var doubleSha256 = imports.doubleSha256 || util.twoSha256; -var nonce = util.generateNonce(); +var SecureRandom = imports.SecureRandom || require('./SecureRandom'); +var nonce = function() {return SecureRandom.getPseudoRandomBuffer(8)}; var BIP0031_VERSION = 60000; diff --git a/lib/SecureRandom.js b/lib/SecureRandom.js new file mode 100644 index 000000000..fa7f5ef4d --- /dev/null +++ b/lib/SecureRandom.js @@ -0,0 +1,5 @@ +if (process.versions) { + module.exports = require('./node/SecureRandom'); + return; +} +module.exports = require('./browser/SecureRandom'); diff --git a/lib/browser/SecureRandom.js b/lib/browser/SecureRandom.js new file mode 100644 index 000000000..79f1db50c --- /dev/null +++ b/lib/browser/SecureRandom.js @@ -0,0 +1,23 @@ +var imports = require('soop'); + +var SecureRandom = require('../common/SecureRandom'); + +SecureRandom.getRandomBuffer = function(size) { + if (!window.crypto && !window.msCrypto) + throw new Error('window.crypto not available'); + + if (window.crypto && window.crypto.getRandomValues) + var crypto = window.crypto; + else if (window.msCrypto && window.msCrypto.getRandomValues) //internet explorer + var crypto = window.msCrypto; + else + throw new Error('window.crypto.getRandomValues not available'); + + var bbuf = new Uint8Array(size); + crypto.getRandomValues(bbuf); + var buf = new Buffer(bbuf); + + return buf; +}; + +module.exports = require('soop')(SecureRandom); diff --git a/lib/common/SecureRandom.js b/lib/common/SecureRandom.js new file mode 100644 index 000000000..a276d22ac --- /dev/null +++ b/lib/common/SecureRandom.js @@ -0,0 +1,28 @@ +var imports = require('soop'); + +var SecureRandom = function() { +}; + +/* secure random bytes that sometimes throws an error due to lack of entropy */ +SecureRandom.getRandomBuffer = function() {}; + +/* insecure random bytes, but it never fails */ +SecureRandom.getPseudoRandomBuffer = function(size) { + var b32 = 0x100000000; + var b = new Buffer(size); + + for (var i = 0; i <= size; i++) { + var j = Math.floor(i / 4); + var k = i - j * 4; + if (k == 0) { + r = Math.random() * b32; + b[i] = r & 0xff; + } else { + b[i] = (r = r >>> 8) & 0xff; + } + } + + return b; +}; + +module.exports = require('soop')(SecureRandom); diff --git a/lib/node/SecureRandom.js b/lib/node/SecureRandom.js new file mode 100644 index 000000000..3639f75c1 --- /dev/null +++ b/lib/node/SecureRandom.js @@ -0,0 +1,10 @@ +var imports = require('soop'); +var crypto = imports.crypto || require('crypto'); + +var SecureRandom = require('../common/SecureRandom'); + +SecureRandom.getRandomBuffer = function(size) { + return crypto.randomBytes(size); +} + +module.exports = require('soop')(SecureRandom); diff --git a/test/index.html b/test/index.html index 551c46d80..9b8da7cea 100644 --- a/test/index.html +++ b/test/index.html @@ -35,6 +35,7 @@ + diff --git a/test/test.SecureRandom.js b/test/test.SecureRandom.js new file mode 100644 index 000000000..ab8207b99 --- /dev/null +++ b/test/test.SecureRandom.js @@ -0,0 +1,57 @@ +'use strict'; + +var chai = chai || require('chai'); +var bitcore = bitcore || require('../bitcore'); +var should = chai.should(); +var assert = chai.assert; +var SecureRandom = bitcore.SecureRandom; + +describe('SecureRandom', function() { + + describe('getRandomBuffer', function() { + + it('should return a buffer', function() { + var bytes = SecureRandom.getRandomBuffer(8); + bytes.length.should.equal(8); + Buffer.isBuffer(bytes).should.equal(true); + }); + + it('should not equate two 256 bit random buffers', function() { + var bytes1 = SecureRandom.getRandomBuffer(32); + var bytes2 = SecureRandom.getRandomBuffer(32); + bytes1.toString('hex').should.not.equal(bytes2.toString('hex')); + }); + + }); + + describe('getPseudoRandomBuffer', function() { + + it('should generate 7 random bytes', function() { + var buf = SecureRandom.getPseudoRandomBuffer(7); + buf.length.should.equal(7); + }); + + it('should generate 8 random bytes', function() { + var buf = SecureRandom.getPseudoRandomBuffer(8); + buf.length.should.equal(8); + }); + + it('should generate 9 random bytes', function() { + var buf = SecureRandom.getPseudoRandomBuffer(9); + buf.length.should.equal(9); + }); + + it('should generate 90 random bytes', function() { + var buf = SecureRandom.getPseudoRandomBuffer(90); + buf.length.should.equal(90); + }); + + it('should generate two 8 byte buffers that are not equal', function() { + var buf1 = SecureRandom.getPseudoRandomBuffer(8); + var buf2 = SecureRandom.getPseudoRandomBuffer(8); + buf1.toString('hex').should.not.equal(buf2.toString('hex')); + }); + + }); + +}); diff --git a/util/util.js b/util/util.js index f89dce844..a59df4b35 100644 --- a/util/util.js +++ b/util/util.js @@ -10,7 +10,6 @@ if (inBrowser) { browser = require('../browser/vendor-bundle.js'); } - var sha256 = exports.sha256 = function(data) { return new Buffer(crypto.createHash('sha256').update(data).digest('binary'), 'binary'); }; @@ -336,36 +335,6 @@ var createSynchrotron = exports.createSynchrotron = function(fn) { }; }; -/** - * Generate a random 64-bit number. - * - * With ideas from node-uuid: - * Copyright (c) 2010 Robert Kieffer - * https://github.com/broofa/node-uuid/ - * - * @returns Buffer random nonce - */ -var generateNonce = exports.generateNonce = function() { - var b32 = 0x100000000, - ff = 0xff; - var b = new Buffer(8), - i = 0; - - // Generate eight random bytes - r = Math.random() * b32; - b[i++] = r & ff; - b[i++] = (r = r >>> 8) & ff; - b[i++] = (r = r >>> 8) & ff; - b[i++] = (r = r >>> 8) & ff; - r = Math.random() * b32; - b[i++] = r & ff; - b[i++] = (r = r >>> 8) & ff; - b[i++] = (r = r >>> 8) & ff; - b[i++] = (r = r >>> 8) & ff; - - return b; -}; - /** * Decode difficulty bits. *