diff --git a/lib/crypto/ec.js b/lib/crypto/ec.js index 74912553..ba556cae 100644 --- a/lib/crypto/ec.js +++ b/lib/crypto/ec.js @@ -10,11 +10,9 @@ var elliptic = require('elliptic'); var bn = require('bn.js'); var utils = require('../utils/utils'); +var random = require('./random'); var assert = utils.assert; -var crypto, secp256k1; - -if (!utils.isBrowser) - crypto = require('crypto'); +var secp256k1; try { if (+process.env.BCOIN_USE_ELLIPTIC !== 1) @@ -67,9 +65,9 @@ ec.curve = ec.elliptic.curve; ec.generatePrivateKey = function generatePrivateKey() { var key, priv; - if (secp256k1 && crypto) { + if (secp256k1) { do { - priv = crypto.randomBytes(32); + priv = random.randomBytes(32); } while (!secp256k1.privateKeyVerify(priv)); } else { key = ec.elliptic.genKeyPair(); @@ -110,7 +108,7 @@ ec.publicKeyConvert = function publicKeyConvert(key, compressed) { if (secp256k1) return secp256k1.publicKeyConvert(key, compressed); - point = ec.elliptic.curve.decodePoint(key); + point = ec.curve.decodePoint(key); return new Buffer(point.encode('array', compressed !== false)); }; @@ -190,51 +188,34 @@ ec.ecdh = function ecdh(pub, priv) { */ ec.recover = function recover(msg, sig, j, compressed) { - var keys = []; - var i, point, key; + var point, key; - if (typeof j === 'boolean') { - compressed = j; - j = null; - } + if (!j) + j = 0; if (secp256k1) { try { sig = secp256k1.signatureImport(sig); } catch (e) { - ; + return; } - } - - for (i = 0; i <= 3; i++) { - if (j != null && j !== i) - continue; - - if (secp256k1) { - try { - key = secp256k1.recover(msg, sig, i, compressed); - } catch (e) { - continue; - } - keys.push(key); - continue; - } - try { - point = ec.elliptic.recoverPubKey(msg, sig, i); + key = secp256k1.recover(msg, sig, j, compressed); } catch (e) { - continue; + return; } - - key = ec.elliptic.keyPair({ pub: point }); - key = key.getPublic(compressed !== false, 'array'); - keys.push(new Buffer(key)); + return key; } - if (j != null) - return keys[0]; + try { + point = ec.elliptic.recoverPubKey(msg, sig, j); + } catch (e) { + return; + } - return keys; + key = point.encode('array', compressed !== false); + + return new Buffer(key); }; /** @@ -243,10 +224,8 @@ ec.recover = function recover(msg, sig, j, compressed) { * @returns {Buffer} */ -ec.random = function random(size) { - if (crypto) - return crypto.randomBytes(size); - return new Buffer(elliptic.rand(size)); +ec.random = function _random(size) { + return random.randomBytes(size); }; /** @@ -259,8 +238,7 @@ ec.random = function random(size) { */ ec.rand = function rand(min, max) { - var num = ec.random(4).readUInt32LE(0, true); - return Math.floor((num / 0x100000000) * (max - min) + min); + return random.randomInt(min, max); }; /** @@ -362,7 +340,7 @@ ec.privateKeyVerify = function privateKeyVerify(key) { key = new bn(key); - return key.cmpn(0) !== 0 && key.cmp(ec.elliptic.curve.n) < 0; + return key.cmpn(0) !== 0 && key.cmp(ec.curve.n) < 0; }; /** @@ -521,7 +499,7 @@ ec.toLowS = function toLowS(sig) { // If S is greater than half the order, // it's too high. if (sig.s.cmp(ec.elliptic.nh) > 0) - sig.s = ec.elliptic.n.sub(sig.s); + sig.s = ec.curve.n.sub(sig.s); return new Buffer(sig.toDER()); }; diff --git a/lib/crypto/random.js b/lib/crypto/random.js new file mode 100644 index 00000000..8a006771 --- /dev/null +++ b/lib/crypto/random.js @@ -0,0 +1,69 @@ +/*! + * random.js - pseudorandom byte generation for bcoin. + * Copyright (c) 2016, Christopher Jeffrey (MIT License). + * https://github.com/bcoin-org/bcoin + * + * Parts of this software are based on brorand: + * https://github.com/indutny/brorand + * Copyright (c) 2014, Fedor Indutny (MIT License). + */ + +var random, crypto, global; + +try { + crypto = require('crypto'); +} catch (e) { + ; +} + +if (crypto) { + random = function random(n) { + return crypto.randomBytes(n); + }; +} else { + if (typeof window !== 'undefined') + global = window; + else if (typeof self !== 'undefined') + global = self; + + if (!global) + throw new Error('Unknown global.'); + + crypto = global.crypto || global.msCrypto; + + if (crypto && crypto.getRandomValues) { + random = function random(n) { + var data = new Uint8Array(n); + crypto.getRandomValues(data); + return new Buffer(data.buffer); + }; + } else { + // Out of luck here. Use bad randomness for now. + // Possibly fall back to randy in the future: + // https://github.com/deestan/randy + random = function random(n) { + var data = new Buffer(n); + var i; + + for (i = 0; i < data.length; i++) + data[i] = ((Math.random() * 0x100000000) >>> 0) % 256; + + return data; + }; + } +} + +function randomInt(min, max) { + var num = random(4).readUInt32LE(0, true); + return Math.floor((num / 0x100000000) * (max - min) + min); +} + +/* + * Expose + */ + +exports = random; +exports.randomBytes = random; +exports.randomInt = randomInt; + +module.exports = random; diff --git a/lib/crypto/schnorr.js b/lib/crypto/schnorr.js index 66f81348..fd75784e 100644 --- a/lib/crypto/schnorr.js +++ b/lib/crypto/schnorr.js @@ -11,6 +11,7 @@ var elliptic = require('elliptic'); var Signature = require('elliptic/lib/elliptic/ec/signature'); var hmacDRBG = require('elliptic/lib/elliptic/hmac-drbg'); var ec = require('./ec'); +var random = require('./random'); var curve = elliptic.ec('secp256k1').curve; var sha256 = require('./crypto').sha256; @@ -109,7 +110,7 @@ schnorr.sign = function sign(msg, key, hash, pubnonce) { throw new Error('Bad private key.'); while (!sig) { - k = new bn(ec.random(32)); + k = new bn(random.randomBytes(32)); sig = schnorr._sign(msg, prv, k, hash, pubnonce); } diff --git a/lib/hd/mnemonic.js b/lib/hd/mnemonic.js index 4cfa449f..0b184523 100644 --- a/lib/hd/mnemonic.js +++ b/lib/hd/mnemonic.js @@ -9,6 +9,7 @@ var bcoin = require('../env'); var utils = require('../utils/utils'); var ec = require('../crypto/ec'); +var random = require('../crypto/random'); var assert = utils.assert; var constants = bcoin.constants; var BufferWriter = require('../utils/writer'); @@ -168,7 +169,7 @@ Mnemonic.prototype.toKey = function toKey(passphrase, network) { Mnemonic.prototype.getEntropy = function getEntropy() { if (!this.entropy) - this.entropy = ec.random(this.bits / 8); + this.entropy = random.randomBytes(this.bits / 8); assert(this.bits / 8 === this.entropy.length); diff --git a/lib/hd/private.js b/lib/hd/private.js index 818c0b31..14934647 100644 --- a/lib/hd/private.js +++ b/lib/hd/private.js @@ -9,6 +9,7 @@ var bcoin = require('../env'); var utils = require('../utils/utils'); var ec = require('../crypto/ec'); +var random = require('../crypto/random'); var assert = utils.assert; var constants = bcoin.constants; var networks = bcoin.networks; @@ -577,7 +578,7 @@ HDPrivateKey.fromKey = function fromKey(key, entropy, network) { HDPrivateKey.generate = function generate(network) { var key = ec.generatePrivateKey(); - var entropy = ec.random(32); + var entropy = random.randomBytes(32); return HDPrivateKey.fromKey(key, entropy, network); }; diff --git a/lib/http/server.js b/lib/http/server.js index 60ae66b1..4da98233 100644 --- a/lib/http/server.js +++ b/lib/http/server.js @@ -15,6 +15,7 @@ var constants = bcoin.constants; var http = require('./'); var HTTPBase = http.base; var utils = require('../utils/utils'); +var random = require('../crypto/random'); var assert = utils.assert; var RPC; /*= require('./rpc'); - load lazily */ @@ -57,7 +58,7 @@ function HTTPServer(options) { this.rpc = null; if (!this.apiKey) - this.apiKey = utils.toBase58(bcoin.ec.random(20)); + this.apiKey = utils.toBase58(random.randomBytes(20)); assert(typeof this.apiKey === 'string', 'API key must be a string.'); assert(this.apiKey.length <= 200, 'API key must be under 200 bytes.'); @@ -233,6 +234,9 @@ HTTPServer.prototype._init = function _init() { if (params.fee) options.fee = utils.satoshi(params.fee); + if (params.hardFee) + options.hardFee = utils.satoshi(params.hardFee); + if (params.maxFee) options.maxFee = utils.satoshi(params.maxFee); diff --git a/lib/mempool/mempool.js b/lib/mempool/mempool.js index 3d2b64c7..7d207065 100644 --- a/lib/mempool/mempool.js +++ b/lib/mempool/mempool.js @@ -19,6 +19,7 @@ var utils = require('../utils/utils'); var assert = utils.assert; var BufferWriter = require('../utils/writer'); var BufferReader = require('../utils/reader'); +var random = require('../crypto/random'); var VerifyError = bcoin.errors.VerifyError; /** @@ -299,7 +300,7 @@ Mempool.prototype.limitOrphans = function limitOrphans() { var i, hash; while (this.totalOrphans > constants.mempool.MAX_ORPHAN_TX) { - i = bcoin.ec.rand(0, orphans.length); + i = random.randomInt(0, orphans.length); hash = orphans[i]; orphans.splice(i, 1); diff --git a/lib/net/bip150.js b/lib/net/bip150.js index 4a1f5131..96c6af59 100644 --- a/lib/net/bip150.js +++ b/lib/net/bip150.js @@ -11,6 +11,7 @@ var EventEmitter = require('events').EventEmitter; var bcoin = require('../env'); var utils = require('../utils/utils'); +var random = require('../crypto/random'); var assert = utils.assert; var constants = bcoin.constants; @@ -118,7 +119,7 @@ BIP150.prototype.reply = function reply(payload) { throw new Error('Auth failure.'); if (!this.peerIdentity) - return bcoin.ec.random(32); + return random.randomBytes(32); sig = bcoin.ec.toDER(data); msg = this.hash(this.output.sid, type, this.peerIdentity); @@ -126,7 +127,7 @@ BIP150.prototype.reply = function reply(payload) { result = bcoin.ec.verify(msg, sig, this.peerIdentity); if (!result) - return bcoin.ec.random(32); + return random.randomBytes(32); if (this.isAuthed()) { this.auth = true; diff --git a/lib/wallet/wallet.js b/lib/wallet/wallet.js index f8afe079..c6abe1e1 100644 --- a/lib/wallet/wallet.js +++ b/lib/wallet/wallet.js @@ -14,6 +14,7 @@ var utils = require('../utils/utils'); var assert = utils.assert; var BufferReader = require('../utils/reader'); var BufferWriter = require('../utils/writer'); +var random = require('../crypto/random'); var TXDB = require('./txdb'); var Path = require('./path'); @@ -905,7 +906,8 @@ Wallet.prototype.importKey = function importKey(account, ring, passphrase, callb * @param {Boolean} options.confirmed - Select only confirmed coins. * @param {Boolean} options.free - Do not apply a fee if the * transaction priority is high enough to be considered free. - * @param {Amount?} options.fee - Use a hard fee rather than calculating one. + * @param {Amount?} options.hardFee - Use a hard fee rather than + * calculating one. * @param {Number|Boolean} options.subtractFee - Whether to subtract the * fee from existing outputs rather than adding more inputs. */ @@ -967,11 +969,12 @@ Wallet.prototype.fund = function fund(tx, options, callback, force) { round: options.round, confirmed: options.confirmed, free: options.free, - fee: options.fee, + hardFee: options.hardFee, subtractFee: options.subtractFee, changeAddress: account.changeAddress.getAddress(), height: self.db.height, rate: rate, + maxFee: options.maxFee, m: account.m, n: account.n, witness: account.witness, @@ -2377,7 +2380,7 @@ MasterKey.prototype.encrypt = function encrypt(passphrase, callback) { return callback(); data = this.key.toExtended(); - iv = bcoin.ec.random(16); + iv = random.randomBytes(16); this.stop();