registration: step-2 optional extraHeaders (A/B instrumentation — anisette-on-mint falsified live 2026-10-13)

This commit is contained in:
fenleon committed 2026-10-13 16:35:03 +02:00
1 parent 7980dd1fe2
commit e5352e77ce
2 files changed
+21 -4

No files matched your search

@@ -149,8 +149,7 @@ class GsaDelegateLoginClient(
val delegates = linkedMapOf<String, Any?>(
IDS_DELEGATE_BUNDLE_ID to linkedMapOf("protocol-version" to "4"),
MOBILEME_DELEGATE_BUNDLE_ID to linkedMapOf<String, Any?>(),
)
// The vendored reference (open_absinthe, RE 2026-10-13) ships ONLY the V1 request
) // The vendored reference (open_absinthe, RE 2026-10-13) ships ONLY the V1 request
// shape on this endpoint: kebab-case `AuthRequest {apple-id, client-id, delegates,
// password}` with the PET in the body — the .so has no V2AuthRequest/protocolVersion/
// userInfo strings at all. Basic `username:PET` still rides (the closure's
@@ -188,6 +187,18 @@ class GsaDelegateLoginClient(
put("User-Agent", SIGNIN_USER_AGENT)
}
val encoded = XmlPlist.encode(body)
// A/B instrumentation: dump the exact outgoing request (headers + body) when the
// marker exists, for a host-side rustls replay with byte-identical content. The
// dump carries the Basic credential — /data/local/tmp only, never logged.
if (java.io.File("/data/local/tmp/debug-capture-signin").isFile) {
runCatching {
java.io.File("/data/local/tmp/debug-last-signin.bin").writeBytes(
(listOf("POST $endpoint", "Content-Type: $CONTENT_TYPE") +
headers.entries.map { (k, v) -> "$k: $v" } + listOf("", "")).joinToString("\r\n")
.toByteArray(Charsets.UTF_8) + encoded,
)
}
}
val response = http.post(endpoint, headers, encoded, CONTENT_TYPE)
if (response.status != 200) {
throw GsaLoginException(
@@ -78,12 +78,18 @@ class ValidationCertClient(
/**
* Step 2: POST the single-key `session-info-request` plist body — no anisette, no auth
* headers (§1.1: body only) — and return the response's `session-info` data.
* [extraHeaders] rides verbatim (the A/B instrumentation: whether the genuine client
* binds the VEN session to an anisette identity here has never been wire-observed —
* the only accepted captures predate any anisette test).
*/
suspend fun initializeValidation(sessionInfoRequest: ByteArray): ByteArray {
suspend fun initializeValidation(
sessionInfoRequest: ByteArray,
extraHeaders: Map<String, String> = emptyMap(),
): ByteArray {
val url = bags.idsBag().url(ValidationBagKeys.INITIALIZE)
logEvent("validation: initializing the validation session")
val body = XmlPlist.encode(linkedMapOf("session-info-request" to sessionInfoRequest))
val response = http.post(url, emptyMap(), body, CONTENT_TYPE)
val response = http.post(url, extraHeaders, body, CONTENT_TYPE)
if (response.status != 200) {
throw ValidationDataException("POST $url → HTTP ${response.status} (§1.1 step 2)")
}