diff --git a/index.js b/index.js index 4ab018d5..f7b2440e 100644 --- a/index.js +++ b/index.js @@ -4,6 +4,7 @@ var rlp = require('rlp'); var Contract = require('./lib/contract.js'); var secretStorage = require('./lib/secret-storage.js'); +var Randomish = require('./lib/randomish.js'); var SigningKey = require('./lib/signing-key.js'); var Wallet = require('./lib/wallet.js'); @@ -62,4 +63,6 @@ utils.defineProperty(Wallet.prototype, 'encrypt', function(password, options, ca secretStorage.encrypt(this.privateKey, password, options, callback); }); +utils.defineProperty(Wallet, 'randomish', new Randomish()); + module.exports = Wallet; diff --git a/lib/browser-random-bytes.js b/lib/browser-random-bytes.js new file mode 100644 index 00000000..874071ba --- /dev/null +++ b/lib/browser-random-bytes.js @@ -0,0 +1,37 @@ +'use strict'; + +var utils = require('./utils.js'); + +var crypto = global.crypto || global.msCrypto; +if (!crypto || !crypto.getRandomValues) { + console.log('WARNING: Missing strong random number source; using weak randomBytes'); + crypto = { + getRandomValues: function(length) { + + for (var i = 0; i < buffer.length; i++) { + buffer[i] = parseInt(256 * Math.random()); + } + + return buffer; + }, + _weakCrypto: true + }; +} else { + console.log('Found strong random number source'); +} + +function randomBytes(length) { + if (length <= 0 || length > 1024 || parseInt(length) != length) { + throw new Error('invalid length'); + } + + var buffer = new Buffer(length); + crypto.getRandomValues(buffer); + return buffer; +}; + +if (crypto._weakCrypto === true) { + utils.defineProperty(randomBytes, '_weakCrypto', true); +} + +module.exports = randomBytes; diff --git a/lib/random-bytes.js b/lib/random-bytes.js new file mode 100644 index 00000000..ce25b2dc --- /dev/null +++ b/lib/random-bytes.js @@ -0,0 +1,4 @@ +'use strict'; + +module.exports = require('crypto').randomBytes; + diff --git a/lib/randomish.js b/lib/randomish.js index 8566463b..106011ba 100644 --- a/lib/randomish.js +++ b/lib/randomish.js @@ -1,10 +1,74 @@ +'use strict'; + +var aes = require('aes-js'); +var randomBytes = require('./random-bytes.js'); + var utils = require('./utils.js'); function Randomish() { + if (!(this instanceof Randomish)) { throw new Error('missing new'); } + + var bits = 0; + Object.defineProperty(this, 'entropy', { + enumerable: true, + get: function() { return bits; } + }); + + var weak = !!(randomBytes._weakCrypto); + + var entropy = new aes.ModeOfOperation.cbc( + Randomish.randomishBytes(32), + Randomish.randomishBytes(16) + ); + + if (!weak) { bits += (32 + 16) * 8; } + + utils.defineProperty(this, 'feedEntropy', function(data, expectedBits) { + if (!data) { data = ''; } + if (!expectedBits) { expectedBits = 0; } + + if (parseInt(expectedBits) != expectedBits) { throw new Error('invalid expectedBits'); } + + data = (new Date()).getTime() + '-' + JSON.stringify(data) + '-' + data.toString(); + var hashed = utils.sha3(new Buffer(data, 'utf8')); + + bits += expectedBits + (weak ? 0: ((32) * 8)); + + // Feed the hashed data and random data to the mode of operation + entropy.encrypt(hashed.slice(0, 16)); + entropy.encrypt(randomBytes(16)); + entropy.encrypt(hashed.slice(0, 16)); + return new Buffer(entropy.encrypt(randomBytes(16))); + }); + + utils.defineProperty(this, 'randomBytes', function(length, key) { + if (parseInt(length) != length || length <= 0 || length > 1024) { + throw new Error('invalid length'); + } + + // If we don't have a key, create one + if (!key) { + key = Buffer.concat([this.feedEntropy(), this.feedEntropy()]); + } + + if (!Buffer.isBuffer(key) || key.length !== 32) { + throw new Error('invalid key'); + } + + var aesCbc = new aes.ModeOfOperation.cbc(key, this.feedEntropy()); + var result = new Buffer(0); + while (result.length < length) { + result = result.concat([result, this.feedEntropy()]); + } + + return result; + }); + + this.feedEntropy(); } -utils.defineProperty(Randomish, 'randomBytes', function() { - +utils.defineProperty(Randomish, 'randomishBytes', function(length) { + return randomBytes(length); }); module.exports = Randomish diff --git a/package.json b/package.json index 7ac159b6..a750a27a 100644 --- a/package.json +++ b/package.json @@ -11,14 +11,14 @@ "aes-js": "0.2.4", "elliptic": "6.3.1", "pbkdf2": "3.0.4", - "randombytes": "2.0.3", "rlp": "2.0.0", "scrypt-js": "2.0.0", "uuid": "2.0.1", "xmlhttprequest": "1.8.0" }, "browser": { - "xmlhttprequest": "./lib/browser/browser-xmlhttprequest.js" + "./lib/random-bytes.js": "./lib/browser-random-bytes.js", + "xmlhttprequest": "./lib/browser-xmlhttprequest.js" }, "devDependencies": { "ethereumjs-abi": "0.6.2",