more hd improvements.

This commit is contained in:
Christopher Jeffrey 2016-05-13 19:28:19 -07:00
parent b164984307
commit 6690991acb
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
6 changed files with 90 additions and 27 deletions

View File

@ -174,6 +174,24 @@ ec.publicKeyVerify = function publicKeyVerify(key) {
return ec.elliptic.keyPair({ pub: key }).validate(); return ec.elliptic.keyPair({ pub: key }).validate();
}; };
/**
* Validate a private key.
* @param {Buffer} key
* @returns {Boolean} True if buffer is a valid private key.
*/
ec.privateKeyVerify = function privateKeyVerify(key) {
if (secp256k1)
return secp256k1.privateKeyVerify(key);
key = new bn(key);
if (key.cmpn(0) === 0 || key.cmp(ec.elliptic.curve.n) >= 0)
return false;
return true;
};
/** /**
* Sign a message. * Sign a message.
* @param {Buffer} msg * @param {Buffer} msg

View File

@ -243,7 +243,7 @@ Mnemonic.isMnemonic = function isMnemonic(obj) {
function HD(options, network) { function HD(options, network) {
if (!options) if (!options)
return HD.fromSeed(null, network); return HD.fromMnemonic(null, network);
return HD.fromAny(options, network); return HD.fromAny(options, network);
} }
@ -284,6 +284,17 @@ HD.fromSeed = function fromSeed(options, network) {
return HDPrivateKey.fromSeed(options, network); return HDPrivateKey.fromSeed(options, network);
}; };
/**
* Instantiate an hd private key from a mnemonic.
* @param {Mnemonic|Object} mnemonic
* @param {String?} network
* @returns {HDPrivateKey}
*/
HD.fromMnemonic = function fromMnemonic(options, network) {
return HDPrivateKey.fromMnemonic(options, network);
};
/** /**
* Instantiate an HD key from a jsonified key object. * Instantiate an HD key from a jsonified key object.
* @param {Object} json - The jsonified transaction object. * @param {Object} json - The jsonified transaction object.
@ -323,7 +334,7 @@ HD.fromAny = function fromAny(options, network) {
if (HDPublicKey.isExtended(xkey)) if (HDPublicKey.isExtended(xkey))
return HDPublicKey.fromBase58(xkey); return HDPublicKey.fromBase58(xkey);
return HDPrivateKey.fromSeed(options, network); return HDPrivateKey.fromMnemonic(options, network);
}; };
/** /**
@ -499,6 +510,9 @@ HDPrivateKey.prototype.derive = function derive(index, hardened) {
if (index < constants.hd.HARDENED && hardened) if (index < constants.hd.HARDENED && hardened)
index += constants.hd.HARDENED; index += constants.hd.HARDENED;
if (!(index >= 0 && index < constants.hd.MAX_INDEX))
throw new Error('Index out of range.');
p = new BufferWriter(); p = new BufferWriter();
if (hardened) { if (hardened) {
@ -686,35 +700,33 @@ HDPrivateKey.prototype.derivePath = function derivePath(path) {
*/ */
HDPrivateKey.parseSeed = function parseSeed(seed, network) { HDPrivateKey.parseSeed = function parseSeed(seed, network) {
var data, hash; var hash, chainCode, privateKey;
if (!seed) if (!seed)
seed = {}; seed = {};
if (Buffer.isBuffer(seed)) { assert(Buffer.isBuffer(seed));
data = seed;
seed = null;
} else if (seed instanceof Mnemonic) {
data = seed.toSeed();
} else {
seed = new Mnemonic(seed);
data = seed.toSeed();
}
if (data.length < constants.hd.MIN_ENTROPY if (seed.length < constants.hd.MIN_ENTROPY
|| data.length > constants.hd.MAX_ENTROPY) { || seed.length > constants.hd.MAX_ENTROPY) {
throw new Error('Entropy not in range.'); throw new Error('Entropy not in range.');
} }
hash = utils.hmac('sha512', data, 'Bitcoin seed'); hash = utils.hmac('sha512', seed, 'Bitcoin seed');
privateKey = hash.slice(0, 32);
chainCode = hash.slice(32, 64);
if (!ec.privateKeyVerify(privateKey))
throw new Error('Master private key is invalid.');
return { return {
network: network, network: network,
depth: 0, depth: 0,
parentFingerPrint: new Buffer([0, 0, 0, 0]), parentFingerPrint: new Buffer([0, 0, 0, 0]),
childIndex: 0, childIndex: 0,
chainCode: hash.slice(32, 64), chainCode: chainCode,
privateKey: hash.slice(0, 32), privateKey: privateKey,
mnemonic: seed mnemonic: seed
}; };
}; };
@ -731,6 +743,40 @@ HDPrivateKey.fromSeed = function fromSeed(seed, network) {
return new HDPrivateKey(HDPrivateKey.parseSeed(seed, network)); return new HDPrivateKey(HDPrivateKey.parseSeed(seed, network));
}; };
/**
* Instantiate an hd private key from a mnemonic.
* @param {Mnemonic|Object} mnemonic
* @param {String?} network
* @returns {HDPrivateKey}
*/
HDPrivateKey.fromMnemonic = function fromMnemonic(mnemonic, network) {
var key;
if (!(mnemonic instanceof Mnemonic))
mnemonic = new Mnemonic(mnemonic);
if (mnemonic.seed || mnemonic.phrase || mnemonic.entropy)
return HDPrivateKey.fromSeed(mnemonic.toSeed(), network);
for (;;) {
try {
key = HDPrivateKey.fromSeed(mnemonic.toSeed(), network);
} catch (e) {
if (e.message === 'Master private key is invalid.') {
mnemonic.seed = null;
mnemonic.phrase = null;
mnemonic.entropy = null;
continue;
}
throw e;
}
break;
}
return key;
};
/** /**
* Generate an hd private key from a key and/or entropy bytes. * Generate an hd private key from a key and/or entropy bytes.
* @param {Object?} options * @param {Object?} options
@ -866,6 +912,7 @@ HDPrivateKey.prototype.toJSON = function toJSON(passphrase) {
var json = { var json = {
v: 1, v: 1,
name: 'hdkey', name: 'hdkey',
network: this.network,
encrypted: false encrypted: false
}; };
@ -953,7 +1000,7 @@ HDPrivateKey.fromJSON = function fromJSON(json, passphrase) {
} }
if (json.mnemonic) if (json.mnemonic)
return HDPrivateKey.fromSeed(json.mnemonic); return HDPrivateKey.fromMnemonic(json.mnemonic, json.network);
if (json.xpubkey) if (json.xpubkey)
return HDPublicKey.fromBase58(json.xprivkey); return HDPublicKey.fromBase58(json.xprivkey);
@ -1048,10 +1095,10 @@ HDPublicKey.prototype.derive = function derive(index, hardened) {
return cached; return cached;
if (index >= constants.hd.HARDENED || hardened) if (index >= constants.hd.HARDENED || hardened)
throw new Error('Invalid index.'); throw new Error('Index out of range.');
if (index < 0) if (index < 0)
throw new Error('Invalid path.'); throw new Error('Index out of range.');
p = new BufferWriter(); p = new BufferWriter();
p.writeBytes(this.publicKey); p.writeBytes(this.publicKey);

View File

@ -70,7 +70,7 @@ function Wallet(options) {
} }
if (!options.master) if (!options.master)
options.master = bcoin.hd.fromSeed(null, this.network); options.master = bcoin.hd.fromMnemonic(null, this.network);
this.provider = options.provider || null; this.provider = options.provider || null;
this.master = options.master || null; this.master = options.master || null;

View File

@ -142,7 +142,7 @@ describe('HD', function() {
}); });
it('should deserialize and reserialize', function() { it('should deserialize and reserialize', function() {
var key = bcoin.hd.fromSeed(); var key = bcoin.hd.fromMnemonic();
assert.equal(bcoin.hd.fromJSON(key.toJSON()).xprivkey, key.xprivkey); assert.equal(bcoin.hd.fromJSON(key.toJSON()).xprivkey, key.xprivkey);
}); });

View File

@ -20,7 +20,7 @@ describe('Mnemonic', function() {
mnemonic.toSeed(); mnemonic.toSeed();
assert.equal(mnemonic.phrase, phrase); assert.equal(mnemonic.phrase, phrase);
assert.equal(mnemonic.toSeed().toString('hex'), seed.toString('hex')); assert.equal(mnemonic.toSeed().toString('hex'), seed.toString('hex'));
var key = bcoin.hd.fromSeed(mnemonic); var key = bcoin.hd.fromMnemonic(mnemonic);
assert.equal(key.xprivkey, xpriv); assert.equal(key.xprivkey, xpriv);
}); });
}); });
@ -37,11 +37,9 @@ describe('Mnemonic', function() {
passphrase: passphrase passphrase: passphrase
}); });
mnemonic.toSeed(); mnemonic.toSeed();
// utils.print(new Buffer(mnemonic.phrase, 'utf8').toString('hex'));
// utils.print(new Buffer(phrase, 'utf8').toString('hex'));
assert.equal(mnemonic.phrase, phrase); assert.equal(mnemonic.phrase, phrase);
assert.equal(mnemonic.toSeed().toString('hex'), seed.toString('hex')); assert.equal(mnemonic.toSeed().toString('hex'), seed.toString('hex'));
var key = bcoin.hd.fromSeed(mnemonic); var key = bcoin.hd.fromMnemonic(mnemonic);
assert.equal(key.xprivkey, xpriv); assert.equal(key.xprivkey, xpriv);
}); });
}); });

View File

@ -113,7 +113,7 @@ describe('Wallet', function() {
m: 1, m: 1,
n: 2 n: 2
}); });
var k2 = bcoin.hd.fromSeed().deriveAccount44(0).hdPublicKey; var k2 = bcoin.hd.fromMnemonic().deriveAccount44(0).hdPublicKey;
w.addKey(k2); w.addKey(k2);
// Input transcation // Input transcation