iOS 8 (OS X Yosemite) представила новый API / константу, используемую для определения, имеет ли пользовательское устройство пароль.
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly
может использоваться для определения, установлен ли на устройстве пароль.
Поток:
- Попытка сохранить новый элемент в цепочке для ключей с этим атрибутом
- Если это успешно, это означает, что пароль в настоящее время включен
- Если пароль не сохраняется, это означает, что пароль отсутствует
- Очистите элемент, потому что, если он уже находится в цепочке для ключей, произойдет сбой «добавления», похоже, что пароль не установлен
Я проверил это на своем iPhone 5S, сначала он вернул true
, затем отключил пароль в настройках, и он вернул false
. Наконец, я снова включил пароль, и он возвращает true
. Предыдущие версии ОС вернут false
. Код работает в симуляторе, возвращая true
на машине с установленным паролем OS X (я не тестировал альтернативные сценарии OS X).
Также см. Пример проекта здесь: https://github.com/project-imas/passcode-check/pull/5
Наконец, насколько мне известно, в iOS 8 нет настройки для отключения защиты данных, поэтому я предполагаю, что это все, что вам нужно для гарантии шифрования.
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. Как указывает Apple на видео WWDC, представляющем это (711 Keychain и аутентификация с помощью Touch ID), они решили не делать статус-пароль напрямую доступным через API специально, чтобы не допустить попадания приложений в ситуации, в которых им не следует (т.е. «Есть ли у этого устройства код доступа? Хорошо, отлично, я буду хранить эту личную информацию в виде обычного текста». Было бы намного лучше создать ключ шифрования, сохранить его в kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly
и зашифровать этот файл, что быть невосстановимым, если пользователь решит отключить свой пароль).