Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Checking for iOS device first unlock to determine if items stored with kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly are available

Assume a following situation:

  1. User restarts his/her iPhone.
  2. User lets device locked, does not unlock it.
  3. Server sends a (silent) push notification on the device (or anything happens that wakes the application up on the background, such as Apple Watch extension requests data, etc.).
  4. App wakes up and tries to access Keychain items stored with kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly accessibility.

Now, the Keychain items should not be accessible, since device was not unlocked yet. How do I correctly check for this situation?

Note: In my case, the presence of an item stored in the keychain determines if the app is "active" or not, so I would need something to stop this check soon enough, otherwise my app will assume it's not active (value cannot be read) and perform init steps...

like image 651
Petr Dvořák Avatar asked Nov 15 '16 16:11

Petr Dvořák


1 Answers

I've come across the same situation in my app and here's how I'm checking if the keychain is available (objective-c code):

+ (BOOL)isKeychainAvailable {
    NSString *testVal = @"testVal";
    NSString *testKey = @"testKey";
    [JNKeychain saveValue:testVal forKey:testKey];
    NSString *validatedValue = [JNKeychain loadValueForKey:testKey];
    [JNKeychain deleteValueForKey:testKey];
    return (validatedValue == testVal);
}

I basically save a value in the keychain and try to read it again. If it's not the same as I've just written it means the keychain is unavailable, which also means the phone hasn't been through the first unlock since the keychain should be available after the first unlock thanks to the option kSecAttrAccessibleAfterFirstUnlock.

What I ended up doing in this situation is terminating the app if it was started in the background and the keychain is unavailable:

- (void) methodStartedInBackgroundThatNeedsKeychain {
    if (!JNKeychain.isKeychainAvailable && [UIApplication sharedApplication].applicationState != UIApplicationStateActive) {
        exit(0);
    }
}

ATTENTION! please be aware that Apple strongly discourages the usage of exit(0) when the app is in foreground mode, thats why I'm making sure to only call it in background with [UIApplication sharedApplication].applicationState != UIApplicationStateActive. Here's Apple's QA discussion on the subject: https://developer.apple.com/library/archive/qa/qa1561/_index.html

like image 76
marcelosalloum Avatar answered Oct 29 '22 11:10

marcelosalloum