From 1b2c41978f5033bf48ca6d872d685c9c18f474db Mon Sep 17 00:00:00 2001 From: Gabe Gattis Date: Tue, 6 Dec 2016 18:58:01 -0500 Subject: [PATCH] revert behavior of derive --- lib/hdprivatekey.js | 37 ++++++++++++++++-- lib/hdpublickey.js | 33 ++++++++++++++-- test/hdkeys.js | 91 ++++++++++++++++++++++---------------------- test/hdprivatekey.js | 12 +++--- test/hdpublickey.js | 16 ++++---- 5 files changed, 124 insertions(+), 65 deletions(-) diff --git a/lib/hdprivatekey.js b/lib/hdprivatekey.js index 8c4e2bb..9d0a42e 100644 --- a/lib/hdprivatekey.js +++ b/lib/hdprivatekey.js @@ -126,12 +126,40 @@ HDPrivateKey._getDerivationIndexes = function(path) { return _.any(indexes, isNaN) ? null : indexes; }; -HDPrivateKey.prototype.derive = function() { - throw new Error('Method has been deprecated, please use deriveChild or deriveNonCompliantChild' + - 'and see documentation for more information'); +/** + * WARNING: This method is deprecated. Use deriveChild or deriveNonCompliantChild instead. + * + * + * Get a derived child based on a string or number. + * + * If the first argument is a string, it's parsed as the full path of + * derivation. Valid values for this argument include "m" (which returns the + * same private key), "m/0/1/40/2'/1000", where the ' quote means a hardened + * derivation. + * + * If the first argument is a number, the child with that index will be + * derived. If the second argument is truthy, the hardened version will be + * derived. See the example usage for clarification. + * + * @example + * ```javascript + * var parent = new HDPrivateKey('xprv...'); + * var child_0_1_2h = parent.derive(0).derive(1).derive(2, true); + * var copy_of_child_0_1_2h = parent.derive("m/0/1/2'"); + * assert(child_0_1_2h.xprivkey === copy_of_child_0_1_2h); + * ``` + * + * @param {string|number} arg + * @param {boolean?} hardened + */ +HDPrivateKey.prototype.derive = function(arg, hardened) { + return this.deriveNonCompliantChild(arg, hardened); }; /** + * WARNING: This method will not be officially supported until v1.0.0. + * + * * Get a derived child based on a string or number. * * If the first argument is a string, it's parsed as the full path of @@ -170,6 +198,9 @@ HDPrivateKey.prototype.deriveChild = function(arg, hardened, nonCompliant) { }; /** + * WARNING: This method will not be officially supported until v1.0.0 + * + * * WARNING: If this is a new implementation you should NOT use this method, you should be using * `derive` instead. * diff --git a/lib/hdpublickey.js b/lib/hdpublickey.js index ee083b6..b4024b7 100644 --- a/lib/hdpublickey.js +++ b/lib/hdpublickey.js @@ -85,12 +85,39 @@ HDPublicKey.isValidPath = function(arg) { return false; }; -HDPublicKey.prototype.derive = function() { - throw new Error('Method has been deprecated, please use deriveChild instead' + - 'and see documentation for more information'); +/** + * WARNING: This method is deprecated. Use deriveChild instead. + * + * + * Get a derivated child based on a string or number. + * + * If the first argument is a string, it's parsed as the full path of + * derivation. Valid values for this argument include "m" (which returns the + * same public key), "m/0/1/40/2/1000". + * + * Note that hardened keys can't be derived from a public extended key. + * + * If the first argument is a number, the child with that index will be + * derived. See the example usage for clarification. + * + * @example + * ```javascript + * var parent = new HDPublicKey('xpub...'); + * var child_0_1_2 = parent.derive(0).derive(1).derive(2); + * var copy_of_child_0_1_2 = parent.derive("m/0/1/2"); + * assert(child_0_1_2.xprivkey === copy_of_child_0_1_2); + * ``` + * + * @param {string|number} arg + */ +HDPublicKey.prototype.derive = function(arg, hardened) { + return this.deriveChild(arg, hardened); }; /** + * WARNING: This method will not be officially supported until v1.0.0. + * + * * Get a derivated child based on a string or number. * * If the first argument is a string, it's parsed as the full path of diff --git a/test/hdkeys.js b/test/hdkeys.js index 0c591ff..ec680d7 100644 --- a/test/hdkeys.js +++ b/test/hdkeys.js @@ -81,69 +81,69 @@ describe('BIP32 compliance', function() { }); it("should get m/0' ext. private key from test vector 1", function() { - var privateKey = new HDPrivateKey(vector1_m_private).deriveChild("m/0'"); + var privateKey = new HDPrivateKey(vector1_m_private).derive("m/0'"); privateKey.xprivkey.should.equal(vector1_m0h_private); }); it("should get m/0' ext. public key from test vector 1", function() { - HDPrivateKey(vector1_m_private).deriveChild("m/0'") + HDPrivateKey(vector1_m_private).derive("m/0'") .xpubkey.should.equal(vector1_m0h_public); }); it("should get m/0'/1 ext. private key from test vector 1", function() { - HDPrivateKey(vector1_m_private).deriveChild("m/0'/1") + HDPrivateKey(vector1_m_private).derive("m/0'/1") .xprivkey.should.equal(vector1_m0h1_private); }); it("should get m/0'/1 ext. public key from test vector 1", function() { - HDPrivateKey(vector1_m_private).deriveChild("m/0'/1") + HDPrivateKey(vector1_m_private).derive("m/0'/1") .xpubkey.should.equal(vector1_m0h1_public); }); it("should get m/0'/1 ext. public key from m/0' public key from test vector 1", function() { - var derivedPublic = HDPrivateKey(vector1_m_private).deriveChild("m/0'").hdPublicKey.deriveChild("m/1"); + var derivedPublic = HDPrivateKey(vector1_m_private).derive("m/0'").hdPublicKey.derive("m/1"); derivedPublic.xpubkey.should.equal(vector1_m0h1_public); }); it("should get m/0'/1/2' ext. private key from test vector 1", function() { var privateKey = new HDPrivateKey(vector1_m_private); - var derived = privateKey.deriveChild("m/0'/1/2'"); + var derived = privateKey.derive("m/0'/1/2'"); derived.xprivkey.should.equal(vector1_m0h12h_private); }); it("should get m/0'/1/2' ext. public key from test vector 1", function() { - HDPrivateKey(vector1_m_private).deriveChild("m/0'/1/2'") + HDPrivateKey(vector1_m_private).derive("m/0'/1/2'") .xpubkey.should.equal(vector1_m0h12h_public); }); it("should get m/0'/1/2'/2 ext. private key from test vector 1", function() { - HDPrivateKey(vector1_m_private).deriveChild("m/0'/1/2'/2") + HDPrivateKey(vector1_m_private).derive("m/0'/1/2'/2") .xprivkey.should.equal(vector1_m0h12h2_private); }); it("should get m/0'/1/2'/2 ext. public key from m/0'/1/2' public key from test vector 1", function() { - var derived = HDPrivateKey(vector1_m_private).deriveChild("m/0'/1/2'").hdPublicKey; - derived.deriveChild("m/2").xpubkey.should.equal(vector1_m0h12h2_public); + var derived = HDPrivateKey(vector1_m_private).derive("m/0'/1/2'").hdPublicKey; + derived.derive("m/2").xpubkey.should.equal(vector1_m0h12h2_public); }); it("should get m/0'/1/2h/2 ext. public key from test vector 1", function() { - HDPrivateKey(vector1_m_private).deriveChild("m/0'/1/2'/2") + HDPrivateKey(vector1_m_private).derive("m/0'/1/2'/2") .xpubkey.should.equal(vector1_m0h12h2_public); }); it("should get m/0'/1/2h/2/1000000000 ext. private key from test vector 1", function() { - HDPrivateKey(vector1_m_private).deriveChild("m/0'/1/2'/2/1000000000") + HDPrivateKey(vector1_m_private).derive("m/0'/1/2'/2/1000000000") .xprivkey.should.equal(vector1_m0h12h21000000000_private); }); it("should get m/0'/1/2h/2/1000000000 ext. public key from test vector 1", function() { - HDPrivateKey(vector1_m_private).deriveChild("m/0'/1/2'/2/1000000000") + HDPrivateKey(vector1_m_private).derive("m/0'/1/2'/2/1000000000") .xpubkey.should.equal(vector1_m0h12h21000000000_public); }); it("should get m/0'/1/2'/2/1000000000 ext. public key from m/0'/1/2'/2 public key from test vector 1", function() { - var derived = HDPrivateKey(vector1_m_private).deriveChild("m/0'/1/2'/2").hdPublicKey; - derived.deriveChild("m/1000000000").xpubkey.should.equal(vector1_m0h12h21000000000_public); + var derived = HDPrivateKey(vector1_m_private).derive("m/0'/1/2'/2").hdPublicKey; + derived.derive("m/1000000000").xpubkey.should.equal(vector1_m0h12h21000000000_public); }); it('should initialize test vector 2 from the extended public key', function() { @@ -159,66 +159,66 @@ describe('BIP32 compliance', function() { }); it("should get m/0 ext. private key from test vector 2", function() { - HDPrivateKey(vector2_m_private).deriveChild(0).xprivkey.should.equal(vector2_m0_private); + HDPrivateKey(vector2_m_private).derive(0).xprivkey.should.equal(vector2_m0_private); }); it("should get m/0 ext. public key from test vector 2", function() { - HDPrivateKey(vector2_m_private).deriveChild(0).xpubkey.should.equal(vector2_m0_public); + HDPrivateKey(vector2_m_private).derive(0).xpubkey.should.equal(vector2_m0_public); }); it("should get m/0 ext. public key from m public key from test vector 2", function() { - HDPrivateKey(vector2_m_private).hdPublicKey.deriveChild(0).xpubkey.should.equal(vector2_m0_public); + HDPrivateKey(vector2_m_private).hdPublicKey.derive(0).xpubkey.should.equal(vector2_m0_public); }); it("should get m/0/2147483647h ext. private key from test vector 2", function() { - HDPrivateKey(vector2_m_private).deriveChild("m/0/2147483647'") + HDPrivateKey(vector2_m_private).derive("m/0/2147483647'") .xprivkey.should.equal(vector2_m02147483647h_private); }); it("should get m/0/2147483647h ext. public key from test vector 2", function() { - HDPrivateKey(vector2_m_private).deriveChild("m/0/2147483647'") + HDPrivateKey(vector2_m_private).derive("m/0/2147483647'") .xpubkey.should.equal(vector2_m02147483647h_public); }); it("should get m/0/2147483647h/1 ext. private key from test vector 2", function() { - HDPrivateKey(vector2_m_private).deriveChild("m/0/2147483647'/1") + HDPrivateKey(vector2_m_private).derive("m/0/2147483647'/1") .xprivkey.should.equal(vector2_m02147483647h1_private); }); it("should get m/0/2147483647h/1 ext. public key from test vector 2", function() { - HDPrivateKey(vector2_m_private).deriveChild("m/0/2147483647'/1") + HDPrivateKey(vector2_m_private).derive("m/0/2147483647'/1") .xpubkey.should.equal(vector2_m02147483647h1_public); }); it("should get m/0/2147483647h/1 ext. public key from m/0/2147483647h public key from test vector 2", function() { - var derived = HDPrivateKey(vector2_m_private).deriveChild("m/0/2147483647'").hdPublicKey; - derived.deriveChild(1).xpubkey.should.equal(vector2_m02147483647h1_public); + var derived = HDPrivateKey(vector2_m_private).derive("m/0/2147483647'").hdPublicKey; + derived.derive(1).xpubkey.should.equal(vector2_m02147483647h1_public); }); it("should get m/0/2147483647h/1/2147483646h ext. private key from test vector 2", function() { - HDPrivateKey(vector2_m_private).deriveChild("m/0/2147483647'/1/2147483646'") + HDPrivateKey(vector2_m_private).derive("m/0/2147483647'/1/2147483646'") .xprivkey.should.equal(vector2_m02147483647h12147483646h_private); }); it("should get m/0/2147483647h/1/2147483646h ext. public key from test vector 2", function() { - HDPrivateKey(vector2_m_private).deriveChild("m/0/2147483647'/1/2147483646'") + HDPrivateKey(vector2_m_private).derive("m/0/2147483647'/1/2147483646'") .xpubkey.should.equal(vector2_m02147483647h12147483646h_public); }); it("should get m/0/2147483647h/1/2147483646h/2 ext. private key from test vector 2", function() { - HDPrivateKey(vector2_m_private).deriveChild("m/0/2147483647'/1/2147483646'/2") + HDPrivateKey(vector2_m_private).derive("m/0/2147483647'/1/2147483646'/2") .xprivkey.should.equal(vector2_m02147483647h12147483646h2_private); }); it("should get m/0/2147483647h/1/2147483646h/2 ext. public key from test vector 2", function() { - HDPrivateKey(vector2_m_private).deriveChild("m/0/2147483647'/1/2147483646'/2") + HDPrivateKey(vector2_m_private).derive("m/0/2147483647'/1/2147483646'/2") .xpubkey.should.equal(vector2_m02147483647h12147483646h2_public); }); it("should get m/0/2147483647h/1/2147483646h/2 ext. public key from m/0/2147483647h/2147483646h public key from test vector 2", function() { var derivedPublic = HDPrivateKey(vector2_m_private) - .deriveChild("m/0/2147483647'/1/2147483646'").hdPublicKey; - derivedPublic.deriveChild("m/2") + .derive("m/0/2147483647'/1/2147483646'").hdPublicKey; + derivedPublic.derive("m/2") .xpubkey.should.equal(vector2_m02147483647h12147483646h2_public); }); @@ -254,19 +254,20 @@ describe('BIP32 compliance', function() { derived.privateKey.toString().should.equal('4811a079bab267bfdca855b3bddff20231ff7044e648514fa099158472df2836'); }); - it('hdprivatekey will throw deprecation message', function() { - expect(function() { - var key = new HDPrivateKey(); - key.derive(); - }).to.throw('Method has been deprecated'); - }); - - it('hdpublickey will throw deprecation message', function() { - expect(function() { - var key = new HDPrivateKey(); - var pubkey = key.hdPublicKey; - pubkey.derive(); - }).to.throw('Method has been deprecated'); + it('should NOT use full 32 bytes for private key data that is hashed with the nonCompliant derive method', function() { + // This is to test that the previously implemented non-compliant to BIP32 + var privateKeyBuffer = new Buffer('00000055378cf5fafb56c711c674143f9b0ee82ab0ba2924f19b64f5ae7cdbfd', 'hex'); + var chainCodeBuffer = new Buffer('9c8a5c863e5941f3d99453e6ba66b328bb17cf0b8dec89ed4fc5ace397a1c089', 'hex'); + var key = HDPrivateKey.fromObject({ + network: 'testnet', + depth: 0, + parentFingerPrint: 0, + childIndex: 0, + privateKey: privateKeyBuffer, + chainCode: chainCodeBuffer + }); + var derived = key.derive("m/44'/0'/0'/0/0'"); + derived.privateKey.toString().should.equal('4811a079bab267bfdca855b3bddff20231ff7044e648514fa099158472df2836'); }); describe('edge cases', function() { @@ -299,7 +300,7 @@ describe('BIP32 compliance', function() { privateKey: privateKeyBuffer, chainCode: chainCodeBuffer }); - var derived = key.deriveChild("m/44'"); + var derived = key.derive("m/44'"); derived.privateKey.toString().should.equal('b15bce3608d607ee3a49069197732c656bca942ee59f3e29b4d56914c1de6825'); bitcore.PrivateKey.isValid.callCount.should.equal(2); }); @@ -320,7 +321,7 @@ describe('BIP32 compliance', function() { throw new Error('Point cannot be equal to Infinity'); }; sandbox.spy(key, '_deriveWithNumber'); - var derived = key.deriveChild("m/44"); + var derived = key.derive("m/44"); key._deriveWithNumber.callCount.should.equal(2); key.publicKey.toString().should.equal('029e58b241790284ef56502667b15157b3fc58c567f044ddc35653860f9455d099'); }); diff --git a/test/hdprivatekey.js b/test/hdprivatekey.js index 2e99591..6b63e62 100644 --- a/test/hdprivatekey.js +++ b/test/hdprivatekey.js @@ -30,7 +30,7 @@ describe('HDPrivate key interface', function() { var expectDerivationFail = function(argument, error) { return expectFail(function() { var privateKey = new HDPrivateKey(xprivkey); - privateKey.deriveChild(argument); + privateKey.derive(argument); }, error); }; @@ -123,14 +123,14 @@ describe('HDPrivate key interface', function() { it('allows derivation of hardened keys by passing a very big number', function() { var privateKey = new HDPrivateKey(xprivkey); - var derivedByNumber = privateKey.deriveChild(0x80000000); - var derivedByArgument = privateKey.deriveChild(0, true); + var derivedByNumber = privateKey.derive(0x80000000); + var derivedByArgument = privateKey.derive(0, true); derivedByNumber.xprivkey.should.equal(derivedByArgument.xprivkey); }); it('returns itself with \'m\' parameter', function() { var privateKey = new HDPrivateKey(xprivkey); - privateKey.should.equal(privateKey.deriveChild('m')); + privateKey.should.equal(privateKey.derive('m')); }); it('returns InvalidArgument if invalid data is given to getSerializedError', function() { @@ -203,8 +203,8 @@ describe('HDPrivate key interface', function() { it('shouldn\'t matter if derivations are made with strings or numbers', function() { var privateKey = new HDPrivateKey(xprivkey); - var derivedByString = privateKey.deriveChild('m/0\'/1/2\''); - var derivedByNumber = privateKey.deriveChild(0, true).deriveChild(1).deriveChild(2, true); + var derivedByString = privateKey.derive('m/0\'/1/2\''); + var derivedByNumber = privateKey.derive(0, true).derive(1).derive(2, true); derivedByNumber.xprivkey.should.equal(derivedByString.xprivkey); }); diff --git a/test/hdpublickey.js b/test/hdpublickey.js index 457f1bd..d51ee35 100644 --- a/test/hdpublickey.js +++ b/test/hdpublickey.js @@ -32,7 +32,7 @@ describe('HDPublicKey interface', function() { var expectDerivationFail = function(argument, error) { (function() { var pubkey = new HDPublicKey(xpubkey); - pubkey.deriveChild(argument); + pubkey.derive(argument); }).should.throw(error); }; @@ -188,15 +188,15 @@ describe('HDPublicKey interface', function() { describe('derivation', function() { it('derivation is the same whether deriving with number or string', function() { var pubkey = new HDPublicKey(xpubkey); - var derived1 = pubkey.deriveChild(0).deriveChild(1).deriveChild(200000); - var derived2 = pubkey.deriveChild('m/0/1/200000'); + var derived1 = pubkey.derive(0).derive(1).derive(200000); + var derived2 = pubkey.derive('m/0/1/200000'); derived1.xpubkey.should.equal(derived_0_1_200000); derived2.xpubkey.should.equal(derived_0_1_200000); }); it('allows special parameters m, M', function() { var expectDerivationSuccess = function(argument) { - new HDPublicKey(xpubkey).deriveChild(argument).xpubkey.should.equal(xpubkey); + new HDPublicKey(xpubkey).derive(argument).xpubkey.should.equal(xpubkey); }; expectDerivationSuccess('m'); expectDerivationSuccess('M'); @@ -204,13 +204,13 @@ describe('HDPublicKey interface', function() { it('doesn\'t allow object arguments for derivation', function() { expectFail(function() { - return new HDPublicKey(xpubkey).deriveChild({}); + return new HDPublicKey(xpubkey).derive({}); }, hdErrors.InvalidDerivationArgument); }); it('needs first argument for derivation', function() { expectFail(function() { - return new HDPublicKey(xpubkey).deriveChild('s'); + return new HDPublicKey(xpubkey).derive('s'); }, hdErrors.InvalidPath); }); @@ -224,13 +224,13 @@ describe('HDPublicKey interface', function() { it('can\'t derive hardened keys', function() { expectFail(function() { - return new HDPublicKey(xpubkey).deriveChild(HDPublicKey.Hardened); + return new HDPublicKey(xpubkey).derive(HDPublicKey.Hardened); }, hdErrors.InvalidIndexCantDeriveHardened); }); it('can\'t derive hardened keys via second argument', function() { expectFail(function() { - return new HDPublicKey(xpubkey).deriveChild(5, true); + return new HDPublicKey(xpubkey).derive(5, true); }, hdErrors.InvalidIndexCantDeriveHardened); });