Запрос цепочки для ключей всегда возвращает errSecItemNotFound после обновления до iOS 13 - PullRequest
3 голосов
/ 21 июня 2019

Я сохраняю пароли для цепочки ключей iOS, а затем извлекаю их для реализации функции «запомнить меня» в моем приложении.

Я реализовал свою собственную оболочку для функций Security.framework (SecItemCopyMatching() и т. Д.), И до iOS 12 она работала как чудо.

Теперь я проверяю, что мойприложение не ломается с грядущей iOS 13, и о чудо:

SecItemCopyMatching() всегда возвращает .errSecItemNotFound

... даже если раньшесохранил данные, которые я запрашиваю.

Моя оболочка - это класс со статическими свойствами, позволяющий удобно предоставлять значения kSecAttrService и kSecAttrAccount при сборке диктонар запроса:

class LocalCredentialStore {

    private static let serviceName: String = {
        guard let name = Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String else {
            return "Unknown App"
        }
        return name
    }()
    private static let accountName = "Login Password" 

// ...

Я вставляю пароль в связку ключей с кодом, подобным следующему:

/* 
  - NOTE: protectWithPasscode is currently always FALSE, so the password
  can later be retrieved programmatically, i.e. without user interaction. 
 */
static func storePassword(_ password: String, protectWithPasscode: Bool, completion: (() -> Void)? = nil, failure: ((Error) -> Void)? = nil) {
    // Encode payload:
    guard let dataToStore = password.data(using: .utf8) else {
        failure?(NSError(localizedDescription: ""))
        return
    }

    // DELETE any previous entry:
    self.deleteStoredPassword()

    // INSERT new value: 
    let protection: CFTypeRef = protectWithPasscode ? kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly : kSecAttrAccessibleWhenUnlocked
    let flags: SecAccessControlCreateFlags = protectWithPasscode ? .userPresence : []

    guard let accessControl = SecAccessControlCreateWithFlags(
        kCFAllocatorDefault,
        protection,
        flags,
        nil) else {
            failure?(NSError(localizedDescription: ""))
            return
    }

    let insertQuery: NSDictionary = [
        kSecClass: kSecClassGenericPassword,
        kSecAttrAccessControl: accessControl,
        kSecValueData: dataToStore,
        kSecUseAuthenticationUI: kSecUseAuthenticationUIAllow,
        kSecAttrService: serviceName, // These two values identify the entry;
        kSecAttrAccount: accountName  // together they become the primary key in the Database.
    ]
    let resultCode = SecItemAdd(insertQuery as CFDictionary, nil)

    guard resultCode == errSecSuccess else {
        failure?(NSError(localizedDescription: ""))
        return
    }
    completion?()
}

... и позже, я извлекаю пароль с:

static func loadPassword(completion: @escaping ((String?) -> Void)) {

    // [1] Perform search on background thread:
    DispatchQueue.global().async {
        let selectQuery: NSDictionary = [
            kSecClass: kSecClassGenericPassword,
            kSecAttrService: serviceName,
            kSecAttrAccount: accountName,
            kSecReturnData: true,
            kSecUseOperationPrompt: "Please authenticate"
        ]
        var extractedData: CFTypeRef?
        let result = SecItemCopyMatching(selectQuery, &extractedData)

        // [2] Rendez-vous with the caller on the main thread:
        DispatchQueue.main.async {
            switch result {
            case errSecSuccess:
                guard let data = extractedData as? Data, let password = String(data: data, encoding: .utf8) else {
                    return completion(nil)
                }
                completion(password) // < SUCCESS

            case errSecUserCanceled:
                completion(nil)

            case errSecAuthFailed:
                completion(nil)

            case errSecItemNotFound:
                completion(nil)

            default:
                completion(nil)
            }
        }
    }
}

(Я не думаю, что какие-либо записи словарей, которые я использую для обоих вызовов, имеют неуместное значение ... но, возможно, я упускаю что-то, что только что произошло, чтобы "получить пропуск"до сих пор)

Я установил репозиторий с работающим профессионаломject (Xcode 11 beta), который демонстрирует проблему.

Сохранение пароля всегда успешно;Загрузка пароля:

  • Успешно на Xcode 10 - iOS 12 (и более ранних версиях), но
  • Сбой при .errSecItemNotFound наXcode 11 - iOS 13.

ОБНОВЛЕНИЕ: Не могу воспроизвести проблему на устройстве, только симулятор.На устройстве сохраненный пароль успешно восстановлен.Возможно, это ошибка или ограничение на iOS 13 Simulator и iOS 13 SDK для платформы x86.

...