Finding out if the device is locked, from a Notification Widget
Asked Answered
A

3

5

I'd like to know if the device is locked when I'm loading my Notification/Today widget, so I can show the widget appropriately. (it's financial, and we don't want to show balances on a locked phone)

On devices with TouchID, I can just try to access the Keychain, and if I get

errSecInteractionNotAllowed

back, it's locked. All good. This doesn't work on devices without touchID (but with a PIN). I've found a few things, which recommend using

[[UIApplication sharedApplication] protectedDataAvailable]

However I don't have [UIApplication sharedApplication] in a widget.

Any ideas where and how to do this? I just need a yes/no: is the device locked.

Thanks

[UPDATE: here's the code I have]

Getting the filename:

+ (NSString *)lockedDeviceFilename {
    NSURL *directoryUrl = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:USER_DEFAULTS_GROUP_NAME];
   return [directoryUrl.path stringByAppendingPathComponent:@"security.dummy"];
}

Writing / creating the file (in the app, not the extension:

NSError *error = nil;

NSString *documentPath = [FOOStorageGatekeeper lockedDeviceFilename];

[[NSFileManager defaultManager] removeItemAtPath:documentPath error:&error];

BOOL created = [[NSFileManager defaultManager] createFileAtPath:documentPath
                                                       contents:[@"super secret file contents. we only care about the permissions" dataUsingEncoding:NSUTF8StringEncoding]
                                                     attributes:@{NSFileProtectionKey : NSFileProtectionComplete}];

Reading:

 BOOL isReadable = [[NSFileManager defaultManager] fileExistsAtPath:[FOOStorageGatekeeper lockedDeviceFilename]];

  NSLog(@"isReadable? %@", isReadable ? @"YES" : @"NO");

It's always able to read the file, even on a TouchID device with the screen locked. If I look at the attributes, it shows the NSFileProtectionKey is set to NSFileProtectionComplete... but I can STILL READ IT :(

Update: found it. Marking Ian's answer as correct

Alienation answered 13/1, 2015 at 23:52 Comment(1)
Checking if a file exists is not the same as verifying that it's readable.Shoon
T
8

Create a file with NSFileProtectionComplete while your app is running and then attempt to access it from your extension. If you can't access it, the screen is locked.

[[NSFileManager defaultManager] createFileAtPath:someFilePath
                                        contents:[@"Lock screen test." dataUsingEncoding:NSUTF8StringEncoding]
                                      attributes:@{NSFileProtectionKey: NSFileProtectionComplete}];

EDIT: Final steps included to complete solution and consolidate answers. (Remaining work provided by Nic Wise.)

NSData *data = [NSData dataWithContentsOfURL:[FOOStorageGatekeeper lockedDeviceUrl] options: NSDataReadingMappedIfSafe error:&error];

if (error != nil && error.code == 257) {
    NSLog(@"**** the keychain appears to be locked, using the file method");
    return YES;
}

The other method, using errSecInteractionNotAllowed also works, but only for TouchID devices.

I found the answer (indirectly) here (rego with the iOS dev program most likely needed)

Thermopile answered 14/1, 2015 at 0:54 Comment(4)
Thanks Ian - I tried that, but I was creating it from the Extension. I'll try again from the app. ThanksAlienation
Marking Ian's answer as correct, as he was mostly there - see my own reply for how to read it back (you need both, obv)Alienation
I updated the answer to include your work just so it's all in one place.Thermopile
This is all great, but only works when the user has a passcodeAikido
A
2

Finally, after 3-4 days of looking, found the answer. It was more in how I was reading the result back. Ian is right: I need to create the file using createFileAtPath, but then read it back using

NSData *data = [NSData dataWithContentsOfURL:[FOOStorageGatekeeper lockedDeviceUrl] options: NSDataReadingMappedIfSafe error:&error];

if (error != nil && error.code == 257) {
    NSLog(@"**** the keychain appears to be locked, using the file method");
    return YES;
}

The other method, using errSecInteractionNotAllowed also works, but only for TouchID devices.

I found the answer (indirectly) here (rego with the iOS dev program most likely needed)

Alienation answered 14/1, 2015 at 2:49 Comment(0)
W
1

I tried that and my file was always readable (in lock screen or not).

I found this document : https://www.apple.com/business/docs/iOS_Security_Guide.pdf

It appeared that the files are locked 10 seconds after the device is locked.

Knowing that, you can create the files from the extensions, and it seems to work.

Wira answered 24/4, 2015 at 12:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.