How can I find out if the iPhone user currently has a passcode set and encryption enabled?

Disclaimer: This answer was valid until ios 4.3.3

If data protection is turned on, a newly created file will have a nil NSFileProtectionKey by default.

If data protection is turned off, a newly created file will have a NSFileProtectionNone NSFileProtectionKey by default.

Thus, you could detect the presence of file protection with the following code:

NSString *tmpDirectoryPath = 
    [NSHomeDirectory() stringByAppendingPathComponent:@"tmp"];
NSString *testFilePath = 
    [tmpDirectoryPath stringByAppendingPathComponent:@"testFile"];
[@"" writeToFile:testFilePath 
      atomically:YES
        encoding:NSUTF8StringEncoding
           error:NULL]; // obviously, do better error handling
NSDictionary *testFileAttributes = 
    [[NSFileManager defaultManager] attributesOfItemAtPath:testFile1Path
                                                     error:NULL];
BOOL fileProtectionEnabled = 
    [NSFileProtectionNone isEqualToString:[testFile1Attributes objectForKey:NSFileProtectionKey]];

iOS 8 (OS X Yosemite) introduced a new API/constant used to detect if a user's device has a passcode.

kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly can be used to detect if a passcode is set on the device.

The flow is:

  1. Attempt to save a new item on the keychain with that attribute set
  2. If it succeeds that indicates that a passcode is currently enabled
  3. If the password doesn't get saved, that indicates there is no passcode
  4. Cleanup the item, because if it is already on the keychain it will make an "add" fail, looking like the passcode is not set

I have tested this on my iPhone 5S, first it returned true, then I disabled the passcode in settings, and it returned false. Finally, I re-enabled the passcode and it returns true. Prior OS versions will return false. The code works in simulator, returning true on a machine with OS X password set (I haven't tested alternate OS X scenarios).

Also see sample project here: https://github.com/project-imas/passcode-check/pull/5

Finally, to my knowledge iOS 8 doesn't have a setting to disable data protection, so I assume this is all you need to guarantee encryption.

BOOL isAPIAvailable = (&kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly != NULL);

// Not available prior to iOS 8 - safe to return false rather than crashing
if(isAPIAvailable) {

    // From http://pastebin.com/T9YwEjnL
    NSData* secret = [@"Device has passcode set?" dataUsingEncoding:NSUTF8StringEncoding];
    NSDictionary *attributes = @{
        (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
        (__bridge id)kSecAttrService: @"LocalDeviceServices",
        (__bridge id)kSecAttrAccount: @"NoAccount",
        (__bridge id)kSecValueData: secret,
        (__bridge id)kSecAttrAccessible: (__bridge id)kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly
    };

    // Original code claimed to check if the item was already on the keychain
    // but in reality you can't add duplicates so this will fail with errSecDuplicateItem
    // if the item is already on the keychain (which could throw off our check if
    // kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly was not set)

    OSStatus status = SecItemAdd((__bridge CFDictionaryRef)attributes, NULL);
    if (status == errSecSuccess) { // item added okay, passcode has been set
        NSDictionary *query = @{
            (__bridge id)kSecClass:  (__bridge id)kSecClassGenericPassword,
            (__bridge id)kSecAttrService: @"LocalDeviceServices",
            (__bridge id)kSecAttrAccount: @"NoAccount"
        };

        status = SecItemDelete((__bridge CFDictionaryRef)query);

        return true;
    }

    // errSecDecode seems to be the error thrown on a device with no passcode set
    if (status == errSecDecode) {
        return false;
    }
}

return false;

P.S. As Apple points out in the WWDC video introducing this (711 Keychain and authentication with Touch ID), they chose not to make the passcode-status directly available via API on purpose, in order to prevent apps from getting in situations they shouldn't be (i.e "Does this device have a passcode? Okay, great, I'll store this private info in plain text". It would be much better to create an encryption key, store it under kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly and encrypt that file, which will be unrecoverable if a user decides to disable their passcode).


Apple does not provide a method to determine whether the user has a passcode set.

If your app needs encryption, you should consider encrypting and decrypting the files with a trusted encryption implementation and either prompting the user for a passcode or storing the key in the keychain.