From 253a6795a3ced8c0194d1d928ad4562f870aae77 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Thu, 21 Jul 2016 05:17:50 -0700 Subject: [PATCH] move crypto to its own module. --- lib/bcoin/crypto.js | 518 ++++++++++++++++++++++++++++++++++++++++++++ lib/bcoin/utils.js | 446 +++++++------------------------------- 2 files changed, 600 insertions(+), 364 deletions(-) create mode 100644 lib/bcoin/crypto.js diff --git a/lib/bcoin/crypto.js b/lib/bcoin/crypto.js new file mode 100644 index 00000000..183d7315 --- /dev/null +++ b/lib/bcoin/crypto.js @@ -0,0 +1,518 @@ +/*! + * crypto.js - crypto for bcoin + * 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 nativeCrypto, supersha, hash, aes; + +var isBrowser = + (typeof process !== 'undefined' && process.browser) + || typeof window !== 'undefined'; + +if (!isBrowser) { + nativeCrypto = require('cry' + 'pto'); + try { + supersha = require('super' + 'sha'); + } catch (e) { + ; + } +} else { + hash = require('hash.js'); + aes = require('./aes'); +} + +/** + * @exports crypto + */ + +var crypto = exports; + +/** + * Hash with chosen algorithm. + * @param {String} alg + * @param {Buffer} data + * @returns {Buffer} + */ + +crypto.hash = function _hash(alg, data) { + if (!nativeCrypto) + return new Buffer(hash[alg]().update(data).digest()); + + return nativeCrypto.createHash(alg).update(data).digest(); +}; + +/** + * Hash with ripemd160. + * @param {Buffer} data + * @returns {Buffer} + */ + +crypto.ripemd160 = function ripemd160(data) { + return crypto.hash('ripemd160', data); +}; + +/** + * Hash with sha1. + * @param {Buffer} data + * @returns {Buffer} + */ + +crypto.sha1 = function sha1(data) { + return crypto.hash('sha1', data); +}; + +/** + * Hash with sha256. + * @param {Buffer} data + * @returns {Buffer} + */ + +crypto.sha256 = function sha256(data) { + if (supersha) + return supersha.sha256(data); + return crypto.hash('sha256', data); +}; + +/** + * Hash with sha256 and ripemd160 (OP_HASH160). + * @param {Buffer} data + * @returns {Buffer} + */ + +crypto.hash160 = function hash160(data) { + return crypto.ripemd160(crypto.sha256(data)); +}; + +/** + * Hash with sha256 twice (OP_HASH256). + * @param {Buffer} data + * @returns {Buffer} + */ + +crypto.hash256 = function hash256(data) { + if (supersha) + return supersha.hash256(data); + return crypto.sha256(crypto.sha256(data)); +}; + +/** + * Create a sha256 checksum (common in bitcoin). + * @param {Buffer} data + * @returns {Buffer} + */ + +crypto.checksum = function checksum(data) { + return crypto.hash256(data).slice(0, 4); +}; + +/** + * Create an HMAC. + * @param {String} alg + * @param {Buffer} data + * @param {Buffer} salt + * @returns {Buffer} HMAC + */ + +crypto.hmac = function hmac(alg, data, salt) { + var hmac; + + if (!nativeCrypto) { + hmac = hash.hmac(hash[alg], salt); + return new Buffer(hmac.update(data).digest()); + } + + hmac = nativeCrypto.createHmac(alg, salt); + return hmac.update(data).digest(); +}; + +/** + * Perform key stretching using PBKDF2. + * @param {Buffer} key + * @param {Buffer} salt + * @param {Number} iter + * @param {Number} len + * @param {String} alg + * @returns {Buffer} + */ + +crypto.pbkdf2Sync = function pbkdf2Sync(key, salt, iter, len, alg) { + if (typeof key === 'string') + key = new Buffer(key, 'utf8'); + + if (typeof salt === 'string') + salt = new Buffer(salt, 'utf8'); + + if (nativeCrypto && nativeCrypto.pbkdf2Sync) + return nativeCrypto.pbkdf2Sync(key, salt, iter, len, alg); + + return crypto._pbkdf2(key, salt, iter, len, alg); +}; + +/** + * Execute pbkdf2 asynchronously. + * @param {Buffer} key + * @param {Buffer} salt + * @param {Number} iter + * @param {Number} len + * @param {String} alg + * @param {Function} callback + */ + +crypto.pbkdf2 = function pbkdf2(key, salt, iter, len, alg, callback) { + var result; + + if (typeof key === 'string') + key = new Buffer(key, 'utf8'); + + if (typeof salt === 'string') + salt = new Buffer(salt, 'utf8'); + + if (nativeCrypto && nativeCrypto.pbkdf2) + return nativeCrypto.pbkdf2(key, salt, iter, len, alg, callback); + + try { + result = crypto._pbkdf2(key, salt, iter, len, alg); + } catch (e) { + return callback(e); + } + + return callback(null, result); +}; + +/** + * Derive a key using pbkdf2 with 50,000 iterations. + * @param {Buffer|String} passphrase + * @param {Function} callback + */ + +crypto.derive = function derive(passphrase, callback) { + crypto.pbkdf2(passphrase, 'bcoin', 50000, 32, 'sha256', callback); +}; + +/** + * Encrypt with aes-256-cbc. Derives key with {@link crypto.derive}. + * @param {Buffer} data + * @param {Buffer|String} passphrase + * @param {Buffer} iv - 128 bit initialization vector. + * @param {Function} callback + */ + +crypto.encrypt = function encrypt(data, passphrase, iv, callback) { + assert(Buffer.isBuffer(data)); + assert(passphrase, 'No passphrase.'); + assert(Buffer.isBuffer(iv)); + + crypto.derive(passphrase, function(err, key) { + if (err) + return callback(err); + + try { + data = crypto.encipher(data, key, iv); + } catch (e) { + key.fill(0); + return callback(e); + } + + key.fill(0); + + return callback(null, data); + }); +}; + +/** + * Encrypt with aes-256-cbc. + * @param {Buffer} data + * @param {Buffer} key - 256 bit key. + * @param {Buffer} iv - 128 bit initialization vector. + * @returns {Buffer} + */ + +crypto.encipher = function encipher(data, key, iv) { + var cipher; + + if (!nativeCrypto) + return aes.cbc.encrypt(data, key, iv); + + cipher = nativeCrypto.createCipheriv('aes-256-cbc', key, iv); + + return Buffer.concat([ + cipher.update(data), + cipher.final() + ]); +}; + +/** + * Decrypt with aes-256-cbc. Derives key with {@link crypto.derive}. + * @param {Buffer} data + * @param {Buffer|String} passphrase + * @param {Buffer} iv - 128 bit initialization vector. + * @param {Function} callback + */ + +crypto.decrypt = function decrypt(data, passphrase, iv, callback) { + assert(Buffer.isBuffer(data)); + assert(passphrase, 'No passphrase.'); + assert(Buffer.isBuffer(iv)); + + crypto.derive(passphrase, function(err, key) { + if (err) + return callback(err); + + try { + data = crypto.decipher(data, key, iv); + } catch (e) { + key.fill(0); + return callback(e); + } + + key.fill(0); + + return callback(null, data); + }); +}; + +/** + * Decrypt with aes-256-cbc. + * @param {Buffer} data + * @param {Buffer} key - 256 bit key. + * @param {Buffer} iv - 128 bit initialization vector. + * @returns {Buffer} + */ + +crypto.decipher = function decipher(data, key, iv) { + var decipher; + + if (!nativeCrypto) + return aes.cbc.decrypt(data, key, iv); + + decipher = nativeCrypto.createDecipheriv('aes-256-cbc', key, iv); + + return Buffer.concat([ + decipher.update(data), + decipher.final() + ]); +}; + +/** + * Perform key stretching using PBKDF2. + * @private + * @param {Buffer} key + * @param {Buffer} salt + * @param {Number} iter + * @param {Number} len + * @param {String} alg + * @returns {Buffer} + */ + +crypto._pbkdf2 = function pbkdf2(key, salt, iter, len, alg) { + var size = crypto.hash(alg, new Buffer(0)).length; + var blocks = Math.ceil(len / size); + var out = new Buffer(blocks * size); + var buf = new Buffer(salt.length + 4); + var block = new Buffer(size); + var pos = 0; + var i, j, k, mac; + + salt.copy(buf, 0); + + for (i = 0; i < blocks; i++) { + buf.writeUInt32BE(i + 1, salt.length, true); + mac = crypto.hmac(alg, buf, key); + mac.copy(block, 0); + for (j = 1; j < iter; j++) { + mac = crypto.hmac(alg, mac, key); + for (k = 0; k < size; k++) + block[k] ^= mac[k]; + } + block.copy(out, pos); + pos += size; + } + + return out.slice(0, len); +}; + +/** + * Perform hkdf extraction. + * @param {Buffer} ikm + * @param {Buffer} salt + * @param {String} alg + * @returns {Buffer} + */ + +crypto.hkdfExtract = function hkdfExtract(ikm, salt, alg) { + return crypto.hmac(alg, ikm, salt); +}; + +/** + * Perform hkdf expansion. + * @param {Buffer} prk + * @param {Buffer} info + * @param {Number} len + * @param {String} alg + * @returns {Buffer} + */ + +crypto.hkdfExpand = function hkdfExpand(prk, info, len, alg) { + var size = crypto.hash(alg, new Buffer(0)).length; + var blocks = Math.ceil(len / size); + var i, okm, buf, out; + + if (blocks > 255) + throw new Error('Too many blocks.'); + + okm = new Buffer(0); + + if (blocks === 0) + return okm; + + buf = new Buffer(size + info.length + 1); + + // First round: + info.copy(buf, size); + buf[buf.length - 1] = 1; + out = crypto.hmac(alg, buf.slice(size), prk); + okm = out; + + for (i = 1; i < blocks; i++) { + out.copy(buf, 0); + buf[buf.length - 1]++; + out = crypto.hmac(alg, buf, prk); + okm = Buffer.concat([okm, out]); + } + + return okm.slice(0, len); +}; + +/** + * memcmp in constant time (can only return true or false). + * This protects us against timing attacks when + * comparing an input against a secret string. + * @see https://cryptocoding.net/index.php/Coding_rules + * @see `$ man 3 memcmp` (NetBSD's consttime_memequal) + * @param {Buffer} a + * @param {Buffer} b + * @returns {Boolean} + */ + +crypto.ccmp = function ccmp(a, b) { + var res = 0; + var i; + + if (!Buffer.isBuffer(a)) + return false; + + if (!Buffer.isBuffer(b)) + return false; + + // It's assumed the target length + // would be known to an attacker anyway. + if (a.length !== b.length) + return false; + + for (i = 0; i < a.length; i++) + res |= a[i] ^ b[i]; + + return res === 0; +}; + +/** + * Build a merkle tree from leaves. + * @param {Buffer[]} leaves + * @returns {Buffer[]} Tree (in rare cases this may return null). + */ + +crypto.buildMerkleTree = function buildMerkleTree(leaves) { + var tree = leaves.slice(); + var size = leaves.length; + var i, j, i2, hash; + + for (j = 0; size > 1; size = ((size + 1) / 2) | 0) { + for (i = 0; i < size; i += 2) { + i2 = Math.min(i + 1, size - 1); + if (i2 === i + 1 && i2 + 1 === size + && tree[j + i].compare(tree[j + i2]) === 0) { + return; + } + hash = Buffer.concat([tree[j + i], tree[j + i2]]); + hash = crypto.hash256(hash); + tree.push(hash); + } + j += size; + } + + if (!tree.length) + return; + + return tree; +}; + +/** + * Calculate merkle root from leaves. + * @param {Buffer[]} leaves + * @returns {Buffer?} Merkle root. + */ + +crypto.getMerkleRoot = function getMerkleRoot(leaves) { + var tree = crypto.buildMerkleTree(leaves); + if (!tree) + return; + + return tree[tree.length - 1]; +}; + +/** + * Collect a merkle branch at vector index. + * @param {Number} index + * @param {Buffer[]} leaves + * @returns {Buffer[]} branch + */ + +crypto.getMerkleBranch = function getMerkleBranch(index, leaves) { + var tree = crypto.buildMerkleTree(leaves); + var size = leaves.length; + var branch = []; + var j = 0; + var i; + + for (; size > 1; size = (size + 1) / 2 | 0) { + i = Math.min(index ^ 1, size - 1); + branch.push(tree[j + i]); + index >>>= 1; + j += size; + } + + return branch; +}; + +/** + * Check a merkle branch at vector index. + * @param {Buffer} hash + * @param {Buffer[]} branch + * @param {Number} index + * @returns {Buffer} Hash. + */ + +crypto.checkMerkleBranch = function checkMerkleBranch(hash, branch, index) { + var otherside, i; + + if (index === -1) + return false; + + for (i = 0; i < branch.length; i++) { + otherside = branch[i]; + + if (index & 1) + hash = crypto.hash256(Buffer.concat([otherside, hash])); + else + hash = crypto.hash256(Buffer.concat([hash, otherside])); + + index >>>= 1; + } + + return hash; +}; diff --git a/lib/bcoin/utils.js b/lib/bcoin/utils.js index f1bc7df3..eafbfcdb 100644 --- a/lib/bcoin/utils.js +++ b/lib/bcoin/utils.js @@ -18,8 +18,9 @@ var utils = exports; var assert = require('assert'); var bn = require('bn.js'); var util = require('util'); +var crypto = require('./crypto'); var Number, Math, Date; -var fs, crypto, supersha, hash, aes; +var fs, crypto; /** * Reference to the global object. @@ -51,18 +52,8 @@ utils.isBrowser = (typeof process !== 'undefined' && process.browser) || typeof window !== 'undefined'; -if (!utils.isBrowser) { +if (!utils.isBrowser) fs = require('f' + 's'); - crypto = require('cry' + 'pto'); - try { - supersha = require('super' + 'sha'); - } catch (e) { - ; - } -} else { - hash = require('hash.js'); - aes = require('./aes'); -} Number = utils.global.Number; Math = utils.global.Math; @@ -240,104 +231,82 @@ utils.isBase58 = function isBase58(obj) { /** * Hash with chosen algorithm. + * @function * @param {String} alg * @param {Buffer} data * @returns {Buffer} */ -utils.hash = function _hash(alg, data) { - if (!crypto) - return new Buffer(hash[alg]().update(data).digest()); - - return crypto.createHash(alg).update(data).digest(); -}; +utils.hash = crypto.hash; /** * Hash with ripemd160. + * @function * @param {Buffer} data * @returns {Buffer} */ -utils.ripemd160 = function ripemd160(data) { - return utils.hash('ripemd160', data); -}; +utils.ripemd160 = crypto.ripemd160; /** * Hash with sha1. + * @function * @param {Buffer} data * @returns {Buffer} */ -utils.sha1 = function sha1(data) { - return utils.hash('sha1', data); -}; +utils.sha1 = crypto.sha1; /** * Hash with sha256. + * @function * @param {Buffer} data * @returns {Buffer} */ -utils.sha256 = function sha256(data) { - if (supersha) - return supersha.sha256(data); - return utils.hash('sha256', data); -}; +utils.sha256 = crypto.sha256; /** * Hash with sha256 and ripemd160 (OP_HASH160). + * @function * @param {Buffer} data * @returns {Buffer} */ -utils.hash160 = function hash160(data) { - return utils.ripemd160(utils.sha256(data)); -}; +utils.hash160 = crypto.hash160; /** * Hash with sha256 twice (OP_HASH256). + * @function * @param {Buffer} data * @returns {Buffer} */ -utils.hash256 = function hash256(data) { - if (supersha) - return supersha.hash256(data); - return utils.sha256(utils.sha256(data)); -}; +utils.hash256 = crypto.hash256; /** * Create a sha256 checksum (common in bitcoin). + * @function * @param {Buffer} data * @returns {Buffer} */ -utils.checksum = function checksum(data) { - return utils.hash256(data).slice(0, 4); -}; +utils.checksum = crypto.checksum; /** * Create an HMAC. + * @function * @param {String} alg * @param {Buffer} data * @param {Buffer} salt * @returns {Buffer} HMAC */ -utils.hmac = function hmac(alg, data, salt) { - var hmac; - - if (!crypto) { - hmac = hash.hmac(hash[alg], salt); - return new Buffer(hmac.update(data).digest()); - } - - hmac = crypto.createHmac(alg, salt); - return hmac.update(data).digest(); -}; +utils.hmac = crypto.hmac; /** * Perform key stretching using PBKDF2. + * @function * @param {Buffer} key * @param {Buffer} salt * @param {Number} iter @@ -346,21 +315,11 @@ utils.hmac = function hmac(alg, data, salt) { * @returns {Buffer} */ -utils.pbkdf2Sync = function pbkdf2Sync(key, salt, iter, len, alg) { - if (typeof key === 'string') - key = new Buffer(key, 'utf8'); - - if (typeof salt === 'string') - salt = new Buffer(salt, 'utf8'); - - if (crypto && crypto.pbkdf2Sync) - return crypto.pbkdf2Sync(key, salt, iter, len, alg); - - return utils._pbkdf2(key, salt, iter, len, alg); -}; +utils.pbkdf2Sync = crypto.pbkdf2Sync; /** * Execute pbkdf2 asynchronously. + * @function * @param {Buffer} key * @param {Buffer} salt * @param {Number} iter @@ -369,193 +328,75 @@ utils.pbkdf2Sync = function pbkdf2Sync(key, salt, iter, len, alg) { * @param {Function} callback */ -utils.pbkdf2 = function pbkdf2(key, salt, iter, len, alg, callback) { - var result; - - if (typeof key === 'string') - key = new Buffer(key, 'utf8'); - - if (typeof salt === 'string') - salt = new Buffer(salt, 'utf8'); - - if (crypto && crypto.pbkdf2) - return crypto.pbkdf2(key, salt, iter, len, alg, callback); - - try { - result = utils._pbkdf2(key, salt, iter, len, alg); - } catch (e) { - return callback(e); - } - - return utils.asyncify(callback)(null, result); -}; +utils.pbkdf2 = crypto.pbkdf2; /** * Derive a key using pbkdf2 with 50,000 iterations. + * @function * @param {Buffer|String} passphrase * @param {Function} callback */ -utils.derive = function derive(passphrase, callback) { - utils.pbkdf2(passphrase, 'bcoin', 50000, 32, 'sha256', callback); -}; +utils.derive = crypto.derive; /** * Encrypt with aes-256-cbc. Derives key with {@link utils.derive}. + * @function * @param {Buffer} data * @param {Buffer|String} passphrase * @param {Buffer} iv - 128 bit initialization vector. * @param {Function} callback */ -utils.encrypt = function encrypt(data, passphrase, iv, callback) { - assert(Buffer.isBuffer(data)); - assert(passphrase, 'No passphrase.'); - assert(Buffer.isBuffer(iv)); - - utils.derive(passphrase, function(err, key) { - if (err) - return callback(err); - - try { - data = utils.encipher(data, key, iv); - } catch (e) { - key.fill(0); - return callback(e); - } - - key.fill(0); - - return callback(null, data); - }); -}; +utils.encrypt = crypto.encrypt; /** * Encrypt with aes-256-cbc. + * @function * @param {Buffer} data * @param {Buffer} key - 256 bit key. * @param {Buffer} iv - 128 bit initialization vector. * @returns {Buffer} */ -utils.encipher = function encipher(data, key, iv) { - var cipher; - - if (!crypto) - return aes.cbc.encrypt(data, key, iv); - - cipher = crypto.createCipheriv('aes-256-cbc', key, iv); - - return Buffer.concat([ - cipher.update(data), - cipher.final() - ]); -}; +utils.encipher = crypto.encipher; /** * Decrypt with aes-256-cbc. Derives key with {@link utils.derive}. + * @function * @param {Buffer} data * @param {Buffer|String} passphrase * @param {Buffer} iv - 128 bit initialization vector. * @param {Function} callback */ -utils.decrypt = function decrypt(data, passphrase, iv, callback) { - assert(Buffer.isBuffer(data)); - assert(passphrase, 'No passphrase.'); - assert(Buffer.isBuffer(iv)); - - utils.derive(passphrase, function(err, key) { - if (err) - return callback(err); - - try { - data = utils.decipher(data, key, iv); - } catch (e) { - key.fill(0); - return callback(e); - } - - key.fill(0); - - return callback(null, data); - }); -}; +utils.decrypt = crypto.decrypt; /** * Decrypt with aes-256-cbc. + * @function * @param {Buffer} data * @param {Buffer} key - 256 bit key. * @param {Buffer} iv - 128 bit initialization vector. * @returns {Buffer} */ -utils.decipher = function decipher(data, key, iv) { - var decipher; - - if (!crypto) - return aes.cbc.decrypt(data, key, iv); - - decipher = crypto.createDecipheriv('aes-256-cbc', key, iv); - - return Buffer.concat([ - decipher.update(data), - decipher.final() - ]); -}; - -/** - * Perform key stretching using PBKDF2. - * @private - * @param {Buffer} key - * @param {Buffer} salt - * @param {Number} iter - * @param {Number} len - * @param {String} alg - * @returns {Buffer} - */ - -utils._pbkdf2 = function pbkdf2(key, salt, iter, len, alg) { - var size = utils.hash(alg, new Buffer(0)).length; - var blocks = Math.ceil(len / size); - var out = new Buffer(blocks * size); - var buf = new Buffer(salt.length + 4); - var block = new Buffer(size); - var pos = 0; - var i, j, k, mac; - - salt.copy(buf, 0); - - for (i = 0; i < blocks; i++) { - buf.writeUInt32BE(i + 1, salt.length, true); - mac = utils.hmac(alg, buf, key); - mac.copy(block, 0); - for (j = 1; j < iter; j++) { - mac = utils.hmac(alg, mac, key); - for (k = 0; k < size; k++) - block[k] ^= mac[k]; - } - block.copy(out, pos); - pos += size; - } - - return out.slice(0, len); -}; +utils.decipher = crypto.decipher; /** * Perform hkdf extraction. + * @function * @param {Buffer} ikm * @param {Buffer} salt * @param {String} alg * @returns {Buffer} */ -utils.hkdfExtract = function hkdfExtract(ikm, salt, alg) { - return utils.hmac(alg, ikm, salt); -}; +utils.hkdfExtract = crypto.hkdfExtract; /** * Perform hkdf expansion. + * @function * @param {Buffer} prk * @param {Buffer} info * @param {Number} len @@ -563,36 +404,59 @@ utils.hkdfExtract = function hkdfExtract(ikm, salt, alg) { * @returns {Buffer} */ -utils.hkdfExpand = function hkdfExpand(prk, info, len, alg) { - var size = utils.hash(alg, new Buffer(0)).length; - var blocks = Math.ceil(len / size); - var i, okm, buf, out; +utils.hkdfExpand = crypto.hkdfExpand; - if (blocks > 255) - throw new Error('Too many blocks.'); +/** + * memcmp in constant time (can only return true or false). + * This protects us against timing attacks when + * comparing an input against a secret string. + * @see https://cryptocoding.net/index.php/Coding_rules + * @see `$ man 3 memcmp` (NetBSD's consttime_memequal) + * @param {Buffer} a + * @param {Buffer} b + * @returns {Boolean} + */ - okm = new Buffer(0); +utils.ccmp = crypto.ccmp; - if (blocks === 0) - return okm; +/** + * Build a merkle tree from leaves. + * @function + * @param {Buffer[]} leaves + * @returns {Buffer[]} Tree (in rare cases this may return null). + */ - buf = new Buffer(size + info.length + 1); +utils.buildMerkleTree = crypto.buildMerkleTree; - // First round: - info.copy(buf, size); - buf[buf.length - 1] = 1; - out = utils.hmac(alg, buf.slice(size), prk); - okm = out; +/** + * Calculate merkle root from leaves. + * @function + * @param {Buffer[]} leaves + * @returns {Buffer?} Merkle root. + */ - for (i = 1; i < blocks; i++) { - out.copy(buf, 0); - buf[buf.length - 1]++; - out = utils.hmac(alg, buf, prk); - okm = Buffer.concat([okm, out]); - } +utils.getMerkleRoot = crypto.getMerkleRoot; - return okm.slice(0, len); -}; +/** + * Collect a merkle branch at vector index. + * @function + * @param {Number} index + * @param {Buffer[]} leaves + * @returns {Buffer[]} branch + */ + +utils.getMerkleBranch = crypto.getMerkleBranch; + +/** + * Check a merkle branch at vector index. + * @function + * @param {Buffer} hash + * @param {Buffer[]} branch + * @param {Number} index + * @returns {Buffer} Hash. + */ + +utils.checkMerkleBranch = crypto.checkMerkleBranch; /** * Test whether a string is hex. Note that this @@ -1064,18 +928,6 @@ utils.sortKeys = function sortKeys(keys) { }); }; -/** - * Sort HD public keys lexicographically. - * @param {HDPublicKey[]} keys - * @returns {HDPublicKey[]} Sorted keys. - */ - -utils.sortHDKeys = function sortHDKeys(keys) { - return keys.slice().sort(function(a, b) { - return utils.cmp(a.publicKey, b.publicKey); - }); -}; - /** * Sort transactions by timestamp. * @param {TX[]} txs @@ -1946,38 +1798,6 @@ utils.icmp = function icmp(target, data, start) { return 0; }; -/** - * memcmp in constant time (can only return true or false). - * This protects us against timing attacks when - * comparing an input against a secret string. - * @see https://cryptocoding.net/index.php/Coding_rules - * @see `$ man 3 memcmp` (NetBSD's consttime_memequal) - * @param {Buffer} a - * @param {Buffer} b - * @returns {Boolean} - */ - -utils.ccmp = function ccmp(a, b) { - var res = 0; - var i; - - if (!Buffer.isBuffer(a)) - return false; - - if (!Buffer.isBuffer(b)) - return false; - - // It's assumed the target length - // would be known to an attacker anyway. - if (a.length !== b.length) - return false; - - for (i = 0; i < a.length; i++) - res |= a[i] ^ b[i]; - - return res === 0; -}; - /** * Asnchronously iterate over a range in parallel. * @param {Number} from @@ -2244,108 +2064,6 @@ utils.once = function once(callback) { return onceFn; }; -/** - * Build a merkle tree from leaves. - * @param {Buffer[]} leaves - * @returns {Buffer[]} Tree (in rare cases this may return null). - */ - -utils.buildMerkleTree = function buildMerkleTree(leaves) { - var tree = leaves.slice(); - var i, j, size, i2, hash; - - j = 0; - size = leaves.length; - - for (; size > 1; size = ((size + 1) / 2) | 0) { - for (i = 0; i < size; i += 2) { - i2 = Math.min(i + 1, size - 1); - if (i2 === i + 1 && i2 + 1 === size - && utils.equal(tree[j + i], tree[j + i2])) { - return; - } - hash = Buffer.concat([tree[j + i], tree[j + i2]]); - hash = utils.hash256(hash); - tree.push(hash); - } - j += size; - } - - if (!tree.length) - return; - - return tree; -}; - -/** - * Calculate merkle root from leaves. - * @param {Buffer[]} leaves - * @returns {Buffer?} Merkle root. - */ - -utils.getMerkleRoot = function getMerkleRoot(leaves) { - var tree = utils.buildMerkleTree(leaves); - if (!tree) - return; - - return tree[tree.length - 1]; -}; - -/** - * Collect a merkle branch at vector index. - * @param {Number} index - * @param {Buffer[]} leaves - * @returns {Buffer[]} branch - */ - -utils.getMerkleBranch = function getMerkleBranch(index, leaves) { - var tree = utils.buildMerkleTree(leaves); - var size = leaves.length; - var branch = []; - var j = 0; - var i; - - for (; size > 1; size = (size + 1) / 2 | 0) { - i = Math.min(index ^ 1, size - 1); - branch.push(tree[j + i]); - index >>>= 1; - j += size; - } - - return branch; -}; - -/** - * Check a merkle branch at vector index. - * @param {Buffer} hash - * @param {Buffer[]} branch - * @param {Number} index - * @returns {Buffer} Hash. - */ - -utils.checkMerkleBranch = function checkMerkleBranch(hash, branch, index) { - var otherside, i; - - if (index === -1) - return false; - - if (typeof hash === 'string') - hash = new Buffer(hash, 'hex'); - - for (i = 0; i < branch.length; i++) { - otherside = branch[i]; - - if (index & 1) - hash = utils.hash256(Buffer.concat([otherside, hash])); - else - hash = utils.hash256(Buffer.concat([hash, otherside])); - - index >>>= 1; - } - - return hash; -}; - /** * Find index of a buffer in an array of buffers. * @param {Buffer[]} obj