diff --git a/Application/AppDelegate.m b/Application/AppDelegate.m index c175d01..a9f7a08 100644 --- a/Application/AppDelegate.m +++ b/Application/AppDelegate.m @@ -66,7 +66,7 @@ -(void)applicationDidFinishLaunching:(NSNotification *)notification NSArray* args = NSProcessInfo.processInfo.arguments; NSMutableDictionary* initialPreferences = [NSMutableDictionary dictionary]; - NSArray* prefKeys = @[PREF_PASSIVE_MODE, PREF_TOUCH_ID_MODE]; + NSArray* prefKeys = @[PREF_PASSIVE_MODE, PREF_TOUCH_ID_MODE, PREF_APPLE_WATCH_MODE]; //extract set key/value pairs for(NSString* key in prefKeys) { diff --git a/Application/Preferences.xib b/Application/Preferences.xib index 43ef7cc..a4e2122 100644 --- a/Application/Preferences.xib +++ b/Application/Preferences.xib @@ -30,7 +30,7 @@ - + @@ -84,11 +84,11 @@ - + - + @@ -108,7 +108,7 @@ - + @@ -117,7 +117,7 @@ - + @@ -137,7 +137,7 @@ - + @@ -145,8 +145,37 @@ + + + + + + + + + + + + + + + + + + + - + @@ -166,7 +195,7 @@ - + @@ -175,7 +204,7 @@ - + @@ -496,6 +525,15 @@ Install Telegram, create a free bot and paste your token below. Then scan the QR + diff --git a/Application/PrefsWindowController.m b/Application/PrefsWindowController.m index ac819c6..9a569b9 100644 --- a/Application/PrefsWindowController.m +++ b/Application/PrefsWindowController.m @@ -50,6 +50,7 @@ @implementation PrefsWindowController #define BUTTON_NO_REMOTE_ALERTS_MODE 5 #define BUTTON_EXECUTE_ACTION 6 #define BUTTON_NO_UPDATE_MODE 7 +#define BUTTON_APPLE_WATCH_MODE 8 //init 'general' view // add it, and make it selected @@ -119,6 +120,7 @@ -(void)showToolbarView:(NSInteger)tag view = self.modesView; ((NSButton*)[view viewWithTag:BUTTON_NO_ICON_MODE]).state = [self.preferences[PREF_NO_ICON_MODE] boolValue]; + ((NSButton*)[view viewWithTag:BUTTON_APPLE_WATCH_MODE]).state = [self.preferences[PREF_APPLE_WATCH_MODE] boolValue]; ((NSButton*)[view viewWithTag:BUTTON_TOUCH_ID_MODE]).state = [self.preferences[PREF_TOUCH_ID_MODE] boolValue]; break; @@ -240,6 +242,11 @@ -(IBAction)togglePreference:(id)sender case BUTTON_TOUCH_ID_MODE: updatedPreferences[PREF_TOUCH_ID_MODE] = state; break; + + //apple watch mode + case BUTTON_APPLE_WATCH_MODE: + updatedPreferences[PREF_APPLE_WATCH_MODE] = state; + break; //include image mode case BUTTON_ALERT_IMAGE_MODE: diff --git a/Daemon/Monitor.h b/Daemon/Monitor.h index 5fa96aa..8bdb222 100644 --- a/Daemon/Monitor.h +++ b/Daemon/Monitor.h @@ -47,6 +47,8 @@ //last touch ID auth timestamp (set by persistent ES client) @property(atomic, retain)NSDate* lastTouchIDAuth; +// last Apple Watch auth timetstamp (set by persistent ES client) +@property(atomic, retain)NSDate* lastAppleWatchAuth; /* METHODS */ @@ -54,6 +56,6 @@ -(BOOL)start; -(BOOL)isExternalDisplayActive; -(void)processEvent:(NSString*)timestamp; --(BOOL)waitForTouchID:(NSTimeInterval)timeout; +-(BOOL)waitForSecureAuth:(NSTimeInterval)timeout :(BOOL)touchIdAllowed :(BOOL)appleWatchAllowed :(NSString*) authMethodUsed; @end diff --git a/Daemon/Monitor.m b/Daemon/Monitor.m index 887e059..12135e3 100644 --- a/Daemon/Monitor.m +++ b/Daemon/Monitor.m @@ -107,17 +107,21 @@ static void pmDomainChange(void *refcon, io_service_t service, uint32_t messageT // log to file os_log_info(logHandle, "[NEW EVENT] lid state: open (sleep state: %d)", sleepState); - //touch id mode? + BOOL touchIdAllowed = YES == [preferences.preferences[PREF_TOUCH_ID_MODE] boolValue]; + BOOL appleWatchAllowed = YES == [preferences.preferences[PREF_APPLE_WATCH_MODE] boolValue]; + + //is a mode of secure authentication set to skip an alert? // wait up to 7 seconds, and ignore event if user auth'd via biometrics - if(YES == [preferences.preferences[PREF_TOUCH_ID_MODE] boolValue]) + if(touchIdAllowed || appleWatchAllowed) { //dbg msg - os_log_debug(logHandle, "'touch ID' mode enabled, waiting for biometric auth event"); + os_log_debug(logHandle, "'secure auth' mode enabled, waiting for event"); - //wait for touch ID - if(YES == [monitor waitForTouchID:7.0]) + //wait for a trustworthy authentication event + NSString* authMethodUsed = NULL; + if(YES == [monitor waitForSecureAuth:touchIdAllowed:appleWatchAllowed:7.0:authMethodUsed]) { - os_log_info(logHandle, "user authenticated via touch ID, ignoring event"); + os_log_info(logHandle, "user authenticated via %@, ignoring event", authMethodUsed); goto bail; } } @@ -250,8 +254,8 @@ -(BOOL)start goto bail; } - //start touch ID monitoring - [self startTouchIDMonitor]; + //start authentication monitoring + [self startAuthMonitor]; //happy registered = YES; @@ -285,8 +289,8 @@ -(void)stop //mark stopped running = NO; - //stop touch id monitoring - [self stopTouchIDMonitor]; + //stop authentication monitoring + [self stopAuthMonitor]; //have a dispatch queue? // serialize teardown with in-flight callbacks @@ -358,8 +362,8 @@ -(BOOL)isExternalDisplayActive return NO; } -//start persistent ES client for touch ID auth monitoring --(void)startTouchIDMonitor +//start persistent ES client for authentication event monitoring +-(void)startAuthMonitor { //already running? if(esAuthClient) return; @@ -370,18 +374,29 @@ -(void)startTouchIDMonitor //only care about auth events if(msg->event_type != ES_EVENT_TYPE_NOTIFY_AUTHENTICATION) return; - //only care about Touch ID successes - if(msg->event.authentication->type != ES_AUTHENTICATION_TYPE_TOUCHID) return; + //only care about auth events that unlocked the account if(msg->event.authentication->success != YES) return; - //record timestamp - self.lastTouchIDAuth = [NSDate date]; - - os_log_debug(logHandle, "touch ID auth detected"); + // only care about Touch ID or Apple Watch successes + switch(msg->event.authentication->type) { + //record timestamp + case ES_AUTHENTICATION_TYPE_TOUCHID: + self.lastTouchIDAuth = [NSDate date]; + os_log_debug(logHandle, "touch ID auth detected"); + break; + case ES_AUTHENTICATION_TYPE_AUTO_UNLOCK: + //only care about when the Apple Watch unlocked the machine itself + if(msg->event.authentication->data.auto_unlock->type != ES_AUTO_UNLOCK_MACHINE_UNLOCK) return; + self.lastAppleWatchAuth = [NSDate date]; + os_log_debug(logHandle, "apple watch auth detected"); + break; + default: + return; + } }); if(ES_NEW_CLIENT_RESULT_SUCCESS != result) { - os_log_error(logHandle, "ERROR: failed to create ES client for touch ID monitoring (result: %d)", result); + os_log_error(logHandle, "ERROR: failed to create ES client for authentication monitoring (result: %d)", result); esAuthClient = NULL; return; } @@ -395,43 +410,56 @@ -(void)startTouchIDMonitor return; } - os_log_debug(logHandle, "persistent touch ID monitor started"); + os_log_debug(logHandle, "persistent authentication monitor started"); } //stop persistent ES client --(void)stopTouchIDMonitor +-(void)stopAuthMonitor { if(esAuthClient) { es_unsubscribe_all(esAuthClient); es_delete_client(esAuthClient); esAuthClient = NULL; - os_log_debug(logHandle, "persistent touch ID monitor stopped"); + os_log_debug(logHandle, "persistent authentication monitor stopped"); } } -//wait for a touch ID auth event +//wait for a trustworhy auth event, Apple Watch or touch ID // polls the persistent ES client's timestamp --(BOOL)waitForTouchID:(NSTimeInterval)timeout +-(BOOL)waitForSecureAuth:(NSTimeInterval)timeout :(BOOL)touchIdAllowed :(BOOL)appleWatchAllowed :(NSString*) authMethodUsed { //no ES client? if(!esAuthClient) { - os_log_error(logHandle, "waitForTouchID: no ES client (FDA not granted?)"); + os_log_error(logHandle, "waitForSecureAuth: no ES client (FDA not granted?)"); return NO; } //reference time (lid just opened) NSDate* lidOpenTime = [NSDate date]; - //poll for touch ID auth + //poll for auth NSTimeInterval elapsed = 0; while(elapsed < timeout) { - //check if a touch ID auth occurred after lid open - NSDate* authTime = self.lastTouchIDAuth; + NSDate* authTime = NULL; + NSString* authTypeFound = NULL; + if(touchIdAllowed) { + //check if a touch ID auth occurred after lid open + authTime = self.lastTouchIDAuth; + authTypeFound = @"touch ID"; + } + + if(appleWatchAllowed) { + //check if an apple watch auth occured after lid open + authTime = self.lastAppleWatchAuth; + authTypeFound = @"Apple Watch"; + } + if(authTime && [authTime timeIntervalSinceDate:lidOpenTime] >= 0) { - os_log_debug(logHandle, "touch ID auth detected (%.1fs relative to lid open)", - [authTime timeIntervalSinceDate:lidOpenTime]); + authMethodUsed = authTypeFound; + os_log_debug(logHandle, "%@ auth detected (%.1fs relative to lid open)", + authTypeFound, [authTime timeIntervalSinceDate:lidOpenTime]); return YES; } @@ -440,7 +468,7 @@ -(BOOL)waitForTouchID:(NSTimeInterval)timeout elapsed += 0.25; } - os_log_debug(logHandle, "waitForTouchID: timed out after %.1fs", timeout); + os_log_debug(logHandle, "waitForSecureAuth: timed out after %.1fs", timeout); return NO; } diff --git a/Installer/Source/ConfigureWindowController.h b/Installer/Source/ConfigureWindowController.h index 39c0063..dc6b380 100644 --- a/Installer/Source/ConfigureWindowController.h +++ b/Installer/Source/ConfigureWindowController.h @@ -49,6 +49,7 @@ @property (strong) IBOutlet NSView *configureView; @property (weak) IBOutlet NSButton *passiveMode; +@property (weak) IBOutlet NSButton *appleWatchMode; @property (weak) IBOutlet NSButton *touchIDMode; //preferences diff --git a/Installer/Source/ConfigureWindowController.m b/Installer/Source/ConfigureWindowController.m index ad92e9a..b0f6574 100644 --- a/Installer/Source/ConfigureWindowController.m +++ b/Installer/Source/ConfigureWindowController.m @@ -163,6 +163,7 @@ -(IBAction)configureButtonHandler:(id)sender { if( (ACTION_SHOW_CONFIGURATION+1) == action) { self.preferences = @{ PREF_PASSIVE_MODE: @(self.passiveMode.state), + PREF_APPLE_WATCH_MODE: @(self.appleWatchMode.state), PREF_TOUCH_ID_MODE: @(self.touchIDMode.state) }; } @@ -293,6 +294,7 @@ -(IBAction)configureButtonHandler:(id)sender { NSDictionary* preferences = [NSDictionary dictionaryWithContentsOfFile:[INSTALL_DIRECTORY stringByAppendingPathComponent:PREFS_FILE]]; if(preferences) { self.passiveMode.state = [preferences[PREF_PASSIVE_MODE] integerValue]; + self.appleWatchMode.state = [preferences[PREF_APPLE_WATCH_MODE] integerValue]; self.touchIDMode.state = [preferences[PREF_TOUCH_ID_MODE] integerValue]; } @@ -345,6 +347,7 @@ -(IBAction)configureButtonHandler:(id)sender { execTask(OPEN, @[[@"/Applications" stringByAppendingPathComponent:APP_NAME], @"--args", INITIAL_LAUNCH, PREF_PASSIVE_MODE, [self.preferences[PREF_PASSIVE_MODE] description], + PREF_APPLE_WATCH_MODE, [self.preferences[PREF_APPLE_WATCH_MODE] description], PREF_TOUCH_ID_MODE, [self.preferences[PREF_TOUCH_ID_MODE] description]], NO, NO); } diff --git a/shared/Consts.h b/shared/Consts.h index 9418598..c276b86 100644 --- a/shared/Consts.h +++ b/shared/Consts.h @@ -104,6 +104,7 @@ #define PREF_NO_ICON_MODE @"noIconMode" #define PREF_PASSIVE_MODE @"passiveMode" +#define PREF_APPLE_WATCH_MODE @"appleWatchMode" #define PREF_TOUCH_ID_MODE @"touchIDMode" #define PREF_CHAT_ID @"telegramChatID"