I see a three of issues here. First, you’re asking for something that doesn’t make sense. accessControl uses a this-device-only modifier, but you’re setting kSecAttrSynchronizable to true. What’s the point of synchronising something if it’s limited to this device only?
This stems from a misunderstanding of kSecAttrAccessibleWhenUnlockedThisDeviceOnly, I think. I set it to kSecAttrAccessibleWhenUnlocked, and it still didn’t work.
Second, you’re setting kSecAttrAccessControl and kSecAttrAccessible. That’s weird because the former subsumes the latter. Note how the value you’re using to kSecAttrAccessible is also being passed when you create accessControl.
I removed the kSecAttrAccessible attribute altogether, and it still gives me paramErr.
Persistent references to synchronizable items should be avoided; while they may work locally, they cannot be moved between devices, and may not resolve if the item is modified on some other device.
I only use the identifier locally and in-memory, so it doesn’t matter if it’s not valid on other machines, but this seems like an unnecessary limitation either way, since it’s not hard to make a globally unique identifier.
Something that's confusing to me is that when you call SecAccessControlCreateWithFlags(), you pass both "protection" and "access control" constraints, which seem to have at least semantic overlap. I want the user to have to provide access to the password each time it is used, rather than only when unlocked. My understanding is that that’s what .userPresence does, but it seems like that could have also been a protection class. What does it mean to say kSecAttrAccessibleAlways and .userPresence, for example? And the "ThisDeviceOnly" variants could imply that unlocking on one device doesn't automatically unlock on another device, hence why I was using that.
In any case, I'm using kSecAttrAccessControl because I want each use of the password to require the user actually be there (I get that the system won’t always prompt, that it caches authorization for some amount of time, but that's okay).
In any case, I still can't figure out why it won’t let me add .userPresence to the password.
…
On a whim I removed the kSecAttrSynchronizable = true, and that got rid of the error! I’m not explicitly using kSecUseDataProtectionKeychain, which is not what I was expecting. For example, TN3137 says:
To target the data protection keychain, set the kSecUseDataProtectionKeychain attribute or the kSecAttrSynchronizable attribute to true.
And lastly just to confirm: Something stored with .userPresence requires presence even to query attributes and not the password value itself? That's the behavior I'm seeing. I can work with that, but I wasn't expecting it for some reason.
Topic:
Privacy & Security
SubTopic:
General