Skip to content

fix(apple): don't evict caches during protection-class migration#162

Merged
jgowdy-godaddy merged 1 commit into
mainfrom
fix/lacontext-cache-eviction-on-migration
May 21, 2026
Merged

fix(apple): don't evict caches during protection-class migration#162
jgowdy-godaddy merged 1 commit into
mainfrom
fix/lacontext-cache-eviction-on-migration

Conversation

@jgowdy-godaddy
Copy link
Copy Markdown
Contributor

Summary

  • The protection-class migration in decrypt_with_cached_key called keychain_store() on every cache miss, which evicts both the wrapping-key cache and the LAContext registry
  • This created a self-perpetuating eviction cycle: every sign triggered a fresh Touch ID prompt because the caches were always empty
  • Extract keychain_store_ffi (raw FFI, no eviction) and use it in the migration path — the key bytes are unchanged, so cached entries remain valid
  • keychain_store retains eviction for callers that change key bytes (generate, relabel)

Root cause

PR #158 added a lazy protection-class migration that re-stores the wrapping key on cache miss. The re-store called keychain_store()cache_evict() → nuked both the wrapping-key cache and LAContext registry. Next sign saw another cache miss → migration fired again → infinite cycle. Every sign got a fresh LAContext and Touch ID prompt.

Test plan

  • cargo test --workspace — all existing tests pass
  • cargo clippy --workspace --all-targets -- -D warnings — clean
  • cargo fmt --all -- --check — clean
  • Manual: after upgrade, first sign triggers Touch ID, subsequent signs within cache window complete in <50ms with no prompt

…ction-class migration

decrypt_with_cached_key re-stores the wrapping key on every cache miss
to migrate items to AfterFirstUnlockThisDeviceOnly. It called
keychain_store(), which evicts both the wrapping-key cache and the
LAContext registry. This created a self-perpetuating cycle: every sign
saw a cache miss, triggered the migration, evicted both caches, and
forced a fresh Touch ID prompt on the next sign.

Extract keychain_store_ffi (the raw FFI call without eviction) and use
it in the migration path. The key bytes are unchanged — only the
protection class is updated — so the cached entries remain valid.
keychain_store retains the eviction for callers that change key bytes
(generate_and_wrap, relabel_wrapping_key).

Restores single-prompt-per-cache-window behavior: first sign triggers
Touch ID, subsequent signs within the TTL reuse the cached LAContext.
@jgowdy-godaddy jgowdy-godaddy merged commit 1c27daf 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.

1 participant