diff --git a/js/app/controllers/share_settings.js b/js/app/controllers/share_settings.js index 1425bff5..e4bd3c67 100644 --- a/js/app/controllers/share_settings.js +++ b/js/app/controllers/share_settings.js @@ -4,33 +4,30 @@ angular.module('passmanApp') .controller('SharingSettingsCtrl', ['$scope', 'VaultService', 'CredentialService', 'SettingsService', '$location', '$routeParams', 'ShareService', 'EncryptService', function ($scope, VaultService, CredentialService, SettingsService, $location, $routeParams, ShareService, EncryptService) { - $scope.active_vault = VaultService.getActiveVault(); + $scope.vault = VaultService.getActiveVault(); + $scope.sharing_keys = ShareService.getSharingKeys(); + $scope.progress = 1; $scope.generating = false; - $scope.sharing_keys = ShareService.getSharingKeys(); - $scope.generateKeys = function (length) { $scope.progress = 1; $scope.generating = true; - ShareService.generateRSAKeys(length, function(progress){ + ShareService.generateRSAKeys(length).progress(function(progress){ $scope.progress = progress > 0 ? 2:1; $scope.$apply(); - console.log($scope.progress); - }, function(kp){ + }).then(function(kp){ + console.log('stuff done'); $scope.generating = false; var pem = ShareService.rsaKeyPairToPEM(kp) - $scope.active_vault.private_sharing_key = pem.privateKey; - $scope.active_vault.public_sharing_key = pem.publicKey; + $scope.vault.private_sharing_key = EncryptService.encryptString(pem.privateKey); + $scope.vault.public_sharing_key = pem.publicKey; - - var _vault = angular.copy($scope.active_vault); - _vault.private_sharing_key = EncryptService.encryptString(_vault.private_sharing_key); - VaultService.updateSharingKeys(_vault).then(function (result) { - console.log('done') + VaultService.updateSharingKeys($scope.vault).then(function (result) { + $scope.sharing_keys = ShareService.getSharingKeys(); }) }); } diff --git a/js/app/services/shareservice.js b/js/app/services/shareservice.js index f8e246ef..a9a26817 100644 --- a/js/app/services/shareservice.js +++ b/js/app/services/shareservice.js @@ -9,6 +9,11 @@ */ angular.module('passmanApp') .service('ShareService', ['$http', 'VaultService', 'EncryptService', function ($http, VaultService, EncryptService) { + // Setup sjcl random engine to max paranoia level and start collecting data + var paranoia_level = 10 + sjcl.random.setDefaultParanoia(paranoia_level); + sjcl.random.startCollectors(); + return { search: function (string) { var queryUrl = OC.generateUrl('apps/passman/api/v2/sharing/search'); @@ -21,24 +26,43 @@ angular.module('passmanApp') }); }, generateRSAKeys: function(key_length, progress, callback){ - var state = forge.pki.rsa.createKeyPairGenerationState(key_length, 0x10001); - var step = function() { - // run for 100 ms - if(!forge.pki.rsa.stepKeyPairGenerationState(state, 100)) { - // console.log(state); - if (state.p !== null) { - progress(50); + var p = new C_Promise(function(promise){ + var state = forge.pki.rsa.createKeyPairGenerationState(key_length, 0x10001); + var step = function() { + // run for 100 ms + if(!forge.pki.rsa.stepKeyPairGenerationState(state, 100)) { + // console.log(state); + if (state.p !== null) { + // progress(50); + promise.call_progress(50); + } + else { + // progress(0); + promise.call_progress(50); + } + setTimeout(step, 1); } else { - progress(0); + // callback(state.keys); + promise.call_then(state.keys); } - setTimeout(step, 1); - } - else { - callback(state.keys); - } - }; - setTimeout(step, 100); + }; + setTimeout(step, 100); + }); + return p; + }, + generateSharedKey: function(size){ + size = size || 20; + return new C_Promise(function(promise){ /** prmise C_Promise **/ + CRYPTO.PASSWORD.generate(size, + function(pass) { + promise.call_then(pass); + }, + function(progress) { + promise.call_progress(progress); + } + ); + }) }, rsaKeyPairToPEM: function(keypair){ return { diff --git a/js/lib/crypto_wrap.js b/js/lib/crypto_wrap.js new file mode 100644 index 00000000..0364bb46 --- /dev/null +++ b/js/lib/crypto_wrap.js @@ -0,0 +1,129 @@ +/** + * ownCloud/NextCloud - passman + * + * This file is licensed under the Affero General Public License version 3 or + * later. See the COPYING file. + * + * @author Marcos Zuriaga + * @copyright Marcos Zuriarga 2015 + */ +var CRYPTO = { // Global variables of the object: + paranoia_level: null, + + PASSWORD : { + /** + * Callback will be called once the password its generated, it should accept one parameter, and the parameter will be the key ( + * CRYPTO.PASSWORD.generate(100, function(password){ + * console.log("The generated password is: " + password); + * // Do more stuff here + * }, function (current_percentage){ + * console.log("The current password generation progress it's: " + current_percentage + "%"); + * // Do real stuff here, update a progressbar, etc. + * } + * ); + * ) + * @param length The minium length of the generated password (it generates in packs of 4 characters, + * so it can end up being up to 3 characters longer) + * @param callback The function to be called after the password generation its done + * @param progress The process of the generation, optional, called each 4 characters generated. + */ + generate : function (length, callback, progress, start_string) { + if (!sjcl.random.isReady(paranoia_level)) { + setTimeout(this.generate(length, callback, progress, start_string), 500); + return; + } + + if (start_string == null) start_string = ""; + if (start_string.length < length) { + start_string += CRYPTO.RANDOM.getRandomASCII(); + if (progress != null) progress(start_string.length / length * 100); + } + else { + callback(start_string); + if (progress != null) progress(100); + return; + } + + setTimeout(this.generate(length, callback, progress, start_string), 100); + }, + + logRepeatedCharCount: function (str) { + var chars = []; + + for (i = 0; i < str.length; i++) { + chars[str.charAt(i)] = (chars[str.charAt(i)] == null) ? 0 : chars[str.charAt(i)] + 1; + } + return chars; + }, + }, + + RANDOM: { + /** + * Returns a random string of 4 characters length + */ + getRandomASCII : function () { + // console.warn(paranoia_level); + + var ret = ""; + while (ret.length < 4) { + var int = sjcl.random.randomWords(1, paranoia_level); + int = int[0]; + + var tmp = this._isASCII((int & 0xFF000000) >> 24); + if (tmp) ret += tmp; + + tmp = this._isASCII((int & 0x00FF0000) >> 16); + if (tmp) ret += tmp; + + tmp = this._isASCII((int & 0x0000FF00) >> 8); + if (tmp) ret += tmp; + + tmp = this._isASCII(int & 0x000000FF); + if (tmp) ret += tmp; + } + + return ret; + }, + + /** + * Checks whether the given data it's an ascii character, returning the corresponding character; returns false otherwise + * + * @param data + * @returns {string} + * @private + */ + _isASCII : function (data) { + return (data > 31 && data < 127) ? String.fromCharCode(data) : false; + } + }, + + /** + * Initializes the random and other cryptographic engines needed for this library to work + * The default paranoia, in case no paranoia level it's provided, it's 10 (1024). + * The higher paranoia level allowed by sjcl. + * + * PARANOIA_LEVELS: + * 0 = 0 + * 1 = 48 + * 2 = 64 + * 3 = 96 + * 4 = 128 + * 5 = 192 + * 6 = 256 + * 7 = 384 + * 8 = 512 + * 9 = 768 + * 10 = 1024 + * + * @param default_paranoia (0-10 integer) + */ + initEngines : function (default_paranoia) { + paranoia_level = default_paranoia || 10; + + sjcl.random.setDefaultParanoia(this.paranoia_level); + sjcl.random.startCollectors(); + + console.warn('Crypto stuff initialized'); + } +}; +CRYPTO.initEngines(); diff --git a/js/lib/promise.js b/js/lib/promise.js new file mode 100644 index 00000000..af3910d0 --- /dev/null +++ b/js/lib/promise.js @@ -0,0 +1,34 @@ +/** + * ownCloud/NextCloud - passman + * + * This file is licensed under the Affero General Public License version 3 or + * later. See the COPYING file. + * + * @author Marcos Zuriaga + * @copyright Marcos Zuriarga 2016 + */ + +function C_Promise(workload){ + this.then = function(callback){ + this.finally = callback; + return this; + } + this.progress = function(callback){ + this.update = callback; + return this; + } + this.error = function (callback){ + this.error_function = callback; + return this; + } + this.call_then = function(data){ + if (this.finally !== undefined) this.finally(data); + } + this.call_progress = function(data){ + if (this.progress !== undefined) this.progress(data); + } + this.call_error = function(data){ + if(this.error_function !== undefined) this.error_function(data); + } + setTimeout(workload(this), 100); +} \ No newline at end of file diff --git a/templates/main.php b/templates/main.php index 1b80af11..70bb5c9d 100644 --- a/templates/main.php +++ b/templates/main.php @@ -22,6 +22,8 @@ script('passman', 'vendor/angular-xeditable/xeditable.min'); script('passman', 'vendor/sha/sha'); script('passman', 'vendor/llqrcode/llqrcode'); script('passman', 'vendor/forge.0.6.9.min'); +script('passman', 'lib/promise'); +script('passman', 'lib/crypto_wrap'); script('passman', 'app/app');