Merge pull request #4 from maraoz/fix/path-validation
Improve path validation code legibility
This commit is contained in:
commit
b2146e4810
|
@ -106,18 +106,23 @@ HDPrivateKey._getDerivationIndexes = function(path) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var indexes = steps.slice(1).map(function(step) {
|
var indexes = steps.slice(1).map(function(step) {
|
||||||
var index = step ? +step : NaN;
|
var isHardened = step.slice(-1) === '\'';
|
||||||
|
|
||||||
var isHardened = isNaN(index) && step[step.length-1] == "'";
|
|
||||||
if (isHardened) {
|
if (isHardened) {
|
||||||
index = (+(step.slice(0, -1))) + HDPrivateKey.Hardened;
|
step = step.slice(0, -1);
|
||||||
|
}
|
||||||
|
if (!step || step[0] === '-') {
|
||||||
|
return NaN;
|
||||||
|
}
|
||||||
|
var index = +step; // cast to number
|
||||||
|
if (isHardened) {
|
||||||
|
index += HDPrivateKey.Hardened;
|
||||||
}
|
}
|
||||||
|
|
||||||
return index;
|
return index;
|
||||||
});
|
});
|
||||||
|
|
||||||
return _.any(indexes, isNaN) ? null : indexes;
|
return _.any(indexes, isNaN) ? null : indexes;
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a derivated child based on a string or number.
|
* 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]);
|
data = BufferUtil.concat([this.publicKey.toBuffer(), indexBuffer]);
|
||||||
}
|
}
|
||||||
var hash = Hash.sha512hmac(data, this._buffers.chainCode);
|
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 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({
|
var derived = new HDPrivateKey({
|
||||||
network: this.network,
|
network: this.network,
|
||||||
|
|
|
@ -14,7 +14,6 @@ var Base58Check = bitcore.encoding.Base58Check;
|
||||||
|
|
||||||
var xprivkey = 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi';
|
var xprivkey = 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi';
|
||||||
var json = '{"network":"livenet","depth":0,"fingerPrint":876747070,"parentFingerPrint":0,"childIndex":0,"chainCode":"873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d508","privateKey":"e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35","checksum":-411132559,"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() {
|
describe('HDPrivate key interface', function() {
|
||||||
/* jshint maxstatements: 50 */
|
/* jshint maxstatements: 50 */
|
||||||
var expectFail = function(func, error) {
|
var expectFail = function(func, error) {
|
||||||
|
@ -113,36 +112,33 @@ describe('HDPrivate key interface', function() {
|
||||||
derivedByNumber.xprivkey.should.equal(derivedByArgument.xprivkey);
|
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);
|
var privateKey = new HDPrivateKey(xprivkey);
|
||||||
privateKey.should.equal(privateKey.derive('m'));
|
privateKey.should.equal(privateKey.derive('m'));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns InvalidArgument if invalid data is given to getSerializedError', function() {
|
it('returns InvalidArgument if invalid data is given to getSerializedError', function() {
|
||||||
expect(
|
expect(
|
||||||
HDPrivateKey.getSerializedError(1) instanceof
|
HDPrivateKey.getSerializedError(1) instanceof hdErrors.UnrecognizedArgument
|
||||||
hdErrors.UnrecognizedArgument
|
|
||||||
).to.equal(true);
|
).to.equal(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns InvalidLength if data of invalid length is given to getSerializedError', function() {
|
it('returns InvalidLength if data of invalid length is given to getSerializedError', function() {
|
||||||
|
var b58s = Base58Check.encode(new buffer.Buffer('onestring'));
|
||||||
expect(
|
expect(
|
||||||
HDPrivateKey.getSerializedError(Base58Check.encode(new buffer.Buffer('onestring'))) instanceof
|
HDPrivateKey.getSerializedError(b58s) instanceof hdErrors.InvalidLength
|
||||||
hdErrors.InvalidLength
|
|
||||||
).to.equal(true);
|
).to.equal(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns InvalidNetworkArgument if an invalid network is provided', function() {
|
it('returns InvalidNetworkArgument if an invalid network is provided', function() {
|
||||||
expect(
|
expect(
|
||||||
HDPrivateKey.getSerializedError(xprivkey, 'invalidNetwork') instanceof
|
HDPrivateKey.getSerializedError(xprivkey, 'invalidNetwork') instanceof errors.InvalidNetworkArgument
|
||||||
errors.InvalidNetworkArgument
|
|
||||||
).to.equal(true);
|
).to.equal(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('recognizes that the wrong network was asked for', function() {
|
it('recognizes that the wrong network was asked for', function() {
|
||||||
expect(
|
expect(
|
||||||
HDPrivateKey.getSerializedError(xprivkey, 'testnet') instanceof
|
HDPrivateKey.getSerializedError(xprivkey, 'testnet') instanceof errors.InvalidNetwork
|
||||||
errors.InvalidNetwork
|
|
||||||
).to.equal(true);
|
).to.equal(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -200,7 +196,7 @@ describe('HDPrivate key interface', function() {
|
||||||
it('validates correct paths', function() {
|
it('validates correct paths', function() {
|
||||||
var valid;
|
var valid;
|
||||||
|
|
||||||
valid = HDPrivateKey.isValidPath("m/0'/1/2'");
|
valid = HDPrivateKey.isValidPath('m/0\'/1/2\'');
|
||||||
valid.should.equal(true);
|
valid.should.equal(true);
|
||||||
|
|
||||||
valid = HDPrivateKey.isValidPath('m');
|
valid = HDPrivateKey.isValidPath('m');
|
||||||
|
@ -219,59 +215,36 @@ describe('HDPrivate key interface', function() {
|
||||||
valid.should.equal(true);
|
valid.should.equal(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('rejects illegal paths', function() {
|
|
||||||
var valid;
|
|
||||||
|
|
||||||
valid = HDPrivateKey.isValidPath('m/-1/12');
|
var invalid = [
|
||||||
valid.should.equal(false);
|
'm/-1/12',
|
||||||
|
'bad path',
|
||||||
|
'K',
|
||||||
|
'm/',
|
||||||
|
'm/12asd',
|
||||||
|
'm/1/2//3'
|
||||||
|
];
|
||||||
|
|
||||||
valid = HDPrivateKey.isValidPath('bad path');
|
invalid.forEach(function(datum) {
|
||||||
valid.should.equal(false);
|
it('rejects illegal path ' + datum, function() {
|
||||||
|
HDPrivateKey.isValidPath(datum).should.equal(false);
|
||||||
valid = HDPrivateKey.isValidPath('K');
|
expect(HDPrivateKey._getDerivationIndexes(datum)).to.equal(null);
|
||||||
valid.should.equal(false);
|
});
|
||||||
|
|
||||||
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);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('generates deriving indexes correctly', function() {
|
it('generates deriving indexes correctly', function() {
|
||||||
var indexes;
|
var indexes;
|
||||||
|
|
||||||
indexes = HDPrivateKey._getDerivationIndexes('m/-1/12');
|
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.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]);
|
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() {
|
describe('conversion to plain object/json', function() {
|
||||||
|
@ -284,7 +257,8 @@ describe('HDPrivate key interface', function() {
|
||||||
'chainCode': '873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d508',
|
'chainCode': '873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d508',
|
||||||
'privateKey': 'e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35',
|
'privateKey': 'e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35',
|
||||||
'checksum': -411132559,
|
'checksum': -411132559,
|
||||||
'xprivkey':'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi'
|
'xprivkey': 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvN' +
|
||||||
|
'KmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi'
|
||||||
};
|
};
|
||||||
it('toObject leaves no Buffer instances', function() {
|
it('toObject leaves no Buffer instances', function() {
|
||||||
var privKey = new HDPrivateKey(xprivkey);
|
var privKey = new HDPrivateKey(xprivkey);
|
||||||
|
@ -308,4 +282,3 @@ describe('HDPrivate key interface', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue