将 SecAccessControl 与密码和生物识别结合使用时,Keychain SecItemAdd 失败

Keychain SecItemAdd fails when using SecAccessControl with Passcode & Biometry

我目前能够重现一个错误,其中 SecItemAdd 在设置了密码但尚未在设备上设置生物识别(但通常可用)的设备上进行测试时失败。

错误: OSStatus 给出 -25293,这似乎是 errSecAuthFailed.

我想达到的目标: 我想使用设备密码将项目存储到钥匙串作为最低安全要求。此外,如果启用生物识别,我希望允许用户使用它来保护和访问项目。与 .userPresence 相比,我想防止在生物统计 更改 时访问该项目,因此标志中的 .biometryCurrentSet 似乎是正确的选择。

因此保护级别的失败组合是kSecAttrAccessibleWhenPasscodeSetThisDeviceOnlySecAccessControlCreateFlags[.biometryCurrentSet, .or, .devicePasscode]

示例代码(可重现的最小演示):

import SwiftUI

struct ContentView: View {

    func keychainTest() {
        var attributes: [String: Any] = [kSecValueData as String: "abc".data(using: .utf8)!]
        var error: Unmanaged<CFError>?

        defer {
            error?.release()
        }

        guard let accessControl = SecAccessControlCreateWithFlags(
            nil,
            kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly as CFString,
            [.biometryCurrentSet, .or, .devicePasscode], // this fails when Device has Biometry deactivated
            &error
        ) else {
            debugPrint("Error creating Access Control")
            return
        }

        attributes[kSecAttrAccessControl as String] = accessControl

        // try adding new keychain item
        attributes[kSecClass as String] = kSecClassGenericPassword
        attributes[kSecAttrAccount as String] = "keychaintest.mybundle.com.keyid1234"

        let saveStatus = SecItemAdd(attributes as CFDictionary, nil)

        if saveStatus != errSecSuccess {
            debugPrint("Error Saving")
        }
    }

    var body: some View {
        Text("Hello, world!")
            .padding()
            .onAppear {
                self.keychainTest()
            }
    }

}

注意:最小演示是在 SwiftUI 中创建的,但原始失败源来自 UIKit 项目,这在这里无关紧要。 FaceID 隐私字符串设置在 Info.plist.

只要我使用 [.userPresence](相当于 [.biometryAny, .or, .devicePasscode])而不是想要的标志,SecItemAdd 就会成功。

我错过了什么?

编辑:当然存在具有相同键的上一个项目。我每次尝试都会更改它并使用新的测试设备 (iOS 14.4)。

编辑 2:这似乎是相关的。 。不一样(这里SecItemAdd),但是Access Control也创建了一个有效的引用但是后来Key方法失败了。

此外,saveStatus 失败分支中的属性转储:

(lldb) po attributes
▿ 4 elements
  ▿ 0 : 2 elements
    - key : "v_Data"
    ▿ value : 3 bytes
      - count : 3
      ▿ pointer : 0x000000016b282d50
        - pointerValue : 6092762448
      ▿ bytes : 3 elements
        - 0 : 97
        - 1 : 98
        - 2 : 99
  ▿ 1 : 2 elements
    - key : "accc"
    - value : <SecAccessControlRef: akpu;od(pkofn(1)cup(true)cbio(pbioc()pbioh()));odel(true);oe(true)>
  ▿ 2 : 2 elements
    - key : "acct"
    - value : "keychaintest.mybundle.com.keyid1234.13"
  ▿ 3 : 2 elements
    - key : "class"
    - value : genp

归档为雷达 FB9039075。

一位队友(https://whosebug.com/users/2451589/julien-klindt,感谢您!)之后收到了 Apple(使用 DTS)的反馈:

The .or operator here refers to the read operation. That is, to read the item the system must be able to satisfy one or more of the specified constraints. However, things are failing in your case at the add operation. Here the presence of .biometryCurrentSet indicates that the keychain must tag the item with the current biometric set, and that can’t possibly work because no biometrics are currently configured.

所以这是设计好的。如果需要,Apple 建议为所描述的行为创建一个功能请求。

In terms of workarounds, it seems reasonable to catch this error and then ask Local Authentication whether biometry is enabled. If it isn’t, you can fall back to .devicePasscode.

如果不需要更改生物特征使物品无效,我建议改用 .userPresence