Skip to content

Add SetUseDataProtectionKeychain for the data protection keychain#123

Open
alphaxdev wants to merge 1 commit into
keybase:masterfrom
alphaxdev:data-protection-keychain
Open

Add SetUseDataProtectionKeychain for the data protection keychain#123
alphaxdev wants to merge 1 commit into
keybase:masterfrom
alphaxdev:data-protection-keychain

Conversation

@alphaxdev
Copy link
Copy Markdown

Summary

go-keychain could only target the legacy file-based keychain on macOS, whose per-item ACLs can trigger "X wants to use your keychain" password prompts for apps not on the ACL. macOS 10.15+ also exposes the data protection keychain (the iOS-style keychain), where access is scoped by access group and entitlements instead. It is opted into per call via kSecUseDataProtectionKeychain, but the library exposed no way to set it — no setter, no exported key, and Item.attr is unexported.

This adds Item.SetUseDataProtectionKeychain(bool) and the exported UseDataProtectionKeychainKey attribute key.

Availability handling

kSecUseDataProtectionKeychain is API_AVAILABLE(macos(10.15), ios(13.0)) — below the README's stated minimums (macOS 10.9, iOS 8). Resolving the weak-imported symbol unconditionally at package init would pass NULL to CFStringToString and crash the entire package on older systems, even for callers not using the new API. To avoid that:

  • macOS: the key is resolved behind a __builtin_available(macOS 10.15, *) guard. On older systems it resolves empty and SetUseDataProtectionKeychain is a no-op.
  • iOS: the data protection keychain is the only keychain, so SetUseDataProtectionKeychain is a no-op that never references the constant, and UseDataProtectionKeychainKey is always empty.

The README's stated minimum OS versions are therefore unchanged.

Usage

item := keychain.NewItem()
item.SetSecClass(keychain.SecClassGenericPassword)
item.SetService("MyService")
item.SetAccount("gabriel")
item.SetAccessGroup("A123456789.group.com.mycorp")
item.SetData([]byte("toomanysecrets"))
item.SetUseDataProtectionKeychain(true)
err := keychain.AddItem(item)

The flag must be set consistently across add/query/update/delete for a given item — the two keychains are separate stores.

Tests

Adds keychain_test.go covering key resolution (including the availability branch), the setter, and the boolCFBoolean marshalling path. These are hermetic — no real keychain writes, so no prompts.

go build ./..., go vet ./..., and go vet -tags ios . all pass.

🤖 Generated with Claude Code

go-keychain could only target the legacy file-based keychain on macOS,
whose per-item ACLs can trigger "wants to use your keychain" prompts.
macOS 10.15+ also exposes the data protection (iOS-style) keychain,
opted into per call via kSecUseDataProtectionKeychain. The library had
no way to set it: no setter, no exported key, and Item.attr is
unexported.

Add Item.SetUseDataProtectionKeychain(bool) and the exported
UseDataProtectionKeychainKey attribute key.

kSecUseDataProtectionKeychain is API_AVAILABLE(macos(10.15), ios(13.0)),
below the README's stated minimums (macOS 10.9, iOS 8). Resolving the
weak-imported symbol unconditionally at package init would pass NULL to
CFStringToString and crash the whole package on older systems, so macOS
resolves the key behind a __builtin_available guard and the setter is a
no-op when it is unavailable. On iOS the data protection keychain is the
only keychain, so the setter is a no-op there and never references the
constant.

Add unit tests covering key resolution, the setter, and CFDictionary
marshalling.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@alphaxdev alphaxdev force-pushed the data-protection-keychain branch from 931ab79 to 7cc7af4 Compare May 14, 2026 23:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant