diff --git a/config.template.js b/config.template.js index 793c7d940..b297a068b 100644 --- a/config.template.js +++ b/config.template.js @@ -42,7 +42,9 @@ var config = { port: 3001 }, verbose: 1, - themes: ['default'] + themes: ['default'], + iterations: 1000, + storageSalt: 'mjuBtGybi/4=', // choose your own salt (base64) }; var log = function () { diff --git a/copay.js b/copay.js index d68d1cfb1..f35ba8732 100644 --- a/copay.js +++ b/copay.js @@ -3,6 +3,8 @@ module.exports.PublicKeyRing = require('./js/models/core/PublicKeyRing'); module.exports.TxProposals = require('./js/models/core/TxProposals'); module.exports.PrivateKey = require('./js/models/core/PrivateKey'); +module.exports.Passphrase = require('./js/models/core/Passphrase'); + // components var WebRTC = module.exports.WebRTC = require('./js/models/network/WebRTC'); @@ -13,7 +15,7 @@ var StorageLocalEncrypted = module.exports.StorageLocalEncrypted = require('./js var WalletFactory = require('soop').load('./js/models/core/WalletFactory',{ Network: WebRTC, Blockchain: Insight, - Storage: StorageLocalPlain, + Storage: StorageLocalEncrypted, }); module.exports.WalletFactory = WalletFactory; diff --git a/css/main.css b/css/main.css index dea360626..6108e8a29 100644 --- a/css/main.css +++ b/css/main.css @@ -356,15 +356,15 @@ hr { margin: 2.25rem 0;} .box-setup-copayers:after { border-color: rgba(255, 255, 255, 0); border-bottom-color: #ffffff; - border-width: 30px; - margin-left: -30px; + border-width: 20px; + margin-left: -20px; } .box-setup-copayers:before { border-color: rgba(238, 238, 238, 0); border-bottom-color: #eee; - border-width: 33px; - margin-left: -33px; + border-width: 23px; + margin-left: -23px; } .box-setup-copayers-fix { diff --git a/index.html b/index.html index 6ccfdd6fa..972943ea6 100644 --- a/index.html +++ b/index.html @@ -142,34 +142,31 @@ Looking for peers...
-
@@ -182,8 +179,6 @@ Import from file
- - @@ -191,7 +186,6 @@ - + + + @@ -551,6 +545,7 @@ + diff --git a/js/app.js b/js/app.js index aa9cdfde1..db15e356c 100644 --- a/js/app.js +++ b/js/app.js @@ -20,7 +20,8 @@ var copayApp = window.copayApp = angular.module('copay',[ 'copay.setup', 'copay.directives', 'copay.video', - 'copay.import' + 'copay.import', + 'copay.passphrase' ]); angular.module('copay.header', []); @@ -37,4 +38,5 @@ angular.module('copay.socket', []); angular.module('copay.directives', []); angular.module('copay.video', []); angular.module('copay.import', []); +angular.module('copay.passphrase', []); diff --git a/js/controllers/setup.js b/js/controllers/setup.js index b3028832d..1da4404e4 100644 --- a/js/controllers/setup.js +++ b/js/controllers/setup.js @@ -1,9 +1,10 @@ 'use strict'; angular.module('copay.setup').controller('SetupController', - function($scope, $rootScope, $location, walletFactory, controllerUtils) { + function($scope, $rootScope, $location, walletFactory, controllerUtils, Passphrase) { $scope.loading = false; + $scope.walletPassword = $rootScope.walletPassword; // ng-repeat defined number of times instead of repeating over array? $scope.getNumber = function(num) { @@ -31,15 +32,18 @@ angular.module('copay.setup').controller('SetupController', updateRCSelect(tc); }); - $scope.create = function(totalCopayers, requiredCopayers, walletName, myNickname) { + $scope.create = function() { $scope.loading = true; + + var passphrase = Passphrase.getBase64($scope.walletPassword); + var opts = { - requiredCopayers: requiredCopayers, - totalCopayers: totalCopayers, - name: walletName, - nickname: myNickname, + requiredCopayers: $scope.requiredCopayers, + totalCopayers: $scope.totalCopayers, + name: $scope.walletName, + nickname: $scope.myNickname, + passphrase: passphrase, }; -console.log('[setup.js.31:opts:]',opts); //TODO var w = walletFactory.create(opts); controllerUtils.startNetwork(w); }; diff --git a/js/controllers/signin.js b/js/controllers/signin.js index fb09d19f7..06584f8f6 100644 --- a/js/controllers/signin.js +++ b/js/controllers/signin.js @@ -1,43 +1,54 @@ 'use strict'; angular.module('copay.signin').controller('SigninController', - function($scope, $rootScope, $location, walletFactory, controllerUtils) { + function($scope, $rootScope, $location, walletFactory, controllerUtils, Passphrase) { $scope.loading = false; $scope.wallets = walletFactory.getWallets(); $scope.selectedWalletId = $scope.wallets.length ? $scope.wallets[0].id : null; + $scope.openPassword = ''; - $scope.create = function(walletName) { + $scope.create = function() { $scope.loading = true; - $rootScope.walletName = walletName; + + $rootScope.walletName = $scope.walletName; + $rootScope.walletPassword = $scope.createPassword; $location.path('setup'); }; - $scope.open = function(walletId, opts) { - $scope.loading = true; - var w = walletFactory.open(walletId, opts); - controllerUtils.startNetwork(w); + $scope.open = function() { + if ($scope.openPassword != '') { + $scope.loading = true; + + var passphrase = Passphrase.getBase64($scope.openPassword); + var w = walletFactory.open($scope.selectedWalletId, { passphrase: passphrase}); + controllerUtils.startNetwork(w); + } }; - $scope.join = function(secret, nickname ) { + $scope.join = function() { $scope.loading = true; walletFactory.network.on('badSecret', function() { }); - walletFactory.joinCreateSession(secret, nickname, function(err,w) { + walletFactory.joinCreateSession($scope.connectionId, $scope.nickname, function(err,w) { $scope.loading = false; - if (err || !w) { + if (err || !w || !$scope.joinPassword) { if (err === 'joinError') $rootScope.flashMessage = { message: 'Can not find peer'}; else if (err === 'badSecret') $rootScope.flashMessage = { message: 'Bad secret secret string', type: 'error'}; + else if (!$scope.joinPassword) + $rootScope.flashMessage = { message: 'Enter your wallet password', type: 'error' }; else $rootScope.flashMessage = { message: 'Unknown error', type: 'error'}; controllerUtils.onErrorDigest(); - } - else + } else { + var passphrase = Passphrase.getBase64($scope.joinPassword); + w.storage._setPassphrase(passphrase); controllerUtils.startNetwork(w); + } }); }; }); diff --git a/js/models/core/Passphrase.js b/js/models/core/Passphrase.js new file mode 100644 index 000000000..57f765cd1 --- /dev/null +++ b/js/models/core/Passphrase.js @@ -0,0 +1,24 @@ +'use strict'; + +function Passphrase(config) { + config = config || {}; + this.salt = config.storageSalt; + this.iterations = config.iterations || 1000; +}; + +Passphrase.prototype.get = function(password) { + var hash = CryptoJS.SHA256(CryptoJS.SHA256(password)); + var salt = CryptoJS.enc.Base64.parse(this.salt); + var key512 = CryptoJS.PBKDF2(hash, salt, { keySize: 512/32, iterations: this.iterations }); + + return key512; +}; + +Passphrase.prototype.getBase64 = function(password) { + var key512 = this.get(password); + var keyBase64 = key512.toString(CryptoJS.enc.Base64); + + return keyBase64; +}; + +module.exports = Passphrase; diff --git a/js/models/core/WalletFactory.js b/js/models/core/WalletFactory.js index 0e704da06..a76446372 100644 --- a/js/models/core/WalletFactory.js +++ b/js/models/core/WalletFactory.js @@ -86,7 +86,6 @@ WalletFactory.prototype.read = function(walletId) { }; WalletFactory.prototype.create = function(opts) { - var s = WalletFactory.storage; opts = opts || {}; this.log('### CREATING NEW WALLET.' + (opts.id ? ' USING ID: ' + opts.id : ' NEW ID') + @@ -112,6 +111,8 @@ WalletFactory.prototype.create = function(opts) { }); this.log('\t### TxProposals Initialized'); + this.storage._setPassphrase(opts.passphrase); + opts.storage = this.storage; opts.network = this.network; opts.blockchain = this.blockchain; @@ -126,12 +127,15 @@ WalletFactory.prototype.create = function(opts) { }; WalletFactory.prototype.open = function(walletId, opts) { - this.log('Opening walletId:' + walletId); opts = opts || {}; opts.id = walletId; opts.verbose = this.verbose; + + this.storage._setPassphrase(opts.passphrase); + var w = this.read(walletId) || this.create(opts); w.store(); + return w; }; diff --git a/js/models/storage/LocalEncrypted.js b/js/models/storage/LocalEncrypted.js index 56922a1f2..470c9f39f 100644 --- a/js/models/storage/LocalEncrypted.js +++ b/js/models/storage/LocalEncrypted.js @@ -1,8 +1,6 @@ 'use strict'; var imports = require('soop').imports(); -//var buffertools = imports.buffertools || require('buffertools'); -var parent = imports.parent || require('./LocalPlain'); var id = 0; function Storage(opts) { @@ -13,8 +11,6 @@ function Storage(opts) { if (opts.password) this._setPassphrase(opts.password); } -Storage.parent = parent; - var pps = {}; Storage.prototype._getPassphrase = function() { @@ -58,38 +54,105 @@ Storage.prototype._read = function(k) { console.log('Error while decrypting: '+e); throw e; }; + return ret; }; Storage.prototype._write = function(k,v) { v = JSON.stringify(v); v = this._encrypt(v); + localStorage.setItem(k, v); }; +// get value by key +Storage.prototype.getGlobal = function(k) { + return localStorage.getItem(k); +}; + +// set value for key +Storage.prototype.setGlobal = function(k,v) { + localStorage.setItem(k, JSON.stringify(v)); +}; + +// remove value for key +Storage.prototype.removeGlobal = function(k) { + localStorage.removeItem(k); +}; + +Storage.prototype._key = function(walletId, k) { + return walletId + '::' + k; +}; +// get value by key +Storage.prototype.get = function(walletId, k) { + var ret = this._read(this._key(walletId,k)); + + return ret; +}; + +// set value for key +Storage.prototype.set = function(walletId, k,v) { + this._write(this._key(walletId,k), v); +}; + +// remove value for key +Storage.prototype.remove = function(walletId, k) { + this.removeGlobal(this._key(walletId,k)); +}; + +Storage.prototype.setName = function(walletId, name) { + this.setGlobal('nameFor::'+walletId, name); +}; + +Storage.prototype.getName = function(walletId) { + return this.getGlobal('nameFor::'+walletId); +}; + +Storage.prototype.getWalletIds = function() { + var walletIds = []; + var uniq = {}; + for (var i = 0; i < localStorage.length; i++) { + var key = localStorage.key(i); + var split = key.split('::'); + if (split.length == 2) { + var walletId = split[0]; + + if (walletId === 'nameFor') continue; + + if (typeof uniq[walletId] === 'undefined' ) { + walletIds.push(walletId); + uniq[walletId] = 1; + } + } + } + return walletIds; +}; + +Storage.prototype.getWallets = function() { + var wallets = []; + var uniq = {}; + var ids = this.getWalletIds(); + + for (var i in ids){ + wallets.push({ + id:ids[i], + name: this.getName(ids[i]), + }); + } + return wallets; +}; + +//obj contains keys to be set Storage.prototype.setFromObj = function(walletId, obj) { - for (var i in keys) { - var key = keys[0]; - obj[key] = this.get(walletId, key); + for (var k in obj) { + this.set(walletId, k, obj[k]); } + this.setName(walletId, obj.opts.name); }; -Storage.prototype.setFromEncryptedObj = function(walletId, base64) { - -}; - -Storage.prototype.getEncryptedObj = function(walletId) { - var keys = this._getWalletKeys(); - var obj = {}; - for (var i in keys) { - var key = keys[0]; - obj[key] = this.get(walletId, key); - } - - var str = JSON.stringify(obj); - var base64 = this._encrypt(str).toString(); - - return base64; +// remove all values +Storage.prototype.clearAll = function() { + localStorage.clear(); }; module.exports = require('soop')(Storage); diff --git a/js/services/passphrase.js b/js/services/passphrase.js new file mode 100644 index 000000000..2ea188869 --- /dev/null +++ b/js/services/passphrase.js @@ -0,0 +1,7 @@ +'use strict'; + +var passphrase; +angular.module('copay.passphrase').factory('Passphrase', function($rootScope) { + passphrase = passphrase || new copay.Passphrase(config); + return passphrase; +}); diff --git a/test/mocks/FakeStorage.js b/test/mocks/FakeStorage.js index 4e12e25fb..77fe38213 100644 --- a/test/mocks/FakeStorage.js +++ b/test/mocks/FakeStorage.js @@ -3,6 +3,10 @@ var FakeStorage = function(){ this.storage = {}; }; +FakeStorage.prototype._setPassphrase = function (password) { + this.storage.passphrase = password; +}; + FakeStorage.prototype.setGlobal = function (id, payload) { this.storage[id] = payload; }; diff --git a/test/test.Walletfactory.js b/test/test.Walletfactory.js index 4aa5398dd..a3feeba50 100644 --- a/test/test.Walletfactory.js +++ b/test/test.Walletfactory.js @@ -32,6 +32,7 @@ describe('WalletFactory model', function() { port: 80 }, networkName: 'testnet', + passphrase: 'test', }; it('should create the factory', function() { diff --git a/util/build.js b/util/build.js index 5cd7a3d7a..fd587da26 100755 --- a/util/build.js +++ b/util/build.js @@ -68,7 +68,9 @@ var createBundle = function(opts) { b.require('./js/models/core/PublicKeyRing', { expose: '../js/models/core/PublicKeyRing' }); - + b.require('./js/models/core/Passphrase', { + expose: '../js/models/core/Passphrase' + }); if (!opts.dontminify) { b.transform({