Skip to content

fix(PlayChain): return CFArray for numeric kSecMatchLimit (fixes FirebaseAuth re-login) - #240

Open
jokerlin wants to merge 3 commits into
PlayCover:masterfrom
jokerlin:fix/playchain-numeric-matchlimit-upstream
Open

jokerlin wants to merge 3 commits into
PlayCover:masterfrom
jokerlin:fix/playchain-numeric-matchlimit-upstream

Conversation

@jokerlin

@jokerlin jokerlin commented Oct 11, 2026 •

Copy link
Copy Markdown

Problem

Apps that use FirebaseAuth (e.g. Brown Dust 2, com.neowizgames.game.browndust2ios) ask the user to sign in again on every launch when PlayChain is enabled.

FirebaseAuth reads the saved user with kSecMatchLimit = 2 (a CFNumber) plus kSecReturnData and kSecReturnAttributes, and expects a CFArray of attribute dictionaries (AuthKeychainServices.getItem).

PlayKeychain.copyMatching only treats kSecMatchLimitAll as a multi-item query. For a numeric limit it returns a single dictionary with errSecSuccess. Firebase's as? [[String: Any]] cast fails and it throws:

[FirebaseAuth][I-AUT000001] Error loading saved user when starting up:
Error Domain=FIRAuthErrorDomain Code=17995 ... NSLocalizedFailureReason=SecItemCopyMatching (0) No error.

Because the read fails, Firebase also writes a fresh item on each sign-in, so duplicate genp rows pile up in the PlayChain db.

Fix

  • kSecMatchLimitAll or a CFNumber limit > 1 now returns a CFArray, truncated to the limit.
    The truncated list goes through the existing copyAllMatches, which already keeps v_Data when kSecReturnData is set.
  • The check is a private static method of PlayKeychain (multiMatchLimit), so copyMatching stays within SwiftLint limits.
  • Multi-item queries (numeric limit or kSecMatchLimitAll) now also handle existence checks (no kSecReturn* key: status only) and kSecReturnRef (array of SecKey). PlayedApple.swift disables file_length, as PlayAction.swift does.

The hook_setCurrentSubscription: signature change moved to #244.

Testing

FirebaseAuth queries with kSecMatchLimit = 2 and expects an array of
attribute dictionaries. PlayChain only recognised kSecMatchLimitAll, so
it returned a single dictionary with errSecSuccess and Firebase failed
with ERROR_KEYCHAIN_ERROR (17995) on every launch, forcing users to sign
in again.
@TheMoonThatRises

TheMoonThatRises commented Oct 11, 2026 •

Copy link
Copy Markdown
Member

Can you move the hook_setCurrentSubscription function signature change into a separate pull request? Also, multiMatchLimit should be a member method of PlayKeychain.

The match-limit check does not use instance state and is only called from
copyMatching, so it belongs on PlayKeychain. Behavior is unchanged.

Co-authored-by: jokerlin <jokerlin@users.noreply.github.com>
@jokerlin

jokerlin commented Oct 17, 2026 •

Copy link
Copy Markdown
Author

@TheMoonThatRises Thank you for the review. multiMatchLimit is now a private static method of PlayKeychain, the hook_setCurrentSubscription: signature change is split out into #244 (needed to build on the Xcode 27 SDK), and 0dc2366 also covers multi-item existence and kSecReturnRef queries; I re-verified the updated build in-game with Brown Dust 2 (v2.37.13), so both #244 and this PR are ready for another look when you have time.

…tem queries

copyAllMatches only built arrays of data or attribute dictionaries.
With a numeric or kSecMatchLimitAll limit:
- a query with no kSecReturn* key now only reports status, as the
  single-item path already did, instead of returning an array;
- a kSecReturnRef query now returns an array of SecKey references
  built by copyReference, instead of attribute dictionaries.

Disable file_length for PlayedApple.swift, as PlayAction.swift does.
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.

3 participants