From e69ba7ce764fa1db566cada06c7b502c802344cc Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Tue, 29 Jul 2014 01:30:51 +1000 Subject: [PATCH 1/4] HDNode: assert chain code length --- src/hdnode.js | 1 + test/hdnode.js | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/hdnode.js b/src/hdnode.js index 0b2399c..8c8a41d 100644 --- a/src/hdnode.js +++ b/src/hdnode.js @@ -31,6 +31,7 @@ function HDNode(K, chainCode, network) { network = network || networks.bitcoin assert(Buffer.isBuffer(chainCode), 'Expected Buffer, got ' + chainCode) + assert.equal(chainCode.length, 32, 'Expected chainCode length of 32, got ' + chainCode.length) assert(network.bip32, 'Unknown BIP32 constants for network') this.chainCode = chainCode diff --git a/test/hdnode.js b/test/hdnode.js index bc951a2..265b5d6 100644 --- a/test/hdnode.js +++ b/test/hdnode.js @@ -49,7 +49,13 @@ describe('HDNode', function() { assert.equal(hd.network, networks.testnet) }) - it('throws an exception when an unknown network is given', function() { + it('throws when an invalid length chain code is given', function() { + assert.throws(function() { + new HDNode(d, chainCode.slice(0, 20), networks.testnet) + }, /Expected chainCode length of 32, got 20/) + }) + + it('throws when an unknown network is given', function() { assert.throws(function() { new HDNode(d, chainCode, {}) }, /Unknown BIP32 constants for network/) From 9d92b6a1a8adbe247444a198bce54920c92562cb Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Tue, 29 Jul 2014 01:34:46 +1000 Subject: [PATCH 2/4] HDNode: add neuter functionality --- src/hdnode.js | 9 +++++++++ test/hdnode.js | 15 +++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/hdnode.js b/src/hdnode.js index 8c8a41d..560866e 100644 --- a/src/hdnode.js +++ b/src/hdnode.js @@ -143,6 +143,15 @@ HDNode.prototype.getAddress = function() { return this.pubKey.getAddress(this.network) } +HDNode.prototype.neutered = function() { + var neutered = new HDNode(this.pubKey.Q, this.chainCode, this.network) + neutered.depth = this.depth + neutered.index = this.index + neutered.parentFingerprint = this.parentFingerprint + + return neutered +} + HDNode.prototype.toBase58 = function(isPrivate) { return base58check.encode(this.toBuffer(isPrivate)) } diff --git a/test/hdnode.js b/test/hdnode.js index 265b5d6..794abe0 100644 --- a/test/hdnode.js +++ b/test/hdnode.js @@ -226,6 +226,21 @@ describe('HDNode', function() { }) }) + describe('neutered', function() { + var f = fixtures.valid[0] + + it('strips all private information', function() { + var hd = HDNode.fromBase58(f.master.base58) + var hdn = hd.neutered() + + assert.equal(hdn.privKey, undefined) + assert.equal(hdn.pubKey.toHex(), hd.pubKey.toHex()) + assert.equal(hdn.chainCode, hd.chainCode) + assert.equal(hdn.depth, hd.depth) + assert.equal(hdn.index, hd.index) + }) + }) + describe('derive', function() { function verifyVector(hd, v, depth) { assert.equal(hd.privKey.toWIF(), v.wif) From 2fbd9449f5e15daa2bfe20a868468486184a6ca8 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Tue, 29 Jul 2014 01:34:56 +1000 Subject: [PATCH 3/4] tests: use neutered rather than import/export --- test/hdnode.js | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/test/hdnode.js b/test/hdnode.js index 794abe0..465f09b 100644 --- a/test/hdnode.js +++ b/test/hdnode.js @@ -277,32 +277,28 @@ describe('HDNode', function() { var f = fixtures.valid[1] var c = f.children[0] - var parentNode = HDNode.fromBase58(f.master.base58Priv) - var child = parentNode.derive(c.m) + var master = HDNode.fromBase58(f.master.base58Priv) + var child = master.derive(c.m).neutered() - // FIXME: N(CKDpriv((kpar, cpar), i)), could be done better... - var childNeutered = HDNode.fromBase58(child.toBase58(false)) // neuter - assert.equal(childNeutered.toBase58(), c.base58) + assert.equal(child.toBase58(), c.base58) }) it('works for Private -> public (neutered, hardened)', function() { var f = fixtures.valid[0] var c = f.children[0] - var parentNode = HDNode.fromBase58(f.master.base58Priv) - var child = parentNode.deriveHardened(c.m) + var master = HDNode.fromBase58(f.master.base58Priv) + var child = master.deriveHardened(c.m).neutered() - // FIXME: N(CKDpriv((kpar, cpar), i)), could be done better... - var childNeutered = HDNode.fromBase58(child.toBase58(false)) // neuter - assert.equal(childNeutered.toBase58(), c.base58) + assert.equal(child.toBase58(), c.base58) }) it('works for Public -> public', function() { var f = fixtures.valid[1] var c = f.children[0] - var parentNode = HDNode.fromBase58(f.master.base58) - var child = parentNode.derive(c.m) + var master = HDNode.fromBase58(f.master.base58) + var child = master.derive(c.m) assert.equal(child.toBase58(), c.base58) }) @@ -311,10 +307,10 @@ describe('HDNode', function() { var f = fixtures.valid[0] var c = f.children[0] - var parentNode = HDNode.fromBase58(f.master.base58) + var master = HDNode.fromBase58(f.master.base58) assert.throws(function() { - parentNode.deriveHardened(c.m) + master.deriveHardened(c.m) }, /Could not derive hardened child key/) }) }) From 6b429493f80ce6b3fe83d5223634562900a6c73f Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Wed, 30 Jul 2014 18:00:20 +1000 Subject: [PATCH 4/4] hdnode: add deprecation message for isPrivate --- src/hdnode.js | 9 ++++++++- test/hdnode.js | 14 ++++++++------ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/hdnode.js b/src/hdnode.js index 560866e..3a7f860 100644 --- a/src/hdnode.js +++ b/src/hdnode.js @@ -157,7 +157,13 @@ HDNode.prototype.toBase58 = function(isPrivate) { } HDNode.prototype.toBuffer = function(isPrivate) { - if (isPrivate == undefined) isPrivate = !!this.privKey + if (isPrivate == undefined) { + isPrivate = !!this.privKey + + // FIXME: remove in 2.x.y + } else { + console.warn('isPrivate flag is deprecated, please use the .neutered() method instead') + } // Version var version = isPrivate ? this.network.bip32.private : this.network.bip32.public @@ -183,6 +189,7 @@ HDNode.prototype.toBuffer = function(isPrivate) { // 33 bytes: the public key or private key data if (isPrivate) { + // FIXME: remove in 2.x.y assert(this.privKey, 'Missing private key') // 0x00 + k for private keys diff --git a/test/hdnode.js b/test/hdnode.js index 465f09b..617261d 100644 --- a/test/hdnode.js +++ b/test/hdnode.js @@ -88,9 +88,9 @@ describe('HDNode', function() { describe('toBase58', function() { fixtures.valid.forEach(function(f) { it('exports ' + f.master.base58 + ' (public) correctly', function() { - var hd = HDNode.fromSeedHex(f.master.seed) + var hd = HDNode.fromSeedHex(f.master.seed).neutered() - assert.equal(hd.toBase58(false), f.master.base58) + assert.equal(hd.toBase58(), f.master.base58) }) }) @@ -98,10 +98,11 @@ describe('HDNode', function() { it('exports ' + f.master.base58Priv + ' (private) correctly', function() { var hd = HDNode.fromSeedHex(f.master.seed) - assert.equal(hd.toBase58(true), f.master.base58Priv) + assert.equal(hd.toBase58(), f.master.base58Priv) }) }) + // FIXME: remove in 2.x.y it('fails when there is no private key', function() { var hd = HDNode.fromBase58(fixtures.valid[0].master.base58) @@ -166,9 +167,9 @@ describe('HDNode', function() { describe('toBuffer/toHex', function() { fixtures.valid.forEach(function(f) { it('exports ' + f.master.hex + ' (public) correctly', function() { - var hd = HDNode.fromSeedHex(f.master.seed) + var hd = HDNode.fromSeedHex(f.master.seed).neutered() - assert.equal(hd.toHex(false), f.master.hex) + assert.equal(hd.toHex(), f.master.hex) }) }) @@ -176,10 +177,11 @@ describe('HDNode', function() { it('exports ' + f.master.hexPriv + ' (private) correctly', function() { var hd = HDNode.fromSeedHex(f.master.seed) - assert.equal(hd.toHex(true), f.master.hexPriv) + assert.equal(hd.toHex(), f.master.hexPriv) }) }) + // FIXME: remove in 2.x.y it('fails when there is no private key', function() { var hd = HDNode.fromHex(fixtures.valid[0].master.hex)