encryption schema

This commit is contained in:
Matias Alejo Garcia 2015-02-23 20:11:07 -03:00
parent a266ec94e2
commit 531a794e96
4 changed files with 194 additions and 27 deletions

View File

@ -42,6 +42,7 @@ Utils.getClient = function(args) {
storage: storage,
baseUrl: args.host || process.env['BIT_HOST'],
verbose: args.verbose
password: args.password,
});
}

View File

@ -118,6 +118,8 @@ function API(opts) {
}
};
API.prototype._tryToCompleteFromServer = function(wcd, cb) {
if (!wcd.walletPrivKey)
@ -176,15 +178,65 @@ API.prototype._tryToComplete = function(opts, wcd, cb) {
};
// access: 'full' > 'readwrite' > readonly'
API.prototype._processWcdAfterRead = function(rawData, requiredAccess, cb) {
var WU = WalletUtils;
requiredAccess = requiredAccess || 'full';
API.prototype._load = function(cb) {
if (!rawData)
return cb(null, rawData);
var requiredAccessLevel = WU.accessNameToLevel(requiredAccess);
var access = WU.accessFromData(rawData);
var accessLevel = WU.accessNameToLevel(access);
// Is the data available?
if (requiredAccessLevel <= accessLevel)
return cb(null, rawData);
// Has any encrypted info?
if (!rawData.enc)
return cb('NOTAUTH');
// Decrypt it and try again
this.emit('needPassword', function(password) {
if (!password) return cb('No password');
rawData = WE.decryptWallet(rawData, password);
var access = WU.accessFromData(rawData);
// Is the data available?
if (requiredAccessLevel <= accessLevel)
return cb(null, rawData);
return cb('NOTAUTH');
});
};
API.prototype._processWcdBeforeWrite = function(wcd, accessWithoutEncrytion, cb) {
// Is any encrypted?
if (encryptedAccess) {
this.emit('needPassword', function(password) {
if (!password) return cb('No password');
rawdata = WE.encryptWallet(wcd, accessWithoutEncrytion, password);
return cb(null, rawdata);
});
} else {
return rawdata;
}
};
API.prototype._load = function(opts, cb) {
var self = this;
$.shouldBeFunction(cb);
this.storage.load(function(err, wcd) {
if (err || !wcd) {
this.storage.load(function(err, rawdata) {
if (err || !rawdata) {
return cb(err || 'wcd file not found.');
}
return cb(null, wcd);
self._processWcdAfterRead(rawdata, opts.requiredAccess, cb);
});
};
@ -197,7 +249,7 @@ API.prototype._load = function(cb) {
API.prototype._loadAndCheck = function(opts, cb) {
var self = this;
this._load(function(err, wcd) {
this._load(opts, function(err, wcd) {
if (err) return cb(err);
if (!wcd.n || (wcd.n > 1 && wcd.publicKeyRing.length != wcd.n)) {
@ -337,7 +389,9 @@ API.prototype.createWallet = function(walletName, copayerName, m, n, network, cb
API.prototype.reCreateWallet = function(walletName, cb) {
var self = this;
this._loadAndCheck({}, function(err, wcd) {
this._loadAndCheck({
requiredAccess: 'readonly',
}, function(err, wcd) {
if (err) return cb(err);
var walletPrivKey = new Bitcore.PrivateKey();
@ -393,7 +447,9 @@ API.prototype.joinWallet = function(secret, copayerName, cb) {
API.prototype.getStatus = function(cb) {
var self = this;
this._load(function(err, wcd) {
this._load({
requiredAccess: 'readonly'
}, function(err, wcd) {
if (err) return cb(err);
var url = '/v1/wallets/';
@ -418,7 +474,9 @@ API.prototype.sendTxProposal = function(opts, cb) {
var self = this;
this._loadAndCheck({}, function(err, wcd) {
this._loadAndCheck({
requiredAccess: 'readonly',
}, function(err, wcd) {
if (err) return cb(err);
if (!wcd.rwPrivKey)
@ -441,7 +499,9 @@ API.prototype.sendTxProposal = function(opts, cb) {
API.prototype.createAddress = function(cb) {
var self = this;
this._loadAndCheck({}, function(err, wcd) {
this._loadAndCheck({
requiredAccess: 'readwrite',
}, function(err, wcd) {
if (err) return cb(err);
var url = '/v1/addresses/';
@ -463,7 +523,9 @@ API.prototype.createAddress = function(cb) {
API.prototype.getMainAddresses = function(opts, cb) {
var self = this;
this._loadAndCheck({}, function(err, wcd) {
this._loadAndCheck({
requiredAccess: 'readonly',
}, function(err, wcd) {
if (err) return cb(err);
var url = '/v1/addresses/';
@ -489,7 +551,9 @@ API.prototype.history = function(limit, cb) {
API.prototype.getBalance = function(cb) {
var self = this;
this._loadAndCheck({}, function(err, wcd) {
this._loadAndCheck({
requiredAccess: 'readonly',
}, function(err, wcd) {
if (err) return cb(err);
var url = '/v1/balance/';
self._doGetRequest(url, wcd, cb);
@ -508,7 +572,9 @@ API.prototype.export = function(opts, cb) {
opts = opts || {};
var access = opts.access || 'full';
this._load(function(err, wcd) {
this._load({
requiredAccess: access,
}, function(err, wcd) {
if (err) return cb(err);
var v = [];
@ -587,6 +653,7 @@ API.prototype.parseTxProposals = function(txData, cb) {
var self = this;
this._loadAndCheck({
requiredAccess: 'readonly',
toComplete: txData.toComplete
}, function(err, wcd) {
if (err) return cb(err);
@ -617,7 +684,9 @@ API.prototype.parseTxProposals = function(txData, cb) {
API.prototype.getTxProposals = function(opts, cb) {
var self = this;
this._loadAndCheck({}, function(err, wcd) {
this._loadAndCheck({
requiredAccess: 'readonly'
}, function(err, wcd) {
if (err) return cb(err);
var url = '/v1/txproposals/';
self._doGetRequest(url, wcd, function(err, txps) {
@ -681,7 +750,9 @@ API.prototype.getSignatures = function(txp, cb) {
$.checkArgument(txp.creatorId);
var self = this;
this._loadAndCheck({}, function(err, wcd) {
this._loadAndCheck({
requiredAccess: 'full'
}, function(err, wcd) {
if (err) return cb(err);
if (!Verifier.checkTxProposal(wcd, txp)) {
@ -695,7 +766,9 @@ API.prototype.getSignatures = function(txp, cb) {
API.prototype.getEncryptedWalletData = function(cb) {
var self = this;
this._loadAndCheck({}, function(err, wcd) {
this._loadAndCheck({
requiredAccess: 'readonly'
}, function(err, wcd) {
if (err) return cb(err);
var toComplete = JSON.stringify(_.pick(wcd, WALLET_AIRGAPPED_TOCOMPLETE));
return cb(null, _encryptMessage(toComplete, WalletUtils.privateKeyToAESKey(wcd.roPrivKey)));
@ -709,7 +782,9 @@ API.prototype.signTxProposal = function(txp, cb) {
var self = this;
this._loadAndCheck({}, function(err, wcd) {
this._loadAndCheck({
requiredAccess: txp.signatures ? 'readwrite' : 'full'
}, function(err, wcd) {
if (err) return cb(err);
if (!Verifier.checkTxProposal(wcd, txp)) {
@ -732,7 +807,9 @@ API.prototype.rejectTxProposal = function(txp, reason, cb) {
var self = this;
this._loadAndCheck({},
this._loadAndCheck({
requiredAccess: 'readwrite'
},
function(err, wcd) {
if (err) return cb(err);
@ -747,7 +824,9 @@ API.prototype.rejectTxProposal = function(txp, reason, cb) {
API.prototype.broadcastTxProposal = function(txp, cb) {
var self = this;
this._loadAndCheck({},
this._loadAndCheck({
requiredAccess: 'readwrite'
},
function(err, wcd) {
if (err) return cb(err);
@ -760,7 +839,9 @@ API.prototype.broadcastTxProposal = function(txp, cb) {
API.prototype.removeTxProposal = function(txp, cb) {
var self = this;
this._loadAndCheck({},
this._loadAndCheck({
requiredAccess: 'readwrite'
},
function(err, wcd) {
if (err) return cb(err);
var url = '/v1/txproposals/' + txp.id;

View File

@ -43,6 +43,21 @@ WalletUtils.accessFromData = function(data) {
return 'readonly';
};
WalletUtils.accessNameToLevel = function(name) {
if (name === 'full')
return 30;
if (name === 'readwrite')
return 20;
if (name === 'readonly')
return 10;
throw new Error('Bad access name:' + name);
};
WalletUtils.isAccessEncrypted = function(name) {};
WalletUtils.verifyMessage = function(text, signature, pubKey) {
$.checkArgument(text);
$.checkArgument(pubKey);
@ -143,4 +158,39 @@ 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 = sjcl.decrypt(password, data.enc);
return _.extend(data, extraFields);
};
WalletUtils.sjclOpts = {
iter: 5000,
};
WalletUtils.encryptWallet = function(data, accessWithoutEncrytion, password) {
var toEncryptByLevel = {
readwrite: [],
readonly: [],
full: [],
};
var toEncrypt = whatToEncryptByLevel[accessWithoutEncrytion];
if (!_.every(toEncrypt, function(k) {
return data[k];
})) throw new Error('Wallet does not contain necesary info to encrypt');
var toEncrypt = _.pick(data, whatToEncrypt);
var enc = sjcl.encrypt(password, JSON.stringify(toEncrypt), WalletUtils.sjclOpts);
var ret = _.omit(data, toEncrypt);
ret.enc = enc;
console.log('[walletutils.js.191:ret:]',ret); //TODO
return ret;
};
module.exports = WalletUtils;

View File

@ -221,6 +221,23 @@ describe('client API ', function() {
});
describe.skip('Storage Encryption', function() {
it('should check balance in a 1-1 ', function(done) {
helpers.createAndJoinWallet(clients, 1, 1, function(err) {
should.not.exist(err);
clients[0].getBalance(function(err, x) {
should.not.exist(err);
var wcd = JSON.parse(fsmock._get('client0'));
console.log('[clientApi.js.236]', wcd); //TODO
done();
})
});
});
});
describe('Wallet Creation', function() {
it('should check balance in a 1-1 ', function(done) {
helpers.createAndJoinWallet(clients, 1, 1, function(err) {
@ -281,7 +298,7 @@ describe('client API ', function() {
should.not.exist(err);
// Get right response
clients[0]._load(function(err, data) {
clients[0]._load({}, function(err, data) {
var url = '/v1/wallets/';
clients[0]._doGetRequest(url, data, function(err, x) {
@ -305,7 +322,7 @@ describe('client API ', function() {
should.not.exist(err);
// Get right response
var data = clients[0]._load(function(err, data) {
var data = clients[0]._load({}, function(err, data) {
var url = '/v1/wallets/';
clients[0]._doGetRequest(url, data, function(err, x) {
@ -330,7 +347,7 @@ describe('client API ', function() {
should.not.exist(err);
// Get right response
var data = clients[0]._load(function(err, data) {
var data = clients[0]._load({}, function(err, data) {
var url = '/v1/wallets/';
clients[0]._doGetRequest(url, data, function(err, x) {
@ -362,6 +379,12 @@ describe('client API ', function() {
delete data.rwPrivKey;
fsmock._set('client0', JSON.stringify(data));
data.rwPrivKey = null;
// Overwrite client's API auth checks
clients[0]._processWcdAfterRead = function(rawData, xx, cb) {
return cb(null, rawData);
};
clients[0].createAddress(function(err, x0) {
err.code.should.equal('NOTAUTHORIZED');
done();
@ -378,6 +401,11 @@ describe('client API ', function() {
clients[1].import(str, function(err, wallet) {
should.not.exist(err);
// Overwrite client's API auth checks
clients[1]._processWcdAfterRead = function(rawData, xx, cb) {
return cb(null, rawData);
};
clients[1].createAddress(function(err, x0) {
err.code.should.equal('NOTAUTHORIZED');
clients[0].createAddress(function(err, x0) {
@ -425,7 +453,14 @@ describe('client API ', function() {
};
clients[1].sendTxProposal(opts, function(err, x) {
should.not.exist(err);
// Overwrite client's API auth checks
clients[1]._processWcdAfterRead = function(rawData, xx, cb) {
return cb(null, rawData);
};
clients[1].signTxProposal(x, function(err, tx) {
console.log('[clientApi.js.456:err:]',err); //TODO
err.code.should.be.equal('BADSIGNATURES');
clients[1].getTxProposals({}, function(err, txs) {
should.not.exist(err);
@ -656,7 +691,7 @@ describe('client API ', function() {
should.not.exist(err);
// Get right response
clients[0]._load(function(err, data) {
clients[0]._load({}, function(err, data) {
var url = '/v1/addresses/';
clients[0]._doPostRequest(url, {}, data, function(err, address) {
@ -680,7 +715,7 @@ describe('client API ', function() {
should.not.exist(err);
// Get right response
clients[0]._load(function(err, data) {
clients[0]._load({}, function(err, data) {
var url = '/v1/addresses/';
clients[0]._doPostRequest(url, {}, data, function(err, address) {
@ -865,7 +900,7 @@ describe('client API ', function() {
// Get right response
clients[0]._load(function(err, data) {
clients[0]._load({}, function(err, data) {
var url = '/v1/txproposals/';
clients[0]._doGetRequest(url, data, function(err, txps) {
@ -904,7 +939,7 @@ describe('client API ', function() {
// Get right response
clients[0]._load(function(err, data) {
clients[0]._load({}, function(err, data) {
var url = '/v1/txproposals/';
clients[0]._doGetRequest(url, data, function(err, txps) {
@ -943,7 +978,7 @@ describe('client API ', function() {
// Get right response
clients[0]._load(function(err, data) {
clients[0]._load({}, function(err, data) {
var url = '/v1/txproposals/';
clients[0]._doGetRequest(url, data, function(err, txps) {
// Tamper data