1
votes

I am currently trying to enable users of my app to add and retrieve data from the keychain using biometric authentication. When adding an item to the keychain, the call to SecItemAdd returns successful status, but when retrieving an item from the keychain via SecItemCopyMatching I get the -50 OSStatus (errSecParam), which indicates that one of my parameters are wrong.

When debugging, I removed either the kSecAttrAccessControl or kSecAttrAccessible parameters from the query, which got rid of the error. However, now I am faced with a different issue the system is retrieving the value from the keychain without first prompting the user.

Code that either results in an errSecParam (-50) result or no authentication prompt:

let query: [String: Any] = [
    kSecClass as String: kSecClassGenericPassword,
    kSecAttrService as String: "MyApp",
    kSecAttrAccount as String: "BiometricLogin",
    // removing this key from the SecItemCopyMatching query gets rid of the error but results in no authentication prompt
    kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
    kSecReturnAttributes as String: kCFBooleanTrue,
    kSecReturnData as String: kCFBooleanTrue,
    // removing this key from the SecItemCopyMatching query gets rid of the error but results in no authentication prompt
    kSecAttrAccessControl as String: SecAccessControlCreateWithFlags(kCFAllocatorDefault, kSecAttrAccessibleWhenUnlockedThisDeviceOnly, .biometryCurrentSet, nil)!,
    kSecUseAuthenticationUI as String: kSecUseAuthenticationUIAllow,
    kSecUseOperationPrompt as String: "Authenticate with Biometrics"
]

let addQuery = query.merging([(kSecValueData as String, "Hi".data(encoding: .utf8)!)], uniquingKeysWith: { $1 })
let status = SecItemAdd(query as CFDictionary, nil)
print(status) // prints 0, success

let retrieveQuery = query
var queryResult: AnyObject?
let status = SecItemCopyMatching(retrieveQuery as CFDictionary, &queryResult)
print(status) // prints -50 if both kSecAttrAccessible and kSecAttrAccessControl are in the query, and prints 0 if either of those attributes are removed but the result is no authentication prompt

So my question is: How can I get the user to be prompted with an authentication prompt to retrieve data from the keychain? To reiterate, I am using kSecClassGenericPassword, kSecAttrAccessibleWhenUnlockedThisDeviceOnly and .biometryCurrentSet.

1

1 Answers

3
votes
kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly,  

The above key causing the issue. This needs to be removed since its already added as part of

SecAccessControlCreateWithFlags(kCFAllocatorDefault, kSecAttrAccessibleWhenUnlockedThisDeviceOnly, .biometryCurrentSet, nil)!