macOS keychain multi-threaded concurrency

I'm helping develop a macOS SDK that saves generic password items in the keychain. A calling app can have multiple threads reading/writing our SDK.


Referring to the guidance on this page: https://developer.apple.com/documentation/security/certificate_key_and_trust_services/working_with_concurrency?language=objc


For macOS is says, "In general, it is safe to use this API in threads other than your main thread, but avoid calling the functions from multiple operations, work queues, or threads concurrently. Instead, serialize function calls or confine them to a single thread."


Because of where this document is located, under "certificate_key_and_trust_services", it's not 100% clear if this advice applies to all keychain usage, including generic password items. Can you confirm whether this applies? Basically, what is the scope of "this API" in the above document? All keychain-related APIs (SecItemAdd(), SecItemUpdate(), SecItemDelete(), SecItemCopyMatching())?


Assuming we need to follow this advice, are concurrent keychain read operations from a single process OK? For example, is it safe to protect keychain API calls that write to the keychain (SecItemUpdate(), SecItemAdd(), SecItemDelete()) with dispatch_barrier_sync() and calls that only read items (SecItemCopyMatching()) with dispatch_sync()? If so, this would permit concurrent reads but still safely serialize writes.


Thanks,

Dean

First up, I agree that the article you referenced is insufficient; I’d appreciate you filing a bug against it, requesting clarification. Please post your bug number, just for the record.

Informally, I divide these APIs into two groups:

  • Stateful APIs, like

    SecTrust
  • Atomic APIs, like the

    SecItem
    API

When dealing with stateful APIs, it’s best to confine your work on a single object to a single thread. Even if the OS has all the required locking, you’ll just confuse yourself if you, say, modify a trust object from one thread while accessing it from another.

The atomic APIs are fully thread safe. There’s still an opportunity for confusing yourself — for example, if you call

SecItemUpdate
from two threads simultaneously — but you won’t confuse the system.

One thing to watch out for here is keychain item references, that is,

SecKey
,
SecCertificate
and
SecIdentity
. These tend to cache properties of the underlying item, and that cache may not be invalidated when you change the item. Fortunately this isn’t a big problem in practice because:
  • Items that have references tend to be immutable.

  • The most commonly mutated items, password items, don’t have references.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Documentation bug #6113746 entered.


Thanks!

@eskimo It's still not documented that SecItem APIs are atomic. We actually rely on this assumption: We assume that when you tell the keychain to delete everything, it will do so atomically. Is this assumption valid? Can you guarantee it won't change in the future?

Thanks

It's still not documented that SecItem APIs are atomic.

Indeed. I tried to look up the status of DeanFromSeattle’s but the bug number they posted is missing a digit )-:

We assume that when you tell the keychain to delete everything, it
will do so atomically. Is this assumption valid?

Are you concerned about macOS? Or iOS-based platforms?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@apple.com"
macOS keychain multi-threaded concurrency
 
 
Q