Add version param, remove auth param

This commit is contained in:
Mo Bitar
2017-08-05 11:26:34 -05:00
parent 5ec0b236d5
commit 1639d4a31f
7 changed files with 51 additions and 68 deletions

View File

@@ -37,7 +37,10 @@ angular.module('app.frontend')
} }
this.getAuthParams = function() { this.getAuthParams = function() {
return JSON.parse(localStorage.getItem("auth_params")); if(!this._authParams) {
this._authParams = JSON.parse(localStorage.getItem("auth_params"));
}
return this._authParams;
} }
this.keys = function() { this.keys = function() {
@@ -49,7 +52,12 @@ angular.module('app.frontend')
return keys; return keys;
} }
this.encryptionVersion = function() { this.protocolVersion = function() {
var version = this.getAuthParams().version;
if(version) {
return version;
}
var keys = this.keys(); var keys = this.keys();
if(keys && keys.ak) { if(keys && keys.ak) {
return "002"; return "002";
@@ -58,6 +66,17 @@ angular.module('app.frontend')
} }
} }
this.costMinimumForVersion = function(version) {
// all current versions have a min of 3000
// future versions will increase this
return 3000;
}
this.isProtocolVersionSupported = function(version) {
var supportedVersions = ["001", "002"];
return supportedVersions.includes(version);
}
this.getAuthParamsForEmail = function(url, email, callback) { this.getAuthParamsForEmail = function(url, email, callback) {
var requestUrl = url + "/auth/params"; var requestUrl = url + "/auth/params";
httpManager.getAbsolute(requestUrl, {email: email}, function(response){ httpManager.getAbsolute(requestUrl, {email: email}, function(response){
@@ -82,8 +101,15 @@ angular.module('app.frontend')
this.login = function(url, email, password, callback) { this.login = function(url, email, password, callback) {
this.getAuthParamsForEmail(url, email, function(authParams){ this.getAuthParamsForEmail(url, email, function(authParams){
if(!authParams.pw_cost) {
callback({error : {message: "Unable to get authentication parameters."}}); if(!authParams || !authParams.pw_cost) {
callback({error : {message: "Invalid email or password."}});
return;
}
if(!this.isProtocolVersionSupported(authParams.version)) {
alert("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.");
callback({didDisplayAlert: true});
return; return;
} }
@@ -96,41 +122,20 @@ angular.module('app.frontend')
return; return;
} }
var minimum = this.costMinimumForVersion(authParams.version);
if(authParams.pw_cost < minimum) {
alert("Unable to login due to insecure password parameters. Please visit standardnotes.org/help/password-upgrade for more information.");
callback({didDisplayAlert: true});
return;
}
Neeto.crypto.computeEncryptionKeysForUser(_.merge({password: password}, authParams), function(keys){ Neeto.crypto.computeEncryptionKeysForUser(_.merge({password: password}, authParams), function(keys){
var uploadVTagOnCompletion = false;
var localVTag = Neeto.crypto.calculateVerificationTag(authParams.pw_cost, authParams.pw_salt, keys.ak);
if(authParams.pw_auth) {
// verify auth params
if(localVTag !== authParams.pw_auth) {
alert("Invalid server verification tag; aborting login. This is most likely caused by an incorrect password. Learn more at standardnotes.org/verification.");
$timeout(function(){
callback({error: true, didDisplayAlert: true});
})
return;
} else {
console.log("Verification tag success.");
}
} else {
// either user has not uploaded pw_auth, or server is attempting to bypass authentication
if(confirm("Unable to locate verification tag for server. If this is your first time seeing this message and your account was created before July 2017, press OK to upload verification tag. If your account was created after July 2017, or if you've already seen this message, press cancel to abort login. Learn more at standardnotes.org/verification.")) {
// upload verification tag on completion
uploadVTagOnCompletion = true;
} else {
return;
}
}
var requestUrl = url + "/auth/sign_in"; var requestUrl = url + "/auth/sign_in";
var params = {password: keys.pw, email: email}; var params = {password: keys.pw, email: email};
httpManager.postAbsolute(requestUrl, params, function(response){ httpManager.postAbsolute(requestUrl, params, function(response){
this.handleAuthResponse(response, email, url, authParams, keys); this.handleAuthResponse(response, email, url, authParams, keys);
callback(response); callback(response);
if(uploadVTagOnCompletion) {
this.uploadVerificationTag(localVTag, authParams);
}
}.bind(this), function(response){ }.bind(this), function(response){
console.error("Error logging in", response); console.error("Error logging in", response);
callback(response); callback(response);
@@ -140,19 +145,6 @@ angular.module('app.frontend')
}.bind(this)) }.bind(this))
} }
this.uploadVerificationTag = function(tag, authParams) {
var requestUrl = localStorage.getItem("server") + "/auth/update";
var params = {pw_auth: tag};
httpManager.postAbsolute(requestUrl, params, function(response){
_.merge(authParams, params);
localStorage.setItem("auth_params", JSON.stringify(authParams));
alert("Your verification tag was successfully uploaded.");
}.bind(this), function(response){
alert("There was an error uploading your verification tag.");
})
}
this.handleAuthResponse = function(response, email, url, authParams, keys) { this.handleAuthResponse = function(response, email, url, authParams, keys) {
try { try {
if(url) { if(url) {
@@ -211,5 +203,9 @@ angular.module('app.frontend')
return JSON.parse(JSON.stringify(object)); return JSON.parse(JSON.stringify(object));
} }
this.signOut = function() {
this._authParams = null;
}
} }
}); });

View File

@@ -165,6 +165,7 @@ class AccountMenu {
return; return;
} }
authManager.signOut();
syncManager.destroyLocalData(function(){ syncManager.destroyLocalData(function(){
window.location.reload(); window.location.reload();
}) })
@@ -345,7 +346,7 @@ class AccountMenu {
$scope.itemsData = function(keys) { $scope.itemsData = function(keys) {
var items = _.map(modelManager.allItems, function(item){ var items = _.map(modelManager.allItems, function(item){
var itemParams = new ItemParams(item, keys, authManager.encryptionVersion()); var itemParams = new ItemParams(item, keys, authManager.protocolVersion());
return itemParams.paramsForExportFile(); return itemParams.paramsForExportFile();
}.bind(this)); }.bind(this));
@@ -393,8 +394,8 @@ class AccountMenu {
// 002 Update // 002 Update
$scope.securityUpdateAvailable = function() { $scope.securityUpdateAvailable = function() {
// whether user needs to upload pw_auth var keys = authManager.keys()
return !authManager.getAuthParams().pw_auth; return keys && !keys.ak;
} }
$scope.clickedSecurityUpdate = function() { $scope.clickedSecurityUpdate = function() {
@@ -417,8 +418,6 @@ class AccountMenu {
return; return;
} }
var tag = Neeto.crypto.calculateVerificationTag(authParams.pw_cost, authParams.pw_salt, keys.ak);
authManager.uploadVerificationTag(tag, authParams);
authManager.saveKeys(keys); authManager.saveKeys(keys);
}); });
} }

View File

@@ -319,7 +319,7 @@ class ExtensionManager {
if(!this.extensionUsesEncryptedData(extension)) { if(!this.extensionUsesEncryptedData(extension)) {
keys = null; keys = null;
} }
var itemParams = new ItemParams(item, keys, this.authManager.encryptionVersion()); var itemParams = new ItemParams(item, keys, this.authManager.protocolVersion());
return itemParams.paramsForExtension(); return itemParams.paramsForExtension();
} }

View File

@@ -82,10 +82,6 @@ class SNCrypto {
return CryptoJS.SHA256(text).toString(); return CryptoJS.SHA256(text).toString();
} }
sha1(text) {
return CryptoJS.SHA1(text).toString();
}
hmac256(message, key) { hmac256(message, key) {
var keyData = CryptoJS.enc.Hex.parse(key); var keyData = CryptoJS.enc.Hex.parse(key);
var messageData = CryptoJS.enc.Utf8.parse(message); var messageData = CryptoJS.enc.Utf8.parse(message);
@@ -99,18 +95,12 @@ class SNCrypto {
}.bind(this)); }.bind(this));
} }
calculateVerificationTag(cost, salt, ak) {
return Neeto.crypto.hmac256([cost, salt].join(":"), ak);
}
generateInitialEncryptionKeysForUser({email, password} = {}, callback) { generateInitialEncryptionKeysForUser({email, password} = {}, callback) {
var pw_cost = this.defaultPasswordGenerationCost(); var pw_cost = this.defaultPasswordGenerationCost();
var pw_nonce = this.generateRandomKey(512); var pw_nonce = this.generateRandomKey(512);
var pw_salt = this.sha1([email, pw_nonce].join(":")); var pw_salt = this.sha256([email, pw_nonce].join(":"));
this.generateSymmetricKeyPair({email: email, password: password, pw_salt: pw_salt, pw_cost: pw_cost}, function(keys){ this.generateSymmetricKeyPair({email: email, password: password, pw_salt: pw_salt, pw_cost: pw_cost}, function(keys){
var ak = keys[2]; callback({pw: keys[0], mk: keys[1], ak: keys[2]}, {pw_salt: pw_salt, pw_cost: pw_cost, version: "002"});
var pw_auth = this.calculateVerificationTag(pw_cost, pw_salt, ak);
callback({pw: keys[0], mk: keys[1], ak: ak}, {pw_auth: pw_auth, pw_salt: pw_salt, pw_cost: pw_cost});
}.bind(this)); }.bind(this));
} }
} }

View File

@@ -6,7 +6,7 @@ class SNCryptoWeb extends SNCrypto {
Overrides Overrides
*/ */
defaultPasswordGenerationCost() { defaultPasswordGenerationCost() {
return 10000; return 61000;
} }
/** Generates two deterministic keys based on one input */ /** Generates two deterministic keys based on one input */

View File

@@ -24,7 +24,7 @@ class SyncManager {
writeItemsToLocalStorage(items, offlineOnly, callback) { writeItemsToLocalStorage(items, offlineOnly, callback) {
var params = items.map(function(item) { var params = items.map(function(item) {
var itemParams = new ItemParams(item, null, this.authManager.encryptionVersion()); var itemParams = new ItemParams(item, null, this.authManager.protocolVersion());
itemParams = itemParams.paramsForLocalStorage(); itemParams = itemParams.paramsForLocalStorage();
if(offlineOnly) { if(offlineOnly) {
delete itemParams.dirty; delete itemParams.dirty;
@@ -189,7 +189,7 @@ class SyncManager {
var params = {}; var params = {};
params.limit = 150; params.limit = 150;
params.items = _.map(subItems, function(item){ params.items = _.map(subItems, function(item){
var itemParams = new ItemParams(item, this.authManager.keys(), this.authManager.encryptionVersion()); var itemParams = new ItemParams(item, this.authManager.keys(), this.authManager.protocolVersion());
itemParams.additionalFields = options.additionalFields; itemParams.additionalFields = options.additionalFields;
return itemParams.paramsForSync(); return itemParams.paramsForSync();
}.bind(this)); }.bind(this));

View File

@@ -97,9 +97,7 @@
%a.block.mt-5{"ng-click" => "clickedSecurityUpdate()"} Security Update Available %a.block.mt-5{"ng-click" => "clickedSecurityUpdate()"} Security Update Available
%section.gray-bg.mt-10.medium-padding{"ng-if" => "securityUpdateData.showForm"} %section.gray-bg.mt-10.medium-padding{"ng-if" => "securityUpdateData.showForm"}
%p %p
A new security feature is available that adds an additional level of verification to your sign-ins. %a{"href" => "https://standardnotes.org/help/security-update", "target" => "_blank"} Learn more.
This feature assures that when you login, the server cannot tamper or modify your authentication parameters.
%a{"href" => "https://standardnotes.org/verification", "target" => "_blank"} Learn more.
%div.mt-10{"ng-if" => "!securityUpdateData.processing"} %div.mt-10{"ng-if" => "!securityUpdateData.processing"}
%p.bold Enter your password to update: %p.bold Enter your password to update:
%form.mt-5 %form.mt-5