diff --git a/MacPass/Base.lproj/PasswordInputView.xib b/MacPass/Base.lproj/PasswordInputView.xib index 88ade066b..86bc97b6c 100644 --- a/MacPass/Base.lproj/PasswordInputView.xib +++ b/MacPass/Base.lproj/PasswordInputView.xib @@ -1,8 +1,8 @@ - + - + @@ -15,6 +15,8 @@ + + @@ -23,13 +25,13 @@ - + - + - + - + - + - + @@ -61,15 +63,15 @@ DQ - + - + - + @@ -86,34 +88,34 @@ DQ + diff --git a/MacPass/Keychain/MPOSHelper.h b/MacPass/Keychain/MPOSHelper.h index 682f91f71..6737cf08d 100644 --- a/MacPass/Keychain/MPOSHelper.h +++ b/MacPass/Keychain/MPOSHelper.h @@ -8,9 +8,16 @@ #import @import LocalAuthentication; +#import "MPOSHelper.h" +#import "MPDocument.h" +#import "SAMKeychain.h" +#import "SAMKeychainQuery.h" +#import "MPSettingsHelper.h" @interface MPOSHelper : NSObject +(BOOL)supportsTouchID; +-(void)askForTouchID:(NSString*)password document:(NSString *)doc; +-(void)deletePasswordFromKeychain:(NSString *)doc; @end diff --git a/MacPass/Keychain/MPOSHelper.m b/MacPass/Keychain/MPOSHelper.m index f41497405..2e23df185 100644 --- a/MacPass/Keychain/MPOSHelper.m +++ b/MacPass/Keychain/MPOSHelper.m @@ -8,6 +8,11 @@ // #import "MPOSHelper.h" +#import "MPDocument.h" +#import "SAMKeychain.h" +#import "SAMKeychainQuery.h" +#import +#import "MPSettingsHelper.h" @implementation MPOSHelper @@ -38,4 +43,83 @@ +(BOOL)supportsTouchID { return NO; } +-(void) askForTouchID:(NSString*)password document:(NSString *)doc { + NSError *authError = nil; + LAContext *myContext = [LAContext new]; + if (@available(macOS 10.12.2, *)) { + if ([myContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&authError]) { + + NSAlert *alert = [NSAlert new]; + [alert addButtonWithTitle:@"Yes"]; + [alert addButtonWithTitle:@"No"]; + alert.messageText = NSLocalizedString(@"ALERT_TOUCH_ID_MESSAGE", @""); + alert.informativeText = NSLocalizedString(@"ALERT_TOUCH_ID_DESCRIPTION", @""); + [alert setAlertStyle:NSAlertStyleInformational]; + + if ([alert runModal] == NSAlertFirstButtonReturn) { + // Yes clicked, use TouchID + [self _savePasswordInKeychain:password document:doc]; + } else { + NSLog(@"User denied Touch ID. Deleting password from keychain."); + [self deletePasswordFromKeychain:doc]; + } + } + else { + NSAlert *alert = [NSAlert new]; + [alert addButtonWithTitle:@"Yes"]; + [alert addButtonWithTitle:@"No"]; + alert.messageText = NSLocalizedString(@"ALERT_TOUCH_ID_MESSAGE", @""); + alert.informativeText = NSLocalizedString(@"ALERT_TOUCH_ID_DESCRIPTION", @""); + [alert setAlertStyle:NSAlertStyleInformational]; + + if ([alert runModal] == NSAlertFirstButtonReturn) { + // Yes clicked, use TouchID + [self _savePasswordInKeychain:password document:doc]; + } else { + NSLog(@"User denied Touch ID. Deleting password from keychain."); + [self deletePasswordFromKeychain:doc]; + } + } + } + else { + // Fallback on earlier versions + + } +} +- (void) _savePasswordInKeychain:(NSString*)password document:(NSString *)dbName { +// MPDocument *document = doc; + // Uses document name -- could also attach file path + // However, if the file is move and/or filename is changes it will fail +// NSString *dbName = document.displayName; + NSError *error = nil; + + [SAMKeychain setPassword:password forService:@"MacPass" account:dbName]; + + + if (error == nil) { + [MPSettingsHelper addTouchIdEnabledDatabaseWithName:dbName]; //Add DB name in the list of Touch ID enabled databases + NSLog(@"Saved DB (%@) password in the keychain.", dbName); + } else { + NSLog(@"Error updating keychain with DB password: %@", error.localizedDescription); + } +} + +- (void) deletePasswordFromKeychain:(NSString *)dbName { +// MPDocument *document = doc; + // Uses document name -- could also attach file path + // However, if the file is moved and/or the filename is changed it will fail +// NSString *dbName = document.displayName; + NSError *error = nil; + + + + if (error == nil) { + [SAMKeychain deletePasswordForService:@"MacPass" account:dbName]; + [MPSettingsHelper removeTouchIdEnabledDatabaseWithName:dbName]; //Remove DB name from the list of Touch ID enabled databases + NSLog(@"DB (%@) password deleted from keychain.", dbName); + } else { + NSLog(@"Error deleting DB password from the keychain: %@", error.localizedDescription); + } +} + @end diff --git a/MacPass/MPPasswordInputController.h b/MacPass/MPPasswordInputController.h index 26ffd2b18..b1ed8be31 100644 --- a/MacPass/MPPasswordInputController.h +++ b/MacPass/MPPasswordInputController.h @@ -31,5 +31,4 @@ typedef BOOL (^passwordInputCompletionBlock)(NSString *password, NSURL *keyURL, - (void)requestPasswordWithCompletionHandler:(passwordInputCompletionBlock)completionHandler; - (void)requestPasswordWithMessage:(NSString *)message cancelLabel:(NSString *)cancelLabel completionHandler:(passwordInputCompletionBlock)completionHandler; - @end diff --git a/MacPass/MPPasswordInputController.m b/MacPass/MPPasswordInputController.m index b9da9a11b..4b9871e5f 100644 --- a/MacPass/MPPasswordInputController.m +++ b/MacPass/MPPasswordInputController.m @@ -52,9 +52,14 @@ @interface MPPasswordInputController () @property (assign) BOOL showPassword; @property (nonatomic, assign) BOOL enablePassword; +@property (nonatomic, assign) BOOL enableTID; @property (copy) passwordInputCompletionBlock completionHandler; @property (nonatomic, readonly) NSString *databaseName; @property (weak) IBOutlet NSButton *useTouchIdButton; +@property (strong) IBOutlet NSButton *touchidEnabled; +@property (weak) IBOutlet NSButtonCell *touchidEnable; +@property (nonatomic) BOOL touchIDCheckButton; + @end @implementation MPPasswordInputController @@ -84,6 +89,14 @@ - (void)viewDidLoad { [self.togglePasswordButton bind:NSEnabledBinding toObject:self withKeyPath:NSStringFromSelector(@selector(enablePassword)) options:nil]; [self.passwordTextField bind:NSEnabledBinding toObject:self withKeyPath:NSStringFromSelector(@selector(enablePassword)) options:nil]; [self _reset]; + if ([MPSettingsHelper.touchIdEnabledDatabases containsObject:self.databaseName]) { + self.touchidEnabled.state = NSOnState; + _touchIDCheckButton = [MPSettingsHelper.touchIdEnabledDatabases containsObject:self.databaseName]; + return; + } else { + self.touchidEnabled.state = NSOffState; + _touchIDCheckButton = [MPSettingsHelper.touchIdEnabledDatabases containsObject:self.databaseName]; + } } -(void)viewDidAppear { @@ -122,6 +135,7 @@ - (void)setEnablePassword:(BOOL)enablePassword { } } + - (NSString*) databaseName { // MPDocumentWindowController *documentWindow = self.windowController; // Pointer needs to be fixed - possible fix: @@ -199,6 +213,9 @@ - (void)_showError:(NSError *)error { - (IBAction)showTouchIdDialog:(id)sender { [self _enableTouchID]; } +- (IBAction)enableTIDforDB:(id)sender { + [self _enableTouchID]; +} - (NSTouchBar *)makeTouchBar { NSTouchBar *touchBar = [[NSTouchBar alloc] init]; @@ -235,6 +252,7 @@ - (void)_enableTouchID { if (![MPSettingsHelper.touchIdEnabledDatabases containsObject:self.databaseName]) { [_useTouchIdButton setHidden:YES]; + self.touchidEnabled.state = NSOffState; return; //Do not ask for TouchID if its not enabled for this database. } else if (MPOSHelper.supportsTouchID) { LAContext *myContext = [LAContext new]; @@ -253,7 +271,9 @@ - (void)_enableTouchID { } } else if ([MPSettingsHelper.touchIdEnabledDatabases containsObject:self.databaseName] && !MPOSHelper.supportsTouchID) { NSLog(@"Else - getting password from keychain"); - [self _getPasswordFromKeychain]; +// [self _getPasswordFromKeychain]; + [_useTouchIdButton setHidden:NO]; + self.touchidEnabled.state = NSOnState; } else { NSLog(@"Skipped Touch ID and Keychain authentication"); } @@ -274,7 +294,42 @@ - (void)_enableTouchID { // } else { // NSLog(@"TouchID is not supported."); // } +- (IBAction)unlockTrigger:(id)sender { + if (self.touchidEnabled.state && [MPSettingsHelper.touchIdEnabledDatabases containsObject:self.databaseName]) { + [self performSelector:@selector(unlockViaTouchID)]; + } else { + NSLog(@"touchid not setup"); + } +} +-(void)unlockViaTouchID { + if (MPOSHelper.supportsTouchID) { + LAContext *myContext = [LAContext new]; + NSString *myLocalizedReasonString = NSLocalizedString(@"TOUCHBAR_TOUCH_ID_MESSAGE", @""); + if (@available(macOS 10.12.2, *)) { + [myContext evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:myLocalizedReasonString reply:^(BOOL success, NSError * _Nullable error) { + if (success) { + // User authenticated successfully, take appropriate action + NSLog(@"User authentication sucessful! Getting password from the keychain..."); + [self _getPasswordFromKeychain]; + } else { + // User did not authenticate successfully, look at error and take appropriate action + NSLog(@"User authentication failed. %@", error.localizedDescription); + } + }]; + } else if ([MPSettingsHelper.touchIdEnabledDatabases containsObject:self.databaseName] && !MPOSHelper.supportsTouchID) { + NSLog(@"Else - getting password from keychain"); + [self _getPasswordFromKeychain]; + } else { + NSLog(@"Skipped Touch ID and Keychain authentication"); + } + } else if ([MPSettingsHelper.touchIdEnabledDatabases containsObject:self.databaseName] && !MPOSHelper.supportsTouchID) { + NSLog(@"Else - getting password from keychain"); + [self _getPasswordFromKeychain]; + } else { + NSLog(@"Skipped Touch ID and Keychain authentication"); + } +} - (void) _getPasswordFromKeychain{ // NSString *passwordItem = [SAMKeychain passwordForService:@"MacPass" account:self.databaseName]; @@ -321,6 +376,34 @@ - (void) _getPasswordFromKeychain{ // NSLog(@"Could not retrieve DB password from the keychain"); // } + + } +- (IBAction)turnOnTouchID:(NSString *)password { + MPOSHelper *helper = [[MPOSHelper alloc] init]; + BOOL buttonState = self.touchidEnabled.state; + if (![MPSettingsHelper.touchIdEnabledDatabases containsObject:self.databaseName]) { + + + password = self.passwordTextField.stringValue; + if (password.kpk_isNotEmpty) { + [helper askForTouchID:password document:self.databaseName]; + self.touchidEnabled.state = NSOnState; + } else + NSLog(@"password field is empty"); + self.touchidEnabled.state = NSOffState; + //Do not ask for TouchID if its not enabled for this database. + } else if (buttonState) { + NSLog(@"remove keychain"); + [helper deletePasswordFromKeychain:self.databaseName]; + self.touchidEnabled.state = NSOffState; + } else { + NSLog(@"else"); + [helper deletePasswordFromKeychain:self.databaseName]; + self.touchidEnabled.state = NSOffState; + } +} + + @end