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>
@lazzurs

lazzurs commented Oct 20, 2026

Copy link
Copy Markdown

Tried this on a phone today and sign-in still fails.

One caveat before reading too much into it: the build was not #261 on its own. It was #261 merged with #273 (which moves the FindMy account's event loop onto its own thread), built as a fresh debug install from https://github.com/lazzurs/OpenTagViewer/tree/test/126-with-261 (run). So #273 is not ruled out as the cause yet.

To separate the two I'm building #261 alone (run) and will follow up with that result and a log.

@lazzurs

lazzurs commented Oct 20, 2026

Copy link
Copy Markdown

Follow-up: #261 on its own (debug build of 24569fd, run) fails the same way, so #273 is not the cause.

Fresh install on a Pixel. Library download, the new Android identity and provisioning all succeed. The first Grand Slam request then gets an HTTP 503 with a 190-byte text/html body:

I LocalAnisette: generated a new device identity as ANDROID - this must now be kept
I adi     : ADIProvisioningStart ok: session 3063594906, 276 bytes of client metadata
I adi     : ADIProvisioningEnd(session -1231372390) = 0
I AppleLoginActivity: local Anisette on the sign-in screen: READY
D AppleLoginActivity: Clicked login button
I python.stdout: [GSA-PROBE] unreadable response: ExpatError('mismatched tag: line 6, column 2')
W python.stderr: The Grand Slam request was refused with HTTP 503.
W python.stderr:   sent:     {'Content-Type': 'text/x-xml-plist', 'Accept': '*/*', 'User-Agent': 'akd/1.0 CFNetwork/978.0.7 Darwin/18.7.0', 'X-MMe-Client-Info': '<Pixel 7> <Android;14;UQ1A.240205.002> <com.apple.AuthKit/1 (com.apple.akd/1.0)>'}
W python.stderr:   received: {'Server': 'Apple', 'Date': 'Tue, 20 Oct 2026 22:45:20 GMT', 'Content-Type': 'text/html', 'Content-Length': '190', 'Connection': 'keep-alive', 'Keep-Alive': 'timeout=30', 'X-Frame-Options': 'SAMEORIGIN', 'Strict-Transport-Security': 'max-age=31536000; includeSubdomains'}
I python.stdout:   File ".../app/main.py", line 478, in loginSync
I python.stdout:   File ".../findmy/reports/account.py", line 2009, in login
I python.stdout:   File ".../findmy/reports/account.py", line 956, in login
I python.stdout:   File ".../findmy/reports/account.py", line 1485, in _gsa_authenticate
I python.stdout:   File ".../findmy/reports/account.py", line 1739, in _gsa_request
I python.stdout: findmy.errors.AppleServiceUnavailableError: The Grand Slam request was refused with HTTP 503. ...

Two things that may or may not matter:

  • The User-Agent still says Darwin/18.7.0 while X-MMe-Client-Info now claims Android. I don't know whether Grand Slam cross-checks these.
  • This was the third fresh install, and so the third freshly provisioned machine, signing in from this phone and network today. Throttling can't be ruled out. I'll retry later from the same install to see whether the 503 persists.

@lazzurs

lazzurs commented Oct 20, 2026

Copy link
Copy Markdown

Ran scripts/probe_gsa_signin.py from this branch to pin down the 503. Short version: the edge now refuses any X-MMe-Client-Info whose OS token is Android, whatever else is in the request. All probes used the synthetic probe-nonexistent@example.com, no credentials. They were sent 2026-10-20 ~23:30 UTC from a different machine than the phone in my previous comment.

Edge probes (junk body, headers only):

client-info User-Agent answer
<Pixel 7> <Android;14;UQ1A.240205.002> <com.apple.AuthKit/1 (com.apple.akd/1.0)> (the app's) library default akd/1.0 CFNetwork/978.0.7 Darwin/18.7.0 503 text/html, 190 B, x2
same akd/1.0 (Linux; Android 14), Probe/1.0, header removed 503, x2 each
<Pixel 8 Pro> <Android;15;AP4A.250205.002> ... default 503
<Pixel 7> <Android;14;...> <com.apple.AuthKit/1 (com.apple.android.music/4.9.6)> default 503
<MacBookPro18,3> <Android;14;UQ1A.240205.002> ... default 503
<Pixel 7> <android;14;...> ... (lower case) default 503
<Pixel 7> <Mac OS X;13.4.1;22F8> ... default through (404)
<Pixel 7> <Linux;14;...> ..., <Pixel 7> <Windows;10.0;19045> ... default through (404)
<iPhone13,2> <iPhone OS;17.5.1;21F90> ..., library default Mac default through (404)

So it isn't the User-Agent, model, build string or bundle. It's the OS token, matched case-insensitively. The Android and Mac requests went out back to back from the same IP and only the Android ones were refused, so it isn't per-IP throttling either.

--real-init (structurally real SRP init):

client-info cpd answer
Mac or iPhone baseline (no Anisette) ec=-80009 "MID is invalid."
Mac or iPhone --anisette-junk ec=-29004 "...possible environment mismatch."
Android (the app's) either 503 at the edge, never reaches the handler

The PR description reports ec=0 from an Android-provisioned machine under this exact client-info on 14 October, so this looks like a new edge rule since then. Combined with the hsc=434 the PR found for an Android-provisioned machine presented as a Mac, neither combination available to the app signs in right now. I couldn't test a provisioned (non-junk) cpd under a non-Android client-info from here; happy to if a dump format for the on-device provider would be useful.

Repro:

AND='<Pixel 7> <Android;14;UQ1A.240205.002> <com.apple.AuthKit/1 (com.apple.akd/1.0)>'
python scripts/probe_gsa_signin.py --client-info "$AND" --set "User-Agent=Probe/1.0" --unset User-Agent --repeat 2
python scripts/probe_gsa_signin.py --client-info '<Pixel 7> <Mac OS X;13.4.1;22F8> <com.apple.AuthKit/1 (com.apple.akd/1.0)>'
python scripts/probe_gsa_signin.py --real-init --anisette-junk

@parawanderer

Copy link
Copy Markdown
Owner

Hi @lazzurs check the last updates and linked FindMy.py issue on this, I think this is the most immediate thing that needs to be fixed/tested to unblock work on the App #248 (TL;DR yes we can't report we are an android phone or the old headers, we apparently should reproduce or use the iMusic or Mac ADI. I think that author was waiting on people to test their work in FindMy.py)

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.

4 participants