diff --git a/lib/api.js b/lib/api.js index 2431913f..4088c8d2 100644 --- a/lib/api.js +++ b/lib/api.js @@ -521,6 +521,7 @@ API.prototype._import = function(cb) { * @param {String} opts.passphrase * @param {Number} opts.account - default 0 * @param {String} opts.derivationStrategy - default 'BIP44' + * @param {String} opts.entropySourcePath - Only used if the wallet was created on a HW wallet, in which that private keys was not available for all the needed derivations */ API.prototype.importFromMnemonic = function(words, opts, cb) { log.debug('Importing from 12 Words'); @@ -531,7 +532,8 @@ API.prototype.importFromMnemonic = function(words, opts, cb) { function derive(nonCompliantDerivation) { return Credentials.fromMnemonic(opts.network || 'livenet', words, opts.passphrase, opts.account || 0, opts.derivationStrategy || Constants.DERIVATION_STRATEGIES.BIP44, { - nonCompliantDerivation: nonCompliantDerivation + nonCompliantDerivation: nonCompliantDerivation, + entropySourcePath: opts.entropySourcePath, }); }; diff --git a/lib/credentials.js b/lib/credentials.js index 200afd24..480e872d 100644 --- a/lib/credentials.js +++ b/lib/credentials.js @@ -38,6 +38,7 @@ var FIELDS = [ 'compliantDerivation', 'addressType', 'hwInfo', + 'entropySourcePath', ]; function Credentials() { @@ -126,6 +127,8 @@ Credentials.fromMnemonic = function(network, words, passphrase, account, derivat x.account = account; x.derivationStrategy = derivationStrategy; x.compliantDerivation = !opts.nonCompliantDerivation; + x.entropySourcePath = opts.entropySourcePath; + x._expand(); return x; }; @@ -185,6 +188,7 @@ Credentials.prototype._hashFromEntropy = function(prefix, length) { Credentials.prototype._expand = function() { $.checkState(this.xPrivKey || (this.xPubKey && this.entropySource)); + var network = Credentials._getNetworkFromExtendedKey(this.xPrivKey || this.xPubKey); if (this.network) { $.checkState(this.network == network); @@ -201,7 +205,25 @@ Credentials.prototype._expand = function() { // this is the xPubKey shared with the server. this.xPubKey = derivedXPrivKey.hdPublicKey.toString(); + } + + // requests keys from mnemonics, but using a xPubkey + // This is only used when importing mnemonics FROM + // an hwwallet, in which xPriv was not available when + // the wallet was created. + if (this.entropySourcePath) { + var seed = deriveFn(this.entropySourcePath).publicKey.toBuffer(); + this.entropySource = Bitcore.crypto.Hash.sha256sha256(seed).toString('hex'); + } + if (this.entropySource) { + // request keys from entropy (hw wallets) + var seed = this._hashFromEntropy('reqPrivKey', 32); + var privKey = new Bitcore.PrivateKey(seed.toString('hex'), network); + this.requestPrivKey = privKey.toString(); + this.requestPubKey = privKey.toPublicKey().toString(); + } else { + // request keys derived from xPriv var requestDerivation = deriveFn(Constants.PATHS.REQUEST_KEY); this.requestPrivKey = requestDerivation.privateKey.toString(); @@ -209,16 +231,10 @@ Credentials.prototype._expand = function() { this.requestPubKey = pubKey.toString(); this.entropySource = Bitcore.crypto.Hash.sha256(requestDerivation.privateKey.toBuffer()).toString('hex'); - } else { - var seed = this._hashFromEntropy('reqPrivKey', 32); - var privKey = new Bitcore.PrivateKey(seed.toString('hex'), network); - this.requestPrivKey = privKey.toString(); - this.requestPubKey = privKey.toPublicKey().toString(); } this.personalEncryptingKey = this._hashFromEntropy('personalKey', 16).toString('base64'); - this.copayerId = Credentials._xPubToCopayerId(this.xPubKey); this.publicKeyRing = [{ xPubKey: this.xPubKey, diff --git a/package.json b/package.json index 703d18fc..1c427985 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "bitcore-wallet-client", "description": "Client for bitcore-wallet-service", "author": "BitPay Inc", - "version": "5.2.1", + "version": "5.3.0", "license": "MIT", "keywords": [ "bitcoin",