This commit is contained in:
Christopher Jeffrey 2016-03-19 10:03:55 -07:00
parent bd54b42dc4
commit 5153e0e1f0
10 changed files with 161 additions and 320 deletions

View File

@ -7,6 +7,13 @@
var bcoin = exports;
var utils = require('./bcoin/utils');
var assert = utils.assert;
var fs;
try {
fs = require('f' + 's');
} catch (e) {
;
}
bcoin.isBrowser =
(typeof process !== 'undefined' && process.browser)
@ -19,7 +26,7 @@ bcoin.profile = +process.env.BCOIN_PROFILE === 1;
bcoin.fresh = +process.env.BCOIN_FRESH === 1;
bcoin.ensurePrefix = function ensurePrefix() {
if (!bcoin.fs)
if (bcoin.isBrowser)
return;
if (bcoin._ensured)
@ -31,21 +38,25 @@ bcoin.ensurePrefix = function ensurePrefix() {
bcoin.rimraf(bcoin.prefix);
try {
bcoin.fs.statSync(bcoin.prefix);
fs.statSync(bcoin.prefix);
} catch (e) {
bcoin.fs.mkdirSync(bcoin.prefix, 0750);
fs.mkdirSync(bcoin.prefix, 0750);
}
};
bcoin.rimraf = function rimraf(file) {
if (!bcoin.cp)
var cp;
if (bcoin.isBrowser)
return;
cp = require('child_' + 'process');
assert(typeof file === 'string');
assert(file !== '/');
assert(file !== process.env.HOME);
bcoin.cp.execFileSync('rm', ['-rf', file], { stdio: 'ignore' });
cp.execFileSync('rm', ['-rf', file], { stdio: 'ignore' });
};
bcoin.debug = function debug() {
@ -57,10 +68,10 @@ bcoin.debug = function debug() {
process.stdout.write(msg);
}
if (bcoin.debugFile && bcoin.fs) {
if (bcoin.debugFile && !bcoin.isBrowser) {
if (!bcoin._debug) {
bcoin.ensurePrefix();
bcoin._debug = bcoin.fs.createWriteStream(
bcoin._debug = fs.createWriteStream(
bcoin.prefix + '/debug.log', { flags: 'a' });
}
msg = utils.format(args, false);
@ -68,31 +79,9 @@ bcoin.debug = function debug() {
}
};
bcoin.bn = require('bn.js');
bcoin.elliptic = require('elliptic');
if (!bcoin.isBrowser) {
bcoin.fs = require('f' + 's');
bcoin.crypto = require('cry' + 'pto');
bcoin.net = require('n' + 'et');
bcoin.cp = require('child_' + 'process');
try {
bcoin.secp256k1 = require('secp' + '256k1');
} catch (e) {
;
}
} else {
bcoin.hash = require('hash.js');
}
bcoin.ecdsa = bcoin.elliptic.ec('secp256k1');
assert(!bcoin.ecdsa.signature);
bcoin.ecdsa.signature = require('elliptic/lib/elliptic/ec/signature');
assert(!bcoin.ecdsa.keypair);
bcoin.ecdsa.keypair = require('elliptic/lib/elliptic/ec/key');
bcoin.utils = utils;
bcoin.utils.debug = bcoin.debug;
bcoin.utils.ensurePrefix = bcoin.ensurePrefix;
bcoin.reader = require('./bcoin/reader');
bcoin.writer = require('./bcoin/writer');
bcoin.profiler = require('./bcoin/profiler');

View File

@ -5,11 +5,11 @@
*/
var EventEmitter = require('events').EventEmitter;
var fs = require('fs');
var bcoin = require('../bcoin');
var network = bcoin.protocol.network;
var utils = require('./utils');
var assert = utils.assert;
var fs = bcoin.fs;
var pad32 = utils.pad32;
var MAX_FILE_SIZE = 128 * 1024 * 1024;

View File

@ -9,6 +9,20 @@ var elliptic = require('elliptic');
var utils = require('./utils');
var assert = utils.assert;
var ec = exports;
var crypto, secp256k1;
if (!bcoin.isBrowser)
crypto = require('cry' + 'pto');
try {
secp256k1 = require('secp' + '256k1');
} catch (e) {
;
}
ec.elliptic = elliptic.ec('secp256k1');
ec.signature = require('elliptic/lib/elliptic/ec/signature');
ec.keypair = require('elliptic/lib/elliptic/ec/key');
/**
* EC
@ -17,12 +31,12 @@ var ec = exports;
ec.generatePrivateKey = function generatePrivateKey() {
var key, priv;
if (bcoin.secp256k1 && bcoin.crypto) {
if (secp256k1 && crypto) {
do {
priv = bcoin.crypto.randomBytes(32);
} while (!bcoin.secp256k1.privateKeyVerify(priv));
priv = crypto.randomBytes(32);
} while (!secp256k1.privateKeyVerify(priv));
} else {
key = bcoin.ecdsa.genKeyPair();
key = ec.elliptic.genKeyPair();
priv = new Buffer(key.getPrivate().toArray('be', 32));
}
@ -32,16 +46,16 @@ ec.generatePrivateKey = function generatePrivateKey() {
ec.publicKeyCreate = function publicKeyCreate(priv, compressed) {
assert(Buffer.isBuffer(priv));
if (bcoin.secp256k1)
return bcoin.secp256k1.publicKeyCreate(priv, compressed);
if (secp256k1)
return secp256k1.publicKeyCreate(priv, compressed);
priv = bcoin.ecdsa.keyPair({ priv: priv }).getPublic(compressed, 'array');
priv = ec.elliptic.keyPair({ priv: priv }).getPublic(compressed, 'array');
return new Buffer(priv);
};
ec.random = function random(size) {
if (bcoin.crypto)
return bcoin.crypto.randomBytes(size);
if (crypto)
return crypto.randomBytes(size);
return new Buffer(elliptic.rand(size));
};
@ -63,23 +77,23 @@ ec.verify = function verify(msg, sig, key, historical) {
sig = ec.normalizeLength(sig);
try {
if (bcoin.secp256k1) {
if (secp256k1) {
// secp256k1 fails on high s values. This is
// bad for verifying historical data.
if (historical)
sig = ec.toLowS(sig);
// Import from DER.
sig = bcoin.secp256k1.signatureImport(sig);
sig = secp256k1.signatureImport(sig);
// This is supposed to lower the S value
// but it doesn't seem to work.
// if (historical)
// sig = bcoin.secp256k1.signatureNormalize(sig);
return bcoin.secp256k1.verify(msg, sig, key);
return secp256k1.verify(msg, sig, key);
}
return bcoin.ecdsa.verify(msg, sig, key);
return ec.elliptic.verify(msg, sig, key);
} catch (e) {
utils.debug('Elliptic threw during verification:');
utils.debug(e.stack + '');
@ -98,18 +112,18 @@ ec.sign = function sign(msg, key) {
if (key.getPrivateKey)
key = key.getPrivateKey();
if (bcoin.secp256k1) {
if (secp256k1) {
// Sign message
sig = bcoin.secp256k1.sign(msg, key);
sig = secp256k1.sign(msg, key);
// Ensure low S value
sig = bcoin.secp256k1.signatureNormalize(sig.signature);
sig = secp256k1.signatureNormalize(sig.signature);
// Convert to DER array
sig = bcoin.secp256k1.signatureExport(sig);
sig = secp256k1.signatureExport(sig);
} else {
// Sign message and ensure low S value
sig = bcoin.ecdsa.sign(msg, key, { canonical: true });
sig = ec.elliptic.sign(msg, key, { canonical: true });
// Convert to DER array
sig = new Buffer(sig.toDER());
@ -168,7 +182,7 @@ ec.isLowS = function isLowS(sig) {
assert(Buffer.isBuffer(sig));
try {
sig = new bcoin.ecdsa.signature(sig);
sig = new ec.signature(sig);
} catch (e) {
return false;
}
@ -182,7 +196,7 @@ ec.isLowS = function isLowS(sig) {
// If S is greater than half the order,
// it's too high.
if (sig.s.cmp(bcoin.ecdsa.nh) > 0)
if (sig.s.cmp(ec.elliptic.nh) > 0)
return false;
return true;
@ -193,7 +207,7 @@ ec.toLowS = function toLowS(sig) {
assert(Buffer.isBuffer(sig));
try {
sig = new bcoin.ecdsa.signature(sig);
sig = new ec.signature(sig);
} catch (e) {
return sig;
}
@ -201,8 +215,8 @@ ec.toLowS = function toLowS(sig) {
// If S is greater than half the order,
// it's too high.
if (sig.s.cmp(bcoin.ecdsa.nh) > 0)
sig.s = bcoin.ecdsa.n.sub(sig.s);
if (sig.s.cmp(ec.elliptic.nh) > 0)
sig.s = ec.elliptic.n.sub(sig.s);
return new Buffer(sig.toDER());
};

View File

@ -53,9 +53,12 @@
var bcoin = require('../bcoin');
var bn = require('bn.js');
var utils = require('./utils');
var ec = require('./ec');
var assert = utils.assert;
var constants = bcoin.protocol.constants;
var network = bcoin.protocol.network;
var KeyPair = require('./keypair');
var LRU = require('./lru');
var english = require('../../etc/english.json');
@ -83,12 +86,12 @@ HDSeed.prototype.createSeed = function createSeed() {
return this.seed;
if (!this.entropy)
this.entropy = bcoin.ec.random(this.bits / 8);
this.entropy = ec.random(this.bits / 8);
if (!this.mnemonic)
this.mnemonic = this.createMnemonic(this.entropy);
this.seed = pbkdf2(this.mnemonic, 'mnemonic' + this.passphrase, 2048, 64);
this.seed = utils.pbkdf2(this.mnemonic, 'mnemonic' + this.passphrase, 2048, 64);
return this.seed;
};
@ -125,7 +128,7 @@ HD.fromSeed = function fromSeed(options) {
return HDPrivateKey.fromSeed(options);
};
HD.cache = new bcoin.lru(500);
HD.cache = new LRU(500);
/**
* HD Private Key
@ -183,107 +186,6 @@ function HDPrivateKey(options) {
utils.inherits(HDPrivateKey, HD);
HDPrivateKey.prototype.scan44 = function scan44(options, txByAddress, callback) {
var self = this;
var accounts = [];
var isAccount = this.isAccount44();
var coinType, root;
// 0. get the root node
if (!isAccount) {
coinType = options.coinType;
if (coinType == null)
coinType = network[this.network].type === 'main' ? 0 : 1;
assert(utils.isFinite(coinType));
root = this
.derive(44, true)
.derive(coinType, true);
}
return (function chainCheck(chainConstant) {
return (function scanner(accountIndex) {
var addressIndex = 0;
var total = 0;
var gap = 0;
// 1. derive the first account's node (index = 0)
var account = isAccount
? self
: root.derive(accountIndex, true);
if (isAccount)
accountIndex = utils.readU32BE(self.childIndex) - constants.hd.hardened;
// 2. derive the external chain node of this account
var chain = account.derive(chainConstant);
// 3. scan addresses of the external chain;
// respect the gap limit described below
return (function next() {
var address = chain.derive(addressIndex++);
var addr = bcoin.address.compileData(address.publicKey);
return txByAddress(addr, function(err, txs) {
var result;
if (err)
return callback(err);
if (txs) {
if (typeof txs === 'boolean')
result = txs;
else if (typeof txs === 'number')
result = txs > 0;
else if (Array.isArray(txs))
result = txs.length > 0;
else
result = false;
}
if (result) {
total++;
gap = 0;
return next();
}
if (++gap < 20)
return next();
assert(accounts[accountIndex] == null || chainConstant === 1);
if (chainConstant === 0)
accounts[accountIndex] = { addressDepth: addressIndex - gap };
else
accounts[accountIndex].changeDepth = addressIndex - gap;
// 4. if no transactions are found on the
// external chain, stop discovery
if (total === 0) {
if (chainConstant === 0)
return chainCheck(1);
if (isAccount)
return callback(null, accounts[accountIndex]);
return callback(null, accounts);
}
// 5. if there are some transactions, increase
// the account index and go to step 1
if (isAccount) {
if (chainConstant === 0)
return chainCheck(1);
return callback(null, accounts[accountIndex]);
}
return scanner(accountIndex + 1);
});
})();
})(0);
})(0);
};
HDPrivateKey.prototype.deriveAccount44 = function deriveAccount44(options) {
var coinType, accountIndex, child;
@ -356,73 +258,6 @@ HDPrivateKey.prototype.deriveAddress = function deriveAddress(accountIndex, addr
});
};
HDPrivateKey.prototype.scan45 = function scan45(options, txByAddress, callback) {
var cosigners = [];
var root;
root = this.isPurpose45()
? this
: this.derivePurpose45(options);
return (function chainCheck(chainConstant) {
return (function scanner(cosignerIndex) {
var addressIndex = 0;
var total = 0;
var gap = 0;
var cosigner = root.derive(cosignerIndex);
var chain = cosigner.derive(chainConstant);
return (function next() {
var address = chain.derive(addressIndex++);
var addr = bcoin.address.compileData(address.publicKey);
return txByAddress(addr, function(err, txs) {
var result;
if (err)
return callback(err);
if (txs) {
if (typeof txs === 'boolean')
result = txs;
else if (typeof txs === 'number')
result = txs > 0;
else if (Array.isArray(txs))
result = txs.length > 0;
else
result = false;
}
if (result) {
total++;
gap = 0;
return next();
}
if (++gap < 20)
return next();
assert(cosigners[cosignerIndex] == null || chainConstant === 1);
if (chainConstant === 0)
cosigners[cosignerIndex] = { addressDepth: addressIndex - gap };
else
cosigners[cosignerIndex].changeDepth = addressIndex - gap;
if (total === 0) {
if (chainConstant === 0)
return chainCheck(1);
return callback(null, cosigners);
}
return scanner(cosignerIndex + 1);
});
})();
})(0);
})(0);
};
HDPrivateKey.prototype.derivePurpose45 = function derivePurpose45() {
var child;
@ -591,10 +426,10 @@ HDPrivateKey.fromSeed = function fromSeed(options) {
HDPrivateKey._generate = function _generate(privateKey, entropy) {
if (!privateKey)
privateKey = bcoin.ec.generatePrivateKey();
privateKey = ec.generatePrivateKey();
if (!entropy)
entropy = bcoin.ec.random(32);
entropy = ec.random(32);
return {
version: null,
@ -685,7 +520,7 @@ HDPrivateKey.prototype._build = function _build(data) {
this.privateKey = data.privateKey;
this.checksum = null;
this.publicKey = bcoin.ec.publicKeyCreate(data.privateKey, true);
this.publicKey = ec.publicKeyCreate(data.privateKey, true);
this.fingerPrint = null;
this.hdPrivateKey = this;
@ -746,7 +581,7 @@ HDPrivateKey.prototype.derive = function derive(index, hardened) {
privateKey = new Buffer(leftPart
.add(new bn(this.privateKey))
.mod(bcoin.ecdsa.curve.n)
.mod(ec.elliptic.curve.n)
.toArray('be', 32));
if (!this.fingerPrint) {
@ -1107,8 +942,8 @@ HDPublicKey.prototype.derive = function derive(index, hardened) {
leftPart = new bn(hash.slice(0, 32));
chainCode = hash.slice(32, 64);
publicPoint = bcoin.ecdsa.curve.decodePoint(this.publicKey);
point = bcoin.ecdsa.curve.g.mul(leftPart).add(publicPoint);
publicPoint = ec.elliptic.curve.decodePoint(this.publicKey);
point = ec.elliptic.curve.g.mul(leftPart).add(publicPoint);
publicKey = new Buffer(point.encode('array', true));
if (!this.fingerPrint) {
@ -1165,26 +1000,26 @@ HDPublicKey.prototype.derivePath = function derivePath(path) {
[HDPrivateKey, HDPublicKey].forEach(function(HD) {
HD.prototype.getPrivateKey = function getPrivateKey() {
return bcoin.keypair.prototype.getPrivateKey.apply(this, arguments);
return KeyPair.prototype.getPrivateKey.apply(this, arguments);
};
HD.prototype.getPublicKey = function getPublicKey() {
return bcoin.keypair.prototype.getPublicKey.apply(this, arguments);
return KeyPair.prototype.getPublicKey.apply(this, arguments);
};
HD.prototype.sign = function sign() {
return bcoin.keypair.prototype.sign.apply(this, arguments);
return KeyPair.prototype.sign.apply(this, arguments);
};
HD.prototype.verify = function verify() {
return bcoin.keypair.prototype.verify.apply(this, arguments);
return KeyPair.prototype.verify.apply(this, arguments);
};
HD.prototype.compressed = true;
});
HDPrivateKey.prototype.toSecret = function toSecret() {
return bcoin.keypair.toSecret.call(this);
return KeyPair.toSecret.call(this);
};
/**
@ -1197,73 +1032,6 @@ function array32(data) {
return b;
}
/**
* PDKBF2
* Credit to: https://github.com/stayradiated/pbkdf2-sha512
* Copyright (c) 2010-2011 Intalio Pte, All Rights Reserved
* Copyright (c) 2014, JP Richardson
*/
function pbkdf2(key, salt, iterations, dkLen) {
'use strict';
if (bcoin.crypto && bcoin.crypto.pbkdf2Sync)
return bcoin.crypto.pbkdf2Sync(key, salt, iterations, dkLen, 'sha512');
var hLen = 64;
if (dkLen > (Math.pow(2, 32) - 1) * hLen)
throw Error('Requested key length too long');
if (typeof key !== 'string' && typeof key.length !== 'number')
throw new TypeError('key must a string or array');
if (typeof salt !== 'string' && typeof salt.length !== 'number')
throw new TypeError('salt must a string or array');
if (typeof key === 'string')
key = new Buffer(key, 'ascii');
if (typeof salt === 'string')
salt = new Buffer(salt, 'ascii');
var DK = new Buffer(dkLen);
var U = new Buffer(hLen);
var T = new Buffer(hLen);
var block1 = new Buffer(salt.length + 4);
var l = Math.ceil(dkLen / hLen);
var r = dkLen - (l - 1) * hLen;
var i, j, k, destPos, len;
utils.copy(salt.slice(0, salt.length), block1, 0);
for (i = 1; i <= l; i++) {
block1[salt.length + 0] = i >> 24 & 0xff;
block1[salt.length + 1] = i >> 16 & 0xff;
block1[salt.length + 2] = i >> 8 & 0xff;
block1[salt.length + 3] = i >> 0 & 0xff;
U = utils.sha512hmac(block1, key);
utils.copy(U.slice(0, hLen), T, 0);
for (j = 1; j < iterations; j++) {
U = utils.sha512hmac(U, key);
for (k = 0; k < hLen; k++)
T[k] ^= U[k];
}
destPos = (i - 1) * hLen;
len = i === l ? r : hLen;
utils.copy(T.slice(0, len), DK, 0);
}
return DK;
}
/**
* Expose
*/
@ -1275,7 +1043,6 @@ exports.priv = HDPrivateKey;
exports.pub = HDPublicKey;
exports.privateKey = HDPrivateKey;
exports.publicKey = HDPublicKey;
exports.pbkdf2 = pbkdf2;
exports.fromJSON = HDPrivateKey.fromJSON;
module.exports = HD;

View File

@ -180,7 +180,7 @@ Peer.prototype._init = function init() {
Peer.prototype.createSocket = function createSocket(port, host) {
var self = this;
var socket;
var socket, net;
assert(port != null);
assert(host);
@ -190,10 +190,11 @@ Peer.prototype.createSocket = function createSocket(port, host) {
if (this._createSocket) {
socket = this._createSocket(port, host);
} else if (bcoin.isBrowser) {
throw new Error('Please include a `createSocket` callback.');
} else {
if (!bcoin.net)
throw new Error('Please include a `createSocket` callback.');
socket = bcoin.net.connect(port, host);
net = require('n' + 'et');
socket = net.connect(port, host);
}
utils.debug(

View File

@ -295,16 +295,19 @@ Pool.prototype.getHeaders = function getHeaders(peer, top, stop, callback) {
Pool.prototype.startServer = function startServer() {
var self = this;
var net;
if (!bcoin.net)
if (bcoin.isBrowser)
return;
net = require('n' + 'et');
if (!this.options.listen)
return;
assert(!this.server);
this.server = new bcoin.net.Server();
this.server = new net.Server();
this.server.on('connection', function(socket) {
self._addLeech(socket);
@ -321,7 +324,7 @@ Pool.prototype.startServer = function startServer() {
};
Pool.prototype.stopServer = function stopServer() {
if (!bcoin.net)
if (bcoin.isBrowser)
return;
if (!this.server)

View File

@ -7,11 +7,12 @@
var bcoin = require('../bcoin');
var utils = require('./utils');
var assert = utils.assert;
var fs = bcoin.fs;
var profiler;
var fs, profiler;
if (bcoin.profile && !bcoin.isBrowser)
if (bcoin.profile && !bcoin.isBrowser) {
profiler = require('v8-' + 'profiler');
fs = require('f' + 's');
}
if (profiler) {
utils.nextTick(function() {

View File

@ -14,11 +14,10 @@ utils.isBrowser =
(typeof process !== 'undefined' && process.browser)
|| typeof window !== 'undefined';
if (!utils.isBrowser) {
if (!utils.isBrowser)
crypto = require('cry' + 'pto');
} else {
else
hash = require('hash.js');
}
/**
* Utils
@ -246,6 +245,73 @@ utils.sha512hmac = function sha512hmac(data, salt) {
return result;
};
/**
* PDKBF2
* Credit to: https://github.com/stayradiated/pbkdf2-sha512
* Copyright (c) 2010-2011 Intalio Pte, All Rights Reserved
* Copyright (c) 2014, JP Richardson
*/
utils.pbkdf2 = function pbkdf2(key, salt, iterations, dkLen) {
'use strict';
if (crypto && crypto.pbkdf2Sync)
return crypto.pbkdf2Sync(key, salt, iterations, dkLen, 'sha512');
var hLen = 64;
if (dkLen > (Math.pow(2, 32) - 1) * hLen)
throw Error('Requested key length too long');
if (typeof key !== 'string' && typeof key.length !== 'number')
throw new TypeError('key must a string or array');
if (typeof salt !== 'string' && typeof salt.length !== 'number')
throw new TypeError('salt must a string or array');
if (typeof key === 'string')
key = new Buffer(key, 'ascii');
if (typeof salt === 'string')
salt = new Buffer(salt, 'ascii');
var DK = new Buffer(dkLen);
var U = new Buffer(hLen);
var T = new Buffer(hLen);
var block1 = new Buffer(salt.length + 4);
var l = Math.ceil(dkLen / hLen);
var r = dkLen - (l - 1) * hLen;
var i, j, k, destPos, len;
utils.copy(salt.slice(0, salt.length), block1, 0);
for (i = 1; i <= l; i++) {
block1[salt.length + 0] = i >> 24 & 0xff;
block1[salt.length + 1] = i >> 16 & 0xff;
block1[salt.length + 2] = i >> 8 & 0xff;
block1[salt.length + 3] = i >> 0 & 0xff;
U = utils.sha512hmac(block1, key);
utils.copy(U.slice(0, hLen), T, 0);
for (j = 1; j < iterations; j++) {
U = utils.sha512hmac(U, key);
for (k = 0; k < hLen; k++)
T[k] ^= U[k];
}
destPos = (i - 1) * hLen;
len = i === l ? r : hLen;
utils.copy(T.slice(0, len), DK, 0);
}
return DK;
};
utils.salt = 'bcoin:';
utils.encrypt = function encrypt(data, passphrase) {
@ -784,6 +850,7 @@ utils.print = function print() {
};
utils.debug = utils.nop;
utils.ensurePrefix = utils.nop;
utils.merge = function merge(target) {
var args = Array.prototype.slice.call(arguments, 1);

View File

@ -30,8 +30,7 @@
"dependencies": {
"bn.js": "4.10.3",
"elliptic": "6.0.2",
"rocksdown": "1.4.4-2",
"levelup": "1.3.1"
"leveldown": "1.4.4"
},
"optionalDependencies": {
"secp256k1": "3.0.0",

View File

@ -32,7 +32,7 @@ describe('HD', function() {
var master, child1, child2, child3, child4, child5, child6;
it('should create a pbkdf2 seed', function() {
var checkSeed = utils.toHex(bcoin.hd.pbkdf2(phrase, 'mnemonic' + 'foo', 2048, 64));
var checkSeed = utils.toHex(bcoin.utils.pbkdf2(phrase, 'mnemonic' + 'foo', 2048, 64));
assert.equal(checkSeed, seed);
});