crypto: implement subtle api for browser.

This commit is contained in:
Christopher Jeffrey 2016-11-21 00:55:52 -08:00
parent 4245cd6bf1
commit f376289684
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
31 changed files with 1009 additions and 377 deletions

View File

@ -8,6 +8,7 @@
var assert = require('assert');
var util = require('../utils/util');
var co = require('../utils/co');
var crypto = require('../crypto/crypto');
var x509 = require('./x509');
var PEM = require('../utils/pem');
@ -226,6 +227,34 @@ PaymentRequest.prototype.verifyChain = function verifyChain() {
return x509.verifyChain(this.getChain());
};
PaymentRequest.prototype.verifyAsync = co(function* verifyAsync() {
var alg, msg, sig, chain;
if (!this.pkiType || this.pkiType === 'none')
return true;
if (!this.signature)
return false;
alg = this.getAlgorithm();
if (!alg)
return false;
msg = this.signatureData();
sig = this.signature;
chain = this.getChain();
return yield x509.verifySubjectAsync(alg.hash, msg, sig, chain);
});
PaymentRequest.prototype.verifyChainAsync = co(function* verifyChain() {
if (!this.pkiType || this.pkiType === 'none')
return true;
return yield x509.verifyChainAsync(this.getChain());
});
PaymentRequest.prototype.getCA = function getCA() {
var chain, root;

View File

@ -7,6 +7,7 @@
'use strict';
var pk = require('../crypto/pk');
var co = require('../utils/co');
exports._verify = function verify(hash, msg, sig, key) {
switch (key.alg) {
@ -41,3 +42,37 @@ exports.sign = function sign(hash, msg, key) {
throw new Error('Unsupported algorithm.');
}
};
exports._verifyAsync = co(function* verifyAsync(hash, msg, sig, key) {
switch (key.alg) {
case 'dsa':
return yield pk.dsa.verifyAsync(hash, msg, sig, key.data, key.params);
case 'rsa':
return yield pk.rsa.verifyAsync(hash, msg, sig, key.data);
case 'ecdsa':
return yield pk.ecdsa.verifyAsync(key.curve, hash, msg, sig, key.data);
default:
throw new Error('Unsupported algorithm.');
}
});
exports.verifyAsync = co(function* verifyAsync(hash, msg, sig, key) {
try {
return yield exports._verifyAsync(hash, msg, sig, key);
} catch (e) {
return false;
}
});
exports.signAsync = co(function* signAsync(hash, msg, key) {
switch (key.alg) {
case 'dsa':
return yield pk.dsa.signAsync(hash, msg, key.data, key.params);
case 'rsa':
return yield pk.rsa.signAsync(hash, msg, key.data);
case 'ecdsa':
return yield pk.ecdsa.signAsync(key.curve, hash, msg, key.data);
default:
throw new Error('Unsupported algorithm.');
}
});

View File

@ -12,6 +12,7 @@ var PEM = require('../utils/pem');
var util = require('../utils/util');
var crypto = require('../crypto/crypto');
var pk = require('./pk');
var co = require('../utils/co');
var x509 = exports;
x509.getSubjectOID = function getSubjectOID(cert, oid) {
@ -177,11 +178,11 @@ x509.getPublicKey = function getPublicKey(cert) {
x509.verifyTime = function verifyTime(cert) {
var time = cert.tbs.validity;
var now = Math.floor(Date.now() / 1000);
var now = util.now();
return now > time.notBefore && now < time.notAfter;
};
x509.signSubject = function signSubject(hash, msg, key, chain) {
x509.getSigningKey = function getSigningKey(key, chain) {
var cert, pub, curve;
assert(chain.length !== 0, 'No chain available.');
@ -210,10 +211,20 @@ x509.signSubject = function signSubject(hash, msg, key, chain) {
};
}
return pk.sign(hash, msg, key);
return key;
};
x509.verifySubject = function verifySubject(hash, msg, sig, chain) {
x509.signSubject = function signSubject(hash, msg, key, chain) {
var priv = x509.getSigningKey(key, chain);
return pk.sign(hash, msg, priv);
};
x509.signSubjectAsync = co(function* signSubjectAsync(hash, msg, key, chain) {
var priv = x509.getSigningKey(key, chain);
return yield pk.signAsync(hash, msg, priv);
});
x509.getVerifyKey = function getVerifyKey(chain) {
var cert, key;
if (chain.length === 0)
@ -229,27 +240,82 @@ x509.verifySubject = function verifySubject(hash, msg, sig, chain) {
if (!key)
return false;
return key;
};
x509.verifySubject = function verifySubject(hash, msg, sig, chain) {
var key = x509.getVerifyKey(chain);
return pk.verify(hash, msg, sig, key);
};
x509.verifyChain = function verifyChain(chain) {
x509.verifySubjectAsync = co(function* verifySubjectAsync(hash, msg, sig, chain) {
var key = x509.getVerifyKey(chain);
return yield pk.verifyAsync(hash, msg, sig, key);
});
x509.parseChain = function parseChain(chain) {
var certs = [];
var i, cert;
for (i = 0; i < chain.length; i++) {
cert = x509.parse(chain[i]);
if (!cert)
return;
certs.push(cert);
}
return certs;
};
x509.verifyTimes = function verifyTimes(chain) {
var i, cert;
for (i = 0; i < chain.length; i++) {
cert = chain[i];
if (!x509.verifyTime(cert))
return false;
}
return true;
};
x509.verifyTrust = function verifyTrust(chain) {
var i, cert;
// If trust hasn't been
// setup, just return.
if (x509.allowUntrusted)
return true;
// Make sure we trust one
// of the certs in the chain.
for (i = 0; i < chain.length; i++) {
cert = chain[i];
// If any certificate in the chain
// is trusted, assume we also trust
// the parent.
if (x509.isTrusted(cert))
return true;
}
// No trusted certs present.
return false;
};
x509.verifyChain = function verifyChain(certs) {
var chain = x509.parseChain(certs);
var i, child, parent, alg, key, sig, msg;
chain = chain.slice();
if (!chain)
return false;
// Parse certificates and
// check validity time.
for (i = 0; i < chain.length; i++) {
child = x509.parse(chain[i]);
if (!child)
return false;
chain[i] = child;
if (!x509.verifyTime(child))
return false;
}
if (!x509.verifyTimes(chain))
return false;
// Verify signatures.
for (i = 1; i < chain.length; i++) {
@ -271,26 +337,47 @@ x509.verifyChain = function verifyChain(chain) {
return false;
}
// If trust hasn't been
// setup, just return.
if (x509.allowUntrusted)
return true;
// Make sure we trust one
// of the certs in the chain.
return x509.verifyTrust(chain);
};
x509.verifyChainAsync = co(function* verifyChainAsync(certs) {
var chain = x509.parseChain(certs);
var i, child, parent, alg, key, sig, msg;
if (!chain)
return false;
// Parse certificates and
// check validity time.
if (!x509.verifyTimes(chain))
return false;
// Verify signatures.
for (i = 1; i < chain.length; i++) {
child = chain[i - 1];
parent = chain[i];
alg = x509.getSigAlgorithm(child);
msg = child.tbs.raw;
sig = child.sig;
key = x509.getPublicKey(parent);
if (!alg || !alg.hash)
return false;
if (!key)
return false;
if (!(yield pk.verifyAsync(alg.hash, msg, sig, key)))
return false;
}
// Make sure we trust one
// of the certs in the chain.
for (i = 0; i < chain.length; i++) {
child = chain[i];
// If any certificate in the chain
// is trusted, assume we also trust
// the parent.
if (x509.isTrusted(child))
return true;
}
// No trusted certs present.
return false;
};
return x509.verifyTrust(chain);
});
function isHash(data) {
if (typeof data === 'string')

View File

@ -1318,6 +1318,8 @@ Chain.prototype._add = co(function* add(block) {
'error parsing message',
100);
}
if (util.isBrowser)
yield block.cacheHashes();
}
// Update the block height early

View File

@ -33,14 +33,22 @@ function AESKey(key, bits) {
this.userKey = key;
this.bits = bits;
if (this.bits === 128)
this.rounds = 10;
else if (this.bits === 192)
this.rounds = 12;
else if (this.bits === 256)
this.rounds = 14;
else
throw new Error('Bad key size.');
switch (this.bits) {
case 128:
this.rounds = 10;
break;
case 192:
this.rounds = 12;
break;
case 256:
this.rounds = 14;
break;
default:
throw new Error('Bad key size.');
}
assert(Buffer.isBuffer(key));
assert(key.length === this.bits / 8);
this.decryptKey = null;
this.encryptKey = null;
@ -688,6 +696,8 @@ AES.ecb = {};
*/
AES.ecb.encrypt = function encrypt(data, key) {
assert(Buffer.isBuffer(data));
assert(key.length === 32);
return AES.encrypt(data, key, null, 256, 'ecb');
};
@ -699,6 +709,8 @@ AES.ecb.encrypt = function encrypt(data, key) {
*/
AES.ecb.decrypt = function decrypt(data, key) {
assert(Buffer.isBuffer(data));
assert(key.length === 32);
return AES.decrypt(data, key, null, 256, 'ecb');
};
@ -717,6 +729,9 @@ AES.cbc = {};
*/
AES.cbc.encrypt = function encrypt(data, key, iv) {
assert(Buffer.isBuffer(data));
assert(key.length === 32);
assert(iv.length === 16);
return AES.encrypt(data, key, iv, 256, 'cbc');
};
@ -729,6 +744,9 @@ AES.cbc.encrypt = function encrypt(data, key, iv) {
*/
AES.cbc.decrypt = function decrypt(data, key, iv) {
assert(Buffer.isBuffer(data));
assert(key.length === 32);
assert(iv.length === 16);
return AES.decrypt(data, key, iv, 256, 'cbc');
};

View File

@ -4,57 +4,168 @@
* https://github.com/bcoin-org/bcoin
*/
/* jshint worker: true */
'use strict';
var assert = require('assert');
var hashjs = require('hash.js');
var util = require('../utils/util');
var aes = require('./aes');
var global = util.global;
var crypto = global.crypto || global.msCrypto || {};
var subtle = crypto.subtle && crypto.subtle.importKey ? crypto.subtle : {};
var backend = exports;
var global, crypto, subtle;
global = (function() {
if (typeof window !== 'undefined')
return window;
/*
* Hashing
*/
if (typeof self !== 'undefined')
return self;
throw new Error('No global found.');
})();
crypto = global.crypto || global.msCrypto;
subtle = crypto ? crypto.subtle : null;
backend.hash = function hash(alg, data) {
return new Buffer(hashjs[alg]().update(data).digest());
backend.hash = function _hash(alg, data) {
var hash = hashjs[alg];
assert(hash != null, 'Unknown algorithm.');
return new Buffer(hash().update(data).digest());
};
backend.hmac = function hmac(alg, data, salt) {
backend.ripemd160 = function ripemd160(data) {
return backend.hash('ripemd160', data);
};
backend.sha1 = function sha1(data) {
return backend.hash('sha1', data);
};
backend.sha256 = function sha256(data) {
return backend.hash('sha256', data);
};
backend.hash160 = function hash160(data) {
return backend.ripemd160(backend.sha256(data));
};
backend.hash256 = function hash256(data) {
return backend.sha256(backend.sha256(data));
};
backend.hmac = function _hmac(alg, data, key) {
var hash = hashjs[alg];
var hmac;
assert(hash != null, 'Unknown algorithm.');
hmac = hashjs.hmac(hash, salt);
hmac = hashjs.hmac(hash, key);
return new Buffer(hmac.update(data).digest());
};
backend.pbkdf2 = null;
backend.hashAsync = function hashAsync(alg, data) {
var name = backend.getHash(alg);
var result;
if (!name) {
try {
result = backend.hash(alg, data);
} catch (e) {
return Promise.reject(e);
}
return Promise.resolve(result);
}
return subtle.digest(name, data).then(function(hash) {
return new Buffer(hash);
});
};
if (!subtle.digest)
backend.hashAsync = util.promisify(backend.hash);
backend.hash256Async = function hash256Async(data) {
return backend.hashAsync('sha256', data).then(function(hash) {
return backend.hashAsync('sha256', hash);
});
};
backend.hmacAsync = function _hmacAsync(alg, data, key) {
var name = backend.getHash(alg);
var use = ['sign'];
var algo, promise, result;
if (!name) {
try {
result = backend.hmac(alg, data, key);
} catch (e) {
return Promise.reject(e);
}
return Promise.resolve(result);
}
algo = {
name: 'HMAC',
hash: name
};
promise = subtle.importKey('raw', key, algo, true, use);
return promise.then(function(key) {
return subtle.sign('HMAC', key, data);
}).then(function(data) {
return new Buffer(data);
});
};
if (!subtle.sign)
backend.hmacAsync = util.promisify(backend.hmac);
/*
* Key Derivation
*/
backend.pbkdf2 = function pbkdf2(key, salt, iter, len, alg) {
var size = backend.hash(alg, new Buffer(0)).length;
var blocks = Math.ceil(len / size);
var out = new Buffer(len);
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 = backend.hmac(alg, buf, key);
mac.copy(block, 0);
for (j = 1; j < iter; j++) {
mac = backend.hmac(alg, mac, key);
for (k = 0; k < size; k++)
block[k] ^= mac[k];
}
block.copy(out, pos);
pos += size;
}
return out;
};
backend.pbkdf2Async = function pbkdf2Async(key, salt, iter, len, alg) {
var algo = { name: 'PBKDF2' };
var use = ['deriveBits'];
var name = backend.getHash(alg);
var length = len * 8;
var options, promise;
var options, promise, result;
if (!name) {
try {
result = backend.pbkdf2(key, salt, iter, len, alg);
} catch (e) {
return Promise.reject(e);
}
return Promise.resolve(result);
}
options = {
name: 'PBKDF2',
salt: salt,
iterations: iter,
hash: getHash(alg)
hash: name
};
promise = subtle.importKey('raw', key, algo, false, use);
@ -66,8 +177,12 @@ backend.pbkdf2Async = function pbkdf2Async(key, salt, iter, len, alg) {
});
};
if (!subtle || !subtle.importKey || !subtle.deriveBits)
backend.pbkdf2Async = null;
if (!subtle.deriveBits)
backend.pbkdf2Async = util.promisify(backend.pbkdf2);
/*
* Ciphers
*/
backend.encipher = function encipher(data, key, iv) {
return aes.cbc.encrypt(data, key, iv);
@ -81,13 +196,53 @@ backend.decipher = function decipher(data, key, iv) {
}
};
backend.encipherAsync = function encipherAsync(data, key, iv) {
var algo = { name: 'AES-CBC' };
var use = ['encrypt'];
var options = { name: 'AES-CBC', iv: iv };
var promise;
promise = subtle.importKey('raw', key, algo, false, use);
return promise.then(function(key) {
return subtle.encrypt(options, key, data);
}).then(function(result) {
return new Buffer(result);
});
};
if (!subtle.encrypt)
backend.encipherAsync = util.promisify(backend.encipher);
backend.decipherAsync = function decipherAsync(data, key, iv) {
var algo = { name: 'AES-CBC' };
var use = ['decrypt'];
var options = { name: 'AES-CBC', iv: iv };
var promise;
promise = subtle.importKey('raw', key, algo, false, use);
return promise.then(function(key) {
return subtle.decrypt(options, key, data);
}).then(function(result) {
return new Buffer(result);
});
};
if (!subtle.decrypt)
backend.decipherAsync = util.promisify(backend.decipher);
/*
* Misc
*/
backend.randomBytes = function randomBytes(n) {
var data = new Uint8Array(n);
crypto.getRandomValues(data);
return new Buffer(data.buffer);
};
if (!crypto || !crypto.getRandomValues) {
if (!crypto.getRandomValues) {
// Out of luck here. Use bad randomness for now.
backend.randomBytes = function randomBytes(n) {
var data = new Buffer(n);
@ -100,7 +255,7 @@ if (!crypto || !crypto.getRandomValues) {
};
}
function getHash(name) {
backend.getHash = function getHash(name) {
switch (name) {
case 'sha1':
return 'SHA-1';
@ -111,6 +266,9 @@ function getHash(name) {
case 'sha512':
return 'SHA-512';
default:
throw new Error('Unknown hash.');
return null;
}
}
};
backend.crypto = crypto;
backend.subtle = subtle;

View File

@ -7,31 +7,78 @@
'use strict';
var util = require('../utils/util');
var co = require('../utils/co');
var crypto = require('crypto');
var native = require('../utils/native').binding;
var backend = exports;
if (!crypto.pbkdf2Sync)
throw new Error('This modules requires node.js v0.11.0 or above.');
/*
* Hashing
*/
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);
backend.ripemd160 = function ripemd160(data) {
return backend.hash('ripemd160', data);
};
backend.sha1 = function sha1(data) {
return backend.hash('sha1', data);
};
backend.sha256 = function sha256(data) {
return backend.hash('sha256', data);
};
backend.hash160 = function hash160(data) {
return backend.ripemd160(backend.sha256(data));
};
backend.hash256 = function hash256(data) {
return backend.sha256(backend.sha256(data));
};
backend.hmac = function hmac(alg, data, key) {
var hmac = crypto.createHmac(alg, key);
return hmac.update(data).digest();
};
if (native) {
backend.hash = native.hash;
backend.hmac = native.hmac;
backend.ripemd160 = native.ripemd160;
backend.sha1 = native.sha1;
backend.sha256 = native.sha256;
backend.hash160 = native.hash160;
backend.hash256 = native.hash256;
}
backend.hashAsync = util.promisify(backend.hash);
backend.hash256Async = util.promisify(backend.hash256);
backend.hmacAsync = util.promisify(backend.hmac);
/*
* Key Derivation
*/
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);
backend.pbkdf2Async = function pbkdf2Async(key, salt, iter, len, alg) {
return new Promise(function(resolve, reject) {
crypto.pbkdf2(key, salt, iter, len, alg, co.wrap(resolve, reject));
});
};
if (!crypto.pbkdf2)
backend.pbkdf2Async = null;
/*
* Ciphers
*/
backend.encipher = function encipher(data, key, iv) {
var cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
@ -47,4 +94,16 @@ backend.decipher = function decipher(data, key, iv) {
}
};
if (native) {
backend.encipher = native.encipher;
backend.decipher = native.decipher;
}
backend.encipherAsync = util.promisify(backend.encipher);
backend.decipherAsync = util.promisify(backend.decipher);
/*
* Misc
*/
backend.randomBytes = crypto.randomBytes;

View File

@ -7,114 +7,111 @@
'use strict';
var assert = require('assert');
var backend = require('./backend');
var native = require('../utils/native').binding;
var scrypt = require('./scrypt');
var scryptAsync = require('./scrypt-async');
var co = require('../utils/co');
var native = require('../utils/native').binding;
var backend = require('./backend');
var crypto = exports;
/**
* Hash with chosen algorithm.
* @function
* @param {String} alg
* @param {Buffer} data
* @returns {Buffer}
*/
crypto.hash = function _hash(alg, data) {
return backend.hash(alg, data);
};
crypto.hash = backend.hash;
if (native)
crypto.hash = native.hash;
/**
* Hash with chosen algorithm (async).
* @function
* @param {String} alg
* @param {Buffer} data
* @returns {Buffer}
*/
crypto.hashAsync = backend.hashAsync;
/**
* Hash with ripemd160.
* @function
* @param {Buffer} data
* @returns {Buffer}
*/
crypto.ripemd160 = function ripemd160(data) {
return crypto.hash('ripemd160', data);
};
crypto.ripemd160 = backend.ripemd160;
/**
* Hash with sha1.
* @function
* @param {Buffer} data
* @returns {Buffer}
*/
crypto.sha1 = function sha1(data) {
return crypto.hash('sha1', data);
};
crypto.sha1 = backend.sha1;
/**
* Hash with sha256.
* @function
* @param {Buffer} data
* @returns {Buffer}
*/
crypto.sha256 = function sha256(data) {
return crypto.hash('sha256', data);
};
if (native)
crypto.sha256 = native.sha256;
crypto.sha256 = backend.sha256;
/**
* Hash with sha256 and ripemd160 (OP_HASH160).
* @function
* @param {Buffer} data
* @returns {Buffer}
*/
crypto.hash160 = function hash160(data) {
return crypto.ripemd160(crypto.sha256(data));
};
if (native)
crypto.hash160 = native.hash160;
crypto.hash160 = backend.hash160;
/**
* Hash with sha256 twice (OP_HASH256).
* @function
* @param {Buffer} data
* @returns {Buffer}
*/
crypto.hash256 = function hash256(data) {
return crypto.sha256(crypto.sha256(data));
};
if (native)
crypto.hash256 = native.hash256;
crypto.hash256 = backend.hash256;
/**
* Create a sha256 checksum (common in bitcoin).
* Hash with sha256 twice (async).
* @function
* @param {Buffer} data
* @returns {Buffer}
*/
crypto.checksum = function checksum(data) {
return crypto.hash256(data).slice(0, 4);
};
crypto.hash256Async = backend.hash256Async;
/**
* Create an HMAC.
* @function
* @param {String} alg
* @param {Buffer} data
* @param {Buffer} salt
* @param {Buffer} key
* @returns {Buffer} HMAC
*/
crypto.hmac = function hmac(alg, data, salt) {
return backend.hmac(alg, data, salt);
};
crypto.hmac = backend.hmac;
if (native)
crypto.hmac = native.hmac;
/**
* Create an HMAC (async).
* @function
* @param {String} alg
* @param {Buffer} data
* @param {Buffer} key
* @returns {Buffer} HMAC
*/
crypto.hmacAsync = backend.hmacAsync;
/**
* Perform key derivation using PBKDF2.
* @function
* @param {Buffer} key
* @param {Buffer} salt
* @param {Number} iter
@ -123,21 +120,11 @@ if (native)
* @returns {Buffer}
*/
crypto.pbkdf2 = function pbkdf2(key, salt, iter, len, alg) {
if (typeof key === 'string')
key = new Buffer(key, 'utf8');
if (typeof salt === 'string')
salt = new Buffer(salt, 'utf8');
if (backend.pbkdf2)
return backend.pbkdf2(key, salt, iter, len, alg);
return crypto._pbkdf2(key, salt, iter, len, alg);
};
crypto.pbkdf2 = backend.pbkdf2;
/**
* Execute pbkdf2 asynchronously.
* @function
* @param {Buffer} key
* @param {Buffer} salt
* @param {Number} iter
@ -146,32 +133,11 @@ crypto.pbkdf2 = function pbkdf2(key, salt, iter, len, alg) {
* @returns {Promise}
*/
crypto.pbkdf2Async = function pbkdf2Async(key, salt, iter, len, alg) {
var result;
if (typeof key === 'string')
key = new Buffer(key, 'utf8');
if (typeof salt === 'string')
salt = new Buffer(salt, 'utf8');
if (backend.pbkdf2Async) {
return new Promise(function(resolve, reject) {
backend.pbkdf2Async(key, salt, iter, len, alg, co.wrap(resolve, reject));
});
}
try {
result = crypto._pbkdf2(key, salt, iter, len, alg);
} catch (e) {
return Promise.reject(e);
}
return Promise.resolve(result);
};
crypto.pbkdf2Async = backend.pbkdf2Async;
/**
* Perform key derivation using scrypt.
* @function
* @param {Buffer} passwd
* @param {Buffer} salt
* @param {Number} N
@ -181,18 +147,11 @@ crypto.pbkdf2Async = function pbkdf2Async(key, salt, iter, len, alg) {
* @returns {Buffer}
*/
crypto.scrypt = function _scrypt(passwd, salt, N, r, p, len) {
if (typeof passwd === 'string')
passwd = new Buffer(passwd, 'utf8');
if (typeof salt === 'string')
salt = new Buffer(salt, 'utf8');
return scrypt(passwd, salt, N, r, p, len);
};
crypto.scrypt = scrypt;
/**
* Execute scrypt asynchronously.
* @function
* @param {Buffer} passwd
* @param {Buffer} salt
* @param {Number} N
@ -202,66 +161,18 @@ crypto.scrypt = function _scrypt(passwd, salt, N, r, p, len) {
* @returns {Promise}
*/
crypto.scryptAsync = function _scrypt(passwd, salt, N, r, p, len) {
if (typeof passwd === 'string')
passwd = new Buffer(passwd, 'utf8');
if (typeof salt === 'string')
salt = new Buffer(salt, 'utf8');
return new Promise(function(resolve, reject) {
scryptAsync(passwd, salt, N, r, p, len, co.wrap(resolve, reject));
});
};
/**
* Perform key derivation 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(len);
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;
};
crypto.scryptAsync = scryptAsync;
/**
* Perform hkdf extraction.
* @param {Buffer} ikm
* @param {Buffer} salt
* @param {Buffer} key
* @param {String} alg
* @returns {Buffer}
*/
crypto.hkdfExtract = function hkdfExtract(ikm, salt, alg) {
return crypto.hmac(alg, ikm, salt);
crypto.hkdfExtract = function hkdfExtract(ikm, key, alg) {
return crypto.hmac(alg, ikm, key);
};
/**
@ -424,38 +335,47 @@ if (native)
/**
* Encrypt with aes-256-cbc.
* @function
* @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) {
assert(Buffer.isBuffer(data));
assert(Buffer.isBuffer(key));
assert(Buffer.isBuffer(iv));
assert(key.length === 32);
assert(iv.length === 16);
crypto.encipher = backend.encipher;
return backend.encipher(data, key, iv);
};
/**
* Encrypt with aes-256-cbc (async).
* @function
* @param {Buffer} data
* @param {Buffer} key - 256 bit key.
* @param {Buffer} iv - 128 bit initialization vector.
* @returns {Promise}
*/
crypto.encipherAsync = backend.encipherAsync;
/**
* Decrypt with aes-256-cbc.
* @function
* @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) {
assert(Buffer.isBuffer(data));
assert(Buffer.isBuffer(key));
assert(Buffer.isBuffer(iv));
assert(key.length === 32);
assert(iv.length === 16);
return backend.decipher(data, key, iv);
};
crypto.decipher = backend.decipher;
/**
* Decrypt with aes-256-cbc (async).
* @function
* @param {Buffer} data
* @param {Buffer} key - 256 bit key.
* @param {Buffer} iv - 128 bit initialization vector.
* @returns {Promise}
*/
crypto.decipherAsync = backend.decipherAsync;
/**
* memcmp in constant time (can only return true or false).
@ -488,21 +408,6 @@ crypto.ccmp = function ccmp(a, b) {
return res === 0;
};
/**
* Compare two bytes in constant time.
* @param {Number} a
* @param {Number} b
* @returns {Boolean}
*/
crypto.ceq = function ceq(a, b) {
var r = ~(a ^ b) & 0xff;
r &= r >>> 4;
r &= r >>> 2;
r &= r >>> 1;
return r === 1;
};
/**
* A maybe-secure memzero.
* @param {Buffer} data

View File

@ -9,7 +9,7 @@
var assert = require('assert');
var util = require('../utils/util');
var crypto = require('./crypto');
var backend = require('./backend');
var secp256k1 = require('secp256k1');
/*
@ -40,7 +40,7 @@ ec.generatePrivateKey = function generatePrivateKey() {
var priv;
do {
priv = crypto.randomBytes(32);
priv = backend.randomBytes(32);
} while (!secp256k1.privateKeyVerify(priv));
return priv;

View File

@ -4,13 +4,15 @@ var crypto = require('./crypto');
exports.crypto = crypto;
exports.hash = crypto.hash;
exports.hashAsync = crypto.hashAsync;
exports.ripemd160 = crypto.ripemd160;
exports.sha1 = crypto.sha1;
exports.sha256 = crypto.sha256;
exports.hash160 = crypto.hash160;
exports.hash256 = crypto.hash256;
exports.checksum = crypto.checksum;
exports.hash256Async = crypto.hash256Async;
exports.hmac = crypto.hmac;
exports.hmacAsync = crypto.hmacAsync;
exports.pbkdf2 = crypto.pbkdf2;
exports.pbkdf2Async = crypto.pbkdf2Async;
exports.scrypt = crypto.scrypt;
@ -24,7 +26,6 @@ exports.checkMerkleBranch = crypto.checkMerkleBranch;
exports.encipher = crypto.encipher;
exports.decipher = crypto.decipher;
exports.ccmp = crypto.ccmp;
exports.ceq = crypto.ceq;
exports.cleanse = crypto.cleanse;
exports.randomBytes = crypto.randomBytes;
exports.randomInt = crypto.randomInt;

View File

@ -9,8 +9,11 @@
var assert = require('assert');
var BN = require('bn.js');
var ASN1 = require('../utils/asn1');
var util = require('../utils/util');
var co = require('../utils/co');
var elliptic = require('elliptic');
var crypto = require('./crypto');
var backend = require('./backend');
var subtle = backend.subtle;
var dsa, rsa, ecdsa;
/*
@ -23,10 +26,14 @@ dsa.verify = function verify(alg, msg, sig, key, params) {
throw new Error('DSA not implemented.');
};
dsa.verifyAsync = util.promisify(dsa.verify);
dsa.sign = function sign(alg, msg, key, params) {
throw new Error('DSA not implemented.');
};
dsa.signAsync = util.promisify(dsa.sign);
/*
* RSA
*/
@ -52,7 +59,7 @@ rsa.verify = function verify(alg, msg, sig, key) {
if (!prefix)
throw new Error('Unknown PKCS prefix.');
hash = crypto.hash(alg, msg);
hash = backend.hash(alg, msg);
len = prefix.length + hash.length;
pub = ASN1.parseRSAPublic(key);
@ -66,14 +73,14 @@ rsa.verify = function verify(alg, msg, sig, key) {
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);
ok = ceq(em[0], 0x00);
ok &= ceq(em[1], 0x01);
ok &= backend.ccmp(em.slice(k - hash.length, k), hash);
ok &= backend.ccmp(em.slice(k - len, k - hash.length), prefix);
ok &= ceq(em[k - len - 1], 0x00);
for (i = 2; i < k - len - 1; i++)
ok &= crypto.ceq(em[i], 0xff);
ok &= ceq(em[i], 0xff);
return ok === 1;
};
@ -86,7 +93,7 @@ rsa.sign = function sign(alg, msg, key) {
if (!prefix)
throw new Error('Unknown PKCS prefix.');
hash = crypto.hash(alg, msg);
hash = backend.hash(alg, msg);
len = prefix.length + hash.length;
priv = ASN1.parseRSAPrivate(key);
@ -110,6 +117,82 @@ rsa.sign = function sign(alg, msg, key) {
return rsa.decrypt(N, D, em);
};
rsa.verifyAsync = co(function* verifyAsync(alg, msg, sig, key) {
var use = ['verify'];
var name = backend.getHash(alg);
var pub, data, algo, ckey;
if (!name)
return rsa.verify(alg, msg, sig, key);
pub = ASN1.parseRSAPublic(key);
data = {
kty: 'RSA',
n: toBase64(pub.modulus),
e: toBase64(pub.publicExponent),
alg: 'RS256',
ext: true
};
algo = {
name: 'RSASSA-PKCS1-v1_5',
hash: { name: name }
};
ckey = yield subtle.importKey('jwk', data, algo, false, use);
algo = {
name: 'RSASSA-PKCS1-v1_5',
};
return yield subtle.verify(algo, ckey, sig, msg);
});
if (!subtle.verify)
rsa.verifyAsync = util.promisify(rsa.verify);
rsa.signAsync = co(function* signAsync(alg, msg, key) {
var use = ['sign'];
var name = backend.getHash(alg);
var pub, data, algo, ckey;
if (!name)
return rsa.sign(alg, msg, key);
pub = ASN1.parseRSAPrivate(key);
data = {
kty: 'RSA',
n: toBase64(pub.modulus),
e: toBase64(pub.publicExponent),
d: toBase64(pub.privateExponent),
p: toBase64(pub.prime1),
q: toBase64(pub.prime2),
dp: toBase64(pub.exponent1),
dq: toBase64(pub.exponent2),
qi: toBase64(pub.coefficient),
alg: 'RS256',
ext: true
};
algo = {
name: 'RSASSA-PKCS1-v1_5',
hash: { name: name }
};
ckey = yield subtle.importKey('jwk', data, algo, false, use);
algo = {
name: 'RSASSA-PKCS1-v1_5',
};
return yield subtle.sign(algo, ckey, msg);
});
if (!subtle.sign)
rsa.signAsync = util.promisify(rsa.sign);
rsa.decrypt = function decrypt(N, D, m) {
var c = new BN(m);
@ -137,28 +220,91 @@ rsa.encrypt = function encrypt(N, e, m) {
ecdsa = {};
ecdsa.verify = function verify(curve, msg, alg, key, sig) {
ecdsa.verify = function verify(curve, alg, msg, key, sig) {
var ec, hash;
assert(curve, 'No curve selected.');
ec = elliptic.ec(curve);
hash = crypto.hash(alg, msg);
hash = backend.hash(alg, msg);
return ec.verify(hash, sig, key);
};
ecdsa.sign = function sign(curve, msg, alg, key) {
ecdsa.sign = function sign(curve, alg, msg, key) {
var ec, hash;
assert(curve, 'No curve selected.');
ec = elliptic.ec(curve);
hash = crypto.hash(alg, msg);
hash = backend.hash(alg, msg);
return new Buffer(ec.sign(hash, key));
};
ecdsa.verifyAsync = co(function* verifyAsync(curve, alg, msg, sig, key) {
var use = ['verify'];
var name = backend.getHash(alg);
var curveName = getCurve(curve);
var pub, data, algo, ckey;
if (!name || !curveName)
return ecdsa.verify(curve, alg, msg, sig, key);
pub = parseECPublic(key, curve);
data = {
kty: 'EC',
x: toBase64(pub.x),
y: toBase64(pub.y),
ext: true
};
algo = {
name: 'ECDSA',
namedCurve: curveName
};
ckey = yield subtle.importKey('jwk', data, algo, false, use);
algo = {
name: 'ECDSA',
hash: name
};
return yield subtle.verify(algo, ckey, sig, msg);
});
if (!subtle.verify)
ecdsa.verifyAsync = util.promisify(ecdsa.verify);
ecdsa.signAsync = co(function* signAsync(curve, alg, msg, key) {
var use = ['sign'];
var name = backend.getHash(alg);
var curveName = getCurve(curve);
var algo, ckey;
if (!name || !curveName)
return ecdsa.sign(curve, alg, msg, key);
algo = {
name: 'ECDSA',
namedCurve: curveName
};
ckey = yield subtle.importKey('raw', key, algo, false, use);
algo = {
name: 'ECDSA',
hash: name
};
return yield subtle.sign(algo, ckey, msg);
});
if (!subtle.sign)
ecdsa.signAsync = util.promisify(ecdsa.sign);
/*
* Helpers
*/
@ -178,6 +324,44 @@ function leftpad(input, size) {
return out;
}
function toBase64(data) {
var str = data.toString('base64');
str = str.replace(/\+/g, '-');
str = str.replace(/\//g, '_');
str = str.replace(/=+$/, '');
return str;
}
function getCurve(name) {
switch (name) {
case 'p256':
return 'P-256';
case 'p384':
return 'P-384';
case 'p521':
return 'P-521';
default:
return null;
}
}
function parseECPublic(data, curve) {
var ec = elliptic.ec(curve).curve;
var point = ec.decodePoint(data);
return {
x: point.toArrayLike(Buffer, 'be', 32),
y: point.toArrayLike(Buffer, 'be', 32)
};
}
function ceq(a, b) {
var r = ~(a ^ b) & 0xff;
r &= r >>> 4;
r &= r >>> 2;
r &= r >>> 1;
return r === 1;
}
/*
* Expose
*/

View File

@ -9,7 +9,8 @@
var assert = require('assert');
var PEM = require('../utils/pem');
var elliptic = require('elliptic');
var crypto = require('./crypto');
var util = require('../utils/util');
var backend = require('./backend');
var nodeCrypto = require('crypto');
var dsa, rsa, ecdsa;
@ -29,6 +30,9 @@ dsa.sign = function _sign(alg, msg, key, params) {
return sign('dsa', alg, msg, pem);
};
dsa.verifyAsync = util.promisify(dsa.verify);
dsa.signAsync = util.promisify(dsa.sign);
/*
* RSA
*/
@ -45,6 +49,9 @@ rsa.sign = function _sign(alg, msg, key) {
return sign('rsa', alg, msg, pem);
};
rsa.verifyAsync = util.promisify(rsa.verify);
rsa.signAsync = util.promisify(rsa.sign);
/*
* ECDSA
*/
@ -57,7 +64,7 @@ ecdsa.verify = function verify(curve, msg, alg, key, sig) {
assert(curve, 'No curve selected.');
ec = elliptic.ec(curve);
hash = crypto.hash(alg, msg);
hash = backend.hash(alg, msg);
return ec.verify(hash, sig, key);
};
@ -68,11 +75,14 @@ ecdsa.sign = function sign(curve, msg, alg, key) {
assert(curve, 'No curve selected.');
ec = elliptic.ec(curve);
hash = crypto.hash(alg, msg);
hash = backend.hash(alg, msg);
return new Buffer(ec.sign(hash, key));
};
ecdsa.verifyAsync = util.promisify(ecdsa.verify);
ecdsa.signAsync = util.promisify(ecdsa.sign);
/*
* Helpers
*/

View File

@ -10,7 +10,7 @@ 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 sha256 = require('./crypto').sha256;
var sha256 = require('./backend').sha256;
var secp256k1 = elliptic.ec('secp256k1');
var curve = secp256k1.curve;
var curves = elliptic.curves;

View File

@ -33,10 +33,11 @@
'use strict';
var util = require('../utils/util');
var crypto = require('./crypto');
var co = require('../utils/co');
var backend = require('./backend');
var native = require('../utils/native').binding;
var U32Array = typeof Uint32Array === 'function' ? Uint32Array : Array;
var scryptAsync, smix;
/**
* Javascript scrypt implementation. Scrypt is
@ -51,35 +52,28 @@ var U32Array = typeof Uint32Array === 'function' ? Uint32Array : Array;
* @returns {Buffer}
*/
function scryptAsync(passwd, salt, N, r, p, len, callback) {
var V, XY;
scryptAsync = co(function* scryptAsync(passwd, salt, N, r, p, len) {
var i, B, V, XY;
if (r * p >= (1 << 30))
return callback(new Error('EFBIG'));
throw new Error('EFBIG');
if ((N & (N - 1)) !== 0 || N === 0)
return callback(new Error('EINVAL'));
throw new Error('EINVAL');
if (N > 0xffffffff)
return callback(new Error('EINVAL'));
throw new Error('EINVAL');
XY = new Buffer(256 * r);
V = new Buffer(128 * r * N);
crypto.pbkdf2Async(passwd, salt, 1, p * 128 * r, 'sha256', function(err, B) {
if (err)
return callback(err);
B = yield backend.pbkdf2Async(passwd, salt, 1, p * 128 * r, 'sha256');
forRangeSerial(0, p, function(i, next) {
smix(B, i * 128 * r, r, N, V, XY, next);
}, function(err) {
if (err)
return callback(err);
for (i = 0; i < p; i++)
yield smix(B, i * 128 * r, r, N, V, XY);
crypto.pbkdf2Async(passwd, B, 1, len, 'sha256', callback);
});
});
}
return yield backend.pbkdf2Async(passwd, B, 1, len, 'sha256');
});
if (native)
scryptAsync = native.scryptAsync;
@ -148,7 +142,7 @@ function R(a, b) {
return (a << b) | (a >>> (32 - b));
}
function blockmix_salsa8(B, Y, Yo, r, callback) {
function blockmix_salsa8(B, Y, Yo, r) {
var X = new Buffer(64);
var i;
@ -165,42 +159,35 @@ function blockmix_salsa8(B, Y, Yo, r, callback) {
for (i = 0; i < r; i++)
blkcpy(B, Y, (i + r) * 64, Yo + (i * 2 + 1) * 64, 64);
util.nextTick(callback);
}
function integerify(B, r) {
return B.readUInt32LE((2 * r - 1) * 64, true);
}
function smix(B, Bo, r, N, V, XY, callback) {
smix = co(function* smix(B, Bo, r, N, V, XY) {
var X = XY;
var Y = XY;
var i;
var j;
blkcpy(X, B, 0, Bo, 128 * r);
forRangeSerial(0, N, function(i, next) {
for (i = 0; i < N; i++) {
blkcpy(V, X, i * (128 * r), 0, 128 * r);
blockmix_salsa8(X, Y, 128 * r, r, next);
}, function(err) {
if (err)
return callback(err);
blockmix_salsa8(X, Y, 128 * r, r);
yield co.wait();
}
forRangeSerial(0, N, function(i, next) {
j = integerify(X, r) & (N - 1);
blkxor(X, V, 0, j * (128 * r), 128 * r);
blockmix_salsa8(X, Y, 128 * r, r, next);
}, function(err) {
if (err)
return callback(err);
for (i = 0; i < N; i++) {
j = integerify(X, r) & (N - 1);
blkxor(X, V, 0, j * (128 * r), 128 * r);
blockmix_salsa8(X, Y, 128 * r, r);
yield co.wait();
}
blkcpy(B, X, Bo, 0, 128 * r);
callback();
});
});
}
blkcpy(B, X, Bo, 0, 128 * r);
});
function blkcpy(dest, src, s1, s2, len) {
src.copy(dest, s1, s2, s2 + len);
@ -211,17 +198,6 @@ function blkxor(dest, src, s1, s2, len) {
dest[s1 + i] ^= src[s2 + i];
}
function forRangeSerial(from, to, iter, callback) {
(function next(err) {
if (err)
return callback(err);
if (from >= to)
return callback();
from++;
iter(from - 1, next, from - 1);
})();
}
/*
* Expose
*/

View File

@ -33,7 +33,7 @@
'use strict';
var crypto = require('./crypto');
var backend = require('./backend');
var native = require('../utils/native').binding;
var U32Array = typeof Uint32Array === 'function' ? Uint32Array : Array;
@ -65,12 +65,12 @@ function scrypt(passwd, salt, N, r, p, len) {
XY = new Buffer(256 * r);
V = new Buffer(128 * r * N);
B = crypto.pbkdf2(passwd, salt, 1, p * 128 * r, 'sha256');
B = backend.pbkdf2(passwd, salt, 1, p * 128 * r, 'sha256');
for (i = 0; i < p; i++)
smix(B, i * 128 * r, r, N, V, XY);
return crypto.pbkdf2(passwd, B, 1, len, 'sha256');
return backend.pbkdf2(passwd, B, 1, len, 'sha256');
}
if (native)

View File

@ -135,14 +135,19 @@ Mnemonic.prototype.destroy = function destroy() {
*/
Mnemonic.prototype.toSeed = function toSeed(passphrase) {
var phrase, passwd;
if (!passphrase)
passphrase = this.passphrase;
this.passphrase = passphrase;
phrase = nfkd(this.getPhrase());
passwd = nfkd('mnemonic' + passphrase);
return crypto.pbkdf2(
nfkd(this.getPhrase()),
nfkd('mnemonic' + passphrase),
new Buffer(phrase, 'utf8'),
new Buffer(passwd, 'utf8'),
2048, 64, 'sha512');
};

View File

@ -349,7 +349,7 @@ CompactBlock.prototype.fromBlock = function fromBlock(block, witness, nonce) {
this.totalTX = block.totalTX;
if (!nonce)
nonce = util.nonce(true);
nonce = util.nonce();
this.keyNonce = nonce;

View File

@ -7,7 +7,6 @@
'use strict';
var BN = require('bn.js');
var constants = require('../protocol/constants');
var util = require('../utils/util');
var assert = require('assert');
@ -122,7 +121,7 @@ function VersionPacket(options) {
this.ts = time.now();
this.recv = new NetworkAddress();
this.from = new NetworkAddress();
this.nonce = new BN(0);
this.nonce = constants.ZERO_U64;
this.agent = constants.USER_AGENT;
this.height = 0;
this.relay = true;
@ -196,7 +195,7 @@ VersionPacket.prototype.toRaw = function toRaw(writer) {
p.write64(this.ts);
this.recv.toRaw(false, p);
this.from.toRaw(false, p);
p.writeU64(this.nonce);
p.writeBytes(this.nonce);
p.writeVarString(this.agent, 'ascii');
p.write32(this.height);
p.writeU8(this.relay ? 1 : 0);
@ -278,7 +277,7 @@ VersionPacket.prototype.fromRaw = function fromRaw(data) {
if (p.left() > 0) {
this.from.fromRaw(p, false);
this.nonce = p.readU64();
this.nonce = p.readBytes(8);
}
if (p.left() > 0)
@ -392,7 +391,7 @@ PingPacket.prototype.toRaw = function toRaw(writer) {
var p = BufferWriter(writer);
if (this.nonce)
p.writeU64(this.nonce);
p.writeBytes(this.nonce);
if (!writer)
p = p.render();
@ -409,7 +408,7 @@ PingPacket.prototype.toRaw = function toRaw(writer) {
PingPacket.prototype.fromRaw = function fromRaw(data) {
var p = BufferReader(data);
if (p.left() >= 8)
this.nonce = p.readU64();
this.nonce = p.readBytes(8);
return this;
};
@ -440,7 +439,7 @@ function PongPacket(nonce) {
Packet.call(this);
this.nonce = nonce || new BN(0);
this.nonce = nonce || constants.ZERO_U64;
}
util.inherits(PongPacket, Packet);
@ -456,7 +455,7 @@ PongPacket.prototype.type = exports.types.PONG;
PongPacket.prototype.toRaw = function toRaw(writer) {
var p = BufferWriter(writer);
p.writeU64(this.nonce);
p.writeBytes(this.nonce);
if (!writer)
p = p.render();
@ -472,7 +471,7 @@ PongPacket.prototype.toRaw = function toRaw(writer) {
PongPacket.prototype.fromRaw = function fromRaw(data) {
var p = BufferReader(data);
this.nonce = p.readU64();
this.nonce = p.readBytes(8);
return this;
};

View File

@ -1439,7 +1439,7 @@ Peer.prototype._handleVersion = co(function* _handleVersion(version) {
throw new Error('Peer sent a duplicate version.');
if (!this.network.selfConnect) {
if (version.nonce.cmp(this.pool.localNonce) === 0) {
if (util.equal(version.nonce, this.pool.localNonce)) {
this.ignore();
throw new Error('We connected to ourself. Oops.');
}
@ -1869,8 +1869,8 @@ Peer.prototype._handlePong = function _handlePong(packet) {
return;
}
if (nonce.cmp(this.challenge) !== 0) {
if (nonce.cmpn(0) === 0) {
if (!util.equal(nonce, this.challenge)) {
if (util.equal(nonce, constants.ZERO_U64)) {
this.logger.debug('Peer sent a zero nonce (%s).', this.hostname);
this.challenge = null;
return;

View File

@ -917,6 +917,9 @@ Pool.prototype._handleBlock = co(function* _handleBlock(block, peer) {
// Fulfill the load request.
requested = this.fulfill(block);
if (util.isBrowser)
yield block.hashAsync();
// Someone is sending us blocks without
// us requesting them.
if (!requested) {
@ -1330,6 +1333,9 @@ Pool.prototype.hasReject = function hasReject(hash) {
Pool.prototype._handleTX = co(function* _handleTX(tx, peer) {
var i, requested, missing;
if (util.isBrowser)
yield tx.hashAsync();
// Fulfill the load request.
requested = this.fulfill(tx);

View File

@ -10,6 +10,7 @@
var assert = require('assert');
var constants = require('../protocol/constants');
var util = require('../utils/util');
var co = require('../utils/co');
var crypto = require('../crypto/crypto');
var btcutils = require('../btc/utils');
var VerifyResult = require('../btc/errors').VerifyResult;
@ -160,6 +161,54 @@ AbstractBlock.prototype.hash = function hash(enc) {
return hash;
};
/**
* Hash the block headers (async).
* @param {String?} enc - Can be `'hex'` or `null`.
* @returns {Hash|Buffer} hash
*/
AbstractBlock.prototype.hashAsync = co(function* hashAsync(enc) {
var hash = this._hash;
var hex;
if (!hash) {
hash = yield crypto.hash256Async(this.abbr());
if (!this.mutable)
this._hash = hash;
}
if (enc === 'hex') {
hex = this._hhash;
if (!hex) {
hex = hash.toString('hex');
if (!this.mutable)
this._hhash = hex;
}
hash = hex;
}
return hash;
});
/**
* Cache hashes.
* @private
*/
AbstractBlock.prototype.cacheHashes = co(function* cacheHashes() {
var i, tx;
yield this.hashAsync();
if (!this.txs)
return;
for (i = 0; i < this.txs.length; i++) {
tx = this.txs[i];
yield tx.hashAsync();
}
});
/**
* Serialize the block headers.
* @returns {Buffer}

View File

@ -78,6 +78,22 @@ MemBlock.fromOptions = function fromOptions(options) {
return new MemBlock().fromOptions(options);
};
/**
* Serialize the block headers.
* @returns {Buffer}
*/
MemBlock.prototype.abbr = function abbr(writer) {
var data = this.raw.slice(0, 80);
if (writer) {
writer.writeBytes(data);
return writer;
}
return data;
};
/**
* Get the full block size.
* @returns {Number}

View File

@ -9,6 +9,7 @@
var assert = require('assert');
var util = require('../utils/util');
var co = require('../utils/co');
var crypto = require('../crypto/crypto');
var btcutils = require('../btc/utils');
var Amount = require('../btc/amount');
@ -241,6 +242,35 @@ TX.prototype.hash = function _hash(enc) {
return hash;
};
/**
* Hash the transaction with the non-witness serialization (async).
* @param {String?} enc - Can be `'hex'` or `null`.
* @returns {Hash|Buffer} hash
*/
TX.prototype.hashAsync = co(function* _hashAsync(enc) {
var hash = this._hash;
var hex;
if (!hash) {
hash = yield crypto.hash256Async(this.toNormal());
if (!this.mutable)
this._hash = hash;
}
if (enc === 'hex') {
hex = this._hhash;
if (!hex) {
hex = hash.toString('hex');
if (!this.mutable)
this._hhash = hex;
}
hash = hex;
}
return hash;
});
/**
* Hash the transaction with the witness
* serialization, return the wtxid (normal

View File

@ -698,6 +698,22 @@ exports.ZERO_SIG64 = new Buffer(''
'hex'
);
/**
* 4 zero bytes.
* @const {Buffer}
* @default
*/
exports.ZERO_U32 = new Buffer('00000000', 'hex');
/**
* 8 zero bytes.
* @const {Buffer}
* @default
*/
exports.ZERO_U64 = new Buffer('0000000000000000', 'hex');
/**
* BCoin version.
* @const {String}

View File

@ -595,7 +595,7 @@ BufferReader.prototype.readNullString = function readNullString(enc) {
BufferReader.prototype.createChecksum = function createChecksum() {
var start = this.stack[this.stack.length - 1] || 0;
var data = this.data.slice(start, this.offset);
return crypto.checksum(data).readUInt32LE(0, true);
return crypto.hash256(data).readUInt32LE(0, true);
};
/**

View File

@ -13,7 +13,6 @@ var assert = require('assert');
var nodeUtil = require('util');
var fs = require('fs');
var os = require('os');
var BN = require('bn.js');
var util = exports;
var Number, Math, Date;
@ -542,38 +541,14 @@ util.time = function time(date) {
/**
* Create a 64 bit nonce.
* @returns {BN}
* @returns {Buffer}
*/
util.nonce = function _nonce(buffer) {
util.nonce = function _nonce() {
var nonce = new Buffer(8);
nonce.writeUInt32LE((Math.random() * 0x100000000) >>> 0, 0, true);
nonce.writeUInt32LE((Math.random() * 0x100000000) >>> 0, 4, true);
if (buffer)
return nonce;
return new BN(nonce);
};
/**
* Test whether a buffer is all zeroes.
* @param {Buffer} data
* @returns {Boolean}
*/
util.isZero = function isZero(data) {
var i;
assert(Buffer.isBuffer(data));
for (i = 0; i < data.length; i++) {
if (data[i] !== 0)
return false;
}
return true;
return nonce;
};
/**
@ -1015,3 +990,21 @@ util._paths = {};
util.fastProp = function fastProp(obj) {
({ __proto__: obj });
};
/**
* Promisify a function.
* @param {Function} func
* @returns {Function}
*/
util.promisify = function promisify(func) {
return function() {
var result;
try {
result = func.apply(this, arguments);
} catch (e) {
return Promise.reject(e);
}
return Promise.resolve(result);
};
};

View File

@ -105,7 +105,7 @@ BufferWriter.prototype.render = function render(keep) {
case BYTES: off += item[1].copy(data, off); break;
case STR: off += data.write(item[1], off, item[2]); break;
case CHECKSUM:
off += crypto.checksum(data.slice(0, off)).copy(data, off);
off += crypto.hash256(data.slice(0, off)).copy(data, off, 0, 4);
break;
case FILL:
data.fill(item[1], off, off + item[2]);

View File

@ -15,6 +15,12 @@ var BufferReader = require('../utils/reader');
var BufferWriter = require('../utils/writer');
var HD = require('../hd/hd');
/*
* Constants
*/
var BCOIN_SALT = new Buffer('bcoin', 'ascii');
/**
* Master BIP32 key which can exist
* in a timed out encrypted state.
@ -234,11 +240,14 @@ MasterKey.prototype.stop = function stop() {
*/
MasterKey.prototype.derive = function derive(passwd) {
if (typeof passwd === 'string')
passwd = new Buffer(passwd, 'utf8');
switch (this.alg) {
case MasterKey.alg.PBKDF2:
return crypto.pbkdf2Async(passwd, 'bcoin', this.N, 32, 'sha256');
return crypto.pbkdf2Async(passwd, BCOIN_SALT, this.N, 32, 'sha256');
case MasterKey.alg.SCRYPT:
return crypto.scryptAsync(passwd, 'bcoin', this.N, this.r, this.p, 32);
return crypto.scryptAsync(passwd, BCOIN_SALT, this.N, this.r, this.p, 32);
default:
return Promise.reject(new Error('Unknown algorithm: ' + this.alg));
}

View File

@ -41,7 +41,7 @@
"elliptic": "6.3.2"
},
"optionalDependencies": {
"bcoin-native": "0.0.9",
"bcoin-native": "0.0.10",
"leveldown": "1.5.0",
"secp256k1": "3.2.0",
"socket.io": "1.4.8",

View File

@ -58,6 +58,38 @@ describe('AES', function() {
]);
}
function bencrypt(data, passphrase) {
var key, cipher;
assert(nativeCrypto, 'No crypto module available.');
assert(passphrase, 'No passphrase.');
if (typeof data === 'string')
data = new Buffer(data, 'utf8');
if (typeof passphrase === 'string')
passphrase = new Buffer(passphrase, 'utf8');
key = pbkdf2key(passphrase, 2048, 32, 16);
return crypto.encipher(data, key.key, key.iv);
}
function bdecrypt(data, passphrase) {
var key, decipher;
assert(nativeCrypto, 'No crypto module available.');
assert(passphrase, 'No passphrase.');
if (typeof data === 'string')
data = new Buffer(data, 'hex');
if (typeof passphrase === 'string')
passphrase = new Buffer(passphrase, 'utf8');
key = pbkdf2key(passphrase, 2048, 32, 16);
return crypto.decipher(data, key.key, key.iv);
}
function encrypt(data, passphrase) {
var key, cipher;
@ -101,9 +133,14 @@ describe('AES', function() {
var enchash2 = nencrypt(hash2, 'foo');
var dechash2 = ndecrypt(enchash2, 'foo');
var hash3 = crypto.sha256(new Buffer([]));
var enchash3 = bencrypt(hash3, 'foo');
var dechash3 = bdecrypt(enchash3, 'foo');
assert.deepEqual(hash, hash2);
assert.deepEqual(enchash, enchash2);
assert.deepEqual(dechash, dechash2);
assert.deepEqual(dechash, dechash3);
});
it('should encrypt and decrypt a hash with uneven blocks', function() {

View File

@ -5,7 +5,9 @@ var scrypt = require('../lib/crypto/crypto').scrypt;
describe('Scrypt', function() {
it('should perform scrypt with N=16', function() {
var result = scrypt('', '', 16, 1, 1, 64);
var pass = new Buffer('');
var salt = new Buffer('');
var result = scrypt(pass, salt, 16, 1, 1, 64);
assert.equal(result.toString('hex'), ''
+ '77d6576238657b203b19ca42c18a0497f16b4844e3074ae8dfdffa3f'
+ 'ede21442fcd0069ded0948f8326a753a0fc81f17e8d3e0fb2e0d3628'
@ -13,7 +15,9 @@ describe('Scrypt', function() {
});
it('should perform scrypt with N=1024', function() {
var result = scrypt('password', 'NaCl', 1024, 8, 16, 64);
var pass = new Buffer('password');
var salt = new Buffer('NaCl');
var result = scrypt(pass, salt, 1024, 8, 16, 64);
assert.equal(result.toString('hex'), ''
+ 'fdbabe1c9d3472007856e7190d01e9fe7c6ad7cbc8237830e773'
+ '76634b3731622eaf30d92e22a3886ff109279d9830dac727afb9'
@ -21,7 +25,9 @@ describe('Scrypt', function() {
});
it('should perform scrypt with N=16384', function() {
var result = scrypt('pleaseletmein', 'SodiumChloride', 16384, 8, 1, 64);
var pass = new Buffer('pleaseletmein');
var salt = new Buffer('SodiumChloride');
var result = scrypt(pass, salt, 16384, 8, 1, 64);
assert.equal(result.toString('hex'), ''
+ '7023bdcb3afd7348461c06cd81fd38ebfda8fbba904f8e3ea9b54'
+ '3f6545da1f2d5432955613f0fcf62d49705242a9af9e61e85dc0d'
@ -30,7 +36,9 @@ describe('Scrypt', function() {
// Only enable if you want to wait a while.
// it('should perform scrypt with N=1048576', function() {
// var result = scrypt('pleaseletmein', 'SodiumChloride', 1048576, 8, 1, 64);
// var pass = new Buffer('pleaseletmein');
// var salt = new Buffer('SodiumChloride');
// var result = scrypt(pass, salt, 1048576, 8, 1, 64);
// assert.equal(result.toString('hex'), ''
// + '2101cb9b6a511aaeaddbbe09cf70f881ec568d574a2ffd4dabe5'
// + 'ee9820adaa478e56fd8f4ba5d09ffa1c6d927c40f4c337304049'