Verifying a stored file-based Keychain trusted-application requirement

I'm evaluating a macOS file-based Keychain ACL that should authorize one application identity, defined by an intended code-signing requirement. The application is a shared Python interpreter; I understand that trusting it does not restrict access to an individual script.

I read the related Apple Developer Forums discussion, which explains that complete ACL introspection is not supported and that the stored trusted-application code-signing requirement is accessible through SPI rather than public API.

My narrower question is whether there is a supported verification operation that does not require exposing that requirement:

Given a SecTrustedApplicationRef obtained through SecACLCopyContents, can a public API establish that its stored caller-matching requirement is no broader than the intended application requirement?

Confirming that the intended application is accepted would establish only that it matches, not that additional application identities are excluded. I also don't want to assume that CFEqual against a newly created reference, matching paths, or matching SecTrustedApplicationCopyData results proves equivalent authorization. Apple's documentation describes the CopyData result as opaque.

If this comparison is unsupported, what documented provisioning or operating-system enforcement guarantees should an application rely on instead? Is controlled provisioning the intended boundary, or is a different supported Keychain design appropriate?

Most importantly, if verifying the exact trusted-application restriction isn't supported, what is Apple's recommended secure approach for allowing a local Python trading application to retrieve Keychain credentials without granting access to every script using the same Python interpreter?

A fake-only example using a disposable test Keychain and synthetic generic-password item has been prepared but has not been built or run because full Xcode was unavailable. This is an API-contract question; no runtime failure or ACL attack is claimed, and no assumption is made that published source exactly matches the installed framework.

Answered by DTS Engineer in 916160022

Thanks for bringing this to the forums.

Taking a big step back, your entire approach is worrying because it relies on the file-based keychain and the file-based keychain is effectively deprecated. We talk about that in detail in TN3137 On Mac keychain APIs and implementations.


Returning to your specific questions:

Written by CarlosDevLab in 857161021

Given a SecTrustedApplicationRef obtained through SecACLCopyContents, can a public API establish that its stored caller-matching requirement is no broader than the intended application requirement?

I don’t see any good way to achieve this goal. The trusted app object maintains internal state that is not accessible via any of its accessor methods. For example, the object internally maintains a code-signing requirment (see TN3127) but there’s no way to get that [1].

Written by CarlosDevLab in 857161021

what is Apple's recommended secure approach for allowing a local Python trading application to retrieve Keychain credentials without granting access to every script using the same Python interpreter?

This is fundamentally impossible. Keychain access, in both the file-based and data protection implementations, is controlled by attributes of the main executable. For a scripting language, the main executable is always the same and thus there’s no way to grant access to one script while denying it to others.

The most obvious way around this is to embed the script interpreter into an app and then sign that app as its own separate thing. That app then gains its own code-signing requirement (for the file-based keychain) and entitlements (for the data protection keychain) and you can thus you can limit keychain access accordingly.

I can imagine other ways around this too — your Python code could talk to some agent that accesses the keychain on its behalf — but that’s not trivial to set up because it’s hard for the agent to know whether to trust the interpreter that it’s talking to.

Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] That includes SecTrustedApplicationCopyData. You are right that this is documented to return an opaque data blob but, if you look inside anyway, just as experiment, you’ll see that this blob doesn’t contain anything close to the hidden state that’s actually managed by this type. It’s basically just a path O-:

Thanks for bringing this to the forums.

Taking a big step back, your entire approach is worrying because it relies on the file-based keychain and the file-based keychain is effectively deprecated. We talk about that in detail in TN3137 On Mac keychain APIs and implementations.


Returning to your specific questions:

Written by CarlosDevLab in 857161021

Given a SecTrustedApplicationRef obtained through SecACLCopyContents, can a public API establish that its stored caller-matching requirement is no broader than the intended application requirement?

I don’t see any good way to achieve this goal. The trusted app object maintains internal state that is not accessible via any of its accessor methods. For example, the object internally maintains a code-signing requirment (see TN3127) but there’s no way to get that [1].

Written by CarlosDevLab in 857161021

what is Apple's recommended secure approach for allowing a local Python trading application to retrieve Keychain credentials without granting access to every script using the same Python interpreter?

This is fundamentally impossible. Keychain access, in both the file-based and data protection implementations, is controlled by attributes of the main executable. For a scripting language, the main executable is always the same and thus there’s no way to grant access to one script while denying it to others.

The most obvious way around this is to embed the script interpreter into an app and then sign that app as its own separate thing. That app then gains its own code-signing requirement (for the file-based keychain) and entitlements (for the data protection keychain) and you can thus you can limit keychain access accordingly.

I can imagine other ways around this too — your Python code could talk to some agent that accesses the keychain on its behalf — but that’s not trivial to set up because it’s hard for the agent to know whether to trust the interpreter that it’s talking to.

Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] That includes SecTrustedApplicationCopyData. You are right that this is documented to return an opaque data blob but, if you look inside anyway, just as experiment, you’ll see that this blob doesn’t contain anything close to the hidden state that’s actually managed by this type. It’s basically just a path O-: