diff --git a/MacPass/Base.lproj/DatabaseSettingsWindow.xib b/MacPass/Base.lproj/DatabaseSettingsWindow.xib
index ea2f50e7..6df349ab 100644
--- a/MacPass/Base.lproj/DatabaseSettingsWindow.xib
+++ b/MacPass/Base.lproj/DatabaseSettingsWindow.xib
@@ -51,14 +51,14 @@
-
+
-
-
+
+
-
+
@@ -569,268 +569,288 @@ Gw
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -848,7 +868,7 @@ Gw
-
+
diff --git a/MacPass/MPAdvancedDatabaseSettingsViewController.m b/MacPass/MPAdvancedDatabaseSettingsViewController.m
index f98eb8bc..27090bdc 100644
--- a/MacPass/MPAdvancedDatabaseSettingsViewController.m
+++ b/MacPass/MPAdvancedDatabaseSettingsViewController.m
@@ -7,9 +7,45 @@
//
#import "MPAdvancedDatabaseSettingsViewController.h"
+#import "MPDocument.h"
+#import "MPDayCountFormatter.h"
+#import "KPKNode+IconImage.h"
+
+#import
+#import
@interface MPAdvancedDatabaseSettingsViewController ()
+@property (strong) IBOutlet NSButton *enableHistoryCheckButton;
+@property (strong) IBOutlet NSTextField *historyMaximumItemsTextField;
+@property (strong) IBOutlet NSStepper *historyMaximumItemsStepper;
+
+@property (strong) IBOutlet NSTextField *historyMaximumSizeTextField;
+@property (strong) IBOutlet NSStepper *historyMaximumSizeStepper;
+
+@property (strong) IBOutlet NSButton *enableTrashCheckButton;
+@property (strong) IBOutlet NSButton *emptyTrashOnQuitCheckButton;
+@property (strong) IBOutlet NSPopUpButton *selectTrashGoupPopUpButton;
+@property (strong) IBOutlet NSTextField *defaultUsernameTextField;
+@property (strong) IBOutlet NSPopUpButton *templateGroupPopUpButton;
+
+@property (strong) IBOutlet NSButton *recommendKeyChangeCheckButton;
+@property (strong) IBOutlet NSButton *enforceKeyChangeCheckButton;
+@property (strong) IBOutlet NSButton *enforceKeyChangeOnceCheckButton;
+@property (strong) IBOutlet NSTextField *recommendKeyChangeIntervalTextField;
+@property (strong) IBOutlet NSStepper *recommendKeyChangeIntervalStepper;
+@property (strong) IBOutlet NSTextField *enforceKeyChangeIntervalTextField;
+@property (strong) IBOutlet NSStepper *enforceKeyChangeIntervalStepper;
+
+@property (assign) BOOL enableHistory;
+@property (assign) NSInteger maxiumHistoryItems;
+@property (assign) NSInteger maxiumHistorySize;
+
+@property (assign) BOOL enforceKeyChange;
+@property (assign) BOOL recommendKeyChange;
+@property (assign) NSInteger enforceKeyChangeInterval;
+@property (assign) NSInteger recommendKeyChangeInterval;
+
@end
@implementation MPAdvancedDatabaseSettingsViewController
@@ -19,4 +55,140 @@
// Do view setup here.
}
+- (void)_setupAdvancedTab {
+ /* history */
+ MPDocument *document = (MPDocument*)self.view.window.windowController.document;
+ KPKTree *tree = document.tree;
+
+ self.enableHistory = tree.metaData.isHistoryEnabled;
+ [self.enableHistoryCheckButton bind:NSValueBinding
+ toObject:self
+ withKeyPath:NSStringFromSelector(@selector(enableHistory))
+ options:nil];
+
+ /* history size */
+ self.maxiumHistorySize = tree.metaData.historyMaxSize;
+ self.historyMaximumSizeStepper.minValue = 0;
+ self.historyMaximumSizeStepper.maxValue = NSIntegerMax;
+ self.historyMaximumSizeStepper.increment = 1024*1024; // 1MB
+ [self.historyMaximumSizeStepper bind:NSEnabledBinding toObject:self withKeyPath:NSStringFromSelector(@selector(enableHistory)) options:nil];
+ [self.historyMaximumSizeStepper bind:NSValueBinding toObject:self withKeyPath:NSStringFromSelector(@selector(maxiumHistorySize)) options:nil];
+ [self.historyMaximumSizeTextField bind:NSEnabledBinding toObject:self withKeyPath:NSStringFromSelector(@selector(enableHistory)) options:nil];
+ [self.historyMaximumSizeTextField bind:NSValueBinding toObject:self withKeyPath:NSStringFromSelector(@selector(maxiumHistorySize)) options:nil];
+
+ /* history count */
+ self.maxiumHistoryItems = MAX(0,tree.metaData.historyMaxItems); // prevent -1 form showing up directly
+ self.historyMaximumItemsStepper.minValue = 0;
+ self.historyMaximumItemsStepper.maxValue = NSIntegerMax;
+ self.historyMaximumItemsStepper.increment = 1;
+ [self.historyMaximumItemsStepper bind:NSEnabledBinding toObject:self withKeyPath:NSStringFromSelector(@selector(enableHistory)) options:nil];
+ [self.historyMaximumItemsStepper bind:NSValueBinding toObject:self withKeyPath:NSStringFromSelector(@selector(maxiumHistoryItems)) options:nil];
+ [self.historyMaximumItemsTextField bind:NSEnabledBinding toObject:self withKeyPath:NSStringFromSelector(@selector(enableHistory)) options:nil];
+ [self.historyMaximumItemsTextField bind:NSValueBinding toObject:self withKeyPath:NSStringFromSelector(@selector(maxiumHistoryItems)) options:nil];
+
+ /* trash */
+ HNHUISetStateFromBool(self.enableTrashCheckButton, tree.metaData.useTrash);
+ self.selectTrashGoupPopUpButton.enabled = tree.metaData.useTrash;
+ [self.enableTrashCheckButton bind:NSValueBinding toObject:self.selectTrashGoupPopUpButton withKeyPath:NSEnabledBinding options:nil];
+ [self _updateTrashFolders:tree];
+
+ /* default username */
+ self.defaultUsernameTextField.stringValue = tree.metaData.defaultUserName;
+ self.defaultUsernameTextField.editable = YES;
+ [self _updateTemplateGroup:tree];
+
+ /* key changes */
+ self.enforceKeyChange = tree.metaData.enforceMasterKeyChange;
+ self.recommendKeyChange = tree.metaData.recommendMasterKeyChange;
+ HNHUISetStateFromBool(self.enforceKeyChangeOnceCheckButton, tree.metaData.enforceMasterKeyChangeOnce);
+
+ [self.enforceKeyChangeCheckButton bind:NSValueBinding toObject:self withKeyPath:NSStringFromSelector(@selector(enforceKeyChange)) options:nil];
+ [self.recommendKeyChangeCheckButton bind:NSValueBinding toObject:self withKeyPath:NSStringFromSelector(@selector(recommendKeyChange)) options:nil];
+
+ /* intervals use -1 to encode disabled, do not show this in text fields! */
+ self.enforceKeyChangeInterval = MAX(0,tree.metaData.masterKeyChangeEnforcementInterval);
+ self.enforceKeyChangeIntervalStepper.minValue = 0;
+ self.enforceKeyChangeIntervalStepper.maxValue = NSIntegerMax;
+ self.enforceKeyChangeIntervalStepper.increment = 1; // 1 day steps
+ [self.enforceKeyChangeIntervalStepper bind:NSEnabledBinding toObject:self withKeyPath:NSStringFromSelector(@selector(enforceKeyChange)) options:nil];
+ [self.enforceKeyChangeIntervalStepper bind:NSValueBinding toObject:self withKeyPath:NSStringFromSelector(@selector(enforceKeyChangeInterval)) options:nil];
+ [self.enforceKeyChangeIntervalTextField bind:NSEnabledBinding toObject:self withKeyPath:NSStringFromSelector(@selector(enforceKeyChange)) options:nil];
+ [self.enforceKeyChangeIntervalTextField bind:NSValueBinding toObject:self withKeyPath:NSStringFromSelector(@selector(enforceKeyChangeInterval)) options:nil];
+
+ NSString *valueFormat = NSLocalizedString(@"EVERY_%ld_DAYS", @"Recommend/Enforce key change intervall format");
+
+ ((MPDayCountFormatter *)self.enforceKeyChangeIntervalTextField.formatter).valueFormat = valueFormat;
+
+ self.recommendKeyChangeInterval = MAX(0,tree.metaData.masterKeyChangeRecommendationInterval);
+ self.recommendKeyChangeIntervalStepper.minValue = 0;
+ self.recommendKeyChangeIntervalStepper.maxValue = NSIntegerMax;
+ self.recommendKeyChangeIntervalStepper.increment = 1; // 1 day steps
+ [self.recommendKeyChangeIntervalStepper bind:NSEnabledBinding toObject:self withKeyPath:NSStringFromSelector(@selector(recommendKeyChange)) options:nil];
+ [self.recommendKeyChangeIntervalStepper bind:NSValueBinding toObject:self withKeyPath:NSStringFromSelector(@selector(recommendKeyChangeInterval)) options:nil];
+ [self.recommendKeyChangeIntervalTextField bind:NSEnabledBinding toObject:self withKeyPath:NSStringFromSelector(@selector(recommendKeyChange)) options:nil];
+ [self.recommendKeyChangeIntervalTextField bind:NSValueBinding toObject:self withKeyPath:NSStringFromSelector(@selector(recommendKeyChangeInterval)) options:nil];
+ ((MPDayCountFormatter *)self.recommendKeyChangeIntervalTextField.formatter).valueFormat = valueFormat;
+}
+
+- (void)_updateTrashFolders:(KPKTree *)tree {
+ NSMenu *menu = [self _buildTrashTreeMenu:tree];
+ self.selectTrashGoupPopUpButton.menu = menu;
+}
+
+- (void)_updateTemplateGroup:(KPKTree *)tree {
+ NSMenu *menu = [self _buildTemplateTreeMenu:tree];
+ self.templateGroupPopUpButton.menu = menu;
+}
+
+- (NSMenu *)_buildTrashTreeMenu:(KPKTree *)tree {
+ NSMenu *menu = [self _buildTreeMenu:tree preselect:tree.metaData.trashUuid];
+
+ NSMenuItem *selectItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"AUTOCREATE_TRASH_FOLDER", @"Menu item for automatic trash creation")
+ action:NULL
+ keyEquivalent:@""];
+ selectItem.enabled = YES;
+ [menu insertItem:selectItem atIndex:0];
+
+ return menu;
+}
+
+- (NSMenu *)_buildTemplateTreeMenu:(KPKTree *)tree {
+ NSMenu *menu = [self _buildTreeMenu:tree preselect:tree.metaData.entryTemplatesGroupUuid];
+
+ NSMenuItem *selectItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"NO_TEMPLATE_GROUP", @"Menu item to reset the template groups")
+ action:NULL
+ keyEquivalent:@""];
+ selectItem.enabled = YES;
+ [menu insertItem:selectItem atIndex:0];
+
+ return menu;
+}
+
+
+- (NSMenu *)_buildTreeMenu:(KPKTree *)tree preselect:(NSUUID *)uuid {
+ NSMenu *menu = [[NSMenu alloc] init];
+ menu.autoenablesItems = NO;
+ for(KPKGroup *group in tree.root.groups) {
+ [self _insertMenuItemsForGroup:group atLevel:0 inMenu:menu preselect:uuid];
+ }
+ return menu;
+}
+
+- (void)_insertMenuItemsForGroup:(KPKGroup *)group atLevel:(NSUInteger)level inMenu:(NSMenu *)menu preselect:(NSUUID *)uuid{
+ NSMenuItem *groupItem = [[NSMenuItem alloc] init];
+ groupItem.image = group.iconImage;
+ groupItem.title = group.title;
+ groupItem.representedObject = group;
+ groupItem.enabled = YES;
+ if(uuid && [group.uuid isEqual:uuid]) {
+ groupItem.state = NSOnState;
+ }
+ groupItem.indentationLevel = level;
+ [menu addItem:groupItem];
+ for(KPKGroup *childGroup in group.groups) {
+ [self _insertMenuItemsForGroup:childGroup atLevel:level + 1 inMenu:menu preselect:uuid];
+ }
+}
+
+
@end
diff --git a/MacPass/MPSecurityDatabaseSettingsViewController.m b/MacPass/MPSecurityDatabaseSettingsViewController.m
index 24a4a1e1..8c49f5f0 100644
--- a/MacPass/MPSecurityDatabaseSettingsViewController.m
+++ b/MacPass/MPSecurityDatabaseSettingsViewController.m
@@ -7,9 +7,33 @@
//
#import "MPSecurityDatabaseSettingsViewController.h"
+#import "MPDocument.h"
+#import
+
@interface MPSecurityDatabaseSettingsViewController ()
+@property (assign) NSInteger argon2dMemory;
+@property (assign) NSInteger argon2idMemory;
+
+@property (strong) IBOutlet NSButton *createKeyDerivationParametersButton;
+@property (strong) IBOutlet NSPopUpButton *cipherPopupButton;
+@property (strong) IBOutlet NSPopUpButton *keyDerivationPopupButton;
+@property (strong) IBOutlet NSTabView *keyDerivationSettingsTabView;
+
+/* AES */
+@property (strong) IBOutlet NSTextField *aesEncryptionRoundsTextField;
+/* Argon2d */
+@property (strong) IBOutlet NSTextField *argon2dThreadsTextField;
+@property (strong) IBOutlet NSTextField *argon2dIterationsTextField;
+@property (strong) IBOutlet NSTextField *argon2dMemoryTextField;
+@property (strong) IBOutlet NSStepper *argon2dMemoryStepper;
+/* Argon2id */
+@property (strong) IBOutlet NSTextField *argon2idThreadsTextField;
+@property (strong) IBOutlet NSTextField *argon2idIterationsTextField;
+@property (strong) IBOutlet NSTextField *argon2idMemoryTextField;
+@property (strong) IBOutlet NSStepper *argon2idMemoryStepper;
+
@end
@implementation MPSecurityDatabaseSettingsViewController
@@ -19,4 +43,75 @@
// Do view setup here.
}
+- (void)_setupSecurityTab {
+
+ MPDocument *document = (MPDocument *)self.view.window.windowController.document;
+ KPKTree *tree = document.tree;
+ KPKMetaData *metaData = tree.metaData;
+
+ /*
+ If kdf or cipher is not found, exceptions are thrown.
+ This should not happen since we should not be able to load a file with unknown cipher/kdf
+ */
+ KPKKeyDerivation *keyDerivation = [KPKKeyDerivation keyDerivationWithParameters:metaData.keyDerivationParameters];
+ NSUInteger kdfIndex = [self.keyDerivationPopupButton.menu indexOfItemWithRepresentedObject:keyDerivation.uuid];
+ [self.keyDerivationPopupButton selectItemAtIndex:kdfIndex];
+ [self.keyDerivationSettingsTabView selectTabViewItemWithIdentifier:keyDerivation.uuid];
+
+ /* fill defaults for AES */
+ KPKAESKeyDerivation *aesKdf = [[KPKAESKeyDerivation alloc] initWithParameters:[KPKAESKeyDerivation defaultParameters]];
+ self.aesEncryptionRoundsTextField.integerValue = aesKdf.rounds;
+
+ /* fill defaults for Argon2d */
+ KPKArgon2DKeyDerivation *argon2dKdf = [[KPKArgon2DKeyDerivation alloc] initWithParameters:[KPKArgon2DKeyDerivation defaultParameters]];
+ self.argon2dIterationsTextField.integerValue = argon2dKdf.iterations;
+ self.argon2dMemory = argon2dKdf.memory;
+ self.argon2dThreadsTextField.integerValue = argon2dKdf.threads;
+
+ /* fill defaults for Argon2id */
+ KPKArgon2IDKeyDerivation *argon2idKdf = [[KPKArgon2IDKeyDerivation alloc] initWithParameters:[KPKArgon2IDKeyDerivation defaultParameters]];
+ self.argon2idIterationsTextField.integerValue = argon2idKdf.iterations;
+ self.argon2idMemory = argon2idKdf.memory;
+ self.argon2idThreadsTextField.integerValue = argon2idKdf.threads;
+
+ if([keyDerivation isMemberOfClass:KPKAESKeyDerivation.class]) {
+ /* set to database values */
+ KPKAESKeyDerivation *aesKdf = (KPKAESKeyDerivation *)keyDerivation;
+ self.aesEncryptionRoundsTextField.integerValue = aesKdf.rounds;
+ self.createKeyDerivationParametersButton.enabled = YES;
+ }
+ else if([keyDerivation isMemberOfClass:KPKArgon2DKeyDerivation.class]) {
+ /* set to database value */
+ KPKArgon2DKeyDerivation *argon2dKdf = (KPKArgon2DKeyDerivation *)keyDerivation;
+ self.argon2dMemory = argon2dKdf.memory;
+ self.argon2dThreadsTextField.integerValue = argon2dKdf.threads;
+ self.argon2dIterationsTextField.integerValue = argon2dKdf.iterations;
+ }
+ else if([keyDerivation isMemberOfClass:KPKArgon2IDKeyDerivation.class]) {
+ /* set to database value */
+ KPKArgon2IDKeyDerivation *argon2idKdf = (KPKArgon2IDKeyDerivation *)keyDerivation;
+ self.argon2idMemory = argon2idKdf.memory;
+ self.argon2idThreadsTextField.integerValue = argon2idKdf.threads;
+ self.argon2idIterationsTextField.integerValue = argon2idKdf.iterations;
+ }
+ else {
+ NSAssert(NO, @"Unkown key derivation");
+ }
+
+ self.argon2dMemoryStepper.minValue = 8*1024; // 8KB minimum
+ self.argon2dMemoryStepper.maxValue = NSIntegerMax;
+ self.argon2dMemoryStepper.increment = 1024*1024; // 1 megabytes steps
+ [self.argon2dMemoryTextField bind:NSValueBinding toObject:self withKeyPath:NSStringFromSelector(@selector(argon2dMemory)) options:nil];
+ [self.argon2dMemoryStepper bind:NSValueBinding toObject:self withKeyPath:NSStringFromSelector(@selector(argon2dMemory)) options:nil];
+
+ self.argon2idMemoryStepper.minValue = 8*1024; // 8KB minimum
+ self.argon2idMemoryStepper.maxValue = NSIntegerMax;
+ self.argon2idMemoryStepper.increment = 1024*1024; // 1 megabytes steps
+ [self.argon2idMemoryTextField bind:NSValueBinding toObject:self withKeyPath:NSStringFromSelector(@selector(argon2idMemory)) options:nil];
+ [self.argon2idMemoryStepper bind:NSValueBinding toObject:self withKeyPath:NSStringFromSelector(@selector(argon2idMemory)) options:nil];
+
+ NSUInteger cipherIndex = [self.cipherPopupButton.menu indexOfItemWithRepresentedObject:metaData.cipherUUID];
+ [self.cipherPopupButton selectItemAtIndex:cipherIndex];
+}
+
@end
diff --git a/MacPass/MPSecurityDatabaseSettingsViewController.xib b/MacPass/MPSecurityDatabaseSettingsViewController.xib
index 7d7a76a8..0ddcbe63 100644
--- a/MacPass/MPSecurityDatabaseSettingsViewController.xib
+++ b/MacPass/MPSecurityDatabaseSettingsViewController.xib
@@ -8,6 +8,7 @@
+
@@ -16,7 +17,319 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+