123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182 |
- /*****************************************************************************
- * VLCKeychainCoordinator.m
- * VLC for iOS
- *****************************************************************************
- * Copyright (c) 2015 VideoLAN. All rights reserved.
- * $Id$
- *
- * Authors: Felix Paul Kühne <fkuehne # videolan.org>
- *
- * Refer to the COPYING file of the official project for license.
- *****************************************************************************/
- #import "VLCKeychainCoordinator.h"
- #import "PAPasscodeViewController.h"
- #import "VLCAppDelegate.h"
- #import "SSKeychain.h"
- #import <LocalAuthentication/LocalAuthentication.h>
- NSString *const VLCPasscodeValidated = @"VLCPasscodeValidated";
- NSString *const VLCPasscode = @"org.videolan.vlc-ios.passcode";
- @interface VLCKeychainCoordinator () <PAPasscodeViewControllerDelegate>
- {
- PAPasscodeViewController *_passcodeLockController;
- NSDictionary *_passcodeQuery;
- BOOL _inValidation;
- BOOL _inTouchID;
- }
- @end
- @implementation VLCKeychainCoordinator
- + (instancetype)defaultCoordinator
- {
- static VLCKeychainCoordinator *sharedInstance = nil;
- static dispatch_once_t pred;
- dispatch_once(&pred, ^{
- sharedInstance = [VLCKeychainCoordinator new];
- });
- return sharedInstance;
- }
- - (instancetype)init
- {
- self = [super init];
- if (!self)
- return nil;
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(appInForeground:)
- name:UIApplicationDidBecomeActiveNotification
- object:nil];
- return self;
- }
- - (void)dealloc
- {
- [[NSNotificationCenter defaultCenter] removeObserver:self];
- }
- - (void)appInForeground:(NSNotification *)notification
- {
- /* our touch ID session is killed by the OS if the app moves to background, so re-init */
- if (_inValidation) {
- if (SYSTEM_RUNS_IOS8_OR_LATER) {
- if ([[NSUserDefaults standardUserDefaults] boolForKey:kVLCSettingPasscodeAllowTouchID]) {
- [self _touchIDQuery];
- }
- }
- }
- }
- - (NSString *)_obtainPasscode
- {
- NSString *passcode = [SSKeychain passwordForService:VLCPasscode account:VLCPasscode];
- if (!passcode) {
- /* legacy passcode retrieval */
- NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
- passcode = [defaults objectForKey:kVLCSettingPasscodeKey];
- if (passcode)
- [self setPasscode:passcode];
- }
- return passcode;
- }
- - (BOOL)passcodeLockEnabled
- {
- NSString *passcode = [self _obtainPasscode];
- if (!passcode)
- return NO;
- if (passcode.length == 0)
- return NO;
- return YES;
- }
- - (void)validatePasscode
- {
- /* we may be called repeatedly as Touch ID uses an out-of-process dialog */
- if (_inValidation)
- return;
- _inValidation = YES;
- NSString *passcode = [self _obtainPasscode];
- if ([passcode isEqualToString:@""]) {
- [[NSNotificationCenter defaultCenter] postNotificationName:VLCPasscodeValidated object:self];
- }
- _passcodeLockController = [[PAPasscodeViewController alloc] initForAction:PasscodeActionEnter];
- _passcodeLockController.delegate = self;
- _passcodeLockController.passcode = passcode;
- VLCAppDelegate *appDelegate = (VLCAppDelegate *)[UIApplication sharedApplication].delegate;
- if (appDelegate.window.rootViewController.presentedViewController)
- [appDelegate.window.rootViewController dismissViewControllerAnimated:NO completion:nil];
- UINavigationController *navCon = [[UINavigationController alloc] initWithRootViewController:_passcodeLockController];
- navCon.modalPresentationStyle = UIModalPresentationFullScreen;
- [appDelegate.window.rootViewController presentViewController:navCon animated:NO completion:nil];
- if (SYSTEM_RUNS_IOS8_OR_LATER) {
- if ([[NSUserDefaults standardUserDefaults] boolForKey:kVLCSettingPasscodeAllowTouchID]) {
- [self _touchIDQuery];
- }
- }
- }
- - (void)_touchIDQuery
- {
- /* don't launch multiple times */
- if (_inTouchID)
- return;
- _inTouchID = YES;
- LAContext *myContext = [[LAContext alloc] init];
- if ([myContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:nil]) {
- [myContext evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
- localizedReason:NSLocalizedString(@"TOUCHID_UNLOCK", nil)
- reply:^(BOOL success, NSError *error) {
- if (success) {
- [self PAPasscodeViewControllerDidEnterPasscode:nil];
- } else if (error.code == LAErrorSystemCancel)
- _inTouchID = NO;
- }];
- }
- }
- - (void)PAPasscodeViewControllerDidEnterPasscode:(PAPasscodeViewController *)controller
- {
- [[NSNotificationCenter defaultCenter] postNotificationName:VLCPasscodeValidated object:self];
- VLCAppDelegate *appDelegate = (VLCAppDelegate *)[UIApplication sharedApplication].delegate;
- [appDelegate.window.rootViewController dismissViewControllerAnimated:YES completion:nil];
- _inValidation = NO;
- _inTouchID = NO;
- }
- - (void)PAPasscodeViewController:(PAPasscodeViewController *)controller didFailToEnterPasscode:(NSInteger)attempts
- {
- // FIXME: handle countless failed passcode attempts
- }
- - (void)setPasscode:(NSString *)passcode
- {
- if (!passcode) {
- [SSKeychain deletePasswordForService:VLCPasscode account:VLCPasscode];
- return;
- }
- [SSKeychain setPassword:passcode forService:VLCPasscode account:VLCPasscode];
- }
- @end
|