hd: do not store mnemonic on hd private key.

This commit is contained in:
Christopher Jeffrey 2017-03-11 23:04:31 -08:00
parent d7e2942117
commit aa7e550f91
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
8 changed files with 146 additions and 155 deletions

View File

@ -5,8 +5,9 @@ var bench = require('./bench');
var HD = require('../lib/hd');
var Mnemonic = require('../lib/hd/mnemonic');
var key = HD.fromMnemonic();
var phrase = key.mnemonic.getPhrase();
var mnemonic = new Mnemonic();
var key = HD.fromMnemonic(mnemonic);
var phrase = mnemonic.getPhrase();
var i, end;
assert.equal(Mnemonic.fromPhrase(phrase).getPhrase(), phrase);

View File

@ -27,14 +27,12 @@ var HDPublicKey = require('./public');
* @constructor
* @param {Object|Base58String} options
* @param {Base58String?} options.xkey - Serialized base58 key.
* @param {Mnemonic?} options.mnemonic
* @param {Number?} options.depth
* @param {Buffer?} options.parentFingerPrint
* @param {Number?} options.childIndex
* @param {Buffer?} options.chainCode
* @param {Buffer?} options.privateKey
* @property {Network} network
* @property {Mnemonic?} mnemonic
* @property {Number} depth
* @property {Buffer} parentFingerPrint
* @property {Number} childIndex
@ -56,8 +54,6 @@ function HDPrivateKey(options) {
this.publicKey = encoding.ZERO_KEY;
this.fingerPrint = null;
this.mnemonic = null;
this._xprivkey = null;
this._hdPublicKey = null;
@ -91,16 +87,6 @@ HDPrivateKey.prototype.fromOptions = function fromOptions(options) {
this.privateKey = options.privateKey;
this.publicKey = ec.publicKeyCreate(options.privateKey, true);
if (options.mnemonic) {
assert(options.mnemonic instanceof Mnemonic);
this.mnemonic = options.mnemonic;
}
if (options.xprivkey) {
assert(typeof options.xprivkey === 'string');
this._xprivkey = options.xprivkey;
}
return this;
};
@ -194,11 +180,6 @@ HDPrivateKey.prototype.destroy = function destroy(pub) {
}
this._xprivkey = null;
if (this.mnemonic) {
this.mnemonic.destroy();
this.mnemonic = null;
}
};
/**
@ -543,21 +524,19 @@ HDPrivateKey.fromSeed = function fromSeed(seed, network) {
/**
* Inject properties from a mnemonic.
* @private
* @param {Mnemonic|Object} mnemonic
* @param {Mnemonic} mnemonic
* @param {(Network|NetworkType)?} network
*/
HDPrivateKey.prototype.fromMnemonic = function fromMnemonic(mnemonic, network) {
if (!(mnemonic instanceof Mnemonic))
mnemonic = new Mnemonic(mnemonic);
assert(mnemonic instanceof Mnemonic);
this.fromSeed(mnemonic.toSeed(), network);
this.mnemonic = mnemonic;
return this;
};
/**
* Instantiate an hd private key from a mnemonic.
* @param {Mnemonic|Object} mnemonic
* @param {Mnemonic} mnemonic
* @param {(Network|NetworkType)?} network
* @returns {HDPrivateKey}
*/
@ -566,6 +545,30 @@ HDPrivateKey.fromMnemonic = function fromMnemonic(mnemonic, network) {
return new HDPrivateKey().fromMnemonic(mnemonic, network);
};
/**
* Inject properties from a mnemonic.
* @private
* @param {String} mnemonic
* @param {(Network|NetworkType)?} network
*/
HDPrivateKey.prototype.fromPhrase = function fromPhrase(phrase, network) {
var mnemonic = Mnemonic.fromPhrase(phrase);
this.fromMnemonic(mnemonic, network);
return this;
};
/**
* Instantiate an hd private key from a phrase.
* @param {String} phrase
* @param {(Network|NetworkType)?} network
* @returns {HDPrivateKey}
*/
HDPrivateKey.fromPhrase = function fromPhrase(phrase, network) {
return new HDPrivateKey().fromPhrase(phrase, network);
};
/**
* Inject properties from privateKey and entropy.
* @private
@ -722,97 +725,6 @@ HDPrivateKey.prototype.toRaw = function toRaw(network) {
return this.toWriter(new StaticWriter(82), network).render();
};
/**
* Calculate extended serialization size.
* @returns {Number}
*/
HDPrivateKey.prototype.getExtendedSize = function getSize() {
var size = this.getSize();
size += 1;
if (this.mnemonic)
size += this.mnemonic.getSize();
return size;
};
/**
* Write the key in "extended"
* format to a buffer writer.
* @param {BufferWriter} bw
* @param {(Network|NetworkType)?} network
*/
HDPrivateKey.prototype.toExtendedWriter = function toExtendedWriter(bw, network) {
this.toWriter(bw, network);
if (this.mnemonic) {
bw.writeU8(1);
this.mnemonic.toWriter(bw);
} else {
bw.writeU8(0);
}
return bw;
};
/**
* Serialize the key in "extended"
* format (includes the mnemonic).
* @param {(Network|NetworkType)?} network
* @returns {Buffer}
*/
HDPrivateKey.prototype.toExtended = function toExtended(network) {
var size = this.getExtendedSize();
return this.toExtendedWriter(new StaticWriter(size), network).render();
};
/**
* Inject properties from buffer reader.
* @private
* @param {BufferReader} br
*/
HDPrivateKey.prototype.fromExtendedReader = function fromExtendedReader(br) {
this.fromReader(br);
if (br.readU8() === 1)
this.mnemonic = Mnemonic.fromReader(br);
return this;
};
/**
* Inject properties from extended serialized data.
* @private
* @param {Buffer} data
*/
HDPrivateKey.prototype.fromExtended = function fromExtended(data) {
return this.fromExtendedReader(new BufferReader(data));
};
/**
* Instantiate key from "extended" buffer reader.
* @param {BufferReader} br
* @returns {HDPrivateKey}
*/
HDPrivateKey.fromExtendedReader = function fromExtendedReader(br) {
return new HDPrivateKey().fromExtendedReader(br);
};
/**
* Instantiate key from "extended" serialized data.
* @param {Buffer} data
* @returns {HDPrivateKey}
*/
HDPrivateKey.fromExtended = function fromExtended(data) {
return new HDPrivateKey().fromExtended(data);
};
/**
* Instantiate an HD private key from a base58 string.
* @param {Base58String} xkey
@ -851,8 +763,7 @@ HDPrivateKey.fromRaw = function fromRaw(raw) {
HDPrivateKey.prototype.toJSON = function toJSON() {
return {
xprivkey: this.xprivkey(),
mnemonic: this.mnemonic ? this.mnemonic.toJSON() : null
xprivkey: this.xprivkey()
};
};
@ -868,9 +779,6 @@ HDPrivateKey.prototype.fromJSON = function fromJSON(json, network) {
this.fromBase58(json.xprivkey, network);
if (json.mnemonic)
this.mnemonic = Mnemonic.fromJSON(json.mnemonic);
return this;
};
@ -894,7 +802,7 @@ HDPrivateKey.fromJSON = function fromJSON(json, network) {
HDPrivateKey.isHDPrivateKey = function isHDPrivateKey(obj) {
return obj
&& typeof obj.derive === 'function'
&& typeof obj.toExtended === 'function'
&& typeof obj.fromMnemonic === 'function'
&& obj.chainCode !== undefined;
};

View File

@ -79,11 +79,6 @@ HDPublicKey.prototype.fromOptions = function fromOptions(options) {
this.chainCode = options.chainCode;
this.publicKey = options.publicKey;
if (options.xpubkey) {
assert(typeof options.xpubkey === 'string');
this._xpubkey = options.xpubkey;
}
return this;
};

View File

@ -15,6 +15,7 @@ var BufferReader = require('../utils/reader');
var StaticWriter = require('../utils/staticwriter');
var encoding = require('../utils/encoding');
var HD = require('../hd/hd');
var Mnemonic = HD.Mnemonic;
/**
* Master BIP32 key which can exist
@ -32,6 +33,7 @@ function MasterKey(options) {
this.iv = null;
this.ciphertext = null;
this.key = null;
this.mnemonic = null;
this.alg = MasterKey.alg.PBKDF2;
this.N = 50000;
@ -103,10 +105,15 @@ MasterKey.prototype.fromOptions = function fromOptions(options) {
}
if (options.key) {
assert(HD.isHD(options.key));
assert(HD.isPrivate(options.key));
this.key = options.key;
}
if (options.mnemonic) {
assert(options.mnemonic instanceof Mnemonic);
this.mnemonic = options.mnemonic;
}
if (options.alg != null) {
if (typeof options.alg === 'string') {
this.alg = MasterKey.alg[options.alg.toUpperCase()];
@ -195,7 +202,7 @@ MasterKey.prototype._unlock = co(function* _unlock(passphrase, timeout) {
key = yield this.derive(passphrase);
data = crypto.decipher(this.ciphertext, key, this.iv);
this.key = HD.fromExtended(data);
this.fromKeyRaw(data);
this.start(timeout);
@ -382,7 +389,7 @@ MasterKey.prototype._decrypt = co(function* decrypt(passphrase, clean) {
key = yield this.derive(passphrase);
data = crypto.decipher(this.ciphertext, key, this.iv);
this.key = HD.fromExtended(data);
this.fromKeyRaw(data);
this.encrypted = false;
this.iv = null;
this.ciphertext = null;
@ -426,7 +433,7 @@ MasterKey.prototype._encrypt = co(function* encrypt(passphrase, clean) {
if (!passphrase)
throw new Error('No passphrase provided.');
data = this.key.toExtended();
data = this.toKeyRaw();
iv = crypto.randomBytes(16);
this.stop();
@ -435,6 +442,7 @@ MasterKey.prototype._encrypt = co(function* encrypt(passphrase, clean) {
data = crypto.encipher(data, key, iv);
this.key = null;
this.mnemonic = null;
this.encrypted = true;
this.iv = iv;
this.ciphertext = data;
@ -447,6 +455,59 @@ MasterKey.prototype._encrypt = co(function* encrypt(passphrase, clean) {
return key;
});
/**
* Calculate key serialization size.
* @returns {Number}
*/
MasterKey.prototype.getKeySize = function getKeySize() {
var size = 0;
size += this.key.getSize();
size += 1;
if (this.mnemonic)
size += this.mnemonic.getSize();
return size;
};
/**
* Serialize key and menmonic to a single buffer.
* @returns {Buffer}
*/
MasterKey.prototype.toKeyRaw = function toKeyRaw() {
var bw = new StaticWriter(this.getKeySize());
this.key.toWriter(bw);
if (this.mnemonic) {
bw.writeU8(1);
this.mnemonic.toWriter(bw);
} else {
bw.writeU8(0);
}
return bw.render();
};
/**
* Inject properties from serialized key.
* @param {Buffer} data
*/
MasterKey.prototype.fromKeyRaw = function fromKeyRaw(data) {
var br = new BufferReader(data);
this.key = HD.fromReader(br);
if (br.readU8() === 1)
this.mnemonic = Mnemonic.fromReader(br);
return this;
};
/**
* Calculate serialization size.
* @returns {Number}
@ -464,7 +525,7 @@ MasterKey.prototype.getSize = function getSize() {
}
size += 1;
size += encoding.sizeVarlen(this.key.getExtendedSize());
size += encoding.sizeVarlen(this.getKeySize());
return size;
};
@ -493,7 +554,19 @@ MasterKey.prototype.toRaw = function toRaw() {
}
bw.writeU8(0);
bw.writeVarBytes(this.key.toExtended());
// NOTE: useless varint
size = this.getKeySize();
bw.writeVarint(size);
bw.writeBytes(this.key.toRaw());
if (this.mnemonic) {
bw.writeU8(1);
this.mnemonic.toWriter(bw);
} else {
bw.writeU8(0);
}
return bw.render();
};
@ -524,7 +597,13 @@ MasterKey.prototype.fromRaw = function fromRaw(raw) {
return this;
}
this.key = HD.fromExtended(br.readVarBytes());
// NOTE: useless varint
br.skipVarint();
this.key = HD.fromRaw(br.readBytes(82));
if (br.readU8() === 1)
this.mnemonic = Mnemonic.fromReader(br);
return this;
};
@ -542,24 +621,27 @@ MasterKey.fromRaw = function fromRaw(raw) {
* Inject properties from an HDPrivateKey.
* @private
* @param {HDPrivateKey} key
* @param {Mnemonic?} mnemonic
*/
MasterKey.prototype.fromKey = function fromKey(key) {
MasterKey.prototype.fromKey = function fromKey(key, mnemonic) {
this.encrypted = false;
this.iv = null;
this.ciphertext = null;
this.key = key;
this.mnemonic = mnemonic || null;
return this;
};
/**
* Instantiate master key from an HDPrivateKey.
* @param {HDPrivateKey} key
* @param {Mnemonic?} mnemonic
* @returns {MasterKey}
*/
MasterKey.fromKey = function fromKey(key) {
return new MasterKey().fromKey(key);
MasterKey.fromKey = function fromKey(key, mnemonic) {
return new MasterKey().fromKey(key, mnemonic);
};
/**
@ -585,7 +667,8 @@ MasterKey.prototype.toJSON = function toJSON(unsafe) {
return {
encrypted: false,
key: unsafe ? this.key.toJSON() : undefined
key: unsafe ? this.key.toJSON() : undefined,
mnemonic: unsafe && this.mnemonic ? this.mnemonic.toJSON() : undefined
};
};
@ -596,8 +679,13 @@ MasterKey.prototype.toJSON = function toJSON(unsafe) {
MasterKey.prototype.inspect = function inspect() {
var json = this.toJSON(true);
if (this.key)
json.key = this.key.toJSON();
if (this.mnemonic)
json.mnemonic = this.mnemonic.toJSON();
return json;
};

View File

@ -31,6 +31,7 @@ var MasterKey = require('./masterkey');
var LRU = require('../utils/lru');
var policy = require('../protocol/policy');
var consensus = require('../protocol/consensus');
var Mnemonic = HD.Mnemonic;
/**
* BIP44 Wallet
@ -102,26 +103,24 @@ util.inherits(Wallet, EventEmitter);
Wallet.prototype.fromOptions = function fromOptions(options) {
var key = options.master;
var id, token;
var id, token, mnemonic;
if (MasterKey.isMasterKey(key)) {
this.master.fromOptions(key);
} else {
if (!key)
key = HD.fromMnemonic(null, this.network);
if (HD.isBase58(key))
if (key) {
if (typeof key === 'string')
key = HD.fromBase58(key, this.network);
assert(HD.isPrivate(key),
'Must create wallet with hd private key.');
assert(key.verifyNetwork(this.network),
'Network mismatch for master key.');
this.master.fromKey(key);
} else {
mnemonic = new Mnemonic(options.mnemonic);
key = HD.fromMnemonic(mnemonic, this.network);
}
assert(key.verifyNetwork(this.network),
'Network mismatch for master key.');
this.master.fromKey(key, mnemonic);
if (options.wid != null) {
assert(util.isNumber(options.wid));
this.wid = options.wid;

View File

@ -87,7 +87,7 @@ describe('HD', function() {
});
it('should deserialize and reserialize', function() {
var key = HD.fromMnemonic();
var key = HD.generate();
assert.equal(HD.fromJSON(key.toJSON()).toBase58(), key.toBase58());
});

View File

@ -87,7 +87,7 @@ MemWallet.prototype.init = function init() {
var i;
if (!this.master)
this.master = HD.PrivateKey.fromMnemonic(null, this.network);
this.master = HD.PrivateKey.generate();
if (!this.key)
this.key = this.master.deriveAccount44(this.account);

View File

@ -154,7 +154,7 @@ describe('Wallet', function() {
n: 2
});
k = HD.fromMnemonic().deriveAccount44(0).toPublic();
k = HD.generate().deriveAccount44(0).toPublic();
yield w.addSharedKey(k);