From 8183a1d3c754bc12d9977d251674440d3272e568 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Mon, 27 Oct 2014 17:23:01 -0300 Subject: [PATCH] fixes #importWallet --- js/models/Identity.js | 29 +++++++++++++++++++++-------- js/util/crypto.js | 32 ++++++++++++++++++-------------- test/Identity.js | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 22 deletions(-) diff --git a/js/models/Identity.js b/js/models/Identity.js index 11a6fce5e..ce2fa7e01 100644 --- a/js/models/Identity.js +++ b/js/models/Identity.js @@ -5,6 +5,7 @@ var _ = require('lodash'); var bitcore = require('bitcore'); var log = require('../log'); var async = require('async'); +var cryptoUtil = require('../util/crypto'); var version = require('../../version').version; var TxProposals = require('./TxProposals'); @@ -252,6 +253,8 @@ Identity.prototype.exportWithWalletInfo = function() { */ Identity.prototype.store = function(opts, cb) { var self = this; + opts = opts || {}; + self.storage.setItem(this.getId(), this.toObj(), function(err) { if (err) return cb(err); @@ -279,24 +282,34 @@ Identity.prototype.close = function(cb) { * @desc Imports a wallet from an encrypted base64 object * @param {string} base64 - the base64 encoded object * @param {string} passphrase - passphrase to decrypt it - * @param {string[]} skipFields - fields to ignore when importing + * @param {string[]} opts.skipFields - fields to ignore when importing + * @param {string[]} opts.salt - + * @param {string[]} opts.iterations - + * @param {string[]} opts.importFunction - for stubbing * @return {Wallet} */ -Identity.prototype.importWallet = function(base64, password, skipFields, cb) { +Identity.prototype.importWallet = function(base64, password, opts, cb) { var self = this; preconditions.checkArgument(password); preconditions.checkArgument(cb); - - var obj = this.storage.decrypt(base64, password); + var importFunction = opts.importWallet || Wallet.fromUntrustedObj; + var crypto = opts.cryptoUtil || cryptoUtil; var readOpts = { networkOpts: this.networkOpts, blockchainOpts: this.blockchainOpts, - skipFields: skipFields + skipFields: opts.skipFields, }; - if (!obj) return cb(null); - var w = Identity._walletFromObj(obj, readOpts); + // TODO set iter and salt using config.js + var key = crypto.kdf(password); + var obj = crypto.decrypt(key, base64); + if (!obj) return cb(new Error('Could not decrypt')); + + + var w = importFunction(obj, readOpts); + if (!w) return cb(new Error('Could not decrypt')); + this._checkVersion(w.version); this.addWallet(w, function(err) { if (err) return cb(err, null); @@ -338,7 +351,7 @@ Identity.importFromFullJson = function(str, password, opts, cb) { json.wallets = json.wallets || {}; async.map(json.wallets, function(walletData, callback) { - iden.importWallet(wstr, password, opts.skipFields, function(err, w) { + iden.importWallet(wstr, password, opts, function(err, w) { if (err) return callback(err); log.debug('Wallet ' + w.getId() + ' imported'); callback(); diff --git a/js/util/crypto.js b/js/util/crypto.js index 96289268b..3b5b774dd 100644 --- a/js/util/crypto.js +++ b/js/util/crypto.js @@ -5,21 +5,25 @@ var sjcl = require('../../lib/sjcl'); var log = require('../log.js'); var _ = require('lodash'); -var SALT = 'copay random string NWRlNmExMTE4NzIzYzYyYWMwODU1MTdkN'; -var SEPARATOR = '&'; -var defaultOptions = { - adata: '', - cipher: 'aes', - ks: 128, - iter: 2000, - mode: 'ccm', - ts: 64 -}; +var defaultSalt = 'mjuBtGybi/4='; +var defaultIterations = 100; + +// var SEPARATOR = '&'; +// var defaultOptions = { +// adata: '', +// cipher: 'aes', +// ks: 128, +// iter: 2000, +// mode: 'ccm', +// ts: 64 +// }; module.exports = { - kdf: function(value1, value2) { - return sjcl.codec.base64.fromBits(sjcl.misc.pbkdf2(value1 + value2, SALT)); + kdf: function(value1, value2, salt, iterations) { + iterations = iterations || defaultIterations; + salt = salt || defaultSalt; + return sjcl.codec.base64.fromBits(sjcl.misc.pbkdf2(value1 + (value2 || ''), salt, iterations)); }, /** @@ -35,10 +39,10 @@ module.exports = { /** * Decrypts symmetrically using a passphrase */ - decrypt: function(key, cypher) { + decrypt: function(key, cyphertext) { var output = {}; try { - return sjcl.decrypt(key, cypher); + return sjcl.decrypt(key, cyphertext); } catch (e) { log.error('Decryption failed due to error: ' + e.message); return null; diff --git a/test/Identity.js b/test/Identity.js index 72841d256..ceeea9450 100644 --- a/test/Identity.js +++ b/test/Identity.js @@ -245,6 +245,38 @@ describe('Identity model', function() { }); }); + describe('#importWallet', function() { + it('should import a wallet, call the right encryption functions',function(done) { + var args = createIdentity(); + args.storage.getItem.onFirstCall().callsArgWith(1, null, '{"wallet": "fakeData"}'); + var backup = Wallet.fromUntrustedObj; + args.params.noWallets = true; + sinon.stub().returns(args.wallet); + + var fakeCrypto = { + kdf: sinon.stub().returns('passphrase'), + decrypt: sinon.stub().returns({walletId:123}), + }; + + var opts = { + importWallet: sinon.stub().returns(getNewWallet()), + cryptoUtil: fakeCrypto, + }; + + Identity.create(args.params, function(err, iden) { + iden.importWallet(123,'password', opts, function(err){ + should.not.exist(err); + fakeCrypto.kdf.getCall(0).args[0].should.equal('password'); + fakeCrypto.decrypt.getCall(0).args[0].should.equal('passphrase'); + fakeCrypto.decrypt.getCall(0).args[1].should.equal(123); + done(); + }); + }); + }); + }); + + + describe('#export', function() { });