Skip to content

Sign in as the Android device the ADI libraries actually are — fixes #248 - #261

Open
rubo77 wants to merge 5 commits into
parawanderer:mainfrom
rubo77:feature/gsa-real-init
Open

rubo77 wants to merge 5 commits into
parawanderer:mainfrom
rubo77:feature/gsa-real-init

Conversation

@rubo77

@rubo77 rubo77 commented Oct 14, 2026

Copy link
Copy Markdown
Contributor

Since 2026-10-12 Grand Slam refuses SRP init (ec=-80045) when a machine id minted by Apple's Android ADI libraries is presented under a macOS or iPhone client-info — every install of this app.

What this changes

  • Hardware.ANDROID (Pixel 7 / Android;14;UQ1A.240205.002 / com.apple.akd/1.0) is the default profile; LEGACY_MAC/IPHONE are marked unable to sign in.
  • Stored refused identities are replaced and files/anisette/provisioning is wiped before the library opens, so ADI re-provisions under the Android claim. A Mac-provisioned machine presented as Android is answered hsc=434 — re-provisioning is required, not just a new string. Costs affected installs one re-login.
  • gsd.apple.com chains to Apple Root CA - G3, absent from trust stores: added as an anchor for gsa/gsd. This is also the -45003/502 every public Anisette server hit.
  • scripts/probe_gsa_signin.py gains --real-init: a structurally real SRP init (no credentials, synthetic account) that reports the handler's ec — the tool the refusal was localised with.

Verified

  • ec=0 from a freshly Android-provisioned machine id, live, via --real-init
  • Pixel 8 Pro: libraries download, provisioning completes (ADIProvisioningEnd = 0), password + 2FA accepted
  • 469 JVM + 46 pytest + 35 instrumented tests green

Not verified, stated plainly

After successful auth our test account hit com.apple.mobileme status=1 — the account-level wall of #221, unrelated to this fix but meaning a full login through to fetching awaits an account that clears it. Whether the Android claim changes mobileme's behaviour for previously-working accounts is unknown; it is the only claim that reaches that stage at all.

Credit

The root-cause detail and the field-level probe matrix are in the issue comment on #248. Credit to xtool-org/xtool#337 for finding the client-info side independently.

Copilot AI balanced review requested due to automatic review settings October 14, 2026 06:23

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Failed provisioning cleanup can permanently retain stale identity state, and the new Android profile is rendered with the wrong device icon.

3 open findings
What changed in this PR

Updates local Anisette to use an Android-consistent identity, enabling Grand Slam sign-in after Apple’s provenance checks changed.

Changes:

  • Adds the Android hardware profile and migrates incompatible stored identities.
  • Adds Apple Root CA G3 trust for provisioning.
  • Extends the GSA probe and identity tests.
File Description
scripts/​probe_gsa_signin.py Adds real SRP-init probes.
app/​src/​test/​python/​test_identity.py Tests Android identity bridging.
app/​src/​main/​res/​xml/​network_security_config.xml Trusts G3 for Apple endpoints.
app/​src/​main/​res/​raw/​apple_root_ca_g3.pem Adds Apple’s G3 root certificate.
app/​src/​main/​java/​dev/​wander/​android/​opentagviewer/​anisette/​LocalAnisette.java Migrates refused identities and provisioning.
app/​src/​main/​java/​dev/​wander/​android/​opentagviewer/​anisette/​AdiDeviceIdentity.java Defines Android as the default profile.
app/​src/​androidTest/​java/​dev/​wander/​android/​opentagviewer/​anisette/​LocalAnisetteIdentityTest.java Covers identity migration behavior.
app/​src/​androidTest/​java/​dev/​wander/​android/​opentagviewer/​anisette/​AdiDeviceIdentityTest.java Verifies Android profile composition.
AGENTS.md Documents the provenance and trust constraints.

🧠 Review effort: Balanced


💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +291 to +295
ANDROID(
"Pixel 7",
"Pixel 7", "Android", "14", "UQ1A.240205.002",
"1404.0.5", "22.3.0",
"com.apple.akd/1.0") {
Comment on lines +471 to +472
if (path.exists() && !path.delete()) {
Log.w(TAG, "could not delete " + path + "; ADI may keep answering as the old machine");
Comment on lines +269 to +271
verdict = _handler_verdict(answer.raw) if is_real_init else None
if verdict is None:
verdict = "refused" if answer.refused else "through"
@parawanderer
parawanderer self-requested a review October 14, 2026 07:05
@parawanderer parawanderer self-assigned this Oct 14, 2026
@parawanderer parawanderer mentioned this pull request Oct 14, 2026
1 task done
rubo77 and others added 2 commits October 14, 2026 11:28
…here

- --real-init builds the structurally real SRP init body that
  AsyncAppleAccount._gsa_authenticate sends (srp.User A2k over a synthetic,
  nonexistent Apple ID - init precedes any credential check, so this is still
  not a sign-in attempt) and reports the response plist's Status.ec/em
- --anisette-json loads a provisioned cpd dump, --anisette-junk fills
  deterministic placeholders, --cpd-set/--cpd-unset vary single body fields
- The edge probes remain untouched; the doc comment that described this mode
  as future work now describes what it found
- With it, the 2026-10-12 refusal localises to one check: an Android-minted
  X-Apple-I-MD-M under a macOS client-info is refused ec=-80045 before OTP,
  serial or device-id are looked at; missing MD-M is -80009, malformed is
  -29004, missing OTP is -80010 - and an Android client-info over a freshly
  Android-provisioned machine id is answered ec=0 (issue parawanderer#248,
  xtool-org/xtool#337)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Since 2026-10-12 Grand Slam refuses SRP init (ec=-80045) when a machine id
minted by Apple's Android ADI libraries is presented under a macOS or iPhone
client-info - issue parawanderer#248, and every install of this app.

- Hardware.ANDROID (Pixel 7 / Android 14 / UQ1A.240205.002, the string xtool
  verified in xtool-org/xtool#337) is the new DEFAULT; canSignIn() marks
  LEGACY_MAC and IPHONE as refused at the handler
- LocalAnisette replaces a stored identity whose profile can no longer sign
  in and wipes files/anisette/provisioning before the library is opened, so
  ADI re-provisions under the Android claim - a Mac-provisioned machine id
  presented as Android is answered hsc=434. Costs affected installs one
  re-login; the alternative is an install that can never sign in again
- profileToShow describes the profile the next load will actually use
- Tests: AdiDeviceIdentityTest pins the Android client-info string and
  canSignIn(); LocalAnisetteIdentityTest covers keep-vs-replace and the
  provisioning wipe; test_identity.py pins that the six bridged values
  render as the verified client-info through FindMy.py's own composer

Verified end to end: a freshly Android-provisioned machine id answers ec=0
via scripts/probe_gsa_signin.py --real-init, and on a Pixel 8 Pro this build
downloads the libraries, provisions under the Android identity
(ADIProvisioningEnd = 0) and signs in - password and 2FA accepted - through
to the account step. 469 JVM tests, 46 pytest, 35 instrumented tests green.

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@rubo77
rubo77 force-pushed the feature/gsa-real-init branch from 256f912 to e82f0af Compare October 14, 2026 09:35
@rubo77
rubo77 deployed to Android Build October 14, 2026 09:35 — with GitHub Actions Active
@rubo77
rubo77 deployed to Android Build October 14, 2026 09:35 — with GitHub Actions Active
Whether an Android identity is handed searchPartyToken at all is the open
question in parawanderer#248 and malmeloo/FindMy.py#278, and it can only be answered by
looking at a live response - so a completed sign-in now logs the token names
issued. In exportToString and after complete_login, the two points every
successful login passes through. Names only: a token's value is a credential
and must not reach a log; which names exist is the diagnostic.

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@rubo77
rubo77 deployed to Android Build October 14, 2026 10:31 — with GitHub Actions Active
@rubo77
rubo77 deployed to Android Build October 14, 2026 10:31 — with GitHub Actions Active
rubo77 and others added 2 commits October 14, 2026 15:07
… provenance

Two stacked experiments around the delegate refusal that follows the
-80045 fix:

- _claimAppleMusicAtMobileme: the delegate request identifies as macOS
  accountsd/iCloudHelper while the identity claims Android - the same
  class of inconsistency Grand Slam rejects one layer earlier. Claiming
  com.apple.android.music instead produced status=1 unchanged on both a
  never-activated and an iCloud/Find My-activated account, so the
  header claim is not the gate.
- loginSync forces the remote Anisette provider so the machine id
  presented is not minted by the local Android ADI libraries. Pending:
  whether status flips with a server-provisioned identity, which would
  locate the gate at ADI provenance itself.

Experiment instrumentation, not a product change.

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
… names

- [MOBILEME-PROBE] logs idms_pet/adsid/idms_hb lengths at the delegate
  step and the delegate response's top-level keys (values as str[NNN]).
  Result on both test accounts via server anisette: idms_pet_len=0,
  status-message='Authentication failed' - the request went out with
  an empty password; status=1 is mechanical, not an account gate.
- [GSA-PROBE] logs hsc/au/ec per GsService2 step and, after
  decrypt_spd_aes_cbc, the spd keys and issued token names in t.
  au=None runs show no pet - consistent with the pet being minted only
  on the trusted-device challenge path (hoorayman: hsc=409 -> pet=523).
- The decrypt hook patches findmy.util.crypto because account.py
  resolves it through the module attribute.

Findings posted to parawanderer#248.

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@rubo77
rubo77 deployed to Android Build October 14, 2026 20:40 — with GitHub Actions Active
@rubo77
rubo77 deployed to Android Build October 14, 2026 20:40 — with GitHub Actions Active

This branch was successfully deployed

1 active deployment
Android Build — 24569fd1 Deployed Oct 14, 2026 by rubo77 via build #334
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