Repository navigation
Conversation
There was a problem hiding this comment.
🟡 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.
| ANDROID( | ||
| "Pixel 7", | ||
| "Pixel 7", "Android", "14", "UQ1A.240205.002", | ||
| "1404.0.5", "22.3.0", | ||
| "com.apple.akd/1.0") { |
| if (path.exists() && !path.delete()) { | ||
| Log.w(TAG, "could not delete " + path + "; ADI may keep answering as the old machine"); |
| verdict = _handler_verdict(answer.raw) if is_real_init else None | ||
| if verdict is None: | ||
| verdict = "refused" if answer.refused else "through" |
…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>
256f912 to
e82f0af
Compare
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>
… 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>
|
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. |
|
Follow-up: #261 on its own (debug build of 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 Two things that may or may not matter:
|
|
Ran Edge probes (junk body, headers only):
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.
The PR description reports Repro: |
|
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) |

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.files/anisette/provisioningis 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.comchains 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.pygains--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=0from a freshly Android-provisioned machine id, live, via--real-initADIProvisioningEnd = 0), password + 2FA acceptedNot verified, stated plainly
After successful auth our test account hit
com.apple.mobilemestatus=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.