diff --git a/lib/hdprivatekey.js b/lib/hdprivatekey.js index a5db864..7cc7e3f 100644 --- a/lib/hdprivatekey.js +++ b/lib/hdprivatekey.js @@ -21,7 +21,7 @@ var BufferUtil = require('./util/buffer'); var JSUtil = require('./util/js'); var MINIMUM_ENTROPY_BITS = 128; -var BITS_TO_BYTES = 1/8; +var BITS_TO_BYTES = 1 / 8; var MAXIMUM_ENTROPY_BITS = 512; @@ -106,18 +106,23 @@ HDPrivateKey._getDerivationIndexes = function(path) { } var indexes = steps.slice(1).map(function(step) { - var index = step ? +step : NaN; - - var isHardened = isNaN(index) && step[step.length-1] == "'"; + var isHardened = step.slice(-1) === '\''; + if (isHardened) { + step = step.slice(0, -1); + } + if (!step || step[0] === '-') { + return NaN; + } + var index = +step; // cast to number if (isHardened) { - index = (+(step.slice(0, -1))) + HDPrivateKey.Hardened; + index += HDPrivateKey.Hardened; } return index; }); return _.any(indexes, isNaN) ? null : indexes; -} +}; /** * Get a derivated child based on a string or number. @@ -177,10 +182,14 @@ HDPrivateKey.prototype._deriveWithNumber = function(index, hardened) { data = BufferUtil.concat([this.publicKey.toBuffer(), indexBuffer]); } var hash = Hash.sha512hmac(data, this._buffers.chainCode); - var leftPart = BN.fromBuffer(hash.slice(0, 32), {size: 32}); + var leftPart = BN.fromBuffer(hash.slice(0, 32), { + size: 32 + }); var chainCode = hash.slice(32, 64); - var privateKey = leftPart.add(this.privateKey.toBigNumber()).mod(Point.getN()).toBuffer({size: 32}); + var privateKey = leftPart.add(this.privateKey.toBigNumber()).mod(Point.getN()).toBuffer({ + size: 32 + }); var derived = new HDPrivateKey({ network: this.network, @@ -295,7 +304,7 @@ HDPrivateKey.prototype._buildFromSerialized = function(arg) { version: decoded.slice(HDPrivateKey.VersionStart, HDPrivateKey.VersionEnd), depth: decoded.slice(HDPrivateKey.DepthStart, HDPrivateKey.DepthEnd), parentFingerPrint: decoded.slice(HDPrivateKey.ParentFingerPrintStart, - HDPrivateKey.ParentFingerPrintEnd), + HDPrivateKey.ParentFingerPrintEnd), childIndex: decoded.slice(HDPrivateKey.ChildIndexStart, HDPrivateKey.ChildIndexEnd), chainCode: decoded.slice(HDPrivateKey.ChainCodeStart, HDPrivateKey.ChainCodeEnd), privateKey: decoded.slice(HDPrivateKey.PrivateKeyStart, HDPrivateKey.PrivateKeyEnd), @@ -508,20 +517,20 @@ HDPrivateKey.CheckSumSize = 4; HDPrivateKey.DataLength = 78; HDPrivateKey.SerializedByteSize = 82; -HDPrivateKey.VersionStart = 0; -HDPrivateKey.VersionEnd = HDPrivateKey.VersionStart + HDPrivateKey.VersionSize; -HDPrivateKey.DepthStart = HDPrivateKey.VersionEnd; -HDPrivateKey.DepthEnd = HDPrivateKey.DepthStart + HDPrivateKey.DepthSize; +HDPrivateKey.VersionStart = 0; +HDPrivateKey.VersionEnd = HDPrivateKey.VersionStart + HDPrivateKey.VersionSize; +HDPrivateKey.DepthStart = HDPrivateKey.VersionEnd; +HDPrivateKey.DepthEnd = HDPrivateKey.DepthStart + HDPrivateKey.DepthSize; HDPrivateKey.ParentFingerPrintStart = HDPrivateKey.DepthEnd; -HDPrivateKey.ParentFingerPrintEnd = HDPrivateKey.ParentFingerPrintStart + HDPrivateKey.ParentFingerPrintSize; -HDPrivateKey.ChildIndexStart = HDPrivateKey.ParentFingerPrintEnd; -HDPrivateKey.ChildIndexEnd = HDPrivateKey.ChildIndexStart + HDPrivateKey.ChildIndexSize; -HDPrivateKey.ChainCodeStart = HDPrivateKey.ChildIndexEnd; -HDPrivateKey.ChainCodeEnd = HDPrivateKey.ChainCodeStart + HDPrivateKey.ChainCodeSize; -HDPrivateKey.PrivateKeyStart = HDPrivateKey.ChainCodeEnd + 1; -HDPrivateKey.PrivateKeyEnd = HDPrivateKey.PrivateKeyStart + HDPrivateKey.PrivateKeySize; -HDPrivateKey.ChecksumStart = HDPrivateKey.PrivateKeyEnd; -HDPrivateKey.ChecksumEnd = HDPrivateKey.ChecksumStart + HDPrivateKey.CheckSumSize; +HDPrivateKey.ParentFingerPrintEnd = HDPrivateKey.ParentFingerPrintStart + HDPrivateKey.ParentFingerPrintSize; +HDPrivateKey.ChildIndexStart = HDPrivateKey.ParentFingerPrintEnd; +HDPrivateKey.ChildIndexEnd = HDPrivateKey.ChildIndexStart + HDPrivateKey.ChildIndexSize; +HDPrivateKey.ChainCodeStart = HDPrivateKey.ChildIndexEnd; +HDPrivateKey.ChainCodeEnd = HDPrivateKey.ChainCodeStart + HDPrivateKey.ChainCodeSize; +HDPrivateKey.PrivateKeyStart = HDPrivateKey.ChainCodeEnd + 1; +HDPrivateKey.PrivateKeyEnd = HDPrivateKey.PrivateKeyStart + HDPrivateKey.PrivateKeySize; +HDPrivateKey.ChecksumStart = HDPrivateKey.PrivateKeyEnd; +HDPrivateKey.ChecksumEnd = HDPrivateKey.ChecksumStart + HDPrivateKey.CheckSumSize; assert(HDPrivateKey.ChecksumEnd === HDPrivateKey.SerializedByteSize); diff --git a/test/hdprivatekey.js b/test/hdprivatekey.js index 0888007..4a0d20b 100644 --- a/test/hdprivatekey.js +++ b/test/hdprivatekey.js @@ -14,7 +14,6 @@ var Base58Check = bitcore.encoding.Base58Check; var xprivkey = 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi'; var json = '{"network":"livenet","depth":0,"fingerPrint":876747070,"parentFingerPrint":0,"childIndex":0,"chainCode":"873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d508","privateKey":"e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35","checksum":-411132559,"xprivkey":"xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi"}'; - describe('HDPrivate key interface', function() { /* jshint maxstatements: 50 */ var expectFail = function(func, error) { @@ -113,36 +112,33 @@ describe('HDPrivate key interface', function() { derivedByNumber.xprivkey.should.equal(derivedByArgument.xprivkey); }); - it('returns itself with "m" parameter', function() { + it('returns itself with \'m\' parameter', function() { var privateKey = new HDPrivateKey(xprivkey); privateKey.should.equal(privateKey.derive('m')); }); it('returns InvalidArgument if invalid data is given to getSerializedError', function() { expect( - HDPrivateKey.getSerializedError(1) instanceof - hdErrors.UnrecognizedArgument + HDPrivateKey.getSerializedError(1) instanceof hdErrors.UnrecognizedArgument ).to.equal(true); }); it('returns InvalidLength if data of invalid length is given to getSerializedError', function() { + var b58s = Base58Check.encode(new buffer.Buffer('onestring')); expect( - HDPrivateKey.getSerializedError(Base58Check.encode(new buffer.Buffer('onestring'))) instanceof - hdErrors.InvalidLength + HDPrivateKey.getSerializedError(b58s) instanceof hdErrors.InvalidLength ).to.equal(true); }); it('returns InvalidNetworkArgument if an invalid network is provided', function() { expect( - HDPrivateKey.getSerializedError(xprivkey, 'invalidNetwork') instanceof - errors.InvalidNetworkArgument + HDPrivateKey.getSerializedError(xprivkey, 'invalidNetwork') instanceof errors.InvalidNetworkArgument ).to.equal(true); }); it('recognizes that the wrong network was asked for', function() { expect( - HDPrivateKey.getSerializedError(xprivkey, 'testnet') instanceof - errors.InvalidNetwork + HDPrivateKey.getSerializedError(xprivkey, 'testnet') instanceof errors.InvalidNetwork ).to.equal(true); }); @@ -200,7 +196,7 @@ describe('HDPrivate key interface', function() { it('validates correct paths', function() { var valid; - valid = HDPrivateKey.isValidPath("m/0'/1/2'"); + valid = HDPrivateKey.isValidPath('m/0\'/1/2\''); valid.should.equal(true); valid = HDPrivateKey.isValidPath('m'); @@ -219,72 +215,50 @@ describe('HDPrivate key interface', function() { valid.should.equal(true); }); - it('rejects illegal paths', function() { - var valid; - - valid = HDPrivateKey.isValidPath('m/-1/12'); - valid.should.equal(false); - - valid = HDPrivateKey.isValidPath('bad path'); - valid.should.equal(false); - valid = HDPrivateKey.isValidPath('K'); - valid.should.equal(false); + var invalid = [ + 'm/-1/12', + 'bad path', + 'K', + 'm/', + 'm/12asd', + 'm/1/2//3' + ]; - valid = HDPrivateKey.isValidPath('m/'); - valid.should.equal(false); - - valid = HDPrivateKey.isValidPath('m/12asd'); - valid.should.equal(false); - - valid = HDPrivateKey.isValidPath(HDPrivateKey.MaxHardened); - valid.should.equal(false); + invalid.forEach(function(datum) { + it('rejects illegal path ' + datum, function() { + HDPrivateKey.isValidPath(datum).should.equal(false); + expect(HDPrivateKey._getDerivationIndexes(datum)).to.equal(null); + }); }); it('generates deriving indexes correctly', function() { var indexes; indexes = HDPrivateKey._getDerivationIndexes('m/-1/12'); - indexes.should.eql([-1, 12]); + expect(indexes).to.equal(null); - indexes = HDPrivateKey._getDerivationIndexes("m/0/12/12'"); + indexes = HDPrivateKey._getDerivationIndexes('m/0/12/12\''); indexes.should.eql([0, 12, HDPrivateKey.Hardened + 12]); - indexes = HDPrivateKey._getDerivationIndexes("m/0/12/12'"); + indexes = HDPrivateKey._getDerivationIndexes('m/0/12/12\''); indexes.should.eql([0, 12, HDPrivateKey.Hardened + 12]); }); - it('rejects invalid derivation path', function() { - var indexes; - - indexes = HDPrivateKey._getDerivationIndexes("m/"); - expect(indexes).to.be.null; - - indexes = HDPrivateKey._getDerivationIndexes("bad path"); - expect(indexes).to.be.null; - - indexes = HDPrivateKey._getDerivationIndexes("K"); - expect(indexes).to.be.null; - - indexes = HDPrivateKey._getDerivationIndexes("m/"); - expect(indexes).to.be.null; - - indexes = HDPrivateKey._getDerivationIndexes("m/123asd"); - expect(indexes).to.be.null; - }); }); describe('conversion to plain object/json', function() { var plainObject = { - 'network':'livenet', - 'depth':0, - 'fingerPrint':876747070, - 'parentFingerPrint':0, - 'childIndex':0, - 'chainCode':'873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d508', - 'privateKey':'e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35', - 'checksum':-411132559, - 'xprivkey':'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi' + 'network': 'livenet', + 'depth': 0, + 'fingerPrint': 876747070, + 'parentFingerPrint': 0, + 'childIndex': 0, + 'chainCode': '873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d508', + 'privateKey': 'e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35', + 'checksum': -411132559, + 'xprivkey': 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvN' + + 'KmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi' }; it('toObject leaves no Buffer instances', function() { var privKey = new HDPrivateKey(xprivkey); @@ -308,4 +282,3 @@ describe('HDPrivate key interface', function() { }); }); }); -