Wip
This commit is contained in:
@@ -296,7 +296,7 @@ class AccountMenu {
|
||||
}.bind(this)
|
||||
|
||||
if(data.auth_params) {
|
||||
SFJS.crypto.computeEncryptionKeysForUser(_.merge({password: password}, data.auth_params), function(keys){
|
||||
SFJS.crypto.computeEncryptionKeysForUser(password, data.auth_params, (keys) => {
|
||||
try {
|
||||
SFItemTransformer.decryptMultipleItems(data.items, keys, false); /* throws = false as we don't want to interrupt all decryption if just one fails */
|
||||
// delete items enc_item_key since the user's actually key will do the encrypting once its passed off
|
||||
@@ -323,7 +323,7 @@ class AccountMenu {
|
||||
callback(null);
|
||||
return;
|
||||
}
|
||||
}.bind(this));
|
||||
});
|
||||
} else {
|
||||
onDataReady();
|
||||
}
|
||||
@@ -468,30 +468,6 @@ class AccountMenu {
|
||||
return keys && !keys.ak;
|
||||
}
|
||||
|
||||
$scope.clickedSecurityUpdate = function() {
|
||||
if(!$scope.securityUpdateData) {
|
||||
$scope.securityUpdateData = {};
|
||||
}
|
||||
$scope.securityUpdateData.showForm = true;
|
||||
}
|
||||
|
||||
$scope.submitSecurityUpdateForm = function() {
|
||||
$scope.securityUpdateData.processing = true;
|
||||
var authParams = authManager.getAuthParams();
|
||||
|
||||
SFJS.crypto.computeEncryptionKeysForUser(_.merge({password: $scope.securityUpdateData.password}, authParams), function(keys){
|
||||
if(keys.mk !== authManager.keys().mk) {
|
||||
alert("Invalid password. Please try again.");
|
||||
$timeout(function(){
|
||||
$scope.securityUpdateData.processing = false;
|
||||
})
|
||||
return;
|
||||
}
|
||||
|
||||
authManager.saveKeys(keys);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Encryption Status
|
||||
|
||||
@@ -3,7 +3,7 @@ class ItemParams {
|
||||
constructor(item, keys, version) {
|
||||
this.item = item;
|
||||
this.keys = keys;
|
||||
this.version = version || "002";
|
||||
this.version = version || SFJS.crypto.version();
|
||||
}
|
||||
|
||||
paramsForExportFile(includeDeleted) {
|
||||
@@ -36,7 +36,7 @@ class ItemParams {
|
||||
|
||||
var params = {uuid: this.item.uuid, content_type: this.item.content_type, deleted: this.item.deleted, created_at: this.item.created_at};
|
||||
if(!this.item.errorDecrypting) {
|
||||
// Items should always be encrypted for export files. Only respect item.doNotEncrypt for remote sync params;
|
||||
// Items should always be encrypted for export files. Only respect item.doNotEncrypt for remote sync params.
|
||||
var doNotEncrypt = this.item.doNotEncrypt() && !this.forExportFile;
|
||||
if(this.keys && !doNotEncrypt) {
|
||||
var encryptedParams = SFItemTransformer.encryptItem(this.item, this.keys, this.version);
|
||||
|
||||
@@ -8,7 +8,7 @@ angular.module('app')
|
||||
}
|
||||
|
||||
this.$get = function($rootScope, $timeout, httpManager, modelManager, dbManager, storageManager, singletonManager) {
|
||||
return new AuthManager($rootScope, $timeout, httpManager, modelManager, dbManager, storageManager, singletonManager);
|
||||
return new AuthManager($rootScope, $timeout, httpManager, modelManager, dbManager, storageManager, singletonManager);
|
||||
}
|
||||
|
||||
function AuthManager($rootScope, $timeout, httpManager, modelManager, dbManager, storageManager, singletonManager) {
|
||||
@@ -77,7 +77,7 @@ angular.module('app')
|
||||
|
||||
var keys = this.keys();
|
||||
if(keys && keys.ak) {
|
||||
return "002";
|
||||
return "3";
|
||||
} else {
|
||||
return "001";
|
||||
}
|
||||
@@ -86,14 +86,25 @@ angular.module('app')
|
||||
this.costMinimumForVersion = function(version) {
|
||||
// all current versions have a min of 3000
|
||||
// future versions will increase this
|
||||
return 3000;
|
||||
return SFJS.crypto.costMinimumForVersion(version);
|
||||
}
|
||||
|
||||
this.isProtocolVersionSupported = function(version) {
|
||||
var supportedVersions = ["001", "002"];
|
||||
var supportedVersions = ["001", "002", "3"];
|
||||
return supportedVersions.includes(version);
|
||||
}
|
||||
|
||||
/* Upon sign in to an outdated version, the user will be presented with an alert requiring them to confirm
|
||||
understanding they are signing in with an older version of the protocol, and must upgrade immediately after completing sign in.
|
||||
*/
|
||||
this.isProtocolVersionOutdated = function(version) {
|
||||
return ["001"].includes(version);
|
||||
}
|
||||
|
||||
this.supportsPasswordDerivationCost = function(cost) {
|
||||
return SFJS.crypto.supportsPasswordDerivationCost(cost);
|
||||
}
|
||||
|
||||
this.getAuthParamsForEmail = function(url, email, extraParams, callback) {
|
||||
var requestUrl = url + "/auth/params";
|
||||
httpManager.getAbsolute(requestUrl, _.merge({email: email}, extraParams), function(response){
|
||||
@@ -107,17 +118,12 @@ angular.module('app')
|
||||
})
|
||||
}
|
||||
|
||||
this.supportsPasswordDerivationCost = function(cost) {
|
||||
// some passwords are created on platforms with stronger pbkdf2 capabilities, like iOS,
|
||||
// which accidentally used 60,000 iterations (now adjusted), which CryptoJS can't handle here (WebCrypto can however).
|
||||
// if user has high password cost and is using browser that doesn't support WebCrypto,
|
||||
// we want to tell them that they can't login with this browser.
|
||||
return SFJS.crypto.supportsPasswordDerivationCost(cost);
|
||||
}
|
||||
|
||||
this.login = function(url, email, password, ephemeral, extraParams, callback) {
|
||||
this.getAuthParamsForEmail(url, email, extraParams, function(authParams){
|
||||
|
||||
// SF3 requires a unique identifier in the auth params
|
||||
authParams.identifier = email;
|
||||
|
||||
if(authParams.error) {
|
||||
callback(authParams);
|
||||
return;
|
||||
@@ -129,11 +135,18 @@ angular.module('app')
|
||||
}
|
||||
|
||||
if(!this.isProtocolVersionSupported(authParams.version)) {
|
||||
let message = "The protocol version associated with your account is outdated and no longer supported by this application. Please visit standardnotes.org/help/security-update for more information.";
|
||||
let message = "The protocol version associated with your account is outdated and no longer supported by this application. Please visit standardnotes.org/help/security for more information.";
|
||||
callback({error: {message: message}});
|
||||
return;
|
||||
}
|
||||
|
||||
if(this.isProtocolVersionOutdated(authParams.version)) {
|
||||
let message = `The encryption version for your account, ${authParams.version}, is outdated. You may proceed with login, but are advised to follow prompts for Security Updates once inside. Please visit standardnotes.org/help/security for more information.`
|
||||
if(!confirm(message)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(!this.supportsPasswordDerivationCost(authParams.pw_cost)) {
|
||||
let message = "Your account was created on a platform with higher security capabilities than this browser supports. " +
|
||||
"If we attempted to generate your login keys here, it would take hours. " +
|
||||
@@ -144,13 +157,13 @@ angular.module('app')
|
||||
|
||||
var minimum = this.costMinimumForVersion(authParams.version);
|
||||
if(authParams.pw_cost < minimum) {
|
||||
let message = "Unable to login due to insecure password parameters. Please visit standardnotes.org/help/password-upgrade for more information.";
|
||||
let message = "Unable to login due to insecure password parameters. Please visit standardnotes.org/help/security for more information.";
|
||||
callback({error: {message: message}});
|
||||
return;
|
||||
}
|
||||
|
||||
SFJS.crypto.computeEncryptionKeysForUser(_.merge({password: password}, authParams), function(keys){
|
||||
|
||||
SFJS.crypto.computeEncryptionKeysForUser(password, authParams, function(keys){
|
||||
console.log("Signing in with params", authParams, keys);
|
||||
var requestUrl = url + "/auth/sign_in";
|
||||
var params = _.merge({password: keys.pw, email: email}, extraParams);
|
||||
httpManager.postAbsolute(requestUrl, params, function(response){
|
||||
@@ -191,40 +204,40 @@ angular.module('app')
|
||||
|
||||
this.saveKeys = function(keys) {
|
||||
this._keys = keys;
|
||||
// Doesn't need to be saved.
|
||||
// pw doesn't need to be saved.
|
||||
// storageManager.setItem("pw", keys.pw);
|
||||
storageManager.setItem("mk", keys.mk);
|
||||
storageManager.setItem("ak", keys.ak);
|
||||
}
|
||||
|
||||
this.register = function(url, email, password, ephemeral, callback) {
|
||||
SFJS.crypto.generateInitialEncryptionKeysForUser({password: password, email: email}, function(keys, authParams){
|
||||
SFJS.crypto.generateInitialEncryptionKeysForUser(email, password, (keys, authParams) => {
|
||||
var requestUrl = url + "/auth";
|
||||
var params = _.merge({password: keys.pw, email: email}, authParams);
|
||||
|
||||
httpManager.postAbsolute(requestUrl, params, function(response){
|
||||
httpManager.postAbsolute(requestUrl, params, (response) => {
|
||||
this.setEphemeral(ephemeral);
|
||||
this.handleAuthResponse(response, email, url, authParams, keys);
|
||||
callback(response);
|
||||
}.bind(this), function(response){
|
||||
}, (response) => {
|
||||
console.error("Registration error", response);
|
||||
if(typeof response !== 'object') {
|
||||
response = {error: {message: "A server error occurred while trying to register. Please try again."}};
|
||||
}
|
||||
callback(response);
|
||||
}.bind(this))
|
||||
}.bind(this));
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
this.changePassword = function(email, new_password, callback) {
|
||||
SFJS.crypto.generateInitialEncryptionKeysForUser({password: new_password, email: email}, function(keys, authParams){
|
||||
this.changePassword = function(email, current_password, new_password, callback) {
|
||||
SFJS.crypto.generateInitialEncryptionKeysForUser(email, new_password, (keys, authParams) => {
|
||||
var requestUrl = storageManager.getItem("server") + "/auth/change_pw";
|
||||
var params = _.merge({new_password: keys.pw}, authParams);
|
||||
var params = _.merge({current_password: current_password, new_password: keys.pw}, authParams);
|
||||
|
||||
httpManager.postAbsolute(requestUrl, params, function(response) {
|
||||
httpManager.postAbsolute(requestUrl, params, (response) => {
|
||||
this.handleAuthResponse(response, email, null, authParams, keys);
|
||||
callback(response);
|
||||
}.bind(this), function(response){
|
||||
}, (response) => {
|
||||
var error = response;
|
||||
if(!error) {
|
||||
error = {message: "Something went wrong while changing your password. Your password was not changed. Please try again."}
|
||||
@@ -232,7 +245,7 @@ angular.module('app')
|
||||
console.error("Change pw error", response);
|
||||
callback({error: error});
|
||||
})
|
||||
}.bind(this))
|
||||
})
|
||||
}
|
||||
|
||||
this.updateAuthParams = function(authParams, callback) {
|
||||
@@ -258,20 +271,10 @@ angular.module('app')
|
||||
return;
|
||||
}
|
||||
|
||||
if(this.protocolVersion() === "001") {
|
||||
if(this.keys().ak) {
|
||||
// upgrade to 002
|
||||
var authParams = this.getAuthParams();
|
||||
authParams.version = "002";
|
||||
this.updateAuthParams(authParams, function(response){
|
||||
if(!response.error) {
|
||||
// let rest of UI load first
|
||||
$timeout(function(){
|
||||
alert("Your encryption version has been updated. To take full advantage of this update, please resync all your items by clicking Account -> Advanced -> Re-encrypt All Items.")
|
||||
}, 750);
|
||||
}
|
||||
});
|
||||
}
|
||||
let latest = SFJS.crypto.version();
|
||||
|
||||
if(this.protocolVersion() !== latest) {
|
||||
// Prompt user to perform security update
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,10 @@ class DesktopManager {
|
||||
return this.applicationDataPath;
|
||||
}
|
||||
|
||||
/* Sending a component in its raw state is really slow for the desktop app */
|
||||
/*
|
||||
Sending a component in its raw state is really slow for the desktop app
|
||||
Keys are not passed into ItemParams, so the result is not encrypted
|
||||
*/
|
||||
convertComponentForTransmission(component) {
|
||||
return new ItemParams(component).paramsForExportFile(true);
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ angular.module('app')
|
||||
|
||||
this.unlock = function(passcode, callback) {
|
||||
var params = this.passcodeAuthParams();
|
||||
SFJS.crypto.computeEncryptionKeysForUser(_.merge({password: passcode}, params), function(keys){
|
||||
SFJS.crypto.computeEncryptionKeysForUser(passcode, params, (keys) => {
|
||||
if(keys.pw !== params.hash) {
|
||||
callback(false);
|
||||
return;
|
||||
@@ -38,26 +38,24 @@ angular.module('app')
|
||||
this.decryptLocalStorage(keys);
|
||||
this._locked = false;
|
||||
callback(true);
|
||||
}.bind(this));
|
||||
});
|
||||
}
|
||||
|
||||
this.setPasscode = (passcode, callback) => {
|
||||
var cost = SFJS.crypto.defaultPasswordGenerationCost();
|
||||
var salt = SFJS.crypto.generateRandomKey(512);
|
||||
var defaultParams = {pw_cost: cost, pw_salt: salt, version: "002"};
|
||||
var uuid = SFJS.crypto.generateUUID();
|
||||
|
||||
SFJS.crypto.computeEncryptionKeysForUser(_.merge({password: passcode}, defaultParams), function(keys) {
|
||||
defaultParams.hash = keys.pw;
|
||||
SFJS.crypto.generateInitialEncryptionKeysForUser(uuid, passcode, (keys, authParams) => {
|
||||
authParams.hash = keys.pw;
|
||||
this._keys = keys;
|
||||
this._hasPasscode = true;
|
||||
|
||||
// Encrypting will initially clear localStorage
|
||||
this.encryptLocalStorage(keys);
|
||||
this.encryptLocalStorage(keys, authParams);
|
||||
|
||||
// After it's cleared, it's safe to write to it
|
||||
storageManager.setItem("offlineParams", JSON.stringify(defaultParams), StorageManager.Fixed);
|
||||
storageManager.setItem("offlineParams", JSON.stringify(authParams), StorageManager.Fixed);
|
||||
callback(true);
|
||||
}.bind(this));
|
||||
});
|
||||
}
|
||||
|
||||
this.changePasscode = (newPasscode, callback) => {
|
||||
@@ -71,7 +69,7 @@ angular.module('app')
|
||||
this._hasPasscode = false;
|
||||
}
|
||||
|
||||
this.encryptLocalStorage = function(keys) {
|
||||
this.encryptLocalStorage = function(keys, authParams) {
|
||||
storageManager.setKeys(keys);
|
||||
// Switch to Ephemeral storage, wiping Fixed storage
|
||||
// Last argument is `force`, which we set to true because in the case of changing passcode
|
||||
|
||||
@@ -142,8 +142,9 @@ class StorageManager {
|
||||
return hash;
|
||||
}
|
||||
|
||||
setKeys(keys) {
|
||||
setKeys(keys, authParams) {
|
||||
this.encryptedStorageKeys = keys;
|
||||
this.encryptedStorageAuthParams = authParams;
|
||||
}
|
||||
|
||||
writeEncryptedStorageToDisk() {
|
||||
@@ -152,7 +153,7 @@ class StorageManager {
|
||||
encryptedStorage.storage = this.storageAsHash();
|
||||
|
||||
// Save new encrypted storage in Fixed storage
|
||||
var params = new ItemParams(encryptedStorage, this.encryptedStorageKeys);
|
||||
var params = new ItemParams(encryptedStorage, this.encryptedStorageKeys, this.encryptedStorageAuthParams.version);
|
||||
this.setItem("encryptedStorage", JSON.stringify(params.paramsForSync()), StorageManager.Fixed);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user