Better handling for errorDecrypting

This commit is contained in:
Mo Bitar
2018-01-03 00:56:31 -06:00
parent fdbd2638a4
commit d8b1559d84
7 changed files with 59 additions and 18 deletions

View File

@@ -31,20 +31,27 @@ class ItemParams {
console.assert(!this.item.dummy, "Item is dummy, should not have gotten here.", this.item.dummy) console.assert(!this.item.dummy, "Item is dummy, should not have gotten here.", this.item.dummy)
var params = {uuid: this.item.uuid, content_type: this.item.content_type, deleted: this.item.deleted, created_at: this.item.created_at}; var params = {uuid: this.item.uuid, content_type: this.item.content_type, deleted: this.item.deleted, created_at: this.item.created_at};
if(this.keys && !this.item.doNotEncrypt()) { if(!this.item.errorDecrypting) {
var encryptedParams = EncryptionHelper.encryptItem(this.item, this.keys, this.version); if(this.keys && !this.item.doNotEncrypt()) {
_.merge(params, encryptedParams); var encryptedParams = EncryptionHelper.encryptItem(this.item, this.keys, this.version);
_.merge(params, encryptedParams);
if(this.version !== "001") { if(this.version !== "001") {
params.auth_hash = null; params.auth_hash = null;
}
} }
} else {
else { params.content = this.forExportFile ? this.item.createContentJSONFromProperties() : "000" + Neeto.crypto.base64(JSON.stringify(this.item.createContentJSONFromProperties()));
params.content = this.forExportFile ? this.item.createContentJSONFromProperties() : "000" + Neeto.crypto.base64(JSON.stringify(this.item.createContentJSONFromProperties())); if(!this.forExportFile) {
if(!this.forExportFile) { params.enc_item_key = null;
params.enc_item_key = null; params.auth_hash = null;
params.auth_hash = null; }
} }
} else {
// Error decrypting, keep "content" and related fields as is (and do not try to encrypt, otherwise that would be undefined behavior)
params.content = this.item.content;
params.enc_item_key = this.item.enc_item_key;
params.auth_hash = this.item.auth_hash;
} }
if(this.additionalFields) { if(this.additionalFields) {

View File

@@ -197,7 +197,8 @@ angular.module('app.frontend')
this.saveKeys = function(keys) { this.saveKeys = function(keys) {
this._keys = keys; this._keys = keys;
storageManager.setItem("pw", keys.pw); // Doesn't need to be saved.
// storageManager.setItem("pw", keys.pw);
storageManager.setItem("mk", keys.mk); storageManager.setItem("mk", keys.mk);
storageManager.setItem("ak", keys.ak); storageManager.setItem("ak", keys.ak);
} }

View File

@@ -149,6 +149,7 @@ class AccountMenu {
var block = function() { var block = function() {
$timeout(function(){ $timeout(function(){
$scope.onSuccessfulAuth()(); $scope.onSuccessfulAuth()();
syncManager.refreshErroredItems();
syncManager.sync(); syncManager.sync();
}) })
} }

View File

@@ -92,6 +92,7 @@ class EncryptionHelper {
// return if uuid in auth hash does not match item uuid. Signs of tampering. // return if uuid in auth hash does not match item uuid. Signs of tampering.
if(keyParams.uuid && keyParams.uuid !== item.uuid) { if(keyParams.uuid && keyParams.uuid !== item.uuid) {
if(!item.errorDecrypting) { item.errorDecryptingValueChanged = true;}
item.errorDecrypting = true; item.errorDecrypting = true;
return; return;
} }
@@ -99,6 +100,7 @@ class EncryptionHelper {
var item_key = Neeto.crypto.decryptText(keyParams, requiresAuth); var item_key = Neeto.crypto.decryptText(keyParams, requiresAuth);
if(!item_key) { if(!item_key) {
if(!item.errorDecrypting) { item.errorDecryptingValueChanged = true;}
item.errorDecrypting = true; item.errorDecrypting = true;
return; return;
} }
@@ -110,6 +112,7 @@ class EncryptionHelper {
// return if uuid in auth hash does not match item uuid. Signs of tampering. // return if uuid in auth hash does not match item uuid. Signs of tampering.
if(itemParams.uuid && itemParams.uuid !== item.uuid) { if(itemParams.uuid && itemParams.uuid !== item.uuid) {
if(!item.errorDecrypting) { item.errorDecryptingValueChanged = true;}
item.errorDecrypting = true; item.errorDecrypting = true;
return; return;
} }
@@ -121,11 +124,14 @@ class EncryptionHelper {
var content = Neeto.crypto.decryptText(itemParams, true); var content = Neeto.crypto.decryptText(itemParams, true);
if(!content) { if(!content) {
if(!item.errorDecrypting) { item.errorDecryptingValueChanged = true;}
item.errorDecrypting = true; item.errorDecrypting = true;
} else { } else {
if(item.errorDecrypting == true) { item.errorDecryptingValueChanged = true;}
// Content should only be set if it was successfully decrypted, and should otherwise remain unchanged.
item.errorDecrypting = false; item.errorDecrypting = false;
item.content = content;
} }
item.content = content;
} }
static decryptMultipleItems(items, keys, throws) { static decryptMultipleItems(items, keys, throws) {
@@ -139,6 +145,7 @@ class EncryptionHelper {
try { try {
this.decryptItem(item, keys); this.decryptItem(item, keys);
} catch (e) { } catch (e) {
if(!item.errorDecrypting) { item.errorDecryptingValueChanged = true;}
item.errorDecrypting = true; item.errorDecrypting = true;
if(throws) { if(throws) {
throw e; throw e;

View File

@@ -118,7 +118,14 @@ class ModelManager {
continue; continue;
} }
json_obj = _.omit(json_obj, omitFields || []) // Lodash's _.omit, which was previously used, seems to cause unexpected behavior
// when json_obj is an ES6 item class. So we instead manually omit each key.
if(Array.isArray(omitFields)) {
for(var key of omitFields) {
delete json_obj[key];
}
}
var item = this.findItem(json_obj.uuid); var item = this.findItem(json_obj.uuid);
if(item) { if(item) {

View File

@@ -77,7 +77,7 @@ class SyncManager {
markAllItemsDirtyAndSaveOffline(callback, alternateUUIDs) { markAllItemsDirtyAndSaveOffline(callback, alternateUUIDs) {
// use a copy, as alternating uuid will affect array // use a copy, as alternating uuid will affect array
var originalItems = this.modelManager.allItems.slice(); var originalItems = this.modelManager.allItems.filter((item) => {return !item.errorDecrypting}).slice();
var block = () => { var block = () => {
var allItems = this.modelManager.allItems; var allItems = this.modelManager.allItems;
@@ -359,9 +359,30 @@ class SyncManager {
var keys = this.authManager.keys() || this.passcodeManager.keys(); var keys = this.authManager.keys() || this.passcodeManager.keys();
EncryptionHelper.decryptMultipleItems(responseItems, keys); EncryptionHelper.decryptMultipleItems(responseItems, keys);
var items = this.modelManager.mapResponseItemsToLocalModelsOmittingFields(responseItems, omitFields, source); var items = this.modelManager.mapResponseItemsToLocalModelsOmittingFields(responseItems, omitFields, source);
// During the decryption process, items may be marked as "errorDecrypting". If so, we want to be sure
// to persist this new state by writing these items back to local storage. When an item's "errorDecrypting"
// flag is changed, its "errorDecryptingValueChanged" flag will be set, so we can find these items by filtering (then unsetting) below:
var itemsWithErrorStatusChange = items.filter((item) => {
var valueChanged = item.errorDecryptingValueChanged;
// unset after consuming value
item.errorDecryptingValueChanged = false;
return valueChanged;
});
if(itemsWithErrorStatusChange.length > 0) {
this.writeItemsToLocalStorage(itemsWithErrorStatusChange, false, null);
}
return items; return items;
} }
refreshErroredItems() {
var erroredItems = this.modelManager.allItems.filter((item) => {return item.errorDecrypting == true});
if(erroredItems.length > 0) {
this.handleItemsResponse(erroredItems, null, ModelManager.MappingSourceLocalRetrieved);
}
}
handleUnsavedItemsResponse(unsaved) { handleUnsavedItemsResponse(unsaved) {
if(unsaved.length == 0) { if(unsaved.length == 0) {
return; return;

View File

@@ -89,9 +89,6 @@
%label.block %label.block
Encryption key: Encryption key:
.wrap.normal.mt-1.selectable {{encryptionKey()}} .wrap.normal.mt-1.selectable {{encryptionKey()}}
%label.block.mt-5.mb-0
Server password:
.wrap.normal.mt-1.selectable {{serverPassword() ? serverPassword() : 'Not available. Sign out then sign back in to compute.'}}
%label.block.mt-5.mb-0 %label.block.mt-5.mb-0
Authentication key: Authentication key:
.wrap.normal.mt-1.selectable {{authKey() ? authKey() : 'Not available. Sign out then sign back in to compute.'}} .wrap.normal.mt-1.selectable {{authKey() ? authKey() : 'Not available. Sign out then sign back in to compute.'}}