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...
-
-
@@ -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({