move crypto to its own module.
This commit is contained in:
parent
78919ffde7
commit
253a6795a3
518
lib/bcoin/crypto.js
Normal file
518
lib/bcoin/crypto.js
Normal file
@ -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;
|
||||
};
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user