Browse Source

add new SecureRandom class that does the right thing

Generating random numbers properly depends on the platform. The new
getRandomBuffer method does the right thing on the right platform. It will
sometimes fail due to insufficient entropy. The getPseudoRandomBuffer class is
also provided that will never fail, but it is not cryptographically secure and
should not be used for keys.
patch-2
Ryan X. Charles 11 years ago
parent
commit
ba692aaa20
  1. 1
      bitcore.js
  2. 1
      browser/build.js
  3. 3
      lib/BIP32.js
  4. 3
      lib/Connection.js
  5. 5
      lib/SecureRandom.js
  6. 23
      lib/browser/SecureRandom.js
  7. 28
      lib/common/SecureRandom.js
  8. 10
      lib/node/SecureRandom.js
  9. 1
      test/index.html
  10. 57
      test/test.SecureRandom.js
  11. 31
      util/util.js

1
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');

1
browser/build.js

@ -42,6 +42,7 @@ var modules = [
'lib/SINKey',
'lib/Script',
'lib/ScriptInterpreter',
'lib/SecureRandom',
'lib/Sign',
'lib/Transaction',
'lib/TransactionBuilder',

3
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);

3
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;

5
lib/SecureRandom.js

@ -0,0 +1,5 @@
if (process.versions) {
module.exports = require('./node/SecureRandom');
return;
}
module.exports = require('./browser/SecureRandom');

23
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);

28
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);

10
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);

1
test/index.html

@ -35,6 +35,7 @@
<script src="test.RpcClient.js"></script>
<script src="test.Script.js"></script>
<script src="test.ScriptInterpreter.js"></script>
<script src="test.SecureRandom.js"></script>
<script src="test.sighash.js"></script>
<script src="test.SIN.js"></script>
<script src="test.SINKey.js"></script>

57
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'));
});
});
});

31
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.
*

Loading…
Cancel
Save