From 67a00bfe50c2a8bd437a78fd9312e5794a7c5c2e Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Fri, 18 Nov 2016 23:04:37 -0800 Subject: [PATCH] modules: remove all conditional requires. see #105. --- browser/empty.js | 3 +- lib/bip70/index.js | 1 - lib/bip70/payment.js | 2 +- lib/bip70/paymentack.js | 2 +- lib/bip70/paymentdetails.js | 2 +- lib/bip70/paymentrequest.js | 6 +- lib/bip70/pk.js | 232 +------------- lib/bip70/x509.js | 13 +- lib/crypto/backend-browser.js | 73 +++++ lib/crypto/backend.js | 50 +++ lib/crypto/chachapoly.js | 2 +- lib/crypto/crypto.js | 104 ++----- lib/crypto/ec-elliptic.js | 347 +++++++++++++++++++++ lib/crypto/ec-secp256k1.js | 263 ++++++++++++++++ lib/crypto/ec.js | 519 +------------------------------ lib/crypto/pk-browser.js | 187 +++++++++++ lib/crypto/pk.js | 142 +++++++++ lib/crypto/random.js | 76 ----- lib/crypto/schnorr.js | 8 +- lib/crypto/scrypt-async.js | 2 +- lib/crypto/scrypt.js | 2 +- lib/crypto/siphash.js | 2 +- lib/db/backends-browser.js | 16 + lib/db/backends.js | 18 ++ lib/db/ldb.js | 28 +- lib/hd/mnemonic.js | 34 +- lib/hd/wordlist-browser.js | 28 ++ lib/hd/wordlist.js | 26 ++ lib/hd/words/index.js | 17 + lib/http/index.js | 20 +- lib/http/rpc.js | 12 +- lib/net/peer.js | 21 +- lib/net/pool.js | 24 +- lib/net/tcp-browser.js | 16 + lib/net/tcp.js | 16 + lib/node/config.js | 9 +- lib/node/fullnode.js | 10 +- lib/node/logger.js | 10 +- lib/node/spvnode.js | 10 +- lib/{bip70 => utils}/asn1.js | 228 ++++---------- lib/utils/base58.js | 2 +- lib/utils/murmur3.js | 2 +- lib/utils/native.js | 10 +- lib/utils/nexttick-browser.js | 7 + lib/utils/nexttick.js | 9 + lib/utils/nfkd-browser.js | 20 ++ lib/utils/nfkd.js | 23 ++ lib/utils/pem.js | 100 ++++++ lib/{bip70 => utils}/protobuf.js | 0 lib/utils/utils.js | 43 +-- lib/workers/workers.js | 13 +- package.json | 10 + vendor/ip.js | 9 +- vendor/setimmediate.js | 31 +- vendor/unorm.js | 61 +--- 55 files changed, 1615 insertions(+), 1306 deletions(-) create mode 100644 lib/crypto/backend-browser.js create mode 100644 lib/crypto/backend.js create mode 100644 lib/crypto/ec-elliptic.js create mode 100644 lib/crypto/ec-secp256k1.js create mode 100644 lib/crypto/pk-browser.js create mode 100644 lib/crypto/pk.js delete mode 100644 lib/crypto/random.js create mode 100644 lib/db/backends-browser.js create mode 100644 lib/db/backends.js create mode 100644 lib/hd/wordlist-browser.js create mode 100644 lib/hd/wordlist.js create mode 100644 lib/hd/words/index.js create mode 100644 lib/net/tcp-browser.js create mode 100644 lib/net/tcp.js rename lib/{bip70 => utils}/asn1.js (56%) create mode 100644 lib/utils/nexttick-browser.js create mode 100644 lib/utils/nexttick.js create mode 100644 lib/utils/nfkd-browser.js create mode 100644 lib/utils/nfkd.js create mode 100644 lib/utils/pem.js rename lib/{bip70 => utils}/protobuf.js (100%) diff --git a/browser/empty.js b/browser/empty.js index f7fc6dad..0da8625c 100644 --- a/browser/empty.js +++ b/browser/empty.js @@ -1,3 +1,2 @@ // Empty module for browserify. -module.exports = null; -throw new Error('Module not available.'); +exports.empty = true; diff --git a/lib/bip70/index.js b/lib/bip70/index.js index a3c90fab..f7502ac7 100644 --- a/lib/bip70/index.js +++ b/lib/bip70/index.js @@ -10,6 +10,5 @@ exports.PaymentRequest = require('./paymentrequest'); exports.PaymentDetails = require('./paymentdetails'); exports.Payment = require('./payment'); exports.PaymentACK = require('./paymentack'); -exports.asn1 = require('./asn1'); exports.x509 = require('./x509'); exports.pk = require('./pk'); diff --git a/lib/bip70/payment.js b/lib/bip70/payment.js index e539ac1d..80903ac1 100644 --- a/lib/bip70/payment.js +++ b/lib/bip70/payment.js @@ -10,7 +10,7 @@ var assert = require('assert'); var Output = require('../primitives/output'); var TX = require('../primitives/tx'); var Script = require('../script/script'); -var protobuf = require('./protobuf'); +var protobuf = require('../utils/protobuf'); var PaymentDetails = require('./paymentdetails'); var ProtoReader = protobuf.ProtoReader; var ProtoWriter = protobuf.ProtoWriter; diff --git a/lib/bip70/paymentack.js b/lib/bip70/paymentack.js index 37a90bfe..34247f37 100644 --- a/lib/bip70/paymentack.js +++ b/lib/bip70/paymentack.js @@ -7,7 +7,7 @@ 'use strict'; var assert = require('assert'); -var protobuf = require('./protobuf'); +var protobuf = require('../utils/protobuf'); var Payment = require('./payment'); var ProtoReader = protobuf.ProtoReader; var ProtoWriter = protobuf.ProtoWriter; diff --git a/lib/bip70/paymentdetails.js b/lib/bip70/paymentdetails.js index 45171f82..f3124fe8 100644 --- a/lib/bip70/paymentdetails.js +++ b/lib/bip70/paymentdetails.js @@ -9,7 +9,7 @@ var assert = require('assert'); var utils = require('../utils/utils'); var Output = require('../primitives/output'); -var protobuf = require('./protobuf'); +var protobuf = require('../utils/protobuf'); var ProtoReader = protobuf.ProtoReader; var ProtoWriter = protobuf.ProtoWriter; diff --git a/lib/bip70/paymentrequest.js b/lib/bip70/paymentrequest.js index 846c6718..9541fb2a 100644 --- a/lib/bip70/paymentrequest.js +++ b/lib/bip70/paymentrequest.js @@ -10,8 +10,8 @@ var assert = require('assert'); var utils = require('../utils/utils'); var crypto = require('../crypto/crypto'); var x509 = require('./x509'); -var asn1 = require('./asn1'); -var protobuf = require('./protobuf'); +var PEM = require('../utils/pem'); +var protobuf = require('../utils/protobuf'); var PaymentDetails = require('./paymentdetails'); var ProtoReader = protobuf.ProtoReader; var ProtoWriter = protobuf.ProtoWriter; @@ -154,7 +154,7 @@ PaymentRequest.prototype.setChain = function setChain(chain) { for (i = 0; i < chain.length; i++) { cert = chain[i]; if (typeof cert === 'string') { - pem = asn1.fromPEM(cert); + pem = PEM.decode(cert); assert(pem.type === 'certificate', 'Bad certificate PEM.'); cert = pem.data; } diff --git a/lib/bip70/pk.js b/lib/bip70/pk.js index 773b8c29..681b05b2 100644 --- a/lib/bip70/pk.js +++ b/lib/bip70/pk.js @@ -6,248 +6,38 @@ 'use strict'; -var BN = require('bn.js'); -var asn1 = require('./asn1'); -var elliptic = require('elliptic'); -var crypto = require('../crypto/crypto'); +var pk = require('../crypto/pk'); -var nativeCrypto; - -try { - nativeCrypto = require('crypto'); -} catch (e) { - ; -} - -var pk = exports; -var rsa = {}; -var ecdsa = {}; -var native = {}; - -rsa.prefixes = { - md5: new Buffer('3020300c06082a864886f70d020505000410', 'hex'), - sha1: new Buffer('3021300906052b0e03021a05000414', 'hex'), - sha224: new Buffer('302d300d06096086480165030402040500041c', 'hex'), - sha256: new Buffer('3031300d060960864801650304020105000420', 'hex'), - sha384: new Buffer('3041300d060960864801650304020205000430', 'hex'), - sha512: new Buffer('3051300d060960864801650304020305000440', 'hex'), - md5sha1: new Buffer(0), - ripemd160: new Buffer('30203008060628cf060300310414', 'hex') -}; - -// Ported from: -// https://github.com/golang/go/blob/master/src/crypto/rsa/pkcs1v15.go - -rsa.verify = function verify(hashAlg, msg, sig, key) { - var hash = crypto.hash(hashAlg, msg); - var prefix = rsa.prefixes[hashAlg]; - var len = prefix.length + hash.length; - var pub = asn1.parseRSAPublic(key); - var N = new BN(pub.modulus); - var e = new BN(pub.publicExponent); - var k = Math.ceil(N.bitLength() / 8); - var m, em, ok, i; - - if (k < len + 11) - throw new Error('Message too long.'); - - m = rsa.encrypt(N, e, sig); - em = leftpad(m, k); - - ok = crypto.ceq(em[0], 0x00); - ok &= crypto.ceq(em[1], 0x01); - ok &= crypto.ccmp(em.slice(k - hash.length, k), hash); - ok &= crypto.ccmp(em.slice(k - len, k - hash.length), prefix); - ok &= crypto.ceq(em[k - len - 1], 0x00); - - for (i = 2; i < k - len - 1; i++) - ok &= crypto.ceq(em[i], 0xff); - - return ok === 1; -}; - -rsa.sign = function sign(hashAlg, msg, key) { - var hash = crypto.hash(hashAlg, msg); - var prefix = rsa.prefixes[hashAlg]; - var len = prefix.length + hash.length; - var priv = asn1.parseRSAPrivate(key); - var N = new BN(priv.modulus); - var D = new BN(priv.privateExponent); - var k = Math.ceil(N.bitLength() / 8); - var i, em; - - if (k < len + 11) - throw new Error('Message too long.'); - - em = new Buffer(k); - em.fill(0); - - em[1] = 0x01; - for (i = 2; i < k - len - 1; i++) - em[i] = 0xff; - - prefix.copy(em, k - len); - hash.copy(em, k - hash.length); - - return rsa.decrypt(N, D, em); -}; - -rsa.decrypt = function decrypt(N, D, m) { - var c = new BN(m); - - if (c.cmp(N) > 0) - throw new Error('Cannot decrypt.'); - - return c - .toRed(BN.red(N)) - .redPow(D) - .fromRed() - .toArrayLike(Buffer, 'be'); -}; - -rsa.encrypt = function encrypt(N, e, m) { - return new BN(m) - .toRed(BN.red(N)) - .redPow(e) - .fromRed() - .toArrayLike(Buffer, 'be'); -}; - -ecdsa.verify = function verify(curve, msg, hashAlg, key, sig) { - var hash = crypto.hash(hashAlg, msg); - var ec = elliptic.ec(curve); - return ec.verify(hash, sig, key); -}; - -ecdsa.sign = function sign(curve, msg, hashAlg, key) { - var hash = crypto.hash(hashAlg, msg); - var ec = elliptic.ec(curve); - return new Buffer(ec.sign(hash, key)); -}; - -native.verify = function verify(alg, hash, msg, sig, key) { - var algo, verify; - - if (!nativeCrypto) - return false; - - algo = normalizeAlg(alg, hash); - verify = nativeCrypto.createVerify(algo); - verify.update(msg); - - return verify.verify(key, sig); -}; - -native.sign = function _sign(alg, hash, msg, key) { - var algo, sig; - - if (!nativeCrypto) - return false; - - algo = normalizeAlg(alg, hash); - sig = nativeCrypto.createSign(algo); - sig.update(msg); - return sig.sign(key); -}; - -pk.pemTag = { - dsa: 'DSA', - rsa: 'RSA', - ecdsa: 'EC' -}; - -pk.toPEM = function toPEM(key, type) { - var tag = pk.pemTag[key.alg]; - var pem = asn1.toPEM(key.data, tag, type); - - // Key parameters, usually present - // if selecting an EC curve. - if (key.params) - pem += asn1.toPEM(key.params, tag, 'parameters'); - - return pem; -}; - -pk._verify = function verify(hash, msg, sig, key) { - var pem; +exports._verify = function verify(hash, msg, sig, key) { switch (key.alg) { case 'dsa': - pem = pk.toPEM(key, 'public key'); - return native.verify(key.alg, hash, msg, sig, pem); + return pk.dsa.verify(hash, msg, sig, key.data, key.params); case 'rsa': - if (nativeCrypto) { - pem = pk.toPEM(key, 'public key'); - return native.verify(key.alg, hash, msg, sig, pem); - } - return rsa.verify(hash, msg, sig, key.data); + return pk.rsa.verify(hash, msg, sig, key.data); case 'ecdsa': - if (!key.curve) - throw new Error('No curve present.'); - return ecdsa.verify(key.curve, hash, msg, sig, key.data); + return pk.ecdsa.verify(key.curve, hash, msg, sig, key.data); default: throw new Error('Unsupported algorithm.'); } }; -pk.verify = function verify(hash, msg, sig, key) { +exports.verify = function verify(hash, msg, sig, key) { try { - return pk._verify(hash, msg, sig, key); + return exports._verify(hash, msg, sig, key); } catch (e) { return false; } }; -pk.sign = function sign(hash, msg, key) { - var pem; +exports.sign = function sign(hash, msg, key) { switch (key.alg) { case 'dsa': - pem = pk.toPEM(key, 'private key'); - return native.sign(key.alg, hash, msg, pem); + return pk.dsa.sign(hash, msg, key.data, key.params); case 'rsa': - if (nativeCrypto) { - pem = pk.toPEM(key, 'private key'); - return native.sign(key.alg, hash, msg, pem); - } - return rsa.sign(hash, msg, key.data); + return pk.rsa.sign(hash, msg, key.data); case 'ecdsa': - if (!key.curve) - throw new Error('No curve present.'); - return ecdsa.sign(key.curve, hash, msg, key.data); + return pk.ecdsa.sign(key.curve, hash, msg, key.data); default: throw new Error('Unsupported algorithm.'); } }; - -function leftpad(input, size) { - var n = input.length; - var out; - - if (n > size) - n = size; - - out = new Buffer(size); - out.fill(0); - - input.copy(out, out.length - n); - - return out; -} - -function normalizeAlg(alg, hash) { - var name = alg.toUpperCase() + '-' + hash.toUpperCase(); - - switch (name) { - case 'ECDSA-SHA1': - name = 'ecdsa-with-SHA1'; - break; - case 'ECDSA-SHA256': - name = 'ecdsa-with-SHA256'; - break; - } - - return name; -} - -pk.rsa = rsa; -pk.ecdsa = ecdsa; -pk.native = native; diff --git a/lib/bip70/x509.js b/lib/bip70/x509.js index 136ca1f0..e7b332b6 100644 --- a/lib/bip70/x509.js +++ b/lib/bip70/x509.js @@ -7,7 +7,8 @@ 'use strict'; var assert = require('assert'); -var asn1 = require('./asn1'); +var ASN1 = require('../utils/asn1'); +var PEM = require('../utils/pem'); var utils = require('../utils/utils'); var crypto = require('../crypto/crypto'); var pk = require('./pk'); @@ -69,7 +70,7 @@ x509.setTrust = function setTrust(certs) { } if (typeof cert === 'string') { - pem = asn1.fromPEM(cert); + pem = PEM.decode(cert); assert(pem.type === 'certificate', 'Must add certificates to trust.'); cert = pem.data; } @@ -137,7 +138,7 @@ x509.getCurve = function getCurve(params) { return; try { - oid = asn1.parseOID(params); + oid = ASN1.parseOID(params); } catch (e) { return; } @@ -147,7 +148,7 @@ x509.getCurve = function getCurve(params) { x509.parse = function parse(der) { try { - return asn1.parseCert(der); + return ASN1.parseCert(der); } catch (e) { ; } @@ -189,7 +190,7 @@ x509.signSubject = function signSubject(hash, msg, key, chain) { assert(cert, 'Could not parse certificate.'); if (typeof key === 'string') { - key = asn1.fromPEM(key); + key = PEM.decode(key); if (key.alg === 'ecdsa') curve = x509.getCurve(key.params); key = { @@ -291,8 +292,6 @@ x509.verifyChain = function verifyChain(chain) { return false; }; -x509.asn1 = asn1; - function isHash(data) { if (typeof data === 'string') return utils.isHex(data) && data.length === 64; diff --git a/lib/crypto/backend-browser.js b/lib/crypto/backend-browser.js new file mode 100644 index 00000000..4ce7807e --- /dev/null +++ b/lib/crypto/backend-browser.js @@ -0,0 +1,73 @@ +/*! + * backend-browser.js - browser crypto backend for bcoin + * Copyright (c) 2014-2016, Christopher Jeffrey (MIT License). + * https://github.com/bcoin-org/bcoin + */ + +/* jshint worker: true */ + +'use strict'; + +var assert = require('assert'); +var hashjs = require('hash.js'); +var aes = require('./aes'); +var backend = exports; +var crypto, global; + +if (typeof window !== 'undefined') + global = window; +else if (typeof self !== 'undefined') + global = self; + +if (global) + crypto = global.crypto || global.msCrypto; + +backend.hash = function hash(alg, data) { + return new Buffer(hashjs[alg]().update(data).digest()); +}; + +backend.hmac = function hmac(alg, data, salt) { + var hash = hashjs[alg]; + var hmac; + + assert(hash != null, 'Unknown algorithm.'); + + hmac = hashjs.hmac(hash, salt); + + return new Buffer(hmac.update(data).digest()); +}; + +backend.pbkdf2 = null; + +backend.pbkdf2Async = null; + +backend.encipher = function encipher(data, key, iv) { + return aes.cbc.encrypt(data, key, iv); +}; + +backend.decipher = function decipher(data, key, iv) { + try { + return aes.cbc.decrypt(data, key, iv); + } catch (e) { + throw new Error('Bad key for decryption.'); + } +}; + +backend.randomBytes = function randomBytes(n) { + var data = new Uint8Array(n); + crypto.getRandomValues(data); + return new Buffer(data.buffer); +}; + +if (!crypto || !crypto.getRandomValues) { + // Out of luck here. Use bad randomness for now. + backend.randomBytes = function randomBytes(n) { + var data = new Buffer(n); + var i; + + for (i = 0; i < data.length; i++) + data[i] = Math.floor(Math.random() * 256); + + return data; + }; +} diff --git a/lib/crypto/backend.js b/lib/crypto/backend.js new file mode 100644 index 00000000..b8918419 --- /dev/null +++ b/lib/crypto/backend.js @@ -0,0 +1,50 @@ +/*! + * backend.js - crypto backend for bcoin + * Copyright (c) 2014-2016, Christopher Jeffrey (MIT License). + * https://github.com/bcoin-org/bcoin + */ + +'use strict'; + +var utils = require('../utils/utils'); +var crypto = require('crypto'); +var backend = exports; + +backend.hash = function hash(alg, data) { + return crypto.createHash(alg).update(data).digest(); +}; + +backend.hmac = function hmac(alg, data, salt) { + var hmac = crypto.createHmac(alg, salt); + return hmac.update(data).digest(); +}; + +backend.pbkdf2 = function pbkdf2(key, salt, iter, len, alg) { + return crypto.pbkdf2Sync(key, salt, iter, len, alg); +}; + +if (!crypto.pbkdf2Sync) + backend.pbkdf2 = null; + +backend.pbkdf2Async = function pbkdf2Async(key, salt, iter, len, alg, callback) { + return crypto.pbkdf2(key, salt, iter, len, alg, callback); +}; + +if (!crypto.pbkdf2) + backend.pbkdf2Async = null; + +backend.encipher = function encipher(data, key, iv) { + var cipher = crypto.createCipheriv('aes-256-cbc', key, iv); + return utils.concat(cipher.update(data), cipher.final()); +}; + +backend.decipher = function decipher(data, key, iv) { + var decipher = crypto.createDecipheriv('aes-256-cbc', key, iv); + try { + return utils.concat(decipher.update(data), decipher.final()); + } catch (e) { + throw new Error('Bad key for decryption.'); + } +}; + +backend.randomBytes = crypto.randomBytes; diff --git a/lib/crypto/chachapoly.js b/lib/crypto/chachapoly.js index 5fa19207..c4d9f6fd 100644 --- a/lib/crypto/chachapoly.js +++ b/lib/crypto/chachapoly.js @@ -7,7 +7,7 @@ 'use strict'; var assert = require('assert'); -var native = require('../utils/native'); +var native = require('../utils/native').binding; var BIG_ENDIAN = new Int8Array(new Int16Array([1]).buffer)[0] === 0; diff --git a/lib/crypto/crypto.js b/lib/crypto/crypto.js index 5b73aacd..9b13a840 100644 --- a/lib/crypto/crypto.js +++ b/lib/crypto/crypto.js @@ -8,25 +8,13 @@ 'use strict'; var assert = require('assert'); -var random = require('./random'); var scrypt = require('./scrypt'); var scryptAsync = require('./scrypt-async'); var utils = require('../utils/utils'); var co = require('../utils/co'); -var native = require('../utils/native'); +var native = require('../utils/native').binding; +var backend = require('./backend'); var lazy = require('../utils/lazy')(require, exports); -var nodeCrypto, hash, aes; - -var isBrowser = - (typeof process !== 'undefined' && process.browser) - || typeof window !== 'undefined'; - -if (!isBrowser) { - nodeCrypto = require('crypto'); -} else { - hash = require('hash.js'); - aes = require('./aes'); -} /** * @exports crypto @@ -42,10 +30,7 @@ var crypto = exports; */ crypto.hash = function _hash(alg, data) { - if (!nodeCrypto) - return new Buffer(hash[alg]().update(data).digest()); - - return nodeCrypto.createHash(alg).update(data).digest(); + return backend.hash(alg, data); }; if (native) @@ -129,15 +114,7 @@ crypto.checksum = function checksum(data) { */ crypto.hmac = function hmac(alg, data, salt) { - var hmac; - - if (!nodeCrypto) { - hmac = hash.hmac(hash[alg], salt); - return new Buffer(hmac.update(data).digest()); - } - - hmac = nodeCrypto.createHmac(alg, salt); - return hmac.update(data).digest(); + return backend.hmac(alg, data, salt); }; if (native) @@ -160,8 +137,8 @@ crypto.pbkdf2 = function pbkdf2(key, salt, iter, len, alg) { if (typeof salt === 'string') salt = new Buffer(salt, 'utf8'); - if (nodeCrypto && nodeCrypto.pbkdf2Sync) - return nodeCrypto.pbkdf2Sync(key, salt, iter, len, alg); + if (backend.pbkdf2) + return backend.pbkdf2(key, salt, iter, len, alg); return crypto._pbkdf2(key, salt, iter, len, alg); }; @@ -185,9 +162,9 @@ crypto.pbkdf2Async = function pbkdf2Async(key, salt, iter, len, alg) { if (typeof salt === 'string') salt = new Buffer(salt, 'utf8'); - if (nodeCrypto && nodeCrypto.pbkdf2) { + if (backend.pbkdf2Async) { return new Promise(function(resolve, reject) { - nodeCrypto.pbkdf2(key, salt, iter, len, alg, co.wrap(resolve, reject)); + backend.pbkdf2Async(key, salt, iter, len, alg, co.wrap(resolve, reject)); }); } @@ -292,20 +269,13 @@ crypto.encrypt = co(function* encrypt(data, passphrase, iv) { */ crypto.encipher = function encipher(data, key, iv) { - var cipher; - assert(Buffer.isBuffer(data)); assert(Buffer.isBuffer(key)); assert(Buffer.isBuffer(iv)); assert(key.length === 32); assert(iv.length === 16); - if (!nodeCrypto) - return aes.cbc.encrypt(data, key, iv); - - cipher = nodeCrypto.createCipheriv('aes-256-cbc', key, iv); - - return utils.concat(cipher.update(data), cipher.final()); + return backend.encipher(data, key, iv); }; /** @@ -351,31 +321,7 @@ crypto.decipher = function decipher(data, key, iv) { assert(Buffer.isBuffer(iv)); assert(key.length === 32); assert(iv.length === 16); - try { - return crypto._decipher(data, key, iv); - } catch (e) { - throw new Error('Bad key for decryption.'); - } -}; - -/** - * Decrypt with aes-256-cbc. - * @private - * @param {Buffer} data - * @param {Buffer} key - * @param {Buffer} iv - * @returns {Buffer} - */ - -crypto._decipher = function decipher(data, key, iv) { - var decipher; - - if (!nodeCrypto) - return aes.cbc.decrypt(data, key, iv); - - decipher = nodeCrypto.createDecipheriv('aes-256-cbc', key, iv); - - return utils.concat(decipher.update(data), decipher.final()); + return backend.decipher(data, key, iv); }; /** @@ -661,7 +607,19 @@ if (native) * @returns {Buffer} */ -crypto.randomBytes = random.randomBytes; +crypto.randomBytes = backend.randomBytes; + +/** + * Generate a random uint32. + * Probably more cryptographically sound than + * `Math.random()`. + * @function + * @returns {Number} + */ + +crypto.randomInt = function randomInt() { + return crypto.randomBytes(4).readUInt32LE(0, true); +}; /** * Generate a random number within a range. @@ -673,17 +631,10 @@ crypto.randomBytes = random.randomBytes; * @returns {Number} */ -crypto.randomRange = random.randomRange; - -/** - * Generate a random uint32. - * Probably more cryptographically sound than - * `Math.random()`. - * @function - * @returns {Number} - */ - -crypto.randomInt = random.randomInt; +crypto.randomRange = function randomRange(min, max) { + var num = crypto.randomInt(); + return Math.floor((num / 0x100000000) * (max - min) + min); +}; /* * Expose other objects. @@ -694,3 +645,4 @@ lazy('chachapoly', './chachapoly'); lazy('ec', './ec'); lazy('schnorr', './schnorr'); lazy('siphash', './siphash'); +lazy('pk', './pk'); diff --git a/lib/crypto/ec-elliptic.js b/lib/crypto/ec-elliptic.js new file mode 100644 index 00000000..26af627f --- /dev/null +++ b/lib/crypto/ec-elliptic.js @@ -0,0 +1,347 @@ +/*! + * ec.js - ecdsa wrapper for elliptic + * Copyright (c) 2014-2015, Fedor Indutny (MIT License) + * Copyright (c) 2014-2016, Christopher Jeffrey (MIT License). + * https://github.com/bcoin-org/bcoin + */ + +'use strict'; + +var assert = require('assert'); +var elliptic = require('elliptic'); +var secp256k1 = elliptic.ec('secp256k1'); +var Signature = require('elliptic/lib/elliptic/ec/signature'); +var curve = elliptic.curve; +var BN = require('bn.js'); + +/** + * @exports ec + */ + +var ec = exports; + +/** + * Generate a private key. + * @returns {Buffer} Private key. + */ + +ec.generatePrivateKey = function generatePrivateKey() { + var key = secp256k1.genKeyPair(); + var priv = key.getPrivate().toArrayLike(Buffer, 'be', 32); + return priv; +}; + +/** + * Create a public key from a private key. + * @param {Buffer} priv + * @param {Boolean?} compressed + * @returns {Buffer} + */ + +ec.publicKeyCreate = function publicKeyCreate(priv, compressed) { + var key; + + assert(Buffer.isBuffer(priv)); + + key = secp256k1.keyPair({ priv: priv }); + key = key.getPublic(compressed !== false, 'array'); + + return new Buffer(key); +}; + +/** + * Compress or decompress public key. + * @param {Buffer} pub + * @returns {Buffer} + */ + +ec.publicKeyConvert = function publicKeyConvert(key, compressed) { + var point = curve.decodePoint(key); + return new Buffer(point.encode('array', compressed !== false)); +}; + +/** + * ((tweak + key) % n) + * @param {Buffer} privateKey + * @param {Buffer} tweak + * @returns {Buffer} privateKey + */ + +ec.privateKeyTweakAdd = function privateKeyTweakAdd(privateKey, tweak) { + var key = new BN(tweak) + .add(new BN(privateKey)) + .mod(curve.n) + .toArrayLike(Buffer, 'be', 32); + + // Only a 1 in 2^127 chance of happening. + if (!ec.privateKeyVerify(key)) + throw new Error('Private key is invalid.'); + + return key; +}; + +/** + * ((g * tweak) + key) + * @param {Buffer} publicKey + * @param {Buffer} tweak + * @returns {Buffer} publicKey + */ + +ec.publicKeyTweakAdd = function publicKeyTweakAdd(publicKey, tweak, compressed) { + var key = curve.decodePoint(publicKey); + var point = curve.g.mul(new BN(tweak)).add(key); + var pub = new Buffer(point.encode('array', compressed !== false)); + + if (!ec.publicKeyVerify(pub)) + throw new Error('Public key is invalid.'); + + return pub; +}; + +/** + * Create an ecdh. + * @param {Buffer} pub + * @param {Buffer} priv + * @returns {Buffer} + */ + +ec.ecdh = function ecdh(pub, priv) { + priv = secp256k1.keyPair({ priv: priv }); + pub = secp256k1.keyPair({ pub: pub }); + return priv.derive(pub.getPublic()).toArrayLike(Buffer, 'be', 32); +}; + +/** + * Recover a public key. + * @param {Buffer} msg + * @param {Buffer} sig + * @param {Number?} j + * @param {Boolean?} compressed + * @returns {Buffer[]|Buffer|null} + */ + +ec.recover = function recover(msg, sig, j, compressed) { + var point, key; + + if (!j) + j = 0; + + try { + point = secp256k1.recoverPubKey(msg, sig, j); + } catch (e) { + return; + } + + key = point.encode('array', compressed !== false); + + return new Buffer(key); +}; + +/** + * Verify a signature. + * @param {Buffer} msg + * @param {Buffer} sig - DER formatted. + * @param {Buffer} key + * @param {Boolean?} - Whether this should be treated as a + * "historical" signature. This allows signatures to be of + * odd lengths. + * @param {Boolean?} high - Allow high S value. + * @returns {Boolean} + */ + +ec.verify = function verify(msg, sig, key, historical, high) { + assert(Buffer.isBuffer(msg)); + assert(Buffer.isBuffer(sig)); + assert(Buffer.isBuffer(key)); + + if (sig.length === 0) + return false; + + if (key.length === 0) + return false; + + // Attempt to normalize the signature + // length before passing to elliptic. + // Note: We only do this for historical data! + // https://github.com/indutny/elliptic/issues/78 + if (historical) + sig = normalizeLength(sig); + + // Make elliptic mimic secp256k1's + // failure with high S values. + if (!high && !ec.isLowS(sig)) + return false; + + try { + return secp256k1.verify(msg, sig, key); + } catch (e) { + return false; + } +}; + +/** + * Validate a public key. + * @param {Buffer} key + * @returns {Boolean} True if buffer is a valid public key. + */ + +ec.publicKeyVerify = function publicKeyVerify(key) { + try { + return secp256k1.keyPair({ pub: key }).validate(); + } catch (e) { + return false; + } +}; + +/** + * Validate a private key. + * @param {Buffer} key + * @returns {Boolean} True if buffer is a valid private key. + */ + +ec.privateKeyVerify = function privateKeyVerify(key) { + if (key.length !== 32) + return false; + + key = new BN(key); + + return key.cmpn(0) !== 0 && key.cmp(curve.n) < 0; +}; + +/** + * Sign a message. + * @param {Buffer} msg + * @param {Buffer} key - Private key. + * @returns {Buffer} DER-formatted signature. + */ + +ec.sign = function sign(msg, key) { + var sig; + + assert(Buffer.isBuffer(msg)); + assert(Buffer.isBuffer(key)); + + // Sign message and ensure low S value + sig = secp256k1.sign(msg, key, { canonical: true }); + + // Convert to DER array + sig = new Buffer(sig.toDER()); + + return sig; +}; + +/** + * Convert DER signature to R/S. + * @param {Buffer} sig + * @returns {Buffer} R/S-formatted signature. + */ + +ec.fromDER = function fromDER(sig) { + var out; + + assert(Buffer.isBuffer(sig)); + + sig = new Signature(sig); + out = new Buffer(64); + + sig.r.toArrayLike(Buffer, 'be', 32).copy(out, 0); + sig.s.toArrayLike(Buffer, 'be', 32).copy(out, 32); + + return out; +}; + +/** + * Convert R/S signature to DER. + * @param {Buffer} sig + * @returns {Buffer} DER-formatted signature. + */ + +ec.toDER = function toDER(sig) { + var out; + + assert(Buffer.isBuffer(sig)); + + out = new Signature({ + r: new BN(sig.slice(0, 32), 'be'), + s: new BN(sig.slice(32, 64), 'be') + }); + + return new Buffer(out.toDER()); +}; + +/** + * Test whether a signature has a low S value. + * @param {Buffer} sig + * @returns {Boolean} + */ + +ec.isLowS = function isLowS(sig) { + try { + sig = new Signature(sig); + } catch (e) { + return false; + } + + if (sig.s.cmpn(0) === 0) + return false; + + // If S is greater than half the order, + // it's too high. + if (sig.s.cmp(secp256k1.nh) > 0) + return false; + + return true; +}; + +/* + * Helpers + */ + +function normalizeLength(sig) { + var data = sig; + var p = { place: 0 }; + var len, rlen, slen; + + if (data[p.place++] !== 0x30) + return sig; + + len = getLength(data, p); + + if (data.length > len + p.place) + data = data.slice(0, len + p.place); + + if (data[p.place++] !== 0x02) + return sig; + + rlen = getLength(data, p); + p.place += rlen; + + if (data[p.place++] !== 0x02) + return sig; + + slen = getLength(data, p); + if (data.length > slen + p.place) + data = data.slice(0, slen + p.place); + + return data; +} + +function getLength(buf, p) { + var initial = buf[p.place++]; + var octetLen, val, i, off; + + if (!(initial & 0x80)) + return initial; + + octetLen = initial & 0xf; + val = 0; + + for (i = 0, off = p.place; i < octetLen; i++, off++) { + val <<= 8; + val |= buf[off]; + } + + p.place = off; + + return val; +} diff --git a/lib/crypto/ec-secp256k1.js b/lib/crypto/ec-secp256k1.js new file mode 100644 index 00000000..5bf3033b --- /dev/null +++ b/lib/crypto/ec-secp256k1.js @@ -0,0 +1,263 @@ +/*! + * ec-secp256k1.js - ecdsa wrapper for secp256k1 + * Copyright (c) 2014-2015, Fedor Indutny (MIT License) + * Copyright (c) 2014-2016, Christopher Jeffrey (MIT License). + * https://github.com/bcoin-org/bcoin + */ + +'use strict'; + +var assert = require('assert'); +var utils = require('../utils/utils'); +var crypto = require('./crypto'); +var secp256k1 = require('secp256k1'); + +/* + * Constants + */ + +var ZERO_S = new Buffer( + '0000000000000000000000000000000000000000000000000000000000000000', + 'hex' +); + +var HALF_ORDER = new Buffer( + '7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0', + 'hex'); + +/** + * @exports ec + */ + +var ec = exports; + +/** + * Generate a private key. + * @returns {Buffer} Private key. + */ + +ec.generatePrivateKey = function generatePrivateKey() { + var priv; + + do { + priv = crypto.randomBytes(32); + } while (!secp256k1.privateKeyVerify(priv)); + + return priv; +}; + +/** + * Create a public key from a private key. + * @param {Buffer} priv + * @param {Boolean?} compressed + * @returns {Buffer} + */ + +ec.publicKeyCreate = function publicKeyCreate(priv, compressed) { + assert(Buffer.isBuffer(priv)); + return secp256k1.publicKeyCreate(priv, compressed); +}; + +/** + * Compress or decompress public key. + * @param {Buffer} pub + * @returns {Buffer} + */ + +ec.publicKeyConvert = function publicKeyConvert(key, compressed) { + return secp256k1.publicKeyConvert(key, compressed); +}; + +/** + * ((tweak + key) % n) + * @param {Buffer} privateKey + * @param {Buffer} tweak + * @returns {Buffer} privateKey + */ + +ec.privateKeyTweakAdd = function privateKeyTweakAdd(privateKey, tweak) { + return secp256k1.privateKeyTweakAdd(privateKey, tweak); +}; + +/** + * ((g * tweak) + key) + * @param {Buffer} publicKey + * @param {Buffer} tweak + * @returns {Buffer} publicKey + */ + +ec.publicKeyTweakAdd = function publicKeyTweakAdd(publicKey, tweak, compressed) { + return secp256k1.publicKeyTweakAdd(publicKey, tweak, compressed); +}; + +/** + * Create an ecdh. + * @param {Buffer} pub + * @param {Buffer} priv + * @returns {Buffer} + */ + +ec.ecdh = function ecdh(pub, priv) { + var point = secp256k1.ecdhUnsafe(pub, priv, true); + return point.slice(1, 33); +}; + +/** + * Recover a public key. + * @param {Buffer} msg + * @param {Buffer} sig + * @param {Number?} j + * @param {Boolean?} compressed + * @returns {Buffer[]|Buffer|null} + */ + +ec.recover = function recover(msg, sig, j, compressed) { + var key; + + if (!j) + j = 0; + + try { + sig = secp256k1.signatureImport(sig); + } catch (e) { + return; + } + + try { + key = secp256k1.recover(msg, sig, j, compressed); + } catch (e) { + return; + } + + return key; +}; + +/** + * Verify a signature. + * @param {Buffer} msg + * @param {Buffer} sig - DER formatted. + * @param {Buffer} key + * @param {Boolean?} - Whether this should be treated as a + * "historical" signature. This allows signatures to be of + * odd lengths. + * @param {Boolean?} high - Allow high S value. + * @returns {Boolean} + */ + +ec.verify = function verify(msg, sig, key, historical, high) { + assert(Buffer.isBuffer(msg)); + assert(Buffer.isBuffer(sig)); + assert(Buffer.isBuffer(key)); + + if (sig.length === 0) + return false; + + if (key.length === 0) + return false; + + try { + if (historical) + sig = secp256k1.signatureImportLax(sig); + else + sig = secp256k1.signatureImport(sig); + + if (high) + sig = secp256k1.signatureNormalize(sig); + + return secp256k1.verify(msg, sig, key); + } catch (e) { + return false; + } +}; + +/** + * Validate a public key. + * @param {Buffer} key + * @returns {Boolean} True if buffer is a valid public key. + */ + +ec.publicKeyVerify = function publicKeyVerify(key) { + return secp256k1.publicKeyVerify(key); +}; + +/** + * Validate a private key. + * @param {Buffer} key + * @returns {Boolean} True if buffer is a valid private key. + */ + +ec.privateKeyVerify = function privateKeyVerify(key) { + return secp256k1.privateKeyVerify(key); +}; + +/** + * Sign a message. + * @param {Buffer} msg + * @param {Buffer} key - Private key. + * @returns {Buffer} DER-formatted signature. + */ + +ec.sign = function sign(msg, key) { + var sig; + + assert(Buffer.isBuffer(msg)); + assert(Buffer.isBuffer(key)); + + // Sign message + sig = secp256k1.sign(msg, key); + + // Ensure low S value + sig = secp256k1.signatureNormalize(sig.signature); + + // Convert to DER array + return secp256k1.signatureExport(sig); +}; + +/** + * Convert DER signature to R/S. + * @param {Buffer} sig + * @returns {Buffer} R/S-formatted signature. + */ + +ec.fromDER = function fromDER(sig) { + assert(Buffer.isBuffer(sig)); + return secp256k1.signatureImport(sig); +}; + +/** + * Convert R/S signature to DER. + * @param {Buffer} sig + * @returns {Buffer} DER-formatted signature. + */ + +ec.toDER = function toDER(sig) { + assert(Buffer.isBuffer(sig)); + return secp256k1.signatureExport(sig); +}; + +/** + * Test whether a signature has a low S value. + * @param {Buffer} sig + * @returns {Boolean} + */ + +ec.isLowS = function isLowS(sig) { + var rs, s; + + try { + rs = secp256k1.signatureImport(sig); + s = rs.slice(32, 64); + } catch (e) { + return false; + } + + if (utils.equal(s, ZERO_S)) + return false; + + // If S is greater than half the order, + // it's too high. + if (utils.cmp(s, HALF_ORDER) > 0) + return false; + + return true; +}; diff --git a/lib/crypto/ec.js b/lib/crypto/ec.js index 98f4c32d..864ccda4 100644 --- a/lib/crypto/ec.js +++ b/lib/crypto/ec.js @@ -1,524 +1,21 @@ /*! * ec.js - ecdsa wrapper for secp256k1 and elliptic - * Copyright (c) 2014-2015, Fedor Indutny (MIT License) * Copyright (c) 2014-2016, Christopher Jeffrey (MIT License). * https://github.com/bcoin-org/bcoin */ 'use strict'; -var elliptic = require('elliptic'); -var BN = require('bn.js'); -var utils = require('../utils/utils'); -var crypto = require('./crypto'); -var assert = require('assert'); var secp256k1; -try { - if (+process.env.BCOIN_USE_ELLIPTIC !== 1) +if (+process.env.BCOIN_USE_ELLIPTIC !== 1) { + try { secp256k1 = require('secp256k1'); -} catch (e) { - ; + } catch (e) { + ; + } } -/* - * Constants - */ - -var ZERO_S = new Buffer( - '0000000000000000000000000000000000000000000000000000000000000000', - 'hex' -); - -var HALF_ORDER = new Buffer( - '7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0', - 'hex'); - -/** - * @exports ec - */ - -var ec = exports; - -/** - * elliptic.js secp256k1 curve. - * @type {Object} - */ - -ec.elliptic = elliptic.ec('secp256k1'); - -/** - * elliptic.js signature constructor. - * @static - * @type {Function} - */ - -ec.signature = require('elliptic/lib/elliptic/ec/signature'); - -/** - * elliptic.js keypair constructor. - * @static - * @type {Function} - */ - -ec.keypair = require('elliptic/lib/elliptic/ec/key'); - -/** - * A reference to the secp256k1 curve. - * @const {Object} - */ - -ec.curve = ec.elliptic.curve; - -/** - * Generate a private key. - * @returns {Buffer} Private key. - */ - -ec.generatePrivateKey = function generatePrivateKey() { - var key, priv; - - if (secp256k1) { - do { - priv = crypto.randomBytes(32); - } while (!secp256k1.privateKeyVerify(priv)); - } else { - key = ec.elliptic.genKeyPair(); - priv = key.getPrivate().toArrayLike(Buffer, 'be', 32); - } - - return priv; -}; - -/** - * Create a public key from a private key. - * @param {Buffer} priv - * @param {Boolean?} compressed - * @returns {Buffer} - */ - -ec.publicKeyCreate = function publicKeyCreate(priv, compressed) { - assert(Buffer.isBuffer(priv)); - - if (secp256k1) - return secp256k1.publicKeyCreate(priv, compressed); - - priv = ec.elliptic.keyPair({ priv: priv }); - priv = priv.getPublic(compressed !== false, 'array'); - - return new Buffer(priv); -}; - -/** - * Compress or decompress public key. - * @param {Buffer} pub - * @returns {Buffer} - */ - -ec.publicKeyConvert = function publicKeyConvert(key, compressed) { - var point; - - if (secp256k1) - return secp256k1.publicKeyConvert(key, compressed); - - point = ec.curve.decodePoint(key); - - return new Buffer(point.encode('array', compressed !== false)); -}; - -/** - * ((tweak + key) % n) - * @param {Buffer} privateKey - * @param {Buffer} tweak - * @returns {Buffer} privateKey - */ - -ec.privateKeyTweakAdd = function privateKeyTweakAdd(privateKey, tweak) { - var key; - - if (secp256k1) - return secp256k1.privateKeyTweakAdd(privateKey, tweak); - - key = new BN(tweak) - .add(new BN(privateKey)) - .mod(ec.curve.n) - .toArrayLike(Buffer, 'be', 32); - - // Only a 1 in 2^127 chance of happening. - if (!ec.privateKeyVerify(key)) - throw new Error('Private key is invalid.'); - - return key; -}; - -/** - * ((g * tweak) + key) - * @param {Buffer} publicKey - * @param {Buffer} tweak - * @returns {Buffer} publicKey - */ - -ec.publicKeyTweakAdd = function publicKeyTweakAdd(publicKey, tweak, compressed) { - var point, key; - - if (secp256k1) - return secp256k1.publicKeyTweakAdd(publicKey, tweak, compressed); - - point = ec.curve.decodePoint(publicKey); - point = ec.curve.g.mul(new BN(tweak)).add(point); - key = new Buffer(point.encode('array', compressed !== false)); - - if (!ec.publicKeyVerify(key)) - throw new Error('Public key is invalid.'); - - return key; -}; - -/** - * Create an ecdh. - * @param {Buffer} pub - * @param {Buffer} priv - * @returns {Buffer} - */ - -ec.ecdh = function ecdh(pub, priv) { - var point; - - if (secp256k1) { - point = secp256k1.ecdhUnsafe(pub, priv, true); - return point.slice(1, 33); - } - - priv = ec.elliptic.keyPair({ priv: priv }); - pub = ec.elliptic.keyPair({ pub: pub }); - - return priv.derive(pub.getPublic()).toArrayLike(Buffer, 'be', 32); -}; - -/** - * Recover a public key. - * @param {Buffer} msg - * @param {Buffer} sig - * @param {Number?} j - * @param {Boolean?} compressed - * @returns {Buffer[]|Buffer|null} - */ - -ec.recover = function recover(msg, sig, j, compressed) { - var point, key; - - if (!j) - j = 0; - - if (secp256k1) { - try { - sig = secp256k1.signatureImport(sig); - } catch (e) { - return; - } - - try { - key = secp256k1.recover(msg, sig, j, compressed); - } catch (e) { - return; - } - - return key; - } - - try { - point = ec.elliptic.recoverPubKey(msg, sig, j); - } catch (e) { - return; - } - - key = point.encode('array', compressed !== false); - - return new Buffer(key); -}; - -/** - * Verify a signature. - * @param {Buffer} msg - * @param {Buffer} sig - DER formatted. - * @param {Buffer} key - * @param {Boolean?} - Whether this should be treated as a - * "historical" signature. This allows signatures to be of - * odd lengths. - * @param {Boolean?} high - Allow high S value. - * @returns {Boolean} - */ - -ec.verify = function verify(msg, sig, key, historical, high) { - var result; - - assert(Buffer.isBuffer(msg)); - assert(Buffer.isBuffer(sig)); - assert(Buffer.isBuffer(key)); - - if (sig.length === 0) - return false; - - if (key.length === 0) - return false; - - if (secp256k1) { - try { - if (historical) - sig = secp256k1.signatureImportLax(sig); - else - sig = secp256k1.signatureImport(sig); - - if (high) - sig = secp256k1.signatureNormalize(sig); - - result = secp256k1.verify(msg, sig, key); - } catch (e) { - result = false; - } - - return result; - } - - // Attempt to normalize the signature - // length before passing to elliptic. - // Note: We only do this for historical data! - // https://github.com/indutny/elliptic/issues/78 - if (historical) - sig = ec.normalizeLength(sig); - - // Make elliptic mimic secp256k1's - // failure with high S values. - if (!high && !ec.isLowS(sig)) - return false; - - try { - result = ec.elliptic.verify(msg, sig, key); - } catch (e) { - result = false; - } - - return result; -}; - -/** - * Validate a public key. - * @param {Buffer} key - * @returns {Boolean} True if buffer is a valid public key. - */ - -ec.publicKeyVerify = function publicKeyVerify(key) { - var result; - - if (secp256k1) - return secp256k1.publicKeyVerify(key); - - try { - result = ec.elliptic.keyPair({ pub: key }).validate(); - } catch (e) { - result = false; - } - - return result; -}; - -/** - * Validate a private key. - * @param {Buffer} key - * @returns {Boolean} True if buffer is a valid private key. - */ - -ec.privateKeyVerify = function privateKeyVerify(key) { - if (secp256k1) - return secp256k1.privateKeyVerify(key); - - if (key.length !== 32) - return false; - - key = new BN(key); - - return key.cmpn(0) !== 0 && key.cmp(ec.curve.n) < 0; -}; - -/** - * Sign a message. - * @param {Buffer} msg - * @param {Buffer} key - Private key. - * @returns {Buffer} DER-formatted signature. - */ - -ec.sign = function sign(msg, key) { - var sig; - - assert(Buffer.isBuffer(msg)); - assert(Buffer.isBuffer(key)); - - if (secp256k1) { - // Sign message - sig = secp256k1.sign(msg, key); - - // Ensure low S value - sig = secp256k1.signatureNormalize(sig.signature); - - // Convert to DER array - sig = secp256k1.signatureExport(sig); - } else { - // Sign message and ensure low S value - sig = ec.elliptic.sign(msg, key, { canonical: true }); - - // Convert to DER array - sig = new Buffer(sig.toDER()); - } - - return sig; -}; - -/** - * Convert DER signature to R/S. - * @param {Buffer} sig - * @returns {Buffer} R/S-formatted signature. - */ - -ec.fromDER = function fromDER(sig) { - var out; - - assert(Buffer.isBuffer(sig)); - - if (secp256k1) - return secp256k1.signatureImport(sig); - - sig = new ec.signature(sig); - out = new Buffer(64); - - sig.r.toArrayLike(Buffer, 'be', 32).copy(out, 0); - sig.s.toArrayLike(Buffer, 'be', 32).copy(out, 32); - - return out; -}; - -/** - * Convert R/S signature to DER. - * @param {Buffer} sig - * @returns {Buffer} DER-formatted signature. - */ - -ec.toDER = function toDER(sig) { - var out; - - assert(Buffer.isBuffer(sig)); - - if (secp256k1) - return secp256k1.signatureExport(sig); - - out = new ec.signature({ - r: new BN(sig.slice(0, 32), 'be'), - s: new BN(sig.slice(32, 64), 'be') - }); - - return new Buffer(out.toDER()); -}; - -/** - * Normalize the length of a signature - * (only done for historical data). - * @param {Buffer} sig - DER formatted signature. - * @returns {Buffer} Signature. - */ - -ec.normalizeLength = function normalizeLength(sig) { - var data = sig; - var p = { place: 0 }; - var len, rlen, slen; - - if (data[p.place++] !== 0x30) - return sig; - - len = getLength(data, p); - - if (data.length > len + p.place) - data = data.slice(0, len + p.place); - - if (data[p.place++] !== 0x02) - return sig; - - rlen = getLength(data, p); - p.place += rlen; - - if (data[p.place++] !== 0x02) - return sig; - - slen = getLength(data, p); - if (data.length > slen + p.place) - data = data.slice(0, slen + p.place); - - return data; -}; - -/** - * Test whether a signature has a low S value. - * @param {Buffer} sig - * @returns {Boolean} - */ - -ec.isLowS = function isLowS(sig) { - var rs, s; - - if (secp256k1) { - try { - rs = secp256k1.signatureImport(sig); - s = rs.slice(32, 64); - } catch (e) { - return false; - } - - if (utils.equal(s, ZERO_S)) - return false; - - // If S is greater than half the order, - // it's too high. - if (utils.cmp(s, HALF_ORDER) > 0) - return false; - - return true; - } - - try { - sig = new ec.signature(sig); - } catch (e) { - return false; - } - - if (sig.s.cmpn(0) === 0) - return false; - - // If S is greater than half the order, - // it's too high. - if (sig.s.cmp(ec.elliptic.nh) > 0) - return false; - - return true; -}; - -/* - * Helpers - */ - -function getLength(buf, p) { - var initial = buf[p.place++]; - var octetLen, val, i, off; - - if (!(initial & 0x80)) - return initial; - - octetLen = initial & 0xf; - val = 0; - - for (i = 0, off = p.place; i < octetLen; i++, off++) { - val <<= 8; - val |= buf[off]; - } - - p.place = off; - - return val; -} +module.exports = secp256k1 + ? require('./ec-secp256k1') + : require('./ec-elliptic'); diff --git a/lib/crypto/pk-browser.js b/lib/crypto/pk-browser.js new file mode 100644 index 00000000..34262bcd --- /dev/null +++ b/lib/crypto/pk-browser.js @@ -0,0 +1,187 @@ +/*! + * pk-browser.js - public key algorithms for bcoin + * Copyright (c) 2016, Christopher Jeffrey (MIT License). + * https://github.com/bcoin-org/bcoin + */ + +'use strict'; + +var assert = require('assert'); +var BN = require('bn.js'); +var ASN1 = require('../utils/asn1'); +var elliptic = require('elliptic'); +var crypto = require('../crypto/crypto'); +var dsa, rsa, ecdsa; + +/* + * DSA + */ + +dsa = {}; + +dsa.verify = function verify(alg, msg, sig, key, params) { + throw new Error('DSA not implemented.'); +}; + +dsa.sign = function sign(alg, msg, key, params) { + throw new Error('DSA not implemented.'); +}; + +/* + * RSA + */ + +rsa = {}; + +rsa.prefixes = { + md5: new Buffer('3020300c06082a864886f70d020505000410', 'hex'), + sha1: new Buffer('3021300906052b0e03021a05000414', 'hex'), + sha224: new Buffer('302d300d06096086480165030402040500041c', 'hex'), + sha256: new Buffer('3031300d060960864801650304020105000420', 'hex'), + sha384: new Buffer('3041300d060960864801650304020205000430', 'hex'), + sha512: new Buffer('3051300d060960864801650304020305000440', 'hex'), + md5sha1: new Buffer(0), + ripemd160: new Buffer('30203008060628cf060300310414', 'hex') +}; + +rsa.verify = function verify(alg, msg, sig, key) { + var prefix = rsa.prefixes[alg]; + var hash, len, pub; + var N, e, k, m, em, ok, i; + + if (!prefix) + throw new Error('Unknown PKCS prefix.'); + + hash = crypto.hash(alg, msg); + len = prefix.length + hash.length; + pub = ASN1.parseRSAPublic(key); + + N = new BN(pub.modulus); + e = new BN(pub.publicExponent); + k = Math.ceil(N.bitLength() / 8); + + if (k < len + 11) + throw new Error('Message too long.'); + + m = rsa.encrypt(N, e, sig); + em = leftpad(m, k); + + ok = crypto.ceq(em[0], 0x00); + ok &= crypto.ceq(em[1], 0x01); + ok &= crypto.ccmp(em.slice(k - hash.length, k), hash); + ok &= crypto.ccmp(em.slice(k - len, k - hash.length), prefix); + ok &= crypto.ceq(em[k - len - 1], 0x00); + + for (i = 2; i < k - len - 1; i++) + ok &= crypto.ceq(em[i], 0xff); + + return ok === 1; +}; + +rsa.sign = function sign(alg, msg, key) { + var prefix = rsa.prefixes[alg]; + var hash, len, priv; + var N, D, k, i, em; + + if (!prefix) + throw new Error('Unknown PKCS prefix.'); + + hash = crypto.hash(alg, msg); + len = prefix.length + hash.length; + priv = ASN1.parseRSAPrivate(key); + + N = new BN(priv.modulus); + D = new BN(priv.privateExponent); + k = Math.ceil(N.bitLength() / 8); + + if (k < len + 11) + throw new Error('Message too long.'); + + em = new Buffer(k); + em.fill(0); + + em[1] = 0x01; + for (i = 2; i < k - len - 1; i++) + em[i] = 0xff; + + prefix.copy(em, k - len); + hash.copy(em, k - hash.length); + + return rsa.decrypt(N, D, em); +}; + +rsa.decrypt = function decrypt(N, D, m) { + var c = new BN(m); + + if (c.cmp(N) > 0) + throw new Error('Cannot decrypt.'); + + return c + .toRed(BN.red(N)) + .redPow(D) + .fromRed() + .toArrayLike(Buffer, 'be'); +}; + +rsa.encrypt = function encrypt(N, e, m) { + return new BN(m) + .toRed(BN.red(N)) + .redPow(e) + .fromRed() + .toArrayLike(Buffer, 'be'); +}; + +/* + * ECDSA + */ + +ecdsa = {}; + +ecdsa.verify = function verify(curve, msg, alg, key, sig) { + var ec, hash; + + assert(curve, 'No curve selected.'); + + ec = elliptic.ec(curve); + hash = crypto.hash(alg, msg); + + return ec.verify(hash, sig, key); +}; + +ecdsa.sign = function sign(curve, msg, alg, key) { + var ec, hash; + + assert(curve, 'No curve selected.'); + + ec = elliptic.ec(curve); + hash = crypto.hash(alg, msg); + + return new Buffer(ec.sign(hash, key)); +}; + +/* + * Helpers + */ + +function leftpad(input, size) { + var n = input.length; + var out; + + if (n > size) + n = size; + + out = new Buffer(size); + out.fill(0); + + input.copy(out, out.length - n); + + return out; +} + +/* + * Expose + */ + +exports.dsa = dsa; +exports.rsa = rsa; +exports.ecdsa = ecdsa; diff --git a/lib/crypto/pk.js b/lib/crypto/pk.js new file mode 100644 index 00000000..b037a4f3 --- /dev/null +++ b/lib/crypto/pk.js @@ -0,0 +1,142 @@ +/*! + * pk.js - public key algorithms for bcoin + * Copyright (c) 2016, Christopher Jeffrey (MIT License). + * https://github.com/bcoin-org/bcoin + */ + +'use strict'; + +var assert = require('assert'); +var PEM = require('../utils/pem'); +var elliptic = require('elliptic'); +var crypto = require('../crypto/crypto'); +var nodeCrypto = require('crypto'); +var dsa, rsa, ecdsa; + +/* + * DSA + */ + +dsa = {}; + +dsa.verify = function _verify(alg, msg, sig, key, params) { + var pem = toPEM('dsa', key, params, 'public key'); + return verify('dsa', alg, msg, sig, pem); +}; + +dsa.sign = function _sign(alg, msg, key, params) { + var pem = toPEM('dsa', key, params, 'private key'); + return sign('dsa', alg, msg, pem); +}; + +/* + * RSA + */ + +rsa = {}; + +rsa.verify = function _verify(alg, msg, sig, key) { + var pem = toPEM('rsa', key, null, 'public key'); + return verify('rsa', alg, msg, sig, pem); +}; + +rsa.sign = function _sign(alg, msg, key) { + var pem = toPEM('rsa', key, null, 'private key'); + return sign('rsa', alg, msg, pem); +}; + +/* + * ECDSA + */ + +ecdsa = {}; + +ecdsa.verify = function verify(curve, msg, alg, key, sig) { + var ec, hash; + + assert(curve, 'No curve selected.'); + + ec = elliptic.ec(curve); + hash = crypto.hash(alg, msg); + + return ec.verify(hash, sig, key); +}; + +ecdsa.sign = function sign(curve, msg, alg, key) { + var ec, hash; + + assert(curve, 'No curve selected.'); + + ec = elliptic.ec(curve); + hash = crypto.hash(alg, msg); + + return new Buffer(ec.sign(hash, key)); +}; + +/* + * Helpers + */ + +function verify(alg, hash, msg, sig, key) { + var algo = normalizeAlg(alg, hash); + var verifier = nodeCrypto.createVerify(algo); + verifier.update(msg); + return verifier.verify(key, sig); +} + +function sign(alg, hash, msg, key) { + var algo = normalizeAlg(alg, hash); + var sig = nodeCrypto.createSign(algo); + sig.update(msg); + return sig.sign(key); +} + +function toPEM(alg, key, params, type) { + var tag, pem; + + switch (alg) { + case 'dsa': + tag = 'DSA'; + break; + case 'rsa': + tag = 'RSA'; + break; + case 'ecdsa': + tag = 'EC'; + break; + default: + throw new Error('Unsupported algorithm.'); + } + + pem = PEM.encode(key, tag, type); + + // Key parameters, usually present + // if selecting an EC curve. + if (params) + pem += PEM.encode(params, tag, 'parameters'); + + return pem; +} + +function normalizeAlg(alg, hash) { + var name = alg.toUpperCase() + '-' + hash.toUpperCase(); + + switch (name) { + case 'ECDSA-SHA1': + name = 'ecdsa-with-SHA1'; + break; + case 'ECDSA-SHA256': + name = 'ecdsa-with-SHA256'; + break; + } + + return name; +} + +/* + * Expose + */ + +exports.dsa = dsa; +exports.rsa = rsa; +exports.ecdsa = ecdsa; diff --git a/lib/crypto/random.js b/lib/crypto/random.js deleted file mode 100644 index 6edd08e0..00000000 --- a/lib/crypto/random.js +++ /dev/null @@ -1,76 +0,0 @@ -/*! - * 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). - */ - -/* jshint worker: true */ - -var randomBytes, crypto, global; - -try { - crypto = require('crypto'); -} catch (e) { - ; -} - -if (crypto) { - randomBytes = function randomBytes(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) { - randomBytes = function randomBytes(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 - randomBytes = function randomBytes(n) { - var data = new Buffer(n); - var i; - - for (i = 0; i < data.length; i++) - data[i] = Math.floor(Math.random() * 256); - - return data; - }; - } -} - -function randomInt() { - return randomBytes(4).readUInt32LE(0, true); -} - -function randomRange(min, max) { - var num = randomInt(); - return Math.floor((num / 0x100000000) * (max - min) + min); -} - -/* - * Expose - */ - -exports = randomBytes; -exports.randomBytes = randomBytes; -exports.randomInt = randomInt; -exports.randomRange = randomRange; - -module.exports = randomBytes; diff --git a/lib/crypto/schnorr.js b/lib/crypto/schnorr.js index 8b716f64..11e6485c 100644 --- a/lib/crypto/schnorr.js +++ b/lib/crypto/schnorr.js @@ -10,9 +10,11 @@ var BN = require('bn.js'); var elliptic = require('elliptic'); var Signature = require('elliptic/lib/elliptic/ec/signature'); var hmacDRBG = require('elliptic/lib/elliptic/hmac-drbg'); -var curves = elliptic.curves; -var curve = elliptic.ec('secp256k1').curve; var sha256 = require('./crypto').sha256; +var secp256k1 = elliptic.ec('secp256k1'); +var curve = secp256k1.curve; +var curves = elliptic.curves; +var hash = curves.secp256k1.hash; /** * @exports schnorr @@ -335,7 +337,7 @@ schnorr.drbg = function drbg(msg, priv, data) { pers = toArray(kdata.slice(64)); return new hmacDRBG({ - hash: curves.secp256k1.hash, + hash: hash, entropy: prv, nonce: msg, pers: pers diff --git a/lib/crypto/scrypt-async.js b/lib/crypto/scrypt-async.js index 419f1afa..db3e73f7 100644 --- a/lib/crypto/scrypt-async.js +++ b/lib/crypto/scrypt-async.js @@ -35,7 +35,7 @@ var utils = require('../utils/utils'); var crypto = require('./crypto'); -var native = require('../utils/native'); +var native = require('../utils/native').binding; var U32Array = typeof Uint32Array === 'function' ? Uint32Array : Array; /** diff --git a/lib/crypto/scrypt.js b/lib/crypto/scrypt.js index 873610f1..d4163185 100644 --- a/lib/crypto/scrypt.js +++ b/lib/crypto/scrypt.js @@ -34,7 +34,7 @@ 'use strict'; var crypto = require('./crypto'); -var native = require('../utils/native'); +var native = require('../utils/native').binding; var U32Array = typeof Uint32Array === 'function' ? Uint32Array : Array; /** diff --git a/lib/crypto/siphash.js b/lib/crypto/siphash.js index 0d06b2c8..4264e416 100644 --- a/lib/crypto/siphash.js +++ b/lib/crypto/siphash.js @@ -9,7 +9,7 @@ 'use strict'; -var native = require('../utils/native'); +var native = require('../utils/native').binding; /** * Javascript siphash implementation. Used for compact block relay. diff --git a/lib/db/backends-browser.js b/lib/db/backends-browser.js new file mode 100644 index 00000000..55047be8 --- /dev/null +++ b/lib/db/backends-browser.js @@ -0,0 +1,16 @@ +/** + * backends-browser.js - database backends for bcoin + * Copyright (c) 2014-2016, Christopher Jeffrey (MIT License). + * https://github.com/bcoin-org/bcoin + */ + +'use strict'; + +var level = require('./level'); +var RBT = require('./rbt'); + +exports.get = function get(name) { + if (name === 'rbt') + return RBT; + return level; +}; diff --git a/lib/db/backends.js b/lib/db/backends.js new file mode 100644 index 00000000..4d9ec611 --- /dev/null +++ b/lib/db/backends.js @@ -0,0 +1,18 @@ +/** + * backends.js - database backends for bcoin + * Copyright (c) 2014-2016, Christopher Jeffrey (MIT License). + * https://github.com/bcoin-org/bcoin + */ + +'use strict'; + +exports.get = function get(name) { + if (name === 'rbt') + return require('./rbt'); + + try { + return require(name); + } catch (e) { + throw new Error('Database backend "' + name + '" not found.'); + } +}; diff --git a/lib/db/ldb.js b/lib/db/ldb.js index 6bdd8970..c8a4440d 100644 --- a/lib/db/ldb.js +++ b/lib/db/ldb.js @@ -1,7 +1,5 @@ /** - * global ldb tracker - * @module ldb - * @license + * ldb.js - database backend for bcoin * Copyright (c) 2014-2015, Fedor Indutny (MIT License) * Copyright (c) 2014-2016, Christopher Jeffrey (MIT License). * https://github.com/bcoin-org/bcoin @@ -9,9 +7,10 @@ 'use strict'; +var assert = require('assert'); var LowlevelUp = require('./lowlevelup'); var utils = require('../utils/utils'); -var assert = require('assert'); +var backends = require('./backends'); /** * @param {Object} options @@ -28,8 +27,8 @@ var assert = require('assert'); * @returns {LowlevelUp} */ -function ldb(options) { - var target = ldb.getTarget(options); +function LDB(options) { + var target = LDB.getTarget(options); if (target.backend !== 'rbt') utils.mkdir(target.location, true); @@ -66,7 +65,7 @@ function ldb(options) { * @returns {Object} */ -ldb.getBackend = function getBackend(db) { +LDB.getBackend = function getBackend(db) { var name, ext; if (!db) @@ -111,17 +110,10 @@ ldb.getBackend = function getBackend(db) { * @returns {Object} */ -ldb.getTarget = function getTarget(options) { - var backend = ldb.getBackend(options.db); +LDB.getTarget = function getTarget(options) { + var backend = LDB.getBackend(options.db); var location = options.location; - var db; - - if (backend.name === 'rbt') - db = require('./rbt'); - else if (utils.isBrowser) - db = require('./level'); - else - db = require(backend.name); + var db = backends.get(backend.name); if (typeof location !== 'string') { assert(backend.name === 'rbt', 'Location required.'); @@ -139,4 +131,4 @@ ldb.getTarget = function getTarget(options) { * Expose */ -module.exports = ldb; +module.exports = LDB; diff --git a/lib/hd/mnemonic.js b/lib/hd/mnemonic.js index b8325093..66d011d7 100644 --- a/lib/hd/mnemonic.js +++ b/lib/hd/mnemonic.js @@ -13,7 +13,8 @@ var constants = require('../protocol/constants'); var BufferWriter = require('../utils/writer'); var BufferReader = require('../utils/reader'); var HD = require('./hd'); -var unorm; +var wordlist = require('./wordlist'); +var nfkd = require('../utils/nfkd'); /** * HD Mnemonic @@ -368,22 +369,7 @@ Mnemonic.getLanguage = function getLanguage(word) { */ Mnemonic.getWordlist = function getWordlist(language) { - switch (language) { - case 'simplified chinese': - return require('./words/chinese-simplified.js'); - case 'traditional chinese': - return require('./words/chinese-traditional.js'); - case 'english': - return require('./words/english.js'); - case 'french': - return require('./words/french.js'); - case 'italian': - return require('./words/italian.js'); - case 'japanese': - return require('./words/japanese.js'); - default: - throw new Error('Unknown language: ' + language); - } + return wordlist.get(language); }; /** @@ -523,20 +509,6 @@ Mnemonic.isMnemonic = function isMnemonic(obj) { && typeof obj.toSeed === 'function'; }; -/* - * Helpers - */ - -function nfkd(str) { - if (str.normalize) - return str.normalize('NFKD'); - - if (!unorm) - unorm = require('../../vendor/unorm'); - - return unorm.nfkd(str); -} - /* * Expose */ diff --git a/lib/hd/wordlist-browser.js b/lib/hd/wordlist-browser.js new file mode 100644 index 00000000..28fb43c3 --- /dev/null +++ b/lib/hd/wordlist-browser.js @@ -0,0 +1,28 @@ +/*! + * wordlist.js - wordlists for bcoin + * Copyright (c) 2015-2016, Christopher Jeffrey (MIT License). + * https://github.com/bcoin-org/bcoin + */ + +'use strict'; + +var words = require('./words'); + +exports.get = function get(name) { + switch (name) { + case 'simplified chinese': + return words.chinese.simplified; + case 'traditional chinese': + return words.chinese.traditional; + case 'english': + return words.english; + case 'french': + return words.french; + case 'italian': + return words.italian; + case 'japanese': + return words.japanese; + default: + throw new Error('Unknown language: ' + name); + } +}; diff --git a/lib/hd/wordlist.js b/lib/hd/wordlist.js new file mode 100644 index 00000000..81fb9c6a --- /dev/null +++ b/lib/hd/wordlist.js @@ -0,0 +1,26 @@ +/*! + * wordlist.js - wordlists for bcoin + * Copyright (c) 2015-2016, Christopher Jeffrey (MIT License). + * https://github.com/bcoin-org/bcoin + */ + +'use strict'; + +exports.get = function get(name) { + switch (name) { + case 'simplified chinese': + return require('./words/chinese-simplified.js'); + case 'traditional chinese': + return require('./words/chinese-traditional.js'); + case 'english': + return require('./words/english.js'); + case 'french': + return require('./words/french.js'); + case 'italian': + return require('./words/italian.js'); + case 'japanese': + return require('./words/japanese.js'); + default: + throw new Error('Unknown language: ' + name); + } +}; diff --git a/lib/hd/words/index.js b/lib/hd/words/index.js new file mode 100644 index 00000000..042c620a --- /dev/null +++ b/lib/hd/words/index.js @@ -0,0 +1,17 @@ +/*! + * index.js - wordlists for bcoin + * Copyright (c) 2015-2016, Christopher Jeffrey (MIT License). + * https://github.com/bcoin-org/bcoin + */ + +'use strict'; + +exports.chinese = { + simplified: require('./chinese-simplified.js'), + traditional: require('./chinese-traditional.js') +}; + +exports.english = require('./english.js'); +exports.french = require('./french.js'); +exports.italian = require('./italian.js'); +exports.japanese = require('./japanese.js'); diff --git a/lib/http/index.js b/lib/http/index.js index 42f54310..ea70e31d 100644 --- a/lib/http/index.js +++ b/lib/http/index.js @@ -7,16 +7,10 @@ 'use strict'; -var utils = require('../utils/utils'); - -if (!utils.isBrowser) { - exports.request = require('./request'); - exports.Client = require('./client'); - exports.RPCClient = require('./rpcclient'); - exports.Wallet = require('./wallet'); - exports.Base = require('./base'); - exports.RPC = require('./rpc'); - exports.Server = require('./server'); -} else { - exports.RPC = require('./rpc'); -} +exports.request = require('./request'); +exports.Client = require('./client'); +exports.RPCClient = require('./rpcclient'); +exports.Wallet = require('./wallet'); +exports.Base = require('./base'); +exports.RPC = require('./rpc'); +exports.Server = require('./server'); diff --git a/lib/http/rpc.js b/lib/http/rpc.js index 5f751ca9..7fb05393 100644 --- a/lib/http/rpc.js +++ b/lib/http/rpc.js @@ -31,13 +31,7 @@ var BufferReader = require('../utils/reader'); var TX = require('../primitives/tx'); var Logger = require('../node/logger'); var EventEmitter = require('events').EventEmitter; -var fs; - -try { - fs = require('fs'); -} catch (e) { - ; -} +var fs = require('fs'); function RPC(node) { if (!(this instanceof RPC)) @@ -2794,7 +2788,7 @@ RPC.prototype.dumpwallet = co(function* dumpwallet(args) { out = out.join('\n'); - if (!fs) + if (fs.unsupported) return out; yield writeFile(file, out); @@ -3216,7 +3210,7 @@ RPC.prototype.importwallet = co(function* importwallet(args) { file = toString(args[0]); - if (!fs) + if (fs.unsupported) throw new RPCError('FS not available.'); data = yield readFile(file, 'utf8'); diff --git a/lib/net/peer.js b/lib/net/peer.js index 08024529..2761e986 100644 --- a/lib/net/peer.js +++ b/lib/net/peer.js @@ -7,7 +7,9 @@ 'use strict'; +var assert = require('assert'); var EventEmitter = require('events').EventEmitter; +var tcp = require('./tcp'); var utils = require('../utils/utils'); var co = require('../utils/co'); var Parser = require('./parser'); @@ -15,7 +17,6 @@ var Framer = require('./framer'); var packets = require('./packets'); var packetTypes = packets.types; var NetworkAddress = require('../primitives/netaddress'); -var assert = require('assert'); var constants = require('../protocol/constants'); var InvItem = require('../primitives/invitem'); var Locker = require('../utils/locker'); @@ -249,21 +250,15 @@ Peer.prototype._init = function init() { Peer.prototype.connect = function connect(port, host) { var self = this; - var socket, proxy, net; + var proxy = this.pool.proxyServer; + var socket; assert(!this.socket); - if (this.createSocket) { - socket = this.createSocket(port, host); - } else { - if (utils.isBrowser) { - proxy = require('./proxysocket'); - socket = proxy.connect(this.pool.proxyServer, port, host); - } else { - net = require('net'); - socket = net.connect(port, host); - } - } + if (this.createSocket) + socket = this.createSocket(port, host, proxy); + else + socket = tcp.connect(port, host, proxy); this.logger.debug('Connecting to %s.', this.hostname); diff --git a/lib/net/pool.js b/lib/net/pool.js index 12713f6e..e6d2ad33 100644 --- a/lib/net/pool.js +++ b/lib/net/pool.js @@ -7,12 +7,12 @@ 'use strict'; -var AsyncObject = require('../utils/async'); +var assert = require('assert'); var EventEmitter = require('events').EventEmitter; +var AsyncObject = require('../utils/async'); var utils = require('../utils/utils'); var IP = require('../utils/ip'); var co = require('../utils/co'); -var assert = require('assert'); var constants = require('../protocol/constants'); var VerifyError = require('../utils/errors').VerifyError; var NetworkAddress = require('../primitives/netaddress'); @@ -27,6 +27,8 @@ var Network = require('../protocol/network'); var time = require('./timedata'); var Peer = require('./peer'); var TX = require('../primitives/tx'); +var tcp = require('./tcp'); +var request = require('../http/request'); /** * A pool of peers for handling all network activity. @@ -396,7 +398,6 @@ Pool.prototype.connect = function connect() { Pool.prototype.listen = function listen() { var self = this; - var net; if (this.server) return Promise.resolve(); @@ -404,10 +405,9 @@ Pool.prototype.listen = function listen() { if (this.createServer) { this.server = this.createServer(); } else { - if (utils.isBrowser) + if (!tcp.Server) return; - net = require('net'); - this.server = new net.Server(); + this.server = new tcp.Server(); } this.server.on('connection', function(socket) { @@ -1871,13 +1871,11 @@ Pool.prototype.isIgnored = function isIgnored(addr) { */ Pool.prototype.getIP = co(function* getIP() { - var request, res, ip; + var res, ip; - if (utils.isBrowser) + if (request.unsupported) throw new Error('Could not find IP.'); - request = require('../http/request'); - try { res = yield request.promise({ method: 'GET', @@ -1903,13 +1901,11 @@ Pool.prototype.getIP = co(function* getIP() { */ Pool.prototype.getIP2 = co(function* getIP2() { - var request, res, ip; + var res, ip; - if (utils.isBrowser) + if (request.unsupported) throw new Error('Could not find IP.'); - request = require('../http/request'); - res = yield request.promise({ method: 'GET', uri: 'http://checkip.dyndns.org', diff --git a/lib/net/tcp-browser.js b/lib/net/tcp-browser.js new file mode 100644 index 00000000..bf347d35 --- /dev/null +++ b/lib/net/tcp-browser.js @@ -0,0 +1,16 @@ +/*! + * tcp.js - tcp backend for bcoin + * Copyright (c) 2014-2016, Christopher Jeffrey (MIT License). + * https://github.com/bcoin-org/bcoin + */ + +'use strict'; + +var ProxySocket = require('./proxysocket'); +var tcp = exports; + +tcp.connect = function connect(port, host, uri) { + return ProxySocket.connect(uri, port, host); +}; + +tcp.Server = null; diff --git a/lib/net/tcp.js b/lib/net/tcp.js new file mode 100644 index 00000000..003019f1 --- /dev/null +++ b/lib/net/tcp.js @@ -0,0 +1,16 @@ +/*! + * tcp.js - tcp backend for bcoin + * Copyright (c) 2014-2016, Christopher Jeffrey (MIT License). + * https://github.com/bcoin-org/bcoin + */ + +'use strict'; + +var net = require('net'); +var tcp = exports; + +tcp.connect = function connect(port, host) { + return net.connect(port, host); +}; + +tcp.Server = net.Server; diff --git a/lib/node/config.js b/lib/node/config.js index fde5c984..4b68a02d 100644 --- a/lib/node/config.js +++ b/lib/node/config.js @@ -9,10 +9,7 @@ var Network = require('../protocol/network'); var utils = require('../utils/utils'); var assert = require('assert'); -var fs; - -if (!utils.isBrowser) - fs = require('fs'); +var fs = require('fs'); /** * @exports config @@ -660,7 +657,7 @@ function boolpath(value, prefix, dirname) { } function file(value, prefix, dirname, enc) { - if (!fs) + if (fs.unsupported) return null; value = path(value, prefix, dirname); @@ -684,7 +681,7 @@ function resolve(a, b) { } function readFile(file) { - if (!fs) + if (fs.unsupported) return ''; if (!file) diff --git a/lib/node/fullnode.js b/lib/node/fullnode.js index 17bcfc37..4e3f7a5c 100644 --- a/lib/node/fullnode.js +++ b/lib/node/fullnode.js @@ -17,13 +17,7 @@ var Mempool = require('../mempool/mempool'); var Pool = require('../net/pool'); var Miner = require('../miner/miner'); var WalletDB = require('../wallet/walletdb'); -var HTTPServer; - -try { - HTTPServer = require('../http/server'); -} catch (e) { - ; -} +var HTTPServer = require('../http/server'); /** * Create a fullnode complete with a chain, @@ -155,7 +149,7 @@ function FullNode(options) { }); // HTTP needs access to the node. - if (!utils.isBrowser) { + if (!HTTPServer.unsupported) { this.http = new HTTPServer({ network: this.network, logger: this.logger, diff --git a/lib/node/logger.js b/lib/node/logger.js index 7c06da3f..622163db 100644 --- a/lib/node/logger.js +++ b/lib/node/logger.js @@ -8,10 +8,7 @@ var utils = require('../utils/utils'); var assert = require('assert'); -var fs; - -if (!utils.isBrowser) - fs = require('fs'); +var fs = require('fs'); /** * Basic stdout and file logger. @@ -287,9 +284,12 @@ Logger.prototype.writeStream = function writeStream(level, args) { if (!this.stream) { if (!this.file) return; - if (utils.isBrowser) + + if (fs.unsupported) return; + utils.mkdir(this.file, true); + this.stream = fs.createWriteStream(this.file, { flags: 'a' }); this.stream.on('error', function() {}); } diff --git a/lib/node/spvnode.js b/lib/node/spvnode.js index 8ffaf65d..80c2c6d3 100644 --- a/lib/node/spvnode.js +++ b/lib/node/spvnode.js @@ -13,13 +13,7 @@ var Node = require('./node'); var Chain = require('../chain/chain'); var Pool = require('../net/pool'); var WalletDB = require('../wallet/walletdb'); -var HTTPServer; - -try { - HTTPServer = require('../http/server'); -} catch (e) { - ; -} +var HTTPServer = require('../http/server'); /** * Create an spv node which only maintains @@ -95,7 +89,7 @@ function SPVNode(options) { verify: true }); - if (!utils.isBrowser) { + if (!HTTPServer.unsupported) { this.http = new HTTPServer({ network: this.network, logger: this.logger, diff --git a/lib/bip70/asn1.js b/lib/utils/asn1.js similarity index 56% rename from lib/bip70/asn1.js rename to lib/utils/asn1.js index 21b2faa2..4c764613 100644 --- a/lib/bip70/asn1.js +++ b/lib/utils/asn1.js @@ -31,9 +31,9 @@ var assert = require('assert'); var BufferReader = require('../utils/reader'); -var asn1 = exports; +var ASN1 = exports; -asn1.parseTag = function parseTag(p) { +ASN1.parseTag = function parseTag(p) { var tag = p.readU8(); var primitive = (tag & 0x20) === 0; var oct; @@ -53,11 +53,11 @@ asn1.parseTag = function parseTag(p) { return { primitive: primitive, tag: tag, - len: asn1.parseLen(p, primitive) + len: ASN1.parseLen(p, primitive) }; }; -asn1.parseLen = function parseLen(p, primitive) { +ASN1.parseLen = function parseLen(p, primitive) { var len = p.readU8(); var num, i, j; @@ -85,52 +85,52 @@ asn1.parseLen = function parseLen(p, primitive) { return len; }; -asn1.parseCert = function parseCert(data) { +ASN1.parseCert = function parseCert(data) { var d = BufferReader(data); var p; d.start(); - p = BufferReader(asn1.parseSeq(d)); + p = BufferReader(ASN1.parseSeq(d)); return { - tbs: asn1.parseTBS(p), - sigAlg: asn1.parseAlgIdent(p), - sig: asn1.parseBitstr(p), + tbs: ASN1.parseTBS(p), + sigAlg: ASN1.parseAlgIdent(p), + sig: ASN1.parseBitstr(p), raw: d.endData(true) }; }; -asn1.parseTBS = function parseTBS(data) { +ASN1.parseTBS = function parseTBS(data) { var d = BufferReader(data); var p; d.start(); - p = BufferReader(asn1.parseSeq(d)); + p = BufferReader(ASN1.parseSeq(d)); return { - version: asn1.parseExplicitInt(p, 0, true), - serial: asn1.parseInt(p), - sig: asn1.parseAlgIdent(p), - issuer: asn1.parseName(p), - validity: asn1.parseValidity(p), - subject: asn1.parseName(p), - pubkey: asn1.parsePubkey(p), + version: ASN1.parseExplicitInt(p, 0, true), + serial: ASN1.parseInt(p), + sig: ASN1.parseAlgIdent(p), + issuer: ASN1.parseName(p), + validity: ASN1.parseValidity(p), + subject: ASN1.parseName(p), + pubkey: ASN1.parsePubkey(p), raw: d.endData(true) }; }; -asn1.parseSeq = function parseSeq(data) { +ASN1.parseSeq = function parseSeq(data) { var p = BufferReader(data); - var tag = asn1.parseTag(p); + var tag = ASN1.parseTag(p); assert.equal(tag.tag, 0x10); // seq return p.readBytes(tag.len, true); }; -asn1.parseInt = function parseInt(data, readNum) { +ASN1.parseInt = function parseInt(data, readNum) { var p = BufferReader(data); - var tag = asn1.parseTag(p); + var tag = ASN1.parseTag(p); var num; assert.equal(tag.tag, 0x02); // int @@ -143,30 +143,30 @@ asn1.parseInt = function parseInt(data, readNum) { return num; }; -asn1.parseExplicitInt = function parseExplicitInt(data, i, readNum) { +ASN1.parseExplicitInt = function parseExplicitInt(data, i, readNum) { var p = BufferReader(data); var off = p.offset; - var tag = asn1.parseTag(p); + var tag = ASN1.parseTag(p); if (tag.tag !== i) { p.seek(-(p.offset - off)); return -1; } - return asn1.parseInt(p, readNum); + return ASN1.parseInt(p, readNum); }; -asn1.parseBitstr = function parseBitstr(data) { +ASN1.parseBitstr = function parseBitstr(data) { var p = BufferReader(data); - var tag = asn1.parseTag(p); + var tag = ASN1.parseTag(p); assert.equal(tag.tag, 0x03); // bitstr - return asn1.alignBitstr(p.readBytes(tag.len, true)); + return ASN1.alignBitstr(p.readBytes(tag.len, true)); }; -asn1.parseString = function parseString(data) { +ASN1.parseString = function parseString(data) { var p = BufferReader(data); - var tag = asn1.parseTag(p); + var tag = ASN1.parseTag(p); switch (tag.tag) { case 0x03: // bitstr - return asn1.alignBitstr(p.readBytes(tag.len, true)); + return ASN1.alignBitstr(p.readBytes(tag.len, true)); case 0x04: // octstr case 0x12: // numstr case 0x13: // prinstr @@ -186,7 +186,7 @@ asn1.parseString = function parseString(data) { } }; -asn1.alignBitstr = function(data) { +ASN1.alignBitstr = function(data) { var padding = data[0]; var bits = (data.length - 1) * 8 - padding; var buf = data.slice(1); @@ -207,48 +207,48 @@ asn1.alignBitstr = function(data) { return out; }; -asn1.parsePubkey = function parsePubkey(data) { +ASN1.parsePubkey = function parsePubkey(data) { var p = BufferReader(data); - p = BufferReader(asn1.parseSeq(p)); + p = BufferReader(ASN1.parseSeq(p)); return { - alg: asn1.parseAlgIdent(p), - pubkey: asn1.parseBitstr(p) + alg: ASN1.parseAlgIdent(p), + pubkey: ASN1.parseBitstr(p) }; }; -asn1.parseName = function parseName(data) { +ASN1.parseName = function parseName(data) { var p = BufferReader(data); var values = []; var tag; - p = BufferReader(asn1.parseSeq(p)); + p = BufferReader(ASN1.parseSeq(p)); while (p.left()) { - tag = asn1.parseTag(p); + tag = ASN1.parseTag(p); assert.equal(tag.tag, 0x11); // set - tag = asn1.parseTag(p); + tag = ASN1.parseTag(p); assert.equal(tag.tag, 0x10); // seq values.push({ - type: asn1.parseOID(p), - value: asn1.parseString(p) + type: ASN1.parseOID(p), + value: ASN1.parseString(p) }); } return values; }; -asn1.parseValidity = function parseValidity(data) { +ASN1.parseValidity = function parseValidity(data) { var p = BufferReader(data); - p = BufferReader(asn1.parseSeq(p)); + p = BufferReader(ASN1.parseSeq(p)); return { - notBefore: asn1.parseTime(p), - notAfter: asn1.parseTime(p) + notBefore: ASN1.parseTime(p), + notAfter: ASN1.parseTime(p) }; }; -asn1.parseTime = function parseTime(data) { +ASN1.parseTime = function parseTime(data) { var p = BufferReader(data); - var tag = asn1.parseTag(p); + var tag = ASN1.parseTag(p); var str = p.readString('ascii', tag.len); var year, mon, day, hour, min, sec; @@ -281,9 +281,9 @@ asn1.parseTime = function parseTime(data) { return Date.UTC(year, mon - 1, day, hour, min, sec, 0) / 1000; }; -asn1.parseOID = function parseOID(data) { +ASN1.parseOID = function parseOID(data) { var p = BufferReader(data); - var tag = asn1.parseTag(p); + var tag = ASN1.parseTag(p); var ids = []; var ident = 0; var subident = 0; @@ -314,17 +314,17 @@ asn1.parseOID = function parseOID(data) { return result.join('.'); }; -asn1.parseAlgIdent = function parseAlgIdent(data) { +ASN1.parseAlgIdent = function parseAlgIdent(data) { var p = BufferReader(data); var params = null; var alg; - p = BufferReader(asn1.parseSeq(p)); + p = BufferReader(ASN1.parseSeq(p)); - alg = asn1.parseOID(p); + alg = ASN1.parseOID(p); if (p.left() > 0) { - params = p.readBytes(asn1.parseTag(p).len, true); + params = p.readBytes(ASN1.parseTag(p).len, true); if (params.length === 0) params = null; } @@ -335,117 +335,27 @@ asn1.parseAlgIdent = function parseAlgIdent(data) { }; }; -asn1.parseRSAPublic = function parseRSAPublic(data) { +ASN1.parseRSAPublic = function parseRSAPublic(data) { var p = BufferReader(data); - p = BufferReader(asn1.parseSeq(p)); + p = BufferReader(ASN1.parseSeq(p)); return { - modulus: asn1.parseInt(p), - publicExponent: asn1.parseInt(p) + modulus: ASN1.parseInt(p), + publicExponent: ASN1.parseInt(p) }; }; -asn1.parseRSAPrivate = function parseRSAPrivate(data) { +ASN1.parseRSAPrivate = function parseRSAPrivate(data) { var p = BufferReader(data); - p = BufferReader(asn1.parseSeq(p)); + p = BufferReader(ASN1.parseSeq(p)); return { - version: asn1.parseInt(p, true), - modulus: asn1.parseInt(p), - publicExponent: asn1.parseInt(p), - privateExponent: asn1.parseInt(p), - prime1: asn1.parseInt(p), - prime2: asn1.parseInt(p), - exponent1: asn1.parseInt(p), - exponent2: asn1.parseInt(p), - coefficient: asn1.parseInt(p) + version: ASN1.parseInt(p, true), + modulus: ASN1.parseInt(p), + publicExponent: ASN1.parseInt(p), + privateExponent: ASN1.parseInt(p), + prime1: ASN1.parseInt(p), + prime2: ASN1.parseInt(p), + exponent1: ASN1.parseInt(p), + exponent2: ASN1.parseInt(p), + coefficient: ASN1.parseInt(p) }; }; - -asn1.parsePEM = function parsePEM(pem) { - var buf = ''; - var chunks = []; - var s, tag, type; - - while (pem.length) { - if (s = /^-----BEGIN ([^\-]+)-----/.exec(pem)) { - pem = pem.substring(s[0].length); - tag = s[1]; - continue; - } - if (s = /^-----END ([^\-]+)-----/.exec(pem)) { - pem = pem.substring(s[0].length); - assert(tag === s[1], 'Tag mismatch.'); - buf = new Buffer(buf, 'base64'); - type = tag.split(' ')[0].toLowerCase(); - chunks.push({ tag: tag, type: type, data: buf }); - buf = ''; - tag = null; - continue; - } - if (s = /^[a-zA-Z0-9\+=\/]+/.exec(pem)) { - pem = pem.substring(s[0].length); - buf += s[0]; - continue; - } - if (s = /^\s+/.exec(pem)) { - pem = pem.substring(s[0].length); - continue; - } - throw new Error('PEM parse error.'); - } - - assert(chunks.length !== 0, 'PEM parse error.'); - assert(!tag, 'Un-ended tag.'); - assert(buf.length === 0, 'Trailing data.'); - - return chunks; -}; - -asn1.fromPEM = function fromPEM(pem) { - var chunks = asn1.parsePEM(pem); - var body = chunks[0]; - var extra = chunks[1]; - var params, alg; - - if (extra) { - if (extra.tag.indexOf('PARAMETERS') !== -1) - params = extra.data; - } - - switch (body.type) { - case 'dsa': - alg = 'dsa'; - break; - case 'rsa': - alg = 'rsa'; - break; - case 'ec': - alg = 'ecdsa'; - break; - } - - return { - type: body.type, - alg: alg, - data: body.data, - params: params - }; -}; - -asn1.toPEM = function toPEM(der, type, suffix) { - var pem = ''; - var i; - - if (suffix) - type += ' ' + suffix; - - type = type.toUpperCase(); - der = der.toString('base64'); - - for (i = 0; i < der.length; i += 64) - pem += der.slice(i, i + 64) + '\r\n'; - - return '' - + '-----BEGIN ' + type + '-----\r\n' - + pem - + '-----END ' + type + '-----\r\n'; -}; diff --git a/lib/utils/base58.js b/lib/utils/base58.js index 87a0bb7d..a762e901 100644 --- a/lib/utils/base58.js +++ b/lib/utils/base58.js @@ -7,7 +7,7 @@ 'use strict'; -var native = require('./native'); +var native = require('./native').binding; var assert = require('assert'); /* diff --git a/lib/utils/murmur3.js b/lib/utils/murmur3.js index b760c7e1..675cd65c 100644 --- a/lib/utils/murmur3.js +++ b/lib/utils/murmur3.js @@ -7,7 +7,7 @@ 'use strict'; -var native = require('./native'); +var native = require('./native').binding; /** * Murmur3 hash. diff --git a/lib/utils/native.js b/lib/utils/native.js index 22e5f5fc..224517c7 100644 --- a/lib/utils/native.js +++ b/lib/utils/native.js @@ -6,15 +6,11 @@ 'use strict'; -var isBrowser = - (typeof process !== 'undefined' && process.browser) - || typeof window !== 'undefined'; +exports.binding = null; -module.exports = null; - -if (!isBrowser && +process.env.BCOIN_NO_NATIVE !== 1) { +if (+process.env.BCOIN_NO_NATIVE !== 1) { try { - module.exports = require('bcoin-native'); + exports.binding = require('bcoin-native'); } catch (e) { ; } diff --git a/lib/utils/nexttick-browser.js b/lib/utils/nexttick-browser.js new file mode 100644 index 00000000..0a7b470a --- /dev/null +++ b/lib/utils/nexttick-browser.js @@ -0,0 +1,7 @@ +/*! + * nexttick.js - setimmediate for bcoin + * Copyright (c) 2014-2016, Christopher Jeffrey (MIT License). + * https://github.com/bcoin-org/bcoin + */ + +module.exports = require('../../vendor/setimmediate'); diff --git a/lib/utils/nexttick.js b/lib/utils/nexttick.js new file mode 100644 index 00000000..61859905 --- /dev/null +++ b/lib/utils/nexttick.js @@ -0,0 +1,9 @@ +/*! + * nexttick.js - setimmediate for bcoin + * Copyright (c) 2014-2016, Christopher Jeffrey (MIT License). + * https://github.com/bcoin-org/bcoin + */ + +module.exports = typeof setImmediate !== 'function' + ? process.nextTick + : setImmediate; diff --git a/lib/utils/nfkd-browser.js b/lib/utils/nfkd-browser.js new file mode 100644 index 00000000..537771d7 --- /dev/null +++ b/lib/utils/nfkd-browser.js @@ -0,0 +1,20 @@ +/** + * nfkd-browser.js - unicode normalization for bcoin + * Copyright (c) 2014-2016, Christopher Jeffrey (MIT License). + * https://github.com/bcoin-org/bcoin + */ + +var unorm = require('../../vendor/unorm'); + +function nfkd(str) { + if (str.normalize) + return str.normalize('NFKD'); + + return unorm.nfkd(str); +} + +/* + * Expose + */ + +module.exports = nfkd; diff --git a/lib/utils/nfkd.js b/lib/utils/nfkd.js new file mode 100644 index 00000000..8c9431a7 --- /dev/null +++ b/lib/utils/nfkd.js @@ -0,0 +1,23 @@ +/** + * nfkd.js - unicode normalization for bcoin + * Copyright (c) 2014-2016, Christopher Jeffrey (MIT License). + * https://github.com/bcoin-org/bcoin + */ + +var unorm; + +function nfkd(str) { + if (str.normalize) + return str.normalize('NFKD'); + + if (!unorm) + unorm = require('../../vendor/unorm'); + + return unorm.nfkd(str); +} + +/* + * Expose + */ + +module.exports = nfkd; diff --git a/lib/utils/pem.js b/lib/utils/pem.js new file mode 100644 index 00000000..2ed2d348 --- /dev/null +++ b/lib/utils/pem.js @@ -0,0 +1,100 @@ +/*! + * pem.js - pem parsing for bcoin + * Copyright (c) 2016, Christopher Jeffrey (MIT License). + * https://github.com/bcoin-org/bcoin + */ + +'use strict'; + +var assert = require('assert'); +var PEM = exports; + +PEM.parse = function parse(pem) { + var buf = ''; + var chunks = []; + var s, tag, type; + + while (pem.length) { + if (s = /^-----BEGIN ([^\-]+)-----/.exec(pem)) { + pem = pem.substring(s[0].length); + tag = s[1]; + continue; + } + if (s = /^-----END ([^\-]+)-----/.exec(pem)) { + pem = pem.substring(s[0].length); + assert(tag === s[1], 'Tag mismatch.'); + buf = new Buffer(buf, 'base64'); + type = tag.split(' ')[0].toLowerCase(); + chunks.push({ tag: tag, type: type, data: buf }); + buf = ''; + tag = null; + continue; + } + if (s = /^[a-zA-Z0-9\+=\/]+/.exec(pem)) { + pem = pem.substring(s[0].length); + buf += s[0]; + continue; + } + if (s = /^\s+/.exec(pem)) { + pem = pem.substring(s[0].length); + continue; + } + throw new Error('PEM parse error.'); + } + + assert(chunks.length !== 0, 'PEM parse error.'); + assert(!tag, 'Un-ended tag.'); + assert(buf.length === 0, 'Trailing data.'); + + return chunks; +}; + +PEM.decode = function decode(pem) { + var chunks = PEM.parse(pem); + var body = chunks[0]; + var extra = chunks[1]; + var params, alg; + + if (extra) { + if (extra.tag.indexOf('PARAMETERS') !== -1) + params = extra.data; + } + + switch (body.type) { + case 'dsa': + alg = 'dsa'; + break; + case 'rsa': + alg = 'rsa'; + break; + case 'ec': + alg = 'ecdsa'; + break; + } + + return { + type: body.type, + alg: alg, + data: body.data, + params: params + }; +}; + +PEM.encode = function encode(der, type, suffix) { + var pem = ''; + var i; + + if (suffix) + type += ' ' + suffix; + + type = type.toUpperCase(); + der = der.toString('base64'); + + for (i = 0; i < der.length; i += 64) + pem += der.slice(i, i + 64) + '\n'; + + return '' + + '-----BEGIN ' + type + '-----\n' + + pem + + '-----END ' + type + '-----\n'; +}; diff --git a/lib/bip70/protobuf.js b/lib/utils/protobuf.js similarity index 100% rename from lib/bip70/protobuf.js rename to lib/utils/protobuf.js diff --git a/lib/utils/utils.js b/lib/utils/utils.js index b9390119..61a77a33 100644 --- a/lib/utils/utils.js +++ b/lib/utils/utils.js @@ -19,8 +19,10 @@ var assert = require('assert'); var base58 = require('./base58'); var BN = require('bn.js'); var util = require('util'); +var fs = require('fs'); +var os = require('os'); var Number, Math, Date; -var fs, lazy; +var lazy; /** * Reference to the global object. @@ -52,9 +54,6 @@ utils.isBrowser = (typeof process !== 'undefined' && process.browser) || typeof window !== 'undefined'; -if (!utils.isBrowser) - fs = require('fs'); - Number = utils.global.Number; Math = utils.global.Math; Date = utils.global.Date; @@ -64,9 +63,9 @@ Date = utils.global.Date; * @const {String} */ -try { - utils.HOME = require('os').homedir(); -} catch (e) { +if (os.homedir) { + utils.HOME = os.homedir(); +} else { utils.HOME = process.env.HOME || process.env.USERPROFILE || process.env.HOMEPATH @@ -149,6 +148,17 @@ utils.isBase58 = function isBase58(obj) { return typeof obj === 'string' && /^[1-9a-zA-Z]+$/.test(obj); }; +/** + * Return uptime (shim for browser). + * @returns {Number} + */ + +utils.uptime = function uptime() { + if (!process.uptime) + return 0; + return process.uptime(); +}; + /** * Return hrtime (shim for browser). * @param {Array} time @@ -227,18 +237,7 @@ utils.equal = function equal(a, b) { * @returns {Promise} */ -if (utils.isBrowser) - require('../../vendor/setimmediate'); - -if (typeof setImmediate === 'function') { - utils.nextTick = setImmediate; -} else if (!utils.isBrowser) { - utils.nextTick = process.nextTick; -} else { - utils.nextTick = function nextTick(fn) { - setTimeout(fn, 1); - }; -} +utils.nextTick = require('./nexttick'); /** * Reverse a hex-string (used because of @@ -1826,7 +1825,7 @@ utils.normalize = function normalize(path, dirname) { utils.mkdirp = function mkdirp(path) { var i, parts, stat; - if (!fs) + if (fs.unsupported) return; path = path.replace(/\\/g, '/'); @@ -1964,3 +1963,7 @@ lazy('co', './co'); lazy('uri', './uri'); lazy('BufferReader', './reader'); lazy('BufferWriter', './writer'); +lazy('protobuf', './protobuf'); +lazy('pem', './pem'); +lazy('asn1', './asn1'); +lazy('nfkd', './nfkd'); diff --git a/lib/workers/workers.js b/lib/workers/workers.js index 5bf65ba8..f08b2e73 100644 --- a/lib/workers/workers.js +++ b/lib/workers/workers.js @@ -15,6 +15,8 @@ var Network = require('../protocol/network'); var jobs = require('./jobs'); var Parser = require('./parser'); var Framer = require('./framer'); +var os = require('os'); +var cp = require('child_process'); /** * A worker pool. @@ -396,7 +398,7 @@ utils.inherits(Worker, EventEmitter); Worker.prototype._init = function _init() { var self = this; - var penv, cp; + var penv; penv = { BCOIN_WORKER_NETWORK: Network.type @@ -423,8 +425,6 @@ Worker.prototype._init = function _init() { this.child.postMessage(JSON.stringify(penv)); } else { - cp = require('child_process'); - this.child = cp.spawn(process.argv[0], [__dirname + '/worker.js'], { stdio: 'pipe', env: utils.merge({}, process.env, penv) @@ -835,13 +835,8 @@ Master.listen = function listen() { */ function getCores() { - var os; - - if (utils.isBrowser) + if (os.unsupported) return 2; - - os = require('os'); - return os.cpus().length; } diff --git a/package.json b/package.json index 6bed6f1e..cb3f5399 100644 --- a/package.json +++ b/package.json @@ -67,6 +67,16 @@ "./lib/http/rpcclient": "./browser/empty.js", "./lib/http/server": "./browser/empty.js", "./lib/http/wallet": "./browser/empty.js", + "./lib/utils/lazy": "./browser/empty.js", + "./lib/crypto/native": "./lib/crypto/empty.js", + "./lib/utils/nfkd": "./lib/utils/nfkd-browser.js", + "./lib/utils/nexttick": "./lib/utils/nexttick-browser.js", + "./lib/crypto/backend": "./lib/crypto/backend-browser.js", + "./lib/crypto/ec": "./lib/crypto/ec-elliptic.js", + "./lib/crypto/pk": "./lib/crypto/pk-browser.js", + "./lib/db/backends": "./lib/db/backends-browser.js", + "./lib/hd/wordlist": "./lib/hd/wordlist-browser.js", + "./lib/net/tcp": "./lib/net/tcp-browser.js", "./lib/chain/layout": "./lib/chain/layout-browser.js", "./lib/wallet/layout": "./lib/wallet/layout-browser.js", "fs": "./browser/empty.js", diff --git a/vendor/ip.js b/vendor/ip.js index 85ee7f00..ad182714 100644 --- a/vendor/ip.js +++ b/vendor/ip.js @@ -26,6 +26,7 @@ * IN THE SOFTWARE. */ +var os = require('os'); var ip = exports; ip.toBuffer = function(ip, buff, offset) { @@ -374,16 +375,10 @@ ip.loopback = function(family) { // * undefined: First address with `ipv4` or loopback address `127.0.0.1`. // ip.address = function(name, family) { - var os; - - try { - os = require('os'); - } catch (e) { + if (os.unsupported) return '127.0.0.1'; - } var interfaces = os.networkInterfaces(); - var all; // // Default to `ipv4` diff --git a/vendor/setimmediate.js b/vendor/setimmediate.js index d5c57e6d..ddd87f56 100644 --- a/vendor/setimmediate.js +++ b/vendor/setimmediate.js @@ -20,11 +20,24 @@ * DEALINGS IN THE SOFTWARE. */ -(function (global, undefined) { - "use strict"; +(function(root, undefined) { + "use strict"; + var global; + + if (typeof window !== "undefined") + global = window; + else if (typeof self !== "undefined") + global = self; + else + global = root; + + if (!global) + throw new Error("Global not found."); + + function install() { if (global.setImmediate) { - return; + return global.setImmediate; } var nextHandle = 1; // Spec says greater than zero @@ -166,10 +179,6 @@ }; } - // If supported, we should attach to the prototype of global, since that is where setTimeout et al. live. - var attachTo = Object.getPrototypeOf && Object.getPrototypeOf(global); - attachTo = attachTo && attachTo.setTimeout ? attachTo : global; - // Don't get fooled by e.g. browserify environments. if ({}.toString.call(global.process) === "[object process]") { // For Node.js before 0.9 @@ -192,6 +201,8 @@ installSetTimeoutImplementation(); } - attachTo.setImmediate = setImmediate; - attachTo.clearImmediate = clearImmediate; -}(typeof self === "undefined" ? typeof global === "undefined" ? this : global : self)); + return setImmediate; + } + + module.exports = install(); +})(this); diff --git a/vendor/unorm.js b/vendor/unorm.js index bb5393f4..79a7c9f7 100644 --- a/vendor/unorm.js +++ b/vendor/unorm.js @@ -425,64 +425,5 @@ UChar.udata={ nfkd: nfkd }; - /*globals module:true,define:true*/ - - // CommonJS - if (typeof module === "object") { - module.exports = unorm; - - // AMD - } else if (typeof define === "function" && define.amd) { - define("unorm", function () { - return unorm; - }); - - // Global - } else { - root.unorm = unorm; - } - - /***** Export as shim for String::normalize method *****/ - /* - http://wiki.ecmascript.org/doku.php?id=harmony:specification_drafts#november_8_2013_draft_rev_21 - - 21.1.3.12 String.prototype.normalize(form="NFC") - When the normalize method is called with one argument form, the following steps are taken: - - 1. Let O be CheckObjectCoercible(this value). - 2. Let S be ToString(O). - 3. ReturnIfAbrupt(S). - 4. If form is not provided or undefined let form be "NFC". - 5. Let f be ToString(form). - 6. ReturnIfAbrupt(f). - 7. If f is not one of "NFC", "NFD", "NFKC", or "NFKD", then throw a RangeError Exception. - 8. Let ns be the String value is the result of normalizing S into the normalization form named by f as specified in Unicode Standard Annex #15, UnicodeNormalizatoin Forms. - 9. Return ns. - - The length property of the normalize method is 0. - - *NOTE* The normalize function is intentionally generic; it does not require that its this value be a String object. Therefore it can be transferred to other kinds of objects for use as a method. - */ - unorm.shimApplied = false; - - if (!String.prototype.normalize) { - String.prototype.normalize = function(form) { - var str = "" + this; - form = form === undefined ? "NFC" : form; - - if (form === "NFC") { - return unorm.nfc(str); - } else if (form === "NFD") { - return unorm.nfd(str); - } else if (form === "NFKC") { - return unorm.nfkc(str); - } else if (form === "NFKD") { - return unorm.nfkd(str); - } else { - throw new RangeError("Invalid normalization form: " + form); - } - }; - - unorm.shimApplied = true; - } + module.exports = unorm; }(this));