Skip to content

fix(apple): restore WhenUnlockedThisDeviceOnly for SE key access control#160

Merged
jgowdy-godaddy merged 1 commit into
mainfrom
fix/restore-se-key-protection-class
May 21, 2026
Merged

fix(apple): restore WhenUnlockedThisDeviceOnly for SE key access control#160
jgowdy-godaddy merged 1 commit into
mainfrom
fix/restore-se-key-protection-class

Conversation

@jgowdy-godaddy
Copy link
Copy Markdown
Contributor

Summary

  • PR fix(apple): handle errSecInteractionNotAllowed and change keychain protection class #158 blanket-replaced all protection classes to AfterFirstUnlockThisDeviceOnly, including makeAccessControl() which controls the SE key's access control at generation time. This broke CryptoKit's touchIDAuthenticationAllowableReuseDuration — every sign required Touch ID instead of caching biometric auth within the TTL window.
  • Reverts makeAccessControl() back to kSecAttrAccessibleWhenUnlockedThisDeviceOnly while keeping keychain_store() at kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly (correct for sleep/wake survival).
  • Adds AGENTS.md with protection class safety rules and updates CLAUDE.md to document the split, preventing this class of mistake from recurring.

Impact: Keys generated after the PR #158 change have the wrong SE access control baked into the SEP. Since the SE key access control is immutable after creation, affected keys must be regenerated.

Test plan

  • cargo test --workspace passes
  • cargo clippy --workspace --all-targets -- -D warnings clean
  • After key regeneration with this fix: first sign triggers Touch ID (~2-3s), second sign within cache window completes in <50ms
  • Agent still survives sleep/wake (wrapping key retains AfterFirstUnlockThisDeviceOnly)

PR #158 blanket-replaced all protection classes to
AfterFirstUnlockThisDeviceOnly, including makeAccessControl() which
sets the Secure Enclave key's access control at generation time. This
broke CryptoKit's touchIDAuthenticationAllowableReuseDuration — every
sign required Touch ID instead of caching biometric auth. Since the
SE key's access control is immutable after creation, fixing this
requires regenerating all affected keys.

The keychain wrapping key (keychain_store) correctly uses
AfterFirstUnlockThisDeviceOnly for sleep/wake survival. The SE key
access control must use WhenUnlockedThisDeviceOnly for biometric
caching. These are separate decisions with different requirements.

Also adds AGENTS.md with protection class safety rules and updates
CLAUDE.md to document the split, so this class of mistake is
prevented in future changes.
@jgowdy-godaddy jgowdy-godaddy merged commit b08880c into main May 21, 2026
3 checks passed
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.

2 participants