|
|
@ -139,6 +139,9 @@ HDPrivateKey._getDerivationIndexes = function(path) { |
|
|
|
* derived. If the second argument is truthy, the hardened version will be |
|
|
|
* derived. See the example usage for clarification. |
|
|
|
* |
|
|
|
* WARNING: The `nonCompliant` option should NOT be used, except for older implementation |
|
|
|
* that used a derivation strategy that used a non-zero padded private key. |
|
|
|
* |
|
|
|
* @example |
|
|
|
* ```javascript
|
|
|
|
* var parent = new HDPrivateKey('xprv...'); |
|
|
@ -149,18 +152,38 @@ HDPrivateKey._getDerivationIndexes = function(path) { |
|
|
|
* |
|
|
|
* @param {string|number} arg |
|
|
|
* @param {boolean?} hardened |
|
|
|
* @param {boolean?} nonCompliant - This should not be used and only for backwards compatibility |
|
|
|
* |
|
|
|
*/ |
|
|
|
HDPrivateKey.prototype.derive = function(arg, hardened) { |
|
|
|
HDPrivateKey.prototype.derive = function(arg, hardened, nonCompliant) { |
|
|
|
if (_.isNumber(arg)) { |
|
|
|
return this._deriveWithNumber(arg, hardened); |
|
|
|
return this._deriveWithNumber(arg, hardened, nonCompliant); |
|
|
|
} else if (_.isString(arg)) { |
|
|
|
return this._deriveFromString(arg); |
|
|
|
return this._deriveFromString(arg, nonCompliant); |
|
|
|
} else { |
|
|
|
throw new hdErrors.InvalidDerivationArgument(arg); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
HDPrivateKey.prototype._deriveWithNumber = function(index, hardened) { |
|
|
|
/** |
|
|
|
* WARNING: If this is a new implementation you should NOT use this method, you should be using |
|
|
|
* `derive` instead. |
|
|
|
* |
|
|
|
* This method is explicitly for use and compatibility with an implementation that |
|
|
|
* was not compliant with BIP32 regarding the derivation algorithm. The private key |
|
|
|
* must be 32 bytes hashing, and this implementation will use the non-zero padded |
|
|
|
* serialization of a private key, such that it's still possible to derive the privateKey |
|
|
|
* to recover those funds. |
|
|
|
* |
|
|
|
* @param {string|number} arg |
|
|
|
* @param {boolean?} hardened |
|
|
|
*/ |
|
|
|
HDPrivateKey.prototype.deriveNonCompliant = function(arg, hardened) { |
|
|
|
var derived = this.derive(arg, hardened, true); |
|
|
|
return derived; |
|
|
|
}; |
|
|
|
|
|
|
|
HDPrivateKey.prototype._deriveWithNumber = function(index, hardened, nonCompliant) { |
|
|
|
/* jshint maxstatements: 20 */ |
|
|
|
/* jshint maxcomplexity: 10 */ |
|
|
|
if (!HDPrivateKey.isValidPath(index, hardened)) { |
|
|
@ -172,15 +195,23 @@ HDPrivateKey.prototype._deriveWithNumber = function(index, hardened) { |
|
|
|
index += HDPrivateKey.Hardened; |
|
|
|
} |
|
|
|
|
|
|
|
var cached = HDKeyCache.get(this.xprivkey, index, hardened); |
|
|
|
var cached = HDKeyCache.get(this.xprivkey, index, hardened, nonCompliant); |
|
|
|
if (cached) { |
|
|
|
return cached; |
|
|
|
} |
|
|
|
|
|
|
|
var indexBuffer = BufferUtil.integerAsBuffer(index); |
|
|
|
var data; |
|
|
|
if (hardened) { |
|
|
|
data = BufferUtil.concat([new buffer.Buffer([0]), this.privateKey.toBuffer(), indexBuffer]); |
|
|
|
if (hardened && nonCompliant) { |
|
|
|
// The private key serialization in this case will not be exactly 32 bytes and can be
|
|
|
|
// any value less, and the value is not zero-padded.
|
|
|
|
var nonZeroPadded = this.privateKey.bn.toBuffer(); |
|
|
|
data = BufferUtil.concat([new buffer.Buffer([0]), nonZeroPadded, indexBuffer]); |
|
|
|
} else if (hardened) { |
|
|
|
// This will use a 32 byte zero padded serialization of the private key
|
|
|
|
var privateKeyBuffer = this.privateKey.bn.toBuffer({size: 32}); |
|
|
|
assert(privateKeyBuffer.length === 32, 'length of private key buffer is expected to be 32 bytes'); |
|
|
|
data = BufferUtil.concat([new buffer.Buffer([0]), privateKeyBuffer, indexBuffer]); |
|
|
|
} else { |
|
|
|
data = BufferUtil.concat([this.publicKey.toBuffer(), indexBuffer]); |
|
|
|
} |
|
|
@ -202,18 +233,18 @@ HDPrivateKey.prototype._deriveWithNumber = function(index, hardened) { |
|
|
|
chainCode: chainCode, |
|
|
|
privateKey: privateKey |
|
|
|
}); |
|
|
|
HDKeyCache.set(this.xprivkey, index, hardened, derived); |
|
|
|
HDKeyCache.set(this.xprivkey, index, hardened, derived, nonCompliant); |
|
|
|
return derived; |
|
|
|
}; |
|
|
|
|
|
|
|
HDPrivateKey.prototype._deriveFromString = function(path) { |
|
|
|
HDPrivateKey.prototype._deriveFromString = function(path, nonCompliant) { |
|
|
|
if (!HDPrivateKey.isValidPath(path)) { |
|
|
|
throw new hdErrors.InvalidPath(path); |
|
|
|
} |
|
|
|
|
|
|
|
var indexes = HDPrivateKey._getDerivationIndexes(path); |
|
|
|
var derived = indexes.reduce(function(prev, index) { |
|
|
|
return prev._deriveWithNumber(index); |
|
|
|
return prev._deriveWithNumber(index, null, nonCompliant); |
|
|
|
}, this); |
|
|
|
|
|
|
|
return derived; |
|
|
|