import & export with compress/encrypt
This commit is contained in:
parent
18884f3c0f
commit
a0019d966c
|
@ -9,6 +9,7 @@ var request = require('request')
|
|||
var events = require('events');
|
||||
log.debug = log.verbose;
|
||||
var Bitcore = require('bitcore')
|
||||
var sjcl = require('sjcl');
|
||||
|
||||
var Credentials = require('./credentials');
|
||||
var WalletUtils = require('../walletutils');
|
||||
|
@ -17,6 +18,9 @@ var ServerCompromisedError = require('./servercompromisederror');
|
|||
var ClientError = require('../clienterror');
|
||||
|
||||
var BASE_URL = 'http://localhost:3001/copay/api';
|
||||
var WALLET_ENCRYPTION_OPTS = {
|
||||
iter: 5000
|
||||
};
|
||||
|
||||
function _encryptMessage(message, encryptingKey) {
|
||||
if (!message) return null;
|
||||
|
@ -95,6 +99,73 @@ API.prototype.seedFromAirGapped = function(seed) {
|
|||
this.credentials = Credentials.fromExtendedPublicKey(seed.xPubKey, seed.requestPrivKey);
|
||||
};
|
||||
|
||||
/**
|
||||
* export
|
||||
*
|
||||
* @param opts
|
||||
* @param opts.compressed
|
||||
* @param opts.password
|
||||
*/
|
||||
API.prototype.export = function(opts) {
|
||||
$.checkState(this.credentials);
|
||||
|
||||
opts = opts || {};
|
||||
|
||||
var output;
|
||||
if (opts.compressed) {
|
||||
output = this.credentials.exportCompressed();
|
||||
} else {
|
||||
output = JSON.stringify(this.credentials.toObj());
|
||||
}
|
||||
|
||||
if (opts.password) {
|
||||
output = sjcl.encrypt(opts.password, output, WALLET_ENCRYPTION_OPTS);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* export
|
||||
*
|
||||
* @param opts
|
||||
* @param opts.compressed
|
||||
* @param opts.password
|
||||
*/
|
||||
API.prototype.import = function(str, opts) {
|
||||
opts = opts || {};
|
||||
|
||||
var input = str;
|
||||
if (opts.password) {
|
||||
try {
|
||||
input = sjcl.decrypt(opts.password, input);
|
||||
} catch (ex) {
|
||||
throw new Error('Incorrect password');
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (opts.compressed) {
|
||||
this.credentials = Credentials.importCompressed(input);
|
||||
// TODO: complete missing fields that live on the server only such as: walletId, walletName, copayerName
|
||||
} else {
|
||||
this.credentials = Credentials.fromObj(JSON.parse(input));
|
||||
}
|
||||
} catch (ex) {
|
||||
throw new Error('Error importing from source');
|
||||
}
|
||||
};
|
||||
|
||||
API.prototype.toString = function(password) {
|
||||
$.checkState(this.credentials);
|
||||
return this.credentials.toObject();
|
||||
};
|
||||
|
||||
API.prototype.fromString = function(str) {
|
||||
this.credentials = Credentials.fromObject(str);
|
||||
};
|
||||
|
||||
API.prototype._doRequest = function(method, url, args, cb) {
|
||||
$.checkState(this.credentials);
|
||||
|
||||
|
@ -340,19 +411,6 @@ API.prototype.getBalance = function(cb) {
|
|||
self._doGetRequest('/v1/balance/', cb);
|
||||
};
|
||||
|
||||
/**
|
||||
* Exports the wallet as it is now.
|
||||
*/
|
||||
API.prototype.export = function() {
|
||||
$.checkState(this.credentials);
|
||||
|
||||
return this.credentials.exportCompressed();
|
||||
}
|
||||
|
||||
|
||||
API.prototype.import = function(str) {
|
||||
this.credentials = Credentials.importCompressed(str);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
|
|
|
@ -133,40 +133,6 @@ WalletUtils.privateKeyToAESKey = function(privKey) {
|
|||
return Bitcore.crypto.Hash.sha256(pk.toBuffer()).slice(0, 16).toString('base64');
|
||||
};
|
||||
|
||||
WalletUtils.decryptWallet = function(data, password) {
|
||||
$.checkArgument(data.enc);
|
||||
var extraFields = JSON.parse(sjcl.decrypt(password, data.enc));
|
||||
delete data.enc;
|
||||
return _.extend(data, extraFields);
|
||||
};
|
||||
|
||||
|
||||
WalletUtils.sjclOpts = {
|
||||
iter: 5000,
|
||||
};
|
||||
|
||||
WalletUtils.encryptWallet = function(data, accessWithoutEncrytion, password) {
|
||||
|
||||
// Fields to encrypt, given the NOPASSWD access level
|
||||
var fieldsEncryptByLevel = {
|
||||
none: _.keys(data),
|
||||
readonly: ['xPrivKey', 'requestPrivKey', 'publicKeyRing'],
|
||||
readwrite: ['xPrivKey', ],
|
||||
full: [],
|
||||
};
|
||||
|
||||
var fieldsEncrypt = fieldsEncryptByLevel[accessWithoutEncrytion];
|
||||
$.checkState(!_.isUndefined(fieldsEncrypt));
|
||||
|
||||
var toEncrypt = _.pick(data, fieldsEncrypt);
|
||||
var enc = sjcl.encrypt(password, JSON.stringify(toEncrypt), WalletUtils.sjclOpts);
|
||||
|
||||
var ret = _.omit(data, fieldsEncrypt);
|
||||
ret.enc = enc;
|
||||
return ret;
|
||||
};
|
||||
|
||||
|
||||
|
||||
WalletUtils.signTxp = function(txp, xPrivKey) {
|
||||
var self = this;
|
||||
|
|
|
@ -865,29 +865,85 @@ describe('client API ', function() {
|
|||
});
|
||||
|
||||
describe('Export & Import', function() {
|
||||
it('should export & import', function(done) {
|
||||
var address, importedClient;
|
||||
beforeEach(function(done) {
|
||||
importedClient = null;
|
||||
helpers.createAndJoinWallet(clients, 1, 1, function() {
|
||||
clients[0].createAddress(function(err, address) {
|
||||
clients[0].createAddress(function(err, addr) {
|
||||
should.not.exist(err);
|
||||
should.exist(address.address);
|
||||
|
||||
var exported = clients[0].export();
|
||||
|
||||
var importedClient = new Client({
|
||||
request: helpers.getRequest(app),
|
||||
});
|
||||
importedClient.import(exported);
|
||||
|
||||
importedClient.getMainAddresses({}, function(err, list) {
|
||||
should.not.exist(err);
|
||||
should.exist(list);
|
||||
list.length.should.equal(1);
|
||||
list[0].address.should.equal(address.address);
|
||||
done();
|
||||
});
|
||||
should.exist(addr.address);
|
||||
address = addr.address;
|
||||
done();
|
||||
});
|
||||
})
|
||||
});
|
||||
});
|
||||
afterEach(function(done) {
|
||||
importedClient.getMainAddresses({}, function(err, list) {
|
||||
should.not.exist(err);
|
||||
should.exist(list);
|
||||
list.length.should.equal(1);
|
||||
list[0].address.should.equal(address);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should export & import', function() {
|
||||
var exported = clients[0].export();
|
||||
|
||||
importedClient = new Client({
|
||||
request: helpers.getRequest(app),
|
||||
});
|
||||
importedClient.import(exported);
|
||||
});
|
||||
it.skip('should export & import compressed', function() {
|
||||
var walletId = clients[0].credentials.walletId;
|
||||
var walletName = clients[0].credentials.walletName;
|
||||
var copayerName = clients[0].credentials.copayerName;
|
||||
|
||||
var exported = clients[0].export({
|
||||
compressed: true
|
||||
});
|
||||
|
||||
importedClient = new Client({
|
||||
request: helpers.getRequest(app),
|
||||
});
|
||||
importedClient.import(exported, {
|
||||
compressed: true
|
||||
});
|
||||
importedClient.credentials.walletId.should.equal(walletId);
|
||||
importedClient.credentials.walletName.should.equal(walletName);
|
||||
importedClient.credentials.copayerName.should.equal(copayerName);
|
||||
});
|
||||
it('should export & import encrypted', function() {
|
||||
var exported = clients[0].export({
|
||||
password: '123'
|
||||
});
|
||||
|
||||
importedClient = new Client({
|
||||
request: helpers.getRequest(app),
|
||||
});
|
||||
importedClient.import(exported, {
|
||||
password: '123'
|
||||
});
|
||||
});
|
||||
it('should export & import compressed & encrypted', function() {
|
||||
var exported = clients[0].export({
|
||||
compressed: true,
|
||||
password: '123'
|
||||
});
|
||||
|
||||
importedClient = new Client({
|
||||
request: helpers.getRequest(app),
|
||||
});
|
||||
importedClient.import(exported, {
|
||||
compressed: true,
|
||||
password: '123'
|
||||
});
|
||||
});
|
||||
it.skip('should fail to export compressed & import uncompressed', function() {});
|
||||
it.skip('should fail to export uncompressed & import compressed', function() {});
|
||||
it.skip('should fail to export unencrypted & import with password', function() {});
|
||||
it.skip('should fail to export encrypted & import with incorrect password', function() {});
|
||||
});
|
||||
|
||||
describe('Air gapped related flows', function() {
|
||||
|
|
Loading…
Reference in New Issue