diff --git a/bit-wallet/bit-txproposals b/bit-wallet/bit-txproposals index 7dd298e..26a27ae 100755 --- a/bit-wallet/bit-txproposals +++ b/bit-wallet/bit-txproposals @@ -24,9 +24,9 @@ function end(err, txps, rawtxps) { utils.renderTxProposals(txps); if (program.output) { - client.getEncryptedPublicKeyRing(function (err, pkr) { + client.getEncryptedWalletData(function (err, toComplete) { var txData = { - pkr: pkr, + toComplete: toComplete, txps: txps, }; fs.writeFileSync(program.output, JSON.stringify(txData)); diff --git a/lib/client/api.js b/lib/client/api.js index ddab8e8..cec3b83 100644 --- a/lib/client/api.js +++ b/lib/client/api.js @@ -18,6 +18,8 @@ var BASE_URL = 'http://localhost:3001/copay/api'; var WALLET_CRITICAL_DATA = ['xPrivKey', 'm', 'publicKeyRing', 'sharedEncryptingKey']; var WALLET_EXTRA_DATA = ['copayerId', 'roPrivKey', 'rwPrivKey']; +var WALLET_AIRGAPPED_TOCOMPLETE = ['publicKeyRing', 'm', 'n', 'sharedEncryptingKey']; + function _encryptMessage(message, encryptingKey) { if (!message) return null; return WalletUtils.encryptMessage(message, encryptingKey); @@ -110,20 +112,30 @@ API.prototype._tryToCompleteFromServer = function(data, cb) { }; +API.prototype._tryToCompleteFromData = function(data, toComplete, cb) { + var inData = _decryptMessage(toComplete, + WalletUtils.privateKeyToAESKey(data.roPrivKey)); + if (!inData) + return cb('Could not complete wallet'); + + try { + inData = JSON.parse(inData); + _.extend(data, _.pick(inData, WALLET_AIRGAPPED_TOCOMPLETE)); + } catch (ex) { + return cb(ex); + } + + this.storage.save(data, function(err) { + return cb(err, data); + }); +}; + API.prototype._tryToComplete = function(opts, data, cb) { - if (opts.pkr) { - var pkr = _decryptMessage(opts.pkr,WalletUtils.privateKeyToAESKey(data.roPrivKey)); - - if (!pkr) - return cb('Could not complete wallet'); - - data.publicKeyRing = JSON.parse(pkr); - this.storage.save(data, function(err) { - return cb(err, data); - }); + if (opts.toComplete) { + this._tryToCompleteFromData(data, opts.toComplete, cb); } else { - this._tryToCompleteFromServer(data,cb); + this._tryToCompleteFromServer(data, cb); } }; @@ -151,13 +163,10 @@ API.prototype._loadAndCheck = function(opts, cb) { this._load(function(err, data) { if (err) return cb(err); - if (data.n > 1) { - var pkrComplete = data.publicKeyRing && data.m && data.publicKeyRing.length === data.n; - if (!pkrComplete) { - return self._tryToComplete(opts, data, cb); - } - } + if (!data.n || (data.n > 1 && data.publicKeyRing.length != data.n)) + return self._tryToComplete(opts, data, cb); + return cb(null, data); }); }; @@ -542,7 +551,10 @@ API.prototype.import = function(str, cb) { API.prototype.parseTxProposals = function(txData, cb) { var self = this; - this._loadAndCheck({pkr: txData.pkr},function(err, data) { + this._loadAndCheck({ + toComplete: txData.toComplete + }, function(err, data) { + if (err) return cb(err); var txps = txData.txps; _processTxps(txps, data.sharedEncryptingKey); @@ -645,13 +657,13 @@ API.prototype.getSignatures = function(txp, cb) { }); }; -API.prototype.getEncryptedPublicKeyRing = function(cb) { +API.prototype.getEncryptedWalletData = function(cb) { var self = this; this._loadAndCheck({}, function(err, data) { if (err) return cb(err); - var pkr = JSON.stringify(data.publicKeyRing); - return cb(null, _encryptMessage(pkr, WalletUtils.privateKeyToAESKey(data.roPrivKey))); + var toComplete = JSON.stringify(_.pick(data, WALLET_AIRGAPPED_TOCOMPLETE)); + return cb(null, _encryptMessage(toComplete, WalletUtils.privateKeyToAESKey(data.roPrivKey))); }); }; diff --git a/test/integration/clientApi.js b/test/integration/clientApi.js index 30d816f..6d09aef 100644 --- a/test/integration/clientApi.js +++ b/test/integration/clientApi.js @@ -533,16 +533,26 @@ describe('client API ', function() { }, function(err, txs, rawTxps) { should.not.exist(err); - clients[2].getEncryptedPublicKeyRing(function(err, pkr) { + + clients[2].getEncryptedWalletData(function(err, toComplete) { should.not.exist(err); + // Disable networking + clients[0].request = sinon.stub().yields('no network'); + + // Make client incomplete + var data = JSON.parse(fsmock._get('client0')); + delete data.n; + fsmock._set('client0', JSON.stringify(data)); + // Back to the air gapped // // Will trigger _tryToComplete and use pkr // then, needs pkr to verify the txps + clients[0].parseTxProposals({ txps: rawTxps, - pkr: pkr, + toComplete: toComplete, }, function(err, txs2) { should.not.exist(err); done();